/* * Target specific user-mode handling * * Copyright (c) 2003-2005 Fabrice Bellard * Copyright (c) 2022 Linaro Ltd * * SPDX-License-Identifier: LGPL-2.0-or-later */ #include "qemu/osdep.h" #include "exec/gdbstub.h" #include "gdbstub/commands.h" #include "qemu.h" #include "internals.h" #ifdef CONFIG_LINUX #include "linux-user/loader.h" #include "linux-user/qemu.h" #endif /* * Map target signal numbers to GDB protocol signal numbers and vice * versa. For user emulation's currently supported systems, we can * assume most signals are defined. */ static int gdb_signal_table[] = { 0, TARGET_SIGHUP, TARGET_SIGINT, TARGET_SIGQUIT, TARGET_SIGILL, TARGET_SIGTRAP, TARGET_SIGABRT, -1, /* SIGEMT */ TARGET_SIGFPE, TARGET_SIGKILL, TARGET_SIGBUS, TARGET_SIGSEGV, TARGET_SIGSYS, TARGET_SIGPIPE, TARGET_SIGALRM, TARGET_SIGTERM, TARGET_SIGURG, TARGET_SIGSTOP, TARGET_SIGTSTP, TARGET_SIGCONT, TARGET_SIGCHLD, TARGET_SIGTTIN, TARGET_SIGTTOU, TARGET_SIGIO, TARGET_SIGXCPU, TARGET_SIGXFSZ, TARGET_SIGVTALRM, TARGET_SIGPROF, TARGET_SIGWINCH, -1, /* SIGLOST */ TARGET_SIGUSR1, TARGET_SIGUSR2, #ifdef TARGET_SIGPWR TARGET_SIGPWR, #else -1, #endif -1, /* SIGPOLL */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, #ifdef __SIGRTMIN __SIGRTMIN + 1, __SIGRTMIN + 2, __SIGRTMIN + 3, __SIGRTMIN + 4, __SIGRTMIN + 5, __SIGRTMIN + 6, __SIGRTMIN + 7, __SIGRTMIN + 8, __SIGRTMIN + 9, __SIGRTMIN + 10, __SIGRTMIN + 11, __SIGRTMIN + 12, __SIGRTMIN + 13, __SIGRTMIN + 14, __SIGRTMIN + 15, __SIGRTMIN + 16, __SIGRTMIN + 17, __SIGRTMIN + 18, __SIGRTMIN + 19, __SIGRTMIN + 20, __SIGRTMIN + 21, __SIGRTMIN + 22, __SIGRTMIN + 23, __SIGRTMIN + 24, __SIGRTMIN + 25, __SIGRTMIN + 26, __SIGRTMIN + 27, __SIGRTMIN + 28, __SIGRTMIN + 29, __SIGRTMIN + 30, __SIGRTMIN + 31, -1, /* SIGCANCEL */ __SIGRTMIN, __SIGRTMIN + 32, __SIGRTMIN + 33, __SIGRTMIN + 34, __SIGRTMIN + 35, __SIGRTMIN + 36, __SIGRTMIN + 37, __SIGRTMIN + 38, __SIGRTMIN + 39, __SIGRTMIN + 40, __SIGRTMIN + 41, __SIGRTMIN + 42, __SIGRTMIN + 43, __SIGRTMIN + 44, __SIGRTMIN + 45, __SIGRTMIN + 46, __SIGRTMIN + 47, __SIGRTMIN + 48, __SIGRTMIN + 49, __SIGRTMIN + 50, __SIGRTMIN + 51, __SIGRTMIN + 52, __SIGRTMIN + 53, __SIGRTMIN + 54, __SIGRTMIN + 55, __SIGRTMIN + 56, __SIGRTMIN + 57, __SIGRTMIN + 58, __SIGRTMIN + 59, __SIGRTMIN + 60, __SIGRTMIN + 61, __SIGRTMIN + 62, __SIGRTMIN + 63, __SIGRTMIN + 64, __SIGRTMIN + 65, __SIGRTMIN + 66, __SIGRTMIN + 67, __SIGRTMIN + 68, __SIGRTMIN + 69, __SIGRTMIN + 70, __SIGRTMIN + 71, __SIGRTMIN + 72, __SIGRTMIN + 73, __SIGRTMIN + 74, __SIGRTMIN + 75, __SIGRTMIN + 76, __SIGRTMIN + 77, __SIGRTMIN + 78, __SIGRTMIN + 79, __SIGRTMIN + 80, __SIGRTMIN + 81, __SIGRTMIN + 82, __SIGRTMIN + 83, __SIGRTMIN + 84, __SIGRTMIN + 85, __SIGRTMIN + 86, __SIGRTMIN + 87, __SIGRTMIN + 88, __SIGRTMIN + 89, __SIGRTMIN + 90, __SIGRTMIN + 91, __SIGRTMIN + 92, __SIGRTMIN + 93, __SIGRTMIN + 94, __SIGRTMIN + 95, -1, /* SIGINFO */ -1, /* UNKNOWN */ -1, /* DEFAULT */ -1, -1, -1, -1, -1, -1 #endif }; int gdb_signal_to_target(int sig) { if (sig < ARRAY_SIZE(gdb_signal_table)) { return gdb_signal_table[sig]; } else { return -1; } } int gdb_target_signal_to_gdb(int sig) { int i; for (i = 0; i < ARRAY_SIZE(gdb_signal_table); i++) { if (gdb_signal_table[i] == sig) { return i; } } return GDB_SIGNAL_UNKNOWN; } int gdb_get_cpu_index(CPUState *cpu) { TaskState *ts = get_task_state(cpu); return ts ? ts->ts_tid : -1; } /* * User-mode specific command helpers */ void gdb_handle_query_offsets(GArray *params, void *user_ctx) { TaskState *ts; ts = get_task_state(gdbserver_state.c_cpu); g_string_printf(gdbserver_state.str_buf, "Text=" TARGET_ABI_FMT_lx ";Data=" TARGET_ABI_FMT_lx ";Bss=" TARGET_ABI_FMT_lx, ts->info->code_offset, ts->info->data_offset, ts->info->data_offset); gdb_put_strbuf(); } #if defined(CONFIG_LINUX) /* Partial user only duplicate of helper in gdbstub.c */ static inline int target_memory_rw_debug(CPUState *cpu, target_ulong 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); } void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx) { TaskState *ts; unsigned long offset, len, saved_auxv, auxv_len; if (params->len < 2) { gdb_put_packet("E22"); return; } offset = gdb_get_cmd_param(params, 0)->val_ul; len = gdb_get_cmd_param(params, 1)->val_ul; ts = get_task_state(gdbserver_state.c_cpu); saved_auxv = ts->info->saved_auxv; auxv_len = ts->info->auxv_len; if (offset >= auxv_len) { gdb_put_packet("E00"); return; } if (len > (MAX_PACKET_LENGTH - 5) / 2) { len = (MAX_PACKET_LENGTH - 5) / 2; } if (len < auxv_len - offset) { g_string_assign(gdbserver_state.str_buf, "m"); } else { g_string_assign(gdbserver_state.str_buf, "l"); len = auxv_len - offset; } g_byte_array_set_size(gdbserver_state.mem_buf, len); if (target_memory_rw_debug(gdbserver_state.g_cpu, saved_auxv + offset, gdbserver_state.mem_buf->data, len, false)) { gdb_put_packet("E14"); return; } gdb_memtox(gdbserver_state.str_buf, (const char *)gdbserver_state.mem_buf->data, len); gdb_put_packet_binary(gdbserver_state.str_buf->str, gdbserver_state.str_buf->len, true); } #endif static const char *get_filename_param(GArray *params, int i) { const char *hex_filename = gdb_get_cmd_param(params, i)->data; gdb_hextomem(gdbserver_state.mem_buf, hex_filename, strlen(hex_filename) / 2); g_byte_array_append(gdbserver_state.mem_buf, (const guint8 *)"", 1); return (const char *)gdbserver_state.mem_buf->data; } static void hostio_reply_with_data(const void *buf, size_t n) { g_string_printf(gdbserver_state.str_buf, "F%zx;", n); gdb_memtox(gdbserver_state.str_buf, buf, n); gdb_put_packet_binary(gdbserver_state.str_buf->str, gdbserver_state.str_buf->len, true); } void gdb_handle_v_file_open(GArray *params, void *user_ctx) { const char *filename = get_filename_param(params, 0); uint64_t flags = gdb_get_cmd_param(params, 1)->val_ull; uint64_t mode = gdb_get_cmd_param(params, 2)->val_ull; #ifdef CONFIG_LINUX int fd = do_guest_openat(cpu_env(gdbserver_state.g_cpu), 0, filename, flags, mode, false); #else int fd = open(filename, flags, mode); #endif if (fd < 0) { g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); } else { g_string_printf(gdbserver_state.str_buf, "F%d", fd); } gdb_put_strbuf(); } void gdb_handle_v_file_close(GArray *params, void *user_ctx) { int fd = gdb_get_cmd_param(params, 0)->val_ul; if (close(fd) == -1) { g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); gdb_put_strbuf(); return; } gdb_put_packet("F00"); } void gdb_handle_v_file_pread(GArray *params, void *user_ctx) { int fd = gdb_get_cmd_param(params, 0)->val_ul; size_t count = gdb_get_cmd_param(params, 1)->val_ull; off_t offset = gdb_get_cmd_param(params, 2)->val_ull; size_t bufsiz = MIN(count, BUFSIZ); g_autofree char *buf = g_try_malloc(bufsiz); if (buf == NULL) { gdb_put_packet("E12"); return; } ssize_t n = pread(fd, buf, bufsiz, offset); if (n < 0) { g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); gdb_put_strbuf(); return; } hostio_reply_with_data(buf, n); } void gdb_handle_v_file_readlink(GArray *params, void *user_ctx) { const char *filename = get_filename_param(params, 0); g_autofree char *buf = g_try_malloc(BUFSIZ); if (buf == NULL) { gdb_put_packet("E12"); return; } #ifdef CONFIG_LINUX ssize_t n = do_guest_readlink(filename, buf, BUFSIZ); #else ssize_t n = readlink(filename, buf, BUFSIZ); #endif if (n < 0) { g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); gdb_put_strbuf(); return; } hostio_reply_with_data(buf, n); } void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx) { uint32_t pid = gdb_get_cmd_param(params, 0)->val_ul; uint32_t offset = gdb_get_cmd_param(params, 1)->val_ul; uint32_t length = gdb_get_cmd_param(params, 2)->val_ul; GDBProcess *process = gdb_get_process(pid); if (!process) { gdb_put_packet("E00"); return; } CPUState *cpu = gdb_get_first_cpu_in_process(process); if (!cpu) { gdb_put_packet("E00"); return; } TaskState *ts = get_task_state(cpu); if (!ts || !ts->bprm || !ts->bprm->filename) { gdb_put_packet("E00"); return; } size_t total_length = strlen(ts->bprm->filename); if (offset > total_length) { gdb_put_packet("E00"); return; } if (offset + length > total_length) { length = total_length - offset; } g_string_printf(gdbserver_state.str_buf, "l%.*s", length, ts->bprm->filename + offset); gdb_put_strbuf(); } int gdb_target_sigtrap(void) { return TARGET_SIGTRAP; }