1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2c889ba80SH. Peter Anvin /* This is included from relocs_32/64.c */ 36520fe55SH. Peter Anvin 4bf11655cSKees Cook #define ElfW(type) _ElfW(ELF_BITS, type) 5bf11655cSKees Cook #define _ElfW(bits, type) __ElfW(bits, type) 6bf11655cSKees Cook #define __ElfW(bits, type) Elf##bits##_##type 7bf11655cSKees Cook 8946166afSKees Cook #define Elf_Addr ElfW(Addr) 9bf11655cSKees Cook #define Elf_Ehdr ElfW(Ehdr) 10bf11655cSKees Cook #define Elf_Phdr ElfW(Phdr) 11bf11655cSKees Cook #define Elf_Shdr ElfW(Shdr) 12bf11655cSKees Cook #define Elf_Sym ElfW(Sym) 13bf11655cSKees Cook 14bf11655cSKees Cook static Elf_Ehdr ehdr; 15f36e7495SArtem Savkov static unsigned long shnum; 16f36e7495SArtem Savkov static unsigned int shstrndx; 175d442e63SKees Cook 185d442e63SKees Cook struct relocs { 195d442e63SKees Cook uint32_t *offset; 205d442e63SKees Cook unsigned long count; 215d442e63SKees Cook unsigned long size; 225d442e63SKees Cook }; 235d442e63SKees Cook 245d442e63SKees Cook static struct relocs relocs16; 255d442e63SKees Cook static struct relocs relocs32; 266d24c5f7SJan Beulich #if ELF_BITS == 64 276d24c5f7SJan Beulich static struct relocs relocs32neg; 28946166afSKees Cook static struct relocs relocs64; 29*03dca99eSBorislav Petkov #define FMT PRIu64 30*03dca99eSBorislav Petkov #else 31*03dca99eSBorislav Petkov #define FMT PRIu32 326d24c5f7SJan Beulich #endif 336520fe55SH. Peter Anvin 346520fe55SH. Peter Anvin struct section { 35bf11655cSKees Cook Elf_Shdr shdr; 366520fe55SH. Peter Anvin struct section *link; 37bf11655cSKees Cook Elf_Sym *symtab; 38bf11655cSKees Cook Elf_Rel *reltab; 396520fe55SH. Peter Anvin char *strtab; 406520fe55SH. Peter Anvin }; 416520fe55SH. Peter Anvin static struct section *secs; 426520fe55SH. Peter Anvin 436520fe55SH. Peter Anvin static const char * const sym_regex_kernel[S_NSYMTYPES] = { 446520fe55SH. Peter Anvin /* 456520fe55SH. Peter Anvin * Following symbols have been audited. There values are constant and do 466520fe55SH. Peter Anvin * not change if bzImage is loaded at a different physical address than 476520fe55SH. Peter Anvin * the address for which it has been compiled. Don't warn user about 486520fe55SH. Peter Anvin * absolute relocations present w.r.t these symbols. 496520fe55SH. Peter Anvin */ 506520fe55SH. Peter Anvin [S_ABS] = 516520fe55SH. Peter Anvin "^(xen_irq_disable_direct_reloc$|" 526520fe55SH. Peter Anvin "xen_save_fl_direct_reloc$|" 536520fe55SH. Peter Anvin "VDSO|" 546520fe55SH. Peter Anvin "__crc_)", 556520fe55SH. Peter Anvin 566520fe55SH. Peter Anvin /* 576520fe55SH. Peter Anvin * These symbols are known to be relative, even if the linker marks them 586520fe55SH. Peter Anvin * as absolute (typically defined outside any section in the linker script.) 596520fe55SH. Peter Anvin */ 606520fe55SH. Peter Anvin [S_REL] = 61a3e854d9SH. Peter Anvin "^(__init_(begin|end)|" 62a3e854d9SH. Peter Anvin "__x86_cpu_dev_(start|end)|" 63fa953adfSH. Nikolaus Schaller "(__parainstructions|__alt_instructions)(_end)?|" 64fa953adfSH. Nikolaus Schaller "(__iommu_table|__apicdrivers|__smp_locks)(_end)?|" 65fd952815SH. Peter Anvin "__(start|end)_pci_.*|" 66fd952815SH. Peter Anvin "__(start|end)_builtin_fw|" 67fa953adfSH. Nikolaus Schaller "__(start|stop)___ksymtab(_gpl)?|" 68fa953adfSH. Nikolaus Schaller "__(start|stop)___kcrctab(_gpl)?|" 69fd952815SH. Peter Anvin "__(start|stop)___param|" 70fd952815SH. Peter Anvin "__(start|stop)___modver|" 71fd952815SH. Peter Anvin "__(start|stop)___bug_table|" 72fd952815SH. Peter Anvin "__tracedata_(start|end)|" 73fd952815SH. Peter Anvin "__(start|stop)_notes|" 74fd952815SH. Peter Anvin "__end_rodata|" 75a29dba16SJoerg Roedel "__end_rodata_aligned|" 76fd952815SH. Peter Anvin "__initramfs_start|" 77ea17e741SH. Peter Anvin "(jiffies|jiffies_64)|" 78c889ba80SH. Peter Anvin #if ELF_BITS == 64 79946166afSKees Cook "__per_cpu_load|" 80946166afSKees Cook "init_per_cpu__.*|" 81946166afSKees Cook "__end_rodata_hpage_align|" 82946166afSKees Cook #endif 83d2312e33SStefani Seibold "__vvar_page|" 84a3e854d9SH. Peter Anvin "_end)$" 856520fe55SH. Peter Anvin }; 866520fe55SH. Peter Anvin 876520fe55SH. Peter Anvin 886520fe55SH. Peter Anvin static const char * const sym_regex_realmode[S_NSYMTYPES] = { 896520fe55SH. Peter Anvin /* 90f2604c14SJarkko Sakkinen * These symbols are known to be relative, even if the linker marks them 91f2604c14SJarkko Sakkinen * as absolute (typically defined outside any section in the linker script.) 92f2604c14SJarkko Sakkinen */ 93f2604c14SJarkko Sakkinen [S_REL] = 94f2604c14SJarkko Sakkinen "^pa_", 95f2604c14SJarkko Sakkinen 96f2604c14SJarkko Sakkinen /* 976520fe55SH. Peter Anvin * These are 16-bit segment symbols when compiling 16-bit code. 986520fe55SH. Peter Anvin */ 996520fe55SH. Peter Anvin [S_SEG] = 1006520fe55SH. Peter Anvin "^real_mode_seg$", 1016520fe55SH. Peter Anvin 1026520fe55SH. Peter Anvin /* 1036520fe55SH. Peter Anvin * These are offsets belonging to segments, as opposed to linear addresses, 1046520fe55SH. Peter Anvin * when compiling 16-bit code. 1056520fe55SH. Peter Anvin */ 1066520fe55SH. Peter Anvin [S_LIN] = 1076520fe55SH. Peter Anvin "^pa_", 1086520fe55SH. Peter Anvin }; 1096520fe55SH. Peter Anvin 1106520fe55SH. Peter Anvin static const char * const *sym_regex; 1116520fe55SH. Peter Anvin 1126520fe55SH. Peter Anvin static regex_t sym_regex_c[S_NSYMTYPES]; 1136520fe55SH. Peter Anvin static int is_reloc(enum symtype type, const char *sym_name) 1146520fe55SH. Peter Anvin { 1156520fe55SH. Peter Anvin return sym_regex[type] && 1166520fe55SH. Peter Anvin !regexec(&sym_regex_c[type], sym_name, 0, NULL, 0); 1176520fe55SH. Peter Anvin } 1186520fe55SH. Peter Anvin 1196520fe55SH. Peter Anvin static void regex_init(int use_real_mode) 1206520fe55SH. Peter Anvin { 1216520fe55SH. Peter Anvin char errbuf[128]; 1226520fe55SH. Peter Anvin int err; 1236520fe55SH. Peter Anvin int i; 1246520fe55SH. Peter Anvin 1256520fe55SH. Peter Anvin if (use_real_mode) 1266520fe55SH. Peter Anvin sym_regex = sym_regex_realmode; 1276520fe55SH. Peter Anvin else 1286520fe55SH. Peter Anvin sym_regex = sym_regex_kernel; 1296520fe55SH. Peter Anvin 1306520fe55SH. Peter Anvin for (i = 0; i < S_NSYMTYPES; i++) { 1316520fe55SH. Peter Anvin if (!sym_regex[i]) 1326520fe55SH. Peter Anvin continue; 1336520fe55SH. Peter Anvin 1346520fe55SH. Peter Anvin err = regcomp(&sym_regex_c[i], sym_regex[i], 1356520fe55SH. Peter Anvin REG_EXTENDED|REG_NOSUB); 1366520fe55SH. Peter Anvin 1376520fe55SH. Peter Anvin if (err) { 1380e96f31eSJordan Borgner regerror(err, &sym_regex_c[i], errbuf, sizeof(errbuf)); 1396520fe55SH. Peter Anvin die("%s", errbuf); 1406520fe55SH. Peter Anvin } 1416520fe55SH. Peter Anvin } 1426520fe55SH. Peter Anvin } 1436520fe55SH. Peter Anvin 1446520fe55SH. Peter Anvin static const char *sym_type(unsigned type) 1456520fe55SH. Peter Anvin { 1466520fe55SH. Peter Anvin static const char *type_name[] = { 1476520fe55SH. Peter Anvin #define SYM_TYPE(X) [X] = #X 1486520fe55SH. Peter Anvin SYM_TYPE(STT_NOTYPE), 1496520fe55SH. Peter Anvin SYM_TYPE(STT_OBJECT), 1506520fe55SH. Peter Anvin SYM_TYPE(STT_FUNC), 1516520fe55SH. Peter Anvin SYM_TYPE(STT_SECTION), 1526520fe55SH. Peter Anvin SYM_TYPE(STT_FILE), 1536520fe55SH. Peter Anvin SYM_TYPE(STT_COMMON), 1546520fe55SH. Peter Anvin SYM_TYPE(STT_TLS), 1556520fe55SH. Peter Anvin #undef SYM_TYPE 1566520fe55SH. Peter Anvin }; 1576520fe55SH. Peter Anvin const char *name = "unknown sym type name"; 1586520fe55SH. Peter Anvin if (type < ARRAY_SIZE(type_name)) { 1596520fe55SH. Peter Anvin name = type_name[type]; 1606520fe55SH. Peter Anvin } 1616520fe55SH. Peter Anvin return name; 1626520fe55SH. Peter Anvin } 1636520fe55SH. Peter Anvin 1646520fe55SH. Peter Anvin static const char *sym_bind(unsigned bind) 1656520fe55SH. Peter Anvin { 1666520fe55SH. Peter Anvin static const char *bind_name[] = { 1676520fe55SH. Peter Anvin #define SYM_BIND(X) [X] = #X 1686520fe55SH. Peter Anvin SYM_BIND(STB_LOCAL), 1696520fe55SH. Peter Anvin SYM_BIND(STB_GLOBAL), 1706520fe55SH. Peter Anvin SYM_BIND(STB_WEAK), 1716520fe55SH. Peter Anvin #undef SYM_BIND 1726520fe55SH. Peter Anvin }; 1736520fe55SH. Peter Anvin const char *name = "unknown sym bind name"; 1746520fe55SH. Peter Anvin if (bind < ARRAY_SIZE(bind_name)) { 1756520fe55SH. Peter Anvin name = bind_name[bind]; 1766520fe55SH. Peter Anvin } 1776520fe55SH. Peter Anvin return name; 1786520fe55SH. Peter Anvin } 1796520fe55SH. Peter Anvin 1806520fe55SH. Peter Anvin static const char *sym_visibility(unsigned visibility) 1816520fe55SH. Peter Anvin { 1826520fe55SH. Peter Anvin static const char *visibility_name[] = { 1836520fe55SH. Peter Anvin #define SYM_VISIBILITY(X) [X] = #X 1846520fe55SH. Peter Anvin SYM_VISIBILITY(STV_DEFAULT), 1856520fe55SH. Peter Anvin SYM_VISIBILITY(STV_INTERNAL), 1866520fe55SH. Peter Anvin SYM_VISIBILITY(STV_HIDDEN), 1876520fe55SH. Peter Anvin SYM_VISIBILITY(STV_PROTECTED), 1886520fe55SH. Peter Anvin #undef SYM_VISIBILITY 1896520fe55SH. Peter Anvin }; 1906520fe55SH. Peter Anvin const char *name = "unknown sym visibility name"; 1916520fe55SH. Peter Anvin if (visibility < ARRAY_SIZE(visibility_name)) { 1926520fe55SH. Peter Anvin name = visibility_name[visibility]; 1936520fe55SH. Peter Anvin } 1946520fe55SH. Peter Anvin return name; 1956520fe55SH. Peter Anvin } 1966520fe55SH. Peter Anvin 1976520fe55SH. Peter Anvin static const char *rel_type(unsigned type) 1986520fe55SH. Peter Anvin { 1996520fe55SH. Peter Anvin static const char *type_name[] = { 2006520fe55SH. Peter Anvin #define REL_TYPE(X) [X] = #X 201c889ba80SH. Peter Anvin #if ELF_BITS == 64 202946166afSKees Cook REL_TYPE(R_X86_64_NONE), 203946166afSKees Cook REL_TYPE(R_X86_64_64), 204b40a142bSArd Biesheuvel REL_TYPE(R_X86_64_PC64), 205946166afSKees Cook REL_TYPE(R_X86_64_PC32), 206946166afSKees Cook REL_TYPE(R_X86_64_GOT32), 207946166afSKees Cook REL_TYPE(R_X86_64_PLT32), 208946166afSKees Cook REL_TYPE(R_X86_64_COPY), 209946166afSKees Cook REL_TYPE(R_X86_64_GLOB_DAT), 210946166afSKees Cook REL_TYPE(R_X86_64_JUMP_SLOT), 211946166afSKees Cook REL_TYPE(R_X86_64_RELATIVE), 212946166afSKees Cook REL_TYPE(R_X86_64_GOTPCREL), 213946166afSKees Cook REL_TYPE(R_X86_64_32), 214946166afSKees Cook REL_TYPE(R_X86_64_32S), 215946166afSKees Cook REL_TYPE(R_X86_64_16), 216946166afSKees Cook REL_TYPE(R_X86_64_PC16), 217946166afSKees Cook REL_TYPE(R_X86_64_8), 218946166afSKees Cook REL_TYPE(R_X86_64_PC8), 219946166afSKees Cook #else 2206520fe55SH. Peter Anvin REL_TYPE(R_386_NONE), 2216520fe55SH. Peter Anvin REL_TYPE(R_386_32), 2226520fe55SH. Peter Anvin REL_TYPE(R_386_PC32), 2236520fe55SH. Peter Anvin REL_TYPE(R_386_GOT32), 2246520fe55SH. Peter Anvin REL_TYPE(R_386_PLT32), 2256520fe55SH. Peter Anvin REL_TYPE(R_386_COPY), 2266520fe55SH. Peter Anvin REL_TYPE(R_386_GLOB_DAT), 2276520fe55SH. Peter Anvin REL_TYPE(R_386_JMP_SLOT), 2286520fe55SH. Peter Anvin REL_TYPE(R_386_RELATIVE), 2296520fe55SH. Peter Anvin REL_TYPE(R_386_GOTOFF), 2306520fe55SH. Peter Anvin REL_TYPE(R_386_GOTPC), 2316520fe55SH. Peter Anvin REL_TYPE(R_386_8), 2326520fe55SH. Peter Anvin REL_TYPE(R_386_PC8), 2336520fe55SH. Peter Anvin REL_TYPE(R_386_16), 2346520fe55SH. Peter Anvin REL_TYPE(R_386_PC16), 235946166afSKees Cook #endif 2366520fe55SH. Peter Anvin #undef REL_TYPE 2376520fe55SH. Peter Anvin }; 2386520fe55SH. Peter Anvin const char *name = "unknown type rel type name"; 2396520fe55SH. Peter Anvin if (type < ARRAY_SIZE(type_name) && type_name[type]) { 2406520fe55SH. Peter Anvin name = type_name[type]; 2416520fe55SH. Peter Anvin } 2426520fe55SH. Peter Anvin return name; 2436520fe55SH. Peter Anvin } 2446520fe55SH. Peter Anvin 2456520fe55SH. Peter Anvin static const char *sec_name(unsigned shndx) 2466520fe55SH. Peter Anvin { 2476520fe55SH. Peter Anvin const char *sec_strtab; 2486520fe55SH. Peter Anvin const char *name; 249f36e7495SArtem Savkov sec_strtab = secs[shstrndx].strtab; 2506520fe55SH. Peter Anvin name = "<noname>"; 251f36e7495SArtem Savkov if (shndx < shnum) { 2526520fe55SH. Peter Anvin name = sec_strtab + secs[shndx].shdr.sh_name; 2536520fe55SH. Peter Anvin } 2546520fe55SH. Peter Anvin else if (shndx == SHN_ABS) { 2556520fe55SH. Peter Anvin name = "ABSOLUTE"; 2566520fe55SH. Peter Anvin } 2576520fe55SH. Peter Anvin else if (shndx == SHN_COMMON) { 2586520fe55SH. Peter Anvin name = "COMMON"; 2596520fe55SH. Peter Anvin } 2606520fe55SH. Peter Anvin return name; 2616520fe55SH. Peter Anvin } 2626520fe55SH. Peter Anvin 263bf11655cSKees Cook static const char *sym_name(const char *sym_strtab, Elf_Sym *sym) 2646520fe55SH. Peter Anvin { 2656520fe55SH. Peter Anvin const char *name; 2666520fe55SH. Peter Anvin name = "<noname>"; 2676520fe55SH. Peter Anvin if (sym->st_name) { 2686520fe55SH. Peter Anvin name = sym_strtab + sym->st_name; 2696520fe55SH. Peter Anvin } 2706520fe55SH. Peter Anvin else { 2716520fe55SH. Peter Anvin name = sec_name(sym->st_shndx); 2726520fe55SH. Peter Anvin } 2736520fe55SH. Peter Anvin return name; 2746520fe55SH. Peter Anvin } 2756520fe55SH. Peter Anvin 276946166afSKees Cook static Elf_Sym *sym_lookup(const char *symname) 277946166afSKees Cook { 278946166afSKees Cook int i; 279f36e7495SArtem Savkov for (i = 0; i < shnum; i++) { 280946166afSKees Cook struct section *sec = &secs[i]; 281946166afSKees Cook long nsyms; 282946166afSKees Cook char *strtab; 283946166afSKees Cook Elf_Sym *symtab; 284946166afSKees Cook Elf_Sym *sym; 2856520fe55SH. Peter Anvin 286946166afSKees Cook if (sec->shdr.sh_type != SHT_SYMTAB) 287946166afSKees Cook continue; 288946166afSKees Cook 289946166afSKees Cook nsyms = sec->shdr.sh_size/sizeof(Elf_Sym); 290946166afSKees Cook symtab = sec->symtab; 291946166afSKees Cook strtab = sec->link->strtab; 292946166afSKees Cook 293946166afSKees Cook for (sym = symtab; --nsyms >= 0; sym++) { 294946166afSKees Cook if (!sym->st_name) 295946166afSKees Cook continue; 296946166afSKees Cook if (strcmp(symname, strtab + sym->st_name) == 0) 297946166afSKees Cook return sym; 298946166afSKees Cook } 299946166afSKees Cook } 300946166afSKees Cook return 0; 301946166afSKees Cook } 3026520fe55SH. Peter Anvin 3036520fe55SH. Peter Anvin #if BYTE_ORDER == LITTLE_ENDIAN 3046520fe55SH. Peter Anvin #define le16_to_cpu(val) (val) 3056520fe55SH. Peter Anvin #define le32_to_cpu(val) (val) 306946166afSKees Cook #define le64_to_cpu(val) (val) 3076520fe55SH. Peter Anvin #endif 3086520fe55SH. Peter Anvin #if BYTE_ORDER == BIG_ENDIAN 3096520fe55SH. Peter Anvin #define le16_to_cpu(val) bswap_16(val) 3106520fe55SH. Peter Anvin #define le32_to_cpu(val) bswap_32(val) 311946166afSKees Cook #define le64_to_cpu(val) bswap_64(val) 3126520fe55SH. Peter Anvin #endif 3136520fe55SH. Peter Anvin 3146520fe55SH. Peter Anvin static uint16_t elf16_to_cpu(uint16_t val) 3156520fe55SH. Peter Anvin { 3166520fe55SH. Peter Anvin return le16_to_cpu(val); 3176520fe55SH. Peter Anvin } 3186520fe55SH. Peter Anvin 3196520fe55SH. Peter Anvin static uint32_t elf32_to_cpu(uint32_t val) 3206520fe55SH. Peter Anvin { 3216520fe55SH. Peter Anvin return le32_to_cpu(val); 3226520fe55SH. Peter Anvin } 3236520fe55SH. Peter Anvin 324bf11655cSKees Cook #define elf_half_to_cpu(x) elf16_to_cpu(x) 325bf11655cSKees Cook #define elf_word_to_cpu(x) elf32_to_cpu(x) 326946166afSKees Cook 327c889ba80SH. Peter Anvin #if ELF_BITS == 64 328946166afSKees Cook static uint64_t elf64_to_cpu(uint64_t val) 329946166afSKees Cook { 330946166afSKees Cook return le64_to_cpu(val); 331946166afSKees Cook } 332946166afSKees Cook #define elf_addr_to_cpu(x) elf64_to_cpu(x) 333946166afSKees Cook #define elf_off_to_cpu(x) elf64_to_cpu(x) 334946166afSKees Cook #define elf_xword_to_cpu(x) elf64_to_cpu(x) 335946166afSKees Cook #else 336bf11655cSKees Cook #define elf_addr_to_cpu(x) elf32_to_cpu(x) 337bf11655cSKees Cook #define elf_off_to_cpu(x) elf32_to_cpu(x) 338bf11655cSKees Cook #define elf_xword_to_cpu(x) elf32_to_cpu(x) 339946166afSKees Cook #endif 340bf11655cSKees Cook 3416520fe55SH. Peter Anvin static void read_ehdr(FILE *fp) 3426520fe55SH. Peter Anvin { 3436520fe55SH. Peter Anvin if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) { 3446520fe55SH. Peter Anvin die("Cannot read ELF header: %s\n", 3456520fe55SH. Peter Anvin strerror(errno)); 3466520fe55SH. Peter Anvin } 3476520fe55SH. Peter Anvin if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) { 3486520fe55SH. Peter Anvin die("No ELF magic\n"); 3496520fe55SH. Peter Anvin } 350bf11655cSKees Cook if (ehdr.e_ident[EI_CLASS] != ELF_CLASS) { 351bf11655cSKees Cook die("Not a %d bit executable\n", ELF_BITS); 3526520fe55SH. Peter Anvin } 3536520fe55SH. Peter Anvin if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) { 3546520fe55SH. Peter Anvin die("Not a LSB ELF executable\n"); 3556520fe55SH. Peter Anvin } 3566520fe55SH. Peter Anvin if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) { 3576520fe55SH. Peter Anvin die("Unknown ELF version\n"); 3586520fe55SH. Peter Anvin } 3596520fe55SH. Peter Anvin /* Convert the fields to native endian */ 360bf11655cSKees Cook ehdr.e_type = elf_half_to_cpu(ehdr.e_type); 361bf11655cSKees Cook ehdr.e_machine = elf_half_to_cpu(ehdr.e_machine); 362bf11655cSKees Cook ehdr.e_version = elf_word_to_cpu(ehdr.e_version); 363bf11655cSKees Cook ehdr.e_entry = elf_addr_to_cpu(ehdr.e_entry); 364bf11655cSKees Cook ehdr.e_phoff = elf_off_to_cpu(ehdr.e_phoff); 365bf11655cSKees Cook ehdr.e_shoff = elf_off_to_cpu(ehdr.e_shoff); 366bf11655cSKees Cook ehdr.e_flags = elf_word_to_cpu(ehdr.e_flags); 367bf11655cSKees Cook ehdr.e_ehsize = elf_half_to_cpu(ehdr.e_ehsize); 368bf11655cSKees Cook ehdr.e_phentsize = elf_half_to_cpu(ehdr.e_phentsize); 369bf11655cSKees Cook ehdr.e_phnum = elf_half_to_cpu(ehdr.e_phnum); 370bf11655cSKees Cook ehdr.e_shentsize = elf_half_to_cpu(ehdr.e_shentsize); 371bf11655cSKees Cook ehdr.e_shnum = elf_half_to_cpu(ehdr.e_shnum); 372bf11655cSKees Cook ehdr.e_shstrndx = elf_half_to_cpu(ehdr.e_shstrndx); 3736520fe55SH. Peter Anvin 374f36e7495SArtem Savkov shnum = ehdr.e_shnum; 375f36e7495SArtem Savkov shstrndx = ehdr.e_shstrndx; 376f36e7495SArtem Savkov 377f36e7495SArtem Savkov if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN)) 3786520fe55SH. Peter Anvin die("Unsupported ELF header type\n"); 379f36e7495SArtem Savkov if (ehdr.e_machine != ELF_MACHINE) 380bf11655cSKees Cook die("Not for %s\n", ELF_MACHINE_NAME); 381f36e7495SArtem Savkov if (ehdr.e_version != EV_CURRENT) 3826520fe55SH. Peter Anvin die("Unknown ELF version\n"); 383f36e7495SArtem Savkov if (ehdr.e_ehsize != sizeof(Elf_Ehdr)) 3846520fe55SH. Peter Anvin die("Bad Elf header size\n"); 385f36e7495SArtem Savkov if (ehdr.e_phentsize != sizeof(Elf_Phdr)) 3866520fe55SH. Peter Anvin die("Bad program header entry\n"); 387f36e7495SArtem Savkov if (ehdr.e_shentsize != sizeof(Elf_Shdr)) 3886520fe55SH. Peter Anvin die("Bad section header entry\n"); 389f36e7495SArtem Savkov 390f36e7495SArtem Savkov 391f36e7495SArtem Savkov if (shnum == SHN_UNDEF || shstrndx == SHN_XINDEX) { 392f36e7495SArtem Savkov Elf_Shdr shdr; 393f36e7495SArtem Savkov 394f36e7495SArtem Savkov if (fseek(fp, ehdr.e_shoff, SEEK_SET) < 0) 395*03dca99eSBorislav Petkov die("Seek to %" FMT " failed: %s\n", ehdr.e_shoff, strerror(errno)); 396f36e7495SArtem Savkov 397f36e7495SArtem Savkov if (fread(&shdr, sizeof(shdr), 1, fp) != 1) 398f36e7495SArtem Savkov die("Cannot read initial ELF section header: %s\n", strerror(errno)); 399f36e7495SArtem Savkov 400f36e7495SArtem Savkov if (shnum == SHN_UNDEF) 401f36e7495SArtem Savkov shnum = elf_xword_to_cpu(shdr.sh_size); 402f36e7495SArtem Savkov 403f36e7495SArtem Savkov if (shstrndx == SHN_XINDEX) 404f36e7495SArtem Savkov shstrndx = elf_word_to_cpu(shdr.sh_link); 4056520fe55SH. Peter Anvin } 406f36e7495SArtem Savkov 407f36e7495SArtem Savkov if (shstrndx >= shnum) 4086520fe55SH. Peter Anvin die("String table index out of bounds\n"); 4096520fe55SH. Peter Anvin } 4106520fe55SH. Peter Anvin 4116520fe55SH. Peter Anvin static void read_shdrs(FILE *fp) 4126520fe55SH. Peter Anvin { 4136520fe55SH. Peter Anvin int i; 414bf11655cSKees Cook Elf_Shdr shdr; 4156520fe55SH. Peter Anvin 416f36e7495SArtem Savkov secs = calloc(shnum, sizeof(struct section)); 4176520fe55SH. Peter Anvin if (!secs) { 418*03dca99eSBorislav Petkov die("Unable to allocate %ld section headers\n", 419f36e7495SArtem Savkov shnum); 4206520fe55SH. Peter Anvin } 4216520fe55SH. Peter Anvin if (fseek(fp, ehdr.e_shoff, SEEK_SET) < 0) { 422*03dca99eSBorislav Petkov die("Seek to %" FMT " failed: %s\n", 4236520fe55SH. Peter Anvin ehdr.e_shoff, strerror(errno)); 4246520fe55SH. Peter Anvin } 425f36e7495SArtem Savkov for (i = 0; i < shnum; i++) { 4266520fe55SH. Peter Anvin struct section *sec = &secs[i]; 4270e96f31eSJordan Borgner if (fread(&shdr, sizeof(shdr), 1, fp) != 1) 428*03dca99eSBorislav Petkov die("Cannot read ELF section headers %d/%ld: %s\n", 429f36e7495SArtem Savkov i, shnum, strerror(errno)); 430bf11655cSKees Cook sec->shdr.sh_name = elf_word_to_cpu(shdr.sh_name); 431bf11655cSKees Cook sec->shdr.sh_type = elf_word_to_cpu(shdr.sh_type); 432bf11655cSKees Cook sec->shdr.sh_flags = elf_xword_to_cpu(shdr.sh_flags); 433bf11655cSKees Cook sec->shdr.sh_addr = elf_addr_to_cpu(shdr.sh_addr); 434bf11655cSKees Cook sec->shdr.sh_offset = elf_off_to_cpu(shdr.sh_offset); 435bf11655cSKees Cook sec->shdr.sh_size = elf_xword_to_cpu(shdr.sh_size); 436bf11655cSKees Cook sec->shdr.sh_link = elf_word_to_cpu(shdr.sh_link); 437bf11655cSKees Cook sec->shdr.sh_info = elf_word_to_cpu(shdr.sh_info); 438bf11655cSKees Cook sec->shdr.sh_addralign = elf_xword_to_cpu(shdr.sh_addralign); 439bf11655cSKees Cook sec->shdr.sh_entsize = elf_xword_to_cpu(shdr.sh_entsize); 440f36e7495SArtem Savkov if (sec->shdr.sh_link < shnum) 4416520fe55SH. Peter Anvin sec->link = &secs[sec->shdr.sh_link]; 4426520fe55SH. Peter Anvin } 4436520fe55SH. Peter Anvin 4446520fe55SH. Peter Anvin } 4456520fe55SH. Peter Anvin 4466520fe55SH. Peter Anvin static void read_strtabs(FILE *fp) 4476520fe55SH. Peter Anvin { 4486520fe55SH. Peter Anvin int i; 449f36e7495SArtem Savkov for (i = 0; i < shnum; i++) { 4506520fe55SH. Peter Anvin struct section *sec = &secs[i]; 4516520fe55SH. Peter Anvin if (sec->shdr.sh_type != SHT_STRTAB) { 4526520fe55SH. Peter Anvin continue; 4536520fe55SH. Peter Anvin } 4546520fe55SH. Peter Anvin sec->strtab = malloc(sec->shdr.sh_size); 4556520fe55SH. Peter Anvin if (!sec->strtab) { 456*03dca99eSBorislav Petkov die("malloc of %" FMT " bytes for strtab 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) { 460*03dca99eSBorislav Petkov die("Seek to %" FMT " failed: %s\n", 4616520fe55SH. Peter Anvin sec->shdr.sh_offset, strerror(errno)); 4626520fe55SH. Peter Anvin } 4636520fe55SH. Peter Anvin if (fread(sec->strtab, 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 } 4686520fe55SH. Peter Anvin } 4696520fe55SH. Peter Anvin } 4706520fe55SH. Peter Anvin 4716520fe55SH. Peter Anvin static void read_symtabs(FILE *fp) 4726520fe55SH. Peter Anvin { 4736520fe55SH. Peter Anvin int i,j; 474f36e7495SArtem Savkov for (i = 0; i < shnum; i++) { 4756520fe55SH. Peter Anvin struct section *sec = &secs[i]; 4766520fe55SH. Peter Anvin if (sec->shdr.sh_type != SHT_SYMTAB) { 4776520fe55SH. Peter Anvin continue; 4786520fe55SH. Peter Anvin } 4796520fe55SH. Peter Anvin sec->symtab = malloc(sec->shdr.sh_size); 4806520fe55SH. Peter Anvin if (!sec->symtab) { 481*03dca99eSBorislav Petkov die("malloc of %" FMT " bytes for symtab failed\n", 4826520fe55SH. Peter Anvin sec->shdr.sh_size); 4836520fe55SH. Peter Anvin } 4846520fe55SH. Peter Anvin if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { 485*03dca99eSBorislav Petkov die("Seek to %" FMT " failed: %s\n", 4866520fe55SH. Peter Anvin sec->shdr.sh_offset, strerror(errno)); 4876520fe55SH. Peter Anvin } 4886520fe55SH. Peter Anvin if (fread(sec->symtab, 1, sec->shdr.sh_size, fp) 4896520fe55SH. Peter Anvin != sec->shdr.sh_size) { 4906520fe55SH. Peter Anvin die("Cannot read symbol table: %s\n", 4916520fe55SH. Peter Anvin strerror(errno)); 4926520fe55SH. Peter Anvin } 493bf11655cSKees Cook for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Sym); j++) { 494bf11655cSKees Cook Elf_Sym *sym = &sec->symtab[j]; 495bf11655cSKees Cook sym->st_name = elf_word_to_cpu(sym->st_name); 496bf11655cSKees Cook sym->st_value = elf_addr_to_cpu(sym->st_value); 497bf11655cSKees Cook sym->st_size = elf_xword_to_cpu(sym->st_size); 498bf11655cSKees Cook sym->st_shndx = elf_half_to_cpu(sym->st_shndx); 4996520fe55SH. Peter Anvin } 5006520fe55SH. Peter Anvin } 5016520fe55SH. Peter Anvin } 5026520fe55SH. Peter Anvin 5036520fe55SH. Peter Anvin 5046520fe55SH. Peter Anvin static void read_relocs(FILE *fp) 5056520fe55SH. Peter Anvin { 5066520fe55SH. Peter Anvin int i,j; 507f36e7495SArtem Savkov for (i = 0; i < shnum; i++) { 5086520fe55SH. Peter Anvin struct section *sec = &secs[i]; 509bf11655cSKees Cook if (sec->shdr.sh_type != SHT_REL_TYPE) { 5106520fe55SH. Peter Anvin continue; 5116520fe55SH. Peter Anvin } 5126520fe55SH. Peter Anvin sec->reltab = malloc(sec->shdr.sh_size); 5136520fe55SH. Peter Anvin if (!sec->reltab) { 514*03dca99eSBorislav Petkov die("malloc of %" FMT " bytes for relocs failed\n", 5156520fe55SH. Peter Anvin sec->shdr.sh_size); 5166520fe55SH. Peter Anvin } 5176520fe55SH. Peter Anvin if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { 518*03dca99eSBorislav Petkov die("Seek to %" FMT " failed: %s\n", 5196520fe55SH. Peter Anvin sec->shdr.sh_offset, strerror(errno)); 5206520fe55SH. Peter Anvin } 5216520fe55SH. Peter Anvin if (fread(sec->reltab, 1, sec->shdr.sh_size, fp) 5226520fe55SH. Peter Anvin != sec->shdr.sh_size) { 5236520fe55SH. Peter Anvin die("Cannot read symbol table: %s\n", 5246520fe55SH. Peter Anvin strerror(errno)); 5256520fe55SH. Peter Anvin } 526bf11655cSKees Cook for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) { 527bf11655cSKees Cook Elf_Rel *rel = &sec->reltab[j]; 528bf11655cSKees Cook rel->r_offset = elf_addr_to_cpu(rel->r_offset); 529bf11655cSKees Cook rel->r_info = elf_xword_to_cpu(rel->r_info); 530946166afSKees Cook #if (SHT_REL_TYPE == SHT_RELA) 531946166afSKees Cook rel->r_addend = elf_xword_to_cpu(rel->r_addend); 532946166afSKees Cook #endif 5336520fe55SH. Peter Anvin } 5346520fe55SH. Peter Anvin } 5356520fe55SH. Peter Anvin } 5366520fe55SH. Peter Anvin 5376520fe55SH. Peter Anvin 5386520fe55SH. Peter Anvin static void print_absolute_symbols(void) 5396520fe55SH. Peter Anvin { 5406520fe55SH. Peter Anvin int i; 541946166afSKees Cook const char *format; 542946166afSKees Cook 543c889ba80SH. Peter Anvin if (ELF_BITS == 64) 544946166afSKees Cook format = "%5d %016"PRIx64" %5"PRId64" %10s %10s %12s %s\n"; 545946166afSKees Cook else 546946166afSKees Cook format = "%5d %08"PRIx32" %5"PRId32" %10s %10s %12s %s\n"; 547946166afSKees Cook 5486520fe55SH. Peter Anvin printf("Absolute symbols\n"); 5496520fe55SH. Peter Anvin printf(" Num: Value Size Type Bind Visibility Name\n"); 550f36e7495SArtem Savkov for (i = 0; i < shnum; i++) { 5516520fe55SH. Peter Anvin struct section *sec = &secs[i]; 5526520fe55SH. Peter Anvin char *sym_strtab; 5536520fe55SH. Peter Anvin int j; 5546520fe55SH. Peter Anvin 5556520fe55SH. Peter Anvin if (sec->shdr.sh_type != SHT_SYMTAB) { 5566520fe55SH. Peter Anvin continue; 5576520fe55SH. Peter Anvin } 5586520fe55SH. Peter Anvin sym_strtab = sec->link->strtab; 559bf11655cSKees Cook for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Sym); j++) { 560bf11655cSKees Cook Elf_Sym *sym; 5616520fe55SH. Peter Anvin const char *name; 5626520fe55SH. Peter Anvin sym = &sec->symtab[j]; 5636520fe55SH. Peter Anvin name = sym_name(sym_strtab, sym); 5646520fe55SH. Peter Anvin if (sym->st_shndx != SHN_ABS) { 5656520fe55SH. Peter Anvin continue; 5666520fe55SH. Peter Anvin } 567946166afSKees Cook printf(format, 5686520fe55SH. Peter Anvin j, sym->st_value, sym->st_size, 569bf11655cSKees Cook sym_type(ELF_ST_TYPE(sym->st_info)), 570bf11655cSKees Cook sym_bind(ELF_ST_BIND(sym->st_info)), 571bf11655cSKees Cook sym_visibility(ELF_ST_VISIBILITY(sym->st_other)), 5726520fe55SH. Peter Anvin name); 5736520fe55SH. Peter Anvin } 5746520fe55SH. Peter Anvin } 5756520fe55SH. Peter Anvin printf("\n"); 5766520fe55SH. Peter Anvin } 5776520fe55SH. Peter Anvin 5786520fe55SH. Peter Anvin static void print_absolute_relocs(void) 5796520fe55SH. Peter Anvin { 5806520fe55SH. Peter Anvin int i, printed = 0; 581946166afSKees Cook const char *format; 582946166afSKees Cook 583c889ba80SH. Peter Anvin if (ELF_BITS == 64) 584946166afSKees Cook format = "%016"PRIx64" %016"PRIx64" %10s %016"PRIx64" %s\n"; 585946166afSKees Cook else 586946166afSKees Cook format = "%08"PRIx32" %08"PRIx32" %10s %08"PRIx32" %s\n"; 5876520fe55SH. Peter Anvin 588f36e7495SArtem Savkov for (i = 0; i < shnum; i++) { 5896520fe55SH. Peter Anvin struct section *sec = &secs[i]; 5906520fe55SH. Peter Anvin struct section *sec_applies, *sec_symtab; 5916520fe55SH. Peter Anvin char *sym_strtab; 592bf11655cSKees Cook Elf_Sym *sh_symtab; 5936520fe55SH. Peter Anvin int j; 594bf11655cSKees Cook if (sec->shdr.sh_type != SHT_REL_TYPE) { 5956520fe55SH. Peter Anvin continue; 5966520fe55SH. Peter Anvin } 5976520fe55SH. Peter Anvin sec_symtab = sec->link; 5986520fe55SH. Peter Anvin sec_applies = &secs[sec->shdr.sh_info]; 5996520fe55SH. Peter Anvin if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) { 6006520fe55SH. Peter Anvin continue; 6016520fe55SH. Peter Anvin } 6026520fe55SH. Peter Anvin sh_symtab = sec_symtab->symtab; 6036520fe55SH. Peter Anvin sym_strtab = sec_symtab->link->strtab; 604bf11655cSKees Cook for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) { 605bf11655cSKees Cook Elf_Rel *rel; 606bf11655cSKees Cook Elf_Sym *sym; 6076520fe55SH. Peter Anvin const char *name; 6086520fe55SH. Peter Anvin rel = &sec->reltab[j]; 609bf11655cSKees Cook sym = &sh_symtab[ELF_R_SYM(rel->r_info)]; 6106520fe55SH. Peter Anvin name = sym_name(sym_strtab, sym); 6116520fe55SH. Peter Anvin if (sym->st_shndx != SHN_ABS) { 6126520fe55SH. Peter Anvin continue; 6136520fe55SH. Peter Anvin } 6146520fe55SH. Peter Anvin 6156520fe55SH. Peter Anvin /* Absolute symbols are not relocated if bzImage is 6166520fe55SH. Peter Anvin * loaded at a non-compiled address. Display a warning 6176520fe55SH. Peter Anvin * to user at compile time about the absolute 6186520fe55SH. Peter Anvin * relocations present. 6196520fe55SH. Peter Anvin * 6206520fe55SH. Peter Anvin * User need to audit the code to make sure 6216520fe55SH. Peter Anvin * some symbols which should have been section 6226520fe55SH. Peter Anvin * relative have not become absolute because of some 6236520fe55SH. Peter Anvin * linker optimization or wrong programming usage. 6246520fe55SH. Peter Anvin * 6256520fe55SH. Peter Anvin * Before warning check if this absolute symbol 6266520fe55SH. Peter Anvin * relocation is harmless. 6276520fe55SH. Peter Anvin */ 6286520fe55SH. Peter Anvin if (is_reloc(S_ABS, name) || is_reloc(S_REL, name)) 6296520fe55SH. Peter Anvin continue; 6306520fe55SH. Peter Anvin 6316520fe55SH. Peter Anvin if (!printed) { 6326520fe55SH. Peter Anvin printf("WARNING: Absolute relocations" 6336520fe55SH. Peter Anvin " present\n"); 6346520fe55SH. Peter Anvin printf("Offset Info Type Sym.Value " 6356520fe55SH. Peter Anvin "Sym.Name\n"); 6366520fe55SH. Peter Anvin printed = 1; 6376520fe55SH. Peter Anvin } 6386520fe55SH. Peter Anvin 639946166afSKees Cook printf(format, 6406520fe55SH. Peter Anvin rel->r_offset, 6416520fe55SH. Peter Anvin rel->r_info, 642bf11655cSKees Cook rel_type(ELF_R_TYPE(rel->r_info)), 6436520fe55SH. Peter Anvin sym->st_value, 6446520fe55SH. Peter Anvin name); 6456520fe55SH. Peter Anvin } 6466520fe55SH. Peter Anvin } 6476520fe55SH. Peter Anvin 6486520fe55SH. Peter Anvin if (printed) 6496520fe55SH. Peter Anvin printf("\n"); 6506520fe55SH. Peter Anvin } 6516520fe55SH. Peter Anvin 6525d442e63SKees Cook static void add_reloc(struct relocs *r, uint32_t offset) 6535d442e63SKees Cook { 6545d442e63SKees Cook if (r->count == r->size) { 6555d442e63SKees Cook unsigned long newsize = r->size + 50000; 6565d442e63SKees Cook void *mem = realloc(r->offset, newsize * sizeof(r->offset[0])); 6575d442e63SKees Cook 6585d442e63SKees Cook if (!mem) 6595d442e63SKees Cook die("realloc of %ld entries for relocs failed\n", 6605d442e63SKees Cook newsize); 6615d442e63SKees Cook r->offset = mem; 6625d442e63SKees Cook r->size = newsize; 6635d442e63SKees Cook } 6645d442e63SKees Cook r->offset[r->count++] = offset; 6655d442e63SKees Cook } 6665d442e63SKees Cook 6675d442e63SKees Cook static void walk_relocs(int (*process)(struct section *sec, Elf_Rel *rel, 6685d442e63SKees Cook Elf_Sym *sym, const char *symname)) 6696520fe55SH. Peter Anvin { 6706520fe55SH. Peter Anvin int i; 6716520fe55SH. Peter Anvin /* Walk through the relocations */ 672f36e7495SArtem Savkov for (i = 0; i < shnum; i++) { 6736520fe55SH. Peter Anvin char *sym_strtab; 674bf11655cSKees Cook Elf_Sym *sh_symtab; 6756520fe55SH. Peter Anvin struct section *sec_applies, *sec_symtab; 6766520fe55SH. Peter Anvin int j; 6776520fe55SH. Peter Anvin struct section *sec = &secs[i]; 6786520fe55SH. Peter Anvin 679bf11655cSKees Cook if (sec->shdr.sh_type != SHT_REL_TYPE) { 6806520fe55SH. Peter Anvin continue; 6816520fe55SH. Peter Anvin } 6826520fe55SH. Peter Anvin sec_symtab = sec->link; 6836520fe55SH. Peter Anvin sec_applies = &secs[sec->shdr.sh_info]; 6846520fe55SH. Peter Anvin if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) { 6856520fe55SH. Peter Anvin continue; 6866520fe55SH. Peter Anvin } 6876520fe55SH. Peter Anvin sh_symtab = sec_symtab->symtab; 6886520fe55SH. Peter Anvin sym_strtab = sec_symtab->link->strtab; 689bf11655cSKees Cook for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) { 6905d442e63SKees Cook Elf_Rel *rel = &sec->reltab[j]; 6915d442e63SKees Cook Elf_Sym *sym = &sh_symtab[ELF_R_SYM(rel->r_info)]; 6925d442e63SKees Cook const char *symname = sym_name(sym_strtab, sym); 69324ab82bdSH. Peter Anvin 6945d442e63SKees Cook process(sec, rel, sym, symname); 6955d442e63SKees Cook } 6965d442e63SKees Cook } 6975d442e63SKees Cook } 6986520fe55SH. Peter Anvin 699946166afSKees Cook /* 700946166afSKees Cook * The .data..percpu section is a special case for x86_64 SMP kernels. 701946166afSKees Cook * It is used to initialize the actual per_cpu areas and to provide 702946166afSKees Cook * definitions for the per_cpu variables that correspond to their offsets 703946166afSKees Cook * within the percpu area. Since the values of all of the symbols need 704946166afSKees Cook * to be offsets from the start of the per_cpu area the virtual address 705946166afSKees Cook * (sh_addr) of .data..percpu is 0 in SMP kernels. 706946166afSKees Cook * 707946166afSKees Cook * This means that: 708946166afSKees Cook * 709946166afSKees Cook * Relocations that reference symbols in the per_cpu area do not 710946166afSKees Cook * need further relocation (since the value is an offset relative 711946166afSKees Cook * to the start of the per_cpu area that does not change). 712946166afSKees Cook * 713946166afSKees Cook * Relocations that apply to the per_cpu area need to have their 714946166afSKees Cook * offset adjusted by by the value of __per_cpu_load to make them 715946166afSKees Cook * point to the correct place in the loaded image (because the 716946166afSKees Cook * virtual address of .data..percpu is 0). 717946166afSKees Cook * 718946166afSKees Cook * For non SMP kernels .data..percpu is linked as part of the normal 719946166afSKees Cook * kernel data and does not require special treatment. 720946166afSKees Cook * 721946166afSKees Cook */ 722946166afSKees Cook static int per_cpu_shndx = -1; 723eeeda4cdSBen Hutchings static Elf_Addr per_cpu_load_addr; 724946166afSKees Cook 725946166afSKees Cook static void percpu_init(void) 726946166afSKees Cook { 727946166afSKees Cook int i; 728f36e7495SArtem Savkov for (i = 0; i < shnum; i++) { 729946166afSKees Cook ElfW(Sym) *sym; 730946166afSKees Cook if (strcmp(sec_name(i), ".data..percpu")) 731946166afSKees Cook continue; 732946166afSKees Cook 733946166afSKees Cook if (secs[i].shdr.sh_addr != 0) /* non SMP kernel */ 734946166afSKees Cook return; 735946166afSKees Cook 736946166afSKees Cook sym = sym_lookup("__per_cpu_load"); 737946166afSKees Cook if (!sym) 738946166afSKees Cook die("can't find __per_cpu_load\n"); 739946166afSKees Cook 740946166afSKees Cook per_cpu_shndx = i; 741946166afSKees Cook per_cpu_load_addr = sym->st_value; 742946166afSKees Cook return; 743946166afSKees Cook } 744946166afSKees Cook } 745946166afSKees Cook 746c889ba80SH. Peter Anvin #if ELF_BITS == 64 747c889ba80SH. Peter Anvin 748946166afSKees Cook /* 749946166afSKees Cook * Check to see if a symbol lies in the .data..percpu section. 750d751c169SMichael Davidson * 751d751c169SMichael Davidson * The linker incorrectly associates some symbols with the 752d751c169SMichael Davidson * .data..percpu section so we also need to check the symbol 753d751c169SMichael Davidson * name to make sure that we classify the symbol correctly. 754d751c169SMichael Davidson * 755d751c169SMichael Davidson * The GNU linker incorrectly associates: 756d751c169SMichael Davidson * __init_begin 757aec58bafSKees Cook * __per_cpu_load 758d751c169SMichael Davidson * 759d751c169SMichael Davidson * The "gold" linker incorrectly associates: 760e6401c13SAndy Lutomirski * init_per_cpu__fixed_percpu_data 761d751c169SMichael Davidson * init_per_cpu__gdt_page 762946166afSKees Cook */ 763946166afSKees Cook static int is_percpu_sym(ElfW(Sym) *sym, const char *symname) 764946166afSKees Cook { 765946166afSKees Cook return (sym->st_shndx == per_cpu_shndx) && 766d751c169SMichael Davidson strcmp(symname, "__init_begin") && 767aec58bafSKees Cook strcmp(symname, "__per_cpu_load") && 768d751c169SMichael Davidson strncmp(symname, "init_per_cpu_", 13); 769946166afSKees Cook } 770946166afSKees Cook 771c889ba80SH. Peter Anvin 772946166afSKees Cook static int do_reloc64(struct section *sec, Elf_Rel *rel, ElfW(Sym) *sym, 773946166afSKees Cook const char *symname) 774946166afSKees Cook { 775946166afSKees Cook unsigned r_type = ELF64_R_TYPE(rel->r_info); 776946166afSKees Cook ElfW(Addr) offset = rel->r_offset; 777946166afSKees Cook int shn_abs = (sym->st_shndx == SHN_ABS) && !is_reloc(S_REL, symname); 778946166afSKees Cook 779946166afSKees Cook if (sym->st_shndx == SHN_UNDEF) 780946166afSKees Cook return 0; 781946166afSKees Cook 782946166afSKees Cook /* 783946166afSKees Cook * Adjust the offset if this reloc applies to the percpu section. 784946166afSKees Cook */ 785946166afSKees Cook if (sec->shdr.sh_info == per_cpu_shndx) 786946166afSKees Cook offset += per_cpu_load_addr; 787946166afSKees Cook 788946166afSKees Cook switch (r_type) { 789946166afSKees Cook case R_X86_64_NONE: 7906d24c5f7SJan Beulich /* NONE can be ignored. */ 7916d24c5f7SJan Beulich break; 7926d24c5f7SJan Beulich 793946166afSKees Cook case R_X86_64_PC32: 794b21ebf2fSH.J. Lu case R_X86_64_PLT32: 795946166afSKees Cook /* 7966d24c5f7SJan Beulich * PC relative relocations don't need to be adjusted unless 7976d24c5f7SJan Beulich * referencing a percpu symbol. 798b21ebf2fSH.J. Lu * 799b21ebf2fSH.J. Lu * NB: R_X86_64_PLT32 can be treated as R_X86_64_PC32. 800946166afSKees Cook */ 8016d24c5f7SJan Beulich if (is_percpu_sym(sym, symname)) 8026d24c5f7SJan Beulich add_reloc(&relocs32neg, offset); 803946166afSKees Cook break; 804946166afSKees Cook 805b40a142bSArd Biesheuvel case R_X86_64_PC64: 806b40a142bSArd Biesheuvel /* 807b40a142bSArd Biesheuvel * Only used by jump labels 808b40a142bSArd Biesheuvel */ 809b40a142bSArd Biesheuvel if (is_percpu_sym(sym, symname)) 810b40a142bSArd Biesheuvel die("Invalid R_X86_64_PC64 relocation against per-CPU symbol %s\n", 811b40a142bSArd Biesheuvel symname); 812b40a142bSArd Biesheuvel break; 813b40a142bSArd Biesheuvel 814946166afSKees Cook case R_X86_64_32: 815946166afSKees Cook case R_X86_64_32S: 816946166afSKees Cook case R_X86_64_64: 817946166afSKees Cook /* 818946166afSKees Cook * References to the percpu area don't need to be adjusted. 819946166afSKees Cook */ 820946166afSKees Cook if (is_percpu_sym(sym, symname)) 821946166afSKees Cook break; 822946166afSKees Cook 823946166afSKees Cook if (shn_abs) { 824946166afSKees Cook /* 825946166afSKees Cook * Whitelisted absolute symbols do not require 826946166afSKees Cook * relocation. 827946166afSKees Cook */ 828946166afSKees Cook if (is_reloc(S_ABS, symname)) 829946166afSKees Cook break; 830946166afSKees Cook 831946166afSKees Cook die("Invalid absolute %s relocation: %s\n", 832946166afSKees Cook rel_type(r_type), symname); 833946166afSKees Cook break; 834946166afSKees Cook } 835946166afSKees Cook 836946166afSKees Cook /* 837946166afSKees Cook * Relocation offsets for 64 bit kernels are output 838946166afSKees Cook * as 32 bits and sign extended back to 64 bits when 839946166afSKees Cook * the relocations are processed. 840946166afSKees Cook * Make sure that the offset will fit. 841946166afSKees Cook */ 842946166afSKees Cook if ((int32_t)offset != (int64_t)offset) 843946166afSKees Cook die("Relocation offset doesn't fit in 32 bits\n"); 844946166afSKees Cook 845946166afSKees Cook if (r_type == R_X86_64_64) 846946166afSKees Cook add_reloc(&relocs64, offset); 847946166afSKees Cook else 848946166afSKees Cook add_reloc(&relocs32, offset); 849946166afSKees Cook break; 850946166afSKees Cook 851946166afSKees Cook default: 852946166afSKees Cook die("Unsupported relocation type: %s (%d)\n", 853946166afSKees Cook rel_type(r_type), r_type); 854946166afSKees Cook break; 855946166afSKees Cook } 856946166afSKees Cook 857946166afSKees Cook return 0; 858946166afSKees Cook } 859946166afSKees Cook 860c889ba80SH. Peter Anvin #else 861946166afSKees Cook 862946166afSKees Cook static int do_reloc32(struct section *sec, Elf_Rel *rel, Elf_Sym *sym, 8635d442e63SKees Cook const char *symname) 8645d442e63SKees Cook { 8655d442e63SKees Cook unsigned r_type = ELF32_R_TYPE(rel->r_info); 8665d442e63SKees Cook int shn_abs = (sym->st_shndx == SHN_ABS) && !is_reloc(S_REL, symname); 86724ab82bdSH. Peter Anvin 8686520fe55SH. Peter Anvin switch (r_type) { 8696520fe55SH. Peter Anvin case R_386_NONE: 8706520fe55SH. Peter Anvin case R_386_PC32: 8716520fe55SH. Peter Anvin case R_386_PC16: 8726520fe55SH. Peter Anvin case R_386_PC8: 873bb73d071SFangrui Song case R_386_PLT32: 8746520fe55SH. Peter Anvin /* 875bb73d071SFangrui Song * NONE can be ignored and PC relative relocations don't need 876bb73d071SFangrui Song * to be adjusted. Because sym must be defined, R_386_PLT32 can 877bb73d071SFangrui Song * be treated the same way as R_386_PC32. 8786520fe55SH. Peter Anvin */ 8796520fe55SH. Peter Anvin break; 8806520fe55SH. Peter Anvin 8815d442e63SKees Cook case R_386_32: 88224ab82bdSH. Peter Anvin if (shn_abs) { 8835d442e63SKees Cook /* 8845d442e63SKees Cook * Whitelisted absolute symbols do not require 8855d442e63SKees Cook * relocation. 8865d442e63SKees Cook */ 8876520fe55SH. Peter Anvin if (is_reloc(S_ABS, symname)) 8886520fe55SH. Peter Anvin break; 8896520fe55SH. Peter Anvin 8905d442e63SKees Cook die("Invalid absolute %s relocation: %s\n", 8915d442e63SKees Cook rel_type(r_type), symname); 8926520fe55SH. Peter Anvin break; 8936520fe55SH. Peter Anvin } 8945d442e63SKees Cook 8955d442e63SKees Cook add_reloc(&relocs32, rel->r_offset); 8966520fe55SH. Peter Anvin break; 8975d442e63SKees Cook 8986520fe55SH. Peter Anvin default: 8996520fe55SH. Peter Anvin die("Unsupported relocation type: %s (%d)\n", 9006520fe55SH. Peter Anvin rel_type(r_type), r_type); 9016520fe55SH. Peter Anvin break; 9025d442e63SKees Cook } 9035d442e63SKees Cook 9045d442e63SKees Cook return 0; 9055d442e63SKees Cook } 9065d442e63SKees Cook 9075d442e63SKees Cook static int do_reloc_real(struct section *sec, Elf_Rel *rel, Elf_Sym *sym, 9085d442e63SKees Cook const char *symname) 9095d442e63SKees Cook { 9105d442e63SKees Cook unsigned r_type = ELF32_R_TYPE(rel->r_info); 9115d442e63SKees Cook int shn_abs = (sym->st_shndx == SHN_ABS) && !is_reloc(S_REL, symname); 9125d442e63SKees Cook 9135d442e63SKees Cook switch (r_type) { 9145d442e63SKees Cook case R_386_NONE: 9155d442e63SKees Cook case R_386_PC32: 9165d442e63SKees Cook case R_386_PC16: 9175d442e63SKees Cook case R_386_PC8: 918bb73d071SFangrui Song case R_386_PLT32: 9195d442e63SKees Cook /* 920bb73d071SFangrui Song * NONE can be ignored and PC relative relocations don't need 921bb73d071SFangrui Song * to be adjusted. Because sym must be defined, R_386_PLT32 can 922bb73d071SFangrui Song * be treated the same way as R_386_PC32. 9235d442e63SKees Cook */ 9245d442e63SKees Cook break; 9255d442e63SKees Cook 9265d442e63SKees Cook case R_386_16: 9275d442e63SKees Cook if (shn_abs) { 9285d442e63SKees Cook /* 9295d442e63SKees Cook * Whitelisted absolute symbols do not require 9305d442e63SKees Cook * relocation. 9315d442e63SKees Cook */ 9325d442e63SKees Cook if (is_reloc(S_ABS, symname)) 9335d442e63SKees Cook break; 9345d442e63SKees Cook 9355d442e63SKees Cook if (is_reloc(S_SEG, symname)) { 9365d442e63SKees Cook add_reloc(&relocs16, rel->r_offset); 9375d442e63SKees Cook break; 9385d442e63SKees Cook } 9395d442e63SKees Cook } else { 9405d442e63SKees Cook if (!is_reloc(S_LIN, symname)) 9415d442e63SKees Cook break; 9425d442e63SKees Cook } 94324ab82bdSH. Peter Anvin die("Invalid %s %s relocation: %s\n", 94424ab82bdSH. Peter Anvin shn_abs ? "absolute" : "relative", 9456520fe55SH. Peter Anvin rel_type(r_type), symname); 9465d442e63SKees Cook break; 9475d442e63SKees Cook 9485d442e63SKees Cook case R_386_32: 9495d442e63SKees Cook if (shn_abs) { 9505d442e63SKees Cook /* 9515d442e63SKees Cook * Whitelisted absolute symbols do not require 9525d442e63SKees Cook * relocation. 9535d442e63SKees Cook */ 9545d442e63SKees Cook if (is_reloc(S_ABS, symname)) 9555d442e63SKees Cook break; 9565d442e63SKees Cook 9575d442e63SKees Cook if (is_reloc(S_REL, symname)) { 9585d442e63SKees Cook add_reloc(&relocs32, rel->r_offset); 9595d442e63SKees Cook break; 9606520fe55SH. Peter Anvin } 9615d442e63SKees Cook } else { 9625d442e63SKees Cook if (is_reloc(S_LIN, symname)) 9635d442e63SKees Cook add_reloc(&relocs32, rel->r_offset); 9645d442e63SKees Cook break; 9656520fe55SH. Peter Anvin } 9665d442e63SKees Cook die("Invalid %s %s relocation: %s\n", 9675d442e63SKees Cook shn_abs ? "absolute" : "relative", 9685d442e63SKees Cook rel_type(r_type), symname); 9695d442e63SKees Cook break; 9705d442e63SKees Cook 9715d442e63SKees Cook default: 9725d442e63SKees Cook die("Unsupported relocation type: %s (%d)\n", 9735d442e63SKees Cook rel_type(r_type), r_type); 9745d442e63SKees Cook break; 9756520fe55SH. Peter Anvin } 9766520fe55SH. Peter Anvin 9775d442e63SKees Cook return 0; 9786520fe55SH. Peter Anvin } 9796520fe55SH. Peter Anvin 980c889ba80SH. Peter Anvin #endif 981c889ba80SH. Peter Anvin 9826520fe55SH. Peter Anvin static int cmp_relocs(const void *va, const void *vb) 9836520fe55SH. Peter Anvin { 9845d442e63SKees Cook const uint32_t *a, *b; 9856520fe55SH. Peter Anvin a = va; b = vb; 9866520fe55SH. Peter Anvin return (*a == *b)? 0 : (*a > *b)? 1 : -1; 9876520fe55SH. Peter Anvin } 9886520fe55SH. Peter Anvin 9895d442e63SKees Cook static void sort_relocs(struct relocs *r) 9905d442e63SKees Cook { 9915d442e63SKees Cook qsort(r->offset, r->count, sizeof(r->offset[0]), cmp_relocs); 9925d442e63SKees Cook } 9935d442e63SKees Cook 9945d442e63SKees Cook static int write32(uint32_t v, FILE *f) 9956520fe55SH. Peter Anvin { 9966520fe55SH. Peter Anvin unsigned char buf[4]; 9976520fe55SH. Peter Anvin 9986520fe55SH. Peter Anvin put_unaligned_le32(v, buf); 9996520fe55SH. Peter Anvin return fwrite(buf, 1, 4, f) == 4 ? 0 : -1; 10006520fe55SH. Peter Anvin } 10016520fe55SH. Peter Anvin 10025d442e63SKees Cook static int write32_as_text(uint32_t v, FILE *f) 10035d442e63SKees Cook { 10045d442e63SKees Cook return fprintf(f, "\t.long 0x%08"PRIx32"\n", v) > 0 ? 0 : -1; 10055d442e63SKees Cook } 10065d442e63SKees Cook 10076520fe55SH. Peter Anvin static void emit_relocs(int as_text, int use_real_mode) 10086520fe55SH. Peter Anvin { 10096520fe55SH. Peter Anvin int i; 10105d442e63SKees Cook int (*write_reloc)(uint32_t, FILE *) = write32; 1011946166afSKees Cook int (*do_reloc)(struct section *sec, Elf_Rel *rel, Elf_Sym *sym, 1012946166afSKees Cook const char *symname); 1013946166afSKees Cook 1014c889ba80SH. Peter Anvin #if ELF_BITS == 64 1015c889ba80SH. Peter Anvin if (!use_real_mode) 1016946166afSKees Cook do_reloc = do_reloc64; 1017c889ba80SH. Peter Anvin else 1018c889ba80SH. Peter Anvin die("--realmode not valid for a 64-bit ELF file"); 1019c889ba80SH. Peter Anvin #else 1020c889ba80SH. Peter Anvin if (!use_real_mode) 1021946166afSKees Cook do_reloc = do_reloc32; 1022946166afSKees Cook else 1023946166afSKees Cook do_reloc = do_reloc_real; 1024c889ba80SH. Peter Anvin #endif 10256520fe55SH. Peter Anvin 10266520fe55SH. Peter Anvin /* Collect up the relocations */ 1027946166afSKees Cook walk_relocs(do_reloc); 10286520fe55SH. Peter Anvin 10295d442e63SKees Cook if (relocs16.count && !use_real_mode) 10306520fe55SH. Peter Anvin die("Segment relocations found but --realmode not specified\n"); 10316520fe55SH. Peter Anvin 10326520fe55SH. Peter Anvin /* Order the relocations for more efficient processing */ 10335d442e63SKees Cook sort_relocs(&relocs32); 10346d24c5f7SJan Beulich #if ELF_BITS == 64 10356d24c5f7SJan Beulich sort_relocs(&relocs32neg); 1036946166afSKees Cook sort_relocs(&relocs64); 10377ebb9167SMarkus Trippelsdorf #else 10387ebb9167SMarkus Trippelsdorf sort_relocs(&relocs16); 10396d24c5f7SJan Beulich #endif 10406520fe55SH. Peter Anvin 10416520fe55SH. Peter Anvin /* Print the relocations */ 10426520fe55SH. Peter Anvin if (as_text) { 10436520fe55SH. Peter Anvin /* Print the relocations in a form suitable that 10446520fe55SH. Peter Anvin * gas will like. 10456520fe55SH. Peter Anvin */ 10466520fe55SH. Peter Anvin printf(".section \".data.reloc\",\"a\"\n"); 10476520fe55SH. Peter Anvin printf(".balign 4\n"); 10485d442e63SKees Cook write_reloc = write32_as_text; 10496520fe55SH. Peter Anvin } 10505d442e63SKees Cook 10515d442e63SKees Cook if (use_real_mode) { 10525d442e63SKees Cook write_reloc(relocs16.count, stdout); 10535d442e63SKees Cook for (i = 0; i < relocs16.count; i++) 10545d442e63SKees Cook write_reloc(relocs16.offset[i], stdout); 10555d442e63SKees Cook 10565d442e63SKees Cook write_reloc(relocs32.count, stdout); 10575d442e63SKees Cook for (i = 0; i < relocs32.count; i++) 10585d442e63SKees Cook write_reloc(relocs32.offset[i], stdout); 10596520fe55SH. Peter Anvin } else { 10606d24c5f7SJan Beulich #if ELF_BITS == 64 1061946166afSKees Cook /* Print a stop */ 1062946166afSKees Cook write_reloc(0, stdout); 1063946166afSKees Cook 1064946166afSKees Cook /* Now print each relocation */ 1065946166afSKees Cook for (i = 0; i < relocs64.count; i++) 1066946166afSKees Cook write_reloc(relocs64.offset[i], stdout); 10676d24c5f7SJan Beulich 10686d24c5f7SJan Beulich /* Print a stop */ 10696d24c5f7SJan Beulich write_reloc(0, stdout); 10706d24c5f7SJan Beulich 10716d24c5f7SJan Beulich /* Now print each inverse 32-bit relocation */ 10726d24c5f7SJan Beulich for (i = 0; i < relocs32neg.count; i++) 10736d24c5f7SJan Beulich write_reloc(relocs32neg.offset[i], stdout); 10746d24c5f7SJan Beulich #endif 1075946166afSKees Cook 10766520fe55SH. Peter Anvin /* Print a stop */ 10775d442e63SKees Cook write_reloc(0, stdout); 10786520fe55SH. Peter Anvin 10796520fe55SH. Peter Anvin /* Now print each relocation */ 10805d442e63SKees Cook for (i = 0; i < relocs32.count; i++) 10815d442e63SKees Cook write_reloc(relocs32.offset[i], stdout); 10826520fe55SH. Peter Anvin } 10836520fe55SH. Peter Anvin } 10846520fe55SH. Peter Anvin 1085214a8876SMichael Davidson /* 1086214a8876SMichael Davidson * As an aid to debugging problems with different linkers 1087214a8876SMichael Davidson * print summary information about the relocs. 1088214a8876SMichael Davidson * Since different linkers tend to emit the sections in 1089214a8876SMichael Davidson * different orders we use the section names in the output. 1090214a8876SMichael Davidson */ 1091214a8876SMichael Davidson static int do_reloc_info(struct section *sec, Elf_Rel *rel, ElfW(Sym) *sym, 1092214a8876SMichael Davidson const char *symname) 1093214a8876SMichael Davidson { 1094214a8876SMichael Davidson printf("%s\t%s\t%s\t%s\n", 1095214a8876SMichael Davidson sec_name(sec->shdr.sh_info), 1096214a8876SMichael Davidson rel_type(ELF_R_TYPE(rel->r_info)), 1097214a8876SMichael Davidson symname, 1098214a8876SMichael Davidson sec_name(sym->st_shndx)); 1099214a8876SMichael Davidson return 0; 1100214a8876SMichael Davidson } 1101214a8876SMichael Davidson 1102214a8876SMichael Davidson static void print_reloc_info(void) 1103214a8876SMichael Davidson { 1104214a8876SMichael Davidson printf("reloc section\treloc type\tsymbol\tsymbol section\n"); 1105214a8876SMichael Davidson walk_relocs(do_reloc_info); 1106214a8876SMichael Davidson } 1107214a8876SMichael Davidson 1108c889ba80SH. Peter Anvin #if ELF_BITS == 64 1109c889ba80SH. Peter Anvin # define process process_64 1110c889ba80SH. Peter Anvin #else 1111c889ba80SH. Peter Anvin # define process process_32 1112c889ba80SH. Peter Anvin #endif 11136520fe55SH. Peter Anvin 1114c889ba80SH. Peter Anvin void process(FILE *fp, int use_real_mode, int as_text, 1115214a8876SMichael Davidson int show_absolute_syms, int show_absolute_relocs, 1116214a8876SMichael Davidson int show_reloc_info) 11176520fe55SH. Peter Anvin { 11186520fe55SH. Peter Anvin regex_init(use_real_mode); 11196520fe55SH. Peter Anvin read_ehdr(fp); 11206520fe55SH. Peter Anvin read_shdrs(fp); 11216520fe55SH. Peter Anvin read_strtabs(fp); 11226520fe55SH. Peter Anvin read_symtabs(fp); 11236520fe55SH. Peter Anvin read_relocs(fp); 1124c889ba80SH. Peter Anvin if (ELF_BITS == 64) 1125946166afSKees Cook percpu_init(); 11266520fe55SH. Peter Anvin if (show_absolute_syms) { 11276520fe55SH. Peter Anvin print_absolute_symbols(); 1128c889ba80SH. Peter Anvin return; 11296520fe55SH. Peter Anvin } 11306520fe55SH. Peter Anvin if (show_absolute_relocs) { 11316520fe55SH. Peter Anvin print_absolute_relocs(); 1132c889ba80SH. Peter Anvin return; 11336520fe55SH. Peter Anvin } 1134214a8876SMichael Davidson if (show_reloc_info) { 1135214a8876SMichael Davidson print_reloc_info(); 1136214a8876SMichael Davidson return; 1137214a8876SMichael Davidson } 11386520fe55SH. Peter Anvin emit_relocs(as_text, use_real_mode); 11396520fe55SH. Peter Anvin } 1140