This is the mail archive of the
elfutils-devel@sourceware.org
mailing list for the elfutils project.
[PATCH] libdw: Add support for reading DW_FORM_addrx[1234] in .debug_addr.
- From: Mark Wielaard <mark at klomp dot org>
- To: elfutils-devel at sourceware dot org
- Cc: Mark Wielaard <mark at klomp dot org>
- Date: Fri, 30 Mar 2018 17:56:39 +0200
- Subject: [PATCH] libdw: Add support for reading DW_FORM_addrx[1234] in .debug_addr.
Recognize the new .debug_addr section. The CU will now hold a new
address base offset in that section for that CU. dwarf_form_addr will
decode DW_FORM_addrx[1234] and return addresses using that address
base from the .debug_addr. A new internal function read_3ubyte_unaligned
will try to read a 24-bit value depending on endianness of the underlying
file.
Signed-off-by: Mark Wielaard <mark@klomp.org>
---
libdw/ChangeLog | 14 ++++++
libdw/dwarf_begin_elf.c | 1 +
libdw/dwarf_error.c | 3 +-
libdw/dwarf_formaddr.c | 113 ++++++++++++++++++++++++++++++++++++++++++++----
libdw/dwarf_formudata.c | 8 ++++
libdw/libdwP.h | 9 ++++
libdw/memory-access.h | 50 ++++++++++++++++++++-
src/ChangeLog | 4 ++
src/readelf.c | 5 +++
9 files changed, 196 insertions(+), 11 deletions(-)
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index f552644..8423cb3 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,17 @@
+2018-03-22 Mark Wielaard <mark@klomp.org>
+
+ * dwarf_begin_elf.c (dwarf_scnnames): Add IDX_debug_addr.
+ * dwarf_error.c (errmsgs): Add DWARF_E_NO_DEBUG_ADDR.
+ * dwarf_formaddr.c (dwarf_formaddr): Handle DW_FORM_addrx[1234].
+ (__libdw_cu_addr_base): New function.
+ * dwarf_formudata.c (dwarf_formudata): Handle DW_AT_addr_base as
+ addrptr.
+ * libdwP.h: Add IDX_debug_addr and DWARF_E_NO_DEBUG_ADDR.
+ (struct Dwarf_CU): Add addr_base field.
+ (__libdw_cu_addr_base): New function definition.
+ * memory-access.h (file_byte_order): New static function.
+ (read_3ubyte_unaligned): New inline function.
+
2018-03-06 Mark Wielaard <mark@klomp.org>
* dwarf.h: Add DW_OP_implicit_pointer, DW_OP_addrx, DW_OP_constx,
diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c
index 6834ac5..1ffa6c9 100644
--- a/libdw/dwarf_begin_elf.c
+++ b/libdw/dwarf_begin_elf.c
@@ -52,6 +52,7 @@ static const char dwarf_scnnames[IDX_last][18] =
[IDX_debug_info] = ".debug_info",
[IDX_debug_types] = ".debug_types",
[IDX_debug_abbrev] = ".debug_abbrev",
+ [IDX_debug_addr] = ".debug_addr",
[IDX_debug_aranges] = ".debug_aranges",
[IDX_debug_line] = ".debug_line",
[IDX_debug_frame] = ".debug_frame",
diff --git a/libdw/dwarf_error.c b/libdw/dwarf_error.c
index 939ec04..212f32e 100644
--- a/libdw/dwarf_error.c
+++ b/libdw/dwarf_error.c
@@ -95,7 +95,8 @@ static const char *errmsgs[] =
[DWARF_E_NO_ALT_DEBUGLINK] = N_("no alternative debug link found"),
[DWARF_E_INVALID_OPCODE] = N_("invalid opcode"),
[DWARF_E_NOT_CUDIE] = N_("not a CU (unit) DIE"),
- [DWARF_E_UNKNOWN_LANGUAGE] = N_("unknown language code")
+ [DWARF_E_UNKNOWN_LANGUAGE] = N_("unknown language code"),
+ [DWARF_E_NO_DEBUG_ADDR] = N_(".debug_addr section missing"),
};
#define nerrmsgs (sizeof (errmsgs) / sizeof (errmsgs[0]))
diff --git a/libdw/dwarf_formaddr.c b/libdw/dwarf_formaddr.c
index ddc4838..25e6970 100644
--- a/libdw/dwarf_formaddr.c
+++ b/libdw/dwarf_formaddr.c
@@ -1,7 +1,6 @@
/* Return address represented by attribute.
- Copyright (C) 2003-2010 Red Hat, Inc.
+ Copyright (C) 2003-2010, 2018 Red Hat, Inc.
This file is part of elfutils.
- Written by Ulrich Drepper <drepper@redhat.com>, 2003.
This file is free software; you can redistribute it and/or modify
it under the terms of either
@@ -41,17 +40,115 @@ dwarf_formaddr (Dwarf_Attribute *attr, Dwarf_Addr *return_addr)
if (attr == NULL)
return -1;
- if (unlikely (attr->form != DW_FORM_addr))
+ Dwarf_Word idx;
+ Dwarf_CU *cu = attr->cu;
+ Dwarf *dbg = cu->dbg;
+ const unsigned char *datap = attr->valp;
+ const unsigned char *endp = attr->cu->endp;
+ switch (attr->form)
{
- __libdw_seterrno (DWARF_E_NO_ADDR);
- return -1;
+ /* There is one form that just encodes the whole address. */
+ case DW_FORM_addr:
+ if (__libdw_read_address (dbg, cu_sec_idx (cu), datap,
+ cu->address_size, return_addr))
+ return -1;
+ return 0;
+
+ /* All others encode an index into the .debug_addr section where
+ the address can be found. */
+ case DW_FORM_addrx:
+ if (datap >= endp)
+ {
+ invalid:
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ return -1;
+ }
+ get_uleb128 (idx, datap, endp);
+ break;
+
+ case DW_FORM_addrx1:
+ if (datap >= endp - 1)
+ goto invalid;
+ idx = *datap;
+ break;
+
+ case DW_FORM_addrx2:
+ if (datap >= endp - 2)
+ goto invalid;
+ idx = read_2ubyte_unaligned (dbg, datap);
+ break;
+
+ case DW_FORM_addrx3:
+ if (datap >= endp - 3)
+ goto invalid;
+ idx = read_3ubyte_unaligned (dbg, datap);
+ break;
+
+ case DW_FORM_addrx4:
+ if (datap >= endp - 4)
+ goto invalid;
+ idx = read_4ubyte_unaligned (dbg, datap);
+ break;
+
+ default:
+ __libdw_seterrno (DWARF_E_NO_ADDR);
+ return -1;
}
- if (__libdw_read_address (attr->cu->dbg,
- cu_sec_idx (attr->cu), attr->valp,
- attr->cu->address_size, return_addr))
+ /* So we got an index. Lets see if it is valid and we can get the actual
+ address. */
+
+ Dwarf_Off addr_off = __libdw_cu_addr_base (cu);
+ if (addr_off == (Dwarf_Off) -1)
return -1;
+ if (dbg->sectiondata[IDX_debug_addr] == NULL)
+ {
+ __libdw_seterrno (DWARF_E_NO_DEBUG_ADDR);
+ return -1;
+ }
+
+ /* The section should at least contain room for one address. */
+ int address_size = cu->address_size;
+ if (cu->address_size > dbg->sectiondata[IDX_debug_addr]->d_size)
+ {
+ invalid_offset:
+ __libdw_seterrno (DWARF_E_INVALID_OFFSET);
+ return -1;
+ }
+
+ if (addr_off > (dbg->sectiondata[IDX_debug_addr]->d_size
+ - address_size))
+ goto invalid_offset;
+
+ idx *= address_size;
+ if (idx > (dbg->sectiondata[IDX_debug_addr]->d_size
+ - address_size - addr_off))
+ goto invalid_offset;
+
+ datap = dbg->sectiondata[IDX_debug_addr]->d_buf + addr_off + idx;
+ if (address_size == 4)
+ *return_addr = read_4ubyte_unaligned (dbg, datap);
+ else
+ *return_addr = read_8ubyte_unaligned (dbg, datap);
+
return 0;
}
INTDEF(dwarf_formaddr)
+
+Dwarf_Off __libdw_cu_addr_base (Dwarf_CU *cu)
+{
+ if (cu->addr_base == (Dwarf_Off) -1)
+ {
+ Dwarf_Die cu_die = CUDIE(cu);
+ Dwarf_Attribute attr;
+ if (dwarf_attr (&cu_die, DW_AT_addr_base, &attr) != NULL)
+ {
+ Dwarf_Word off;
+ if (dwarf_formudata (&attr, &off) == 0)
+ cu->addr_base = off;
+ }
+ }
+
+ return cu->addr_base;
+}
diff --git a/libdw/dwarf_formudata.c b/libdw/dwarf_formudata.c
index 95872d6..5d5fc63 100644
--- a/libdw/dwarf_formudata.c
+++ b/libdw/dwarf_formudata.c
@@ -182,6 +182,14 @@ dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval)
return -1;
break;
+ case DW_AT_addr_base:
+ /* addrptr */
+ if (__libdw_formptr (attr, IDX_debug_addr,
+ DWARF_E_NO_DEBUG_ADDR, NULL,
+ return_uval) == NULL)
+ return -1;
+ break;
+
default:
/* sec_offset can only be used by one of the above attrs. */
if (attr->form == DW_FORM_sec_offset)
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index ad55558..ba50c35 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -74,6 +74,7 @@ enum
IDX_debug_types,
IDX_debug_abbrev,
IDX_debug_aranges,
+ IDX_debug_addr,
IDX_debug_line,
IDX_debug_frame,
IDX_debug_loc,
@@ -131,6 +132,7 @@ enum
DWARF_E_INVALID_OPCODE,
DWARF_E_NOT_CUDIE,
DWARF_E_UNKNOWN_LANGUAGE,
+ DWARF_E_NO_DEBUG_ADDR,
};
@@ -324,6 +326,10 @@ struct Dwarf_CU
/* Known location lists. */
void *locs;
+ /* The offset into the .debug_addr section where index zero begins.
+ Don't access directly, call __libdw_cu_addr_base. */
+ Dwarf_Off addr_base;
+
/* Memory boundaries of this CU. */
void *startp;
void *endp;
@@ -865,6 +871,9 @@ int __libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset,
/* Load and return value of DW_AT_comp_dir from CUDIE. */
const char *__libdw_getcompdir (Dwarf_Die *cudie);
+/* Get the address base for the CU, fetches it when not yet set. */
+Dwarf_Off __libdw_cu_addr_base (Dwarf_CU *cu);
+
/* Given a file descriptor, dir and file returns a full path. If the
file is absolute (starts with a /) a copy of file is returned. If
the file isn't absolute, but dir is absolute, then a path that is
diff --git a/libdw/memory-access.h b/libdw/memory-access.h
index 5f96a14..22918cb 100644
--- a/libdw/memory-access.h
+++ b/libdw/memory-access.h
@@ -1,7 +1,6 @@
/* Unaligned memory access functionality.
- Copyright (C) 2000-2014 Red Hat, Inc.
+ Copyright (C) 2000-2014, 2018 Red Hat, Inc.
This file is part of elfutils.
- Written by Ulrich Drepper <drepper@redhat.com>, 2001.
This file is free software; you can redistribute it and/or modify
it under the terms of either
@@ -31,6 +30,7 @@
#define _MEMORY_ACCESS_H 1
#include <byteswap.h>
+#include <endian.h>
#include <limits.h>
#include <stdint.h>
@@ -315,6 +315,52 @@ read_8sbyte_unaligned_1 (bool other_byte_order, const void *p)
Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 8); \
t_; })
+/* 3ubyte reads are only used for DW_FORM_addrx3 and DW_FORM_strx3.
+ And are probably very rare. They are not optimized. They are
+ handled as if reading a 4byte value with the first (for big endian)
+ or last (for little endian) byte zero. */
+
+static inline int
+file_byte_order (bool other_byte_order)
+{
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ return other_byte_order ? __BIG_ENDIAN : __LITTLE_ENDIAN;
+#else
+ return other_byte_order ? __LITTLE_ENDIAN : __BIG_ENDIAN;
+#endif
+}
+
+static inline uint32_t
+read_3ubyte_unaligned (Dwarf *dbg, const unsigned char *p)
+{
+ union
+ {
+ uint32_t u4;
+ unsigned char c[4];
+ } d;
+ bool other_byte_order = dbg->other_byte_order;
+
+ if (file_byte_order (other_byte_order) == __BIG_ENDIAN)
+ {
+ d.c[0] = 0x00;
+ d.c[1] = p[0];
+ d.c[2] = p[1];
+ d.c[3] = p[2];
+ }
+ else
+ {
+ d.c[0] = p[0];
+ d.c[1] = p[1];
+ d.c[2] = p[2];
+ d.c[3] = 0x00;
+ }
+
+ if (other_byte_order)
+ return bswap_32 (d.u4);
+ else
+ return d.u4;
+}
+
#define read_addr_unaligned_inc(Nbytes, Dbg, Addr) \
(assert ((Nbytes) == 4 || (Nbytes) == 8), \
diff --git a/src/ChangeLog b/src/ChangeLog
index 1ad6b3d..7b8084f 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,7 @@
+2018-03-22 Mark Wielaard <mark@klomp.org>
+
+ * readelf.c (attr_callback): Handle DW_FORM_addrx[1234].
+
2018-03-27 Mark Wielaard <mark@klomp.org>
* readelf.c (attr_callback): Print dwarf_dieoffset as %PRIx64,
diff --git a/src/readelf.c b/src/readelf.c
index 4e35b61..c1d6ac1 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -6064,6 +6064,11 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
switch (form)
{
case DW_FORM_addr:
+ case DW_FORM_addrx:
+ case DW_FORM_addrx1:
+ case DW_FORM_addrx2:
+ case DW_FORM_addrx3:
+ case DW_FORM_addrx4:
if (!cbargs->silent)
{
Dwarf_Addr addr;
--
1.8.3.1