e282010b2e
Currently the GDB's generate-core-file command doesn't work well with qemu-user: the resulting dumps are huge [1] and at the same time incomplete (argv and envp are missing). The reason is that GDB has no access to proc mappings and therefore has to fall back to using heuristics for discovering them. This is, in turn, because qemu-user does not implement the Host I/O feature of the GDB Remote Serial Protocol. Implement vFile:{open,close,pread,readlink} and also qXfer:exec-file:read+. With that, generate-core-file begins to work on aarch64 and s390x. [1] https://sourceware.org/pipermail/gdb-patches/2023-May/199432.html Co-developed-by: Dominik 'Disconnect3d' Czarnota <dominik.b.czarnota@gmail.com> Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com> Message-Id: <20230621203627.1808446-7-iii@linux.ibm.com> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20230630180423.558337-37-alex.bennee@linaro.org>
238 lines
6.4 KiB
C
238 lines
6.4 KiB
C
/*
|
|
* gdbstub internals
|
|
*
|
|
* Copyright (c) 2022 Linaro Ltd
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#ifndef GDBSTUB_INTERNALS_H
|
|
#define GDBSTUB_INTERNALS_H
|
|
|
|
#include "exec/cpu-common.h"
|
|
|
|
#define MAX_PACKET_LENGTH 4096
|
|
|
|
/*
|
|
* Shared structures and definitions
|
|
*/
|
|
|
|
enum {
|
|
GDB_SIGNAL_0 = 0,
|
|
GDB_SIGNAL_INT = 2,
|
|
GDB_SIGNAL_QUIT = 3,
|
|
GDB_SIGNAL_TRAP = 5,
|
|
GDB_SIGNAL_ABRT = 6,
|
|
GDB_SIGNAL_ALRM = 14,
|
|
GDB_SIGNAL_IO = 23,
|
|
GDB_SIGNAL_XCPU = 24,
|
|
GDB_SIGNAL_UNKNOWN = 143
|
|
};
|
|
|
|
typedef struct GDBProcess {
|
|
uint32_t pid;
|
|
bool attached;
|
|
|
|
char target_xml[1024];
|
|
} GDBProcess;
|
|
|
|
enum RSState {
|
|
RS_INACTIVE,
|
|
RS_IDLE,
|
|
RS_GETLINE,
|
|
RS_GETLINE_ESC,
|
|
RS_GETLINE_RLE,
|
|
RS_CHKSUM1,
|
|
RS_CHKSUM2,
|
|
};
|
|
|
|
typedef struct GDBState {
|
|
bool init; /* have we been initialised? */
|
|
CPUState *c_cpu; /* current CPU for step/continue ops */
|
|
CPUState *g_cpu; /* current CPU for other ops */
|
|
CPUState *query_cpu; /* for q{f|s}ThreadInfo */
|
|
enum RSState state; /* parsing state */
|
|
char line_buf[MAX_PACKET_LENGTH];
|
|
int line_buf_index;
|
|
int line_sum; /* running checksum */
|
|
int line_csum; /* checksum at the end of the packet */
|
|
GByteArray *last_packet;
|
|
int signal;
|
|
bool multiprocess;
|
|
GDBProcess *processes;
|
|
int process_num;
|
|
GString *str_buf;
|
|
GByteArray *mem_buf;
|
|
int sstep_flags;
|
|
int supported_sstep_flags;
|
|
/*
|
|
* Whether we are allowed to send a stop reply packet at this moment.
|
|
* Must be set off after sending the stop reply itself.
|
|
*/
|
|
bool allow_stop_reply;
|
|
} GDBState;
|
|
|
|
/* lives in main gdbstub.c */
|
|
extern GDBState gdbserver_state;
|
|
|
|
/*
|
|
* Inline utility function, convert from int to hex and back
|
|
*/
|
|
|
|
static inline int fromhex(int v)
|
|
{
|
|
if (v >= '0' && v <= '9') {
|
|
return v - '0';
|
|
} else if (v >= 'A' && v <= 'F') {
|
|
return v - 'A' + 10;
|
|
} else if (v >= 'a' && v <= 'f') {
|
|
return v - 'a' + 10;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static inline int tohex(int v)
|
|
{
|
|
if (v < 10) {
|
|
return v + '0';
|
|
} else {
|
|
return v - 10 + 'a';
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Connection helpers for both softmmu and user backends
|
|
*/
|
|
|
|
void gdb_put_strbuf(void);
|
|
int gdb_put_packet(const char *buf);
|
|
int gdb_put_packet_binary(const char *buf, int len, bool dump);
|
|
void gdb_hextomem(GByteArray *mem, const char *buf, int len);
|
|
void gdb_memtohex(GString *buf, const uint8_t *mem, int len);
|
|
void gdb_memtox(GString *buf, const char *mem, int len);
|
|
void gdb_read_byte(uint8_t ch);
|
|
|
|
/*
|
|
* Packet acknowledgement - we handle this slightly differently
|
|
* between user and softmmu mode, mainly to deal with the differences
|
|
* between the flexible chardev and the direct fd approaches.
|
|
*
|
|
* We currently don't support a negotiated QStartNoAckMode
|
|
*/
|
|
|
|
/**
|
|
* gdb_got_immediate_ack() - check ok to continue
|
|
*
|
|
* Returns true to continue, false to re-transmit for user only, the
|
|
* softmmu stub always returns true.
|
|
*/
|
|
bool gdb_got_immediate_ack(void);
|
|
/* utility helpers */
|
|
GDBProcess *gdb_get_process(uint32_t pid);
|
|
CPUState *gdb_get_first_cpu_in_process(GDBProcess *process);
|
|
CPUState *gdb_first_attached_cpu(void);
|
|
void gdb_append_thread_id(CPUState *cpu, GString *buf);
|
|
int gdb_get_cpu_index(CPUState *cpu);
|
|
unsigned int gdb_get_max_cpus(void); /* both */
|
|
bool gdb_can_reverse(void); /* softmmu, stub for user */
|
|
|
|
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
|
|
*/
|
|
void gdb_put_buffer(const uint8_t *buf, int len);
|
|
|
|
/*
|
|
* Command handlers - either specialised or softmmu or user only
|
|
*/
|
|
void gdb_init_gdbserver_state(void);
|
|
|
|
typedef enum GDBThreadIdKind {
|
|
GDB_ONE_THREAD = 0,
|
|
GDB_ALL_THREADS, /* One process, all threads */
|
|
GDB_ALL_PROCESSES,
|
|
GDB_READ_THREAD_ERR
|
|
} GDBThreadIdKind;
|
|
|
|
typedef union GdbCmdVariant {
|
|
const char *data;
|
|
uint8_t opcode;
|
|
unsigned long val_ul;
|
|
unsigned long long val_ull;
|
|
struct {
|
|
GDBThreadIdKind kind;
|
|
uint32_t pid;
|
|
uint32_t tid;
|
|
} thread_id;
|
|
} GdbCmdVariant;
|
|
|
|
#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_offsets(GArray *params, void *user_ctx); /* user */
|
|
void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx); /*user */
|
|
void gdb_handle_v_file_open(GArray *params, void *user_ctx); /* user */
|
|
void gdb_handle_v_file_close(GArray *params, void *user_ctx); /* user */
|
|
void gdb_handle_v_file_pread(GArray *params, void *user_ctx); /* user */
|
|
void gdb_handle_v_file_readlink(GArray *params, void *user_ctx); /* user */
|
|
void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx); /* user */
|
|
|
|
void gdb_handle_query_attached(GArray *params, void *user_ctx); /* both */
|
|
|
|
/* softmmu only */
|
|
void gdb_handle_query_qemu_phy_mem_mode(GArray *params, void *user_ctx);
|
|
void gdb_handle_set_qemu_phy_mem_mode(GArray *params, void *user_ctx);
|
|
|
|
/* sycall handling */
|
|
void gdb_handle_file_io(GArray *params, void *user_ctx);
|
|
bool gdb_handled_syscall(void);
|
|
void gdb_disable_syscalls(void);
|
|
void gdb_syscall_reset(void);
|
|
|
|
/* user/softmmu specific syscall handling */
|
|
void gdb_syscall_handling(const char *syscall_packet);
|
|
|
|
/*
|
|
* Break/Watch point support - there is an implementation for softmmu
|
|
* and user mode.
|
|
*/
|
|
bool gdb_supports_guest_debug(void);
|
|
int gdb_breakpoint_insert(CPUState *cs, int type, vaddr addr, vaddr len);
|
|
int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len);
|
|
void gdb_breakpoint_remove_all(CPUState *cs);
|
|
|
|
/**
|
|
* gdb_target_memory_rw_debug() - handle debug access to memory
|
|
* @cs: CPUState
|
|
* @addr: nominal address, could be an entire physical address
|
|
* @buf: data
|
|
* @len: length of access
|
|
* @is_write: is it a write operation
|
|
*
|
|
* This function is specialised depending on the mode we are running
|
|
* in. For softmmu guests we can switch the interpretation of the
|
|
* address to a physical address.
|
|
*/
|
|
int gdb_target_memory_rw_debug(CPUState *cs, hwaddr addr,
|
|
uint8_t *buf, int len, bool is_write);
|
|
|
|
#endif /* GDBSTUB_INTERNALS_H */
|