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