diff options
author | Roland McGrath <roland@redhat.com> | 2011-01-04 19:29:53 -0800 |
---|---|---|
committer | Roland McGrath <roland@redhat.com> | 2011-01-05 09:42:09 -0800 |
commit | 2c7d0ddfb8a08b8c450d0d3a5e297ec5d624661d (patch) | |
tree | 7350b6892876ab863ce50751c2a059797a0fc7ca | |
parent | 0b9d1fb534604a9ba19999cd8ce8e7efce28da24 (diff) | |
download | elfutils-2c7d0ddfb8a08b8c450d0d3a5e297ec5d624661d.tar.gz |
libdwfl: Enhance address_sync calculation to handle more prelink permutations.
-rw-r--r-- | libdwfl/ChangeLog | 7 | ||||
-rw-r--r-- | libdwfl/dwfl_module_getdwarf.c | 96 | ||||
-rw-r--r-- | libdwfl/libdwflP.h | 12 |
3 files changed, 101 insertions, 14 deletions
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index c5746c87..9aebb786 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,10 @@ +2011-01-04 Roland McGrath <roland@redhat.com> + + * dwfl_module_getdwarf.c (open_elf): Enhance address_sync calculation + logic to consider section addresses, the better to survive all the + possible prelink machinations. + * libdwflP.h (struct dwfl_file): Comment change. + 2010-11-30 Roland McGrath <roland@redhat.com> * derelocate.c (dwfl_module_relocations): Remove over-eager assert. diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c index d89081b7..0bd231f9 100644 --- a/libdwfl/dwfl_module_getdwarf.c +++ b/libdwfl/dwfl_module_getdwarf.c @@ -1,5 +1,5 @@ /* Find debugging and symbol information for a module in libdwfl. - Copyright (C) 2005-2010 Red Hat, Inc. + Copyright (C) 2005-2011 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -95,10 +95,58 @@ open_elf (Dwfl_Module *mod, struct dwfl_file *file) if (mod->e_type != ET_REL) { + /* In any non-ET_REL file, we compute the "synchronization address". + + We start with the address at the end of the first PT_LOAD + segment. When prelink converts REL to RELA in an ET_DYN + file, it expands the space between the beginning of the + segment and the actual code/data addresses. Since that + change wasn't made in the debug file, the distance from + p_vaddr to an address of interest (in an st_value or DWARF + data) now differs between the main and debug files. The + distance from address_sync to an address of interest remains + consistent. + + If there are no section headers at all (full stripping), then + the end of the first segment is a valid synchronization address. + This cannot happen in a prelinked file, since prelink itself + relies on section headers for prelinking and for undoing it. + (If you do full stripping on a prelinked file, then you get what + you deserve--you can neither undo the prelinking, nor expect to + line it up with a debug file separated before prelinking.) + + However, when prelink processes an ET_EXEC file, it can do + something different. There it juggles the "special" sections + (SHT_DYNSYM et al) to make space for the additional prelink + special sections. Sometimes it will do this by moving a special + section like .dynstr after the real program sections in the + first PT_LOAD segment--i.e. to the end. That changes the end + address of the segment, so it no longer lines up correctly and + is not a valid synchronization address to use. + + So, instead we use a method based on the section headers. We + look at the allocated SHT_PROGBITS (or SHT_NOBITS) sections. + (Most every file will have some SHT_PROGBITS sections, but it's + possible to have one with nothing but .bss, i.e. SHT_NOBITS.) + The special sections that can be moved around have different + sh_type values--except for .interp, the section that became the + PT_INTERP segment. So we exclude the SHT_PROGBITS section whose + address matches the PT_INTERP p_vaddr. + + Since the debug file will always have section headers, we must + choose a method of examining section headers that will also line + up with the end of the first PT_LOAD segment, in case the main + file was fully stripped so we are synchronizing between a + PT_LOAD-based and a section-based calculation. To that end, we + use the highest section end address that lies inside the first + segment. If none does, then we use the highest end address of + any non-special section. */ + size_t phnum; if (unlikely (elf_getphdrnum (file->elf, &phnum) != 0)) goto elf_error; + GElf_Addr interp = 0; file->vaddr = file->address_sync = 0; for (size_t i = 0; i < phnum; ++i) { @@ -106,12 +154,52 @@ open_elf (Dwfl_Module *mod, struct dwfl_file *file) GElf_Phdr *ph = gelf_getphdr (file->elf, i, &ph_mem); if (unlikely (ph == NULL)) goto elf_error; - if (ph->p_type == PT_LOAD) + switch (ph->p_type) { - file->vaddr = ph->p_vaddr & -ph->p_align; - file->address_sync = ph->p_vaddr + ph->p_memsz; + case PT_INTERP: + interp = ph->p_vaddr; + break; + case PT_LOAD: + if (file->address_sync == 0) + { + file->vaddr = ph->p_vaddr & -ph->p_align; + file->address_sync = ph->p_vaddr + ph->p_memsz; + } break; + default: + continue; + } + if (interp != 0 && file->address_sync != 0) + break; + } + + if (file->address_sync != 0) + { + GElf_Addr highest_end = 0; + GElf_Addr highest_end_in_seg = 0; + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (file->elf, scn)) != NULL) + { + GElf_Shdr sh_mem; + GElf_Shdr *sh = gelf_getshdr (scn, &sh_mem); + if (unlikely (sh == NULL)) + goto elf_error; + if ((sh->sh_flags & SHF_ALLOC) + && ((sh->sh_type == SHT_PROGBITS && sh->sh_addr != interp) + || sh->sh_type == SHT_NOBITS)) + { + const GElf_Addr sh_end = sh->sh_addr + sh->sh_size; + if (sh_end > highest_end) + highest_end = sh_end; + if (sh_end <= file->address_sync + && sh_end > highest_end_in_seg) + highest_end_in_seg = sh_end; + } } + if (highest_end_in_seg >= file->vaddr && highest_end_in_seg != 0) + file->address_sync = highest_end_in_seg; + else if (highest_end != 0) + file->address_sync = highest_end; } } diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index 1003b2d3..93db5298 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -1,5 +1,5 @@ /* Internal definitions for libdwfl. - Copyright (C) 2005-2010 Red Hat, Inc. + Copyright (C) 2005-2011 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -146,15 +146,7 @@ struct dwfl_file GElf_Addr vaddr; /* This is an address chosen for synchronization between the main file - and the debug file. In a file without phdrs, this is zero. In - other files it is the address at the end of the first PT_LOAD - segment. When prelink converts REL to RELA in an ET_DYN file, it - expands the space between the beginning of the segment and the - actual code/data addresses. Since that change wasn't made in the - debug file, the distance from p_vaddr to an address of interest (in - an st_value or DWARF data) now differs between the main and debug - files. The distance from address_sync to an address of interest - remains consistent. */ + and the debug file. See dwfl_module_getdwarf.c for how it's chosen. */ GElf_Addr address_sync; }; |