Commit a54c401a authored by Kristen Carlson Accardi's avatar Kristen Carlson Accardi Committed by Peter Zijlstra

x86/tools/relocs: Support >64K section headers

While the relocs tool already supports finding the total number of
section headers if vmlinux exceeds 64K sections, it fails to read the
extended symbol table to get section header indexes for symbols, causing
incorrect symbol table indexes to be used when there are > 64K symbols.

Parse the ELF file to read the extended symbol table info, and then
replace all direct references to st_shndx with calls to sym_index(),
which will determine whether the value can be read directly or whether
the value should be pulled out of the extended table.

This is needed for future FGKASLR support, which uses a separate section
per function.
Signed-off-by: default avatarKristen Carlson Accardi <kristen@linux.intel.com>
Signed-off-by: default avatarAlexander Lobakin <alexandr.lobakin@intel.com>
Signed-off-by: default avatarKees Cook <keescook@chromium.org>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: default avatarKees Cook <keescook@chromium.org>
Reviewed-by: default avatarTony Luck <tony.luck@intel.com>
Acked-by: default avatarH. Peter Anvin (Intel) <hpa@zytor.com>
Tested-by: default avatarTony Luck <tony.luck@intel.com>
Link: https://lore.kernel.org/r/20211013175742.1197608-2-keescook@chromium.org
parent 541ac971
...@@ -14,6 +14,10 @@ ...@@ -14,6 +14,10 @@
static Elf_Ehdr ehdr; static Elf_Ehdr ehdr;
static unsigned long shnum; static unsigned long shnum;
static unsigned int shstrndx; static unsigned int shstrndx;
static unsigned int shsymtabndx;
static unsigned int shxsymtabndx;
static int sym_index(Elf_Sym *sym);
struct relocs { struct relocs {
uint32_t *offset; uint32_t *offset;
...@@ -35,6 +39,7 @@ struct section { ...@@ -35,6 +39,7 @@ struct section {
Elf_Shdr shdr; Elf_Shdr shdr;
struct section *link; struct section *link;
Elf_Sym *symtab; Elf_Sym *symtab;
Elf32_Word *xsymtab;
Elf_Rel *reltab; Elf_Rel *reltab;
char *strtab; char *strtab;
}; };
...@@ -268,7 +273,7 @@ static const char *sym_name(const char *sym_strtab, Elf_Sym *sym) ...@@ -268,7 +273,7 @@ static const char *sym_name(const char *sym_strtab, Elf_Sym *sym)
name = sym_strtab + sym->st_name; name = sym_strtab + sym->st_name;
} }
else { else {
name = sec_name(sym->st_shndx); name = sec_name(sym_index(sym));
} }
return name; return name;
} }
...@@ -338,6 +343,23 @@ static uint64_t elf64_to_cpu(uint64_t val) ...@@ -338,6 +343,23 @@ static uint64_t elf64_to_cpu(uint64_t val)
#define elf_xword_to_cpu(x) elf32_to_cpu(x) #define elf_xword_to_cpu(x) elf32_to_cpu(x)
#endif #endif
static int sym_index(Elf_Sym *sym)
{
Elf_Sym *symtab = secs[shsymtabndx].symtab;
Elf32_Word *xsymtab = secs[shxsymtabndx].xsymtab;
unsigned long offset;
int index;
if (sym->st_shndx != SHN_XINDEX)
return sym->st_shndx;
/* calculate offset of sym from head of table. */
offset = (unsigned long)sym - (unsigned long)symtab;
index = offset / sizeof(*sym);
return elf32_to_cpu(xsymtab[index]);
}
static void read_ehdr(FILE *fp) static void read_ehdr(FILE *fp)
{ {
if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) { if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) {
...@@ -471,11 +493,33 @@ static void read_strtabs(FILE *fp) ...@@ -471,11 +493,33 @@ static void read_strtabs(FILE *fp)
static void read_symtabs(FILE *fp) static void read_symtabs(FILE *fp)
{ {
int i,j; int i,j;
for (i = 0; i < shnum; i++) { for (i = 0; i < shnum; i++) {
struct section *sec = &secs[i]; struct section *sec = &secs[i];
if (sec->shdr.sh_type != SHT_SYMTAB) { int num_syms;
continue;
switch (sec->shdr.sh_type) {
case SHT_SYMTAB_SHNDX:
sec->xsymtab = malloc(sec->shdr.sh_size);
if (!sec->xsymtab) {
die("malloc of %" FMT " bytes for xsymtab failed\n",
sec->shdr.sh_size);
}
if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) {
die("Seek to %" FMT " failed: %s\n",
sec->shdr.sh_offset, strerror(errno));
}
if (fread(sec->xsymtab, 1, sec->shdr.sh_size, fp)
!= sec->shdr.sh_size) {
die("Cannot read extended symbol table: %s\n",
strerror(errno));
} }
shxsymtabndx = i;
continue;
case SHT_SYMTAB:
num_syms = sec->shdr.sh_size / sizeof(Elf_Sym);
sec->symtab = malloc(sec->shdr.sh_size); sec->symtab = malloc(sec->shdr.sh_size);
if (!sec->symtab) { if (!sec->symtab) {
die("malloc of %" FMT " bytes for symtab failed\n", die("malloc of %" FMT " bytes for symtab failed\n",
...@@ -490,13 +534,20 @@ static void read_symtabs(FILE *fp) ...@@ -490,13 +534,20 @@ static void read_symtabs(FILE *fp)
die("Cannot read symbol table: %s\n", die("Cannot read symbol table: %s\n",
strerror(errno)); strerror(errno));
} }
for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Sym); j++) { for (j = 0; j < num_syms; j++) {
Elf_Sym *sym = &sec->symtab[j]; Elf_Sym *sym = &sec->symtab[j];
sym->st_name = elf_word_to_cpu(sym->st_name); sym->st_name = elf_word_to_cpu(sym->st_name);
sym->st_value = elf_addr_to_cpu(sym->st_value); sym->st_value = elf_addr_to_cpu(sym->st_value);
sym->st_size = elf_xword_to_cpu(sym->st_size); sym->st_size = elf_xword_to_cpu(sym->st_size);
sym->st_shndx = elf_half_to_cpu(sym->st_shndx); sym->st_shndx = elf_half_to_cpu(sym->st_shndx);
} }
shsymtabndx = i;
continue;
default:
continue;
}
} }
} }
...@@ -762,7 +813,9 @@ static void percpu_init(void) ...@@ -762,7 +813,9 @@ static void percpu_init(void)
*/ */
static int is_percpu_sym(ElfW(Sym) *sym, const char *symname) static int is_percpu_sym(ElfW(Sym) *sym, const char *symname)
{ {
return (sym->st_shndx == per_cpu_shndx) && int shndx = sym_index(sym);
return (shndx == per_cpu_shndx) &&
strcmp(symname, "__init_begin") && strcmp(symname, "__init_begin") &&
strcmp(symname, "__per_cpu_load") && strcmp(symname, "__per_cpu_load") &&
strncmp(symname, "init_per_cpu_", 13); strncmp(symname, "init_per_cpu_", 13);
...@@ -1095,7 +1148,7 @@ static int do_reloc_info(struct section *sec, Elf_Rel *rel, ElfW(Sym) *sym, ...@@ -1095,7 +1148,7 @@ static int do_reloc_info(struct section *sec, Elf_Rel *rel, ElfW(Sym) *sym,
sec_name(sec->shdr.sh_info), sec_name(sec->shdr.sh_info),
rel_type(ELF_R_TYPE(rel->r_info)), rel_type(ELF_R_TYPE(rel->r_info)),
symname, symname,
sec_name(sym->st_shndx)); sec_name(sym_index(sym)));
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment