add .asm function renamer
[ia32rtools.git] / tools / cmpmrg_text.c
diff --git a/tools/cmpmrg_text.c b/tools/cmpmrg_text.c
new file mode 100644 (file)
index 0000000..0bd858a
--- /dev/null
@@ -0,0 +1,530 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <linux/coff.h>
+#include <assert.h>
+#include <stdint.h>
+
+#include "my_assert.h"
+
+/* http://www.delorie.com/djgpp/doc/coff/ */
+
+typedef struct {
+  unsigned short f_magic;         /* magic number             */
+  unsigned short f_nscns;         /* number of sections       */
+  unsigned int   f_timdat;        /* time & date stamp        */
+  unsigned int   f_symptr;        /* file pointer to symtab   */
+  unsigned int   f_nsyms;         /* number of symtab entries */
+  unsigned short f_opthdr;        /* sizeof(optional hdr)     */
+  unsigned short f_flags;         /* flags                    */
+} FILHDR;
+
+typedef struct {
+  unsigned short magic;          /* type of file                         */
+  unsigned short vstamp;         /* version stamp                        */
+  unsigned int   tsize;          /* text size in bytes, padded to FW bdry*/
+  unsigned int   dsize;          /* initialized data    "  "             */
+  unsigned int   bsize;          /* uninitialized data  "  "             */
+  unsigned int   entry;          /* entry pt.                            */
+  unsigned int   text_start;     /* base of text used for this file      */
+  unsigned int   data_start;     /* base of data used for this file      */
+} AOUTHDR;
+
+typedef struct {
+  char           s_name[8];  /* section name                     */
+  unsigned int   s_paddr;    /* physical address, aliased s_nlib */
+  unsigned int   s_vaddr;    /* virtual address                  */
+  unsigned int   s_size;     /* section size                     */
+  unsigned int   s_scnptr;   /* file ptr to raw data for section */
+  unsigned int   s_relptr;   /* file ptr to relocation           */
+  unsigned int   s_lnnoptr;  /* file ptr to line numbers         */
+  unsigned short s_nreloc;   /* number of relocation entries     */
+  unsigned short s_nlnno;    /* number of line number entries    */
+  unsigned int   s_flags;    /* flags                            */
+} SCNHDR;
+
+typedef struct {
+  unsigned int  r_vaddr;   /* address of relocation      */
+  unsigned int  r_symndx;  /* symbol we're adjusting for */
+  unsigned short r_type;    /* type of relocation         */
+} __attribute__((packed)) RELOC;
+
+typedef struct {
+  union {
+    char e_name[E_SYMNMLEN];
+    struct {
+      unsigned int e_zeroes;
+      unsigned int e_offset;
+    } e;
+  } e;
+  unsigned int e_value;
+  short e_scnum;
+  unsigned short e_type;
+  unsigned char e_sclass;
+  unsigned char e_numaux;
+} __attribute__((packed)) SYMENT;
+
+#define C_EXT 2
+
+struct my_symtab {
+  unsigned int addr;
+  unsigned int fpos; // for patching
+  char *name;
+};
+
+static int symt_cmp(const void *p1_, const void *p2_)
+{
+       const struct my_symtab *p1 = p1_, *p2 = p2_;
+       return p1->addr - p2->addr;
+}
+
+void parse_headers(FILE *f, unsigned int *base_out,
+       long *sect_ofs, uint8_t **sect_data, long *sect_sz,
+       RELOC **relocs, long *reloc_cnt,
+       struct my_symtab **symtab_out, long *sym_cnt)
+{
+       struct my_symtab *symt_o = NULL;
+       char *stringtab = NULL;
+       unsigned int base = 0;
+       int text_scnum = 0;
+       long filesize;
+       char symname[9];
+       long opthdr_pos;
+       long reloc_size;
+       FILHDR hdr;
+       AOUTHDR opthdr;
+       SCNHDR scnhdr;
+       SYMENT syment;
+       int i, s, val;
+       int ret;
+       
+       ret = fseek(f, 0, SEEK_END);
+       my_assert(ret, 0);
+
+       filesize = ftell(f);
+
+       ret = fseek(f, 0, SEEK_SET);
+       my_assert(ret, 0);
+
+       ret = fread(&hdr, 1, sizeof(hdr), f);
+       my_assert(ret, sizeof(hdr));
+
+       if (hdr.f_magic == 0x5a4d) // MZ
+       {
+               ret = fseek(f, 0x3c, SEEK_SET);
+               my_assert(ret, 0);
+               ret = fread(&val, 1, sizeof(val), f);
+               my_assert(ret, sizeof(val));
+
+               ret = fseek(f, val, SEEK_SET);
+               my_assert(ret, 0);
+               ret = fread(&val, 1, sizeof(val), f);
+               my_assert(ret, sizeof(val));
+               my_assert(val, 0x4550); // PE
+
+               // should be COFF now
+               ret = fread(&hdr, 1, sizeof(hdr), f);
+               my_assert(ret, sizeof(hdr));
+       }
+
+       my_assert(hdr.f_magic, COFF_I386MAGIC);
+
+       if (hdr.f_opthdr != 0)
+       {
+               opthdr_pos = ftell(f);
+
+               if (hdr.f_opthdr < sizeof(opthdr))
+                       my_assert(1, 0);
+
+               ret = fread(&opthdr, 1, sizeof(opthdr), f);
+               my_assert(ret, sizeof(opthdr));
+               my_assert(opthdr.magic, COFF_ZMAGIC);
+
+               printf("text_start: %x\n", opthdr.text_start);
+
+               if (hdr.f_opthdr > sizeof(opthdr)) {
+                       ret = fread(&base, 1, sizeof(base), f);
+                       my_assert(ret, sizeof(base));
+                       printf("base: %x\n", base);
+               }
+               ret = fseek(f, opthdr_pos + hdr.f_opthdr, SEEK_SET);
+               my_assert(ret, 0);
+       }
+
+       // note: assuming first non-empty one is .text ..
+       for (s = 0; s < hdr.f_nscns; s++) {
+               ret = fread(&scnhdr, 1, sizeof(scnhdr), f);
+               my_assert(ret, sizeof(scnhdr));
+
+               if (scnhdr.s_size != 0) {
+                       text_scnum = s + 1;
+                       break;
+               }
+       }
+
+       printf("f_nsyms:  %x\n", hdr.f_nsyms);
+       printf("s_name:   '%s'\n", scnhdr.s_name);
+       printf("s_vaddr:  %x\n", scnhdr.s_vaddr);
+       printf("s_size:   %x\n", scnhdr.s_size);
+       //printf("s_scnptr: %x\n", scnhdr.s_scnptr);
+       printf("s_nreloc: %x\n", scnhdr.s_nreloc);
+       printf("--\n");
+
+       ret = fseek(f, scnhdr.s_scnptr, SEEK_SET);
+       my_assert(ret, 0);
+
+       *sect_data = malloc(scnhdr.s_size);
+       my_assert_not(*sect_data, NULL);
+       ret = fread(*sect_data, 1, scnhdr.s_size, f);
+       my_assert(ret, scnhdr.s_size);
+
+       *sect_ofs = scnhdr.s_scnptr;
+       *sect_sz = scnhdr.s_size;
+
+       // relocs
+       ret = fseek(f, scnhdr.s_relptr, SEEK_SET);
+       my_assert(ret, 0);
+
+       reloc_size = scnhdr.s_nreloc * sizeof((*relocs)[0]);
+       *relocs = malloc(reloc_size + 1);
+       my_assert_not(*relocs, NULL);
+       ret = fread(*relocs, 1, reloc_size, f);
+       my_assert(ret, reloc_size);
+
+       *reloc_cnt = scnhdr.s_nreloc;
+
+       // symtab
+       if (hdr.f_nsyms != 0) {
+               symname[8] = 0;
+
+               symt_o = malloc(hdr.f_nsyms * sizeof(symt_o[0]) + 1);
+               my_assert_not(symt_o, NULL);
+
+               ret = fseek(f, hdr.f_symptr
+                               + hdr.f_nsyms * sizeof(syment), SEEK_SET);
+               my_assert(ret, 0);
+               ret = fread(&i, 1, sizeof(i), f);
+               my_assert(ret, sizeof(i));
+               my_assert((unsigned int)i < filesize, 1);
+
+               stringtab = malloc(i);
+               my_assert_not(stringtab, NULL);
+               memset(stringtab, 0, 4);
+               ret = fread(stringtab + 4, 1, i - 4, f);
+               my_assert(ret, i - 4);
+
+               ret = fseek(f, hdr.f_symptr, SEEK_SET);
+               my_assert(ret, 0);
+       }
+
+       for (i = s = 0; i < hdr.f_nsyms; i++) {
+               long pos = ftell(f);
+
+               ret = fread(&syment, 1, sizeof(syment), f);
+               my_assert(ret, sizeof(syment));
+
+               strncpy(symname, syment.e.e_name, 8);
+               //printf("%3d %2d %08x '%s'\n", syment.e_sclass,
+               //      syment.e_scnum, syment.e_value, symname);
+
+               if (syment.e_scnum != text_scnum || syment.e_sclass != C_EXT)
+                       continue;
+
+               symt_o[s].addr = syment.e_value;
+               symt_o[s].fpos = pos;
+               if (syment.e.e.e_zeroes == 0)
+                       symt_o[s].name = stringtab + syment.e.e.e_offset;
+               else
+                       symt_o[s].name = strdup(symname);
+               s++;
+
+               if (syment.e_numaux) {
+                       ret = fseek(f, syment.e_numaux * sizeof(syment),
+                                   SEEK_CUR);
+                       my_assert(ret, 0);
+                       i += syment.e_numaux;
+               }
+       }
+
+       if (symt_o != NULL)
+               qsort(symt_o, s, sizeof(symt_o[0]), symt_cmp);
+
+       *sym_cnt = s;
+       *symtab_out = symt_o;
+
+       if (base != 0 && base_out != NULL)
+               *base_out = base + scnhdr.s_vaddr;
+}
+
+static int handle_pad(uint8_t *d_obj, uint8_t *d_exe, int maxlen)
+{
+       static const uint8_t p7[7] = { 0x8d, 0xa4, 0x24, 0x00, 0x00, 0x00, 0x00 };
+       static const uint8_t p6[6] = { 0x8d, 0x9b, 0x00, 0x00, 0x00, 0x00 };
+       static const uint8_t p5[5] = { 0x05, 0x00, 0x00, 0x00, 0x00 };
+       static const uint8_t p4[4] = { 0x8d, 0x64, 0x24, 0x00 };
+       static const uint8_t p3[3] = { 0x8d, 0x49, 0x00 };
+       static const uint8_t p2[2] = { 0x8b, 0xff };
+       static const uint8_t p1[1] = { 0x90 };
+       int len;
+       int i;
+
+       for (i = 0; i < maxlen; i++)
+               if (d_exe[i] != 0xcc)
+                       break;
+
+       for (len = i; len > 0; )
+       {
+               i = len;
+               if (i > 7)
+                       i = 7;
+
+               switch (i) {
+               #define CASE(x) \
+               case sizeof(p ## x): \
+                       if (memcmp(d_obj, p ## x, sizeof(p ## x))) \
+                               return 0; \
+                       memset(d_obj, 0xcc, sizeof(p ## x)); \
+                       break;
+               CASE(7)
+               CASE(6)
+               CASE(5)
+               CASE(4)
+               CASE(3)
+               CASE(2)
+               CASE(1)
+               default:
+                       printf("%s: unhandled len: %d\n", __func__, len);
+                       return 0;
+               #undef CASE
+               }
+
+               len -= i;
+               d_obj += i;
+       }
+
+       return 1;
+}
+
+struct equiv_opcode {
+       signed char len;
+       signed char ofs;
+       short cmp_rm;
+       uint8_t v_masm[8];
+       uint8_t v_masm_mask[8];
+       uint8_t v_msvc[8];
+       uint8_t v_msvc_mask[8];
+} equiv_ops[] = {
+       // cmp    $0x11,%ax
+       { 4, -1, 0,
+        { 0x66,0x83,0xf8,0x03 }, { 0xff,0xff,0xff,0x00 },
+        { 0x66,0x3d,0x03,0x00 }, { 0xff,0xff,0x00,0xff }, },
+       // lea    -0x1(%ebx,%eax,1),%esi // op mod/rm sib offs
+       // mov, test, imm grp 1
+       { 3, -2, 1,
+        { 0x8d,0x74,0x03 }, { 0xf0,0x07,0xc0 },
+        { 0x8d,0x74,0x18 }, { 0xf0,0x07,0xc0 }, },
+       // movzbl 0x58f24a(%eax,%ecx,1),%eax
+       { 4, -3, 1,
+        { 0x0f,0xb6,0x84,0x08 }, { 0xff,0xff,0x07,0xc0 },
+        { 0x0f,0xb6,0x84,0x01 }, { 0xff,0xff,0x07,0xc0 }, },
+       // inc/dec
+       { 3, -2, 1,
+        { 0xfe,0x4c,0x03 }, { 0xfe,0xff,0xc0 },
+        { 0xfe,0x4c,0x18 }, { 0xfe,0xff,0xc0 }, },
+       // cmp
+       { 3, -2, 1,
+        { 0x38,0x0c,0x0c }, { 0xff,0xff,0xc0 },
+        { 0x38,0x0c,0x30 }, { 0xff,0xff,0xc0 }, },
+       // test   %dl,%bl
+       { 2, -1, 1,
+        { 0x84,0xd3 }, { 0xfe,0xc0 },
+        { 0x84,0xda }, { 0xfe,0xc0 }, },
+       // cmp    r,r/m vs rm/r
+       { 2, 0, 1,
+        { 0x3a,0xca }, { 0xff,0xc0 },
+        { 0x38,0xd1 }, { 0xff,0xc0 }, },
+       // rep + 66 prefix
+       { 2, 0, 0,
+        { 0xf3,0x66 }, { 0xfe,0xff },
+        { 0x66,0xf3 }, { 0xff,0xfe }, },
+       // fadd   st, st(0) vs st(0), st
+       { 2, 0, 0,
+        { 0xd8,0xc0 }, { 0xff,0xf7 },
+        { 0xdc,0xc0 }, { 0xff,0xf7 }, },
+
+       // broad filters (may take too much..)
+       // testb  $0x4,0x1d(%esi,%eax,1)
+       // movb, push, ..
+       { 3, -2, 1,
+        { 0xf6,0x44,0x06 }, { 0x00,0x07,0xc0 },
+        { 0xf6,0x44,0x30 }, { 0x00,0x07,0xc0 }, },
+};
+
+static int cmp_mask(uint8_t *d, uint8_t *expect, uint8_t *mask, int len)
+{
+       int i;
+
+       for (i = 0; i < len; i++)
+               if ((d[i] & mask[i]) != (expect[i] & mask[i]))
+                       return 1;
+
+       return 0;
+}
+
+static int check_equiv(uint8_t *d_obj, uint8_t *d_exe, int maxlen)
+{
+       uint8_t vo, ve, vo2, ve2;
+       int i, jo, je;
+       int len, ofs;
+
+       for (i = 0; i < sizeof(equiv_ops) / sizeof(equiv_ops[0]); i++)
+       {
+               struct equiv_opcode *op = &equiv_ops[i];
+
+               len = op->len;
+               if (maxlen < len)
+                       continue;
+
+               ofs = op->ofs;
+               if (cmp_mask(d_obj + ofs, op->v_masm,
+                            op->v_masm_mask, len))
+                       continue;
+               if (cmp_mask(d_exe + ofs, op->v_msvc,
+                            op->v_msvc_mask, len))
+                       continue;
+
+               jo = je = 0;
+               d_obj += ofs;
+               d_exe += ofs;
+               while (1)
+               {
+                       for (; jo < len; jo++)
+                               if (op->v_masm_mask[jo] != 0xff)
+                                       break;
+                       for (; je < len; je++)
+                               if (op->v_msvc_mask[je] != 0xff)
+                                       break;
+
+                       if ((jo == len && je != len) || (jo != len && je == len)) {
+                               printf("invalid equiv_ops\n");
+                               return -1;
+                       }
+                       if (jo == len)
+                               return len + ofs - 1; // matched
+
+                       // var byte
+                       vo = d_obj[jo] & ~op->v_masm_mask[jo];
+                       ve = d_exe[je] & ~op->v_msvc_mask[je];
+                       if (op->cmp_rm && op->v_masm_mask[jo] == 0xc0) {
+                               vo2 = vo >> 3;
+                               vo &= 7;
+                               ve2 = ve & 7;
+                               ve >>= 3;
+                               if (vo != ve || vo2 != ve2)
+                                       return -1;
+                       }
+                       else {
+                               if (vo != ve)
+                                       return -1;
+                       }
+
+                       jo++;
+                       je++;
+               }
+       }
+
+       return -1;
+}
+
+int main(int argc, char *argv[])
+{
+       FILE *f_obj, *f_exe;
+       long text_ofs_obj, text_ofs_exe;
+       long sztext_obj, sztext_exe, sztext_cmn;
+       RELOC *relocs_obj, *relocs_exe;
+       long reloc_cnt_obj, reloc_cnt_exe;
+       struct my_symtab *syms_obj, *syms_exe;
+       long sym_cnt_obj, sym_cnt_exe;
+       uint8_t *d_obj, *d_exe;
+       unsigned int base = 0;
+       int retval = 1;
+       int left;
+       int ret;
+       int i;
+
+       if (argc != 3) {
+               printf("usage:\n%s <a_obj> <exe>\n", argv[0]);
+               return 1;
+       }
+
+       f_obj = fopen(argv[1], "r+b");
+       if (f_obj == NULL) {
+               fprintf(stderr, "%s", argv[1]);
+               perror("");
+               return 1;
+       }
+
+       f_exe = fopen(argv[2], "r");
+       if (f_exe == NULL) {
+               fprintf(stderr, "%s", argv[2]);
+               perror("");
+               return 1;
+       }
+
+       parse_headers(f_obj, NULL, &text_ofs_obj, &d_obj, &sztext_obj,
+               &relocs_obj, &reloc_cnt_obj, &syms_obj, &sym_cnt_obj);
+       parse_headers(f_exe, &base, &text_ofs_exe, &d_exe, &sztext_exe,
+               &relocs_exe, &reloc_cnt_exe, &syms_exe, &sym_cnt_exe);
+
+       sztext_cmn = sztext_obj;
+       if (sztext_cmn > sztext_exe)
+               sztext_cmn = sztext_exe;
+
+       if (sztext_cmn == 0) {
+               printf("bad .text size(s): %ld, %ld\n",
+                       sztext_obj, sztext_exe);
+               return 1;
+       }
+
+       for (i = 0; i < reloc_cnt_obj; i++)
+       {
+               unsigned int a = relocs_obj[i].r_vaddr;
+               //printf("%04x %08x\n", relocs_obj[i].r_type, a);
+
+               switch (relocs_obj[i].r_type) {
+               case 0x06:
+                       memset(d_obj + a, 0, 4);
+                       memset(d_exe + a, 0, 4);
+                       break;
+               default:
+                       printf("unknown reloc %x @%08x\n",
+                               relocs_obj[i].r_type, base + a);
+                       return 1;
+               }
+       }
+
+       for (i = 0; i < sztext_cmn; i++)
+       {
+               if (d_obj[i] == d_exe[i])
+                       continue;
+
+               left = sztext_cmn - i;
+
+               if (d_exe[i] == 0xcc) { // padding
+                       if (handle_pad(d_obj + i, d_exe + i, left))
+                               continue;
+               }
+
+               ret = check_equiv(d_obj + i, d_exe + i, left);
+               if (ret >= 0) {
+                       i += ret;
+                       continue;
+               }
+
+               printf("%x: %02x vs %02x\n", base + i, d_obj[i], d_exe[i]);
+               goto out;
+       }
+
+out:
+       return retval;
+}