Annotation of hatari/tools/debugger/gst2ascii.c, revision 1.1.1.4

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.