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