--- hatari/tools/debugger/gst2ascii.c 2019/04/09 08:56:06 1.1.1.3 +++ hatari/tools/debugger/gst2ascii.c 2019/04/09 08:58:31 1.1.1.5 @@ -1,15 +1,15 @@ /* * Hatari - gst2ascii.c * - * Copyright (C) 2013-2015 by Eero Tamminen + * Copyright (C) 2013-2017 by Eero Tamminen * * This file is distributed under the GNU General Public License, version 2 * or at your option any later version. Read the file gpl.txt for details. * - * Convert DRI/GST symbol table in a binary into ASCII symbols file accepted - * by Hatari debugger and its profiler data post-processor. This will also - * allow manual editing of the symbol table (removing irrelevant labels or - * adding missing symbols for functions). + * Convert DRI/GST and a.out format symbol table in a binary into ASCII symbols + * file accepted by Hatari debugger and its profiler data post-processor. + * This will also allow manual editing of the symbol table (removing irrelevant + * labels or adding missing symbols for functions). */ #include @@ -25,11 +25,13 @@ # include #endif #include +#include "../../src/debug/a.out.h" typedef enum { SYMTYPE_TEXT = 1, SYMTYPE_DATA = 2, - SYMTYPE_BSS = 4 + SYMTYPE_BSS = 4, + SYMTYPE_ABS = 8 } symtype_t; typedef struct { @@ -43,6 +45,7 @@ typedef struct { int symbols; /* initial symbol count */ symbol_t *addresses; /* items sorted by address */ symbol_t *names; /* items sorted by symbol name */ + char *strtab; } symbol_list_t; typedef struct { @@ -50,6 +53,15 @@ typedef struct { uint32_t end; } prg_section_t; +/* Magic used to denote different symbol table formats */ +#define SYMBOL_FORMAT_GNU 0x474E555f /* "MiNT" */ +#define SYMBOL_FORMAT_MINT 0x4D694E54 /* "GNU_" */ +#define SYMBOL_FORMAT_DRI 0x0 + +/* Magic identifying Atari programs */ +#define ATARI_PROGRAM_MAGIC 0x601A + + /* ------------------ options & usage ------------------ */ #ifdef WIN32 @@ -58,33 +70,33 @@ typedef struct { #define PATHSEP '/' #endif -#define ARRAYSIZE(x) (int)(sizeof(x)/sizeof(x[0])) +#define ARRAY_SIZE(x) (int)(sizeof(x)/sizeof(x[0])) static const char *PrgPath; static struct { symtype_t notypes; - bool no_obj; bool no_local; + bool no_obj; bool sort_name; } Options; /* - * Show program usage, given error message - * return empty list + * Show program usage, given error message, and exit */ -static symbol_list_t* usage(const char *msg) +static void usage(const char *msg) { const struct { const char opt; const char *desc; } OptInfo[] = { - { 'n', "sort by name (not address)" }, + { 'a', "no absolute symbols (are values, not addresses)" }, { 'b', "no BSS symbols" }, { 'd', "no DATA symbols" }, { 't', "no TEXT symbols" }, { 'l', "no local (.L*) symbols" }, { 'o', "no object symbols (filenames or GCC internals)" }, + { 'n', "sort by name (not address)" }, }; const char *name; int i; @@ -98,18 +110,23 @@ static symbol_list_t* usage(const char * "\n" "Usage: %s [options] \n" "\n" - "Outputs given program DRI/GST symbol table content\n" - "in ASCII format accepted by Hatari debugger and\n" - "its profiler data post-processor.\n" + "Outputs given program (DRI/GST or a.out format) symbol table\n" + "content in ASCII format accepted by Hatari debugger and its\n" + "profiler data post-processor.\n" + "\n" + "All symbol addresses are output as TEXT relative, i.e. you need\n" + "to give only that as section address for the Hatari debugger:\n" + "\tsymbols TEXT\n" "\n" "Options:\n", name); - for (i = 0; i < ARRAYSIZE(OptInfo); i++) { + for (i = 0; i < ARRAY_SIZE(OptInfo); i++) { fprintf(stderr, "\t-%c\t%s\n", OptInfo[i].opt, OptInfo[i].desc); } if (msg) { fprintf(stderr, "\nERROR: %s!\n", msg); } - return NULL; + + exit(msg != NULL); } /* ------------------ load and free functions ------------------ */ @@ -128,8 +145,6 @@ static int symbols_by_address(const void if (addr1 > addr2) { return 1; } - fprintf(stderr, "WARNING: symbols '%s' & '%s' have the same 0x%x address.\n", - ((const symbol_t*)s1)->name, ((const symbol_t*)s2)->name, addr1); return 0; } @@ -143,14 +158,51 @@ static int symbols_by_name(const void *s int ret; ret = strcmp(name1, name2); - if (!ret) { - fprintf(stderr, "WARNING: addresses 0x%x & 0x%x have the same '%s' name.\n", - ((const symbol_t*)s1)->address, ((const symbol_t*)s2)->address, name1); - } return ret; } /** + * check for duplicate addresses in symbol list + */ +static void symbols_check_addresses(const symbol_t *syms, int count) +{ + int i, j; + + for (i = 0; i < (count - 1); i++) + { + /* absolute symbols have values, not addresses */ + if (syms[i].type == SYMTYPE_ABS) { + continue; + } + for (j = i + 1; j < count && syms[i].address == syms[j].address; j++) { + if (syms[j].type == SYMTYPE_ABS) { + continue; + } + fprintf(stderr, "WARNING: symbols '%s' & '%s' have the same 0x%x address.\n", + syms[i].name, syms[j].name, syms[i].address); + i = j; + } + } +} + +/** + * check for duplicate names in symbol list + */ +static void symbols_check_names(const symbol_t *syms, int count) +{ + int i, j; + + for (i = 0; i < (count - 1); i++) + { + for (j = i + 1; j < count && strcmp(syms[i].name, syms[j].name) == 0; j++) { + fprintf(stderr, "WARNING: addresses 0x%x & 0x%x have the same '%s' name.\n", + syms[i].address, syms[j].address, syms[i].name); + i = j; + } + } +} + +/** * Allocate symbol list & names for given number of items. * Return allocated list or NULL on failure. */ @@ -194,11 +246,35 @@ static char symbol_char(int type) case SYMTYPE_TEXT: return 'T'; case SYMTYPE_DATA: return 'D'; case SYMTYPE_BSS: return 'B'; + case SYMTYPE_ABS: return 'A'; default: return '?'; } } -#define INVALID_SYMBOL_OFFSETS ((symbol_list_t*)1) +/** + * Return true if symbol name matches internal GCC symbol name, + * or is object / file name. + */ +static bool symbol_remove_obj(const char *name) +{ + static const char *gcc_sym[] = { + "___gnu_compiled_c", + "gcc2_compiled." + }; + int i, len = strlen(name); + /* object (.a or .o) / file name? */ + if (len > 2 && ((name[len-2] == '.' && (name[len-1] == 'a' || name[len-1] == 'o')) || strchr(name, '/'))) { + return true; + } + /* useless symbols GCC (v2) seems to add to every object? */ + for (i = 0; i < ARRAY_SIZE(gcc_sym); i++) { + if (strcmp(name, gcc_sym[i]) == 0) { + return true; + } + } + return false; +} + /** * Load symbols of given type and the symbol address addresses from @@ -208,7 +284,7 @@ static char symbol_char(int type) */ static symbol_list_t* symbols_load_dri(FILE *fp, prg_section_t *sections, uint32_t tablesize) { - int i, count, symbols, outside; + int i, count, symbols, invalid; int notypes, dtypes, locals, ofiles; prg_section_t *section; symbol_list_t *list; @@ -227,7 +303,7 @@ static symbol_list_t* symbols_load_dri(F return NULL; } - outside = dtypes = notypes = ofiles = locals = count = 0; + invalid = dtypes = notypes = ofiles = locals = count = 0; for (i = 1; i <= symbols; i++) { /* read DRI symbol table slot */ if (fread(name, 8, 1, fp) != 1 || @@ -269,7 +345,13 @@ static symbol_list_t* symbols_load_dri(F dtypes++; continue; } + if ((symid & 0x4000) == 0x4000) { + symtype = SYMTYPE_ABS; + section = NULL; + break; + } fprintf(stderr, "WARNING: ignoring symbol '%s' in slot %d of unknown type 0x%x.\n", name, i, symid); + invalid++; continue; } if (Options.notypes & symtype) { @@ -283,40 +365,19 @@ static symbol_list_t* symbols_load_dri(F } } if (Options.no_obj) { - const char *gcc_sym[] = { - "___gnu_compiled_c", - "gcc2_compiled." - }; - int j, len = strlen(name); - /* object / file name? */ - if (len > 2 && ((name[len-2] == '.' && name[len-1] == 'o') || strchr(name, '/'))) { + if (symbol_remove_obj(name)) { ofiles++; continue; } - /* useless symbols GCC (v2) seems to add to every object? */ - for (j = 0; j < ARRAYSIZE(gcc_sym); j++) { - if (strcmp(name, gcc_sym[j]) == 0) { - ofiles++; - j = -1; - break; - } - } - if (j < 0) { - continue; - } } - address += section->offset; - if (address > section->end) { - /* VBCC has 1 symbol outside of its section */ - if (++outside > 2) { - /* potentially buggy version of VBCC vlink used */ - fprintf(stderr, "ERROR: too many invalid offsets, skipping rest of symbols!\n"); - symbol_list_free(list); - return INVALID_SYMBOL_OFFSETS; + if (section) { + address += section->offset; + if (address > section->end) { + fprintf(stderr, "WARNING: ignoring symbol '%s' of type %c in slot %d with invalid offset 0x%x (>= 0x%x).\n", + name, symbol_char(symtype), i, address, section->end); + invalid++; + continue; } - fprintf(stderr, "WARNING: ignoring symbol '%s' of %c type in slot %d with invalid offset 0x%x (>= 0x%x).\n", - name, symbol_char(symtype), i, address, section->end); - continue; } list->names[count].address = address; list->names[count].type = symtype; @@ -329,11 +390,14 @@ static symbol_list_t* symbols_load_dri(F symbol_list_free(list); return NULL; } - if (notypes) { - fprintf(stderr, "NOTE: ignored %d unwanted symbol types.\n", notypes); + if (invalid) { + fprintf(stderr, "NOTE: ignored %d invalid symbols.\n", invalid); } if (dtypes) { - fprintf(stderr, "NOTE: ignored %d globally defined equated values.\n", dtypes); + fprintf(stderr, "NOTE: ignored %d debugging symbols.\n", dtypes); + } + if (notypes) { + fprintf(stderr, "NOTE: ignored %d other unwanted symbol types.\n", notypes); } if (locals) { fprintf(stderr, "NOTE: ignored %d unnamed / local symbols (= name starts with '.L').\n", locals); @@ -344,13 +408,216 @@ static symbol_list_t* symbols_load_dri(F * addition to object file addresses conflicting with * first symbol in the object file. */ - fprintf(stderr, "NOTE: ignored %d object symbols (= name has '/', ends in '.o' or is GCC internal).\n", ofiles); + fprintf(stderr, "NOTE: ignored %d object symbols (= name has '/', ends in '.[ao]' or is GCC internal).\n", ofiles); } list->symbols = symbols; list->count = count; return list; } + +/** + * Load symbols of given type and the symbol address addresses from + * a.out format symbol table, and add given offsets to the addresses: + * Return symbols list or NULL for failure. + */ +static symbol_list_t* symbols_load_gnu(FILE *fp, prg_section_t *sections, Uint32 tablesize, Uint32 stroff, Uint32 strsize) +{ + size_t slots = tablesize / SIZEOF_STRUCT_NLIST; + size_t i; + size_t strx; + unsigned char *p; + char *name; + symbol_t *sym; + symtype_t symtype; + uint32_t address; + uint32_t nread; + symbol_list_t *list; + unsigned char n_type; + unsigned char n_other; + unsigned short n_desc; + static char dummy[] = ""; + int dtypes, locals, ofiles, count, notypes, invalid, weak; + prg_section_t *section; + + if (!(list = symbol_list_alloc(slots))) { + return NULL; + } + + list->strtab = (char *)malloc(tablesize + strsize); + + if (list->strtab == NULL) + { + symbol_list_free(list); + return NULL; + } + + nread = fread(list->strtab, tablesize + strsize, 1, fp); + if (nread != 1) + { + perror("ERROR: reading symbols failed"); + symbol_list_free(list); + return NULL; + } + + p = (unsigned char *)list->strtab; + sym = list->names; + + weak = invalid = dtypes = notypes = ofiles = locals = count = 0; + for (i = 0; i < slots; i++) + { + strx = SDL_SwapBE32(*(Uint32*)p); + p += 4; + n_type = *p++; + n_other = *p++; + n_desc = SDL_SwapBE16(*(Uint16*)p); + p += 2; + address = SDL_SwapBE32(*(Uint32*)p); + p += 4; + name = dummy; + if (!strx) { + invalid++; + continue; + } + if (strx >= strsize) { + fprintf(stderr, "symbol name index %x out of range\n", (unsigned int)strx); + invalid++; + continue; + } + name = list->strtab + strx + stroff; + + if (n_type & N_STAB) + { + dtypes++; + continue; + } + section = NULL; + switch (n_type & (N_TYPE|N_EXT)) + { + case N_UNDF: + case N_UNDF|N_EXT: + /* shouldn't happen here */ + weak++; + continue; + case N_ABS: + case N_ABS|N_EXT: + symtype = SYMTYPE_ABS; + break; + case N_TEXT: + case N_TEXT|N_EXT: + symtype = SYMTYPE_TEXT; + section = &(sections[0]); + break; + case N_DATA: + case N_DATA|N_EXT: + symtype = SYMTYPE_DATA; + section = &(sections[1]); + break; + case N_BSS: + case N_BSS|N_EXT: + case N_COMM: + case N_COMM|N_EXT: + symtype = SYMTYPE_BSS; + section = &(sections[2]); + break; + case N_FN: /* filenames, not object addresses? */ + dtypes++; + continue; + case N_SIZE: + case N_WARNING: + case N_SETA: + case N_SETT: + case N_SETD: + case N_SETB: + case N_SETV: + dtypes++; + continue; + case N_WEAKU: + case N_WEAKT: + case N_WEAKD: + case N_WEAKB: + weak++; + continue; + default: + fprintf(stderr, "WARNING: ignoring symbol '%s' in slot %u of unknown type 0x%x.\n", name, (unsigned int)i, n_type); + invalid++; + continue; + } + /* + * the value of a common symbol is its size, not its address: + */ + if (((n_type & N_TYPE) == N_COMM) || + (((n_type & N_EXT) && (n_type & N_TYPE) == N_UNDF && address != 0))) + { + /* if we ever want to know a symbols size, get that here */ + fprintf(stderr, "WARNING: ignoring common symbol '%s' in slot %u.\n", sym->name, (unsigned int)i); + dtypes++; + continue; + } + if (Options.notypes & symtype) { + notypes++; + continue; + } + if (Options.no_local) { + if (name[0] == '.' && name[1] == 'L') { + locals++; + continue; + } + } + if (Options.no_obj) { + if (symbol_remove_obj(name)) { + ofiles++; + continue; + } + } + if (section) { + address += sections[0].offset; /* all GNU symbol addresses are TEXT relative */ + if (address > section->end) { + fprintf(stderr, "WARNING: ignoring symbol '%s' of type %c in slot %u with invalid offset 0x%x (>= 0x%x).\n", + name, symbol_char(symtype), (unsigned int)i, address, section->end); + invalid++; + continue; + } + } + sym->address = address; + sym->type = symtype; + sym->name = name; + sym++; + count++; + (void) n_desc; + (void) n_other; + } + + if (invalid) { + fprintf(stderr, "NOTE: ignored %d invalid symbols.\n", invalid); + } + if (dtypes) { + fprintf(stderr, "NOTE: ignored %d debugging symbols.\n", dtypes); + } + if (weak) { + fprintf(stderr, "NOTE: ignored %d weak / undefined symbols.\n", weak); + } + if (notypes) { + fprintf(stderr, "NOTE: ignored %d other unwanted symbol types.\n", notypes); + } + if (locals) { + fprintf(stderr, "NOTE: ignored %d unnamed / local symbols (= name starts with '.L').\n", locals); + } + if (ofiles) { + /* object file path names most likely get truncated and + * as result cause unnecessary symbol name conflicts in + * addition to object file addresses conflicting with + * first symbol in the object file. + */ + fprintf(stderr, "NOTE: ignored %d object symbols (= name has '/', ends in '.[ao]' or is GCC internal).\n", ofiles); + } + + list->symbols = slots; + list->count = count; + return list; +} + + /** * Print program header information. * Return false for unrecognized symbol table type. @@ -371,10 +638,13 @@ static bool symbols_print_prg_info(Uint3 int i; switch (tabletype) { - case 0x4D694E54: /* "MiNT" */ + case SYMBOL_FORMAT_MINT: /* "MiNT" */ info = "GCC/MiNT executable, GST symbol table"; break; - case 0x0: + case SYMBOL_FORMAT_GNU: /* "GNU_" */ + info = "GCC/MiNT executable, a.out symbol table"; + break; + case SYMBOL_FORMAT_DRI: info = "TOS executable, DRI / GST symbol table"; break; default: @@ -383,7 +653,7 @@ static bool symbols_print_prg_info(Uint3 } fprintf(stderr, "%s, reloc=%d, program flags:", info, relocflag); /* bit flags */ - for (i = 0; i < ARRAYSIZE(flags); i++) { + for (i = 0; i < ARRAY_SIZE(flags); i++) { if (prgflags & flags[i].flag) { fprintf(stderr, " %s", flags[i].name); } @@ -411,6 +681,9 @@ static symbol_list_t* symbols_load_binar int offset, reads = 0; Uint16 relocflag; symbol_list_t* symbols; + Uint32 symoff = 0; + Uint32 stroff = 0; + Uint32 strsize = 0; /* get TEXT, DATA & BSS section sizes */ reads += fread(&textlen, sizeof(textlen), 1, fp); @@ -436,46 +709,120 @@ static symbol_list_t* symbols_load_binar fprintf(stderr, "ERROR: program header reading failed!\n"); return NULL; } + /* + * check for GNU-style symbol table in aexec header + */ + if (tabletype == SYMBOL_FORMAT_MINT) { /* MiNT */ + Uint32 magic1, magic2; + Uint32 dummy; + Uint32 a_text, a_data, a_bss, a_syms, a_entry, a_trsize, a_drsize; + Uint32 g_tparel_pos, g_tparel_size, g_stkpos, g_symbol_format; + + reads = fread(&magic1, sizeof(magic1), 1, fp); + magic1 = SDL_SwapBE32(magic1); + reads += fread(&magic2, sizeof(magic2), 1, fp); + magic2 = SDL_SwapBE32(magic2); + if (reads == 2 && + ((magic1 == 0x283a001a && magic2 == 0x4efb48fa) || /* Original binutils: move.l 28(pc),d4; jmp 0(pc,d4.l) */ + (magic1 == 0x203a001a && magic2 == 0x4efb08fa))) { /* binutils >= 2.18-mint-20080209: move.l 28(pc),d0; jmp 0(pc,d0.l) */ + reads += fread(&dummy, sizeof(dummy), 1, fp); /* skip a_info */ + reads += fread(&a_text, sizeof(a_text), 1, fp); + a_text = SDL_SwapBE32(a_text); + reads += fread(&a_data, sizeof(a_data), 1, fp); + a_data = SDL_SwapBE32(a_data); + reads += fread(&a_bss, sizeof(a_bss), 1, fp); + a_bss = SDL_SwapBE32(a_bss); + reads += fread(&a_syms, sizeof(a_syms), 1, fp); + a_syms = SDL_SwapBE32(a_syms); + reads += fread(&a_entry, sizeof(a_entry), 1, fp); + a_entry = SDL_SwapBE32(a_entry); + reads += fread(&a_trsize, sizeof(a_trsize), 1, fp); + a_trsize = SDL_SwapBE32(a_trsize); + reads += fread(&a_drsize, sizeof(a_drsize), 1, fp); + a_drsize = SDL_SwapBE32(a_drsize); + reads += fread(&g_tparel_pos, sizeof(g_tparel_pos), 1, fp); + g_tparel_pos = SDL_SwapBE32(g_tparel_pos); + reads += fread(&g_tparel_size, sizeof(g_tparel_size), 1, fp); + g_tparel_size = SDL_SwapBE32(g_tparel_size); + reads += fread(&g_stkpos, sizeof(g_stkpos), 1, fp); + g_stkpos = SDL_SwapBE32(g_stkpos); + reads += fread(&g_symbol_format, sizeof(g_symbol_format), 1, fp); + g_symbol_format = SDL_SwapBE32(g_symbol_format); + if (g_symbol_format == 0) + { + tabletype = SYMBOL_FORMAT_GNU; + } + if ((a_text + (256 - 28)) != textlen) + fprintf(stderr, "warning: inconsistent text segment size %08x != %08x\n", textlen, a_text + (256 - 28)); + if (a_data != datalen) + fprintf(stderr, "warning: inconsistent data segment size %08x != %08x\n", datalen, a_data); + if (a_bss != bsslen) + fprintf(stderr, "warning: inconsistent bss segment size %08x != %08x\n", bsslen, a_bss); + /* + * the symbol table size in the GEMDOS header includes the string table, + * the symbol table size in the exec header does not. + */ + if (tabletype == SYMBOL_FORMAT_GNU) + { + strsize = tablesize - a_syms; + tablesize = a_syms; + stroff = a_syms; + } + + textlen = a_text + (256 - 28); + datalen = a_data; + bsslen = a_bss; + symoff = 0x100 + /* sizeof(extended exec header) */ + a_text + + a_data + + a_trsize + + a_drsize; + } + } if (!symbols_print_prg_info(tabletype, prgflags, relocflag)) { return NULL; } + fprintf(stderr, "Program section sizes:\n- text: %d\n- data: %d\n- bss: %d\n", + textlen, datalen, bsslen); + if (!tablesize) { fprintf(stderr, "ERROR: symbol table missing from the program!\n"); return NULL; } + fprintf(stderr, "- syms: %d\n", tablesize); /* symbols already have suitable offsets, so only acceptable end position needs to be calculated */ sections[0].offset = 0; sections[0].end = textlen; - sections[1].offset = 0; - sections[1].end = datalen; - sections[2].offset = 0; - sections[2].end = bsslen; - - /* go to start of symbol table */ - offset = 0x1C + textlen + datalen; - if (fseek(fp, offset, SEEK_SET) < 0) { - perror("ERROR: seeking to symbol table failed"); - return NULL; - } - fprintf(stderr, "Trying to load symbol table at offset 0x%x...\n", offset); - symbols = symbols_load_dri(fp, sections, tablesize); - - if (symbols == INVALID_SYMBOL_OFFSETS && fseek(fp, offset, SEEK_SET) == 0) { - fprintf(stderr, "Re-trying with TEXT-relative BSS/DATA section offsets...\n"); - sections[1].end += textlen; - sections[2].end += (textlen + datalen); - symbols = symbols_load_dri(fp, sections, tablesize); - if (symbols) { - fprintf(stderr, "Load symbols without giving separate BSS/DATA offsets (they're TEXT relative).\n"); + sections[1].offset = textlen; + sections[1].end = textlen + datalen; + sections[2].offset = textlen + datalen; + sections[2].end = textlen + datalen + bsslen; + + if (tabletype == SYMBOL_FORMAT_GNU) { + /* go to start of symbol table */ + offset = symoff; + if (fseek(fp, offset, SEEK_SET) < 0) { + perror("ERROR: seeking to symbol table failed"); + return NULL; } + fprintf(stderr, "Trying to load symbol table at offset 0x%x...\n", offset); + symbols = symbols_load_gnu(fp, sections, tablesize, stroff, strsize); } else { - fprintf(stderr, "Load symbols with 'symbols TEXT DATA BSS' after starting the program.\n"); + /* go to start of symbol table */ + offset = 0x1C + textlen + datalen; + if (fseek(fp, offset, SEEK_SET) < 0) { + perror("ERROR: seeking to symbol table failed"); + return NULL; + } + fprintf(stderr, "Trying to load symbol table at offset 0x%x...\n", offset); + symbols = symbols_load_dri(fp, sections, tablesize); } - if (!symbols || symbols == INVALID_SYMBOL_OFFSETS) { + if (!symbols) { fprintf(stderr, "\n\n*** Try with 'nm -n ' (Atari/cross-compiler tool) instead ***\n\n"); return NULL; } + fprintf(stderr, "Load the listed symbols to Hatari debugger with 'symbols TEXT'.\n"); return symbols; } @@ -492,25 +839,25 @@ static symbol_list_t* symbols_load(const fprintf(stderr, "Reading symbols from program '%s' symbol table...\n", filename); if (!(fp = fopen(filename, "rb"))) { - return usage("opening program file failed"); + usage("opening program file failed"); } if (fread(&magic, sizeof(magic), 1, fp) != 1) { - return usage("reading program file failed"); + usage("reading program file failed"); } - if (SDL_SwapBE16(magic) != 0x601A) { - return usage("file isn't an Atari program file"); + if (SDL_SwapBE16(magic) != ATARI_PROGRAM_MAGIC) { + usage("file isn't an Atari program file"); } list = symbols_load_binary(fp); fclose(fp); if (!list) { - return usage("no symbols, or reading them failed"); + usage("no symbols, or reading them failed"); } if (list->count < list->symbols) { if (!list->count) { - return usage("no valid symbols in program, symbol table loading failed"); + usage("no valid symbols in program, symbol table loading failed"); } /* parsed less than there were "content" lines */ list->names = realloc(list->names, list->count * sizeof(symbol_t)); @@ -525,6 +872,13 @@ static symbol_list_t* symbols_load(const /* sort both lists, with different criteria */ qsort(list->addresses, list->count, sizeof(symbol_t), symbols_by_address); qsort(list->names, list->count, sizeof(symbol_t), symbols_by_name); + + /* check for duplicate addresses */ + symbols_check_addresses(list->addresses, list->count); + + /* check for duplicate names */ + symbols_check_names(list->names, list->count); + return list; } @@ -550,24 +904,12 @@ static int symbols_show(symbol_list_t* l entries = list->addresses; } for (entry = entries, i = 0; i < list->count; i++, entry++) { - switch (entry->type) { - case SYMTYPE_TEXT: - symchar = 'T'; - break; - case SYMTYPE_DATA: - symchar = 'D'; - break; - case SYMTYPE_BSS: - symchar = 'B'; - break; - default: - symchar = '?'; - } + symchar = symbol_char(entry->type); fprintf(stdout, "0x%08x %c %s\n", entry->address, symchar, entry->name); } - fprintf(stderr, "%d symbols processed.\n", list->count); + fprintf(stderr, "%d (unignored) symbols processed.\n", list->count); return 0; } @@ -584,8 +926,9 @@ int main(int argc, const char *argv[]) break; } switch(tolower((unsigned char)argv[i][1])) { - case 'n': - Options.sort_name = true; + case 'a': + Options.notypes |= SYMTYPE_ABS; + break; case 'b': Options.notypes |= SYMTYPE_BSS; break; @@ -601,14 +944,15 @@ int main(int argc, const char *argv[]) case 'o': Options.no_obj = true; break; + case 'n': + Options.sort_name = true; + break; default: usage("unknown option"); - return 1; } } if (i+1 != argc) { usage("incorrect number of arguments"); - return 1; } return symbols_show(symbols_load(argv[i])); }