commit:     e7b8f34f4557d7071a955ccab813ec41aeeb966b
Author:     Sergei Trofimovich <slyfox <AT> gentoo <DOT> org>
AuthorDate: Sun Feb 18 20:10:45 2018 +0000
Commit:     Sergei Trofimovich <slyfox <AT> gentoo <DOT> org>
CommitDate: Sun Feb 18 20:10:45 2018 +0000
URL:        https://gitweb.gentoo.org/proj/pax-utils.git/commit/?id=e7b8f34f

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 <slyfox <AT> gentoo.org>

 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); \

Reply via email to