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