X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=gdbstub.c;h=5da66f1794173740e5f85071a44d76725e18a89b;hb=2368635d3943294c672a62abd60a233aca708982;hp=71aaa23da33865e9e1bc4ee9f83d1cc3a74a4a43;hpb=462efe9e530e22b1b60aaf01716e1423cd94302c;p=mirror_qemu.git diff --git a/gdbstub.c b/gdbstub.c index 71aaa23da3..5da66f1794 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -16,17 +16,11 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "config.h" -#include "qemu-common.h" +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/cutils.h" +#include "cpu.h" #ifdef CONFIG_USER_ONLY -#include -#include -#include -#include -#include -#include -#include - #include "qemu.h" #else #include "monitor/monitor.h" @@ -40,6 +34,14 @@ #include "cpu.h" #include "qemu/sockets.h" #include "sysemu/kvm.h" +#include "exec/semihost.h" +#include "exec/exec-all.h" + +#ifdef CONFIG_USER_ONLY +#define GDB_ATTACHED "0" +#else +#define GDB_ATTACHED "1" +#endif static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr, uint8_t *buf, int len, bool is_write) @@ -331,7 +333,7 @@ static int get_char(GDBState *s) if (ret < 0) { if (errno == ECONNRESET) s->fd = -1; - if (errno != EINTR && errno != EAGAIN) + if (errno != EINTR) return -1; } else if (ret == 0) { close(s->fd); @@ -351,10 +353,20 @@ static enum { GDB_SYS_DISABLED, } gdb_syscall_mode; -/* If gdb is connected when the first semihosting syscall occurs then use - remote gdb syscalls. Otherwise use native file IO. */ +/* Decide if either remote gdb syscalls or native file IO should be used. */ int use_gdb_syscalls(void) { + SemihostingTarget target = semihosting_get_target(); + if (target == SEMIHOSTING_TARGET_NATIVE) { + /* -semihosting-config target=native */ + return false; + } else if (target == SEMIHOSTING_TARGET_GDB) { + /* -semihosting-config target=gdb */ + return true; + } + + /* -semihosting-config target=auto */ + /* On the first call check if gdb is connected and remember. */ if (gdb_syscall_mode == GDB_SYS_UNKNOWN) { gdb_syscall_mode = (gdbserver_state ? GDB_SYS_ENABLED : GDB_SYS_DISABLED); @@ -382,7 +394,7 @@ static void put_buffer(GDBState *s, const uint8_t *buf, int len) while (len > 0) { ret = send(s->fd, buf, len, 0); if (ret < 0) { - if (errno != EINTR && errno != EAGAIN) + if (errno != EINTR) return; } else { buf += ret; @@ -523,13 +535,20 @@ static const char *get_feature_xml(const char *p, const char **newp, GDBRegisterState *r; CPUState *cpu = first_cpu; - snprintf(target_xml, sizeof(target_xml), - "" - "" - "" - "", - cc->gdb_core_xml_file); - + pstrcat(target_xml, sizeof(target_xml), + "" + "" + ""); + if (cc->gdb_arch_name) { + gchar *arch = cc->gdb_arch_name(cpu); + pstrcat(target_xml, sizeof(target_xml), ""); + pstrcat(target_xml, sizeof(target_xml), arch); + pstrcat(target_xml, sizeof(target_xml), ""); + g_free(arch); + } + pstrcat(target_xml, sizeof(target_xml), "gdb_core_xml_file); + pstrcat(target_xml, sizeof(target_xml), "\"/>"); for (r = cpu->gdb_regs; r; r = r->next) { pstrcat(target_xml, sizeof(target_xml), "xml); @@ -625,11 +644,23 @@ void gdb_register_coprocessor(CPUState *cpu, } #ifndef CONFIG_USER_ONLY -static const int xlat_gdb_type[] = { - [GDB_WATCHPOINT_WRITE] = BP_GDB | BP_MEM_WRITE, - [GDB_WATCHPOINT_READ] = BP_GDB | BP_MEM_READ, - [GDB_WATCHPOINT_ACCESS] = BP_GDB | BP_MEM_ACCESS, -}; +/* Translate GDB watchpoint type to a flags value for cpu_watchpoint_* */ +static inline int xlat_gdb_type(CPUState *cpu, int gdbtype) +{ + static const int xlat[] = { + [GDB_WATCHPOINT_WRITE] = BP_GDB | BP_MEM_WRITE, + [GDB_WATCHPOINT_READ] = BP_GDB | BP_MEM_READ, + [GDB_WATCHPOINT_ACCESS] = BP_GDB | BP_MEM_ACCESS, + }; + + CPUClass *cc = CPU_GET_CLASS(cpu); + int cputype = xlat[gdbtype]; + + if (cc->gdb_stop_before_watchpoint) { + cputype |= BP_STOP_BEFORE_ACCESS; + } + return cputype; +} #endif static int gdb_breakpoint_insert(target_ulong addr, target_ulong len, int type) @@ -656,10 +687,11 @@ static int gdb_breakpoint_insert(target_ulong addr, target_ulong len, int type) case GDB_WATCHPOINT_READ: case GDB_WATCHPOINT_ACCESS: CPU_FOREACH(cpu) { - err = cpu_watchpoint_insert(cpu, addr, len, xlat_gdb_type[type], - NULL); - if (err) + err = cpu_watchpoint_insert(cpu, addr, len, + xlat_gdb_type(cpu, type), NULL); + if (err) { break; + } } return err; #endif @@ -692,7 +724,8 @@ static int gdb_breakpoint_remove(target_ulong addr, target_ulong len, int type) case GDB_WATCHPOINT_READ: case GDB_WATCHPOINT_ACCESS: CPU_FOREACH(cpu) { - err = cpu_watchpoint_remove(cpu, addr, len, xlat_gdb_type[type]); + err = cpu_watchpoint_remove(cpu, addr, len, + xlat_gdb_type(cpu, type)); if (err) break; } @@ -723,12 +756,9 @@ static void gdb_breakpoint_remove_all(void) static void gdb_set_cpu_pc(GDBState *s, target_ulong pc) { CPUState *cpu = s->c_cpu; - CPUClass *cc = CPU_GET_CLASS(cpu); cpu_synchronize_state(cpu); - if (cc->set_pc) { - cc->set_pc(cpu, pc); - } + cpu_set_pc(cpu, pc); } static CPUState *find_cpu(uint32_t thread_id) @@ -744,6 +774,14 @@ static CPUState *find_cpu(uint32_t thread_id) return NULL; } +static int is_query_packet(const char *p, const char *query, char separator) +{ + unsigned int query_len = strlen(query); + + return strncmp(p, query, query_len) == 0 && + (p[query_len] == '\0' || p[query_len] == separator); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -809,7 +847,10 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) action = *p++; signal = 0; if (action == 'C' || action == 'S') { - signal = strtoul(p, (char **)&p, 16); + signal = gdb_signal_to_target(strtoul(p, (char **)&p, 16)); + if (signal == -1) { + signal = 0; + } } else if (action != 'c' && action != 's') { res = 0; break; @@ -846,11 +887,9 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) goto unknown_command; } case 'k': -#ifdef CONFIG_USER_ONLY /* Kill the target */ fprintf(stderr, "\nQEMU: Terminated via GDBstub\n"); exit(0); -#endif case 'D': /* Detach packet */ gdb_breakpoint_remove_all(); @@ -919,6 +958,13 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) if (*p == ',') p++; len = strtoull(p, NULL, 16); + + /* memtohex() doubles the required space */ + if (len > MAX_PACKET_LENGTH / 2) { + put_packet (s, "E22"); + break; + } + if (target_memory_rw_debug(s->g_cpu, addr, mem_buf, len, false) != 0) { put_packet (s, "E14"); } else { @@ -933,6 +979,12 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) len = strtoull(p, (char **)&p, 16); if (*p == ':') p++; + + /* hextomem() reads 2*len bytes */ + if (len > strlen(p) / 2) { + put_packet (s, "E22"); + break; + } hextomem(mem_buf, p, len); if (target_memory_rw_debug(s->g_cpu, addr, mem_buf, len, true) != 0) { @@ -1034,7 +1086,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) SSTEP_NOTIMER); put_packet(s, buf); break; - } else if (strncmp(p,"qemu.sstep",10) == 0) { + } else if (is_query_packet(p, "qemu.sstep", '=')) { /* Display or change the sstep_flags */ p += 10; if (*p != '=') { @@ -1070,7 +1122,8 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) cpu = find_cpu(thread); if (cpu != NULL) { cpu_synchronize_state(cpu); - len = snprintf((char *)mem_buf, sizeof(mem_buf), + /* memtohex() doubles the required space */ + len = snprintf((char *)mem_buf, sizeof(buf) / 2, "CPU#%d [%s]", cpu->cpu_index, cpu->halted ? "halted " : "running"); memtohex(buf, mem_buf, len); @@ -1079,7 +1132,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) break; } #ifdef CONFIG_USER_ONLY - else if (strncmp(p, "Offsets", 7) == 0) { + else if (strcmp(p, "Offsets") == 0) { TaskState *ts = s->c_cpu->opaque; snprintf(buf, sizeof(buf), @@ -1099,15 +1152,15 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet(s, "E01"); break; } - hextomem(mem_buf, p + 5, len); len = len / 2; + hextomem(mem_buf, p + 5, len); mem_buf[len++] = 0; qemu_chr_be_write(s->mon_chr, mem_buf, len); put_packet(s, "OK"); break; } #endif /* !CONFIG_USER_ONLY */ - if (strncmp(p, "Supported", 9) == 0) { + if (is_query_packet(p, "Supported", ':')) { snprintf(buf, sizeof(buf), "PacketSize=%x", MAX_PACKET_LENGTH); cc = CPU_GET_CLASS(first_cpu); if (cc->gdb_core_xml_file != NULL) { @@ -1159,6 +1212,10 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet_binary(s, buf, len + 1); break; } + if (is_query_packet(p, "Attached", ':')) { + put_packet(s, GDB_ATTACHED); + break; + } /* Unrecognised 'q' command. */ goto unknown_command; @@ -1182,7 +1239,6 @@ void gdb_set_stop_cpu(CPUState *cpu) static void gdb_vm_state_change(void *opaque, int running, RunState state) { GDBState *s = gdbserver_state; - CPUArchState *env = s->c_cpu->env_ptr; CPUState *cpu = s->c_cpu; char buf[256]; const char *type; @@ -1217,7 +1273,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) cpu->watchpoint_hit = NULL; goto send_packet; } - tb_flush(env); + tb_flush(cpu); ret = GDB_SIGNAL_TRAP; break; case RUN_STATE_PAUSED: @@ -1245,6 +1301,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) ret = GDB_SIGNAL_UNKNOWN; break; } + gdb_set_stop_cpu(cpu); snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, cpu_index(cpu)); send_packet: @@ -1260,9 +1317,8 @@ send_packet: %x - target_ulong argument printed in hex. %lx - 64-bit argument printed in hex. %s - string pointer (target_ulong) and length (int) pair. */ -void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) +void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va) { - va_list va; char *p; char *p_end; target_ulong addr; @@ -1276,7 +1332,6 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) #ifndef CONFIG_USER_ONLY vm_stop(RUN_STATE_DEBUG); #endif - va_start(va, fmt); p = s->syscall_buf; p_end = &s->syscall_buf[sizeof(s->syscall_buf)]; *(p++) = 'F'; @@ -1310,7 +1365,6 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) } } *p = 0; - va_end(va); #ifdef CONFIG_USER_ONLY put_packet(s, s->syscall_buf); gdb_handlesig(s->c_cpu, 0); @@ -1321,10 +1375,19 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) is still in the running state, which can cause packets to be dropped and state transition 'T' packets to be sent while the syscall is still being processed. */ - cpu_exit(s->c_cpu); + qemu_cpu_kick(s->c_cpu); #endif } +void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + gdb_do_syscallv(cb, fmt, va); + va_end(va); +} + static void gdb_read_byte(GDBState *s, int ch) { int i, csum; @@ -1415,36 +1478,24 @@ void gdb_exit(CPUArchState *env, int code) if (gdbserver_fd < 0 || s->fd < 0) { return; } +#else + if (!s->chr) { + return; + } #endif snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); put_packet(s, buf); #ifndef CONFIG_USER_ONLY - if (s->chr) { - qemu_chr_delete(s->chr); - } + qemu_chr_delete(s->chr); #endif } #ifdef CONFIG_USER_ONLY -int -gdb_queuesig (void) -{ - GDBState *s; - - s = gdbserver_state; - - if (gdbserver_fd < 0 || s->fd < 0) - return 0; - else - return 1; -} - int gdb_handlesig(CPUState *cpu, int sig) { - CPUArchState *env = cpu->env_ptr; GDBState *s; char buf[256]; int n; @@ -1456,7 +1507,7 @@ gdb_handlesig(CPUState *cpu, int sig) /* disable single step if it was enabled */ cpu_single_step(cpu, 0); - tb_flush(env); + tb_flush(cpu); if (sig != 0) { snprintf(buf, sizeof(buf), "S%02x", target_signal_to_gdb(sig)); @@ -1479,9 +1530,13 @@ gdb_handlesig(CPUState *cpu, int sig) for (i = 0; i < n; i++) { gdb_read_byte(s, buf[i]); } - } else if (n == 0 || errno != EAGAIN) { + } else { /* XXX: Connection closed. Should probably wait for another connection before continuing. */ + if (n == 0) { + close(s->fd); + } + s->fd = -1; return sig; } } @@ -1536,8 +1591,6 @@ static void gdb_accept(void) gdb_has_xml = false; gdbserver_state = s; - - fcntl(fd, F_SETFL, O_NONBLOCK); } static int gdbserver_open(int port) @@ -1565,7 +1618,7 @@ static int gdbserver_open(int port) close(fd); return -1; } - ret = listen(fd, 0); + ret = listen(fd, 1); if (ret < 0) { perror("listen"); close(fd); @@ -1585,9 +1638,8 @@ int gdbserver_start(int port) } /* Disable gdb stub for child processes. */ -void gdbserver_fork(CPUArchState *env) +void gdbserver_fork(CPUState *cpu) { - CPUState *cpu = ENV_GET_CPU(env); GDBState *s = gdbserver_state; if (gdbserver_fd < 0 || s->fd < 0) { @@ -1671,6 +1723,7 @@ int gdbserver_start(const char *device) char gdbstub_device_name[128]; CharDriverState *chr = NULL; CharDriverState *mon_chr; + ChardevCommon common = { 0 }; if (!device) return -1; @@ -1690,7 +1743,7 @@ int gdbserver_start(const char *device) sigaction(SIGINT, &act, NULL); } #endif - chr = qemu_chr_new("gdb", device, NULL); + chr = qemu_chr_new_noreplay("gdb", device, NULL); if (!chr) return -1; @@ -1707,7 +1760,7 @@ int gdbserver_start(const char *device) qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL); /* Initialize a monitor terminal for gdb */ - mon_chr = qemu_chr_alloc(); + mon_chr = qemu_chr_alloc(&common, &error_abort); mon_chr->chr_write = gdb_monitor_write; monitor_init(mon_chr, 0); } else {