mirror of https://github.com/xemu-project/xemu.git
gdbstub: move chunks of user code into own files
The process was pretty similar to the softmmu move except we take the time to split stuff between user.c and user-target.c to avoid as much target specific compilation as possible. We also start to make use of our shiny new header scheme so the user-only helpers can be included without the rest of the exec/gsbstub.h cruft. As before we split some functions into user and softmmu versions Reviewed-by: Fabiano Rosas <farosas@suse.de> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20230302190846.2593720-12-alex.bennee@linaro.org> Message-Id: <20230303025805.625589-12-richard.henderson@linaro.org>
This commit is contained in:
parent
b6fa2ec238
commit
d96bf49ba8
|
@ -2743,6 +2743,7 @@ S: Maintained
|
||||||
F: docs/system/gdb.rst
|
F: docs/system/gdb.rst
|
||||||
F: gdbstub/*
|
F: gdbstub/*
|
||||||
F: include/exec/gdbstub.h
|
F: include/exec/gdbstub.h
|
||||||
|
F: include/gdbstub/*
|
||||||
F: gdb-xml/
|
F: gdb-xml/
|
||||||
F: tests/tcg/multiarch/gdbstub/
|
F: tests/tcg/multiarch/gdbstub/
|
||||||
F: scripts/feature_to_c.sh
|
F: scripts/feature_to_c.sh
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#include "trace/control.h"
|
#include "trace/control.h"
|
||||||
#include "crypto/init.h"
|
#include "crypto/init.h"
|
||||||
#include "qemu/guest-random.h"
|
#include "qemu/guest-random.h"
|
||||||
|
#include "gdbstub/user.h"
|
||||||
|
|
||||||
#include "host-os.h"
|
#include "host-os.h"
|
||||||
#include "target_arch_cpu.h"
|
#include "target_arch_cpu.h"
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/log.h"
|
#include "qemu/log.h"
|
||||||
#include "qemu.h"
|
#include "qemu.h"
|
||||||
|
#include "gdbstub/user.h"
|
||||||
#include "signal-common.h"
|
#include "signal-common.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "hw/core/tcg-cpu-ops.h"
|
#include "hw/core/tcg-cpu-ops.h"
|
||||||
|
|
|
@ -30,13 +30,12 @@
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "exec/gdbstub.h"
|
#include "exec/gdbstub.h"
|
||||||
#ifdef CONFIG_USER_ONLY
|
#ifdef CONFIG_USER_ONLY
|
||||||
#include "qemu.h"
|
#include "gdbstub/user.h"
|
||||||
#else
|
#else
|
||||||
#include "hw/cpu/cluster.h"
|
#include "hw/cpu/cluster.h"
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "qemu/sockets.h"
|
|
||||||
#include "sysemu/hw_accel.h"
|
#include "sysemu/hw_accel.h"
|
||||||
#include "sysemu/runstate.h"
|
#include "sysemu/runstate.h"
|
||||||
#include "semihosting/semihost.h"
|
#include "semihosting/semihost.h"
|
||||||
|
@ -80,223 +79,6 @@ static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr,
|
||||||
return cpu_memory_rw_debug(cpu, addr, buf, len, is_write);
|
return cpu_memory_rw_debug(cpu, addr, buf, len, is_write);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Return the GDB index for a given vCPU state.
|
|
||||||
*
|
|
||||||
* For user mode this is simply the thread id.
|
|
||||||
*/
|
|
||||||
#if defined(CONFIG_USER_ONLY)
|
|
||||||
int gdb_get_cpu_index(CPUState *cpu)
|
|
||||||
{
|
|
||||||
TaskState *ts = (TaskState *) cpu->opaque;
|
|
||||||
return ts ? ts->ts_tid : -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_USER_ONLY
|
|
||||||
|
|
||||||
/* 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
|
|
||||||
};
|
|
||||||
#else
|
|
||||||
/* In system mode we only need SIGINT and SIGTRAP; other signals
|
|
||||||
are not yet supported. */
|
|
||||||
|
|
||||||
enum {
|
|
||||||
TARGET_SIGINT = 2,
|
|
||||||
TARGET_SIGTRAP = 5
|
|
||||||
};
|
|
||||||
|
|
||||||
static int gdb_signal_table[] = {
|
|
||||||
-1,
|
|
||||||
-1,
|
|
||||||
TARGET_SIGINT,
|
|
||||||
-1,
|
|
||||||
-1,
|
|
||||||
TARGET_SIGTRAP
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_USER_ONLY
|
|
||||||
static int 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;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int gdb_signal_to_target (int sig)
|
|
||||||
{
|
|
||||||
if (sig < ARRAY_SIZE (gdb_signal_table))
|
|
||||||
return gdb_signal_table[sig];
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct GDBRegisterState {
|
typedef struct GDBRegisterState {
|
||||||
int base_reg;
|
int base_reg;
|
||||||
int num_regs;
|
int num_regs;
|
||||||
|
@ -306,15 +88,6 @@ typedef struct GDBRegisterState {
|
||||||
struct GDBRegisterState *next;
|
struct GDBRegisterState *next;
|
||||||
} GDBRegisterState;
|
} GDBRegisterState;
|
||||||
|
|
||||||
#ifdef CONFIG_USER_ONLY
|
|
||||||
typedef struct {
|
|
||||||
int fd;
|
|
||||||
char *socket_path;
|
|
||||||
int running_state;
|
|
||||||
} GDBUserState;
|
|
||||||
static GDBUserState gdbserver_user_state;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
GDBState gdbserver_state;
|
GDBState gdbserver_state;
|
||||||
|
|
||||||
void gdb_init_gdbserver_state(void)
|
void gdb_init_gdbserver_state(void)
|
||||||
|
@ -338,34 +111,6 @@ void gdb_init_gdbserver_state(void)
|
||||||
|
|
||||||
bool gdb_has_xml;
|
bool gdb_has_xml;
|
||||||
|
|
||||||
#ifdef CONFIG_USER_ONLY
|
|
||||||
|
|
||||||
static int 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;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return true if there is a GDB currently connected to the stub
|
* Return true if there is a GDB currently connected to the stub
|
||||||
* and attached to a CPU
|
* and attached to a CPU
|
||||||
|
@ -410,104 +155,6 @@ static bool stub_can_reverse(void)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Resume execution. */
|
|
||||||
static void gdb_continue(void)
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef CONFIG_USER_ONLY
|
|
||||||
gdbserver_user_state.running_state = 1;
|
|
||||||
trace_gdbstub_op_continue();
|
|
||||||
#else
|
|
||||||
if (!runstate_needs_reset()) {
|
|
||||||
trace_gdbstub_op_continue();
|
|
||||||
vm_start();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Resume execution, per CPU actions. For user-mode emulation it's
|
|
||||||
* equivalent to gdb_continue.
|
|
||||||
*/
|
|
||||||
static int gdb_continue_partial(char *newstates)
|
|
||||||
{
|
|
||||||
CPUState *cpu;
|
|
||||||
int res = 0;
|
|
||||||
#ifdef CONFIG_USER_ONLY
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
#else
|
|
||||||
int flag = 0;
|
|
||||||
|
|
||||||
if (!runstate_needs_reset()) {
|
|
||||||
bool step_requested = false;
|
|
||||||
CPU_FOREACH(cpu) {
|
|
||||||
if (newstates[cpu->cpu_index] == 's') {
|
|
||||||
step_requested = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vm_prepare_start(step_requested)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
CPU_FOREACH(cpu) {
|
|
||||||
switch (newstates[cpu->cpu_index]) {
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
break; /* nothing to do here */
|
|
||||||
case 's':
|
|
||||||
trace_gdbstub_op_stepping(cpu->cpu_index);
|
|
||||||
cpu_single_step(cpu, gdbserver_state.sstep_flags);
|
|
||||||
cpu_resume(cpu);
|
|
||||||
flag = 1;
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
trace_gdbstub_op_continue_cpu(cpu->cpu_index);
|
|
||||||
cpu_resume(cpu);
|
|
||||||
flag = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
res = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (flag) {
|
|
||||||
qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_USER_ONLY
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* writes 2*len+1 bytes in buf */
|
/* writes 2*len+1 bytes in buf */
|
||||||
void gdb_memtohex(GString *buf, const uint8_t *mem, int len)
|
void gdb_memtohex(GString *buf, const uint8_t *mem, int len)
|
||||||
{
|
{
|
||||||
|
@ -593,7 +240,7 @@ int gdb_put_packet_binary(const char *buf, int len, bool dump)
|
||||||
gdbserver_state.last_packet->len);
|
gdbserver_state.last_packet->len);
|
||||||
|
|
||||||
#ifdef CONFIG_USER_ONLY
|
#ifdef CONFIG_USER_ONLY
|
||||||
i = get_char();
|
i = gdb_get_char();
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
return -1;
|
return -1;
|
||||||
if (i == '+')
|
if (i == '+')
|
||||||
|
@ -1950,23 +1597,6 @@ static void handle_query_thread_extra(GArray *params, void *user_ctx)
|
||||||
gdb_put_strbuf();
|
gdb_put_strbuf();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_USER_ONLY
|
|
||||||
static void handle_query_offsets(GArray *params, void *user_ctx)
|
|
||||||
{
|
|
||||||
TaskState *ts;
|
|
||||||
|
|
||||||
ts = gdbserver_state.c_cpu->opaque;
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void handle_query_supported(GArray *params, void *user_ctx)
|
static void handle_query_supported(GArray *params, void *user_ctx)
|
||||||
{
|
{
|
||||||
CPUClass *cc;
|
CPUClass *cc;
|
||||||
|
@ -2049,53 +1679,6 @@ static void handle_query_xfer_features(GArray *params, void *user_ctx)
|
||||||
gdbserver_state.str_buf->len, true);
|
gdbserver_state.str_buf->len, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX_USER)
|
|
||||||
static void 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 = get_param(params, 0)->val_ul;
|
|
||||||
len = get_param(params, 1)->val_ul;
|
|
||||||
ts = gdbserver_state.c_cpu->opaque;
|
|
||||||
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 void handle_query_attached(GArray *params, void *user_ctx)
|
static void handle_query_attached(GArray *params, void *user_ctx)
|
||||||
{
|
{
|
||||||
gdb_put_packet(GDB_ATTACHED);
|
gdb_put_packet(GDB_ATTACHED);
|
||||||
|
@ -2173,7 +1756,7 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = {
|
||||||
},
|
},
|
||||||
#ifdef CONFIG_USER_ONLY
|
#ifdef CONFIG_USER_ONLY
|
||||||
{
|
{
|
||||||
.handler = handle_query_offsets,
|
.handler = gdb_handle_query_offsets,
|
||||||
.cmd = "Offsets",
|
.cmd = "Offsets",
|
||||||
},
|
},
|
||||||
#else
|
#else
|
||||||
|
@ -2203,7 +1786,7 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = {
|
||||||
},
|
},
|
||||||
#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX_USER)
|
#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX_USER)
|
||||||
{
|
{
|
||||||
.handler = handle_query_xfer_auxv,
|
.handler = gdb_handle_query_xfer_auxv,
|
||||||
.cmd = "Xfer:auxv:read::",
|
.cmd = "Xfer:auxv:read::",
|
||||||
.cmd_startswith = 1,
|
.cmd_startswith = 1,
|
||||||
.schema = "l,l0"
|
.schema = "l,l0"
|
||||||
|
@ -2791,29 +2374,6 @@ void gdb_read_byte(uint8_t ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_USER_ONLY
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code);
|
|
||||||
gdb_put_packet(buf);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create the process that will contain all the "orphan" CPUs (that are not
|
* Create the process that will contain all the "orphan" CPUs (that are not
|
||||||
* part of a CPU cluster). Note that if this process contains no CPUs, it won't
|
* part of a CPU cluster). Note that if this process contains no CPUs, it won't
|
||||||
|
@ -2839,233 +2399,3 @@ void gdb_create_default_process(GDBState *s)
|
||||||
process->target_xml[0] = '\0';
|
process->target_xml[0] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_USER_ONLY
|
|
||||||
int
|
|
||||||
gdb_handlesig(CPUState *cpu, int sig)
|
|
||||||
{
|
|
||||||
char buf[256];
|
|
||||||
int n;
|
|
||||||
|
|
||||||
if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
|
|
||||||
return sig;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* disable single step if it was enabled */
|
|
||||||
cpu_single_step(cpu, 0);
|
|
||||||
tb_flush(cpu);
|
|
||||||
|
|
||||||
if (sig != 0) {
|
|
||||||
gdb_set_stop_cpu(cpu);
|
|
||||||
g_string_printf(gdbserver_state.str_buf,
|
|
||||||
"T%02xthread:", target_signal_to_gdb(sig));
|
|
||||||
gdb_append_thread_id(cpu, gdbserver_state.str_buf);
|
|
||||||
g_string_append_c(gdbserver_state.str_buf, ';');
|
|
||||||
gdb_put_strbuf();
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* 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) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), "X%02x", target_signal_to_gdb(sig));
|
|
||||||
gdb_put_packet(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
gdb_has_xml = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Disable gdb stub for child processes. */
|
|
||||||
void gdbserver_fork(CPUState *cpu)
|
|
||||||
{
|
|
||||||
if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
close(gdbserver_user_state.fd);
|
|
||||||
gdbserver_user_state.fd = -1;
|
|
||||||
cpu_breakpoint_remove_all(cpu, BP_GDB);
|
|
||||||
cpu_watchpoint_remove_all(cpu, BP_GDB);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -117,6 +117,22 @@ int gdb_get_cpu_index(CPUState *cpu);
|
||||||
|
|
||||||
void gdb_create_default_process(GDBState *s);
|
void gdb_create_default_process(GDBState *s);
|
||||||
|
|
||||||
|
/* signal mapping, common for softmmu, specialised for user-mode */
|
||||||
|
int gdb_signal_to_target(int sig);
|
||||||
|
int gdb_target_signal_to_gdb(int sig);
|
||||||
|
|
||||||
|
int gdb_get_char(void); /* user only */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gdb_continue() - handle continue in mode specific way.
|
||||||
|
*/
|
||||||
|
void gdb_continue(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gdb_continue_partial() - handle partial continue in mode specific way.
|
||||||
|
*/
|
||||||
|
int gdb_continue_partial(char *newstates);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helpers with separate softmmu and user implementations
|
* Helpers with separate softmmu and user implementations
|
||||||
*/
|
*/
|
||||||
|
@ -149,6 +165,8 @@ typedef union GdbCmdVariant {
|
||||||
#define get_param(p, i) (&g_array_index(p, GdbCmdVariant, i))
|
#define get_param(p, i) (&g_array_index(p, GdbCmdVariant, i))
|
||||||
|
|
||||||
void gdb_handle_query_rcmd(GArray *params, void *user_ctx); /* softmmu */
|
void gdb_handle_query_rcmd(GArray *params, void *user_ctx); /* softmmu */
|
||||||
|
void gdb_handle_query_offsets(GArray *params, void *user_ctx); /* user */
|
||||||
|
void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx); /*user */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Break/Watch point support - there is an implementation for softmmu
|
* Break/Watch point support - there is an implementation for softmmu
|
||||||
|
|
|
@ -7,3 +7,6 @@
|
||||||
specific_ss.add(files('gdbstub.c'))
|
specific_ss.add(files('gdbstub.c'))
|
||||||
softmmu_ss.add(files('softmmu.c'))
|
softmmu_ss.add(files('softmmu.c'))
|
||||||
user_ss.add(files('user.c'))
|
user_ss.add(files('user.c'))
|
||||||
|
|
||||||
|
# The user-target is specialised by the guest
|
||||||
|
specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-target.c'))
|
||||||
|
|
|
@ -434,6 +434,96 @@ void gdb_handle_query_rcmd(GArray *params, void *user_ctx)
|
||||||
gdb_put_packet("OK");
|
gdb_put_packet("OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Execution state helpers
|
||||||
|
*/
|
||||||
|
|
||||||
|
void gdb_continue(void)
|
||||||
|
{
|
||||||
|
if (!runstate_needs_reset()) {
|
||||||
|
trace_gdbstub_op_continue();
|
||||||
|
vm_start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Resume execution, per CPU actions.
|
||||||
|
*/
|
||||||
|
int gdb_continue_partial(char *newstates)
|
||||||
|
{
|
||||||
|
CPUState *cpu;
|
||||||
|
int res = 0;
|
||||||
|
int flag = 0;
|
||||||
|
|
||||||
|
if (!runstate_needs_reset()) {
|
||||||
|
bool step_requested = false;
|
||||||
|
CPU_FOREACH(cpu) {
|
||||||
|
if (newstates[cpu->cpu_index] == 's') {
|
||||||
|
step_requested = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm_prepare_start(step_requested)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CPU_FOREACH(cpu) {
|
||||||
|
switch (newstates[cpu->cpu_index]) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
break; /* nothing to do here */
|
||||||
|
case 's':
|
||||||
|
trace_gdbstub_op_stepping(cpu->cpu_index);
|
||||||
|
cpu_single_step(cpu, gdbserver_state.sstep_flags);
|
||||||
|
cpu_resume(cpu);
|
||||||
|
flag = 1;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
trace_gdbstub_op_continue_cpu(cpu->cpu_index);
|
||||||
|
cpu_resume(cpu);
|
||||||
|
flag = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
res = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flag) {
|
||||||
|
qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Signal Handling - in system mode we only need SIGINT and SIGTRAP; other
|
||||||
|
* signals are not yet supported.
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TARGET_SIGINT = 2,
|
||||||
|
TARGET_SIGTRAP = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
static int gdb_signal_table[] = {
|
||||||
|
-1,
|
||||||
|
-1,
|
||||||
|
TARGET_SIGINT,
|
||||||
|
-1,
|
||||||
|
-1,
|
||||||
|
TARGET_SIGTRAP
|
||||||
|
};
|
||||||
|
|
||||||
|
int gdb_signal_to_target(int sig)
|
||||||
|
{
|
||||||
|
if (sig < ARRAY_SIZE(gdb_signal_table)) {
|
||||||
|
return gdb_signal_table[sig];
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Break/Watch point helpers
|
* Break/Watch point helpers
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,283 @@
|
||||||
|
/*
|
||||||
|
* Target specific user-mode handling
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2005 Fabrice Bellard
|
||||||
|
* Copyright (c) 2022 Linaro Ltd
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "exec/gdbstub.h"
|
||||||
|
#include "qemu.h"
|
||||||
|
#include "internals.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 = (TaskState *) cpu->opaque;
|
||||||
|
return ts ? ts->ts_tid : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* User-mode specific command helpers
|
||||||
|
*/
|
||||||
|
|
||||||
|
void gdb_handle_query_offsets(GArray *params, void *user_ctx)
|
||||||
|
{
|
||||||
|
TaskState *ts;
|
||||||
|
|
||||||
|
ts = gdbserver_state.c_cpu->opaque;
|
||||||
|
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 = get_param(params, 0)->val_ul;
|
||||||
|
len = get_param(params, 1)->val_ul;
|
||||||
|
ts = gdbserver_state.c_cpu->opaque;
|
||||||
|
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
|
344
gdbstub/user.c
344
gdbstub/user.c
|
@ -10,10 +10,354 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/cutils.h"
|
||||||
|
#include "qemu/sockets.h"
|
||||||
|
#include "exec/hwaddr.h"
|
||||||
|
#include "exec/tb-flush.h"
|
||||||
#include "exec/gdbstub.h"
|
#include "exec/gdbstub.h"
|
||||||
|
#include "gdbstub/user.h"
|
||||||
#include "hw/core/cpu.h"
|
#include "hw/core/cpu.h"
|
||||||
|
#include "trace.h"
|
||||||
#include "internals.h"
|
#include "internals.h"
|
||||||
|
|
||||||
|
/* User-mode specific state */
|
||||||
|
typedef struct {
|
||||||
|
int fd;
|
||||||
|
char *socket_path;
|
||||||
|
int running_state;
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code);
|
||||||
|
gdb_put_packet(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
int gdb_handlesig(CPUState *cpu, int sig)
|
||||||
|
{
|
||||||
|
char buf[256];
|
||||||
|
int n;
|
||||||
|
|
||||||
|
if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
|
||||||
|
return sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* disable single step if it was enabled */
|
||||||
|
cpu_single_step(cpu, 0);
|
||||||
|
tb_flush(cpu);
|
||||||
|
|
||||||
|
if (sig != 0) {
|
||||||
|
gdb_set_stop_cpu(cpu);
|
||||||
|
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, ';');
|
||||||
|
gdb_put_strbuf();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* 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) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "X%02x", gdb_target_signal_to_gdb(sig));
|
||||||
|
gdb_put_packet(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
gdb_has_xml = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disable gdb stub for child processes. */
|
||||||
|
void gdbserver_fork(CPUState *cpu)
|
||||||
|
{
|
||||||
|
if (!gdbserver_state.init || gdbserver_user_state.fd < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
close(gdbserver_user_state.fd);
|
||||||
|
gdbserver_user_state.fd = -1;
|
||||||
|
cpu_breakpoint_remove_all(cpu, BP_GDB);
|
||||||
|
/* no cpu_watchpoint_remove_all for user-mode */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Execution state helpers
|
||||||
|
*/
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Break/Watch point helpers
|
||||||
|
*/
|
||||||
|
|
||||||
bool gdb_supports_guest_debug(void)
|
bool gdb_supports_guest_debug(void)
|
||||||
{
|
{
|
||||||
/* user-mode == TCG == supported */
|
/* user-mode == TCG == supported */
|
||||||
|
|
|
@ -103,27 +103,6 @@ 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);
|
void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va);
|
||||||
int use_gdb_syscalls(void);
|
int use_gdb_syscalls(void);
|
||||||
|
|
||||||
#ifdef CONFIG_USER_ONLY
|
|
||||||
/**
|
|
||||||
* gdb_handlesig: yield control to gdb
|
|
||||||
* @cpu: CPU
|
|
||||||
* @sig: if non-zero, the signal number which caused us to stop
|
|
||||||
*
|
|
||||||
* This function yields control to gdb, when a user-mode-only target
|
|
||||||
* needs to stop execution. If @sig is non-zero, then we will send a
|
|
||||||
* stop packet to tell gdb that we have stopped because of this signal.
|
|
||||||
*
|
|
||||||
* This function will block (handling protocol requests from gdb)
|
|
||||||
* until gdb tells us to continue target execution. When it does
|
|
||||||
* return, the return value is a signal to deliver to the target,
|
|
||||||
* or 0 if no signal should be delivered, ie the signal that caused
|
|
||||||
* us to stop should be ignored.
|
|
||||||
*/
|
|
||||||
int gdb_handlesig(CPUState *, int);
|
|
||||||
void gdb_signalled(CPUArchState *, int);
|
|
||||||
void gdbserver_fork(CPUState *);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Get or set a register. Returns the size of the register. */
|
/* Get or set a register. Returns the size of the register. */
|
||||||
typedef int (*gdb_get_reg_cb)(CPUArchState *env, GByteArray *buf, int reg);
|
typedef int (*gdb_get_reg_cb)(CPUArchState *env, GByteArray *buf, int reg);
|
||||||
typedef int (*gdb_set_reg_cb)(CPUArchState *env, uint8_t *buf, int reg);
|
typedef int (*gdb_set_reg_cb)(CPUArchState *env, uint8_t *buf, int reg);
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* gdbstub user-mode only APIs
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022 Linaro Ltd
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GDBSTUB_USER_H
|
||||||
|
#define GDBSTUB_USER_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gdb_handlesig() - yield control to gdb
|
||||||
|
* @cpu: CPU
|
||||||
|
* @sig: if non-zero, the signal number which caused us to stop
|
||||||
|
*
|
||||||
|
* This function yields control to gdb, when a user-mode-only target
|
||||||
|
* needs to stop execution. If @sig is non-zero, then we will send a
|
||||||
|
* stop packet to tell gdb that we have stopped because of this signal.
|
||||||
|
*
|
||||||
|
* This function will block (handling protocol requests from gdb)
|
||||||
|
* until gdb tells us to continue target execution. When it does
|
||||||
|
* return, the return value is a signal to deliver to the target,
|
||||||
|
* or 0 if no signal should be delivered, ie the signal that caused
|
||||||
|
* us to stop should be ignored.
|
||||||
|
*/
|
||||||
|
int gdb_handlesig(CPUState *, int);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gdb_signalled() - inform remote gdb of sig exit
|
||||||
|
* @as: current CPUArchState
|
||||||
|
* @sig: signal number
|
||||||
|
*/
|
||||||
|
void gdb_signalled(CPUArchState *as, int sig);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gdbserver_fork() - disable gdb stub for child processes.
|
||||||
|
* @cs: CPU
|
||||||
|
*/
|
||||||
|
void gdbserver_fork(CPUState *cs);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* GDBSTUB_USER_H */
|
|
@ -40,6 +40,7 @@
|
||||||
#include "qemu/plugin.h"
|
#include "qemu/plugin.h"
|
||||||
#include "exec/exec-all.h"
|
#include "exec/exec-all.h"
|
||||||
#include "exec/gdbstub.h"
|
#include "exec/gdbstub.h"
|
||||||
|
#include "gdbstub/user.h"
|
||||||
#include "tcg/tcg.h"
|
#include "tcg/tcg.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
#include "qemu/envlist.h"
|
#include "qemu/envlist.h"
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/bitops.h"
|
#include "qemu/bitops.h"
|
||||||
#include "exec/gdbstub.h"
|
#include "gdbstub/user.h"
|
||||||
#include "hw/core/tcg-cpu-ops.h"
|
#include "hw/core/tcg-cpu-ops.h"
|
||||||
|
|
||||||
#include <sys/ucontext.h>
|
#include <sys/ucontext.h>
|
||||||
|
|
Loading…
Reference in New Issue