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