minor fixes
[ia32rtools.git] / tools / cmpmrg_text.c
1 /*
2  * ia32rtools
3  * (C) notaz, 2013,2014
4  *
5  * This work is licensed under the terms of 3-clause BSD license.
6  * See COPYING file in the top-level directory.
7  */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <linux/coff.h>
13 #include <assert.h>
14 #include <stdint.h>
15
16 #include "my_assert.h"
17
18 /* http://www.delorie.com/djgpp/doc/coff/ */
19
20 typedef struct {
21   unsigned short f_magic;         /* magic number             */
22   unsigned short f_nscns;         /* number of sections       */
23   unsigned int   f_timdat;        /* time & date stamp        */
24   unsigned int   f_symptr;        /* file pointer to symtab   */
25   unsigned int   f_nsyms;         /* number of symtab entries */
26   unsigned short f_opthdr;        /* sizeof(optional hdr)     */
27   unsigned short f_flags;         /* flags                    */
28 } FILHDR;
29
30 typedef struct {
31   unsigned short magic;          /* type of file                         */
32   unsigned short vstamp;         /* version stamp                        */
33   unsigned int   tsize;          /* text size in bytes, padded to FW bdry*/
34   unsigned int   dsize;          /* initialized data    "  "             */
35   unsigned int   bsize;          /* uninitialized data  "  "             */
36   unsigned int   entry;          /* entry pt.                            */
37   unsigned int   text_start;     /* base of text used for this file      */
38   unsigned int   data_start;     /* base of data used for this file      */
39 } AOUTHDR;
40
41 typedef struct {
42   char           s_name[8];  /* section name                     */
43   unsigned int   s_paddr;    /* physical address, aliased s_nlib */
44   unsigned int   s_vaddr;    /* virtual address                  */
45   unsigned int   s_size;     /* section size                     */
46   unsigned int   s_scnptr;   /* file ptr to raw data for section */
47   unsigned int   s_relptr;   /* file ptr to relocation           */
48   unsigned int   s_lnnoptr;  /* file ptr to line numbers         */
49   unsigned short s_nreloc;   /* number of relocation entries     */
50   unsigned short s_nlnno;    /* number of line number entries    */
51   unsigned int   s_flags;    /* flags                            */
52 } SCNHDR;
53
54 typedef struct {
55   unsigned int  r_vaddr;   /* address of relocation      */
56   unsigned int  r_symndx;  /* symbol we're adjusting for */
57   unsigned short r_type;    /* type of relocation         */
58 } __attribute__((packed)) RELOC;
59
60 typedef struct {
61   union {
62     char e_name[E_SYMNMLEN];
63     struct {
64       unsigned int e_zeroes;
65       unsigned int e_offset;
66     } e;
67   } e;
68   unsigned int e_value;
69   short e_scnum;
70   unsigned short e_type;
71   unsigned char e_sclass;
72   unsigned char e_numaux;
73 } __attribute__((packed)) SYMENT;
74
75 #define C_EXT 2
76
77 struct my_symtab {
78   unsigned int addr;
79   //unsigned int fpos; // for patching
80   unsigned int is_text:1;
81   char *name;
82 };
83
84 struct my_sect_info {
85         long scnhdr_fofs;
86         long sect_fofs;
87         long reloc_fofs;
88         uint8_t *data;
89         long size;
90         RELOC *relocs;
91         long reloc_cnt;
92 };
93
94 static int symt_cmp(const void *p1_, const void *p2_)
95 {
96         const struct my_symtab *p1 = p1_, *p2 = p2_;
97         return p1->addr - p2->addr;
98 }
99
100 void parse_headers(FILE *f, unsigned int *base_out,
101         struct my_sect_info *sect_i,
102         struct my_symtab **symtab_out, long *sym_cnt,
103         struct my_symtab **raw_symtab_out, long *raw_sym_cnt)
104 {
105         struct my_symtab *symt_txt = NULL;
106         struct my_symtab *symt_all = NULL;
107         char *stringtab = NULL;
108         unsigned int base = 0;
109         int text_scnum = 0;
110         long filesize;
111         char symname[9];
112         long opthdr_pos;
113         long reloc_size;
114         FILHDR hdr;
115         AOUTHDR opthdr;
116         SCNHDR scnhdr;
117         SYMENT syment;
118         int i, s, val;
119         int ret;
120         
121         ret = fseek(f, 0, SEEK_END);
122         my_assert(ret, 0);
123
124         filesize = ftell(f);
125
126         ret = fseek(f, 0, SEEK_SET);
127         my_assert(ret, 0);
128
129         ret = fread(&hdr, 1, sizeof(hdr), f);
130         my_assert(ret, sizeof(hdr));
131
132         if (hdr.f_magic == 0x5a4d) // MZ
133         {
134                 ret = fseek(f, 0x3c, SEEK_SET);
135                 my_assert(ret, 0);
136                 ret = fread(&val, 1, sizeof(val), f);
137                 my_assert(ret, sizeof(val));
138
139                 ret = fseek(f, val, SEEK_SET);
140                 my_assert(ret, 0);
141                 ret = fread(&val, 1, sizeof(val), f);
142                 my_assert(ret, sizeof(val));
143                 my_assert(val, 0x4550); // PE
144
145                 // should be COFF now
146                 ret = fread(&hdr, 1, sizeof(hdr), f);
147                 my_assert(ret, sizeof(hdr));
148         }
149
150         my_assert(hdr.f_magic, COFF_I386MAGIC);
151
152         if (hdr.f_opthdr != 0)
153         {
154                 opthdr_pos = ftell(f);
155
156                 if (hdr.f_opthdr < sizeof(opthdr))
157                         my_assert(1, 0);
158
159                 ret = fread(&opthdr, 1, sizeof(opthdr), f);
160                 my_assert(ret, sizeof(opthdr));
161                 my_assert(opthdr.magic, COFF_ZMAGIC);
162
163                 //printf("text_start: %x\n", opthdr.text_start);
164
165                 if (hdr.f_opthdr > sizeof(opthdr)) {
166                         ret = fread(&base, 1, sizeof(base), f);
167                         my_assert(ret, sizeof(base));
168                         //printf("base: %x\n", base);
169                 }
170                 ret = fseek(f, opthdr_pos + hdr.f_opthdr, SEEK_SET);
171                 my_assert(ret, 0);
172         }
173
174         // note: assuming first non-empty one is .text ..
175         for (s = 0; s < hdr.f_nscns; s++) {
176                 sect_i->scnhdr_fofs = ftell(f);
177
178                 ret = fread(&scnhdr, 1, sizeof(scnhdr), f);
179                 my_assert(ret, sizeof(scnhdr));
180
181                 if (scnhdr.s_size != 0) {
182                         text_scnum = s + 1;
183                         break;
184                 }
185         }
186         my_assert(s < hdr.f_nscns, 1);
187
188 #if 0
189         printf("f_nsyms:  %x\n", hdr.f_nsyms);
190         printf("s_name:   '%s'\n", scnhdr.s_name);
191         printf("s_vaddr:  %x\n", scnhdr.s_vaddr);
192         printf("s_size:   %x\n", scnhdr.s_size);
193         //printf("s_scnptr: %x\n", scnhdr.s_scnptr);
194         printf("s_nreloc: %x\n", scnhdr.s_nreloc);
195         printf("--\n");
196 #endif
197
198         ret = fseek(f, scnhdr.s_scnptr, SEEK_SET);
199         my_assert(ret, 0);
200
201         sect_i->data = malloc(scnhdr.s_size);
202         my_assert_not(sect_i->data, NULL);
203         ret = fread(sect_i->data, 1, scnhdr.s_size, f);
204         my_assert(ret, scnhdr.s_size);
205
206         sect_i->sect_fofs = scnhdr.s_scnptr;
207         sect_i->size = scnhdr.s_size;
208
209         // relocs
210         ret = fseek(f, scnhdr.s_relptr, SEEK_SET);
211         my_assert(ret, 0);
212
213         reloc_size = scnhdr.s_nreloc * sizeof(sect_i->relocs[0]);
214         sect_i->relocs = malloc(reloc_size + 1);
215         my_assert_not(sect_i->relocs, NULL);
216         ret = fread(sect_i->relocs, 1, reloc_size, f);
217         my_assert(ret, reloc_size);
218
219         sect_i->reloc_cnt = scnhdr.s_nreloc;
220         sect_i->reloc_fofs = scnhdr.s_relptr;
221
222         if (base != 0 && base_out != NULL)
223                 *base_out = base + scnhdr.s_vaddr;
224
225         if (symtab_out == NULL || sym_cnt == NULL)
226                 return;
227
228         // symtab
229         if (hdr.f_nsyms != 0) {
230                 symname[8] = 0;
231
232                 symt_txt = malloc(hdr.f_nsyms * sizeof(symt_txt[0]) + 1);
233                 my_assert_not(symt_txt, NULL);
234                 symt_all = malloc(hdr.f_nsyms * sizeof(symt_all[0]) + 1);
235                 my_assert_not(symt_all, NULL);
236
237                 ret = fseek(f, hdr.f_symptr
238                                 + hdr.f_nsyms * sizeof(syment), SEEK_SET);
239                 my_assert(ret, 0);
240                 ret = fread(&i, 1, sizeof(i), f);
241                 my_assert(ret, sizeof(i));
242                 my_assert((unsigned int)i < filesize, 1);
243
244                 stringtab = malloc(i);
245                 my_assert_not(stringtab, NULL);
246                 memset(stringtab, 0, 4);
247                 ret = fread(stringtab + 4, 1, i - 4, f);
248                 my_assert(ret, i - 4);
249
250                 ret = fseek(f, hdr.f_symptr, SEEK_SET);
251                 my_assert(ret, 0);
252         }
253
254         for (i = s = 0; i < hdr.f_nsyms; i++) {
255                 //long pos = ftell(f);
256
257                 ret = fread(&syment, 1, sizeof(syment), f);
258                 my_assert(ret, sizeof(syment));
259
260                 strncpy(symname, syment.e.e_name, 8);
261                 //printf("%3d %2d %08x '%s'\n", syment.e_sclass,
262                 //      syment.e_scnum, syment.e_value, symname);
263
264                 symt_all[i].addr = syment.e_value;
265                 //symt_all[i].fpos = pos;
266                 if (syment.e.e.e_zeroes == 0)
267                         symt_all[i].name = stringtab + syment.e.e.e_offset;
268                 else
269                         symt_all[i].name = strdup(symname);
270
271                 symt_all[i].is_text = (syment.e_scnum == text_scnum);
272                 if (symt_all[i].is_text && syment.e_sclass == C_EXT) {
273                         symt_txt[s] = symt_all[i];
274                         s++;
275                 }
276
277                 if (syment.e_numaux) {
278                         ret = fseek(f, syment.e_numaux * sizeof(syment),
279                                     SEEK_CUR);
280                         my_assert(ret, 0);
281                         i += syment.e_numaux;
282                 }
283         }
284
285         if (symt_txt != NULL)
286                 qsort(symt_txt, s, sizeof(symt_txt[0]), symt_cmp);
287
288         *sym_cnt = s;
289         *symtab_out = symt_txt;
290         *raw_sym_cnt = i;
291         *raw_symtab_out = symt_all;
292 }
293
294 static int try_align(uint8_t *d_obj, uint8_t *d_exe, int maxlen)
295 {
296         static const uint8_t aligns[8][7] = {
297                 { }, // [0] not used
298                 { 0x90 }, // [1] nop
299                 { 0x8b, 0xff }, // mov edi, edi
300                 { 0x8d, 0x49, 0x00 }, // lea ecx, [ecx]
301                 { 0x8d, 0x64, 0x24, 0x00 }, // lea
302                 { 0x05, 0x00, 0x00, 0x00, 0x00 }, // add eax, 0
303                 { 0x8d, 0x9b, 0x00, 0x00, 0x00, 0x00 },
304                 { 0x8d, 0xa4, 0x24, 0x00, 0x00, 0x00, 0x00 },
305         };
306         int j = 0;
307         int len;
308         int i;
309
310         // check exe for common pad/align patterns
311         for (i = 0; i < maxlen; i++)
312                 if (d_exe[i] != 0xcc)
313                         break;
314
315         while (j < 8) {
316                 for (j = 1; j < 8; j++) {
317                         if (maxlen - i < j) {
318                                 j = 8;
319                                 break;
320                         }
321                         if (memcmp(&d_exe[i], aligns[j], j) == 0) {
322                                 i += j;
323                                 break;
324                         }
325                 }
326         }
327         if (i == 0)
328                 return 0;
329
330         // now check the obj
331         for (j = 0, len = i; len > 0; )
332         {
333                 i = len;
334                 if (i > 7)
335                         i = 7;
336
337                 if (memcmp(d_obj, aligns[i], i) != 0)
338                         break;
339
340                 memcpy(d_obj, d_exe, i);
341                 j += i;
342
343                 len -= i;
344                 d_obj += i;
345                 d_exe += i;
346         }
347
348         return j;
349 }
350
351 struct equiv_opcode {
352         signed char len;
353         signed char ofs;
354         unsigned short cmp_rm:1;
355         unsigned short simple:1;
356         uint8_t v_masm[8];
357         uint8_t v_masm_mask[8];
358         uint8_t v_msvc[8];
359         uint8_t v_msvc_mask[8];
360 } equiv_ops[] = {
361         // cmp    $0x11,%ax
362         { 4, -1, 0, 0,
363          { 0x66,0x83,0xf8,0x03 }, { 0xff,0xff,0xff,0x00 },
364          { 0x66,0x3d,0x03,0x00 }, { 0xff,0xff,0x00,0xff }, },
365         // lea    -0x1(%ebx,%eax,1),%esi // op mod/rm sib offs
366         // mov, test, imm grp 1
367         { 3, -2, 1, 0,
368          { 0x8d,0x74,0x03 }, { 0xf0,0x07,0xc0 },
369          { 0x8d,0x74,0x18 }, { 0xf0,0x07,0xc0 }, },
370         // movzbl 0x58f24a(%eax,%ecx,1),%eax
371         { 4, -3, 1, 0,
372          { 0x0f,0xb6,0x84,0x08 }, { 0xff,0xff,0x07,0xc0 },
373          { 0x0f,0xb6,0x84,0x01 }, { 0xff,0xff,0x07,0xc0 }, },
374         // inc/dec
375         { 3, -2, 1, 0,
376          { 0xfe,0x4c,0x03 }, { 0xfe,0xff,0xc0 },
377          { 0xfe,0x4c,0x18 }, { 0xfe,0xff,0xc0 }, },
378         // cmp
379         { 3, -2, 1, 0,
380          { 0x38,0x0c,0x0c }, { 0xff,0xff,0xc0 },
381          { 0x38,0x0c,0x30 }, { 0xff,0xff,0xc0 }, },
382         // test   %dl,%bl
383         { 2, -1, 1, 0,
384          { 0x84,0xd3 }, { 0xfe,0xc0 },
385          { 0x84,0xda }, { 0xfe,0xc0 }, },
386         // cmp    r,r/m vs rm/r
387         { 2, 0, 1, 0,
388          { 0x3a,0xca }, { 0xff,0xc0 },
389          { 0x38,0xd1 }, { 0xff,0xc0 }, },
390         // rep + 66 prefix
391         { 2, 0, 0, 0,
392          { 0xf3,0x66 }, { 0xfe,0xff },
393          { 0x66,0xf3 }, { 0xff,0xfe }, },
394         // fadd   st, st(0) vs st(0), st
395         { 2, 0, 0, 0,
396          { 0xd8,0xc0 }, { 0xff,0xf7 },
397          { 0xdc,0xc0 }, { 0xff,0xf7 }, },
398         // [esp] vs [esp+0]
399         { 4, -1, 0, 0,
400          { 0x00,0x04,0x24,0x90 }, { 0x00,0xc7,0xff,0xff },
401          { 0x00,0x44,0x24,0x00 }, { 0x00,0xc7,0xff,0xff }, },
402         { 5, -1, 0, 0,
403          { 0x00,0x04,0x24,0x00,0x90 }, { 0x00,0xc7,0xff,0x00,0xff },
404          { 0x00,0x44,0x24,0x00,0x00 }, { 0x00,0xc7,0xff,0xff,0x00 }, },
405         { 8, -1, 0, 0,
406          { 0x00,0x04,0x24,0x00,0x00,0x00,0x00,0x90 }, { 0x00,0xc7,0xff,0x00,0x00,0x00,0x00,0xff },
407          { 0x00,0x44,0x24,0x00,0x00,0x00,0x00,0x00 }, { 0x00,0xc7,0xff,0xff,0x00,0x00,0x00,0x00 }, },
408
409         // various align insns/fillups
410         { 2, -1, 0, 0,
411          { 0x8b,0xff }, { 0xff,0xff },
412          { 0x8b,0xc0 }, { 0xff,0xff }, },
413         { 2, 0, 0, 1,
414          { 0x00,0x00 }, { 0x00,0x00 },
415          { 0x8b,0xc0 }, { 0xff,0xff }, },
416         { 3, 0, 0, 1,
417          { 0x00,0x00,0x00 }, { 0x50,0x00,0x00 },
418          { 0x2e,0x8b,0xc0 }, { 0xff,0xff,0xff }, },
419
420         // broad filters (may take too much..)
421         // testb  $0x4,0x1d(%esi,%eax,1)
422         // movb, push, ..
423         { 3, -2, 1, 0,
424          { 0xf6,0x44,0x06 }, { 0x00,0x07,0xc0 },
425          { 0xf6,0x44,0x30 }, { 0x00,0x07,0xc0 }, },
426 };
427
428 static int cmp_mask(uint8_t *d, uint8_t *expect, uint8_t *mask, int len)
429 {
430         int i;
431
432         for (i = 0; i < len; i++)
433                 if ((d[i] & mask[i]) != (expect[i] & mask[i]))
434                         return 1;
435
436         return 0;
437 }
438
439 static int check_equiv(uint8_t *d_obj, uint8_t *d_exe, int maxlen)
440 {
441         uint8_t vo, ve, vo2, ve2;
442         int i, jo, je;
443         int len, ofs;
444
445         for (i = 0; i < sizeof(equiv_ops) / sizeof(equiv_ops[0]); i++)
446         {
447                 struct equiv_opcode *op = &equiv_ops[i];
448
449                 len = op->len;
450                 if (maxlen < len)
451                         continue;
452
453                 ofs = op->ofs;
454                 if (cmp_mask(d_obj + ofs, op->v_masm,
455                              op->v_masm_mask, len))
456                         continue;
457                 if (cmp_mask(d_exe + ofs, op->v_msvc,
458                              op->v_msvc_mask, len))
459                         continue;
460
461                 if (op->simple)
462                         return len + ofs;
463
464                 jo = je = 0;
465                 d_obj += ofs;
466                 d_exe += ofs;
467                 while (1)
468                 {
469                         for (; jo < len; jo++)
470                                 if (op->v_masm_mask[jo] != 0xff)
471                                         break;
472                         for (; je < len; je++)
473                                 if (op->v_msvc_mask[je] != 0xff)
474                                         break;
475
476                         if ((jo == len && je != len) || (jo != len && je == len)) {
477                                 printf("invalid equiv_op #%td\n", op - equiv_ops);
478                                 return -1;
479                         }
480                         if (jo == len)
481                                 return len + ofs; // matched
482
483                         // var byte
484                         vo = d_obj[jo] & ~op->v_masm_mask[jo];
485                         ve = d_exe[je] & ~op->v_msvc_mask[je];
486                         if (op->cmp_rm && op->v_masm_mask[jo] == 0xc0) {
487                                 vo2 = vo >> 3;
488                                 vo &= 7;
489                                 ve2 = ve & 7;
490                                 ve >>= 3;
491                                 if (vo != ve || vo2 != ve2)
492                                         return -1;
493                         }
494                         else {
495                                 if (vo != ve)
496                                         return -1;
497                         }
498
499                         jo++;
500                         je++;
501                 }
502         }
503
504         return -1;
505 }
506
507 static void fill_int3(unsigned char *d, int len)
508 {
509         while (len-- > 0) {
510                 if (d[0] == 0xcc && d[1] == 0xcc)
511                         break;
512                 *d++ = 0xcc;
513         }
514 }
515
516 int main(int argc, char *argv[])
517 {
518         unsigned int base = 0, addr, end, sym, *t;
519         struct my_sect_info s_text_obj, s_text_exe;
520         struct my_symtab *raw_syms_obj = NULL;
521         struct my_symtab *syms_obj = NULL;
522         long sym_cnt_obj, raw_sym_cnt_obj;
523         FILE *f_obj, *f_exe;
524         SCNHDR tmphdr;
525         long sztext_cmn;
526         int do_cmp = 1;
527         int retval = 1;
528         int bad = 0;
529         int left;
530         int arg;
531         int ret;
532         int i;
533
534         for (arg = 1; arg < argc; arg++) {
535                 if (!strcmp(argv[arg], "-n"))
536                         do_cmp = 0;
537                 else
538                         break;
539         }
540
541         if (argc != arg + 2) {
542                 printf("usage:\n%s [-n] <a_obj> <exe>\n", argv[0]);
543                 return 1;
544         }
545
546         f_obj = fopen(argv[arg++], "r+b");
547         if (f_obj == NULL) {
548                 fprintf(stderr, "%s: ", argv[1]);
549                 perror("");
550                 return 1;
551         }
552
553         f_exe = fopen(argv[arg++], "r");
554         if (f_exe == NULL) {
555                 fprintf(stderr, "%s: ", argv[2]);
556                 perror("");
557                 return 1;
558         }
559
560         parse_headers(f_obj, NULL, &s_text_obj, &syms_obj, &sym_cnt_obj,
561                       &raw_syms_obj, &raw_sym_cnt_obj);
562         parse_headers(f_exe, &base, &s_text_exe, NULL, NULL, NULL, NULL);
563
564         sztext_cmn = s_text_obj.size;
565         if (sztext_cmn > s_text_exe.size)
566                 sztext_cmn = s_text_exe.size;
567
568         if (sztext_cmn == 0) {
569                 printf("bad .text size(s): %ld, %ld\n",
570                         s_text_obj.size, s_text_exe.size);
571                 return 1;
572         }
573
574         for (i = 0; i < s_text_obj.reloc_cnt; i++)
575         {
576                 unsigned int a = s_text_obj.relocs[i].r_vaddr;
577                 //printf("%04x %08x\n", s_text_obj.relocs[i].r_type, a);
578
579                 switch (s_text_obj.relocs[i].r_type) {
580                 case 0x06: // RELOC_ADDR32
581                 case 0x14: // RELOC_REL32
582                         // must preserve stored val,
583                         // so trash exe so that cmp passes
584                         memcpy(s_text_exe.data + a, s_text_obj.data + a, 4);
585                         break;
586                 default:
587                         printf("unknown reloc %x @%08x/%08x\n",
588                                 s_text_obj.relocs[i].r_type, a, base + a);
589                         return 1;
590                 }
591         }
592
593         if (do_cmp)
594         for (i = 0; i < sztext_cmn; i++)
595         {
596                 if (s_text_obj.data[i] == s_text_exe.data[i]) {
597                         bad = 0;
598                         continue;
599                 }
600
601                 left = sztext_cmn - i;
602
603                 ret = try_align(s_text_obj.data + i, s_text_exe.data + i, left);
604                 if (ret > 0) {
605                         i += ret - 1;
606                         continue;
607                 }
608
609                 ret = check_equiv(s_text_obj.data + i, s_text_exe.data + i, left);
610                 if (ret >= 0) {
611                         i += ret - 1;
612                         continue;
613                 }
614
615                 printf("%x: %02x vs %02x\n", base + i,
616                         s_text_obj.data[i], s_text_exe.data[i]);
617                 if (bad)
618                         goto out;
619
620                 bad = 1;
621         }
622
623         // fill removed funcs with 'int3'
624         for (i = 0; i < sym_cnt_obj; i++) {
625                 if (strncmp(syms_obj[i].name, "rm_", 3))
626                         continue;
627
628                 addr = syms_obj[i].addr;
629                 end = (i < sym_cnt_obj - 1)
630                         ? syms_obj[i + 1].addr : s_text_obj.size;
631                 if (addr >= s_text_obj.size || end > s_text_obj.size) {
632                         printf("addr OOR: %x-%x '%s'\n", addr, end,
633                                 syms_obj[i].name);
634                         goto out;
635                 }
636                 fill_int3(s_text_obj.data + addr, end - addr);
637         }
638
639         // remove relocs
640         for (i = 0; i < s_text_obj.reloc_cnt; i++) {
641                 addr = s_text_obj.relocs[i].r_vaddr;
642                 sym = s_text_obj.relocs[i].r_symndx;
643                 if (addr > s_text_obj.size - 4) {
644                         printf("reloc addr OOR: %x\n", addr);
645                         goto out;
646                 }
647                 if (sym >= raw_sym_cnt_obj) {
648                         printf("reloc sym OOR: %d/%ld\n",
649                                 sym, raw_sym_cnt_obj);
650                         goto out;
651                 }
652 #if 0
653                 printf("r %08x -> %08x %s\n", base + addr,
654                         raw_syms_obj[sym].addr,
655                         raw_syms_obj[sym].name);
656 #endif
657                 t = (unsigned int *)(s_text_obj.data + addr);
658                 if (t[0] == 0xcccccccc
659                  || t[-1] == 0xcccccccc) { // jumptab of a func?
660                         t[0] = 0xcccccccc;
661                         memmove(&s_text_obj.relocs[i],
662                                 &s_text_obj.relocs[i + 1],
663                                 (s_text_obj.reloc_cnt - i - 1)
664                                  * sizeof(s_text_obj.relocs[0]));
665                         i--;
666                         s_text_obj.reloc_cnt--;
667                 }
668 #if 0
669                 // note: branches/calls already linked,
670                 // so only useful for dd refs
671                 // XXX: rm'd because of switch tables
672                 else if (raw_syms_obj[sym].is_text) {
673                         unsigned int addr2 = raw_syms_obj[sym].addr;
674                         if (s_text_obj.data[addr2] == 0xcc) {
675                                 printf("warning: reloc %08x -> %08x "
676                                         "points to rm'd target '%s'\n",
677                                         base + addr, base + addr2,
678                                         raw_syms_obj[sym].name);
679                         }
680                 }
681 #endif
682         }
683
684         // patch .text
685         ret = fseek(f_obj, s_text_obj.sect_fofs, SEEK_SET);
686         my_assert(ret, 0);
687         ret = fwrite(s_text_obj.data, 1, s_text_obj.size, f_obj);
688         my_assert(ret, s_text_obj.size);
689
690         // patch relocs
691         ret = fseek(f_obj, s_text_obj.reloc_fofs, SEEK_SET);
692         my_assert(ret, 0);
693         ret = fwrite(s_text_obj.relocs, sizeof(s_text_obj.relocs[0]),
694                 s_text_obj.reloc_cnt, f_obj);
695         my_assert(ret, s_text_obj.reloc_cnt);
696
697         ret = fseek(f_obj, s_text_obj.scnhdr_fofs, SEEK_SET);
698         my_assert(ret, 0);
699         ret = fread(&tmphdr, 1, sizeof(tmphdr), f_obj);
700         my_assert(ret, sizeof(tmphdr));
701
702         tmphdr.s_nreloc = s_text_obj.reloc_cnt;
703
704         ret = fseek(f_obj, s_text_obj.scnhdr_fofs, SEEK_SET);
705         my_assert(ret, 0);
706         ret = fwrite(&tmphdr, 1, sizeof(tmphdr), f_obj);
707         my_assert(ret, sizeof(tmphdr));
708
709         fclose(f_obj);
710         fclose(f_exe);
711
712         retval = 0;
713 out:
714         return retval;
715 }