summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChih-Hung Hsieh <chh@google.com>2016-01-04 10:18:17 -0800
committerChih-Hung Hsieh <chh@google.com>2016-01-04 11:35:34 -0800
commit645a828a7aef8113f07efae9b48e742bb65b712e (patch)
treebcd20af43068df2ab3c279089461ef1c69ca6ff6
parent611603536be3a6caaafb4289e2713cd117c70048 (diff)
parent9d1e2365db8499b50ffc767ec42fbd6e49d669ac (diff)
downloadandroid_external_elfutils-645a828a7aef8113f07efae9b48e742bb65b712e.tar.gz
android_external_elfutils-645a828a7aef8113f07efae9b48e742bb65b712e.tar.bz2
android_external_elfutils-645a828a7aef8113f07efae9b48e742bb65b712e.zip
Merge upstream SHA '9d1e236'
* git merge 9d1e236 * See all upstream changes since the previous merge in branch aosp/upstream-master: git diff b47fb23..9d1e236 * Android relevant upstream changes: Move nested functions to file scope to compile with clang/llvm: libdw/dwarf_entry_breakpoints.c libdw/libdw_visit_scopes.c libdwfl/core-file.c libdwfl/dwfl_module_addrsym.c libdwfl/dwfl_module_getdwarf.c libdwfl/link_map.c libdwfl/linux-proc-maps.c * Other upstream changes: backends: sparc: support for live backtraces elfcmp: Fix leaking Ebl handles. libdw: Don't leak fake_loc_cu. libdw: Don't leak duplicate FDEs. libdw: Don't use a FDE if it doesn't actually cover a real code range. libdw: Make sure Ebl is always freed from cfi frame cache. libdwfl: Don't leak core Elf and core file descriptor from argp-std. libdwfl: Fix memory leak in link_map.c dwfl_link_map_report. libdwfl: make the unwinder aware of the backend defined ra_offset. libebl: new backend attribute ra_offset. nm: Don't leak duplicate Dwarf local names. nm: If the file is ET_REL and we need Dwarf use libdwfl to get it relocated. tests: Fix some memory leaks in testcases. tests: Use valgrind --leak-check=full. unstrip: Don't leak new section data. * No changes to the following generated files: version.h, config.h, libdw/known-dwarf.h * Post merge change: Enable clang compilation in the libdw directory. Change-Id: Ifa9e60b1d5ab1c70da69410509cc9f3abdf58661
-rw-r--r--backends/ChangeLog16
-rw-r--r--backends/Makefile.am3
-rw-r--r--backends/sparc_cfi.c83
-rw-r--r--backends/sparc_corenote.c10
-rw-r--r--backends/sparc_init.c8
-rw-r--r--backends/sparc_initreg.c129
-rwxr-xr-xlibdw/Android.mk6
-rw-r--r--libdw/ChangeLog29
-rw-r--r--libdw/dwarf_end.c6
-rw-r--r--libdw/dwarf_entry_breakpoints.c110
-rw-r--r--libdw/fde.c18
-rw-r--r--libdw/frame-cache.c6
-rw-r--r--libdw/libdw_visit_scopes.c160
-rw-r--r--libdwfl/ChangeLog62
-rw-r--r--libdwfl/argp-std.c16
-rw-r--r--libdwfl/core-file.c116
-rw-r--r--libdwfl/dwfl_build_id_find_elf.c9
-rw-r--r--libdwfl/dwfl_end.c12
-rw-r--r--libdwfl/dwfl_frame.c2
-rw-r--r--libdwfl/dwfl_module.c23
-rw-r--r--libdwfl/dwfl_module_addrsym.c335
-rw-r--r--libdwfl/dwfl_module_getdwarf.c306
-rw-r--r--libdwfl/frame_unwind.c9
-rw-r--r--libdwfl/libdwflP.h14
-rw-r--r--libdwfl/link_map.c121
-rw-r--r--libdwfl/linux-proc-maps.c31
-rw-r--r--libebl/ChangeLog6
-rw-r--r--libebl/eblabicfi.c6
-rw-r--r--libebl/libebl.h6
-rw-r--r--libebl/libeblP.h6
-rw-r--r--src/ChangeLog28
-rw-r--r--src/elfcmp.c2
-rw-r--r--src/nm.c129
-rw-r--r--src/unstrip.c39
-rw-r--r--tests/ChangeLog28
-rw-r--r--tests/Makefile.am6
-rw-r--r--tests/addrcfi.c17
-rw-r--r--tests/addrscopes.c1
-rw-r--r--tests/alldts.c3
-rw-r--r--tests/allregs.c1
-rw-r--r--tests/backtrace.sparc.core.bz2bin0 -> 9628 bytes
-rwxr-xr-xtests/backtrace.sparc.exec.bz2bin0 -> 348964 bytes
-rw-r--r--tests/dwarf-getstring.c1
-rw-r--r--tests/funcscopes.c1
-rw-r--r--tests/rerequest_tag.c1
-rwxr-xr-xtests/run-backtrace-core-sparc.sh20
-rwxr-xr-xtests/run-backtrace-demangle.sh11
-rwxr-xr-xtests/run-stack-d-test.sh10
-rwxr-xr-xtests/run-stack-demangled-test.sh10
-rwxr-xr-xtests/run-stack-i-test.sh10
-rw-r--r--tests/test-flag-nobits.c3
51 files changed, 1407 insertions, 578 deletions
diff --git a/backends/ChangeLog b/backends/ChangeLog
index 7f0d1c00..4b604fd2 100644
--- a/backends/ChangeLog
+++ b/backends/ChangeLog
@@ -1,3 +1,19 @@
+2015-12-08 Jose E. Marchesi <jose.marchesi@oracle.com>
+
+ * sparc_init.c (sparc_init): Hook sparc_set_initial_registers_tid.
+ * sparc_initreg.c: New file.
+ * Makefile.am (sparc_SRCS): Added sparc_initreg.c.
+
+2015-12-08 Jose E. Marchesi <jose.marchesi@oracle.com>
+
+ * sparc_corenote.c: Header comment typo fixed.
+ (PRSTATUS_REGSET_ITEMS): Defined, so the PC can be fetched from
+ core files.
+ * Makefile.am (sparc_SRCS): Added sparc_cfi.c
+ * sparc_cfi.c: New file.
+ * sparc_init.c (sparc_init): Set eh->frame_nregs, eh->ra_offset
+ and hook sparc_abi_cfi.
+
2015-10-21 Chih-Hung Hsieh <chh@google.com>
* ia64_retval.c (hfa_type): Move nested function 'hfa' to file scope.
diff --git a/backends/Makefile.am b/backends/Makefile.am
index f7002fb5..b16f9486 100644
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -84,7 +84,8 @@ libebl_aarch64_pic_a_SOURCES = $(aarch64_SRCS)
am_libebl_aarch64_pic_a_OBJECTS = $(aarch64_SRCS:.c=.os)
sparc_SRCS = sparc_init.c sparc_symbol.c sparc_regs.c sparc_retval.c \
- sparc_corenote.c sparc64_corenote.c sparc_auxv.c sparc_attrs.c
+ sparc_corenote.c sparc64_corenote.c sparc_auxv.c sparc_attrs.c \
+ sparc_cfi.c sparc_initreg.c
libebl_sparc_pic_a_SOURCES = $(sparc_SRCS)
am_libebl_sparc_pic_a_OBJECTS = $(sparc_SRCS:.c=.os)
diff --git a/backends/sparc_cfi.c b/backends/sparc_cfi.c
new file mode 100644
index 00000000..dcc17bd6
--- /dev/null
+++ b/backends/sparc_cfi.c
@@ -0,0 +1,83 @@
+/* SPARC defaults for DWARF CFI.
+ Copyright (C) 2015 Oracle 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>
+
+#define BACKEND sparc_
+#include "libebl_CPU.h"
+
+int
+sparc_abi_cfi (Ebl *ebl __attribute__ ((unused)), Dwarf_CIE *abi_info)
+{
+ static const uint8_t abi_cfi[] =
+ {
+#define SV(n) DW_CFA_same_value, ULEB128_7 (n)
+ /* %g0 .. %g7 */
+ SV (0), SV (1), SV (2), SV (3), SV (4), SV (5), SV (6), SV (7),
+ /* %o0 .. %o7 */
+ SV (8), SV (9), SV (10), SV (11), SV (12), SV (13), SV (14), SV (15),
+ /* %l0 .. %l7 */
+ SV (16), SV (17), SV (18), SV (19), SV (20), SV (21), SV (22), SV (23),
+ /* %i0 .. %i7 */
+ SV (24), SV (25), SV (26), SV (27), SV (28), SV (29), SV (30), SV (31),
+ /* %f0 .. %f32 */
+ SV (32), SV (33), SV (34), SV (35), SV (36), SV (37), SV (38), SV (39),
+ SV (40), SV (41), SV (42), SV (43), SV (44), SV (45), SV (46), SV (47),
+ SV (48), SV (49), SV (50), SV (51), SV (52), SV (53), SV (54), SV (55),
+ SV (56), SV (57), SV (58), SV (59), SV (60), SV (61), SV (52), SV (63),
+ /* %f33 .. %63
+ Note that there are DWARF columns for the odd registers, even
+ if they don't exist in hardware) */
+ SV (64), SV (65), SV (66), SV (67), SV (68), SV (69), SV (70), SV (71),
+ SV (72), SV (73), SV (74), SV (75), SV (76), SV (77), SV (78), SV (79),
+ SV (80), SV (81), SV (82), SV (83), SV (84), SV (85), SV (86), SV (87),
+ SV (88), SV (89), SV (90), SV (91), SV (92), SV (93), SV (94), SV (95),
+ /* %fcc[0123] */
+ SV (96), SV (97), SV (98), SV (99),
+ /* %icc/%xcc */
+ SV (100),
+ /* Soft frame-pointer */
+ SV (101),
+ /* %gsr */
+ SV (102)
+#undef SV
+ };
+
+ abi_info->initial_instructions = abi_cfi;
+ abi_info->initial_instructions_end = &abi_cfi[sizeof abi_cfi];
+ abi_info->data_alignment_factor = 4;
+
+ abi_info->return_address_register = 31; /* %i7 */
+
+ return 0;
+}
+
diff --git a/backends/sparc_corenote.c b/backends/sparc_corenote.c
index 7912539b..c9b6ff3d 100644
--- a/backends/sparc_corenote.c
+++ b/backends/sparc_corenote.c
@@ -1,5 +1,6 @@
-/* PowerPC specific core note handling.
+/* SPARC specific core note handling.
Copyright (C) 2007 Red Hat, Inc.
+ Copyright (C) 2015 Oracle, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -109,4 +110,11 @@ static const Ebl_Register_Location fpregset_regs[] =
#define ALIGN_PID_T 4
#define TYPE_PID_T ELF_T_SWORD
+#define PRSTATUS_REGSET_ITEMS \
+ { \
+ .name = "pc", .type = ELF_T_ADDR, .format = 'x', \
+ .offset = offsetof (struct EBLHOOK(prstatus), pr_reg[33]), \
+ .group = "register", .pc_register = true \
+ }
+
#include "linux-core-note.c"
diff --git a/backends/sparc_init.c b/backends/sparc_init.c
index f8a7cfbd..8e946fb2 100644
--- a/backends/sparc_init.c
+++ b/backends/sparc_init.c
@@ -76,6 +76,14 @@ sparc_init (Elf *elf __attribute__ ((unused)),
HOOK (eh, register_info);
HOOK (eh, return_value_location);
HOOK (eh, check_object_attribute);
+ HOOK (eh, abi_cfi);
+ /* gcc/config/sparc.h define FIRST_PSEUDO_REGISTER */
+ eh->frame_nregs = 103;
+ /* The CFI Dwarf register with the "return address" in sparc
+ actually contains the call address. The return address is
+ located 8 bytes after it. */
+ eh->ra_offset = 8;
+ HOOK (eh, set_initial_registers_tid);
return MODVERSION;
}
diff --git a/backends/sparc_initreg.c b/backends/sparc_initreg.c
new file mode 100644
index 00000000..c2a9b32a
--- /dev/null
+++ b/backends/sparc_initreg.c
@@ -0,0 +1,129 @@
+/* Fetch live process registers from TID.
+ Copyright (C) 2015 Oracle, In
+ 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 "system.h"
+#include <stdlib.h>
+#ifdef __sparc__
+# include <asm/ptrace.h>
+# include <sys/ptrace.h>
+#endif
+
+#define BACKEND sparc_
+#include "libebl_CPU.h"
+
+bool
+EBLHOOK (set_initial_registers_tid) (pid_t tid __attribute__ ((unused)),
+ ebl_tid_registers_t *setfunc __attribute__ ((unused)),
+ void *arg __attribute__ ((unused)))
+{
+#ifndef __sparc__
+ return false;
+#else /* __sparc__ */
+
+
+ /* The pt_regs structure filled in by PTRACE_GETREGS provides the
+ PC, the global registers and the output registers. Note how the
+ %g0 register is not explicitly provided in the structure (it's
+ value is always 0) and the resulting weird packing in the u_regs
+ array: the last element is not used. */
+
+ struct pt_regs regs;
+ if (ptrace (PTRACE_GETREGS, tid, &regs, 0) == -1)
+ return false;
+
+ /* PC: no DWARF number */
+ if (!setfunc (-1, 1, (Dwarf_Word *) &regs.tpc, arg))
+ return false;
+
+ /* Global registers: DWARF 0 .. 7 */
+ Dwarf_Word zero = 0;
+ if (!setfunc (0, 1, &zero, arg))
+ return false;
+ if (!setfunc (1, 7, (Dwarf_Word *) &regs.u_regs[0], arg))
+ return false;
+
+ /* Output registers: DWARF 8 .. 15 */
+ if (!setfunc (8, 8, (Dwarf_Word *) &regs.u_regs[7], arg))
+ return false;
+
+ /* Local and input registers must be read from the stack. They are
+ saved in the previous stack frame. The stack pointer is %o6,
+ read above. */
+
+ Dwarf_Word locals_outs[16];
+ Dwarf_Word sp = regs.u_regs[13];
+
+ if (sp & 1)
+ {
+ /* Registers are 64 bits, and we need to apply the 2047 stack
+ bias in order to get the real stack pointer. */
+
+ sp += 2047;
+
+ for (unsigned i = 0; i < 16; i++)
+ {
+ locals_outs[i] = ptrace (PTRACE_PEEKDATA, tid,
+ (void *) (uintptr_t) (sp + (i * 8)),
+ NULL);
+ if (errno != 0)
+ return false;
+ }
+ }
+ else
+ {
+ /* Registers are 32 bits. */
+
+ for (unsigned i = 0; i < 8; i++)
+ {
+ Dwarf_Word tuple = ptrace (PTRACE_PEEKDATA, tid,
+ (void *) (uintptr_t) (sp + (i * 8)),
+ NULL);
+ if (errno != 0)
+ return false;
+
+ locals_outs[2*i] = (tuple >> 32) & 0xffffffff;
+ locals_outs[2*i+1] = tuple & 0xffffffff;
+ }
+ }
+
+
+ /* Local registers: DWARF 16 .. 23 */
+ if (!setfunc (16, 8, &locals_outs[0], arg))
+ return false;
+
+ /* Input registers: DWARF 24 .. 31 */
+ if (!setfunc (24, 8, &locals_outs[8], arg))
+ return false;
+
+ return true;
+#endif
+}
diff --git a/libdw/Android.mk b/libdw/Android.mk
index 55a04dc9..5558a1ad 100755
--- a/libdw/Android.mk
+++ b/libdw/Android.mk
@@ -143,9 +143,6 @@ ifeq ($(HOST_OS),linux)
include $(CLEAR_VARS)
-# Clang has no nested functions.
-LOCAL_CLANG := false
-
LOCAL_SRC_FILES := $(LIBDW_SRC_FILES)
LOCAL_C_INCLUDES := \
@@ -175,9 +172,6 @@ endif # linux
include $(CLEAR_VARS)
-# Clang has no nested functions.
-LOCAL_CLANG := false
-
# b/25642296, local __thread variable does not work with arm64 clang/llvm.
LOCAL_CLANG_arm64 := false
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 5218145e..d0e97f3c 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,32 @@
+2015-12-02 Mark Wielaard <mjw@redhat.com>
+
+ * fde.c (intern_fde): Don't leak duplicate FDEs.
+
+2015-12-01 Mark Wielaard <mjw@redhat.com>
+
+ * fde.c (intern_fde): Don't intern an fde that doesn't cover a
+ valid code range.
+
+2015-12-01 Mark Wielaard <mjw@redhat.com>
+
+ * dwarf_end.c (dwarf_end): Call cu_free on fake_loc_cu if it exists.
+
+2015-10-14 Chih-Hung Hsieh <chh@google.com>
+
+ * dwarf_entry_breakpoints.c (dwarf_entry_breakpoints): Move recursive
+ functions 'add_bkpt', 'entrypc_bkpt', and 'search_range' to file scope.
+
+2015-10-14 Chih-Hung Hsieh <chh@google.com>
+
+ * libdw_visit_scopes.c (__libdw_visit_scopes): Move recursive nested
+ function 'walk_children' to file scope; inline 'recurse' at its call
+ site.
+
+2015-10-19 Mark Wielaard <mjw@redhat.com>
+
+ * frame-cache.c (__libdw_destroy_frame_cache): Call ebl_closebackend
+ if necessary.
+
2015-10-16 Dmitry V. Levin <ldv@altlinux.org>
* dwarf_getsrclines.c (read_srclines): Initialize state early.
diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c
index 32b551df..2108063d 100644
--- a/libdw/dwarf_end.c
+++ b/libdw/dwarf_end.c
@@ -117,7 +117,11 @@ dwarf_end (Dwarf *dwarf)
elf_end (dwarf->elf);
/* Free the fake location list CU. */
- free (dwarf->fake_loc_cu);
+ if (dwarf->fake_loc_cu != NULL)
+ {
+ cu_free (dwarf->fake_loc_cu);
+ free (dwarf->fake_loc_cu);
+ }
/* Free the context descriptor. */
free (dwarf);
diff --git a/libdw/dwarf_entry_breakpoints.c b/libdw/dwarf_entry_breakpoints.c
index abfee73c..c3c0f399 100644
--- a/libdw/dwarf_entry_breakpoints.c
+++ b/libdw/dwarf_entry_breakpoints.c
@@ -34,54 +34,40 @@
#include <stdlib.h>
-int
-dwarf_entry_breakpoints (Dwarf_Die *die, Dwarf_Addr **bkpts)
+/* Add one breakpoint location to the result vector. */
+static inline int
+add_bkpt (Dwarf_Addr pc, Dwarf_Addr **bkpts, int *pnbkpts)
{
- int nbkpts = 0;
- *bkpts = NULL;
-
- /* Add one breakpoint location to the result vector. */
- inline int add_bkpt (Dwarf_Addr pc)
- {
- Dwarf_Addr *newlist = realloc (*bkpts, ++nbkpts * sizeof newlist[0]);
- if (newlist == NULL)
- {
- free (*bkpts);
- *bkpts = NULL;
- __libdw_seterrno (DWARF_E_NOMEM);
- return -1;
- }
- newlist[nbkpts - 1] = pc;
- *bkpts = newlist;
- return nbkpts;
- }
-
- /* Fallback result, break at the entrypc/lowpc value. */
- inline int entrypc_bkpt (void)
+ Dwarf_Addr *newlist = realloc (*bkpts, ++(*pnbkpts) * sizeof newlist[0]);
+ if (newlist == NULL)
{
- Dwarf_Addr pc;
- return INTUSE(dwarf_entrypc) (die, &pc) < 0 ? -1 : add_bkpt (pc);
- }
-
- /* Fetch the CU's line records to look for this DIE's addresses. */
- Dwarf_Die cudie = CUDIE (die->cu);
- Dwarf_Lines *lines;
- size_t nlines;
- if (INTUSE(dwarf_getsrclines) (&cudie, &lines, &nlines) < 0)
- {
- int error = INTUSE (dwarf_errno) ();
- if (error == 0) /* CU has no DW_AT_stmt_list. */
- return entrypc_bkpt ();
- __libdw_seterrno (error);
+ free (*bkpts);
+ *bkpts = NULL;
+ __libdw_seterrno (DWARF_E_NOMEM);
return -1;
}
+ newlist[*pnbkpts - 1] = pc;
+ *bkpts = newlist;
+ return *pnbkpts;
+}
- /* Search a contiguous PC range for prologue-end markers.
- If DWARF, look for proper markers.
- Failing that, if ADHOC, look for the ad hoc convention. */
- inline int search_range (Dwarf_Addr low, Dwarf_Addr high,
- bool dwarf, bool adhoc)
- {
+/* Fallback result, break at the entrypc/lowpc value. */
+static inline int
+entrypc_bkpt (Dwarf_Die *die, Dwarf_Addr **bkpts, int *pnbkpts)
+{
+ Dwarf_Addr pc;
+ return INTUSE(dwarf_entrypc) (die, &pc) < 0 ? -1 : add_bkpt (pc, bkpts, pnbkpts);
+}
+
+/* Search a contiguous PC range for prologue-end markers.
+ If DWARF, look for proper markers.
+ Failing that, if ADHOC, look for the ad hoc convention. */
+static inline int
+search_range (Dwarf_Addr low, Dwarf_Addr high,
+ bool dwarf, bool adhoc,
+ Dwarf_Lines *lines, size_t nlines,
+ Dwarf_Addr **bkpts, int *pnbkpts)
+{
size_t l = 0, u = nlines;
while (l < u)
{
@@ -103,16 +89,35 @@ dwarf_entry_breakpoints (Dwarf_Die *die, Dwarf_Addr **bkpts)
if (dwarf)
for (size_t i = l; i < u && lines->info[i].addr < high; ++i)
if (lines->info[i].prologue_end
- && add_bkpt (lines->info[i].addr) < 0)
+ && add_bkpt (lines->info[i].addr, bkpts, pnbkpts) < 0)
return -1;
- if (adhoc && nbkpts == 0)
+ if (adhoc && *pnbkpts == 0)
while (++l < nlines && lines->info[l].addr < high)
if (!lines->info[l].end_sequence)
- return add_bkpt (lines->info[l].addr);
- return nbkpts;
+ return add_bkpt (lines->info[l].addr, bkpts, pnbkpts);
+ return *pnbkpts;
}
__libdw_seterrno (DWARF_E_INVALID_DWARF);
return -1;
+}
+
+int
+dwarf_entry_breakpoints (Dwarf_Die *die, Dwarf_Addr **bkpts)
+{
+ int nbkpts = 0;
+ *bkpts = NULL;
+
+ /* Fetch the CU's line records to look for this DIE's addresses. */
+ Dwarf_Die cudie = CUDIE (die->cu);
+ Dwarf_Lines *lines;
+ size_t nlines;
+ if (INTUSE(dwarf_getsrclines) (&cudie, &lines, &nlines) < 0)
+ {
+ int error = INTUSE (dwarf_errno) ();
+ if (error == 0) /* CU has no DW_AT_stmt_list. */
+ return entrypc_bkpt (die, bkpts, &nbkpts);
+ __libdw_seterrno (error);
+ return -1;
}
/* Search each contiguous address range for DWARF prologue_end markers. */
@@ -126,14 +131,16 @@ dwarf_entry_breakpoints (Dwarf_Die *die, Dwarf_Addr **bkpts)
/* Most often there is a single contiguous PC range for the DIE. */
if (offset == 1)
- return search_range (begin, end, true, true) ?: entrypc_bkpt ();
+ return search_range (begin, end, true, true, lines, nlines, bkpts, &nbkpts)
+ ?: entrypc_bkpt (die, bkpts, &nbkpts);
Dwarf_Addr lowpc = (Dwarf_Addr) -1l;
Dwarf_Addr highpc = (Dwarf_Addr) -1l;
while (offset > 0)
{
/* We have an address range entry. */
- if (search_range (begin, end, true, false) < 0)
+ if (search_range (begin, end, true, false,
+ lines, nlines, bkpts, &nbkpts) < 0)
return -1;
if (begin < lowpc)
@@ -150,6 +157,7 @@ dwarf_entry_breakpoints (Dwarf_Die *die, Dwarf_Addr **bkpts)
fall back to just using the entrypc value. */
return (nbkpts
?: (lowpc == (Dwarf_Addr) -1l ? 0
- : search_range (lowpc, highpc, false, true))
- ?: entrypc_bkpt ());
+ : search_range (lowpc, highpc, false, true,
+ lines, nlines, bkpts, &nbkpts))
+ ?: entrypc_bkpt (die, bkpts, &nbkpts));
}
diff --git a/libdw/fde.c b/libdw/fde.c
index c8475f3e..f5f6fbe1 100644
--- a/libdw/fde.c
+++ b/libdw/fde.c
@@ -90,6 +90,13 @@ intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry)
}
fde->end += fde->start;
+ /* Make sure the fde actually covers a real code range. */
+ if (fde->start >= fde->end)
+ {
+ free (fde);
+ return (void *) -1;
+ }
+
fde->cie = cie;
if (cie->sized_augmentation_data)
@@ -112,12 +119,21 @@ intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry)
fde->instructions += cie->fde_augmentation_data_size;
/* Add the new entry to the search tree. */
- if (tsearch (fde, &cache->fde_tree, &compare_fde) == NULL)
+ struct dwarf_fde **tres = tsearch (fde, &cache->fde_tree, &compare_fde);
+ if (tres == NULL)
{
free (fde);
__libdw_seterrno (DWARF_E_NOMEM);
return NULL;
}
+ else if (*tres != fde)
+ {
+ /* There is already an FDE in the cache that covers the same
+ address range. That is odd. Ignore this FDE. And just use
+ the one in the cache for consistency. */
+ free (fde);
+ return *tres;
+ }
return fde;
}
diff --git a/libdw/frame-cache.c b/libdw/frame-cache.c
index 54a1cc9a..5b6afb5d 100644
--- a/libdw/frame-cache.c
+++ b/libdw/frame-cache.c
@@ -1,5 +1,5 @@
/* Frame cache handling.
- Copyright (C) 2009 Red Hat, Inc.
+ Copyright (C) 2009, 2015 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -30,6 +30,7 @@
# include <config.h>
#endif
+#include "../libebl/libebl.h"
#include "cfi.h"
#include <search.h>
#include <stdlib.h>
@@ -63,4 +64,7 @@ __libdw_destroy_frame_cache (Dwarf_CFI *cache)
tdestroy (cache->fde_tree, free_fde);
tdestroy (cache->cie_tree, free_cie);
tdestroy (cache->expr_tree, free_expr);
+
+ if (cache->ebl != NULL && cache->ebl != (void *) -1l)
+ ebl_closebackend (cache->ebl);
}
diff --git a/libdw/libdw_visit_scopes.c b/libdw/libdw_visit_scopes.c
index 5e5c26fd..eb892e10 100644
--- a/libdw/libdw_visit_scopes.c
+++ b/libdw/libdw_visit_scopes.c
@@ -64,6 +64,21 @@ may_have_scopes (Dwarf_Die *die)
return false;
}
+struct walk_children_state
+{
+ /* Parameters of __libdw_visit_scopes. */
+ unsigned int depth;
+ struct Dwarf_Die_Chain *imports;
+ int (*previsit) (unsigned int depth, struct Dwarf_Die_Chain *, void *);
+ int (*postvisit) (unsigned int depth, struct Dwarf_Die_Chain *, void *);
+ void *arg;
+ /* Extra local variables for the walker. */
+ struct Dwarf_Die_Chain child;
+};
+
+static inline int
+walk_children (struct walk_children_state *state);
+
int
internal_function
__libdw_visit_scopes (unsigned int depth, struct Dwarf_Die_Chain *root,
@@ -76,95 +91,98 @@ __libdw_visit_scopes (unsigned int depth, struct Dwarf_Die_Chain *root,
void *),
void *arg)
{
- struct Dwarf_Die_Chain child;
+ struct walk_children_state state =
+ {
+ .depth = depth,
+ .imports = imports,
+ .previsit = previsit,
+ .postvisit = postvisit,
+ .arg = arg
+ };
+
+ state.child.parent = root;
int ret;
-
- child.parent = root;
- if ((ret = INTUSE(dwarf_child) (&root->die, &child.die)) != 0)
+ if ((ret = INTUSE(dwarf_child) (&root->die, &state.child.die)) != 0)
return ret < 0 ? -1 : 0; // Having zero children is legal.
- inline int recurse (void)
- {
- return __libdw_visit_scopes (depth + 1, &child, imports,
- previsit, postvisit, arg);
- }
-
- /* Checks the given DIE hasn't been imported yet to prevent cycles. */
- inline bool imports_contains (Dwarf_Die *die)
- {
- for (struct Dwarf_Die_Chain *import = imports; import != NULL;
- import = import->parent)
- if (import->die.addr == die->addr)
- return true;
-
- return false;
- }
+ return walk_children (&state);
+}
- inline int walk_children (void)
+static inline int
+walk_children (struct walk_children_state *state)
{
- do
- {
- /* For an imported unit, it is logically as if the children of
- that unit are siblings of the other children. So don't do
- a full recursion into the imported unit, but just walk the
- children in place before moving to the next real child. */
- while (INTUSE(dwarf_tag) (&child.die) == DW_TAG_imported_unit)
- {
- Dwarf_Die orig_child_die = child.die;
- Dwarf_Attribute attr_mem;
- Dwarf_Attribute *attr = INTUSE(dwarf_attr) (&child.die,
- DW_AT_import,
- &attr_mem);
- if (INTUSE(dwarf_formref_die) (attr, &child.die) != NULL
- && INTUSE(dwarf_child) (&child.die, &child.die) == 0)
- {
- if (imports_contains (&orig_child_die))
- {
- __libdw_seterrno (DWARF_E_INVALID_DWARF);
- return -1;
- }
- struct Dwarf_Die_Chain *orig_imports = imports;
- struct Dwarf_Die_Chain import = { .die = orig_child_die,
- .parent = orig_imports };
- imports = &import;
- int result = walk_children ();
- imports = orig_imports;
- if (result != DWARF_CB_OK)
- return result;
- }
-
- /* Any "real" children left? */
- if ((ret = INTUSE(dwarf_siblingof) (&orig_child_die,
- &child.die)) != 0)
- return ret < 0 ? -1 : 0;
- };
-
- child.prune = false;
+ int ret;
+ do
+ {
+ /* For an imported unit, it is logically as if the children of
+ that unit are siblings of the other children. So don't do
+ a full recursion into the imported unit, but just walk the
+ children in place before moving to the next real child. */
+ while (INTUSE(dwarf_tag) (&state->child.die) == DW_TAG_imported_unit)
+ {
+ Dwarf_Die orig_child_die = state->child.die;
+ Dwarf_Attribute attr_mem;
+ Dwarf_Attribute *attr = INTUSE(dwarf_attr) (&state->child.die,
+ DW_AT_import,
+ &attr_mem);
+ if (INTUSE(dwarf_formref_die) (attr, &state->child.die) != NULL
+ && INTUSE(dwarf_child) (&state->child.die, &state->child.die) == 0)
+ {
+ /* Checks the given DIE hasn't been imported yet
+ to prevent cycles. */
+ bool imported = false;
+ for (struct Dwarf_Die_Chain *import = state->imports; import != NULL;
+ import = import->parent)
+ if (import->die.addr == orig_child_die.addr)
+ {
+ imported = true;
+ break;
+ }
+ if (imported)
+ {
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ return -1;
+ }
+ struct Dwarf_Die_Chain *orig_imports = state->imports;
+ struct Dwarf_Die_Chain import = { .die = orig_child_die,
+ .parent = orig_imports };
+ state->imports = &import;
+ int result = walk_children (state);
+ state->imports = orig_imports;
+ if (result != DWARF_CB_OK)
+ return result;
+ }
+
+ /* Any "real" children left? */
+ if ((ret = INTUSE(dwarf_siblingof) (&orig_child_die,
+ &state->child.die)) != 0)
+ return ret < 0 ? -1 : 0;
+ };
+
+ state->child.prune = false;
/* previsit is declared NN */
- int result = (*previsit) (depth + 1, &child, arg);
+ int result = (*state->previsit) (state->depth + 1, &state->child, state->arg);
if (result != DWARF_CB_OK)
return result;
- if (!child.prune && may_have_scopes (&child.die)
- && INTUSE(dwarf_haschildren) (&child.die))
+ if (!state->child.prune && may_have_scopes (&state->child.die)
+ && INTUSE(dwarf_haschildren) (&state->child.die))
{
- result = recurse ();
+ result = __libdw_visit_scopes (state->depth + 1, &state->child, state->imports,
+ state->previsit, state->postvisit, state->arg);
if (result != DWARF_CB_OK)
return result;
}
- if (postvisit != NULL)
+ if (state->postvisit != NULL)
{
- result = (*postvisit) (depth + 1, &child, arg);
+ result = (*state->postvisit) (state->depth + 1, &state->child, state->arg);
if (result != DWARF_CB_OK)
return result;
}
- }
- while ((ret = INTUSE(dwarf_siblingof) (&child.die, &child.die)) == 0);
-
- return ret < 0 ? -1 : 0;
- }
+ }
+ while ((ret = INTUSE(dwarf_siblingof) (&state->child.die, &state->child.die)) == 0);
- return walk_children ();
+ return ret < 0 ? -1 : 0;
}
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index 163a6f1e..06b8469d 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,65 @@
+2015-11-18 Chih-Hung Hsieh <chh@google.com>
+
+ * linux-proc-maps.c (proc_maps_report): Move nested function
+ 'report' to file scope.
+
+2015-11-18 Chih-Hung Hsieh <chh@google.com>
+
+ * core-file.c (elf_begin_rand): Move nested function 'fail' to file
+ scope.
+ * core-file.c (dwfl_elf_phdr_memory_callback): Move nested functions
+ 'update_end' and 'more' to file scope.
+
+2015-11-17 Chih-Hung Hsieh <chh@google.com>
+
+ * link_map.c (auxv_format_probe): Move nested functions
+ check64 and check32 to file scope.
+
+2015-12-08 Jose E. Marchesi <jose.marchesi@oracle.com>
+
+ * dwfl_frame.c (state_fetch_pc): Add a backend-defined offset to
+ the value of the return address register as defined by the CFI
+ abi.
+ * frame_unwind.c (handle_cfi): Likewise.
+
+2015-12-01 Mark Wielaard <mjw@redhat.com>
+
+ * link_map.c (dwfl_link_map_report): Track whether in.d_buf comes
+ from exec or memory_callback, free as appropriate.
+
+2015-12-01 Mark Wielaard <mjw@redhat.com>
+
+ * libdwflP.h (struct Dwfl_User_Core): New.
+ (struct DWfl): Replace executable_for_core with user_core.
+ * argp-std.c (parse_opt): Store core and fd in Dwfl user_core.
+ * core-file.c (dwfl_core_file_report): Check and store
+ executable_for_core in Dwfl user_core.
+ * dwfl_build_id_find_elf.c (dwfl_build_id_find_elf): Check and use
+ executable_for_core in Dwfl user_core.
+ * dwfl_end.c (dwfl_end): Release resources held in Dwfl user_core.
+ * link-map.c (report_r_debug): Check executable_for_core in Dwfl
+ user_core.
+ (dwfl_link_map_report): Likewise.
+
+2015-11-16 Chih-Hung Hsieh <chh@google.com>
+
+ * dwfl_module_getdwarf.c (find_prelink_address_sync): Move nested
+ function 'consider_shdr' to file scope.
+ * dwfl_module_getdwarf.c (find_dynsym): Move nested function
+ 'translate_offs' to file scope.
+
+2015-11-16 Chih-Hung Hsieh <chh@google.com>
+
+ * dwfl_module_addrsym.c (__libdwfl_addrsym): Move nested functions
+ 'same_section', 'binding_value', 'try_sym_value', and 'search_table'
+ to file scope.
+
+2015-11-19 Mark Wielaard <mjw@redhat.com>
+
+ * dwfl_module.c (__libdwfl_module_free): Remove Dwfl_Module Ebl from
+ eh_cfi and dwarf_cfi cache if necessary before calling dwarf_end and
+ dwarf_cfi_end.
+
2015-11-13 Chih-Hung Hsieh <chh@google.com>
* gzip.c (unzip): Move nested functions to file scope.
diff --git a/libdwfl/argp-std.c b/libdwfl/argp-std.c
index 2bbf74fc..501530a5 100644
--- a/libdwfl/argp-std.c
+++ b/libdwfl/argp-std.c
@@ -1,5 +1,5 @@
/* Standard argp argument parsers for tools using libdwfl.
- Copyright (C) 2005-2010, 2012 Red Hat, Inc.
+ Copyright (C) 2005-2010, 2012, 2015 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -303,7 +303,19 @@ parse_opt (int key, char *arg, struct argp_state *state)
/* Non-fatal to not be able to attach to core, ignore error. */
INTUSE(dwfl_core_file_attach) (dwfl, core);
- /* From now we leak FD and CORE. */
+ /* Store core Elf and fd in Dwfl to expose with dwfl_end. */
+ if (dwfl->user_core == NULL)
+ {
+ dwfl->user_core = calloc (1, sizeof (struct Dwfl_User_Core));
+ if (dwfl->user_core == NULL)
+ {
+ argp_failure (state, EXIT_FAILURE, 0,
+ _("Not enough memory"));
+ return ENOMEM;
+ }
+ }
+ dwfl->user_core->core = core;
+ dwfl->user_core->fd = fd;
if (result == 0)
{
diff --git a/libdwfl/core-file.c b/libdwfl/core-file.c
index b317ecaa..67684c9a 100644
--- a/libdwfl/core-file.c
+++ b/libdwfl/core-file.c
@@ -39,6 +39,19 @@
#include "system.h"
+/* On failure return, we update *NEXT to point back at OFFSET. */
+static inline Elf *
+do_fail (int error, off_t *next, off_t offset)
+{
+ if (next != NULL)
+ *next = offset;
+ //__libelf_seterrno (error);
+ __libdwfl_seterrno (DWFL_E (LIBELF, error));
+ return NULL;
+}
+
+#define fail(error) do_fail (error, next, offset)
+
/* This is a prototype of what a new libelf interface might be.
This implementation is pessimal for non-mmap cases and should
be replaced by more diddling inside libelf internals. */
@@ -48,16 +61,6 @@ elf_begin_rand (Elf *parent, off_t offset, off_t size, off_t *next)
if (parent == NULL)
return NULL;
- /* On failure return, we update *NEXT to point back at OFFSET. */
- inline Elf *fail (int error)
- {
- if (next != NULL)
- *next = offset;
- //__libelf_seterrno (error);
- __libdwfl_seterrno (DWFL_E (LIBELF, error));
- return NULL;
- }
-
off_t min = (parent->kind == ELF_K_ELF ?
(parent->class == ELFCLASS32
? sizeof (Elf32_Ehdr) : sizeof (Elf64_Ehdr))
@@ -238,6 +241,44 @@ core_file_read_eagerly (Dwfl_Module *mod,
return cost <= MAX_EAGER_COST;
}
+static inline void
+update_end (GElf_Phdr *pphdr, const GElf_Off align,
+ GElf_Off *pend, GElf_Addr *pend_vaddr)
+{
+ *pend = (pphdr->p_offset + pphdr->p_filesz + align - 1) & -align;
+ *pend_vaddr = (pphdr->p_vaddr + pphdr->p_memsz + align - 1) & -align;
+}
+
+/* Use following contiguous segments to get towards SIZE. */
+static inline bool
+do_more (size_t size, GElf_Phdr *pphdr, const GElf_Off align,
+ Elf *elf, GElf_Off start, int *pndx,
+ GElf_Off *pend, GElf_Addr *pend_vaddr)
+{
+ while (*pend <= start || *pend - start < size)
+ {
+ if (pphdr->p_filesz < pphdr->p_memsz)
+ /* This segment is truncated, so no following one helps us. */
+ return false;
+
+ if (unlikely (gelf_getphdr (elf, (*pndx)++, pphdr) == NULL))
+ return false;
+
+ if (pphdr->p_type == PT_LOAD)
+ {
+ if (pphdr->p_offset > *pend
+ || pphdr->p_vaddr > *pend_vaddr)
+ /* It's discontiguous! */
+ return false;
+
+ update_end (pphdr, align, pend, pend_vaddr);
+ }
+ }
+ return true;
+}
+
+#define more(size) do_more (size, &phdr, align, elf, start, &ndx, &end, &end_vaddr)
+
bool
dwfl_elf_phdr_memory_callback (Dwfl *dwfl, int ndx,
void **buffer, size_t *buffer_available,
@@ -270,38 +311,7 @@ dwfl_elf_phdr_memory_callback (Dwfl *dwfl, int ndx,
GElf_Off end;
GElf_Addr end_vaddr;
- inline void update_end (void)
-{
- end = (phdr.p_offset + phdr.p_filesz + align - 1) & -align;
- end_vaddr = (phdr.p_vaddr + phdr.p_memsz + align - 1) & -align;
- }
-
- update_end ();
-
- /* Use following contiguous segments to get towards SIZE. */
- inline bool more (size_t size)
- {
- while (end <= start || end - start < size)
- {
- if (phdr.p_filesz < phdr.p_memsz)
- /* This segment is truncated, so no following one helps us. */
- return false;
-
- if (unlikely (gelf_getphdr (elf, ndx++, &phdr) == NULL))
- return false;
-
- if (phdr.p_type == PT_LOAD)
- {
- if (phdr.p_offset > end
- || phdr.p_vaddr > end_vaddr)
- /* It's discontiguous! */
- return false;
-
- update_end ();
- }
- }
- return true;
- }
+ update_end (&phdr, align, &end, &end_vaddr);
/* We need at least this much. */
if (! more (minread))
@@ -441,13 +451,27 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable)
return -1;
}
- free (dwfl->executable_for_core);
+ if (dwfl->user_core != NULL)
+ free (dwfl->user_core->executable_for_core);
if (executable == NULL)
- dwfl->executable_for_core = NULL;
+ {
+ if (dwfl->user_core != NULL)
+ dwfl->user_core->executable_for_core = NULL;
+ }
else
{
- dwfl->executable_for_core = strdup (executable);
- if (dwfl->executable_for_core == NULL)
+ if (dwfl->user_core == NULL)
+ {
+ dwfl->user_core = calloc (1, sizeof (struct Dwfl_User_Core));
+ if (dwfl->user_core == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return -1;
+ }
+ dwfl->user_core->fd = -1;
+ }
+ dwfl->user_core->executable_for_core = strdup (executable);
+ if (dwfl->user_core->executable_for_core == NULL)
{
__libdwfl_seterrno (DWFL_E_NOMEM);
return -1;
diff --git a/libdwfl/dwfl_build_id_find_elf.c b/libdwfl/dwfl_build_id_find_elf.c
index 2e30b7ab..903e1931 100644
--- a/libdwfl/dwfl_build_id_find_elf.c
+++ b/libdwfl/dwfl_build_id_find_elf.c
@@ -140,16 +140,19 @@ dwfl_build_id_find_elf (Dwfl_Module *mod,
char **file_name, Elf **elfp)
{
*elfp = NULL;
- if (mod->is_executable && mod->dwfl->executable_for_core != NULL)
+ if (mod->is_executable
+ && mod->dwfl->user_core != NULL
+ && mod->dwfl->user_core->executable_for_core != NULL)
{
/* When dwfl_core_file_report was called with a non-NULL executable file
name this callback will replace the Dwfl_Module main.name with the
recorded executable file when MOD was identified as main executable
(which then triggers opening and reporting of the executable). */
- int fd = open (mod->dwfl->executable_for_core, O_RDONLY);
+ const char *executable = mod->dwfl->user_core->executable_for_core;
+ int fd = open (executable, O_RDONLY);
if (fd >= 0)
{
- *file_name = strdup (mod->dwfl->executable_for_core);
+ *file_name = strdup (executable);
if (*file_name != NULL)
return fd;
else
diff --git a/libdwfl/dwfl_end.c b/libdwfl/dwfl_end.c
index 33cae48c..0b35bd28 100644
--- a/libdwfl/dwfl_end.c
+++ b/libdwfl/dwfl_end.c
@@ -1,5 +1,5 @@
/* Finish a session using libdwfl.
- Copyright (C) 2005, 2008, 2012-2013 Red Hat, Inc.
+ Copyright (C) 2005, 2008, 2012-2013, 2015 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -27,6 +27,7 @@
not, see <http://www.gnu.org/licenses/>. */
#include "libdwflP.h"
+#include <unistd.h>
void
dwfl_end (Dwfl *dwfl)
@@ -49,6 +50,13 @@ dwfl_end (Dwfl *dwfl)
__libdwfl_module_free (dead);
}
- free (dwfl->executable_for_core);
+ if (dwfl->user_core != NULL)
+ {
+ free (dwfl->user_core->executable_for_core);
+ elf_end (dwfl->user_core->core);
+ if (dwfl->user_core->fd != -1)
+ close (dwfl->user_core->fd);
+ free (dwfl->user_core);
+ }
free (dwfl);
}
diff --git a/libdwfl/dwfl_frame.c b/libdwfl/dwfl_frame.c
index a91a1d68..d6399398 100644
--- a/libdwfl/dwfl_frame.c
+++ b/libdwfl/dwfl_frame.c
@@ -57,7 +57,7 @@ state_fetch_pc (Dwfl_Frame *state)
__libdwfl_seterrno (DWFL_E_LIBEBL_BAD);
return false;
}
- state->pc = state->regs[ra];
+ state->pc = state->regs[ra] + ebl_ra_offset (ebl);
state->pc_state = DWFL_FRAME_STATE_PC_SET;
}
return true;
diff --git a/libdwfl/dwfl_module.c b/libdwfl/dwfl_module.c
index 76d45a83..515092f3 100644
--- a/libdwfl/dwfl_module.c
+++ b/libdwfl/dwfl_module.c
@@ -1,5 +1,5 @@
/* Maintenance of module list in libdwfl.
- Copyright (C) 2005, 2006, 2007, 2008, 2014 Red Hat, Inc.
+ Copyright (C) 2005, 2006, 2007, 2008, 2014, 2015 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -27,6 +27,7 @@
not, see <http://www.gnu.org/licenses/>. */
#include "libdwflP.h"
+#include "../libdw/cfi.h"
#include <search.h>
#include <unistd.h>
@@ -70,6 +71,23 @@ __libdwfl_module_free (Dwfl_Module *mod)
free (mod->cu);
}
+ /* We might have primed the Dwarf_CFI ebl cache with our own ebl
+ in __libdwfl_set_cfi. Make sure we don't free it twice. */
+ if (mod->eh_cfi != NULL)
+ {
+ if (mod->eh_cfi->ebl != NULL && mod->eh_cfi->ebl == mod->ebl)
+ mod->eh_cfi->ebl = NULL;
+ dwarf_cfi_end (mod->eh_cfi);
+ }
+
+ if (mod->dwarf_cfi != NULL)
+ {
+ if (mod->dwarf_cfi->ebl != NULL && mod->dwarf_cfi->ebl == mod->ebl)
+ mod->dwarf_cfi->ebl = NULL;
+ /* We don't need to explicitly destroy the dwarf_cfi.
+ That will be done by dwarf_end. */
+ }
+
if (mod->dw != NULL)
{
INTUSE(dwarf_end) (mod->dw);
@@ -97,9 +115,6 @@ __libdwfl_module_free (Dwfl_Module *mod)
if (mod->reloc_info != NULL)
free (mod->reloc_info);
- if (mod->eh_cfi != NULL)
- dwarf_cfi_end (mod->eh_cfi);
-
free (mod->name);
free (mod);
}
diff --git a/libdwfl/dwfl_module_addrsym.c b/libdwfl/dwfl_module_addrsym.c
index d205832c..5a7bf714 100644
--- a/libdwfl/dwfl_module_addrsym.c
+++ b/libdwfl/dwfl_module_addrsym.c
@@ -28,141 +28,144 @@
#include "libdwflP.h"
-/* Returns the name of the symbol "closest" to ADDR.
- Never returns symbols at addresses above ADDR. */
-
-const char *
-internal_function
-__libdwfl_addrsym (Dwfl_Module *mod, GElf_Addr addr, GElf_Off *off,
- GElf_Sym *closest_sym, GElf_Word *shndxp,
- Elf **elfp, Dwarf_Addr *biasp, bool adjust_st_value)
+struct search_state
{
- int syments = INTUSE(dwfl_module_getsymtab) (mod);
- if (syments < 0)
- return NULL;
+ Dwfl_Module *mod;
+ GElf_Addr addr;
- /* Return true iff we consider ADDR to lie in the same section as SYM. */
- GElf_Word addr_shndx = SHN_UNDEF;
- Elf *addr_symelf = NULL;
- inline bool same_section (GElf_Addr value, Elf *symelf, GElf_Word shndx)
- {
- /* For absolute symbols and the like, only match exactly. */
- if (shndx >= SHN_LORESERVE)
- return value == addr;
+ GElf_Sym *closest_sym;
+ bool adjust_st_value;
+ GElf_Word addr_shndx;
+ Elf *addr_symelf;
- /* If value might not be st_value, the shndx of the symbol might
- not match the section of the value. Explicitly look both up. */
- if (! adjust_st_value)
- {
- Dwarf_Addr v;
- if (addr_shndx == SHN_UNDEF)
- {
- v = addr;
- addr_shndx = __libdwfl_find_section_ndx (mod, &v);
- }
+ /* Keep track of the closest symbol we have seen so far.
+ Here we store only symbols with nonzero st_size. */
+ const char *closest_name;
+ GElf_Addr closest_value;
+ GElf_Word closest_shndx;
+ Elf *closest_elf;
- v = value;
- return addr_shndx == __libdwfl_find_section_ndx (mod, &v);
- }
+ /* Keep track of an eligible symbol with st_size == 0 as a fallback. */
+ const char *sizeless_name;
+ GElf_Sym sizeless_sym;
+ GElf_Addr sizeless_value;
+ GElf_Word sizeless_shndx;
+ Elf *sizeless_elf;
- /* Figure out what section ADDR lies in. */
- if (addr_shndx == SHN_UNDEF || addr_symelf != symelf)
- {
- GElf_Addr mod_addr = dwfl_deadjust_st_value (mod, symelf, addr);
- Elf_Scn *scn = NULL;
- addr_shndx = SHN_ABS;
- addr_symelf = symelf;
- while ((scn = elf_nextscn (symelf, scn)) != NULL)
- {
- GElf_Shdr shdr_mem;
- GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
- if (likely (shdr != NULL)
- && mod_addr >= shdr->sh_addr
- && mod_addr < shdr->sh_addr + shdr->sh_size)
- {
- addr_shndx = elf_ndxscn (scn);
- break;
- }
- }
- }
+ /* Keep track of the lowest address a relevant sizeless symbol could have. */
+ GElf_Addr min_label;
+};
- return shndx == addr_shndx && addr_symelf == symelf;
+/* Return true iff we consider ADDR to lie in the same section as SYM. */
+static inline bool
+same_section (struct search_state *state,
+ GElf_Addr value, Elf *symelf, GElf_Word shndx)
+{
+ /* For absolute symbols and the like, only match exactly. */
+ if (shndx >= SHN_LORESERVE)
+ return value == state->addr;
+
+ /* If value might not be st_value, the shndx of the symbol might
+ not match the section of the value. Explicitly look both up. */
+ if (! state->adjust_st_value)
+ {
+ Dwarf_Addr v;
+ if (state->addr_shndx == SHN_UNDEF)
+ {
+ v = state->addr;
+ state->addr_shndx = __libdwfl_find_section_ndx (state->mod, &v);
+ }
+
+ v = value;
+ return state->addr_shndx == __libdwfl_find_section_ndx (state->mod, &v);
}
- /* Keep track of the closest symbol we have seen so far.
- Here we store only symbols with nonzero st_size. */
- const char *closest_name = NULL;
- GElf_Addr closest_value = 0;
- GElf_Word closest_shndx = SHN_UNDEF;
- Elf *closest_elf = NULL;
+ /* Figure out what section ADDR lies in. */
+ if (state->addr_shndx == SHN_UNDEF || state->addr_symelf != symelf)
+ {
+ GElf_Addr mod_addr = dwfl_deadjust_st_value (state->mod, symelf,
+ state->addr);
+ Elf_Scn *scn = NULL;
+ state->addr_shndx = SHN_ABS;
+ state->addr_symelf = symelf;
+ while ((scn = elf_nextscn (symelf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (likely (shdr != NULL)
+ && mod_addr >= shdr->sh_addr
+ && mod_addr < shdr->sh_addr + shdr->sh_size)
+ {
+ state->addr_shndx = elf_ndxscn (scn);
+ break;
+ }
+ }
+ }
- /* Keep track of an eligible symbol with st_size == 0 as a fallback. */
- const char *sizeless_name = NULL;
- GElf_Sym sizeless_sym = { 0, 0, 0, 0, 0, SHN_UNDEF };
- GElf_Addr sizeless_value = 0;
- GElf_Word sizeless_shndx = SHN_UNDEF;
- Elf *sizeless_elf = NULL;
+ return shndx == state->addr_shndx && state->addr_symelf == symelf;
+}
- /* Keep track of the lowest address a relevant sizeless symbol could have. */
- GElf_Addr min_label = 0;
+/* Return GELF_ST_BIND as higher-is-better integer. */
+static inline int
+binding_value (const GElf_Sym *symp)
+{
+ switch (GELF_ST_BIND (symp->st_info))
+ {
+ case STB_GLOBAL:
+ return 3;
+ case STB_WEAK:
+ return 2;
+ case STB_LOCAL:
+ return 1;
+ default:
+ return 0;
+ }
+}
- /* Try one symbol and associated value from the search table. */
- inline void try_sym_value (GElf_Addr value, GElf_Sym *sym,
- const char *name, GElf_Word shndx,
- Elf *elf, bool resolved)
- {
+/* Try one symbol and associated value from the search table. */
+static inline void
+try_sym_value (struct search_state *state,
+ GElf_Addr value, GElf_Sym *sym,
+ const char *name, GElf_Word shndx,
+ Elf *elf, bool resolved)
+{
/* Even if we don't choose this symbol, its existence excludes
any sizeless symbol (assembly label) that is below its upper
bound. */
- if (value + sym->st_size > min_label)
- min_label = value + sym->st_size;
+ if (value + sym->st_size > state->min_label)
+ state->min_label = value + sym->st_size;
- if (sym->st_size == 0 || addr - value < sym->st_size)
+ if (sym->st_size == 0 || state->addr - value < sym->st_size)
{
- /* Return GELF_ST_BIND as higher-is-better integer. */
- inline int binding_value (const GElf_Sym *symp)
- {
- switch (GELF_ST_BIND (symp->st_info))
- {
- case STB_GLOBAL:
- return 3;
- case STB_WEAK:
- return 2;
- case STB_LOCAL:
- return 1;
- default:
- return 0;
- }
- }
-
/* This symbol is a better candidate than the current one
if it's closer to ADDR or is global when it was local. */
- if (closest_name == NULL
- || closest_value < value
- || binding_value (closest_sym) < binding_value (sym))
+ if (state->closest_name == NULL
+ || state->closest_value < value
+ || binding_value (state->closest_sym) < binding_value (sym))
{
if (sym->st_size != 0)
{
- *closest_sym = *sym;
- closest_value = value;
- closest_shndx = shndx;
- closest_elf = elf;
- closest_name = name;
+ *state->closest_sym = *sym;
+ state->closest_value = value;
+ state->closest_shndx = shndx;
+ state->closest_elf = elf;
+ state->closest_name = name;
}
- else if (closest_name == NULL
- && value >= min_label
- && same_section (value,
- resolved ? mod->main.elf : elf, shndx))
+ else if (state->closest_name == NULL
+ && value >= state->min_label
+ && same_section (state, value,
+ resolved ? state->mod->main.elf : elf,
+ shndx))
{
/* Handwritten assembly symbols sometimes have no
st_size. If no symbol with proper size includes
the address, we'll use the closest one that is in
the same section as ADDR. */
- sizeless_sym = *sym;
- sizeless_value = value;
- sizeless_shndx = shndx;
- sizeless_elf = elf;
- sizeless_name = name;
+ state->sizeless_sym = *sym;
+ state->sizeless_value = value;
+ state->sizeless_shndx = shndx;
+ state->sizeless_elf = elf;
+ state->sizeless_name = name;
}
}
/* When the beginning of its range is no closer,
@@ -170,26 +173,27 @@ __libdwfl_addrsym (Dwfl_Module *mod, GElf_Addr addr, GElf_Off *off,
GELF_ST_BIND preference. If all are equal prefer
the first symbol found. */
else if (sym->st_size != 0
- && closest_value == value
- && ((closest_sym->st_size > sym->st_size
- && (binding_value (closest_sym)
+ && state->closest_value == value
+ && ((state->closest_sym->st_size > sym->st_size
+ && (binding_value (state->closest_sym)
<= binding_value (sym)))
- || (closest_sym->st_size >= sym->st_size
- && (binding_value (closest_sym)
+ || (state->closest_sym->st_size >= sym->st_size
+ && (binding_value (state->closest_sym)
< binding_value (sym)))))
{
- *closest_sym = *sym;
- closest_value = value;
- closest_shndx = shndx;
- closest_elf = elf;
- closest_name = name;
+ *state->closest_sym = *sym;
+ state->closest_value = value;
+ state->closest_shndx = shndx;
+ state->closest_elf = elf;
+ state->closest_name = name;
}
}
- }
+}
- /* Look through the symbol table for a matching symbol. */
- inline void search_table (int start, int end)
- {
+/* Look through the symbol table for a matching symbol. */
+static inline void
+search_table (struct search_state *state, int start, int end)
+{
for (int i = start; i < end; ++i)
{
GElf_Sym sym;
@@ -197,32 +201,66 @@ __libdwfl_addrsym (Dwfl_Module *mod, GElf_Addr addr, GElf_Off *off,
GElf_Word shndx;
Elf *elf;
bool resolved;
- const char *name = __libdwfl_getsym (mod, i, &sym, &value,
+ const char *name = __libdwfl_getsym (state->mod, i, &sym, &value,
&shndx, &elf, NULL,
- &resolved, adjust_st_value);
+ &resolved,
+ state->adjust_st_value);
if (name != NULL && name[0] != '\0'
&& sym.st_shndx != SHN_UNDEF
- && value <= addr
+ && value <= state->addr
&& GELF_ST_TYPE (sym.st_info) != STT_SECTION
&& GELF_ST_TYPE (sym.st_info) != STT_FILE
&& GELF_ST_TYPE (sym.st_info) != STT_TLS)
{
- try_sym_value (value, &sym, name, shndx, elf, resolved);
+ try_sym_value (state, value, &sym, name, shndx, elf, resolved);
/* If this is an addrinfo variant and the value could be
resolved then also try matching the (adjusted) st_value. */
- if (resolved && mod->e_type != ET_REL)
+ if (resolved && state->mod->e_type != ET_REL)
{
GElf_Addr adjusted_st_value;
- adjusted_st_value = dwfl_adjusted_st_value (mod, elf,
+ adjusted_st_value = dwfl_adjusted_st_value (state->mod, elf,
sym.st_value);
- if (value != adjusted_st_value && adjusted_st_value <= addr)
- try_sym_value (adjusted_st_value, &sym, name, shndx,
+ if (value != adjusted_st_value
+ && adjusted_st_value <= state->addr)
+ try_sym_value (state, adjusted_st_value, &sym, name, shndx,
elf, false);
}
}
}
- }
+}
+
+/* Returns the name of the symbol "closest" to ADDR.
+ Never returns symbols at addresses above ADDR. */
+const char *
+internal_function
+__libdwfl_addrsym (Dwfl_Module *_mod, GElf_Addr _addr, GElf_Off *off,
+ GElf_Sym *_closest_sym, GElf_Word *shndxp,
+ Elf **elfp, Dwarf_Addr *biasp, bool _adjust_st_value)
+{
+ int syments = INTUSE(dwfl_module_getsymtab) (_mod);
+ if (syments < 0)
+ return NULL;
+
+ struct search_state state =
+ {
+ .addr = _addr,
+ .mod = _mod,
+ .closest_sym = _closest_sym,
+ .adjust_st_value = _adjust_st_value,
+ .addr_shndx = SHN_UNDEF,
+ .addr_symelf = NULL,
+ .closest_name = NULL,
+ .closest_value = 0,
+ .closest_shndx = SHN_UNDEF,
+ .closest_elf = NULL,
+ .sizeless_name = NULL,
+ .sizeless_sym = { 0, 0, 0, 0, 0, SHN_UNDEF },
+ .sizeless_value = 0,
+ .sizeless_shndx = SHN_UNDEF,
+ .sizeless_elf = NULL,
+ .min_label = 0
+ };
/* First go through global symbols. mod->first_global and
mod->aux_first_global are setup by dwfl_module_getsymtab to the
@@ -233,38 +271,39 @@ __libdwfl_addrsym (Dwfl_Module *mod, GElf_Addr addr, GElf_Off *off,
come first in the symbol table, then all globals. The zeroth,
null entry, in the auxiliary table is skipped if there is a main
table. */
- int first_global = INTUSE (dwfl_module_getsymtab_first_global) (mod);
+ int first_global = INTUSE (dwfl_module_getsymtab_first_global) (state.mod);
if (first_global < 0)
return NULL;
- search_table (first_global == 0 ? 1 : first_global, syments);
+ search_table (&state, first_global == 0 ? 1 : first_global, syments);
/* If we found nothing searching the global symbols, then try the locals.
Unless we have a global sizeless symbol that matches exactly. */
- if (closest_name == NULL && first_global > 1
- && (sizeless_name == NULL || sizeless_value != addr))
- search_table (1, first_global);
+ if (state.closest_name == NULL && first_global > 1
+ && (state.sizeless_name == NULL || state.sizeless_value != state.addr))
+ search_table (&state, 1, first_global);
/* If we found no proper sized symbol to use, fall back to the best
candidate sizeless symbol we found, if any. */
- if (closest_name == NULL
- && sizeless_name != NULL && sizeless_value >= min_label)
+ if (state.closest_name == NULL
+ && state.sizeless_name != NULL
+ && state.sizeless_value >= state.min_label)
{
- *closest_sym = sizeless_sym;
- closest_value = sizeless_value;
- closest_shndx = sizeless_shndx;
- closest_elf = sizeless_elf;
- closest_name = sizeless_name;
+ *state.closest_sym = state.sizeless_sym;
+ state.closest_value = state.sizeless_value;
+ state.closest_shndx = state.sizeless_shndx;
+ state.closest_elf = state.sizeless_elf;
+ state.closest_name = state.sizeless_name;
}
- *off = addr - closest_value;
+ *off = state.addr - state.closest_value;
if (shndxp != NULL)
- *shndxp = closest_shndx;
+ *shndxp = state.closest_shndx;
if (elfp != NULL)
- *elfp = closest_elf;
+ *elfp = state.closest_elf;
if (biasp != NULL)
- *biasp = dwfl_adjusted_st_value (mod, closest_elf, 0);
- return closest_name;
+ *biasp = dwfl_adjusted_st_value (state.mod, state.closest_elf, 0);
+ return state.closest_name;
}
diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c
index 8483fa21..e9589b3f 100644
--- a/libdwfl/dwfl_module_getdwarf.c
+++ b/libdwfl/dwfl_module_getdwarf.c
@@ -231,6 +231,24 @@ __libdwfl_getelf (Dwfl_Module *mod)
mod->main_bias = mod->e_type == ET_REL ? 0 : mod->low_addr - mod->main.vaddr;
}
+static inline void
+consider_shdr (GElf_Addr interp,
+ GElf_Word sh_type,
+ GElf_Xword sh_flags,
+ GElf_Addr sh_addr,
+ GElf_Xword sh_size,
+ GElf_Addr *phighest)
+{
+ if ((sh_flags & SHF_ALLOC)
+ && ((sh_type == SHT_PROGBITS && sh_addr != interp)
+ || sh_type == SHT_NOBITS))
+ {
+ const GElf_Addr sh_end = sh_addr + sh_size;
+ if (sh_end > *phighest)
+ *phighest = sh_end;
+ }
+}
+
/* If the main file might have been prelinked, then we need to
discover the correct synchronization address between the main and
debug files. Because of prelink's section juggling, we cannot rely
@@ -448,22 +466,6 @@ find_prelink_address_sync (Dwfl_Module *mod, struct dwfl_file *file)
GElf_Addr highest;
- inline void consider_shdr (GElf_Addr interp,
- GElf_Word sh_type,
- GElf_Xword sh_flags,
- GElf_Addr sh_addr,
- GElf_Xword sh_size)
- {
- if ((sh_flags & SHF_ALLOC)
- && ((sh_type == SHT_PROGBITS && sh_addr != interp)
- || sh_type == SHT_NOBITS))
- {
- const GElf_Addr sh_end = sh_addr + sh_size;
- if (sh_end > highest)
- highest = sh_end;
- }
- }
-
highest = 0;
scn = NULL;
while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
@@ -476,7 +478,7 @@ find_prelink_address_sync (Dwfl_Module *mod, struct dwfl_file *file)
return DWFL_E_LIBELF;
}
consider_shdr (main_interp, sh->sh_type, sh->sh_flags,
- sh->sh_addr, sh->sh_size);
+ sh->sh_addr, sh->sh_size, &highest);
}
if (highest > mod->main.vaddr)
{
@@ -489,7 +491,7 @@ find_prelink_address_sync (Dwfl_Module *mod, struct dwfl_file *file)
for (size_t i = 0; i < shnum - 1; ++i)
consider_shdr (undo_interp, (*s32)[i].sh_type,
(*s32)[i].sh_flags, (*s32)[i].sh_addr,
- (*s32)[i].sh_size);
+ (*s32)[i].sh_size, &highest);
}
else
{
@@ -497,7 +499,7 @@ find_prelink_address_sync (Dwfl_Module *mod, struct dwfl_file *file)
for (size_t i = 0; i < shnum - 1; ++i)
consider_shdr (undo_interp, (*s64)[i].sh_type,
(*s64)[i].sh_flags, (*s64)[i].sh_addr,
- (*s64)[i].sh_size);
+ (*s64)[i].sh_size, &highest);
}
if (highest > file->vaddr)
@@ -676,6 +678,141 @@ find_offsets (Elf *elf, GElf_Addr main_bias, size_t phnum, size_t n,
}
}
+/* Various addresses we might want to pull from the dynamic segment. */
+enum
+{
+ i_symtab,
+ i_strtab,
+ i_hash,
+ i_gnu_hash,
+ i_max
+};
+
+/* Translate pointers into file offsets. ADJUST is either zero
+ in case the dynamic segment wasn't adjusted or mod->main_bias.
+ Will set mod->symfile if the translated offsets can be used as
+ symbol table. */
+static void
+translate_offs (GElf_Addr adjust,
+ Dwfl_Module *mod, size_t phnum,
+ GElf_Addr addrs[i_max], GElf_Xword strsz,
+ GElf_Ehdr *ehdr)
+{
+ GElf_Off offs[i_max] = { 0, };
+ find_offsets (mod->main.elf, adjust, phnum, i_max, addrs, offs);
+
+ /* Figure out the size of the symbol table. */
+ if (offs[i_hash] != 0)
+ {
+ /* In the original format, .hash says the size of .dynsym. */
+
+ size_t entsz = SH_ENTSIZE_HASH (ehdr);
+ Elf_Data *data = elf_getdata_rawchunk (mod->main.elf,
+ offs[i_hash] + entsz, entsz,
+ (entsz == 4
+ ? ELF_T_WORD : ELF_T_XWORD));
+ if (data != NULL)
+ mod->syments = (entsz == 4
+ ? *(const GElf_Word *) data->d_buf
+ : *(const GElf_Xword *) data->d_buf);
+ }
+ if (offs[i_gnu_hash] != 0 && mod->syments == 0)
+ {
+ /* In the new format, we can derive it with some work. */
+
+ const struct
+ {
+ Elf32_Word nbuckets;
+ Elf32_Word symndx;
+ Elf32_Word maskwords;
+ Elf32_Word shift2;
+ } *header;
+
+ Elf_Data *data = elf_getdata_rawchunk (mod->main.elf, offs[i_gnu_hash],
+ sizeof *header, ELF_T_WORD);
+ if (data != NULL)
+ {
+ header = data->d_buf;
+ Elf32_Word nbuckets = header->nbuckets;
+ Elf32_Word symndx = header->symndx;
+ GElf_Off buckets_at = (offs[i_gnu_hash] + sizeof *header
+ + (gelf_getclass (mod->main.elf)
+ * sizeof (Elf32_Word)
+ * header->maskwords));
+
+ // elf_getdata_rawchunk takes a size_t, make sure it
+ // doesn't overflow.
+#if SIZE_MAX <= UINT32_MAX
+ if (nbuckets > SIZE_MAX / sizeof (Elf32_Word))
+ data = NULL;
+ else
+#endif
+ data = elf_getdata_rawchunk (mod->main.elf, buckets_at,
+ nbuckets * sizeof (Elf32_Word),
+ ELF_T_WORD);
+ if (data != NULL && symndx < nbuckets)
+ {
+ const Elf32_Word *const buckets = data->d_buf;
+ Elf32_Word maxndx = symndx;
+ for (Elf32_Word bucket = 0; bucket < nbuckets; ++bucket)
+ if (buckets[bucket] > maxndx)
+ maxndx = buckets[bucket];
+
+ GElf_Off hasharr_at = (buckets_at
+ + nbuckets * sizeof (Elf32_Word));
+ hasharr_at += (maxndx - symndx) * sizeof (Elf32_Word);
+ do
+ {
+ data = elf_getdata_rawchunk (mod->main.elf,
+ hasharr_at,
+ sizeof (Elf32_Word),
+ ELF_T_WORD);
+ if (data != NULL
+ && (*(const Elf32_Word *) data->d_buf & 1u))
+ {
+ mod->syments = maxndx + 1;
+ break;
+ }
+ ++maxndx;
+ hasharr_at += sizeof (Elf32_Word);
+ }
+ while (data != NULL);
+ }
+ }
+ }
+ if (offs[i_strtab] > offs[i_symtab] && mod->syments == 0)
+ mod->syments = ((offs[i_strtab] - offs[i_symtab])
+ / gelf_fsize (mod->main.elf,
+ ELF_T_SYM, 1, EV_CURRENT));
+
+ if (mod->syments > 0)
+ {
+ mod->symdata = elf_getdata_rawchunk (mod->main.elf,
+ offs[i_symtab],
+ gelf_fsize (mod->main.elf,
+ ELF_T_SYM,
+ mod->syments,
+ EV_CURRENT),
+ ELF_T_SYM);
+ if (mod->symdata != NULL)
+ {
+ mod->symstrdata = elf_getdata_rawchunk (mod->main.elf,
+ offs[i_strtab],
+ strsz,
+ ELF_T_BYTE);
+ if (mod->symstrdata == NULL)
+ mod->symdata = NULL;
+ }
+ if (mod->symdata == NULL)
+ mod->symerr = DWFL_E (LIBELF, elf_errno ());
+ else
+ {
+ mod->symfile = &mod->main;
+ mod->symerr = DWFL_E_NOERROR;
+ }
+ }
+}
+
/* Try to find a dynamic symbol table via phdrs. */
static void
find_dynsym (Dwfl_Module *mod)
@@ -704,14 +841,6 @@ find_dynsym (Dwfl_Module *mod)
if (data == NULL)
continue;
- enum
- {
- i_symtab,
- i_strtab,
- i_hash,
- i_gnu_hash,
- i_max
- };
GElf_Addr addrs[i_max] = { 0, };
GElf_Xword strsz = 0;
size_t n = data->d_size / gelf_fsize (mod->main.elf,
@@ -752,131 +881,12 @@ find_dynsym (Dwfl_Module *mod)
break;
}
- /* Translate pointers into file offsets. ADJUST is either zero
- in case the dynamic segment wasn't adjusted or mod->main_bias. */
- void translate_offs (GElf_Addr adjust)
- {
- GElf_Off offs[i_max] = { 0, };
- find_offsets (mod->main.elf, adjust, phnum, i_max, addrs, offs);
-
- /* Figure out the size of the symbol table. */
- if (offs[i_hash] != 0)
- {
- /* In the original format, .hash says the size of .dynsym. */
-
- size_t entsz = SH_ENTSIZE_HASH (ehdr);
- data = elf_getdata_rawchunk (mod->main.elf,
- offs[i_hash] + entsz, entsz,
- entsz == 4 ? ELF_T_WORD
- : ELF_T_XWORD);
- if (data != NULL)
- mod->syments = (entsz == 4
- ? *(const GElf_Word *) data->d_buf
- : *(const GElf_Xword *) data->d_buf);
- }
- if (offs[i_gnu_hash] != 0 && mod->syments == 0)
- {
- /* In the new format, we can derive it with some work. */
-
- const struct
- {
- Elf32_Word nbuckets;
- Elf32_Word symndx;
- Elf32_Word maskwords;
- Elf32_Word shift2;
- } *header;
-
- data = elf_getdata_rawchunk (mod->main.elf, offs[i_gnu_hash],
- sizeof *header, ELF_T_WORD);
- if (data != NULL)
- {
- header = data->d_buf;
- Elf32_Word nbuckets = header->nbuckets;
- Elf32_Word symndx = header->symndx;
- GElf_Off buckets_at = (offs[i_gnu_hash] + sizeof *header
- + (gelf_getclass (mod->main.elf)
- * sizeof (Elf32_Word)
- * header->maskwords));
-
- // elf_getdata_rawchunk takes a size_t, make sure it
- // doesn't overflow.
-#if SIZE_MAX <= UINT32_MAX
- if (nbuckets > SIZE_MAX / sizeof (Elf32_Word))
- data = NULL;
- else
-#endif
- data
- = elf_getdata_rawchunk (mod->main.elf, buckets_at,
- nbuckets * sizeof (Elf32_Word),
- ELF_T_WORD);
- if (data != NULL && symndx < nbuckets)
- {
- const Elf32_Word *const buckets = data->d_buf;
- Elf32_Word maxndx = symndx;
- for (Elf32_Word bucket = 0; bucket < nbuckets; ++bucket)
- if (buckets[bucket] > maxndx)
- maxndx = buckets[bucket];
-
- GElf_Off hasharr_at = (buckets_at
- + nbuckets * sizeof (Elf32_Word));
- hasharr_at += (maxndx - symndx) * sizeof (Elf32_Word);
- do
- {
- data = elf_getdata_rawchunk (mod->main.elf,
- hasharr_at,
- sizeof (Elf32_Word),
- ELF_T_WORD);
- if (data != NULL
- && (*(const Elf32_Word *) data->d_buf & 1u))
- {
- mod->syments = maxndx + 1;
- break;
- }
- ++maxndx;
- hasharr_at += sizeof (Elf32_Word);
- } while (data != NULL);
- }
- }
- }
- if (offs[i_strtab] > offs[i_symtab] && mod->syments == 0)
- mod->syments = ((offs[i_strtab] - offs[i_symtab])
- / gelf_fsize (mod->main.elf,
- ELF_T_SYM, 1, EV_CURRENT));
-
- if (mod->syments > 0)
- {
- mod->symdata = elf_getdata_rawchunk (mod->main.elf,
- offs[i_symtab],
- gelf_fsize (mod->main.elf,
- ELF_T_SYM,
- mod->syments,
- EV_CURRENT),
- ELF_T_SYM);
- if (mod->symdata != NULL)
- {
- mod->symstrdata = elf_getdata_rawchunk (mod->main.elf,
- offs[i_strtab],
- strsz,
- ELF_T_BYTE);
- if (mod->symstrdata == NULL)
- mod->symdata = NULL;
- }
- if (mod->symdata == NULL)
- mod->symerr = DWFL_E (LIBELF, elf_errno ());
- else
- {
- mod->symfile = &mod->main;
- mod->symerr = DWFL_E_NOERROR;
- }
- }
- }
-
/* First try unadjusted, like ELF files from disk, vdso.
Then try for already adjusted dynamic section, like ELF
from remote memory. */
- translate_offs (0);
+ translate_offs (0, mod, phnum, addrs, strsz, ehdr);
if (mod->symfile == NULL)
- translate_offs (mod->main_bias);
+ translate_offs (mod->main_bias, mod, phnum, addrs, strsz, ehdr);
return;
}
diff --git a/libdwfl/frame_unwind.c b/libdwfl/frame_unwind.c
index 39509b70..0e470b97 100644
--- a/libdwfl/frame_unwind.c
+++ b/libdwfl/frame_unwind.c
@@ -637,7 +637,14 @@ handle_cfi (Dwfl_Frame *state, Dwarf_Addr pc, Dwarf_CFI *cfi, Dwarf_Addr bias)
if (unwound->pc == 0)
unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
else
- unwound->pc_state = DWFL_FRAME_STATE_PC_SET;
+ {
+ unwound->pc_state = DWFL_FRAME_STATE_PC_SET;
+ /* In SPARC the return address register actually contains
+ the address of the call instruction instead of the return
+ address. Therefore we add here an offset defined by the
+ backend. Most likely 0. */
+ unwound->pc += ebl_ra_offset (ebl);
+ }
}
free (frame);
}
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index 63556d51..2a83646e 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -1,5 +1,5 @@
/* Internal definitions for libdwfl.
- Copyright (C) 2005-2014 Red Hat, Inc.
+ Copyright (C) 2005-2015 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -104,6 +104,16 @@ typedef enum { DWFL_ERRORS DWFL_E_NUM } Dwfl_Error;
extern int __libdwfl_canon_error (Dwfl_Error) internal_function;
extern void __libdwfl_seterrno (Dwfl_Error) internal_function;
+/* Resources we might keep for the user about the core file that the
+ Dwfl might have been created from. Can currently only be set
+ through std-argp. */
+struct Dwfl_User_Core
+{
+ char *executable_for_core; /* --executable if --core was specified. */
+ Elf *core; /* non-NULL if we need to free it. */
+ int fd; /* close if >= 0. */
+};
+
struct Dwfl
{
const Dwfl_Callbacks *callbacks;
@@ -130,7 +140,7 @@ struct Dwfl
GElf_Off lookup_tail_offset;
int lookup_tail_ndx;
- char *executable_for_core; /* --executable if --core was specified. */
+ struct Dwfl_User_Core *user_core;
};
#define OFFLINE_REDZONE 0x10000
diff --git a/libdwfl/link_map.c b/libdwfl/link_map.c
index 13cac529..28d7382f 100644
--- a/libdwfl/link_map.c
+++ b/libdwfl/link_map.c
@@ -42,64 +42,70 @@
#define PROBE_VAL64 sizeof (Elf64_Phdr)
-/* Examine an auxv data block and determine its format.
- Return true iff we figured it out. */
-static bool
-auxv_format_probe (const void *auxv, size_t size,
- uint_fast8_t *elfclass, uint_fast8_t *elfdata)
+static inline bool
+do_check64 (size_t i, const Elf64_auxv_t (*a64)[], uint_fast8_t *elfdata)
{
- const Elf32_auxv_t (*a32)[size / sizeof (Elf32_auxv_t)] = (void *) auxv;
- const Elf64_auxv_t (*a64)[size / sizeof (Elf64_auxv_t)] = (void *) auxv;
+ /* The AUXV pointer might not even be naturally aligned for 64-bit
+ data, because note payloads in a core file are not aligned. */
- inline bool check64 (size_t i)
- {
- /* The AUXV pointer might not even be naturally aligned for 64-bit
- data, because note payloads in a core file are not aligned. */
+ uint64_t type = read_8ubyte_unaligned_noncvt (&(*a64)[i].a_type);
+ uint64_t val = read_8ubyte_unaligned_noncvt (&(*a64)[i].a_un.a_val);
- uint64_t type = read_8ubyte_unaligned_noncvt (&(*a64)[i].a_type);
- uint64_t val = read_8ubyte_unaligned_noncvt (&(*a64)[i].a_un.a_val);
+ if (type == BE64 (PROBE_TYPE)
+ && val == BE64 (PROBE_VAL64))
+ {
+ *elfdata = ELFDATA2MSB;
+ return true;
+ }
- if (type == BE64 (PROBE_TYPE)
- && val == BE64 (PROBE_VAL64))
- {
- *elfdata = ELFDATA2MSB;
- return true;
- }
+ if (type == LE64 (PROBE_TYPE)
+ && val == LE64 (PROBE_VAL64))
+ {
+ *elfdata = ELFDATA2LSB;
+ return true;
+ }
- if (type == LE64 (PROBE_TYPE)
- && val == LE64 (PROBE_VAL64))
- {
- *elfdata = ELFDATA2LSB;
- return true;
- }
+ return false;
+}
- return false;
- }
+#define check64(n) do_check64 (n, a64, elfdata)
- inline bool check32 (size_t i)
- {
- /* The AUXV pointer might not even be naturally aligned for 32-bit
- data, because note payloads in a core file are not aligned. */
+static inline bool
+do_check32 (size_t i, const Elf32_auxv_t (*a32)[], uint_fast8_t *elfdata)
+{
+ /* The AUXV pointer might not even be naturally aligned for 32-bit
+ data, because note payloads in a core file are not aligned. */
- uint32_t type = read_4ubyte_unaligned_noncvt (&(*a32)[i].a_type);
- uint32_t val = read_4ubyte_unaligned_noncvt (&(*a32)[i].a_un.a_val);
+ uint32_t type = read_4ubyte_unaligned_noncvt (&(*a32)[i].a_type);
+ uint32_t val = read_4ubyte_unaligned_noncvt (&(*a32)[i].a_un.a_val);
- if (type == BE32 (PROBE_TYPE)
- && val == BE32 (PROBE_VAL32))
- {
- *elfdata = ELFDATA2MSB;
- return true;
- }
+ if (type == BE32 (PROBE_TYPE)
+ && val == BE32 (PROBE_VAL32))
+ {
+ *elfdata = ELFDATA2MSB;
+ return true;
+ }
- if (type == LE32 (PROBE_TYPE)
- && val == LE32 (PROBE_VAL32))
- {
- *elfdata = ELFDATA2LSB;
- return true;
- }
+ if (type == LE32 (PROBE_TYPE)
+ && val == LE32 (PROBE_VAL32))
+ {
+ *elfdata = ELFDATA2LSB;
+ return true;
+ }
- return false;
- }
+ return false;
+}
+
+#define check32(n) do_check32 (n, a32, elfdata)
+
+/* Examine an auxv data block and determine its format.
+ Return true iff we figured it out. */
+static bool
+auxv_format_probe (const void *auxv, size_t size,
+ uint_fast8_t *elfclass, uint_fast8_t *elfdata)
+{
+ const Elf32_auxv_t (*a32)[size / sizeof (Elf32_auxv_t)] = (void *) auxv;
+ const Elf64_auxv_t (*a64)[size / sizeof (Elf64_auxv_t)] = (void *) auxv;
for (size_t i = 0; i < size / sizeof (Elf64_auxv_t); ++i)
{
@@ -356,8 +362,10 @@ report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata,
if (name != NULL && name[0] == '\0')
name = NULL;
- if (iterations == 1 && dwfl->executable_for_core != NULL)
- name = dwfl->executable_for_core;
+ if (iterations == 1
+ && dwfl->user_core != NULL
+ && dwfl->user_core->executable_for_core != NULL)
+ name = dwfl->user_core->executable_for_core;
struct r_debug_info_module *r_debug_info_module = NULL;
if (r_debug_info != NULL)
@@ -789,7 +797,10 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
bool in_ok = (*memory_callback) (dwfl, phdr_segndx, &in.d_buf,
&in.d_size, phdr, phnum * phent,
memory_callback_arg);
- if (! in_ok && dwfl->executable_for_core != NULL)
+ bool in_from_exec = false;
+ if (! in_ok
+ && dwfl->user_core != NULL
+ && dwfl->user_core->executable_for_core != NULL)
{
/* AUXV -> PHDR -> DYNAMIC
Both AUXV and DYNAMIC should be always present in a core file.
@@ -797,7 +808,7 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
EXECUTABLE_FOR_CORE to find where DYNAMIC is located in the
core file. */
- int fd = open (dwfl->executable_for_core, O_RDONLY);
+ int fd = open (dwfl->user_core->executable_for_core, O_RDONLY);
Elf *elf;
Dwfl_Error error = DWFL_E_ERRNO;
if (fd != -1)
@@ -851,6 +862,7 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
return false;
}
in_ok = true;
+ in_from_exec = true;
}
if (in_ok)
{
@@ -899,8 +911,11 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
}
}
- (*memory_callback) (dwfl, -1, &in.d_buf, &in.d_size, 0, 0,
- memory_callback_arg);
+ if (in_from_exec)
+ free (in.d_buf);
+ else
+ (*memory_callback) (dwfl, -1, &in.d_buf, &in.d_size, 0, 0,
+ memory_callback_arg);
free (buf);
}
else
diff --git a/libdwfl/linux-proc-maps.c b/libdwfl/linux-proc-maps.c
index 2e2c8f92..9e7b2a21 100644
--- a/libdwfl/linux-proc-maps.c
+++ b/libdwfl/linux-proc-maps.c
@@ -175,6 +175,23 @@ grovel_auxv (pid_t pid, Dwfl *dwfl, GElf_Addr *sysinfo_ehdr)
return ENOEXEC;
}
+static inline bool
+do_report (Dwfl *dwfl, char **plast_file, Dwarf_Addr low, Dwarf_Addr high)
+{
+ if (*plast_file != NULL)
+ {
+ Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, *plast_file,
+ low, high);
+ free (*plast_file);
+ *plast_file = NULL;
+ if (unlikely (mod == NULL))
+ return true;
+ }
+ return false;
+}
+
+#define report() do_report(dwfl, &last_file, low, high)
+
static int
proc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr sysinfo_ehdr, pid_t pid)
{
@@ -183,20 +200,6 @@ proc_maps_report (Dwfl *dwfl, FILE *f, GElf_Addr sysinfo_ehdr, pid_t pid)
char *last_file = NULL;
Dwarf_Addr low = 0, high = 0;
- inline bool report (void)
- {
- if (last_file != NULL)
- {
- Dwfl_Module *mod = INTUSE(dwfl_report_module) (dwfl, last_file,
- low, high);
- free (last_file);
- last_file = NULL;
- if (unlikely (mod == NULL))
- return true;
- }
- return false;
- }
-
char *line = NULL;
size_t linesz;
ssize_t len;
diff --git a/libebl/ChangeLog b/libebl/ChangeLog
index d040c08f..a1a10224 100644
--- a/libebl/ChangeLog
+++ b/libebl/ChangeLog
@@ -1,3 +1,9 @@
+2015-12-08 Jose E. Marchesi <jose.marchesi@oracle.com>
+
+ * libebl.h: Prototype for ebl_ra_offset.
+ * eblabicfi.c (ebl_ra_offset): New function.
+ * libeblP.h (struct ebl): new field ra_offset;
+
2015-09-24 Jose E. Marchesi <jose.marchesi@oracle.com>
* Makefile.am (AM_CFLAGS): Use -fPIC instead of -fpic to avoid
diff --git a/libebl/eblabicfi.c b/libebl/eblabicfi.c
index 6b0e18e4..8bf189f1 100644
--- a/libebl/eblabicfi.c
+++ b/libebl/eblabicfi.c
@@ -38,3 +38,9 @@ ebl_abi_cfi (Ebl *ebl, Dwarf_CIE *abi_info)
{
return ebl == NULL ? -1 : ebl->abi_cfi (ebl, abi_info);
}
+
+int
+ebl_ra_offset (Ebl *ebl)
+{
+ return ebl->ra_offset;
+}
diff --git a/libebl/libebl.h b/libebl/libebl.h
index 7dbf4608..efcb6d60 100644
--- a/libebl/libebl.h
+++ b/libebl/libebl.h
@@ -422,6 +422,12 @@ extern bool ebl_set_initial_registers_tid (Ebl *ebl,
extern size_t ebl_frame_nregs (Ebl *ebl)
__nonnull_attribute__ (1);
+/* Offset to apply to the value of the return_address_register, as
+ fetched from a Dwarf CFI. This is used by some backends, where the
+ return_address_register actually contains the call address. */
+extern int ebl_ra_offset (Ebl *ebl)
+ __nonnull_attribute__ (1);
+
/* Mask to use for function symbol or unwind return addresses in case
the architecture adds some extra non-address bits to it. This is
different from ebl_resolve_sym_value which only works for actual
diff --git a/libebl/libeblP.h b/libebl/libeblP.h
index dbd67f3e..5b339b31 100644
--- a/libebl/libeblP.h
+++ b/libebl/libeblP.h
@@ -64,6 +64,12 @@ struct ebl
Ebl architecture can unwind iff FRAME_NREGS > 0. */
size_t frame_nregs;
+ /* Offset to apply to the value of the return_address_register, as
+ fetched from a Dwarf CFI. This is used by some backends, where
+ the return_address_register actually contains the call
+ address. */
+ int ra_offset;
+
/* Mask to use to turn a function value into a real function address
in case the architecture adds some extra non-address bits to it.
If not initialized (0) then ebl_func_addr_mask will return ~0,
diff --git a/src/ChangeLog b/src/ChangeLog
index d6d2936e..b5b5e4a4 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,31 @@
+2015-12-02 Mark Wielaard <mjw@redhat.com>
+
+ * nm.c (process_file): Accept fd and pass it on.
+ (handle_elf): Likewise.
+ (find_no_debuginfo): New.
+ (struct getdbg): Likewise.
+ (getdbg_dwflmod): Likewise.
+ (show_symbols): Take fd. If the file is ET_REL use libdwfl to get
+ the relocated Dwarf.
+
+2015-12-02 Mark Wielaard <mjw@redhat.com>
+
+ * nm.c (get_local_names): Check for duplicates in local_root tree.
+
+2015-12-02 Mark Wielaard <mjw@redhat.com>
+
+ * unstrip.c (struct data_list): New.
+ (new_data_list): Likewise.
+ (record_new_data): Likewise.
+ (free_new_data): Likewise.
+ (adjust_relocs): Call record_new_data.
+ (add_new_section_symbols): Likewise.
+ (copy_elided_sections): Call free_new_data.
+
+2015-12-01 Mark Wielaard <mjw@redhat.com>
+
+ * elfcmp.c (main): Close ebl1 and ebl2 backends.
+
2015-10-16 Mark Wielaard <mjw@redhat.com>
* Makefile.am [BUILD_STATIC](libdw): Add -lz.
diff --git a/src/elfcmp.c b/src/elfcmp.c
index 0250fbe3..852b92f5 100644
--- a/src/elfcmp.c
+++ b/src/elfcmp.c
@@ -655,6 +655,8 @@ cannot read note section [%zu] '%s' in '%s': %s"),
out:
elf_end (elf1);
elf_end (elf2);
+ ebl_closebackend (ebl1);
+ ebl_closebackend (ebl2);
close (fd1);
close (fd2);
diff --git a/src/nm.c b/src/nm.c
index 15d9da4a..2911afa8 100644
--- a/src/nm.c
+++ b/src/nm.c
@@ -45,6 +45,7 @@
#include <system.h>
#include "../libebl/libeblP.h"
+#include "../libdwfl/libdwflP.h"
/* Name and version of program. */
@@ -131,7 +132,7 @@ static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname,
const char *suffix);
/* Handle ELF file. */
-static int handle_elf (Elf *elf, const char *prefix, const char *fname,
+static int handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
const char *suffix);
@@ -384,7 +385,7 @@ process_file (const char *fname, bool more_than_one)
{
if (elf_kind (elf) == ELF_K_ELF)
{
- int result = handle_elf (elf, more_than_one ? "" : NULL,
+ int result = handle_elf (fd, elf, more_than_one ? "" : NULL,
fname, NULL);
if (elf_end (elf) != 0)
@@ -493,7 +494,7 @@ handle_ar (int fd, Elf *elf, const char *prefix, const char *fname,
&& strcmp (arhdr->ar_name, "/SYM64/") != 0)
{
if (elf_kind (subelf) == ELF_K_ELF)
- result |= handle_elf (subelf, new_prefix, arhdr->ar_name,
+ result |= handle_elf (fd, subelf, new_prefix, arhdr->ar_name,
new_suffix);
else if (elf_kind (subelf) == ELF_K_AR)
result |= handle_ar (fd, subelf, new_prefix, arhdr->ar_name,
@@ -708,11 +709,16 @@ get_local_names (Dwarf *dbg)
newp->lowpc = lowpc;
newp->highpc = highpc;
- /* Since we cannot deallocate individual memory we do not test
- for duplicates in the tree. This should not happen anyway. */
- if (tsearch (newp, &local_root, local_compare) == NULL)
- error (EXIT_FAILURE, errno,
- gettext ("cannot create search tree"));
+ /* Check whether a similar local_name is already in the
+ cache. That should not happen. But if it does, we
+ don't want to leak memory. */
+ struct local_name **tres = tsearch (newp, &local_root,
+ local_compare);
+ if (tres == NULL)
+ error (EXIT_FAILURE, errno,
+ gettext ("cannot create search tree"));
+ else if (*tres != newp)
+ free (newp);
}
while (dwarf_siblingof (die, die) == 0);
}
@@ -1148,8 +1154,63 @@ sort_by_name (const void *p1, const void *p2)
return reverse_sort ? -result : result;
}
+/* Stub libdwfl callback, only the ELF handle already open is ever
+ used. Only used for finding the alternate debug file if the Dwarf
+ comes from the main file. We are not interested in separate
+ debuginfo. */
+static int
+find_no_debuginfo (Dwfl_Module *mod,
+ void **userdata,
+ const char *modname,
+ Dwarf_Addr base,
+ const char *file_name,
+ const char *debuglink_file,
+ GElf_Word debuglink_crc,
+ char **debuginfo_file_name)
+{
+ Dwarf_Addr dwbias;
+ dwfl_module_info (mod, NULL, NULL, NULL, &dwbias, NULL, NULL, NULL);
+
+ /* We are only interested if the Dwarf has been setup on the main
+ elf file but is only missing the alternate debug link. If dwbias
+ hasn't even been setup, this is searching for separate debuginfo
+ for the main elf. We don't care in that case. */
+ if (dwbias == (Dwarf_Addr) -1)
+ return -1;
+
+ return dwfl_standard_find_debuginfo (mod, userdata, modname, base,
+ file_name, debuglink_file,
+ debuglink_crc, debuginfo_file_name);
+}
+
+/* Get the Dwarf for the module/file we want. */
+struct getdbg
+{
+ const char *name;
+ Dwarf **dbg;
+};
+
+static int
+getdbg_dwflmod (Dwfl_Module *dwflmod,
+ void **userdata __attribute__ ((unused)),
+ const char *name,
+ Dwarf_Addr base __attribute__ ((unused)),
+ void *arg)
+{
+ struct getdbg *get = (struct getdbg *) arg;
+ if (get != NULL && get->name != NULL && strcmp (get->name, name) == 0)
+ {
+ Dwarf_Addr bias;
+ *get->dbg = dwfl_module_getdwarf (dwflmod, &bias);
+ return DWARF_CB_ABORT;
+ }
+
+ return DWARF_CB_OK;
+}
+
static void
-show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn,
+show_symbols (int fd, Ebl *ebl, GElf_Ehdr *ehdr,
+ Elf_Scn *scn, Elf_Scn *xndxscn,
GElf_Shdr *shdr, const char *prefix, const char *fname,
const char *fullname)
{
@@ -1189,9 +1250,48 @@ show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn,
/* Get a DWARF debugging descriptor. It's no problem if this isn't
possible. We just won't print any line number information. */
Dwarf *dbg = NULL;
+ Dwfl *dwfl = NULL;
if (format == format_sysv)
{
- dbg = dwarf_begin_elf (ebl->elf, DWARF_C_READ, NULL);
+ if (ehdr->e_type != ET_REL)
+ dbg = dwarf_begin_elf (ebl->elf, DWARF_C_READ, NULL);
+ else
+ {
+ /* Abuse libdwfl to do the relocations for us. This is just
+ for the ET_REL file containing Dwarf, so no need for
+ fancy lookups. */
+
+ /* Duplicate an fd for dwfl_report_offline to swallow. */
+ int dwfl_fd = dup (fd);
+ if (likely (dwfl_fd >= 0))
+ {
+ static const Dwfl_Callbacks callbacks =
+ {
+ .section_address = dwfl_offline_section_address,
+ .find_debuginfo = find_no_debuginfo
+ };
+ dwfl = dwfl_begin (&callbacks);
+ if (likely (dwfl != NULL))
+ {
+ /* Let 0 be the logical address of the file (or
+ first in archive). */
+ dwfl->offline_next_address = 0;
+ if (dwfl_report_offline (dwfl, fname, fname, dwfl_fd)
+ == NULL)
+ {
+ /* Consumed on success, not on failure. */
+ close (dwfl_fd);
+ }
+ else
+ {
+ dwfl_report_end (dwfl, NULL, NULL);
+
+ struct getdbg get = { .name = fname, .dbg = &dbg };
+ dwfl_getmodules (dwfl, &getdbg_dwflmod, &get, 0);
+ }
+ }
+ }
+ }
if (dbg != NULL)
{
(void) dwarf_getpubnames (dbg, get_global, NULL, 0);
@@ -1396,13 +1496,16 @@ show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn,
tdestroy (local_root, free);
local_root = NULL;
- (void) dwarf_end (dbg);
+ if (dwfl == NULL)
+ (void) dwarf_end (dbg);
}
+ if (dwfl != NULL)
+ dwfl_end (dwfl);
}
static int
-handle_elf (Elf *elf, const char *prefix, const char *fname,
+handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
const char *suffix)
{
size_t prefix_len = prefix == NULL ? 0 : strlen (prefix);
@@ -1481,7 +1584,7 @@ handle_elf (Elf *elf, const char *prefix, const char *fname,
}
}
- show_symbols (ebl, ehdr, scn, xndxscn, shdr, prefix, fname,
+ show_symbols (fd, ebl, ehdr, scn, xndxscn, shdr, prefix, fname,
fullname);
}
}
diff --git a/src/unstrip.c b/src/unstrip.c
index bc8ed503..85e0a1da 100644
--- a/src/unstrip.c
+++ b/src/unstrip.c
@@ -311,6 +311,38 @@ make_directories (const char *path)
error (EXIT_FAILURE, errno, _("cannot create directory '%s'"), dir);
}
+/* Keep track of new section data we are creating, so we can free it
+ when done. */
+struct data_list
+{
+ void *data;
+ struct data_list *next;
+};
+
+struct data_list *new_data_list;
+
+static void
+record_new_data (void *data)
+{
+ struct data_list *next = new_data_list;
+ new_data_list = xmalloc (sizeof (struct data_list));
+ new_data_list->data = data;
+ new_data_list->next = next;
+}
+
+static void
+free_new_data (void)
+{
+ struct data_list *list = new_data_list;
+ while (list != NULL)
+ {
+ struct data_list *next = list->next;
+ free (list->data);
+ free (list);
+ list = next;
+ }
+ new_data_list = NULL;
+}
/* The binutils linker leaves gratuitous section symbols in .symtab
that strip has to remove. Older linkers likewise include a
@@ -472,6 +504,7 @@ adjust_relocs (Elf_Scn *outscn, Elf_Scn *inscn, const GElf_Shdr *shdr,
if (old_chain[i] != STN_UNDEF) \
new_chain[map[i - 1]] = map[old_chain[i] - 1]; \
\
+ record_new_data (new_hash); \
data->d_buf = new_hash; \
data->d_size = nent * sizeof new_hash[0]; \
}
@@ -514,6 +547,7 @@ adjust_relocs (Elf_Scn *outscn, Elf_Scn *inscn, const GElf_Shdr *shdr,
ELF_CHECK (v != NULL, _("cannot get symbol version: %s"));
}
+ record_new_data (versym);
data->d_buf = versym;
data->d_size = nent * shdr->sh_entsize;
elf_flagdata (data, ELF_C_SET, ELF_F_DIRTY);
@@ -571,6 +605,7 @@ add_new_section_symbols (Elf_Scn *old_symscn, size_t old_shnum,
symdata->d_size = shdr->sh_size;
symdata->d_buf = xmalloc (symdata->d_size);
+ record_new_data (symdata->d_buf);
/* Copy the existing section symbols. */
Elf_Data *old_symdata = elf_getdata (old_symscn, NULL);
@@ -1762,6 +1797,7 @@ more sections in stripped file than debug file -- arguments reversed?"));
shdr->sh_size = symdata->d_size = (1 + nsym) * shdr->sh_entsize;
symdata->d_buf = xmalloc (symdata->d_size);
+ record_new_data (symdata->d_buf);
GElf_Sym sym;
memset (&sym, 0, sizeof sym);
@@ -1927,13 +1963,12 @@ more sections in stripped file than debug file -- arguments reversed?"));
free (strtab_data->d_buf);
}
- if (symdata != NULL)
- free (symdata->d_buf);
if (symstrtab != NULL)
{
ebl_strtabfree (symstrtab);
free (symstrdata->d_buf);
}
+ free_new_data ();
}
/* Process one pair of files, already opened. */
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 69a25fc6..b8d1d954 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,31 @@
+2015-12-08 Jose E. Marchesi <jose.marchesi@oracle.com>
+
+ * run-backtrace-core-sparc.sh: New file.
+ * backtrace.sparc.core.bz2: New file.
+ * backtrace.sparc.exec.bz2: New file.
+ * Makefile.am (EXTRA_DIST): ... and added all here.
+ (TESTS): Added run-backtrace-core-sparc.sh.
+
+2015-12-02 Mark Wielaard <mjw@redhat.com>
+
+ * Makefile.am (valgrind_cmd): Use --leak-check=full.
+ * run-backtrace-demangle.sh: Disable valgrind.
+ * run-stack-demangled-test.sh: Likewise.
+ * run-stack-d-test.sh: Likewise.
+ * run-stack-i-test.sh: Likewise.
+
+2015-12-01 Mark Wielaard <mjw@redhat.com>
+
+ * test-flag-nobits.c (main): Call elf_end.
+ * rerequest_tag.c (main): Call dwarf_end.
+ * funcscopes.c (handle_function): Free scopes.
+ * dwarf-getstring.c (main): Call dwarf_end.
+ * allregs.c (main): Free state.info.
+ * alldts.c (main): Free dyn.
+ * addrcfi.c (handle_address): Free stuff.frame between handle_cfi
+ calls.
+ * addrscopes.c (handle_address): Free scopes.
+
2015-10-16 Mark Wielaard <mjw@redhat.com>
* Makefile.am [BUILD_STATIC] (libdw): Add -lz.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 30cf137c..8fca8014 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -114,7 +114,7 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
run-backtrace-core-x32.sh \
run-backtrace-core-i386.sh run-backtrace-core-ppc.sh \
run-backtrace-core-s390x.sh run-backtrace-core-s390.sh \
- run-backtrace-core-aarch64.sh \
+ run-backtrace-core-aarch64.sh run-backtrace-core-sparc.sh \
run-backtrace-demangle.sh run-stack-d-test.sh run-stack-i-test.sh \
run-stack-demangled-test.sh \
run-readelf-dwz-multi.sh run-allfcts-multi.sh run-deleted.sh \
@@ -287,6 +287,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
run-backtrace-core-s390x.sh run-backtrace-core-s390.sh \
run-backtrace-core-aarch64.sh \
backtrace.aarch64.core.bz2 backtrace.aarch64.exec.bz2 \
+ run-backtrace-core-sparc.sh \
+ backtrace.sparc.core.bz2 backtrace.sparc.exec.bz2 \
run-backtrace-demangle.sh testfile-backtrace-demangle.bz2 \
testfile-backtrace-demangle.cc \
testfile-backtrace-demangle.core.bz2 \
@@ -307,7 +309,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
run-lfs-symbols.sh lfs-symbols testfile-nolfs.bz2
if USE_VALGRIND
-valgrind_cmd='valgrind -q --error-exitcode=1 --run-libc-freeres=no'
+valgrind_cmd='valgrind -q --leak-check=full --error-exitcode=1'
endif
diff --git a/tests/addrcfi.c b/tests/addrcfi.c
index eb950c0b..589b8513 100644
--- a/tests/addrcfi.c
+++ b/tests/addrcfi.c
@@ -160,10 +160,19 @@ handle_address (GElf_Addr pc, Dwfl *dwfl)
Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc);
struct stuff stuff;
- return (handle_cfi (dwfl, ".eh_frame",
- dwfl_module_eh_cfi (mod, &stuff.bias), pc, &stuff)
- & handle_cfi (dwfl, ".debug_frame",
- dwfl_module_dwarf_cfi (mod, &stuff.bias), pc, &stuff));
+ stuff.frame = NULL;
+ stuff.bias = 0;
+ int res = handle_cfi (dwfl, ".eh_frame",
+ dwfl_module_eh_cfi (mod, &stuff.bias), pc, &stuff);
+ free (stuff.frame);
+
+ stuff.frame = NULL;
+ stuff.bias = 0;
+ res &= handle_cfi (dwfl, ".debug_frame",
+ dwfl_module_dwarf_cfi (mod, &stuff.bias), pc, &stuff);
+ free (stuff.frame);
+
+ return res;
}
int
diff --git a/tests/addrscopes.c b/tests/addrscopes.c
index 2285d2ca..791569f5 100644
--- a/tests/addrscopes.c
+++ b/tests/addrscopes.c
@@ -134,6 +134,7 @@ handle_address (GElf_Addr pc, Dwfl *dwfl)
print_vars (indent + INDENT, die);
}
+ free (scopes);
}
}
diff --git a/tests/alldts.c b/tests/alldts.c
index c39b8fb6..eaecaf57 100644
--- a/tests/alldts.c
+++ b/tests/alldts.c
@@ -256,6 +256,9 @@ main (void)
/* And the data allocated in the .shstrtab section. */
free (data->d_buf);
+ /* And the dynamic entries. */
+ free (dyn);
+
/* All done. */
if (elf_end (elf) != 0)
{
diff --git a/tests/allregs.c b/tests/allregs.c
index 901d4e88..286f7e3c 100644
--- a/tests/allregs.c
+++ b/tests/allregs.c
@@ -184,6 +184,7 @@ main (int argc, char **argv)
dwarf_encoding_string (state.info[i].type),
state.info[i].bits);
}
+ free (state.info);
}
else
do
diff --git a/tests/backtrace.sparc.core.bz2 b/tests/backtrace.sparc.core.bz2
new file mode 100644
index 00000000..ad37f75c
--- /dev/null
+++ b/tests/backtrace.sparc.core.bz2
Binary files differ
diff --git a/tests/backtrace.sparc.exec.bz2 b/tests/backtrace.sparc.exec.bz2
new file mode 100755
index 00000000..b049ec50
--- /dev/null
+++ b/tests/backtrace.sparc.exec.bz2
Binary files differ
diff --git a/tests/dwarf-getstring.c b/tests/dwarf-getstring.c
index 824edef8..ffa3e375 100644
--- a/tests/dwarf-getstring.c
+++ b/tests/dwarf-getstring.c
@@ -70,6 +70,7 @@ main (int argc, char *argv[])
offset += len + 1;
}
+ dwarf_end (dbg);
close (fd);
}
diff --git a/tests/funcscopes.c b/tests/funcscopes.c
index 55cb4fac..9c901858 100644
--- a/tests/funcscopes.c
+++ b/tests/funcscopes.c
@@ -162,6 +162,7 @@ handle_function (Dwarf_Die *funcdie, void *arg)
print_vars (indent + INDENT, die);
}
+ free (scopes);
}
return 0;
diff --git a/tests/rerequest_tag.c b/tests/rerequest_tag.c
index d0bf5f24..b4d46271 100644
--- a/tests/rerequest_tag.c
+++ b/tests/rerequest_tag.c
@@ -42,5 +42,6 @@ main (int argc, char **argv)
assert (die == &die_mem);
assert (dwarf_tag (die) == 0);
+ dwarf_end (dw);
return 0;
}
diff --git a/tests/run-backtrace-core-sparc.sh b/tests/run-backtrace-core-sparc.sh
new file mode 100755
index 00000000..60399baa
--- /dev/null
+++ b/tests/run-backtrace-core-sparc.sh
@@ -0,0 +1,20 @@
+#! /bin/bash
+# Copyright (C) 2015 Oracle, 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/backtrace-subr.sh
+
+check_core sparc
diff --git a/tests/run-backtrace-demangle.sh b/tests/run-backtrace-demangle.sh
index 71a73518..2d253240 100755
--- a/tests/run-backtrace-demangle.sh
+++ b/tests/run-backtrace-demangle.sh
@@ -26,6 +26,11 @@ child=testfile-backtrace-demangle
testfiles $child{,.core}
tempfiles $child.{bt,err}
+# Disable valgrind while dumping because of a bug unmapping libc.so.
+# https://bugs.kde.org/show_bug.cgi?id=327427
+SAVED_VALGRIND_CMD="$VALGRIND_CMD"
+unset VALGRIND_CMD
+
# There can be more than 3 frames, but depending on the system/installed
# glibc we might not be able to unwind fully till the end.
# cxxfunc -> f -> main
@@ -33,6 +38,12 @@ tempfiles $child.{bt,err}
# (exit code 1)
testrun ${abs_top_builddir}/src/stack -n 2 -e $child --core $child.core >$child.bt 2>$child.err || exitcode=$?
cat $child.{bt,err}
+
+if [ "x$SAVED_VALGRIND_CMD" != "x" ]; then
+ VALGRIND_CMD="$SAVED_VALGRIND_CMD"
+ export VALGRIND_CMD
+fi
+
if test $exitcode != 1 || ! grep "shown max number of frames" $child.err; then
echo >&2 $2: expected more than 2 frames
false
diff --git a/tests/run-stack-d-test.sh b/tests/run-stack-d-test.sh
index 41982014..a9f03806 100755
--- a/tests/run-stack-d-test.sh
+++ b/tests/run-stack-d-test.sh
@@ -68,6 +68,11 @@ else
STACKCMD=${abs_top_builddir}/src/stack
fi
+# Disable valgrind while dumping because of a bug unmapping libc.so.
+# https://bugs.kde.org/show_bug.cgi?id=327427
+SAVED_VALGRIND_CMD="$VALGRIND_CMD"
+unset VALGRIND_CMD
+
# Without -d the top function comes out as fu. Use --raw to not demangle.
testrun_compare ${abs_top_builddir}/src/stack -r -n 2 -e testfiledwarfinlines --core testfiledwarfinlines.core<<EOF
PID 13654 - core
@@ -109,4 +114,9 @@ TID 13654:
$STACKCMD: tid 13654: shown max number of frames (2, use -n 0 for unlimited)
EOF
+if [ "x$SAVED_VALGRIND_CMD" != "x" ]; then
+ VALGRIND_CMD="$SAVED_VALGRIND_CMD"
+ export VALGRIND_CMD
+fi
+
exit 0
diff --git a/tests/run-stack-demangled-test.sh b/tests/run-stack-demangled-test.sh
index 98f8ff8e..c26918f9 100755
--- a/tests/run-stack-demangled-test.sh
+++ b/tests/run-stack-demangled-test.sh
@@ -37,6 +37,11 @@ else
STACKCMD=${abs_top_builddir}/src/stack
fi
+# Disable valgrind while dumping because of a bug unmapping libc.so.
+# https://bugs.kde.org/show_bug.cgi?id=327427
+SAVED_VALGRIND_CMD="$VALGRIND_CMD"
+unset VALGRIND_CMD
+
# Without -d the top function comes out as fu.
testrun_compare ${abs_top_builddir}/src/stack -n 2 -e testfiledwarfinlines --core testfiledwarfinlines.core<<EOF
PID 13654 - core
@@ -92,4 +97,9 @@ TID 13654:
$STACKCMD: tid 13654: shown max number of frames (6, use -n 0 for unlimited)
EOF
+if [ "x$SAVED_VALGRIND_CMD" != "x" ]; then
+ VALGRIND_CMD="$SAVED_VALGRIND_CMD"
+ export VALGRIND_CMD
+fi
+
exit 0
diff --git a/tests/run-stack-i-test.sh b/tests/run-stack-i-test.sh
index ffab85f9..3722ab09 100755
--- a/tests/run-stack-i-test.sh
+++ b/tests/run-stack-i-test.sh
@@ -29,6 +29,11 @@ else
STACKCMD=${abs_top_builddir}/src/stack
fi
+# Disable valgrind while dumping because of a bug unmapping libc.so.
+# https://bugs.kde.org/show_bug.cgi?id=327427
+SAVED_VALGRIND_CMD="$VALGRIND_CMD"
+unset VALGRIND_CMD
+
# Compare with run-stack-d-test.sh to see the output without --inlines.
# Only two call frames are visible (there is a jump from main to fu or
# fubar). Explicitly use --raw so demangler support being configured in
@@ -68,4 +73,9 @@ TID 13654:
$STACKCMD: tid 13654: shown max number of frames (6, use -n 0 for unlimited)
EOF
+if [ "x$SAVED_VALGRIND_CMD" != "x" ]; then
+ VALGRIND_CMD="$SAVED_VALGRIND_CMD"
+ export VALGRIND_CMD
+fi
+
exit 0
diff --git a/tests/test-flag-nobits.c b/tests/test-flag-nobits.c
index ff19ce20..15d44ea8 100644
--- a/tests/test-flag-nobits.c
+++ b/tests/test-flag-nobits.c
@@ -36,4 +36,7 @@ main (int argc, char **argv)
Elf_Scn *scn = NULL;
while ((scn = elf_nextscn (stripped, scn)) != NULL)
elf_flagdata (elf_getdata (scn, NULL), ELF_C_SET, ELF_F_DIRTY);
+
+ elf_end (stripped);
+ return 0;
}