Add dwz support.

This commit is contained in:
Mark Wielaard 2012-08-01 23:49:29 +02:00
parent 3152a53945
commit ffa143207f
2 changed files with 480 additions and 1 deletions

474
elfutils-0.154-dwz.patch Normal file
View File

@ -0,0 +1,474 @@
https://lists.fedorahosted.org/pipermail/elfutils-devel/2012-July/002418.html
From b1e42797293bcf34385d5cb0a18e8c773279241b Mon Sep 17 00:00:00 2001
From: Mark Wielaard <mjw@redhat.com>
Date: Fri, 22 Jun 2012 12:02:45 +0200
Subject: [PATCH] libdw: Add support for DWZ multifile forms
DW_FORM_GNU_ref_alt/strp_alt.
DWZ multifile forms http://www.dwarfstd.org/ShowIssue.php?issue=120604.1
DW_FORM_GNU_ref_alt and DW_FORM_GNU_strp_alt reference an alternative
debuginfo file. dwarf_begin and dwarf_begin_elf will try to use this
automatically. There are no user visible changes to the libdw interface.
dwarf_formref_die, dwarf_formstring and dwarf_formudata can now return
a Dwarf_Die which comes from a CU in the alternative Dwarf descriptor.
__libdw_read_offset was adjusted to take an alternative Dwarf descriptor
into account.
Signed-off-by: Mark Wielaard <mjw@redhat.com>
diff --git a/libdw/dwarf.h b/libdw/dwarf.h
index f41d296..81bc7fe 100644
--- a/libdw/dwarf.h
+++ b/libdw/dwarf.h
@@ -299,7 +299,10 @@ enum
DW_FORM_sec_offset = 0x17,
DW_FORM_exprloc = 0x18,
DW_FORM_flag_present = 0x19,
- DW_FORM_ref_sig8 = 0x20
+ DW_FORM_ref_sig8 = 0x20,
+
+ DW_FORM_GNU_ref_alt = 0x1f20, /* offset in alternate .debuginfo. */
+ DW_FORM_GNU_strp_alt = 0x1f21 /* offset in alternate .debug_str. */
};
diff --git a/libdw/dwarf_begin.c b/libdw/dwarf_begin.c
index 1f3fc3b..9f3050f 100644
--- a/libdw/dwarf_begin.c
+++ b/libdw/dwarf_begin.c
@@ -98,3 +98,4 @@ dwarf_begin (fd, cmd)
return result;
}
+INTDEF(dwarf_begin)
diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c
index 3e01800..fd95770 100644
--- a/libdw/dwarf_begin_elf.c
+++ b/libdw/dwarf_begin_elf.c
@@ -31,12 +31,17 @@
# include <config.h>
#endif
+#include <assert.h>
+#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
#include <unistd.h>
+#include <sys/types.h>
#include <sys/stat.h>
+#include <fcntl.h>
#include "libdwP.h"
@@ -66,6 +71,110 @@ static const char dwarf_scnnames[IDX_last][17] =
};
#define ndwarf_scnnames (sizeof (dwarf_scnnames) / sizeof (dwarf_scnnames[0]))
+internal_function int
+__check_build_id (Dwarf *dw, const uint8_t *build_id, const size_t id_len)
+{
+ if (dw == NULL)
+ return -1;
+
+ Elf *elf = dw->elf;
+ Elf_Scn *scn = elf_nextscn (elf, NULL);
+ if (scn == NULL)
+ return -1;
+
+ do
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (likely (shdr != NULL) && shdr->sh_type == SHT_NOTE)
+ {
+ size_t pos = 0;
+ GElf_Nhdr nhdr;
+ size_t name_pos;
+ size_t desc_pos;
+ Elf_Data *data = elf_getdata (scn, NULL);
+ while ((pos = gelf_getnote (data, pos, &nhdr, &name_pos,
+ &desc_pos)) > 0)
+ if (nhdr.n_type == NT_GNU_BUILD_ID
+ && nhdr.n_namesz == sizeof "GNU"
+ && ! memcmp (data->d_buf + name_pos, "GNU", sizeof "GNU"))
+ return (nhdr.n_descsz == id_len
+ && ! memcmp (data->d_buf + desc_pos,
+ build_id, id_len)) ? 0 : 1;
+ }
+ }
+ while ((scn = elf_nextscn (elf, scn)) != NULL);
+
+ return -1;
+}
+
+/* Try to open an debug alt link by name, checking build_id.
+ Marks free_alt on success, return NULL on failure. */
+static Dwarf *
+try_debugaltlink (Dwarf *result, const char *try_name,
+ const uint8_t *build_id, const size_t id_len)
+{
+ int fd = open (try_name, O_RDONLY);
+ if (fd > 0)
+ {
+ result->alt_dwarf = INTUSE (dwarf_begin) (fd, DWARF_C_READ);
+ if (result->alt_dwarf != NULL)
+ {
+ Elf *elf = result->alt_dwarf->elf;
+ if (__check_build_id (result->alt_dwarf, build_id, id_len) == 0
+ && elf_cntl (elf, ELF_C_FDREAD) == 0)
+ {
+ close (fd);
+ result->free_alt = 1;
+ return result;
+ }
+ INTUSE (dwarf_end) (result->alt_dwarf);
+ }
+ close (fd);
+ }
+ return NULL;
+}
+
+/* For dwz multifile support, ignore if it looks wrong. */
+static Dwarf *
+open_debugaltlink (Dwarf *result, const char *alt_name,
+ const uint8_t *build_id, const size_t id_len)
+{
+ /* First try the name itself, it is either an absolute path or
+ a relative one. Sadly we don't know relative from where at
+ this point. */
+ if (try_debugaltlink (result, alt_name, build_id, id_len) != NULL)
+ return result;
+
+ /* Lets try based on the build-id. This is somewhat distro specific,
+ we are following the Fedora implementation described at
+ https://fedoraproject.org/wiki/Releases/FeatureBuildId#Find_files_by_build_ID
+ */
+#define DEBUG_PREFIX "/usr/lib/debug/.build-id/"
+#define PREFIX_LEN sizeof (DEBUG_PREFIX)
+ char id_name[PREFIX_LEN + 1 + id_len * 2 + sizeof ".debug" - 1];
+ strcpy (id_name, DEBUG_PREFIX);
+ int n = snprintf (&id_name[PREFIX_LEN - 1],
+ 4, "%02" PRIx8 "/", (uint8_t) build_id[0]);
+ assert (n == 3);
+ for (size_t i = 1; i < id_len; ++i)
+ {
+ n = snprintf (&id_name[PREFIX_LEN - 1 + 3 + (i - 1) * 2],
+ 3, "%02" PRIx8, (uint8_t) build_id[i]);
+ assert (n == 2);
+ }
+ strcpy (&id_name[PREFIX_LEN - 1 + 3 + (id_len - 1) * 2],
+ ".debug");
+
+ if (try_debugaltlink (result, id_name, build_id, id_len))
+ return result;
+
+ /* Everything failed, mark this Dwarf as not having an alternate,
+ but don't fail the load. The user may want to set it by hand
+ before usage. */
+ result->alt_dwarf = NULL;
+ return result;
+}
static Dwarf *
check_section (Dwarf *result, GElf_Ehdr *ehdr, Elf_Scn *scn, bool inscngrp)
@@ -110,6 +219,20 @@ check_section (Dwarf *result, GElf_Ehdr *ehdr, Elf_Scn *scn, bool inscngrp)
return NULL;
}
+ /* For dwz multifile support, ignore if it looks wrong. */
+ if (strcmp (scnname, ".gnu_debugaltlink") == 0)
+ {
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data != NULL && data->d_size != 0)
+ {
+ const char *alt_name = data->d_buf;
+ const void *build_id = memchr (data->d_buf, '\0', data->d_size);
+ const int id_len = data->d_size - (build_id - data->d_buf + 1);
+ if (alt_name && build_id && id_len > 0)
+ return open_debugaltlink (result, alt_name, build_id + 1, id_len);
+ }
+ }
+
/* Recognize the various sections. Most names start with .debug_. */
size_t cnt;
diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c
index b77988f..e65314a 100644
--- a/libdw/dwarf_end.c
+++ b/libdw/dwarf_end.c
@@ -111,6 +111,10 @@ dwarf_end (dwarf)
if (dwarf->free_elf)
elf_end (dwarf->elf);
+ /* Free the alternative Dwarf descriptor if necessary. */
+ if (dwarf->free_alt)
+ INTUSE (dwarf_end) (dwarf->alt_dwarf);
+
/* Free the context descriptor. */
free (dwarf);
}
diff --git a/libdw/dwarf_error.c b/libdw/dwarf_error.c
index 89047dc..2292914 100644
--- a/libdw/dwarf_error.c
+++ b/libdw/dwarf_error.c
@@ -91,6 +91,7 @@ static const char *errmsgs[] =
[DWARF_E_INVALID_OFFSET] = N_("invalid offset"),
[DWARF_E_NO_DEBUG_RANGES] = N_(".debug_ranges section missing"),
[DWARF_E_INVALID_CFI] = N_("invalid CFI section"),
+ [DWARF_E_NO_ALT_DEBUGLINK] = N_("no alternative debug link found"),
};
#define nerrmsgs (sizeof (errmsgs) / sizeof (errmsgs[0]))
diff --git a/libdw/dwarf_formref.c b/libdw/dwarf_formref.c
index a2554e9..86da7ea 100644
--- a/libdw/dwarf_formref.c
+++ b/libdw/dwarf_formref.c
@@ -72,6 +72,8 @@ __libdw_formref (attr, return_offset)
case DW_FORM_ref_addr:
case DW_FORM_ref_sig8:
+ case DW_FORM_GNU_ref_alt:
+ /* These aren't handled by dwarf_formref, only by dwarf_formref_die. */
__libdw_seterrno (DWARF_E_INVALID_REFERENCE);
return -1;
diff --git a/libdw/dwarf_formref_die.c b/libdw/dwarf_formref_die.c
index 342f6b9..f070127 100644
--- a/libdw/dwarf_formref_die.c
+++ b/libdw/dwarf_formref_die.c
@@ -46,7 +46,7 @@ dwarf_formref_die (attr, result)
struct Dwarf_CU *cu = attr->cu;
Dwarf_Off offset;
- if (attr->form == DW_FORM_ref_addr)
+ if (attr->form == DW_FORM_ref_addr || attr->form == DW_FORM_GNU_ref_alt)
{
/* This has an absolute offset. */
@@ -54,11 +54,20 @@ dwarf_formref_die (attr, result)
? cu->address_size
: cu->offset_size);
- if (__libdw_read_offset (cu->dbg, IDX_debug_info, attr->valp,
+ Dwarf *dbg_ret = (attr->form == DW_FORM_GNU_ref_alt
+ ? cu->dbg->alt_dwarf : cu->dbg);
+
+ if (dbg_ret == NULL)
+ {
+ __libdw_seterrno (DWARF_E_NO_ALT_DEBUGLINK);
+ return NULL;
+ }
+
+ if (__libdw_read_offset (cu->dbg, dbg_ret, IDX_debug_info, attr->valp,
ref_size, &offset, IDX_debug_info, 0))
return NULL;
- return INTUSE(dwarf_offdie) (cu->dbg, offset, result);
+ return INTUSE(dwarf_offdie) (dbg_ret, offset, result);
}
Elf_Data *data;
diff --git a/libdw/dwarf_formstring.c b/libdw/dwarf_formstring.c
index fe2183a..c66454e 100644
--- a/libdw/dwarf_formstring.c
+++ b/libdw/dwarf_formstring.c
@@ -49,8 +49,17 @@ dwarf_formstring (attrp)
return (const char *) attrp->valp;
Dwarf *dbg = attrp->cu->dbg;
+ Dwarf *dbg_ret = attrp->form == DW_FORM_GNU_strp_alt ? dbg->alt_dwarf : dbg;
- if (unlikely (attrp->form != DW_FORM_strp)
+ if (unlikely (dbg_ret == NULL))
+ {
+ __libdw_seterrno (DWARF_E_NO_ALT_DEBUGLINK);
+ return NULL;
+ }
+
+
+ if (unlikely (attrp->form != DW_FORM_strp
+ && attrp->form != DW_FORM_GNU_strp_alt)
|| dbg->sectiondata[IDX_debug_str] == NULL)
{
__libdw_seterrno (DWARF_E_NO_STRING);
@@ -58,10 +67,10 @@ dwarf_formstring (attrp)
}
uint64_t off;
- if (__libdw_read_offset (dbg, cu_sec_idx (attrp->cu), attrp->valp,
+ if (__libdw_read_offset (dbg, dbg_ret, cu_sec_idx (attrp->cu), attrp->valp,
attrp->cu->offset_size, &off, IDX_debug_str, 1))
return NULL;
- return (const char *) dbg->sectiondata[IDX_debug_str]->d_buf + off;
+ return (const char *) dbg_ret->sectiondata[IDX_debug_str]->d_buf + off;
}
INTDEF(dwarf_formstring)
diff --git a/libdw/dwarf_formudata.c b/libdw/dwarf_formudata.c
index f08e0d8..41b09e1 100644
--- a/libdw/dwarf_formudata.c
+++ b/libdw/dwarf_formudata.c
@@ -52,7 +52,8 @@ __libdw_formptr (Dwarf_Attribute *attr, int sec_index,
Dwarf_Word offset;
if (attr->form == DW_FORM_sec_offset)
{
- if (__libdw_read_offset (attr->cu->dbg, cu_sec_idx (attr->cu), attr->valp,
+ if (__libdw_read_offset (attr->cu->dbg, attr->cu->dbg,
+ cu_sec_idx (attr->cu), attr->valp,
attr->cu->offset_size, &offset, sec_index, 0))
return NULL;
}
@@ -63,7 +64,8 @@ __libdw_formptr (Dwarf_Attribute *attr, int sec_index,
{
case DW_FORM_data4:
case DW_FORM_data8:
- if (__libdw_read_offset (attr->cu->dbg, cu_sec_idx (attr->cu),
+ if (__libdw_read_offset (attr->cu->dbg, attr->cu->dbg,
+ cu_sec_idx (attr->cu),
attr->valp,
attr->form == DW_FORM_data4 ? 4 : 8,
&offset, sec_index, 0))
diff --git a/libdw/dwarf_getpubnames.c b/libdw/dwarf_getpubnames.c
index 4ea3889..12728a3 100644
--- a/libdw/dwarf_getpubnames.c
+++ b/libdw/dwarf_getpubnames.c
@@ -102,7 +102,8 @@ get_offsets (Dwarf *dbg)
}
/* Get the CU offset. */
- if (__libdw_read_offset (dbg, IDX_debug_pubnames, readp + 2, len_bytes,
+ if (__libdw_read_offset (dbg, dbg, IDX_debug_pubnames,
+ readp + 2, len_bytes,
&mem[cnt].cu_offset, IDX_debug_info, 3))
/* Error has been already set in reader. */
goto err_return;
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index 77e1b31..da82e5d 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -116,6 +116,7 @@ enum
DWARF_E_INVALID_OFFSET,
DWARF_E_NO_DEBUG_RANGES,
DWARF_E_INVALID_CFI,
+ DWARF_E_NO_ALT_DEBUGLINK
};
@@ -127,6 +128,9 @@ struct Dwarf
/* The underlying ELF file. */
Elf *elf;
+ /* dwz alternate DWARF file. */
+ Dwarf *alt_dwarf;
+
/* The section data. */
Elf_Data *sectiondata[IDX_last];
@@ -141,6 +145,9 @@ struct Dwarf
/* If true, we allocated the ELF descriptor ourselves. */
bool free_elf;
+ /* If true, we allocated the Dwarf descriptor for alt_dwarf ourselves. */
+ bool free_alt;
+
/* Information for traversing the .debug_pubnames section. This is
an array and separately allocated with malloc. */
struct pubnames_s
@@ -580,13 +587,13 @@ __libdw_read_offset_inc (Dwarf *dbg,
}
static inline int
-__libdw_read_offset (Dwarf *dbg,
+__libdw_read_offset (Dwarf *dbg, Dwarf *dbg_ret,
int sec_index, const unsigned char *addr,
int width, Dwarf_Off *ret, int sec_ret,
size_t size)
{
READ_AND_RELOCATE (__libdw_relocate_offset, (*ret));
- return __libdw_offset_in_section (dbg, sec_ret, *ret, size);
+ return __libdw_offset_in_section (dbg_ret, sec_ret, *ret, size);
}
static inline size_t
@@ -617,12 +624,19 @@ unsigned char * __libdw_formptr (Dwarf_Attribute *attr, int sec_index,
Dwarf_Off *offsetp)
internal_function;
+/* Checks that the build_id of the underlying Elf matches the expected.
+ Returns zero on match, -1 on error or no build_id found or 1 when
+ build_id doesn't match. */
+int __check_build_id (Dwarf *dw, const uint8_t *build_id, const size_t id_len)
+ internal_function;
+
/* Aliases to avoid PLTs. */
INTDECL (dwarf_aggregate_size)
INTDECL (dwarf_attr)
INTDECL (dwarf_attr_integrate)
+INTDECL (dwarf_begin)
INTDECL (dwarf_begin_elf)
INTDECL (dwarf_child)
INTDECL (dwarf_dieoffset)
diff --git a/libdw/libdw_form.c b/libdw/libdw_form.c
index 2ff8868..c476a6e 100644
--- a/libdw/libdw_form.c
+++ b/libdw/libdw_form.c
@@ -58,6 +58,8 @@ __libdw_form_val_len (Dwarf *dbg, struct Dwarf_CU *cu, unsigned int form,
case DW_FORM_strp:
case DW_FORM_sec_offset:
+ case DW_FORM_GNU_ref_alt:
+ case DW_FORM_GNU_strp_alt:
result = cu->offset_size;
break;
diff --git a/src/readelf.c b/src/readelf.c
index 3a27f8f..644e0f7 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -3651,6 +3651,20 @@ dwarf_form_string (unsigned int form)
if (likely (form < nknown_forms))
result = known_forms[form];
+ else
+ {
+ /* GNU extensions use vendor numbers. */
+ switch (form)
+ {
+ case DW_FORM_GNU_ref_alt:
+ result = "GNU_ref_alt";
+ break;
+
+ case DW_FORM_GNU_strp_alt:
+ result = "GNU_strp_alt";
+ break;
+ }
+ }
if (unlikely (result == NULL))
{
@@ -5593,6 +5607,7 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
case DW_FORM_indirect:
case DW_FORM_strp:
case DW_FORM_string:
+ case DW_FORM_GNU_strp_alt:
if (cbargs->silent)
break;
const char *str = dwarf_formstring (attrp);
@@ -5608,7 +5623,8 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
case DW_FORM_ref8:
case DW_FORM_ref4:
case DW_FORM_ref2:
- case DW_FORM_ref1:;
+ case DW_FORM_ref1:
+ case DW_FORM_GNU_ref_alt:
if (cbargs->silent)
break;
Dwarf_Die ref;

View File

@ -1,7 +1,7 @@
Name: elfutils
Summary: A collection of utilities and DSOs to handle compiled objects
Version: 0.154
%global baserelease 2
%global baserelease 3
URL: https://fedorahosted.org/elfutils/
%global source_url http://fedorahosted.org/releases/e/l/elfutils/%{version}/
License: GPLv3+ and (GPLv2+ or LGPLv3+)
@ -47,6 +47,7 @@ Patch1: %{?source_url}elfutils-robustify.patch
Patch2: %{?source_url}elfutils-portability.patch
Patch3: elfutils-0.154-binutils-pr-ld-13621.patch
Patch4: elfutils-0.154-xlatetom-835877.patch
Patch5: elfutils-0.154-dwz.patch
%if !%{compat}
Release: %{baserelease}%{?dist}
@ -213,6 +214,7 @@ sed -i.scanf-m -e 's/%m/%a/g' src/addr2line.c tests/line2addr.c
%patch3 -p1 -b .binutils-pr-ld-13621
%patch4 -p1 -b .xlatetom-835877
%patch5 -p1 -b .dwz
find . -name \*.sh ! -perm -0100 -print | xargs chmod +x
@ -329,6 +331,9 @@ rm -rf ${RPM_BUILD_ROOT}
%{_libdir}/libelf.a
%changelog
* Wed Aug 01 2012 Mark Wielaard <mjw@redhat.com> 0.154-3
- Add dwz support
* Mon Jul 18 2012 Mark Wielaard <mjw@redhat.com> 0.154-2
- Add upstream xlatetom fix (#835877)