1c889ba80SH. Peter Anvin /* This is included from relocs_32/64.c */ 26520fe55SH. Peter Anvin 3bf11655cSKees Cook #define ElfW(type) _ElfW(ELF_BITS, type) 4bf11655cSKees Cook #define _ElfW(bits, type) __ElfW(bits, type) 5bf11655cSKees Cook #define __ElfW(bits, type) Elf##bits##_##type 6bf11655cSKees Cook 7946166afSKees Cook #define Elf_Addr ElfW(Addr) 8bf11655cSKees Cook #define Elf_Ehdr ElfW(Ehdr) 9bf11655cSKees Cook #define Elf_Phdr ElfW(Phdr) 10bf11655cSKees Cook #define Elf_Shdr ElfW(Shdr) 11bf11655cSKees Cook #define Elf_Sym ElfW(Sym) 12bf11655cSKees Cook 13bf11655cSKees Cook static Elf_Ehdr ehdr; 145d442e63SKees Cook 155d442e63SKees Cook struct relocs { 165d442e63SKees Cook uint32_t *offset; 175d442e63SKees Cook unsigned long count; 185d442e63SKees Cook unsigned long size; 195d442e63SKees Cook }; 205d442e63SKees Cook 215d442e63SKees Cook static struct relocs relocs16; 225d442e63SKees Cook static struct relocs relocs32; 23946166afSKees Cook static struct relocs relocs64; 246520fe55SH. Peter Anvin 256520fe55SH. Peter Anvin struct section { 26bf11655cSKees Cook Elf_Shdr shdr; 276520fe55SH. Peter Anvin struct section *link; 28bf11655cSKees Cook Elf_Sym *symtab; 29bf11655cSKees Cook Elf_Rel *reltab; 306520fe55SH. Peter Anvin char *strtab; 316520fe55SH. Peter Anvin }; 326520fe55SH. Peter Anvin static struct section *secs; 336520fe55SH. Peter Anvin 346520fe55SH. Peter Anvin static const char * const sym_regex_kernel[S_NSYMTYPES] = { 356520fe55SH. Peter Anvin /* 366520fe55SH. Peter Anvin * Following symbols have been audited. There values are constant and do 376520fe55SH. Peter Anvin * not change if bzImage is loaded at a different physical address than 386520fe55SH. Peter Anvin * the address for which it has been compiled. Don't warn user about 396520fe55SH. Peter Anvin * absolute relocations present w.r.t these symbols. 406520fe55SH. Peter Anvin */ 416520fe55SH. Peter Anvin [S_ABS] = 426520fe55SH. Peter Anvin "^(xen_irq_disable_direct_reloc$|" 436520fe55SH. Peter Anvin "xen_save_fl_direct_reloc$|" 446520fe55SH. Peter Anvin "VDSO|" 456520fe55SH. Peter Anvin "__crc_)", 466520fe55SH. Peter Anvin 476520fe55SH. Peter Anvin /* 486520fe55SH. Peter Anvin * These symbols are known to be relative, even if the linker marks them 496520fe55SH. Peter Anvin * as absolute (typically defined outside any section in the linker script.) 506520fe55SH. Peter Anvin */ 516520fe55SH. Peter Anvin [S_REL] = 52a3e854d9SH. Peter Anvin "^(__init_(begin|end)|" 53a3e854d9SH. Peter Anvin "__x86_cpu_dev_(start|end)|" 54a3e854d9SH. Peter Anvin "(__parainstructions|__alt_instructions)(|_end)|" 55a3e854d9SH. Peter Anvin "(__iommu_table|__apicdrivers|__smp_locks)(|_end)|" 56fd952815SH. Peter Anvin "__(start|end)_pci_.*|" 57fd952815SH. Peter Anvin "__(start|end)_builtin_fw|" 58fd952815SH. Peter Anvin "__(start|stop)___ksymtab(|_gpl|_unused|_unused_gpl|_gpl_future)|" 59fd952815SH. Peter Anvin "__(start|stop)___kcrctab(|_gpl|_unused|_unused_gpl|_gpl_future)|" 60fd952815SH. Peter Anvin "__(start|stop)___param|" 61fd952815SH. Peter Anvin "__(start|stop)___modver|" 62fd952815SH. Peter Anvin "__(start|stop)___bug_table|" 63fd952815SH. Peter Anvin "__tracedata_(start|end)|" 64fd952815SH. Peter Anvin "__(start|stop)_notes|" 65fd952815SH. Peter Anvin "__end_rodata|" 66fd952815SH. Peter Anvin "__initramfs_start|" 67ea17e741SH. Peter Anvin "(jiffies|jiffies_64)|" 68c889ba80SH. Peter Anvin #if ELF_BITS == 64 69946166afSKees Cook "__per_cpu_load|" 70946166afSKees Cook "init_per_cpu__.*|" 71946166afSKees Cook "__end_rodata_hpage_align|" 72b1983b0aSKees Cook "__vvar_page|" 73946166afSKees Cook #endif 74a3e854d9SH. Peter Anvin "_end)$" 756520fe55SH. Peter Anvin }; 766520fe55SH. Peter Anvin 776520fe55SH. Peter Anvin 786520fe55SH. Peter Anvin static const char * const sym_regex_realmode[S_NSYMTYPES] = { 796520fe55SH. Peter Anvin /* 80f2604c14SJarkko Sakkinen * These symbols are known to be relative, even if the linker marks them 81f2604c14SJarkko Sakkinen * as absolute (typically defined outside any section in the linker script.) 82f2604c14SJarkko Sakkinen */ 83f2604c14SJarkko Sakkinen [S_REL] = 84f2604c14SJarkko Sakkinen "^pa_", 85f2604c14SJarkko Sakkinen 86f2604c14SJarkko Sakkinen /* 876520fe55SH. Peter Anvin * These are 16-bit segment symbols when compiling 16-bit code. 886520fe55SH. Peter Anvin */ 896520fe55SH. Peter Anvin [S_SEG] = 906520fe55SH. Peter Anvin "^real_mode_seg$", 916520fe55SH. Peter Anvin 926520fe55SH. Peter Anvin /* 936520fe55SH. Peter Anvin * These are offsets belonging to segments, as opposed to linear addresses, 946520fe55SH. Peter Anvin * when compiling 16-bit code. 956520fe55SH. Peter Anvin */ 966520fe55SH. Peter Anvin [S_LIN] = 976520fe55SH. Peter Anvin "^pa_", 986520fe55SH. Peter Anvin }; 996520fe55SH. Peter Anvin 1006520fe55SH. Peter Anvin static const char * const *sym_regex; 1016520fe55SH. Peter Anvin 1026520fe55SH. Peter Anvin static regex_t sym_regex_c[S_NSYMTYPES]; 1036520fe55SH. Peter Anvin static int is_reloc(enum symtype type, const char *sym_name) 1046520fe55SH. Peter Anvin { 1056520fe55SH. Peter Anvin return sym_regex[type] && 1066520fe55SH. Peter Anvin !regexec(&sym_regex_c[type], sym_name, 0, NULL, 0); 1076520fe55SH. Peter Anvin } 1086520fe55SH. Peter Anvin 1096520fe55SH. Peter Anvin static void regex_init(int use_real_mode) 1106520fe55SH. Peter Anvin { 1116520fe55SH. Peter Anvin char errbuf[128]; 1126520fe55SH. Peter Anvin int err; 1136520fe55SH. Peter Anvin int i; 1146520fe55SH. Peter Anvin 1156520fe55SH. Peter Anvin if (use_real_mode) 1166520fe55SH. Peter Anvin sym_regex = sym_regex_realmode; 1176520fe55SH. Peter Anvin else 1186520fe55SH. Peter Anvin sym_regex = sym_regex_kernel; 1196520fe55SH. Peter Anvin 1206520fe55SH. Peter Anvin for (i = 0; i < S_NSYMTYPES; i++) { 1216520fe55SH. Peter Anvin if (!sym_regex[i]) 1226520fe55SH. Peter Anvin continue; 1236520fe55SH. Peter Anvin 1246520fe55SH. Peter Anvin err = regcomp(&sym_regex_c[i], sym_regex[i], 1256520fe55SH. Peter Anvin REG_EXTENDED|REG_NOSUB); 1266520fe55SH. Peter Anvin 1276520fe55SH. Peter Anvin if (err) { 1286520fe55SH. Peter Anvin regerror(err, &sym_regex_c[i], errbuf, sizeof errbuf); 1296520fe55SH. Peter Anvin die("%s", errbuf); 1306520fe55SH. Peter Anvin } 1316520fe55SH. Peter Anvin } 1326520fe55SH. Peter Anvin } 1336520fe55SH. Peter Anvin 1346520fe55SH. Peter Anvin static const char *sym_type(unsigned type) 1356520fe55SH. Peter Anvin { 1366520fe55SH. Peter Anvin static const char *type_name[] = { 1376520fe55SH. Peter Anvin #define SYM_TYPE(X) [X] = #X 1386520fe55SH. Peter Anvin SYM_TYPE(STT_NOTYPE), 1396520fe55SH. Peter Anvin SYM_TYPE(STT_OBJECT), 1406520fe55SH. Peter Anvin SYM_TYPE(STT_FUNC), 1416520fe55SH. Peter Anvin SYM_TYPE(STT_SECTION), 1426520fe55SH. Peter Anvin SYM_TYPE(STT_FILE), 1436520fe55SH. Peter Anvin SYM_TYPE(STT_COMMON), 1446520fe55SH. Peter Anvin SYM_TYPE(STT_TLS), 1456520fe55SH. Peter Anvin #undef SYM_TYPE 1466520fe55SH. Peter Anvin }; 1476520fe55SH. Peter Anvin const char *name = "unknown sym type name"; 1486520fe55SH. Peter Anvin if (type < ARRAY_SIZE(type_name)) { 1496520fe55SH. Peter Anvin name = type_name[type]; 1506520fe55SH. Peter Anvin } 1516520fe55SH. Peter Anvin return name; 1526520fe55SH. Peter Anvin } 1536520fe55SH. Peter Anvin 1546520fe55SH. Peter Anvin static const char *sym_bind(unsigned bind) 1556520fe55SH. Peter Anvin { 1566520fe55SH. Peter Anvin static const char *bind_name[] = { 1576520fe55SH. Peter Anvin #define SYM_BIND(X) [X] = #X 1586520fe55SH. Peter Anvin SYM_BIND(STB_LOCAL), 1596520fe55SH. Peter Anvin SYM_BIND(STB_GLOBAL), 1606520fe55SH. Peter Anvin SYM_BIND(STB_WEAK), 1616520fe55SH. Peter Anvin #undef SYM_BIND 1626520fe55SH. Peter Anvin }; 1636520fe55SH. Peter Anvin const char *name = "unknown sym bind name"; 1646520fe55SH. Peter Anvin if (bind < ARRAY_SIZE(bind_name)) { 1656520fe55SH. Peter Anvin name = bind_name[bind]; 1666520fe55SH. Peter Anvin } 1676520fe55SH. Peter Anvin return name; 1686520fe55SH. Peter Anvin } 1696520fe55SH. Peter Anvin 1706520fe55SH. Peter Anvin static const char *sym_visibility(unsigned visibility) 1716520fe55SH. Peter Anvin { 1726520fe55SH. Peter Anvin static const char *visibility_name[] = { 1736520fe55SH. Peter Anvin #define SYM_VISIBILITY(X) [X] = #X 1746520fe55SH. Peter Anvin SYM_VISIBILITY(STV_DEFAULT), 1756520fe55SH. Peter Anvin SYM_VISIBILITY(STV_INTERNAL), 1766520fe55SH. Peter Anvin SYM_VISIBILITY(STV_HIDDEN), 1776520fe55SH. Peter Anvin SYM_VISIBILITY(STV_PROTECTED), 1786520fe55SH. Peter Anvin #undef SYM_VISIBILITY 1796520fe55SH. Peter Anvin }; 1806520fe55SH. Peter Anvin const char *name = "unknown sym visibility name"; 1816520fe55SH. Peter Anvin if (visibility < ARRAY_SIZE(visibility_name)) { 1826520fe55SH. Peter Anvin name = visibility_name[visibility]; 1836520fe55SH. Peter Anvin } 1846520fe55SH. Peter Anvin return name; 1856520fe55SH. Peter Anvin } 1866520fe55SH. Peter Anvin 1876520fe55SH. Peter Anvin static const char *rel_type(unsigned type) 1886520fe55SH. Peter Anvin { 1896520fe55SH. Peter Anvin static const char *type_name[] = { 1906520fe55SH. Peter Anvin #define REL_TYPE(X) [X] = #X 191c889ba80SH. Peter Anvin #if ELF_BITS == 64 192946166afSKees Cook REL_TYPE(R_X86_64_NONE), 193946166afSKees Cook REL_TYPE(R_X86_64_64), 194946166afSKees Cook REL_TYPE(R_X86_64_PC32), 195946166afSKees Cook REL_TYPE(R_X86_64_GOT32), 196946166afSKees Cook REL_TYPE(R_X86_64_PLT32), 197946166afSKees Cook REL_TYPE(R_X86_64_COPY), 198946166afSKees Cook REL_TYPE(R_X86_64_GLOB_DAT), 199946166afSKees Cook REL_TYPE(R_X86_64_JUMP_SLOT), 200946166afSKees Cook REL_TYPE(R_X86_64_RELATIVE), 201946166afSKees Cook REL_TYPE(R_X86_64_GOTPCREL), 202946166afSKees Cook REL_TYPE(R_X86_64_32), 203946166afSKees Cook REL_TYPE(R_X86_64_32S), 204946166afSKees Cook REL_TYPE(R_X86_64_16), 205946166afSKees Cook REL_TYPE(R_X86_64_PC16), 206946166afSKees Cook REL_TYPE(R_X86_64_8), 207946166afSKees Cook REL_TYPE(R_X86_64_PC8), 208946166afSKees Cook #else 2096520fe55SH. Peter Anvin REL_TYPE(R_386_NONE), 2106520fe55SH. Peter Anvin REL_TYPE(R_386_32), 2116520fe55SH. Peter Anvin REL_TYPE(R_386_PC32), 2126520fe55SH. Peter Anvin REL_TYPE(R_386_GOT32), 2136520fe55SH. Peter Anvin REL_TYPE(R_386_PLT32), 2146520fe55SH. Peter Anvin REL_TYPE(R_386_COPY), 2156520fe55SH. Peter Anvin REL_TYPE(R_386_GLOB_DAT), 2166520fe55SH. Peter Anvin REL_TYPE(R_386_JMP_SLOT), 2176520fe55SH. Peter Anvin REL_TYPE(R_386_RELATIVE), 2186520fe55SH. Peter Anvin REL_TYPE(R_386_GOTOFF), 2196520fe55SH. Peter Anvin REL_TYPE(R_386_GOTPC), 2206520fe55SH. Peter Anvin REL_TYPE(R_386_8), 2216520fe55SH. Peter Anvin REL_TYPE(R_386_PC8), 2226520fe55SH. Peter Anvin REL_TYPE(R_386_16), 2236520fe55SH. Peter Anvin REL_TYPE(R_386_PC16), 224946166afSKees Cook #endif 2256520fe55SH. Peter Anvin #undef REL_TYPE 2266520fe55SH. Peter Anvin }; 2276520fe55SH. Peter Anvin const char *name = "unknown type rel type name"; 2286520fe55SH. Peter Anvin if (type < ARRAY_SIZE(type_name) && type_name[type]) { 2296520fe55SH. Peter Anvin name = type_name[type]; 2306520fe55SH. Peter Anvin } 2316520fe55SH. Peter Anvin return name; 2326520fe55SH. Peter Anvin } 2336520fe55SH. Peter Anvin 2346520fe55SH. Peter Anvin static const char *sec_name(unsigned shndx) 2356520fe55SH. Peter Anvin { 2366520fe55SH. Peter Anvin const char *sec_strtab; 2376520fe55SH. Peter Anvin const char *name; 2386520fe55SH. Peter Anvin sec_strtab = secs[ehdr.e_shstrndx].strtab; 2396520fe55SH. Peter Anvin name = "<noname>"; 2406520fe55SH. Peter Anvin if (shndx < ehdr.e_shnum) { 2416520fe55SH. Peter Anvin name = sec_strtab + secs[shndx].shdr.sh_name; 2426520fe55SH. Peter Anvin } 2436520fe55SH. Peter Anvin else if (shndx == SHN_ABS) { 2446520fe55SH. Peter Anvin name = "ABSOLUTE"; 2456520fe55SH. Peter Anvin } 2466520fe55SH. Peter Anvin else if (shndx == SHN_COMMON) { 2476520fe55SH. Peter Anvin name = "COMMON"; 2486520fe55SH. Peter Anvin } 2496520fe55SH. Peter Anvin return name; 2506520fe55SH. Peter Anvin } 2516520fe55SH. Peter Anvin 252bf11655cSKees Cook static const char *sym_name(const char *sym_strtab, Elf_Sym *sym) 2536520fe55SH. Peter Anvin { 2546520fe55SH. Peter Anvin const char *name; 2556520fe55SH. Peter Anvin name = "<noname>"; 2566520fe55SH. Peter Anvin if (sym->st_name) { 2576520fe55SH. Peter Anvin name = sym_strtab + sym->st_name; 2586520fe55SH. Peter Anvin } 2596520fe55SH. Peter Anvin else { 2606520fe55SH. Peter Anvin name = sec_name(sym->st_shndx); 2616520fe55SH. Peter Anvin } 2626520fe55SH. Peter Anvin return name; 2636520fe55SH. Peter Anvin } 2646520fe55SH. Peter Anvin 265946166afSKees Cook static Elf_Sym *sym_lookup(const char *symname) 266946166afSKees Cook { 267946166afSKees Cook int i; 268946166afSKees Cook for (i = 0; i < ehdr.e_shnum; i++) { 269946166afSKees Cook struct section *sec = &secs[i]; 270946166afSKees Cook long nsyms; 271946166afSKees Cook char *strtab; 272946166afSKees Cook Elf_Sym *symtab; 273946166afSKees Cook Elf_Sym *sym; 2746520fe55SH. Peter Anvin 275946166afSKees Cook if (sec->shdr.sh_type != SHT_SYMTAB) 276946166afSKees Cook continue; 277946166afSKees Cook 278946166afSKees Cook nsyms = sec->shdr.sh_size/sizeof(Elf_Sym); 279946166afSKees Cook symtab = sec->symtab; 280946166afSKees Cook strtab = sec->link->strtab; 281946166afSKees Cook 282946166afSKees Cook for (sym = symtab; --nsyms >= 0; sym++) { 283946166afSKees Cook if (!sym->st_name) 284946166afSKees Cook continue; 285946166afSKees Cook if (strcmp(symname, strtab + sym->st_name) == 0) 286946166afSKees Cook return sym; 287946166afSKees Cook } 288946166afSKees Cook } 289946166afSKees Cook return 0; 290946166afSKees Cook } 2916520fe55SH. Peter Anvin 2926520fe55SH. Peter Anvin #if BYTE_ORDER == LITTLE_ENDIAN 2936520fe55SH. Peter Anvin #define le16_to_cpu(val) (val) 2946520fe55SH. Peter Anvin #define le32_to_cpu(val) (val) 295946166afSKees Cook #define le64_to_cpu(val) (val) 2966520fe55SH. Peter Anvin #endif 2976520fe55SH. Peter Anvin #if BYTE_ORDER == BIG_ENDIAN 2986520fe55SH. Peter Anvin #define le16_to_cpu(val) bswap_16(val) 2996520fe55SH. Peter Anvin #define le32_to_cpu(val) bswap_32(val) 300946166afSKees Cook #define le64_to_cpu(val) bswap_64(val) 3016520fe55SH. Peter Anvin #endif 3026520fe55SH. Peter Anvin 3036520fe55SH. Peter Anvin static uint16_t elf16_to_cpu(uint16_t val) 3046520fe55SH. Peter Anvin { 3056520fe55SH. Peter Anvin return le16_to_cpu(val); 3066520fe55SH. Peter Anvin } 3076520fe55SH. Peter Anvin 3086520fe55SH. Peter Anvin static uint32_t elf32_to_cpu(uint32_t val) 3096520fe55SH. Peter Anvin { 3106520fe55SH. Peter Anvin return le32_to_cpu(val); 3116520fe55SH. Peter Anvin } 3126520fe55SH. Peter Anvin 313bf11655cSKees Cook #define elf_half_to_cpu(x) elf16_to_cpu(x) 314bf11655cSKees Cook #define elf_word_to_cpu(x) elf32_to_cpu(x) 315946166afSKees Cook 316c889ba80SH. Peter Anvin #if ELF_BITS == 64 317946166afSKees Cook static uint64_t elf64_to_cpu(uint64_t val) 318946166afSKees Cook { 319946166afSKees Cook return le64_to_cpu(val); 320946166afSKees Cook } 321946166afSKees Cook #define elf_addr_to_cpu(x) elf64_to_cpu(x) 322946166afSKees Cook #define elf_off_to_cpu(x) elf64_to_cpu(x) 323946166afSKees Cook #define elf_xword_to_cpu(x) elf64_to_cpu(x) 324946166afSKees Cook #else 325bf11655cSKees Cook #define elf_addr_to_cpu(x) elf32_to_cpu(x) 326bf11655cSKees Cook #define elf_off_to_cpu(x) elf32_to_cpu(x) 327bf11655cSKees Cook #define elf_xword_to_cpu(x) elf32_to_cpu(x) 328946166afSKees Cook #endif 329bf11655cSKees Cook 3306520fe55SH. Peter Anvin static void read_ehdr(FILE *fp) 3316520fe55SH. Peter Anvin { 3326520fe55SH. Peter Anvin if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) { 3336520fe55SH. Peter Anvin die("Cannot read ELF header: %s\n", 3346520fe55SH. Peter Anvin strerror(errno)); 3356520fe55SH. Peter Anvin } 3366520fe55SH. Peter Anvin if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) { 3376520fe55SH. Peter Anvin die("No ELF magic\n"); 3386520fe55SH. Peter Anvin } 339bf11655cSKees Cook if (ehdr.e_ident[EI_CLASS] != ELF_CLASS) { 340bf11655cSKees Cook die("Not a %d bit executable\n", ELF_BITS); 3416520fe55SH. Peter Anvin } 3426520fe55SH. Peter Anvin if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) { 3436520fe55SH. Peter Anvin die("Not a LSB ELF executable\n"); 3446520fe55SH. Peter Anvin } 3456520fe55SH. Peter Anvin if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) { 3466520fe55SH. Peter Anvin die("Unknown ELF version\n"); 3476520fe55SH. Peter Anvin } 3486520fe55SH. Peter Anvin /* Convert the fields to native endian */ 349bf11655cSKees Cook ehdr.e_type = elf_half_to_cpu(ehdr.e_type); 350bf11655cSKees Cook ehdr.e_machine = elf_half_to_cpu(ehdr.e_machine); 351bf11655cSKees Cook ehdr.e_version = elf_word_to_cpu(ehdr.e_version); 352bf11655cSKees Cook ehdr.e_entry = elf_addr_to_cpu(ehdr.e_entry); 353bf11655cSKees Cook ehdr.e_phoff = elf_off_to_cpu(ehdr.e_phoff); 354bf11655cSKees Cook ehdr.e_shoff = elf_off_to_cpu(ehdr.e_shoff); 355bf11655cSKees Cook ehdr.e_flags = elf_word_to_cpu(ehdr.e_flags); 356bf11655cSKees Cook ehdr.e_ehsize = elf_half_to_cpu(ehdr.e_ehsize); 357bf11655cSKees Cook ehdr.e_phentsize = elf_half_to_cpu(ehdr.e_phentsize); 358bf11655cSKees Cook ehdr.e_phnum = elf_half_to_cpu(ehdr.e_phnum); 359bf11655cSKees Cook ehdr.e_shentsize = elf_half_to_cpu(ehdr.e_shentsize); 360bf11655cSKees Cook ehdr.e_shnum = elf_half_to_cpu(ehdr.e_shnum); 361bf11655cSKees Cook ehdr.e_shstrndx = elf_half_to_cpu(ehdr.e_shstrndx); 3626520fe55SH. Peter Anvin 3636520fe55SH. Peter Anvin if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN)) { 3646520fe55SH. Peter Anvin die("Unsupported ELF header type\n"); 3656520fe55SH. Peter Anvin } 366bf11655cSKees Cook if (ehdr.e_machine != ELF_MACHINE) { 367bf11655cSKees Cook die("Not for %s\n", ELF_MACHINE_NAME); 3686520fe55SH. Peter Anvin } 3696520fe55SH. Peter Anvin if (ehdr.e_version != EV_CURRENT) { 3706520fe55SH. Peter Anvin die("Unknown ELF version\n"); 3716520fe55SH. Peter Anvin } 372bf11655cSKees Cook if (ehdr.e_ehsize != sizeof(Elf_Ehdr)) { 3736520fe55SH. Peter Anvin die("Bad Elf header size\n"); 3746520fe55SH. Peter Anvin } 375bf11655cSKees Cook if (ehdr.e_phentsize != sizeof(Elf_Phdr)) { 3766520fe55SH. Peter Anvin die("Bad program header entry\n"); 3776520fe55SH. Peter Anvin } 378bf11655cSKees Cook if (ehdr.e_shentsize != sizeof(Elf_Shdr)) { 3796520fe55SH. Peter Anvin die("Bad section header entry\n"); 3806520fe55SH. Peter Anvin } 3816520fe55SH. Peter Anvin if (ehdr.e_shstrndx >= ehdr.e_shnum) { 3826520fe55SH. Peter Anvin die("String table index out of bounds\n"); 3836520fe55SH. Peter Anvin } 3846520fe55SH. Peter Anvin } 3856520fe55SH. Peter Anvin 3866520fe55SH. Peter Anvin static void read_shdrs(FILE *fp) 3876520fe55SH. Peter Anvin { 3886520fe55SH. Peter Anvin int i; 389bf11655cSKees Cook Elf_Shdr shdr; 3906520fe55SH. Peter Anvin 3916520fe55SH. Peter Anvin secs = calloc(ehdr.e_shnum, sizeof(struct section)); 3926520fe55SH. Peter Anvin if (!secs) { 3936520fe55SH. Peter Anvin die("Unable to allocate %d section headers\n", 3946520fe55SH. Peter Anvin ehdr.e_shnum); 3956520fe55SH. Peter Anvin } 3966520fe55SH. Peter Anvin if (fseek(fp, ehdr.e_shoff, SEEK_SET) < 0) { 3976520fe55SH. Peter Anvin die("Seek to %d failed: %s\n", 3986520fe55SH. Peter Anvin ehdr.e_shoff, strerror(errno)); 3996520fe55SH. Peter Anvin } 4006520fe55SH. Peter Anvin for (i = 0; i < ehdr.e_shnum; i++) { 4016520fe55SH. Peter Anvin struct section *sec = &secs[i]; 4026520fe55SH. Peter Anvin if (fread(&shdr, sizeof shdr, 1, fp) != 1) 4036520fe55SH. Peter Anvin die("Cannot read ELF section headers %d/%d: %s\n", 4046520fe55SH. Peter Anvin i, ehdr.e_shnum, strerror(errno)); 405bf11655cSKees Cook sec->shdr.sh_name = elf_word_to_cpu(shdr.sh_name); 406bf11655cSKees Cook sec->shdr.sh_type = elf_word_to_cpu(shdr.sh_type); 407bf11655cSKees Cook sec->shdr.sh_flags = elf_xword_to_cpu(shdr.sh_flags); 408bf11655cSKees Cook sec->shdr.sh_addr = elf_addr_to_cpu(shdr.sh_addr); 409bf11655cSKees Cook sec->shdr.sh_offset = elf_off_to_cpu(shdr.sh_offset); 410bf11655cSKees Cook sec->shdr.sh_size = elf_xword_to_cpu(shdr.sh_size); 411bf11655cSKees Cook sec->shdr.sh_link = elf_word_to_cpu(shdr.sh_link); 412bf11655cSKees Cook sec->shdr.sh_info = elf_word_to_cpu(shdr.sh_info); 413bf11655cSKees Cook sec->shdr.sh_addralign = elf_xword_to_cpu(shdr.sh_addralign); 414bf11655cSKees Cook sec->shdr.sh_entsize = elf_xword_to_cpu(shdr.sh_entsize); 4156520fe55SH. Peter Anvin if (sec->shdr.sh_link < ehdr.e_shnum) 4166520fe55SH. Peter Anvin sec->link = &secs[sec->shdr.sh_link]; 4176520fe55SH. Peter Anvin } 4186520fe55SH. Peter Anvin 4196520fe55SH. Peter Anvin } 4206520fe55SH. Peter Anvin 4216520fe55SH. Peter Anvin static void read_strtabs(FILE *fp) 4226520fe55SH. Peter Anvin { 4236520fe55SH. Peter Anvin int i; 4246520fe55SH. Peter Anvin for (i = 0; i < ehdr.e_shnum; i++) { 4256520fe55SH. Peter Anvin struct section *sec = &secs[i]; 4266520fe55SH. Peter Anvin if (sec->shdr.sh_type != SHT_STRTAB) { 4276520fe55SH. Peter Anvin continue; 4286520fe55SH. Peter Anvin } 4296520fe55SH. Peter Anvin sec->strtab = malloc(sec->shdr.sh_size); 4306520fe55SH. Peter Anvin if (!sec->strtab) { 4316520fe55SH. Peter Anvin die("malloc of %d bytes for strtab failed\n", 4326520fe55SH. Peter Anvin sec->shdr.sh_size); 4336520fe55SH. Peter Anvin } 4346520fe55SH. Peter Anvin if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { 4356520fe55SH. Peter Anvin die("Seek to %d failed: %s\n", 4366520fe55SH. Peter Anvin sec->shdr.sh_offset, strerror(errno)); 4376520fe55SH. Peter Anvin } 4386520fe55SH. Peter Anvin if (fread(sec->strtab, 1, sec->shdr.sh_size, fp) 4396520fe55SH. Peter Anvin != sec->shdr.sh_size) { 4406520fe55SH. Peter Anvin die("Cannot read symbol table: %s\n", 4416520fe55SH. Peter Anvin strerror(errno)); 4426520fe55SH. Peter Anvin } 4436520fe55SH. Peter Anvin } 4446520fe55SH. Peter Anvin } 4456520fe55SH. Peter Anvin 4466520fe55SH. Peter Anvin static void read_symtabs(FILE *fp) 4476520fe55SH. Peter Anvin { 4486520fe55SH. Peter Anvin int i,j; 4496520fe55SH. Peter Anvin for (i = 0; i < ehdr.e_shnum; i++) { 4506520fe55SH. Peter Anvin struct section *sec = &secs[i]; 4516520fe55SH. Peter Anvin if (sec->shdr.sh_type != SHT_SYMTAB) { 4526520fe55SH. Peter Anvin continue; 4536520fe55SH. Peter Anvin } 4546520fe55SH. Peter Anvin sec->symtab = malloc(sec->shdr.sh_size); 4556520fe55SH. Peter Anvin if (!sec->symtab) { 4566520fe55SH. Peter Anvin die("malloc of %d bytes for symtab failed\n", 4576520fe55SH. Peter Anvin sec->shdr.sh_size); 4586520fe55SH. Peter Anvin } 4596520fe55SH. Peter Anvin if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { 4606520fe55SH. Peter Anvin die("Seek to %d failed: %s\n", 4616520fe55SH. Peter Anvin sec->shdr.sh_offset, strerror(errno)); 4626520fe55SH. Peter Anvin } 4636520fe55SH. Peter Anvin if (fread(sec->symtab, 1, sec->shdr.sh_size, fp) 4646520fe55SH. Peter Anvin != sec->shdr.sh_size) { 4656520fe55SH. Peter Anvin die("Cannot read symbol table: %s\n", 4666520fe55SH. Peter Anvin strerror(errno)); 4676520fe55SH. Peter Anvin } 468bf11655cSKees Cook for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Sym); j++) { 469bf11655cSKees Cook Elf_Sym *sym = &sec->symtab[j]; 470bf11655cSKees Cook sym->st_name = elf_word_to_cpu(sym->st_name); 471bf11655cSKees Cook sym->st_value = elf_addr_to_cpu(sym->st_value); 472bf11655cSKees Cook sym->st_size = elf_xword_to_cpu(sym->st_size); 473bf11655cSKees Cook sym->st_shndx = elf_half_to_cpu(sym->st_shndx); 4746520fe55SH. Peter Anvin } 4756520fe55SH. Peter Anvin } 4766520fe55SH. Peter Anvin } 4776520fe55SH. Peter Anvin 4786520fe55SH. Peter Anvin 4796520fe55SH. Peter Anvin static void read_relocs(FILE *fp) 4806520fe55SH. Peter Anvin { 4816520fe55SH. Peter Anvin int i,j; 4826520fe55SH. Peter Anvin for (i = 0; i < ehdr.e_shnum; i++) { 4836520fe55SH. Peter Anvin struct section *sec = &secs[i]; 484bf11655cSKees Cook if (sec->shdr.sh_type != SHT_REL_TYPE) { 4856520fe55SH. Peter Anvin continue; 4866520fe55SH. Peter Anvin } 4876520fe55SH. Peter Anvin sec->reltab = malloc(sec->shdr.sh_size); 4886520fe55SH. Peter Anvin if (!sec->reltab) { 4896520fe55SH. Peter Anvin die("malloc of %d bytes for relocs failed\n", 4906520fe55SH. Peter Anvin sec->shdr.sh_size); 4916520fe55SH. Peter Anvin } 4926520fe55SH. Peter Anvin if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { 4936520fe55SH. Peter Anvin die("Seek to %d failed: %s\n", 4946520fe55SH. Peter Anvin sec->shdr.sh_offset, strerror(errno)); 4956520fe55SH. Peter Anvin } 4966520fe55SH. Peter Anvin if (fread(sec->reltab, 1, sec->shdr.sh_size, fp) 4976520fe55SH. Peter Anvin != sec->shdr.sh_size) { 4986520fe55SH. Peter Anvin die("Cannot read symbol table: %s\n", 4996520fe55SH. Peter Anvin strerror(errno)); 5006520fe55SH. Peter Anvin } 501bf11655cSKees Cook for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) { 502bf11655cSKees Cook Elf_Rel *rel = &sec->reltab[j]; 503bf11655cSKees Cook rel->r_offset = elf_addr_to_cpu(rel->r_offset); 504bf11655cSKees Cook rel->r_info = elf_xword_to_cpu(rel->r_info); 505946166afSKees Cook #if (SHT_REL_TYPE == SHT_RELA) 506946166afSKees Cook rel->r_addend = elf_xword_to_cpu(rel->r_addend); 507946166afSKees Cook #endif 5086520fe55SH. Peter Anvin } 5096520fe55SH. Peter Anvin } 5106520fe55SH. Peter Anvin } 5116520fe55SH. Peter Anvin 5126520fe55SH. Peter Anvin 5136520fe55SH. Peter Anvin static void print_absolute_symbols(void) 5146520fe55SH. Peter Anvin { 5156520fe55SH. Peter Anvin int i; 516946166afSKees Cook const char *format; 517946166afSKees Cook 518c889ba80SH. Peter Anvin if (ELF_BITS == 64) 519946166afSKees Cook format = "%5d %016"PRIx64" %5"PRId64" %10s %10s %12s %s\n"; 520946166afSKees Cook else 521946166afSKees Cook format = "%5d %08"PRIx32" %5"PRId32" %10s %10s %12s %s\n"; 522946166afSKees Cook 5236520fe55SH. Peter Anvin printf("Absolute symbols\n"); 5246520fe55SH. Peter Anvin printf(" Num: Value Size Type Bind Visibility Name\n"); 5256520fe55SH. Peter Anvin for (i = 0; i < ehdr.e_shnum; i++) { 5266520fe55SH. Peter Anvin struct section *sec = &secs[i]; 5276520fe55SH. Peter Anvin char *sym_strtab; 5286520fe55SH. Peter Anvin int j; 5296520fe55SH. Peter Anvin 5306520fe55SH. Peter Anvin if (sec->shdr.sh_type != SHT_SYMTAB) { 5316520fe55SH. Peter Anvin continue; 5326520fe55SH. Peter Anvin } 5336520fe55SH. Peter Anvin sym_strtab = sec->link->strtab; 534bf11655cSKees Cook for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Sym); j++) { 535bf11655cSKees Cook Elf_Sym *sym; 5366520fe55SH. Peter Anvin const char *name; 5376520fe55SH. Peter Anvin sym = &sec->symtab[j]; 5386520fe55SH. Peter Anvin name = sym_name(sym_strtab, sym); 5396520fe55SH. Peter Anvin if (sym->st_shndx != SHN_ABS) { 5406520fe55SH. Peter Anvin continue; 5416520fe55SH. Peter Anvin } 542946166afSKees Cook printf(format, 5436520fe55SH. Peter Anvin j, sym->st_value, sym->st_size, 544bf11655cSKees Cook sym_type(ELF_ST_TYPE(sym->st_info)), 545bf11655cSKees Cook sym_bind(ELF_ST_BIND(sym->st_info)), 546bf11655cSKees Cook sym_visibility(ELF_ST_VISIBILITY(sym->st_other)), 5476520fe55SH. Peter Anvin name); 5486520fe55SH. Peter Anvin } 5496520fe55SH. Peter Anvin } 5506520fe55SH. Peter Anvin printf("\n"); 5516520fe55SH. Peter Anvin } 5526520fe55SH. Peter Anvin 5536520fe55SH. Peter Anvin static void print_absolute_relocs(void) 5546520fe55SH. Peter Anvin { 5556520fe55SH. Peter Anvin int i, printed = 0; 556946166afSKees Cook const char *format; 557946166afSKees Cook 558c889ba80SH. Peter Anvin if (ELF_BITS == 64) 559946166afSKees Cook format = "%016"PRIx64" %016"PRIx64" %10s %016"PRIx64" %s\n"; 560946166afSKees Cook else 561946166afSKees Cook format = "%08"PRIx32" %08"PRIx32" %10s %08"PRIx32" %s\n"; 5626520fe55SH. Peter Anvin 5636520fe55SH. Peter Anvin for (i = 0; i < ehdr.e_shnum; i++) { 5646520fe55SH. Peter Anvin struct section *sec = &secs[i]; 5656520fe55SH. Peter Anvin struct section *sec_applies, *sec_symtab; 5666520fe55SH. Peter Anvin char *sym_strtab; 567bf11655cSKees Cook Elf_Sym *sh_symtab; 5686520fe55SH. Peter Anvin int j; 569bf11655cSKees Cook if (sec->shdr.sh_type != SHT_REL_TYPE) { 5706520fe55SH. Peter Anvin continue; 5716520fe55SH. Peter Anvin } 5726520fe55SH. Peter Anvin sec_symtab = sec->link; 5736520fe55SH. Peter Anvin sec_applies = &secs[sec->shdr.sh_info]; 5746520fe55SH. Peter Anvin if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) { 5756520fe55SH. Peter Anvin continue; 5766520fe55SH. Peter Anvin } 5776520fe55SH. Peter Anvin sh_symtab = sec_symtab->symtab; 5786520fe55SH. Peter Anvin sym_strtab = sec_symtab->link->strtab; 579bf11655cSKees Cook for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) { 580bf11655cSKees Cook Elf_Rel *rel; 581bf11655cSKees Cook Elf_Sym *sym; 5826520fe55SH. Peter Anvin const char *name; 5836520fe55SH. Peter Anvin rel = &sec->reltab[j]; 584bf11655cSKees Cook sym = &sh_symtab[ELF_R_SYM(rel->r_info)]; 5856520fe55SH. Peter Anvin name = sym_name(sym_strtab, sym); 5866520fe55SH. Peter Anvin if (sym->st_shndx != SHN_ABS) { 5876520fe55SH. Peter Anvin continue; 5886520fe55SH. Peter Anvin } 5896520fe55SH. Peter Anvin 5906520fe55SH. Peter Anvin /* Absolute symbols are not relocated if bzImage is 5916520fe55SH. Peter Anvin * loaded at a non-compiled address. Display a warning 5926520fe55SH. Peter Anvin * to user at compile time about the absolute 5936520fe55SH. Peter Anvin * relocations present. 5946520fe55SH. Peter Anvin * 5956520fe55SH. Peter Anvin * User need to audit the code to make sure 5966520fe55SH. Peter Anvin * some symbols which should have been section 5976520fe55SH. Peter Anvin * relative have not become absolute because of some 5986520fe55SH. Peter Anvin * linker optimization or wrong programming usage. 5996520fe55SH. Peter Anvin * 6006520fe55SH. Peter Anvin * Before warning check if this absolute symbol 6016520fe55SH. Peter Anvin * relocation is harmless. 6026520fe55SH. Peter Anvin */ 6036520fe55SH. Peter Anvin if (is_reloc(S_ABS, name) || is_reloc(S_REL, name)) 6046520fe55SH. Peter Anvin continue; 6056520fe55SH. Peter Anvin 6066520fe55SH. Peter Anvin if (!printed) { 6076520fe55SH. Peter Anvin printf("WARNING: Absolute relocations" 6086520fe55SH. Peter Anvin " present\n"); 6096520fe55SH. Peter Anvin printf("Offset Info Type Sym.Value " 6106520fe55SH. Peter Anvin "Sym.Name\n"); 6116520fe55SH. Peter Anvin printed = 1; 6126520fe55SH. Peter Anvin } 6136520fe55SH. Peter Anvin 614946166afSKees Cook printf(format, 6156520fe55SH. Peter Anvin rel->r_offset, 6166520fe55SH. Peter Anvin rel->r_info, 617bf11655cSKees Cook rel_type(ELF_R_TYPE(rel->r_info)), 6186520fe55SH. Peter Anvin sym->st_value, 6196520fe55SH. Peter Anvin name); 6206520fe55SH. Peter Anvin } 6216520fe55SH. Peter Anvin } 6226520fe55SH. Peter Anvin 6236520fe55SH. Peter Anvin if (printed) 6246520fe55SH. Peter Anvin printf("\n"); 6256520fe55SH. Peter Anvin } 6266520fe55SH. Peter Anvin 6275d442e63SKees Cook static void add_reloc(struct relocs *r, uint32_t offset) 6285d442e63SKees Cook { 6295d442e63SKees Cook if (r->count == r->size) { 6305d442e63SKees Cook unsigned long newsize = r->size + 50000; 6315d442e63SKees Cook void *mem = realloc(r->offset, newsize * sizeof(r->offset[0])); 6325d442e63SKees Cook 6335d442e63SKees Cook if (!mem) 6345d442e63SKees Cook die("realloc of %ld entries for relocs failed\n", 6355d442e63SKees Cook newsize); 6365d442e63SKees Cook r->offset = mem; 6375d442e63SKees Cook r->size = newsize; 6385d442e63SKees Cook } 6395d442e63SKees Cook r->offset[r->count++] = offset; 6405d442e63SKees Cook } 6415d442e63SKees Cook 6425d442e63SKees Cook static void walk_relocs(int (*process)(struct section *sec, Elf_Rel *rel, 6435d442e63SKees Cook Elf_Sym *sym, const char *symname)) 6446520fe55SH. Peter Anvin { 6456520fe55SH. Peter Anvin int i; 6466520fe55SH. Peter Anvin /* Walk through the relocations */ 6476520fe55SH. Peter Anvin for (i = 0; i < ehdr.e_shnum; i++) { 6486520fe55SH. Peter Anvin char *sym_strtab; 649bf11655cSKees Cook Elf_Sym *sh_symtab; 6506520fe55SH. Peter Anvin struct section *sec_applies, *sec_symtab; 6516520fe55SH. Peter Anvin int j; 6526520fe55SH. Peter Anvin struct section *sec = &secs[i]; 6536520fe55SH. Peter Anvin 654bf11655cSKees Cook if (sec->shdr.sh_type != SHT_REL_TYPE) { 6556520fe55SH. Peter Anvin continue; 6566520fe55SH. Peter Anvin } 6576520fe55SH. Peter Anvin sec_symtab = sec->link; 6586520fe55SH. Peter Anvin sec_applies = &secs[sec->shdr.sh_info]; 6596520fe55SH. Peter Anvin if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) { 6606520fe55SH. Peter Anvin continue; 6616520fe55SH. Peter Anvin } 6626520fe55SH. Peter Anvin sh_symtab = sec_symtab->symtab; 6636520fe55SH. Peter Anvin sym_strtab = sec_symtab->link->strtab; 664bf11655cSKees Cook for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) { 6655d442e63SKees Cook Elf_Rel *rel = &sec->reltab[j]; 6665d442e63SKees Cook Elf_Sym *sym = &sh_symtab[ELF_R_SYM(rel->r_info)]; 6675d442e63SKees Cook const char *symname = sym_name(sym_strtab, sym); 66824ab82bdSH. Peter Anvin 6695d442e63SKees Cook process(sec, rel, sym, symname); 6705d442e63SKees Cook } 6715d442e63SKees Cook } 6725d442e63SKees Cook } 6736520fe55SH. Peter Anvin 674946166afSKees Cook /* 675946166afSKees Cook * The .data..percpu section is a special case for x86_64 SMP kernels. 676946166afSKees Cook * It is used to initialize the actual per_cpu areas and to provide 677946166afSKees Cook * definitions for the per_cpu variables that correspond to their offsets 678946166afSKees Cook * within the percpu area. Since the values of all of the symbols need 679946166afSKees Cook * to be offsets from the start of the per_cpu area the virtual address 680946166afSKees Cook * (sh_addr) of .data..percpu is 0 in SMP kernels. 681946166afSKees Cook * 682946166afSKees Cook * This means that: 683946166afSKees Cook * 684946166afSKees Cook * Relocations that reference symbols in the per_cpu area do not 685946166afSKees Cook * need further relocation (since the value is an offset relative 686946166afSKees Cook * to the start of the per_cpu area that does not change). 687946166afSKees Cook * 688946166afSKees Cook * Relocations that apply to the per_cpu area need to have their 689946166afSKees Cook * offset adjusted by by the value of __per_cpu_load to make them 690946166afSKees Cook * point to the correct place in the loaded image (because the 691946166afSKees Cook * virtual address of .data..percpu is 0). 692946166afSKees Cook * 693946166afSKees Cook * For non SMP kernels .data..percpu is linked as part of the normal 694946166afSKees Cook * kernel data and does not require special treatment. 695946166afSKees Cook * 696946166afSKees Cook */ 697946166afSKees Cook static int per_cpu_shndx = -1; 698946166afSKees Cook Elf_Addr per_cpu_load_addr; 699946166afSKees Cook 700946166afSKees Cook static void percpu_init(void) 701946166afSKees Cook { 702946166afSKees Cook int i; 703946166afSKees Cook for (i = 0; i < ehdr.e_shnum; i++) { 704946166afSKees Cook ElfW(Sym) *sym; 705946166afSKees Cook if (strcmp(sec_name(i), ".data..percpu")) 706946166afSKees Cook continue; 707946166afSKees Cook 708946166afSKees Cook if (secs[i].shdr.sh_addr != 0) /* non SMP kernel */ 709946166afSKees Cook return; 710946166afSKees Cook 711946166afSKees Cook sym = sym_lookup("__per_cpu_load"); 712946166afSKees Cook if (!sym) 713946166afSKees Cook die("can't find __per_cpu_load\n"); 714946166afSKees Cook 715946166afSKees Cook per_cpu_shndx = i; 716946166afSKees Cook per_cpu_load_addr = sym->st_value; 717946166afSKees Cook return; 718946166afSKees Cook } 719946166afSKees Cook } 720946166afSKees Cook 721c889ba80SH. Peter Anvin #if ELF_BITS == 64 722c889ba80SH. Peter Anvin 723946166afSKees Cook /* 724946166afSKees Cook * Check to see if a symbol lies in the .data..percpu section. 725d751c169SMichael Davidson * 726d751c169SMichael Davidson * The linker incorrectly associates some symbols with the 727d751c169SMichael Davidson * .data..percpu section so we also need to check the symbol 728d751c169SMichael Davidson * name to make sure that we classify the symbol correctly. 729d751c169SMichael Davidson * 730d751c169SMichael Davidson * The GNU linker incorrectly associates: 731d751c169SMichael Davidson * __init_begin 732d751c169SMichael Davidson * 733d751c169SMichael Davidson * The "gold" linker incorrectly associates: 734d751c169SMichael Davidson * init_per_cpu__irq_stack_union 735d751c169SMichael Davidson * init_per_cpu__gdt_page 736946166afSKees Cook */ 737946166afSKees Cook static int is_percpu_sym(ElfW(Sym) *sym, const char *symname) 738946166afSKees Cook { 739946166afSKees Cook return (sym->st_shndx == per_cpu_shndx) && 740d751c169SMichael Davidson strcmp(symname, "__init_begin") && 741d751c169SMichael Davidson strncmp(symname, "init_per_cpu_", 13); 742946166afSKees Cook } 743946166afSKees Cook 744c889ba80SH. Peter Anvin 745946166afSKees Cook static int do_reloc64(struct section *sec, Elf_Rel *rel, ElfW(Sym) *sym, 746946166afSKees Cook const char *symname) 747946166afSKees Cook { 748946166afSKees Cook unsigned r_type = ELF64_R_TYPE(rel->r_info); 749946166afSKees Cook ElfW(Addr) offset = rel->r_offset; 750946166afSKees Cook int shn_abs = (sym->st_shndx == SHN_ABS) && !is_reloc(S_REL, symname); 751946166afSKees Cook 752946166afSKees Cook if (sym->st_shndx == SHN_UNDEF) 753946166afSKees Cook return 0; 754946166afSKees Cook 755946166afSKees Cook /* 756946166afSKees Cook * Adjust the offset if this reloc applies to the percpu section. 757946166afSKees Cook */ 758946166afSKees Cook if (sec->shdr.sh_info == per_cpu_shndx) 759946166afSKees Cook offset += per_cpu_load_addr; 760946166afSKees Cook 761946166afSKees Cook switch (r_type) { 762946166afSKees Cook case R_X86_64_NONE: 763946166afSKees Cook case R_X86_64_PC32: 764946166afSKees Cook /* 765946166afSKees Cook * NONE can be ignored and PC relative relocations don't 766946166afSKees Cook * need to be adjusted. 767946166afSKees Cook */ 768946166afSKees Cook break; 769946166afSKees Cook 770946166afSKees Cook case R_X86_64_32: 771946166afSKees Cook case R_X86_64_32S: 772946166afSKees Cook case R_X86_64_64: 773946166afSKees Cook /* 774946166afSKees Cook * References to the percpu area don't need to be adjusted. 775946166afSKees Cook */ 776946166afSKees Cook if (is_percpu_sym(sym, symname)) 777946166afSKees Cook break; 778946166afSKees Cook 779946166afSKees Cook if (shn_abs) { 780946166afSKees Cook /* 781946166afSKees Cook * Whitelisted absolute symbols do not require 782946166afSKees Cook * relocation. 783946166afSKees Cook */ 784946166afSKees Cook if (is_reloc(S_ABS, symname)) 785946166afSKees Cook break; 786946166afSKees Cook 787946166afSKees Cook die("Invalid absolute %s relocation: %s\n", 788946166afSKees Cook rel_type(r_type), symname); 789946166afSKees Cook break; 790946166afSKees Cook } 791946166afSKees Cook 792946166afSKees Cook /* 793946166afSKees Cook * Relocation offsets for 64 bit kernels are output 794946166afSKees Cook * as 32 bits and sign extended back to 64 bits when 795946166afSKees Cook * the relocations are processed. 796946166afSKees Cook * Make sure that the offset will fit. 797946166afSKees Cook */ 798946166afSKees Cook if ((int32_t)offset != (int64_t)offset) 799946166afSKees Cook die("Relocation offset doesn't fit in 32 bits\n"); 800946166afSKees Cook 801946166afSKees Cook if (r_type == R_X86_64_64) 802946166afSKees Cook add_reloc(&relocs64, offset); 803946166afSKees Cook else 804946166afSKees Cook add_reloc(&relocs32, offset); 805946166afSKees Cook break; 806946166afSKees Cook 807946166afSKees Cook default: 808946166afSKees Cook die("Unsupported relocation type: %s (%d)\n", 809946166afSKees Cook rel_type(r_type), r_type); 810946166afSKees Cook break; 811946166afSKees Cook } 812946166afSKees Cook 813946166afSKees Cook return 0; 814946166afSKees Cook } 815946166afSKees Cook 816c889ba80SH. Peter Anvin #else 817946166afSKees Cook 818946166afSKees Cook static int do_reloc32(struct section *sec, Elf_Rel *rel, Elf_Sym *sym, 8195d442e63SKees Cook const char *symname) 8205d442e63SKees Cook { 8215d442e63SKees Cook unsigned r_type = ELF32_R_TYPE(rel->r_info); 8225d442e63SKees Cook int shn_abs = (sym->st_shndx == SHN_ABS) && !is_reloc(S_REL, symname); 82324ab82bdSH. Peter Anvin 8246520fe55SH. Peter Anvin switch (r_type) { 8256520fe55SH. Peter Anvin case R_386_NONE: 8266520fe55SH. Peter Anvin case R_386_PC32: 8276520fe55SH. Peter Anvin case R_386_PC16: 8286520fe55SH. Peter Anvin case R_386_PC8: 8296520fe55SH. Peter Anvin /* 8305d442e63SKees Cook * NONE can be ignored and PC relative relocations don't 8315d442e63SKees Cook * need to be adjusted. 8326520fe55SH. Peter Anvin */ 8336520fe55SH. Peter Anvin break; 8346520fe55SH. Peter Anvin 8355d442e63SKees Cook case R_386_32: 83624ab82bdSH. Peter Anvin if (shn_abs) { 8375d442e63SKees Cook /* 8385d442e63SKees Cook * Whitelisted absolute symbols do not require 8395d442e63SKees Cook * relocation. 8405d442e63SKees Cook */ 8416520fe55SH. Peter Anvin if (is_reloc(S_ABS, symname)) 8426520fe55SH. Peter Anvin break; 8436520fe55SH. Peter Anvin 8445d442e63SKees Cook die("Invalid absolute %s relocation: %s\n", 8455d442e63SKees Cook rel_type(r_type), symname); 8466520fe55SH. Peter Anvin break; 8476520fe55SH. Peter Anvin } 8485d442e63SKees Cook 8495d442e63SKees Cook add_reloc(&relocs32, rel->r_offset); 8506520fe55SH. Peter Anvin break; 8515d442e63SKees Cook 8526520fe55SH. Peter Anvin default: 8536520fe55SH. Peter Anvin die("Unsupported relocation type: %s (%d)\n", 8546520fe55SH. Peter Anvin rel_type(r_type), r_type); 8556520fe55SH. Peter Anvin break; 8565d442e63SKees Cook } 8575d442e63SKees Cook 8585d442e63SKees Cook return 0; 8595d442e63SKees Cook } 8605d442e63SKees Cook 8615d442e63SKees Cook static int do_reloc_real(struct section *sec, Elf_Rel *rel, Elf_Sym *sym, 8625d442e63SKees Cook const char *symname) 8635d442e63SKees Cook { 8645d442e63SKees Cook unsigned r_type = ELF32_R_TYPE(rel->r_info); 8655d442e63SKees Cook int shn_abs = (sym->st_shndx == SHN_ABS) && !is_reloc(S_REL, symname); 8665d442e63SKees Cook 8675d442e63SKees Cook switch (r_type) { 8685d442e63SKees Cook case R_386_NONE: 8695d442e63SKees Cook case R_386_PC32: 8705d442e63SKees Cook case R_386_PC16: 8715d442e63SKees Cook case R_386_PC8: 8725d442e63SKees Cook /* 8735d442e63SKees Cook * NONE can be ignored and PC relative relocations don't 8745d442e63SKees Cook * need to be adjusted. 8755d442e63SKees Cook */ 8765d442e63SKees Cook break; 8775d442e63SKees Cook 8785d442e63SKees Cook case R_386_16: 8795d442e63SKees Cook if (shn_abs) { 8805d442e63SKees Cook /* 8815d442e63SKees Cook * Whitelisted absolute symbols do not require 8825d442e63SKees Cook * relocation. 8835d442e63SKees Cook */ 8845d442e63SKees Cook if (is_reloc(S_ABS, symname)) 8855d442e63SKees Cook break; 8865d442e63SKees Cook 8875d442e63SKees Cook if (is_reloc(S_SEG, symname)) { 8885d442e63SKees Cook add_reloc(&relocs16, rel->r_offset); 8895d442e63SKees Cook break; 8905d442e63SKees Cook } 8915d442e63SKees Cook } else { 8925d442e63SKees Cook if (!is_reloc(S_LIN, symname)) 8935d442e63SKees Cook break; 8945d442e63SKees Cook } 89524ab82bdSH. Peter Anvin die("Invalid %s %s relocation: %s\n", 89624ab82bdSH. Peter Anvin shn_abs ? "absolute" : "relative", 8976520fe55SH. Peter Anvin rel_type(r_type), symname); 8985d442e63SKees Cook break; 8995d442e63SKees Cook 9005d442e63SKees Cook case R_386_32: 9015d442e63SKees Cook if (shn_abs) { 9025d442e63SKees Cook /* 9035d442e63SKees Cook * Whitelisted absolute symbols do not require 9045d442e63SKees Cook * relocation. 9055d442e63SKees Cook */ 9065d442e63SKees Cook if (is_reloc(S_ABS, symname)) 9075d442e63SKees Cook break; 9085d442e63SKees Cook 9095d442e63SKees Cook if (is_reloc(S_REL, symname)) { 9105d442e63SKees Cook add_reloc(&relocs32, rel->r_offset); 9115d442e63SKees Cook break; 9126520fe55SH. Peter Anvin } 9135d442e63SKees Cook } else { 9145d442e63SKees Cook if (is_reloc(S_LIN, symname)) 9155d442e63SKees Cook add_reloc(&relocs32, rel->r_offset); 9165d442e63SKees Cook break; 9176520fe55SH. Peter Anvin } 9185d442e63SKees Cook die("Invalid %s %s relocation: %s\n", 9195d442e63SKees Cook shn_abs ? "absolute" : "relative", 9205d442e63SKees Cook rel_type(r_type), symname); 9215d442e63SKees Cook break; 9225d442e63SKees Cook 9235d442e63SKees Cook default: 9245d442e63SKees Cook die("Unsupported relocation type: %s (%d)\n", 9255d442e63SKees Cook rel_type(r_type), r_type); 9265d442e63SKees Cook break; 9276520fe55SH. Peter Anvin } 9286520fe55SH. Peter Anvin 9295d442e63SKees Cook return 0; 9306520fe55SH. Peter Anvin } 9316520fe55SH. Peter Anvin 932c889ba80SH. Peter Anvin #endif 933c889ba80SH. Peter Anvin 9346520fe55SH. Peter Anvin static int cmp_relocs(const void *va, const void *vb) 9356520fe55SH. Peter Anvin { 9365d442e63SKees Cook const uint32_t *a, *b; 9376520fe55SH. Peter Anvin a = va; b = vb; 9386520fe55SH. Peter Anvin return (*a == *b)? 0 : (*a > *b)? 1 : -1; 9396520fe55SH. Peter Anvin } 9406520fe55SH. Peter Anvin 9415d442e63SKees Cook static void sort_relocs(struct relocs *r) 9425d442e63SKees Cook { 9435d442e63SKees Cook qsort(r->offset, r->count, sizeof(r->offset[0]), cmp_relocs); 9445d442e63SKees Cook } 9455d442e63SKees Cook 9465d442e63SKees Cook static int write32(uint32_t v, FILE *f) 9476520fe55SH. Peter Anvin { 9486520fe55SH. Peter Anvin unsigned char buf[4]; 9496520fe55SH. Peter Anvin 9506520fe55SH. Peter Anvin put_unaligned_le32(v, buf); 9516520fe55SH. Peter Anvin return fwrite(buf, 1, 4, f) == 4 ? 0 : -1; 9526520fe55SH. Peter Anvin } 9536520fe55SH. Peter Anvin 9545d442e63SKees Cook static int write32_as_text(uint32_t v, FILE *f) 9555d442e63SKees Cook { 9565d442e63SKees Cook return fprintf(f, "\t.long 0x%08"PRIx32"\n", v) > 0 ? 0 : -1; 9575d442e63SKees Cook } 9585d442e63SKees Cook 9596520fe55SH. Peter Anvin static void emit_relocs(int as_text, int use_real_mode) 9606520fe55SH. Peter Anvin { 9616520fe55SH. Peter Anvin int i; 9625d442e63SKees Cook int (*write_reloc)(uint32_t, FILE *) = write32; 963946166afSKees Cook int (*do_reloc)(struct section *sec, Elf_Rel *rel, Elf_Sym *sym, 964946166afSKees Cook const char *symname); 965946166afSKees Cook 966c889ba80SH. Peter Anvin #if ELF_BITS == 64 967c889ba80SH. Peter Anvin if (!use_real_mode) 968946166afSKees Cook do_reloc = do_reloc64; 969c889ba80SH. Peter Anvin else 970c889ba80SH. Peter Anvin die("--realmode not valid for a 64-bit ELF file"); 971c889ba80SH. Peter Anvin #else 972c889ba80SH. Peter Anvin if (!use_real_mode) 973946166afSKees Cook do_reloc = do_reloc32; 974946166afSKees Cook else 975946166afSKees Cook do_reloc = do_reloc_real; 976c889ba80SH. Peter Anvin #endif 9776520fe55SH. Peter Anvin 9786520fe55SH. Peter Anvin /* Collect up the relocations */ 979946166afSKees Cook walk_relocs(do_reloc); 9806520fe55SH. Peter Anvin 9815d442e63SKees Cook if (relocs16.count && !use_real_mode) 9826520fe55SH. Peter Anvin die("Segment relocations found but --realmode not specified\n"); 9836520fe55SH. Peter Anvin 9846520fe55SH. Peter Anvin /* Order the relocations for more efficient processing */ 9855d442e63SKees Cook sort_relocs(&relocs16); 9865d442e63SKees Cook sort_relocs(&relocs32); 987946166afSKees Cook sort_relocs(&relocs64); 9886520fe55SH. Peter Anvin 9896520fe55SH. Peter Anvin /* Print the relocations */ 9906520fe55SH. Peter Anvin if (as_text) { 9916520fe55SH. Peter Anvin /* Print the relocations in a form suitable that 9926520fe55SH. Peter Anvin * gas will like. 9936520fe55SH. Peter Anvin */ 9946520fe55SH. Peter Anvin printf(".section \".data.reloc\",\"a\"\n"); 9956520fe55SH. Peter Anvin printf(".balign 4\n"); 9965d442e63SKees Cook write_reloc = write32_as_text; 9976520fe55SH. Peter Anvin } 9985d442e63SKees Cook 9995d442e63SKees Cook if (use_real_mode) { 10005d442e63SKees Cook write_reloc(relocs16.count, stdout); 10015d442e63SKees Cook for (i = 0; i < relocs16.count; i++) 10025d442e63SKees Cook write_reloc(relocs16.offset[i], stdout); 10035d442e63SKees Cook 10045d442e63SKees Cook write_reloc(relocs32.count, stdout); 10055d442e63SKees Cook for (i = 0; i < relocs32.count; i++) 10065d442e63SKees Cook write_reloc(relocs32.offset[i], stdout); 10076520fe55SH. Peter Anvin } else { 1008c889ba80SH. Peter Anvin if (ELF_BITS == 64) { 1009946166afSKees Cook /* Print a stop */ 1010946166afSKees Cook write_reloc(0, stdout); 1011946166afSKees Cook 1012946166afSKees Cook /* Now print each relocation */ 1013946166afSKees Cook for (i = 0; i < relocs64.count; i++) 1014946166afSKees Cook write_reloc(relocs64.offset[i], stdout); 1015946166afSKees Cook } 1016946166afSKees Cook 10176520fe55SH. Peter Anvin /* Print a stop */ 10185d442e63SKees Cook write_reloc(0, stdout); 10196520fe55SH. Peter Anvin 10206520fe55SH. Peter Anvin /* Now print each relocation */ 10215d442e63SKees Cook for (i = 0; i < relocs32.count; i++) 10225d442e63SKees Cook write_reloc(relocs32.offset[i], stdout); 10236520fe55SH. Peter Anvin } 10246520fe55SH. Peter Anvin } 10256520fe55SH. Peter Anvin 1026c889ba80SH. Peter Anvin #if ELF_BITS == 64 1027c889ba80SH. Peter Anvin # define process process_64 1028c889ba80SH. Peter Anvin #else 1029c889ba80SH. Peter Anvin # define process process_32 1030c889ba80SH. Peter Anvin #endif 10316520fe55SH. Peter Anvin 1032c889ba80SH. Peter Anvin void process(FILE *fp, int use_real_mode, int as_text, 1033c889ba80SH. Peter Anvin int show_absolute_syms, int show_absolute_relocs) 10346520fe55SH. Peter Anvin { 10356520fe55SH. Peter Anvin regex_init(use_real_mode); 10366520fe55SH. Peter Anvin read_ehdr(fp); 10376520fe55SH. Peter Anvin read_shdrs(fp); 10386520fe55SH. Peter Anvin read_strtabs(fp); 10396520fe55SH. Peter Anvin read_symtabs(fp); 10406520fe55SH. Peter Anvin read_relocs(fp); 1041c889ba80SH. Peter Anvin if (ELF_BITS == 64) 1042946166afSKees Cook percpu_init(); 10436520fe55SH. Peter Anvin if (show_absolute_syms) { 10446520fe55SH. Peter Anvin print_absolute_symbols(); 1045c889ba80SH. Peter Anvin return; 10466520fe55SH. Peter Anvin } 10476520fe55SH. Peter Anvin if (show_absolute_relocs) { 10486520fe55SH. Peter Anvin print_absolute_relocs(); 1049c889ba80SH. Peter Anvin return; 10506520fe55SH. Peter Anvin } 10516520fe55SH. Peter Anvin emit_relocs(as_text, use_real_mode); 10526520fe55SH. Peter Anvin } 1053