| /* Dump and interpret an object file. */ |
| |
| /* |
| Copyright (c) 2001, Richard Krehbiel |
| All rights reserved. |
| |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions are |
| met: |
| |
| o Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| |
| o Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in the |
| documentation and/or other materials provided with the distribution. |
| |
| o Neither the name of the copyright holder nor the names of its |
| contributors may be used to endorse or promote products derived from |
| this software without specific prior written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
| OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
| TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE |
| USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| DAMAGE. |
| |
| */ |
| |
| #include <stdio.h> |
| #include <stddef.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <assert.h> |
| |
| #include "rad50.h" |
| |
| #include "util.h" |
| |
| #define WORD(cp) ((*(cp) & 0xff) + ((*((cp)+1) & 0xff) << 8)) |
| |
| int psectid = 0; |
| char *psects[256];
|
| FILE *bin = NULL;
|
| int badbin = 0;
|
| int xferad = 1; |
| |
| char *readrec(FILE *fp, int *len) |
| { |
| int c, i; |
| int chksum; |
| char *buf; |
| |
| chksum = 0; |
| |
| while(c = fgetc(fp), c != EOF && c == 0) |
| ; |
| |
| if(c == EOF) |
| return NULL; |
| |
| if(c != 1) |
| { |
| fprintf(stderr, "Improperly formatted OBJ file (1)\n"); |
| return NULL; // Not a properly formatted file. |
| } |
| |
| chksum -= c; |
| |
| c = fgetc(fp); |
| if(c != 0) |
| { |
| fprintf(stderr, "Improperly formatted OBJ file (2)\n"); |
| return NULL; // Not properly formatted |
| } |
| |
| chksum -= c; // even though for 0 the checksum isn't changed... |
| |
| c = fgetc(fp); |
| if(c == EOF) |
| { |
| fprintf(stderr, "Improperly formatted OBJ file (3)\n"); |
| return NULL; |
| } |
| *len = c; |
| |
| chksum -= c; |
| |
| c = fgetc(fp); |
| if(c == EOF) |
| { |
| fprintf(stderr, "Improperly formatted OBJ file (4)\n"); |
| return NULL; |
| } |
| |
| *len += (c << 8); |
| |
| chksum -= c; |
| |
| *len -= 4; // Subtract header and length bytes from length |
| if(*len < 0) |
| { |
| fprintf(stderr, "Improperly formatted OBJ file (5)\n"); |
| return NULL; |
| } |
| |
| buf = malloc(*len); |
| if(buf == NULL) |
| { |
| fprintf(stderr, "Out of memory allocating %d bytes\n", *len); |
| return NULL; // Bad alloc |
| } |
| |
| i = fread(buf, 1, *len, fp); |
| if(i < *len) |
| { |
| free(buf); |
| fprintf(stderr, "Improperly formatted OBJ file (6)\n"); |
| return NULL; |
| } |
| |
| for(i = 0; i < *len; i++) |
| { |
| chksum -= (buf[i] & 0xff); |
| } |
| |
| c = fgetc(fp); |
| c &= 0xff; |
| chksum &= 0xff; |
| |
| if(c != chksum) |
| { |
| free(buf); |
| fprintf(stderr, "Bad record checksum, " |
| "calculated=%d, recorded=%d\n", chksum, c); |
| return NULL; |
| } |
| |
| return buf; |
| } |
| |
| void dump_bytes(char *buf, int len) |
| { |
| int i, j; |
| |
| for(i = 0; i < len; i += 8) |
| { |
| printf("\t%3.3o: ", i); |
| for(j = i; j < len && j < i+8; j++) |
| { |
| printf("%3.3o ", buf[j] & 0xff); |
| } |
| |
| printf("%*s", (i+8 - j) * 4, ""); |
| |
| for(j = i; j < len && j < i+8; j++) |
| { |
| int c = buf[j] & 0xff; |
| if(!isprint(c)) |
| c = '.'; |
| putchar(c); |
| } |
| |
| putchar('\n'); |
| } |
| } |
| |
| void dump_words(unsigned addr, char *buf, int len) |
| { |
| int i, j; |
| |
| for(i = 0; i < len; i += 8) |
| { |
| printf("\t%6.6o: ", addr); |
| |
| for(j = i; j < len && j < i+8; j += 2) |
| { |
| if(len - j >= 2) |
| { |
| unsigned word = WORD(buf + j); |
| printf("%6.6o ", word); |
| } |
| else |
| printf("%3.3o ", buf[j] & 0xff); |
| } |
| |
| printf("%*s", (i+8 - j) * 7 / 2, ""); |
| |
| for(j = i; j < len && j < i+8; j++) |
| { |
| int c = buf[j] & 0xff; |
| if(!isprint(c)) |
| c = '.'; |
| putchar(c); |
| } |
| |
| putchar('\n'); |
| addr += 8; |
| } |
| } |
|
|
| void dump_bin(unsigned addr, char *buf, int len)
|
| {
|
| int chksum; /* Checksum is negative sum of all
|
| bytes including header and length */
|
| int FBR_LEAD1 = 1, FBR_LEAD2 = 0;
|
| int i;
|
| unsigned hdrlen = len + 6;
|
|
|
| for(i = 0; i < 8; i++) fputc (0, bin);
|
| chksum = 0;
|
| if(fputc(FBR_LEAD1, bin) == EOF) return; /* All recs begin with 1,0 */
|
| chksum -= FBR_LEAD1;
|
| if(fputc(FBR_LEAD2, bin) == EOF) return;
|
| chksum -= FBR_LEAD2;
|
|
|
| i = hdrlen & 0xff; /* length, lsb */
|
| chksum -= i;
|
| if(fputc(i, bin) == EOF) return;
|
|
|
| i = (hdrlen >> 8) & 0xff; /* length, msb */
|
| chksum -= i;
|
| if(fputc(i, bin) == EOF) return;
|
|
|
| i = addr & 0xff; /* origin, msb */
|
| chksum -= i;
|
| if(fputc(i, bin) == EOF) return;
|
|
|
| i = (addr >> 8) & 0xff; /* origin, lsb */
|
| chksum -= i;
|
| if(fputc(i, bin) == EOF) return;
|
|
|
| if ((len == 0) || (buf == NULL)) return; /* end of tape block */
|
|
|
| i = fwrite(buf, 1, len, bin);
|
| if(i < len) return;
|
|
|
| while(len > 0) /* All the data bytes */
|
| {
|
| chksum -= *buf++ & 0xff;
|
| len--;
|
| }
|
|
|
| chksum &= 0xff;
|
|
|
| fputc(chksum, bin); /* Followed by the checksum byte */
|
|
|
| return; /* Worked okay. */
|
| }
|
|
|
| void trim(char *buf) |
| { |
| char *cp; |
| |
| for(cp = buf + strlen(buf); cp > buf; cp--) |
| { |
| if(cp[-1] != ' ') |
| break; |
| } |
| *cp = 0; |
| } |
| |
| char **all_gsds = NULL; |
| int nr_gsds = 0; |
| int gsdsize = 0; |
| |
| void add_gsdline(char *line) |
| { |
| if(nr_gsds >= gsdsize || all_gsds == NULL) |
| { |
| gsdsize += 128; |
| all_gsds = realloc(all_gsds, gsdsize * sizeof(char *)); |
| if(all_gsds == NULL) |
| { |
| fprintf(stderr, "Out of memory\n"); |
| exit(EXIT_FAILURE); |
| } |
| } |
| |
| all_gsds[nr_gsds++] = line; |
| } |
| |
| void got_gsd(char *cp, int len) |
| { |
| int i; |
| char *gsdline; |
| |
| for(i = 2; i < len; i += 8) |
| { |
| char name[8]; |
| unsigned value; |
| unsigned flags; |
| |
| gsdline = malloc(256); |
| if(gsdline == NULL) |
| { |
| fprintf(stderr, "Out of memory\n"); |
| exit(EXIT_FAILURE); |
| } |
| |
| unrad50(WORD(cp+i), name); |
| unrad50(WORD(cp+i+2), name+3); |
| name[6] = 0; |
| |
| value = WORD(cp+i+6); |
| flags = cp[i+4] & 0xff; |
| |
| switch(cp[i+5] & 0xff) |
| { |
| case 0: |
| sprintf(gsdline, |
| "\tMODNAME %s=%o flags=%o\n", name, value, flags); |
| break; |
| case 1: |
| sprintf(gsdline, |
| "\tCSECT %s=%o flags=%o\n", name, value, flags);
|
| break; |
| case 2: |
| sprintf(gsdline, |
| "\tISD %s=%o flags=%o\n", name, value, flags); |
| break; |
| case 3: |
| sprintf(gsdline, |
| "\tXFER %s=%o flags=%o\n", name, value, flags);
|
| xferad = value; |
| break; |
| case 4: |
| sprintf(gsdline, |
| "\tGLOBAL %s=%o %s flags=%o\n", |
| name, value, cp[i+4] & 8 ? "DEF" : "REF", flags);
|
| break; |
| case 5: |
| sprintf(gsdline, |
| "\tPSECT %s=%o flags=%o\n", name, value, flags); |
| psects[psectid] = strdup(name); |
| trim(psects[psectid++]); |
| break; |
| case 6: |
| sprintf(gsdline, |
| "\tIDENT %s=%o flags=%o\n", name, value, flags); |
| break; |
| case 7: |
| sprintf(gsdline, |
| "\tVSECT %s=%o flags=%o\n", name, value, flags); |
| break; |
| default: |
| sprintf(gsdline, |
| "\t***Unknown GSD entry type %d flags=%o\n", |
| cp[i+5] & 0xff, flags); |
| break; |
| } |
| |
| gsdline = realloc(gsdline, strlen(gsdline)+1); |
| add_gsdline(gsdline); |
| } |
| |
| } |
| |
| int compare_gsdlines(const void *p1, const void *p2) |
| { |
| const char * const *l1 = p1, * const *l2 = p2; |
| |
| return strcmp(*l1, *l2); |
| } |
| |
| void got_endgsd(char *cp, int len) |
| { |
| int i; |
| |
| qsort(all_gsds, nr_gsds, sizeof(char *), compare_gsdlines); |
| |
| printf("GSD:\n"); |
| |
| for(i = 0; i < nr_gsds; i++) |
| { |
| fputs(all_gsds[i], stdout); |
| free(all_gsds[i]); |
| } |
| |
| printf("ENDGSD\n"); |
| |
| free(all_gsds); |
| } |
| |
| unsigned last_text_addr = 0; |
| |
| void got_text(char *cp, int len) |
| { |
| unsigned addr = WORD(cp+2); |
| |
| last_text_addr = addr; |
| |
| printf("TEXT ADDR=%o LEN=%o\n", last_text_addr, len-4); |
| |
| dump_words(last_text_addr, cp+4, len-4);
|
|
|
| if (bin) dump_bin(last_text_addr, cp+4, len-4);
|
| } |
| |
| void rad50name(char *cp, char *name) |
| { |
| unrad50(WORD(cp), name); |
| unrad50(WORD(cp+2), name+3); |
| name[6] = 0; |
| trim(name); |
| } |
| |
| void got_rld(char *cp, int len) |
| { |
| int i; |
| printf("RLD\n"); |
| |
| for(i = 2; i < len;) |
| { |
| unsigned addr; |
| unsigned word; |
| unsigned disp = cp[i+1] & 0xff; |
| char name[8]; |
| char *byte; |
| |
| addr = last_text_addr + disp - 4; |
| |
| byte = ""; |
| if(cp[i] & 0200) |
| byte = " byte"; |
| |
| switch(cp[i] & 0x7f) |
| { |
| case 01: |
| printf("\tInternal%s %o=%o\n", byte, addr, WORD(cp+i+2)); |
| i += 4; |
| break; |
| case 02: |
| rad50name(cp+i+2, name); |
| printf("\tGlobal%s %o=%s\n", byte, addr, name); |
| i += 6; |
| break; |
| case 03: |
| printf("\tInternal displaced%s %o=%o\n", byte, addr, WORD(cp+i+2)); |
| i += 4; |
| badbin = 1;
|
| break; |
| case 04: |
| rad50name(cp+i+2, name); |
| printf("\tGlobal displaced%s %o=%s\n", byte, addr, name); |
| i += 6; |
| badbin = 1;
|
| break; |
| case 05: |
| rad50name(cp+i+2, name); |
| word = WORD(cp+i+6); |
| printf("\tGlobal plus offset%s %o=%s+%o\n", |
| byte, addr, name, word); |
| i += 8; |
| badbin = 1;
|
| break; |
| case 06: |
| rad50name(cp+i+2, name); |
| word = WORD(cp+i+6); |
| printf("\tGlobal plus offset displaced%s %o=%s+%o\n", |
| byte, addr, name, word); |
| i += 8; |
| badbin = 1;
|
| break; |
| case 07: |
| rad50name(cp+i+2, name); |
| word = WORD(cp+i+6); |
| printf("\tLocation counter definition %s+%o\n", |
| name, word); |
| i += 8; |
| |
| last_text_addr = word; |
| break; |
| case 010: |
| word = WORD(cp+i+2); |
| printf("\tLocation counter modification %o\n", word); |
| i += 4; |
| |
| last_text_addr = word; |
| break; |
| case 011: |
| printf("\t.LIMIT %o\n", addr); |
| i += 2; |
| break; |
| |
| case 012: |
| rad50name(cp+i+2, name); |
| printf("\tPSECT%s %o=%s\n", byte, addr, name); |
| i += 6; |
| badbin = 1;
|
| break; |
| case 014: |
| rad50name(cp+i+2, name); |
| |
| printf("\tPSECT displaced%s %o=%s+%o\n", byte, addr, name, word); |
| i += 6; |
| badbin = 1;
|
| break; |
| case 015: |
| rad50name(cp+i+2, name); |
| word = WORD(cp+i+6); |
| printf("\tPSECT plus offset%s %o=%s+%o\n", |
| byte, addr, name, word); |
| i += 8; |
| badbin = 1;
|
| break; |
| case 016: |
| rad50name(cp+i+2, name); |
| word = WORD(cp+i+6); |
| printf("\tPSECT plus offset displaced%s %o=%s+%o\n", |
| byte, addr, name, word); |
| i += 8; |
| badbin = 1;
|
| break; |
| |
| case 017: |
| badbin = 1;
|
| printf("\tComplex%s %o=", byte, addr); |
| i += 2; |
| { |
| char *xp = cp + i; |
| int size; |
| for(;;) |
| { |
| size = 1; |
| switch(*xp) |
| { |
| case 000: |
| fputs("nop ", stdout); break; |
| case 001: |
| fputs("+ ", stdout); break; |
| case 002: |
| fputs("- ", stdout); break; |
| case 003: |
| fputs("* ", stdout); break; |
| case 004: |
| fputs("/ ", stdout); break; |
| case 005: |
| fputs("& ", stdout); break; |
| case 006: |
| fputs("! ", stdout); break; |
| case 010: |
| fputs("neg ", stdout); break; |
| case 011: |
| fputs("^C ", stdout); break; |
| case 012: |
| fputs("store ", stdout); break; |
| case 013: |
| fputs("store{disp} ", stdout); break; |
| |
| case 016: |
| rad50name(xp+1, name); |
| printf("%s ", name); |
| size = 5; |
| break; |
| |
| case 017: |
| assert((xp[1] & 0377) < psectid); |
| printf("%s:%o ", |
| psects[xp[1] & 0377], |
| WORD(xp+2)); |
| size = 4; |
| break; |
| |
| case 020: |
| printf("%o ", WORD(xp+1)); |
| size = 3; |
| break; |
| default: |
| printf("**UNKNOWN COMPLEX CODE** %o\n", *xp & 0377); |
| return; |
| } |
| i += size; |
| if(*xp == 012 || *xp == 013) |
| break; |
| xp += size; |
| } |
| fputc('\n', stdout); |
| break; |
| } |
| |
| default: |
| printf("\t***Unknown RLD code %o\n", cp[i] & 0xff); |
| return; |
| } |
| } |
| |
| } |
| |
| void got_isd(char *cp, int len) |
| { |
| printf("ISD len=%o\n"); |
| } |
| |
| void got_endmod(char *cp, int len) |
| { |
| printf("ENDMOD\n"); |
| } |
| |
| void got_libhdr(char *cp, int len) |
| { |
| printf("LIBHDR\n"); |
| } |
| |
| void got_libend(char *cp, int len) |
| { |
| printf("LIBEND\n"); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int len; |
| FILE *fp; |
| char *cp; |
| |
| fp = fopen(argv[1], "rb"); |
| if(fp == NULL) |
| return EXIT_FAILURE;
|
| if(argv[2])
|
| {
|
| bin = fopen(argv[2], "wb");
|
| if(bin == NULL) return EXIT_FAILURE;
|
| } |
| |
| while((cp = readrec(fp, &len)) != NULL) |
| { |
| switch(cp[0] & 0xff) |
| { |
| case 1: |
| got_gsd(cp, len); |
| break; |
| case 2: |
| got_endgsd(cp, len); |
| break; |
| case 3: |
| got_text(cp, len); |
| break; |
| case 4: |
| got_rld(cp, len); |
| break; |
| case 5: |
| got_isd(cp, len); |
| break; |
| case 6: |
| got_endmod(cp, len); |
| break; |
| case 7: |
| got_libhdr(cp, len); |
| break; |
| case 8: |
| got_libend(cp, len); |
| break; |
| default: |
| printf("Unknown record type %d\n", cp[0] & 0xff); |
| break; |
| } |
| |
| free(cp); |
| }
|
| |
| if (bin)
|
| { dump_bin (xferad, NULL, 0);
|
| fclose (bin);
|
| if (badbin) fprintf (stderr, "Probable errors in binary file\n");
|
| }
|
|
|
| fclose (fp); |
| return EXIT_SUCCESS; |
| } |