1 /*
2 * Hatari - symbols.c
3 *
4 * Copyright (C) 2010-2014 by Eero Tamminen
5 *
6 * This file is distributed under the GNU General Public License, version 2
7 * or at your option any later version. Read the file gpl.txt for details.
8 *
9 * symbols.c - Hatari debugger symbol/address handling; parsing, sorting,
10 * matching, TAB completion support etc.
11 *
12 * Symbol/address information is read either from:
13 * - A program file's DRI/GST format symbol table, or
14 * - ASCII file which contents are subset of "nm" output i.e. composed of
15 * a hexadecimal addresses followed by a space, letter indicating symbol
16 * type (T = text/code, D = data, B = BSS), space and the symbol name.
17 * Empty lines and lines starting with '#' are ignored. It's AHCC SYM
18 * output compatible.
19 */
20 const char Symbols_fileid[] = "Hatari symbols.c : " __DATE__ " " __TIME__;
21
22 #include <ctype.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <assert.h>
26 #include <SDL_types.h>
27 #include <SDL_endian.h>
28 #include "main.h"
29 #include "symbols.h"
30 #include "debugui.h"
31 #include "debug_priv.h"
32 #include "debugInfo.h"
33 #include "evaluate.h"
34
35 typedef struct {
36 char *name;
37 Uint32 address;
38 symtype_t type;
39 } symbol_t;
40
41 typedef struct {
42 int count; /* final symbol count */
43 int symbols; /* initial symbol count */
44 symbol_t *addresses; /* items sorted by address */
45 symbol_t *names; /* items sorted by symbol name */
46 } symbol_list_t;
47
48 typedef struct {
49 Uint32 offset;
50 Uint32 end;
51 } prg_section_t;
52
53
54 /* how many characters the symbol name can have.
55 * NOTE: change also sscanf width arg if you change this!!!
56 */
57 #define MAX_SYM_SIZE 32
58
59
60 /* TODO: add symbol name/address file names to configuration? */
61 static symbol_list_t *CpuSymbolsList;
62 static symbol_list_t *DspSymbolsList;
63
64 /* path for last loaded program (through GEMDOS HD emulation) */
65 static char *CurrentProgramPath;
66 static bool SymbolsAreForProgram;
67 static bool AutoLoadFailed;
68
69
70 /* ------------------ load and free functions ------------------ */
71
72 /**
73 * compare function for qsort() to sort according to symbol address
74 */
symbols_by_address(const void * s1,const void * s2)75 static int symbols_by_address(const void *s1, const void *s2)
76 {
77 Uint32 addr1 = ((const symbol_t*)s1)->address;
78 Uint32 addr2 = ((const symbol_t*)s2)->address;
79
80 if (addr1 < addr2) {
81 return -1;
82 }
83 if (addr1 > addr2) {
84 return 1;
85 }
86 fprintf(stderr, "WARNING: symbols '%s' & '%s' have the same 0x%x address.\n",
87 ((const symbol_t*)s1)->name, ((const symbol_t*)s2)->name, addr1);
88 return 0;
89 }
90
91 /**
92 * compare function for qsort() to sort according to symbol name
93 */
symbols_by_name(const void * s1,const void * s2)94 static int symbols_by_name(const void *s1, const void *s2)
95 {
96 const char* name1 = ((const symbol_t*)s1)->name;
97 const char* name2 = ((const symbol_t*)s2)->name;
98 int ret;
99
100 ret = strcmp(name1, name2);
101 if (!ret) {
102 fprintf(stderr, "WARNING: addresses 0x%x & 0x%x have the same '%s' name.\n",
103 ((const symbol_t*)s1)->address, ((const symbol_t*)s2)->address, name1);
104 }
105 return ret;
106 }
107
108
109 /**
110 * Allocate symbol list & names for given number of items.
111 * Return allocated list or NULL on failure.
112 */
symbol_list_alloc(int symbols)113 static symbol_list_t* symbol_list_alloc(int symbols)
114 {
115 symbol_list_t *list;
116
117 if (!symbols) {
118 return NULL;
119 }
120 list = calloc(1, sizeof(symbol_list_t));
121 if (list) {
122 list->names = malloc(symbols * sizeof(symbol_t));
123 if (!list->names) {
124 free(list);
125 list = NULL;
126 }
127 }
128 return list;
129 }
130
131 /**
132 * Free symbol list & names.
133 */
symbol_list_free(symbol_list_t * list)134 static void symbol_list_free(symbol_list_t *list)
135 {
136 if (list) {
137 if (list->names) {
138 free(list->names);
139 }
140 free(list);
141 }
142 }
143
144 /**
145 * Return symbol type identifier char
146 */
symbol_char(int type)147 static char symbol_char(int type)
148 {
149 switch (type) {
150 case SYMTYPE_TEXT: return 'T';
151 case SYMTYPE_DATA: return 'D';
152 case SYMTYPE_BSS: return 'B';
153 default: return '?';
154 }
155 }
156
157 #define INVALID_SYMBOL_OFFSETS ((symbol_list_t*)1)
158
159 /**
160 * Load symbols of given type and the symbol address addresses from
161 * DRI/GST format symbol table, and add given offsets to the addresses:
162 * http://toshyp.atari.org/en/005005.html
163 * Return symbols list or NULL for failure.
164 */
symbols_load_dri(FILE * fp,prg_section_t * sections,symtype_t gettype,Uint32 tablesize)165 static symbol_list_t* symbols_load_dri(FILE *fp, prg_section_t *sections, symtype_t gettype, Uint32 tablesize)
166 {
167 int i, count, symbols, len, outside;
168 int dtypes, locals, ofiles;
169 prg_section_t *section;
170 symbol_list_t *list;
171 symtype_t symtype;
172 #define DRI_ENTRY_SIZE 14
173 char name[23];
174 Uint16 symid;
175 Uint32 address;
176
177 if (tablesize % DRI_ENTRY_SIZE || !tablesize) {
178 fprintf(stderr, "ERROR: invalid DRI/GST symbol table size %d!\n", tablesize);
179 return NULL;
180 }
181 symbols = tablesize / DRI_ENTRY_SIZE;
182 if (!(list = symbol_list_alloc(symbols))) {
183 return NULL;
184 }
185
186 outside = dtypes = ofiles = locals = count = 0;
187 for (i = 1; i <= symbols; i++) {
188 /* read DRI symbol table slot */
189 if (fread(name, 8, 1, fp) != 1 ||
190 fread(&symid, sizeof(symid), 1, fp) != 1 ||
191 fread(&address, sizeof(address), 1, fp) != 1) {
192 break;
193 }
194 address = SDL_SwapBE32(address);
195 symid = SDL_SwapBE16(symid);
196
197 /* GST extended DRI symbol format? */
198 if ((symid & 0x0048)) {
199 /* next slot is rest of name */
200 i += 1;
201 if (fread(name+8, 14, 1, fp) != 1) {
202 break;
203 }
204 name[22] = '\0';
205 } else {
206 name[8] = '\0';
207 }
208
209 /* check section */
210 switch (symid & 0xf00) {
211 case 0x0200:
212 symtype = SYMTYPE_TEXT;
213 section = &(sections[0]);
214 break;
215 case 0x0400:
216 symtype = SYMTYPE_DATA;
217 section = &(sections[1]);
218 break;
219 case 0x0100:
220 symtype = SYMTYPE_BSS;
221 section = &(sections[2]);
222 break;
223 default:
224 if ((symid & 0xe000) == 0xe000) {
225 dtypes++;
226 continue;
227 }
228 fprintf(stderr, "WARNING: ignoring symbol '%s' in slot %d of unknown type 0x%x.\n", name, i, symid);
229 continue;
230 }
231 if (!(gettype & symtype)) {
232 continue;
233 }
234 if (name[0] == '.' && name[1] == 'L') {
235 locals++;
236 continue;
237 }
238 len = strlen(name);
239 if (strchr(name, '/') || (len > 2 && name[len-2] == '.' && name[len-1] == 'o')) {
240 ofiles++;
241 continue;
242 }
243 address += section->offset;
244 if (address > section->end) {
245 /* VBCC has 1 symbol outside of its section */
246 if (++outside > 2) {
247 /* potentially buggy version of VBCC vlink used */
248 fprintf(stderr, "ERROR: too many invalid offsets, skipping rest of symbols!\n");
249 symbol_list_free(list);
250 return INVALID_SYMBOL_OFFSETS;
251 }
252 fprintf(stderr, "WARNING: ignoring symbol '%s' of %c type in slot %d with invalid offset 0x%x (>= 0x%x).\n",
253 name, symbol_char(symtype), i, address, section->end);
254 continue;
255 }
256 list->names[count].address = address;
257 list->names[count].type = symtype;
258 list->names[count].name = strdup(name);
259 assert(list->names[count].name);
260 count++;
261 }
262 if (i <= symbols) {
263 perror("ERROR: reading symbol failed");
264 symbol_list_free(list);
265 return NULL;
266 }
267 if (dtypes) {
268 fprintf(stderr, "NOTE: ignored %d globally defined equated values.\n", dtypes);
269 }
270 if (locals) {
271 fprintf(stderr, "NOTE: ignored %d unnamed / local symbols (= name starts with '.L').\n", locals);
272 }
273 if (ofiles) {
274 /* object file path names most likely get truncated and
275 * as result cause unnecessary symbol name conflicts in
276 * addition to object file addresses conflicting with
277 * first symbol in the object file.
278 */
279 fprintf(stderr, "NOTE: ignored %d object file names (= name has '/' or ends in '.o').\n", ofiles);
280 }
281 list->symbols = symbols;
282 list->count = count;
283 return list;
284 }
285
286 /**
287 * Parse program header and use symbol table format specific loader
288 * loader function to load the symbols.
289 * Return symbols list or NULL for failure.
290 */
symbols_load_binary(FILE * fp,symtype_t gettype)291 static symbol_list_t* symbols_load_binary(FILE *fp, symtype_t gettype)
292 {
293 Uint32 textlen, datalen, bsslen, start, tablesize, tabletype, prgflags;
294 prg_section_t sections[3];
295 int offset, reads = 0;
296 Uint16 relocflag;
297 const char *info;
298 symbol_list_t* symbols;
299
300 /* get TEXT, DATA & BSS section sizes */
301 fseek(fp, 2, SEEK_SET);
302 reads += fread(&textlen, sizeof(textlen), 1, fp);
303 textlen = SDL_SwapBE32(textlen);
304 reads += fread(&datalen, sizeof(datalen), 1, fp);
305 datalen = SDL_SwapBE32(datalen);
306 reads += fread(&bsslen, sizeof(bsslen), 1, fp);
307 bsslen = SDL_SwapBE32(bsslen);
308
309 /* get symbol table size & type and check that all reads succeeded */
310 reads += fread(&tablesize, sizeof(tablesize), 1, fp);
311 tablesize = SDL_SwapBE32(tablesize);
312 if (!tablesize) {
313 fprintf(stderr, "ERROR: symbol table missing from the program!\n");
314 return NULL;
315 }
316 reads += fread(&tabletype, sizeof(tabletype), 1, fp);
317 tabletype = SDL_SwapBE32(tabletype);
318
319 /* get program header and whether there's reloc table */
320 reads += fread(&prgflags, sizeof(prgflags), 1, fp);
321 prgflags = SDL_SwapBE32(prgflags);
322 reads += fread(&relocflag, sizeof(relocflag), 1, fp);
323 relocflag = SDL_SwapBE32(relocflag);
324
325 if (reads != 7) {
326 fprintf(stderr, "ERROR: program header reading failed!\n");
327 return NULL;
328 }
329
330 /* offsets & max sizes for running program TEXT/DATA/BSS section symbols */
331 start = DebugInfo_GetTEXT();
332 if (!start) {
333 fprintf(stderr, "ERROR: no valid program basepage!\n");
334 return NULL;
335 }
336 sections[0].offset = start;
337 sections[0].end = start + textlen;
338 if (DebugInfo_GetTEXTEnd() != sections[0].end - 1) {
339 fprintf(stderr, "ERROR: given program TEXT section size differs from one in RAM!\n");
340 return NULL;
341 }
342
343 start = DebugInfo_GetDATA();
344 sections[1].offset = start;
345 sections[1].end = start + datalen - 1;
346
347 start = DebugInfo_GetBSS();
348 sections[2].offset = start;
349 sections[2].end = start + bsslen - 1;
350
351 /* go to start of symbol table */
352 offset = 0x1C + textlen + datalen;
353 if (fseek(fp, offset, SEEK_SET) < 0) {
354 perror("ERROR: seeking to symbol table failed");
355 return NULL;
356 }
357 switch (tabletype) {
358 case 0x4D694E54: /* "MiNT" */
359 info = "GCC/MiNT executable, GST symbol table.";
360 break;
361 case 0x0:
362 info = "TOS executable, DRI / GST symbol table.";
363 break;
364 default:
365 fprintf(stderr, "ERROR: unknown executable type 0x%x at offset 0x%x!\n", tabletype, offset);
366 return NULL;
367 }
368 fprintf(stderr, "0x%x program flags, reloc=%d, %s\n", prgflags, relocflag, info);
369 fprintf(stderr, "Trying to load symbol table at offset 0x%x...\n", offset);
370 symbols = symbols_load_dri(fp, sections, gettype, tablesize);
371
372 if (symbols == INVALID_SYMBOL_OFFSETS && fseek(fp, offset, SEEK_SET) == 0) {
373 fprintf(stderr, "Re-trying with TEXT-relative BSS/DATA section offsets...\n");
374 start = DebugInfo_GetTEXT();
375 sections[1].offset = start;
376 sections[2].offset = start;
377 sections[1].end += textlen;
378 sections[2].end += (textlen + datalen);
379 symbols = symbols_load_dri(fp, sections, gettype, tablesize);
380 }
381 if (symbols == INVALID_SYMBOL_OFFSETS) {
382 return NULL;
383 }
384 return symbols;
385 }
386
387 /**
388 * Load symbols of given type and the symbol address addresses from
389 * the given ASCII file and add given offsets to the addresses.
390 * Return symbols list or NULL for failure.
391 */
symbols_load_ascii(FILE * fp,Uint32 * offsets,Uint32 maxaddr,symtype_t gettype)392 static symbol_list_t* symbols_load_ascii(FILE *fp, Uint32 *offsets, Uint32 maxaddr, symtype_t gettype)
393 {
394 symbol_list_t *list;
395 char symchar, buffer[128], name[MAX_SYM_SIZE+1], *buf;
396 int count, line, symbols;
397 Uint32 address, offset;
398 symtype_t symtype;
399
400 /* count content lines */
401 symbols = 0;
402 while (fgets(buffer, sizeof(buffer), fp)) {
403 /* skip comments (AHCC SYM file comments start with '*') */
404 if (*buffer == '#' || *buffer == '*') {
405 continue;
406 }
407 /* skip empty lines */
408 for (buf = buffer; isspace((unsigned char)*buf); buf++);
409 if (!*buf) {
410 continue;
411 }
412 symbols++;
413 }
414 if (!symbols) {
415 fprintf(stderr, "ERROR: no symbols.\n");
416 }
417
418 fseek(fp, 0, SEEK_SET);
419
420 /* allocate space for symbol list & names */
421 if (!(list = symbol_list_alloc(symbols))) {
422 return NULL;
423 }
424
425 /* read symbols */
426 count = 0;
427 for (line = 1; fgets(buffer, sizeof(buffer), fp); line++) {
428 /* skip comments (AHCC SYM file comments start with '*') */
429 if (*buffer == '#' || *buffer == '*') {
430 continue;
431 }
432 /* skip empty lines */
433 for (buf = buffer; isspace((unsigned char)*buf); buf++);
434 if (!*buf) {
435 continue;
436 }
437 assert(count < symbols); /* file not modified in meanwhile? */
438 if (sscanf(buffer, "%x %c %32[0-9A-Za-z_.-]s", &address, &symchar, name) != 3) {
439 fprintf(stderr, "WARNING: syntax error on line %d, skipping.\n", line);
440 continue;
441 }
442 switch (toupper((unsigned char)symchar)) {
443 case 'T':
444 symtype = SYMTYPE_TEXT;
445 offset = offsets[0];
446 break;
447 case 'O': /* AHCC type for _StkSize etc */
448 case 'D':
449 symtype = SYMTYPE_DATA;
450 offset = offsets[1];
451 break;
452 case 'B':
453 symtype = SYMTYPE_BSS;
454 offset = offsets[2];
455 break;
456 default:
457 fprintf(stderr, "WARNING: unrecognized symbol type '%c' on line %d, skipping.\n", symchar, line);
458 continue;
459 }
460 if (!(gettype & symtype)) {
461 continue;
462 }
463 address += offset;
464 if (address > maxaddr) {
465 fprintf(stderr, "WARNING: invalid address 0x%x on line %d, skipping.\n", address, line);
466 continue;
467 }
468 list->names[count].address = address;
469 list->names[count].type = symtype;
470 list->names[count].name = strdup(name);
471 assert(list->names[count].name);
472 count++;
473 }
474 list->symbols = symbols;
475 list->count = count;
476 return list;
477 }
478
479 /**
480 * Return true if given FILE* is Atari program.
481 */
is_atari_program(FILE * fp)482 static bool is_atari_program(FILE *fp)
483 {
484 long oldpos = ftell(fp);
485 Uint16 magic;
486
487 fseek(fp, 0, SEEK_SET);
488 if (fread(&magic, sizeof(magic), 1, fp) != 1) {
489 return false;
490 }
491 fseek(fp, oldpos, SEEK_SET);
492
493 return (SDL_SwapBE16(magic) == 0x601A);
494 }
495
496 /**
497 * Load symbols of given type and the symbol address addresses from
498 * the given file and add given offsets to the addresses.
499 * Return symbols list or NULL for failure.
500 */
Symbols_Load(const char * filename,Uint32 * offsets,Uint32 maxaddr)501 static symbol_list_t* Symbols_Load(const char *filename, Uint32 *offsets, Uint32 maxaddr)
502 {
503 symbol_list_t *list;
504 FILE *fp;
505
506 if (!(fp = fopen(filename, "r"))) {
507 fprintf(stderr, "ERROR: opening '%s' failed!\n", filename);
508 return NULL;
509 }
510 if (is_atari_program(fp)) {
511 const char *last = CurrentProgramPath;
512 if (!last) {
513 /* "pc=text" breakpoint used as point for loading program symbols gives false hits during bootup */
514 fprintf(stderr, "WARNING: no program loaded yet (through GEMDOS HD emu)!\n");
515 } else if (strcmp(last, filename) != 0) {
516 fprintf(stderr, "WARNING: given program doesn't match last program executed by GEMDOS HD emulation:\n\t%s\n", last);
517 }
518 fprintf(stderr, "Reading symbols from program '%s' symbol table...\n", filename);
519 list = symbols_load_binary(fp, SYMTYPE_ALL);
520 SymbolsAreForProgram = true;
521 } else {
522 fprintf(stderr, "Reading 'nm' style ASCII symbols from '%s'...\n", filename);
523 list = symbols_load_ascii(fp, offsets, maxaddr, SYMTYPE_ALL);
524 SymbolsAreForProgram = false;
525 }
526 fclose(fp);
527
528 if (!list) {
529 fprintf(stderr, "ERROR: reading symbols from '%s' failed!\n", filename);
530 return NULL;
531 }
532
533 if (list->count < list->symbols) {
534 if (!list->count) {
535 fprintf(stderr, "ERROR: no valid symbols in '%s', loading failed!\n", filename);
536 symbol_list_free(list);
537 return NULL;
538 }
539 /* parsed less than there were "content" lines */
540 list->names = realloc(list->names, list->count * sizeof(symbol_t));
541 assert(list->names);
542 }
543
544 /* copy name list to address list */
545 list->addresses = malloc(list->count * sizeof(symbol_t));
546 assert(list->addresses);
547 memcpy(list->addresses, list->names, list->count * sizeof(symbol_t));
548
549 /* sort both lists, with different criteria */
550 qsort(list->addresses, list->count, sizeof(symbol_t), symbols_by_address);
551 qsort(list->names, list->count, sizeof(symbol_t), symbols_by_name);
552
553 fprintf(stderr, "Loaded %d symbols from '%s'.\n", list->count, filename);
554 return list;
555 }
556
557
558 /**
559 * Free read symbols.
560 */
Symbols_Free(symbol_list_t * list)561 static void Symbols_Free(symbol_list_t* list)
562 {
563 int i;
564
565 if (!list) {
566 return;
567 }
568 assert(list->count);
569 for (i = 0; i < list->count; i++) {
570 free(list->names[i].name);
571 }
572 free(list->addresses);
573 free(list->names);
574
575 /* catch use of freed list */
576 list->addresses = NULL;
577 list->names = NULL;
578 list->count = 0;
579 free(list);
580 }
581
582
583 /* ---------------- symbol name completion support ------------------ */
584
585 /**
586 * Helper for symbol name completion and finding their addresses.
587 * STATE = 0 -> different text from previous one.
588 * Return (copy of) next name or NULL if no matches.
589 */
Symbols_MatchByName(symbol_list_t * list,symtype_t symtype,const char * text,int state)590 static char* Symbols_MatchByName(symbol_list_t* list, symtype_t symtype, const char *text, int state)
591 {
592 static int i, len;
593 const symbol_t *entry;
594
595 if (!list) {
596 return NULL;
597 }
598
599 if (!state) {
600 /* first match */
601 len = strlen(text);
602 i = 0;
603 }
604
605 /* next match */
606 entry = list->names;
607 while (i < list->count) {
608 if ((entry[i].type & symtype) &&
609 strncmp(entry[i].name, text, len) == 0) {
610 return strdup(entry[i++].name);
611 } else {
612 i++;
613 }
614 }
615 return NULL;
616 }
617
618 /**
619 * Readline match callbacks for CPU symbol name completion.
620 * STATE = 0 -> different text from previous one.
621 * Return next match or NULL if no matches.
622 */
Symbols_MatchCpuAddress(const char * text,int state)623 char* Symbols_MatchCpuAddress(const char *text, int state)
624 {
625 return Symbols_MatchByName(CpuSymbolsList, SYMTYPE_ALL, text, state);
626 }
Symbols_MatchCpuCodeAddress(const char * text,int state)627 char* Symbols_MatchCpuCodeAddress(const char *text, int state)
628 {
629 return Symbols_MatchByName(CpuSymbolsList, SYMTYPE_TEXT, text, state);
630 }
Symbols_MatchCpuDataAddress(const char * text,int state)631 char* Symbols_MatchCpuDataAddress(const char *text, int state)
632 {
633 return Symbols_MatchByName(CpuSymbolsList, SYMTYPE_DATA|SYMTYPE_BSS, text, state);
634 }
635
636 /**
637 * Readline match callback for DSP symbol name completion.
638 * STATE = 0 -> different text from previous one.
639 * Return next match or NULL if no matches.
640 */
Symbols_MatchDspAddress(const char * text,int state)641 char* Symbols_MatchDspAddress(const char *text, int state)
642 {
643 return Symbols_MatchByName(DspSymbolsList, SYMTYPE_ALL, text, state);
644 }
Symbols_MatchDspCodeAddress(const char * text,int state)645 char* Symbols_MatchDspCodeAddress(const char *text, int state)
646 {
647 return Symbols_MatchByName(DspSymbolsList, SYMTYPE_TEXT, text, state);
648 }
Symbols_MatchDspDataAddress(const char * text,int state)649 char* Symbols_MatchDspDataAddress(const char *text, int state)
650 {
651 return Symbols_MatchByName(DspSymbolsList, SYMTYPE_DATA|SYMTYPE_BSS, text, state);
652 }
653
654
655 /* ---------------- symbol name -> address search ------------------ */
656
657 /**
658 * Search symbol of given type by name.
659 * Return symbol if name matches, zero otherwise.
660 */
Symbols_SearchByName(symbol_list_t * list,symtype_t symtype,const char * name)661 static const symbol_t* Symbols_SearchByName(symbol_list_t* list, symtype_t symtype, const char *name)
662 {
663 symbol_t *entries;
664 /* left, right, middle */
665 int l, r, m, dir;
666
667 if (!list) {
668 return NULL;
669 }
670 entries = list->names;
671
672 /* bisect */
673 l = 0;
674 r = list->count - 1;
675 do {
676 m = (l+r) >> 1;
677 dir = strcmp(entries[m].name, name);
678 if (dir == 0 && (entries[m].type & symtype)) {
679 return &(entries[m]);
680 }
681 if (dir > 0) {
682 r = m-1;
683 } else {
684 l = m+1;
685 }
686 } while (l <= r);
687 return NULL;
688 }
689
690 /**
691 * Set given CPU symbol's address to variable and return TRUE if one was found.
692 */
Symbols_GetCpuAddress(symtype_t symtype,const char * name,Uint32 * addr)693 bool Symbols_GetCpuAddress(symtype_t symtype, const char *name, Uint32 *addr)
694 {
695 const symbol_t *entry;
696 entry = Symbols_SearchByName(CpuSymbolsList, symtype, name);
697 if (entry) {
698 *addr = entry->address;
699 return true;
700 }
701 return false;
702 }
703
704 /**
705 * Set given DSP symbol's address to variable and return TRUE if one was found.
706 */
Symbols_GetDspAddress(symtype_t symtype,const char * name,Uint32 * addr)707 bool Symbols_GetDspAddress(symtype_t symtype, const char *name, Uint32 *addr)
708 {
709 const symbol_t *entry;
710 entry = Symbols_SearchByName(DspSymbolsList, symtype, name);
711 if (entry) {
712 *addr = entry->address;
713 return true;
714 }
715 return false;
716 }
717
718
719 /* ---------------- symbol address -> name search ------------------ */
720
721 /**
722 * Search symbol by address.
723 * Return symbol index if address matches, -1 otherwise.
724 */
Symbols_SearchByAddress(symbol_list_t * list,Uint32 addr)725 static int Symbols_SearchByAddress(symbol_list_t* list, Uint32 addr)
726 {
727 symbol_t *entries;
728 /* left, right, middle */
729 int l, r, m;
730 Uint32 curr;
731
732 if (!list) {
733 return -1;
734 }
735 entries = list->addresses;
736
737 /* bisect */
738 l = 0;
739 r = list->count - 1;
740 do {
741 m = (l+r) >> 1;
742 curr = entries[m].address;
743 if (curr == addr) {
744 return m;
745 }
746 if (curr > addr) {
747 r = m-1;
748 } else {
749 l = m+1;
750 }
751 } while (l <= r);
752 return -1;
753 }
754
755 /**
756 * Search CPU symbol by address.
757 * Return symbol name if address matches, NULL otherwise.
758 * Returned name is valid only until next Symbols_* function call.
759 */
Symbols_GetByCpuAddress(Uint32 addr)760 const char* Symbols_GetByCpuAddress(Uint32 addr)
761 {
762 int idx = Symbols_SearchByAddress(CpuSymbolsList, addr);
763 if (idx < 0) {
764 return NULL;
765 }
766 return CpuSymbolsList->addresses[idx].name;
767 }
768 /**
769 * Search DSP symbol by address.
770 * Return symbol name if address matches, NULL otherwise.
771 * Returned name is valid only until next Symbols_* function call.
772 */
Symbols_GetByDspAddress(Uint32 addr)773 const char* Symbols_GetByDspAddress(Uint32 addr)
774 {
775 int idx = Symbols_SearchByAddress(DspSymbolsList, addr);
776 if (idx < 0) {
777 return NULL;
778 }
779 return DspSymbolsList->addresses[idx].name;
780 }
781
782 /**
783 * Search CPU symbol by address.
784 * Return symbol index if address matches, -1 otherwise.
785 */
Symbols_GetCpuAddressIndex(Uint32 addr)786 int Symbols_GetCpuAddressIndex(Uint32 addr)
787 {
788 return Symbols_SearchByAddress(CpuSymbolsList, addr);
789 }
790
791 /**
792 * Search DSP symbol by address.
793 * Return symbol index if address matches, -1 otherwise.
794 */
Symbols_GetDspAddressIndex(Uint32 addr)795 int Symbols_GetDspAddressIndex(Uint32 addr)
796 {
797 return Symbols_SearchByAddress(DspSymbolsList, addr);
798 }
799
800 /**
801 * Return how many symbols are loaded/available
802 */
Symbols_CpuCount(void)803 int Symbols_CpuCount(void)
804 {
805 return (CpuSymbolsList ? CpuSymbolsList->count : 0);
806 }
Symbols_DspCount(void)807 int Symbols_DspCount(void)
808 {
809 return (DspSymbolsList ? DspSymbolsList->count : 0);
810 }
811
812 /* ---------------- symbol showing ------------------ */
813
814 /**
815 * Show symbols from given list with paging.
816 */
Symbols_Show(symbol_list_t * list,const char * sorttype)817 static void Symbols_Show(symbol_list_t* list, const char *sorttype)
818 {
819 symbol_t *entry, *entries;
820 char symchar;
821 int i;
822
823 if (!list) {
824 fprintf(stderr, "No symbols!\n");
825 return;
826 }
827
828 if (strcmp("addr", sorttype) == 0) {
829 entries = list->addresses;
830 } else {
831 entries = list->names;
832 }
833 fprintf(stderr, "%s symbols sorted by %s:\n",
834 (list == CpuSymbolsList ? "CPU" : "DSP"), sorttype);
835
836 for (entry = entries, i = 0; i < list->count; i++, entry++) {
837 symchar = symbol_char(entry->type);
838 fprintf(stderr, "0x%08x %c %s\n",
839 entry->address, symchar, entry->name);
840 if (i && i % 20 == 0) {
841 fprintf(stderr, "--- q to exit listing, just enter to continue --- ");
842 if (toupper(getchar()) == 'Q') {
843 return;
844 }
845 }
846 }
847 }
848
849 /* ---------------- binary load handling ------------------ */
850
851
852 /**
853 * Remove last opened program path.
854 */
Symbols_RemoveCurrentProgram(void)855 void Symbols_RemoveCurrentProgram(void)
856 {
857 if (CurrentProgramPath) {
858 free(CurrentProgramPath);
859 CurrentProgramPath = NULL;
860
861 if (SymbolsAreForProgram) {
862 Symbols_Free(CpuSymbolsList);
863 CpuSymbolsList = NULL;
864 }
865 }
866 AutoLoadFailed = false;
867 }
868
869 /**
870 * Set last opened program path.
871 */
Symbols_ChangeCurrentProgram(FILE * fp,const char * path)872 void Symbols_ChangeCurrentProgram(FILE *fp, const char *path)
873 {
874 if (is_atari_program(fp)) {
875 Symbols_RemoveCurrentProgram();
876 CurrentProgramPath = strdup(path);
877 }
878 }
879
880 /**
881 * Load symbols for last opened program.
882 */
Symbols_LoadCurrentProgram(void)883 void Symbols_LoadCurrentProgram(void)
884 {
885 /* symbols already loaded, program path missing or previous load failed? */
886 if (CpuSymbolsList || !CurrentProgramPath || AutoLoadFailed) {
887 return;
888 }
889 CpuSymbolsList = Symbols_Load(CurrentProgramPath, NULL, 0);
890 if (!CpuSymbolsList) {
891 AutoLoadFailed = true;
892 } else {
893 AutoLoadFailed = false;
894 }
895 }
896
897 /* ---------------- command parsing ------------------ */
898
899 /**
900 * Readline match callback to list symbols subcommands.
901 * STATE = 0 -> different text from previous one.
902 * Return next match or NULL if no matches.
903 */
Symbols_MatchCommand(const char * text,int state)904 char *Symbols_MatchCommand(const char *text, int state)
905 {
906 static const char* subs[] = {
907 "addr", "free", "name", "prg"
908 };
909 return DebugUI_MatchHelper(subs, ARRAYSIZE(subs), text, state);
910 }
911
912 const char Symbols_Description[] =
913 "<filename|prg|addr|name|free> [<T offset> [<D offset> <B offset>]]\n"
914 "\tLoads symbol names and their addresses from the given file.\n"
915 "\tIf there were previously loaded symbols, they're replaced.\n"
916 "\n"
917 "\tGiving 'prg' instead of a file name, loads DRI/GST symbol table\n"
918 "\tfrom the last program executed through the GEMDOS HD emulation.\n"
919 "\n"
920 "\tGiving either 'name' or 'addr' instead of a file name, will\n"
921 "\tlist the currently loaded symbols. Giving 'free' will remove\n"
922 "\tthe loaded symbols.\n"
923 "\n"
924 "\tIf one base address/offset is given, its added to all addresses.\n"
925 "\tIf three offsets are given (and non-zero), they're applied to\n"
926 "\ttext (T), data (D) and BSS (B) symbols. Given offsets are used\n"
927 "\tonly when loading ASCII symbol files.";
928
929 /**
930 * Handle debugger 'symbols' command and its arguments
931 */
Symbols_Command(int nArgc,char * psArgs[])932 int Symbols_Command(int nArgc, char *psArgs[])
933 {
934 enum { TYPE_NONE, TYPE_CPU, TYPE_DSP } listtype;
935 Uint32 offsets[3], maxaddr;
936 symbol_list_t *list;
937 const char *file;
938 int i;
939
940 if (strcmp("dspsymbols", psArgs[0]) == 0) {
941 listtype = TYPE_DSP;
942 maxaddr = 0xFFFF;
943 } else if (strcmp("symbols", psArgs[0]) == 0) {
944 listtype = TYPE_CPU;
945 maxaddr = 0xFFFFFF;
946 } else {
947 listtype = TYPE_NONE;
948 maxaddr = 0;
949 }
950 if (nArgc < 2 || listtype == TYPE_NONE) {
951 return DebugUI_PrintCmdHelp(psArgs[0]);
952 }
953 file = psArgs[1];
954
955 /* handle special cases */
956 if (strcmp(file, "name") == 0 || strcmp(file, "addr") == 0) {
957 list = (listtype == TYPE_DSP ? DspSymbolsList : CpuSymbolsList);
958 Symbols_Show(list, file);
959 return DEBUGGER_CMDDONE;
960 }
961 if (strcmp(file, "free") == 0) {
962 if (listtype == TYPE_DSP) {
963 Symbols_Free(DspSymbolsList);
964 DspSymbolsList = NULL;
965 } else {
966 Symbols_Free(CpuSymbolsList);
967 CpuSymbolsList = NULL;
968 }
969 return DEBUGGER_CMDDONE;
970 }
971
972 /* get offsets */
973 offsets[0] = 0;
974 for (i = 0; i < ARRAYSIZE(offsets); i++) {
975 if (i+2 < nArgc) {
976 int dummy;
977 Eval_Expression(psArgs[i+2], &(offsets[i]), &dummy, listtype==TYPE_DSP);
978 } else {
979 /* default to first (text) offset */
980 offsets[i] = offsets[0];
981 }
982 }
983
984 if (strcmp(file, "prg") == 0) {
985 file = CurrentProgramPath;
986 if (!file) {
987 fprintf(stderr, "ERROR: no program loaded (through GEMDOS HD emu)!\n");
988 return DEBUGGER_CMDDONE;
989 }
990 }
991 list = Symbols_Load(file, offsets, maxaddr);
992 if (list) {
993 if (listtype == TYPE_CPU) {
994 Symbols_Free(CpuSymbolsList);
995 CpuSymbolsList = list;
996 } else {
997 Symbols_Free(DspSymbolsList);
998 DspSymbolsList = list;
999 }
1000 } else {
1001 DebugUI_PrintCmdHelp(psArgs[0]);
1002 }
1003 return DEBUGGER_CMDDONE;
1004 }
1005