|
|
1.1 ! root 1: /* ! 2: * Hatari - gst2ascii.c ! 3: * ! 4: * Copyright (C) 2013 by Eero Tamminen ! 5: * ! 6: * This file is distributed under the GNU General Public License, version 2 ! 7: * or at your option any later version. Read the file gpl.txt for details. ! 8: * ! 9: * 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: ! 61: #define ARRAYSIZE(x) (int)(sizeof(x)/sizeof(x[0])) ! 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); ! 106: for (i = 0; i < ARRAYSIZE(OptInfo); i++) { ! 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: /** ! 189: * Load symbols of given type and the symbol address addresses from ! 190: * DRI/GST format symbol table, and add given offsets to the addresses: ! 191: * http://toshyp.atari.org/en/005005.html ! 192: * Return symbols list or NULL for failure. ! 193: */ ! 194: static symbol_list_t* symbols_load_dri(FILE *fp, prg_section_t *sections, uint32_t tablesize) ! 195: { ! 196: int i, count, symbols; ! 197: int notypes, dtypes, locals, ofiles; ! 198: prg_section_t *section; ! 199: symbol_list_t *list; ! 200: symtype_t symtype; ! 201: #define DRI_ENTRY_SIZE 14 ! 202: char name[23]; ! 203: uint16_t symid; ! 204: uint32_t address; ! 205: ! 206: if (tablesize % DRI_ENTRY_SIZE) { ! 207: fprintf(stderr, "ERROR: invalid DRI/GST symbol table size %d!\n", tablesize); ! 208: return NULL; ! 209: } ! 210: symbols = tablesize / DRI_ENTRY_SIZE; ! 211: if (!(list = symbol_list_alloc(symbols))) { ! 212: return NULL; ! 213: } ! 214: ! 215: dtypes = notypes = ofiles = locals = count = 0; ! 216: for (i = 1; i <= symbols; i++) { ! 217: /* read DRI symbol table slot */ ! 218: if (fread(name, 8, 1, fp) != 1 || ! 219: fread(&symid, sizeof(symid), 1, fp) != 1 || ! 220: fread(&address, sizeof(address), 1, fp) != 1) { ! 221: break; ! 222: } ! 223: address = SDL_SwapBE32(address); ! 224: symid = SDL_SwapBE16(symid); ! 225: ! 226: /* GST extended DRI symbol format? */ ! 227: if ((symid & 0x0048)) { ! 228: /* next slot is rest of name */ ! 229: i += 1; ! 230: if (fread(name+8, 14, 1, fp) != 1) { ! 231: break; ! 232: } ! 233: name[22] = '\0'; ! 234: } else { ! 235: name[8] = '\0'; ! 236: } ! 237: ! 238: /* check section */ ! 239: switch (symid & 0xf00) { ! 240: case 0x0200: ! 241: symtype = SYMTYPE_TEXT; ! 242: section = &(sections[0]); ! 243: break; ! 244: case 0x0400: ! 245: symtype = SYMTYPE_DATA; ! 246: section = &(sections[1]); ! 247: break; ! 248: case 0x0100: ! 249: symtype = SYMTYPE_BSS; ! 250: section = &(sections[2]); ! 251: break; ! 252: default: ! 253: if ((symid & 0xe000) == 0xe000) { ! 254: dtypes++; ! 255: continue; ! 256: } ! 257: fprintf(stderr, "WARNING: ignoring symbol '%s' in slot %d of unknown type 0x%x.\n", name, i, symid); ! 258: continue; ! 259: } ! 260: if (Options.notypes & symtype) { ! 261: notypes++; ! 262: continue; ! 263: } ! 264: if (Options.no_local) { ! 265: if (name[0] == '.' && name[1] == 'L') { ! 266: locals++; ! 267: continue; ! 268: } ! 269: } ! 270: if (Options.no_obj) { ! 271: const char *gcc_sym[] = { ! 272: "___gnu_compiled_c", ! 273: "gcc2_compiled." ! 274: }; ! 275: int j, len = strlen(name); ! 276: /* object / file name? */ ! 277: if (len > 2 && ((name[len-2] == '.' && name[len-1] == 'o') || strchr(name, '/'))) { ! 278: ofiles++; ! 279: continue; ! 280: } ! 281: /* useless symbols GCC (v2) seems to add to every object? */ ! 282: for (j = 0; j < ARRAYSIZE(gcc_sym); j++) { ! 283: if (strcmp(name, gcc_sym[j]) == 0) { ! 284: ofiles++; ! 285: j = -1; ! 286: break; ! 287: } ! 288: } ! 289: if (j < 0) { ! 290: continue; ! 291: } ! 292: } ! 293: address += section->offset; ! 294: if (address > section->end) { ! 295: fprintf(stderr, "WARNING: ignoring symbol '%s' in slot %d with invalid offset 0x%x (>= 0x%x).\n", name, i, address, section->end); ! 296: continue; ! 297: } ! 298: list->names[count].address = address; ! 299: list->names[count].type = symtype; ! 300: list->names[count].name = strdup(name); ! 301: assert(list->names[count].name); ! 302: count++; ! 303: } ! 304: if (i <= symbols) { ! 305: perror("ERROR: reading symbol failed"); ! 306: symbol_list_free(list); ! 307: return NULL; ! 308: } ! 309: if (notypes) { ! 310: fprintf(stderr, "NOTE: ignored %d unwanted symbol types.\n", notypes); ! 311: } ! 312: if (dtypes) { ! 313: fprintf(stderr, "NOTE: ignored %d globally defined equated values.\n", dtypes); ! 314: } ! 315: if (locals) { ! 316: fprintf(stderr, "NOTE: ignored %d unnamed / local symbols (= name starts with '.L').\n", locals); ! 317: } ! 318: if (ofiles) { ! 319: /* object file path names most likely get truncated and ! 320: * as result cause unnecessary symbol name conflicts in ! 321: * addition to object file addresses conflicting with ! 322: * first symbol in the object file. ! 323: */ ! 324: fprintf(stderr, "NOTE: ignored %d object symbols (= name has '/', ends in '.o' or is GCC internal).\n", ofiles); ! 325: } ! 326: list->symbols = symbols; ! 327: list->count = count; ! 328: return list; ! 329: } ! 330: ! 331: /** ! 332: * Parse program header and use symbol table format specific loader ! 333: * loader function to load the symbols. ! 334: * Return symbols list or NULL for failure. ! 335: */ ! 336: static symbol_list_t* symbols_load_binary(FILE *fp) ! 337: { ! 338: uint32_t textlen, datalen, bsslen, tablesize, tabletype; ! 339: prg_section_t sections[3]; ! 340: int offset, reads = 0; ! 341: ! 342: /* get TEXT, DATA & BSS section sizes */ ! 343: reads += fread(&textlen, sizeof(textlen), 1, fp); ! 344: textlen = SDL_SwapBE32(textlen); ! 345: reads += fread(&datalen, sizeof(datalen), 1, fp); ! 346: datalen = SDL_SwapBE32(datalen); ! 347: reads += fread(&bsslen, sizeof(bsslen), 1, fp); ! 348: bsslen = SDL_SwapBE32(bsslen); ! 349: ! 350: /* get symbol table size & type and check that all reads succeeded */ ! 351: reads += fread(&tablesize, sizeof(tablesize), 1, fp); ! 352: tablesize = SDL_SwapBE32(tablesize); ! 353: if (!tablesize) { ! 354: fprintf(stderr, "ERROR: symbol table missing from the program!\n"); ! 355: return NULL; ! 356: } ! 357: reads += fread(&tabletype, sizeof(tabletype), 1, fp); ! 358: tabletype = SDL_SwapBE32(tabletype); ! 359: if (reads != 5) { ! 360: fprintf(stderr, "ERROR: program header reading failed!\n"); ! 361: return NULL; ! 362: } ! 363: ! 364: /* go to start of symbol table */ ! 365: offset = 0x1C + textlen + datalen; ! 366: if (fseek(fp, offset, SEEK_SET) < 0) { ! 367: perror("ERROR: seeking to symbol table failed"); ! 368: return NULL; ! 369: } ! 370: /* symbols already have suitable offsets, so only acceptable end position needs to be calculated */ ! 371: sections[0].offset = 0; ! 372: sections[0].end = textlen; ! 373: sections[1].offset = 0; ! 374: sections[1].end = textlen + datalen; ! 375: sections[2].offset = 0; ! 376: sections[2].end = textlen + datalen + bsslen; ! 377: ! 378: switch (tabletype) { ! 379: case 0x4D694E54: /* "MiNT" */ ! 380: fprintf(stderr, "MiNT executable, trying to load GST symbol table at offset 0x%x...\n", offset); ! 381: return symbols_load_dri(fp, sections, tablesize); ! 382: case 0x0: ! 383: fprintf(stderr, "Old style excutable, loading DRI / GST symbol table at offset 0x%x.\n", offset); ! 384: return symbols_load_dri(fp, sections, tablesize); ! 385: default: ! 386: fprintf(stderr, "ERROR: unknown executable type 0x%x at offset 0x%x!\n", tabletype, offset); ! 387: } ! 388: return NULL; ! 389: } ! 390: ! 391: /** ! 392: * Load symbols of given type and the symbol address addresses from ! 393: * the given file and add given offsets to the addresses. ! 394: * Return symbols list or NULL for failure. ! 395: */ ! 396: static symbol_list_t* symbols_load(const char *filename) ! 397: { ! 398: symbol_list_t *list; ! 399: uint16_t magic; ! 400: FILE *fp; ! 401: ! 402: fprintf(stderr, "Reading symbols from program '%s' symbol table...\n", filename); ! 403: if (!(fp = fopen(filename, "r"))) { ! 404: return usage("opening program file failed"); ! 405: } ! 406: if (fread(&magic, sizeof(magic), 1, fp) != 1) { ! 407: return usage("reading program file failed"); ! 408: } ! 409: ! 410: if (SDL_SwapBE16(magic) != 0x601A) { ! 411: return usage("file isn't an Atari program file"); ! 412: } ! 413: list = symbols_load_binary(fp); ! 414: fclose(fp); ! 415: ! 416: if (!list) { ! 417: return usage("no symbols, or reading them failed"); ! 418: } ! 419: ! 420: if (list->count < list->symbols) { ! 421: if (!list->count) { ! 422: return usage("no valid symbols in program, symbol table loading failed"); ! 423: } ! 424: /* parsed less than there were "content" lines */ ! 425: list->names = realloc(list->names, list->count * sizeof(symbol_t)); ! 426: assert(list->names); ! 427: } ! 428: ! 429: /* copy name list to address list */ ! 430: list->addresses = malloc(list->count * sizeof(symbol_t)); ! 431: assert(list->addresses); ! 432: memcpy(list->addresses, list->names, list->count * sizeof(symbol_t)); ! 433: ! 434: /* sort both lists, with different criteria */ ! 435: qsort(list->addresses, list->count, sizeof(symbol_t), symbols_by_address); ! 436: qsort(list->names, list->count, sizeof(symbol_t), symbols_by_name); ! 437: return list; ! 438: } ! 439: ! 440: ! 441: /* ---------------- symbol showing & option parsing ------------------ */ ! 442: ! 443: /** ! 444: * Show symbols sorted by selected option ! 445: */ ! 446: static int symbols_show(symbol_list_t* list) ! 447: { ! 448: symbol_t *entry, *entries; ! 449: char symchar; ! 450: int i; ! 451: ! 452: if (!list) { ! 453: fprintf(stderr, "No symbols!\n"); ! 454: return 1; ! 455: } ! 456: if (Options.sort_name) { ! 457: entries = list->names; ! 458: } else { ! 459: entries = list->addresses; ! 460: } ! 461: for (entry = entries, i = 0; i < list->count; i++, entry++) { ! 462: switch (entry->type) { ! 463: case SYMTYPE_TEXT: ! 464: symchar = 'T'; ! 465: break; ! 466: case SYMTYPE_DATA: ! 467: symchar = 'D'; ! 468: break; ! 469: case SYMTYPE_BSS: ! 470: symchar = 'B'; ! 471: break; ! 472: default: ! 473: symchar = '?'; ! 474: } ! 475: fprintf(stdout, "0x%08x %c %s\n", ! 476: entry->address, symchar, entry->name); ! 477: } ! 478: ! 479: fprintf(stderr, "%d symbols processed.\n", list->count); ! 480: return 0; ! 481: } ! 482: ! 483: /** ! 484: * parse program options and then call symbol load+save ! 485: */ ! 486: int main(int argc, const char *argv[]) ! 487: { ! 488: int i; ! 489: ! 490: PrgPath = *argv; ! 491: for (i = 1; i+1 < argc; i++) { ! 492: if (argv[i][0] != '-') { ! 493: break; ! 494: } ! 495: switch(tolower(argv[i][1])) { ! 496: case 'n': ! 497: Options.sort_name = true; ! 498: case 'b': ! 499: Options.notypes |= SYMTYPE_BSS; ! 500: break; ! 501: case 'd': ! 502: Options.notypes |= SYMTYPE_DATA; ! 503: break; ! 504: case 't': ! 505: Options.notypes |= SYMTYPE_TEXT; ! 506: break; ! 507: case 'l': ! 508: Options.no_local = true; ! 509: break; ! 510: case 'o': ! 511: Options.no_obj = true; ! 512: break; ! 513: default: ! 514: usage("unknown option"); ! 515: return 1; ! 516: } ! 517: } ! 518: if (i+1 != argc) { ! 519: usage("incorrect number of arguments"); ! 520: return 1; ! 521: } ! 522: return symbols_show(symbols_load(argv[i])); ! 523: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.