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