|
|
1.1 root 1: /*
2: * Hatari - symbols.c
3: *
1.1.1.9 ! root 4: * Copyright (C) 2010-2017 by Eero Tamminen
1.1 root 5: *
1.1.1.5 root 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.
1.1 root 8: *
9: * symbols.c - Hatari debugger symbol/address handling; parsing, sorting,
10: * matching, TAB completion support etc.
11: *
1.1.1.5 root 12: * Symbol/address information is read either from:
1.1.1.9 ! root 13: * - A program file's DRI/GST or a.out format symbol table, or
1.1.1.5 root 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.
1.1 root 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>
1.1.1.5 root 27: #include <SDL_endian.h>
1.1 root 28: #include "main.h"
1.1.1.7 root 29: #include "file.h"
30: #include "options.h"
1.1 root 31: #include "symbols.h"
32: #include "debugui.h"
33: #include "debug_priv.h"
1.1.1.5 root 34: #include "debugInfo.h"
1.1 root 35: #include "evaluate.h"
1.1.1.7 root 36: #include "configuration.h"
1.1.1.9 ! root 37: #include "a.out.h"
1.1 root 38:
39: typedef struct {
40: char *name;
41: Uint32 address;
42: symtype_t type;
43: } symbol_t;
44:
45: typedef struct {
1.1.1.5 root 46: int symbols; /* initial symbol count */
1.1.1.9 ! root 47: int namecount; /* final symbol count */
! 48: int codecount; /* TEXT symbol address count */
! 49: int datacount; /* DATA/BSS symbol address count */
! 50: symbol_t *addresses; /* TEXT + DATA/BSS items sorted by address */
! 51: symbol_t *names; /* all items sorted by symbol name */
! 52: char *strtab;
1.1 root 53: } symbol_list_t;
54:
1.1.1.5 root 55: typedef struct {
56: Uint32 offset;
57: Uint32 end;
58: } prg_section_t;
59:
1.1 root 60:
61: /* how many characters the symbol name can have.
62: * NOTE: change also sscanf width arg if you change this!!!
63: */
64: #define MAX_SYM_SIZE 32
65:
1.1.1.9 ! root 66: /* Magic used to denote different symbol table formats */
! 67: #define SYMBOL_FORMAT_GNU 0x474E555f /* "MiNT" */
! 68: #define SYMBOL_FORMAT_MINT 0x4D694E54 /* "GNU_" */
! 69: #define SYMBOL_FORMAT_DRI 0x0
! 70:
1.1 root 71:
72: /* TODO: add symbol name/address file names to configuration? */
73: static symbol_list_t *CpuSymbolsList;
74: static symbol_list_t *DspSymbolsList;
75:
1.1.1.6 root 76: /* path for last loaded program (through GEMDOS HD emulation) */
77: static char *CurrentProgramPath;
1.1.1.9 ! root 78: /* whether current symbols were loaded from a program file */
1.1.1.6 root 79: static bool SymbolsAreForProgram;
1.1.1.9 ! root 80: /* prevent repeated failing on every debugger invocation */
1.1.1.6 root 81: static bool AutoLoadFailed;
82:
1.1 root 83:
84: /* ------------------ load and free functions ------------------ */
85:
86: /**
1.1.1.9 ! root 87: * return true if given symbol name is object/library/file name
! 88: */
! 89: static bool is_obj_file(const char *name)
! 90: {
! 91: int len = strlen(name);
! 92: /* object (.a or .o) / file name? */
! 93: if (len > 2 && ((name[len-2] == '.' && (name[len-1] == 'a' || name[len-1] == 'o')) || strchr(name, '/'))) {
! 94: return true;
! 95: }
! 96: return false;
! 97: }
! 98:
! 99: /**
! 100: * compare function for qsort() to sort according to
! 101: * symbol type & address. Text section symbols will
! 102: * be sorted first.
1.1 root 103: */
104: static int symbols_by_address(const void *s1, const void *s2)
105: {
1.1.1.9 ! root 106: const symbol_t *sym1 = (const symbol_t*)s1;
! 107: const symbol_t *sym2 = (const symbol_t*)s2;
1.1 root 108:
1.1.1.9 ! root 109: /* separate TEXT type addresses from others */
! 110: if (sym1->type != sym2->type) {
! 111: if (sym1->type == SYMTYPE_TEXT) {
! 112: return -1;
! 113: }
! 114: if (sym2->type == SYMTYPE_TEXT) {
! 115: return 1;
! 116: }
! 117: }
! 118: /* then sort by address */
! 119: if (sym1->address < sym2->address) {
1.1 root 120: return -1;
121: }
1.1.1.9 ! root 122: if (sym1->address > sym2->address) {
1.1 root 123: return 1;
124: }
125: return 0;
126: }
127:
128: /**
1.1.1.9 ! root 129: * compare function for qsort() to sort according to
! 130: * symbol name & address
1.1 root 131: */
132: static int symbols_by_name(const void *s1, const void *s2)
133: {
1.1.1.9 ! root 134: const symbol_t *sym1 = (const symbol_t*)s1;
! 135: const symbol_t *sym2 = (const symbol_t*)s2;
1.1 root 136: int ret;
137:
1.1.1.9 ! root 138: /* first by name */
! 139: ret = strcmp(sym1->name, sym2->name);
! 140: if (ret) {
! 141: return ret;
! 142: }
! 143: /* then by address */
! 144: return (sym1->address - sym2->address);
! 145: }
! 146:
! 147: /**
! 148: * Check for duplicate addresses in symbol list
! 149: * (called separately for TEXT & non-TEXT symbols)
! 150: * Return number of duplicates
! 151: */
! 152: static int symbols_check_addresses(const symbol_t *syms, int count)
! 153: {
! 154: int i, j, dups = 0;
! 155:
! 156: for (i = 0; i < (count - 1); i++)
! 157: {
! 158: /* absolute symbols have values, not addresses */
! 159: if (syms[i].type == SYMTYPE_ABS) {
! 160: continue;
! 161: }
! 162: for (j = i + 1; j < count && syms[i].address == syms[j].address; j++) {
! 163: if (syms[j].type == SYMTYPE_ABS) {
! 164: continue;
! 165: }
! 166: /* ASCII symbol files contain also object file addresses,
! 167: * those will often have the same address as the first symbol
! 168: * in given object -> no point warning about them
! 169: */
! 170: if (is_obj_file(syms[i].name) || is_obj_file(syms[j].name)) {
! 171: continue;
! 172: }
! 173: fprintf(stderr, "WARNING: symbols '%s' & '%s' have the same 0x%x address\n",
! 174: syms[i].name, syms[j].name, syms[i].address);
! 175: dups++;
! 176: i = j;
! 177: }
1.1 root 178: }
1.1.1.9 ! root 179: return dups;
1.1 root 180: }
181:
1.1.1.9 ! root 182: /**
! 183: * Check for duplicate names in symbol list
! 184: * Return number of duplicates
! 185: */
! 186: static int symbols_check_names(const symbol_t *syms, int count)
! 187: {
! 188: int i, j, dups = 0;
! 189:
! 190: for (i = 0; i < (count - 1); i++)
! 191: {
! 192: for (j = i + 1; j < count && strcmp(syms[i].name, syms[j].name) == 0; j++) {
! 193: /* this is common case for object files having different sections */
! 194: if (syms[i].type != syms[j].type && is_obj_file(syms[i].name)) {
! 195: continue;
! 196: }
! 197: fprintf(stderr, "WARNING: addresses 0x%x & 0x%x have the same '%s' name\n",
! 198: syms[i].address, syms[j].address, syms[i].name);
! 199: dups++;
! 200: i = j;
! 201: }
! 202: }
! 203: return dups;
! 204: }
1.1 root 205:
206: /**
1.1.1.5 root 207: * Allocate symbol list & names for given number of items.
208: * Return allocated list or NULL on failure.
209: */
210: static symbol_list_t* symbol_list_alloc(int symbols)
211: {
212: symbol_list_t *list;
213:
214: if (!symbols) {
215: return NULL;
216: }
217: list = calloc(1, sizeof(symbol_list_t));
218: if (list) {
219: list->names = malloc(symbols * sizeof(symbol_t));
220: if (!list->names) {
221: free(list);
222: list = NULL;
223: }
224: }
225: return list;
226: }
227:
228: /**
229: * Free symbol list & names.
230: */
231: static void symbol_list_free(symbol_list_t *list)
232: {
233: if (list) {
234: if (list->names) {
235: free(list->names);
236: }
237: free(list);
238: }
239: }
240:
241: /**
1.1.1.6 root 242: * Return symbol type identifier char
243: */
244: static char symbol_char(int type)
245: {
246: switch (type) {
247: case SYMTYPE_TEXT: return 'T';
248: case SYMTYPE_DATA: return 'D';
249: case SYMTYPE_BSS: return 'B';
1.1.1.9 ! root 250: case SYMTYPE_ABS: return 'A';
1.1.1.6 root 251: default: return '?';
252: }
253: }
254:
1.1.1.9 ! root 255: /**
! 256: * Return true if symbol name matches internal GCC symbol name,
! 257: * or is object / file name.
! 258: */
! 259: static bool symbol_remove_obj(const char *name)
! 260: {
! 261: static const char *gcc_sym[] = {
! 262: "___gnu_compiled_c",
! 263: "gcc2_compiled."
! 264: };
! 265: int i;
! 266:
! 267: if (is_obj_file(name)) {
! 268: return true;
! 269: }
! 270: /* useless symbols GCC (v2) seems to add to every object? */
! 271: for (i = 0; i < ARRAY_SIZE(gcc_sym); i++) {
! 272: if (strcmp(name, gcc_sym[i]) == 0) {
! 273: return true;
! 274: }
! 275: }
! 276: return false;
! 277: }
! 278:
1.1.1.6 root 279:
280: /**
1.1 root 281: * Load symbols of given type and the symbol address addresses from
1.1.1.5 root 282: * DRI/GST format symbol table, and add given offsets to the addresses:
283: * http://toshyp.atari.org/en/005005.html
1.1 root 284: * Return symbols list or NULL for failure.
285: */
1.1.1.5 root 286: static symbol_list_t* symbols_load_dri(FILE *fp, prg_section_t *sections, symtype_t gettype, Uint32 tablesize)
1.1 root 287: {
1.1.1.9 ! root 288: int i, count, symbols, invalid;
! 289: int notypes, dtypes, locals, ofiles;
1.1.1.5 root 290: prg_section_t *section;
1.1 root 291: symbol_list_t *list;
292: symtype_t symtype;
1.1.1.5 root 293: #define DRI_ENTRY_SIZE 14
294: char name[23];
295: Uint16 symid;
1.1 root 296: Uint32 address;
1.1.1.5 root 297:
1.1.1.6 root 298: if (tablesize % DRI_ENTRY_SIZE || !tablesize) {
1.1.1.5 root 299: fprintf(stderr, "ERROR: invalid DRI/GST symbol table size %d!\n", tablesize);
300: return NULL;
301: }
302: symbols = tablesize / DRI_ENTRY_SIZE;
303: if (!(list = symbol_list_alloc(symbols))) {
304: return NULL;
305: }
306:
1.1.1.9 ! root 307: invalid = dtypes = notypes = ofiles = locals = count = 0;
1.1.1.5 root 308: for (i = 1; i <= symbols; i++) {
309: /* read DRI symbol table slot */
310: if (fread(name, 8, 1, fp) != 1 ||
311: fread(&symid, sizeof(symid), 1, fp) != 1 ||
312: fread(&address, sizeof(address), 1, fp) != 1) {
313: break;
314: }
315: address = SDL_SwapBE32(address);
316: symid = SDL_SwapBE16(symid);
317:
318: /* GST extended DRI symbol format? */
319: if ((symid & 0x0048)) {
320: /* next slot is rest of name */
321: i += 1;
322: if (fread(name+8, 14, 1, fp) != 1) {
323: break;
324: }
325: name[22] = '\0';
326: } else {
327: name[8] = '\0';
328: }
329:
330: /* check section */
331: switch (symid & 0xf00) {
332: case 0x0200:
333: symtype = SYMTYPE_TEXT;
334: section = &(sections[0]);
335: break;
336: case 0x0400:
337: symtype = SYMTYPE_DATA;
338: section = &(sections[1]);
339: break;
340: case 0x0100:
341: symtype = SYMTYPE_BSS;
342: section = &(sections[2]);
343: break;
344: default:
345: if ((symid & 0xe000) == 0xe000) {
346: dtypes++;
347: continue;
348: }
1.1.1.9 ! root 349: if ((symid & 0x4000) == 0x4000) {
! 350: symtype = SYMTYPE_ABS;
! 351: section = NULL;
! 352: break;
! 353: }
1.1.1.5 root 354: fprintf(stderr, "WARNING: ignoring symbol '%s' in slot %d of unknown type 0x%x.\n", name, i, symid);
1.1.1.9 ! root 355: invalid++;
1.1.1.5 root 356: continue;
357: }
358: if (!(gettype & symtype)) {
1.1.1.9 ! root 359: notypes++;
1.1.1.5 root 360: continue;
361: }
362: if (name[0] == '.' && name[1] == 'L') {
363: locals++;
364: continue;
365: }
1.1.1.9 ! root 366: if (symbol_remove_obj(name)) {
1.1.1.5 root 367: ofiles++;
368: continue;
369: }
1.1.1.9 ! root 370: if (section) {
! 371: address += section->offset;
! 372: if (address > section->end) {
! 373: fprintf(stderr, "WARNING: ignoring symbol '%s' of type %c in slot %d with invalid offset 0x%x (>= 0x%x).\n",
! 374: name, symbol_char(symtype), i, address, section->end);
! 375: invalid++;
! 376: continue;
1.1.1.6 root 377: }
1.1.1.5 root 378: }
379: list->names[count].address = address;
380: list->names[count].type = symtype;
381: list->names[count].name = strdup(name);
382: assert(list->names[count].name);
383: count++;
384: }
385: if (i <= symbols) {
386: perror("ERROR: reading symbol failed");
387: symbol_list_free(list);
1.1 root 388: return NULL;
389: }
1.1.1.9 ! root 390: list->symbols = symbols;
! 391: list->namecount = count;
! 392:
! 393: /* skip verbose output when symbol loading is forced */
! 394: if (ConfigureParams.Debugger.bSymbolsResident) {
! 395: return list;
! 396: }
! 397:
! 398: if (invalid) {
! 399: fprintf(stderr, "NOTE: ignored %d invalid symbols.\n", invalid);
! 400: }
1.1.1.5 root 401: if (dtypes) {
1.1.1.9 ! root 402: fprintf(stderr, "NOTE: ignored %d debugging symbols.\n", dtypes);
! 403: }
! 404: if (notypes) {
! 405: fprintf(stderr, "NOTE: ignored %d other unwanted symbol types.\n", notypes);
1.1.1.5 root 406: }
407: if (locals) {
408: fprintf(stderr, "NOTE: ignored %d unnamed / local symbols (= name starts with '.L').\n", locals);
409: }
410: if (ofiles) {
411: /* object file path names most likely get truncated and
412: * as result cause unnecessary symbol name conflicts in
413: * addition to object file addresses conflicting with
414: * first symbol in the object file.
415: */
1.1.1.9 ! root 416: fprintf(stderr, "NOTE: ignored %d object symbols (= name has '/', ends in '.[ao]' or is GCC internal).\n", ofiles);
! 417: }
! 418: return list;
! 419: }
! 420:
! 421:
! 422: /**
! 423: * Load symbols of given type and the symbol address addresses from
! 424: * a.out format symbol table, and add given offsets to the addresses:
! 425: * Return symbols list or NULL for failure.
! 426: */
! 427: static symbol_list_t* symbols_load_gnu(FILE *fp, prg_section_t *sections, symtype_t gettype, Uint32 tablesize, Uint32 stroff, Uint32 strsize)
! 428: {
! 429: size_t slots = tablesize / SIZEOF_STRUCT_NLIST;
! 430: size_t i;
! 431: size_t strx;
! 432: unsigned char *p;
! 433: char *name;
! 434: symbol_t *sym;
! 435: symtype_t symtype;
! 436: uint32_t address;
! 437: uint32_t nread;
! 438: symbol_list_t *list;
! 439: unsigned char n_type;
! 440: unsigned char n_other;
! 441: unsigned short n_desc;
! 442: static char dummy[] = "<invalid>";
! 443: int dtypes, locals, ofiles, count, notypes, invalid, weak;
! 444: prg_section_t *section;
! 445:
! 446: if (!(list = symbol_list_alloc(slots))) {
! 447: return NULL;
! 448: }
! 449:
! 450: list->strtab = (char *)malloc(tablesize + strsize);
! 451:
! 452: if (list->strtab == NULL)
! 453: {
! 454: symbol_list_free(list);
! 455: return NULL;
! 456: }
! 457:
! 458: nread = fread(list->strtab, tablesize + strsize, 1, fp);
! 459: if (nread != 1)
! 460: {
! 461: perror("ERROR: reading symbols failed");
! 462: symbol_list_free(list);
! 463: return NULL;
! 464: }
! 465:
! 466: p = (unsigned char *)list->strtab;
! 467: sym = list->names;
! 468:
! 469: weak = invalid = dtypes = notypes = ofiles = locals = count = 0;
! 470: for (i = 0; i < slots; i++)
! 471: {
! 472: strx = SDL_SwapBE32(*(Uint32*)p);
! 473: p += 4;
! 474: n_type = *p++;
! 475: n_other = *p++;
! 476: n_desc = SDL_SwapBE16(*(Uint16*)p);
! 477: p += 2;
! 478: address = SDL_SwapBE32(*(Uint32*)p);
! 479: p += 4;
! 480: name = dummy;
! 481: if (!strx) {
! 482: invalid++;
! 483: continue;
! 484: }
! 485: if (strx >= strsize) {
! 486: fprintf(stderr, "symbol name index %x out of range\n", (unsigned int)strx);
! 487: invalid++;
! 488: continue;
! 489: }
! 490: name = list->strtab + strx + stroff;
! 491:
! 492: if (n_type & N_STAB)
! 493: {
! 494: dtypes++;
! 495: continue;
! 496: }
! 497: section = NULL;
! 498: switch (n_type & (N_TYPE|N_EXT))
! 499: {
! 500: case N_UNDF:
! 501: case N_UNDF|N_EXT:
! 502: /* shouldn't happen here */
! 503: weak++;
! 504: continue;
! 505: case N_ABS:
! 506: case N_ABS|N_EXT:
! 507: symtype = SYMTYPE_ABS;
! 508: break;
! 509: case N_TEXT:
! 510: case N_TEXT|N_EXT:
! 511: symtype = SYMTYPE_TEXT;
! 512: section = &(sections[0]);
! 513: break;
! 514: case N_DATA:
! 515: case N_DATA|N_EXT:
! 516: symtype = SYMTYPE_DATA;
! 517: section = &(sections[1]);
! 518: break;
! 519: case N_BSS:
! 520: case N_BSS|N_EXT:
! 521: case N_COMM:
! 522: case N_COMM|N_EXT:
! 523: symtype = SYMTYPE_BSS;
! 524: section = &(sections[2]);
! 525: break;
! 526: case N_FN: /* filenames, not object addresses? */
! 527: dtypes++;
! 528: continue;
! 529: case N_SIZE:
! 530: case N_WARNING:
! 531: case N_SETA:
! 532: case N_SETT:
! 533: case N_SETD:
! 534: case N_SETB:
! 535: case N_SETV:
! 536: dtypes++;
! 537: continue;
! 538: case N_WEAKU:
! 539: case N_WEAKT:
! 540: case N_WEAKD:
! 541: case N_WEAKB:
! 542: weak++;
! 543: continue;
! 544: default:
! 545: fprintf(stderr, "WARNING: ignoring symbol '%s' in slot %u of unknown type 0x%x.\n", name, (unsigned int)i, n_type);
! 546: invalid++;
! 547: continue;
! 548: }
! 549: /*
! 550: * the value of a common symbol is its size, not its address:
! 551: */
! 552: if (((n_type & N_TYPE) == N_COMM) ||
! 553: (((n_type & N_EXT) && (n_type & N_TYPE) == N_UNDF && address != 0)))
! 554: {
! 555: /* if we ever want to know a symbols size, get that here */
! 556: fprintf(stderr, "WARNING: ignoring common symbol '%s' in slot %u.\n", name, (unsigned int)i);
! 557: dtypes++;
! 558: continue;
! 559: }
! 560: if (!(gettype & symtype)) {
! 561: notypes++;
! 562: continue;
! 563: }
! 564: if (name[0] == '.' && name[1] == 'L') {
! 565: locals++;
! 566: continue;
! 567: }
! 568: if (symbol_remove_obj(name)) {
! 569: ofiles++;
! 570: continue;
! 571: }
! 572: if (section) {
! 573: address += sections[0].offset; /* all GNU symbol addresses are TEXT relative */
! 574: if (address > section->end) {
! 575: fprintf(stderr, "WARNING: ignoring symbol '%s' of type %c in slot %u with invalid offset 0x%x (>= 0x%x).\n",
! 576: name, symbol_char(symtype), (unsigned int)i, address, section->end);
! 577: invalid++;
! 578: continue;
! 579: }
! 580: }
! 581: sym->address = address;
! 582: sym->type = symtype;
! 583: sym->name = name;
! 584: sym++;
! 585: count++;
! 586: (void) n_desc;
! 587: (void) n_other;
! 588: }
! 589: list->symbols = slots;
! 590: list->namecount = count;
! 591:
! 592: /* skip verbose output when symbol loading is forced */
! 593: if (ConfigureParams.Debugger.bSymbolsResident) {
! 594: return list;
! 595: }
! 596:
! 597: if (invalid) {
! 598: fprintf(stderr, "NOTE: ignored %d invalid symbols.\n", invalid);
! 599: }
! 600: if (dtypes) {
! 601: fprintf(stderr, "NOTE: ignored %d debugging symbols.\n", dtypes);
! 602: }
! 603: if (weak) {
! 604: fprintf(stderr, "NOTE: ignored %d weak / undefined symbols.\n", weak);
! 605: }
! 606: if (notypes) {
! 607: fprintf(stderr, "NOTE: ignored %d other unwanted symbol types.\n", notypes);
! 608: }
! 609: if (locals) {
! 610: fprintf(stderr, "NOTE: ignored %d unnamed / local symbols (= name starts with '.L').\n", locals);
! 611: }
! 612: if (ofiles) {
! 613: /* object file path names most likely get truncated and
! 614: * as result cause unnecessary symbol name conflicts in
! 615: * addition to object file addresses conflicting with
! 616: * first symbol in the object file.
! 617: */
! 618: fprintf(stderr, "NOTE: ignored %d object symbols (= name has '/', ends in '.[ao]' or is GCC internal).\n", ofiles);
1.1.1.5 root 619: }
620: return list;
621: }
622:
1.1.1.7 root 623:
624: /**
625: * Print program header information.
626: * Return false for unrecognized symbol table type.
627: */
628: static bool symbols_print_prg_info(Uint32 tabletype, Uint32 prgflags, Uint16 relocflag)
629: {
630: static const struct {
631: Uint32 flag;
632: const char *name;
633: } flags[] = {
634: { 0x0001, "FASTLOAD" },
635: { 0x0002, "TTRAMLOAD" },
636: { 0x0004, "TTRAMMEM" },
637: { 0x0008, "MINIMUM" }, /* MagiC */
638: { 0x1000, "SHAREDTEXT" }
639: };
640: const char *info;
641: int i;
642:
643: switch (tabletype) {
1.1.1.9 ! root 644: case SYMBOL_FORMAT_MINT: /* "MiNT" */
1.1.1.7 root 645: info = "GCC/MiNT executable, GST symbol table";
646: break;
1.1.1.9 ! root 647: case SYMBOL_FORMAT_GNU: /* "GNU_" */
! 648: info = "GCC/MiNT executable, a.out symbol table";
! 649: break;
! 650: case SYMBOL_FORMAT_DRI:
1.1.1.7 root 651: info = "TOS executable, DRI / GST symbol table";
652: break;
653: default:
654: fprintf(stderr, "ERROR: unknown executable type 0x%x!\n", tabletype);
655: return false;
656: }
657: fprintf(stderr, "%s, reloc=%d, program flags:", info, relocflag);
658: /* bit flags */
1.1.1.8 root 659: for (i = 0; i < ARRAY_SIZE(flags); i++) {
1.1.1.7 root 660: if (prgflags & flags[i].flag) {
661: fprintf(stderr, " %s", flags[i].name);
662: }
663: }
664: /* memory protection flags */
665: switch((prgflags >> 4) & 3) {
666: case 0: info = "PRIVATE"; break;
667: case 1: info = "GLOBAL"; break;
668: case 2: info = "SUPER"; break;
669: case 3: info = "READONLY"; break;
670: }
671: fprintf(stderr, " %s (0x%x)\n", info, prgflags);
672: return true;
673: }
674:
1.1.1.5 root 675: /**
676: * Parse program header and use symbol table format specific loader
677: * loader function to load the symbols.
678: * Return symbols list or NULL for failure.
679: */
680: static symbol_list_t* symbols_load_binary(FILE *fp, symtype_t gettype)
681: {
1.1.1.6 root 682: Uint32 textlen, datalen, bsslen, start, tablesize, tabletype, prgflags;
1.1.1.5 root 683: prg_section_t sections[3];
684: int offset, reads = 0;
1.1.1.6 root 685: Uint16 relocflag;
686: symbol_list_t* symbols;
1.1.1.9 ! root 687: Uint32 symoff = 0;
! 688: Uint32 stroff = 0;
! 689: Uint32 strsize = 0;
1.1.1.5 root 690:
691: /* get TEXT, DATA & BSS section sizes */
1.1.1.6 root 692: fseek(fp, 2, SEEK_SET);
1.1.1.5 root 693: reads += fread(&textlen, sizeof(textlen), 1, fp);
694: textlen = SDL_SwapBE32(textlen);
695: reads += fread(&datalen, sizeof(datalen), 1, fp);
696: datalen = SDL_SwapBE32(datalen);
697: reads += fread(&bsslen, sizeof(bsslen), 1, fp);
698: bsslen = SDL_SwapBE32(bsslen);
699:
700: /* get symbol table size & type and check that all reads succeeded */
701: reads += fread(&tablesize, sizeof(tablesize), 1, fp);
702: tablesize = SDL_SwapBE32(tablesize);
703: reads += fread(&tabletype, sizeof(tabletype), 1, fp);
704: tabletype = SDL_SwapBE32(tabletype);
1.1.1.6 root 705:
706: /* get program header and whether there's reloc table */
707: reads += fread(&prgflags, sizeof(prgflags), 1, fp);
708: prgflags = SDL_SwapBE32(prgflags);
709: reads += fread(&relocflag, sizeof(relocflag), 1, fp);
710: relocflag = SDL_SwapBE32(relocflag);
711:
712: if (reads != 7) {
1.1.1.5 root 713: fprintf(stderr, "ERROR: program header reading failed!\n");
714: return NULL;
715: }
1.1.1.9 ! root 716: /*
! 717: * check for GNU-style symbol table in aexec header
! 718: */
! 719: if (tabletype == SYMBOL_FORMAT_MINT) { /* MiNT */
! 720: Uint32 magic1, magic2;
! 721: Uint32 dummy;
! 722: Uint32 a_text, a_data, a_bss, a_syms, a_entry, a_trsize, a_drsize;
! 723: Uint32 g_tparel_pos, g_tparel_size, g_stkpos, g_symbol_format;
! 724:
! 725: reads = fread(&magic1, sizeof(magic1), 1, fp);
! 726: magic1 = SDL_SwapBE32(magic1);
! 727: reads += fread(&magic2, sizeof(magic2), 1, fp);
! 728: magic2 = SDL_SwapBE32(magic2);
! 729: if (reads == 2 &&
! 730: ((magic1 == 0x283a001a && magic2 == 0x4efb48fa) || /* Original binutils: move.l 28(pc),d4; jmp 0(pc,d4.l) */
! 731: (magic1 == 0x203a001a && magic2 == 0x4efb08fa))) { /* binutils >= 2.18-mint-20080209: move.l 28(pc),d0; jmp 0(pc,d0.l) */
! 732: reads += fread(&dummy, sizeof(dummy), 1, fp); /* skip a_info */
! 733: reads += fread(&a_text, sizeof(a_text), 1, fp);
! 734: a_text = SDL_SwapBE32(a_text);
! 735: reads += fread(&a_data, sizeof(a_data), 1, fp);
! 736: a_data = SDL_SwapBE32(a_data);
! 737: reads += fread(&a_bss, sizeof(a_bss), 1, fp);
! 738: a_bss = SDL_SwapBE32(a_bss);
! 739: reads += fread(&a_syms, sizeof(a_syms), 1, fp);
! 740: a_syms = SDL_SwapBE32(a_syms);
! 741: reads += fread(&a_entry, sizeof(a_entry), 1, fp);
! 742: a_entry = SDL_SwapBE32(a_entry);
! 743: reads += fread(&a_trsize, sizeof(a_trsize), 1, fp);
! 744: a_trsize = SDL_SwapBE32(a_trsize);
! 745: reads += fread(&a_drsize, sizeof(a_drsize), 1, fp);
! 746: a_drsize = SDL_SwapBE32(a_drsize);
! 747: reads += fread(&g_tparel_pos, sizeof(g_tparel_pos), 1, fp);
! 748: g_tparel_pos = SDL_SwapBE32(g_tparel_pos);
! 749: reads += fread(&g_tparel_size, sizeof(g_tparel_size), 1, fp);
! 750: g_tparel_size = SDL_SwapBE32(g_tparel_size);
! 751: reads += fread(&g_stkpos, sizeof(g_stkpos), 1, fp);
! 752: g_stkpos = SDL_SwapBE32(g_stkpos);
! 753: reads += fread(&g_symbol_format, sizeof(g_symbol_format), 1, fp);
! 754: g_symbol_format = SDL_SwapBE32(g_symbol_format);
! 755: if (g_symbol_format == 0)
! 756: {
! 757: tabletype = SYMBOL_FORMAT_GNU;
! 758: }
! 759: if ((a_text + (256 - 28)) != textlen)
! 760: fprintf(stderr, "warning: inconsistent text segment size %08x != %08x\n", textlen, a_text + (256 - 28));
! 761: if (a_data != datalen)
! 762: fprintf(stderr, "warning: inconsistent data segment size %08x != %08x\n", datalen, a_data);
! 763: if (a_bss != bsslen)
! 764: fprintf(stderr, "warning: inconsistent bss segment size %08x != %08x\n", bsslen, a_bss);
! 765: /*
! 766: * the symbol table size in the GEMDOS header includes the string table,
! 767: * the symbol table size in the exec header does not.
! 768: */
! 769: if (tabletype == SYMBOL_FORMAT_GNU)
! 770: {
! 771: strsize = tablesize - a_syms;
! 772: tablesize = a_syms;
! 773: stroff = a_syms;
! 774: }
! 775:
! 776: textlen = a_text + (256 - 28);
! 777: datalen = a_data;
! 778: bsslen = a_bss;
! 779: symoff = 0x100 + /* sizeof(extended exec header) */
! 780: a_text +
! 781: a_data +
! 782: a_trsize +
! 783: a_drsize;
! 784: }
! 785: }
1.1.1.7 root 786: if (!symbols_print_prg_info(tabletype, prgflags, relocflag)) {
787: return NULL;
788: }
789: if (!tablesize) {
790: fprintf(stderr, "ERROR: symbol table missing from the program!\n");
791: return NULL;
792: }
1.1.1.5 root 793:
794: /* offsets & max sizes for running program TEXT/DATA/BSS section symbols */
795: start = DebugInfo_GetTEXT();
796: if (!start) {
797: fprintf(stderr, "ERROR: no valid program basepage!\n");
798: return NULL;
799: }
800: sections[0].offset = start;
801: sections[0].end = start + textlen;
1.1.1.9 ! root 802: if (DebugInfo_GetTEXTEnd() != sections[0].end) {
1.1.1.5 root 803: fprintf(stderr, "ERROR: given program TEXT section size differs from one in RAM!\n");
804: return NULL;
805: }
806:
807: start = DebugInfo_GetDATA();
1.1.1.6 root 808: sections[1].offset = start;
1.1.1.9 ! root 809: sections[1].end = start + datalen;
1.1.1.5 root 810:
811: start = DebugInfo_GetBSS();
1.1.1.6 root 812: sections[2].offset = start;
1.1.1.9 ! root 813: sections[2].end = start + bsslen;
1.1.1.5 root 814:
1.1.1.9 ! root 815: if (sections[0].end != sections[1].offset) {
! 816: fprintf(stderr, "WARNIGN: DATA start doesn't match TEXT start + size!\n");
! 817: }
! 818: if (sections[1].end != sections[2].offset) {
! 819: fprintf(stderr, "WARNIGN: BSS start doesn't match DATA start + size!\n");
1.1.1.6 root 820: }
821:
1.1.1.9 ! root 822: if (tabletype == SYMBOL_FORMAT_GNU) {
! 823: /* go to start of symbol table */
! 824: offset = symoff;
! 825: if (fseek(fp, offset, SEEK_SET) < 0) {
! 826: perror("ERROR: seeking to symbol table failed");
! 827: return NULL;
! 828: }
! 829: fprintf(stderr, "Trying to load symbol table at offset 0x%x...\n", offset);
! 830: symbols = symbols_load_gnu(fp, sections, gettype, tablesize, stroff, strsize);
! 831: } else {
! 832: /* go to start of symbol table */
! 833: offset = 0x1C + textlen + datalen;
! 834: if (fseek(fp, offset, SEEK_SET) < 0) {
! 835: perror("ERROR: seeking to symbol table failed");
! 836: return NULL;
! 837: }
! 838: fprintf(stderr, "Trying to load symbol table at offset 0x%x...\n", offset);
1.1.1.6 root 839: symbols = symbols_load_dri(fp, sections, gettype, tablesize);
840: }
841: return symbols;
1.1.1.5 root 842: }
843:
844: /**
845: * Load symbols of given type and the symbol address addresses from
846: * the given ASCII file and add given offsets to the addresses.
847: * Return symbols list or NULL for failure.
848: */
849: static symbol_list_t* symbols_load_ascii(FILE *fp, Uint32 *offsets, Uint32 maxaddr, symtype_t gettype)
850: {
851: symbol_list_t *list;
852: char symchar, buffer[128], name[MAX_SYM_SIZE+1], *buf;
853: int count, line, symbols;
854: Uint32 address, offset;
855: symtype_t symtype;
1.1 root 856:
857: /* count content lines */
1.1.1.9 ! root 858: line = symbols = 0;
1.1 root 859: while (fgets(buffer, sizeof(buffer), fp)) {
1.1.1.9 ! root 860: line++;
! 861:
1.1.1.3 root 862: /* skip comments (AHCC SYM file comments start with '*') */
863: if (*buffer == '#' || *buffer == '*') {
1.1 root 864: continue;
865: }
866: /* skip empty lines */
1.1.1.6 root 867: for (buf = buffer; isspace((unsigned char)*buf); buf++);
1.1 root 868: if (!*buf) {
869: continue;
870: }
1.1.1.9 ! root 871: if (!isxdigit(*buf)) {
! 872: fprintf(stderr, "ERROR: line %d doesn't start with an address.\n", line);
! 873: return NULL;
! 874: }
1.1 root 875: symbols++;
876: }
1.1.1.6 root 877: if (!symbols) {
878: fprintf(stderr, "ERROR: no symbols.\n");
1.1.1.9 ! root 879: return NULL;
1.1.1.6 root 880: }
881:
1.1 root 882: fseek(fp, 0, SEEK_SET);
883:
1.1.1.5 root 884: /* allocate space for symbol list & names */
885: if (!(list = symbol_list_alloc(symbols))) {
1.1 root 886: return NULL;
887: }
888:
889: /* read symbols */
890: count = 0;
891: for (line = 1; fgets(buffer, sizeof(buffer), fp); line++) {
1.1.1.3 root 892: /* skip comments (AHCC SYM file comments start with '*') */
893: if (*buffer == '#' || *buffer == '*') {
1.1 root 894: continue;
895: }
896: /* skip empty lines */
1.1.1.6 root 897: for (buf = buffer; isspace((unsigned char)*buf); buf++);
1.1 root 898: if (!*buf) {
899: continue;
900: }
901: assert(count < symbols); /* file not modified in meanwhile? */
1.1.1.5 root 902: if (sscanf(buffer, "%x %c %32[0-9A-Za-z_.-]s", &address, &symchar, name) != 3) {
903: fprintf(stderr, "WARNING: syntax error on line %d, skipping.\n", line);
1.1 root 904: continue;
905: }
1.1.1.6 root 906: switch (toupper((unsigned char)symchar)) {
1.1.1.5 root 907: case 'T':
908: symtype = SYMTYPE_TEXT;
909: offset = offsets[0];
910: break;
911: case 'O': /* AHCC type for _StkSize etc */
912: case 'D':
913: symtype = SYMTYPE_DATA;
914: offset = offsets[1];
915: break;
916: case 'B':
917: symtype = SYMTYPE_BSS;
918: offset = offsets[2];
919: break;
1.1.1.9 ! root 920: case 'A':
! 921: symtype = SYMTYPE_ABS;
! 922: offset = 0;
! 923: break;
1.1 root 924: default:
1.1.1.5 root 925: fprintf(stderr, "WARNING: unrecognized symbol type '%c' on line %d, skipping.\n", symchar, line);
1.1 root 926: continue;
927: }
928: if (!(gettype & symtype)) {
929: continue;
930: }
1.1.1.5 root 931: address += offset;
932: if (address > maxaddr) {
933: fprintf(stderr, "WARNING: invalid address 0x%x on line %d, skipping.\n", address, line);
934: continue;
935: }
1.1 root 936: list->names[count].address = address;
937: list->names[count].type = symtype;
938: list->names[count].name = strdup(name);
939: assert(list->names[count].name);
940: count++;
941: }
1.1.1.5 root 942: list->symbols = symbols;
1.1.1.9 ! root 943: list->namecount = count;
1.1.1.5 root 944: return list;
945: }
1.1 root 946:
1.1.1.5 root 947: /**
1.1.1.9 ! root 948: * Remove full duplicates from the sorted names list
! 949: * and trim the allocation to remaining symbols
! 950: */
! 951: static void symbols_trim_names(symbol_list_t* list)
! 952: {
! 953: symbol_t *sym = list->names;
! 954: int i, next, count, dups;
! 955:
! 956: count = list->namecount;
! 957: for (dups = i = 0; i < count - 1; i++) {
! 958: next = i + 1;
! 959: if (strcmp(sym[i].name, sym[next].name) == 0 &&
! 960: sym[i].address == sym[next].address &&
! 961: sym[i].type == sym[next].type) {
! 962: /* remove duplicate */
! 963: memmove(sym+i, sym+next, (count-next) * sizeof(symbol_t));
! 964: count--;
! 965: dups++;
! 966: }
! 967: }
! 968: if (dups || list->namecount < list->symbols) {
! 969: list->names = realloc(list->names, i * sizeof(symbol_t));
! 970: assert(list->names);
! 971: list->namecount = i;
! 972: }
! 973: if (dups) {
! 974: fprintf(stderr, "WARNING: removed %d complete symbol duplicates\n", dups);
! 975: }
! 976: }
! 977:
! 978: /**
! 979: * Separate TEXT symbols from other symbols in address list.
! 980: */
! 981: static void symbols_trim_addresses(symbol_list_t* list)
! 982: {
! 983: symbol_t *sym = list->addresses;
! 984: int i;
! 985:
! 986: for (i = 0; i < list->namecount; i++) {
! 987: if (sym[i].type != SYMTYPE_TEXT) {
! 988: break;
! 989: }
! 990: }
! 991: list->codecount = i;
! 992: list->datacount = list->namecount - i;
! 993: }
! 994:
! 995: /**
1.1.1.5 root 996: * Load symbols of given type and the symbol address addresses from
997: * the given file and add given offsets to the addresses.
998: * Return symbols list or NULL for failure.
999: */
1000: static symbol_list_t* Symbols_Load(const char *filename, Uint32 *offsets, Uint32 maxaddr)
1001: {
1002: symbol_list_t *list;
1003: FILE *fp;
1004:
1.1.1.7 root 1005: if (!File_Exists(filename)) {
1006: fprintf(stderr, "ERROR: file '%s' doesn't exist or isn't readable!\n", filename);
1.1.1.5 root 1007: return NULL;
1008: }
1.1.1.7 root 1009: if (Opt_IsAtariProgram(filename)) {
1.1.1.6 root 1010: const char *last = CurrentProgramPath;
1011: if (!last) {
1012: /* "pc=text" breakpoint used as point for loading program symbols gives false hits during bootup */
1013: fprintf(stderr, "WARNING: no program loaded yet (through GEMDOS HD emu)!\n");
1014: } else if (strcmp(last, filename) != 0) {
1015: fprintf(stderr, "WARNING: given program doesn't match last program executed by GEMDOS HD emulation:\n\t%s\n", last);
1.1.1.5 root 1016: }
1.1.1.6 root 1017: fprintf(stderr, "Reading symbols from program '%s' symbol table...\n", filename);
1.1.1.7 root 1018: fp = fopen(filename, "rb");
1.1.1.5 root 1019: list = symbols_load_binary(fp, SYMTYPE_ALL);
1.1.1.6 root 1020: SymbolsAreForProgram = true;
1.1.1.5 root 1021: } else {
1022: fprintf(stderr, "Reading 'nm' style ASCII symbols from '%s'...\n", filename);
1.1.1.7 root 1023: fp = fopen(filename, "r");
1.1.1.5 root 1024: list = symbols_load_ascii(fp, offsets, maxaddr, SYMTYPE_ALL);
1.1.1.6 root 1025: SymbolsAreForProgram = false;
1.1.1.5 root 1026: }
1027: fclose(fp);
1028:
1029: if (!list) {
1.1.1.6 root 1030: fprintf(stderr, "ERROR: reading symbols from '%s' failed!\n", filename);
1.1.1.5 root 1031: return NULL;
1032: }
1033:
1.1.1.9 ! root 1034: if (!list->namecount) {
! 1035: fprintf(stderr, "ERROR: no valid symbols in '%s', loading failed!\n", filename);
! 1036: symbol_list_free(list);
! 1037: return NULL;
1.1 root 1038: }
1039:
1.1.1.9 ! root 1040: /* sort and trim names list */
! 1041: qsort(list->names, list->namecount, sizeof(symbol_t), symbols_by_name);
! 1042: symbols_trim_names(list);
! 1043:
1.1 root 1044: /* copy name list to address list */
1.1.1.9 ! root 1045: list->addresses = malloc(list->namecount * sizeof(symbol_t));
1.1 root 1046: assert(list->addresses);
1.1.1.9 ! root 1047: memcpy(list->addresses, list->names, list->namecount * sizeof(symbol_t));
1.1 root 1048:
1.1.1.9 ! root 1049: /* sort address list and trim to contain just TEXT symbols */
! 1050: qsort(list->addresses, list->namecount, sizeof(symbol_t), symbols_by_address);
! 1051: symbols_trim_addresses(list);
1.1 root 1052:
1.1.1.9 ! root 1053: /* skip verbose output when symbol loading is forced */
! 1054: if (!ConfigureParams.Debugger.bSymbolsResident) {
! 1055: /* check for duplicate names */
! 1056: if (symbols_check_names(list->names, list->namecount)) {
! 1057: fprintf(stderr, "-> Hatari symbol expansion can match only one of the addresses for name duplicates!\n");
! 1058: }
! 1059: /* check for duplicate TEXT & other addresses */
! 1060: if (symbols_check_addresses(list->addresses, list->codecount)) {
! 1061: fprintf(stderr, "-> Hatari profile/dissassembly will show only one of the TEXT symbols for given address!\n");
! 1062: }
! 1063: if (symbols_check_addresses(list->addresses + list->codecount, list->datacount)) {
! 1064: fprintf(stderr, "-> Hatari dissassembly will show only one of the symbols for given address!\n");
! 1065: }
! 1066: }
! 1067:
! 1068: fprintf(stderr, "Loaded %d symbols (%d TEXT) from '%s'.\n",
! 1069: list->namecount, list->codecount, filename);
1.1 root 1070: return list;
1071: }
1072:
1073:
1074: /**
1075: * Free read symbols.
1076: */
1077: static void Symbols_Free(symbol_list_t* list)
1078: {
1.1.1.5 root 1079: int i;
1.1 root 1080:
1081: if (!list) {
1082: return;
1083: }
1.1.1.9 ! root 1084: assert(list->namecount);
! 1085: if (list->strtab) {
! 1086: free(list->strtab);
! 1087: list->strtab = NULL;
! 1088: } else {
! 1089: for (i = 0; i < list->namecount; i++) {
! 1090: free(list->names[i].name);
! 1091: }
1.1 root 1092: }
1093: free(list->addresses);
1094: free(list->names);
1.1.1.5 root 1095:
1096: /* catch use of freed list */
1097: list->addresses = NULL;
1.1.1.9 ! root 1098: list->codecount = 0;
! 1099: list->datacount = 0;
1.1.1.5 root 1100: list->names = NULL;
1.1.1.9 ! root 1101: list->namecount = 0;
1.1 root 1102: free(list);
1103: }
1104:
1105:
1106: /* ---------------- symbol name completion support ------------------ */
1107:
1108: /**
1109: * Helper for symbol name completion and finding their addresses.
1110: * STATE = 0 -> different text from previous one.
1111: * Return (copy of) next name or NULL if no matches.
1112: */
1113: static char* Symbols_MatchByName(symbol_list_t* list, symtype_t symtype, const char *text, int state)
1114: {
1.1.1.5 root 1115: static int i, len;
1.1 root 1116: const symbol_t *entry;
1117:
1118: if (!list) {
1119: return NULL;
1120: }
1121:
1122: if (!state) {
1123: /* first match */
1124: len = strlen(text);
1125: i = 0;
1126: }
1127:
1128: /* next match */
1129: entry = list->names;
1.1.1.9 ! root 1130: while (i < list->namecount) {
1.1 root 1131: if ((entry[i].type & symtype) &&
1132: strncmp(entry[i].name, text, len) == 0) {
1133: return strdup(entry[i++].name);
1134: } else {
1135: i++;
1136: }
1137: }
1138: return NULL;
1139: }
1140:
1141: /**
1142: * Readline match callbacks for CPU symbol name completion.
1143: * STATE = 0 -> different text from previous one.
1144: * Return next match or NULL if no matches.
1145: */
1146: char* Symbols_MatchCpuAddress(const char *text, int state)
1147: {
1148: return Symbols_MatchByName(CpuSymbolsList, SYMTYPE_ALL, text, state);
1149: }
1150: char* Symbols_MatchCpuCodeAddress(const char *text, int state)
1151: {
1.1.1.9 ! root 1152: if (ConfigureParams.Debugger.bMatchAllSymbols) {
! 1153: return Symbols_MatchByName(CpuSymbolsList, SYMTYPE_ALL, text, state);
! 1154: } else {
! 1155: return Symbols_MatchByName(CpuSymbolsList, SYMTYPE_TEXT, text, state);
! 1156: }
1.1 root 1157: }
1158: char* Symbols_MatchCpuDataAddress(const char *text, int state)
1159: {
1.1.1.9 ! root 1160: if (ConfigureParams.Debugger.bMatchAllSymbols) {
! 1161: return Symbols_MatchByName(CpuSymbolsList, SYMTYPE_ALL, text, state);
! 1162: } else {
! 1163: return Symbols_MatchByName(CpuSymbolsList, SYMTYPE_DATA|SYMTYPE_BSS, text, state);
! 1164: }
1.1 root 1165: }
1166:
1167: /**
1168: * Readline match callback for DSP symbol name completion.
1169: * STATE = 0 -> different text from previous one.
1170: * Return next match or NULL if no matches.
1171: */
1172: char* Symbols_MatchDspAddress(const char *text, int state)
1173: {
1174: return Symbols_MatchByName(DspSymbolsList, SYMTYPE_ALL, text, state);
1175: }
1176: char* Symbols_MatchDspCodeAddress(const char *text, int state)
1177: {
1178: return Symbols_MatchByName(DspSymbolsList, SYMTYPE_TEXT, text, state);
1179: }
1180: char* Symbols_MatchDspDataAddress(const char *text, int state)
1181: {
1182: return Symbols_MatchByName(DspSymbolsList, SYMTYPE_DATA|SYMTYPE_BSS, text, state);
1183: }
1184:
1185:
1186: /* ---------------- symbol name -> address search ------------------ */
1187:
1188: /**
1.1.1.9 ! root 1189: * Binary search symbol of given type by name.
1.1 root 1190: * Return symbol if name matches, zero otherwise.
1191: */
1.1.1.9 ! root 1192: static const symbol_t* Symbols_SearchByName(symbol_t* entries, int count, symtype_t symtype, const char *name)
1.1 root 1193: {
1194: /* left, right, middle */
1195: int l, r, m, dir;
1196:
1197: /* bisect */
1198: l = 0;
1.1.1.9 ! root 1199: r = count - 1;
1.1 root 1200: do {
1201: m = (l+r) >> 1;
1202: dir = strcmp(entries[m].name, name);
1203: if (dir == 0 && (entries[m].type & symtype)) {
1204: return &(entries[m]);
1205: }
1206: if (dir > 0) {
1207: r = m-1;
1208: } else {
1209: l = m+1;
1210: }
1211: } while (l <= r);
1212: return NULL;
1213: }
1214:
1215: /**
1.1.1.9 ! root 1216: * Set given symbol's address to variable and return true if one
! 1217: * was found from given list.
1.1 root 1218: */
1.1.1.9 ! root 1219: static bool Symbols_GetAddress(symbol_list_t* list, symtype_t symtype, const char *name, Uint32 *addr)
1.1 root 1220: {
1221: const symbol_t *entry;
1.1.1.9 ! root 1222: if (!(list && list->names)) {
! 1223: return false;
! 1224: }
! 1225: entry = Symbols_SearchByName(list->names, list->namecount, symtype, name);
1.1 root 1226: if (entry) {
1227: *addr = entry->address;
1228: return true;
1229: }
1230: return false;
1231: }
1.1.1.9 ! root 1232: bool Symbols_GetCpuAddress(symtype_t symtype, const char *name, Uint32 *addr)
! 1233: {
! 1234: return Symbols_GetAddress(CpuSymbolsList, symtype, name, addr);
! 1235: }
1.1 root 1236: bool Symbols_GetDspAddress(symtype_t symtype, const char *name, Uint32 *addr)
1237: {
1.1.1.9 ! root 1238: return Symbols_GetAddress(DspSymbolsList, symtype, name, addr);
1.1 root 1239: }
1240:
1241:
1242: /* ---------------- symbol address -> name search ------------------ */
1243:
1244: /**
1.1.1.9 ! root 1245: * Binary search symbol by address in given sorted list.
1.1.1.5 root 1246: * Return symbol index if address matches, -1 otherwise.
1.1 root 1247: */
1.1.1.9 ! root 1248: static int Symbols_SearchByAddress(symbol_t* entries, int count, Uint32 addr)
1.1 root 1249: {
1250: /* left, right, middle */
1251: int l, r, m;
1252: Uint32 curr;
1253:
1254: /* bisect */
1255: l = 0;
1.1.1.9 ! root 1256: r = count - 1;
1.1 root 1257: do {
1258: m = (l+r) >> 1;
1259: curr = entries[m].address;
1260: if (curr == addr) {
1.1.1.5 root 1261: return m;
1.1 root 1262: }
1263: if (curr > addr) {
1264: r = m-1;
1265: } else {
1266: l = m+1;
1267: }
1268: } while (l <= r);
1.1.1.5 root 1269: return -1;
1.1 root 1270: }
1271:
1272: /**
1.1.1.9 ! root 1273: * Search symbol in given list by type & address.
! 1274: * Return symbol name if there's a match, NULL otherwise.
! 1275: * TEXT symbols will be matched before other symbol types.
1.1 root 1276: * Returned name is valid only until next Symbols_* function call.
1277: */
1.1.1.9 ! root 1278: static const char* Symbols_GetByAddress(symbol_list_t* list, Uint32 addr, symtype_t type)
1.1 root 1279: {
1.1.1.9 ! root 1280: if (!(list && list->addresses)) {
1.1.1.5 root 1281: return NULL;
1282: }
1.1.1.9 ! root 1283: if (type & SYMTYPE_TEXT) {
! 1284: int i = Symbols_SearchByAddress(list->addresses, list->codecount, addr);
! 1285: if (i >= 0) {
! 1286: return list->addresses[i].name;
! 1287: }
! 1288: }
! 1289: if (type & ~SYMTYPE_TEXT) {
! 1290: int i = Symbols_SearchByAddress(list->addresses + list->codecount, list->datacount, addr);
! 1291: if (i >= 0) {
! 1292: return list->addresses[list->codecount + i].name;
! 1293: }
! 1294: }
! 1295: return NULL;
1.1 root 1296: }
1.1.1.9 ! root 1297: const char* Symbols_GetByCpuAddress(Uint32 addr, symtype_t type)
1.1 root 1298: {
1.1.1.9 ! root 1299: return Symbols_GetByAddress(CpuSymbolsList, addr, type);
1.1.1.5 root 1300: }
1.1.1.9 ! root 1301: const char* Symbols_GetByDspAddress(Uint32 addr, symtype_t type)
1.1.1.5 root 1302: {
1.1.1.9 ! root 1303: return Symbols_GetByAddress(DspSymbolsList, addr, type);
1.1.1.5 root 1304: }
1305:
1306: /**
1.1.1.9 ! root 1307: * Search given list for TEXT symbol by address.
1.1.1.5 root 1308: * Return symbol index if address matches, -1 otherwise.
1309: */
1.1.1.9 ! root 1310: static int Symbols_GetCodeIndex(symbol_list_t* list, Uint32 addr)
! 1311: {
! 1312: if (!list) {
! 1313: return -1;
! 1314: }
! 1315: return Symbols_SearchByAddress(list->addresses, list->codecount, addr);
! 1316: }
! 1317: int Symbols_GetCpuCodeIndex(Uint32 addr)
! 1318: {
! 1319: return Symbols_GetCodeIndex(CpuSymbolsList, addr);
! 1320: }
! 1321: int Symbols_GetDspCodeIndex(Uint32 addr)
1.1.1.5 root 1322: {
1.1.1.9 ! root 1323: return Symbols_GetCodeIndex(DspSymbolsList, addr);
1.1 root 1324: }
1325:
1.1.1.5 root 1326: /**
1.1.1.9 ! root 1327: * Return how many TEXT symbols are loaded/available
1.1.1.5 root 1328: */
1.1.1.9 ! root 1329: int Symbols_CpuCodeCount(void)
1.1.1.5 root 1330: {
1.1.1.9 ! root 1331: return (CpuSymbolsList ? CpuSymbolsList->codecount : 0);
1.1.1.5 root 1332: }
1.1.1.9 ! root 1333: int Symbols_DspCodeCount(void)
1.1.1.5 root 1334: {
1.1.1.9 ! root 1335: return (DspSymbolsList ? DspSymbolsList->codecount : 0);
1.1.1.5 root 1336: }
1.1 root 1337:
1.1.1.6 root 1338: /* ---------------- symbol showing ------------------ */
1.1 root 1339:
1340: /**
1341: * Show symbols from given list with paging.
1342: */
1.1.1.9 ! root 1343: static void Symbols_Show(symbol_list_t* list, const char *sortcmd)
1.1 root 1344: {
1345: symbol_t *entry, *entries;
1.1.1.9 ! root 1346: const char *symtype, *sorttype;
! 1347: int i, rows, count;
1.1 root 1348: char symchar;
1.1.1.9 ! root 1349: char line[80];
1.1 root 1350:
1351: if (!list) {
1352: fprintf(stderr, "No symbols!\n");
1353: return;
1354: }
1355:
1.1.1.9 ! root 1356: if (strcmp("code", sortcmd) == 0) {
! 1357: sorttype = "address";
1.1 root 1358: entries = list->addresses;
1.1.1.9 ! root 1359: count = list->codecount;
! 1360: symtype = " TEXT";
! 1361: } else if (strcmp("data", sortcmd) == 0) {
! 1362: sorttype = "address";
! 1363: entries = list->addresses + list->codecount;
! 1364: count = list->datacount;
! 1365: symtype = " DATA/BSS/ABS";
1.1 root 1366: } else {
1.1.1.9 ! root 1367: sorttype = "name";
1.1 root 1368: entries = list->names;
1.1.1.9 ! root 1369: count = list->namecount;
! 1370: symtype = "";
1.1 root 1371: }
1.1.1.9 ! root 1372: rows = DebugUI_GetPageLines(ConfigureParams.Debugger.nSymbolLines, 20);
1.1 root 1373:
1.1.1.9 ! root 1374: for (entry = entries, i = 0; i < count; i++, entry++) {
1.1.1.6 root 1375: symchar = symbol_char(entry->type);
1.1 root 1376: fprintf(stderr, "0x%08x %c %s\n",
1377: entry->address, symchar, entry->name);
1.1.1.9 ! root 1378: if ((i + 1) % rows == 0) {
1.1 root 1379: fprintf(stderr, "--- q to exit listing, just enter to continue --- ");
1.1.1.9 ! root 1380: if (fgets(line, sizeof(line), stdin) == NULL ||
! 1381: toupper(line[0]) == 'Q') {
! 1382: break;
1.1 root 1383: }
1384: }
1385: }
1.1.1.9 ! root 1386: fprintf(stderr, "%d %s%s symbols (of %d) sorted by %s.\n", i,
! 1387: (list == CpuSymbolsList ? "CPU" : "DSP"),
! 1388: symtype, count, sorttype);
1.1 root 1389: }
1390:
1.1.1.6 root 1391: /* ---------------- binary load handling ------------------ */
1392:
1393: /**
1.1.1.9 ! root 1394: * If symbols are set resident, load them if they aren't yet loaded,
! 1395: * otherwise remove them along with program path.
! 1396: *
! 1397: * Called on GEMDOS reset and when program terminates
! 1398: * (unless terminated with Ptermres()).
1.1.1.6 root 1399: */
1400: void Symbols_RemoveCurrentProgram(void)
1401: {
1402: if (CurrentProgramPath) {
1.1.1.9 ! root 1403: if (ConfigureParams.Debugger.bSymbolsResident) {
! 1404: Symbols_LoadCurrentProgram();
! 1405: }
1.1.1.6 root 1406: free(CurrentProgramPath);
1407: CurrentProgramPath = NULL;
1408:
1.1.1.9 ! root 1409: if (CpuSymbolsList && SymbolsAreForProgram && !ConfigureParams.Debugger.bSymbolsResident) {
1.1.1.6 root 1410: Symbols_Free(CpuSymbolsList);
1.1.1.9 ! root 1411: fprintf(stderr, "Program exit, removing its symbols.\n");
1.1.1.6 root 1412: CpuSymbolsList = NULL;
1413: }
1414: }
1415: AutoLoadFailed = false;
1416: }
1417:
1418: /**
1.1.1.9 ! root 1419: * Set last opened program path and remove symbols if they
! 1420: * didn't get remove beforehand.
! 1421: *
! 1422: * Called on first Fopen() after Pexec().
1.1.1.6 root 1423: */
1.1.1.7 root 1424: void Symbols_ChangeCurrentProgram(const char *path)
1.1.1.6 root 1425: {
1.1.1.7 root 1426: if (Opt_IsAtariProgram(path)) {
1.1.1.9 ! root 1427: if (ConfigureParams.Debugger.bSymbolsResident) {
! 1428: if (CpuSymbolsList && SymbolsAreForProgram) {
! 1429: Symbols_Free(CpuSymbolsList);
! 1430: fprintf(stderr, "Program launch, removing previous program symbols.\n");
! 1431: CpuSymbolsList = NULL;
! 1432: }
! 1433: if (CurrentProgramPath) {
! 1434: free(CurrentProgramPath);
! 1435: }
! 1436: } else {
! 1437: Symbols_RemoveCurrentProgram();
! 1438: }
1.1.1.6 root 1439: CurrentProgramPath = strdup(path);
1440: }
1441: }
1442:
1443: /**
1444: * Load symbols for last opened program.
1.1.1.9 ! root 1445: * Called when debugger is invoked.
1.1.1.6 root 1446: */
1447: void Symbols_LoadCurrentProgram(void)
1448: {
1449: /* symbols already loaded, program path missing or previous load failed? */
1450: if (CpuSymbolsList || !CurrentProgramPath || AutoLoadFailed) {
1451: return;
1452: }
1453: CpuSymbolsList = Symbols_Load(CurrentProgramPath, NULL, 0);
1454: if (!CpuSymbolsList) {
1455: AutoLoadFailed = true;
1456: } else {
1457: AutoLoadFailed = false;
1458: }
1459: }
1460:
1461: /* ---------------- command parsing ------------------ */
1462:
1463: /**
1464: * Readline match callback to list symbols subcommands.
1465: * STATE = 0 -> different text from previous one.
1466: * Return next match or NULL if no matches.
1467: */
1468: char *Symbols_MatchCommand(const char *text, int state)
1469: {
1470: static const char* subs[] = {
1.1.1.9 ! root 1471: "code", "data", "free", "match", "name", "prg", "resident"
1.1.1.6 root 1472: };
1.1.1.8 root 1473: return DebugUI_MatchHelper(subs, ARRAY_SIZE(subs), text, state);
1.1.1.6 root 1474: }
1475:
1.1 root 1476: const char Symbols_Description[] =
1.1.1.9 ! root 1477: "<|code|data|name> -- list symbols\n"
! 1478: "\tsymbols <prg|free> -- load/free symbols\n"
! 1479: "\tsymbols <filename> [<T offset> [<D offset> <B offset>]]\n"
! 1480: "\tsymbols <resident|match> -- toggle symbol options\n"
1.1.1.5 root 1481: "\n"
1.1.1.9 ! root 1482: "\t'name' command lists the currently loaded symbols, sorted by name.\n"
! 1483: "\t'code' and 'data' commands list them sorted by address; 'code' lists\n"
! 1484: "\tonly TEXT symbols, 'data' lists DATA/BSS/ABS symbols.\n"
1.1.1.5 root 1485: "\n"
1.1.1.9 ! root 1486: "\tBy default, symbols are loaded from the currently executing program's\n"
! 1487: "\tbinary when entering the debugger, IF program is started through\n"
! 1488: "\tGEMDOS HD, and they're freed when that program terminates.\n"
1.1.1.5 root 1489: "\n"
1.1.1.9 ! root 1490: "\tThat corresponds to 'prg' command which loads (DRI/GST or a.out\n"
! 1491: "\tformat) symbol table from the last program executed through\n"
! 1492: "\tthe GEMDOS HD emulation.\n"
! 1493: "\n"
! 1494: "\t'free' command removes the loaded symbols.\n"
! 1495: "\n"
! 1496: "\tIf program lacks symbols, or it's not run through the GEMDOS HD\n"
! 1497: "\temulation, user can ask symbols to be loaded from a file that's\n"
! 1498: "\tan unstripped version of the binary. Or from an ASCII symbols file\n"
! 1499: "\tproduced by the 'nm' and (Hatari) 'gst2ascii' tools.\n"
! 1500: "\n"
! 1501: "\tWith ASCII symbols files, given non-zero offset(s) are added to\n"
! 1502: "\tthe text (T), data (D) and BSS (B) symbols. Typically one uses\n"
! 1503: "\tTEXT variable, sometimes also DATA & BSS, variables for this.\n"
! 1504: "\n"
! 1505: "\t'resident' command toggles whether debugger will load symbols\n"
! 1506: "\tbefore program terminates (if user hasn't entered debugger before\n"
! 1507: "\tthis), and defers symbol freeing until another program is started.\n"
! 1508: "\n"
! 1509: "\t'match' command toggles whether TAB completion matches all symbols,\n"
! 1510: "\tor only symbol types that should be relevant for given command.";
! 1511:
1.1 root 1512:
1513: /**
1514: * Handle debugger 'symbols' command and its arguments
1515: */
1516: int Symbols_Command(int nArgc, char *psArgs[])
1517: {
1.1.1.9 ! root 1518: enum { TYPE_CPU, TYPE_DSP } listtype;
1.1.1.5 root 1519: Uint32 offsets[3], maxaddr;
1.1 root 1520: symbol_list_t *list;
1521: const char *file;
1.1.1.5 root 1522: int i;
1.1 root 1523:
1524: if (strcmp("dspsymbols", psArgs[0]) == 0) {
1525: listtype = TYPE_DSP;
1526: maxaddr = 0xFFFF;
1.1.1.9 ! root 1527: } else {
1.1 root 1528: listtype = TYPE_CPU;
1.1.1.7 root 1529: if ( ConfigureParams.System.bAddressSpace24 )
1530: maxaddr = 0x00FFFFFF;
1531: else
1532: maxaddr = 0xFFFFFFFF;
1.1.1.9 ! root 1533: }
! 1534: if (nArgc < 2) {
! 1535: file = "name";
1.1 root 1536: } else {
1.1.1.9 ! root 1537: file = psArgs[1];
1.1 root 1538: }
1.1.1.9 ! root 1539:
! 1540: /* toggle whether to autoload symbols on program start,
! 1541: * and keep them until next program start (=resident),
! 1542: * OR only loading them when entering the debugger and
! 1543: * freeing them when program terminates.
! 1544: */
! 1545: if (strcmp(file, "resident") == 0) {
! 1546: ConfigureParams.Debugger.bSymbolsResident = !ConfigureParams.Debugger.bSymbolsResident;
! 1547: if (ConfigureParams.Debugger.bSymbolsResident) {
! 1548: Symbols_LoadCurrentProgram();
! 1549: fprintf(stderr, "Program symbols will always be loaded (with reduced warnings)\nand kept resident until next program start.\n");
! 1550: } else {
! 1551: fprintf(stderr, "Program symbols will be removed when program terminates.\n");
! 1552: if (!CurrentProgramPath) {
! 1553: /* make sure normal autoloading isn't prevented */
! 1554: Symbols_Free(CpuSymbolsList);
! 1555: CpuSymbolsList = NULL;
! 1556: }
! 1557: }
! 1558: return DEBUGGER_CMDDONE;
! 1559: }
! 1560: /* toggling whether all or only specific symbols types get TAB completed */
! 1561: if (strcmp(file, "match") == 0) {
! 1562: ConfigureParams.Debugger.bMatchAllSymbols = !ConfigureParams.Debugger.bMatchAllSymbols;
! 1563: if (ConfigureParams.Debugger.bMatchAllSymbols) {
! 1564: fprintf(stderr, "Matching all symbols types.\n");
! 1565: } else {
! 1566: fprintf(stderr, "Matching only symbols (most) relevant for given command.\n");
! 1567: }
! 1568: return DEBUGGER_CMDDONE;
1.1 root 1569: }
1570:
1571: /* handle special cases */
1.1.1.9 ! root 1572: if (strcmp(file, "name") == 0 || strcmp(file, "code") == 0 || strcmp(file, "data") == 0) {
1.1 root 1573: list = (listtype == TYPE_DSP ? DspSymbolsList : CpuSymbolsList);
1574: Symbols_Show(list, file);
1575: return DEBUGGER_CMDDONE;
1576: }
1577: if (strcmp(file, "free") == 0) {
1578: if (listtype == TYPE_DSP) {
1579: Symbols_Free(DspSymbolsList);
1580: DspSymbolsList = NULL;
1581: } else {
1582: Symbols_Free(CpuSymbolsList);
1583: CpuSymbolsList = NULL;
1584: }
1585: return DEBUGGER_CMDDONE;
1586: }
1.1.1.5 root 1587:
1588: /* get offsets */
1589: offsets[0] = 0;
1.1.1.8 root 1590: for (i = 0; i < ARRAY_SIZE(offsets); i++) {
1.1.1.5 root 1591: if (i+2 < nArgc) {
1592: int dummy;
1593: Eval_Expression(psArgs[i+2], &(offsets[i]), &dummy, listtype==TYPE_DSP);
1594: } else {
1595: /* default to first (text) offset */
1596: offsets[i] = offsets[0];
1597: }
1.1 root 1598: }
1599:
1.1.1.5 root 1600: if (strcmp(file, "prg") == 0) {
1.1.1.6 root 1601: file = CurrentProgramPath;
1.1.1.5 root 1602: if (!file) {
1603: fprintf(stderr, "ERROR: no program loaded (through GEMDOS HD emu)!\n");
1604: return DEBUGGER_CMDDONE;
1605: }
1606: }
1607: list = Symbols_Load(file, offsets, maxaddr);
1.1 root 1608: if (list) {
1609: if (listtype == TYPE_CPU) {
1610: Symbols_Free(CpuSymbolsList);
1611: CpuSymbolsList = list;
1612: } else {
1613: Symbols_Free(DspSymbolsList);
1614: DspSymbolsList = list;
1615: }
1616: } else {
1617: DebugUI_PrintCmdHelp(psArgs[0]);
1618: }
1619: return DEBUGGER_CMDDONE;
1620: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.