[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Libunwind-devel] [PATCH 5/5] Search MiniDebugInfo for procedure names
From: |
Martin Milata |
Subject: |
[Libunwind-devel] [PATCH 5/5] Search MiniDebugInfo for procedure names |
Date: |
Fri, 10 Aug 2012 16:03:57 +0200 |
After searching the normal symbol table, look if the binary contains
.gnu_debugdata section. If it does, run LZMA decompression on it, load
the resulting ELF image into memory and call lookup_symbol() on it
again.
lookup_symbol() is modified so that it takes min_dist as a parameter and
only returns a symbol when it finds one that is closer than indicated by
the parameter.
Signed-off-by: Martin Milata <address@hidden>
---
src/elfxx.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 149 insertions(+), 9 deletions(-)
diff --git a/src/elfxx.c b/src/elfxx.c
index 514033d..6060169 100644
--- a/src/elfxx.c
+++ b/src/elfxx.c
@@ -27,6 +27,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. */
#include <stdio.h>
#include <sys/param.h>
+#ifdef HAVE_LZMA
+#include <lzma.h>
+#endif /* HAVE_LZMA */
+
#include "libunwind_i.h"
static Elf_W (Shdr)*
@@ -83,13 +87,13 @@ static int
elf_w (lookup_symbol) (unw_addr_space_t as,
unw_word_t ip, struct elf_image *ei,
Elf_W (Addr) load_offset,
- char *buf, size_t buf_len, unw_word_t *offp)
+ char *buf, size_t buf_len, Elf_W (Addr) *min_dist)
{
size_t syment_size;
Elf_W (Ehdr) *ehdr = ei->image;
Elf_W (Sym) *sym, *symtab, *symtab_end;
Elf_W (Shdr) *shdr;
- Elf_W (Addr) val, min_dist = ~(Elf_W (Addr))0;
+ Elf_W (Addr) val;
int i, ret = -UNW_ENOINFO;
char *strtab;
@@ -131,9 +135,9 @@ elf_w (lookup_symbol) (unw_addr_space_t as,
Debug (16, "0x%016lx info=0x%02x %s\n",
(long) val, sym->st_info, strtab + sym->st_name);
- if ((Elf_W (Addr)) (ip - val) < min_dist)
+ if ((Elf_W (Addr)) (ip - val) < *min_dist)
{
- min_dist = (Elf_W (Addr)) (ip - val);
+ *min_dist = (Elf_W (Addr)) (ip - val);
strncpy (buf, strtab + sym->st_name, buf_len);
buf[buf_len - 1] = '\0';
ret = (strlen (strtab + sym->st_name) >= buf_len
@@ -148,10 +152,6 @@ elf_w (lookup_symbol) (unw_addr_space_t as,
}
shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
}
- if (min_dist >= ei->size)
- return -UNW_ENOINFO; /* not found */
- if (offp)
- *offp = min_dist;
return ret;
}
@@ -177,6 +177,123 @@ elf_w (get_load_offset) (struct elf_image *ei, unsigned
long segbase,
return offset;
}
+#if HAVE_LZMA
+static size_t
+xz_uncompressed_size (uint8_t *compressed, size_t length)
+{
+ uint64_t memlimit = UINT64_MAX;
+ size_t ret = 0, pos = 0;
+ lzma_stream_flags options;
+ lzma_index *index;
+
+ if (length < LZMA_STREAM_HEADER_SIZE)
+ return 0;
+
+ uint8_t *footer = compressed + length - LZMA_STREAM_HEADER_SIZE;
+ if (lzma_stream_footer_decode (&options, footer) != LZMA_OK)
+ return 0;
+
+ if (length < LZMA_STREAM_HEADER_SIZE + options.backward_size)
+ return 0;
+
+ uint8_t *indexdata = footer - options.backward_size;
+ if (lzma_index_buffer_decode (&index, &memlimit, NULL, indexdata,
+ &pos, options.backward_size) != LZMA_OK)
+ return 0;
+
+ if (lzma_index_size (index) == options.backward_size)
+ {
+ ret = lzma_index_uncompressed_size (index);
+ }
+
+ lzma_index_end (index, NULL);
+ return ret;
+}
+
+static int
+elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
+{
+ Elf_W (Ehdr) *ehdr = ei->image;
+ Elf_W (Shdr) *shdr;
+ char *strtab;
+ int i;
+ uint8_t *compressed = NULL;
+ uint64_t memlimit = UINT64_MAX; /* no memory limit */
+ size_t compressed_len, uncompressed_len;
+
+ if (!elf_w (valid_object) (ei))
+ return 0;
+
+ shdr = elf_w (section_table) (ei);
+ if (!shdr)
+ return 0;
+
+ strtab = elf_w (string_table) (ei, ehdr->e_shstrndx);
+ if (!strtab)
+ return 0;
+
+ for (i = 0; i < ehdr->e_shnum; ++i)
+ {
+ if (strcmp (strtab + shdr->sh_name, ".gnu_debugdata") == 0)
+ {
+ if (shdr->sh_offset + shdr->sh_size > ei->size)
+ {
+ Debug (1, ".gnu_debugdata outside image? (0x%lu > 0x%lu)\n",
+ (unsigned long) shdr->sh_offset + shdr->sh_size,
+ (unsigned long) ei->size);
+ return 0;
+ }
+
+ Debug (16, "found .gnu_debugdata at 0x%lx\n",
+ (unsigned long) shdr->sh_offset);
+ compressed = ((uint8_t *) ei->image) + shdr->sh_offset;
+ compressed_len = shdr->sh_size;
+ break;
+ }
+
+ shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
+ }
+
+ /* not found */
+ if (!compressed)
+ return 0;
+
+ uncompressed_len = xz_uncompressed_size (compressed, compressed_len);
+ if (uncompressed_len == 0)
+ {
+ Debug (1, "invalid .gnu_debugdata contents\n");
+ return 0;
+ }
+
+ mdi->size = uncompressed_len;
+ mdi->image = mmap (NULL, uncompressed_len, PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+
+ if (mdi->image == MAP_FAILED)
+ return 0;
+
+ size_t in_pos = 0, out_pos = 0;
+ lzma_ret lret;
+ lret = lzma_stream_buffer_decode (&memlimit, 0, NULL,
+ compressed, &in_pos, compressed_len,
+ mdi->image, &out_pos, mdi->size);
+ if (lret != LZMA_OK)
+ {
+ Debug (1, "LZMA decompression failed: %d\n", lret);
+ munmap (mdi->image, mdi->size);
+ return 0;
+ }
+
+ return 1;
+}
+#else
+static int
+elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
+{
+ return 0;
+}
+#endif /* !HAVE_LZMA */
+
/* Find the ELF image that contains IP and return the "closest"
procedure name, if there is one. With some caching, this could be
sped up greatly, but until an application materializes that's
@@ -190,13 +307,36 @@ elf_w (get_proc_name_in_image) (unw_addr_space_t as,
struct elf_image *ei,
char *buf, size_t buf_len, unw_word_t *offp)
{
Elf_W (Addr) load_offset;
+ Elf_W (Addr) min_dist = ~(Elf_W (Addr))0;
int ret;
load_offset = elf_w (get_load_offset) (ei, segbase, mapoff);
- ret = elf_w (lookup_symbol) (as, ip, ei, load_offset, buf, buf_len, offp);
+ ret = elf_w (lookup_symbol) (as, ip, ei, load_offset, buf, buf_len,
&min_dist);
+
+ /* If the ELF image has MiniDebugInfo embedded in it, look up the symbol in
+ there as well and replace the previously found if it is closer. */
+ struct elf_image mdi;
+ if (elf_w (extract_minidebuginfo) (ei, &mdi))
+ {
+ int ret_mdi;
+ load_offset = elf_w (get_load_offset) (&mdi, segbase, mapoff);
+ ret_mdi = elf_w (lookup_symbol) (as, ip, &mdi, load_offset, buf,
+ buf_len, &min_dist);
+ /* Closer symbol was found (possibly truncated). */
+ if (ret_mdi == 0 || ret_mdi == -UNW_ENOMEM)
+ {
+ ret = ret_mdi;
+ }
+
+ munmap (mdi.image, mdi.size);
+ }
+ if (min_dist >= ei->size)
+ return -UNW_ENOINFO; /* not found */
+ if (offp)
+ *offp = min_dist;
return ret;
}
--
1.7.7.6
[Libunwind-devel] [PATCH 3/5] MiniDebugInfo test: tests/run-coredump-unwind-mdi, Martin Milata, 2012/08/10
[Libunwind-devel] [PATCH 4/5] Refactor parts of elfxx.c to allow reusing code, Martin Milata, 2012/08/10