]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
libdw: Add dwarf_die_addr_die function.
authorMark Wielaard <mark@klomp.org>
Thu, 25 Jan 2018 00:13:10 +0000 (01:13 +0100)
committerMark Wielaard <mark@klomp.org>
Tue, 13 Feb 2018 16:10:28 +0000 (17:10 +0100)
Currently storing a lot of Dwarf_Dies might be inefficient since it
costs a lot of memory since the sizeof (Dwarf_Die) == 32 bytes on 64bit
arches. You can try storing just the Dwarf_Off from dwarf_dieoffset.
Which is just 8 bytes. But then you have to keep track of whether to
call dwarf_dieoffset, if the Dwarf_Die came from the main .debug_info,
or call dwarf_dieoffset_types, if it came from .debug_types. And you'll
have to keep track of whether it came from the main Dwarf or the alt
Dwarf (dwz multi file). With DWARF5 or GNU DebugFission split-dwarf
you will also need to know which split Dwarf file the original DIE
came from.

A Dwarf_Die consists of an addr pointer where the actual DIE data
comes from, a CU pointer that provides context (and has a pointer
to the Dwarf file the Die is associated with) and a (cached)
Dwarf_Abbrev pointer that is initialized when the Dwarf_Die is
first read and describes how to interpret the DIE data.

libdw already keeps track of the data pointers (sections) of a
Dwarf file and given an offset it can already reconstruct the
other Dwarf_Die fields. So this patch introduces dwarf_die_addr_die.
Given a Dwarf_Die addr dwarf_die_addr_die returns a (reconstructed)
Dwarf_Die, or NULL if the given addr didn't come from a valid
Dwarf_Die. In particular it will make sure that the correct Dwarf_CU
pointer is set for the Dwarf_Die, the Dwarf_Abbrev pointer will not
be set up yet (it will only be once the Dwarf_Die is used to read
attributes, children or siblings).

This functions can be used to keep a reference to a Dwarf_Die which
you want to refer to later. The addr, and the result of this function,
is only valid while the associated Dwarf is valid.

Since libdw already had to lookup the Dwarf_CU given an offset, this
function is as efficient as dwarf_dieoffset (or dwarf_dieoffset_types)
without having to know the original origin of the Dwarf_Die. It will
search both the .debug_info and .debug_types data sections from both
the main Dwarf or the alt Dwarf file. Once split dwarf support is added
it will also look in any split dwarf .dwo (or the .dwp) file.

The only limitation, compared to using a Dwarf_Off and dwarf_dieoffset,
is that it only works during runtime while the main Dwarf object is
valid (till dwarf_end has been called on it).

Signed-off-by: Mark Wielaard <mark@klomp.org>
libdw/ChangeLog
libdw/Makefile.am
libdw/dwarf_die_addr_die.c [new file with mode: 0644]
libdw/libdw.h
libdw/libdw.map
libdw/libdwP.h
libdw/libdw_findcu.c
tests/ChangeLog
tests/Makefile.am
tests/dwarf-die-addr-die.c [new file with mode: 0644]
tests/run-dwarf-die-addr-die.sh [new file with mode: 0755]

index 7a6d3110e6aa8f8aaed0bbc68ca078bc6c8b1b8e..a65d691ea562f81387e8aac3f96dddca9f2cd375 100644 (file)
@@ -1,3 +1,12 @@
+2018-01-25  Mark Wielaard  <mark@klomp.org>
+
+       * Makefile.am (libdw_a_SOURCES): Add dwarf_die_addr_die.c.
+       * dwarf_die_addr_die.c: New file.
+       * libdw.h (dwarf_die_addr_die): New function declaration.
+       * libdw.map (ELFUTILS_0.171): New section with dwarf_die_addr_die.
+       * libdwP.h (__libdw_findcu_addr): New internal function declaration.
+       * libdw_findcu.c (__libdw_findcu_addr): New internal function.
+
 2018-02-09  Joshua Watt  <JPEWhacker@gmail.com>
 
        * cfi.c (execute_cfi): Use FALLTHROUGH macro instead of comment.
index 8545b5b48053f9e15d9bf621be2a5355a6cf14c6..b1da44065f6115081b7f4c80f1b07a89bc505307 100644 (file)
@@ -89,7 +89,8 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \
                  dwarf_aggregate_size.c dwarf_getlocation_implicit_pointer.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_cu_die.c dwarf_peel_type.c dwarf_default_lower_bound.c \
+                 dwarf_die_addr_die.c
 
 if MAINTAINER_MODE
 BUILT_SOURCES = $(srcdir)/known-dwarf.h
diff --git a/libdw/dwarf_die_addr_die.c b/libdw/dwarf_die_addr_die.c
new file mode 100644 (file)
index 0000000..02d63b7
--- /dev/null
@@ -0,0 +1,58 @@
+/* Return offset of DIE.
+   Copyright (C) 2018 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 <dwarf.h>
+#include "libdwP.h"
+
+
+Dwarf_Die *
+dwarf_die_addr_die (Dwarf *dbg, void *addr, Dwarf_Die *result)
+{
+  if (dbg == NULL)
+    return NULL;
+
+  Dwarf_CU *cu = __libdw_findcu_addr (dbg, addr);
+
+  if (cu == NULL)
+    {
+      Dwarf *alt = INTUSE (dwarf_getalt) (dbg);
+      if (alt != NULL)
+       cu = __libdw_findcu_addr (alt, addr);
+    }
+
+  if (cu == NULL)
+    return NULL;
+
+  *result = (Dwarf_Die) { .addr = addr, .cu = cu };
+
+  return result;
+}
index 1dcc81531489bd10593786fbdbe2f09c654b1bd3..d85d28593aa685a0154bad75efe2c928d3870faf 100644 (file)
@@ -350,6 +350,18 @@ extern Dwarf_Die *dwarf_diecu (Dwarf_Die *die, Dwarf_Die *result,
                               uint8_t *address_sizep, uint8_t *offset_sizep)
      __nonnull_attribute__ (2);
 
+/* Given a Dwarf_Die addr returns a (reconstructed) Dwarf_Die, or NULL
+   if the given addr didn't come from a valid Dwarf_Die.  In particular
+   it will make sure that the correct Dwarf_CU pointer is set for the
+   Dwarf_Die, the Dwarf_Abbrev pointer will not be set up yet (it will
+   only be once the Dwarf_Die is used to read attributes, children or
+   siblings).  This functions can be used to keep a reference to a
+   Dwarf_Die which you want to refer to later.  The addr, and the result
+   of this function, is only valid while the associated Dwarf is valid.  */
+extern Dwarf_Die *dwarf_die_addr_die (Dwarf *dbg, void *addr,
+                                     Dwarf_Die *result)
+     __nonnull_attribute__ (3);
+
 /* Return the CU DIE and the header info associated with a Dwarf_Die
    or Dwarf_Attribute.  A Dwarf_Die or a Dwarf_Attribute is associated
    with a particular Dwarf_CU handle.  This function returns the CU or
index 143070560a8f7ae294728fc9ec8b0156e884c3ef..cdc63ce28d560fc6161918f614304f8bbd30ff4b 100644 (file)
@@ -344,3 +344,8 @@ ELFUTILS_0.170 {
     dwarf_default_lower_bound;
     dwarf_line_file;
 } ELFUTILS_0.167;
+
+ELFUTILS_0.171 {
+  global:
+    dwarf_die_addr_die;
+} ELFUTILS_0.170;
index 0681aa15e6d3769e1877329377ec21962d1185a8..a38dcfb3577d2d8eda9b4e8d600a0e2a6380163a 100644 (file)
@@ -449,6 +449,10 @@ extern struct Dwarf_CU *__libdw_intern_next_unit (Dwarf *dbg, bool debug_types)
 extern struct Dwarf_CU *__libdw_findcu (Dwarf *dbg, Dwarf_Off offset, bool tu)
      __nonnull_attribute__ (1) internal_function;
 
+/* Find CU for given DIE address.  */
+extern struct Dwarf_CU *__libdw_findcu_addr (Dwarf *dbg, void *addr)
+     __nonnull_attribute__ (1) internal_function;
+
 /* Get abbreviation with given code.  */
 extern Dwarf_Abbrev *__libdw_findabbrev (struct Dwarf_CU *cu,
                                         unsigned int code)
index 4e025e26faaf8ebdd7d52a21986d68a91d56ceed..3ec1ce5921a3cf2c97f1207de008c013be0572a9 100644 (file)
@@ -1,5 +1,5 @@
 /* Find CU for given offset.
-   Copyright (C) 2003-2010, 2014 Red Hat, Inc.
+   Copyright (C) 2003-2010, 2014, 2018 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2003.
 
@@ -167,3 +167,36 @@ __libdw_findcu (Dwarf *dbg, Dwarf_Off start, bool debug_types)
     }
   /* NOTREACHED */
 }
+
+struct Dwarf_CU *
+internal_function
+__libdw_findcu_addr (Dwarf *dbg, void *addr)
+{
+  void **tree;
+  Dwarf_Off start;
+  if (addr >= dbg->sectiondata[IDX_debug_info]->d_buf
+      && addr < (dbg->sectiondata[IDX_debug_info]->d_buf
+                + dbg->sectiondata[IDX_debug_info]->d_size))
+    {
+      tree = &dbg->cu_tree;
+      start = addr - dbg->sectiondata[IDX_debug_info]->d_buf;
+    }
+  else if (dbg->sectiondata[IDX_debug_types] != NULL
+          && addr >= dbg->sectiondata[IDX_debug_types]->d_buf
+          && addr < (dbg->sectiondata[IDX_debug_types]->d_buf
+                     + dbg->sectiondata[IDX_debug_types]->d_size))
+    {
+      tree = &dbg->tu_tree;
+      start = addr - dbg->sectiondata[IDX_debug_types]->d_buf;
+    }
+  else
+    return NULL;
+
+  struct Dwarf_CU fake = { .start = start, .end = 0 };
+  struct Dwarf_CU **found = tfind (&fake, tree, findcu_cb);
+
+  if (found != NULL)
+    return *found;
+
+  return NULL;
+}
index 5ee8626ed62126cf0d68525b7851e42dfe612044..eb4576cafeb21153e77c2c400dba79f5e7768935 100644 (file)
@@ -1,3 +1,12 @@
+2018-01-25  Mark Wielaard  <mark@klomp.org>
+
+       * Makefile.am (check_PROGRAMS): Add dwarf-die-addr-die.
+       (TESTS): Add run-dwarf-die-addr-die.sh.
+       (EXTRA_DIST): Likewise.
+       (dwarf_die_addr_die_LDADD): New variable.
+       * dwarf-die-addr-die.c: New file.
+       * run-dwarf-die-addr-die.sh: New test.
+
 2018-02-09  Joshua Watt  <JPEWhacker@gmail.com>
 
        * elfstrmerge.c (main): Use FALLTHROUGH macro instead of comment.
index 1fce4474009cc1072a09da118cdc153183099bbc..fe6c8ec563e6588ecc9243ffa978f6ee0798bca6 100644 (file)
@@ -55,7 +55,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
                  getsrc_die strptr newdata elfstrtab dwfl-proc-attach \
                  elfshphehdr elfstrmerge dwelfgnucompressed elfgetchdr \
                  elfgetzdata elfputzdata zstrptr emptyfile vendorelf \
-                 fillfile dwarf_default_lower_bound
+                 fillfile dwarf_default_lower_bound dwarf-die-addr-die
 
 asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
            asm-tst6 asm-tst7 asm-tst8 asm-tst9
@@ -137,7 +137,8 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \
        run-elfgetzdata.sh run-elfputzdata.sh run-zstrptr.sh \
        run-compress-test.sh \
        run-readelf-zdebug.sh run-readelf-zdebug-rel.sh \
-       emptyfile vendorelf fillfile dwarf_default_lower_bound
+       emptyfile vendorelf fillfile dwarf_default_lower_bound \
+       run-dwarf-die-addr-die.sh
 
 if !BIARCH
 export ELFUTILS_DISABLE_BIARCH = 1
@@ -352,7 +353,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
             run-zstrptr.sh run-compress-test.sh \
             run-disasm-bpf.sh \
             testfile-bpf-dis1.expect.bz2 testfile-bpf-dis1.o.bz2 \
-            testfile-m68k-core.bz2 testfile-m68k.bz2 testfile-m68k-s.bz2
+            testfile-m68k-core.bz2 testfile-m68k.bz2 testfile-m68k-s.bz2 \
+            run-dwarf-die-addr-die.sh
 
 if USE_VALGRIND
 valgrind_cmd='valgrind -q --leak-check=full --error-exitcode=1'
@@ -510,6 +512,7 @@ emptyfile_LDADD = $(libelf)
 vendorelf_LDADD = $(libelf)
 fillfile_LDADD = $(libelf)
 dwarf_default_lower_bound_LDADD = $(libdw)
+dwarf_die_addr_die_LDADD = $(libdw)
 
 # We want to test the libelf header against the system elf.h header.
 # Don't include any -I CPPFLAGS.
diff --git a/tests/dwarf-die-addr-die.c b/tests/dwarf-die-addr-die.c
new file mode 100644 (file)
index 0000000..b4f6dbc
--- /dev/null
@@ -0,0 +1,172 @@
+/* Test program for dwarf_die_addr_die.
+   Copyright (C) 2018 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 the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   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 a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+#include ELFUTILS_HEADER(dw)
+#include <dwarf.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+/* The main Dwarf file.  */
+static Dwarf *dwarf;
+
+int
+check_die (Dwarf_Die *die)
+{
+  if (dwarf_tag (die) == DW_TAG_invalid)
+    {
+      printf ("Invalid die\n");
+      return -1;
+    }
+
+  int res = 0;
+  void *addr = die->addr;
+  Dwarf_Die die2;
+  if (dwarf_die_addr_die (dwarf, addr, &die2) == NULL)
+    {
+      printf ("Bad die addr die at offset %" PRIx64 "\n",
+             dwarf_dieoffset (die));
+      res = -1;
+    }
+
+  if (dwarf_tag (die) != dwarf_tag (&die2))
+    {
+      printf ("Tags differ for die at offset %" PRIx64 "\n",
+             dwarf_dieoffset (die));
+      res = -1;
+    }
+
+  if (dwarf_cuoffset (die) != dwarf_cuoffset (&die2))
+    {
+      printf ("CU offsets differ for die at offset %" PRIx64 "\n",
+             dwarf_dieoffset (die));
+      res = -1;
+    }
+
+  Dwarf_Die child;
+  if (dwarf_child (die, &child) == 0)
+    res |= check_die (&child);
+
+  Dwarf_Die sibling;
+  if (dwarf_siblingof (die, &sibling) == 0)
+    res |= check_die (&sibling);
+
+  return res;
+}
+
+int
+check_dbg (Dwarf *dbg)
+{
+  int res = 0;
+  Dwarf_Off off = 0;
+  Dwarf_Off old_off = 0;
+  size_t hsize;
+  Dwarf_Off abbrev;
+  uint8_t addresssize;
+  uint8_t offsetsize;
+  while (dwarf_nextcu (dbg, off, &off, &hsize, &abbrev, &addresssize,
+                       &offsetsize) == 0)
+    {
+      Dwarf_Die die;
+      if (dwarf_offdie (dbg, old_off + hsize, &die) != NULL)
+       {
+         printf ("checking CU at %" PRIx64 "\n", old_off);
+         res |= check_die (&die);
+       }
+
+      old_off = off;
+    }
+
+  // Same for type...
+  Dwarf_Half version;
+  uint64_t typesig;
+  Dwarf_Off typeoff;
+  off = 0;
+  old_off = 0;
+  while (dwarf_next_unit (dbg, off, &off, &hsize, &version, &abbrev,
+                         &addresssize, &offsetsize, &typesig, &typeoff) == 0)
+    {
+      Dwarf_Die die;
+      if (dwarf_offdie_types (dbg, old_off + hsize, &die) != NULL)
+       {
+         printf ("checking TU at %" PRIx64 "\n", old_off);
+         res |= check_die (&die);
+       }
+
+      // We should have seen this already, but double check...
+      if (dwarf_offdie_types (dbg, old_off + typeoff, &die) != NULL)
+       {
+         printf ("checking Type DIE at %" PRIx64 "\n",
+                 old_off + hsize + typeoff);
+         res |= check_die (&die);
+       }
+
+      old_off = off;
+    }
+
+  Dwarf *alt = dwarf_getalt (dbg);
+  if (alt != NULL)
+    {
+      printf ("checking alt debug\n");
+      res |= check_dbg (alt);
+    }
+
+  return res;
+}
+
+int
+main (int argc, char *argv[])
+{
+  if (argc < 2)
+    {
+      printf ("No file given.\n");
+      return -1;
+    }
+
+  const char *name = argv[1];
+  int fd = open (name, O_RDONLY);
+  if (fd < 0)
+    {
+      printf ("Cannnot open '%s': %s\n", name, strerror (errno));
+      return -1;
+    }
+
+  dwarf = dwarf_begin (fd, DWARF_C_READ);
+  if (dwarf == NULL)
+    {
+      printf ("Not a Dwarf file '%s': %s\n", name, dwarf_errmsg (-1));
+      close (fd);
+      return -1;
+    }
+
+  printf ("checking %s\n", name);
+  int res = check_dbg (dwarf);
+
+  dwarf_end (dwarf);
+  close (fd);
+
+  return res;
+}
diff --git a/tests/run-dwarf-die-addr-die.sh b/tests/run-dwarf-die-addr-die.sh
new file mode 100755 (executable)
index 0000000..16fe7b0
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/sh
+# Copyright (C) 2012, 2015 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 the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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 a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/test-subr.sh
+
+# See run-typeiter.sh
+testfiles testfile-debug-types
+
+testrun ${abs_builddir}/dwarf-die-addr-die testfile-debug-types
+
+# see run-readelf-dwz-multi.sh
+testfiles testfile_multi_main testfile_multi.dwz
+
+testrun ${abs_builddir}/dwarf-die-addr-die testfile_multi_main
+
+# see tests/run-dwflsyms.sh
+testfiles testfilebazdbgppc64.debug
+
+testrun ${abs_builddir}/dwarf-die-addr-die testfilebazdbgppc64.debug
+
+# Self test
+testrun_on_self ${abs_builddir}/dwarf-die-addr-die
+
+exit 0