summaryrefslogtreecommitdiff
path: root/elfutils/libdwfl/dwfl_module_getdwarf.c
diff options
context:
space:
mode:
Diffstat (limited to 'elfutils/libdwfl/dwfl_module_getdwarf.c')
-rw-r--r--elfutils/libdwfl/dwfl_module_getdwarf.c62
1 files changed, 48 insertions, 14 deletions
diff --git a/elfutils/libdwfl/dwfl_module_getdwarf.c b/elfutils/libdwfl/dwfl_module_getdwarf.c
index 0825722d..dbb1d604 100644
--- a/elfutils/libdwfl/dwfl_module_getdwarf.c
+++ b/elfutils/libdwfl/dwfl_module_getdwarf.c
@@ -395,11 +395,29 @@ find_prelink_address_sync (Dwfl_Module *mod)
the SHT_PROGBITS section whose address matches the PT_INTERP p_vaddr.
For this reason, we must examine the phdrs first to find PT_INTERP. */
+ GElf_Addr main_interp = 0;
+ {
+ size_t main_phnum;
+ if (unlikely (elf_getphdrnum (mod->main.elf, &main_phnum)))
+ return DWFL_E_LIBELF;
+ for (size_t i = 0; i < main_phnum; ++i)
+ {
+ GElf_Phdr phdr;
+ if (unlikely (gelf_getphdr (mod->main.elf, i, &phdr) == NULL))
+ return DWFL_E_LIBELF;
+ if (phdr.p_type == PT_INTERP)
+ {
+ main_interp = phdr.p_vaddr;
+ break;
+ }
+ }
+ }
+
src.d_buf += src.d_size;
src.d_type = ELF_T_PHDR;
src.d_size = phnum * phentsize;
- GElf_Addr interp = 0;
+ GElf_Addr undo_interp = 0;
{
union
{
@@ -416,21 +434,24 @@ find_prelink_address_sync (Dwfl_Module *mod)
for (uint_fast16_t i = 0; i < phnum; ++i)
if (phdr.p32[i].p_type == PT_INTERP)
{
- interp = phdr.p32[i].p_vaddr;
+ undo_interp = phdr.p32[i].p_vaddr;
break;
}
}
else
{
for (uint_fast16_t i = 0; i < phnum; ++i)
- if (phdr.p32[i].p_type == PT_INTERP)
+ if (phdr.p64[i].p_type == PT_INTERP)
{
- interp = phdr.p32[i].p_vaddr;
+ undo_interp = phdr.p64[i].p_vaddr;
break;
}
}
}
+ if (unlikely ((main_interp == 0) != (undo_interp == 0)))
+ return DWFL_E_BAD_PRELINK;
+
src.d_buf += src.d_size;
src.d_type = ELF_T_SHDR;
src.d_size = gelf_fsize (mod->main.elf, ELF_T_SHDR, shnum - 1, EV_CURRENT);
@@ -451,20 +472,32 @@ find_prelink_address_sync (Dwfl_Module *mod)
file sections as they are after prelinking, to calculate the
synchronization address of the main file. Then we'll apply that
same method to the saved section headers, to calculate the matching
- synchronization address of the debug file. */
+ synchronization address of the debug file.
+
+ The method is to consider SHF_ALLOC sections that are either
+ SHT_PROGBITS or SHT_NOBITS, excluding the section whose sh_addr
+ matches the PT_INTERP p_vaddr. The special sections that can be
+ moved by prelink have other types, except for .interp (which
+ becomes PT_INTERP). The "real" sections cannot move as such, but
+ .bss can be split into .dynbss and .bss, with the total memory
+ image remaining the same but being spread across the two sections.
+ So we consider the highest section end, which still matches up. */
GElf_Addr highest;
- inline void consider_shdr (GElf_Word sh_type,
+ inline void consider_shdr (GElf_Addr interp,
+ GElf_Word sh_type,
GElf_Xword sh_flags,
- GElf_Addr sh_addr)
+ GElf_Addr sh_addr,
+ GElf_Xword sh_size)
{
if ((sh_flags & SHF_ALLOC)
&& ((sh_type == SHT_PROGBITS && sh_addr != interp)
|| sh_type == SHT_NOBITS))
{
- if (sh_addr > highest)
- highest = sh_addr;
+ const GElf_Addr sh_end = sh_addr + sh_size;
+ if (sh_end > highest)
+ highest = sh_end;
}
}
@@ -476,7 +509,8 @@ find_prelink_address_sync (Dwfl_Module *mod)
GElf_Shdr *sh = gelf_getshdr (scn, &sh_mem);
if (unlikely (sh == NULL))
return DWFL_E_LIBELF;
- consider_shdr (sh->sh_type, sh->sh_flags, sh->sh_addr);
+ consider_shdr (main_interp, sh->sh_type, sh->sh_flags,
+ sh->sh_addr, sh->sh_size);
}
if (highest > mod->main.vaddr)
{
@@ -485,12 +519,12 @@ find_prelink_address_sync (Dwfl_Module *mod)
highest = 0;
if (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32)
for (size_t i = 0; i < shnum - 1; ++i)
- consider_shdr (shdr.s32[i].sh_type, shdr.s32[i].sh_flags,
- shdr.s32[i].sh_addr);
+ consider_shdr (undo_interp, shdr.s32[i].sh_type, shdr.s32[i].sh_flags,
+ shdr.s32[i].sh_addr, shdr.s32[i].sh_size);
else
for (size_t i = 0; i < shnum - 1; ++i)
- consider_shdr (shdr.s64[i].sh_type, shdr.s64[i].sh_flags,
- shdr.s64[i].sh_addr);
+ consider_shdr (undo_interp, shdr.s64[i].sh_type, shdr.s64[i].sh_flags,
+ shdr.s64[i].sh_addr, shdr.s64[i].sh_size);
if (highest > mod->debug.vaddr)
mod->debug.address_sync = highest;