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