/* * Copyright (c) 2016-2022 Matt Borgerson * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef GDBSTUB_H #define GDBSTUB_H /* Enable debug statements (printf) */ #ifndef DEBUG #define DEBUG 0 #endif /***************************************************************************** * * Mock * ****************************************************************************/ #ifdef GDBSTUB_ARCH_MOCK /***************************************************************************** * Types ****************************************************************************/ typedef unsigned int address; typedef unsigned int reg; enum GDB_REGISTER { GDB_CPU_NUM_REGISTERS = 4 }; struct gdb_state { int signum; reg registers[GDB_CPU_NUM_REGISTERS]; }; /***************************************************************************** * Prototypes ****************************************************************************/ struct gdb_buffer; extern struct gdb_buffer gdb_input, gdb_output; void gdb_buf_write(struct gdb_buffer *buf, int ch); int gdb_buf_read(struct gdb_buffer *buf); #endif /* GDBSTUB_ARCH_MOCK */ /***************************************************************************** * * x86 * ****************************************************************************/ #ifdef GDBSTUB_ARCH_X86 /***************************************************************************** * Types ****************************************************************************/ #ifndef GDBSTUB_DONT_DEFINE_STDINT_TYPES typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned long uint32_t; #endif typedef unsigned int address; typedef unsigned int reg; enum GDB_REGISTER { GDB_CPU_I386_REG_EAX = 0, GDB_CPU_I386_REG_ECX = 1, GDB_CPU_I386_REG_EDX = 2, GDB_CPU_I386_REG_EBX = 3, GDB_CPU_I386_REG_ESP = 4, GDB_CPU_I386_REG_EBP = 5, GDB_CPU_I386_REG_ESI = 6, GDB_CPU_I386_REG_EDI = 7, GDB_CPU_I386_REG_PC = 8, GDB_CPU_I386_REG_PS = 9, GDB_CPU_I386_REG_CS = 10, GDB_CPU_I386_REG_SS = 11, GDB_CPU_I386_REG_DS = 12, GDB_CPU_I386_REG_ES = 13, GDB_CPU_I386_REG_FS = 14, GDB_CPU_I386_REG_GS = 15, GDB_CPU_NUM_REGISTERS = 16 }; struct gdb_state { int signum; reg registers[GDB_CPU_NUM_REGISTERS]; }; #endif /* GDBSTUB_ARCH_X86 */ /***************************************************************************** * * UVM32 * ****************************************************************************/ #ifdef GDBSTUB_ARCH_UVM32 #include "uvm32.h" typedef uint32_t address; typedef uint32_t reg; typedef int32_t (*uvm32_event_callback_t)(uvm32_state_t* vmst, uvm32_evt_t *evt); typedef enum { SIGNONE = 0, SIGILL = 4, SIGTRAP = 5, SIGSEGV = 11, } gdbstub_signal_t; enum GDB_REGISTER { /* 32 registers and PC */ GDB_CPU_NUM_REGISTERS = (32 + 1), }; struct gdb_state { /* Not used, only for compatibility */ uint8_t signum; /* UVM32 state */ uvm32_state_t* vmst; /* User defined callback to handle events */ uvm32_event_callback_t event_cb; /* Will be upated with `vmst->_core' registers */ reg registers[GDB_CPU_NUM_REGISTERS]; }; int32_t gdb_sys_init_state(struct gdb_state* state, uvm32_state_t* vmst, uvm32_event_callback_t event_cb); #endif /* GDBSTUB_ARCH_UVM32 */ /***************************************************************************** * * GDB Remote Serial Protocol * ****************************************************************************/ /***************************************************************************** * Macros ****************************************************************************/ #if DEBUG #include #include #include #define GDB_PRINT(...) fprintf(stderr, __VA_ARGS__) #else #define GDB_PRINT(...) #endif #define GDB_EOF (-1) #ifndef NULL #define NULL ((void*)0) #endif #ifndef GDB_ASSERT #if DEBUG #define GDB_ASSERT(x) { \ if (!(x)) { \ fprintf(stderr, "ASSERTION FAILED\n"); \ fprintf(stderr, " Assertion: %s\n", #x); \ fprintf(stderr, " Location: %s @ %s:%d\n", __func__, \ __FILE__, __LINE__); \ exit(1); \ } \ } #else #define GDB_ASSERT(x) \ do {} while (0) #endif #endif /***************************************************************************** * Prototypes ****************************************************************************/ int gdb_main(struct gdb_state *state); /* System functions, supported by all stubs */ void gdb_sys_init(void); int gdb_sys_getc(struct gdb_state *state); int gdb_sys_putchar(struct gdb_state *state, int ch); int gdb_sys_mem_readb(struct gdb_state *state, address addr, char *val); int gdb_sys_mem_writeb(struct gdb_state *state, address addr, char val); int gdb_sys_continue(struct gdb_state *state); int gdb_sys_step(struct gdb_state *state); #ifdef GDBSTUB_IMPLEMENTATION /***************************************************************************** * Types ****************************************************************************/ typedef int (*gdb_enc_func)(char *buf, unsigned int buf_len, const char *data, unsigned int data_len); typedef int (*gdb_dec_func)(const char *buf, unsigned int buf_len, char *data, unsigned int data_len); /***************************************************************************** * Const Data ****************************************************************************/ static const char digits[] = "0123456789abcdef"; /***************************************************************************** * Prototypes ****************************************************************************/ /* Communication functions */ static int gdb_write(struct gdb_state *state, const char *buf, unsigned int len); static int gdb_read(struct gdb_state *state, char *buf, unsigned int buf_len, unsigned int len); /* String processing helper functions */ static int gdb_strlen(const char *ch); #if DEBUG static int gdb_is_printable_char(char ch); #endif static char gdb_get_digit(int val); static int gdb_get_val(char digit, int base); static int gdb_strtol(const char *str, unsigned int len, int base, const char **endptr); /* Packet functions */ static int gdb_send_packet(struct gdb_state *state, const char *pkt, unsigned int pkt_len); static int gdb_recv_packet(struct gdb_state *state, char *pkt_buf, unsigned int pkt_buf_len, unsigned int *pkt_len); static int gdb_checksum(const char *buf, unsigned int len); static int gdb_recv_ack(struct gdb_state *state); /* Data encoding/decoding */ static int gdb_enc_hex(char *buf, unsigned int buf_len, const char *data, unsigned int data_len); static int gdb_dec_hex(const char *buf, unsigned int buf_len, char *data, unsigned int data_len); static int gdb_enc_bin(char *buf, unsigned int buf_len, const char *data, unsigned int data_len); static int gdb_dec_bin(const char *buf, unsigned int buf_len, char *data, unsigned int data_len); /* Packet creation helpers */ static int gdb_send_ok_packet(struct gdb_state *state, char *buf, unsigned int buf_len); static int gdb_send_conmsg_packet(struct gdb_state *state, char *buf, unsigned int buf_len, const char *msg); static int gdb_send_signal_packet(struct gdb_state *state, char *buf, unsigned int buf_len, char signal); static int gdb_send_error_packet(struct gdb_state *state, char *buf, unsigned int buf_len, char error); /* Command functions */ static int gdb_mem_read(struct gdb_state *state, char *buf, unsigned int buf_len, address addr, unsigned int len, gdb_enc_func enc); static int gdb_mem_write(struct gdb_state *state, const char *buf, unsigned int buf_len, address addr, unsigned int len, gdb_dec_func dec); static int gdb_continue(struct gdb_state *state); static int gdb_step(struct gdb_state *state); /***************************************************************************** * String Processing Helper Functions ****************************************************************************/ /* * Get null-terminated string length. */ static int gdb_strlen(const char *ch) { int len; len = 0; while (*ch++) { len += 1; } return len; } /* * Get integer value for a string representation. * * If the string starts with + or -, it will be signed accordingly. * * If base == 0, the base will be determined: * base 16 if the string starts with 0x or 0X, * base 10 otherwise * * If endptr is specified, it will point to the last non-digit in the * string. If there are no digits in the string, it will be set to NULL. */ static int gdb_strtol(const char *str, unsigned int len, int base, const char **endptr) { unsigned int pos; int sign, tmp, value, valid; value = 0; pos = 0; sign = 1; valid = 0; if (endptr) { *endptr = NULL; } if (len < 1) { return 0; } /* Detect negative numbers */ if (str[pos] == '-') { sign = -1; pos += 1; } else if (str[pos] == '+') { sign = 1; pos += 1; } /* Detect '0x' hex prefix */ if ((pos + 2 < len) && (str[pos] == '0') && ((str[pos+1] == 'x') || (str[pos+1] == 'X'))) { base = 16; pos += 2; } if (base == 0) { base = 10; } for (; (pos < len) && (str[pos] != '\x00'); pos++) { tmp = gdb_get_val(str[pos], base); if (tmp == GDB_EOF) { break; } value = value*base + tmp; valid = 1; /* At least one digit is valid */ } if (!valid) { return 0; } if (endptr) { *endptr = str+pos; } value *= sign; return value; } /* * Get the corresponding ASCII hex digit character for a value. */ static char gdb_get_digit(int val) { if ((val >= 0) && (val <= 0xf)) { return digits[val]; } else { return GDB_EOF; } } /* * Get the corresponding value for a ASCII digit character. * * Supports bases 2-16. */ static int gdb_get_val(char digit, int base) { int value; if ((digit >= '0') && (digit <= '9')) { value = digit-'0'; } else if ((digit >= 'a') && (digit <= 'f')) { value = digit-'a'+0xa; } else if ((digit >= 'A') && (digit <= 'F')) { value = digit-'A'+0xa; } else { return GDB_EOF; } return (value < base) ? value : GDB_EOF; } #if DEBUG /* * Determine if this is a printable ASCII character. */ static int gdb_is_printable_char(char ch) { return (ch >= 0x20 && ch <= 0x7e); } #endif /***************************************************************************** * Packet Functions ****************************************************************************/ /* * Receive a packet acknowledgment * * Returns: * 0 if an ACK (+) was received * 1 if a NACK (-) was received * GDB_EOF otherwise */ static int gdb_recv_ack(struct gdb_state *state) { int response; /* Wait for packet ack */ switch (response = gdb_sys_getc(state)) { case '+': /* Packet acknowledged */ return 0; case '-': /* Packet negative acknowledged */ return 1; default: /* Bad response! */ GDB_PRINT("received bad packet response: 0x%2x\n", response); return GDB_EOF; } } /* * Calculate 8-bit checksum of a buffer. * * Returns: * 8-bit checksum. */ static int gdb_checksum(const char *buf, unsigned int len) { unsigned char csum; csum = 0; while (len--) { csum += *buf++; } return csum; } /* * Transmits a packet of data. * Packets are of the form: $# * * Returns: * 0 if the packet was transmitted and acknowledged * 1 if the packet was transmitted but not acknowledged * GDB_EOF otherwise */ static int gdb_send_packet(struct gdb_state *state, const char *pkt_data, unsigned int pkt_len) { char buf[3]; char csum; /* Send packet start */ if (gdb_sys_putchar(state, '$') == GDB_EOF) { return GDB_EOF; } #if DEBUG { unsigned int p; GDB_PRINT("-> "); for (p = 0; p < pkt_len; p++) { if (gdb_is_printable_char(pkt_data[p])) { GDB_PRINT("%c", pkt_data[p]); } else { GDB_PRINT("\\x%02x", pkt_data[p]&0xff); } } GDB_PRINT("\n"); } #endif /* Send packet data */ if (gdb_write(state, pkt_data, pkt_len) == GDB_EOF) { return GDB_EOF; } /* Send the checksum */ buf[0] = '#'; csum = gdb_checksum(pkt_data, pkt_len); if ((gdb_enc_hex(buf+1, sizeof(buf)-1, &csum, 1) == GDB_EOF) || (gdb_write(state, buf, sizeof(buf)) == GDB_EOF)) { return GDB_EOF; } return gdb_recv_ack(state); } /* * Receives a packet of data, assuming a 7-bit clean connection. * * Returns: * 0 if the packet was received * GDB_EOF otherwise */ static int gdb_recv_packet(struct gdb_state *state, char *pkt_buf, unsigned int pkt_buf_len, unsigned int *pkt_len) { int data; char expected_csum, actual_csum; char buf[2]; /* Wait for packet start */ actual_csum = 0; while (1) { data = gdb_sys_getc(state); if (data == GDB_EOF) { return GDB_EOF; } else if (data == '$') { /* Detected start of packet. */ break; } } /* Read until checksum */ *pkt_len = 0; while (1) { data = gdb_sys_getc(state); if (data == GDB_EOF) { /* Error receiving character */ return GDB_EOF; } else if (data == '#') { /* End of packet */ break; } else { /* Check for space */ if (*pkt_len >= pkt_buf_len) { GDB_PRINT("packet buffer overflow\n"); return GDB_EOF; } /* Store character and update checksum */ pkt_buf[(*pkt_len)++] = (char) data; } } #if DEBUG { unsigned int p; GDB_PRINT("<- "); for (p = 0; p < *pkt_len; p++) { if (gdb_is_printable_char(pkt_buf[p])) { GDB_PRINT("%c", pkt_buf[p]); } else { GDB_PRINT("\\x%02x", pkt_buf[p] & 0xff); } } GDB_PRINT("\n"); } #endif /* Receive the checksum */ if ((gdb_read(state, buf, sizeof(buf), 2) == GDB_EOF) || (gdb_dec_hex(buf, 2, &expected_csum, 1) == GDB_EOF)) { return GDB_EOF; } /* Verify checksum */ actual_csum = gdb_checksum(pkt_buf, *pkt_len); if (actual_csum != expected_csum) { /* Send packet nack */ GDB_PRINT("received packet with bad checksum\n"); gdb_sys_putchar(state, '-'); return GDB_EOF; } /* Send packet ack */ gdb_sys_putchar(state, '+'); return 0; } /***************************************************************************** * Data Encoding/Decoding ****************************************************************************/ /* * Encode data to its hex-value representation in a buffer. * * Returns: * 0+ number of bytes written to buf * GDB_EOF if the buffer is too small */ static int gdb_enc_hex(char *buf, unsigned int buf_len, const char *data, unsigned int data_len) { unsigned int pos; if (buf_len < data_len*2) { /* Buffer too small */ return GDB_EOF; } for (pos = 0; pos < data_len; pos++) { *buf++ = gdb_get_digit((data[pos] >> 4) & 0xf); *buf++ = gdb_get_digit((data[pos] ) & 0xf); } return data_len*2; } /* * Decode data from its hex-value representation to a buffer. * * Returns: * 0 if successful * GDB_EOF if the buffer is too small */ static int gdb_dec_hex(const char *buf, unsigned int buf_len, char *data, unsigned int data_len) { unsigned int pos; int tmp; if (buf_len != data_len*2) { /* Buffer too small */ return GDB_EOF; } for (pos = 0; pos < data_len; pos++) { /* Decode high nibble */ tmp = gdb_get_val(*buf++, 16); if (tmp == GDB_EOF) { /* Buffer contained junk. */ GDB_ASSERT(0); return GDB_EOF; } data[pos] = tmp << 4; /* Decode low nibble */ tmp = gdb_get_val(*buf++, 16); if (tmp == GDB_EOF) { /* Buffer contained junk. */ GDB_ASSERT(0); return GDB_EOF; } data[pos] |= tmp; } return 0; } /* * Encode data to its binary representation in a buffer. * * Returns: * 0+ number of bytes written to buf * GDB_EOF if the buffer is too small */ static int gdb_enc_bin(char *buf, unsigned int buf_len, const char *data, unsigned int data_len) { unsigned int buf_pos, data_pos; for (buf_pos = 0, data_pos = 0; data_pos < data_len; data_pos++) { if (data[data_pos] == '$' || data[data_pos] == '#' || data[data_pos] == '}' || data[data_pos] == '*') { if (buf_pos+1 >= buf_len) { GDB_ASSERT(0); return GDB_EOF; } buf[buf_pos++] = '}'; buf[buf_pos++] = data[data_pos] ^ 0x20; } else { if (buf_pos >= buf_len) { GDB_ASSERT(0); return GDB_EOF; } buf[buf_pos++] = data[data_pos]; } } return buf_pos; } /* * Decode data from its bin-value representation to a buffer. * * Returns: * 0+ if successful, number of bytes decoded * GDB_EOF if the buffer is too small */ static int gdb_dec_bin(const char *buf, unsigned int buf_len, char *data, unsigned int data_len) { unsigned int buf_pos, data_pos; for (buf_pos = 0, data_pos = 0; buf_pos < buf_len; buf_pos++) { if (data_pos >= data_len) { /* Output buffer overflow */ GDB_ASSERT(0); return GDB_EOF; } if (buf[buf_pos] == '}') { /* The next byte is escaped! */ if (buf_pos+1 >= buf_len) { /* There's an escape character, but no escaped character * following the escape character. */ GDB_ASSERT(0); return GDB_EOF; } buf_pos += 1; data[data_pos++] = buf[buf_pos] ^ 0x20; } else { data[data_pos++] = buf[buf_pos]; } } return data_pos; } /***************************************************************************** * Command Functions ****************************************************************************/ /* * Read from memory and encode into buf. * * Returns: * 0+ number of bytes written to buf * GDB_EOF if the buffer is too small */ static int gdb_mem_read(struct gdb_state *state, char *buf, unsigned int buf_len, address addr, unsigned int len, gdb_enc_func enc) { char data[64]; unsigned int pos; if (len > sizeof(data)) { return GDB_EOF; } /* Read from system memory */ for (pos = 0; pos < len; pos++) { if (gdb_sys_mem_readb(state, addr+pos, &data[pos])) { /* Failed to read */ return GDB_EOF; } } /* Encode data */ return enc(buf, buf_len, data, len); } /* * Write to memory from encoded buf. */ static int gdb_mem_write(struct gdb_state *state, const char *buf, unsigned int buf_len, address addr, unsigned int len, gdb_dec_func dec) { char data[64]; unsigned int pos; if (len > sizeof(data)) { return GDB_EOF; } /* Decode data */ if (dec(buf, buf_len, data, len) == GDB_EOF) { return GDB_EOF; } /* Write to system memory */ for (pos = 0; pos < len; pos++) { if (gdb_sys_mem_writeb(state, addr+pos, data[pos])) { /* Failed to write */ return GDB_EOF; } } return 0; } /* * Continue program execution at PC. */ int gdb_continue(struct gdb_state *state) { gdb_sys_continue(state); return 0; } /* * Step one instruction. */ int gdb_step(struct gdb_state *state) { gdb_sys_step(state); return 0; } /***************************************************************************** * Packet Creation Helpers ****************************************************************************/ /* * Send OK packet */ static int gdb_send_ok_packet(struct gdb_state *state, char *buf, unsigned int buf_len) { return gdb_send_packet(state, "OK", 2); } /* * Send a message to the debugging console (via O XX... packet) */ static int gdb_send_conmsg_packet(struct gdb_state *state, char *buf, unsigned int buf_len, const char *msg) { unsigned int size; int status; if (buf_len < 2) { /* Buffer too small */ return GDB_EOF; } buf[0] = 'O'; status = gdb_enc_hex(&buf[1], buf_len-1, msg, gdb_strlen(msg)); if (status == GDB_EOF) { return GDB_EOF; } size = 1 + status; return gdb_send_packet(state, buf, size); } /* * Send a signal packet (S AA). */ static int gdb_send_signal_packet(struct gdb_state *state, char *buf, unsigned int buf_len, char signal) { unsigned int size; int status; if (buf_len < 4) { /* Buffer too small */ return GDB_EOF; } buf[0] = 'S'; status = gdb_enc_hex(&buf[1], buf_len-1, &signal, 1); if (status == GDB_EOF) { return GDB_EOF; } size = 1 + status; return gdb_send_packet(state, buf, size); } /* * Send a error packet (E AA). */ static int gdb_send_error_packet(struct gdb_state *state, char *buf, unsigned int buf_len, char error) { unsigned int size; int status; if (buf_len < 4) { /* Buffer too small */ return GDB_EOF; } buf[0] = 'E'; status = gdb_enc_hex(&buf[1], buf_len-1, &error, 1); if (status == GDB_EOF) { return GDB_EOF; } size = 1 + status; return gdb_send_packet(state, buf, size); } /***************************************************************************** * Communication Functions ****************************************************************************/ /* * Write a sequence of bytes. * * Returns: * 0 if successful * GDB_EOF if failed to write all bytes */ static int gdb_write(struct gdb_state *state, const char *buf, unsigned int len) { while (len--) { if (gdb_sys_putchar(state, *buf++) == GDB_EOF) { return GDB_EOF; } } return 0; } /* * Read a sequence of bytes. * * Returns: * 0 if successfully read len bytes * GDB_EOF if failed to read all bytes */ static int gdb_read(struct gdb_state *state, char *buf, unsigned int buf_len, unsigned int len) { char c; if (buf_len < len) { /* Buffer too small */ return GDB_EOF; } while (len--) { if ((c = gdb_sys_getc(state)) == GDB_EOF) { return GDB_EOF; } *buf++ = c; } return 0; } /***************************************************************************** * Main Loop ****************************************************************************/ /* * Main debug loop. Handles commands. */ int gdb_main(struct gdb_state *state) { address addr; char pkt_buf[512]; int status; unsigned int length; unsigned int pkt_len; const char *ptr_next; gdb_send_signal_packet(state, pkt_buf, sizeof(pkt_buf), state->signum); while (1) { /* Receive the next packet */ status = gdb_recv_packet(state, pkt_buf, sizeof(pkt_buf), &pkt_len); if (status == GDB_EOF) { break; } if (pkt_len == 0) { /* Received empty packet.. */ continue; } ptr_next = pkt_buf; /* * Handle one letter commands */ switch (pkt_buf[0]) { /* Calculate remaining space in packet from ptr_next position. */ #define token_remaining_buf (pkt_len-(ptr_next-pkt_buf)) /* Expecting a seperator. If not present, go to error */ #define token_expect_seperator(c) \ { \ if (!ptr_next || *ptr_next != c) { \ goto error; \ } else { \ ptr_next += 1; \ } \ } /* Expecting an integer argument. If not present, go to error */ #define token_expect_integer_arg(arg) \ { \ arg = gdb_strtol(ptr_next, token_remaining_buf, \ 16, &ptr_next); \ if (!ptr_next) { \ goto error; \ } \ } /* * Read Registers * Command Format: g */ case 'g': /* Encode registers */ status = gdb_enc_hex(pkt_buf, sizeof(pkt_buf), (char *)&(state->registers), sizeof(state->registers)); if (status == GDB_EOF) { goto error; } pkt_len = status; gdb_send_packet(state, pkt_buf, pkt_len); break; /* * Write Registers * Command Format: G XX... */ case 'G': status = gdb_dec_hex(pkt_buf+1, pkt_len-1, (char *)&(state->registers), sizeof(state->registers)); if (status == GDB_EOF) { goto error; } gdb_send_ok_packet(state, pkt_buf, sizeof(pkt_buf)); break; /* * Read a Register * Command Format: p n */ case 'p': ptr_next += 1; token_expect_integer_arg(addr); if (addr >= GDB_CPU_NUM_REGISTERS) { goto error; } /* Read Register */ status = gdb_enc_hex(pkt_buf, sizeof(pkt_buf), (char *)&(state->registers[addr]), sizeof(state->registers[addr])); if (status == GDB_EOF) { goto error; } gdb_send_packet(state, pkt_buf, status); break; /* * Write a Register * Command Format: P n...=r... */ case 'P': ptr_next += 1; token_expect_integer_arg(addr); token_expect_seperator('='); if (addr < GDB_CPU_NUM_REGISTERS) { status = gdb_dec_hex(ptr_next, token_remaining_buf, (char *)&(state->registers[addr]), sizeof(state->registers[addr])); if (status == GDB_EOF) { goto error; } } gdb_send_ok_packet(state, pkt_buf, sizeof(pkt_buf)); break; /* * Read Memory * Command Format: m addr,length */ case 'm': ptr_next += 1; token_expect_integer_arg(addr); token_expect_seperator(','); token_expect_integer_arg(length); /* Read Memory */ status = gdb_mem_read(state, pkt_buf, sizeof(pkt_buf), addr, length, gdb_enc_hex); if (status == GDB_EOF) { goto error; } gdb_send_packet(state, pkt_buf, status); break; /* * Write Memory * Command Format: M addr,length:XX.. */ case 'M': ptr_next += 1; token_expect_integer_arg(addr); token_expect_seperator(','); token_expect_integer_arg(length); token_expect_seperator(':'); /* Write Memory */ status = gdb_mem_write(state, ptr_next, token_remaining_buf, addr, length, gdb_dec_hex); if (status == GDB_EOF) { goto error; } gdb_send_ok_packet(state, pkt_buf, sizeof(pkt_buf)); break; /* * Write Memory (Binary) * Command Format: X addr,length:XX.. */ case 'X': ptr_next += 1; token_expect_integer_arg(addr); token_expect_seperator(','); token_expect_integer_arg(length); token_expect_seperator(':'); /* Write Memory */ status = gdb_mem_write(state, ptr_next, token_remaining_buf, addr, length, gdb_dec_bin); if (status == GDB_EOF) { goto error; } gdb_send_ok_packet(state, pkt_buf, sizeof(pkt_buf)); break; /* * Continue * Command Format: c [addr] */ case 'c': gdb_continue(state); return 0; /* * Single-step * Command Format: s [addr] */ case 's': gdb_step(state); return 0; case '?': gdb_send_signal_packet(state, pkt_buf, sizeof(pkt_buf), state->signum); break; /* * Unsupported Command */ default: gdb_send_packet(state, NULL, 0); } continue; error: gdb_send_error_packet(state, pkt_buf, sizeof(pkt_buf), 0x00); #undef token_remaining_buf #undef token_expect_seperator #undef token_expect_integer_arg } return 0; } /***************************************************************************** * * Mock * ****************************************************************************/ #ifdef GDBSTUB_ARCH_MOCK #include #include #include #include static char gdb_mem[256]; struct gdb_buffer { char *buf; unsigned int pos_write; unsigned int pos_read; unsigned int size; } gdb_input, gdb_output; void gdb_buf_write(struct gdb_buffer *buf, int ch) { if (buf->buf == NULL) { buf->pos_write = 0; buf->pos_read = 0; buf->size = 512; buf->buf = malloc(buf->size); } else if (buf->pos_write >= buf->size) { buf->size *= 2; buf->buf = realloc(buf->buf, buf->size); } assert(buf->buf); buf->buf[buf->pos_write++] = ch; } int gdb_buf_read(struct gdb_buffer *buf) { if (buf->buf && buf->pos_read < buf->pos_write) { return buf->buf[buf->pos_read++]; } return EOF; } /***************************************************************************** * Debugging System Functions ****************************************************************************/ /* * Write one character to the debugging stream. */ int gdb_sys_putchar(struct gdb_state *state, int ch) { #ifdef USE_STDIO putchar(ch); #else gdb_buf_write(&gdb_output, ch); #endif return 0; } /* * Read one character from the debugging stream. */ int gdb_sys_getc(struct gdb_state *state) { #ifdef USE_STDIO int ch = getchar(); return ch == EOF ? GDB_EOF : ch; #else return gdb_buf_read(&gdb_input); #endif } /* * Read one byte from memory. */ int gdb_sys_mem_readb(struct gdb_state *state, address addr, char *val) { if (addr >= sizeof(gdb_mem)) { return 1; } *val = gdb_mem[addr]; return 0; } /* * Write one byte to memory. */ int gdb_sys_mem_writeb(struct gdb_state *state, address addr, char val) { if (addr >= sizeof(gdb_mem)) { return 1; } gdb_mem[addr] = val; return 0; } /* * Continue program execution. */ int gdb_sys_continue(struct gdb_state *state) { return 0; } /* * Single step the next instruction. */ int gdb_sys_step(struct gdb_state *state) { return 0; } #endif /* GDBSTUB_ARCH_MOCK */ /***************************************************************************** * * x86 * ****************************************************************************/ #ifdef GDBSTUB_ARCH_X86 /***************************************************************************** * Types ****************************************************************************/ #pragma pack(1) struct gdb_interrupt_state { uint32_t ss; uint32_t gs; uint32_t fs; uint32_t es; uint32_t ds; uint32_t edi; uint32_t esi; uint32_t ebp; uint32_t esp; uint32_t ebx; uint32_t edx; uint32_t ecx; uint32_t eax; uint32_t vector; uint32_t error_code; uint32_t eip; uint32_t cs; uint32_t eflags; }; struct gdb_idtr { uint16_t len; uint32_t offset; }; struct gdb_idt_gate { uint16_t offset_low; uint16_t segment; uint16_t flags; uint16_t offset_high; }; #pragma pack() /***************************************************************************** * Const Data ****************************************************************************/ extern void const * const gdb_x86_int_handlers[]; /***************************************************************************** * Prototypes ****************************************************************************/ void gdb_x86_int_handler(struct gdb_interrupt_state *istate); static void gdb_x86_hook_idt(uint8_t vector, const void *function); static void gdb_x86_init_gates(void); static void gdb_x86_init_idt(void); static void gdb_x86_load_idt(struct gdb_idtr *idtr); static void gdb_x86_store_idt(struct gdb_idtr *idtr); static uint32_t gdb_x86_get_cs(void); static void gdb_x86_interrupt(struct gdb_interrupt_state *istate); static void gdb_x86_io_write_8(uint16_t port, uint8_t val); static uint8_t gdb_x86_io_read_8(uint16_t port); static int gdb_x86_serial_getc(void); static int gdb_x86_serial_putchar(int ch); #ifdef __STRICT_ANSI__ #define asm __asm__ #endif #define SERIAL_COM1 0x3f8 #define SERIAL_COM2 0x2f8 #define SERIAL_PORT SERIAL_COM1 #define NUM_IDT_ENTRIES 32 /***************************************************************************** * BSS Data ****************************************************************************/ static struct gdb_idt_gate gdb_idt_gates[NUM_IDT_ENTRIES]; static struct gdb_state gdb_state; /***************************************************************************** * Misc. Functions ****************************************************************************/ /* * Get current code segment (CS register). */ static uint32_t gdb_x86_get_cs(void) { uint32_t cs; asm volatile ( "push %%cs;" "pop %%eax;" /* Outputs */ : "=a" (cs) /* Inputs */ : /* None */ /* Clobbers */ : /* None */ ); return cs; } /***************************************************************************** * Interrupt Management Functions ****************************************************************************/ /* * Initialize idt_gates with the interrupt handlers. */ static void gdb_x86_init_gates(void) { unsigned int i; uint16_t cs; cs = gdb_x86_get_cs(); for (i = 0; i < NUM_IDT_ENTRIES; i++) { gdb_idt_gates[i].flags = 0x8E00; gdb_idt_gates[i].segment = cs; gdb_idt_gates[i].offset_low = ((uint32_t)gdb_x86_int_handlers[i] ) & 0xffff; gdb_idt_gates[i].offset_high = ((uint32_t)gdb_x86_int_handlers[i] >> 16) & 0xffff; } } /* * Load a new IDT. */ static void gdb_x86_load_idt(struct gdb_idtr *idtr) { asm volatile ( "lidt %0" /* Outputs */ : /* None */ /* Inputs */ : "m" (*idtr) /* Clobbers */ : /* None */ ); } /* * Get current IDT. */ static void gdb_x86_store_idt(struct gdb_idtr *idtr) { asm volatile ( "sidt %0" /* Outputs */ : "=m" (*idtr) /* Inputs */ : /* None */ /* Clobbers */ : /* None */ ); } /* * Hook a vector of the current IDT. */ static void gdb_x86_hook_idt(uint8_t vector, const void *function) { struct gdb_idtr idtr; struct gdb_idt_gate *gates; gdb_x86_store_idt(&idtr); gates = (struct gdb_idt_gate *)idtr.offset; gates[vector].flags = 0x8E00; gates[vector].segment = gdb_x86_get_cs(); gates[vector].offset_low = (((uint32_t)function) ) & 0xffff; gates[vector].offset_high = (((uint32_t)function) >> 16) & 0xffff; } /* * Initialize IDT gates and load the new IDT. */ static void gdb_x86_init_idt(void) { struct gdb_idtr idtr; gdb_x86_init_gates(); idtr.len = sizeof(gdb_idt_gates)-1; idtr.offset = (uint32_t)gdb_idt_gates; gdb_x86_load_idt(&idtr); } /* * Common interrupt handler routine. */ void gdb_x86_int_handler(struct gdb_interrupt_state *istate) { gdb_x86_interrupt(istate); } /* * Debug interrupt handler. */ static void gdb_x86_interrupt(struct gdb_interrupt_state *istate) { /* Translate vector to signal */ switch (istate->vector) { case 1: gdb_state.signum = 5; break; case 3: gdb_state.signum = 5; break; default: gdb_state.signum = 7; } /* Load Registers */ gdb_state.registers[GDB_CPU_I386_REG_EAX] = istate->eax; gdb_state.registers[GDB_CPU_I386_REG_ECX] = istate->ecx; gdb_state.registers[GDB_CPU_I386_REG_EDX] = istate->edx; gdb_state.registers[GDB_CPU_I386_REG_EBX] = istate->ebx; gdb_state.registers[GDB_CPU_I386_REG_ESP] = istate->esp; gdb_state.registers[GDB_CPU_I386_REG_EBP] = istate->ebp; gdb_state.registers[GDB_CPU_I386_REG_ESI] = istate->esi; gdb_state.registers[GDB_CPU_I386_REG_EDI] = istate->edi; gdb_state.registers[GDB_CPU_I386_REG_PC] = istate->eip; gdb_state.registers[GDB_CPU_I386_REG_CS] = istate->cs; gdb_state.registers[GDB_CPU_I386_REG_PS] = istate->eflags; gdb_state.registers[GDB_CPU_I386_REG_SS] = istate->ss; gdb_state.registers[GDB_CPU_I386_REG_DS] = istate->ds; gdb_state.registers[GDB_CPU_I386_REG_ES] = istate->es; gdb_state.registers[GDB_CPU_I386_REG_FS] = istate->fs; gdb_state.registers[GDB_CPU_I386_REG_GS] = istate->gs; gdb_main(&gdb_state); /* Restore Registers */ istate->eax = gdb_state.registers[GDB_CPU_I386_REG_EAX]; istate->ecx = gdb_state.registers[GDB_CPU_I386_REG_ECX]; istate->edx = gdb_state.registers[GDB_CPU_I386_REG_EDX]; istate->ebx = gdb_state.registers[GDB_CPU_I386_REG_EBX]; istate->esp = gdb_state.registers[GDB_CPU_I386_REG_ESP]; istate->ebp = gdb_state.registers[GDB_CPU_I386_REG_EBP]; istate->esi = gdb_state.registers[GDB_CPU_I386_REG_ESI]; istate->edi = gdb_state.registers[GDB_CPU_I386_REG_EDI]; istate->eip = gdb_state.registers[GDB_CPU_I386_REG_PC]; istate->cs = gdb_state.registers[GDB_CPU_I386_REG_CS]; istate->eflags = gdb_state.registers[GDB_CPU_I386_REG_PS]; istate->ss = gdb_state.registers[GDB_CPU_I386_REG_SS]; istate->ds = gdb_state.registers[GDB_CPU_I386_REG_DS]; istate->es = gdb_state.registers[GDB_CPU_I386_REG_ES]; istate->fs = gdb_state.registers[GDB_CPU_I386_REG_FS]; istate->gs = gdb_state.registers[GDB_CPU_I386_REG_GS]; } /***************************************************************************** * I/O Functions ****************************************************************************/ /* * Write to I/O port. */ static void gdb_x86_io_write_8(uint16_t port, uint8_t val) { asm volatile ( "outb %%al, %%dx;" /* Outputs */ : /* None */ /* Inputs */ : "a" (val), "d" (port) /* Clobbers */ : /* None */ ); } /* * Read from I/O port. */ static uint8_t gdb_x86_io_read_8(uint16_t port) { uint8_t val; asm volatile ( "inb %%dx, %%al;" /* Outputs */ : "=a" (val) /* Inputs */ : "d" (port) /* Clobbers */ : /* None */ ); return val; } /***************************************************************************** * NS16550 Serial Port (IO) ****************************************************************************/ #define SERIAL_THR 0 #define SERIAL_RBR 0 #define SERIAL_LSR 5 static int gdb_x86_serial_getc(void) { /* Wait for data */ while ((gdb_x86_io_read_8(SERIAL_PORT + SERIAL_LSR) & 1) == 0); return gdb_x86_io_read_8(SERIAL_PORT + SERIAL_RBR); } static int gdb_x86_serial_putchar(int ch) { /* Wait for THRE (bit 5) to be high */ while ((gdb_x86_io_read_8(SERIAL_PORT + SERIAL_LSR) & (1<<5)) == 0); gdb_x86_io_write_8(SERIAL_PORT + SERIAL_THR, ch); return ch; } /***************************************************************************** * Debugging System Functions ****************************************************************************/ /* * Write one character to the debugging stream. */ int gdb_sys_putchar(struct gdb_state *state, int ch) { return gdb_x86_serial_putchar(ch); } /* * Read one character from the debugging stream. */ int gdb_sys_getc(struct gdb_state *state) { return gdb_x86_serial_getc() & 0xff; } /* * Read one byte from memory. */ int gdb_sys_mem_readb(struct gdb_state *state, address addr, char *val) { *val = *(volatile char *)addr; return 0; } /* * Write one byte to memory. */ int gdb_sys_mem_writeb(struct gdb_state *state, address addr, char val) { *(volatile char *)addr = val; return 0; } /* * Continue program execution. */ int gdb_sys_continue(struct gdb_state *state) { gdb_state.registers[GDB_CPU_I386_REG_PS] &= ~(1<<8); return 0; } /* * Single step the next instruction. */ int gdb_sys_step(struct gdb_state *state) { gdb_state.registers[GDB_CPU_I386_REG_PS] |= 1<<8; return 0; } /* * Debugger init function. * * Hooks the IDT to enable debugging. */ void gdb_sys_init(void) { /* Hook current IDT. */ gdb_x86_hook_idt(1, gdb_x86_int_handlers[1]); gdb_x86_hook_idt(3, gdb_x86_int_handlers[3]); /* Interrupt to start debugging. */ asm volatile ("int3"); } #endif /* GDBSTUB_ARCH_X86 */ /***************************************************************************** * * UVM32 * ****************************************************************************/ #ifdef GDBSTUB_ARCH_UVM32 #include #include #include #include /***************************************************************************** * Debugging System Functions ****************************************************************************/ /* * Write one character to the debugging stream. */ int gdb_sys_putchar(struct gdb_state *state, int ch) { if (EOF == fputc(ch, stdout)) return GDB_EOF; fflush(stdout); return 0; } /* * Read one character from the debugging stream. */ int gdb_sys_getc(struct gdb_state *state) { int ch = fgetc(stdin); return (EOF == ch) ? GDB_EOF : ch; } /* * Read one byte from memory. */ int gdb_sys_mem_readb(struct gdb_state *state, address addr, char *val) { if (addr >= MINIRV32_RAM_IMAGE_OFFSET) { addr -= MINIRV32_RAM_IMAGE_OFFSET; } if (addr >= UVM32_MEMORY_SIZE) { return 1; } const uint8_t *mem = uvm32_getMemory(state->vmst); *val = (char)mem[addr]; return 0; } /* * Write one byte to memory. */ int gdb_sys_mem_writeb(struct gdb_state *state, address addr, char val) { if (addr >= MINIRV32_RAM_IMAGE_OFFSET) { addr -= MINIRV32_RAM_IMAGE_OFFSET; } if (addr >= UVM32_MEMORY_SIZE) { return 1; } uint8_t *mem = (uint8_t *)uvm32_getMemory(state->vmst); mem[addr] = (uint8_t)val; return 0; } static int gdb_sys_run(struct gdb_state *state, bool cont) { bool running = true; enum { MAX_STEPS = 1000 }; while (running) { uvm32_evt_t evt = {0}; uint32_t steps = uvm32_run(state->vmst, &evt, cont ? MAX_STEPS : 1); /* Call user callback if available */ if (NULL != state->event_cb) { state->event_cb(state->vmst, &evt); } switch (evt.typ) { case UVM32_EVT_BREAK: running = false; state->signum = SIGTRAP; uvm32_clearError(state->vmst); break; case UVM32_EVT_ERR: switch (evt.data.err.errcode) { case UVM32_ERR_HUNG: if (!cont) { // single step state->signum = SIGTRAP; uvm32_clearError(state->vmst); running = false; } else if (MAX_STEPS == steps) { // continue? /* Ran up to max steps, keep going */ uvm32_clearError(state->vmst); } else { state->signum = SIGTRAP; // FIXME running = false; } break; case UVM32_ERR_MEM_RD: case UVM32_ERR_MEM_WR: running = false; state->signum = SIGSEGV; break; case UVM32_ERR_INTERNAL_CORE: running = false; state->signum = SIGILL; break; default: break; } // switch(evt.data.err.errcode) break; default: break; } // switch(evt.typ) } // while /* Update registers */ memcpy(state->registers, &state->vmst->_core.regs, sizeof(state->registers)); return 0; } /* * Continue program execution. */ int gdb_sys_continue(struct gdb_state *state) { return gdb_sys_run(state, true); } /* * Single step the next instruction. */ int gdb_sys_step(struct gdb_state *state) { return gdb_sys_run(state, false); } /* * Debugger init function. * */ void gdb_sys_init(void) { } /* Helper function to init gdb_state */ int32_t gdb_sys_init_state(struct gdb_state* state, uvm32_state_t* vmst, uvm32_event_callback_t event_cb) { /* Sanity check argument */ if ((NULL == state) || (NULL == vmst)) { return -1; } memset(state, 0, sizeof(struct gdb_state)); state->vmst = vmst; state->event_cb = event_cb; /* Update registers */ memcpy(state->registers, vmst->_core.regs, sizeof(state->registers)); return 0; } #endif /* GDBSTUB_ARCH_UVM32 */ #endif /* GDBSTUB_IMPLEMENTATION */ #endif /* GDBSTUB_H */