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[32];
142 if (mode == MODE_ARM) {
143 uint32_t instruction = debugger->d.cpu->memory->load32(debugger->d.cpu->memory, address, 0);
144 printf("%08X\n", instruction);
145 } else {
146 uint16_t instruction = debugger->d.cpu->memory->loadU16(debugger->d.cpu->memory, address, 0);
147 ARMDisassembleThumb(instruction, debugger->d.cpu->gprs[ARM_PC] + WORD_SIZE_THUMB, disassembly, sizeof(disassembly));
148 printf("%04X: %s\n", instruction, disassembly);
149 }
150}
151
152static void _printStatus(struct CLIDebugger* debugger, struct DebugVector* dv) {
153 (void)(dv);
154 int r;
155 for (r = 0; r < 4; ++r) {
156 printf("%08X %08X %08X %08X\n",
157 debugger->d.cpu->gprs[r << 2],
158 debugger->d.cpu->gprs[(r << 2) + 1],
159 debugger->d.cpu->gprs[(r << 2) + 2],
160 debugger->d.cpu->gprs[(r << 2) + 3]);
161 }
162 _printPSR(debugger->d.cpu->cpsr);
163 int instructionLength;
164 enum ExecutionMode mode = debugger->d.cpu->cpsr.t;
165 if (mode == MODE_ARM) {
166 instructionLength = WORD_SIZE_ARM;
167 } else {
168 instructionLength = WORD_SIZE_THUMB;
169 }
170 _printLine(debugger, debugger->d.cpu->gprs[ARM_PC] - instructionLength, mode);
171}
172
173static void _quit(struct CLIDebugger* debugger, struct DebugVector* dv) {
174 (void)(dv);
175 debugger->d.state = DEBUGGER_SHUTDOWN;
176}
177
178static void _readByte(struct CLIDebugger* debugger, struct DebugVector* dv) {
179 if (!dv || dv->type != INT_TYPE) {
180 printf("%s\n", ERROR_MISSING_ARGS);
181 return;
182 }
183 uint32_t address = dv->intValue;
184 uint8_t value = debugger->d.cpu->memory->loadU8(debugger->d.cpu->memory, address, 0);
185 printf(" 0x%02X\n", value);
186}
187
188static void _readHalfword(struct CLIDebugger* debugger, struct DebugVector* dv) {
189 if (!dv || dv->type != INT_TYPE) {
190 printf("%s\n", ERROR_MISSING_ARGS);
191 return;
192 }
193 uint32_t address = dv->intValue;
194 uint16_t value = debugger->d.cpu->memory->loadU16(debugger->d.cpu->memory, address, 0);
195 printf(" 0x%04X\n", value);
196}
197
198static void _readWord(struct CLIDebugger* debugger, struct DebugVector* dv) {
199 if (!dv || dv->type != INT_TYPE) {
200 printf("%s\n", ERROR_MISSING_ARGS);
201 return;
202 }
203 uint32_t address = dv->intValue;
204 uint32_t value = debugger->d.cpu->memory->load32(debugger->d.cpu->memory, address, 0);
205 printf(" 0x%08X\n", value);
206}
207
208static void _setBreakpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
209 if (!dv || dv->type != INT_TYPE) {
210 printf("%s\n", ERROR_MISSING_ARGS);
211 return;
212 }
213 uint32_t address = dv->intValue;
214 ARMDebuggerSetBreakpoint(&debugger->d, address);
215}
216
217static void _clearBreakpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
218 if (!dv || dv->type != INT_TYPE) {
219 printf("%s\n", ERROR_MISSING_ARGS);
220 return;
221 }
222 uint32_t address = dv->intValue;
223 ARMDebuggerClearBreakpoint(&debugger->d, address);
224}
225
226static void _setWatchpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
227 if (!dv || dv->type != INT_TYPE) {
228 printf("%s\n", ERROR_MISSING_ARGS);
229 return;
230 }
231 uint32_t address = dv->intValue;
232 ARMDebuggerSetWatchpoint(&debugger->d, address);
233}
234
235static void _breakIntoDefault(int signal) {
236 (void)(signal);
237 ARMDebuggerEnter(&_activeDebugger->d, DEBUGGER_ENTER_MANUAL);
238}
239
240enum _DVParseState {
241 PARSE_ERROR = -1,
242 PARSE_ROOT = 0,
243 PARSE_EXPECT_REGISTER,
244 PARSE_EXPECT_REGISTER_2,
245 PARSE_EXPECT_LR,
246 PARSE_EXPECT_PC,
247 PARSE_EXPECT_SP,
248 PARSE_EXPECT_DECIMAL,
249 PARSE_EXPECT_HEX,
250 PARSE_EXPECT_PREFIX,
251 PARSE_EXPECT_SUFFIX,
252};
253
254static struct DebugVector* _DVParse(struct CLIDebugger* debugger, const char* string, size_t length) {
255 if (!string || length < 1) {
256 return 0;
257 }
258
259 enum _DVParseState state = PARSE_ROOT;
260 struct DebugVector dvTemp = { .type = INT_TYPE };
261 uint32_t current = 0;
262
263 while (length > 0 && string[0] && string[0] != ' ' && state != PARSE_ERROR) {
264 char token = string[0];
265 ++string;
266 --length;
267 switch (state) {
268 case PARSE_ROOT:
269 switch (token) {
270 case 'r':
271 state = PARSE_EXPECT_REGISTER;
272 break;
273 case 'p':
274 state = PARSE_EXPECT_PC;
275 break;
276 case 's':
277 state = PARSE_EXPECT_SP;
278 break;
279 case 'l':
280 state = PARSE_EXPECT_LR;
281 break;
282 case '1':
283 case '2':
284 case '3':
285 case '4':
286 case '5':
287 case '6':
288 case '7':
289 case '8':
290 case '9':
291 state = PARSE_EXPECT_DECIMAL;
292 current = token - '0';
293 break;
294 case '0':
295 state = PARSE_EXPECT_PREFIX;
296 break;
297 case '$':
298 state = PARSE_EXPECT_HEX;
299 current = 0;
300 break;
301 default:
302 state = PARSE_ERROR;
303 break;
304 };
305 break;
306 case PARSE_EXPECT_LR:
307 switch (token) {
308 case 'r':
309 current = debugger->d.cpu->gprs[ARM_LR];
310 state = PARSE_EXPECT_SUFFIX;
311 break;
312 default:
313 state = PARSE_ERROR;
314 break;
315 }
316 break;
317 case PARSE_EXPECT_PC:
318 switch (token) {
319 case 'c':
320 current = debugger->d.cpu->gprs[ARM_PC];
321 state = PARSE_EXPECT_SUFFIX;
322 break;
323 default:
324 state = PARSE_ERROR;
325 break;
326 }
327 break;
328 case PARSE_EXPECT_SP:
329 switch (token) {
330 case 'p':
331 current = debugger->d.cpu->gprs[ARM_SP];
332 state = PARSE_EXPECT_SUFFIX;
333 break;
334 default:
335 state = PARSE_ERROR;
336 break;
337 }
338 break;
339 case PARSE_EXPECT_REGISTER:
340 switch (token) {
341 case '0':
342 case '2':
343 case '3':
344 case '4':
345 case '5':
346 case '6':
347 case '7':
348 case '8':
349 case '9':
350 current = debugger->d.cpu->gprs[token - '0'];
351 state = PARSE_EXPECT_SUFFIX;
352 break;
353 case '1':
354 state = PARSE_EXPECT_REGISTER_2;
355 break;
356 default:
357 state = PARSE_ERROR;
358 break;
359 }
360 break;
361 case PARSE_EXPECT_REGISTER_2:
362 switch (token) {
363 case '0':
364 case '1':
365 case '2':
366 case '3':
367 case '4':
368 case '5':
369 current = debugger->d.cpu->gprs[token - '0' + 10];
370 state = PARSE_EXPECT_SUFFIX;
371 break;
372 default:
373 state = PARSE_ERROR;
374 break;
375 }
376 break;
377 case PARSE_EXPECT_DECIMAL:
378 switch (token) {
379 case '0':
380 case '1':
381 case '2':
382 case '3':
383 case '4':
384 case '5':
385 case '6':
386 case '7':
387 case '8':
388 case '9':
389 // TODO: handle overflow
390 current *= 10;
391 current += token - '0';
392 break;
393 default:
394 state = PARSE_ERROR;
395 }
396 break;
397 case PARSE_EXPECT_HEX:
398 switch (token) {
399 case '0':
400 case '1':
401 case '2':
402 case '3':
403 case '4':
404 case '5':
405 case '6':
406 case '7':
407 case '8':
408 case '9':
409 // TODO: handle overflow
410 current *= 16;
411 current += token - '0';
412 break;
413 case 'A':
414 case 'B':
415 case 'C':
416 case 'D':
417 case 'E':
418 case 'F':
419 // TODO: handle overflow
420 current *= 16;
421 current += token - 'A' + 10;
422 break;
423 case 'a':
424 case 'b':
425 case 'c':
426 case 'd':
427 case 'e':
428 case 'f':
429 // TODO: handle overflow
430 current *= 16;
431 current += token - 'a' + 10;
432 break;
433 default:
434 state = PARSE_ERROR;
435 break;
436 }
437 break;
438 case PARSE_EXPECT_PREFIX:
439 switch (token) {
440 case 'X':
441 case 'x':
442 current = 0;
443 state = PARSE_EXPECT_HEX;
444 break;
445 default:
446 state = PARSE_ERROR;
447 break;
448 }
449 break;
450 case PARSE_EXPECT_SUFFIX:
451 // TODO
452 state = PARSE_ERROR;
453 break;
454 case PARSE_ERROR:
455 // This shouldn't be reached
456 break;
457 }
458 }
459
460 struct DebugVector* dv = malloc(sizeof(struct DebugVector));
461 if (state == PARSE_ERROR) {
462 dv->type = ERROR_TYPE;
463 dv->next = 0;
464 } else {
465 dvTemp.intValue = current;
466 *dv = dvTemp;
467 if (string[0] == ' ') {
468 dv->next = _DVParse(debugger, string + 1, length - 1);
469 }
470 }
471 return dv;
472}
473
474static void _DVFree(struct DebugVector* dv) {
475 struct DebugVector* next;
476 while (dv) {
477 next = dv->next;
478 free(dv);
479 dv = next;
480 }
481}
482
483static int _parse(struct CLIDebugger* debugger, const char* line, size_t count) {
484 const char* firstSpace = strchr(line, ' ');
485 size_t cmdLength;
486 struct DebugVector* dv = 0;
487 if (firstSpace) {
488 cmdLength = firstSpace - line;
489 dv = _DVParse(debugger, firstSpace + 1, count - cmdLength - 1);
490 if (dv && dv->type == ERROR_TYPE) {
491 printf("Parse error\n");
492 _DVFree(dv);
493 return 0;
494 }
495 } else {
496 cmdLength = count;
497 }
498
499 int i;
500 const char* name;
501 for (i = 0; (name = _debuggerCommands[i].name); ++i) {
502 if (strlen(name) != cmdLength) {
503 continue;
504 }
505 if (strncasecmp(name, line, cmdLength) == 0) {
506 _debuggerCommands[i].command(debugger, dv);
507 _DVFree(dv);
508 return 1;
509 }
510 }
511 _DVFree(dv);
512 printf("Command not found\n");
513 return 0;
514}
515
516static char* _prompt(EditLine* el) {
517 (void)(el);
518 return "> ";
519}
520
521static void _commandLine(struct ARMDebugger* debugger) {
522 struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
523 const char* line;
524 _printStatus(cliDebugger, 0);
525 int count = 0;
526 HistEvent ev;
527 while (debugger->state == DEBUGGER_PAUSED) {
528 line = el_gets(cliDebugger->elstate, &count);
529 if (!line) {
530 debugger->state = DEBUGGER_EXITING;
531 return;
532 }
533 if (line[0] == '\n') {
534 if (history(cliDebugger->histate, &ev, H_FIRST) >= 0) {
535 _parse(cliDebugger, ev.str, strlen(ev.str) - 1);
536 }
537 } else {
538 if (_parse(cliDebugger, line, count - 1)) {
539 history(cliDebugger->histate, &ev, H_ENTER, line);
540 }
541 }
542 }
543}
544
545static void _reportEntry(struct ARMDebugger* debugger, enum DebuggerEntryReason reason) {
546 (void) (debugger);
547 switch (reason) {
548 case DEBUGGER_ENTER_MANUAL:
549 case DEBUGGER_ENTER_ATTACHED:
550 break;
551 case DEBUGGER_ENTER_BREAKPOINT:
552 printf("Hit breakpoint\n");
553 break;
554 case DEBUGGER_ENTER_WATCHPOINT:
555 printf("Hit watchpoint\n");
556 break;
557 case DEBUGGER_ENTER_ILLEGAL_OP:
558 printf("Hit illegal opcode\n");
559 break;
560 }
561}
562
563static unsigned char _tabComplete(EditLine* elstate, int ch) {
564 (void)(ch);
565 const LineInfo* li = el_line(elstate);
566 const char* commandPtr;
567 int cmd = 0, len = 0;
568 const char* name = 0;
569 for (commandPtr = li->buffer; commandPtr <= li->cursor; ++commandPtr, ++len) {
570 for (; (name = _debuggerCommands[cmd].name); ++cmd) {
571 int cmp = strncasecmp(name, li->buffer, len);
572 if (cmp > 0) {
573 return CC_ERROR;
574 }
575 if (cmp == 0) {
576 break;
577 }
578 }
579 }
580 if (_debuggerCommands[cmd + 1].name && strncasecmp(_debuggerCommands[cmd + 1].name, li->buffer, len - 1) == 0) {
581 return CC_ERROR;
582 }
583 name += len - 1;
584 el_insertstr(elstate, name);
585 el_insertstr(elstate, " ");
586 return CC_REDISPLAY;
587}
588
589static void _cliDebuggerInit(struct ARMDebugger* debugger) {
590 struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
591 // TODO: get argv[0]
592 cliDebugger->elstate = el_init("gbac", stdin, stdout, stderr);
593 el_set(cliDebugger->elstate, EL_PROMPT, _prompt);
594 el_set(cliDebugger->elstate, EL_EDITOR, "emacs");
595
596 el_set(cliDebugger->elstate, EL_CLIENTDATA, cliDebugger);
597 el_set(cliDebugger->elstate, EL_ADDFN, "tab-complete", "Tab completion", _tabComplete);
598 el_set(cliDebugger->elstate, EL_BIND, "\t", "tab-complete", 0);
599 cliDebugger->histate = history_init();
600 HistEvent ev;
601 history(cliDebugger->histate, &ev, H_SETSIZE, 200);
602 el_set(cliDebugger->elstate, EL_HIST, history, cliDebugger->histate);
603 _activeDebugger = cliDebugger;
604 signal(SIGINT, _breakIntoDefault);
605}
606
607static void _cliDebuggerDeinit(struct ARMDebugger* debugger) {
608 struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
609 history_end(cliDebugger->histate);
610 el_end(cliDebugger->elstate);
611}
612
613void CLIDebuggerCreate(struct CLIDebugger* debugger) {
614 debugger->d.init = _cliDebuggerInit;
615 debugger->d.deinit = _cliDebuggerDeinit;
616 debugger->d.paused = _commandLine;
617 debugger->d.entered = _reportEntry;
618}