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_INACTIVE,
|
||||||
RS_IDLE,
|
RS_IDLE,
|
||||||
RS_GETLINE,
|
RS_GETLINE,
|
||||||
|
RS_GETLINE_ESC,
|
||||||
|
RS_GETLINE_RLE,
|
||||||
RS_CHKSUM1,
|
RS_CHKSUM1,
|
||||||
RS_CHKSUM2,
|
RS_CHKSUM2,
|
||||||
};
|
};
|
||||||
@ -296,7 +298,8 @@ typedef struct GDBState {
|
|||||||
enum RSState state; /* parsing state */
|
enum RSState state; /* parsing state */
|
||||||
char line_buf[MAX_PACKET_LENGTH];
|
char line_buf[MAX_PACKET_LENGTH];
|
||||||
int line_buf_index;
|
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];
|
uint8_t last_packet[MAX_PACKET_LENGTH + 4];
|
||||||
int last_packet_len;
|
int last_packet_len;
|
||||||
int signal;
|
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)
|
static void gdb_read_byte(GDBState *s, int ch)
|
||||||
{
|
{
|
||||||
int i, csum;
|
|
||||||
uint8_t reply;
|
uint8_t reply;
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
@ -1542,35 +1544,123 @@ static void gdb_read_byte(GDBState *s, int ch)
|
|||||||
switch(s->state) {
|
switch(s->state) {
|
||||||
case RS_IDLE:
|
case RS_IDLE:
|
||||||
if (ch == '$') {
|
if (ch == '$') {
|
||||||
|
/* start of command packet */
|
||||||
s->line_buf_index = 0;
|
s->line_buf_index = 0;
|
||||||
|
s->line_sum = 0;
|
||||||
s->state = RS_GETLINE;
|
s->state = RS_GETLINE;
|
||||||
|
} else {
|
||||||
|
#ifdef DEBUG_GDB
|
||||||
|
printf("gdbstub received garbage between packets: 0x%x\n", ch);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RS_GETLINE:
|
case RS_GETLINE:
|
||||||
if (ch == '#') {
|
if (ch == '}') {
|
||||||
s->state = RS_CHKSUM1;
|
/* 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) {
|
} 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;
|
s->state = RS_IDLE;
|
||||||
} else {
|
} 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;
|
break;
|
||||||
case RS_CHKSUM1:
|
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_buf[s->line_buf_index] = '\0';
|
||||||
s->line_csum = fromhex(ch) << 4;
|
s->line_csum = fromhex(ch) << 4;
|
||||||
s->state = RS_CHKSUM2;
|
s->state = RS_CHKSUM2;
|
||||||
break;
|
break;
|
||||||
case RS_CHKSUM2:
|
case RS_CHKSUM2:
|
||||||
s->line_csum |= fromhex(ch);
|
/* get low hex digit of checksum */
|
||||||
csum = 0;
|
if (!isxdigit(ch)) {
|
||||||
for(i = 0; i < s->line_buf_index; i++) {
|
#ifdef DEBUG_GDB
|
||||||
csum += s->line_buf[i];
|
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 = '-';
|
reply = '-';
|
||||||
put_buffer(s, &reply, 1);
|
put_buffer(s, &reply, 1);
|
||||||
|
#ifdef DEBUG_GDB
|
||||||
|
printf("gdbstub got command packet with incorrect checksum\n");
|
||||||
|
#endif
|
||||||
s->state = RS_IDLE;
|
s->state = RS_IDLE;
|
||||||
} else {
|
} else {
|
||||||
|
/* send ACK reply */
|
||||||
reply = '+';
|
reply = '+';
|
||||||
put_buffer(s, &reply, 1);
|
put_buffer(s, &reply, 1);
|
||||||
s->state = gdb_handle_packet(s, s->line_buf);
|
s->state = gdb_handle_packet(s, s->line_buf);
|
||||||
|
Loading…
Reference in New Issue
Block a user