1 | /* |
---|
2 | * This file is taken from the Linux kernel and is distributed under GPL v2. |
---|
3 | */ |
---|
4 | #include <stdio.h> |
---|
5 | #include <stdarg.h> |
---|
6 | #include <stdlib.h> |
---|
7 | #include <stdint.h> |
---|
8 | #include <string.h> |
---|
9 | #include <errno.h> |
---|
10 | #include <unistd.h> |
---|
11 | #include <elf.h> |
---|
12 | #include <byteswap.h> |
---|
13 | #define USE_BSD |
---|
14 | #include <endian.h> |
---|
15 | #include <regex.h> |
---|
16 | #include <tools/le_byteshift.h> |
---|
17 | |
---|
18 | static void die(char *fmt, ...); |
---|
19 | |
---|
20 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) |
---|
21 | static Elf32_Ehdr ehdr; |
---|
22 | static unsigned long reloc_count, reloc_idx; |
---|
23 | static unsigned long *relocs; |
---|
24 | static unsigned long reloc16_count, reloc16_idx; |
---|
25 | static unsigned long *relocs16; |
---|
26 | |
---|
27 | struct section { |
---|
28 | Elf32_Shdr shdr; |
---|
29 | struct section *link; |
---|
30 | Elf32_Sym *symtab; |
---|
31 | Elf32_Rel *reltab; |
---|
32 | char *strtab; |
---|
33 | }; |
---|
34 | static struct section *secs; |
---|
35 | |
---|
36 | enum symtype { |
---|
37 | S_ABS, |
---|
38 | S_REL, |
---|
39 | S_SEG, |
---|
40 | S_LIN, |
---|
41 | S_NSYMTYPES |
---|
42 | }; |
---|
43 | |
---|
44 | static const char * const sym_regex_kernel[S_NSYMTYPES] = { |
---|
45 | /* |
---|
46 | * Following symbols have been audited. Don't warn user about |
---|
47 | * absolute relocations present w.r.t these symbols. |
---|
48 | */ |
---|
49 | [S_ABS] = |
---|
50 | "^(__.*_len|__.*_dwords)$", |
---|
51 | |
---|
52 | /* |
---|
53 | * These symbols are known to be relative, even if the linker marks them |
---|
54 | * as absolute (typically defined outside any section in the linker script.) |
---|
55 | */ |
---|
56 | [S_REL] = |
---|
57 | "^(__.*_start|__.*_end|_end|_[se](text|data))$", |
---|
58 | }; |
---|
59 | |
---|
60 | |
---|
61 | static const char * const sym_regex_realmode[S_NSYMTYPES] = { |
---|
62 | /* |
---|
63 | * These are 16-bit segment symbols when compiling 16-bit code. |
---|
64 | */ |
---|
65 | [S_SEG] = |
---|
66 | "^real_mode_seg$", |
---|
67 | |
---|
68 | /* |
---|
69 | * These are offsets belonging to segments, as opposed to linear addresses, |
---|
70 | * when compiling 16-bit code. |
---|
71 | */ |
---|
72 | [S_LIN] = |
---|
73 | "^pa_", |
---|
74 | }; |
---|
75 | |
---|
76 | static const char * const *sym_regex; |
---|
77 | |
---|
78 | static regex_t sym_regex_c[S_NSYMTYPES]; |
---|
79 | static int is_reloc(enum symtype type, const char *sym_name) |
---|
80 | { |
---|
81 | return sym_regex[type] && |
---|
82 | !regexec(&sym_regex_c[type], sym_name, 0, NULL, 0); |
---|
83 | } |
---|
84 | |
---|
85 | static void regex_init(int use_real_mode) |
---|
86 | { |
---|
87 | char errbuf[128]; |
---|
88 | int err; |
---|
89 | int i; |
---|
90 | |
---|
91 | if (use_real_mode) |
---|
92 | sym_regex = sym_regex_realmode; |
---|
93 | else |
---|
94 | sym_regex = sym_regex_kernel; |
---|
95 | |
---|
96 | for (i = 0; i < S_NSYMTYPES; i++) { |
---|
97 | if (!sym_regex[i]) |
---|
98 | continue; |
---|
99 | |
---|
100 | err = regcomp(&sym_regex_c[i], sym_regex[i], |
---|
101 | REG_EXTENDED|REG_NOSUB); |
---|
102 | |
---|
103 | if (err) { |
---|
104 | regerror(err, &sym_regex_c[i], errbuf, sizeof errbuf); |
---|
105 | die("%s", errbuf); |
---|
106 | } |
---|
107 | } |
---|
108 | } |
---|
109 | |
---|
110 | static void die(char *fmt, ...) |
---|
111 | { |
---|
112 | va_list ap; |
---|
113 | va_start(ap, fmt); |
---|
114 | vfprintf(stderr, fmt, ap); |
---|
115 | va_end(ap); |
---|
116 | exit(1); |
---|
117 | } |
---|
118 | |
---|
119 | static const char *sym_type(unsigned type) |
---|
120 | { |
---|
121 | static const char *type_name[] = { |
---|
122 | #define SYM_TYPE(X) [X] = #X |
---|
123 | SYM_TYPE(STT_NOTYPE), |
---|
124 | SYM_TYPE(STT_OBJECT), |
---|
125 | SYM_TYPE(STT_FUNC), |
---|
126 | SYM_TYPE(STT_SECTION), |
---|
127 | SYM_TYPE(STT_FILE), |
---|
128 | SYM_TYPE(STT_COMMON), |
---|
129 | SYM_TYPE(STT_TLS), |
---|
130 | #undef SYM_TYPE |
---|
131 | }; |
---|
132 | const char *name = "unknown sym type name"; |
---|
133 | if (type < ARRAY_SIZE(type_name)) { |
---|
134 | name = type_name[type]; |
---|
135 | } |
---|
136 | return name; |
---|
137 | } |
---|
138 | |
---|
139 | static const char *sym_bind(unsigned bind) |
---|
140 | { |
---|
141 | static const char *bind_name[] = { |
---|
142 | #define SYM_BIND(X) [X] = #X |
---|
143 | SYM_BIND(STB_LOCAL), |
---|
144 | SYM_BIND(STB_GLOBAL), |
---|
145 | SYM_BIND(STB_WEAK), |
---|
146 | #undef SYM_BIND |
---|
147 | }; |
---|
148 | const char *name = "unknown sym bind name"; |
---|
149 | if (bind < ARRAY_SIZE(bind_name)) { |
---|
150 | name = bind_name[bind]; |
---|
151 | } |
---|
152 | return name; |
---|
153 | } |
---|
154 | |
---|
155 | static const char *sym_visibility(unsigned visibility) |
---|
156 | { |
---|
157 | static const char *visibility_name[] = { |
---|
158 | #define SYM_VISIBILITY(X) [X] = #X |
---|
159 | SYM_VISIBILITY(STV_DEFAULT), |
---|
160 | SYM_VISIBILITY(STV_INTERNAL), |
---|
161 | SYM_VISIBILITY(STV_HIDDEN), |
---|
162 | SYM_VISIBILITY(STV_PROTECTED), |
---|
163 | #undef SYM_VISIBILITY |
---|
164 | }; |
---|
165 | const char *name = "unknown sym visibility name"; |
---|
166 | if (visibility < ARRAY_SIZE(visibility_name)) { |
---|
167 | name = visibility_name[visibility]; |
---|
168 | } |
---|
169 | return name; |
---|
170 | } |
---|
171 | |
---|
172 | static const char *rel_type(unsigned type) |
---|
173 | { |
---|
174 | static const char *type_name[] = { |
---|
175 | #define REL_TYPE(X) [X] = #X |
---|
176 | REL_TYPE(R_386_NONE), |
---|
177 | REL_TYPE(R_386_32), |
---|
178 | REL_TYPE(R_386_PC32), |
---|
179 | REL_TYPE(R_386_GOT32), |
---|
180 | REL_TYPE(R_386_PLT32), |
---|
181 | REL_TYPE(R_386_COPY), |
---|
182 | REL_TYPE(R_386_GLOB_DAT), |
---|
183 | REL_TYPE(R_386_JMP_SLOT), |
---|
184 | REL_TYPE(R_386_RELATIVE), |
---|
185 | REL_TYPE(R_386_GOTOFF), |
---|
186 | REL_TYPE(R_386_GOTPC), |
---|
187 | REL_TYPE(R_386_8), |
---|
188 | REL_TYPE(R_386_PC8), |
---|
189 | REL_TYPE(R_386_16), |
---|
190 | REL_TYPE(R_386_PC16), |
---|
191 | #undef REL_TYPE |
---|
192 | }; |
---|
193 | const char *name = "unknown type rel type name"; |
---|
194 | if (type < ARRAY_SIZE(type_name) && type_name[type]) { |
---|
195 | name = type_name[type]; |
---|
196 | } |
---|
197 | return name; |
---|
198 | } |
---|
199 | |
---|
200 | static const char *sec_name(unsigned shndx) |
---|
201 | { |
---|
202 | const char *sec_strtab; |
---|
203 | const char *name; |
---|
204 | sec_strtab = secs[ehdr.e_shstrndx].strtab; |
---|
205 | name = "<noname>"; |
---|
206 | if (shndx < ehdr.e_shnum) { |
---|
207 | name = sec_strtab + secs[shndx].shdr.sh_name; |
---|
208 | } |
---|
209 | else if (shndx == SHN_ABS) { |
---|
210 | name = "ABSOLUTE"; |
---|
211 | } |
---|
212 | else if (shndx == SHN_COMMON) { |
---|
213 | name = "COMMON"; |
---|
214 | } |
---|
215 | return name; |
---|
216 | } |
---|
217 | |
---|
218 | static const char *sym_name(const char *sym_strtab, Elf32_Sym *sym) |
---|
219 | { |
---|
220 | const char *name; |
---|
221 | name = "<noname>"; |
---|
222 | if (sym->st_name) { |
---|
223 | name = sym_strtab + sym->st_name; |
---|
224 | } |
---|
225 | else { |
---|
226 | name = sec_name(sym->st_shndx); |
---|
227 | } |
---|
228 | return name; |
---|
229 | } |
---|
230 | |
---|
231 | |
---|
232 | |
---|
233 | #if BYTE_ORDER == LITTLE_ENDIAN |
---|
234 | #define le16_to_cpu(val) (val) |
---|
235 | #define le32_to_cpu(val) (val) |
---|
236 | #endif |
---|
237 | #if BYTE_ORDER == BIG_ENDIAN |
---|
238 | #define le16_to_cpu(val) bswap_16(val) |
---|
239 | #define le32_to_cpu(val) bswap_32(val) |
---|
240 | #endif |
---|
241 | |
---|
242 | static uint16_t elf16_to_cpu(uint16_t val) |
---|
243 | { |
---|
244 | return le16_to_cpu(val); |
---|
245 | } |
---|
246 | |
---|
247 | static uint32_t elf32_to_cpu(uint32_t val) |
---|
248 | { |
---|
249 | return le32_to_cpu(val); |
---|
250 | } |
---|
251 | |
---|
252 | static void read_ehdr(FILE *fp) |
---|
253 | { |
---|
254 | if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) { |
---|
255 | die("Cannot read ELF header: %s\n", |
---|
256 | strerror(errno)); |
---|
257 | } |
---|
258 | if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) { |
---|
259 | die("No ELF magic\n"); |
---|
260 | } |
---|
261 | if (ehdr.e_ident[EI_CLASS] != ELFCLASS32) { |
---|
262 | die("Not a 32 bit executable\n"); |
---|
263 | } |
---|
264 | if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) { |
---|
265 | die("Not a LSB ELF executable\n"); |
---|
266 | } |
---|
267 | if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) { |
---|
268 | die("Unknown ELF version\n"); |
---|
269 | } |
---|
270 | /* Convert the fields to native endian */ |
---|
271 | ehdr.e_type = elf16_to_cpu(ehdr.e_type); |
---|
272 | ehdr.e_machine = elf16_to_cpu(ehdr.e_machine); |
---|
273 | ehdr.e_version = elf32_to_cpu(ehdr.e_version); |
---|
274 | ehdr.e_entry = elf32_to_cpu(ehdr.e_entry); |
---|
275 | ehdr.e_phoff = elf32_to_cpu(ehdr.e_phoff); |
---|
276 | ehdr.e_shoff = elf32_to_cpu(ehdr.e_shoff); |
---|
277 | ehdr.e_flags = elf32_to_cpu(ehdr.e_flags); |
---|
278 | ehdr.e_ehsize = elf16_to_cpu(ehdr.e_ehsize); |
---|
279 | ehdr.e_phentsize = elf16_to_cpu(ehdr.e_phentsize); |
---|
280 | ehdr.e_phnum = elf16_to_cpu(ehdr.e_phnum); |
---|
281 | ehdr.e_shentsize = elf16_to_cpu(ehdr.e_shentsize); |
---|
282 | ehdr.e_shnum = elf16_to_cpu(ehdr.e_shnum); |
---|
283 | ehdr.e_shstrndx = elf16_to_cpu(ehdr.e_shstrndx); |
---|
284 | |
---|
285 | if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN)) { |
---|
286 | die("Unsupported ELF header type\n"); |
---|
287 | } |
---|
288 | if (ehdr.e_machine != EM_386) { |
---|
289 | die("Not for x86\n"); |
---|
290 | } |
---|
291 | if (ehdr.e_version != EV_CURRENT) { |
---|
292 | die("Unknown ELF version\n"); |
---|
293 | } |
---|
294 | if (ehdr.e_ehsize != sizeof(Elf32_Ehdr)) { |
---|
295 | die("Bad Elf header size\n"); |
---|
296 | } |
---|
297 | if (ehdr.e_phentsize != sizeof(Elf32_Phdr)) { |
---|
298 | die("Bad program header entry\n"); |
---|
299 | } |
---|
300 | if (ehdr.e_shentsize != sizeof(Elf32_Shdr)) { |
---|
301 | die("Bad section header entry\n"); |
---|
302 | } |
---|
303 | if (ehdr.e_shstrndx >= ehdr.e_shnum) { |
---|
304 | die("String table index out of bounds\n"); |
---|
305 | } |
---|
306 | } |
---|
307 | |
---|
308 | static void read_shdrs(FILE *fp) |
---|
309 | { |
---|
310 | int i; |
---|
311 | Elf32_Shdr shdr; |
---|
312 | |
---|
313 | secs = calloc(ehdr.e_shnum, sizeof(struct section)); |
---|
314 | if (!secs) { |
---|
315 | die("Unable to allocate %d section headers\n", |
---|
316 | ehdr.e_shnum); |
---|
317 | } |
---|
318 | if (fseek(fp, ehdr.e_shoff, SEEK_SET) < 0) { |
---|
319 | die("Seek to %d failed: %s\n", |
---|
320 | ehdr.e_shoff, strerror(errno)); |
---|
321 | } |
---|
322 | for (i = 0; i < ehdr.e_shnum; i++) { |
---|
323 | struct section *sec = &secs[i]; |
---|
324 | if (fread(&shdr, sizeof shdr, 1, fp) != 1) |
---|
325 | die("Cannot read ELF section headers %d/%d: %s\n", |
---|
326 | i, ehdr.e_shnum, strerror(errno)); |
---|
327 | sec->shdr.sh_name = elf32_to_cpu(shdr.sh_name); |
---|
328 | sec->shdr.sh_type = elf32_to_cpu(shdr.sh_type); |
---|
329 | sec->shdr.sh_flags = elf32_to_cpu(shdr.sh_flags); |
---|
330 | sec->shdr.sh_addr = elf32_to_cpu(shdr.sh_addr); |
---|
331 | sec->shdr.sh_offset = elf32_to_cpu(shdr.sh_offset); |
---|
332 | sec->shdr.sh_size = elf32_to_cpu(shdr.sh_size); |
---|
333 | sec->shdr.sh_link = elf32_to_cpu(shdr.sh_link); |
---|
334 | sec->shdr.sh_info = elf32_to_cpu(shdr.sh_info); |
---|
335 | sec->shdr.sh_addralign = elf32_to_cpu(shdr.sh_addralign); |
---|
336 | sec->shdr.sh_entsize = elf32_to_cpu(shdr.sh_entsize); |
---|
337 | if (sec->shdr.sh_link < ehdr.e_shnum) |
---|
338 | sec->link = &secs[sec->shdr.sh_link]; |
---|
339 | } |
---|
340 | |
---|
341 | } |
---|
342 | |
---|
343 | static void read_strtabs(FILE *fp) |
---|
344 | { |
---|
345 | int i; |
---|
346 | for (i = 0; i < ehdr.e_shnum; i++) { |
---|
347 | struct section *sec = &secs[i]; |
---|
348 | if (sec->shdr.sh_type != SHT_STRTAB) { |
---|
349 | continue; |
---|
350 | } |
---|
351 | sec->strtab = malloc(sec->shdr.sh_size); |
---|
352 | if (!sec->strtab) { |
---|
353 | die("malloc of %d bytes for strtab failed\n", |
---|
354 | sec->shdr.sh_size); |
---|
355 | } |
---|
356 | if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { |
---|
357 | die("Seek to %d failed: %s\n", |
---|
358 | sec->shdr.sh_offset, strerror(errno)); |
---|
359 | } |
---|
360 | if (fread(sec->strtab, 1, sec->shdr.sh_size, fp) |
---|
361 | != sec->shdr.sh_size) { |
---|
362 | die("Cannot read symbol table: %s\n", |
---|
363 | strerror(errno)); |
---|
364 | } |
---|
365 | } |
---|
366 | } |
---|
367 | |
---|
368 | static void read_symtabs(FILE *fp) |
---|
369 | { |
---|
370 | int i,j; |
---|
371 | for (i = 0; i < ehdr.e_shnum; i++) { |
---|
372 | struct section *sec = &secs[i]; |
---|
373 | if (sec->shdr.sh_type != SHT_SYMTAB) { |
---|
374 | continue; |
---|
375 | } |
---|
376 | sec->symtab = malloc(sec->shdr.sh_size); |
---|
377 | if (!sec->symtab) { |
---|
378 | die("malloc of %d bytes for symtab failed\n", |
---|
379 | sec->shdr.sh_size); |
---|
380 | } |
---|
381 | if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { |
---|
382 | die("Seek to %d failed: %s\n", |
---|
383 | sec->shdr.sh_offset, strerror(errno)); |
---|
384 | } |
---|
385 | if (fread(sec->symtab, 1, sec->shdr.sh_size, fp) |
---|
386 | != sec->shdr.sh_size) { |
---|
387 | die("Cannot read symbol table: %s\n", |
---|
388 | strerror(errno)); |
---|
389 | } |
---|
390 | for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Sym); j++) { |
---|
391 | Elf32_Sym *sym = &sec->symtab[j]; |
---|
392 | sym->st_name = elf32_to_cpu(sym->st_name); |
---|
393 | sym->st_value = elf32_to_cpu(sym->st_value); |
---|
394 | sym->st_size = elf32_to_cpu(sym->st_size); |
---|
395 | sym->st_shndx = elf16_to_cpu(sym->st_shndx); |
---|
396 | } |
---|
397 | } |
---|
398 | } |
---|
399 | |
---|
400 | |
---|
401 | static void read_relocs(FILE *fp) |
---|
402 | { |
---|
403 | int i,j; |
---|
404 | for (i = 0; i < ehdr.e_shnum; i++) { |
---|
405 | struct section *sec = &secs[i]; |
---|
406 | if (sec->shdr.sh_type != SHT_REL) { |
---|
407 | continue; |
---|
408 | } |
---|
409 | sec->reltab = malloc(sec->shdr.sh_size); |
---|
410 | if (!sec->reltab) { |
---|
411 | die("malloc of %d bytes for relocs failed\n", |
---|
412 | sec->shdr.sh_size); |
---|
413 | } |
---|
414 | if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { |
---|
415 | die("Seek to %d failed: %s\n", |
---|
416 | sec->shdr.sh_offset, strerror(errno)); |
---|
417 | } |
---|
418 | if (fread(sec->reltab, 1, sec->shdr.sh_size, fp) |
---|
419 | != sec->shdr.sh_size) { |
---|
420 | die("Cannot read symbol table: %s\n", |
---|
421 | strerror(errno)); |
---|
422 | } |
---|
423 | for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) { |
---|
424 | Elf32_Rel *rel = &sec->reltab[j]; |
---|
425 | rel->r_offset = elf32_to_cpu(rel->r_offset); |
---|
426 | rel->r_info = elf32_to_cpu(rel->r_info); |
---|
427 | } |
---|
428 | } |
---|
429 | } |
---|
430 | |
---|
431 | |
---|
432 | static void print_absolute_symbols(void) |
---|
433 | { |
---|
434 | int i; |
---|
435 | printf("Absolute symbols\n"); |
---|
436 | printf(" Num: Value Size Type Bind Visibility Name\n"); |
---|
437 | for (i = 0; i < ehdr.e_shnum; i++) { |
---|
438 | struct section *sec = &secs[i]; |
---|
439 | char *sym_strtab; |
---|
440 | int j; |
---|
441 | |
---|
442 | if (sec->shdr.sh_type != SHT_SYMTAB) { |
---|
443 | continue; |
---|
444 | } |
---|
445 | sym_strtab = sec->link->strtab; |
---|
446 | for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Sym); j++) { |
---|
447 | Elf32_Sym *sym; |
---|
448 | const char *name; |
---|
449 | sym = &sec->symtab[j]; |
---|
450 | name = sym_name(sym_strtab, sym); |
---|
451 | if (sym->st_shndx != SHN_ABS) { |
---|
452 | continue; |
---|
453 | } |
---|
454 | printf("%5d %08x %5d %10s %10s %12s %s\n", |
---|
455 | j, sym->st_value, sym->st_size, |
---|
456 | sym_type(ELF32_ST_TYPE(sym->st_info)), |
---|
457 | sym_bind(ELF32_ST_BIND(sym->st_info)), |
---|
458 | sym_visibility(ELF32_ST_VISIBILITY(sym->st_other)), |
---|
459 | name); |
---|
460 | } |
---|
461 | } |
---|
462 | printf("\n"); |
---|
463 | } |
---|
464 | |
---|
465 | static void print_absolute_relocs(void) |
---|
466 | { |
---|
467 | int i, printed = 0; |
---|
468 | |
---|
469 | for (i = 0; i < ehdr.e_shnum; i++) { |
---|
470 | struct section *sec = &secs[i]; |
---|
471 | struct section *sec_applies, *sec_symtab; |
---|
472 | char *sym_strtab; |
---|
473 | Elf32_Sym *sh_symtab; |
---|
474 | int j; |
---|
475 | if (sec->shdr.sh_type != SHT_REL) { |
---|
476 | continue; |
---|
477 | } |
---|
478 | sec_symtab = sec->link; |
---|
479 | sec_applies = &secs[sec->shdr.sh_info]; |
---|
480 | if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) { |
---|
481 | continue; |
---|
482 | } |
---|
483 | sh_symtab = sec_symtab->symtab; |
---|
484 | sym_strtab = sec_symtab->link->strtab; |
---|
485 | for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) { |
---|
486 | Elf32_Rel *rel; |
---|
487 | Elf32_Sym *sym; |
---|
488 | const char *name; |
---|
489 | rel = &sec->reltab[j]; |
---|
490 | sym = &sh_symtab[ELF32_R_SYM(rel->r_info)]; |
---|
491 | name = sym_name(sym_strtab, sym); |
---|
492 | if (sym->st_shndx != SHN_ABS) { |
---|
493 | continue; |
---|
494 | } |
---|
495 | |
---|
496 | /* Absolute symbols are not relocated if bzImage is |
---|
497 | * loaded at a non-compiled address. Display a warning |
---|
498 | * to user at compile time about the absolute |
---|
499 | * relocations present. |
---|
500 | * |
---|
501 | * User need to audit the code to make sure |
---|
502 | * some symbols which should have been section |
---|
503 | * relative have not become absolute because of some |
---|
504 | * linker optimization or wrong programming usage. |
---|
505 | * |
---|
506 | * Before warning check if this absolute symbol |
---|
507 | * relocation is harmless. |
---|
508 | */ |
---|
509 | if (is_reloc(S_ABS, name) || is_reloc(S_REL, name)) |
---|
510 | continue; |
---|
511 | |
---|
512 | if (!printed) { |
---|
513 | printf("WARNING: Absolute relocations" |
---|
514 | " present\n"); |
---|
515 | printf("Offset Info Type Sym.Value " |
---|
516 | "Sym.Name\n"); |
---|
517 | printed = 1; |
---|
518 | } |
---|
519 | |
---|
520 | printf("%08x %08x %10s %08x %s\n", |
---|
521 | rel->r_offset, |
---|
522 | rel->r_info, |
---|
523 | rel_type(ELF32_R_TYPE(rel->r_info)), |
---|
524 | sym->st_value, |
---|
525 | name); |
---|
526 | } |
---|
527 | } |
---|
528 | |
---|
529 | if (printed) |
---|
530 | printf("\n"); |
---|
531 | } |
---|
532 | |
---|
533 | static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym), |
---|
534 | int use_real_mode) |
---|
535 | { |
---|
536 | int i; |
---|
537 | /* Walk through the relocations */ |
---|
538 | for (i = 0; i < ehdr.e_shnum; i++) { |
---|
539 | char *sym_strtab; |
---|
540 | Elf32_Sym *sh_symtab; |
---|
541 | struct section *sec_applies, *sec_symtab; |
---|
542 | int j; |
---|
543 | struct section *sec = &secs[i]; |
---|
544 | |
---|
545 | if (sec->shdr.sh_type != SHT_REL) { |
---|
546 | continue; |
---|
547 | } |
---|
548 | sec_symtab = sec->link; |
---|
549 | sec_applies = &secs[sec->shdr.sh_info]; |
---|
550 | if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) { |
---|
551 | continue; |
---|
552 | } |
---|
553 | sh_symtab = sec_symtab->symtab; |
---|
554 | sym_strtab = sec_symtab->link->strtab; |
---|
555 | for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) { |
---|
556 | Elf32_Rel *rel; |
---|
557 | Elf32_Sym *sym; |
---|
558 | unsigned r_type; |
---|
559 | const char *symname; |
---|
560 | int shn_abs; |
---|
561 | |
---|
562 | rel = &sec->reltab[j]; |
---|
563 | sym = &sh_symtab[ELF32_R_SYM(rel->r_info)]; |
---|
564 | r_type = ELF32_R_TYPE(rel->r_info); |
---|
565 | |
---|
566 | shn_abs = sym->st_shndx == SHN_ABS; |
---|
567 | |
---|
568 | switch (r_type) { |
---|
569 | case R_386_NONE: |
---|
570 | case R_386_PC32: |
---|
571 | case R_386_PC16: |
---|
572 | case R_386_PC8: |
---|
573 | case R_386_GOTPC: |
---|
574 | case R_386_GOTOFF: |
---|
575 | case R_386_GOT32: |
---|
576 | case R_386_PLT32: |
---|
577 | /* |
---|
578 | * NONE can be ignored and and PC relative |
---|
579 | * relocations don't need to be adjusted. |
---|
580 | */ |
---|
581 | break; |
---|
582 | |
---|
583 | case R_386_16: |
---|
584 | symname = sym_name(sym_strtab, sym); |
---|
585 | if (!use_real_mode) |
---|
586 | goto bad; |
---|
587 | if (shn_abs) { |
---|
588 | if (is_reloc(S_ABS, symname)) |
---|
589 | break; |
---|
590 | else if (!is_reloc(S_SEG, symname)) |
---|
591 | goto bad; |
---|
592 | } else { |
---|
593 | if (is_reloc(S_LIN, symname)) |
---|
594 | goto bad; |
---|
595 | else |
---|
596 | break; |
---|
597 | } |
---|
598 | visit(rel, sym); |
---|
599 | break; |
---|
600 | |
---|
601 | case R_386_32: |
---|
602 | symname = sym_name(sym_strtab, sym); |
---|
603 | if (shn_abs) { |
---|
604 | if (is_reloc(S_ABS, symname)) |
---|
605 | break; |
---|
606 | else if (!is_reloc(S_REL, symname)) |
---|
607 | goto bad; |
---|
608 | } else { |
---|
609 | if (use_real_mode && |
---|
610 | !is_reloc(S_LIN, symname)) |
---|
611 | break; |
---|
612 | } |
---|
613 | visit(rel, sym); |
---|
614 | break; |
---|
615 | default: |
---|
616 | die("Unsupported relocation type: %s (%d)\n", |
---|
617 | rel_type(r_type), r_type); |
---|
618 | break; |
---|
619 | bad: |
---|
620 | symname = sym_name(sym_strtab, sym); |
---|
621 | die("Invalid %s %s relocation: %s\n", |
---|
622 | shn_abs ? "absolute" : "relative", |
---|
623 | rel_type(r_type), symname); |
---|
624 | } |
---|
625 | } |
---|
626 | } |
---|
627 | } |
---|
628 | |
---|
629 | static void count_reloc(Elf32_Rel *rel, Elf32_Sym *sym) |
---|
630 | { |
---|
631 | (void)sym; |
---|
632 | |
---|
633 | if (ELF32_R_TYPE(rel->r_info) == R_386_16) |
---|
634 | reloc16_count++; |
---|
635 | else |
---|
636 | reloc_count++; |
---|
637 | } |
---|
638 | |
---|
639 | static void collect_reloc(Elf32_Rel *rel, Elf32_Sym *sym) |
---|
640 | { |
---|
641 | (void)sym; |
---|
642 | |
---|
643 | /* Remember the address that needs to be adjusted. */ |
---|
644 | if (ELF32_R_TYPE(rel->r_info) == R_386_16) |
---|
645 | relocs16[reloc16_idx++] = rel->r_offset; |
---|
646 | else |
---|
647 | relocs[reloc_idx++] = rel->r_offset; |
---|
648 | } |
---|
649 | |
---|
650 | static int cmp_relocs(const void *va, const void *vb) |
---|
651 | { |
---|
652 | const unsigned long *a, *b; |
---|
653 | a = va; b = vb; |
---|
654 | return (*a == *b)? 0 : (*a > *b)? 1 : -1; |
---|
655 | } |
---|
656 | |
---|
657 | static int write32(unsigned int v, FILE *f) |
---|
658 | { |
---|
659 | unsigned char buf[4]; |
---|
660 | |
---|
661 | put_unaligned_le32(v, buf); |
---|
662 | return fwrite(buf, 1, 4, f) == 4 ? 0 : -1; |
---|
663 | } |
---|
664 | |
---|
665 | static void emit_relocs(int as_text, int use_real_mode) |
---|
666 | { |
---|
667 | int i; |
---|
668 | /* Count how many relocations I have and allocate space for them. */ |
---|
669 | reloc_count = 0; |
---|
670 | walk_relocs(count_reloc, use_real_mode); |
---|
671 | relocs = malloc(reloc_count * sizeof(relocs[0])); |
---|
672 | if (!relocs) { |
---|
673 | die("malloc of %d entries for relocs failed\n", |
---|
674 | reloc_count); |
---|
675 | } |
---|
676 | |
---|
677 | relocs16 = malloc(reloc16_count * sizeof(relocs[0])); |
---|
678 | if (!relocs16) { |
---|
679 | die("malloc of %d entries for relocs16 failed\n", |
---|
680 | reloc16_count); |
---|
681 | } |
---|
682 | /* Collect up the relocations */ |
---|
683 | reloc_idx = 0; |
---|
684 | walk_relocs(collect_reloc, use_real_mode); |
---|
685 | |
---|
686 | if (reloc16_count && !use_real_mode) |
---|
687 | die("Segment relocations found but --realmode not specified\n"); |
---|
688 | |
---|
689 | /* Order the relocations for more efficient processing */ |
---|
690 | qsort(relocs, reloc_count, sizeof(relocs[0]), cmp_relocs); |
---|
691 | qsort(relocs16, reloc16_count, sizeof(relocs16[0]), cmp_relocs); |
---|
692 | |
---|
693 | /* Print the relocations */ |
---|
694 | if (as_text) { |
---|
695 | /* Print the relocations in a form suitable that |
---|
696 | * gas will like. |
---|
697 | */ |
---|
698 | printf(".section \".data.reloc\",\"a\"\n"); |
---|
699 | printf(".balign 4\n"); |
---|
700 | if (use_real_mode) { |
---|
701 | printf("\t.long %lu\n", reloc16_count); |
---|
702 | for (i = 0; i < reloc16_count; i++) |
---|
703 | printf("\t.long 0x%08lx\n", relocs16[i]); |
---|
704 | printf("\t.long %lu\n", reloc_count); |
---|
705 | for (i = 0; i < reloc_count; i++) { |
---|
706 | printf("\t.long 0x%08lx\n", relocs[i]); |
---|
707 | } |
---|
708 | } else { |
---|
709 | for (i = 0; i < reloc_count; i++) { |
---|
710 | printf("\t.long 0x%08lx\n", relocs[i]); |
---|
711 | } |
---|
712 | /* Print a stop */ |
---|
713 | printf("\t.long 0x%08lx\n", (unsigned long)0); |
---|
714 | } |
---|
715 | |
---|
716 | printf("\n"); |
---|
717 | } |
---|
718 | else { |
---|
719 | if (use_real_mode) { |
---|
720 | write32(reloc16_count, stdout); |
---|
721 | for (i = 0; i < reloc16_count; i++) |
---|
722 | write32(relocs16[i], stdout); |
---|
723 | write32(reloc_count, stdout); |
---|
724 | |
---|
725 | /* Now print each relocation */ |
---|
726 | for (i = 0; i < reloc_count; i++) |
---|
727 | write32(relocs[i], stdout); |
---|
728 | } else { |
---|
729 | /* Now print each relocation */ |
---|
730 | for (i = 0; i < reloc_count; i++) { |
---|
731 | write32(relocs[i], stdout); |
---|
732 | } |
---|
733 | |
---|
734 | /* Print a stop */ |
---|
735 | write32(0, stdout); |
---|
736 | } |
---|
737 | } |
---|
738 | } |
---|
739 | |
---|
740 | static void usage(void) |
---|
741 | { |
---|
742 | die("relocs [--abs-syms|--abs-relocs|--text|--realmode] vmlinux\n"); |
---|
743 | } |
---|
744 | |
---|
745 | int main(int argc, char **argv) |
---|
746 | { |
---|
747 | int show_absolute_syms, show_absolute_relocs; |
---|
748 | int as_text, use_real_mode; |
---|
749 | const char *fname; |
---|
750 | FILE *fp; |
---|
751 | int i; |
---|
752 | |
---|
753 | show_absolute_syms = 0; |
---|
754 | show_absolute_relocs = 0; |
---|
755 | as_text = 0; |
---|
756 | use_real_mode = 0; |
---|
757 | fname = NULL; |
---|
758 | for (i = 1; i < argc; i++) { |
---|
759 | char *arg = argv[i]; |
---|
760 | if (*arg == '-') { |
---|
761 | if (strcmp(arg, "--abs-syms") == 0) { |
---|
762 | show_absolute_syms = 1; |
---|
763 | continue; |
---|
764 | } |
---|
765 | if (strcmp(arg, "--abs-relocs") == 0) { |
---|
766 | show_absolute_relocs = 1; |
---|
767 | continue; |
---|
768 | } |
---|
769 | if (strcmp(arg, "--text") == 0) { |
---|
770 | as_text = 1; |
---|
771 | continue; |
---|
772 | } |
---|
773 | if (strcmp(arg, "--realmode") == 0) { |
---|
774 | use_real_mode = 1; |
---|
775 | continue; |
---|
776 | } |
---|
777 | } |
---|
778 | else if (!fname) { |
---|
779 | fname = arg; |
---|
780 | continue; |
---|
781 | } |
---|
782 | usage(); |
---|
783 | } |
---|
784 | if (!fname) { |
---|
785 | usage(); |
---|
786 | } |
---|
787 | regex_init(use_real_mode); |
---|
788 | fp = fopen(fname, "r"); |
---|
789 | if (!fp) { |
---|
790 | die("Cannot open %s: %s\n", |
---|
791 | fname, strerror(errno)); |
---|
792 | } |
---|
793 | read_ehdr(fp); |
---|
794 | read_shdrs(fp); |
---|
795 | read_strtabs(fp); |
---|
796 | read_symtabs(fp); |
---|
797 | read_relocs(fp); |
---|
798 | if (show_absolute_syms) { |
---|
799 | print_absolute_symbols(); |
---|
800 | return 0; |
---|
801 | } |
---|
802 | if (show_absolute_relocs) { |
---|
803 | print_absolute_relocs(); |
---|
804 | return 0; |
---|
805 | } |
---|
806 | emit_relocs(as_text, use_real_mode); |
---|
807 | return 0; |
---|
808 | } |
---|