4 #include <linux/coff.h>
8 /* http://www.delorie.com/djgpp/doc/coff/ */
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 */
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 */
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 */
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;
52 char e_name[E_SYMNMLEN];
54 unsigned int e_zeroes;
55 unsigned int e_offset;
60 unsigned short e_type;
61 unsigned char e_sclass;
62 unsigned char e_numaux;
63 } __attribute__((packed)) SYMENT;
69 unsigned int fpos; // for patching
73 static void my_assert_(int line, const char *name, long v, long expect, int is_eq)
83 printf("%d: '%s' is %lx, need %s%lx\n", line, name,
84 v, is_eq ? "" : "!", expect);
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)
93 static int symt_cmp(const void *p1_, const void *p2_)
95 const struct my_symtab *p1 = p1_, *p2 = p2_;
96 return p1->addr - p2->addr;
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)
104 struct my_symtab *symt_o = NULL;
105 char *stringtab = NULL;
106 unsigned int base = 0;
119 ret = fseek(f, 0, SEEK_END);
124 ret = fseek(f, 0, SEEK_SET);
127 ret = fread(&hdr, 1, sizeof(hdr), f);
128 my_assert(ret, sizeof(hdr));
130 if (hdr.f_magic == 0x5a4d) // MZ
132 ret = fseek(f, 0x3c, SEEK_SET);
134 ret = fread(&val, 1, sizeof(val), f);
135 my_assert(ret, sizeof(val));
137 ret = fseek(f, val, SEEK_SET);
139 ret = fread(&val, 1, sizeof(val), f);
140 my_assert(ret, sizeof(val));
141 my_assert(val, 0x4550); // PE
143 // should be COFF now
144 ret = fread(&hdr, 1, sizeof(hdr), f);
145 my_assert(ret, sizeof(hdr));
148 my_assert(hdr.f_magic, COFF_I386MAGIC);
150 if (hdr.f_opthdr != 0)
152 opthdr_pos = ftell(f);
154 if (hdr.f_opthdr < sizeof(opthdr))
157 ret = fread(&opthdr, 1, sizeof(opthdr), f);
158 my_assert(ret, sizeof(opthdr));
159 my_assert(opthdr.magic, COFF_ZMAGIC);
161 printf("text_start: %x\n", opthdr.text_start);
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);
168 ret = fseek(f, opthdr_pos + hdr.f_opthdr, SEEK_SET);
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));
177 if (scnhdr.s_size != 0) {
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);
191 ret = fseek(f, scnhdr.s_scnptr, SEEK_SET);
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);
199 *sect_ofs = scnhdr.s_scnptr;
200 *sect_sz = scnhdr.s_size;
203 ret = fseek(f, scnhdr.s_relptr, SEEK_SET);
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);
212 *reloc_cnt = scnhdr.s_nreloc;
215 if (hdr.f_nsyms != 0) {
218 symt_o = malloc(hdr.f_nsyms * sizeof(symt_o[0]) + 1);
219 my_assert_not(symt_o, NULL);
221 ret = fseek(f, hdr.f_symptr
222 + hdr.f_nsyms * sizeof(syment), SEEK_SET);
224 ret = fread(&i, 1, sizeof(i), f);
225 my_assert(ret, sizeof(i));
226 my_assert((unsigned int)i < filesize, 1);
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);
234 ret = fseek(f, hdr.f_symptr, SEEK_SET);
238 for (i = s = 0; i < hdr.f_nsyms; i++) {
241 ret = fread(&syment, 1, sizeof(syment), f);
242 my_assert(ret, sizeof(syment));
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);
248 if (syment.e_scnum != text_scnum || syment.e_sclass != C_EXT)
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;
256 symt_o[s].name = strdup(symname);
259 if (syment.e_numaux) {
260 ret = fseek(f, syment.e_numaux * sizeof(syment),
263 i += syment.e_numaux;
268 qsort(symt_o, s, sizeof(symt_o[0]), symt_cmp);
271 *symtab_out = symt_o;
273 if (base != 0 && base_out != NULL)
274 *base_out = base + scnhdr.s_vaddr;
277 static int handle_pad(uint8_t *d_obj, uint8_t *d_exe, int maxlen)
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 };
289 for (i = 0; i < maxlen; i++)
290 if (d_exe[i] != 0xcc)
293 for (len = i; len > 0; )
301 case sizeof(p ## x): \
302 if (memcmp(d_obj, p ## x, sizeof(p ## x))) \
304 memset(d_obj, 0xcc, sizeof(p ## x)); \
314 printf("%s: unhandled len: %d\n", __func__, len);
326 struct equiv_opcode {
331 uint8_t v_masm_mask[8];
333 uint8_t v_msvc_mask[8];
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
342 { 0x8d,0x74,0x03 }, { 0xf0,0x07,0xc0 },
343 { 0x8d,0x74,0x18 }, { 0xf0,0x07,0xc0 }, },
344 // movzbl 0x58f24a(%eax,%ecx,1),%eax
346 { 0x0f,0xb6,0x84,0x08 }, { 0xff,0xff,0x07,0xc0 },
347 { 0x0f,0xb6,0x84,0x01 }, { 0xff,0xff,0x07,0xc0 }, },
350 { 0xfe,0x4c,0x03 }, { 0xfe,0xff,0xc0 },
351 { 0xfe,0x4c,0x18 }, { 0xfe,0xff,0xc0 }, },
354 { 0x38,0x0c,0x0c }, { 0xff,0xff,0xc0 },
355 { 0x38,0x0c,0x30 }, { 0xff,0xff,0xc0 }, },
358 { 0x84,0xd3 }, { 0xfe,0xc0 },
359 { 0x84,0xda }, { 0xfe,0xc0 }, },
362 { 0x3a,0xca }, { 0xff,0xc0 },
363 { 0x38,0xd1 }, { 0xff,0xc0 }, },
366 { 0xf3,0x66 }, { 0xfe,0xff },
367 { 0x66,0xf3 }, { 0xff,0xfe }, },
368 // fadd st, st(0) vs st(0), st
370 { 0xd8,0xc0 }, { 0xff,0xf7 },
371 { 0xdc,0xc0 }, { 0xff,0xf7 }, },
373 // broad filters (may take too much..)
374 // testb $0x4,0x1d(%esi,%eax,1)
377 { 0xf6,0x44,0x06 }, { 0x00,0x07,0xc0 },
378 { 0xf6,0x44,0x30 }, { 0x00,0x07,0xc0 }, },
381 static int cmp_mask(uint8_t *d, uint8_t *expect, uint8_t *mask, int len)
385 for (i = 0; i < len; i++)
386 if ((d[i] & mask[i]) != (expect[i] & mask[i]))
392 static int check_equiv(uint8_t *d_obj, uint8_t *d_exe, int maxlen)
394 uint8_t vo, ve, vo2, ve2;
398 for (i = 0; i < sizeof(equiv_ops) / sizeof(equiv_ops[0]); i++)
400 struct equiv_opcode *op = &equiv_ops[i];
407 if (cmp_mask(d_obj + ofs, op->v_masm,
408 op->v_masm_mask, len))
410 if (cmp_mask(d_exe + ofs, op->v_msvc,
411 op->v_msvc_mask, len))
419 for (; jo < len; jo++)
420 if (op->v_masm_mask[jo] != 0xff)
422 for (; je < len; je++)
423 if (op->v_msvc_mask[je] != 0xff)
426 if ((jo == len && je != len) || (jo != len && je == len)) {
427 printf("invalid equiv_ops\n");
431 return len + ofs - 1; // matched
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) {
441 if (vo != ve || vo2 != ve2)
457 int main(int argc, char *argv[])
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;
474 printf("usage:\n%s <a_obj> <exe>\n", argv[0]);
478 f_obj = fopen(argv[1], "r+b");
480 fprintf(stderr, "%s", argv[1]);
485 f_exe = fopen(argv[2], "r");
487 fprintf(stderr, "%s", argv[2]);
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);
497 sztext_cmn = sztext_obj;
498 if (sztext_cmn > sztext_exe)
499 sztext_cmn = sztext_exe;
501 if (sztext_cmn == 0) {
502 printf("bad .text size(s): %ld, %ld\n",
503 sztext_obj, sztext_exe);
507 for (i = 0; i < reloc_cnt_obj; i++)
509 unsigned int a = relocs_obj[i].r_vaddr;
510 //printf("%04x %08x\n", relocs_obj[i].r_type, a);
512 switch (relocs_obj[i].r_type) {
514 memset(d_obj + a, 0, 4);
515 memset(d_exe + a, 0, 4);
518 printf("unknown reloc %x @%08x\n",
519 relocs_obj[i].r_type, base + a);
524 for (i = 0; i < sztext_cmn; i++)
526 if (d_obj[i] == d_exe[i])
529 left = sztext_cmn - i;
531 if (d_exe[i] == 0xcc) { // padding
532 if (handle_pad(d_obj + i, d_exe + i, left))
536 ret = check_equiv(d_obj + i, d_exe + i, left);
542 printf("%x: %02x vs %02x\n", base + i, d_obj[i], d_exe[i]);