This is the mail archive of the elfutils-devel@sourceware.org mailing list for the elfutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]