|
|
1.1 root 1: /*
2: * Hatari - gst2ascii.c
3: *
1.1.1.5 ! root 4: * Copyright (C) 2013-2017 by Eero Tamminen
1.1 root 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: *
1.1.1.5 ! root 9: * Convert DRI/GST and a.out format symbol table in a binary into ASCII symbols
! 10: * file accepted by Hatari debugger and its profiler data post-processor.
! 11: * This will also allow manual editing of the symbol table (removing irrelevant
! 12: * labels or adding missing symbols for functions).
1.1 root 13: */
14:
15: #include <ctype.h>
16: #include <stdio.h>
17: #include <string.h>
18: #include <stdlib.h>
19: #include <stdint.h>
20: #include <stdbool.h>
21: #if defined(__MINT__) /* assume MiNT/lib is always big-endian */
22: # define SDL_SwapBE16(x) x
23: # define SDL_SwapBE32(x) x
24: #else
25: # include <SDL_endian.h>
26: #endif
27: #include <assert.h>
1.1.1.5 ! root 28: #include "../../src/debug/a.out.h"
1.1 root 29:
30: typedef enum {
31: SYMTYPE_TEXT = 1,
32: SYMTYPE_DATA = 2,
1.1.1.5 ! root 33: SYMTYPE_BSS = 4,
! 34: SYMTYPE_ABS = 8
1.1 root 35: } symtype_t;
36:
37: typedef struct {
38: char *name;
39: uint32_t address;
40: symtype_t type;
41: } symbol_t;
42:
43: typedef struct {
44: int count; /* final symbol count */
45: int symbols; /* initial symbol count */
46: symbol_t *addresses; /* items sorted by address */
47: symbol_t *names; /* items sorted by symbol name */
1.1.1.5 ! root 48: char *strtab;
1.1 root 49: } symbol_list_t;
50:
51: typedef struct {
52: uint32_t offset;
53: uint32_t end;
54: } prg_section_t;
55:
1.1.1.5 ! root 56: /* Magic used to denote different symbol table formats */
! 57: #define SYMBOL_FORMAT_GNU 0x474E555f /* "MiNT" */
! 58: #define SYMBOL_FORMAT_MINT 0x4D694E54 /* "GNU_" */
! 59: #define SYMBOL_FORMAT_DRI 0x0
! 60:
! 61: /* Magic identifying Atari programs */
! 62: #define ATARI_PROGRAM_MAGIC 0x601A
! 63:
! 64:
1.1 root 65: /* ------------------ options & usage ------------------ */
66:
67: #ifdef WIN32
68: #define PATHSEP '\\'
69: #else
70: #define PATHSEP '/'
71: #endif
72:
1.1.1.4 root 73: #define ARRAY_SIZE(x) (int)(sizeof(x)/sizeof(x[0]))
1.1 root 74:
75: static const char *PrgPath;
76:
77: static struct {
78: symtype_t notypes;
79: bool no_local;
1.1.1.5 ! root 80: bool no_obj;
1.1 root 81: bool sort_name;
82: } Options;
83:
84: /*
1.1.1.5 ! root 85: * Show program usage, given error message, and exit
1.1 root 86: */
1.1.1.5 ! root 87: static void usage(const char *msg)
1.1 root 88: {
89: const struct {
90: const char opt;
91: const char *desc;
92: } OptInfo[] = {
1.1.1.5 ! root 93: { 'a', "no absolute symbols (are values, not addresses)" },
1.1 root 94: { 'b', "no BSS symbols" },
95: { 'd', "no DATA symbols" },
96: { 't', "no TEXT symbols" },
97: { 'l', "no local (.L*) symbols" },
98: { 'o', "no object symbols (filenames or GCC internals)" },
1.1.1.5 ! root 99: { 'n', "sort by name (not address)" },
1.1 root 100: };
101: const char *name;
102: int i;
103:
104: if ((name = strrchr(PrgPath, PATHSEP))) {
105: name++;
106: } else {
107: name = PrgPath;
108: }
109: fprintf(stderr,
110: "\n"
111: "Usage: %s [options] <Atari program>\n"
112: "\n"
1.1.1.5 ! root 113: "Outputs given program (DRI/GST or a.out format) symbol table\n"
! 114: "content in ASCII format accepted by Hatari debugger and its\n"
! 115: "profiler data post-processor.\n"
! 116: "\n"
! 117: "All symbol addresses are output as TEXT relative, i.e. you need\n"
! 118: "to give only that as section address for the Hatari debugger:\n"
! 119: "\tsymbols <filename> TEXT\n"
1.1 root 120: "\n"
121: "Options:\n", name);
1.1.1.4 root 122: for (i = 0; i < ARRAY_SIZE(OptInfo); i++) {
1.1 root 123: fprintf(stderr, "\t-%c\t%s\n", OptInfo[i].opt, OptInfo[i].desc);
124: }
125: if (msg) {
126: fprintf(stderr, "\nERROR: %s!\n", msg);
127: }
1.1.1.5 ! root 128:
! 129: exit(msg != NULL);
1.1 root 130: }
131:
132: /* ------------------ load and free functions ------------------ */
133:
134: /**
135: * compare function for qsort() to sort according to symbol address
136: */
137: static int symbols_by_address(const void *s1, const void *s2)
138: {
139: uint32_t addr1 = ((const symbol_t*)s1)->address;
140: uint32_t addr2 = ((const symbol_t*)s2)->address;
141:
142: if (addr1 < addr2) {
143: return -1;
144: }
145: if (addr1 > addr2) {
146: return 1;
147: }
148: return 0;
149: }
150:
151: /**
152: * compare function for qsort() to sort according to symbol name
153: */
154: static int symbols_by_name(const void *s1, const void *s2)
155: {
156: const char* name1 = ((const symbol_t*)s1)->name;
157: const char* name2 = ((const symbol_t*)s2)->name;
158: int ret;
159:
160: ret = strcmp(name1, name2);
161: return ret;
162: }
163:
164: /**
1.1.1.5 ! root 165: * check for duplicate addresses in symbol list
! 166: */
! 167: static void symbols_check_addresses(const symbol_t *syms, int count)
! 168: {
! 169: int i, j;
! 170:
! 171: for (i = 0; i < (count - 1); i++)
! 172: {
! 173: /* absolute symbols have values, not addresses */
! 174: if (syms[i].type == SYMTYPE_ABS) {
! 175: continue;
! 176: }
! 177: for (j = i + 1; j < count && syms[i].address == syms[j].address; j++) {
! 178: if (syms[j].type == SYMTYPE_ABS) {
! 179: continue;
! 180: }
! 181: fprintf(stderr, "WARNING: symbols '%s' & '%s' have the same 0x%x address.\n",
! 182: syms[i].name, syms[j].name, syms[i].address);
! 183: i = j;
! 184: }
! 185: }
! 186: }
! 187:
! 188: /**
! 189: * check for duplicate names in symbol list
! 190: */
! 191: static void symbols_check_names(const symbol_t *syms, int count)
! 192: {
! 193: int i, j;
! 194:
! 195: for (i = 0; i < (count - 1); i++)
! 196: {
! 197: for (j = i + 1; j < count && strcmp(syms[i].name, syms[j].name) == 0; j++) {
! 198: fprintf(stderr, "WARNING: addresses 0x%x & 0x%x have the same '%s' name.\n",
! 199: syms[i].address, syms[j].address, syms[i].name);
! 200: i = j;
! 201: }
! 202: }
! 203: }
! 204:
! 205: /**
1.1 root 206: * Allocate symbol list & names for given number of items.
207: * Return allocated list or NULL on failure.
208: */
209: static symbol_list_t* symbol_list_alloc(int symbols)
210: {
211: symbol_list_t *list;
212:
213: if (!symbols) {
214: return NULL;
215: }
216: list = calloc(1, sizeof(symbol_list_t));
217: if (list) {
218: list->names = malloc(symbols * sizeof(symbol_t));
219: if (!list->names) {
220: free(list);
221: list = NULL;
222: }
223: }
224: return list;
225: }
226:
227: /**
228: * Free symbol list & names.
229: */
230: static void symbol_list_free(symbol_list_t *list)
231: {
232: if (list) {
233: if (list->names) {
234: free(list->names);
235: }
236: free(list);
237: }
238: }
239:
240: /**
1.1.1.2 root 241: * Return symbol type identifier char
242: */
243: static char symbol_char(int type)
244: {
245: switch (type) {
246: case SYMTYPE_TEXT: return 'T';
247: case SYMTYPE_DATA: return 'D';
248: case SYMTYPE_BSS: return 'B';
1.1.1.5 ! root 249: case SYMTYPE_ABS: return 'A';
1.1.1.2 root 250: default: return '?';
251: }
252: }
253:
1.1.1.5 ! root 254: /**
! 255: * Return true if symbol name matches internal GCC symbol name,
! 256: * or is object / file name.
! 257: */
! 258: static bool symbol_remove_obj(const char *name)
! 259: {
! 260: static const char *gcc_sym[] = {
! 261: "___gnu_compiled_c",
! 262: "gcc2_compiled."
! 263: };
! 264: int i, len = strlen(name);
! 265: /* object (.a or .o) / file name? */
! 266: if (len > 2 && ((name[len-2] == '.' && (name[len-1] == 'a' || name[len-1] == 'o')) || strchr(name, '/'))) {
! 267: return true;
! 268: }
! 269: /* useless symbols GCC (v2) seems to add to every object? */
! 270: for (i = 0; i < ARRAY_SIZE(gcc_sym); i++) {
! 271: if (strcmp(name, gcc_sym[i]) == 0) {
! 272: return true;
! 273: }
! 274: }
! 275: return false;
! 276: }
! 277:
1.1.1.2 root 278:
279: /**
1.1 root 280: * Load symbols of given type and the symbol address addresses from
281: * DRI/GST format symbol table, and add given offsets to the addresses:
282: * http://toshyp.atari.org/en/005005.html
283: * Return symbols list or NULL for failure.
284: */
285: static symbol_list_t* symbols_load_dri(FILE *fp, prg_section_t *sections, uint32_t tablesize)
286: {
1.1.1.5 ! root 287: int i, count, symbols, invalid;
1.1 root 288: int notypes, dtypes, locals, ofiles;
289: prg_section_t *section;
290: symbol_list_t *list;
291: symtype_t symtype;
292: #define DRI_ENTRY_SIZE 14
293: char name[23];
294: uint16_t symid;
295: uint32_t address;
296:
297: if (tablesize % DRI_ENTRY_SIZE) {
298: fprintf(stderr, "ERROR: invalid DRI/GST symbol table size %d!\n", tablesize);
299: return NULL;
300: }
301: symbols = tablesize / DRI_ENTRY_SIZE;
302: if (!(list = symbol_list_alloc(symbols))) {
303: return NULL;
304: }
305:
1.1.1.5 ! root 306: invalid = dtypes = notypes = ofiles = locals = count = 0;
1.1 root 307: for (i = 1; i <= symbols; i++) {
308: /* read DRI symbol table slot */
309: if (fread(name, 8, 1, fp) != 1 ||
310: fread(&symid, sizeof(symid), 1, fp) != 1 ||
311: fread(&address, sizeof(address), 1, fp) != 1) {
312: break;
313: }
314: address = SDL_SwapBE32(address);
315: symid = SDL_SwapBE16(symid);
316:
317: /* GST extended DRI symbol format? */
318: if ((symid & 0x0048)) {
319: /* next slot is rest of name */
320: i += 1;
321: if (fread(name+8, 14, 1, fp) != 1) {
322: break;
323: }
324: name[22] = '\0';
325: } else {
326: name[8] = '\0';
327: }
328:
329: /* check section */
330: switch (symid & 0xf00) {
331: case 0x0200:
332: symtype = SYMTYPE_TEXT;
333: section = &(sections[0]);
334: break;
335: case 0x0400:
336: symtype = SYMTYPE_DATA;
337: section = &(sections[1]);
338: break;
339: case 0x0100:
340: symtype = SYMTYPE_BSS;
341: section = &(sections[2]);
342: break;
343: default:
344: if ((symid & 0xe000) == 0xe000) {
345: dtypes++;
346: continue;
347: }
1.1.1.5 ! root 348: if ((symid & 0x4000) == 0x4000) {
! 349: symtype = SYMTYPE_ABS;
! 350: section = NULL;
! 351: break;
! 352: }
1.1 root 353: fprintf(stderr, "WARNING: ignoring symbol '%s' in slot %d of unknown type 0x%x.\n", name, i, symid);
1.1.1.5 ! root 354: invalid++;
1.1 root 355: continue;
356: }
357: if (Options.notypes & symtype) {
358: notypes++;
359: continue;
360: }
361: if (Options.no_local) {
362: if (name[0] == '.' && name[1] == 'L') {
363: locals++;
364: continue;
365: }
366: }
367: if (Options.no_obj) {
1.1.1.5 ! root 368: if (symbol_remove_obj(name)) {
1.1 root 369: ofiles++;
370: continue;
371: }
372: }
1.1.1.5 ! root 373: if (section) {
! 374: address += section->offset;
! 375: if (address > section->end) {
! 376: fprintf(stderr, "WARNING: ignoring symbol '%s' of type %c in slot %d with invalid offset 0x%x (>= 0x%x).\n",
! 377: name, symbol_char(symtype), i, address, section->end);
! 378: invalid++;
! 379: continue;
1.1.1.2 root 380: }
1.1 root 381: }
382: list->names[count].address = address;
383: list->names[count].type = symtype;
384: list->names[count].name = strdup(name);
385: assert(list->names[count].name);
386: count++;
387: }
388: if (i <= symbols) {
389: perror("ERROR: reading symbol failed");
390: symbol_list_free(list);
391: return NULL;
392: }
1.1.1.5 ! root 393: if (invalid) {
! 394: fprintf(stderr, "NOTE: ignored %d invalid symbols.\n", invalid);
1.1 root 395: }
396: if (dtypes) {
1.1.1.5 ! root 397: fprintf(stderr, "NOTE: ignored %d debugging symbols.\n", dtypes);
! 398: }
! 399: if (notypes) {
! 400: fprintf(stderr, "NOTE: ignored %d other unwanted symbol types.\n", notypes);
1.1 root 401: }
402: if (locals) {
403: fprintf(stderr, "NOTE: ignored %d unnamed / local symbols (= name starts with '.L').\n", locals);
404: }
405: if (ofiles) {
406: /* object file path names most likely get truncated and
407: * as result cause unnecessary symbol name conflicts in
408: * addition to object file addresses conflicting with
409: * first symbol in the object file.
410: */
1.1.1.5 ! root 411: fprintf(stderr, "NOTE: ignored %d object symbols (= name has '/', ends in '.[ao]' or is GCC internal).\n", ofiles);
1.1 root 412: }
413: list->symbols = symbols;
414: list->count = count;
415: return list;
416: }
417:
1.1.1.5 ! root 418:
! 419: /**
! 420: * Load symbols of given type and the symbol address addresses from
! 421: * a.out format symbol table, and add given offsets to the addresses:
! 422: * Return symbols list or NULL for failure.
! 423: */
! 424: static symbol_list_t* symbols_load_gnu(FILE *fp, prg_section_t *sections, Uint32 tablesize, Uint32 stroff, Uint32 strsize)
! 425: {
! 426: size_t slots = tablesize / SIZEOF_STRUCT_NLIST;
! 427: size_t i;
! 428: size_t strx;
! 429: unsigned char *p;
! 430: char *name;
! 431: symbol_t *sym;
! 432: symtype_t symtype;
! 433: uint32_t address;
! 434: uint32_t nread;
! 435: symbol_list_t *list;
! 436: unsigned char n_type;
! 437: unsigned char n_other;
! 438: unsigned short n_desc;
! 439: static char dummy[] = "<invalid>";
! 440: int dtypes, locals, ofiles, count, notypes, invalid, weak;
! 441: prg_section_t *section;
! 442:
! 443: if (!(list = symbol_list_alloc(slots))) {
! 444: return NULL;
! 445: }
! 446:
! 447: list->strtab = (char *)malloc(tablesize + strsize);
! 448:
! 449: if (list->strtab == NULL)
! 450: {
! 451: symbol_list_free(list);
! 452: return NULL;
! 453: }
! 454:
! 455: nread = fread(list->strtab, tablesize + strsize, 1, fp);
! 456: if (nread != 1)
! 457: {
! 458: perror("ERROR: reading symbols failed");
! 459: symbol_list_free(list);
! 460: return NULL;
! 461: }
! 462:
! 463: p = (unsigned char *)list->strtab;
! 464: sym = list->names;
! 465:
! 466: weak = invalid = dtypes = notypes = ofiles = locals = count = 0;
! 467: for (i = 0; i < slots; i++)
! 468: {
! 469: strx = SDL_SwapBE32(*(Uint32*)p);
! 470: p += 4;
! 471: n_type = *p++;
! 472: n_other = *p++;
! 473: n_desc = SDL_SwapBE16(*(Uint16*)p);
! 474: p += 2;
! 475: address = SDL_SwapBE32(*(Uint32*)p);
! 476: p += 4;
! 477: name = dummy;
! 478: if (!strx) {
! 479: invalid++;
! 480: continue;
! 481: }
! 482: if (strx >= strsize) {
! 483: fprintf(stderr, "symbol name index %x out of range\n", (unsigned int)strx);
! 484: invalid++;
! 485: continue;
! 486: }
! 487: name = list->strtab + strx + stroff;
! 488:
! 489: if (n_type & N_STAB)
! 490: {
! 491: dtypes++;
! 492: continue;
! 493: }
! 494: section = NULL;
! 495: switch (n_type & (N_TYPE|N_EXT))
! 496: {
! 497: case N_UNDF:
! 498: case N_UNDF|N_EXT:
! 499: /* shouldn't happen here */
! 500: weak++;
! 501: continue;
! 502: case N_ABS:
! 503: case N_ABS|N_EXT:
! 504: symtype = SYMTYPE_ABS;
! 505: break;
! 506: case N_TEXT:
! 507: case N_TEXT|N_EXT:
! 508: symtype = SYMTYPE_TEXT;
! 509: section = &(sections[0]);
! 510: break;
! 511: case N_DATA:
! 512: case N_DATA|N_EXT:
! 513: symtype = SYMTYPE_DATA;
! 514: section = &(sections[1]);
! 515: break;
! 516: case N_BSS:
! 517: case N_BSS|N_EXT:
! 518: case N_COMM:
! 519: case N_COMM|N_EXT:
! 520: symtype = SYMTYPE_BSS;
! 521: section = &(sections[2]);
! 522: break;
! 523: case N_FN: /* filenames, not object addresses? */
! 524: dtypes++;
! 525: continue;
! 526: case N_SIZE:
! 527: case N_WARNING:
! 528: case N_SETA:
! 529: case N_SETT:
! 530: case N_SETD:
! 531: case N_SETB:
! 532: case N_SETV:
! 533: dtypes++;
! 534: continue;
! 535: case N_WEAKU:
! 536: case N_WEAKT:
! 537: case N_WEAKD:
! 538: case N_WEAKB:
! 539: weak++;
! 540: continue;
! 541: default:
! 542: fprintf(stderr, "WARNING: ignoring symbol '%s' in slot %u of unknown type 0x%x.\n", name, (unsigned int)i, n_type);
! 543: invalid++;
! 544: continue;
! 545: }
! 546: /*
! 547: * the value of a common symbol is its size, not its address:
! 548: */
! 549: if (((n_type & N_TYPE) == N_COMM) ||
! 550: (((n_type & N_EXT) && (n_type & N_TYPE) == N_UNDF && address != 0)))
! 551: {
! 552: /* if we ever want to know a symbols size, get that here */
! 553: fprintf(stderr, "WARNING: ignoring common symbol '%s' in slot %u.\n", sym->name, (unsigned int)i);
! 554: dtypes++;
! 555: continue;
! 556: }
! 557: if (Options.notypes & symtype) {
! 558: notypes++;
! 559: continue;
! 560: }
! 561: if (Options.no_local) {
! 562: if (name[0] == '.' && name[1] == 'L') {
! 563: locals++;
! 564: continue;
! 565: }
! 566: }
! 567: if (Options.no_obj) {
! 568: if (symbol_remove_obj(name)) {
! 569: ofiles++;
! 570: continue;
! 571: }
! 572: }
! 573: if (section) {
! 574: address += sections[0].offset; /* all GNU symbol addresses are TEXT relative */
! 575: if (address > section->end) {
! 576: fprintf(stderr, "WARNING: ignoring symbol '%s' of type %c in slot %u with invalid offset 0x%x (>= 0x%x).\n",
! 577: name, symbol_char(symtype), (unsigned int)i, address, section->end);
! 578: invalid++;
! 579: continue;
! 580: }
! 581: }
! 582: sym->address = address;
! 583: sym->type = symtype;
! 584: sym->name = name;
! 585: sym++;
! 586: count++;
! 587: (void) n_desc;
! 588: (void) n_other;
! 589: }
! 590:
! 591: if (invalid) {
! 592: fprintf(stderr, "NOTE: ignored %d invalid symbols.\n", invalid);
! 593: }
! 594: if (dtypes) {
! 595: fprintf(stderr, "NOTE: ignored %d debugging symbols.\n", dtypes);
! 596: }
! 597: if (weak) {
! 598: fprintf(stderr, "NOTE: ignored %d weak / undefined symbols.\n", weak);
! 599: }
! 600: if (notypes) {
! 601: fprintf(stderr, "NOTE: ignored %d other unwanted symbol types.\n", notypes);
! 602: }
! 603: if (locals) {
! 604: fprintf(stderr, "NOTE: ignored %d unnamed / local symbols (= name starts with '.L').\n", locals);
! 605: }
! 606: if (ofiles) {
! 607: /* object file path names most likely get truncated and
! 608: * as result cause unnecessary symbol name conflicts in
! 609: * addition to object file addresses conflicting with
! 610: * first symbol in the object file.
! 611: */
! 612: fprintf(stderr, "NOTE: ignored %d object symbols (= name has '/', ends in '.[ao]' or is GCC internal).\n", ofiles);
! 613: }
! 614:
! 615: list->symbols = slots;
! 616: list->count = count;
! 617: return list;
! 618: }
! 619:
! 620:
1.1 root 621: /**
1.1.1.3 root 622: * Print program header information.
623: * Return false for unrecognized symbol table type.
624: */
625: static bool symbols_print_prg_info(Uint32 tabletype, Uint32 prgflags, Uint16 relocflag)
626: {
627: static const struct {
628: Uint32 flag;
629: const char *name;
630: } flags[] = {
631: { 0x0001, "FASTLOAD" },
632: { 0x0002, "TTRAMLOAD" },
633: { 0x0004, "TTRAMMEM" },
634: { 0x0008, "MINIMUM" }, /* MagiC */
635: { 0x1000, "SHAREDTEXT" }
636: };
637: const char *info;
638: int i;
639:
640: switch (tabletype) {
1.1.1.5 ! root 641: case SYMBOL_FORMAT_MINT: /* "MiNT" */
1.1.1.3 root 642: info = "GCC/MiNT executable, GST symbol table";
643: break;
1.1.1.5 ! root 644: case SYMBOL_FORMAT_GNU: /* "GNU_" */
! 645: info = "GCC/MiNT executable, a.out symbol table";
! 646: break;
! 647: case SYMBOL_FORMAT_DRI:
1.1.1.3 root 648: info = "TOS executable, DRI / GST symbol table";
649: break;
650: default:
651: fprintf(stderr, "ERROR: unknown executable type 0x%x!\n", tabletype);
652: return false;
653: }
654: fprintf(stderr, "%s, reloc=%d, program flags:", info, relocflag);
655: /* bit flags */
1.1.1.4 root 656: for (i = 0; i < ARRAY_SIZE(flags); i++) {
1.1.1.3 root 657: if (prgflags & flags[i].flag) {
658: fprintf(stderr, " %s", flags[i].name);
659: }
660: }
661: /* memory protection flags */
662: switch((prgflags >> 4) & 3) {
663: case 0: info = "PRIVATE"; break;
664: case 1: info = "GLOBAL"; break;
665: case 2: info = "SUPER"; break;
666: case 3: info = "READONLY"; break;
667: }
668: fprintf(stderr, " %s (0x%x)\n", info, prgflags);
669: return true;
670: }
671:
672: /**
1.1 root 673: * Parse program header and use symbol table format specific loader
674: * loader function to load the symbols.
675: * Return symbols list or NULL for failure.
676: */
677: static symbol_list_t* symbols_load_binary(FILE *fp)
678: {
1.1.1.2 root 679: Uint32 textlen, datalen, bsslen, tablesize, tabletype, prgflags;
1.1 root 680: prg_section_t sections[3];
681: int offset, reads = 0;
1.1.1.2 root 682: Uint16 relocflag;
683: symbol_list_t* symbols;
1.1.1.5 ! root 684: Uint32 symoff = 0;
! 685: Uint32 stroff = 0;
! 686: Uint32 strsize = 0;
1.1 root 687:
688: /* get TEXT, DATA & BSS section sizes */
689: reads += fread(&textlen, sizeof(textlen), 1, fp);
690: textlen = SDL_SwapBE32(textlen);
691: reads += fread(&datalen, sizeof(datalen), 1, fp);
692: datalen = SDL_SwapBE32(datalen);
693: reads += fread(&bsslen, sizeof(bsslen), 1, fp);
694: bsslen = SDL_SwapBE32(bsslen);
695:
696: /* get symbol table size & type and check that all reads succeeded */
697: reads += fread(&tablesize, sizeof(tablesize), 1, fp);
698: tablesize = SDL_SwapBE32(tablesize);
699: reads += fread(&tabletype, sizeof(tabletype), 1, fp);
700: tabletype = SDL_SwapBE32(tabletype);
1.1.1.2 root 701:
702: /* get program header and whether there's reloc table */
703: reads += fread(&prgflags, sizeof(prgflags), 1, fp);
704: prgflags = SDL_SwapBE32(prgflags);
705: reads += fread(&relocflag, sizeof(relocflag), 1, fp);
706: relocflag = SDL_SwapBE32(relocflag);
707:
708: if (reads != 7) {
1.1 root 709: fprintf(stderr, "ERROR: program header reading failed!\n");
710: return NULL;
711: }
1.1.1.5 ! root 712: /*
! 713: * check for GNU-style symbol table in aexec header
! 714: */
! 715: if (tabletype == SYMBOL_FORMAT_MINT) { /* MiNT */
! 716: Uint32 magic1, magic2;
! 717: Uint32 dummy;
! 718: Uint32 a_text, a_data, a_bss, a_syms, a_entry, a_trsize, a_drsize;
! 719: Uint32 g_tparel_pos, g_tparel_size, g_stkpos, g_symbol_format;
! 720:
! 721: reads = fread(&magic1, sizeof(magic1), 1, fp);
! 722: magic1 = SDL_SwapBE32(magic1);
! 723: reads += fread(&magic2, sizeof(magic2), 1, fp);
! 724: magic2 = SDL_SwapBE32(magic2);
! 725: if (reads == 2 &&
! 726: ((magic1 == 0x283a001a && magic2 == 0x4efb48fa) || /* Original binutils: move.l 28(pc),d4; jmp 0(pc,d4.l) */
! 727: (magic1 == 0x203a001a && magic2 == 0x4efb08fa))) { /* binutils >= 2.18-mint-20080209: move.l 28(pc),d0; jmp 0(pc,d0.l) */
! 728: reads += fread(&dummy, sizeof(dummy), 1, fp); /* skip a_info */
! 729: reads += fread(&a_text, sizeof(a_text), 1, fp);
! 730: a_text = SDL_SwapBE32(a_text);
! 731: reads += fread(&a_data, sizeof(a_data), 1, fp);
! 732: a_data = SDL_SwapBE32(a_data);
! 733: reads += fread(&a_bss, sizeof(a_bss), 1, fp);
! 734: a_bss = SDL_SwapBE32(a_bss);
! 735: reads += fread(&a_syms, sizeof(a_syms), 1, fp);
! 736: a_syms = SDL_SwapBE32(a_syms);
! 737: reads += fread(&a_entry, sizeof(a_entry), 1, fp);
! 738: a_entry = SDL_SwapBE32(a_entry);
! 739: reads += fread(&a_trsize, sizeof(a_trsize), 1, fp);
! 740: a_trsize = SDL_SwapBE32(a_trsize);
! 741: reads += fread(&a_drsize, sizeof(a_drsize), 1, fp);
! 742: a_drsize = SDL_SwapBE32(a_drsize);
! 743: reads += fread(&g_tparel_pos, sizeof(g_tparel_pos), 1, fp);
! 744: g_tparel_pos = SDL_SwapBE32(g_tparel_pos);
! 745: reads += fread(&g_tparel_size, sizeof(g_tparel_size), 1, fp);
! 746: g_tparel_size = SDL_SwapBE32(g_tparel_size);
! 747: reads += fread(&g_stkpos, sizeof(g_stkpos), 1, fp);
! 748: g_stkpos = SDL_SwapBE32(g_stkpos);
! 749: reads += fread(&g_symbol_format, sizeof(g_symbol_format), 1, fp);
! 750: g_symbol_format = SDL_SwapBE32(g_symbol_format);
! 751: if (g_symbol_format == 0)
! 752: {
! 753: tabletype = SYMBOL_FORMAT_GNU;
! 754: }
! 755: if ((a_text + (256 - 28)) != textlen)
! 756: fprintf(stderr, "warning: inconsistent text segment size %08x != %08x\n", textlen, a_text + (256 - 28));
! 757: if (a_data != datalen)
! 758: fprintf(stderr, "warning: inconsistent data segment size %08x != %08x\n", datalen, a_data);
! 759: if (a_bss != bsslen)
! 760: fprintf(stderr, "warning: inconsistent bss segment size %08x != %08x\n", bsslen, a_bss);
! 761: /*
! 762: * the symbol table size in the GEMDOS header includes the string table,
! 763: * the symbol table size in the exec header does not.
! 764: */
! 765: if (tabletype == SYMBOL_FORMAT_GNU)
! 766: {
! 767: strsize = tablesize - a_syms;
! 768: tablesize = a_syms;
! 769: stroff = a_syms;
! 770: }
! 771:
! 772: textlen = a_text + (256 - 28);
! 773: datalen = a_data;
! 774: bsslen = a_bss;
! 775: symoff = 0x100 + /* sizeof(extended exec header) */
! 776: a_text +
! 777: a_data +
! 778: a_trsize +
! 779: a_drsize;
! 780: }
! 781: }
1.1.1.3 root 782: if (!symbols_print_prg_info(tabletype, prgflags, relocflag)) {
783: return NULL;
784: }
1.1.1.5 ! root 785: fprintf(stderr, "Program section sizes:\n- text: %d\n- data: %d\n- bss: %d\n",
! 786: textlen, datalen, bsslen);
! 787:
1.1.1.3 root 788: if (!tablesize) {
789: fprintf(stderr, "ERROR: symbol table missing from the program!\n");
790: return NULL;
791: }
1.1.1.5 ! root 792: fprintf(stderr, "- syms: %d\n", tablesize);
1.1 root 793:
1.1.1.2 root 794: /* symbols already have suitable offsets, so only acceptable end position needs to be calculated */
795: sections[0].offset = 0;
796: sections[0].end = textlen;
1.1.1.5 ! root 797: sections[1].offset = textlen;
! 798: sections[1].end = textlen + datalen;
! 799: sections[2].offset = textlen + datalen;
! 800: sections[2].end = textlen + datalen + bsslen;
! 801:
! 802: if (tabletype == SYMBOL_FORMAT_GNU) {
! 803: /* go to start of symbol table */
! 804: offset = symoff;
! 805: if (fseek(fp, offset, SEEK_SET) < 0) {
! 806: perror("ERROR: seeking to symbol table failed");
! 807: return NULL;
1.1.1.2 root 808: }
1.1.1.5 ! root 809: fprintf(stderr, "Trying to load symbol table at offset 0x%x...\n", offset);
! 810: symbols = symbols_load_gnu(fp, sections, tablesize, stroff, strsize);
1.1.1.2 root 811: } else {
1.1.1.5 ! root 812: /* go to start of symbol table */
! 813: offset = 0x1C + textlen + datalen;
! 814: if (fseek(fp, offset, SEEK_SET) < 0) {
! 815: perror("ERROR: seeking to symbol table failed");
! 816: return NULL;
! 817: }
! 818: fprintf(stderr, "Trying to load symbol table at offset 0x%x...\n", offset);
! 819: symbols = symbols_load_dri(fp, sections, tablesize);
1.1.1.2 root 820: }
1.1.1.5 ! root 821: if (!symbols) {
1.1.1.2 root 822: fprintf(stderr, "\n\n*** Try with 'nm -n <program>' (Atari/cross-compiler tool) instead ***\n\n");
823: return NULL;
824: }
1.1.1.5 ! root 825: fprintf(stderr, "Load the listed symbols to Hatari debugger with 'symbols <filename> TEXT'.\n");
1.1.1.2 root 826: return symbols;
1.1 root 827: }
828:
829: /**
830: * Load symbols of given type and the symbol address addresses from
831: * the given file and add given offsets to the addresses.
832: * Return symbols list or NULL for failure.
833: */
834: static symbol_list_t* symbols_load(const char *filename)
835: {
836: symbol_list_t *list;
837: uint16_t magic;
838: FILE *fp;
839:
840: fprintf(stderr, "Reading symbols from program '%s' symbol table...\n", filename);
1.1.1.3 root 841: if (!(fp = fopen(filename, "rb"))) {
1.1.1.5 ! root 842: usage("opening program file failed");
1.1 root 843: }
844: if (fread(&magic, sizeof(magic), 1, fp) != 1) {
1.1.1.5 ! root 845: usage("reading program file failed");
1.1 root 846: }
847:
1.1.1.5 ! root 848: if (SDL_SwapBE16(magic) != ATARI_PROGRAM_MAGIC) {
! 849: usage("file isn't an Atari program file");
1.1 root 850: }
851: list = symbols_load_binary(fp);
852: fclose(fp);
853:
854: if (!list) {
1.1.1.5 ! root 855: usage("no symbols, or reading them failed");
1.1 root 856: }
857:
858: if (list->count < list->symbols) {
859: if (!list->count) {
1.1.1.5 ! root 860: usage("no valid symbols in program, symbol table loading failed");
1.1 root 861: }
862: /* parsed less than there were "content" lines */
863: list->names = realloc(list->names, list->count * sizeof(symbol_t));
864: assert(list->names);
865: }
866:
867: /* copy name list to address list */
868: list->addresses = malloc(list->count * sizeof(symbol_t));
869: assert(list->addresses);
870: memcpy(list->addresses, list->names, list->count * sizeof(symbol_t));
871:
872: /* sort both lists, with different criteria */
873: qsort(list->addresses, list->count, sizeof(symbol_t), symbols_by_address);
874: qsort(list->names, list->count, sizeof(symbol_t), symbols_by_name);
1.1.1.5 ! root 875:
! 876: /* check for duplicate addresses */
! 877: symbols_check_addresses(list->addresses, list->count);
! 878:
! 879: /* check for duplicate names */
! 880: symbols_check_names(list->names, list->count);
! 881:
1.1 root 882: return list;
883: }
884:
885:
886: /* ---------------- symbol showing & option parsing ------------------ */
887:
888: /**
889: * Show symbols sorted by selected option
890: */
891: static int symbols_show(symbol_list_t* list)
892: {
893: symbol_t *entry, *entries;
894: char symchar;
895: int i;
896:
897: if (!list) {
898: fprintf(stderr, "No symbols!\n");
899: return 1;
900: }
901: if (Options.sort_name) {
902: entries = list->names;
903: } else {
904: entries = list->addresses;
905: }
906: for (entry = entries, i = 0; i < list->count; i++, entry++) {
1.1.1.5 ! root 907: symchar = symbol_char(entry->type);
1.1 root 908: fprintf(stdout, "0x%08x %c %s\n",
909: entry->address, symchar, entry->name);
910: }
911:
1.1.1.5 ! root 912: fprintf(stderr, "%d (unignored) symbols processed.\n", list->count);
1.1 root 913: return 0;
914: }
915:
916: /**
917: * parse program options and then call symbol load+save
918: */
919: int main(int argc, const char *argv[])
920: {
921: int i;
922:
923: PrgPath = *argv;
924: for (i = 1; i+1 < argc; i++) {
925: if (argv[i][0] != '-') {
926: break;
927: }
1.1.1.2 root 928: switch(tolower((unsigned char)argv[i][1])) {
1.1.1.5 ! root 929: case 'a':
! 930: Options.notypes |= SYMTYPE_ABS;
! 931: break;
1.1 root 932: case 'b':
933: Options.notypes |= SYMTYPE_BSS;
934: break;
935: case 'd':
936: Options.notypes |= SYMTYPE_DATA;
937: break;
938: case 't':
939: Options.notypes |= SYMTYPE_TEXT;
940: break;
941: case 'l':
942: Options.no_local = true;
943: break;
944: case 'o':
945: Options.no_obj = true;
946: break;
1.1.1.5 ! root 947: case 'n':
! 948: Options.sort_name = true;
! 949: break;
1.1 root 950: default:
951: usage("unknown option");
952: }
953: }
954: if (i+1 != argc) {
955: usage("incorrect number of arguments");
956: }
957: return symbols_show(symbols_load(argv[i]));
958: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.