src/debugger/cli-debugger.c (view raw)
1#include "cli-debugger.h"
2#include "decoder.h"
3
4#include <signal.h>
5
6#ifdef USE_PTHREADS
7#include <pthread.h>
8#endif
9
10struct DebugVector {
11 struct DebugVector* next;
12 enum DVType {
13 ERROR_TYPE,
14 INT_TYPE,
15 CHAR_TYPE
16 } type;
17 union {
18 int32_t intValue;
19 const char* charValue;
20 };
21};
22
23static const char* ERROR_MISSING_ARGS = "Arguments missing";
24
25static struct CLIDebugger* _activeDebugger;
26
27typedef void (DebuggerCommand)(struct CLIDebugger*, struct DebugVector*);
28
29static void _breakInto(struct CLIDebugger*, struct DebugVector*);
30static void _continue(struct CLIDebugger*, struct DebugVector*);
31static void _disassemble(struct CLIDebugger*, struct DebugVector*);
32static void _next(struct CLIDebugger*, struct DebugVector*);
33static void _print(struct CLIDebugger*, struct DebugVector*);
34static void _printHex(struct CLIDebugger*, struct DebugVector*);
35static void _printStatus(struct CLIDebugger*, struct DebugVector*);
36static void _quit(struct CLIDebugger*, struct DebugVector*);
37static void _readByte(struct CLIDebugger*, struct DebugVector*);
38static void _readHalfword(struct CLIDebugger*, struct DebugVector*);
39static void _readWord(struct CLIDebugger*, struct DebugVector*);
40static void _setBreakpoint(struct CLIDebugger*, struct DebugVector*);
41static void _clearBreakpoint(struct CLIDebugger*, struct DebugVector*);
42static void _setWatchpoint(struct CLIDebugger*, struct DebugVector*);
43
44static void _breakIntoDefault(int signal);
45static void _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode);
46
47static struct {
48 const char* name;
49 DebuggerCommand* command;
50} _debuggerCommands[] = {
51 { "b", _setBreakpoint },
52 { "break", _setBreakpoint },
53 { "c", _continue },
54 { "continue", _continue },
55 { "d", _clearBreakpoint },
56 { "delete", _clearBreakpoint },
57 { "dis", _disassemble },
58 { "disasm", _disassemble },
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 UNUSED(sig);
92 printf("No debugger attached!\n");
93}
94
95static void _breakInto(struct CLIDebugger* debugger, struct DebugVector* dv) {
96 UNUSED(debugger);
97 UNUSED(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 UNUSED(dv);
114 debugger->d.state = DEBUGGER_RUNNING;
115}
116
117static void _next(struct CLIDebugger* debugger, struct DebugVector* dv) {
118 UNUSED(dv);
119 ARMRun(debugger->d.cpu);
120 _printStatus(debugger, 0);
121}
122
123static void _disassemble(struct CLIDebugger* debugger, struct DebugVector* dv) {
124 uint32_t address;
125 int size;
126 int wordSize;
127 enum ExecutionMode mode = debugger->d.cpu->executionMode;
128
129 if (mode == MODE_ARM) {
130 wordSize = WORD_SIZE_ARM;
131 } else {
132 wordSize = WORD_SIZE_THUMB;
133 }
134
135 if (!dv || dv->type != INT_TYPE) {
136 address = debugger->d.cpu->gprs[ARM_PC] - wordSize;
137 } else {
138 address = dv->intValue;
139 dv = dv->next;
140 }
141
142 if (!dv || dv->type != INT_TYPE) {
143 size = 1;
144 } else {
145 size = dv->intValue;
146 dv = dv->next; // TODO: Check for excess args
147 }
148
149 int i;
150 for (i = 0; i < size; ++i) {
151 _printLine(debugger, address, mode);
152 address += wordSize;
153 }
154}
155
156static void _print(struct CLIDebugger* debugger, struct DebugVector* dv) {
157 UNUSED(debugger);
158 for ( ; dv; dv = dv->next) {
159 printf(" %u", dv->intValue);
160 }
161 printf("\n");
162}
163
164static void _printHex(struct CLIDebugger* debugger, struct DebugVector* dv) {
165 UNUSED(debugger);
166 for ( ; dv; dv = dv->next) {
167 printf(" 0x%08X", dv->intValue);
168 }
169 printf("\n");
170}
171
172static inline void _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode) {
173 char disassembly[48];
174 struct ARMInstructionInfo info;
175 if (mode == MODE_ARM) {
176 uint32_t instruction = debugger->d.cpu->memory.load32(debugger->d.cpu, address, 0);
177 ARMDecodeARM(instruction, &info);
178 ARMDisassemble(&info, address + WORD_SIZE_ARM * 2, disassembly, sizeof(disassembly));
179 printf("%08X: %s\n", instruction, disassembly);
180 } else {
181 uint16_t instruction = debugger->d.cpu->memory.loadU16(debugger->d.cpu, address, 0);
182 ARMDecodeThumb(instruction, &info);
183 ARMDisassemble(&info, address + WORD_SIZE_THUMB * 2, disassembly, sizeof(disassembly));
184 printf("%04X: %s\n", instruction, disassembly);
185 }
186}
187
188static void _printStatus(struct CLIDebugger* debugger, struct DebugVector* dv) {
189 UNUSED(dv);
190 int r;
191 for (r = 0; r < 4; ++r) {
192 printf("%08X %08X %08X %08X\n",
193 debugger->d.cpu->gprs[r << 2],
194 debugger->d.cpu->gprs[(r << 2) + 1],
195 debugger->d.cpu->gprs[(r << 2) + 2],
196 debugger->d.cpu->gprs[(r << 2) + 3]);
197 }
198 _printPSR(debugger->d.cpu->cpsr);
199 int instructionLength;
200 enum ExecutionMode mode = debugger->d.cpu->cpsr.t;
201 if (mode == MODE_ARM) {
202 instructionLength = WORD_SIZE_ARM;
203 } else {
204 instructionLength = WORD_SIZE_THUMB;
205 }
206 _printLine(debugger, debugger->d.cpu->gprs[ARM_PC] - instructionLength, mode);
207}
208
209static void _quit(struct CLIDebugger* debugger, struct DebugVector* dv) {
210 UNUSED(dv);
211 debugger->d.state = DEBUGGER_SHUTDOWN;
212}
213
214static void _readByte(struct CLIDebugger* debugger, struct DebugVector* dv) {
215 if (!dv || dv->type != INT_TYPE) {
216 printf("%s\n", ERROR_MISSING_ARGS);
217 return;
218 }
219 uint32_t address = dv->intValue;
220 uint8_t value = debugger->d.cpu->memory.loadU8(debugger->d.cpu, address, 0);
221 printf(" 0x%02X\n", value);
222}
223
224static void _readHalfword(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 uint16_t value = debugger->d.cpu->memory.loadU16(debugger->d.cpu, address, 0);
231 printf(" 0x%04X\n", value);
232}
233
234static void _readWord(struct CLIDebugger* debugger, struct DebugVector* dv) {
235 if (!dv || dv->type != INT_TYPE) {
236 printf("%s\n", ERROR_MISSING_ARGS);
237 return;
238 }
239 uint32_t address = dv->intValue;
240 uint32_t value = debugger->d.cpu->memory.load32(debugger->d.cpu, address, 0);
241 printf(" 0x%08X\n", value);
242}
243
244static void _setBreakpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
245 if (!dv || dv->type != INT_TYPE) {
246 printf("%s\n", ERROR_MISSING_ARGS);
247 return;
248 }
249 uint32_t address = dv->intValue;
250 ARMDebuggerSetBreakpoint(&debugger->d, address);
251}
252
253static void _clearBreakpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
254 if (!dv || dv->type != INT_TYPE) {
255 printf("%s\n", ERROR_MISSING_ARGS);
256 return;
257 }
258 uint32_t address = dv->intValue;
259 ARMDebuggerClearBreakpoint(&debugger->d, address);
260}
261
262static void _setWatchpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
263 if (!dv || dv->type != INT_TYPE) {
264 printf("%s\n", ERROR_MISSING_ARGS);
265 return;
266 }
267 uint32_t address = dv->intValue;
268 ARMDebuggerSetWatchpoint(&debugger->d, address);
269}
270
271static void _breakIntoDefault(int signal) {
272 UNUSED(signal);
273 ARMDebuggerEnter(&_activeDebugger->d, DEBUGGER_ENTER_MANUAL);
274}
275
276enum _DVParseState {
277 PARSE_ERROR = -1,
278 PARSE_ROOT = 0,
279 PARSE_EXPECT_REGISTER,
280 PARSE_EXPECT_REGISTER_2,
281 PARSE_EXPECT_LR,
282 PARSE_EXPECT_PC,
283 PARSE_EXPECT_SP,
284 PARSE_EXPECT_DECIMAL,
285 PARSE_EXPECT_HEX,
286 PARSE_EXPECT_PREFIX,
287 PARSE_EXPECT_SUFFIX,
288};
289
290enum Operation {
291 ASSIGN,
292 ADD,
293 SUBTRACT,
294 MULTIPLY,
295 DIVIDE
296};
297
298static void _performOperation(enum Operation operation, uint32_t next, struct DebugVector* dv) {
299 switch (operation) {
300 case ASSIGN:
301 dv->intValue = next;
302 break;
303 case ADD:
304 dv->intValue += next;
305 break;
306 case SUBTRACT:
307 dv->intValue -= next;
308 break;
309 case MULTIPLY:
310 dv->intValue *= next;
311 break;
312 case DIVIDE:
313 if (next != 0) {
314 dv->intValue /= next;
315 } else {
316 dv->type = ERROR_TYPE;
317 return;
318 }
319 break;
320 }
321}
322
323static size_t _parseExpression(struct CLIDebugger* debugger, const char* string, size_t length, struct DebugVector* dv) {
324 if (!string || length < 1) {
325 return 0;
326 }
327
328 uint32_t next = 0;
329 size_t adjusted = 0;
330
331 enum _DVParseState state = PARSE_ROOT;
332 enum Operation operation = ASSIGN;
333
334 while (length > 0 && string[0] && string[0] != ' ' && state != PARSE_ERROR) {
335 char token = string[0];
336 ++string;
337 ++adjusted;
338 --length;
339 switch (state) {
340 case PARSE_ROOT:
341 switch (token) {
342 case 'r':
343 state = PARSE_EXPECT_REGISTER;
344 break;
345 case 'p':
346 state = PARSE_EXPECT_PC;
347 break;
348 case 's':
349 state = PARSE_EXPECT_SP;
350 break;
351 case 'l':
352 state = PARSE_EXPECT_LR;
353 break;
354 case '1':
355 case '2':
356 case '3':
357 case '4':
358 case '5':
359 case '6':
360 case '7':
361 case '8':
362 case '9':
363 state = PARSE_EXPECT_DECIMAL;
364 next = token - '0';
365 break;
366 case '0':
367 state = PARSE_EXPECT_PREFIX;
368 break;
369 case '$':
370 state = PARSE_EXPECT_HEX;
371 next = 0;
372 break;
373 default:
374 state = PARSE_ERROR;
375 break;
376 };
377 break;
378 case PARSE_EXPECT_LR:
379 switch (token) {
380 case 'r':
381 next = debugger->d.cpu->gprs[ARM_LR];
382 state = PARSE_EXPECT_SUFFIX;
383 break;
384 default:
385 state = PARSE_ERROR;
386 break;
387 }
388 break;
389 case PARSE_EXPECT_PC:
390 switch (token) {
391 case 'c':
392 next = debugger->d.cpu->gprs[ARM_PC];
393 state = PARSE_EXPECT_SUFFIX;
394 break;
395 default:
396 state = PARSE_ERROR;
397 break;
398 }
399 break;
400 case PARSE_EXPECT_SP:
401 switch (token) {
402 case 'p':
403 next = debugger->d.cpu->gprs[ARM_SP];
404 state = PARSE_EXPECT_SUFFIX;
405 break;
406 default:
407 state = PARSE_ERROR;
408 break;
409 }
410 break;
411 case PARSE_EXPECT_REGISTER:
412 switch (token) {
413 case '0':
414 case '2':
415 case '3':
416 case '4':
417 case '5':
418 case '6':
419 case '7':
420 case '8':
421 case '9':
422 next = debugger->d.cpu->gprs[token - '0'];
423 state = PARSE_EXPECT_SUFFIX;
424 break;
425 case '1':
426 state = PARSE_EXPECT_REGISTER_2;
427 break;
428 default:
429 state = PARSE_ERROR;
430 break;
431 }
432 break;
433 case PARSE_EXPECT_REGISTER_2:
434 switch (token) {
435 case '0':
436 case '1':
437 case '2':
438 case '3':
439 case '4':
440 case '5':
441 next = debugger->d.cpu->gprs[token - '0' + 10];
442 state = PARSE_EXPECT_SUFFIX;
443 break;
444 default:
445 state = PARSE_ERROR;
446 break;
447 }
448 break;
449 case PARSE_EXPECT_DECIMAL:
450 switch (token) {
451 case '0':
452 case '1':
453 case '2':
454 case '3':
455 case '4':
456 case '5':
457 case '6':
458 case '7':
459 case '8':
460 case '9':
461 // TODO: handle overflow
462 next *= 10;
463 next += token - '0';
464 break;
465 case '+':
466 _performOperation(operation, next, dv);
467 next = 0;
468 state = PARSE_ROOT;
469 operation = ADD;
470 break;
471 case '-':
472 _performOperation(operation, next, dv);
473 next = 0;
474 state = PARSE_ROOT;
475 operation = SUBTRACT;
476 break;
477 case '*':
478 _performOperation(operation, next, dv);
479 next = 0;
480 state = PARSE_ROOT;
481 operation = MULTIPLY;
482 break;
483 case '/':
484 _performOperation(operation, next, dv);
485 next = 0;
486 state = PARSE_ROOT;
487 operation = DIVIDE;
488 break;
489 default:
490 state = PARSE_ERROR;
491 }
492 break;
493 case PARSE_EXPECT_HEX:
494 switch (token) {
495 case '0':
496 case '1':
497 case '2':
498 case '3':
499 case '4':
500 case '5':
501 case '6':
502 case '7':
503 case '8':
504 case '9':
505 // TODO: handle overflow
506 next *= 16;
507 next += token - '0';
508 break;
509 case 'A':
510 case 'B':
511 case 'C':
512 case 'D':
513 case 'E':
514 case 'F':
515 // TODO: handle overflow
516 next *= 16;
517 next += token - 'A' + 10;
518 break;
519 case 'a':
520 case 'b':
521 case 'c':
522 case 'd':
523 case 'e':
524 case 'f':
525 // TODO: handle overflow
526 next *= 16;
527 next += token - 'a' + 10;
528 break;
529 case '+':
530 _performOperation(operation, next, dv);
531 next = 0;
532 state = PARSE_ROOT;
533 operation = ADD;
534 break;
535 case '-':
536 _performOperation(operation, next, dv);
537 next = 0;
538 state = PARSE_ROOT;
539 operation = SUBTRACT;
540 break;
541 case '*':
542 _performOperation(operation, next, dv);
543 next = 0;
544 state = PARSE_ROOT;
545 operation = MULTIPLY;
546 break;
547 case '/':
548 _performOperation(operation, next, dv);
549 next = 0;
550 state = PARSE_ROOT;
551 operation = DIVIDE;
552 break;
553 default:
554 state = PARSE_ERROR;
555 break;
556 }
557 break;
558 case PARSE_EXPECT_PREFIX:
559 switch (token) {
560 case 'X':
561 case 'x':
562 next = 0;
563 state = PARSE_EXPECT_HEX;
564 break;
565 default:
566 state = PARSE_ERROR;
567 break;
568 }
569 break;
570 case PARSE_EXPECT_SUFFIX:
571 _performOperation(operation, next, dv);
572 next = 0;
573 state = PARSE_ROOT;
574 switch (token) {
575 case '+':
576 operation = ADD;
577 break;
578 case '-':
579 operation = SUBTRACT;
580 break;
581 case '*':
582 operation = MULTIPLY;
583 break;
584 case '/':
585 operation = DIVIDE;
586 break;
587 default:
588 state = PARSE_ERROR;
589 break;
590 }
591 break;
592 case PARSE_ERROR:
593 // This shouldn't be reached
594 break;
595 }
596 }
597
598 if (state == PARSE_ERROR) {
599 dv->type = ERROR_TYPE;
600 } else {
601 _performOperation(operation, next, dv);
602 }
603 return adjusted;
604}
605
606static struct DebugVector* _DVParse(struct CLIDebugger* debugger, const char* string, size_t length) {
607 if (!string || length < 1) {
608 return 0;
609 }
610
611 struct DebugVector dvTemp = { .type = INT_TYPE };
612
613 size_t adjusted = _parseExpression(debugger, string, length, &dvTemp);
614 if (adjusted > length) {
615 dvTemp.type = ERROR_TYPE;
616 }
617 length -= adjusted;
618 string += adjusted;
619
620 struct DebugVector* dv = malloc(sizeof(struct DebugVector));
621 if (dvTemp.type == ERROR_TYPE) {
622 dv->type = ERROR_TYPE;
623 dv->next = 0;
624 } else {
625 *dv = dvTemp;
626 if (string[0] == ' ') {
627 dv->next = _DVParse(debugger, string + 1, length - 1);
628 if (dv->next && dv->next->type == ERROR_TYPE) {
629 dv->type = ERROR_TYPE;
630 }
631 }
632 }
633 return dv;
634}
635
636static void _DVFree(struct DebugVector* dv) {
637 struct DebugVector* next;
638 while (dv) {
639 next = dv->next;
640 free(dv);
641 dv = next;
642 }
643}
644
645static int _parse(struct CLIDebugger* debugger, const char* line, size_t count) {
646 const char* firstSpace = strchr(line, ' ');
647 size_t cmdLength;
648 struct DebugVector* dv = 0;
649 if (firstSpace) {
650 cmdLength = firstSpace - line;
651 dv = _DVParse(debugger, firstSpace + 1, count - cmdLength - 1);
652 if (dv && dv->type == ERROR_TYPE) {
653 printf("Parse error\n");
654 _DVFree(dv);
655 return 0;
656 }
657 } else {
658 cmdLength = count;
659 }
660
661 int i;
662 const char* name;
663 for (i = 0; (name = _debuggerCommands[i].name); ++i) {
664 if (strlen(name) != cmdLength) {
665 continue;
666 }
667 if (strncasecmp(name, line, cmdLength) == 0) {
668 _debuggerCommands[i].command(debugger, dv);
669 _DVFree(dv);
670 return 1;
671 }
672 }
673 _DVFree(dv);
674 printf("Command not found\n");
675 return 0;
676}
677
678static char* _prompt(EditLine* el) {
679 UNUSED(el);
680 return "> ";
681}
682
683static void _commandLine(struct ARMDebugger* debugger) {
684 struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
685 const char* line;
686 _printStatus(cliDebugger, 0);
687 int count = 0;
688 HistEvent ev;
689 while (debugger->state == DEBUGGER_PAUSED) {
690 line = el_gets(cliDebugger->elstate, &count);
691 if (!line) {
692 debugger->state = DEBUGGER_EXITING;
693 return;
694 }
695 if (line[0] == '\n') {
696 if (history(cliDebugger->histate, &ev, H_FIRST) >= 0) {
697 _parse(cliDebugger, ev.str, strlen(ev.str) - 1);
698 }
699 } else {
700 if (_parse(cliDebugger, line, count - 1)) {
701 history(cliDebugger->histate, &ev, H_ENTER, line);
702 }
703 }
704 }
705}
706
707static void _reportEntry(struct ARMDebugger* debugger, enum DebuggerEntryReason reason) {
708 UNUSED(debugger);
709 switch (reason) {
710 case DEBUGGER_ENTER_MANUAL:
711 case DEBUGGER_ENTER_ATTACHED:
712 break;
713 case DEBUGGER_ENTER_BREAKPOINT:
714 printf("Hit breakpoint\n");
715 break;
716 case DEBUGGER_ENTER_WATCHPOINT:
717 printf("Hit watchpoint\n");
718 break;
719 case DEBUGGER_ENTER_ILLEGAL_OP:
720 printf("Hit illegal opcode\n");
721 break;
722 }
723}
724
725static unsigned char _tabComplete(EditLine* elstate, int ch) {
726 UNUSED(ch);
727 const LineInfo* li = el_line(elstate);
728 const char* commandPtr;
729 int cmd = 0, len = 0;
730 const char* name = 0;
731 for (commandPtr = li->buffer; commandPtr <= li->cursor; ++commandPtr, ++len) {
732 for (; (name = _debuggerCommands[cmd].name); ++cmd) {
733 int cmp = strncasecmp(name, li->buffer, len);
734 if (cmp > 0) {
735 return CC_ERROR;
736 }
737 if (cmp == 0) {
738 break;
739 }
740 }
741 }
742 if (_debuggerCommands[cmd + 1].name && strncasecmp(_debuggerCommands[cmd + 1].name, li->buffer, len - 1) == 0) {
743 return CC_ERROR;
744 }
745 name += len - 1;
746 el_insertstr(elstate, name);
747 el_insertstr(elstate, " ");
748 return CC_REDISPLAY;
749}
750
751static void _cliDebuggerInit(struct ARMDebugger* debugger) {
752 struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
753 // TODO: get argv[0]
754 cliDebugger->elstate = el_init(BINARY_NAME, stdin, stdout, stderr);
755 el_set(cliDebugger->elstate, EL_PROMPT, _prompt);
756 el_set(cliDebugger->elstate, EL_EDITOR, "emacs");
757
758 el_set(cliDebugger->elstate, EL_CLIENTDATA, cliDebugger);
759 el_set(cliDebugger->elstate, EL_ADDFN, "tab-complete", "Tab completion", _tabComplete);
760 el_set(cliDebugger->elstate, EL_BIND, "\t", "tab-complete", 0);
761 cliDebugger->histate = history_init();
762 HistEvent ev;
763 history(cliDebugger->histate, &ev, H_SETSIZE, 200);
764 el_set(cliDebugger->elstate, EL_HIST, history, cliDebugger->histate);
765 _activeDebugger = cliDebugger;
766 signal(SIGINT, _breakIntoDefault);
767}
768
769static void _cliDebuggerDeinit(struct ARMDebugger* debugger) {
770 struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
771 history_end(cliDebugger->histate);
772 el_end(cliDebugger->elstate);
773}
774
775void CLIDebuggerCreate(struct CLIDebugger* debugger) {
776 ARMDebuggerCreate(&debugger->d);
777 debugger->d.init = _cliDebuggerInit;
778 debugger->d.deinit = _cliDebuggerDeinit;
779 debugger->d.paused = _commandLine;
780 debugger->d.entered = _reportEntry;
781}