1 #include <fcntl.h>
2 #include <inttypes.h>
3 #include <limits.h>
4 #include <stddef.h>
5 #include <stdint.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <sys/mman.h>
10 #include <unistd.h>
11 
12 struct mz_hdr {
13     uint16_t signature;
14 #define MZ_SIGNATURE 0x5a4d
15     uint16_t last_page_size;
16     uint16_t page_count;
17     uint16_t relocation_count;
18     uint16_t header_paras;
19     uint16_t min_paras;
20     uint16_t max_paras;
21     uint16_t entry_ss;
22     uint16_t entry_sp;
23     uint16_t checksum;
24     uint16_t entry_ip;
25     uint16_t entry_cs;
26     uint16_t relocations;
27     uint16_t overlay;
28     uint8_t reserved[32];
29     uint32_t extended_header_base;
30 };
31 
32 struct pe_hdr {
33     uint32_t signature;
34 #define PE_SIGNATURE 0x00004550
35     uint16_t cpu;
36     uint16_t section_count;
37     int32_t timestamp;
38     uint32_t symbols_file_offset;
39     uint32_t symbol_count;
40     uint16_t opt_hdr_size;
41     uint16_t flags;
42     struct {
43         uint16_t magic;
44 #define PE_MAGIC_EXE32     0x010b
45 #define PE_MAGIC_EXE32PLUS 0x020b
46         uint8_t linker_major, linker_minor;
47         uint32_t code_size, data_size, bss_size;
48         uint32_t entry_rva, code_rva, data_rva;
49     } opt_hdr;
50 };
51 
52 #define PE_PAGE_SIZE 0x1000
53 
54 #define PE_BASE_RELOC_ABS      0
55 #define PE_BASE_RELOC_HIGHLOW  3
56 #define PE_BASE_RELOC_DIR64   10
57 
58 struct coff_section {
59     char name[8];
60     uint32_t size;
61     uint32_t rva;
62     uint32_t file_size;
63     uint32_t file_offset;
64     uint32_t relocation_file_offset;
65     uint32_t line_number_file_offset;
66     uint16_t relocation_count;
67     uint16_t line_number_count;
68     uint32_t flags;
69 #define COFF_SECTION_BSS         0x00000080U
70 #define COFF_SECTION_DISCARDABLE 0x02000000U
71 #define COFF_SECTION_WRITEABLE   0x80000000U
72 };
73 
usage(const char * cmd,int rc)74 static void usage(const char *cmd, int rc)
75 {
76     fprintf(rc ? stderr : stdout,
77             "Usage: %s <image1> <image2>\n",
78             cmd);
79     exit(rc);
80 }
81 
load(const char * name,int * handle,struct coff_section ** sections,uint_fast64_t * image_base,uint32_t * image_size,unsigned int * width)82 static unsigned int load(const char *name, int *handle,
83                          struct coff_section **sections,
84                          uint_fast64_t *image_base,
85                          uint32_t *image_size,
86                          unsigned int *width)
87 {
88     int in = open(name, O_RDONLY);
89     struct mz_hdr mz_hdr;
90     struct pe_hdr pe_hdr;
91     uint32_t base;
92 
93     if ( in < 0 ||
94          read(in, &mz_hdr, sizeof(mz_hdr)) != sizeof(mz_hdr) )
95     {
96         perror(name);
97         exit(2);
98     }
99     if ( mz_hdr.signature != MZ_SIGNATURE ||
100          mz_hdr.relocations < sizeof(mz_hdr) ||
101          !mz_hdr.extended_header_base )
102     {
103         fprintf(stderr, "%s: Wrong DOS file format\n", name);
104         exit(2);
105     }
106 
107     if ( lseek(in, mz_hdr.extended_header_base, SEEK_SET) < 0 ||
108          read(in, &pe_hdr, sizeof(pe_hdr)) != sizeof(pe_hdr) ||
109          read(in, &base, sizeof(base)) != sizeof(base) ||
110          /*
111           * Luckily the image size field lives at the
112           * same offset for both formats.
113           */
114          lseek(in, 24, SEEK_CUR) < 0 ||
115          read(in, image_size, sizeof(*image_size)) != sizeof(*image_size) )
116     {
117         perror(name);
118         exit(3);
119     }
120     switch ( (pe_hdr.signature == PE_SIGNATURE &&
121               pe_hdr.opt_hdr_size > sizeof(pe_hdr.opt_hdr)) *
122              pe_hdr.opt_hdr.magic )
123     {
124     case PE_MAGIC_EXE32:
125         *width = 32;
126         *image_base = base;
127         break;
128     case PE_MAGIC_EXE32PLUS:
129         *width = 64;
130         *image_base = ((uint64_t)base << 32) | pe_hdr.opt_hdr.data_rva;
131         break;
132     default:
133         fprintf(stderr, "%s: Wrong PE file format\n", name);
134         exit(3);
135     }
136 
137     *sections = malloc(pe_hdr.section_count * sizeof(**sections));
138     if ( !*sections )
139     {
140         perror(NULL);
141         exit(4);
142     }
143     if ( lseek(in,
144                mz_hdr.extended_header_base + offsetof(struct pe_hdr, opt_hdr) +
145                   pe_hdr.opt_hdr_size,
146                SEEK_SET) < 0 ||
147          read(in, *sections, pe_hdr.section_count * sizeof(**sections)) !=
148              pe_hdr.section_count * sizeof(**sections) )
149     {
150         perror(name);
151         exit(4);
152     }
153 
154     *handle = in;
155 
156     return pe_hdr.section_count;
157 }
158 
159 static long page_size;
160 
map_section(const struct coff_section * sec,int in,const char * name)161 static const void *map_section(const struct coff_section *sec, int in,
162                                const char *name)
163 {
164     const char *ptr;
165     unsigned long offs;
166 
167     if ( !page_size )
168         page_size = sysconf(_SC_PAGESIZE);
169     offs = sec->file_offset & (page_size - 1);
170 
171     ptr = mmap(0, offs + sec->file_size, PROT_READ, MAP_PRIVATE, in,
172                sec->file_offset - offs);
173     if ( ptr == MAP_FAILED )
174     {
175         perror(name);
176         exit(6);
177     }
178 
179     return ptr + offs;
180 }
181 
unmap_section(const void * ptr,const struct coff_section * sec)182 static void unmap_section(const void *ptr, const struct coff_section *sec)
183 {
184     unsigned long offs = sec->file_offset & (page_size - 1);
185 
186     munmap((char *)ptr - offs, offs + sec->file_size);
187 }
188 
diff_sections(const unsigned char * ptr1,const unsigned char * ptr2,const struct coff_section * sec,int_fast64_t diff,unsigned int width,uint_fast64_t base,uint_fast64_t end)189 static void diff_sections(const unsigned char *ptr1, const unsigned char *ptr2,
190                           const struct coff_section *sec,
191                           int_fast64_t diff, unsigned int width,
192                           uint_fast64_t base, uint_fast64_t end)
193 {
194     static uint_fast32_t cur_rva, reloc_size;
195     unsigned int disp = 0;
196     uint_fast32_t i;
197 
198     if ( !sec )
199     {
200         reloc_size += reloc_size & 2;
201         if ( reloc_size )
202             printf("\t.balign 4\n"
203                    "\t.equ rva_%08" PRIxFAST32 "_relocs, %#08" PRIxFAST32 "\n",
204                    cur_rva, reloc_size);
205         return;
206     }
207 
208     while ( !(diff & (((int_fast64_t)1 << ((disp + 1) * CHAR_BIT)) - 1)) )
209         ++disp;
210 
211     for ( i = 0; i < sec->file_size; ++i )
212     {
213         uint_fast32_t rva;
214         union {
215             uint32_t u32;
216             uint64_t u64;
217         } val1, val2;
218         int_fast64_t delta;
219         unsigned int reloc = (width == 4 ? PE_BASE_RELOC_HIGHLOW :
220                                            PE_BASE_RELOC_DIR64);
221 
222         if ( ptr1[i] == ptr2[i] )
223             continue;
224 
225         if ( i < disp || i + width - disp > sec->file_size )
226         {
227             fprintf(stderr,
228                     "Bogus difference at %.8s:%08" PRIxFAST32 "\n",
229                     sec->name, i);
230             exit(3);
231         }
232 
233         memcpy(&val1, ptr1 + i - disp, width);
234         memcpy(&val2, ptr2 + i - disp, width);
235         delta = width == 4 ? val2.u32 - val1.u32 : val2.u64 - val1.u64;
236         if ( delta != diff )
237         {
238             fprintf(stderr,
239                     "Difference at %.8s:%08" PRIxFAST32 " is %#" PRIxFAST64
240                     " (expected %#" PRIxFAST64 ")\n",
241                     sec->name, i - disp, delta, diff);
242             continue;
243         }
244         if ( width == 8 && (val1.u64 < base || val1.u64 > end) )
245             reloc = PE_BASE_RELOC_HIGHLOW;
246 
247         rva = (sec->rva + i - disp) & ~(PE_PAGE_SIZE - 1);
248         if ( rva > cur_rva )
249         {
250             reloc_size += reloc_size & 2;
251             if ( reloc_size )
252                 printf("\t.equ rva_%08" PRIxFAST32 "_relocs,"
253                              " %#08" PRIxFAST32 "\n",
254                        cur_rva, reloc_size);
255             printf("\t.balign 4\n"
256                    "\t.long %#08" PRIxFAST32 ","
257                           " rva_%08" PRIxFAST32 "_relocs\n",
258                    rva, rva);
259             cur_rva = rva;
260             reloc_size = 8;
261         }
262         else if ( rva != cur_rva )
263         {
264             fprintf(stderr,
265                     "Cannot handle decreasing RVA (at %.8s:%08" PRIxFAST32 ")\n",
266                     sec->name, i - disp);
267             exit(3);
268         }
269 
270         if ( !(sec->flags & COFF_SECTION_WRITEABLE) )
271             fprintf(stderr,
272                     "Warning: relocation to r/o section %.8s:%08" PRIxFAST32 "\n",
273                     sec->name, i - disp);
274 
275         printf("\t.word (%u << 12) | 0x%03" PRIxFAST32 "\n",
276                reloc, sec->rva + i - disp - rva);
277         reloc_size += 2;
278         i += width - disp - 1;
279     }
280 }
281 
main(int argc,char * argv[])282 int main(int argc, char *argv[])
283 {
284     int in1, in2;
285     unsigned int i, nsec, width1, width2;
286     uint_fast64_t base1, base2;
287     uint32_t size1, size2;
288     struct coff_section *sec1, *sec2;
289 
290     if ( argc == 1 ||
291          !strcmp(argv[1], "-?") ||
292          !strcmp(argv[1], "-h") ||
293          !strcmp(argv[1], "--help") )
294         usage(*argv, argc == 1);
295 
296     if ( argc != 3 )
297         usage(*argv, 1);
298 
299     nsec = load(argv[1], &in1, &sec1, &base1, &size1, &width1);
300     if ( nsec != load(argv[2], &in2, &sec2, &base2, &size2, &width2) )
301     {
302         fputs("Mismatched section counts\n", stderr);
303         return 5;
304     }
305     if ( width1 != width2 )
306     {
307         fputs("Mismatched image types\n", stderr);
308         return 5;
309     }
310     width1 >>= 3;
311     if ( base1 == base2 )
312     {
313         fputs("Images must have different base addresses\n", stderr);
314         return 5;
315     }
316     if ( size1 != size2 )
317     {
318         fputs("Images must have identical sizes\n", stderr);
319         return 5;
320     }
321 
322     puts("\t.section .reloc, \"a\", @progbits\n"
323          "\t.balign 4\n"
324          "\t.globl __base_relocs_start, __base_relocs_end\n"
325          "__base_relocs_start:");
326 
327     for ( i = 0; i < nsec; ++i )
328     {
329         const void *ptr1, *ptr2;
330 
331         if ( memcmp(sec1[i].name, sec2[i].name, sizeof(sec1[i].name)) ||
332              sec1[i].rva != sec2[i].rva ||
333              sec1[i].size != sec2[i].size ||
334              sec1[i].file_size != sec2[i].file_size ||
335              sec1[i].flags != sec2[i].flags )
336         {
337             fprintf(stderr, "Mismatched section %u parameters\n", i);
338             return 5;
339         }
340 
341         if ( !sec1[i].size ||
342              (sec1[i].flags & (COFF_SECTION_DISCARDABLE|COFF_SECTION_BSS)) )
343             continue;
344 
345         /*
346          * Don't generate relocations for sections that definitely
347          * aren't used by the boot loader code.
348          */
349         if ( memcmp(sec1[i].name, ".initcal", sizeof(sec1[i].name)) == 0 ||
350              memcmp(sec1[i].name, ".init.se", sizeof(sec1[i].name)) == 0 ||
351              memcmp(sec1[i].name, ".buildid", sizeof(sec1[i].name)) == 0 ||
352              memcmp(sec1[i].name, ".lockpro", sizeof(sec1[i].name)) == 0 )
353             continue;
354 
355         if ( !sec1[i].rva )
356         {
357             fprintf(stderr, "Can't handle section %u with zero RVA\n", i);
358             return 3;
359         }
360 
361         if ( sec1[i].file_size > sec1[i].size )
362         {
363             sec1[i].file_size = sec1[i].size;
364             sec2[i].file_size = sec2[i].size;
365         }
366         ptr1 = map_section(sec1 + i, in1, argv[1]);
367         ptr2 = map_section(sec2 + i, in2, argv[2]);
368 
369         diff_sections(ptr1, ptr2, sec1 + i, base2 - base1, width1,
370                       base1, base1 + size1);
371 
372         unmap_section(ptr1, sec1 + i);
373         unmap_section(ptr2, sec2 + i);
374     }
375 
376     diff_sections(NULL, NULL, NULL, 0, 0, 0, 0);
377 
378     puts("__base_relocs_end:");
379 
380     close(in1);
381     close(in2);
382 
383     return 0;
384 }
385