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