>From e6a9bb8c63adaeaceafc50301b085121c962b631 Mon Sep 17 00:00:00 2001
From: Mark Wielaard <mjw@redhat.com>
Date: Thu, 10 Oct 2013 11:40:12 +0200
Subject: [PATCH] libdw: Handle dwz multi files correctly in dwarf_getfuncs.

Don't use DIE offsets, but use their addresses to make sure they are unique.
Include test cases where main and alt file have subprograms at same offsets.

Reported-by: Josh Stone <jistone@redhat.com>
Signed-off-by: Mark Wielaard <mjw@redhat.com>
---
 libdw/ChangeLog                |    8 +++++
 libdw/dwarf_getfuncs.c         |   26 +++++++++---------
 tests/ChangeLog                |   10 +++++++
 tests/Makefile.am              |    4 ++-
 tests/run-allfcts-multi.sh     |   56 ++++++++++++++++++++++++++++++++++++++++
 tests/test-offset-loop.alt.bz2 |  Bin 0 -> 685 bytes
 tests/test-offset-loop.bz2     |  Bin 0 -> 3062 bytes
 7 files changed, 90 insertions(+), 14 deletions(-)
 create mode 100755 tests/run-allfcts-multi.sh
 create mode 100644 tests/test-offset-loop.alt.bz2
 create mode 100755 tests/test-offset-loop.bz2

diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 960f5aa..1bf1de9 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,11 @@
+2013-10-10  Mark Wielaard  <mjw@redhat.com>
+
+	* dwarf_getfuncs.c (struct visitor_info): Rename start_offset to
+	start_addr and rename last_offset to last_addr. Now both void *.
+	(tree_visitor): Use start_add and die_addr instead of start_offset
+	and die_offset.
+	(dwarf_getfuncs): Use last_addr instead of last_offset.
+
 2013-10-06  Mark Wielaard  <mjw@redhat.com>
 
 	* cfi.c (execute_cfi): Make sure DW_CFA_expression and
diff --git a/libdw/dwarf_getfuncs.c b/libdw/dwarf_getfuncs.c
index 87e0341..82894c9 100644
--- a/libdw/dwarf_getfuncs.c
+++ b/libdw/dwarf_getfuncs.c
@@ -43,11 +43,11 @@ struct visitor_info
   /* The user arg value to dwarf_getfuncs.  */
   void *arg;
 
-  /* The DIE offset where to (re)start the search.  Zero for all.  */
-  Dwarf_Off start_offset;
+  /* Addr of the DIE offset where to (re)start the search.  Zero for all.  */
+  void *start_addr;
 
-  /* Last subprogram DIE offset seen.  */
-  Dwarf_Off last_offset;
+  /* Last subprogram DIE addr seen.  */
+  void *last_addr;
 
   /* The CU only contains C functions.  Allows pruning of most subtrees.  */
   bool c_cu;
@@ -59,8 +59,8 @@ tree_visitor (unsigned int depth __attribute__ ((unused)),
 {
   struct visitor_info *const v = arg;
   Dwarf_Die *die = &chain->die;
-  Dwarf_Off start_offset = v->start_offset;
-  Dwarf_Off die_offset = INTUSE(dwarf_dieoffset) (die);
+  void *start_addr = v->start_addr;
+  void *die_addr = die->addr;
 
   /* Pure C CUs can only contain defining subprogram DIEs as direct
      children of the CU DIE or as nested function inside a normal C
@@ -75,11 +75,11 @@ tree_visitor (unsigned int depth __attribute__ ((unused)),
       return DWARF_CB_OK;
     }
 
-  /* Skip all DIEs till we found the (re)start offset.  */
-  if (start_offset != 0)
+  /* Skip all DIEs till we found the (re)start addr.  */
+  if (start_addr != NULL)
     {
-      if (die_offset == start_offset)
-	v->start_offset = 0;
+      if (die_addr == start_addr)
+	v->start_addr = NULL;
       return DWARF_CB_OK;
     }
 
@@ -88,7 +88,7 @@ tree_visitor (unsigned int depth __attribute__ ((unused)),
       || INTUSE(dwarf_hasattr) (die, DW_AT_declaration))
     return DWARF_CB_OK;
 
-  v->last_offset = die_offset;
+  v->last_addr = die_addr;
   return (*v->callback) (die, v->arg);
 }
 
@@ -105,13 +105,13 @@ dwarf_getfuncs (Dwarf_Die *cudie, int (*callback) (Dwarf_Die *, void *),
 	       || lang == DW_LANG_C
 	       || lang == DW_LANG_C99);
 
-  struct visitor_info v = { callback, arg, offset, 0, c_cu };
+  struct visitor_info v = { callback, arg, (void *) offset, NULL, c_cu };
   struct Dwarf_Die_Chain chain = { .die = CUDIE (cudie->cu),
 				   .parent = NULL };
   int res = __libdw_visit_scopes (0, &chain, &tree_visitor, NULL, &v);
 
   if (res == DWARF_CB_ABORT)
-    return v.last_offset;
+    return (ptrdiff_t) v.last_addr;
   else
     return res;
 }
diff --git a/tests/ChangeLog b/tests/ChangeLog
index f6b794b..11974e1 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,13 @@
+2013-10-10  Mark Wielaard  <mjw@redhat.com>
+	    Josh Stone  <jistone@redhat.com>
+
+	* run-allfcts-multi.sh: New test.
+	* test-offset-loop.bz2: New testfile.
+	* test-offset-loop.alt.bz2: New testfile.
+	* Makefile.am (TESTS): Add run-allcft-multi.sh if ENABLE_DWZ.
+	(EXTRA_DIST): Add run-allfcts-multi.sh, test-offset-loop.bz2 and
+	test-offset-loop.alt.bz2.
+
 2013-10-15  Mark Wielaard  <mjw@redhat.com>
 
 	* run-unstrip-M.sh: New test.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index f3c56bf..d07cb0b 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -106,7 +106,7 @@ TESTS += $(asm_TESTS)
 endif
 
 if ENABLE_DWZ
-TESTS += run-readelf-dwz-multi.sh
+TESTS += run-readelf-dwz-multi.sh run-allfcts-multi.sh
 endif
 
 
@@ -164,6 +164,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
 	     run-readelf-dwz-multi.sh libtestfile_multi_shared.so.bz2 \
 	     testfile_multi.dwz.bz2 testfile_multi_main.bz2 \
 	     testfile-dwzstr.bz2 testfile-dwzstr.multi.bz2 \
+	     run-allfcts-multi.sh \
+	     test-offset-loop.bz2 test-offset-loop.alt.bz2 \
 	     run-prelink-addr-test.sh \
 	     testfile52-32.so.bz2 testfile52-32.so.debug.bz2 \
 	     testfile52-32.prelink.so.bz2 testfile52-32.noshdrs.so.bz2 \
diff --git a/tests/run-allfcts-multi.sh b/tests/run-allfcts-multi.sh
new file mode 100755
index 0000000..727b76e
--- /dev/null
+++ b/tests/run-allfcts-multi.sh
@@ -0,0 +1,56 @@
+#! /bin/sh
+# Copyright (C) 2013 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-readelf-dwz-multi.sh
+testfiles libtestfile_multi_shared.so testfile_multi_main testfile_multi.dwz
+testfiles testfile-dwzstr testfile-dwzstr.multi
+
+testrun_compare ${abs_builddir}/allfcts testfile_multi_main libtestfile_multi_shared.so testfile-dwzstr <<\EOF
+/home/mark/src/tests/dwz/main.c:3:main
+/home/mark/src/tests/dwz/shared.c:3:call_foo
+/home/mark/src/tests/main.c:8:main
+EOF
+
+# - test-offset-loop.c
+#
+# #include <stdbool.h>
+# #include <string.h>
+# #include <errno.h>
+# void padding (int x, int y, int z) { }
+# static inline bool is_error (int err) { return err != 0; }
+# static inline int get_errno (void) { return errno; }
+# int main () { return is_error (get_errno ()); }
+#
+# gcc -g -O2 test-offset-loop.c -o test-offset-loop
+# cp test-offset-loop test-offset-loop2
+# dwz test-offset-loop test-offset-loop2 -m test-offset-loop.alt
+
+testfiles test-offset-loop test-offset-loop.alt
+tempfiles allfcts.out
+
+# Use head to capture output because the output could be infinite...
+testrun ${abs_builddir}/allfcts test-offset-loop | head -n 20 > allfcts.out
+testrun_compare cat allfcts.out <<\EOF
+/tmp/test-offset-loop.c:6:get_errno
+/tmp/test-offset-loop.c:5:is_error
+/tmp/test-offset-loop.c:4:padding
+/tmp/test-offset-loop.c:7:main
+EOF
+
+exit 0
diff --git a/tests/test-offset-loop.alt.bz2 b/tests/test-offset-loop.alt.bz2
new file mode 100644
index 0000000000000000000000000000000000000000..c1906b6da8a9e3e1408bf2411cce4cbc11d5c4c7
GIT binary patch
diff --git a/tests/test-offset-loop.bz2 b/tests/test-offset-loop.bz2
new file mode 100755
index 0000000000000000000000000000000000000000..62185c0acdc328054e1cf3cacd1e249aa6a18674
GIT binary patch
-- 
1.7.1

