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