/* * gdbstub user-mode helper routines. * * We know for user-mode we are using TCG so we can call stuff directly. * * Copyright (c) 2003-2005 Fabrice Bellard * Copyright (c) 2022 Linaro Ltd * * SPDX-License-Identifier: LGPL-2.0+ */ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "qemu/cutils.h" #include "qemu/sockets.h" #include "exec/hwaddr.h" #include "exec/tb-flush.h" #include "exec/gdbstub.h" #include "gdbstub/syscalls.h" #include "gdbstub/user.h" #include "gdbstub/enums.h" #include "hw/core/cpu.h" #include "trace.h" #include "internals.h" #define GDB_NR_SYSCALLS 1024 typedef unsigned long GDBSyscallsMask[BITS_TO_LONGS(GDB_NR_SYSCALLS)]; /* * Forked child talks to its parent in order to let GDB enforce the * follow-fork-mode. This happens inside a start_exclusive() section, so that * the other threads, which may be forking too, do not interfere. The * implementation relies on GDB not sending $vCont until it has detached * either from the parent (follow-fork-mode child) or from the child * (follow-fork-mode parent). * * The parent and the child share the GDB socket; at any given time only one * of them is allowed to use it, as is reflected in the respective fork_state. * This is negotiated via the fork_sockets pair as a reaction to $Hg. * * Below is a short summary of the possible state transitions: * * ENABLED : Terminal state. * DISABLED : Terminal state. * ACTIVE : Parent initial state. * INACTIVE : Child initial state. * ACTIVE -> DEACTIVATING: On $Hg. * ACTIVE -> ENABLING : On $D. * ACTIVE -> DISABLING : On $D. * ACTIVE -> DISABLED : On communication error. * DEACTIVATING -> INACTIVE : On gdb_read_byte() return. * DEACTIVATING -> DISABLED : On communication error. * INACTIVE -> ACTIVE : On $Hg in the peer. * INACTIVE -> ENABLE : On $D in the peer. * INACTIVE -> DISABLE : On $D in the peer. * INACTIVE -> DISABLED : On communication error. * ENABLING -> ENABLED : On gdb_read_byte() return. * ENABLING -> DISABLED : On communication error. * DISABLING -> DISABLED : On gdb_read_byte() return. */ enum GDBForkState { /* Fully owning the GDB socket. */ GDB_FORK_ENABLED, /* Working with the GDB socket; the peer is inactive. */ GDB_FORK_ACTIVE, /* Handing off the GDB socket to the peer. */ GDB_FORK_DEACTIVATING, /* The peer is working with the GDB socket. */ GDB_FORK_INACTIVE, /* Asking the peer to close its GDB socket fd. */ GDB_FORK_ENABLING, /* Asking the peer to take over, closing our GDB socket fd. */ GDB_FORK_DISABLING, /* The peer has taken over, our GDB socket fd is closed. */ GDB_FORK_DISABLED, }; enum GDBForkMessage { GDB_FORK_ACTIVATE = 'a', GDB_FORK_ENABLE = 'e', GDB_FORK_DISABLE = 'd', }; /* User-mode specific state */ typedef struct { int fd; char *socket_path; int running_state; /* * Store syscalls mask without memory allocation in order to avoid * implementing synchronization. */ bool catch_all_syscalls; GDBSyscallsMask catch_syscalls_mask; bool fork_events; enum GDBForkState fork_state; int fork_sockets[2]; pid_t fork_peer_pid, fork_peer_tid; uint8_t siginfo[MAX_SIGINFO_LENGTH]; unsigned long siginfo_len; } GDBUserState; static GDBUserState gdbserver_user_state; int gdb_get_char(void) { uint8_t ch; int ret; for (;;) { ret = recv(gdbserver_user_state.fd, &ch, 1, 0); if (ret < 0) { if (errno == ECONNRESET) { gdbserver_user_state.fd = -1; } if (errno != EINTR) { return -1; } } else if (ret == 0) { close(gdbserver_user_state.fd); gdbserver_user_state.fd = -1; return -1; } else { break; } } return ch; } bool gdb_got_immediate_ack(void) { int i; i = gdb_get_char(); if (i < 0) { /* no response, continue anyway */ return true; } if (i == '+') { /* received correctly, continue */ return true; } /* anything else, including '-' then try again */ return false; } void gdb_put_buffer(const uint8_t *buf, int len) { int ret; while (len > 0) { ret = send(gdbserver_user_state.fd, buf, len, 0); if (ret < 0) { if (errno != EINTR) { return; } } else { buf += ret; len -= ret; } } } /* Tell the remote gdb that the process has exited. */ void gdb_exit(int code) { char buf[4]; if (!gdbserver_state.init) { return; } if (gdbserver_user_state.socket_path) { unlink(gdbserver_user_state.socket_path); } if (gdbserver_user_state.fd < 0) { return; } trace_gdbstub_op_exiting((uint8_t)code); if (gdbserver_state.allow_stop_reply) { snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); gdb_put_packet(buf); gdbserver_state.allow_stop_reply = false; } } void gdb_qemu_exit(int code) { exit(code); } int gdb_handlesig(CPUState *cpu, int sig, const char *reason, void *siginfo, int siginfo_len) { char buf[256]; int n; if (!gdbserver_state.init || gdbserver_user_state.fd < 0) { return sig; } if (siginfo) { /* * Save target-specific siginfo. * * siginfo size, i.e. siginfo_len, is asserted at compile-time to fit in * gdbserver_user_state.siginfo, usually in the source file calling * gdb_handlesig. See, for instance, {linux,bsd}-user/signal.c. */ memcpy(gdbserver_user_state.siginfo, siginfo, siginfo_len); gdbserver_user_state.siginfo_len = siginfo_len; } /* disable single step if it was enabled */ cpu_single_step(cpu, 0); tb_flush(cpu); if (sig != 0) { gdb_set_stop_cpu(cpu); if (gdbserver_state.allow_stop_reply) { g_string_printf(gdbserver_state.str_buf, "T%02xthread:", gdb_target_signal_to_gdb(sig)); gdb_append_thread_id(cpu, gdbserver_state.str_buf); g_string_append_c(gdbserver_state.str_buf, ';'); if (reason) { g_string_append(gdbserver_state.str_buf, reason); } gdb_put_strbuf(); gdbserver_state.allow_stop_reply = false; } } /* * gdb_put_packet() might have detected that the peer terminated the * connection. */ if (gdbserver_user_state.fd < 0) { return sig; } sig = 0; gdbserver_state.state = RS_IDLE; gdbserver_user_state.running_state = 0; while (gdbserver_user_state.running_state == 0) { n = read(gdbserver_user_state.fd, buf, 256); if (n > 0) { int i; for (i = 0; i < n; i++) { gdb_read_byte(buf[i]); } } else { /* * XXX: Connection closed. Should probably wait for another * connection before continuing. */ if (n == 0) { close(gdbserver_user_state.fd); } gdbserver_user_state.fd = -1; return sig; } } sig = gdbserver_state.signal; gdbserver_state.signal = 0; return sig; } /* Tell the remote gdb that the process has exited due to SIG. */ void gdb_signalled(CPUArchState *env, int sig) { char buf[4]; if (!gdbserver_state.init || gdbserver_user_state.fd < 0 || !gdbserver_state.allow_stop_reply) { return; } snprintf(buf, sizeof(buf), "X%02x", gdb_target_signal_to_gdb(sig)); gdb_put_packet(buf); gdbserver_state.allow_stop_reply = false; } static void gdb_accept_init(int fd) { gdb_init_gdbserver_state(); gdb_create_default_process(&gdbserver_state); gdbserver_state.processes[0].attached = true; gdbserver_state.c_cpu = gdb_first_attached_cpu(); gdbserver_state.g_cpu = gdbserver_state.c_cpu; gdbserver_user_state.fd = fd; } static bool gdb_accept_socket(int gdb_fd) { int fd; for (;;) { fd = accept(gdb_fd, NULL, NULL); if (fd < 0 && errno != EINTR) { perror("accept socket"); return false; } else if (fd >= 0) { qemu_set_cloexec(fd); break; } } gdb_accept_init(fd); return true; } static int gdbserver_open_socket(const char *path) { struct sockaddr_un sockaddr = {}; int fd, ret; fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { perror("create socket"); return -1; } sockaddr.sun_family = AF_UNIX; pstrcpy(sockaddr.sun_path, sizeof(sockaddr.sun_path) - 1, path); ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); if (ret < 0) { perror("bind socket"); close(fd); return -1; } ret = listen(fd, 1); if (ret < 0) { perror("listen socket"); close(fd); return -1; } return fd; } static bool gdb_accept_tcp(int gdb_fd) { struct sockaddr_in sockaddr = {}; socklen_t len; int fd; for (;;) { len = sizeof(sockaddr); fd = accept(gdb_fd, (struct sockaddr *)&sockaddr, &len); if (fd < 0 && errno != EINTR) { perror("accept"); return false; } else if (fd >= 0) { qemu_set_cloexec(fd); break; } } /* set short latency */ if (socket_set_nodelay(fd)) { perror("setsockopt"); close(fd); return false; } gdb_accept_init(fd); return true; } static int gdbserver_open_port(int port) { struct sockaddr_in sockaddr; int fd, ret; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return -1; } qemu_set_cloexec(fd); socket_set_fast_reuse(fd); sockaddr.sin_family = AF_INET; sockaddr.sin_port = htons(port); sockaddr.sin_addr.s_addr = 0; ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); if (ret < 0) { perror("bind"); close(fd); return -1; } ret = listen(fd, 1); if (ret < 0) { perror("listen"); close(fd); return -1; } return fd; } int gdbserver_start(const char *port_or_path) { int port = g_ascii_strtoull(port_or_path, NULL, 10); int gdb_fd; if (port > 0) { gdb_fd = gdbserver_open_port(port); } else { gdb_fd = gdbserver_open_socket(port_or_path); } if (gdb_fd < 0) { return -1; } if (port > 0 && gdb_accept_tcp(gdb_fd)) { return 0; } else if (gdb_accept_socket(gdb_fd)) { gdbserver_user_state.socket_path = g_strdup(port_or_path); return 0; } /* gone wrong */ close(gdb_fd); return -1; } void gdbserver_fork_start(void) { if (!gdbserver_state.init || gdbserver_user_state.fd < 0) { return; } if (!gdbserver_user_state.fork_events || qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, gdbserver_user_state.fork_sockets) < 0) { gdbserver_user_state.fork_state = GDB_FORK_DISABLED; return; } gdbserver_user_state.fork_state = GDB_FORK_INACTIVE; gdbserver_user_state.fork_peer_pid = getpid(); gdbserver_user_state.fork_peer_tid = qemu_get_thread_id(); } static void disable_gdbstub(CPUState *thread_cpu) { CPUState *cpu; close(gdbserver_user_state.fd); gdbserver_user_state.fd = -1; CPU_FOREACH(cpu) { cpu_breakpoint_remove_all(cpu, BP_GDB); /* no cpu_watchpoint_remove_all for user-mode */ cpu_single_step(cpu, 0); } tb_flush(thread_cpu); } void gdbserver_fork_end(CPUState *cpu, pid_t pid) { char b; int fd; if (!gdbserver_state.init || gdbserver_user_state.fd < 0) { return; } if (pid == -1) { if (gdbserver_user_state.fork_state != GDB_FORK_DISABLED) { g_assert(gdbserver_user_state.fork_state == GDB_FORK_INACTIVE); close(gdbserver_user_state.fork_sockets[0]); close(gdbserver_user_state.fork_sockets[1]); } return; } if (gdbserver_user_state.fork_state == GDB_FORK_DISABLED) { if (pid == 0) { disable_gdbstub(cpu); } return; } if (pid == 0) { close(gdbserver_user_state.fork_sockets[0]); fd = gdbserver_user_state.fork_sockets[1]; g_assert(gdbserver_state.process_num == 1); g_assert(gdbserver_state.processes[0].pid == gdbserver_user_state.fork_peer_pid); g_assert(gdbserver_state.processes[0].attached); gdbserver_state.processes[0].pid = getpid(); } else { close(gdbserver_user_state.fork_sockets[1]); fd = gdbserver_user_state.fork_sockets[0]; gdbserver_user_state.fork_state = GDB_FORK_ACTIVE; gdbserver_user_state.fork_peer_pid = pid; gdbserver_user_state.fork_peer_tid = pid; if (!gdbserver_state.allow_stop_reply) { goto fail; } g_string_printf(gdbserver_state.str_buf, "T%02xfork:p%02x.%02x;thread:p%02x.%02x;", gdb_target_signal_to_gdb(gdb_target_sigtrap()), pid, pid, (int)getpid(), qemu_get_thread_id()); gdb_put_strbuf(); } gdbserver_state.state = RS_IDLE; gdbserver_state.allow_stop_reply = false; gdbserver_user_state.running_state = 0; for (;;) { switch (gdbserver_user_state.fork_state) { case GDB_FORK_ENABLED: if (gdbserver_user_state.running_state) { close(fd); return; } QEMU_FALLTHROUGH; case GDB_FORK_ACTIVE: if (read(gdbserver_user_state.fd, &b, 1) != 1) { goto fail; } gdb_read_byte(b); break; case GDB_FORK_DEACTIVATING: b = GDB_FORK_ACTIVATE; if (write(fd, &b, 1) != 1) { goto fail; } gdbserver_user_state.fork_state = GDB_FORK_INACTIVE; break; case GDB_FORK_INACTIVE: if (read(fd, &b, 1) != 1) { goto fail; } switch (b) { case GDB_FORK_ACTIVATE: gdbserver_user_state.fork_state = GDB_FORK_ACTIVE; break; case GDB_FORK_ENABLE: gdbserver_user_state.fork_state = GDB_FORK_ENABLED; break; case GDB_FORK_DISABLE: gdbserver_user_state.fork_state = GDB_FORK_DISABLED; break; default: g_assert_not_reached(); } break; case GDB_FORK_ENABLING: b = GDB_FORK_DISABLE; if (write(fd, &b, 1) != 1) { goto fail; } gdbserver_user_state.fork_state = GDB_FORK_ENABLED; break; case GDB_FORK_DISABLING: b = GDB_FORK_ENABLE; if (write(fd, &b, 1) != 1) { goto fail; } gdbserver_user_state.fork_state = GDB_FORK_DISABLED; break; case GDB_FORK_DISABLED: close(fd); disable_gdbstub(cpu); return; default: g_assert_not_reached(); } } fail: close(fd); if (pid == 0) { disable_gdbstub(cpu); } } void gdb_handle_query_supported_user(const char *gdb_supported) { if (strstr(gdb_supported, "fork-events+")) { gdbserver_user_state.fork_events = true; } g_string_append(gdbserver_state.str_buf, ";fork-events+"); } bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid) { if (gdbserver_user_state.fork_state == GDB_FORK_ACTIVE && pid == gdbserver_user_state.fork_peer_pid && tid == gdbserver_user_state.fork_peer_tid) { gdbserver_user_state.fork_state = GDB_FORK_DEACTIVATING; gdb_put_packet("OK"); return true; } return false; } bool gdb_handle_detach_user(uint32_t pid) { bool enable; if (gdbserver_user_state.fork_state == GDB_FORK_ACTIVE) { enable = pid == gdbserver_user_state.fork_peer_pid; if (enable || pid == getpid()) { gdbserver_user_state.fork_state = enable ? GDB_FORK_ENABLING : GDB_FORK_DISABLING; gdb_put_packet("OK"); return true; } } return false; } /* * Execution state helpers */ void gdb_handle_query_attached(GArray *params, void *user_ctx) { gdb_put_packet("0"); } void gdb_continue(void) { gdbserver_user_state.running_state = 1; trace_gdbstub_op_continue(); } /* * Resume execution, for user-mode emulation it's equivalent to * gdb_continue. */ int gdb_continue_partial(char *newstates) { CPUState *cpu; int res = 0; /* * This is not exactly accurate, but it's an improvement compared to the * previous situation, where only one CPU would be single-stepped. */ CPU_FOREACH(cpu) { if (newstates[cpu->cpu_index] == 's') { trace_gdbstub_op_stepping(cpu->cpu_index); cpu_single_step(cpu, gdbserver_state.sstep_flags); } } gdbserver_user_state.running_state = 1; return res; } /* * Memory access helpers */ int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr, uint8_t *buf, int len, bool is_write) { CPUClass *cc; cc = CPU_GET_CLASS(cpu); if (cc->memory_rw_debug) { return cc->memory_rw_debug(cpu, addr, buf, len, is_write); } return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); } /* * cpu helpers */ unsigned int gdb_get_max_cpus(void) { CPUState *cpu; unsigned int max_cpus = 1; CPU_FOREACH(cpu) { max_cpus = max_cpus <= cpu->cpu_index ? cpu->cpu_index + 1 : max_cpus; } return max_cpus; } /* replay not supported for user-mode */ bool gdb_can_reverse(void) { return false; } /* * Break/Watch point helpers */ bool gdb_supports_guest_debug(void) { /* user-mode == TCG == supported */ return true; } int gdb_breakpoint_insert(CPUState *cs, int type, vaddr addr, vaddr len) { CPUState *cpu; int err = 0; switch (type) { case GDB_BREAKPOINT_SW: case GDB_BREAKPOINT_HW: CPU_FOREACH(cpu) { err = cpu_breakpoint_insert(cpu, addr, BP_GDB, NULL); if (err) { break; } } return err; default: /* user-mode doesn't support watchpoints */ return -ENOSYS; } } int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len) { CPUState *cpu; int err = 0; switch (type) { case GDB_BREAKPOINT_SW: case GDB_BREAKPOINT_HW: CPU_FOREACH(cpu) { err = cpu_breakpoint_remove(cpu, addr, BP_GDB); if (err) { break; } } return err; default: /* user-mode doesn't support watchpoints */ return -ENOSYS; } } void gdb_breakpoint_remove_all(CPUState *cs) { cpu_breakpoint_remove_all(cs, BP_GDB); } /* * For user-mode syscall support we send the system call immediately * and then return control to gdb for it to process the syscall request. * Since the protocol requires that gdb hands control back to us * using a "here are the results" F packet, we don't need to check * gdb_handlesig's return value (which is the signal to deliver if * execution was resumed via a continue packet). */ void gdb_syscall_handling(const char *syscall_packet) { gdb_put_packet(syscall_packet); gdb_handlesig(gdbserver_state.c_cpu, 0, NULL, NULL, 0); } static bool should_catch_syscall(int num) { if (gdbserver_user_state.catch_all_syscalls) { return true; } if (num < 0 || num >= GDB_NR_SYSCALLS) { return false; } return test_bit(num, gdbserver_user_state.catch_syscalls_mask); } void gdb_syscall_entry(CPUState *cs, int num) { if (should_catch_syscall(num)) { g_autofree char *reason = g_strdup_printf("syscall_entry:%x;", num); gdb_handlesig(cs, gdb_target_sigtrap(), reason, NULL, 0); } } void gdb_syscall_return(CPUState *cs, int num) { if (should_catch_syscall(num)) { g_autofree char *reason = g_strdup_printf("syscall_return:%x;", num); gdb_handlesig(cs, gdb_target_sigtrap(), reason, NULL, 0); } } void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx) { const char *param = get_param(params, 0)->data; GDBSyscallsMask catch_syscalls_mask; bool catch_all_syscalls; unsigned int num; const char *p; /* "0" means not catching any syscalls. */ if (strcmp(param, "0") == 0) { gdbserver_user_state.catch_all_syscalls = false; memset(gdbserver_user_state.catch_syscalls_mask, 0, sizeof(gdbserver_user_state.catch_syscalls_mask)); gdb_put_packet("OK"); return; } /* "1" means catching all syscalls. */ if (strcmp(param, "1") == 0) { gdbserver_user_state.catch_all_syscalls = true; gdb_put_packet("OK"); return; } /* * "1;..." means catching only the specified syscalls. * The syscall list must not be empty. */ if (param[0] == '1' && param[1] == ';') { catch_all_syscalls = false; memset(catch_syscalls_mask, 0, sizeof(catch_syscalls_mask)); for (p = ¶m[2];; p++) { if (qemu_strtoui(p, &p, 16, &num) || (*p && *p != ';')) { goto err; } if (num >= GDB_NR_SYSCALLS) { /* * Fall back to reporting all syscalls. Reporting extra * syscalls is inefficient, but the spec explicitly allows it. * Keep parsing in case there is a syntax error ahead. */ catch_all_syscalls = true; } else { set_bit(num, catch_syscalls_mask); } if (!*p) { break; } } gdbserver_user_state.catch_all_syscalls = catch_all_syscalls; if (!catch_all_syscalls) { memcpy(gdbserver_user_state.catch_syscalls_mask, catch_syscalls_mask, sizeof(catch_syscalls_mask)); } gdb_put_packet("OK"); return; } err: gdb_put_packet("E00"); } void gdb_handle_query_xfer_siginfo(GArray *params, void *user_ctx) { unsigned long offset, len; uint8_t *siginfo_offset; offset = get_param(params, 0)->val_ul; len = get_param(params, 1)->val_ul; if (offset + len > gdbserver_user_state.siginfo_len) { /* Invalid offset and/or requested length. */ gdb_put_packet("E01"); return; } siginfo_offset = (uint8_t *)gdbserver_user_state.siginfo + offset; /* Reply */ g_string_assign(gdbserver_state.str_buf, "l"); gdb_memtox(gdbserver_state.str_buf, (const char *)siginfo_offset, len); gdb_put_packet_binary(gdbserver_state.str_buf->str, gdbserver_state.str_buf->len, true); }