From e7b8f34f4557d7071a955ccab813ec41aeeb966b Mon Sep 17 00:00:00 2001 From: Sergei Trofimovich Date: Sun, 18 Feb 2018 20:10:45 +0000 Subject: [PATCH] scanelf.c: fix TEXTREL parsing for files with non-zero load address In bug #566118 scanelf failed to decode TEXTRELs on gcc binaries where program headers have absolute addresses: $ dumpelf ia64_bug_gcc/cc1plus /* Dynamic tag #25 'DT_RELA' 0x2099518 */ { .d_tag = 0x7 , .d_un = { .d_val = 0x4000000000104B08 , .d_ptr = 0x4000000000104B08 , }, }, /* Section Header #8 '.rela.dyn' 0x20AA610 */ { .sh_type = 4 , /* [SHT_RELA] */ .sh_addr = 0x4000000000104B08 , .sh_offset = 1067784 , /* (bytes) */ }, Before the change scanelf assumed DT_RELA.d_ptr is a relative offset. This is not true in general case but good-enough for DSOs as they have zero load address. This change extends the check for executables. To make addresses relative again we find load address of first byte from program header with 'p_offset'. /* Program Header #2 0xB0 */ { .p_type = 1 , /* [PT_LOAD] */ .p_offset = 0 , /* (bytes into file) */ .p_vaddr = 0x4000000000000000 , /* (virtual addr at runtime) */ .p_paddr = 0x4000000000000000 , /* (physical addr at runtime) */ }, Bug: https://bugs.gentoo.org/566118 Signed-off-by: Sergei Trofimovich --- scanelf.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/scanelf.c b/scanelf.c index a054408..530edfb 100644 --- a/scanelf.c +++ b/scanelf.c @@ -579,6 +579,8 @@ static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *foun Elf ## B ## _Rela *rela; \ Elf ## B ## _Dyn *dyn, *drel, *drelsz, *drelent, *dpltrel; \ uint32_t pltrel; \ + Elf ## B ## _Addr load_address = 0; \ + Elf ## B ## _Addr file_offset; \ \ /* Walk all the dynamic tags to find relocation info */ \ drel = drelsz = drelent = dpltrel = NULL; \ @@ -605,27 +607,40 @@ static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *foun warnf("ELF is missing relocation information"); \ break; \ } \ + phdr = PHDR ## B(elf->phdr); \ + /* Lookup load base: byte 0 is mapped at load_address */ \ + for (i = 0; i < EGET(ehdr->e_phnum); ++i) { \ + /* Only care about loadable segments. */ \ + if (EGET(phdr[i].p_type) != PT_LOAD) \ + continue; \ + /* We search for the first program header to map into memory */ \ + if (EGET(phdr[i].p_offset) != 0) \ + continue; \ + load_address = EGET(phdr[i].p_vaddr); \ + } \ switch (EGET(dpltrel->d_un.d_val)) { \ case DT_REL: \ - if (!VALID_RANGE(elf, EGET(drel->d_un.d_val), sizeof (drel->d_un.d_val))) { \ + file_offset = EGET(drel->d_un.d_val) - load_address; \ + if (!VALID_RANGE(elf, file_offset, sizeof (drel->d_un.d_val))) { \ rel = NULL; \ rela = NULL; \ warn("%s: DT_REL is out of file range", elf->filename); \ break; \ } \ - rel = REL##B(elf->vdata + EGET(drel->d_un.d_val)); \ + rel = REL##B(elf->vdata + file_offset); \ rela = NULL; \ pltrel = DT_REL; \ break; \ case DT_RELA: \ - if (!VALID_RANGE(elf, EGET(drel->d_un.d_val), sizeof (drel->d_un.d_val))) { \ + file_offset = EGET(drel->d_un.d_val) - load_address; \ + if (!VALID_RANGE(elf, file_offset, sizeof (drel->d_un.d_val))) { \ rel = NULL; \ rela = NULL; \ warn("%s: DT_RELA is out of file range", elf->filename); \ break; \ } \ rel = NULL; \ - rela = RELA##B(elf->vdata + EGET(drel->d_un.d_val)); \ + rela = RELA##B(elf->vdata + file_offset); \ pltrel = DT_RELA; \ break; \ default: \ @@ -639,7 +654,6 @@ static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *foun rmax = EGET(drelsz->d_un.d_val) / EGET(drelent->d_un.d_val); \ \ /* search the program segments for relocations */ \ - phdr = PHDR ## B(elf->phdr); \ for (i = 0; i < EGET(ehdr->e_phnum); ++i) { \ Elf ## B ## _Addr vaddr = EGET(phdr[i].p_vaddr); \ uint ## B ## _t memsz = EGET(phdr[i].p_memsz); \ -- 2.16.1