src/debugger/cli-debugger.c (view raw)
1#include "cli-debugger.h"
2#include "decoder.h"
3
4#include <signal.h>
5#include <stdarg.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <unistd.h>
10
11#ifdef USE_PTHREADS
12#include <pthread.h>
13#endif
14
15struct DebugVector {
16 struct DebugVector* next;
17 enum DVType {
18 ERROR_TYPE,
19 INT_TYPE,
20 CHAR_TYPE
21 } type;
22 union {
23 int32_t intValue;
24 const char* charValue;
25 };
26};
27
28static const char* ERROR_MISSING_ARGS = "Arguments missing";
29
30static struct CLIDebugger* _activeDebugger;
31
32typedef void (DebuggerCommand)(struct CLIDebugger*, struct DebugVector*);
33
34static void _breakInto(struct CLIDebugger*, struct DebugVector*);
35static void _continue(struct CLIDebugger*, struct DebugVector*);
36static void _next(struct CLIDebugger*, struct DebugVector*);
37static void _print(struct CLIDebugger*, struct DebugVector*);
38static void _printHex(struct CLIDebugger*, struct DebugVector*);
39static void _printStatus(struct CLIDebugger*, struct DebugVector*);
40static void _quit(struct CLIDebugger*, struct DebugVector*);
41static void _readByte(struct CLIDebugger*, struct DebugVector*);
42static void _readHalfword(struct CLIDebugger*, struct DebugVector*);
43static void _readWord(struct CLIDebugger*, struct DebugVector*);
44static void _setBreakpoint(struct CLIDebugger*, struct DebugVector*);
45static void _clearBreakpoint(struct CLIDebugger*, struct DebugVector*);
46static void _setWatchpoint(struct CLIDebugger*, struct DebugVector*);
47
48static void _breakIntoDefault(int signal);
49
50static struct {
51 const char* name;
52 DebuggerCommand* command;
53} _debuggerCommands[] = {
54 { "b", _setBreakpoint },
55 { "break", _setBreakpoint },
56 { "c", _continue },
57 { "continue", _continue },
58 { "d", _clearBreakpoint },
59 { "delete", _clearBreakpoint },
60 { "i", _printStatus },
61 { "info", _printStatus },
62 { "n", _next },
63 { "next", _next },
64 { "p", _print },
65 { "p/x", _printHex },
66 { "print", _print },
67 { "print/x", _printHex },
68 { "q", _quit },
69 { "quit", _quit },
70 { "rb", _readByte },
71 { "rh", _readHalfword },
72 { "rw", _readWord },
73 { "status", _printStatus },
74 { "w", _setWatchpoint },
75 { "watch", _setWatchpoint },
76 { "x", _breakInto },
77 { 0, 0 }
78};
79
80static inline void _printPSR(union PSR psr) {
81 printf("%08X [%c%c%c%c%c%c%c]\n", psr.packed,
82 psr.n ? 'N' : '-',
83 psr.z ? 'Z' : '-',
84 psr.c ? 'C' : '-',
85 psr.v ? 'V' : '-',
86 psr.i ? 'I' : '-',
87 psr.f ? 'F' : '-',
88 psr.t ? 'T' : '-');
89}
90
91static void _handleDeath(int sig) {
92 (void)(sig);
93 printf("No debugger attached!\n");
94}
95
96static void _breakInto(struct CLIDebugger* debugger, struct DebugVector* dv) {
97 (void)(debugger);
98 (void)(dv);
99 struct sigaction sa, osa;
100 sa.sa_handler = _handleDeath;
101 sigemptyset(&sa.sa_mask);
102 sigaddset(&sa.sa_mask, SIGTRAP);
103 sa.sa_flags = SA_RESTART;
104 sigaction(SIGTRAP, &sa, &osa);
105#ifdef USE_PTHREADS
106 pthread_kill(pthread_self(), SIGTRAP);
107#else
108 kill(getpid(), SIGTRAP);
109#endif
110 sigaction(SIGTRAP, &osa, 0);
111}
112
113static void _continue(struct CLIDebugger* debugger, struct DebugVector* dv) {
114 (void)(dv);
115 debugger->d.state = DEBUGGER_RUNNING;
116}
117
118static void _next(struct CLIDebugger* debugger, struct DebugVector* dv) {
119 (void)(dv);
120 ARMRun(debugger->d.cpu);
121 _printStatus(debugger, 0);
122}
123
124static void _print(struct CLIDebugger* debugger, struct DebugVector* dv) {
125 (void)(debugger);
126 for ( ; dv; dv = dv->next) {
127 printf(" %u", dv->intValue);
128 }
129 printf("\n");
130}
131
132static void _printHex(struct CLIDebugger* debugger, struct DebugVector* dv) {
133 (void)(debugger);
134 for ( ; dv; dv = dv->next) {
135 printf(" 0x%08X", dv->intValue);
136 }
137 printf("\n");
138}
139
140static inline void _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode) {
141 char disassembly[48];
142 struct ARMInstructionInfo info;
143 if (mode == MODE_ARM) {
144 uint32_t instruction = debugger->d.cpu->memory->load32(debugger->d.cpu->memory, address, 0);
145 ARMDecodeARM(instruction, &info);
146 ARMDisassemble(&info, debugger->d.cpu->gprs[ARM_PC] + WORD_SIZE_ARM, disassembly, sizeof(disassembly));
147 printf("%08X: %s\n", instruction, disassembly);
148 } else {
149 uint16_t instruction = debugger->d.cpu->memory->loadU16(debugger->d.cpu->memory, address, 0);
150 ARMDecodeThumb(instruction, &info);
151 ARMDisassemble(&info, debugger->d.cpu->gprs[ARM_PC] + WORD_SIZE_THUMB, disassembly, sizeof(disassembly));
152 printf("%04X: %s\n", instruction, disassembly);
153 }
154}
155
156static void _printStatus(struct CLIDebugger* debugger, struct DebugVector* dv) {
157 (void)(dv);
158 int r;
159 for (r = 0; r < 4; ++r) {
160 printf("%08X %08X %08X %08X\n",
161 debugger->d.cpu->gprs[r << 2],
162 debugger->d.cpu->gprs[(r << 2) + 1],
163 debugger->d.cpu->gprs[(r << 2) + 2],
164 debugger->d.cpu->gprs[(r << 2) + 3]);
165 }
166 _printPSR(debugger->d.cpu->cpsr);
167 int instructionLength;
168 enum ExecutionMode mode = debugger->d.cpu->cpsr.t;
169 if (mode == MODE_ARM) {
170 instructionLength = WORD_SIZE_ARM;
171 } else {
172 instructionLength = WORD_SIZE_THUMB;
173 }
174 _printLine(debugger, debugger->d.cpu->gprs[ARM_PC] - instructionLength, mode);
175}
176
177static void _quit(struct CLIDebugger* debugger, struct DebugVector* dv) {
178 (void)(dv);
179 debugger->d.state = DEBUGGER_SHUTDOWN;
180}
181
182static void _readByte(struct CLIDebugger* debugger, struct DebugVector* dv) {
183 if (!dv || dv->type != INT_TYPE) {
184 printf("%s\n", ERROR_MISSING_ARGS);
185 return;
186 }
187 uint32_t address = dv->intValue;
188 uint8_t value = debugger->d.cpu->memory->loadU8(debugger->d.cpu->memory, address, 0);
189 printf(" 0x%02X\n", value);
190}
191
192static void _readHalfword(struct CLIDebugger* debugger, struct DebugVector* dv) {
193 if (!dv || dv->type != INT_TYPE) {
194 printf("%s\n", ERROR_MISSING_ARGS);
195 return;
196 }
197 uint32_t address = dv->intValue;
198 uint16_t value = debugger->d.cpu->memory->loadU16(debugger->d.cpu->memory, address, 0);
199 printf(" 0x%04X\n", value);
200}
201
202static void _readWord(struct CLIDebugger* debugger, struct DebugVector* dv) {
203 if (!dv || dv->type != INT_TYPE) {
204 printf("%s\n", ERROR_MISSING_ARGS);
205 return;
206 }
207 uint32_t address = dv->intValue;
208 uint32_t value = debugger->d.cpu->memory->load32(debugger->d.cpu->memory, address, 0);
209 printf(" 0x%08X\n", value);
210}
211
212static void _setBreakpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
213 if (!dv || dv->type != INT_TYPE) {
214 printf("%s\n", ERROR_MISSING_ARGS);
215 return;
216 }
217 uint32_t address = dv->intValue;
218 ARMDebuggerSetBreakpoint(&debugger->d, address);
219}
220
221static void _clearBreakpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
222 if (!dv || dv->type != INT_TYPE) {
223 printf("%s\n", ERROR_MISSING_ARGS);
224 return;
225 }
226 uint32_t address = dv->intValue;
227 ARMDebuggerClearBreakpoint(&debugger->d, address);
228}
229
230static void _setWatchpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
231 if (!dv || dv->type != INT_TYPE) {
232 printf("%s\n", ERROR_MISSING_ARGS);
233 return;
234 }
235 uint32_t address = dv->intValue;
236 ARMDebuggerSetWatchpoint(&debugger->d, address);
237}
238
239static void _breakIntoDefault(int signal) {
240 (void)(signal);
241 ARMDebuggerEnter(&_activeDebugger->d, DEBUGGER_ENTER_MANUAL);
242}
243
244enum _DVParseState {
245 PARSE_ERROR = -1,
246 PARSE_ROOT = 0,
247 PARSE_EXPECT_REGISTER,
248 PARSE_EXPECT_REGISTER_2,
249 PARSE_EXPECT_LR,
250 PARSE_EXPECT_PC,
251 PARSE_EXPECT_SP,
252 PARSE_EXPECT_DECIMAL,
253 PARSE_EXPECT_HEX,
254 PARSE_EXPECT_PREFIX,
255 PARSE_EXPECT_SUFFIX,
256};
257
258static struct DebugVector* _DVParse(struct CLIDebugger* debugger, const char* string, size_t length) {
259 if (!string || length < 1) {
260 return 0;
261 }
262
263 enum _DVParseState state = PARSE_ROOT;
264 struct DebugVector dvTemp = { .type = INT_TYPE };
265 uint32_t current = 0;
266
267 while (length > 0 && string[0] && string[0] != ' ' && state != PARSE_ERROR) {
268 char token = string[0];
269 ++string;
270 --length;
271 switch (state) {
272 case PARSE_ROOT:
273 switch (token) {
274 case 'r':
275 state = PARSE_EXPECT_REGISTER;
276 break;
277 case 'p':
278 state = PARSE_EXPECT_PC;
279 break;
280 case 's':
281 state = PARSE_EXPECT_SP;
282 break;
283 case 'l':
284 state = PARSE_EXPECT_LR;
285 break;
286 case '1':
287 case '2':
288 case '3':
289 case '4':
290 case '5':
291 case '6':
292 case '7':
293 case '8':
294 case '9':
295 state = PARSE_EXPECT_DECIMAL;
296 current = token - '0';
297 break;
298 case '0':
299 state = PARSE_EXPECT_PREFIX;
300 break;
301 case '$':
302 state = PARSE_EXPECT_HEX;
303 current = 0;
304 break;
305 default:
306 state = PARSE_ERROR;
307 break;
308 };
309 break;
310 case PARSE_EXPECT_LR:
311 switch (token) {
312 case 'r':
313 current = debugger->d.cpu->gprs[ARM_LR];
314 state = PARSE_EXPECT_SUFFIX;
315 break;
316 default:
317 state = PARSE_ERROR;
318 break;
319 }
320 break;
321 case PARSE_EXPECT_PC:
322 switch (token) {
323 case 'c':
324 current = debugger->d.cpu->gprs[ARM_PC];
325 state = PARSE_EXPECT_SUFFIX;
326 break;
327 default:
328 state = PARSE_ERROR;
329 break;
330 }
331 break;
332 case PARSE_EXPECT_SP:
333 switch (token) {
334 case 'p':
335 current = debugger->d.cpu->gprs[ARM_SP];
336 state = PARSE_EXPECT_SUFFIX;
337 break;
338 default:
339 state = PARSE_ERROR;
340 break;
341 }
342 break;
343 case PARSE_EXPECT_REGISTER:
344 switch (token) {
345 case '0':
346 case '2':
347 case '3':
348 case '4':
349 case '5':
350 case '6':
351 case '7':
352 case '8':
353 case '9':
354 current = debugger->d.cpu->gprs[token - '0'];
355 state = PARSE_EXPECT_SUFFIX;
356 break;
357 case '1':
358 state = PARSE_EXPECT_REGISTER_2;
359 break;
360 default:
361 state = PARSE_ERROR;
362 break;
363 }
364 break;
365 case PARSE_EXPECT_REGISTER_2:
366 switch (token) {
367 case '0':
368 case '1':
369 case '2':
370 case '3':
371 case '4':
372 case '5':
373 current = debugger->d.cpu->gprs[token - '0' + 10];
374 state = PARSE_EXPECT_SUFFIX;
375 break;
376 default:
377 state = PARSE_ERROR;
378 break;
379 }
380 break;
381 case PARSE_EXPECT_DECIMAL:
382 switch (token) {
383 case '0':
384 case '1':
385 case '2':
386 case '3':
387 case '4':
388 case '5':
389 case '6':
390 case '7':
391 case '8':
392 case '9':
393 // TODO: handle overflow
394 current *= 10;
395 current += token - '0';
396 break;
397 default:
398 state = PARSE_ERROR;
399 }
400 break;
401 case PARSE_EXPECT_HEX:
402 switch (token) {
403 case '0':
404 case '1':
405 case '2':
406 case '3':
407 case '4':
408 case '5':
409 case '6':
410 case '7':
411 case '8':
412 case '9':
413 // TODO: handle overflow
414 current *= 16;
415 current += token - '0';
416 break;
417 case 'A':
418 case 'B':
419 case 'C':
420 case 'D':
421 case 'E':
422 case 'F':
423 // TODO: handle overflow
424 current *= 16;
425 current += token - 'A' + 10;
426 break;
427 case 'a':
428 case 'b':
429 case 'c':
430 case 'd':
431 case 'e':
432 case 'f':
433 // TODO: handle overflow
434 current *= 16;
435 current += token - 'a' + 10;
436 break;
437 default:
438 state = PARSE_ERROR;
439 break;
440 }
441 break;
442 case PARSE_EXPECT_PREFIX:
443 switch (token) {
444 case 'X':
445 case 'x':
446 current = 0;
447 state = PARSE_EXPECT_HEX;
448 break;
449 default:
450 state = PARSE_ERROR;
451 break;
452 }
453 break;
454 case PARSE_EXPECT_SUFFIX:
455 // TODO
456 state = PARSE_ERROR;
457 break;
458 case PARSE_ERROR:
459 // This shouldn't be reached
460 break;
461 }
462 }
463
464 struct DebugVector* dv = malloc(sizeof(struct DebugVector));
465 if (state == PARSE_ERROR) {
466 dv->type = ERROR_TYPE;
467 dv->next = 0;
468 } else {
469 dvTemp.intValue = current;
470 *dv = dvTemp;
471 if (string[0] == ' ') {
472 dv->next = _DVParse(debugger, string + 1, length - 1);
473 }
474 }
475 return dv;
476}
477
478static void _DVFree(struct DebugVector* dv) {
479 struct DebugVector* next;
480 while (dv) {
481 next = dv->next;
482 free(dv);
483 dv = next;
484 }
485}
486
487static int _parse(struct CLIDebugger* debugger, const char* line, size_t count) {
488 const char* firstSpace = strchr(line, ' ');
489 size_t cmdLength;
490 struct DebugVector* dv = 0;
491 if (firstSpace) {
492 cmdLength = firstSpace - line;
493 dv = _DVParse(debugger, firstSpace + 1, count - cmdLength - 1);
494 if (dv && dv->type == ERROR_TYPE) {
495 printf("Parse error\n");
496 _DVFree(dv);
497 return 0;
498 }
499 } else {
500 cmdLength = count;
501 }
502
503 int i;
504 const char* name;
505 for (i = 0; (name = _debuggerCommands[i].name); ++i) {
506 if (strlen(name) != cmdLength) {
507 continue;
508 }
509 if (strncasecmp(name, line, cmdLength) == 0) {
510 _debuggerCommands[i].command(debugger, dv);
511 _DVFree(dv);
512 return 1;
513 }
514 }
515 _DVFree(dv);
516 printf("Command not found\n");
517 return 0;
518}
519
520static char* _prompt(EditLine* el) {
521 (void)(el);
522 return "> ";
523}
524
525static void _commandLine(struct ARMDebugger* debugger) {
526 struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
527 const char* line;
528 _printStatus(cliDebugger, 0);
529 int count = 0;
530 HistEvent ev;
531 while (debugger->state == DEBUGGER_PAUSED) {
532 line = el_gets(cliDebugger->elstate, &count);
533 if (!line) {
534 debugger->state = DEBUGGER_EXITING;
535 return;
536 }
537 if (line[0] == '\n') {
538 if (history(cliDebugger->histate, &ev, H_FIRST) >= 0) {
539 _parse(cliDebugger, ev.str, strlen(ev.str) - 1);
540 }
541 } else {
542 if (_parse(cliDebugger, line, count - 1)) {
543 history(cliDebugger->histate, &ev, H_ENTER, line);
544 }
545 }
546 }
547}
548
549static void _reportEntry(struct ARMDebugger* debugger, enum DebuggerEntryReason reason) {
550 (void) (debugger);
551 switch (reason) {
552 case DEBUGGER_ENTER_MANUAL:
553 case DEBUGGER_ENTER_ATTACHED:
554 break;
555 case DEBUGGER_ENTER_BREAKPOINT:
556 printf("Hit breakpoint\n");
557 break;
558 case DEBUGGER_ENTER_WATCHPOINT:
559 printf("Hit watchpoint\n");
560 break;
561 case DEBUGGER_ENTER_ILLEGAL_OP:
562 printf("Hit illegal opcode\n");
563 break;
564 }
565}
566
567static unsigned char _tabComplete(EditLine* elstate, int ch) {
568 (void)(ch);
569 const LineInfo* li = el_line(elstate);
570 const char* commandPtr;
571 int cmd = 0, len = 0;
572 const char* name = 0;
573 for (commandPtr = li->buffer; commandPtr <= li->cursor; ++commandPtr, ++len) {
574 for (; (name = _debuggerCommands[cmd].name); ++cmd) {
575 int cmp = strncasecmp(name, li->buffer, len);
576 if (cmp > 0) {
577 return CC_ERROR;
578 }
579 if (cmp == 0) {
580 break;
581 }
582 }
583 }
584 if (_debuggerCommands[cmd + 1].name && strncasecmp(_debuggerCommands[cmd + 1].name, li->buffer, len - 1) == 0) {
585 return CC_ERROR;
586 }
587 name += len - 1;
588 el_insertstr(elstate, name);
589 el_insertstr(elstate, " ");
590 return CC_REDISPLAY;
591}
592
593static void _cliDebuggerInit(struct ARMDebugger* debugger) {
594 struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
595 // TODO: get argv[0]
596 cliDebugger->elstate = el_init("gbac", stdin, stdout, stderr);
597 el_set(cliDebugger->elstate, EL_PROMPT, _prompt);
598 el_set(cliDebugger->elstate, EL_EDITOR, "emacs");
599
600 el_set(cliDebugger->elstate, EL_CLIENTDATA, cliDebugger);
601 el_set(cliDebugger->elstate, EL_ADDFN, "tab-complete", "Tab completion", _tabComplete);
602 el_set(cliDebugger->elstate, EL_BIND, "\t", "tab-complete", 0);
603 cliDebugger->histate = history_init();
604 HistEvent ev;
605 history(cliDebugger->histate, &ev, H_SETSIZE, 200);
606 el_set(cliDebugger->elstate, EL_HIST, history, cliDebugger->histate);
607 _activeDebugger = cliDebugger;
608 signal(SIGINT, _breakIntoDefault);
609}
610
611static void _cliDebuggerDeinit(struct ARMDebugger* debugger) {
612 struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
613 history_end(cliDebugger->histate);
614 el_end(cliDebugger->elstate);
615}
616
617void CLIDebuggerCreate(struct CLIDebugger* debugger) {
618 debugger->d.init = _cliDebuggerInit;
619 debugger->d.deinit = _cliDebuggerDeinit;
620 debugger->d.paused = _commandLine;
621 debugger->d.entered = _reportEntry;
622}