1 /*
2  * omfdump.c
3  *
4  * Very simple program to dump the contents of an OMF (OBJ) file
5  *
6  * This assumes a littleendian, unaligned-load-capable host and a
7  * C compiler which handles basic C99.
8  */
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <inttypes.h>
13 #include <ctype.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <stdbool.h>
17 #include <string.h>
18 #include <sys/mman.h>
19 #include <sys/stat.h>
20 
21 const char *progname;
22 
23 static const char *record_types[256] =
24 {
25     [0x80] = "THEADR",
26     [0x82] = "LHEADR",
27     [0x88] = "COMENT",
28     [0x8a] = "MODEND16",
29     [0x8b] = "MODEND32",
30     [0x8c] = "EXTDEF",
31     [0x90] = "PUBDEF16",
32     [0x91] = "PUBDEF32",
33     [0x94] = "LINNUM16",
34     [0x95] = "LINNUM32",
35     [0x96] = "LNAMES",
36     [0x98] = "SEGDEF16",
37     [0x99] = "SEGDEF32",
38     [0x9a] = "GRPDEF",
39     [0x9c] = "FIXUPP16",
40     [0x9d] = "FIXUPP32",
41     [0xa0] = "LEDATA16",
42     [0xa1] = "LEDATA32",
43     [0xa2] = "LIDATA16",
44     [0xa3] = "LIDATA32",
45     [0xb0] = "COMDEF",
46     [0xb2] = "BAKPAT16",
47     [0xb3] = "BAKPAT32",
48     [0xb4] = "LEXTDEF",
49     [0xb6] = "LPUBDEF16",
50     [0xb7] = "LPUBDEF32",
51     [0xb8] = "LCOMDEF",
52     [0xbc] = "CEXTDEF",
53     [0xc2] = "COMDAT16",
54     [0xc3] = "COMDAT32",
55     [0xc4] = "LINSYM16",
56     [0xc5] = "LINSYM32",
57     [0xc6] = "ALIAS",
58     [0xc8] = "NBKPAT16",
59     [0xc9] = "NBKPAT32",
60     [0xca] = "LLNAMES",
61     [0xcc] = "VERNUM",
62     [0xce] = "VENDEXT",
63     [0xf0] = "LIBHDR",
64     [0xf1] = "LIBEND",
65 };
66 
67 typedef void (*dump_func)(uint8_t, const uint8_t *, size_t);
68 
69 /* Ordered collection type */
70 struct collection {
71     size_t n;			/* Elements in collection (not including 0) */
72     size_t s;			/* Elements allocated (not including 0) */
73     const void **p;		/* Element pointers */
74 };
75 
76 struct collection c_names, c_lsegs, c_groups, c_extsym;
77 
nomem(void)78 static void nomem(void)
79 {
80     fprintf(stderr, "%s: memory allocation error\n", progname);
81     exit(1);
82 }
83 
84 #define INIT_SIZE 64
add_collection(struct collection * c,const void * p)85 static void add_collection(struct collection *c, const void *p)
86 {
87     if (c->n >= c->s) {
88 	size_t cs = c->s ? (c->s << 1) : INIT_SIZE;
89 	const void **cp = realloc(c->p, cs*sizeof(const void *));
90 
91 	if (!cp)
92 	    nomem();
93 
94 	c->p = cp;
95 	c->s = cs;
96 
97 	memset(cp + c->n, 0, (cs - c->n)*sizeof(const void *));
98     }
99 
100     c->p[++c->n] = p;
101 }
102 
get_collection(struct collection * c,size_t index)103 static const void *get_collection(struct collection *c, size_t index)
104 {
105     if (index >= c->n)
106 	return NULL;
107 
108     return c->p[index];
109 }
110 
hexdump_data(unsigned int offset,const uint8_t * data,size_t n,size_t field)111 static void hexdump_data(unsigned int offset, const uint8_t *data,
112                          size_t n, size_t field)
113 {
114     unsigned int i, j;
115 
116     for (i = 0; i < n; i += 16) {
117 	printf("   %04x: ", i+offset);
118 	for (j = 0; j < 16; j++) {
119             char sep = (j == 7) ? '-' : ' ';
120 	    if (i+j < field)
121 		printf("%02x%c", data[i+j], sep);
122 	    else if (i+j < n)
123                 printf("xx%c", sep); /* Beyond end of... */
124             else
125 		printf("   ");  /* No separator */
126 	}
127 	printf(" :  ");
128 	for (j = 0; j < 16; j++) {
129             if (i+j < n)
130                 putchar((i+j >= field) ? 'x' :
131                         isprint(data[i+j]) ? data[i+j] : '.');
132 	}
133 	putchar('\n');
134     }
135 }
136 
dump_unknown(uint8_t type,const uint8_t * data,size_t n)137 static void dump_unknown(uint8_t type, const uint8_t *data, size_t n)
138 {
139     (void)type;
140     hexdump_data(0, data, n, n);
141 }
142 
print_dostime(const uint8_t * p)143 static void print_dostime(const uint8_t *p)
144 {
145     uint16_t da = (p[3] << 8) + p[2];
146     uint16_t ti = (p[1] << 8) + p[0];
147 
148     printf("%04u-%02u-%02u %02u:%02u:%02u",
149            (da >> 9) + 1980, (da >> 5) & 15, da & 31,
150            (ti >> 11), (ti >> 5) & 63, (ti << 1) & 63);
151 }
152 
dump_coment_depfile(uint8_t type,const uint8_t * data,size_t n)153 static void dump_coment_depfile(uint8_t type, const uint8_t *data, size_t n)
154 {
155     if (n > 4 && data[4] == n-5) {
156         printf("   # ");
157         print_dostime(data);
158         printf("  %.*s\n", n-5, data+5);
159     }
160 
161     hexdump_data(2, data, n, n);
162 }
163 
164 static const dump_func dump_coment_class[256] = {
165     [0xe9] = dump_coment_depfile
166 };
167 
dump_coment(uint8_t type,const uint8_t * data,size_t n)168 static void dump_coment(uint8_t type, const uint8_t *data, size_t n)
169 {
170     uint8_t class;
171     static const char *coment_class[256] = {
172 	[0x00] = "Translator",
173 	[0x01] = "Copyright",
174 	[0x81] = "Library specifier",
175 	[0x9c] = "MS-DOS version",
176 	[0x9d] = "Memory model",
177 	[0x9e] = "DOSSEG",
178 	[0x9f] = "Library search",
179 	[0xa0] = "OMF extensions",
180 	[0xa1] = "New OMF extension",
181 	[0xa2] = "Link pass separator",
182 	[0xa3] = "LIBMOD",
183 	[0xa4] = "EXESTR",
184 	[0xa6] = "INCERR",
185 	[0xa7] = "NOPAD",
186 	[0xa8] = "WKEXT",
187 	[0xa9] = "LZEXT",
188 	[0xda] = "Comment",
189 	[0xdb] = "Compiler",
190 	[0xdc] = "Date",
191 	[0xdd] = "Timestamp",
192 	[0xdf] = "User",
193         [0xe3] = "Type definition",
194         [0xe8] = "Filename",
195 	[0xe9] = "Dependency file",
196 	[0xff] = "Command line"
197     };
198 
199     if (n < 2) {
200 	hexdump_data(type, data, 2, n);
201 	return;
202     }
203 
204     type  = data[0];
205     class = data[1];
206 
207     printf("   [NP=%d NL=%d UD=%02X] %02X %s\n",
208 	   (type >> 7) & 1,
209 	   (type >> 6) & 1,
210 	   type & 0x3f,
211 	   class,
212 	   coment_class[class] ? coment_class[class] : "???");
213 
214     if (dump_coment_class[class])
215         dump_coment_class[class](class, data+2, n-2);
216     else
217         hexdump_data(2, data+2, n-2, n-2);
218 }
219 
220 /* Parse an index field */
get_index(const uint8_t ** pp)221 static uint16_t get_index(const uint8_t **pp)
222 {
223     uint8_t c;
224 
225     c = *(*pp)++;
226     if (c & 0x80) {
227         return ((c & 0x7f) << 8) + *(*pp)++;
228     } else {
229         return c;
230     }
231 }
232 
get_16(const uint8_t ** pp)233 static uint16_t get_16(const uint8_t **pp)
234 {
235     uint16_t v = *(const uint16_t *)(*pp);
236     (*pp) += 2;
237 
238     return v;
239 }
240 
get_32(const uint8_t ** pp)241 static uint32_t get_32(const uint8_t **pp)
242 {
243     const uint32_t v = *(const uint32_t *)(*pp);
244     (*pp) += 4;
245 
246     return v;
247 }
248 
249 /* Returns a name as a C string in a newly allocated buffer */
lname(int index)250 char *lname(int index)
251 {
252     char *s;
253     const char *p = get_collection(&c_names, index);
254     size_t len;
255 
256     if (!p)
257 	return NULL;
258 
259     len = (uint8_t)p[0];
260 
261     s = malloc(len+1);
262     if (!s)
263 	nomem();
264 
265     memcpy(s, p+1, len);
266     s[len] = '\0';
267 
268     return s;
269 }
270 
271 /* LNAMES or LLNAMES */
dump_lnames(uint8_t type,const uint8_t * data,size_t n)272 static void dump_lnames(uint8_t type, const uint8_t *data, size_t n)
273 {
274     const uint8_t *p = data;
275     const uint8_t *end = data + n;
276 
277     while (p < end) {
278         size_t l = *p+1;
279         if (l > n) {
280 	    add_collection(&c_names, NULL);
281             printf("   # %4u 0x%04x: \"%.*s... <%zu missing bytes>\n",
282                    c_names.n, c_names.n, n-1, p+1, l-n);
283         } else {
284 	    add_collection(&c_names, p);
285             printf("   # %4u 0x%04x: \"%.*s\"\n",
286 		   c_names.n, c_names.n, l-1, p+1);
287         }
288         hexdump_data(p-data, p, l, n);
289         p += l;
290         n -= l;
291     }
292 }
293 
294 /* SEGDEF16 or SEGDEF32 */
dump_segdef(uint8_t type,const uint8_t * data,size_t n)295 static void dump_segdef(uint8_t type, const uint8_t *data, size_t n)
296 {
297     bool big = type & 1;
298     const uint8_t *p = data;
299     const uint8_t *end = data+n;
300     uint8_t attr;
301     static const char * const alignment[8] =
302 	{ "ABS", "BYTE", "WORD", "PARA", "PAGE", "DWORD", "LTL", "?ALIGN" };
303     static const char * const combine[8] =
304 	{ "PRIVATE", "?COMMON", "PUBLIC", "?COMBINE", "?PUBLIC", "STACK", "COMMON", "?PUBLIC" };
305     uint16_t idx;
306     char *s;
307 
308     if (p >= end)
309 	return;
310 
311     attr = *p++;
312 
313     printf("   # %s (A%u) %s (C%u) %s%s",
314 	   alignment[(attr >> 5) & 7], (attr >> 5) & 7,
315 	   combine[(attr >> 2) & 7], (attr >> 2) & 7,
316 	   (attr & 0x02) ? "MAXSIZE " : "",
317 	   (attr & 0x01) ? "USE32" : "USE16");
318 
319     if (((attr >> 5) & 7) == 0) {
320 	/* Absolute segment */
321 	if (p+3 > end)
322 	    goto dump;
323 	printf(" AT %04x:", get_16(&p));
324 	printf("%02x", *p++);
325     }
326 
327     if (big) {
328 	if (p+4 > end)
329 	    goto dump;
330 	printf(" size 0x%08x", get_32(&p));
331     } else {
332 	if (p+2 > end)
333 	    goto dump;
334 	printf(" size 0x%04x", get_16(&p));
335     }
336 
337     idx = get_index(&p);
338     if (p > end)
339 	goto dump;
340     s = lname(idx);
341     printf(" name '%s'", s);
342 
343     idx = get_index(&p);
344     if (p > end)
345 	goto dump;
346     s = lname(idx);
347     printf(" class '%s'", s);
348 
349     idx = get_index(&p);
350     if (p > end)
351 	goto dump;
352     s = lname(idx);
353     printf(" ovl '%s'", s);
354 
355 dump:
356     putchar('\n');
357     hexdump_data(0, data, n, n);
358 }
359 
360 /* FIXUPP16 or FIXUPP32 */
dump_fixupp(uint8_t type,const uint8_t * data,size_t n)361 static void dump_fixupp(uint8_t type, const uint8_t *data, size_t n)
362 {
363     bool big = type & 1;
364     const uint8_t *p = data;
365     const uint8_t *end = data + n;
366     static const char * const method_base[4] =
367         { "SEGDEF", "GRPDEF", "EXTDEF", "frame#" };
368 
369     while (p < end) {
370         const uint8_t *start = p;
371         uint8_t op = *p++;
372         uint16_t index;
373         uint32_t disp;
374 
375         if (!(op & 0x80)) {
376             /* THREAD record */
377             bool frame = !!(op & 0x40);
378 
379             printf("   THREAD %-7s%d%s method %c%d (%s)",
380                    frame ? "frame" : "target", op & 3,
381                    (op & 0x20) ? " +flag5?" : "",
382                    (op & 0x40) ? 'F' : 'T',
383                    op & 3, method_base[op & 3]);
384 
385             if ((op & 0x50) != 0x50) {
386                 printf(" index 0x%04x", get_index(&p));
387             }
388             putchar('\n');
389         } else {
390             /* FIXUP subrecord */
391             uint8_t fix;
392 
393             printf("   FIXUP  %s-rel location %2d offset 0x%03x",
394                    (op & 0x40) ? "seg" : "self",
395                    (op & 0x3c) >> 2,
396                    ((op & 3) << 8) + *p++);
397 
398             fix = *p++;
399             printf("\n          frame %s%d%s",
400                    (fix & 0x80) ? "thread " : "F",
401                    ((fix & 0x70) >> 4),
402                    ((fix & 0xc0) == 0xc0) ? "?" : "");
403 
404             if ((fix & 0xc0) == 0)
405                 printf(" datum 0x%04x", get_index(&p));
406 
407             printf("\n          target %s%d",
408                    (fix & 0x10) ? "thread " : "method T",
409                    fix & 3);
410 
411             if ((fix & 0x10) == 0)
412                 printf(" (%s)", method_base[fix & 3]);
413 
414             printf(" datum 0x%04x", get_index(&p));
415 
416             if ((fix & 0x08) == 0) {
417                 if (big) {
418                     printf(" disp 0x%08x", get_32(&p));
419                 } else {
420                     printf(" disp 0x%04x", get_16(&p));
421                 }
422             }
423             putchar('\n');
424         }
425         hexdump_data(start-data, start, p-start, n-(start-data));
426     }
427 }
428 
429 static const dump_func dump_type[256] =
430 {
431     [0x88] = dump_coment,
432     [0x96] = dump_lnames,
433     [0x98] = dump_segdef,
434     [0x99] = dump_segdef,
435     [0x9c] = dump_fixupp,
436     [0x9d] = dump_fixupp,
437     [0xca] = dump_lnames,
438 };
439 
dump_omf(int fd)440 int dump_omf(int fd)
441 {
442     struct stat st;
443     size_t len, n;
444     uint8_t type;
445     const uint8_t *p, *data;
446 
447     if (fstat(fd, &st))
448 	return -1;
449 
450     len = st.st_size;
451 
452     data = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
453     if (data == MAP_FAILED)
454 	return -1;
455 
456     p = data;
457     while (len >= 3) {
458 	uint8_t csum;
459 	int i;
460 
461 	type = p[0];
462 	n = *(uint16_t *)(p+1);
463 
464 	printf("%02x %-10s %4zd bytes",
465 	       type,
466 	       record_types[type] ? record_types[type] : "???",
467 	       n);
468 
469 	if (len < n+3) {
470 	    printf("\n  (truncated, only %zd bytes left)\n", len-3);
471 	    break;		/* Truncated */
472 	}
473 
474 	p += 3;	      /* Header doesn't count in the length */
475 	n--;	      /* Remove checksum byte */
476 
477 	csum = 0;
478 	for (i = -3; i < (int)n; i++)
479 	    csum -= p[i];
480 
481 	printf(", checksum %02X", p[i]);
482 	if (csum == p[i])
483 	    printf(" (valid)\n");
484 	else
485 	    printf(" (actual = %02X)\n", csum);
486 
487 	if (dump_type[type])
488 	    dump_type[type](type, p, n);
489 	else
490 	    dump_unknown(type, p, n);
491 
492 	p   += n+1;
493 	len -= (n+4);
494     }
495 
496     munmap((void *)data, st.st_size);
497     return 0;
498 }
499 
main(int argc,char * argv[])500 int main(int argc, char *argv[])
501 {
502     int fd;
503     int i;
504 
505     progname = argv[0];
506 
507     for (i = 1; i < argc; i++) {
508 	fd = open(argv[i], O_RDONLY);
509 	if (fd < 0 || dump_omf(fd)) {
510 	    perror(argv[i]);
511 	    return 1;
512 	}
513 	close(fd);
514     }
515 
516     return 0;
517 }
518