void dbg_reply_get_thread_list(struct dbg_context* dbg, const dbg_threadid_t* threads, size_t len) { assert(DREQ_GET_THREAD_LIST == dbg->req.type); if (0 == len) { write_packet(dbg, "l"); } else { size_t maxlen = 1/*m char*/ + (2 * sizeof(pid_t) + 1/*,*/) * len + 1/*\0*/; char* str = sys_malloc(maxlen); int i, offset = 0; str[offset++] = 'm'; for (i = 0; i < len; ++i) { offset += snprintf(&str[offset], maxlen - offset, "%02x,", threads[i]); } /* Overwrite the trailing ',' */ str[offset - 1] = '\0'; write_packet(dbg, str); sys_free((void**)&str); } consume_request(dbg); }
void dbg_reply_get_is_thread_alive(struct dbg_context* dbg, int alive) { assert(DREQ_GET_IS_THREAD_ALIVE == dbg->req.type); write_packet(dbg, alive ? "OK" : "E01"); consume_request(dbg); }
void dbg_reply_watchpoint_request(struct dbg_context* dbg, int code) { assert(DREQ_WATCH_FIRST <= dbg->req.type && dbg->req.type <= DREQ_WATCH_LAST); write_packet(dbg, code ? "" : "OK"); consume_request(dbg); }
void dbg_reply_get_stop_reason(struct dbg_context* dbg, dbg_threadid_t which, int sig) { assert(DREQ_GET_STOP_REASON == dbg->req.type); send_stop_reply_packet(dbg, which, sig); consume_request(dbg); }
void dbg_reply_get_offsets(struct dbg_context* dbg/*, TODO */) { assert(DREQ_GET_OFFSETS == dbg->req.type); /* XXX FIXME TODO */ write_packet(dbg, ""); consume_request(dbg); }
void dbg_notify_stop(struct dbg_context* dbg, dbg_threadid_t thread, int sig) { assert(dbg_is_resume_request(&dbg->req) || dbg->req.type == DREQ_INTERRUPT); send_stop_reply_packet(dbg, thread, sig); consume_request(dbg); }
void dbg_reply_get_current_thread(struct dbg_context* dbg, dbg_threadid_t thread) { assert(DREQ_GET_CURRENT_THREAD == dbg->req.type); /* TODO multiprocess */ write_hex_packet(dbg, thread); consume_request(dbg); }
void dbg_reply_get_reg(struct dbg_context* dbg, dbg_regvalue_t value) { char buf[32]; assert(DREQ_GET_REG == dbg->req.type); print_reg(value, buf); write_packet(dbg, buf); consume_request(dbg); }
void dbg_notify_exit_signal(struct dbg_context* dbg, int sig) { char buf[64]; assert(dbg_is_resume_request(&dbg->req) || dbg->req.type == DREQ_INTERRUPT); snprintf(buf, sizeof(buf) - 1, "X%02x", sig); write_packet(dbg, buf); consume_request(dbg); }
void dbg_reply_get_regs(struct dbg_context* dbg, const struct dbg_regfile* file) { /* XXX this will be wrong on x64 WINNT */ char buf[1 + DREG_NUM_LINUX_I386 * 2 * sizeof(long)]; int i; assert(DREQ_GET_REGS == dbg->req.type); for (i = 0; i < DREG_NUM_LINUX_I386; ++i) { print_reg(file->regs[i], &buf[i * 2 * sizeof(long)]); } write_packet(dbg, buf); consume_request(dbg); }
void dbg_reply_get_mem(struct dbg_context* dbg, const byte* mem, size_t len) { char* buf; assert(DREQ_GET_MEM == dbg->req.type); assert(len <= dbg->req.mem.len); if (len > 0) { size_t i; buf = sys_malloc(2 * len + 1); for (i = 0; i < len; ++i) { unsigned long b = mem[i]; snprintf(&buf[2 * i], 3, "%02lx", b); } write_packet(dbg, buf); sys_free((void**)&buf); } else { write_packet(dbg, ""); } consume_request(dbg); }
static int process_packet(struct dbg_context* dbg) { char request; char* payload = NULL; int ret; assert(INTERRUPT_CHAR == dbg->inbuf[0] || ('$' == dbg->inbuf[0] && (((byte*)memchr(dbg->inbuf, '#', dbg->inlen) - dbg->inbuf) == dbg->packetend))); if (INTERRUPT_CHAR == dbg->inbuf[0]) { request = INTERRUPT_CHAR; } else { request = dbg->inbuf[1]; payload = (char*)&dbg->inbuf[2]; dbg->inbuf[dbg->packetend] = '\0'; } debug("raw request %c(%s)", request, payload); switch(request) { case INTERRUPT_CHAR: debug("gdb requests interrupt"); dbg->req.type = DREQ_INTERRUPT; ret = 1; break; case 'D': log_info("gdb is detaching from us, exiting"); write_packet(dbg, "OK"); exit(0); case 'g': dbg->req.type = DREQ_GET_REGS; dbg->req.target = dbg->query_thread; debug("gdb requests registers"); ret = 1; break; case 'G': /* XXX we can't let gdb spray registers in general, * because it may cause replay to diverge. But some * writes may be OK. Let's see how far we can get * with ignoring these requests. */ write_packet(dbg, ""); ret = 0; break; case 'H': ret = set_selected_thread(dbg, payload); break; case 'k': log_info("gdb requests kill, exiting"); write_packet(dbg, "OK"); exit(0); case 'm': dbg->req.type = DREQ_GET_MEM; dbg->req.target = dbg->query_thread; dbg->req.mem.addr = (void*)strtoul(payload, &payload, 16); ++payload; dbg->req.mem.len = strtoul(payload, &payload, 16); assert('\0' == *payload); debug("gdb requests memory (addr=0x%p, len=%u)", dbg->req.mem.addr, dbg->req.mem.len); ret = 1; break; case 'M': /* We can't allow the debugger to write arbitrary data * to memory, or the replay may diverge. */ write_packet(dbg, ""); ret = 0; break; case 'p': dbg->req.type = DREQ_GET_REG; dbg->req.target = dbg->query_thread; dbg->req.reg = strtoul(payload, &payload, 16); assert('\0' == *payload); debug("gdb requests register value (%d)", dbg->req.reg); ret = 1; break; case 'P': /* XXX we can't let gdb spray registers in general, * because it may cause replay to diverge. But some * writes may be OK. Let's see how far we can get * with ignoring these requests. */ write_packet(dbg, ""); ret = 0; break; case 'q': ret = query(dbg, payload); break; case 'Q': ret = set(dbg, payload); break; case 'T': dbg->req.type = DREQ_GET_IS_THREAD_ALIVE; dbg->req.target = parse_threadid(payload, &payload); assert('\0' == *payload); debug("gdb wants to know if %d is alive", dbg->req.target); ret = 1; break; case 'v': ret = process_vpacket(dbg, payload); break; case 'X': /* We can't allow the debugger to write arbitrary data * to memory, or the replay may diverge. */ write_packet(dbg, ""); ret = 0; break; case 'z': case 'Z': { int type = strtol(payload, &payload, 16); assert(',' == *payload++); if (!(0 <= type && type <= 4)) { log_warn("Unknown watch type %d", type); write_packet(dbg, ""); ret = 0; break; } dbg->req.type = type + (request == 'Z' ? DREQ_SET_SW_BREAK : DREQ_REMOVE_SW_BREAK); dbg->req.mem.addr = (void*)strtoul(payload, &payload, 16); assert(',' == *payload++); dbg->req.mem.len = strtoul(payload, &payload, 16); assert('\0' == *payload); debug("gdb requests %s breakpoint (addr=%p, len=%u)", 'Z' == request ? "set" : "remove", dbg->req.mem.addr, dbg->req.mem.len); ret = 1; break; } case '?': debug("gdb requests stop reason"); dbg->req.type = DREQ_GET_STOP_REASON; dbg->req.target = dbg->query_thread; ret = 1; break; default: log_warn("Unhandled gdb request '%c'", dbg->inbuf[1]); /* Play dumb and hope gdb doesn't /really/ need this * request ... */ write_packet(dbg, ""); ret = 0; } /* Erase the newly processed packet from the input buffer. */ memmove(dbg->inbuf, dbg->inbuf + dbg->packetend, dbg->inlen - dbg->packetend); dbg->inlen = (dbg->inlen - dbg->packetend); /* If we processed the request internally, consume it. */ if (ret == 0) { consume_request(dbg); } return ret; }
static void fifo_server(FILE *fifo_stream) { char buf[MAX_FIFO_COMMAND]; int line_len; char *file_sep, *command, *file; struct fifo_command *f; file_sep=command=file=0; while(1) { /* commands must look this way ':<command>:[filename]' */ if (!read_line(buf, MAX_FIFO_COMMAND, fifo_stream, &line_len)) { /* line breaking must have failed -- consume the rest and proceed to a new request */ LOG(L_ERR, "ERROR: fifo_server: command expected\n"); goto consume; } if (line_len==0) { LOG(L_DBG, "INFO: fifo_server: command empty\n"); continue; } if (line_len<3) { LOG(L_ERR, "ERROR: fifo_server: command must have at least 3 chars\n"); goto consume; } if (*buf!=CMD_SEPARATOR) { LOG(L_ERR, "ERROR: fifo_server: command must begin with %c: %.*s\n", CMD_SEPARATOR, line_len, buf ); goto consume; } command=buf+1; file_sep=strchr(command, CMD_SEPARATOR ); if (file_sep==NULL) { LOG(L_ERR, "ERROR: fifo_server: file separator missing\n"); goto consume; } if (file_sep==command) { LOG(L_ERR, "ERROR: fifo_server: empty command\n"); goto consume; } if (*(file_sep+1)==0) file=NULL; else { file=file_sep+1; file=trim_filename(file); if (file==0) { LOG(L_ERR, "ERROR: fifo_server: trimming filename\n"); goto consume; } } /* make command zero-terminated */ *file_sep=0; f=lookup_fifo_cmd( command ); if (f==0) { LOG(L_ERR, "ERROR: fifo_server: command %s is not available\n", command); fifo_reply(file, "500 command '%s' not available\n", command); goto consume; } if (f->f(fifo_stream, file)<0) { LOG(L_ERR, "ERROR: fifo_server: command (%s) " "processing failed\n", command ); goto consume; } consume: if (file) { pkg_free(file); file=0;} consume_request(fifo_stream); } }