gdbstub: implement remote debugging protocol escapes for command receive
- decode escape sequences - decompress run-length encoding escape sequences - report command parsing problems to output when debug output is enabled - reject packet checksums that are not valid hex digits - compute the checksum based on the packet stream, not based on the decoded packet Tested with GDB and QtCreator integrated debugger on SMP QEMU instance. Works for me. Signed-off-by: Doug Gale <doug16k@gmail.com> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
parent
dd1559bb26
commit
4bf43122db
110
gdbstub.c
110
gdbstub.c
@ -286,6 +286,8 @@ enum RSState {
|
||||
RS_INACTIVE,
|
||||
RS_IDLE,
|
||||
RS_GETLINE,
|
||||
RS_GETLINE_ESC,
|
||||
RS_GETLINE_RLE,
|
||||
RS_CHKSUM1,
|
||||
RS_CHKSUM2,
|
||||
};
|
||||
@ -296,7 +298,8 @@ typedef struct GDBState {
|
||||
enum RSState state; /* parsing state */
|
||||
char line_buf[MAX_PACKET_LENGTH];
|
||||
int line_buf_index;
|
||||
int line_csum;
|
||||
int line_sum; /* running checksum */
|
||||
int line_csum; /* checksum at the end of the packet */
|
||||
uint8_t last_packet[MAX_PACKET_LENGTH + 4];
|
||||
int last_packet_len;
|
||||
int signal;
|
||||
@ -1508,7 +1511,6 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
|
||||
|
||||
static void gdb_read_byte(GDBState *s, int ch)
|
||||
{
|
||||
int i, csum;
|
||||
uint8_t reply;
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
@ -1542,35 +1544,123 @@ static void gdb_read_byte(GDBState *s, int ch)
|
||||
switch(s->state) {
|
||||
case RS_IDLE:
|
||||
if (ch == '$') {
|
||||
/* start of command packet */
|
||||
s->line_buf_index = 0;
|
||||
s->line_sum = 0;
|
||||
s->state = RS_GETLINE;
|
||||
} else {
|
||||
#ifdef DEBUG_GDB
|
||||
printf("gdbstub received garbage between packets: 0x%x\n", ch);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case RS_GETLINE:
|
||||
if (ch == '#') {
|
||||
s->state = RS_CHKSUM1;
|
||||
if (ch == '}') {
|
||||
/* start escape sequence */
|
||||
s->state = RS_GETLINE_ESC;
|
||||
s->line_sum += ch;
|
||||
} else if (ch == '*') {
|
||||
/* start run length encoding sequence */
|
||||
s->state = RS_GETLINE_RLE;
|
||||
s->line_sum += ch;
|
||||
} else if (ch == '#') {
|
||||
/* end of command, start of checksum*/
|
||||
s->state = RS_CHKSUM1;
|
||||
} else if (s->line_buf_index >= sizeof(s->line_buf) - 1) {
|
||||
#ifdef DEBUG_GDB
|
||||
printf("gdbstub command buffer overrun, dropping command\n");
|
||||
#endif
|
||||
s->state = RS_IDLE;
|
||||
} else {
|
||||
s->line_buf[s->line_buf_index++] = ch;
|
||||
/* unescaped command character */
|
||||
s->line_buf[s->line_buf_index++] = ch;
|
||||
s->line_sum += ch;
|
||||
}
|
||||
break;
|
||||
case RS_GETLINE_ESC:
|
||||
if (ch == '#') {
|
||||
/* unexpected end of command in escape sequence */
|
||||
s->state = RS_CHKSUM1;
|
||||
} else if (s->line_buf_index >= sizeof(s->line_buf) - 1) {
|
||||
/* command buffer overrun */
|
||||
#ifdef DEBUG_GDB
|
||||
printf("gdbstub command buffer overrun, dropping command\n");
|
||||
#endif
|
||||
s->state = RS_IDLE;
|
||||
} else {
|
||||
/* parse escaped character and leave escape state */
|
||||
s->line_buf[s->line_buf_index++] = ch ^ 0x20;
|
||||
s->line_sum += ch;
|
||||
s->state = RS_GETLINE;
|
||||
}
|
||||
break;
|
||||
case RS_GETLINE_RLE:
|
||||
if (ch < ' ') {
|
||||
/* invalid RLE count encoding */
|
||||
#ifdef DEBUG_GDB
|
||||
printf("gdbstub got invalid RLE count: 0x%x\n", ch);
|
||||
#endif
|
||||
s->state = RS_GETLINE;
|
||||
} else {
|
||||
/* decode repeat length */
|
||||
int repeat = (unsigned char)ch - ' ' + 3;
|
||||
if (s->line_buf_index + repeat >= sizeof(s->line_buf) - 1) {
|
||||
/* that many repeats would overrun the command buffer */
|
||||
#ifdef DEBUG_GDB
|
||||
printf("gdbstub command buffer overrun,"
|
||||
" dropping command\n");
|
||||
#endif
|
||||
s->state = RS_IDLE;
|
||||
} else if (s->line_buf_index < 1) {
|
||||
/* got a repeat but we have nothing to repeat */
|
||||
#ifdef DEBUG_GDB
|
||||
printf("gdbstub got invalid RLE sequence\n");
|
||||
#endif
|
||||
s->state = RS_GETLINE;
|
||||
} else {
|
||||
/* repeat the last character */
|
||||
memset(s->line_buf + s->line_buf_index,
|
||||
s->line_buf[s->line_buf_index - 1], repeat);
|
||||
s->line_buf_index += repeat;
|
||||
s->line_sum += ch;
|
||||
s->state = RS_GETLINE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RS_CHKSUM1:
|
||||
/* get high hex digit of checksum */
|
||||
if (!isxdigit(ch)) {
|
||||
#ifdef DEBUG_GDB
|
||||
printf("gdbstub got invalid command checksum digit\n");
|
||||
#endif
|
||||
s->state = RS_GETLINE;
|
||||
break;
|
||||
}
|
||||
s->line_buf[s->line_buf_index] = '\0';
|
||||
s->line_csum = fromhex(ch) << 4;
|
||||
s->state = RS_CHKSUM2;
|
||||
break;
|
||||
case RS_CHKSUM2:
|
||||
s->line_csum |= fromhex(ch);
|
||||
csum = 0;
|
||||
for(i = 0; i < s->line_buf_index; i++) {
|
||||
csum += s->line_buf[i];
|
||||
/* get low hex digit of checksum */
|
||||
if (!isxdigit(ch)) {
|
||||
#ifdef DEBUG_GDB
|
||||
printf("gdbstub got invalid command checksum digit\n");
|
||||
#endif
|
||||
s->state = RS_GETLINE;
|
||||
break;
|
||||
}
|
||||
if (s->line_csum != (csum & 0xff)) {
|
||||
s->line_csum |= fromhex(ch);
|
||||
|
||||
if (s->line_csum != (s->line_sum & 0xff)) {
|
||||
/* send NAK reply */
|
||||
reply = '-';
|
||||
put_buffer(s, &reply, 1);
|
||||
#ifdef DEBUG_GDB
|
||||
printf("gdbstub got command packet with incorrect checksum\n");
|
||||
#endif
|
||||
s->state = RS_IDLE;
|
||||
} else {
|
||||
/* send ACK reply */
|
||||
reply = '+';
|
||||
put_buffer(s, &reply, 1);
|
||||
s->state = gdb_handle_packet(s, s->line_buf);
|
||||
|
Loading…
Reference in New Issue
Block a user