]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
libdw: Add support for reading DW_FORM_addrx[1234] in .debug_addr.
authorMark Wielaard <mark@klomp.org>
Fri, 24 Nov 2017 10:18:39 +0000 (11:18 +0100)
committerMark Wielaard <mark@klomp.org>
Tue, 3 Apr 2018 11:13:07 +0000 (13:13 +0200)
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
libdw/dwarf_begin_elf.c
libdw/dwarf_error.c
libdw/dwarf_formaddr.c
libdw/dwarf_formudata.c
libdw/libdwP.h
libdw/memory-access.h
src/ChangeLog
src/readelf.c

index 025b24ad6609da01b19faf2f524688168f3fa5ad..f6322b58dd2a08c2aae07df193d690aae28604b2 100644 (file)
@@ -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-29  Mark Wielaard  <mark@klomp.org>
 
        * libdw.h (dwarf_decl_file): Extend documentation.
index 6834ac533f19749b9703d2435d4837f8dc5a759f..1ffa6c933cf49b171a3c1f1243c0336693d788e7 100644 (file)
@@ -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",
index 939ec047f0eb8797daa235e05264a0be2672d99a..212f32e92926120f78b190aeeb1079a864a17ea1 100644 (file)
@@ -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]))
 
index ddc483822af6d86e0012f48700ba8932fd5b15d6..25e6970cbce448512c74aa2903c177514da2e49e 100644 (file)
@@ -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;
+}
index 95872d6be0501d2680e9a1b3fd36cc5b3b3cb45b..5d5fc635a508ab88c37428dbfa978d8394c670da 100644 (file)
@@ -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)
index ad5555888efacefde2dfefb555d948375db499fd..ba50c35b3ddf12469d24c7cba2733eb16b542ec2 100644 (file)
@@ -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
index 5f96a14a645736ba618bf4228d0f51fb9f10f8fe..22918cb9497aa5b4061ae02c277b15dcf7fe9231 100644 (file)
@@ -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),                            \
index e8bd6bf1fda17b1588c61fc49cc463893822728e..3ca090dcdfc7f019d86c33455433aff0840e9240 100644 (file)
@@ -1,3 +1,7 @@
+2018-03-22  Mark Wielaard  <mark@klomp.org>
+
+       * readelf.c (attr_callback): Handle DW_FORM_addrx[1234].
+
 2018-03-28  Mark Wielaard  <mark@klomp.org>
 
        * readelf.c (handle_sysv_hash): Break bucket chain after nchain
index 226b19bee60d01d94115a032086be54d4da0ee42..9210b2ac068a3c086b83afa1788bba17094a4576 100644 (file)
@@ -6072,6 +6072,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;