omfdump.c 11 KB

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