]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
libdw: Add new dwarf_get_units function to iterate over all units.
authorMark Wielaard <mark@klomp.org>
Tue, 30 Jan 2018 11:05:57 +0000 (12:05 +0100)
committerMark Wielaard <mark@klomp.org>
Wed, 21 Feb 2018 16:10:23 +0000 (17:10 +0100)
The dwarf_nextcu and dwarf_next_unit functions provide information to
construct the offset to construct the associated CU DIE using dwarf_offdie
or dwarf_offdie_types.  This requires the user to know beforehand where
to DIE data is stored (in the .debug_info or .debug_types section).
For type units one also needs to use the type offset to create the actual
type DIE. In DWARF5 DIEs can come from even more data locations. And there
are also skeleton units which require the user to find the associated
split compile unit DIE (which would come from a different file).

The new dwarf_get_units function simplifies iterating over the units in
a DWARF file. It doesn't require the user to know where the DIE data is
stored, it will automagically iterate over all know data sources (sections)
returning the Dwarf_CU and the associated Dwarf_Die if requested. If the
user requests to know the associated "subdie" it will also be resolved.

This implementation returns the correct subdie for type units. A future
version will also handle skeleton units and return the associated skeleton
DIE and split unit DIE.

readelf has been adapted to use the new iterator and print the new DWARF5
unit header information (which it gets through dwarf_cu_die). The new
interface hides which section exactly to iterate on (by design). readelf
works around that by "cheating". It sets up a Dwarf_CU so that it gets
the data from the right section, using the (normally) internal data
structure.

Signed-off-by: Mark Wielaard <mark@klomp.org>
libdw/ChangeLog
libdw/Makefile.am
libdw/dwarf_get_units.c [new file with mode: 0644]
libdw/libdw.h
libdw/libdw.map
src/ChangeLog
src/readelf.c

index d0ceec174c0d5dd50b0b627ccb2831f1f767b33d..caa9f1d52695fc5d50166222268db806a54720ad 100644 (file)
@@ -1,3 +1,10 @@
+2018-01-30  Mark Wielaard  <mark@klomp.org>
+
+       * Makefile.am (libdw_a_SOURCES): Add dwarf_get_units.c.
+       * dwarf_get_units.c: New file.
+       * libdw.h (dwarf_get_units): New function declaration.
+       * libdw.map (ELFUTILS_0.170): Add dwarf_get_units.
+
 2018-01-29  Mark Wielaard  <mark@klomp.org>
 
        * dwarf.h (DW_UT_*): Add DWARF Unit Header Types.
index b1da44065f6115081b7f4c80f1b07a89bc505307..8848f1415037032cdd86109ad29746e76d13acbf 100644 (file)
@@ -90,7 +90,7 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \
                  dwarf_getlocation_die.c dwarf_getlocation_attr.c \
                  dwarf_getalt.c dwarf_setalt.c dwarf_cu_getdwarf.c \
                  dwarf_cu_die.c dwarf_peel_type.c dwarf_default_lower_bound.c \
-                 dwarf_die_addr_die.c
+                 dwarf_die_addr_die.c dwarf_get_units.c
 
 if MAINTAINER_MODE
 BUILT_SOURCES = $(srcdir)/known-dwarf.h
diff --git a/libdw/dwarf_get_units.c b/libdw/dwarf_get_units.c
new file mode 100644 (file)
index 0000000..19ff5de
--- /dev/null
@@ -0,0 +1,112 @@
+/* Iterate through the CU units for a given Dwarf.
+   Copyright (C) 2016, 2017 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+
+#include "libdwP.h"
+
+int
+dwarf_get_units (Dwarf *dwarf, Dwarf_CU *cu, Dwarf_CU **next_cu,
+                Dwarf_Half *version, uint8_t *unit_type,
+                Dwarf_Die *cudie, Dwarf_Die *subdie)
+{
+  Dwarf_Off off;
+  bool v4type;
+  if (cu == NULL)
+    {
+      off = 0;
+      v4type = false;
+    }
+  else
+    {
+      off = cu->end;
+      v4type = cu->sec_idx != IDX_debug_info;
+
+      /* Make sure we got a real (not fake) CU.  */
+      if (cu->sec_idx != IDX_debug_info && cu->sec_idx != IDX_debug_types)
+       {
+         __libdw_seterrno (DWARF_E_INVALID_OFFSET);
+         return -1;
+       }
+
+      /* Do we have to switch to the other section, or are we at the end?  */
+      if (! v4type)
+       {
+         if (off >= cu->dbg->sectiondata[IDX_debug_info]->d_size)
+           {
+             if (cu->dbg->sectiondata[IDX_debug_types] == NULL)
+               return 1;
+
+             off = 0;
+             v4type = true;
+           }
+       }
+      else
+       if (off >= cu->dbg->sectiondata[IDX_debug_types]->d_size)
+         return 1;
+    }
+
+  *next_cu = __libdw_findcu (dwarf, off, v4type);
+  if (*next_cu == NULL)
+    return -1;
+
+  Dwarf_CU *next = (*next_cu);
+
+  if (version != NULL)
+    *version = next->version;
+
+  if (unit_type != NULL)
+    *unit_type = next->unit_type;
+
+  if (cudie != NULL)
+    {
+      if (next->version >= 2 && next->version <= 5
+         && next->unit_type >= DW_UT_compile
+         && next->unit_type <= DW_UT_split_type)
+       *cudie = CUDIE (next);
+      else
+       memset (cudie, '\0', sizeof (Dwarf_Die));
+    }
+
+  if (subdie != NULL)
+    {
+      if (next->version >= 2 && next->version <= 5
+         && (next->unit_type == DW_UT_type
+             || next->unit_type == DW_UT_split_type))
+       *subdie = SUBDIE(next);
+      else
+       memset (subdie, '\0', sizeof (Dwarf_Die));
+    }
+
+  return 0;
+}
index d85d28593aa685a0154bad75efe2c928d3870faf..acc389164f8557f11227ac69019e11dba17eba73 100644 (file)
@@ -289,6 +289,21 @@ extern int dwarf_next_unit (Dwarf *dwarf, Dwarf_Off off, Dwarf_Off *next_off,
      __nonnull_attribute__ (3);
 
 
+/* Gets the next Dwarf_CU (unit), version, unit type and if available
+   the CU DIE and sub (type) DIE of the unit.  Returns 0 on success,
+   -1 on error or 1 if there are no more units.  To start iterating
+   provide NULL for CU.  If version < 5 the unit type is set from the
+   CU DIE if available (DW_UT_compile for DW_TAG_compile_unit,
+   DW_UT_type for DW_TAG_type_unit or DW_UT_partial for
+   DW_TAG_partial_unit), otherwise it is set to zero.  If unavailable
+   (the version or unit type is unknown) the CU DIE is cleared.
+   Likewise ff the sub DIE isn't isn't available (the unit type is not
+   DW_UT_type or DW_UT_split_type) the sub DIE tag is cleared.  */
+extern int dwarf_get_units (Dwarf *dwarf, Dwarf_CU *cu, Dwarf_CU **next_cu,
+                           Dwarf_Half *version, uint8_t *unit_type,
+                           Dwarf_Die *cudie, Dwarf_Die *subdie)
+     __nonnull_attribute__ (3);
+
 /* Decode one DWARF CFI entry (CIE or FDE) from the raw section data.
    The E_IDENT from the originating ELF file indicates the address
    size and byte order used in the CFI section contained in DATA;
index cdc63ce28d560fc6161918f614304f8bbd30ff4b..8d12e892a4dcf353c589c0e9e2bc2e386aaa3c28 100644 (file)
@@ -348,4 +348,5 @@ ELFUTILS_0.170 {
 ELFUTILS_0.171 {
   global:
     dwarf_die_addr_die;
+    dwarf_get_units;
 } ELFUTILS_0.170;
index a490705fd79998ab239d3ab992c81507d3e4792d..a828966f541c707ed145dee08ae2ee5aa1368207 100644 (file)
@@ -1,3 +1,10 @@
+2018-01-30  Mark Wielaard  <mark@klomp.org>
+
+       * readelf.c (dwarf_unit_string): New function.
+       (dwarf_unit_name): Likewise.
+       (print_debug_units): Use dwarf_get_units and dwarf_cu_die instead
+       of dwarf_next_unit. Print unit type, id and subdie if available.
+
 2018-02-09  Joshua Watt  <JPEWhacker@gmail.com>
 
        * addr2line.c (handle_address): Use FALLTHROUGH macro instead of
index d606cf5a1dabc3069d0bd4b5b9ccb81b31398540..93bc45dbaff6e4395754a2ceea2dbb711bafe50d 100644 (file)
@@ -3950,6 +3950,20 @@ dwarf_locexpr_opcode_string (unsigned int code)
 }
 
 
+static const char *
+dwarf_unit_string (unsigned int type)
+{
+  switch (type)
+    {
+#define DWARF_ONE_KNOWN_DW_UT(NAME, CODE) case CODE: return #NAME;
+      DWARF_ALL_KNOWN_DW_UT
+#undef DWARF_ONE_KNOWN_DW_UT
+    default:
+      return NULL;
+    }
+}
+
+
 /* Used by all dwarf_foo_name functions.  */
 static const char *
 string_or_unknown (const char *known, unsigned int code,
@@ -4089,6 +4103,14 @@ dwarf_discr_list_name (unsigned int code)
 }
 
 
+static const char *
+dwarf_unit_name (unsigned int type)
+{
+  const char *ret = dwarf_unit_string (type);
+  return string_or_unknown (ret, type, DW_UT_lo_user, DW_UT_hi_user, true);
+}
+
+
 static void
 print_block (size_t n, const void *block)
 {
@@ -6336,41 +6358,102 @@ print_debug_units (Dwfl_Module *dwflmod,
   int maxdies = 20;
   Dwarf_Die *dies = (Dwarf_Die *) xmalloc (maxdies * sizeof (Dwarf_Die));
 
-  Dwarf_Off offset = 0;
-
   /* New compilation unit.  */
-  size_t cuhl;
   Dwarf_Half version;
+
+  Dwarf_Die result;
   Dwarf_Off abbroffset;
   uint8_t addrsize;
   uint8_t offsize;
-  Dwarf_Off nextcu;
-  uint64_t typesig;
-  Dwarf_Off typeoff;
+  uint64_t unit_id;
+  Dwarf_Off subdie_off;
+
+  int unit_res;
+  Dwarf_CU *cu;
+  Dwarf_CU cu_mem;
+  uint8_t unit_type;
+  Dwarf_Die cudie;
+  Dwarf_Die subdie;
+
+  /* We cheat a little because we want to see only the CUs from .debug_info
+     or .debug_types.  We know the Dwarf_CU struct layout.  Set it up at
+     the end of .debug_info if we want .debug_types only.  Check the returned
+     Dwarf_CU is still in the expected section.  */
+  if (debug_types)
+    {
+      cu_mem.dbg = dbg;
+      cu_mem.end = dbg->sectiondata[IDX_debug_info]->d_size;
+      cu_mem.sec_idx = IDX_debug_info;
+      cu = &cu_mem;
+    }
+  else
+    cu = NULL;
+
  next_cu:
-  if (dwarf_next_unit (dbg, offset, &nextcu, &cuhl, &version,
-                      &abbroffset, &addrsize, &offsize,
-                      debug_types ? &typesig : NULL,
-                      debug_types ? &typeoff : NULL) != 0)
+  unit_res = dwarf_get_units (dbg, cu, &cu, &version, &unit_type,
+                             &cudie, &subdie);
+  if (unit_res == 1)
+    goto do_return;
+
+  if (unit_res == -1)
+    {
+      if (!silent)
+       error (0, 0, gettext ("cannot get next unit: %s"), dwarf_errmsg (-1));
+      goto do_return;
+    }
+
+  if (cu->sec_idx != (size_t) (debug_types ? IDX_debug_types : IDX_debug_info))
     goto do_return;
 
+  dwarf_cu_die (cu, &result, NULL, &abbroffset, &addrsize, &offsize,
+               &unit_id, &subdie_off);
+
   if (!silent)
     {
-      if (debug_types)
+      Dwarf_Off offset = cu->start;
+      if (debug_types && version < 5)
        printf (gettext (" Type unit at offset %" PRIu64 ":\n"
                         " Version: %" PRIu16 ", Abbreviation section offset: %"
                         PRIu64 ", Address size: %" PRIu8
                         ", Offset size: %" PRIu8
                         "\n Type signature: %#" PRIx64
-                        ", Type offset: %#" PRIx64 "\n"),
+                        ", Type offset: %#" PRIx64 " [%" PRIx64 "]\n"),
                (uint64_t) offset, version, abbroffset, addrsize, offsize,
-               typesig, (uint64_t) typeoff);
+               unit_id, (uint64_t) subdie_off, dwarf_dieoffset (&subdie));
       else
-       printf (gettext (" Compilation unit at offset %" PRIu64 ":\n"
-                        " Version: %" PRIu16 ", Abbreviation section offset: %"
-                        PRIu64 ", Address size: %" PRIu8
-                        ", Offset size: %" PRIu8 "\n"),
-               (uint64_t) offset, version, abbroffset, addrsize, offsize);
+       {
+         printf (gettext (" Compilation unit at offset %" PRIu64 ":\n"
+                          " Version: %" PRIu16
+                          ", Abbreviation section offset: %" PRIu64
+                          ", Address size: %" PRIu8
+                          ", Offset size: %" PRIu8 "\n"),
+                 (uint64_t) offset, version, abbroffset, addrsize, offsize);
+
+         if (version >= 5)
+           {
+             printf (gettext (" Unit type: %s (%" PRIu8 ")"),
+                              dwarf_unit_name (unit_type), unit_type);
+             if (unit_type == DW_UT_type
+                 || unit_type == DW_UT_skeleton
+                 || unit_type == DW_UT_split_compile
+                 || unit_type == DW_UT_split_type)
+               printf (", Unit id: 0x%.16" PRIx64 "", unit_id);
+             if (unit_type == DW_UT_type
+                 || unit_type == DW_UT_split_type)
+               printf (", Unit DIE off: %#" PRIx64 " [%" PRIx64 "]",
+                       subdie_off, dwarf_dieoffset (&subdie));
+             printf ("\n");
+           }
+       }
+    }
+
+  if (version < 2 || version > 5
+      || unit_type < DW_UT_compile || unit_type > DW_UT_split_type)
+    {
+      if (!silent)
+       error (0, 0, gettext ("unknown version (%d) or unit type (%d)"),
+              version, unit_type);
+      goto next_cu;
     }
 
   struct attrcb_args args =
@@ -6383,25 +6466,13 @@ print_debug_units (Dwfl_Module *dwflmod,
       .offset_size = offsize
     };
 
-  offset += cuhl;
-
   int level = 0;
-
-  if (unlikely ((debug_types ? dwarf_offdie_types : dwarf_offdie)
-               (dbg, offset, &dies[level]) == NULL))
-    {
-      if (!silent)
-       error (0, 0, gettext ("cannot get DIE at offset %" PRIu64
-                             " in section '%s': %s"),
-              (uint64_t) offset, secname, dwarf_errmsg (-1));
-      goto do_return;
-    }
-
+  dies[0] = cudie;
   args.cu = dies[0].cu;
 
   do
     {
-      offset = dwarf_dieoffset (&dies[level]);
+      Dwarf_Off offset = dwarf_dieoffset (&dies[level]);
       if (unlikely (offset == ~0ul))
        {
          if (!silent)
@@ -6466,9 +6537,8 @@ print_debug_units (Dwfl_Module *dwflmod,
     }
   while (level >= 0);
 
-  offset = nextcu;
-  if (offset != 0)
-     goto next_cu;
+  /* And again... */
+  goto next_cu;
 
  do_return:
   free (dies);