|
|
1.1 root 1: /*
2: * Hatari - gst2ascii.c
3: *
1.1.1.3 root 4: * Copyright (C) 2013-2015 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: *
9: * Convert DRI/GST symbol table in a binary into ASCII symbols file accepted
10: * by Hatari debugger and its profiler data post-processor. This will also
11: * allow manual editing of the symbol table (removing irrelevant labels or
12: * adding missing symbols for functions).
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>
28:
29: typedef enum {
30: SYMTYPE_TEXT = 1,
31: SYMTYPE_DATA = 2,
32: SYMTYPE_BSS = 4
33: } symtype_t;
34:
35: typedef struct {
36: char *name;
37: uint32_t 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_t offset;
50: uint32_t end;
51: } prg_section_t;
52:
53: /* ------------------ options & usage ------------------ */
54:
55: #ifdef WIN32
56: #define PATHSEP '\\'
57: #else
58: #define PATHSEP '/'
59: #endif
60:
1.1.1.4 ! root 61: #define ARRAY_SIZE(x) (int)(sizeof(x)/sizeof(x[0]))
1.1 root 62:
63: static const char *PrgPath;
64:
65: static struct {
66: symtype_t notypes;
67: bool no_obj;
68: bool no_local;
69: bool sort_name;
70: } Options;
71:
72: /*
73: * Show program usage, given error message
74: * return empty list
75: */
76: static symbol_list_t* usage(const char *msg)
77: {
78: const struct {
79: const char opt;
80: const char *desc;
81: } OptInfo[] = {
82: { 'n', "sort by name (not address)" },
83: { 'b', "no BSS symbols" },
84: { 'd', "no DATA symbols" },
85: { 't', "no TEXT symbols" },
86: { 'l', "no local (.L*) symbols" },
87: { 'o', "no object symbols (filenames or GCC internals)" },
88: };
89: const char *name;
90: int i;
91:
92: if ((name = strrchr(PrgPath, PATHSEP))) {
93: name++;
94: } else {
95: name = PrgPath;
96: }
97: fprintf(stderr,
98: "\n"
99: "Usage: %s [options] <Atari program>\n"
100: "\n"
101: "Outputs given program DRI/GST symbol table content\n"
102: "in ASCII format accepted by Hatari debugger and\n"
103: "its profiler data post-processor.\n"
104: "\n"
105: "Options:\n", name);
1.1.1.4 ! root 106: for (i = 0; i < ARRAY_SIZE(OptInfo); i++) {
1.1 root 107: fprintf(stderr, "\t-%c\t%s\n", OptInfo[i].opt, OptInfo[i].desc);
108: }
109: if (msg) {
110: fprintf(stderr, "\nERROR: %s!\n", msg);
111: }
112: return NULL;
113: }
114:
115: /* ------------------ load and free functions ------------------ */
116:
117: /**
118: * compare function for qsort() to sort according to symbol address
119: */
120: static int symbols_by_address(const void *s1, const void *s2)
121: {
122: uint32_t addr1 = ((const symbol_t*)s1)->address;
123: uint32_t addr2 = ((const symbol_t*)s2)->address;
124:
125: if (addr1 < addr2) {
126: return -1;
127: }
128: if (addr1 > addr2) {
129: return 1;
130: }
131: fprintf(stderr, "WARNING: symbols '%s' & '%s' have the same 0x%x address.\n",
132: ((const symbol_t*)s1)->name, ((const symbol_t*)s2)->name, addr1);
133: return 0;
134: }
135:
136: /**
137: * compare function for qsort() to sort according to symbol name
138: */
139: static int symbols_by_name(const void *s1, const void *s2)
140: {
141: const char* name1 = ((const symbol_t*)s1)->name;
142: const char* name2 = ((const symbol_t*)s2)->name;
143: int ret;
144:
145: ret = strcmp(name1, name2);
146: if (!ret) {
147: fprintf(stderr, "WARNING: addresses 0x%x & 0x%x have the same '%s' name.\n",
148: ((const symbol_t*)s1)->address, ((const symbol_t*)s2)->address, name1);
149: }
150: return ret;
151: }
152:
153: /**
154: * Allocate symbol list & names for given number of items.
155: * Return allocated list or NULL on failure.
156: */
157: static symbol_list_t* symbol_list_alloc(int symbols)
158: {
159: symbol_list_t *list;
160:
161: if (!symbols) {
162: return NULL;
163: }
164: list = calloc(1, sizeof(symbol_list_t));
165: if (list) {
166: list->names = malloc(symbols * sizeof(symbol_t));
167: if (!list->names) {
168: free(list);
169: list = NULL;
170: }
171: }
172: return list;
173: }
174:
175: /**
176: * Free symbol list & names.
177: */
178: static void symbol_list_free(symbol_list_t *list)
179: {
180: if (list) {
181: if (list->names) {
182: free(list->names);
183: }
184: free(list);
185: }
186: }
187:
188: /**
1.1.1.2 root 189: * Return symbol type identifier char
190: */
191: static char symbol_char(int type)
192: {
193: switch (type) {
194: case SYMTYPE_TEXT: return 'T';
195: case SYMTYPE_DATA: return 'D';
196: case SYMTYPE_BSS: return 'B';
197: default: return '?';
198: }
199: }
200:
201: #define INVALID_SYMBOL_OFFSETS ((symbol_list_t*)1)
202:
203: /**
1.1 root 204: * Load symbols of given type and the symbol address addresses from
205: * DRI/GST format symbol table, and add given offsets to the addresses:
206: * http://toshyp.atari.org/en/005005.html
207: * Return symbols list or NULL for failure.
208: */
209: static symbol_list_t* symbols_load_dri(FILE *fp, prg_section_t *sections, uint32_t tablesize)
210: {
1.1.1.2 root 211: int i, count, symbols, outside;
1.1 root 212: int notypes, dtypes, locals, ofiles;
213: prg_section_t *section;
214: symbol_list_t *list;
215: symtype_t symtype;
216: #define DRI_ENTRY_SIZE 14
217: char name[23];
218: uint16_t symid;
219: uint32_t address;
220:
221: if (tablesize % DRI_ENTRY_SIZE) {
222: fprintf(stderr, "ERROR: invalid DRI/GST symbol table size %d!\n", tablesize);
223: return NULL;
224: }
225: symbols = tablesize / DRI_ENTRY_SIZE;
226: if (!(list = symbol_list_alloc(symbols))) {
227: return NULL;
228: }
229:
1.1.1.2 root 230: outside = dtypes = notypes = ofiles = locals = count = 0;
1.1 root 231: for (i = 1; i <= symbols; i++) {
232: /* read DRI symbol table slot */
233: if (fread(name, 8, 1, fp) != 1 ||
234: fread(&symid, sizeof(symid), 1, fp) != 1 ||
235: fread(&address, sizeof(address), 1, fp) != 1) {
236: break;
237: }
238: address = SDL_SwapBE32(address);
239: symid = SDL_SwapBE16(symid);
240:
241: /* GST extended DRI symbol format? */
242: if ((symid & 0x0048)) {
243: /* next slot is rest of name */
244: i += 1;
245: if (fread(name+8, 14, 1, fp) != 1) {
246: break;
247: }
248: name[22] = '\0';
249: } else {
250: name[8] = '\0';
251: }
252:
253: /* check section */
254: switch (symid & 0xf00) {
255: case 0x0200:
256: symtype = SYMTYPE_TEXT;
257: section = &(sections[0]);
258: break;
259: case 0x0400:
260: symtype = SYMTYPE_DATA;
261: section = &(sections[1]);
262: break;
263: case 0x0100:
264: symtype = SYMTYPE_BSS;
265: section = &(sections[2]);
266: break;
267: default:
268: if ((symid & 0xe000) == 0xe000) {
269: dtypes++;
270: continue;
271: }
272: fprintf(stderr, "WARNING: ignoring symbol '%s' in slot %d of unknown type 0x%x.\n", name, i, symid);
273: continue;
274: }
275: if (Options.notypes & symtype) {
276: notypes++;
277: continue;
278: }
279: if (Options.no_local) {
280: if (name[0] == '.' && name[1] == 'L') {
281: locals++;
282: continue;
283: }
284: }
285: if (Options.no_obj) {
286: const char *gcc_sym[] = {
287: "___gnu_compiled_c",
288: "gcc2_compiled."
289: };
290: int j, len = strlen(name);
291: /* object / file name? */
292: if (len > 2 && ((name[len-2] == '.' && name[len-1] == 'o') || strchr(name, '/'))) {
293: ofiles++;
294: continue;
295: }
296: /* useless symbols GCC (v2) seems to add to every object? */
1.1.1.4 ! root 297: for (j = 0; j < ARRAY_SIZE(gcc_sym); j++) {
1.1 root 298: if (strcmp(name, gcc_sym[j]) == 0) {
299: ofiles++;
300: j = -1;
301: break;
302: }
303: }
304: if (j < 0) {
305: continue;
306: }
307: }
308: address += section->offset;
309: if (address > section->end) {
1.1.1.2 root 310: /* VBCC has 1 symbol outside of its section */
311: if (++outside > 2) {
312: /* potentially buggy version of VBCC vlink used */
313: fprintf(stderr, "ERROR: too many invalid offsets, skipping rest of symbols!\n");
314: symbol_list_free(list);
315: return INVALID_SYMBOL_OFFSETS;
316: }
317: fprintf(stderr, "WARNING: ignoring symbol '%s' of %c type in slot %d with invalid offset 0x%x (>= 0x%x).\n",
318: name, symbol_char(symtype), i, address, section->end);
1.1 root 319: continue;
320: }
321: list->names[count].address = address;
322: list->names[count].type = symtype;
323: list->names[count].name = strdup(name);
324: assert(list->names[count].name);
325: count++;
326: }
327: if (i <= symbols) {
328: perror("ERROR: reading symbol failed");
329: symbol_list_free(list);
330: return NULL;
331: }
332: if (notypes) {
333: fprintf(stderr, "NOTE: ignored %d unwanted symbol types.\n", notypes);
334: }
335: if (dtypes) {
336: fprintf(stderr, "NOTE: ignored %d globally defined equated values.\n", dtypes);
337: }
338: if (locals) {
339: fprintf(stderr, "NOTE: ignored %d unnamed / local symbols (= name starts with '.L').\n", locals);
340: }
341: if (ofiles) {
342: /* object file path names most likely get truncated and
343: * as result cause unnecessary symbol name conflicts in
344: * addition to object file addresses conflicting with
345: * first symbol in the object file.
346: */
347: fprintf(stderr, "NOTE: ignored %d object symbols (= name has '/', ends in '.o' or is GCC internal).\n", ofiles);
348: }
349: list->symbols = symbols;
350: list->count = count;
351: return list;
352: }
353:
354: /**
1.1.1.3 root 355: * Print program header information.
356: * Return false for unrecognized symbol table type.
357: */
358: static bool symbols_print_prg_info(Uint32 tabletype, Uint32 prgflags, Uint16 relocflag)
359: {
360: static const struct {
361: Uint32 flag;
362: const char *name;
363: } flags[] = {
364: { 0x0001, "FASTLOAD" },
365: { 0x0002, "TTRAMLOAD" },
366: { 0x0004, "TTRAMMEM" },
367: { 0x0008, "MINIMUM" }, /* MagiC */
368: { 0x1000, "SHAREDTEXT" }
369: };
370: const char *info;
371: int i;
372:
373: switch (tabletype) {
374: case 0x4D694E54: /* "MiNT" */
375: info = "GCC/MiNT executable, GST symbol table";
376: break;
377: case 0x0:
378: info = "TOS executable, DRI / GST symbol table";
379: break;
380: default:
381: fprintf(stderr, "ERROR: unknown executable type 0x%x!\n", tabletype);
382: return false;
383: }
384: fprintf(stderr, "%s, reloc=%d, program flags:", info, relocflag);
385: /* bit flags */
1.1.1.4 ! root 386: for (i = 0; i < ARRAY_SIZE(flags); i++) {
1.1.1.3 root 387: if (prgflags & flags[i].flag) {
388: fprintf(stderr, " %s", flags[i].name);
389: }
390: }
391: /* memory protection flags */
392: switch((prgflags >> 4) & 3) {
393: case 0: info = "PRIVATE"; break;
394: case 1: info = "GLOBAL"; break;
395: case 2: info = "SUPER"; break;
396: case 3: info = "READONLY"; break;
397: }
398: fprintf(stderr, " %s (0x%x)\n", info, prgflags);
399: return true;
400: }
401:
402: /**
1.1 root 403: * Parse program header and use symbol table format specific loader
404: * loader function to load the symbols.
405: * Return symbols list or NULL for failure.
406: */
407: static symbol_list_t* symbols_load_binary(FILE *fp)
408: {
1.1.1.2 root 409: Uint32 textlen, datalen, bsslen, tablesize, tabletype, prgflags;
1.1 root 410: prg_section_t sections[3];
411: int offset, reads = 0;
1.1.1.2 root 412: Uint16 relocflag;
413: symbol_list_t* symbols;
1.1 root 414:
415: /* get TEXT, DATA & BSS section sizes */
416: reads += fread(&textlen, sizeof(textlen), 1, fp);
417: textlen = SDL_SwapBE32(textlen);
418: reads += fread(&datalen, sizeof(datalen), 1, fp);
419: datalen = SDL_SwapBE32(datalen);
420: reads += fread(&bsslen, sizeof(bsslen), 1, fp);
421: bsslen = SDL_SwapBE32(bsslen);
422:
423: /* get symbol table size & type and check that all reads succeeded */
424: reads += fread(&tablesize, sizeof(tablesize), 1, fp);
425: tablesize = SDL_SwapBE32(tablesize);
426: reads += fread(&tabletype, sizeof(tabletype), 1, fp);
427: tabletype = SDL_SwapBE32(tabletype);
1.1.1.2 root 428:
429: /* get program header and whether there's reloc table */
430: reads += fread(&prgflags, sizeof(prgflags), 1, fp);
431: prgflags = SDL_SwapBE32(prgflags);
432: reads += fread(&relocflag, sizeof(relocflag), 1, fp);
433: relocflag = SDL_SwapBE32(relocflag);
434:
435: if (reads != 7) {
1.1 root 436: fprintf(stderr, "ERROR: program header reading failed!\n");
437: return NULL;
438: }
1.1.1.3 root 439: if (!symbols_print_prg_info(tabletype, prgflags, relocflag)) {
440: return NULL;
441: }
442: if (!tablesize) {
443: fprintf(stderr, "ERROR: symbol table missing from the program!\n");
444: return NULL;
445: }
1.1 root 446:
1.1.1.2 root 447: /* symbols already have suitable offsets, so only acceptable end position needs to be calculated */
448: sections[0].offset = 0;
449: sections[0].end = textlen;
450: sections[1].offset = 0;
451: sections[1].end = datalen;
452: sections[2].offset = 0;
453: sections[2].end = bsslen;
454:
1.1 root 455: /* go to start of symbol table */
456: offset = 0x1C + textlen + datalen;
457: if (fseek(fp, offset, SEEK_SET) < 0) {
458: perror("ERROR: seeking to symbol table failed");
459: return NULL;
460: }
1.1.1.2 root 461: fprintf(stderr, "Trying to load symbol table at offset 0x%x...\n", offset);
462: symbols = symbols_load_dri(fp, sections, tablesize);
463:
464: if (symbols == INVALID_SYMBOL_OFFSETS && fseek(fp, offset, SEEK_SET) == 0) {
465: fprintf(stderr, "Re-trying with TEXT-relative BSS/DATA section offsets...\n");
466: sections[1].end += textlen;
467: sections[2].end += (textlen + datalen);
468: symbols = symbols_load_dri(fp, sections, tablesize);
469: if (symbols) {
470: fprintf(stderr, "Load symbols without giving separate BSS/DATA offsets (they're TEXT relative).\n");
471: }
472: } else {
473: fprintf(stderr, "Load symbols with 'symbols <filename> TEXT DATA BSS' after starting the program.\n");
474: }
475: if (!symbols || symbols == INVALID_SYMBOL_OFFSETS) {
476: fprintf(stderr, "\n\n*** Try with 'nm -n <program>' (Atari/cross-compiler tool) instead ***\n\n");
477: return NULL;
478: }
479: return symbols;
1.1 root 480: }
481:
482: /**
483: * Load symbols of given type and the symbol address addresses from
484: * the given file and add given offsets to the addresses.
485: * Return symbols list or NULL for failure.
486: */
487: static symbol_list_t* symbols_load(const char *filename)
488: {
489: symbol_list_t *list;
490: uint16_t magic;
491: FILE *fp;
492:
493: fprintf(stderr, "Reading symbols from program '%s' symbol table...\n", filename);
1.1.1.3 root 494: if (!(fp = fopen(filename, "rb"))) {
1.1 root 495: return usage("opening program file failed");
496: }
497: if (fread(&magic, sizeof(magic), 1, fp) != 1) {
498: return usage("reading program file failed");
499: }
500:
501: if (SDL_SwapBE16(magic) != 0x601A) {
502: return usage("file isn't an Atari program file");
503: }
504: list = symbols_load_binary(fp);
505: fclose(fp);
506:
507: if (!list) {
508: return usage("no symbols, or reading them failed");
509: }
510:
511: if (list->count < list->symbols) {
512: if (!list->count) {
513: return usage("no valid symbols in program, symbol table loading failed");
514: }
515: /* parsed less than there were "content" lines */
516: list->names = realloc(list->names, list->count * sizeof(symbol_t));
517: assert(list->names);
518: }
519:
520: /* copy name list to address list */
521: list->addresses = malloc(list->count * sizeof(symbol_t));
522: assert(list->addresses);
523: memcpy(list->addresses, list->names, list->count * sizeof(symbol_t));
524:
525: /* sort both lists, with different criteria */
526: qsort(list->addresses, list->count, sizeof(symbol_t), symbols_by_address);
527: qsort(list->names, list->count, sizeof(symbol_t), symbols_by_name);
528: return list;
529: }
530:
531:
532: /* ---------------- symbol showing & option parsing ------------------ */
533:
534: /**
535: * Show symbols sorted by selected option
536: */
537: static int symbols_show(symbol_list_t* list)
538: {
539: symbol_t *entry, *entries;
540: char symchar;
541: int i;
542:
543: if (!list) {
544: fprintf(stderr, "No symbols!\n");
545: return 1;
546: }
547: if (Options.sort_name) {
548: entries = list->names;
549: } else {
550: entries = list->addresses;
551: }
552: for (entry = entries, i = 0; i < list->count; i++, entry++) {
553: switch (entry->type) {
554: case SYMTYPE_TEXT:
555: symchar = 'T';
556: break;
557: case SYMTYPE_DATA:
558: symchar = 'D';
559: break;
560: case SYMTYPE_BSS:
561: symchar = 'B';
562: break;
563: default:
564: symchar = '?';
565: }
566: fprintf(stdout, "0x%08x %c %s\n",
567: entry->address, symchar, entry->name);
568: }
569:
570: fprintf(stderr, "%d symbols processed.\n", list->count);
571: return 0;
572: }
573:
574: /**
575: * parse program options and then call symbol load+save
576: */
577: int main(int argc, const char *argv[])
578: {
579: int i;
580:
581: PrgPath = *argv;
582: for (i = 1; i+1 < argc; i++) {
583: if (argv[i][0] != '-') {
584: break;
585: }
1.1.1.2 root 586: switch(tolower((unsigned char)argv[i][1])) {
1.1 root 587: case 'n':
588: Options.sort_name = true;
589: case 'b':
590: Options.notypes |= SYMTYPE_BSS;
591: break;
592: case 'd':
593: Options.notypes |= SYMTYPE_DATA;
594: break;
595: case 't':
596: Options.notypes |= SYMTYPE_TEXT;
597: break;
598: case 'l':
599: Options.no_local = true;
600: break;
601: case 'o':
602: Options.no_obj = true;
603: break;
604: default:
605: usage("unknown option");
606: return 1;
607: }
608: }
609: if (i+1 != argc) {
610: usage("incorrect number of arguments");
611: return 1;
612: }
613: return symbols_show(symbols_load(argv[i]));
614: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.