symtab loader
[ia32rtools.git] / cmpmrg_text.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <linux/coff.h>
5 #include <assert.h>
6 #include <stdint.h>
7
8 /* http://www.delorie.com/djgpp/doc/coff/ */
9
10 typedef struct {
11   unsigned short f_magic;         /* magic number             */
12   unsigned short f_nscns;         /* number of sections       */
13   unsigned int   f_timdat;        /* time & date stamp        */
14   unsigned int   f_symptr;        /* file pointer to symtab   */
15   unsigned int   f_nsyms;         /* number of symtab entries */
16   unsigned short f_opthdr;        /* sizeof(optional hdr)     */
17   unsigned short f_flags;         /* flags                    */
18 } FILHDR;
19
20 typedef struct {
21   unsigned short magic;          /* type of file                         */
22   unsigned short vstamp;         /* version stamp                        */
23   unsigned int   tsize;          /* text size in bytes, padded to FW bdry*/
24   unsigned int   dsize;          /* initialized data    "  "             */
25   unsigned int   bsize;          /* uninitialized data  "  "             */
26   unsigned int   entry;          /* entry pt.                            */
27   unsigned int   text_start;     /* base of text used for this file      */
28   unsigned int   data_start;     /* base of data used for this file      */
29 } AOUTHDR;
30
31 typedef struct {
32   char           s_name[8];  /* section name                     */
33   unsigned int   s_paddr;    /* physical address, aliased s_nlib */
34   unsigned int   s_vaddr;    /* virtual address                  */
35   unsigned int   s_size;     /* section size                     */
36   unsigned int   s_scnptr;   /* file ptr to raw data for section */
37   unsigned int   s_relptr;   /* file ptr to relocation           */
38   unsigned int   s_lnnoptr;  /* file ptr to line numbers         */
39   unsigned short s_nreloc;   /* number of relocation entries     */
40   unsigned short s_nlnno;    /* number of line number entries    */
41   unsigned int   s_flags;    /* flags                            */
42 } SCNHDR;
43
44 typedef struct {
45   unsigned int  r_vaddr;   /* address of relocation      */
46   unsigned int  r_symndx;  /* symbol we're adjusting for */
47   unsigned short r_type;    /* type of relocation         */
48 } __attribute__((packed)) RELOC;
49
50 typedef struct {
51   union {
52     char e_name[E_SYMNMLEN];
53     struct {
54       unsigned int e_zeroes;
55       unsigned int e_offset;
56     } e;
57   } e;
58   unsigned int e_value;
59   short e_scnum;
60   unsigned short e_type;
61   unsigned char e_sclass;
62   unsigned char e_numaux;
63 } __attribute__((packed)) SYMENT;
64
65 #define C_EXT 2
66
67 struct my_symtab {
68   unsigned int addr;
69   unsigned int fpos; // for patching
70   char *name;
71 };
72
73 static void my_assert_(int line, const char *name, long v, long expect, int is_eq)
74 {
75         int ok;
76         if (is_eq)
77                 ok = (v == expect);
78         else
79                 ok = (v != expect);
80
81         if (!ok)
82         {
83                 printf("%d: '%s' is %lx, need %s%lx\n", line, name,
84                         v, is_eq ? "" : "!", expect);
85                 exit(1);
86         }
87 }
88 #define my_assert(v, exp) \
89         my_assert_(__LINE__, #v, (long)(v), (long)(exp), 1)
90 #define my_assert_not(v, exp) \
91         my_assert_(__LINE__, #v, (long)(v), (long)(exp), 0)
92
93 static int symt_cmp(const void *p1_, const void *p2_)
94 {
95         const struct my_symtab *p1 = p1_, *p2 = p2_;
96         return p1->addr - p2->addr;
97 }
98
99 void parse_headers(FILE *f, unsigned int *base_out,
100         long *sect_ofs, uint8_t **sect_data, long *sect_sz,
101         RELOC **relocs, long *reloc_cnt,
102         struct my_symtab **symtab_out, long *sym_cnt)
103 {
104         struct my_symtab *symt_o = NULL;
105         char *stringtab = NULL;
106         unsigned int base = 0;
107         int text_scnum = 0;
108         long filesize;
109         char symname[9];
110         long opthdr_pos;
111         long reloc_size;
112         FILHDR hdr;
113         AOUTHDR opthdr;
114         SCNHDR scnhdr;
115         SYMENT syment;
116         int i, s, val;
117         int ret;
118         
119         ret = fseek(f, 0, SEEK_END);
120         my_assert(ret, 0);
121
122         filesize = ftell(f);
123
124         ret = fseek(f, 0, SEEK_SET);
125         my_assert(ret, 0);
126
127         ret = fread(&hdr, 1, sizeof(hdr), f);
128         my_assert(ret, sizeof(hdr));
129
130         if (hdr.f_magic == 0x5a4d) // MZ
131         {
132                 ret = fseek(f, 0x3c, SEEK_SET);
133                 my_assert(ret, 0);
134                 ret = fread(&val, 1, sizeof(val), f);
135                 my_assert(ret, sizeof(val));
136
137                 ret = fseek(f, val, SEEK_SET);
138                 my_assert(ret, 0);
139                 ret = fread(&val, 1, sizeof(val), f);
140                 my_assert(ret, sizeof(val));
141                 my_assert(val, 0x4550); // PE
142
143                 // should be COFF now
144                 ret = fread(&hdr, 1, sizeof(hdr), f);
145                 my_assert(ret, sizeof(hdr));
146         }
147
148         my_assert(hdr.f_magic, COFF_I386MAGIC);
149
150         if (hdr.f_opthdr != 0)
151         {
152                 opthdr_pos = ftell(f);
153
154                 if (hdr.f_opthdr < sizeof(opthdr))
155                         my_assert(1, 0);
156
157                 ret = fread(&opthdr, 1, sizeof(opthdr), f);
158                 my_assert(ret, sizeof(opthdr));
159                 my_assert(opthdr.magic, COFF_ZMAGIC);
160
161                 printf("text_start: %x\n", opthdr.text_start);
162
163                 if (hdr.f_opthdr > sizeof(opthdr)) {
164                         ret = fread(&base, 1, sizeof(base), f);
165                         my_assert(ret, sizeof(base));
166                         printf("base: %x\n", base);
167                 }
168                 ret = fseek(f, opthdr_pos + hdr.f_opthdr, SEEK_SET);
169                 my_assert(ret, 0);
170         }
171
172         // note: assuming first non-empty one is .text ..
173         for (s = 0; s < hdr.f_nscns; s++) {
174                 ret = fread(&scnhdr, 1, sizeof(scnhdr), f);
175                 my_assert(ret, sizeof(scnhdr));
176
177                 if (scnhdr.s_size != 0) {
178                         text_scnum = s + 1;
179                         break;
180                 }
181         }
182
183         printf("f_nsyms:  %x\n", hdr.f_nsyms);
184         printf("s_name:   '%s'\n", scnhdr.s_name);
185         printf("s_vaddr:  %x\n", scnhdr.s_vaddr);
186         printf("s_size:   %x\n", scnhdr.s_size);
187         //printf("s_scnptr: %x\n", scnhdr.s_scnptr);
188         printf("s_nreloc: %x\n", scnhdr.s_nreloc);
189         printf("--\n");
190
191         ret = fseek(f, scnhdr.s_scnptr, SEEK_SET);
192         my_assert(ret, 0);
193
194         *sect_data = malloc(scnhdr.s_size);
195         my_assert_not(*sect_data, NULL);
196         ret = fread(*sect_data, 1, scnhdr.s_size, f);
197         my_assert(ret, scnhdr.s_size);
198
199         *sect_ofs = scnhdr.s_scnptr;
200         *sect_sz = scnhdr.s_size;
201
202         // relocs
203         ret = fseek(f, scnhdr.s_relptr, SEEK_SET);
204         my_assert(ret, 0);
205
206         reloc_size = scnhdr.s_nreloc * sizeof((*relocs)[0]);
207         *relocs = malloc(reloc_size + 1);
208         my_assert_not(*relocs, NULL);
209         ret = fread(*relocs, 1, reloc_size, f);
210         my_assert(ret, reloc_size);
211
212         *reloc_cnt = scnhdr.s_nreloc;
213
214         // symtab
215         if (hdr.f_nsyms != 0) {
216                 symname[8] = 0;
217
218                 symt_o = malloc(hdr.f_nsyms * sizeof(symt_o[0]) + 1);
219                 my_assert_not(symt_o, NULL);
220
221                 ret = fseek(f, hdr.f_symptr
222                                 + hdr.f_nsyms * sizeof(syment), SEEK_SET);
223                 my_assert(ret, 0);
224                 ret = fread(&i, 1, sizeof(i), f);
225                 my_assert(ret, sizeof(i));
226                 my_assert((unsigned int)i < filesize, 1);
227
228                 stringtab = malloc(i);
229                 my_assert_not(stringtab, NULL);
230                 memset(stringtab, 0, 4);
231                 ret = fread(stringtab + 4, 1, i - 4, f);
232                 my_assert(ret, i - 4);
233
234                 ret = fseek(f, hdr.f_symptr, SEEK_SET);
235                 my_assert(ret, 0);
236         }
237
238         for (i = s = 0; i < hdr.f_nsyms; i++) {
239                 long pos = ftell(f);
240
241                 ret = fread(&syment, 1, sizeof(syment), f);
242                 my_assert(ret, sizeof(syment));
243
244                 strncpy(symname, syment.e.e_name, 8);
245                 //printf("%3d %2d %08x '%s'\n", syment.e_sclass,
246                 //      syment.e_scnum, syment.e_value, symname);
247
248                 if (syment.e_scnum != text_scnum || syment.e_sclass != C_EXT)
249                         continue;
250
251                 symt_o[s].addr = syment.e_value;
252                 symt_o[s].fpos = pos;
253                 if (syment.e.e.e_zeroes == 0)
254                         symt_o[s].name = stringtab + syment.e.e.e_offset;
255                 else
256                         symt_o[s].name = strdup(symname);
257                 s++;
258
259                 if (syment.e_numaux) {
260                         ret = fseek(f, syment.e_numaux * sizeof(syment),
261                                     SEEK_CUR);
262                         my_assert(ret, 0);
263                         i += syment.e_numaux;
264                 }
265         }
266
267         if (symt_o != NULL)
268                 qsort(symt_o, s, sizeof(symt_o[0]), symt_cmp);
269
270         *sym_cnt = s;
271         *symtab_out = symt_o;
272
273         if (base != 0 && base_out != NULL)
274                 *base_out = base + scnhdr.s_vaddr;
275 }
276
277 static int handle_pad(uint8_t *d_obj, uint8_t *d_exe, int maxlen)
278 {
279         static const uint8_t p7[7] = { 0x8d, 0xa4, 0x24, 0x00, 0x00, 0x00, 0x00 };
280         static const uint8_t p6[6] = { 0x8d, 0x9b, 0x00, 0x00, 0x00, 0x00 };
281         static const uint8_t p5[5] = { 0x05, 0x00, 0x00, 0x00, 0x00 };
282         static const uint8_t p4[4] = { 0x8d, 0x64, 0x24, 0x00 };
283         static const uint8_t p3[3] = { 0x8d, 0x49, 0x00 };
284         static const uint8_t p2[2] = { 0x8b, 0xff };
285         static const uint8_t p1[1] = { 0x90 };
286         int len;
287         int i;
288
289         for (i = 0; i < maxlen; i++)
290                 if (d_exe[i] != 0xcc)
291                         break;
292
293         for (len = i; len > 0; )
294         {
295                 i = len;
296                 if (i > 7)
297                         i = 7;
298
299                 switch (i) {
300                 #define CASE(x) \
301                 case sizeof(p ## x): \
302                         if (memcmp(d_obj, p ## x, sizeof(p ## x))) \
303                                 return 0; \
304                         memset(d_obj, 0xcc, sizeof(p ## x)); \
305                         break;
306                 CASE(7)
307                 CASE(6)
308                 CASE(5)
309                 CASE(4)
310                 CASE(3)
311                 CASE(2)
312                 CASE(1)
313                 default:
314                         printf("%s: unhandled len: %d\n", __func__, len);
315                         return 0;
316                 #undef CASE
317                 }
318
319                 len -= i;
320                 d_obj += i;
321         }
322
323         return 1;
324 }
325
326 struct equiv_opcode {
327         signed char len;
328         signed char ofs;
329         short cmp_rm;
330         uint8_t v_masm[8];
331         uint8_t v_masm_mask[8];
332         uint8_t v_msvc[8];
333         uint8_t v_msvc_mask[8];
334 } equiv_ops[] = {
335         // cmp    $0x11,%ax
336         { 4, -1, 0,
337          { 0x66,0x83,0xf8,0x03 }, { 0xff,0xff,0xff,0x00 },
338          { 0x66,0x3d,0x03,0x00 }, { 0xff,0xff,0x00,0xff }, },
339         // lea    -0x1(%ebx,%eax,1),%esi // op mod/rm sib offs
340         // mov, test, imm grp 1
341         { 3, -2, 1,
342          { 0x8d,0x74,0x03 }, { 0xf0,0x07,0xc0 },
343          { 0x8d,0x74,0x18 }, { 0xf0,0x07,0xc0 }, },
344         // movzbl 0x58f24a(%eax,%ecx,1),%eax
345         { 4, -3, 1,
346          { 0x0f,0xb6,0x84,0x08 }, { 0xff,0xff,0x07,0xc0 },
347          { 0x0f,0xb6,0x84,0x01 }, { 0xff,0xff,0x07,0xc0 }, },
348         // inc/dec
349         { 3, -2, 1,
350          { 0xfe,0x4c,0x03 }, { 0xfe,0xff,0xc0 },
351          { 0xfe,0x4c,0x18 }, { 0xfe,0xff,0xc0 }, },
352         // cmp
353         { 3, -2, 1,
354          { 0x38,0x0c,0x0c }, { 0xff,0xff,0xc0 },
355          { 0x38,0x0c,0x30 }, { 0xff,0xff,0xc0 }, },
356         // test   %dl,%bl
357         { 2, -1, 1,
358          { 0x84,0xd3 }, { 0xfe,0xc0 },
359          { 0x84,0xda }, { 0xfe,0xc0 }, },
360         // cmp    r,r/m vs rm/r
361         { 2, 0, 1,
362          { 0x3a,0xca }, { 0xff,0xc0 },
363          { 0x38,0xd1 }, { 0xff,0xc0 }, },
364         // rep + 66 prefix
365         { 2, 0, 0,
366          { 0xf3,0x66 }, { 0xfe,0xff },
367          { 0x66,0xf3 }, { 0xff,0xfe }, },
368         // fadd   st, st(0) vs st(0), st
369         { 2, 0, 0,
370          { 0xd8,0xc0 }, { 0xff,0xf7 },
371          { 0xdc,0xc0 }, { 0xff,0xf7 }, },
372
373         // broad filters (may take too much..)
374         // testb  $0x4,0x1d(%esi,%eax,1)
375         // movb, push, ..
376         { 3, -2, 1,
377          { 0xf6,0x44,0x06 }, { 0x00,0x07,0xc0 },
378          { 0xf6,0x44,0x30 }, { 0x00,0x07,0xc0 }, },
379 };
380
381 static int cmp_mask(uint8_t *d, uint8_t *expect, uint8_t *mask, int len)
382 {
383         int i;
384
385         for (i = 0; i < len; i++)
386                 if ((d[i] & mask[i]) != (expect[i] & mask[i]))
387                         return 1;
388
389         return 0;
390 }
391
392 static int check_equiv(uint8_t *d_obj, uint8_t *d_exe, int maxlen)
393 {
394         uint8_t vo, ve, vo2, ve2;
395         int i, jo, je;
396         int len, ofs;
397
398         for (i = 0; i < sizeof(equiv_ops) / sizeof(equiv_ops[0]); i++)
399         {
400                 struct equiv_opcode *op = &equiv_ops[i];
401
402                 len = op->len;
403                 if (maxlen < len)
404                         continue;
405
406                 ofs = op->ofs;
407                 if (cmp_mask(d_obj + ofs, op->v_masm,
408                              op->v_masm_mask, len))
409                         continue;
410                 if (cmp_mask(d_exe + ofs, op->v_msvc,
411                              op->v_msvc_mask, len))
412                         continue;
413
414                 jo = je = 0;
415                 d_obj += ofs;
416                 d_exe += ofs;
417                 while (1)
418                 {
419                         for (; jo < len; jo++)
420                                 if (op->v_masm_mask[jo] != 0xff)
421                                         break;
422                         for (; je < len; je++)
423                                 if (op->v_msvc_mask[je] != 0xff)
424                                         break;
425
426                         if ((jo == len && je != len) || (jo != len && je == len)) {
427                                 printf("invalid equiv_ops\n");
428                                 return -1;
429                         }
430                         if (jo == len)
431                                 return len + ofs - 1; // matched
432
433                         // var byte
434                         vo = d_obj[jo] & ~op->v_masm_mask[jo];
435                         ve = d_exe[je] & ~op->v_msvc_mask[je];
436                         if (op->cmp_rm && op->v_masm_mask[jo] == 0xc0) {
437                                 vo2 = vo >> 3;
438                                 vo &= 7;
439                                 ve2 = ve & 7;
440                                 ve >>= 3;
441                                 if (vo != ve || vo2 != ve2)
442                                         return -1;
443                         }
444                         else {
445                                 if (vo != ve)
446                                         return -1;
447                         }
448
449                         jo++;
450                         je++;
451                 }
452         }
453
454         return -1;
455 }
456
457 int main(int argc, char *argv[])
458 {
459         FILE *f_obj, *f_exe;
460         long text_ofs_obj, text_ofs_exe;
461         long sztext_obj, sztext_exe, sztext_cmn;
462         RELOC *relocs_obj, *relocs_exe;
463         long reloc_cnt_obj, reloc_cnt_exe;
464         struct my_symtab *syms_obj, *syms_exe;
465         long sym_cnt_obj, sym_cnt_exe;
466         uint8_t *d_obj, *d_exe;
467         unsigned int base = 0;
468         int retval = 1;
469         int left;
470         int ret;
471         int i;
472
473         if (argc != 3) {
474                 printf("usage:\n%s <a_obj> <exe>\n", argv[0]);
475                 return 1;
476         }
477
478         f_obj = fopen(argv[1], "r+b");
479         if (f_obj == NULL) {
480                 fprintf(stderr, "%s", argv[1]);
481                 perror("");
482                 return 1;
483         }
484
485         f_exe = fopen(argv[2], "r");
486         if (f_exe == NULL) {
487                 fprintf(stderr, "%s", argv[2]);
488                 perror("");
489                 return 1;
490         }
491
492         parse_headers(f_obj, NULL, &text_ofs_obj, &d_obj, &sztext_obj,
493                 &relocs_obj, &reloc_cnt_obj, &syms_obj, &sym_cnt_obj);
494         parse_headers(f_exe, &base, &text_ofs_exe, &d_exe, &sztext_exe,
495                 &relocs_exe, &reloc_cnt_exe, &syms_exe, &sym_cnt_exe);
496
497         sztext_cmn = sztext_obj;
498         if (sztext_cmn > sztext_exe)
499                 sztext_cmn = sztext_exe;
500
501         if (sztext_cmn == 0) {
502                 printf("bad .text size(s): %ld, %ld\n",
503                         sztext_obj, sztext_exe);
504                 return 1;
505         }
506
507         for (i = 0; i < reloc_cnt_obj; i++)
508         {
509                 unsigned int a = relocs_obj[i].r_vaddr;
510                 //printf("%04x %08x\n", relocs_obj[i].r_type, a);
511
512                 switch (relocs_obj[i].r_type) {
513                 case 0x06:
514                         memset(d_obj + a, 0, 4);
515                         memset(d_exe + a, 0, 4);
516                         break;
517                 default:
518                         printf("unknown reloc %x @%08x\n",
519                                 relocs_obj[i].r_type, base + a);
520                         return 1;
521                 }
522         }
523
524         for (i = 0; i < sztext_cmn; i++)
525         {
526                 if (d_obj[i] == d_exe[i])
527                         continue;
528
529                 left = sztext_cmn - i;
530
531                 if (d_exe[i] == 0xcc) { // padding
532                         if (handle_pad(d_obj + i, d_exe + i, left))
533                                 continue;
534                 }
535
536                 ret = check_equiv(d_obj + i, d_exe + i, left);
537                 if (ret >= 0) {
538                         i += ret;
539                         continue;
540                 }
541
542                 printf("%x: %02x vs %02x\n", base + i, d_obj[i], d_exe[i]);
543                 goto out;
544         }
545
546 out:
547         return retval;
548 }