summaryrefslogtreecommitdiff
path: root/elfutils/libdwfl
diff options
context:
space:
mode:
Diffstat (limited to 'elfutils/libdwfl')
-rw-r--r--elfutils/libdwfl/ChangeLog16
-rw-r--r--elfutils/libdwfl/dwfl_module_getdwarf.c62
-rw-r--r--elfutils/libdwfl/linux-kernel-modules.c46
3 files changed, 108 insertions, 16 deletions
diff --git a/elfutils/libdwfl/ChangeLog b/elfutils/libdwfl/ChangeLog
index 16fb71d3..0cbeb850 100644
--- a/elfutils/libdwfl/ChangeLog
+++ b/elfutils/libdwfl/ChangeLog
@@ -1,3 +1,19 @@
+2011-02-11 Roland McGrath <roland@redhat.com>
+
+ * linux-kernel-modules.c (try_kernel_name): Try .gz, .bz2, .xz
+ suffixes if corresponding decompression support is enabled.
+
+2011-02-01 Roland McGrath <roland@redhat.com>
+
+ * dwfl_module_getdwarf.c (find_prelink_address_sync): Use the
+ section-end address as the synchronization point, rather than sh_addr.
+
+ * dwfl_module_getdwarf.c (find_prelink_address_sync): Discover
+ PT_INTERP p_vaddr separately from main phdrs and undo phdrs.
+
+ * dwfl_module_getdwarf.c (find_prelink_address_sync): Fix pasto in
+ last change, so we recognize PT_INTERP in ELFCLASS64 correctly.
+
2011-01-11 Roland McGrath <roland@redhat.com>
* dwfl_module_getdwarf.c (open_elf): Remove section-based
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;
diff --git a/elfutils/libdwfl/linux-kernel-modules.c b/elfutils/libdwfl/linux-kernel-modules.c
index 2479292a..f3d9af10 100644
--- a/elfutils/libdwfl/linux-kernel-modules.c
+++ b/elfutils/libdwfl/linux-kernel-modules.c
@@ -1,5 +1,5 @@
/* Standard libdwfl callbacks for debugging the running Linux kernel.
- 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
@@ -78,6 +78,19 @@
#define MODULE_SECT_NAME_LEN 32 /* Minimum any linux/module.h has had. */
+static const char *vmlinux_suffixes[] =
+ {
+#ifdef USE_ZLIB
+ ".gz",
+#endif
+#ifdef USE_BZLIB
+ ".bz2",
+#endif
+#ifdef USE_LZMA
+ ".xz",
+#endif
+ };
+
/* Try to open the given file as it is or under the debuginfo directory. */
static int
try_kernel_name (Dwfl *dwfl, char **fname, bool try_debug)
@@ -91,6 +104,7 @@ try_kernel_name (Dwfl *dwfl, char **fname, bool try_debug)
? *dwfl->callbacks->debuginfo_path : NULL)
?: DEFAULT_DEBUGINFO_PATH)[0] == ':') ? -1
: TEMP_FAILURE_RETRY (open64 (*fname, O_RDONLY)));
+
if (fd < 0)
{
char *debugfname = NULL;
@@ -106,8 +120,36 @@ try_kernel_name (Dwfl *dwfl, char **fname, bool try_debug)
fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0,
*fname, NULL, 0,
&debugfname);
+ if (debugfname != NULL)
+ {
+ free (*fname);
+ *fname = debugfname;
+ }
+ }
+
+ if (fd < 0)
+ for (size_t i = 0;
+ i < sizeof vmlinux_suffixes / sizeof vmlinux_suffixes[0];
+ ++i)
+ {
+ char *zname;
+ if (asprintf (&zname, "%s%s", *fname, vmlinux_suffixes[i]) > 0)
+ {
+ fd = TEMP_FAILURE_RETRY (open64 (zname, O_RDONLY));
+ if (fd < 0)
+ free (zname);
+ else
+ {
+ free (*fname);
+ *fname = zname;
+ }
+ }
+ }
+
+ if (fd < 0)
+ {
free (*fname);
- *fname = debugfname;
+ *fname = NULL;
}
return fd;