16520fe55SH. Peter Anvin #include <stdio.h> 26520fe55SH. Peter Anvin #include <stdarg.h> 36520fe55SH. Peter Anvin #include <stdlib.h> 46520fe55SH. Peter Anvin #include <stdint.h> 5*5d442e63SKees Cook #include <inttypes.h> 66520fe55SH. Peter Anvin #include <string.h> 76520fe55SH. Peter Anvin #include <errno.h> 86520fe55SH. Peter Anvin #include <unistd.h> 96520fe55SH. Peter Anvin #include <elf.h> 106520fe55SH. Peter Anvin #include <byteswap.h> 116520fe55SH. Peter Anvin #define USE_BSD 126520fe55SH. Peter Anvin #include <endian.h> 136520fe55SH. Peter Anvin #include <regex.h> 146520fe55SH. Peter Anvin #include <tools/le_byteshift.h> 156520fe55SH. Peter Anvin 16bf11655cSKees Cook #define ElfW(type) _ElfW(ELF_BITS, type) 17bf11655cSKees Cook #define _ElfW(bits, type) __ElfW(bits, type) 18bf11655cSKees Cook #define __ElfW(bits, type) Elf##bits##_##type 19bf11655cSKees Cook 20bf11655cSKees Cook #define ELF_BITS 32 21bf11655cSKees Cook #define ELF_MACHINE EM_386 22bf11655cSKees Cook #define ELF_MACHINE_NAME "i386" 23bf11655cSKees Cook #define SHT_REL_TYPE SHT_REL 24bf11655cSKees Cook 25bf11655cSKees Cook #define ELF_CLASS ELFCLASS32 26bf11655cSKees Cook #define ELF_R_SYM(val) ELF32_R_SYM(val) 27bf11655cSKees Cook #define ELF_R_TYPE(val) ELF32_R_TYPE(val) 28bf11655cSKees Cook #define ELF_ST_TYPE(o) ELF32_ST_TYPE(o) 29bf11655cSKees Cook #define ELF_ST_BIND(o) ELF32_ST_BIND(o) 30bf11655cSKees Cook #define ELF_ST_VISIBILITY(o) ELF32_ST_VISIBILITY(o) 31bf11655cSKees Cook 32bf11655cSKees Cook #define Elf_Rel ElfW(Rel) 33bf11655cSKees Cook #define Elf_Ehdr ElfW(Ehdr) 34bf11655cSKees Cook #define Elf_Phdr ElfW(Phdr) 35bf11655cSKees Cook #define Elf_Shdr ElfW(Shdr) 36bf11655cSKees Cook #define Elf_Sym ElfW(Sym) 37bf11655cSKees Cook 386520fe55SH. Peter Anvin static void die(char *fmt, ...); 396520fe55SH. Peter Anvin 406520fe55SH. Peter Anvin #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 41bf11655cSKees Cook static Elf_Ehdr ehdr; 42*5d442e63SKees Cook 43*5d442e63SKees Cook struct relocs { 44*5d442e63SKees Cook uint32_t *offset; 45*5d442e63SKees Cook unsigned long count; 46*5d442e63SKees Cook unsigned long size; 47*5d442e63SKees Cook }; 48*5d442e63SKees Cook 49*5d442e63SKees Cook static struct relocs relocs16; 50*5d442e63SKees Cook static struct relocs relocs32; 516520fe55SH. Peter Anvin 526520fe55SH. Peter Anvin struct section { 53bf11655cSKees Cook Elf_Shdr shdr; 546520fe55SH. Peter Anvin struct section *link; 55bf11655cSKees Cook Elf_Sym *symtab; 56bf11655cSKees Cook Elf_Rel *reltab; 576520fe55SH. Peter Anvin char *strtab; 586520fe55SH. Peter Anvin }; 596520fe55SH. Peter Anvin static struct section *secs; 606520fe55SH. Peter Anvin 616520fe55SH. Peter Anvin enum symtype { 626520fe55SH. Peter Anvin S_ABS, 636520fe55SH. Peter Anvin S_REL, 646520fe55SH. Peter Anvin S_SEG, 656520fe55SH. Peter Anvin S_LIN, 666520fe55SH. Peter Anvin S_NSYMTYPES 676520fe55SH. Peter Anvin }; 686520fe55SH. Peter Anvin 696520fe55SH. Peter Anvin static const char * const sym_regex_kernel[S_NSYMTYPES] = { 706520fe55SH. Peter Anvin /* 716520fe55SH. Peter Anvin * Following symbols have been audited. There values are constant and do 726520fe55SH. Peter Anvin * not change if bzImage is loaded at a different physical address than 736520fe55SH. Peter Anvin * the address for which it has been compiled. Don't warn user about 746520fe55SH. Peter Anvin * absolute relocations present w.r.t these symbols. 756520fe55SH. Peter Anvin */ 766520fe55SH. Peter Anvin [S_ABS] = 776520fe55SH. Peter Anvin "^(xen_irq_disable_direct_reloc$|" 786520fe55SH. Peter Anvin "xen_save_fl_direct_reloc$|" 796520fe55SH. Peter Anvin "VDSO|" 806520fe55SH. Peter Anvin "__crc_)", 816520fe55SH. Peter Anvin 826520fe55SH. Peter Anvin /* 836520fe55SH. Peter Anvin * These symbols are known to be relative, even if the linker marks them 846520fe55SH. Peter Anvin * as absolute (typically defined outside any section in the linker script.) 856520fe55SH. Peter Anvin */ 866520fe55SH. Peter Anvin [S_REL] = 87a3e854d9SH. Peter Anvin "^(__init_(begin|end)|" 88a3e854d9SH. Peter Anvin "__x86_cpu_dev_(start|end)|" 89a3e854d9SH. Peter Anvin "(__parainstructions|__alt_instructions)(|_end)|" 90a3e854d9SH. Peter Anvin "(__iommu_table|__apicdrivers|__smp_locks)(|_end)|" 91fd952815SH. Peter Anvin "__(start|end)_pci_.*|" 92fd952815SH. Peter Anvin "__(start|end)_builtin_fw|" 93fd952815SH. Peter Anvin "__(start|stop)___ksymtab(|_gpl|_unused|_unused_gpl|_gpl_future)|" 94fd952815SH. Peter Anvin "__(start|stop)___kcrctab(|_gpl|_unused|_unused_gpl|_gpl_future)|" 95fd952815SH. Peter Anvin "__(start|stop)___param|" 96fd952815SH. Peter Anvin "__(start|stop)___modver|" 97fd952815SH. Peter Anvin "__(start|stop)___bug_table|" 98fd952815SH. Peter Anvin "__tracedata_(start|end)|" 99fd952815SH. Peter Anvin "__(start|stop)_notes|" 100fd952815SH. Peter Anvin "__end_rodata|" 101fd952815SH. Peter Anvin "__initramfs_start|" 102ea17e741SH. Peter Anvin "(jiffies|jiffies_64)|" 103a3e854d9SH. Peter Anvin "_end)$" 1046520fe55SH. Peter Anvin }; 1056520fe55SH. Peter Anvin 1066520fe55SH. Peter Anvin 1076520fe55SH. Peter Anvin static const char * const sym_regex_realmode[S_NSYMTYPES] = { 1086520fe55SH. Peter Anvin /* 109f2604c14SJarkko Sakkinen * These symbols are known to be relative, even if the linker marks them 110f2604c14SJarkko Sakkinen * as absolute (typically defined outside any section in the linker script.) 111f2604c14SJarkko Sakkinen */ 112f2604c14SJarkko Sakkinen [S_REL] = 113f2604c14SJarkko Sakkinen "^pa_", 114f2604c14SJarkko Sakkinen 115f2604c14SJarkko Sakkinen /* 1166520fe55SH. Peter Anvin * These are 16-bit segment symbols when compiling 16-bit code. 1176520fe55SH. Peter Anvin */ 1186520fe55SH. Peter Anvin [S_SEG] = 1196520fe55SH. Peter Anvin "^real_mode_seg$", 1206520fe55SH. Peter Anvin 1216520fe55SH. Peter Anvin /* 1226520fe55SH. Peter Anvin * These are offsets belonging to segments, as opposed to linear addresses, 1236520fe55SH. Peter Anvin * when compiling 16-bit code. 1246520fe55SH. Peter Anvin */ 1256520fe55SH. Peter Anvin [S_LIN] = 1266520fe55SH. Peter Anvin "^pa_", 1276520fe55SH. Peter Anvin }; 1286520fe55SH. Peter Anvin 1296520fe55SH. Peter Anvin static const char * const *sym_regex; 1306520fe55SH. Peter Anvin 1316520fe55SH. Peter Anvin static regex_t sym_regex_c[S_NSYMTYPES]; 1326520fe55SH. Peter Anvin static int is_reloc(enum symtype type, const char *sym_name) 1336520fe55SH. Peter Anvin { 1346520fe55SH. Peter Anvin return sym_regex[type] && 1356520fe55SH. Peter Anvin !regexec(&sym_regex_c[type], sym_name, 0, NULL, 0); 1366520fe55SH. Peter Anvin } 1376520fe55SH. Peter Anvin 1386520fe55SH. Peter Anvin static void regex_init(int use_real_mode) 1396520fe55SH. Peter Anvin { 1406520fe55SH. Peter Anvin char errbuf[128]; 1416520fe55SH. Peter Anvin int err; 1426520fe55SH. Peter Anvin int i; 1436520fe55SH. Peter Anvin 1446520fe55SH. Peter Anvin if (use_real_mode) 1456520fe55SH. Peter Anvin sym_regex = sym_regex_realmode; 1466520fe55SH. Peter Anvin else 1476520fe55SH. Peter Anvin sym_regex = sym_regex_kernel; 1486520fe55SH. Peter Anvin 1496520fe55SH. Peter Anvin for (i = 0; i < S_NSYMTYPES; i++) { 1506520fe55SH. Peter Anvin if (!sym_regex[i]) 1516520fe55SH. Peter Anvin continue; 1526520fe55SH. Peter Anvin 1536520fe55SH. Peter Anvin err = regcomp(&sym_regex_c[i], sym_regex[i], 1546520fe55SH. Peter Anvin REG_EXTENDED|REG_NOSUB); 1556520fe55SH. Peter Anvin 1566520fe55SH. Peter Anvin if (err) { 1576520fe55SH. Peter Anvin regerror(err, &sym_regex_c[i], errbuf, sizeof errbuf); 1586520fe55SH. Peter Anvin die("%s", errbuf); 1596520fe55SH. Peter Anvin } 1606520fe55SH. Peter Anvin } 1616520fe55SH. Peter Anvin } 1626520fe55SH. Peter Anvin 1636520fe55SH. Peter Anvin static void die(char *fmt, ...) 1646520fe55SH. Peter Anvin { 1656520fe55SH. Peter Anvin va_list ap; 1666520fe55SH. Peter Anvin va_start(ap, fmt); 1676520fe55SH. Peter Anvin vfprintf(stderr, fmt, ap); 1686520fe55SH. Peter Anvin va_end(ap); 1696520fe55SH. Peter Anvin exit(1); 1706520fe55SH. Peter Anvin } 1716520fe55SH. Peter Anvin 1726520fe55SH. Peter Anvin static const char *sym_type(unsigned type) 1736520fe55SH. Peter Anvin { 1746520fe55SH. Peter Anvin static const char *type_name[] = { 1756520fe55SH. Peter Anvin #define SYM_TYPE(X) [X] = #X 1766520fe55SH. Peter Anvin SYM_TYPE(STT_NOTYPE), 1776520fe55SH. Peter Anvin SYM_TYPE(STT_OBJECT), 1786520fe55SH. Peter Anvin SYM_TYPE(STT_FUNC), 1796520fe55SH. Peter Anvin SYM_TYPE(STT_SECTION), 1806520fe55SH. Peter Anvin SYM_TYPE(STT_FILE), 1816520fe55SH. Peter Anvin SYM_TYPE(STT_COMMON), 1826520fe55SH. Peter Anvin SYM_TYPE(STT_TLS), 1836520fe55SH. Peter Anvin #undef SYM_TYPE 1846520fe55SH. Peter Anvin }; 1856520fe55SH. Peter Anvin const char *name = "unknown sym type name"; 1866520fe55SH. Peter Anvin if (type < ARRAY_SIZE(type_name)) { 1876520fe55SH. Peter Anvin name = type_name[type]; 1886520fe55SH. Peter Anvin } 1896520fe55SH. Peter Anvin return name; 1906520fe55SH. Peter Anvin } 1916520fe55SH. Peter Anvin 1926520fe55SH. Peter Anvin static const char *sym_bind(unsigned bind) 1936520fe55SH. Peter Anvin { 1946520fe55SH. Peter Anvin static const char *bind_name[] = { 1956520fe55SH. Peter Anvin #define SYM_BIND(X) [X] = #X 1966520fe55SH. Peter Anvin SYM_BIND(STB_LOCAL), 1976520fe55SH. Peter Anvin SYM_BIND(STB_GLOBAL), 1986520fe55SH. Peter Anvin SYM_BIND(STB_WEAK), 1996520fe55SH. Peter Anvin #undef SYM_BIND 2006520fe55SH. Peter Anvin }; 2016520fe55SH. Peter Anvin const char *name = "unknown sym bind name"; 2026520fe55SH. Peter Anvin if (bind < ARRAY_SIZE(bind_name)) { 2036520fe55SH. Peter Anvin name = bind_name[bind]; 2046520fe55SH. Peter Anvin } 2056520fe55SH. Peter Anvin return name; 2066520fe55SH. Peter Anvin } 2076520fe55SH. Peter Anvin 2086520fe55SH. Peter Anvin static const char *sym_visibility(unsigned visibility) 2096520fe55SH. Peter Anvin { 2106520fe55SH. Peter Anvin static const char *visibility_name[] = { 2116520fe55SH. Peter Anvin #define SYM_VISIBILITY(X) [X] = #X 2126520fe55SH. Peter Anvin SYM_VISIBILITY(STV_DEFAULT), 2136520fe55SH. Peter Anvin SYM_VISIBILITY(STV_INTERNAL), 2146520fe55SH. Peter Anvin SYM_VISIBILITY(STV_HIDDEN), 2156520fe55SH. Peter Anvin SYM_VISIBILITY(STV_PROTECTED), 2166520fe55SH. Peter Anvin #undef SYM_VISIBILITY 2176520fe55SH. Peter Anvin }; 2186520fe55SH. Peter Anvin const char *name = "unknown sym visibility name"; 2196520fe55SH. Peter Anvin if (visibility < ARRAY_SIZE(visibility_name)) { 2206520fe55SH. Peter Anvin name = visibility_name[visibility]; 2216520fe55SH. Peter Anvin } 2226520fe55SH. Peter Anvin return name; 2236520fe55SH. Peter Anvin } 2246520fe55SH. Peter Anvin 2256520fe55SH. Peter Anvin static const char *rel_type(unsigned type) 2266520fe55SH. Peter Anvin { 2276520fe55SH. Peter Anvin static const char *type_name[] = { 2286520fe55SH. Peter Anvin #define REL_TYPE(X) [X] = #X 2296520fe55SH. Peter Anvin REL_TYPE(R_386_NONE), 2306520fe55SH. Peter Anvin REL_TYPE(R_386_32), 2316520fe55SH. Peter Anvin REL_TYPE(R_386_PC32), 2326520fe55SH. Peter Anvin REL_TYPE(R_386_GOT32), 2336520fe55SH. Peter Anvin REL_TYPE(R_386_PLT32), 2346520fe55SH. Peter Anvin REL_TYPE(R_386_COPY), 2356520fe55SH. Peter Anvin REL_TYPE(R_386_GLOB_DAT), 2366520fe55SH. Peter Anvin REL_TYPE(R_386_JMP_SLOT), 2376520fe55SH. Peter Anvin REL_TYPE(R_386_RELATIVE), 2386520fe55SH. Peter Anvin REL_TYPE(R_386_GOTOFF), 2396520fe55SH. Peter Anvin REL_TYPE(R_386_GOTPC), 2406520fe55SH. Peter Anvin REL_TYPE(R_386_8), 2416520fe55SH. Peter Anvin REL_TYPE(R_386_PC8), 2426520fe55SH. Peter Anvin REL_TYPE(R_386_16), 2436520fe55SH. Peter Anvin REL_TYPE(R_386_PC16), 2446520fe55SH. Peter Anvin #undef REL_TYPE 2456520fe55SH. Peter Anvin }; 2466520fe55SH. Peter Anvin const char *name = "unknown type rel type name"; 2476520fe55SH. Peter Anvin if (type < ARRAY_SIZE(type_name) && type_name[type]) { 2486520fe55SH. Peter Anvin name = type_name[type]; 2496520fe55SH. Peter Anvin } 2506520fe55SH. Peter Anvin return name; 2516520fe55SH. Peter Anvin } 2526520fe55SH. Peter Anvin 2536520fe55SH. Peter Anvin static const char *sec_name(unsigned shndx) 2546520fe55SH. Peter Anvin { 2556520fe55SH. Peter Anvin const char *sec_strtab; 2566520fe55SH. Peter Anvin const char *name; 2576520fe55SH. Peter Anvin sec_strtab = secs[ehdr.e_shstrndx].strtab; 2586520fe55SH. Peter Anvin name = "<noname>"; 2596520fe55SH. Peter Anvin if (shndx < ehdr.e_shnum) { 2606520fe55SH. Peter Anvin name = sec_strtab + secs[shndx].shdr.sh_name; 2616520fe55SH. Peter Anvin } 2626520fe55SH. Peter Anvin else if (shndx == SHN_ABS) { 2636520fe55SH. Peter Anvin name = "ABSOLUTE"; 2646520fe55SH. Peter Anvin } 2656520fe55SH. Peter Anvin else if (shndx == SHN_COMMON) { 2666520fe55SH. Peter Anvin name = "COMMON"; 2676520fe55SH. Peter Anvin } 2686520fe55SH. Peter Anvin return name; 2696520fe55SH. Peter Anvin } 2706520fe55SH. Peter Anvin 271bf11655cSKees Cook static const char *sym_name(const char *sym_strtab, Elf_Sym *sym) 2726520fe55SH. Peter Anvin { 2736520fe55SH. Peter Anvin const char *name; 2746520fe55SH. Peter Anvin name = "<noname>"; 2756520fe55SH. Peter Anvin if (sym->st_name) { 2766520fe55SH. Peter Anvin name = sym_strtab + sym->st_name; 2776520fe55SH. Peter Anvin } 2786520fe55SH. Peter Anvin else { 2796520fe55SH. Peter Anvin name = sec_name(sym->st_shndx); 2806520fe55SH. Peter Anvin } 2816520fe55SH. Peter Anvin return name; 2826520fe55SH. Peter Anvin } 2836520fe55SH. Peter Anvin 2846520fe55SH. Peter Anvin 2856520fe55SH. Peter Anvin 2866520fe55SH. Peter Anvin #if BYTE_ORDER == LITTLE_ENDIAN 2876520fe55SH. Peter Anvin #define le16_to_cpu(val) (val) 2886520fe55SH. Peter Anvin #define le32_to_cpu(val) (val) 2896520fe55SH. Peter Anvin #endif 2906520fe55SH. Peter Anvin #if BYTE_ORDER == BIG_ENDIAN 2916520fe55SH. Peter Anvin #define le16_to_cpu(val) bswap_16(val) 2926520fe55SH. Peter Anvin #define le32_to_cpu(val) bswap_32(val) 2936520fe55SH. Peter Anvin #endif 2946520fe55SH. Peter Anvin 2956520fe55SH. Peter Anvin static uint16_t elf16_to_cpu(uint16_t val) 2966520fe55SH. Peter Anvin { 2976520fe55SH. Peter Anvin return le16_to_cpu(val); 2986520fe55SH. Peter Anvin } 2996520fe55SH. Peter Anvin 3006520fe55SH. Peter Anvin static uint32_t elf32_to_cpu(uint32_t val) 3016520fe55SH. Peter Anvin { 3026520fe55SH. Peter Anvin return le32_to_cpu(val); 3036520fe55SH. Peter Anvin } 3046520fe55SH. Peter Anvin 305bf11655cSKees Cook #define elf_half_to_cpu(x) elf16_to_cpu(x) 306bf11655cSKees Cook #define elf_word_to_cpu(x) elf32_to_cpu(x) 307bf11655cSKees Cook #define elf_addr_to_cpu(x) elf32_to_cpu(x) 308bf11655cSKees Cook #define elf_off_to_cpu(x) elf32_to_cpu(x) 309bf11655cSKees Cook #define elf_xword_to_cpu(x) elf32_to_cpu(x) 310bf11655cSKees Cook 3116520fe55SH. Peter Anvin static void read_ehdr(FILE *fp) 3126520fe55SH. Peter Anvin { 3136520fe55SH. Peter Anvin if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) { 3146520fe55SH. Peter Anvin die("Cannot read ELF header: %s\n", 3156520fe55SH. Peter Anvin strerror(errno)); 3166520fe55SH. Peter Anvin } 3176520fe55SH. Peter Anvin if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) { 3186520fe55SH. Peter Anvin die("No ELF magic\n"); 3196520fe55SH. Peter Anvin } 320bf11655cSKees Cook if (ehdr.e_ident[EI_CLASS] != ELF_CLASS) { 321bf11655cSKees Cook die("Not a %d bit executable\n", ELF_BITS); 3226520fe55SH. Peter Anvin } 3236520fe55SH. Peter Anvin if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) { 3246520fe55SH. Peter Anvin die("Not a LSB ELF executable\n"); 3256520fe55SH. Peter Anvin } 3266520fe55SH. Peter Anvin if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) { 3276520fe55SH. Peter Anvin die("Unknown ELF version\n"); 3286520fe55SH. Peter Anvin } 3296520fe55SH. Peter Anvin /* Convert the fields to native endian */ 330bf11655cSKees Cook ehdr.e_type = elf_half_to_cpu(ehdr.e_type); 331bf11655cSKees Cook ehdr.e_machine = elf_half_to_cpu(ehdr.e_machine); 332bf11655cSKees Cook ehdr.e_version = elf_word_to_cpu(ehdr.e_version); 333bf11655cSKees Cook ehdr.e_entry = elf_addr_to_cpu(ehdr.e_entry); 334bf11655cSKees Cook ehdr.e_phoff = elf_off_to_cpu(ehdr.e_phoff); 335bf11655cSKees Cook ehdr.e_shoff = elf_off_to_cpu(ehdr.e_shoff); 336bf11655cSKees Cook ehdr.e_flags = elf_word_to_cpu(ehdr.e_flags); 337bf11655cSKees Cook ehdr.e_ehsize = elf_half_to_cpu(ehdr.e_ehsize); 338bf11655cSKees Cook ehdr.e_phentsize = elf_half_to_cpu(ehdr.e_phentsize); 339bf11655cSKees Cook ehdr.e_phnum = elf_half_to_cpu(ehdr.e_phnum); 340bf11655cSKees Cook ehdr.e_shentsize = elf_half_to_cpu(ehdr.e_shentsize); 341bf11655cSKees Cook ehdr.e_shnum = elf_half_to_cpu(ehdr.e_shnum); 342bf11655cSKees Cook ehdr.e_shstrndx = elf_half_to_cpu(ehdr.e_shstrndx); 3436520fe55SH. Peter Anvin 3446520fe55SH. Peter Anvin if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN)) { 3456520fe55SH. Peter Anvin die("Unsupported ELF header type\n"); 3466520fe55SH. Peter Anvin } 347bf11655cSKees Cook if (ehdr.e_machine != ELF_MACHINE) { 348bf11655cSKees Cook die("Not for %s\n", ELF_MACHINE_NAME); 3496520fe55SH. Peter Anvin } 3506520fe55SH. Peter Anvin if (ehdr.e_version != EV_CURRENT) { 3516520fe55SH. Peter Anvin die("Unknown ELF version\n"); 3526520fe55SH. Peter Anvin } 353bf11655cSKees Cook if (ehdr.e_ehsize != sizeof(Elf_Ehdr)) { 3546520fe55SH. Peter Anvin die("Bad Elf header size\n"); 3556520fe55SH. Peter Anvin } 356bf11655cSKees Cook if (ehdr.e_phentsize != sizeof(Elf_Phdr)) { 3576520fe55SH. Peter Anvin die("Bad program header entry\n"); 3586520fe55SH. Peter Anvin } 359bf11655cSKees Cook if (ehdr.e_shentsize != sizeof(Elf_Shdr)) { 3606520fe55SH. Peter Anvin die("Bad section header entry\n"); 3616520fe55SH. Peter Anvin } 3626520fe55SH. Peter Anvin if (ehdr.e_shstrndx >= ehdr.e_shnum) { 3636520fe55SH. Peter Anvin die("String table index out of bounds\n"); 3646520fe55SH. Peter Anvin } 3656520fe55SH. Peter Anvin } 3666520fe55SH. Peter Anvin 3676520fe55SH. Peter Anvin static void read_shdrs(FILE *fp) 3686520fe55SH. Peter Anvin { 3696520fe55SH. Peter Anvin int i; 370bf11655cSKees Cook Elf_Shdr shdr; 3716520fe55SH. Peter Anvin 3726520fe55SH. Peter Anvin secs = calloc(ehdr.e_shnum, sizeof(struct section)); 3736520fe55SH. Peter Anvin if (!secs) { 3746520fe55SH. Peter Anvin die("Unable to allocate %d section headers\n", 3756520fe55SH. Peter Anvin ehdr.e_shnum); 3766520fe55SH. Peter Anvin } 3776520fe55SH. Peter Anvin if (fseek(fp, ehdr.e_shoff, SEEK_SET) < 0) { 3786520fe55SH. Peter Anvin die("Seek to %d failed: %s\n", 3796520fe55SH. Peter Anvin ehdr.e_shoff, strerror(errno)); 3806520fe55SH. Peter Anvin } 3816520fe55SH. Peter Anvin for (i = 0; i < ehdr.e_shnum; i++) { 3826520fe55SH. Peter Anvin struct section *sec = &secs[i]; 3836520fe55SH. Peter Anvin if (fread(&shdr, sizeof shdr, 1, fp) != 1) 3846520fe55SH. Peter Anvin die("Cannot read ELF section headers %d/%d: %s\n", 3856520fe55SH. Peter Anvin i, ehdr.e_shnum, strerror(errno)); 386bf11655cSKees Cook sec->shdr.sh_name = elf_word_to_cpu(shdr.sh_name); 387bf11655cSKees Cook sec->shdr.sh_type = elf_word_to_cpu(shdr.sh_type); 388bf11655cSKees Cook sec->shdr.sh_flags = elf_xword_to_cpu(shdr.sh_flags); 389bf11655cSKees Cook sec->shdr.sh_addr = elf_addr_to_cpu(shdr.sh_addr); 390bf11655cSKees Cook sec->shdr.sh_offset = elf_off_to_cpu(shdr.sh_offset); 391bf11655cSKees Cook sec->shdr.sh_size = elf_xword_to_cpu(shdr.sh_size); 392bf11655cSKees Cook sec->shdr.sh_link = elf_word_to_cpu(shdr.sh_link); 393bf11655cSKees Cook sec->shdr.sh_info = elf_word_to_cpu(shdr.sh_info); 394bf11655cSKees Cook sec->shdr.sh_addralign = elf_xword_to_cpu(shdr.sh_addralign); 395bf11655cSKees Cook sec->shdr.sh_entsize = elf_xword_to_cpu(shdr.sh_entsize); 3966520fe55SH. Peter Anvin if (sec->shdr.sh_link < ehdr.e_shnum) 3976520fe55SH. Peter Anvin sec->link = &secs[sec->shdr.sh_link]; 3986520fe55SH. Peter Anvin } 3996520fe55SH. Peter Anvin 4006520fe55SH. Peter Anvin } 4016520fe55SH. Peter Anvin 4026520fe55SH. Peter Anvin static void read_strtabs(FILE *fp) 4036520fe55SH. Peter Anvin { 4046520fe55SH. Peter Anvin int i; 4056520fe55SH. Peter Anvin for (i = 0; i < ehdr.e_shnum; i++) { 4066520fe55SH. Peter Anvin struct section *sec = &secs[i]; 4076520fe55SH. Peter Anvin if (sec->shdr.sh_type != SHT_STRTAB) { 4086520fe55SH. Peter Anvin continue; 4096520fe55SH. Peter Anvin } 4106520fe55SH. Peter Anvin sec->strtab = malloc(sec->shdr.sh_size); 4116520fe55SH. Peter Anvin if (!sec->strtab) { 4126520fe55SH. Peter Anvin die("malloc of %d bytes for strtab failed\n", 4136520fe55SH. Peter Anvin sec->shdr.sh_size); 4146520fe55SH. Peter Anvin } 4156520fe55SH. Peter Anvin if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { 4166520fe55SH. Peter Anvin die("Seek to %d failed: %s\n", 4176520fe55SH. Peter Anvin sec->shdr.sh_offset, strerror(errno)); 4186520fe55SH. Peter Anvin } 4196520fe55SH. Peter Anvin if (fread(sec->strtab, 1, sec->shdr.sh_size, fp) 4206520fe55SH. Peter Anvin != sec->shdr.sh_size) { 4216520fe55SH. Peter Anvin die("Cannot read symbol table: %s\n", 4226520fe55SH. Peter Anvin strerror(errno)); 4236520fe55SH. Peter Anvin } 4246520fe55SH. Peter Anvin } 4256520fe55SH. Peter Anvin } 4266520fe55SH. Peter Anvin 4276520fe55SH. Peter Anvin static void read_symtabs(FILE *fp) 4286520fe55SH. Peter Anvin { 4296520fe55SH. Peter Anvin int i,j; 4306520fe55SH. Peter Anvin for (i = 0; i < ehdr.e_shnum; i++) { 4316520fe55SH. Peter Anvin struct section *sec = &secs[i]; 4326520fe55SH. Peter Anvin if (sec->shdr.sh_type != SHT_SYMTAB) { 4336520fe55SH. Peter Anvin continue; 4346520fe55SH. Peter Anvin } 4356520fe55SH. Peter Anvin sec->symtab = malloc(sec->shdr.sh_size); 4366520fe55SH. Peter Anvin if (!sec->symtab) { 4376520fe55SH. Peter Anvin die("malloc of %d bytes for symtab failed\n", 4386520fe55SH. Peter Anvin sec->shdr.sh_size); 4396520fe55SH. Peter Anvin } 4406520fe55SH. Peter Anvin if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { 4416520fe55SH. Peter Anvin die("Seek to %d failed: %s\n", 4426520fe55SH. Peter Anvin sec->shdr.sh_offset, strerror(errno)); 4436520fe55SH. Peter Anvin } 4446520fe55SH. Peter Anvin if (fread(sec->symtab, 1, sec->shdr.sh_size, fp) 4456520fe55SH. Peter Anvin != sec->shdr.sh_size) { 4466520fe55SH. Peter Anvin die("Cannot read symbol table: %s\n", 4476520fe55SH. Peter Anvin strerror(errno)); 4486520fe55SH. Peter Anvin } 449bf11655cSKees Cook for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Sym); j++) { 450bf11655cSKees Cook Elf_Sym *sym = &sec->symtab[j]; 451bf11655cSKees Cook sym->st_name = elf_word_to_cpu(sym->st_name); 452bf11655cSKees Cook sym->st_value = elf_addr_to_cpu(sym->st_value); 453bf11655cSKees Cook sym->st_size = elf_xword_to_cpu(sym->st_size); 454bf11655cSKees Cook sym->st_shndx = elf_half_to_cpu(sym->st_shndx); 4556520fe55SH. Peter Anvin } 4566520fe55SH. Peter Anvin } 4576520fe55SH. Peter Anvin } 4586520fe55SH. Peter Anvin 4596520fe55SH. Peter Anvin 4606520fe55SH. Peter Anvin static void read_relocs(FILE *fp) 4616520fe55SH. Peter Anvin { 4626520fe55SH. Peter Anvin int i,j; 4636520fe55SH. Peter Anvin for (i = 0; i < ehdr.e_shnum; i++) { 4646520fe55SH. Peter Anvin struct section *sec = &secs[i]; 465bf11655cSKees Cook if (sec->shdr.sh_type != SHT_REL_TYPE) { 4666520fe55SH. Peter Anvin continue; 4676520fe55SH. Peter Anvin } 4686520fe55SH. Peter Anvin sec->reltab = malloc(sec->shdr.sh_size); 4696520fe55SH. Peter Anvin if (!sec->reltab) { 4706520fe55SH. Peter Anvin die("malloc of %d bytes for relocs failed\n", 4716520fe55SH. Peter Anvin sec->shdr.sh_size); 4726520fe55SH. Peter Anvin } 4736520fe55SH. Peter Anvin if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { 4746520fe55SH. Peter Anvin die("Seek to %d failed: %s\n", 4756520fe55SH. Peter Anvin sec->shdr.sh_offset, strerror(errno)); 4766520fe55SH. Peter Anvin } 4776520fe55SH. Peter Anvin if (fread(sec->reltab, 1, sec->shdr.sh_size, fp) 4786520fe55SH. Peter Anvin != sec->shdr.sh_size) { 4796520fe55SH. Peter Anvin die("Cannot read symbol table: %s\n", 4806520fe55SH. Peter Anvin strerror(errno)); 4816520fe55SH. Peter Anvin } 482bf11655cSKees Cook for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) { 483bf11655cSKees Cook Elf_Rel *rel = &sec->reltab[j]; 484bf11655cSKees Cook rel->r_offset = elf_addr_to_cpu(rel->r_offset); 485bf11655cSKees Cook rel->r_info = elf_xword_to_cpu(rel->r_info); 4866520fe55SH. Peter Anvin } 4876520fe55SH. Peter Anvin } 4886520fe55SH. Peter Anvin } 4896520fe55SH. Peter Anvin 4906520fe55SH. Peter Anvin 4916520fe55SH. Peter Anvin static void print_absolute_symbols(void) 4926520fe55SH. Peter Anvin { 4936520fe55SH. Peter Anvin int i; 4946520fe55SH. Peter Anvin printf("Absolute symbols\n"); 4956520fe55SH. Peter Anvin printf(" Num: Value Size Type Bind Visibility Name\n"); 4966520fe55SH. Peter Anvin for (i = 0; i < ehdr.e_shnum; i++) { 4976520fe55SH. Peter Anvin struct section *sec = &secs[i]; 4986520fe55SH. Peter Anvin char *sym_strtab; 4996520fe55SH. Peter Anvin int j; 5006520fe55SH. Peter Anvin 5016520fe55SH. Peter Anvin if (sec->shdr.sh_type != SHT_SYMTAB) { 5026520fe55SH. Peter Anvin continue; 5036520fe55SH. Peter Anvin } 5046520fe55SH. Peter Anvin sym_strtab = sec->link->strtab; 505bf11655cSKees Cook for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Sym); j++) { 506bf11655cSKees Cook Elf_Sym *sym; 5076520fe55SH. Peter Anvin const char *name; 5086520fe55SH. Peter Anvin sym = &sec->symtab[j]; 5096520fe55SH. Peter Anvin name = sym_name(sym_strtab, sym); 5106520fe55SH. Peter Anvin if (sym->st_shndx != SHN_ABS) { 5116520fe55SH. Peter Anvin continue; 5126520fe55SH. Peter Anvin } 5136520fe55SH. Peter Anvin printf("%5d %08x %5d %10s %10s %12s %s\n", 5146520fe55SH. Peter Anvin j, sym->st_value, sym->st_size, 515bf11655cSKees Cook sym_type(ELF_ST_TYPE(sym->st_info)), 516bf11655cSKees Cook sym_bind(ELF_ST_BIND(sym->st_info)), 517bf11655cSKees Cook sym_visibility(ELF_ST_VISIBILITY(sym->st_other)), 5186520fe55SH. Peter Anvin name); 5196520fe55SH. Peter Anvin } 5206520fe55SH. Peter Anvin } 5216520fe55SH. Peter Anvin printf("\n"); 5226520fe55SH. Peter Anvin } 5236520fe55SH. Peter Anvin 5246520fe55SH. Peter Anvin static void print_absolute_relocs(void) 5256520fe55SH. Peter Anvin { 5266520fe55SH. Peter Anvin int i, printed = 0; 5276520fe55SH. Peter Anvin 5286520fe55SH. Peter Anvin for (i = 0; i < ehdr.e_shnum; i++) { 5296520fe55SH. Peter Anvin struct section *sec = &secs[i]; 5306520fe55SH. Peter Anvin struct section *sec_applies, *sec_symtab; 5316520fe55SH. Peter Anvin char *sym_strtab; 532bf11655cSKees Cook Elf_Sym *sh_symtab; 5336520fe55SH. Peter Anvin int j; 534bf11655cSKees Cook if (sec->shdr.sh_type != SHT_REL_TYPE) { 5356520fe55SH. Peter Anvin continue; 5366520fe55SH. Peter Anvin } 5376520fe55SH. Peter Anvin sec_symtab = sec->link; 5386520fe55SH. Peter Anvin sec_applies = &secs[sec->shdr.sh_info]; 5396520fe55SH. Peter Anvin if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) { 5406520fe55SH. Peter Anvin continue; 5416520fe55SH. Peter Anvin } 5426520fe55SH. Peter Anvin sh_symtab = sec_symtab->symtab; 5436520fe55SH. Peter Anvin sym_strtab = sec_symtab->link->strtab; 544bf11655cSKees Cook for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) { 545bf11655cSKees Cook Elf_Rel *rel; 546bf11655cSKees Cook Elf_Sym *sym; 5476520fe55SH. Peter Anvin const char *name; 5486520fe55SH. Peter Anvin rel = &sec->reltab[j]; 549bf11655cSKees Cook sym = &sh_symtab[ELF_R_SYM(rel->r_info)]; 5506520fe55SH. Peter Anvin name = sym_name(sym_strtab, sym); 5516520fe55SH. Peter Anvin if (sym->st_shndx != SHN_ABS) { 5526520fe55SH. Peter Anvin continue; 5536520fe55SH. Peter Anvin } 5546520fe55SH. Peter Anvin 5556520fe55SH. Peter Anvin /* Absolute symbols are not relocated if bzImage is 5566520fe55SH. Peter Anvin * loaded at a non-compiled address. Display a warning 5576520fe55SH. Peter Anvin * to user at compile time about the absolute 5586520fe55SH. Peter Anvin * relocations present. 5596520fe55SH. Peter Anvin * 5606520fe55SH. Peter Anvin * User need to audit the code to make sure 5616520fe55SH. Peter Anvin * some symbols which should have been section 5626520fe55SH. Peter Anvin * relative have not become absolute because of some 5636520fe55SH. Peter Anvin * linker optimization or wrong programming usage. 5646520fe55SH. Peter Anvin * 5656520fe55SH. Peter Anvin * Before warning check if this absolute symbol 5666520fe55SH. Peter Anvin * relocation is harmless. 5676520fe55SH. Peter Anvin */ 5686520fe55SH. Peter Anvin if (is_reloc(S_ABS, name) || is_reloc(S_REL, name)) 5696520fe55SH. Peter Anvin continue; 5706520fe55SH. Peter Anvin 5716520fe55SH. Peter Anvin if (!printed) { 5726520fe55SH. Peter Anvin printf("WARNING: Absolute relocations" 5736520fe55SH. Peter Anvin " present\n"); 5746520fe55SH. Peter Anvin printf("Offset Info Type Sym.Value " 5756520fe55SH. Peter Anvin "Sym.Name\n"); 5766520fe55SH. Peter Anvin printed = 1; 5776520fe55SH. Peter Anvin } 5786520fe55SH. Peter Anvin 5796520fe55SH. Peter Anvin printf("%08x %08x %10s %08x %s\n", 5806520fe55SH. Peter Anvin rel->r_offset, 5816520fe55SH. Peter Anvin rel->r_info, 582bf11655cSKees Cook rel_type(ELF_R_TYPE(rel->r_info)), 5836520fe55SH. Peter Anvin sym->st_value, 5846520fe55SH. Peter Anvin name); 5856520fe55SH. Peter Anvin } 5866520fe55SH. Peter Anvin } 5876520fe55SH. Peter Anvin 5886520fe55SH. Peter Anvin if (printed) 5896520fe55SH. Peter Anvin printf("\n"); 5906520fe55SH. Peter Anvin } 5916520fe55SH. Peter Anvin 592*5d442e63SKees Cook static void add_reloc(struct relocs *r, uint32_t offset) 593*5d442e63SKees Cook { 594*5d442e63SKees Cook if (r->count == r->size) { 595*5d442e63SKees Cook unsigned long newsize = r->size + 50000; 596*5d442e63SKees Cook void *mem = realloc(r->offset, newsize * sizeof(r->offset[0])); 597*5d442e63SKees Cook 598*5d442e63SKees Cook if (!mem) 599*5d442e63SKees Cook die("realloc of %ld entries for relocs failed\n", 600*5d442e63SKees Cook newsize); 601*5d442e63SKees Cook r->offset = mem; 602*5d442e63SKees Cook r->size = newsize; 603*5d442e63SKees Cook } 604*5d442e63SKees Cook r->offset[r->count++] = offset; 605*5d442e63SKees Cook } 606*5d442e63SKees Cook 607*5d442e63SKees Cook static void walk_relocs(int (*process)(struct section *sec, Elf_Rel *rel, 608*5d442e63SKees Cook Elf_Sym *sym, const char *symname)) 6096520fe55SH. Peter Anvin { 6106520fe55SH. Peter Anvin int i; 6116520fe55SH. Peter Anvin /* Walk through the relocations */ 6126520fe55SH. Peter Anvin for (i = 0; i < ehdr.e_shnum; i++) { 6136520fe55SH. Peter Anvin char *sym_strtab; 614bf11655cSKees Cook Elf_Sym *sh_symtab; 6156520fe55SH. Peter Anvin struct section *sec_applies, *sec_symtab; 6166520fe55SH. Peter Anvin int j; 6176520fe55SH. Peter Anvin struct section *sec = &secs[i]; 6186520fe55SH. Peter Anvin 619bf11655cSKees Cook if (sec->shdr.sh_type != SHT_REL_TYPE) { 6206520fe55SH. Peter Anvin continue; 6216520fe55SH. Peter Anvin } 6226520fe55SH. Peter Anvin sec_symtab = sec->link; 6236520fe55SH. Peter Anvin sec_applies = &secs[sec->shdr.sh_info]; 6246520fe55SH. Peter Anvin if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) { 6256520fe55SH. Peter Anvin continue; 6266520fe55SH. Peter Anvin } 6276520fe55SH. Peter Anvin sh_symtab = sec_symtab->symtab; 6286520fe55SH. Peter Anvin sym_strtab = sec_symtab->link->strtab; 629bf11655cSKees Cook for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) { 630*5d442e63SKees Cook Elf_Rel *rel = &sec->reltab[j]; 631*5d442e63SKees Cook Elf_Sym *sym = &sh_symtab[ELF_R_SYM(rel->r_info)]; 632*5d442e63SKees Cook const char *symname = sym_name(sym_strtab, sym); 63324ab82bdSH. Peter Anvin 634*5d442e63SKees Cook process(sec, rel, sym, symname); 635*5d442e63SKees Cook } 636*5d442e63SKees Cook } 637*5d442e63SKees Cook } 6386520fe55SH. Peter Anvin 639*5d442e63SKees Cook static int do_reloc(struct section *sec, Elf_Rel *rel, Elf_Sym *sym, 640*5d442e63SKees Cook const char *symname) 641*5d442e63SKees Cook { 642*5d442e63SKees Cook unsigned r_type = ELF32_R_TYPE(rel->r_info); 643*5d442e63SKees Cook int shn_abs = (sym->st_shndx == SHN_ABS) && !is_reloc(S_REL, symname); 64424ab82bdSH. Peter Anvin 6456520fe55SH. Peter Anvin switch (r_type) { 6466520fe55SH. Peter Anvin case R_386_NONE: 6476520fe55SH. Peter Anvin case R_386_PC32: 6486520fe55SH. Peter Anvin case R_386_PC16: 6496520fe55SH. Peter Anvin case R_386_PC8: 6506520fe55SH. Peter Anvin /* 651*5d442e63SKees Cook * NONE can be ignored and PC relative relocations don't 652*5d442e63SKees Cook * need to be adjusted. 6536520fe55SH. Peter Anvin */ 6546520fe55SH. Peter Anvin break; 6556520fe55SH. Peter Anvin 656*5d442e63SKees Cook case R_386_32: 65724ab82bdSH. Peter Anvin if (shn_abs) { 658*5d442e63SKees Cook /* 659*5d442e63SKees Cook * Whitelisted absolute symbols do not require 660*5d442e63SKees Cook * relocation. 661*5d442e63SKees Cook */ 6626520fe55SH. Peter Anvin if (is_reloc(S_ABS, symname)) 6636520fe55SH. Peter Anvin break; 6646520fe55SH. Peter Anvin 665*5d442e63SKees Cook die("Invalid absolute %s relocation: %s\n", 666*5d442e63SKees Cook rel_type(r_type), symname); 6676520fe55SH. Peter Anvin break; 6686520fe55SH. Peter Anvin } 669*5d442e63SKees Cook 670*5d442e63SKees Cook add_reloc(&relocs32, rel->r_offset); 6716520fe55SH. Peter Anvin break; 672*5d442e63SKees Cook 6736520fe55SH. Peter Anvin default: 6746520fe55SH. Peter Anvin die("Unsupported relocation type: %s (%d)\n", 6756520fe55SH. Peter Anvin rel_type(r_type), r_type); 6766520fe55SH. Peter Anvin break; 677*5d442e63SKees Cook } 678*5d442e63SKees Cook 679*5d442e63SKees Cook return 0; 680*5d442e63SKees Cook } 681*5d442e63SKees Cook 682*5d442e63SKees Cook static int do_reloc_real(struct section *sec, Elf_Rel *rel, Elf_Sym *sym, 683*5d442e63SKees Cook const char *symname) 684*5d442e63SKees Cook { 685*5d442e63SKees Cook unsigned r_type = ELF32_R_TYPE(rel->r_info); 686*5d442e63SKees Cook int shn_abs = (sym->st_shndx == SHN_ABS) && !is_reloc(S_REL, symname); 687*5d442e63SKees Cook 688*5d442e63SKees Cook switch (r_type) { 689*5d442e63SKees Cook case R_386_NONE: 690*5d442e63SKees Cook case R_386_PC32: 691*5d442e63SKees Cook case R_386_PC16: 692*5d442e63SKees Cook case R_386_PC8: 693*5d442e63SKees Cook /* 694*5d442e63SKees Cook * NONE can be ignored and PC relative relocations don't 695*5d442e63SKees Cook * need to be adjusted. 696*5d442e63SKees Cook */ 697*5d442e63SKees Cook break; 698*5d442e63SKees Cook 699*5d442e63SKees Cook case R_386_16: 700*5d442e63SKees Cook if (shn_abs) { 701*5d442e63SKees Cook /* 702*5d442e63SKees Cook * Whitelisted absolute symbols do not require 703*5d442e63SKees Cook * relocation. 704*5d442e63SKees Cook */ 705*5d442e63SKees Cook if (is_reloc(S_ABS, symname)) 706*5d442e63SKees Cook break; 707*5d442e63SKees Cook 708*5d442e63SKees Cook if (is_reloc(S_SEG, symname)) { 709*5d442e63SKees Cook add_reloc(&relocs16, rel->r_offset); 710*5d442e63SKees Cook break; 711*5d442e63SKees Cook } 712*5d442e63SKees Cook } else { 713*5d442e63SKees Cook if (!is_reloc(S_LIN, symname)) 714*5d442e63SKees Cook break; 715*5d442e63SKees Cook } 71624ab82bdSH. Peter Anvin die("Invalid %s %s relocation: %s\n", 71724ab82bdSH. Peter Anvin shn_abs ? "absolute" : "relative", 7186520fe55SH. Peter Anvin rel_type(r_type), symname); 719*5d442e63SKees Cook break; 720*5d442e63SKees Cook 721*5d442e63SKees Cook case R_386_32: 722*5d442e63SKees Cook if (shn_abs) { 723*5d442e63SKees Cook /* 724*5d442e63SKees Cook * Whitelisted absolute symbols do not require 725*5d442e63SKees Cook * relocation. 726*5d442e63SKees Cook */ 727*5d442e63SKees Cook if (is_reloc(S_ABS, symname)) 728*5d442e63SKees Cook break; 729*5d442e63SKees Cook 730*5d442e63SKees Cook if (is_reloc(S_REL, symname)) { 731*5d442e63SKees Cook add_reloc(&relocs32, rel->r_offset); 732*5d442e63SKees Cook break; 7336520fe55SH. Peter Anvin } 734*5d442e63SKees Cook } else { 735*5d442e63SKees Cook if (is_reloc(S_LIN, symname)) 736*5d442e63SKees Cook add_reloc(&relocs32, rel->r_offset); 737*5d442e63SKees Cook break; 7386520fe55SH. Peter Anvin } 739*5d442e63SKees Cook die("Invalid %s %s relocation: %s\n", 740*5d442e63SKees Cook shn_abs ? "absolute" : "relative", 741*5d442e63SKees Cook rel_type(r_type), symname); 742*5d442e63SKees Cook break; 743*5d442e63SKees Cook 744*5d442e63SKees Cook default: 745*5d442e63SKees Cook die("Unsupported relocation type: %s (%d)\n", 746*5d442e63SKees Cook rel_type(r_type), r_type); 747*5d442e63SKees Cook break; 7486520fe55SH. Peter Anvin } 7496520fe55SH. Peter Anvin 750*5d442e63SKees Cook return 0; 7516520fe55SH. Peter Anvin } 7526520fe55SH. Peter Anvin 7536520fe55SH. Peter Anvin static int cmp_relocs(const void *va, const void *vb) 7546520fe55SH. Peter Anvin { 755*5d442e63SKees Cook const uint32_t *a, *b; 7566520fe55SH. Peter Anvin a = va; b = vb; 7576520fe55SH. Peter Anvin return (*a == *b)? 0 : (*a > *b)? 1 : -1; 7586520fe55SH. Peter Anvin } 7596520fe55SH. Peter Anvin 760*5d442e63SKees Cook static void sort_relocs(struct relocs *r) 761*5d442e63SKees Cook { 762*5d442e63SKees Cook qsort(r->offset, r->count, sizeof(r->offset[0]), cmp_relocs); 763*5d442e63SKees Cook } 764*5d442e63SKees Cook 765*5d442e63SKees Cook static int write32(uint32_t v, FILE *f) 7666520fe55SH. Peter Anvin { 7676520fe55SH. Peter Anvin unsigned char buf[4]; 7686520fe55SH. Peter Anvin 7696520fe55SH. Peter Anvin put_unaligned_le32(v, buf); 7706520fe55SH. Peter Anvin return fwrite(buf, 1, 4, f) == 4 ? 0 : -1; 7716520fe55SH. Peter Anvin } 7726520fe55SH. Peter Anvin 773*5d442e63SKees Cook static int write32_as_text(uint32_t v, FILE *f) 774*5d442e63SKees Cook { 775*5d442e63SKees Cook return fprintf(f, "\t.long 0x%08"PRIx32"\n", v) > 0 ? 0 : -1; 776*5d442e63SKees Cook } 777*5d442e63SKees Cook 7786520fe55SH. Peter Anvin static void emit_relocs(int as_text, int use_real_mode) 7796520fe55SH. Peter Anvin { 7806520fe55SH. Peter Anvin int i; 781*5d442e63SKees Cook int (*write_reloc)(uint32_t, FILE *) = write32; 7826520fe55SH. Peter Anvin 7836520fe55SH. Peter Anvin /* Collect up the relocations */ 784*5d442e63SKees Cook walk_relocs(use_real_mode ? do_reloc_real : do_reloc); 7856520fe55SH. Peter Anvin 786*5d442e63SKees Cook if (relocs16.count && !use_real_mode) 7876520fe55SH. Peter Anvin die("Segment relocations found but --realmode not specified\n"); 7886520fe55SH. Peter Anvin 7896520fe55SH. Peter Anvin /* Order the relocations for more efficient processing */ 790*5d442e63SKees Cook sort_relocs(&relocs16); 791*5d442e63SKees Cook sort_relocs(&relocs32); 7926520fe55SH. Peter Anvin 7936520fe55SH. Peter Anvin /* Print the relocations */ 7946520fe55SH. Peter Anvin if (as_text) { 7956520fe55SH. Peter Anvin /* Print the relocations in a form suitable that 7966520fe55SH. Peter Anvin * gas will like. 7976520fe55SH. Peter Anvin */ 7986520fe55SH. Peter Anvin printf(".section \".data.reloc\",\"a\"\n"); 7996520fe55SH. Peter Anvin printf(".balign 4\n"); 800*5d442e63SKees Cook write_reloc = write32_as_text; 8016520fe55SH. Peter Anvin } 802*5d442e63SKees Cook 803*5d442e63SKees Cook if (use_real_mode) { 804*5d442e63SKees Cook write_reloc(relocs16.count, stdout); 805*5d442e63SKees Cook for (i = 0; i < relocs16.count; i++) 806*5d442e63SKees Cook write_reloc(relocs16.offset[i], stdout); 807*5d442e63SKees Cook 808*5d442e63SKees Cook write_reloc(relocs32.count, stdout); 809*5d442e63SKees Cook for (i = 0; i < relocs32.count; i++) 810*5d442e63SKees Cook write_reloc(relocs32.offset[i], stdout); 8116520fe55SH. Peter Anvin } else { 8126520fe55SH. Peter Anvin /* Print a stop */ 813*5d442e63SKees Cook write_reloc(0, stdout); 8146520fe55SH. Peter Anvin 8156520fe55SH. Peter Anvin /* Now print each relocation */ 816*5d442e63SKees Cook for (i = 0; i < relocs32.count; i++) 817*5d442e63SKees Cook write_reloc(relocs32.offset[i], stdout); 8186520fe55SH. Peter Anvin } 8196520fe55SH. Peter Anvin } 8206520fe55SH. Peter Anvin 8216520fe55SH. Peter Anvin static void usage(void) 8226520fe55SH. Peter Anvin { 8236520fe55SH. Peter Anvin die("relocs [--abs-syms|--abs-relocs|--text|--realmode] vmlinux\n"); 8246520fe55SH. Peter Anvin } 8256520fe55SH. Peter Anvin 8266520fe55SH. Peter Anvin int main(int argc, char **argv) 8276520fe55SH. Peter Anvin { 8286520fe55SH. Peter Anvin int show_absolute_syms, show_absolute_relocs; 8296520fe55SH. Peter Anvin int as_text, use_real_mode; 8306520fe55SH. Peter Anvin const char *fname; 8316520fe55SH. Peter Anvin FILE *fp; 8326520fe55SH. Peter Anvin int i; 8336520fe55SH. Peter Anvin 8346520fe55SH. Peter Anvin show_absolute_syms = 0; 8356520fe55SH. Peter Anvin show_absolute_relocs = 0; 8366520fe55SH. Peter Anvin as_text = 0; 8376520fe55SH. Peter Anvin use_real_mode = 0; 8386520fe55SH. Peter Anvin fname = NULL; 8396520fe55SH. Peter Anvin for (i = 1; i < argc; i++) { 8406520fe55SH. Peter Anvin char *arg = argv[i]; 8416520fe55SH. Peter Anvin if (*arg == '-') { 8426520fe55SH. Peter Anvin if (strcmp(arg, "--abs-syms") == 0) { 8436520fe55SH. Peter Anvin show_absolute_syms = 1; 8446520fe55SH. Peter Anvin continue; 8456520fe55SH. Peter Anvin } 8466520fe55SH. Peter Anvin if (strcmp(arg, "--abs-relocs") == 0) { 8476520fe55SH. Peter Anvin show_absolute_relocs = 1; 8486520fe55SH. Peter Anvin continue; 8496520fe55SH. Peter Anvin } 8506520fe55SH. Peter Anvin if (strcmp(arg, "--text") == 0) { 8516520fe55SH. Peter Anvin as_text = 1; 8526520fe55SH. Peter Anvin continue; 8536520fe55SH. Peter Anvin } 8546520fe55SH. Peter Anvin if (strcmp(arg, "--realmode") == 0) { 8556520fe55SH. Peter Anvin use_real_mode = 1; 8566520fe55SH. Peter Anvin continue; 8576520fe55SH. Peter Anvin } 8586520fe55SH. Peter Anvin } 8596520fe55SH. Peter Anvin else if (!fname) { 8606520fe55SH. Peter Anvin fname = arg; 8616520fe55SH. Peter Anvin continue; 8626520fe55SH. Peter Anvin } 8636520fe55SH. Peter Anvin usage(); 8646520fe55SH. Peter Anvin } 8656520fe55SH. Peter Anvin if (!fname) { 8666520fe55SH. Peter Anvin usage(); 8676520fe55SH. Peter Anvin } 8686520fe55SH. Peter Anvin regex_init(use_real_mode); 8696520fe55SH. Peter Anvin fp = fopen(fname, "r"); 8706520fe55SH. Peter Anvin if (!fp) { 8716520fe55SH. Peter Anvin die("Cannot open %s: %s\n", 8726520fe55SH. Peter Anvin fname, strerror(errno)); 8736520fe55SH. Peter Anvin } 8746520fe55SH. Peter Anvin read_ehdr(fp); 8756520fe55SH. Peter Anvin read_shdrs(fp); 8766520fe55SH. Peter Anvin read_strtabs(fp); 8776520fe55SH. Peter Anvin read_symtabs(fp); 8786520fe55SH. Peter Anvin read_relocs(fp); 8796520fe55SH. Peter Anvin if (show_absolute_syms) { 8806520fe55SH. Peter Anvin print_absolute_symbols(); 88165315d48SCong Ding goto out; 8826520fe55SH. Peter Anvin } 8836520fe55SH. Peter Anvin if (show_absolute_relocs) { 8846520fe55SH. Peter Anvin print_absolute_relocs(); 88565315d48SCong Ding goto out; 8866520fe55SH. Peter Anvin } 8876520fe55SH. Peter Anvin emit_relocs(as_text, use_real_mode); 88865315d48SCong Ding out: 88965315d48SCong Ding fclose(fp); 8906520fe55SH. Peter Anvin return 0; 8916520fe55SH. Peter Anvin } 892