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