[PATCH] Note that .rela.debug* sections are debuginfo sections.
by Nick Alcock
Without this fix, strip -g misbehaves when stripping object files
and other ELF objects containing debuginfo relocation sections,
preserving all debuginfo sections in the output file because the
.rela.debug* sections reference them.
Signed-off-by: Nick Alcock <nick.alcock(a)oracle.com>
---
The selection of sections to RELAFY here is pretty much arbitrary,
covering everything I've seen .rela.* created for and anything else that
seems likely to need it.
Testing is trivial: try to strip -g a Linux kernel module (or any other
object file containing debug info) and observe the .debug* and
.rela.debug* sections turning up in the stripped output rather than (if
specified) the -f file. binutils behaves correctly in this area.
I haven't considered DWARF 1 sections not shared with DWARF 2+ at all
because I have no way to generate such sections for testing.
libebl/ChangeLog | 5 +++++
libebl/eblopenbackend.c | 36 +++++++++++++++++++-----------------
2 files changed, 24 insertions(+), 17 deletions(-)
diff --git a/libebl/ChangeLog b/libebl/ChangeLog
index af819a2..c6204c6 100644
--- a/libebl/ChangeLog
+++ b/libebl/ChangeLog
@@ -1,3 +1,8 @@
+2012-09-18 Nick Alcock <nick.alcock(a)oracle.com>
+
+ * eblopenbackend.c (default_debugscn_p): Add .rela.* versions of
+ many DWARF 2+ debuginfo sections.
+
2012-08-22 Jeff Kenton <jkenton(a)tilera.com>
* eblopenbackend.c (machines): Add tilegx.
diff --git a/libebl/eblopenbackend.c b/libebl/eblopenbackend.c
index 89e5da5..d10b0d0 100644
--- a/libebl/eblopenbackend.c
+++ b/libebl/eblopenbackend.c
@@ -619,6 +619,7 @@ default_object_note (const char *name __attribute__ ((unused)),
static bool
default_debugscn_p (const char *name)
{
+#define RELAFY(x) x, ".rela" x
/* We know by default only about the DWARF debug sections which have
fixed names. */
static const char *dwarf_scn_names[] =
@@ -630,31 +631,32 @@ default_debugscn_p (const char *name)
".debug_srcinfo",
".debug_sfnames",
/* DWARF 1.1 and DWARF 2 */
- ".debug_aranges",
- ".debug_pubnames",
+ RELAFY(".debug_aranges"),
+ RELAFY(".debug_pubnames"),
/* DWARF 2 */
- ".debug_info",
- ".debug_abbrev",
- ".debug_line",
- ".debug_frame",
- ".debug_str",
- ".debug_loc",
- ".debug_macinfo",
+ RELAFY(".debug_info"),
+ RELAFY(".debug_abbrev"),
+ RELAFY(".debug_line"),
+ RELAFY(".debug_frame"),
+ RELAFY(".debug_str"),
+ RELAFY(".debug_loc"),
+ RELAFY(".debug_macinfo"),
/* DWARF 3 */
- ".debug_ranges",
- ".debug_pubtypes",
+ RELAFY(".debug_ranges"),
+ RELAFY(".debug_pubtypes"),
/* DWARF 4 */
- ".debug_types",
+ RELAFY(".debug_types"),
/* GDB DWARF 4 extension */
".gdb_index",
/* GNU/DWARF 5 extension/proposal */
- ".debug_macro",
+ RELAFY(".debug_macro"),
/* SGI/MIPS DWARF 2 extensions */
- ".debug_weaknames",
- ".debug_funcnames",
- ".debug_typenames",
- ".debug_varnames"
+ RELAFY(".debug_weaknames"),
+ RELAFY(".debug_funcnames"),
+ RELAFY(".debug_typenames"),
+ RELAFY(".debug_varnames")
};
+#undef RELAFY
const size_t ndwarf_scn_names = (sizeof (dwarf_scn_names)
/ sizeof (dwarf_scn_names[0]));
for (size_t cnt = 0; cnt < ndwarf_scn_names; ++cnt)
--
1.7.12.156.gd7c4257
--
NULL && (void)
10 years
[patch 5/4] unwinder: non-x86* parts of the unwinder [resent]
by Jan Kratochvil
Hi,
dependent on:
[patch 4/4] unwinder: The unwinder (x86* only)
present in:
jankratochvil/forunwind-baseaddr-hookvars-corereport-unwindx86-nonx86
but this branch is not suitable for merge as it is unclear in which form get
checked in the patches above. The branch is usable only for "git checkout",
neither diff nor merge should be done on it.
ppc* and s390* extensions with core files to test them.
The testfiles are present only in the GIT branch, not in the diff below.
Thanks,
Jan
backends/
2012-11-14 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* Makefile.am (ppc_SRCS, ppc64_SRCS): Add ppc_frame_state.c and
ppc_cfi.c.
(s390_SRCS): Add s390_cfi.c, s390_frame_state.c and
s390_frame_unwind.c.
(noinst_HEADERS): Add core-get-pc.c.
* core-get-pc.c: New file.
* ppc64_init.c (ppc64_init): Initialize frame_state_nregs, frame_state,
abi_cfi, frame_dwarf_to_regno, get_func_pc and destr.
* ppc_cfi.c: New file.
* ppc_frame_state.c: New file.
* ppc_init.c (ppc_init): Initialize frame_state_nregs, frame_state,
abi_cfi and frame_dwarf_to_regno.
* s390_cfi.c: New file.
* s390_frame_state.c: New file.
* s390_frame_unwind.c: New file.
* s390_init.c (s390_init): Initialize abi_cfi, frame_state_nregs,
frame_state, optionally normalize_pc and also frame_unwind.
libdwfl/
2012-11-14 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* dwfl_frame_state_pc.c (dwfl_frame_state_pc): Call ebl_normalize_pc.
* dwfl_frame_unwind.c (handle_cfi): Call ebl_frame_dwarf_to_regno.
(dwfl_frame_unwind): Reinitialzie *STATEP, call ebl_frame_unwind,
handle its return value.
* libdwflP.h (dwfl_frame_state_reg_get, dwfl_frame_state_reg_set): Call
frame_dwarf_to_regno.
libebl/
2012-11-14 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* Makefile.am (gen_SOURCES): Add eblnormalizepc.c, eblframeunwind.c and
eblframedwarftoregno.c.
* ebl-hooks.h (frame_dwarf_to_regno, normalize_pc, frame_unwind): New
entries.
* eblframedwarftoregno.c: New file.
* eblframeunwind.c: New file.
* eblnormalizepc.c: New file.
* libebl.h (ebl_normalize_pc, struct Dwfl_Frame_State_Process)
(ebl_frame_unwind, ebl_frame_dwarf_to_regno): New declarations.
tests/
2012-11-14 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* backtrace.ppc.core.bz2: New file.
* backtrace.ppc.exec.bz2: New file.
* backtrace.ppc64.core.bz2: New file.
* backtrace.ppc64.exec.bz2: New file.
* backtrace.s390.core.bz2: New file.
* backtrace.s390.exec.bz2: New file.
* backtrace.s390x.core.bz2: New file.
* backtrace.s390x.exec.bz2: New file.
* run-backtrace.sh: Add tests for the core files above.
diff --git a/backends/Makefile.am b/backends/Makefile.am
index 6f99758..ad909da 100644
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -87,18 +87,20 @@ libebl_sparc_pic_a_SOURCES = $(sparc_SRCS)
am_libebl_sparc_pic_a_OBJECTS = $(sparc_SRCS:.c=.os)
ppc_SRCS = ppc_init.c ppc_symbol.c ppc_retval.c ppc_regs.c \
- ppc_corenote.c ppc_auxv.c ppc_attrs.c ppc_syscall.c
+ ppc_corenote.c ppc_auxv.c ppc_attrs.c ppc_syscall.c \
+ ppc_frame_state.c ppc_cfi.c
libebl_ppc_pic_a_SOURCES = $(ppc_SRCS)
am_libebl_ppc_pic_a_OBJECTS = $(ppc_SRCS:.c=.os)
ppc64_SRCS = ppc64_init.c ppc64_symbol.c ppc64_retval.c \
ppc64_corenote.c ppc_regs.c ppc_auxv.c ppc_attrs.c ppc_syscall.c \
- ppc64_get_func_pc.c
+ ppc64_get_func_pc.c ppc_frame_state.c ppc_cfi.c
libebl_ppc64_pic_a_SOURCES = $(ppc64_SRCS)
am_libebl_ppc64_pic_a_OBJECTS = $(ppc64_SRCS:.c=.os)
s390_SRCS = s390_init.c s390_symbol.c s390_regs.c s390_retval.c \
- s390_corenote.c s390x_corenote.c
+ s390_corenote.c s390x_corenote.c s390_cfi.c s390_frame_state.c \
+ s390_frame_unwind.c
libebl_s390_pic_a_SOURCES = $(s390_SRCS)
am_libebl_s390_pic_a_OBJECTS = $(s390_SRCS:.c=.os)
@@ -136,7 +138,8 @@ uninstall: uninstall-am
done
rmdir --ignore-fail-on-non-empty $(DESTDIR)$(libdir)/$(LIBEBL_SUBDIR)
-noinst_HEADERS = libebl_CPU.h common-reloc.c linux-core-note.c x86_corenote.c
+noinst_HEADERS = libebl_CPU.h common-reloc.c linux-core-note.c x86_corenote.c \
+ core-get-pc.c
EXTRA_DIST = $(foreach m,$(modules),$($(m)_SRCS)) $(modules:=_reloc.def)
CLEANFILES += $(foreach m,$(modules),\
diff --git a/backends/core-get-pc.c b/backends/core-get-pc.c
new file mode 100644
index 0000000..4d1b92e
--- /dev/null
+++ b/backends/core-get-pc.c
@@ -0,0 +1,86 @@
+/* Common core note PC address extraction.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+
+static bool
+core_get_pc (Elf *core, Dwarf_Addr *core_pc, unsigned pc_offset)
+{
+ size_t phnum;
+ if (elf_getphdrnum (core, &phnum) < 0)
+ return NULL;
+ unsigned bits = gelf_getclass (core) == ELFCLASS32 ? 32 : 64;
+ for (size_t cnt = 0; cnt < phnum; ++cnt)
+ {
+ GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
+ if (phdr == NULL || phdr->p_type != PT_NOTE)
+ continue;
+ Elf_Data *data = elf_getdata_rawchunk (core, phdr->p_offset,
+ phdr->p_filesz, ELF_T_NHDR);
+ if (data == NULL)
+ return NULL;
+ size_t offset = 0;
+ GElf_Nhdr nhdr;
+ size_t name_offset;
+ size_t desc_offset;
+ while (offset < data->d_size
+ && (offset = gelf_getnote (data, offset,
+ &nhdr, &name_offset, &desc_offset)) > 0)
+ {
+ if (nhdr.n_type != NT_PRSTATUS)
+ continue;
+ const char *reg_desc = data->d_buf + desc_offset + pc_offset;
+ if (reg_desc + bits / 8 > (const char *) data->d_buf + nhdr.n_descsz)
+ continue;
+ Dwarf_Addr val;
+ switch (bits)
+ {
+ case 32:;
+ uint32_t val32 = *(const uint32_t *) reg_desc;
+ reg_desc += sizeof val32;
+ val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be32toh (val32) : le32toh (val32));
+ /* Do a host width conversion. */
+ val = val32;
+ break;
+ case 64:;
+ uint64_t val64 = *(const uint64_t *) reg_desc;
+ reg_desc += sizeof val64;
+ val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be64toh (val64) : le64toh (val64));
+ val = val64;
+ break;
+ default:
+ abort ();
+ }
+ *core_pc = val;
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/backends/ppc64_init.c b/backends/ppc64_init.c
index da7d02c..32ca876 100644
--- a/backends/ppc64_init.c
+++ b/backends/ppc64_init.c
@@ -66,6 +66,13 @@ ppc64_init (elf, machine, eh, ehlen)
HOOK (eh, auxv_info);
HOOK (eh, get_func_pc);
HOOK (eh, destr);
+ /* gcc/config/ #define DWARF_FRAME_REGISTERS. */
+ eh->frame_state_nregs = (114 - 1) + 32;
+ HOOK (eh, frame_state);
+ HOOK (eh, abi_cfi);
+ HOOK (eh, frame_dwarf_to_regno);
+ HOOK (eh, get_func_pc);
+ HOOK (eh, destr);
return MODVERSION;
}
diff --git a/backends/ppc_cfi.c b/backends/ppc_cfi.c
new file mode 100644
index 0000000..2c7091f
--- /dev/null
+++ b/backends/ppc_cfi.c
@@ -0,0 +1,77 @@
+/* ppc ABI-specified defaults for DWARF CFI.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <dwarf.h>
+
+#define BACKEND ppc_
+#include "libebl_CPU.h"
+
+int
+ppc_abi_cfi (Ebl *ebl __attribute__ ((unused)), Dwarf_CIE *abi_info)
+{
+ static const uint8_t abi_cfi[] =
+ {
+ /* This instruction is provided in every CIE. It is not repeated here:
+ DW_CFA_def_cfa, ULEB128_7 (1), ULEB128_7 (0) */
+ /* r1 is assumed to be restored from cfa adress,
+ r1 acts as a stack frame pointer. */
+ DW_CFA_val_expression, ULEB128_7 (1), ULEB128_7 (1), DW_OP_nop,
+ /* lr is not callee-saved but it needs to be preserved as it is pre-set
+ by the caller. */
+ DW_CFA_same_value, ULEB128_7 (65), /* lr */
+
+ /* Callee-saved regs. */
+#define SV(n) DW_CFA_same_value, ULEB128_7 (n)
+ SV (2), /* r2 is TOC pointer. */
+ SV (13), /* Reserved as system thread id (is it for CFI?). */
+ /* r14-r31 are non-volatile registers. */
+ SV (14), SV (15), SV (16), SV (17), SV (18), SV (19), SV (20), SV (21),
+ SV (22), SV (23), SV (24), SV (25), SV (26), SV (27), SV (28), SV (29),
+ SV (30), SV (31)
+ /* VMX registers v20-v31 and vrsave are non-volatile but they are
+ assigned DWARF registers 1144-1156 (v20-v31) which is outside of the
+ CFI supported range. */
+#undef SV
+ };
+
+ abi_info->initial_instructions = abi_cfi;
+ abi_info->initial_instructions_end = &abi_cfi[sizeof abi_cfi];
+ abi_info->data_alignment_factor = ebl->class == ELFCLASS64 ? 8 : 4;
+
+ abi_info->return_address_register = 65;
+
+ return 0;
+}
+
+__typeof (ppc_abi_cfi)
+ ppc64_abi_cfi
+ __attribute__ ((alias ("ppc_abi_cfi")));
diff --git a/backends/ppc_frame_state.c b/backends/ppc_frame_state.c
new file mode 100644
index 0000000..0965e23
--- /dev/null
+++ b/backends/ppc_frame_state.c
@@ -0,0 +1,122 @@
+/* Fetch live process Dwfl_Frame_State from PID.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef __powerpc__
+# include <sys/user.h>
+# include <sys/ptrace.h>
+#endif
+#include "libdwflP.h"
+
+#define BACKEND ppc_
+#include "libebl_CPU.h"
+
+#include "core-get-pc.c"
+
+bool
+ppc_frame_dwarf_to_regno (Ebl *ebl __attribute__ ((unused)), unsigned *regno)
+{
+ switch (*regno)
+ {
+ case 108:
+ *regno = 65;
+ return true;
+ case 0 ... 107:
+ case 109 ... (114 - 1) -1:
+ return true;
+ case 1200 ... 1231:
+ *regno = *regno - 1200 + (114 - 1);
+ return true;
+ default:
+ return false;
+ }
+ abort ();
+}
+
+__typeof (ppc_frame_dwarf_to_regno)
+ ppc64_frame_dwarf_to_regno
+ __attribute__ ((alias ("ppc_frame_dwarf_to_regno")));
+
+bool
+ppc_frame_state (Dwfl_Frame_State *state)
+{
+ Dwfl_Frame_State_Thread *thread = state->thread;
+ Dwfl_Frame_State_Process *process = thread->process;
+ Ebl *ebl = process->ebl;
+ Elf *core = process->core;
+ pid_t tid = thread->tid;
+ if (core == NULL && tid)
+ {
+#ifndef __powerpc__
+ return false;
+#else /* __powerpc__ */
+ union
+ {
+ struct pt_regs r;
+ long l[sizeof (struct pt_regs) / sizeof (long)];
+ }
+ user_regs;
+ eu_static_assert (sizeof (struct pt_regs) % sizeof (long) == 0);
+ /* PTRACE_GETREGS is EIO on kernel-2.6.18-308.el5.ppc64. */
+ errno = 0;
+ for (unsigned regno = 0; regno < sizeof (user_regs) / sizeof (long);
+ regno++)
+ {
+ user_regs.l[regno] = ptrace (PTRACE_PEEKUSER, tid,
+ (void *) (uintptr_t) (regno
+ * sizeof (long)),
+ NULL);
+ if (errno != 0)
+ return false;
+ }
+ for (unsigned gpr = 0;
+ gpr < sizeof (user_regs.r.gpr) / sizeof (*user_regs.r.gpr); gpr++)
+ dwfl_frame_state_reg_set (state, gpr, user_regs.r.gpr[gpr]);
+ state->pc = user_regs.r.nip;
+ state->pc_state = DWFL_FRAME_STATE_PC_SET;
+ dwfl_frame_state_reg_set (state, 65, user_regs.r.link); // or 108
+ /* Registers like msr, ctr, xer, dar, dsisr etc. are probably irrelevant
+ for CFI. */
+#endif /* __powerpc__ */
+ }
+ else if (core)
+ {
+ if (! core_get_pc (core, &state->pc,
+ ebl->class == ELFCLASS64 ? 0x170 : 0xc8))
+ return false;
+ state->pc_state = DWFL_FRAME_STATE_PC_SET;
+ }
+ return true;
+}
+
+__typeof (ppc_frame_state)
+ ppc64_frame_state
+ __attribute__ ((alias ("ppc_frame_state")));
diff --git a/backends/ppc_init.c b/backends/ppc_init.c
index 6054007..87864ae 100644
--- a/backends/ppc_init.c
+++ b/backends/ppc_init.c
@@ -64,6 +64,11 @@ ppc_init (elf, machine, eh, ehlen)
HOOK (eh, core_note);
HOOK (eh, auxv_info);
HOOK (eh, check_object_attribute);
+ /* gcc/config/ #define DWARF_FRAME_REGISTERS. */
+ eh->frame_state_nregs = (114 - 1) + 32;
+ HOOK (eh, frame_state);
+ HOOK (eh, abi_cfi);
+ HOOK (eh, frame_dwarf_to_regno);
return MODVERSION;
}
diff --git a/backends/s390_cfi.c b/backends/s390_cfi.c
new file mode 100644
index 0000000..66a79ff
--- /dev/null
+++ b/backends/s390_cfi.c
@@ -0,0 +1,65 @@
+/* s390 ABI-specified defaults for DWARF CFI.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <dwarf.h>
+
+#define BACKEND s390_
+#include "libebl_CPU.h"
+
+int
+s390_abi_cfi (Ebl *ebl, Dwarf_CIE *abi_info)
+{
+ static const uint8_t abi_cfi[] =
+ {
+ /* This instruction is provided in every CIE. It is not repeated here:
+ DW_CFA_def_cfa, ULEB128_7 (15), ULEB128_7 (96) */
+ /* r14 is not callee-saved but it needs to be preserved as it is pre-set
+ by the caller. */
+ DW_CFA_same_value, ULEB128_7 (14), /* r14 */
+
+ /* Callee-saved regs. */
+#define SV(n) DW_CFA_same_value, ULEB128_7 (n)
+ SV (6), SV (7), SV (8), SV (9), SV (10), /* r6-r13, r15 */
+ SV (11), SV (12), SV (13), SV (15),
+ SV (16 + 8), SV (16 + 9), SV (16 + 10), SV (16 + 11), /* f8-f15 */
+ SV (16 + 12), SV (16 + 13), SV (16 + 14), SV (16 + 15)
+#undef SV
+ };
+
+ abi_info->initial_instructions = abi_cfi;
+ abi_info->initial_instructions_end = &abi_cfi[sizeof abi_cfi];
+ abi_info->data_alignment_factor = ebl->class == ELFCLASS64 ? 8 : 4;
+
+ abi_info->return_address_register = 14;
+
+ return 0;
+}
diff --git a/backends/s390_frame_state.c b/backends/s390_frame_state.c
new file mode 100644
index 0000000..ddb15aa
--- /dev/null
+++ b/backends/s390_frame_state.c
@@ -0,0 +1,99 @@
+/* Fetch live process Dwfl_Frame_State from PID.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef __s390__
+# include <sys/user.h>
+# include <asm/ptrace.h>
+# include <sys/ptrace.h>
+#endif
+#include "libdwflP.h"
+
+#define BACKEND s390_
+#include "libebl_CPU.h"
+
+#include "core-get-pc.c"
+
+bool
+s390_frame_state (Dwfl_Frame_State *state)
+{
+ Dwfl_Frame_State_Thread *thread = state->thread;
+ Dwfl_Frame_State_Process *process = thread->process;
+ Ebl *ebl = process->ebl;
+ Elf *core = process->core;
+ pid_t tid = thread->tid;
+ if (core == NULL && tid)
+ {
+#ifndef __s390__
+ return false;
+#else /* __s390__ */
+ struct user user_regs;
+ ptrace_area parea;
+ parea.process_addr = (uintptr_t) &user_regs;
+ parea.kernel_addr = 0;
+ parea.len = sizeof (user_regs);
+ if (ptrace (PTRACE_PEEKUSR_AREA, tid, &parea, NULL) != 0)
+ return false;
+ /* If we run as s390x we get the 64-bit registers of tid.
+ But -m31 executable seems to use only the 32-bit parts of its
+ registers so we ignore the upper half. */
+ for (unsigned u = 0; u < 16; u++)
+ dwarf_frame_state_reg_set (state, 0 + u, user_regs.regs.gprs[u]);
+ /* Avoid conversion double -> integer. */
+ eu_static_assert (sizeof user_regs.regs.fp_regs.fprs[0]
+ == sizeof state->regs[0]);
+ for (unsigned u = 0; u < 16; u++)
+ dwarf_frame_state_reg_set (state, 16 + u,
+ *((const __typeof (*state->regs) *)
+ &user_regs.regs.fp_regs.fprs[u]));
+ state->pc = user_regs.regs.psw.addr;
+ state->pc_state = DWFL_FRAME_STATE_PC_SET;
+#endif /* __s390__ */
+ }
+ else if (core)
+ {
+ /* Fetch PSWA. */
+ if (! core_get_pc (core, &state->pc,
+ ebl->class == ELFCLASS32 ? 0x4c : 0x78))
+ return false;
+ state->pc_state = DWFL_FRAME_STATE_PC_SET;
+ }
+ return true;
+}
+
+void
+s390_normalize_pc (Ebl *ebl __attribute__ ((unused)), Dwarf_Addr *pc)
+{
+ assert (ebl->class == ELFCLASS32);
+
+ /* Clear S390 bit 31. */
+ *pc &= (1U << 31) - 1;
+}
diff --git a/backends/s390_frame_unwind.c b/backends/s390_frame_unwind.c
new file mode 100644
index 0000000..fa4c0ac
--- /dev/null
+++ b/backends/s390_frame_unwind.c
@@ -0,0 +1,149 @@
+/* Get previous frame state for an existing frame state.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <assert.h>
+#include "libdwflP.h"
+
+#define BACKEND s390_
+#include "libebl_CPU.h"
+
+bool
+s390_frame_unwind (Ebl *ebl, Dwfl_Frame_State **statep, Dwarf_Addr pc)
+{
+ Dwfl_Frame_State *state = *statep;
+ Dwfl_Frame_State_Process *process = state->thread->process;
+ assert (state->unwound == NULL);
+ /* Caller already assumed caller adjustment but S390 instructions are 4 bytes
+ long. Undo it. */
+ if ((pc & 0x3) != 0x3)
+ return false;
+ pc++;
+ /* We can assume big-endian read here. */
+ Dwarf_Addr instr;
+ if (! process->memory_read (pc, &instr, process->memory_read_user_data))
+ return false;
+ /* Fetch only the very first two bytes. */
+ instr = (instr >> (ebl->class == ELFCLASS64 ? 48 : 16)) & 0xffff;
+ /* See GDB s390_sigtramp_frame_sniffer. */
+ /* Check for 'svc'. */
+ if (((instr >> 8) & 0xff) != 0x0a)
+ return false;
+ /* Check for 'sigreturn' or 'rt_sigreturn'. */
+ if ((instr & 0xff) != 119 && (instr & 0xff) != 173)
+ return false;
+ /* See GDB s390_sigtramp_frame_unwind_cache. */
+# define S390_SP_REGNUM (0 + 15) /* S390_R15_REGNUM */
+ Dwarf_Addr this_sp;
+ if (! dwfl_frame_state_reg_get (state, S390_SP_REGNUM, &this_sp))
+ return false;
+ unsigned word_size = ebl->class == ELFCLASS64 ? 8 : 4;
+ Dwarf_Addr next_cfa = this_sp + 16 * word_size + 32;
+ /* "New-style RT frame" is not supported,
+ assuming "Old-style RT frame and all non-RT frames". */
+ Dwarf_Addr sigreg_ptr;
+ if (! process->memory_read (next_cfa + 8, &sigreg_ptr,
+ process->memory_read_user_data))
+ return false;
+ /* Skip PSW mask. */
+ sigreg_ptr += word_size;
+ /* Read PSW address. */
+ Dwarf_Addr val;
+ if (! process->memory_read (sigreg_ptr, &val, process->memory_read_user_data))
+ return false;
+ sigreg_ptr += word_size;
+ size_t nregs = ebl->frame_state_nregs;
+ Dwfl_Frame_State *unwound;
+ unwound = malloc (sizeof (*unwound) + sizeof (*unwound->regs) * nregs);
+ state->unwound = unwound;
+ unwound->thread = state->thread;
+ unwound->unwound = NULL;
+ unwound->pc = val;
+ unwound->pc_state = DWFL_FRAME_STATE_ERROR;
+ memset (unwound->regs_set, 0, sizeof (unwound->regs_set));
+ unwound->signal_frame = true;
+ /* Then the GPRs. */
+ for (int i = 0; i < 16; i++)
+ {
+ if (! process->memory_read (sigreg_ptr, &val,
+ process->memory_read_user_data))
+ return false;
+ if (! dwfl_frame_state_reg_set (unwound, 0 + i, val))
+ return false;
+ sigreg_ptr += word_size;
+ }
+ /* Then the ACRs. Skip them, they are not used in CFI. */
+ for (int i = 0; i < 16; i++)
+ sigreg_ptr += 4;
+ /* The floating-point control word. */
+ sigreg_ptr += 8;
+ /* And finally the FPRs. */
+ for (int i = 0; i < 16; i++)
+ {
+ if (! process->memory_read (sigreg_ptr, &val,
+ process->memory_read_user_data))
+ return false;
+ if (ebl->class == ELFCLASS32)
+ {
+ Dwarf_Addr val_low;
+ if (! process->memory_read (sigreg_ptr + 4, &val_low,
+ process->memory_read_user_data))
+ return false;
+ val = (val << 32) | val_low;
+ }
+ if (! dwfl_frame_state_reg_set (unwound, 16 + i, val))
+ return false;
+ sigreg_ptr += 8;
+ }
+ /* If we have them, the GPR upper halves are appended at the end. */
+ if (ebl->class == ELFCLASS32)
+ {
+ unsigned sigreg_high_off = 4;
+ sigreg_ptr += sigreg_high_off;
+ for (int i = 0; i < 16; i++)
+ {
+ if (! process->memory_read (sigreg_ptr, &val,
+ process->memory_read_user_data))
+ return false;
+ Dwarf_Addr val_low;
+ if (! dwfl_frame_state_reg_get (unwound, 0 + i, &val_low))
+ return false;
+ val = (val << 32) | val_low;
+ if (! dwfl_frame_state_reg_set (unwound, 0 + i, val))
+ return false;
+ sigreg_ptr += 4;
+ }
+ }
+ unwound->pc_state = DWFL_FRAME_STATE_PC_SET;
+ *statep = unwound;
+ return true;
+}
diff --git a/backends/s390_init.c b/backends/s390_init.c
index 91fe4b8..8c28fb8 100644
--- a/backends/s390_init.c
+++ b/backends/s390_init.c
@@ -61,6 +61,16 @@ s390_init (elf, machine, eh, ehlen)
eh->core_note = s390x_core_note;
else
HOOK (eh, core_note);
+ HOOK (eh, abi_cfi);
+ /* gcc/config/ #define DWARF_FRAME_REGISTERS 34.
+ But from the gcc/config/s390/s390.h "Register usage." comment it looks as
+ if #32 (Argument pointer) and #33 (Condition code) are not used for
+ unwinding. */
+ eh->frame_state_nregs = 32;
+ HOOK (eh, frame_state);
+ if (eh->class == ELFCLASS32)
+ HOOK (eh, normalize_pc);
+ HOOK (eh, frame_unwind);
/* Only the 64-bit format uses the incorrect hash table entry size. */
if (eh->class == ELFCLASS64)
diff --git a/libdwfl/dwfl_frame_state_pc.c b/libdwfl/dwfl_frame_state_pc.c
index 6e9928b..4d6fc8b 100644
--- a/libdwfl/dwfl_frame_state_pc.c
+++ b/libdwfl/dwfl_frame_state_pc.c
@@ -37,6 +37,7 @@ dwfl_frame_state_pc (Dwfl_Frame_State *state, Dwarf_Addr *pc, bool *minusone)
{
assert (state->pc_state == DWFL_FRAME_STATE_PC_SET);
*pc = state->pc;
+ ebl_normalize_pc (state->thread->process->ebl, pc);
if (minusone)
{
/* Bottom frame? */
diff --git a/libdwfl/dwfl_frame_unwind.c b/libdwfl/dwfl_frame_unwind.c
index 5705f51..e35ccb5 100644
--- a/libdwfl/dwfl_frame_unwind.c
+++ b/libdwfl/dwfl_frame_unwind.c
@@ -410,7 +410,7 @@ handle_cfi (Dwfl_Frame_State **statep, Dwarf_Addr pc, Dwfl_Module *mod,
{
/* REGNO is undefined. */
unsigned ra = frame->fde->cie->return_address_register;
- if (regno == ra)
+ if (ebl_frame_dwarf_to_regno (ebl, &ra) && regno == ra)
unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
continue;
}
@@ -494,6 +494,14 @@ dwfl_frame_unwind (Dwfl_Frame_State **statep)
}
}
}
+ *statep = state;
+ if (ebl_frame_unwind (state->thread->process->ebl, statep, pc))
+ return true;
+ if (state->unwound)
+ {
+ assert (state->unwound->pc_state == DWFL_FRAME_STATE_ERROR);
+ return false;
+ }
__libdwfl_seterrno (DWFL_E_NO_DWARF);
return false;
}
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index 3999d66..fb2f839 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -265,6 +265,9 @@ dwfl_frame_state_reg_get (Dwfl_Frame_State *state, unsigned regno,
Dwarf_Addr *val)
{
Ebl *ebl = state->thread->process->ebl;
+ if (ebl->frame_dwarf_to_regno != NULL
+ && ! ebl->frame_dwarf_to_regno (ebl, ®no))
+ return false;
if (regno >= ebl->frame_state_nregs)
return false;
if ((state->regs_set[regno / sizeof (*state->regs_set) / 8]
@@ -283,6 +286,9 @@ dwfl_frame_state_reg_set (Dwfl_Frame_State *state, unsigned regno,
Dwarf_Addr val)
{
Ebl *ebl = state->thread->process->ebl;
+ if (ebl->frame_dwarf_to_regno != NULL
+ && ! ebl->frame_dwarf_to_regno (ebl, ®no))
+ return false;
if (regno >= ebl->frame_state_nregs)
return false;
/* For example i386 user_regs_struct has signed fields. */
diff --git a/libebl/Makefile.am b/libebl/Makefile.am
index 1e39689..92497c3 100644
--- a/libebl/Makefile.am
+++ b/libebl/Makefile.am
@@ -55,7 +55,8 @@ gen_SOURCES = eblopenbackend.c eblclosebackend.c eblstrtab.c \
eblreginfo.c eblnonerelocp.c eblrelativerelocp.c \
eblsysvhashentrysize.c eblauxvinfo.c eblcheckobjattr.c \
ebl_check_special_section.c ebl_syscall_abi.c eblabicfi.c \
- eblstother.c eblgetfuncpc.c eblframestate.c
+ eblstother.c eblgetfuncpc.c eblframestate.c eblnormalizepc.c \
+ eblframeunwind.c eblframedwarftoregno.c
libebl_a_SOURCES = $(gen_SOURCES)
diff --git a/libebl/ebl-hooks.h b/libebl/ebl-hooks.h
index 5b77f92..72f1aad 100644
--- a/libebl/ebl-hooks.h
+++ b/libebl/ebl-hooks.h
@@ -168,5 +168,18 @@ bool EBLHOOK(frame_state) (struct Dwfl_Frame_State *state);
above. */
size_t EBLHOOKVAR(frame_state_nregs);
+/* Convert *REGNO as is in DWARF to a lower range suitable for
+ Dwarf_Frame_State->REGS indexing. RETURN_ADDRESS_REGISTER should not change
+ on second call; other registers may map to numbers invalid on input. */
+bool EBLHOOK(frame_dwarf_to_regno) (Ebl *ebl, unsigned *regno);
+
+/* Optionally modify *PC as fetched from inferior data into valid PC
+ instruction pointer. */
+void EBLHOOK(normalize_pc) (Ebl *ebl, Dwarf_Addr *pc);
+
+/* See dwfl_frame_unwind. */
+bool EBLHOOK(frame_unwind) (Ebl *ebl, struct Dwfl_Frame_State **statep,
+ Dwarf_Addr pc);
+
/* Destructor for ELF backend handle. */
void EBLHOOK(destr) (struct ebl *);
diff --git a/libebl/eblframedwarftoregno.c b/libebl/eblframedwarftoregno.c
new file mode 100644
index 0000000..4cfbd46
--- /dev/null
+++ b/libebl/eblframedwarftoregno.c
@@ -0,0 +1,42 @@
+/* Convert *REGNO as is in DWARF to a lower range.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <libeblP.h>
+
+bool
+ebl_frame_dwarf_to_regno (Ebl *ebl, unsigned *regno)
+{
+ if (ebl == NULL)
+ return false;
+ return (ebl->frame_dwarf_to_regno == NULL
+ ? true : ebl->frame_dwarf_to_regno (ebl, regno));
+}
diff --git a/libebl/eblframeunwind.c b/libebl/eblframeunwind.c
new file mode 100644
index 0000000..adc658b
--- /dev/null
+++ b/libebl/eblframeunwind.c
@@ -0,0 +1,41 @@
+/* Get previous frame state for an existing frame state.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <libeblP.h>
+
+bool
+ebl_frame_unwind (Ebl *ebl, struct Dwfl_Frame_State **statep, Dwarf_Addr pc)
+{
+ if (ebl == NULL || ebl->frame_unwind == NULL)
+ return false;
+ return ebl->frame_unwind (ebl, statep, pc);
+}
diff --git a/libebl/eblnormalizepc.c b/libebl/eblnormalizepc.c
new file mode 100644
index 0000000..abe6352
--- /dev/null
+++ b/libebl/eblnormalizepc.c
@@ -0,0 +1,40 @@
+/* Modify PC as fetched from inferior data into valid PC.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <libeblP.h>
+
+void
+ebl_normalize_pc (Ebl *ebl, Dwarf_Addr *pc)
+{
+ if (ebl != NULL && ebl->normalize_pc != NULL)
+ ebl->normalize_pc (ebl, pc);
+}
diff --git a/libebl/libebl.h b/libebl/libebl.h
index 09b7bfb..967980e 100644
--- a/libebl/libebl.h
+++ b/libebl/libebl.h
@@ -393,6 +393,20 @@ extern bool ebl_frame_state (struct Dwfl_Frame_State *state)
extern size_t ebl_frame_state_nregs (Ebl *ebl)
__nonnull_attribute__ (1);
+/* Modify PC as fetched from inferior data into valid PC. */
+extern void ebl_normalize_pc (Ebl *ebl, Dwarf_Addr *pc)
+ __nonnull_attribute__ (1, 2);
+
+/* Get previous frame state for an existing frame state. */
+struct Dwfl_Frame_State_Process;
+extern bool
+ ebl_frame_unwind (Ebl *ebl, struct Dwfl_Frame_State **statep, Dwarf_Addr pc)
+ __nonnull_attribute__ (1, 2);
+
+/* Convert *REGNO as is in DWARF to a lower range. */
+extern bool ebl_frame_dwarf_to_regno (Ebl *ebl, unsigned *regno)
+ __nonnull_attribute__ (1, 2);
+
#ifdef __cplusplus
}
#endif
diff --git a/tests/backtrace.ppc.core.bz2 b/tests/backtrace.ppc.core.bz2
new file mode 100644
index 0000000..2dca8f1
Binary files /dev/null and b/tests/backtrace.ppc.core.bz2 differ
diff --git a/tests/backtrace.ppc.exec.bz2 b/tests/backtrace.ppc.exec.bz2
new file mode 100644
index 0000000..15070a9
Binary files /dev/null and b/tests/backtrace.ppc.exec.bz2 differ
diff --git a/tests/backtrace.ppc64.core.bz2 b/tests/backtrace.ppc64.core.bz2
new file mode 100644
index 0000000..a07aa61
Binary files /dev/null and b/tests/backtrace.ppc64.core.bz2 differ
diff --git a/tests/backtrace.ppc64.exec.bz2 b/tests/backtrace.ppc64.exec.bz2
new file mode 100644
index 0000000..21a7e0d
Binary files /dev/null and b/tests/backtrace.ppc64.exec.bz2 differ
diff --git a/tests/backtrace.s390.core.bz2 b/tests/backtrace.s390.core.bz2
new file mode 100644
index 0000000..ce21012
Binary files /dev/null and b/tests/backtrace.s390.core.bz2 differ
diff --git a/tests/backtrace.s390.exec.bz2 b/tests/backtrace.s390.exec.bz2
new file mode 100644
index 0000000..8bbb75d
Binary files /dev/null and b/tests/backtrace.s390.exec.bz2 differ
diff --git a/tests/backtrace.s390x.core.bz2 b/tests/backtrace.s390x.core.bz2
new file mode 100644
index 0000000..2a2c9e7
Binary files /dev/null and b/tests/backtrace.s390x.core.bz2 differ
diff --git a/tests/backtrace.s390x.exec.bz2 b/tests/backtrace.s390x.exec.bz2
new file mode 100644
index 0000000..195d1af
Binary files /dev/null and b/tests/backtrace.s390x.exec.bz2 differ
diff --git a/tests/run-backtrace.sh b/tests/run-backtrace.sh
index 6e14653..a57d474 100755
--- a/tests/run-backtrace.sh
+++ b/tests/run-backtrace.sh
@@ -53,4 +53,15 @@ for child in backtrace-child{,-biarch}; do
check_empty $core.err
done
+for arch in ppc ppc64 s390 s390x; do
+ testfiles backtrace.$arch.{exec,core}
+ tempfiles backtrace.$arch.{bt,err}
+ echo ./backtrace ./backtrace.$arch.{exec,core}
+ mytestrun ./backtrace ./backtrace.$arch.{exec,core} 1>backtrace.$arch.bt \
+ 2>backtrace.$arch.err
+ cat backtrace.$arch.{bt,err}
+ check_gsignal backtrace.$arch.bt
+ check_empty backtrace.$arch.err
+done
+
exit 0
10 years
[patch 4/4] unwinder: The unwinder (x86* only)
by Jan Kratochvil
Hi,
dependent on:
[patch 3/4] unwinder: Provide dwfl_core_filename_report
present in:
jankratochvil/forunwind-baseaddr-hookvars-corereport-unwindx86
but this branch is not suitable for merge as it is unclear in which form get
checked in the patches above. The branch is usable only for "git checkout",
neither diff nor merge should be done on it.
ppc* and s390* extensions exist in
jankratochvil/forunwind-baseaddr-hookvars-corereport-unwindx86-nonx86
and they are not present here for simplicity, I may post them later.
Some comments were already posted, it supports natively core files and live
PIDs, "data" API interface for backtracing application-provided inferiors is
also present.
Testcases are provided, src/stack.c utility is also provided.
Thanks,
Jan
backends/
2012-11-13 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* Makefile.am (i386_SRCS): Add i386_frame_state.c.
(x86_64_SRCS): Add x86_64_frame_state.c.
* i386_frame_state.c: New file.
* i386_init.c (i386_init): Initialize frame_state_nregs and
frame_state.
* x86_64_frame_state.c: New file.
* x86_64_init.c (x86_64_init): Initialize frame_state_nregs and
frame_state.
./
2012-11-13 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* configure.ac: New AC_CHECK_SIZEOF for long. Call utrace_BIARCH, new
AC_SUBST for CC_BIARCH.
libdw/
2012-11-13 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* cfi.h (struct Dwarf_Frame_s): Make the comment more specific.
* libdw.map (ELFUTILS_0.156): Add dwfl_report_elf_baseaddr,
dwfl_frame_state_pid, dwfl_frame_state_core, dwfl_frame_state_data,
dwfl_frame_unwind, dwfl_frame_state_pc, dwfl_frame_tid_get and
dwfl_frame_thread_next.
libdwfl/
2012-11-13 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* Makefile.am (INCLUDES): Add ../libasm.
(libdwfl_a_SOURCES): Add dwfl_frame_state.c, dwfl_frame_unwind.c,
dwfl_frame_state_pc.c, dwfl_frame_state_pid.c, dwfl_frame_state_core.c,
dwfl_frame_state_data.c, dwfl_frame_thread_next.c and
dwfl_frame_tid_get.c.
* dwfl_end.c (dwfl_end): Call __libdwfl_process_free for
dwfl->framestatelist.
* dwfl_frame_state.c: New file.
* dwfl_frame_state_core.c: New file.
* dwfl_frame_state_data.c: New file.
* dwfl_frame_state_pc.c: New file.
* dwfl_frame_state_pid.c: New file.
* dwfl_frame_thread_next.c: New file.
* dwfl_frame_tid_get.c: New file.
* dwfl_frame_unwind.c: New file.
* libdwfl.h (Dwfl_Frame_State): New typedef.
(dwfl_frame_state_pid, dwfl_frame_state_core, dwfl_frame_memory_read_t)
(dwfl_frame_state_data, dwfl_frame_unwind, dwfl_frame_state_pc)
(dwfl_frame_thread_next, dwfl_frame_tid_get): New declaration.
* libdwflP.h: Include libeblP.h.
(Dwfl_Frame_State_Process, Dwfl_Frame_State_Thread): New typedefs.
(LIBEBL_BAD, CORE_MISSING, INVALID_REGISTER, PROCESS_MEMORY_READ)
(PROCESS_NO_ARCH, PARSE_PROC, NO_THREAD, INVALID_DWARF)
(UNSUPPORTED_DWARF): New DWFL_ERROR entries.
(struct Dwfl): New entry framestatelist.
(struct Dwfl_Frame_State_Process, struct Dwfl_Frame_State_Thread)
(struct Dwfl_Frame_State, dwfl_frame_state_reg_get)
(dwfl_frame_state_reg_set): New.
(__libdwfl_state_fetch_pc, __libdwfl_thread_free)
(__libdwfl_thread_alloc, __libdwfl_process_free)
(__libdwfl_process_alloc, __libdwfl_segment_start)
(__libdwfl_segment_end): New declarations.
(dwfl_frame_state_pid, dwfl_frame_state_core, dwfl_frame_unwind)
(dwfl_frame_state_pc): New INTDECL entries.
* segment.c (segment_start): Rename to ...
(__libdwfl_segment_start): ... here and make it internal_function.
(segment_end): Rename to ...
(__libdwfl_segment_end): ... here and make it internal_function.
(reify_segments, dwfl_report_segment): Rename them at the callers.
libebl/
2012-11-13 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* Makefile.am (INCLUDES): Add ../libdwfl.
(gen_SOURCES): Add eblframestate.c.
* ebl-hooks.h (frame_state, frame_state_nregs): New entries.
* eblframestate.c: New file.
* libebl.h (struct Dwfl_Frame_State, ebl_frame_state)
(ebl_frame_state_nregs): New declarations.
m4/
2012-11-13 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* biarch.m4: New file.
src/
2012-11-13 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* Makefile.am (bin_PROGRAMS): Add stack.
(stack_LDADD): New.
* stack.c: New file.
tests/
2012-11-13 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* Makefile.am (check_PROGRAMS): Add backtrace, backtrace-child and
backtrace-data.
(BUILT_SOURCES, clean-local, backtrace-child-biarch): New.
(TESTS): Add run-backtrace.sh.
(backtrace_LDADD, backtrace_child_CFLAGS, backtrace_child_LDFLAGS)
(backtrace_data_LDADD): New.
* backtrace-child.c: New file.
* backtrace-data.c: New file.
* backtrace.c: New file.
* run-backtrace.sh: New file.
diff --git a/backends/Makefile.am b/backends/Makefile.am
index 243a95a..6f99758 100644
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -50,7 +50,8 @@ libdw = ../libdw/libdw.so
endif
i386_SRCS = i386_init.c i386_symbol.c i386_corenote.c i386_cfi.c \
- i386_retval.c i386_regs.c i386_auxv.c i386_syscall.c
+ i386_retval.c i386_regs.c i386_auxv.c i386_syscall.c \
+ i386_frame_state.c
cpu_i386 = ../libcpu/libcpu_i386.a
libebl_i386_pic_a_SOURCES = $(i386_SRCS)
am_libebl_i386_pic_a_OBJECTS = $(i386_SRCS:.c=.os)
@@ -60,7 +61,8 @@ libebl_sh_pic_a_SOURCES = $(sh_SRCS)
am_libebl_sh_pic_a_OBJECTS = $(sh_SRCS:.c=.os)
x86_64_SRCS = x86_64_init.c x86_64_symbol.c x86_64_corenote.c x86_64_cfi.c \
- x86_64_retval.c x86_64_regs.c i386_auxv.c x86_64_syscall.c
+ x86_64_retval.c x86_64_regs.c i386_auxv.c x86_64_syscall.c \
+ x86_64_frame_state.c
cpu_x86_64 = ../libcpu/libcpu_x86_64.a
libebl_x86_64_pic_a_SOURCES = $(x86_64_SRCS)
am_libebl_x86_64_pic_a_OBJECTS = $(x86_64_SRCS:.c=.os)
diff --git a/backends/i386_frame_state.c b/backends/i386_frame_state.c
new file mode 100644
index 0000000..b5d5439
--- /dev/null
+++ b/backends/i386_frame_state.c
@@ -0,0 +1,80 @@
+/* Fetch process data from STATE->base->pid or STATE->base->core.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined __i386__ || defined __x86_64__
+# include <sys/user.h>
+# include <sys/ptrace.h>
+#endif
+#include "libdwflP.h"
+
+#define BACKEND i386_
+#include "libebl_CPU.h"
+
+bool
+i386_frame_state (Dwfl_Frame_State *state)
+{
+ pid_t tid = state->thread->tid;
+ if (state->thread->process->core == NULL && tid)
+ {
+#if !defined __i386__ && !defined __x86_64__
+ return false;
+#else /* __i386__ || __x86_64__ */
+ struct user_regs_struct user_regs;
+ if (ptrace (PTRACE_GETREGS, tid, NULL, &user_regs) != 0)
+ return false;
+# if defined __i386__
+ dwfl_frame_state_reg_set (state, 0, user_regs.eax);
+ dwfl_frame_state_reg_set (state, 1, user_regs.ecx);
+ dwfl_frame_state_reg_set (state, 2, user_regs.edx);
+ dwfl_frame_state_reg_set (state, 3, user_regs.ebx);
+ dwfl_frame_state_reg_set (state, 4, user_regs.esp);
+ dwfl_frame_state_reg_set (state, 5, user_regs.ebp);
+ dwfl_frame_state_reg_set (state, 6, user_regs.esi);
+ dwfl_frame_state_reg_set (state, 7, user_regs.edi);
+ dwfl_frame_state_reg_set (state, 8, user_regs.eip);
+# elif defined __x86_64__
+ dwfl_frame_state_reg_set (state, 0, user_regs.rax);
+ dwfl_frame_state_reg_set (state, 1, user_regs.rcx);
+ dwfl_frame_state_reg_set (state, 2, user_regs.rdx);
+ dwfl_frame_state_reg_set (state, 3, user_regs.rbx);
+ dwfl_frame_state_reg_set (state, 4, user_regs.rsp);
+ dwfl_frame_state_reg_set (state, 5, user_regs.rbp);
+ dwfl_frame_state_reg_set (state, 6, user_regs.rsi);
+ dwfl_frame_state_reg_set (state, 7, user_regs.rdi);
+ dwfl_frame_state_reg_set (state, 8, user_regs.rip);
+# else /* (__i386__ || __x86_64__) && (!__i386__ && !__x86_64__) */
+# error
+# endif /* (__i386__ || __x86_64__) && (!__i386__ && !__x86_64__) */
+#endif /* __i386__ || __x86_64__ */
+ }
+ return true;
+}
diff --git a/backends/i386_init.c b/backends/i386_init.c
index cc9b2d7..4200f31 100644
--- a/backends/i386_init.c
+++ b/backends/i386_init.c
@@ -63,6 +63,9 @@ i386_init (elf, machine, eh, ehlen)
HOOK (eh, auxv_info);
HOOK (eh, disasm);
HOOK (eh, abi_cfi);
+ /* gcc/config/ #define DWARF_FRAME_REGISTERS. For i386 it is 17, why? */
+ eh->frame_state_nregs = 9;
+ HOOK (eh, frame_state);
return MODVERSION;
}
diff --git a/backends/x86_64_frame_state.c b/backends/x86_64_frame_state.c
new file mode 100644
index 0000000..b304250
--- /dev/null
+++ b/backends/x86_64_frame_state.c
@@ -0,0 +1,75 @@
+/* Fetch live process Dwfl_Frame_State from PID.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#ifdef __x86_64__
+# include <sys/user.h>
+# include <sys/ptrace.h>
+#endif
+#include "libdwflP.h"
+
+#define BACKEND x86_64_
+#include "libebl_CPU.h"
+
+bool
+x86_64_frame_state (Dwfl_Frame_State *state)
+{
+ pid_t tid = state->thread->tid;
+ if (state->thread->process->core == NULL && tid)
+ {
+#ifndef __x86_64__
+ return false;
+#else /* __x86_64__ */
+ struct user_regs_struct user_regs;
+ if (ptrace (PTRACE_GETREGS, tid, NULL, &user_regs) != 0)
+ return false;
+ dwfl_frame_state_reg_set (state, 0, user_regs.rax);
+ dwfl_frame_state_reg_set (state, 1, user_regs.rdx);
+ dwfl_frame_state_reg_set (state, 2, user_regs.rcx);
+ dwfl_frame_state_reg_set (state, 3, user_regs.rbx);
+ dwfl_frame_state_reg_set (state, 4, user_regs.rsi);
+ dwfl_frame_state_reg_set (state, 5, user_regs.rdi);
+ dwfl_frame_state_reg_set (state, 6, user_regs.rbp);
+ dwfl_frame_state_reg_set (state, 7, user_regs.rsp);
+ dwfl_frame_state_reg_set (state, 8, user_regs.r8);
+ dwfl_frame_state_reg_set (state, 9, user_regs.r9);
+ dwfl_frame_state_reg_set (state, 10, user_regs.r10);
+ dwfl_frame_state_reg_set (state, 11, user_regs.r11);
+ dwfl_frame_state_reg_set (state, 12, user_regs.r12);
+ dwfl_frame_state_reg_set (state, 13, user_regs.r13);
+ dwfl_frame_state_reg_set (state, 14, user_regs.r14);
+ dwfl_frame_state_reg_set (state, 15, user_regs.r15);
+ dwfl_frame_state_reg_set (state, 16, user_regs.rip);
+#endif /* __x86_64__ */
+ }
+ return true;
+}
diff --git a/backends/x86_64_init.c b/backends/x86_64_init.c
index 67a5880..08c63f1 100644
--- a/backends/x86_64_init.c
+++ b/backends/x86_64_init.c
@@ -60,6 +60,9 @@ x86_64_init (elf, machine, eh, ehlen)
HOOK (eh, auxv_info);
HOOK (eh, disasm);
HOOK (eh, abi_cfi);
+ /* gcc/config/ #define DWARF_FRAME_REGISTERS. */
+ eh->frame_state_nregs = 17;
+ HOOK (eh, frame_state);
return MODVERSION;
}
diff --git a/configure.ac b/configure.ac
index 52a2322..01c5510 100644
--- a/configure.ac
+++ b/configure.ac
@@ -317,4 +317,15 @@ esac
# Round up to the next release API (x.y) version.
eu_version=$(( (eu_version + 999) / 1000 ))
+AC_CHECK_SIZEOF(long)
+
+# On a 64-bit host where can can use $CC -m32, we'll run two sets of tests.
+# Likewise in a 32-bit build on a host where $CC -m64 works.
+utrace_BIARCH
+# `$utrace_biarch' will be `-m64' even on an uniarch i386 machine.
+AS_IF([test $utrace_cv_cc_biarch = yes],
+ [CC_BIARCH="$CC $utrace_biarch"],
+ [CC_BIARCH="$CC"])
+AC_SUBST([CC_BIARCH])
+
AC_OUTPUT
diff --git a/libdw/cfi.h b/libdw/cfi.h
index 8949833..44a84d4 100644
--- a/libdw/cfi.h
+++ b/libdw/cfi.h
@@ -150,8 +150,8 @@ struct dwarf_frame_register
Dwarf_Sword value:(sizeof (Dwarf_Sword) * 8 - 3);
};
-/* This holds everything we know about the state of the frame
- at a particular PC location described by an FDE. */
+/* This holds instructions for unwinding frame at a particular PC location
+ described by an FDE. */
struct Dwarf_Frame_s
{
/* This frame description covers PC values in [start, end). */
diff --git a/libdw/libdw.map b/libdw/libdw.map
index 5a055e8..bf65d83 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -258,4 +258,12 @@ ELFUTILS_0.149 {
ELFUTILS_0.156 {
global:
dwfl_core_filename_report;
+ dwfl_report_elf_baseaddr;
+ dwfl_frame_state_pid;
+ dwfl_frame_state_core;
+ dwfl_frame_state_data;
+ dwfl_frame_unwind;
+ dwfl_frame_state_pc;
+ dwfl_frame_tid_get;
+ dwfl_frame_thread_next;
} ELFUTILS_0.149;
diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am
index 0ef4928..641b9eb 100644
--- a/libdwfl/Makefile.am
+++ b/libdwfl/Makefile.am
@@ -31,7 +31,7 @@
##
include $(top_srcdir)/config/eu.am
INCLUDES += -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \
- -I$(srcdir)/../libdw
+ -I$(srcdir)/../libdw -I$(srcdir)/../libasm
VERSION = 1
noinst_LIBRARIES = libdwfl.a
@@ -68,7 +68,11 @@ libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \
dwfl_module_return_value_location.c \
dwfl_module_register_names.c \
dwfl_segment_report_module.c \
- link_map.c core-file.c open.c image-header.c
+ link_map.c core-file.c open.c image-header.c \
+ dwfl_frame_state.c dwfl_frame_unwind.c \
+ dwfl_frame_state_pc.c dwfl_frame_state_pid.c \
+ dwfl_frame_state_core.c dwfl_frame_state_data.c \
+ dwfl_frame_thread_next.c dwfl_frame_tid_get.c
if ZLIB
libdwfl_a_SOURCES += gzip.c
diff --git a/libdwfl/dwfl_end.c b/libdwfl/dwfl_end.c
index fd38e0f..cca38c7 100644
--- a/libdwfl/dwfl_end.c
+++ b/libdwfl/dwfl_end.c
@@ -34,6 +34,9 @@ dwfl_end (Dwfl *dwfl)
if (dwfl == NULL)
return;
+ while (dwfl->framestatelist)
+ __libdwfl_process_free (dwfl->framestatelist);
+
free (dwfl->lookup_addr);
free (dwfl->lookup_module);
free (dwfl->lookup_segndx);
diff --git a/libdwfl/dwfl_frame_state.c b/libdwfl/dwfl_frame_state.c
new file mode 100644
index 0000000..1229aa2
--- /dev/null
+++ b/libdwfl/dwfl_frame_state.c
@@ -0,0 +1,175 @@
+/* Get Dwarf Frame state for target PID or core file.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#include "libdwflP.h"
+#include <sys/ptrace.h>
+#include <unistd.h>
+
+#ifndef MIN
+# define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+bool
+internal_function
+__libdwfl_state_fetch_pc (Dwfl_Frame_State *state)
+{
+ switch (state->pc_state)
+ {
+ case DWFL_FRAME_STATE_PC_SET:
+ return true;
+ case DWFL_FRAME_STATE_PC_UNDEFINED:
+ abort ();
+ case DWFL_FRAME_STATE_ERROR:;
+ Ebl *ebl = state->thread->process->ebl;
+ Dwarf_CIE abi_info;
+ if (ebl_abi_cfi (ebl, &abi_info) != 0)
+ {
+ __libdwfl_seterrno (DWFL_E_LIBEBL);
+ return false;
+ }
+ unsigned ra = abi_info.return_address_register;
+ /* dwarf_frame_state_reg_is_set is not applied here. */
+ if (ra >= ebl_frame_state_nregs (ebl))
+ {
+ __libdwfl_seterrno (DWFL_E_LIBEBL_BAD);
+ return false;
+ }
+ state->pc = state->regs[ra];
+ state->pc_state = DWFL_FRAME_STATE_PC_SET;
+ return true;
+ }
+ abort ();
+}
+
+/* Do not call it on your own, to be used by thread_* functions only. */
+
+static void
+state_free (Dwfl_Frame_State *state)
+{
+ Dwfl_Frame_State_Thread *thread = state->thread;
+ assert (thread->unwound == state);
+ thread->unwound = state->unwound;
+ free (state);
+}
+
+/* Do not call it on your own, to be used by thread_* functions only. */
+
+static Dwfl_Frame_State *
+state_alloc (Dwfl_Frame_State_Thread *thread)
+{
+ assert (thread->unwound == NULL);
+ Ebl *ebl = thread->process->ebl;
+ size_t nregs = ebl_frame_state_nregs (ebl);
+ if (nregs == 0)
+ return NULL;
+ assert (nregs < sizeof (((Dwfl_Frame_State *) NULL)->regs_set) * 8);
+ Dwfl_Frame_State *state = malloc (sizeof (*state)
+ + sizeof (*state->regs) * nregs);
+ if (state == NULL)
+ return NULL;
+ state->thread = thread;
+ state->signal_frame = false;
+ state->pc_state = DWFL_FRAME_STATE_ERROR;
+ memset (state->regs_set, 0, sizeof (state->regs_set));
+ thread->unwound = state;
+ state->unwound = NULL;
+ return state;
+}
+
+void
+internal_function
+__libdwfl_thread_free (Dwfl_Frame_State_Thread *thread)
+{
+ while (thread->unwound)
+ state_free (thread->unwound);
+ if (thread->tid_attached)
+ ptrace (PTRACE_DETACH, thread->tid, NULL, NULL);
+ Dwfl_Frame_State_Process *process = thread->process;
+ assert (process->thread == thread);
+ process->thread = thread->next;
+ free (thread);
+}
+
+Dwfl_Frame_State_Thread *
+internal_function
+__libdwfl_thread_alloc (Dwfl_Frame_State_Process *process, pid_t tid)
+{
+ Dwfl_Frame_State_Thread *thread = malloc (sizeof (*thread));
+ if (thread == NULL)
+ return NULL;
+ thread->process = process;
+ thread->tid = tid;
+ thread->tid_attached = false;
+ thread->unwound = NULL;
+ thread->next = process->thread;
+ process->thread = thread;
+ if (state_alloc (thread) == NULL)
+ {
+ __libdwfl_thread_free (thread);
+ return NULL;
+ }
+ return thread;
+}
+
+void
+internal_function
+__libdwfl_process_free (Dwfl_Frame_State_Process *process)
+{
+ while (process->thread)
+ __libdwfl_thread_free (process->thread);
+ if (process->ebl_close)
+ ebl_closebackend (process->ebl);
+ elf_end (process->core);
+ if (process->core_fd != -1)
+ close (process->core_fd);
+ Dwfl *dwfl = process->dwfl;
+ assert (dwfl->framestatelist == process);
+ dwfl->framestatelist = process->next;
+ free (process);
+}
+
+Dwfl_Frame_State_Process *
+internal_function
+__libdwfl_process_alloc (Dwfl *dwfl, dwfl_frame_memory_read_t *memory_read,
+ void *memory_read_user_data)
+{
+ Dwfl_Frame_State_Process *process = malloc (sizeof (*process));
+ if (process == NULL)
+ return NULL;
+ process->dwfl = dwfl;
+ process->ebl = NULL;
+ process->ebl_close = NULL;
+ process->memory_read = memory_read;
+ process->memory_read_user_data = memory_read_user_data;
+ process->core = NULL;
+ process->core_fd = -1;
+ process->thread = NULL;
+ process->next = dwfl->framestatelist;
+ dwfl->framestatelist = process;
+ return process;
+}
diff --git a/libdwfl/dwfl_frame_state_core.c b/libdwfl/dwfl_frame_state_core.c
new file mode 100644
index 0000000..737bcbb
--- /dev/null
+++ b/libdwfl/dwfl_frame_state_core.c
@@ -0,0 +1,286 @@
+/* Get Dwarf Frame state for target core file.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#include "libdwflP.h"
+#include <fcntl.h>
+#include "system.h"
+
+#ifndef MIN
+# define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+static bool
+dwfl_frame_state_core_memory_read (Dwarf_Addr addr, Dwarf_Addr *result,
+ void *user_data)
+{
+ Dwfl_Frame_State_Process *process = user_data;
+ Elf *core = process->core;
+ assert (core != NULL);
+ Dwfl *dwfl = process->dwfl;
+ static size_t phnum;
+ if (elf_getphdrnum (core, &phnum) < 0)
+ {
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return false;
+ }
+ for (size_t cnt = 0; cnt < phnum; ++cnt)
+ {
+ GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
+ if (phdr == NULL || phdr->p_type != PT_LOAD)
+ continue;
+ /* Bias is zero here, a core file itself has no bias. */
+ GElf_Addr start = __libdwfl_segment_start (dwfl, phdr->p_vaddr);
+ GElf_Addr end = __libdwfl_segment_end (dwfl,
+ phdr->p_vaddr + phdr->p_memsz);
+ unsigned bytes = process->ebl->class == ELFCLASS64 ? 8 : 4;
+ if (addr < start || addr + bytes > end)
+ continue;
+ Elf_Data *data;
+ data = elf_getdata_rawchunk (core, phdr->p_offset + addr - start,
+ bytes, ELF_T_ADDR);
+ if (data == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return false;
+ }
+ assert (data->d_size == bytes);
+ /* FIXME: Currently any arch supported for unwinding supports
+ unaligned access. */
+ if (bytes == 8)
+ *result = *(const uint64_t *) data->d_buf;
+ else
+ *result = *(const uint32_t *) data->d_buf;
+ return true;
+ }
+ __libdwfl_seterrno (DWFL_E_CORE_MISSING);
+ return false;
+}
+
+Dwfl_Frame_State *
+dwfl_frame_state_core (Dwfl *dwfl, const char *corefile)
+{
+ Dwfl_Frame_State_Process *process;
+ process = __libdwfl_process_alloc (dwfl, dwfl_frame_state_core_memory_read,
+ NULL);
+ if (process == NULL)
+ return NULL;
+ process->memory_read_user_data = process;
+ int core_fd = open64 (corefile, O_RDONLY);
+ if (core_fd < 0)
+ {
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_BADELF);
+ return NULL;
+ }
+ process->core_fd = core_fd;
+ Elf *core;
+ Dwfl_Error err = __libdw_open_file (&core_fd, &core, true, false);
+ if (err != DWFL_E_NOERROR)
+ {
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (err);
+ return NULL;
+ }
+ process->core = core;
+ Ebl *ebl = ebl_openbackend (core);
+ if (ebl == NULL)
+ {
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_LIBEBL);
+ return NULL;
+ }
+ process->ebl = ebl;
+ process->ebl_close = true;
+ size_t nregs = ebl_frame_state_nregs (ebl);
+ if (nregs == 0)
+ {
+ /* We do not support unwinding this CORE file EBL. */
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_LIBEBL);
+ return NULL;
+ }
+ GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem);
+ if (ehdr == NULL)
+ {
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return NULL;
+ }
+ assert (ehdr->e_type == ET_CORE);
+ size_t phnum;
+ if (elf_getphdrnum (core, &phnum) < 0)
+ {
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return NULL;
+ }
+ Dwfl_Frame_State_Thread *thread = NULL;
+ for (size_t cnt = 0; cnt < phnum; ++cnt)
+ {
+ GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
+ if (phdr == NULL || phdr->p_type != PT_NOTE)
+ continue;
+ Elf_Data *data = elf_getdata_rawchunk (core, phdr->p_offset,
+ phdr->p_filesz, ELF_T_NHDR);
+ if (data == NULL)
+ {
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return NULL;
+ }
+ size_t offset = 0;
+ GElf_Nhdr nhdr;
+ size_t name_offset;
+ size_t desc_offset;
+ while (offset < data->d_size
+ && (offset = gelf_getnote (data, offset,
+ &nhdr, &name_offset, &desc_offset)) > 0)
+ {
+ /* Do not check NAME for now, help broken Linux kernels. */
+ const char *name = data->d_buf + name_offset;
+ const char *desc = data->d_buf + desc_offset;
+ GElf_Word regs_offset;
+ size_t nregloc;
+ const Ebl_Register_Location *reglocs;
+ size_t nitems;
+ const Ebl_Core_Item *items;
+ if (! ebl_core_note (ebl, &nhdr, name,
+ ®s_offset, &nregloc, ®locs, &nitems, &items))
+ {
+ /* This note may be just not recognized, skip it. */
+ continue;
+ }
+ if (nhdr.n_type == NT_PRSTATUS)
+ {
+ const Ebl_Core_Item *item;
+ for (item = items; item < items + nitems; item++)
+ if (strcmp (item->name, "pid") == 0)
+ break;
+ if (item == items + nitems)
+ {
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_BADELF);
+ return NULL;
+ }
+ uint32_t val32 = *(const uint32_t *) (desc + item->offset);
+ val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be32toh (val32) : le32toh (val32));
+ pid_t tid = (int32_t) val32;
+ eu_static_assert (sizeof val32 <= sizeof tid);
+ if (thread)
+ {
+ /* Delay initialization of THREAD till all notes for it have
+ been read in. */
+ Dwfl_Frame_State *state = thread->unwound;
+ if (! ebl_frame_state (state)
+ || ! __libdwfl_state_fetch_pc (state))
+ {
+ __libdwfl_thread_free (thread);
+ thread = NULL;
+ continue;
+ }
+ }
+ thread = __libdwfl_thread_alloc (process, tid);
+ if (thread == NULL)
+ {
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return NULL;
+ }
+ }
+ if (thread == NULL)
+ {
+ /* Ignore notes before first PR_NTSTATUS. */
+ continue;
+ }
+ Dwfl_Frame_State *state = thread->unwound;
+ desc += regs_offset;
+ for (size_t regloci = 0; regloci < nregloc; regloci++)
+ {
+ const Ebl_Register_Location *regloc = reglocs + regloci;
+ if (regloc->regno >= nregs)
+ continue;
+ assert (regloc->bits == 32 || regloc->bits == 64);
+ const char *reg_desc = desc + regloc->offset;
+ for (unsigned regno = regloc->regno;
+ regno < MIN (regloc->regno + (regloc->count ?: 1U), nregs);
+ regno++)
+ {
+ /* PPC provides DWARF register 65 irrelevant for
+ CFI which clashes with register 108 (LR) we need.
+ LR (108) is provided earlier (in NT_PRSTATUS) than the # 65.
+ FIXME: It depends now on their order in core notes. */
+ if (dwfl_frame_state_reg_get (state, regno, NULL))
+ continue;
+ Dwarf_Addr val;
+ switch (regloc->bits)
+ {
+ case 32:;
+ uint32_t val32 = *(const uint32_t *) reg_desc;
+ reg_desc += sizeof val32;
+ val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be32toh (val32) : le32toh (val32));
+ /* Do a host width conversion. */
+ val = val32;
+ break;
+ case 64:;
+ uint64_t val64 = *(const uint64_t *) reg_desc;
+ reg_desc += sizeof val64;
+ val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be64toh (val64) : le64toh (val64));
+ assert (sizeof (*state->regs) == sizeof val64);
+ val = val64;
+ break;
+ default:
+ abort ();
+ }
+ /* Registers not valid for CFI are just ignored. */
+ dwfl_frame_state_reg_set (state, regno, val);
+ reg_desc += regloc->pad;
+ }
+ }
+ }
+ }
+ if (thread)
+ {
+ /* Delay initialization of THREAD till all notes for it have been read
+ in. */
+ Dwfl_Frame_State *state = thread->unwound;
+ if (! ebl_frame_state (state) || ! __libdwfl_state_fetch_pc (state))
+ __libdwfl_thread_free (thread);
+ }
+ if (process->thread == NULL)
+ {
+ /* No valid threads recognized in this CORE. */
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_BADELF);
+ return NULL;
+ }
+ return process->thread->unwound;
+}
+INTDEF (dwfl_frame_state_core)
diff --git a/libdwfl/dwfl_frame_state_data.c b/libdwfl/dwfl_frame_state_data.c
new file mode 100644
index 0000000..9adfa7c
--- /dev/null
+++ b/libdwfl/dwfl_frame_state_data.c
@@ -0,0 +1,91 @@
+/* Get Dwarf Frame state from modules present in DWFL.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#include "libdwflP.h"
+
+Dwfl_Frame_State *
+dwfl_frame_state_data (Dwfl *dwfl, bool pc_set, Dwarf_Addr pc, unsigned nregs,
+ const uint64_t *regs_set, const Dwarf_Addr *regs,
+ dwfl_frame_memory_read_t *memory_read,
+ void *memory_read_user_data)
+{
+ Ebl *ebl = NULL;
+ for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
+ {
+ Dwfl_Error error = __libdwfl_module_getebl (mod);
+ if (error != DWFL_E_NOERROR)
+ continue;
+ ebl = mod->ebl;
+ }
+ if (ebl == NULL || nregs > ebl_frame_state_nregs (ebl))
+ {
+ __libdwfl_seterrno (DWFL_E_LIBEBL_BAD);
+ return NULL;
+ }
+ Dwfl_Frame_State_Process *process;
+ process = __libdwfl_process_alloc (dwfl, memory_read, memory_read_user_data);
+ if (process == NULL)
+ return NULL;
+ process->ebl = ebl;
+ Dwfl_Frame_State_Thread *thread = __libdwfl_thread_alloc (process, 0);
+ if (thread == NULL)
+ {
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return NULL;
+ }
+ Dwfl_Frame_State *state = thread->unwound;
+ state->pc_state = DWFL_FRAME_STATE_ERROR;
+ if (pc_set)
+ {
+ state->pc = pc;
+ state->pc_state = DWFL_FRAME_STATE_PC_SET;
+ }
+ for (unsigned regno = 0; regno < nregs; regno++)
+ if ((regs_set[regno / sizeof (*regs_set) / 8]
+ & (1U << (regno % (sizeof (*regs_set) * 8)))) != 0
+ && ! dwfl_frame_state_reg_set (state, regno, regs[regno]))
+ {
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_INVALID_REGISTER);
+ return NULL;
+ }
+ if (! ebl_frame_state (state))
+ {
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_LIBEBL);
+ return NULL;
+ }
+ if (! __libdwfl_state_fetch_pc (state))
+ {
+ __libdwfl_process_free (process);
+ return NULL;
+ }
+ return process->thread->unwound;
+}
+INTDEF (dwfl_frame_state_data)
diff --git a/libdwfl/dwfl_frame_state_pc.c b/libdwfl/dwfl_frame_state_pc.c
new file mode 100644
index 0000000..6e9928b
--- /dev/null
+++ b/libdwfl/dwfl_frame_state_pc.c
@@ -0,0 +1,56 @@
+/* Get return address register value for frame.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+bool
+dwfl_frame_state_pc (Dwfl_Frame_State *state, Dwarf_Addr *pc, bool *minusone)
+{
+ assert (state->pc_state == DWFL_FRAME_STATE_PC_SET);
+ *pc = state->pc;
+ if (minusone)
+ {
+ /* Bottom frame? */
+ if (state == state->thread->unwound)
+ *minusone = false;
+ /* *MINUSONE is logical or of both current and previous frame state. */
+ else if (state->signal_frame)
+ *minusone = false;
+ /* Not affected by unsuccessfully unwound frame. */
+ else if (! INTUSE(dwfl_frame_unwind) (&state) || state == NULL)
+ *minusone = true;
+ else
+ *minusone = ! state->signal_frame;
+ }
+ return true;
+}
+INTDEF (dwfl_frame_state_pc)
diff --git a/libdwfl/dwfl_frame_state_pid.c b/libdwfl/dwfl_frame_state_pid.c
new file mode 100644
index 0000000..89fbee2
--- /dev/null
+++ b/libdwfl/dwfl_frame_state_pid.c
@@ -0,0 +1,220 @@
+/* Get Dwarf Frame state for target live PID process.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#include "libdwflP.h"
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <dirent.h>
+
+static bool
+tid_is_attached (Dwfl *dwfl, pid_t tid)
+{
+ for (Dwfl_Frame_State_Process *process = dwfl->framestatelist; process;
+ process = process->next)
+ for (Dwfl_Frame_State_Thread *thread = process->thread; thread;
+ thread = thread->next)
+ if (thread->tid_attached && thread->tid == tid)
+ return true;
+ return false;
+}
+
+static bool
+ptrace_attach (pid_t tid)
+{
+ if (ptrace (PTRACE_ATTACH, tid, NULL, NULL) != 0)
+ return false;
+ /* FIXME: Handle missing SIGSTOP on old Linux kernels. */
+ for (;;)
+ {
+ int status;
+ if (waitpid (tid, &status, __WALL) != tid || !WIFSTOPPED (status))
+ {
+ ptrace (PTRACE_DETACH, tid, NULL, NULL);
+ return false;
+ }
+ if (WSTOPSIG (status) == SIGSTOP)
+ break;
+ if (ptrace (PTRACE_CONT, tid, NULL,
+ (void *) (uintptr_t) WSTOPSIG (status)) != 0)
+ {
+ ptrace (PTRACE_DETACH, tid, NULL, NULL);
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool
+dwfl_frame_state_pid_memory_read (Dwarf_Addr addr, Dwarf_Addr *result,
+ void *user_data)
+{
+ Dwfl_Frame_State_Process *process = user_data;
+ assert (process->core == NULL && process->thread->tid);
+ if (process->ebl->class == ELFCLASS64)
+ {
+ errno = 0;
+ *result = ptrace (PTRACE_PEEKDATA, process->thread->tid,
+ (void *) (uintptr_t) addr, NULL);
+ if (errno != 0)
+ {
+ __libdwfl_seterrno (DWFL_E_PROCESS_MEMORY_READ);
+ return false;
+ }
+ return true;
+ }
+#if SIZEOF_LONG == 8
+ /* We do not care about reads unaliged to 4 bytes boundary.
+ But 0x...ffc read of 8 bytes could overrun a page. */
+ bool lowered = (addr & 4) != 0;
+ if (lowered)
+ addr -= 4;
+#endif /* SIZEOF_LONG == 8 */
+ errno = 0;
+ *result = ptrace (PTRACE_PEEKDATA, process->thread->tid,
+ (void *) (uintptr_t) addr, NULL);
+ if (errno != 0)
+ {
+ __libdwfl_seterrno (DWFL_E_PROCESS_MEMORY_READ);
+ return false;
+ }
+#if SIZEOF_LONG == 8
+# if BYTE_ORDER == BIG_ENDIAN
+ if (! lowered)
+ *result >>= 32;
+# else
+ if (lowered)
+ *result >>= 32;
+# endif
+#endif /* SIZEOF_LONG == 8 */
+ *result &= 0xffffffff;
+ return true;
+}
+
+Dwfl_Frame_State *
+dwfl_frame_state_pid (Dwfl *dwfl, pid_t pid)
+{
+ char dirname[64];
+ int i = snprintf (dirname, sizeof (dirname), "/proc/%ld/task", (long) pid);
+ assert (i > 0 && i < (ssize_t) sizeof (dirname) - 1);
+ Dwfl_Frame_State_Process *process;
+ process = __libdwfl_process_alloc (dwfl, dwfl_frame_state_pid_memory_read,
+ NULL);
+ if (process == NULL)
+ return NULL;
+ process->memory_read_user_data = process;
+ for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
+ {
+ Dwfl_Error error = __libdwfl_module_getebl (mod);
+ if (error != DWFL_E_NOERROR)
+ continue;
+ process->ebl = mod->ebl;
+ }
+ if (process->ebl == NULL)
+ {
+ /* Not identified EBL from any of the modules. */
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_PROCESS_NO_ARCH);
+ return NULL;
+ }
+ DIR *dir = opendir (dirname);
+ if (dir == NULL)
+ {
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_PARSE_PROC);
+ return NULL;
+ }
+ for (;;)
+ {
+ errno = 0;
+ struct dirent *dirent = readdir (dir);
+ if (dirent == NULL)
+ {
+ if (errno == 0)
+ break;
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_PARSE_PROC);
+ return NULL;
+ }
+ if (strcmp (dirent->d_name, ".") == 0
+ || strcmp (dirent->d_name, "..") == 0)
+ continue;
+ char *end;
+ errno = 0;
+ long tidl = strtol (dirent->d_name, &end, 10);
+ if (errno != 0)
+ {
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_PARSE_PROC);
+ return NULL;
+ }
+ pid_t tid = tidl;
+ if (tidl <= 0 || (end && *end) || tid != tidl)
+ {
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_PARSE_PROC);
+ return NULL;
+ }
+ Dwfl_Frame_State_Thread *thread = __libdwfl_thread_alloc (process, tid);
+ if (thread == NULL)
+ {
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return NULL;
+ }
+ if (! tid_is_attached (dwfl, thread->tid))
+ {
+ if (! ptrace_attach (thread->tid))
+ {
+ __libdwfl_thread_free (thread);
+ continue;
+ }
+ thread->tid_attached = true;
+ }
+ Dwfl_Frame_State *state = thread->unwound;
+ if (! ebl_frame_state (state) || ! __libdwfl_state_fetch_pc (state))
+ {
+ __libdwfl_thread_free (thread);
+ continue;
+ }
+ }
+ if (closedir (dir) != 0)
+ {
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_PARSE_PROC);
+ return NULL;
+ }
+ if (process->thread == NULL)
+ {
+ /* No valid threads recognized. */
+ __libdwfl_process_free (process);
+ __libdwfl_seterrno (DWFL_E_NO_THREAD);
+ return NULL;
+ }
+ return process->thread->unwound;
+}
+INTDEF (dwfl_frame_state_pid)
diff --git a/libdwfl/dwfl_frame_thread_next.c b/libdwfl/dwfl_frame_thread_next.c
new file mode 100644
index 0000000..df4aebb
--- /dev/null
+++ b/libdwfl/dwfl_frame_thread_next.c
@@ -0,0 +1,37 @@
+/* Get innermost frame of the next thread from state.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#include "libdwflP.h"
+
+Dwfl_Frame_State *
+dwfl_frame_thread_next (Dwfl_Frame_State *state)
+{
+ Dwfl_Frame_State_Thread *thread_next = state->thread->next;
+ return thread_next ? thread_next->unwound : NULL;
+}
+INTDEF (dwfl_frame_thread_next)
diff --git a/libdwfl/dwfl_frame_tid_get.c b/libdwfl/dwfl_frame_tid_get.c
new file mode 100644
index 0000000..7883b5e
--- /dev/null
+++ b/libdwfl/dwfl_frame_tid_get.c
@@ -0,0 +1,36 @@
+/* Get Task ID of the thread of state.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#include "libdwflP.h"
+
+pid_t
+dwfl_frame_tid_get (Dwfl_Frame_State *state)
+{
+ return state->thread->tid;
+}
+INTDEF (dwfl_frame_tid_get)
diff --git a/libdwfl/dwfl_frame_unwind.c b/libdwfl/dwfl_frame_unwind.c
new file mode 100644
index 0000000..5705f51
--- /dev/null
+++ b/libdwfl/dwfl_frame_unwind.c
@@ -0,0 +1,500 @@
+/* Get previous frame state for an existing frame state.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "cfi.h"
+#include <stdlib.h>
+#include "libdwflP.h"
+#include "../libdw/dwarf.h"
+#include <sys/ptrace.h>
+
+#ifndef MAX
+# define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+static bool
+state_get_reg (Dwfl_Frame_State *state, unsigned regno, Dwarf_Addr *val)
+{
+ if (! dwfl_frame_state_reg_get (state, regno, val))
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_REGISTER);
+ return false;
+ }
+ return true;
+}
+
+static int
+bra_compar (const void *key_voidp, const void *elem_voidp)
+{
+ Dwarf_Word offset = (uintptr_t) key_voidp;
+ const Dwarf_Op *op = elem_voidp;
+ return (offset > op->offset) - (offset < op->offset);
+}
+
+/* FIXME: Handle bytecode deadlocks and overflows. */
+
+static bool
+expr_eval (Dwfl_Frame_State *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
+ size_t nops, Dwarf_Addr *result)
+{
+ Dwfl_Frame_State_Process *process = state->thread->process;
+ if (nops == 0)
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
+ Dwarf_Addr *stack = NULL;
+ size_t stack_used = 0, stack_allocated = 0;
+ bool
+ push (Dwarf_Addr val)
+ {
+ if (stack_used == stack_allocated)
+ {
+ stack_allocated = MAX (stack_allocated * 2, 32);
+ Dwarf_Addr *stack_new = realloc (stack, stack_allocated * sizeof (*stack));
+ if (stack_new == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return false;
+ }
+ stack = stack_new;
+ }
+ stack[stack_used++] = val;
+ return true;
+ }
+ bool
+ pop (Dwarf_Addr *val)
+ {
+ if (stack_used == 0)
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
+ *val = stack[--stack_used];
+ return true;
+ }
+ Dwarf_Addr val1, val2;
+ bool is_location = false;
+ for (const Dwarf_Op *op = ops; op < ops + nops; op++)
+ switch (op->atom)
+ {
+ case DW_OP_reg0 ... DW_OP_reg31:
+ if (! state_get_reg (state, op->atom - DW_OP_reg0, &val1)
+ || ! push (val1))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+ case DW_OP_regx:
+ if (! state_get_reg (state, op->number, &val1) || ! push (val1))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+ case DW_OP_breg0 ... DW_OP_breg31:
+ if (! state_get_reg (state, op->atom - DW_OP_breg0, &val1))
+ {
+ free (stack);
+ return false;
+ }
+ val1 += op->number;
+ if (! push (val1))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+ case DW_OP_bregx:
+ if (! state_get_reg (state, op->number, &val1))
+ {
+ free (stack);
+ return false;
+ }
+ val1 += op->number2;
+ if (! push (val1))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+ case DW_OP_lit0 ... DW_OP_lit31:
+ if (! push (op->atom - DW_OP_lit0))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+ case DW_OP_plus_uconst:
+ if (! pop (&val1) || ! push (val1 + op->number))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+ case DW_OP_call_frame_cfa:;
+ Dwarf_Op *cfa_ops;
+ size_t cfa_nops;
+ Dwarf_Addr cfa;
+ if (dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops) != 0
+ || ! expr_eval (state, frame, cfa_ops, cfa_nops, &cfa)
+ || ! push (cfa))
+ {
+ __libdwfl_seterrno (DWFL_E_LIBDW);
+ free (stack);
+ return false;
+ }
+ is_location = true;
+ break;
+ case DW_OP_stack_value:
+ is_location = false;
+ break;
+ case DW_OP_deref:
+ if (! pop (&val1)
+ || ! process->memory_read (val1, &val1,
+ process->memory_read_user_data)
+ || ! push (val1))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+ case DW_OP_nop:
+ break;
+ case DW_OP_dup:
+ if (! pop (&val1) || ! push (val1) || ! push (val1))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+ case DW_OP_const1u:
+ case DW_OP_const1s:
+ case DW_OP_const2u:
+ case DW_OP_const2s:
+ case DW_OP_const4u:
+ case DW_OP_const4s:
+ case DW_OP_const8u:
+ case DW_OP_const8s:
+ case DW_OP_constu:
+ case DW_OP_consts:
+ if (! push (op->number))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+ case DW_OP_bra:
+ if (! pop (&val1))
+ {
+ free (stack);
+ return false;
+ }
+ if (val1 == 0)
+ break;
+ /* FALLTHRU */
+ case DW_OP_skip:;
+ Dwarf_Word offset = op->offset + 1 + 2 + (int16_t) op->number;
+ const Dwarf_Op *found = bsearch ((void *) (uintptr_t) offset, ops, nops,
+ sizeof (*ops), bra_compar);
+ if (found == NULL)
+ {
+ free (stack);
+ /* PPC32 vDSO has such invalid operations. */
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
+ /* Undo the 'for' statement increment. */
+ op = found - 1;
+ break;
+ case DW_OP_drop:
+ if (! pop (&val1))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+#define BINOP(atom, op) \
+ case atom: \
+ if (! pop (&val2) || ! pop (&val1) || ! push (val1 op val2)) \
+ { \
+ free (stack); \
+ return false; \
+ } \
+ break;
+ BINOP (DW_OP_and, &)
+ BINOP (DW_OP_shl, <<)
+ BINOP (DW_OP_plus, +)
+ BINOP (DW_OP_mul, *)
+#undef BINOP
+#define BINOP_SIGNED(atom, op) \
+ case atom: \
+ if (! pop (&val2) || ! pop (&val1) \
+ || ! push ((int64_t) val1 op (int64_t) val2)) \
+ { \
+ free (stack); \
+ return false; \
+ } \
+ break;
+ BINOP_SIGNED (DW_OP_le, <=)
+ BINOP_SIGNED (DW_OP_ge, >=)
+ BINOP_SIGNED (DW_OP_eq, ==)
+ BINOP_SIGNED (DW_OP_lt, <)
+ BINOP_SIGNED (DW_OP_gt, >)
+ BINOP_SIGNED (DW_OP_ne, !=)
+#undef BINOP_SIGNED
+ default:
+ __libdwfl_seterrno (DWFL_E_UNSUPPORTED_DWARF);
+ return false;
+ }
+ if (! pop (result))
+ {
+ free (stack);
+ return false;
+ }
+ free (stack);
+ if (is_location && ! process->memory_read (*result, result,
+ process->memory_read_user_data))
+ return false;
+ return true;
+}
+
+/* Return TRUE and update *STATEP for the unwound frame for successful unwind.
+ Return TRUE and set *STATEP to NULL for the outermost frame. Return FALSE
+ (and call __libdwfl_seterrno) otherwise. */
+
+static bool
+have_unwound (Dwfl_Frame_State **statep)
+{
+ Dwfl_Frame_State *state = *statep, *unwound = state->unwound;
+ switch (unwound->pc_state)
+ {
+ case DWFL_FRAME_STATE_ERROR:
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ *statep = NULL;
+ return false;
+ case DWFL_FRAME_STATE_PC_SET:
+ *statep = unwound;
+ return true;
+ case DWFL_FRAME_STATE_PC_UNDEFINED:
+ *statep = NULL;
+ return true;
+ }
+ abort ();
+}
+
+/* Check if PC is in the "_start" function which may have no FDE.
+ It corresponds to the GDB get_prev_frame logic "inside entry func".
+ Return TRUE if PC is in an outer frame. Return FALSE (and call
+ __libdwfl_seterrno) otherwise. */
+
+static bool
+no_fde (Dwarf_Addr pc, Dwfl_Module *mod, Dwarf_Addr bias)
+{
+ GElf_Sym sym;
+ const char *symname = INTUSE(dwfl_module_addrsym) (mod, pc, &sym, NULL);
+ if (symname == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NO_DWARF);
+ return false;
+ }
+ /* It has no FDE on PPC64; it can be still unwound via the stack frame. */
+ if (strcmp (symname, ".generic_start_main") == 0)
+ return true;
+ GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (mod->main.elf, &ehdr_mem);
+ if (ehdr == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return false;
+ }
+ if (pc < ehdr->e_entry + bias)
+ {
+ __libdwfl_seterrno (DWFL_E_NO_DWARF);
+ return false;
+ }
+ /* "_start" is size-less. Search for PC, if the closest symbol is the one
+ for E_ENTRY it belongs into the function starting at E_ENTRY. */
+ if (sym.st_value != ehdr->e_entry + bias
+ || (sym.st_size != 0 && pc >= sym.st_value + sym.st_size))
+ {
+ __libdwfl_seterrno (DWFL_E_NO_DWARF);
+ return false;
+ }
+ return true;
+}
+
+/* The logic is to call __libdwfl_seterrno for any CFI bytecode interpretation
+ error so one can easily catch the problem with a debugger. Still there are
+ archs with invalid CFI for some registers where the registers are never used
+ later. Therefore we continue unwinding leaving the registers undefined.
+
+ The only exception is PC itself, when there is an error unwinding PC we
+ return false. Otherwise we would return successful end of backtrace seeing
+ an undefined PC register (due to an error unwinding it). */
+
+static bool
+handle_cfi (Dwfl_Frame_State **statep, Dwarf_Addr pc, Dwfl_Module *mod,
+ Dwarf_CFI *cfi, Dwarf_Addr bias)
+{
+ Dwfl_Frame_State *state = *statep;
+ Dwarf_Frame *frame;
+ if (INTUSE(dwarf_cfi_addrframe) (cfi, pc - bias, &frame) != 0)
+ {
+ int dw_errno = dwarf_errno ();
+ if (dw_errno == DWARF_E_NO_MATCH)
+ {
+ if (! no_fde (pc, mod, bias))
+ return false;
+ *statep = NULL;
+ return true;
+ }
+ __libdw_seterrno (dw_errno);
+ __libdwfl_seterrno (DWFL_E_LIBDW);
+ return false;
+ }
+ Dwfl_Frame_State_Thread *thread = state->thread;
+ Dwfl_Frame_State_Process *process = thread->process;
+ Ebl *ebl = process->ebl;
+ size_t nregs = ebl_frame_state_nregs (ebl);
+ Dwfl_Frame_State *unwound;
+ unwound = malloc (sizeof (*unwound) + sizeof (*unwound->regs) * nregs);
+ state->unwound = unwound;
+ unwound->thread = thread;
+ unwound->unwound = NULL;
+ unwound->signal_frame = frame->fde->cie->signal_frame;
+ unwound->pc_state = DWFL_FRAME_STATE_ERROR;
+ memset (unwound->regs_set, 0, sizeof (unwound->regs_set));
+ for (unsigned regno = 0; regno < nregs; regno++)
+ {
+ Dwarf_Op reg_ops_mem[3], *reg_ops;
+ size_t reg_nops;
+ if (dwarf_frame_register (frame, regno, reg_ops_mem, ®_ops,
+ ®_nops) != 0)
+ {
+ __libdwfl_seterrno (DWFL_E_LIBDW);
+ continue;
+ }
+ Dwarf_Addr regval;
+ if (reg_nops == 0)
+ {
+ if (reg_ops == reg_ops_mem)
+ {
+ /* REGNO is undefined. */
+ unsigned ra = frame->fde->cie->return_address_register;
+ if (regno == ra)
+ unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
+ continue;
+ }
+ else if (reg_ops == NULL)
+ {
+ /* REGNO is same-value. */
+ if (! state_get_reg (state, regno, ®val))
+ continue;
+ }
+ else
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ continue;
+ }
+ }
+ else if (! expr_eval (state, frame, reg_ops, reg_nops, ®val))
+ {
+ /* PPC32 vDSO has various invalid operations, ignore them. The
+ register will look as unset causing an error later, if used.
+ But PPC32 does not use such registers. */
+ continue;
+ }
+ if (! dwfl_frame_state_reg_set (unwound, regno, regval))
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_REGISTER);
+ continue;
+ }
+ }
+ if (unwound->pc_state == DWFL_FRAME_STATE_ERROR
+ && dwfl_frame_state_reg_get (unwound,
+ frame->fde->cie->return_address_register,
+ &unwound->pc))
+ {
+ /* PPC32 __libc_start_main properly CFI-unwinds PC as zero. Currently
+ none of the archs supported for unwinding have zero as a valid PC. */
+ if (unwound->pc == 0)
+ unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
+ else
+ unwound->pc_state = DWFL_FRAME_STATE_PC_SET;
+ }
+ return have_unwound (statep);
+}
+
+bool
+dwfl_frame_unwind (Dwfl_Frame_State **statep)
+{
+ Dwfl_Frame_State *state = *statep;
+ if (state->unwound)
+ return have_unwound (statep);
+ Dwarf_Addr pc;
+ bool ok = INTUSE(dwfl_frame_state_pc) (state, &pc, NULL);
+ assert (ok);
+ /* Do not ask for MINUSONE dwfl_frame_state_pc, it would try to unwind STATE
+ which would deadlock us. */
+ if (state != state->thread->unwound && ! state->signal_frame)
+ pc--;
+ Dwfl_Module *mod = INTUSE(dwfl_addrmodule) (state->thread->process->dwfl, pc);
+ if (mod != NULL)
+ {
+ Dwarf_Addr bias;
+ Dwarf_CFI *cfi_eh = INTUSE(dwfl_module_eh_cfi) (mod, &bias);
+ if (cfi_eh)
+ {
+ if (handle_cfi (statep, pc, mod, cfi_eh, bias))
+ return true;
+ if (state->unwound)
+ {
+ assert (state->unwound->pc_state == DWFL_FRAME_STATE_ERROR);
+ return false;
+ }
+ }
+ Dwarf_CFI *cfi_dwarf = INTUSE(dwfl_module_dwarf_cfi) (mod, &bias);
+ if (cfi_dwarf)
+ {
+ if (handle_cfi (statep, pc, mod, cfi_dwarf, bias) && state->unwound)
+ return true;
+ if (state->unwound)
+ {
+ assert (state->unwound->pc_state == DWFL_FRAME_STATE_ERROR);
+ return false;
+ }
+ }
+ }
+ __libdwfl_seterrno (DWFL_E_NO_DWARF);
+ return false;
+}
+INTDEF(dwfl_frame_unwind)
diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h
index c2b85f4..dee3e2f 100644
--- a/libdwfl/libdwfl.h
+++ b/libdwfl/libdwfl.h
@@ -41,6 +41,10 @@ typedef struct Dwfl_Module Dwfl_Module;
/* Handle describing a line record. */
typedef struct Dwfl_Line Dwfl_Line;
+/* This holds everything we know about the state of the frame at a particular
+ PC location described by an FDE. */
+typedef struct Dwfl_Frame_State Dwfl_Frame_State;
+
/* Callbacks. */
typedef struct
{
@@ -573,6 +577,46 @@ extern int dwfl_module_register_names (Dwfl_Module *mod,
extern Dwarf_CFI *dwfl_module_dwarf_cfi (Dwfl_Module *mod, Dwarf_Addr *bias);
extern Dwarf_CFI *dwfl_module_eh_cfi (Dwfl_Module *mod, Dwarf_Addr *bias);
+/* Get innermost frame of first thread of live process PID. Returns NULL on
+ failure. */
+extern Dwfl_Frame_State *dwfl_frame_state_pid (Dwfl *dwfl, pid_t pid);
+
+/* Get innermost frame of first thread of core file COREFILE. Returns NULL on
+ failure. */
+extern Dwfl_Frame_State *dwfl_frame_state_core (Dwfl *dwfl,
+ const char *corefile);
+
+/* Fetch inferior registers from a caller supplied storage. */
+typedef bool dwfl_frame_memory_read_t (Dwarf_Addr addr, Dwarf_Addr *result,
+ void *user_data);
+extern Dwfl_Frame_State *dwfl_frame_state_data (Dwfl *dwfl, bool pc_set,
+ Dwarf_Addr pc, unsigned nregs,
+ const uint64_t *regs_set,
+ const Dwarf_Addr *regs,
+ dwfl_frame_memory_read_t
+ *memory_read,
+ void *memory_read_user_data);
+
+/* Return TRUE and update *STATEP for the unwound frame for successful unwind.
+ Return TRUE and set *STATEP to NULL for the outermost frame. Return FALSE
+ (and call __libdwfl_seterrno) otherwise. */
+extern bool dwfl_frame_unwind (Dwfl_Frame_State **statep);
+
+/* Get return address register value for frame. Return TRUE if *PC set and
+ optionally *MINUSONE is also set, if MINUSONE is not NULL. Return FALSE
+ (and call __libdw_seterrno) otherwise. *MINUSONE is TRUE for normal calls
+ where *PC should be decremented by one to get the call instruction, it is
+ FALSE if this frame was interrupted by a signal handler. */
+extern bool dwfl_frame_state_pc (Dwfl_Frame_State *state, Dwarf_Addr *pc,
+ bool *minusone);
+
+/* Get innermost frame of the next thread from STATE. STATE can be any frame
+ of (the previous) thread. */
+extern Dwfl_Frame_State *dwfl_frame_thread_next (Dwfl_Frame_State *state);
+
+/* Get Task ID of the thread of STATE. This is PID for the thread started by
+ function main and gettid () for the other threads. */
+extern pid_t dwfl_frame_tid_get (Dwfl_Frame_State *state);
#ifdef __cplusplus
}
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index e5e638c..3999d66 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -41,6 +41,10 @@
#include <string.h>
#include "../libdw/libdwP.h" /* We need its INTDECLs. */
+#include "libeblP.h"
+
+typedef struct Dwfl_Frame_State_Process Dwfl_Frame_State_Process;
+typedef struct Dwfl_Frame_State_Thread Dwfl_Frame_State_Thread;
/* gettext helper macros. */
#define _(Str) dgettext ("elfutils", Str)
@@ -74,7 +78,16 @@
DWFL_ERROR (BADELF, N_("not a valid ELF file")) \
DWFL_ERROR (WEIRD_TYPE, N_("cannot handle DWARF type description")) \
DWFL_ERROR (WRONG_ID_ELF, N_("ELF file does not match build ID")) \
- DWFL_ERROR (BAD_PRELINK, N_("corrupt .gnu.prelink_undo section data"))
+ DWFL_ERROR (BAD_PRELINK, N_("corrupt .gnu.prelink_undo section data")) \
+ DWFL_ERROR (LIBEBL_BAD, N_("Internal error due to ebl")) \
+ DWFL_ERROR (CORE_MISSING, N_("Missing data in core file")) \
+ DWFL_ERROR (INVALID_REGISTER, N_("Invalid register")) \
+ DWFL_ERROR (PROCESS_MEMORY_READ, N_("Error reading process memory")) \
+ DWFL_ERROR (PROCESS_NO_ARCH, N_("Have not found ELF module in a process")) \
+ DWFL_ERROR (PARSE_PROC, N_("Error parsing /proc filesystem")) \
+ DWFL_ERROR (NO_THREAD, N_("No thread found")) \
+ DWFL_ERROR (INVALID_DWARF, N_("Invalid DWARF")) \
+ DWFL_ERROR (UNSUPPORTED_DWARF, N_("Unsupported DWARF"))
#define DWFL_ERROR(name, text) DWFL_E_##name,
typedef enum { DWFL_ERRORS DWFL_E_NUM } Dwfl_Error;
@@ -92,6 +105,8 @@ struct Dwfl
Dwfl_Module *modulelist; /* List in order used by full traversals. */
+ Dwfl_Frame_State_Process *framestatelist;
+
GElf_Addr offline_next_address;
GElf_Addr segment_align; /* Smallest granularity of segments. */
@@ -182,7 +197,102 @@ struct Dwfl_Module
bool gc; /* Mark/sweep flag. */
};
+/* This holds information common for all the threads/tasks/TIDs of one process
+ for backtraces. */
+
+struct Dwfl_Frame_State_Process
+{
+ Dwfl_Frame_State_Process *next;
+ struct Dwfl *dwfl;
+ struct ebl *ebl;
+ /* If it is false we share EBL with one of DWFL's Dwfl_Module->ebl. */
+ bool ebl_close : 1;
+ dwfl_frame_memory_read_t *memory_read;
+ void *memory_read_user_data;
+ /* If there is no core file both CORE is NULL and CORE_FD is -1. */
+ Elf *core;
+ int core_fd;
+ Dwfl_Frame_State_Thread *thread;
+};
+
+/* This holds information common for all the frames of one backtrace for
+ a partical thread/task/TID. */
+
+struct Dwfl_Frame_State_Thread
+{
+ Dwfl_Frame_State_Process *process;
+ Dwfl_Frame_State_Thread *next;
+ /* If there is no TID it is 0. */
+ pid_t tid;
+ bool tid_attached : 1;
+ /* Bottom frame. */
+ Dwfl_Frame_State *unwound;
+};
+
+/* See its typedef in libdwfl.h. */
+
+struct Dwfl_Frame_State
+{
+ Dwfl_Frame_State_Thread *thread;
+ /* Previous (outer) frame. */
+ Dwfl_Frame_State *unwound;
+ bool signal_frame : 1;
+ enum
+ {
+ /* This structure is still being initialized or there was an error
+ initializing it. */
+ DWFL_FRAME_STATE_ERROR,
+ /* PC field is valid. */
+ DWFL_FRAME_STATE_PC_SET,
+ /* PC field is undefined, this means the next (inner) frame was the
+ outermost frame. */
+ DWFL_FRAME_STATE_PC_UNDEFINED
+ } pc_state;
+ /* Either initialized from appropriate REGS element or on some archs
+ initialized separately as the return address has no DWARF register. */
+ Dwarf_Addr pc;
+ /* (1 << X) bitmask where 0 <= X < ebl_frame_state_nregs. */
+ uint64_t regs_set[3];
+ /* REGS array size is ebl_frame_state_nregs. */
+ Dwarf_Addr regs[];
+};
+
+/* Fetch value from Dwfl_Frame_State->regs indexed by DWARF REGNO.
+ No error code is set if the function returns FALSE. */
+
+static inline bool
+dwfl_frame_state_reg_get (Dwfl_Frame_State *state, unsigned regno,
+ Dwarf_Addr *val)
+{
+ Ebl *ebl = state->thread->process->ebl;
+ if (regno >= ebl->frame_state_nregs)
+ return false;
+ if ((state->regs_set[regno / sizeof (*state->regs_set) / 8]
+ & (1U << (regno % (sizeof (*state->regs_set) * 8)))) == 0)
+ return false;
+ if (val)
+ *val = state->regs[regno];
+ return true;
+}
+/* Store value to Dwfl_Frame_State->regs indexed by DWARF REGNO.
+ No error code is set if the function returns FALSE. */
+
+static inline bool
+dwfl_frame_state_reg_set (Dwfl_Frame_State *state, unsigned regno,
+ Dwarf_Addr val)
+{
+ Ebl *ebl = state->thread->process->ebl;
+ if (regno >= ebl->frame_state_nregs)
+ return false;
+ /* For example i386 user_regs_struct has signed fields. */
+ if (ebl->class == ELFCLASS32)
+ val &= 0xffffffff;
+ state->regs_set[regno / sizeof (*state->regs_set) / 8] |=
+ (1U << (regno % (sizeof (*state->regs_set) * 8)));
+ state->regs[regno] = val;
+ return true;
+}
/* Information cached about each CU in Dwfl_Module.dw. */
struct dwfl_cu
@@ -365,6 +475,36 @@ extern Dwfl_Module *__libdwfl_report_offline (Dwfl *dwfl, const char *name,
const char *))
internal_function;
+/* Set STATE->pc_set from STATE->regs according to the backend. Return true on
+ success, false on error. */
+extern bool __libdwfl_state_fetch_pc (Dwfl_Frame_State *state)
+ internal_function;
+
+/* Free and unlink THREAD from the internal lists. */
+extern void __libdwfl_thread_free (Dwfl_Frame_State_Thread *thread)
+ internal_function;
+
+/* Allocate new Dwfl_Frame_State_Thread for PID and link it to PROCESS.
+ Automatically create and link in also the first Dwfl_Frame_State. */
+extern Dwfl_Frame_State_Thread *
+ __libdwfl_thread_alloc (Dwfl_Frame_State_Process *process, pid_t tid)
+ internal_function;
+
+/* Free PROCESS. Unlink and free also any structures it references. */
+extern void __libdwfl_process_free (Dwfl_Frame_State_Process *process)
+ internal_function;
+
+/* Allocate new Dwfl_Frame_State_Process for DWFL with callback MEMORY_READ
+ (which is passed MEMORY_READ_USER_DATA). */
+extern Dwfl_Frame_State_Process *
+ __libdwfl_process_alloc (Dwfl *dwfl, dwfl_frame_memory_read_t *memory_read,
+ void *memory_read_user_data)
+ internal_function;
+
+/* Align segment START downwards or END upwards addresses according to DWFL. */
+extern GElf_Addr __libdwfl_segment_start (Dwfl *dwfl, GElf_Addr start);
+extern GElf_Addr __libdwfl_segment_end (Dwfl *dwfl, GElf_Addr end);
+
/* Decompression wrappers: decompress whole file into memory. */
extern Dwfl_Error __libdw_gunzip (int fd, off64_t start_offset,
void *mapped, size_t mapped_size,
@@ -479,6 +619,10 @@ INTDECL (dwfl_offline_section_address)
INTDECL (dwfl_module_relocate_address)
INTDECL (dwfl_module_dwarf_cfi)
INTDECL (dwfl_module_eh_cfi)
+INTDECL (dwfl_frame_state_pid)
+INTDECL (dwfl_frame_state_core)
+INTDECL (dwfl_frame_unwind)
+INTDECL (dwfl_frame_state_pc)
/* Leading arguments standard to callbacks passed a Dwfl_Module. */
#define MODCB_ARGS(mod) (mod), &(mod)->userdata, (mod)->name, (mod)->low_addr
diff --git a/libdwfl/segment.c b/libdwfl/segment.c
index 496b4fd..3a2f349 100644
--- a/libdwfl/segment.c
+++ b/libdwfl/segment.c
@@ -28,16 +28,18 @@
#include "libdwflP.h"
-static GElf_Addr
-segment_start (Dwfl *dwfl, GElf_Addr start)
+GElf_Addr
+internal_function
+__libdwfl_segment_start (Dwfl *dwfl, GElf_Addr start)
{
if (dwfl->segment_align > 1)
start &= -dwfl->segment_align;
return start;
}
-static GElf_Addr
-segment_end (Dwfl *dwfl, GElf_Addr end)
+GElf_Addr
+internal_function
+__libdwfl_segment_end (Dwfl *dwfl, GElf_Addr end)
{
if (dwfl->segment_align > 1)
end = (end + dwfl->segment_align - 1) & -dwfl->segment_align;
@@ -156,8 +158,8 @@ reify_segments (Dwfl *dwfl)
for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
if (! mod->gc)
{
- const GElf_Addr start = segment_start (dwfl, mod->low_addr);
- const GElf_Addr end = segment_end (dwfl, mod->high_addr);
+ const GElf_Addr start = __libdwfl_segment_start (dwfl, mod->low_addr);
+ const GElf_Addr end = __libdwfl_segment_end (dwfl, mod->high_addr);
bool resized = false;
int idx = lookup (dwfl, start, hint);
@@ -296,8 +298,9 @@ dwfl_report_segment (Dwfl *dwfl, int ndx, const GElf_Phdr *phdr, GElf_Addr bias,
dwfl->lookup_module = NULL;
}
- GElf_Addr start = segment_start (dwfl, bias + phdr->p_vaddr);
- GElf_Addr end = segment_end (dwfl, bias + phdr->p_vaddr + phdr->p_memsz);
+ GElf_Addr start = __libdwfl_segment_start (dwfl, bias + phdr->p_vaddr);
+ GElf_Addr end = __libdwfl_segment_end (dwfl,
+ bias + phdr->p_vaddr + phdr->p_memsz);
/* Coalesce into the last one if contiguous and matching. */
if (ndx != dwfl->lookup_tail_ndx
diff --git a/libebl/Makefile.am b/libebl/Makefile.am
index 1ab08e7..1e39689 100644
--- a/libebl/Makefile.am
+++ b/libebl/Makefile.am
@@ -29,7 +29,8 @@
##
include $(top_srcdir)/config/eu.am
AM_CFLAGS += -fpic
-INCLUDES += -I$(srcdir)/../libelf -I$(srcdir)/../libdw -I$(srcdir)/../libasm
+INCLUDES += -I$(srcdir)/../libelf -I$(srcdir)/../libdw -I$(srcdir)/../libasm \
+ -I$(srcdir)/../libdwfl
VERSION = 1
LIBEBL_SUBDIR = @LIBEBL_SUBDIR@
@@ -54,7 +55,7 @@ gen_SOURCES = eblopenbackend.c eblclosebackend.c eblstrtab.c \
eblreginfo.c eblnonerelocp.c eblrelativerelocp.c \
eblsysvhashentrysize.c eblauxvinfo.c eblcheckobjattr.c \
ebl_check_special_section.c ebl_syscall_abi.c eblabicfi.c \
- eblstother.c eblgetfuncpc.c
+ eblstother.c eblgetfuncpc.c eblframestate.c
libebl_a_SOURCES = $(gen_SOURCES)
diff --git a/libebl/ebl-hooks.h b/libebl/ebl-hooks.h
index b39131d..5b77f92 100644
--- a/libebl/ebl-hooks.h
+++ b/libebl/ebl-hooks.h
@@ -161,5 +161,12 @@ int EBLHOOK(abi_cfi) (Ebl *ebl, Dwarf_CIE *abi_info);
const char *EBLHOOK(get_func_pc) (Ebl *ebl, struct Dwfl_Module *mod,
GElf_Sym *sym);
+/* Fetch process data from STATE->base->pid or STATE->base->core. */
+bool EBLHOOK(frame_state) (struct Dwfl_Frame_State *state);
+
+/* Number of Dwarf_Frame_State->regs entries to allocate for frame_state
+ above. */
+size_t EBLHOOKVAR(frame_state_nregs);
+
/* Destructor for ELF backend handle. */
void EBLHOOK(destr) (struct ebl *);
diff --git a/libebl/eblframestate.c b/libebl/eblframestate.c
new file mode 100644
index 0000000..a7cc13c
--- /dev/null
+++ b/libebl/eblframestate.c
@@ -0,0 +1,52 @@
+/* Fetch live process Dwfl_Frame_State from PID.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <libeblP.h>
+#include <assert.h>
+#include "libdwflP.h"
+
+bool
+ebl_frame_state (Dwfl_Frame_State *state)
+{
+ Dwfl_Frame_State_Process *process = state->thread->process;
+ Ebl *ebl = process->ebl;
+ /* Otherwise caller could not allocate STATE of proper size. If FRAME_STATE
+ is unsupported then FRAME_STATE_NREGS is zero. */
+ assert (ebl->frame_state != NULL);
+ return ebl->frame_state (state);
+}
+
+size_t
+ebl_frame_state_nregs (Ebl *ebl)
+{
+ return ebl == NULL ? 0 : ebl->frame_state_nregs;
+}
diff --git a/libebl/libebl.h b/libebl/libebl.h
index 6c50769..09b7bfb 100644
--- a/libebl/libebl.h
+++ b/libebl/libebl.h
@@ -384,6 +384,14 @@ extern const char *ebl_get_func_pc (Ebl *ebl, struct Dwfl_Module *mod,
GElf_Sym *sym)
__nonnull_attribute__ (1, 2, 3);
+/* Fetch process data from STATE->base->pid or STATE->base->core. */
+struct Dwfl_Frame_State;
+extern bool ebl_frame_state (struct Dwfl_Frame_State *state)
+ __nonnull_attribute__ (1);
+
+/* Number of registers to allocate for STATE of ebl_frame_state. */
+extern size_t ebl_frame_state_nregs (Ebl *ebl)
+ __nonnull_attribute__ (1);
#ifdef __cplusplus
}
diff --git a/m4/biarch.m4 b/m4/biarch.m4
new file mode 100644
index 0000000..a15323e
--- /dev/null
+++ b/m4/biarch.m4
@@ -0,0 +1,45 @@
+AC_DEFUN([utrace_CC_m32], [dnl
+AC_CACHE_CHECK([$CC option for 32-bit word size], utrace_cv_CC_m32, [dnl
+save_CC="$CC"
+utrace_cv_CC_m32=none
+for ut_try in -m32 -m31; do
+ [CC=`echo "$save_CC" | sed 's/ -m[36][241]//'`" $ut_try"]
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int foo (void) { return 1; }]])],
+ [utrace_cv_CC_m32=$ut_try])
+ test x$utrace_cv_CC_m32 = xnone || break
+done
+CC="$save_CC"])])
+
+AC_DEFUN([utrace_HOST64], [AC_REQUIRE([utrace_CC_m32])
+AS_IF([test x$utrace_cv_CC_m32 != xnone], [dnl
+AC_CACHE_CHECK([for 64-bit host], utrace_cv_host64, [dnl
+AC_EGREP_CPP([@utrace_host64@], [#include <stdint.h>
+#if (UINTPTR_MAX > 0xffffffffUL)
+@utrace_host64@
+#endif],
+ utrace_cv_host64=yes, utrace_cv_host64=no)])
+AS_IF([test $utrace_cv_host64 = no],
+ [utrace_biarch=-m64 utrace_thisarch=$utrace_cv_CC_m32],
+ [utrace_biarch=$utrace_cv_CC_m32 utrace_thisarch=-m64])
+
+biarch_CC=`echo "$CC" | sed "s/ *${utrace_thisarch}//"`
+biarch_CC="$biarch_CC $utrace_biarch"])])
+
+AC_DEFUN([utrace_BIARCH], [AC_REQUIRE([utrace_HOST64])
+utrace_biarch_forced=no
+AC_ARG_WITH([biarch],
+ AC_HELP_STRING([--with-biarch],
+ [enable biarch tests despite build problems]),
+ [AS_IF([test "x$with_biarch" != xno], [utrace_biarch_forced=yes])])
+AS_IF([test $utrace_biarch_forced = yes], [dnl
+utrace_cv_cc_biarch=yes
+AC_MSG_NOTICE([enabling biarch tests regardless using $biarch_CC])], [dnl
+AS_IF([test x$utrace_cv_CC_m32 != xnone], [dnl
+AC_CACHE_CHECK([whether $biarch_CC makes executables we can run],
+ utrace_cv_cc_biarch, [dnl
+save_CC="$CC"
+CC="$biarch_CC"
+AC_RUN_IFELSE([AC_LANG_PROGRAM([], [])],
+ utrace_cv_cc_biarch=yes, utrace_cv_cc_biarch=no)
+CC="$save_CC"])], [utrace_cv_cc_biarch=no])])
+AM_CONDITIONAL(BIARCH, [test $utrace_cv_cc_biarch = yes])])
diff --git a/src/Makefile.am b/src/Makefile.am
index cf4875e..2fa5009 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -37,7 +37,7 @@ native_ld = @native_ld@
base_cpu = @base_cpu@
bin_PROGRAMS = readelf nm size strip ld elflint findtextrel addr2line \
- elfcmp objdump ranlib strings ar unstrip
+ elfcmp objdump ranlib strings ar unstrip stack
ld_dsos = libld_elf_i386_pic.a
@@ -115,6 +115,7 @@ ranlib_LDADD = libar.a $(libelf) $(libeu) $(libmudflap)
strings_LDADD = $(libelf) $(libeu) $(libmudflap)
ar_LDADD = libar.a $(libelf) $(libeu) $(libmudflap)
unstrip_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(libmudflap) -ldl
+stack_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(libmudflap) -ldl
ldlex.o: ldscript.c
ldlex_no_Werror = yes
diff --git a/src/stack.c b/src/stack.c
new file mode 100644
index 0000000..dd7be04
--- /dev/null
+++ b/src/stack.c
@@ -0,0 +1,145 @@
+/* Unwinding of frames like gstack/pstack.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <assert.h>
+#include <argp.h>
+#include <error.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <locale.h>
+#include ELFUTILS_HEADER(dwfl)
+
+/* libdwfl/argp-std.c */
+#define OPT_COREFILE 0x101
+
+static void
+report_pid (Dwfl *dwfl, pid_t pid)
+{
+ int result = dwfl_linux_proc_report (dwfl, pid);
+ if (result < 0)
+ error (2, 0, "dwfl_linux_proc_report: %s", dwfl_errmsg (-1));
+ else if (result > 0)
+ error (2, result, "dwfl_linux_proc_report");
+
+ if (dwfl_report_end (dwfl, NULL, NULL) != 0)
+ error (2, 0, "dwfl_report_end: %s", dwfl_errmsg (-1));
+}
+
+static void
+dump (Dwfl *dwfl, pid_t pid, const char *corefile)
+{
+ if (pid)
+ report_pid (dwfl, pid);
+ Dwfl_Frame_State *state;
+ if (pid)
+ state = dwfl_frame_state_pid (dwfl, pid);
+ else if (corefile)
+ state = dwfl_frame_state_core (dwfl, corefile);
+ else
+ abort ();
+ if (state == NULL)
+ error (2, 0, "dwfl_frame_state: %s", dwfl_errmsg (-1));
+ do
+ {
+ Dwfl_Frame_State *thread = state;
+ pid_t tid = dwfl_frame_tid_get (thread);
+ printf ("TID %ld:\n", (long) tid);
+ unsigned frameno;
+ for (frameno = 0; state; frameno++)
+ {
+ Dwarf_Addr pc;
+ bool minusone;
+ if (! dwfl_frame_state_pc (state, &pc, &minusone))
+ {
+ fprintf (stderr, "%s\n", dwfl_errmsg (-1));
+ break;
+ }
+ Dwarf_Addr pc_adjusted = pc - (minusone ? 1 : 0);
+
+ /* Get PC->SYMNAME. */
+ Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted);
+ const char *symname = NULL;
+ if (mod)
+ symname = dwfl_module_addrname (mod, pc_adjusted);
+
+ printf ("#%2u %#" PRIx64 "%4s\t%s\n", frameno, (uint64_t) pc,
+ minusone ? "- 1" : "", symname);
+ if (! dwfl_frame_unwind (&state))
+ {
+ fprintf (stderr, "%s\n", dwfl_errmsg (-1));
+ break;
+ }
+ }
+ state = dwfl_frame_thread_next (thread);
+ }
+ while (state);
+
+ dwfl_end (dwfl);
+}
+
+static argp_parser_t parse_opt_orig;
+static pid_t pid;
+static const char *corefile;
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case 'p':
+ pid = atoi (arg);
+ break;
+ case OPT_COREFILE:
+ corefile = arg;
+ break;
+ }
+ return parse_opt_orig (key, arg, state);
+}
+
+int
+main (int argc, char **argv)
+{
+ /* We use no threads here which can interfere with handling a stream. */
+ __fsetlocking (stdin, FSETLOCKING_BYCALLER);
+ __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+ __fsetlocking (stderr, FSETLOCKING_BYCALLER);
+
+ /* Set locale. */
+ (void) setlocale (LC_ALL, "");
+
+ struct argp argp = *dwfl_standard_argp ();
+ parse_opt_orig = argp.parser;
+ argp.parser = parse_opt;
+ int remaining;
+ Dwfl *dwfl = NULL;
+ argp_parse (&argp, argc, argv, 0, &remaining, &dwfl);
+ assert (dwfl != NULL);
+ assert (remaining == argc);
+
+ if (pid && !corefile)
+ dump (dwfl, pid, NULL);
+ else if (corefile && !pid)
+ dump (dwfl, 0, corefile);
+ else
+ error (2, 0, "eu-stack [--debuginfo-path=<path>] {-p <process id>|"
+ "--core=<file> [--executable=<file>]|--help}");
+
+ return 0;
+}
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a1ed284..b72bcc4 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -51,10 +51,24 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
dwfl-bug-getmodules dwarf-getmacros addrcfi \
test-flag-nobits dwarf-getstring rerequest_tag \
alldts md5-sha1-test typeiter low_high_pc \
- test-elf_cntl_gelf_getshdr dwfl-report-elf-align
+ test-elf_cntl_gelf_getshdr dwfl-report-elf-align backtrace \
+ backtrace-child backtrace-data
asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
asm-tst6 asm-tst7 asm-tst8 asm-tst9
+BUILT_SOURCES = backtrace-child-biarch
+
+clean-local:
+ $(RM) backtrace-child-biarch
+
+# Substitute $(COMPILE).
+backtrace-child-biarch: backtrace-child.c
+ $(CC_BIARCH) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS) $(backtrace_child_CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) $(backtrace_child_LDFLAGS) \
+ -o $@ $<
+
TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
update1 update2 update3 update4 \
run-show-die-info.sh run-get-files.sh run-get-lines.sh \
@@ -85,7 +99,8 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
run-readelf-d.sh run-readelf-gdb_index.sh run-unstrip-n.sh \
run-low_high_pc.sh run-macro-test.sh run-elf_cntl_gelf_getshdr.sh \
run-test-archive64.sh run-readelf-vmcoreinfo.sh \
- run-readelf-mixed-corenote.sh run-dwfl-report-elf-align.sh
+ run-readelf-mixed-corenote.sh run-dwfl-report-elf-align.sh \
+ run-backtrace.sh
if !STANDALONE
check_PROGRAMS += msg_tst md5-sha1-test
@@ -290,6 +305,10 @@ typeiter_LDADD = $(libdw) $(libelf) $(libmudflap)
low_high_pc_LDADD = $(libdw) $(libelf) $(libmudflap)
test_elf_cntl_gelf_getshdr_LDADD = $(libelf) $(libmudflap)
dwfl_report_elf_align_LDADD = $(libdw) $(libmudflap)
+backtrace_LDADD = $(libdw) $(libelf) $(libmudflap)
+backtrace_child_CFLAGS = -fPIE
+backtrace_child_LDFLAGS = -pie -pthread
+backtrace_data_LDADD = $(libdw) $(libelf) $(libmudflap)
if GCOV
check: check-am coverage
diff --git a/tests/backtrace-child.c b/tests/backtrace-child.c
new file mode 100644
index 0000000..68ea3a7
--- /dev/null
+++ b/tests/backtrace-child.c
@@ -0,0 +1,147 @@
+/* Test child for parent backtrace test.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/ptrace.h>
+#include <string.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+static int ptraceme, gencore;
+
+/* Execution will arrive here from jmp by an artificial ptrace-spawn signal. */
+
+static void
+sigusr2 (int signo)
+{
+ assert (signo == SIGUSR2);
+ if (! gencore)
+ raise (SIGUSR1);
+
+ /* Catch the .plt jump, it will come from this abort call. */
+ abort ();
+}
+
+static __attribute__ ((noinline, noclone)) void
+dummy1 (void)
+{
+ asm volatile ("");
+}
+
+#ifdef __x86_64__
+static __attribute__ ((noinline, noclone, used)) void
+jmp (void)
+{
+ /* Not reached, signal will get ptrace-spawn to jump into sigusr2. */
+ abort ();
+}
+#endif
+
+static __attribute__ ((noinline, noclone)) void
+dummy2 (void)
+{
+ asm volatile ("");
+}
+
+static __attribute__ ((noinline, noclone, noreturn)) void
+stdarg (int f __attribute__ ((unused)), ...)
+{
+ sighandler_t sigusr2_orig = signal (SIGUSR2, sigusr2);
+ assert (sigusr2_orig == SIG_DFL);
+ errno = 0;
+ if (ptraceme)
+ {
+ long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
+ assert_perror (errno);
+ assert (l == 0);
+ }
+#ifdef __x86_64__
+ if (! gencore)
+ {
+ /* Execution will get PC patched into function jmp. */
+ raise (SIGUSR1);
+ }
+#endif
+ sigusr2 (SIGUSR2);
+ abort ();
+}
+
+static __attribute__ ((noinline, noclone)) void
+dummy3 (void)
+{
+ asm volatile ("");
+}
+
+static __attribute__ ((noinline, noclone)) void
+backtracegen (void)
+{
+ stdarg (1);
+ /* Here should be no instruction after the stdarg call as it is noreturn
+ function. It must be stdarg so that it is a call and not jump (jump as
+ a tail-call). */
+}
+
+static __attribute__ ((noinline, noclone)) void
+dummy4 (void)
+{
+ asm volatile ("");
+}
+
+static void *
+start (void *arg __attribute__ ((unused)))
+{
+ backtracegen ();
+ abort ();
+}
+
+int
+main (int argc __attribute__ ((unused)), char **argv)
+{
+ assert (*argv++);
+ ptraceme = (*argv && strcmp (*argv, "--ptraceme") == 0);
+ argv += ptraceme;
+ gencore = (*argv && strcmp (*argv, "--gencore") == 0);
+ argv += gencore;
+ assert (*argv && strcmp (*argv, "--run") == 0);
+ dummy1 ();
+ dummy2 ();
+ dummy3 ();
+ dummy4 ();
+ if (gencore)
+ printf ("%ld\n", (long) getpid ());
+ errno = 0;
+ pthread_t thread;
+ int i = pthread_create (&thread, NULL, start, NULL);
+ assert_perror (errno);
+ assert (i == 0);
+ if (ptraceme)
+ {
+ long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
+ assert_perror (errno);
+ assert (l == 0);
+ }
+ if (gencore)
+ pthread_join (thread, NULL);
+ else
+ raise (SIGUSR2);
+ abort ();
+}
diff --git a/tests/backtrace-data.c b/tests/backtrace-data.c
new file mode 100644
index 0000000..bdaf99d
--- /dev/null
+++ b/tests/backtrace-data.c
@@ -0,0 +1,255 @@
+/* Test program for unwinding of frames.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <locale.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <error.h>
+#include <unistd.h>
+#include <dwarf.h>
+#include <sys/resource.h>
+#include <sys/ptrace.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/user.h>
+#include <fcntl.h>
+#include <string.h>
+#include ELFUTILS_HEADER(dwfl)
+
+#ifndef __x86_64__
+
+int
+main (void)
+{
+ return 77;
+}
+
+#else /* __x86_64__ */
+
+static int
+find_elf (Dwfl_Module *mod __attribute__ ((unused)),
+ void **userdata __attribute__ ((unused)),
+ const char *modname __attribute__ ((unused)),
+ Dwarf_Addr base __attribute__ ((unused)),
+ char **file_name __attribute__ ((unused)),
+ Elf **elfp __attribute__ ((unused)))
+{
+ /* Not used as modules are reported explicitly. */
+ assert (0);
+}
+
+static bool
+memory_read (Dwarf_Addr addr, Dwarf_Addr *result, void *user_data)
+{
+ pid_t child = (uintptr_t) user_data;
+
+ errno = 0;
+ long l = ptrace (PTRACE_PEEKDATA, child, (void *) (uintptr_t) addr, NULL);
+ assert_perror (errno);
+ *result = l;
+
+ /* We could also return false for failed ptrace. */
+ return true;
+}
+
+/* Return filename and VMA address *BASEP where its mapping starts which
+ contains ADDR. */
+
+static char *
+maps_lookup (pid_t pid, Dwarf_Addr addr, GElf_Addr *basep)
+{
+ char *fname;
+ int i = asprintf (&fname, "/proc/%ld/maps", (long) pid);
+ assert_perror (errno);
+ assert (i > 0);
+ FILE *f = fopen (fname, "r");
+ assert_perror (errno);
+ assert (f);
+ free (fname);
+ for (;;)
+ {
+ // 37e3c22000-37e3c23000 rw-p 00022000 00:11 49532 /lib64/ld-2.14.90.so */
+ unsigned long start, end, offset;
+ i = fscanf (f, "%lx-%lx %*s %lx %*x:%*x %*x", &start, &end, &offset);
+ assert_perror (errno);
+ assert (i == 3);
+ char *filename = strdup ("");
+ assert (filename);
+ size_t filename_len = 0;
+ for (;;)
+ {
+ int c = fgetc (f);
+ assert (c != EOF);
+ if (c == '\n')
+ break;
+ if (c == ' ' && *filename == '\0')
+ continue;
+ filename = realloc (filename, filename_len + 2);
+ assert (filename);
+ filename[filename_len++] = c;
+ filename[filename_len] = '\0';
+ }
+ if (start <= addr && addr < end)
+ {
+ i = fclose (f);
+ assert_perror (errno);
+ assert (i == 0);
+
+ *basep = start - offset;
+ return filename;
+ }
+ free (filename);
+ }
+}
+
+/* Add module containing ADDR to the DWFL address space. */
+
+static void
+report_module (Dwfl *dwfl, pid_t child, Dwarf_Addr addr)
+{
+ GElf_Addr base;
+ char *long_name = maps_lookup (child, addr, &base);
+ Dwfl_Module *mod = dwfl_report_elf_baseaddr (dwfl, long_name, long_name, -1,
+ base);
+ assert (mod);
+ free (long_name);
+ assert (dwfl_addrmodule (dwfl, addr) == mod);
+}
+
+int
+main (int argc __attribute__ ((unused)), char **argv __attribute__ ((unused)))
+{
+ /* We use no threads here which can interfere with handling a stream. */
+ __fsetlocking (stdin, FSETLOCKING_BYCALLER);
+ __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+ __fsetlocking (stderr, FSETLOCKING_BYCALLER);
+
+ /* Set locale. */
+ (void) setlocale (LC_ALL, "");
+
+ pid_t child = fork ();
+ switch (child)
+ {
+ case -1:
+ assert_perror (errno);
+ assert (0);
+ case 0:;
+ long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
+ assert_perror (errno);
+ assert (l == 0);
+ raise (SIGUSR1);
+ assert (0);
+ default:
+ break;
+ }
+
+ int status;
+ pid_t pid = waitpid (child, &status, 0);
+ assert_perror (errno);
+ assert (pid == child);
+ assert (WIFSTOPPED (status));
+ assert (WSTOPSIG (status) == SIGUSR1);
+
+ struct user_regs_struct user_regs;
+ long l = ptrace (PTRACE_GETREGS, child, NULL, &user_regs);
+ assert_perror (errno);
+ assert (l == 0);
+
+ const unsigned nregs = 17;
+ const uint64_t regs_set = (1U << nregs) - 1;
+ Dwarf_Addr regs[17];
+ regs[0] = user_regs.rax;
+ regs[1] = user_regs.rdx;
+ regs[2] = user_regs.rcx;
+ regs[3] = user_regs.rbx;
+ regs[4] = user_regs.rsi;
+ regs[5] = user_regs.rdi;
+ regs[6] = user_regs.rbp;
+ regs[7] = user_regs.rsp;
+ regs[8] = user_regs.r8;
+ regs[9] = user_regs.r9;
+ regs[10] = user_regs.r10;
+ regs[11] = user_regs.r11;
+ regs[12] = user_regs.r12;
+ regs[13] = user_regs.r13;
+ regs[14] = user_regs.r14;
+ regs[15] = user_regs.r15;
+ regs[16] = user_regs.rip;
+
+ /* x86_64 has PC contained in its CFI subset of DWARF register set so
+ elfutils will figure out the real PC value from REGS. */
+ const bool pc_set = false;
+ Dwarf_Addr pc = 0;
+
+ void *memory_read_user_data = (void *) (uintptr_t) child;
+
+ static char *debuginfo_path;
+ static const Dwfl_Callbacks offline_callbacks =
+ {
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+ .debuginfo_path = &debuginfo_path,
+ .section_address = dwfl_offline_section_address,
+ .find_elf = find_elf,
+ };
+ Dwfl *dwfl = dwfl_begin (&offline_callbacks);
+ assert (dwfl);
+
+ report_module (dwfl, child, user_regs.rip);
+
+ Dwfl_Frame_State *state;
+ state = dwfl_frame_state_data (dwfl, pc_set, pc, nregs, ®s_set, regs,
+ memory_read, memory_read_user_data);
+ assert (state != NULL);
+
+ /* Multiple threads are not handled here. */
+ do
+ {
+ bool minusone;
+ if (! dwfl_frame_state_pc (state, &pc, &minusone))
+ error (1, 0, "dwfl_frame_state_pc: %s", dwfl_errmsg (-1));
+ Dwarf_Addr pc_adjusted = pc - (minusone ? 1 : 0);
+
+ printf ("%#" PRIx64 "\n", (uint64_t) pc);
+
+ Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted);
+ if (mod == NULL)
+ report_module (dwfl, child, pc_adjusted);
+
+ if (! dwfl_frame_unwind (&state))
+ error (1, 0, "dwfl_frame_unwind: %s", dwfl_errmsg (-1));
+ }
+ while (state);
+
+ dwfl_end (dwfl);
+ kill (child, SIGKILL);
+ pid = waitpid (child, &status, 0);
+ assert_perror (errno);
+ assert (pid == child);
+ assert (WIFSIGNALED (status));
+ assert (WTERMSIG (status) == SIGKILL);
+
+ return EXIT_SUCCESS;
+}
+
+#endif /* x86_64 */
diff --git a/tests/backtrace.c b/tests/backtrace.c
new file mode 100644
index 0000000..a01ba35
--- /dev/null
+++ b/tests/backtrace.c
@@ -0,0 +1,499 @@
+/* Test program for unwinding of frames.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <locale.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <error.h>
+#include <unistd.h>
+#include <dwarf.h>
+#include <sys/resource.h>
+#include <sys/ptrace.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/user.h>
+#include <fcntl.h>
+#include <string.h>
+#include ELFUTILS_HEADER(dwfl)
+
+static void
+report_pid (Dwfl *dwfl, pid_t pid)
+{
+ int result = dwfl_linux_proc_report (dwfl, pid);
+ if (result < 0)
+ error (2, 0, "dwfl_linux_proc_report: %s", dwfl_errmsg (-1));
+ else if (result > 0)
+ error (2, result, "dwfl_linux_proc_report");
+
+ if (dwfl_report_end (dwfl, NULL, NULL) != 0)
+ error (2, 0, "dwfl_report_end: %s", dwfl_errmsg (-1));
+}
+
+static Dwfl *
+dwfl_pid (pid_t pid)
+{
+ static char *debuginfo_path;
+ static const Dwfl_Callbacks proc_callbacks =
+ {
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+ .debuginfo_path = &debuginfo_path,
+
+ .find_elf = dwfl_linux_proc_find_elf,
+ };
+ Dwfl *dwfl = dwfl_begin (&proc_callbacks);
+ if (dwfl == NULL)
+ error (2, 0, "dwfl_begin: %s", dwfl_errmsg (-1));
+ report_pid (dwfl, pid);
+ return dwfl;
+}
+
+static const char *executable;
+
+static int
+find_elf (Dwfl_Module *mod, void **userdata, const char *modname,
+ Dwarf_Addr base, char **file_name, Elf **elfp)
+{
+ if (executable && modname != NULL
+ && (strcmp (modname, "[exe]") == 0 || strcmp (modname, "[pie]") == 0))
+ {
+ char *executable_dup = strdup (executable);
+ if (executable_dup)
+ {
+ free (*file_name);
+ *file_name = executable_dup;
+ return -1;
+ }
+ }
+ return dwfl_build_id_find_elf (mod, userdata, modname, base, file_name, elfp);
+}
+
+static Dwfl *
+dwfl_offline (void)
+{
+ static char *debuginfo_path;
+ static const Dwfl_Callbacks offline_callbacks =
+ {
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+ .debuginfo_path = &debuginfo_path,
+
+ .section_address = dwfl_offline_section_address,
+
+ /* We use this table for core files too. */
+ .find_elf = find_elf,
+ };
+ Dwfl *dwfl = dwfl_begin (&offline_callbacks);
+ if (dwfl == NULL)
+ error (2, 0, "dwfl_begin: %s", dwfl_errmsg (-1));
+ return dwfl;
+}
+
+static Dwfl *
+dwfl_core (const char *corefile)
+{
+ Dwfl *dwfl = dwfl_offline ();
+ if (dwfl_core_filename_report (dwfl, NULL, corefile) == NULL)
+ error (2, 0, "dwfl_core_filename_report: %s", dwfl_errmsg (-1));
+ if (dwfl_report_end (dwfl, NULL, NULL) != 0)
+ error (2, 0, "dwfl_report_end: %s", dwfl_errmsg (-1));
+ return dwfl;
+}
+
+static int
+dump_modules (Dwfl_Module *mod, void **userdata __attribute__ ((unused)),
+ const char *name, Dwarf_Addr start,
+ void *arg __attribute__ ((unused)))
+{
+ Dwarf_Addr end;
+ dwfl_module_info (mod, NULL, NULL, &end, NULL, NULL, NULL, NULL);
+ printf ("%#" PRIx64 "\t%#" PRIx64 "\t%s\n", (uint64_t) start, (uint64_t) end,
+ name);
+ return DWARF_CB_OK;
+}
+
+static void
+dump (pid_t pid, const char *corefile,
+ void (*callback) (pid_t tid, unsigned frameno, Dwarf_Addr pc,
+ const char *symname, Dwfl *dwfl, void *data),
+ void *data)
+{
+ Dwfl *dwfl;
+ Dwfl_Frame_State *state;
+ if (pid && !corefile)
+ {
+ dwfl = dwfl_pid (pid);
+ state = dwfl_frame_state_pid (dwfl, pid);
+ }
+ else if (corefile && !pid)
+ {
+ dwfl = dwfl_core (corefile);
+ state = dwfl_frame_state_core (dwfl, corefile);
+ }
+ else
+ abort ();
+ if (state == NULL)
+ error (2, 0, "dwfl_frame_state: %s", dwfl_errmsg (-1));
+ ptrdiff_t ptrdiff = dwfl_getmodules (dwfl, dump_modules, NULL, 0);
+ assert (ptrdiff == 0);
+ int err = 0;
+ do
+ {
+ Dwfl_Frame_State *thread = state;
+ pid_t tid = dwfl_frame_tid_get (thread);
+ printf ("TID %ld:\n", (long) tid);
+ unsigned frameno;
+ for (frameno = 0; state; frameno++)
+ {
+ Dwarf_Addr pc;
+ bool minusone;
+ if (! dwfl_frame_state_pc (state, &pc, &minusone))
+ {
+ fprintf (stderr, "%s\n", dwfl_errmsg (-1));
+ err = 1;
+ break;
+ }
+ Dwarf_Addr pc_adjusted = pc - (minusone ? 1 : 0);
+
+ /* Get PC->SYMNAME. */
+ Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted);
+ const char *symname = NULL;
+ if (mod)
+ symname = dwfl_module_addrname (mod, pc_adjusted);
+
+ printf ("#%2u %#" PRIx64 "%4s\t%s\n", frameno, (uint64_t) pc,
+ minusone ? "- 1" : "", symname);
+ if (callback)
+ callback (tid, frameno, pc, symname, dwfl, data);
+ if (! dwfl_frame_unwind (&state))
+ {
+ fprintf (stderr, "%s\n", dwfl_errmsg (-1));
+ err = 1;
+ break;
+ }
+ }
+ state = dwfl_frame_thread_next (thread);
+ }
+ while (state);
+ if (callback)
+ callback (0, 0, 0, NULL, dwfl, data);
+ dwfl_end (dwfl);
+ if (err)
+ exit (EXIT_FAILURE);
+}
+
+struct see_exec_module
+{
+ Dwfl_Module *mod;
+ char selfpath[PATH_MAX + 1];
+};
+
+static int
+see_exec_module (Dwfl_Module *mod, void **userdata __attribute__ ((unused)),
+ const char *name __attribute__ ((unused)),
+ Dwarf_Addr start __attribute__ ((unused)), void *arg)
+{
+ struct see_exec_module *data = arg;
+ if (strcmp (name, data->selfpath) != 0)
+ return DWARF_CB_OK;
+ assert (data->mod == NULL);
+ data->mod = mod;
+ return DWARF_CB_OK;
+}
+
+static void
+selfdump_callback (pid_t tid, unsigned frameno, Dwarf_Addr pc,
+ const char *symname, Dwfl *dwfl, void *data)
+{
+ pid_t check_tid = (intptr_t) data;
+ bool disable = check_tid < 0;
+ if (disable)
+ check_tid = -check_tid;
+ static bool seen_main = false;
+ if (symname && strcmp (symname, "main") == 0)
+ seen_main = true;
+ if (pc == 0)
+ {
+ assert (seen_main);
+ return;
+ }
+ if (disable || tid != check_tid)
+ return;
+ Dwfl_Module *mod;
+ const char *symname2 = NULL;
+ switch (frameno)
+ {
+ case 0:
+ /* .plt has no symbols. */
+ assert (symname == NULL);
+ break;
+ case 1:
+ assert (symname != NULL && strcmp (symname, "sigusr2") == 0);
+ break;
+ case 2:
+ /* __restore_rt - glibc maybe does not have to have this symbol. */
+ break;
+ case 3:
+ /* Verify we trapped on the very first instruction of jmp. */
+ assert (symname != NULL && strcmp (symname, "jmp") == 0);
+ mod = dwfl_addrmodule (dwfl, pc - 1);
+ if (mod)
+ symname2 = dwfl_module_addrname (mod, pc - 1);
+ assert (symname2 == NULL || strcmp (symname2, "jmp") != 0);
+ break;
+ case 4:
+ assert (symname != NULL && strcmp (symname, "stdarg") == 0);
+ break;
+ case 5:
+ /* Verify we trapped on the very last instruction of child. */
+ assert (symname != NULL && strcmp (symname, "backtracegen") == 0);
+ mod = dwfl_addrmodule (dwfl, pc);
+ if (mod)
+ symname2 = dwfl_module_addrname (mod, pc);
+ assert (symname2 == NULL || strcmp (symname2, "backtracegen") != 0);
+ break;
+ }
+}
+
+#ifdef __x86_64__
+static void
+prepare_thread (pid_t pid2, Dwarf_Addr plt_start, Dwarf_Addr plt_end,
+ void (*jmp) (void))
+{
+ long l;
+ errno = 0;
+ l = ptrace (PTRACE_POKEUSER, pid2,
+ (void *) (intptr_t) offsetof (struct user_regs_struct, rip), jmp);
+ assert_perror (errno);
+ assert (l == 0);
+ l = ptrace (PTRACE_CONT, pid2, NULL, (void *) (intptr_t) SIGUSR2);
+ int status;
+ pid_t got = waitpid (pid2, &status, __WALL);
+ assert_perror (errno);
+ assert (got == pid2);
+ assert (WIFSTOPPED (status));
+ assert (WSTOPSIG (status) == SIGUSR1);
+ for (;;)
+ {
+ errno = 0;
+ l = ptrace (PTRACE_PEEKUSER, pid2,
+ (void *) (intptr_t) offsetof (struct user_regs_struct, rip),
+ NULL);
+ assert_perror (errno);
+ if ((unsigned long) l >= plt_start && (unsigned long) l < plt_end)
+ break;
+ l = ptrace (PTRACE_SINGLESTEP, pid2, NULL, NULL);
+ assert_perror (errno);
+ assert (l == 0);
+ got = waitpid (pid2, &status, __WALL);
+ assert_perror (errno);
+ assert (got == pid2);
+ assert (WIFSTOPPED (status));
+ assert (WSTOPSIG (status) == SIGTRAP);
+ }
+}
+#endif /* __x86_64__ */
+
+#include <asm/unistd.h>
+#include <unistd.h>
+#define tgkill(pid, tid, sig) syscall (__NR_tgkill, (pid), (tid), (sig))
+
+static void
+ptrace_detach_stopped (pid_t pid)
+{
+ errno = 0;
+ long l = ptrace (PTRACE_DETACH, pid, NULL, (void *) (intptr_t) SIGSTOP);
+ assert_perror (errno);
+ assert (l == 0);
+}
+
+static void
+selfdump (const char *exec)
+{
+ pid_t pid = fork ();
+ switch (pid)
+ {
+ case -1:
+ abort ();
+ case 0:
+ execl (exec, exec, "--ptraceme", "--run", NULL);
+ abort ();
+ default:
+ break;
+ }
+
+ /* Catch the main thread. Catch it first otherwise the /proc evaluation of
+ PID may have caught still ourselves before executing execl above. */
+ errno = 0;
+ int status;
+ pid_t got = waitpid (pid, &status, 0);
+ assert_perror (errno);
+ assert (got == pid);
+ assert (WIFSTOPPED (status));
+ assert (WSTOPSIG (status) == SIGUSR2);
+
+ /* Catch the spawned thread. Do not use __WCLONE as we could get racy
+ __WCLONE, probably despite pthread_create already had to be called the new
+ task is not yet alive enough for waitpid. */
+ pid_t pid2 = waitpid (-1, &status, __WALL);
+ assert_perror (errno);
+ assert (pid2 > 0);
+ assert (pid2 != pid);
+ assert (WIFSTOPPED (status));
+ assert (WSTOPSIG (status) == SIGUSR1);
+
+ Dwfl *dwfl = dwfl_pid (pid);
+ char *selfpathname;
+ int i = asprintf (&selfpathname, "/proc/%ld/exe", (long) pid);
+ assert (i > 0);
+ struct see_exec_module data;
+ ssize_t ssize = readlink (selfpathname, data.selfpath,
+ sizeof (data.selfpath));
+ free (selfpathname);
+ assert (ssize > 0 && ssize < (ssize_t) sizeof (data.selfpath));
+ data.selfpath[ssize] = '\0';
+ data.mod = NULL;
+ ptrdiff_t ptrdiff = dwfl_getmodules (dwfl, see_exec_module, &data, 0);
+ assert (ptrdiff == 0);
+ assert (data.mod != NULL);
+ GElf_Addr loadbase;
+ Elf *elf = dwfl_module_getelf (data.mod, &loadbase);
+ GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ assert (ehdr != NULL);
+ Elf_Scn *scn = NULL, *plt = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ GElf_Shdr scn_shdr_mem, *scn_shdr = gelf_getshdr (scn, &scn_shdr_mem);
+ assert (scn_shdr != NULL);
+ if (strcmp (elf_strptr (elf, ehdr->e_shstrndx, scn_shdr->sh_name),
+ ".plt") != 0)
+ continue;
+ assert (plt == NULL);
+ plt = scn;
+ }
+ assert (plt != NULL);
+ GElf_Shdr scn_shdr_mem, *scn_shdr = gelf_getshdr (plt, &scn_shdr_mem);
+ assert (scn_shdr != NULL);
+ /* Make it true on x86_64 with i386 inferior. */
+ int disable = ehdr->e_ident[EI_CLASS] == ELFCLASS32;
+#ifdef __x86_64__
+ Dwarf_Addr plt_start = scn_shdr->sh_addr + loadbase;
+ Dwarf_Addr plt_end = plt_start + scn_shdr->sh_size;
+ void (*jmp) (void);
+ if (! disable)
+ {
+ int nsym = dwfl_module_getsymtab (data.mod);
+ int symi;
+ for (symi = 1; symi < nsym; ++symi)
+ {
+ GElf_Sym symbol;
+ const char *symbol_name = dwfl_module_getsym (data.mod, symi, &symbol, NULL);
+ if (symbol_name == NULL)
+ continue;
+ switch (GELF_ST_TYPE (symbol.st_info))
+ {
+ case STT_SECTION:
+ case STT_FILE:
+ case STT_TLS:
+ continue;
+ default:
+ if (strcmp (symbol_name, "jmp") != 0)
+ continue;
+ break;
+ }
+ /* LOADBASE is already applied here. */
+ jmp = (void (*) (void)) (uintptr_t) symbol.st_value;
+ break;
+ }
+ assert (symi < nsym);
+ prepare_thread (pid2, plt_start, plt_end, jmp);
+ }
+#endif
+ dwfl_end (dwfl);
+ ptrace_detach_stopped (pid);
+ ptrace_detach_stopped (pid2);
+ dump (pid, NULL, selfdump_callback,
+ (void *) (intptr_t) (disable ? -pid2 : pid2));
+}
+
+static bool
+is_core (const char *corefile)
+{
+ Dwfl *dwfl = dwfl_offline ();
+ Dwfl_Module *mod = dwfl_report_elf (dwfl, "core", corefile, -1, 0 /* base */);
+ assert (mod != NULL);
+ GElf_Addr loadbase_ignore;
+ Elf *core = dwfl_module_getelf (mod, &loadbase_ignore);
+ assert (core != NULL);
+ GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem);
+ assert (ehdr != NULL);
+ assert (ehdr->e_type == ET_CORE || ehdr->e_type == ET_EXEC
+ || ehdr->e_type == ET_DYN);
+ bool retval = ehdr->e_type == ET_CORE;
+ dwfl_end (dwfl);
+ return retval;
+}
+
+int
+main (int argc __attribute__ ((unused)), char **argv)
+{
+ /* We use no threads here which can interfere with handling a stream. */
+ __fsetlocking (stdin, FSETLOCKING_BYCALLER);
+ __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+ __fsetlocking (stderr, FSETLOCKING_BYCALLER);
+
+ /* Set locale. */
+ (void) setlocale (LC_ALL, "");
+
+ if (argc == 1)
+ {
+ selfdump ("./backtrace-child");
+ return 0;
+ }
+ argv++;
+ if (argc == 2)
+ {
+ if (strcmp (*argv, "--help") == 0)
+ error (2, 0, "backtrace {{no args for ./backtrace-child}|<pid>|<core>|"
+ "<executable>|<executable core>}");
+ char *end;
+ long l = strtol (*argv, &end, 10);
+ if (**argv && !*end)
+ dump (l, NULL, NULL, NULL);
+ else if (is_core (*argv))
+ dump (0, *argv, NULL, NULL);
+ else
+ selfdump (*argv);
+ return 0;
+ }
+ if (argc == 3)
+ {
+ assert (! is_core (argv[0]));
+ assert (is_core (argv[1]));
+ executable = argv[0];
+ dump (0, argv[1], NULL, NULL);
+ return 0;
+ }
+ assert (0);
+
+ return 0;
+}
diff --git a/tests/run-backtrace.sh b/tests/run-backtrace.sh
new file mode 100755
index 0000000..6e14653
--- /dev/null
+++ b/tests/run-backtrace.sh
@@ -0,0 +1,56 @@
+#! /bin/sh
+# Copyright (C) 2012 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
+
+if [ -z "$VERBOSE" ]; then
+ exec >/dev/null
+fi
+
+mytestrun()
+{
+ echo "$*"
+ testrun "$@"
+}
+
+check_gsignal()
+{
+ # Without proper ELF symbols resolution we could get inappropriate weak
+ # symbol "gsignal" with the same address as the correct symbol "raise".
+ if grep -w gsignal $1; then
+ false
+ fi
+}
+
+check_empty()
+{
+ if test -s $1; then
+ false
+ fi
+}
+
+for child in backtrace-child{,-biarch}; do
+ mytestrun ./backtrace ./$child
+ core="core.`ulimit -c unlimited; set +e; ./$child --gencore --run; true`"
+ tempfiles $core{,.bt,.err}
+ mytestrun ./backtrace ./$child ./$core 1>$core.bt 2>$core.err
+ cat $core.{bt,err}
+ check_gsignal $core.bt
+ check_empty $core.err
+done
+
+exit 0
10 years, 3 months
[patch 2/2] Fix loading core files without build-ids
by Jan Kratochvil
Hi,
jankratochvil/move-core
New testcase:
run-addrname-test.sh -> libfunc+0x9
a follow-up to:
Re: [commit] [patch] Fix dwfl_report_elf BASE alignment [Re: pending patches ping#2]
https://lists.fedorahosted.org/pipermail/elfutils-devel/2013-April/003015...
Message-ID: <20130401192624.GA17190(a)host2.jankratochvil.net>
With my former patches I improved some cases and regressed different cases, so
with this patch it should behave better in general - but not in 100% cases.
The former code using ELF headers from segments could find more modules but
their proper address was only a luck, sometimes wrong, as proven in the mail
above ELF.
"Fix loading core files without build-ids" - with build-id header the ELF
headers from segments method almost always works, it has more wrong results
when build-ids are missing, therefore even the ELF headers are missing.
elfutils was always fragile by searching for ELF headers in core files
segments first
/* Now sniff segment contents for modules. */
and only then trying to adjust them from libc link_map list.
/* Next, we should follow the chain from DT_DEBUG. */
+
/* If content-sniffing already reported a module covering
the same area, find that existing module to adjust.
The l_ld address is the only one we know for sure
to be within the module's own segments (its .dynamic). */
But that adjustment currently does not work as it uses
Dwfl_Module *mod = INTUSE(dwfl_addrmodule) (dwfl, l_ld);
but if the module is placed incorrectly one cannot find the proper module by
its address -- by its placement.
IMO one should follow link_map first and fall back to the ELF headers only if
it fails.
Not sure if one should try to map address-non-conflicting files from ELF
headers, this patch does not do it. (That could better fit case (2) below.)
There are IMO two basic use cases with different pros/cons:
(1) Map address -> symbol: eu-addr2line -S --core=xxx 0xaddress
Exact modules placement is needed and it is better to report no symbol
than a wrong symbol.
(2) I want to know all build-ids: eu-unstrip -n --core=xxx
Modules placement does not matter, excessive output does not matter,
I need to only install needed debuginfos.
This change will behave better in (1) but it may regress (2), for example in
cases where link_map list is overwritten/corrupted in its middle.
I admit I do not fully understand how the current raw segments ELF headers
reporting code worked and how well it copes with overlapping files due to the
false matches.
Also there are some FIXMEs how this patch could behave better. But the
current patch should be an improvement (at least for the case (1)) already so
those further fixes for various corner cases can be incremental updates.
Unfortunately it is duplicating more and more the work already done in GDB.
The testcase run-unstrip-n.sh had to be updated:
(a) It was currently reporting filenames like /lib/libc.so.6 but those were
a false match, the system files do not match build-ids from the core file,
therefore there should be no successful match to system files.
(b) Current code outputs [exe] and [vdso] in slightly different order.
Thanks,
Jan
commit e88be1bac6665f8905ef1cb6e3003267f92e912d
Author: Jan Kratochvil <jan.kratochvil(a)redhat.com>
Date: Fri Apr 26 20:27:10 2013 +0200
Use DT_DEBUG library search first.
libdwfl/
2013-04-26 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* argp-std.c (parse_opt) <ARGP_KEY_SUCCESS> <opt->core> <opt->e>: Set
executable_for_core before calling dwfl_core_file_report.
* core-file.c (dwfl_core_file_report): Move raw segments reporting
lower. New variables vdso_bias, vdso_l_ld and exec_vaddr. Call also
dwfl_segment_report_module for EXEC_VADDR and VDSO_L_LD. Call
dwfl_segment_report_module for raw segments only if LISTED is zero.
* dwfl_module_build_id.c (check_notes): Move into
__libdwfl_find_elf_build_id.
(__libdwfl_find_build_id): Rename to ...
(__libdwfl_find_elf_build_id): ... here. Add parameters build_id_bits,
build_id_elfaddr and build_id_len. Verify MOD vs. ELF.
(__libdwfl_find_elf_build_id) (check_notes): Remove parameters mod and
set, rename data_vaddr to data_elfaddr. Do not call found_build_id.
(__libdwfl_find_elf_build_id): Update the check_notes caller, do not
adjust its data_elfaddr parameter.
(__libdwfl_find_build_id): New wrapper of __libdwfl_find_elf_build_id.
* libdwflP.h (__libdwfl_find_elf_build_id): New declaration.
(dwfl_link_map_report): Add parameters vdso_bias, vdso_l_ld and
exec_vaddr.
* link_map.c: Include system.h and fcntl.h.
(report_r_debug): Add parameters vdso_bias, vdso_l_ld and exec_vaddr,
describe them in the function comment. Delete dwfl_addrmodule call and
its dependent code. Verify build-id before calling dwfl_report_elf,
also supply executable_for_core to it and skip vDSO. Report vdso_bias
and vdso_l_ld when found. Clear exec_vaddr when found.
(dwfl_link_map_report): Add parameters vdso_bias, vdso_l_ld and
exec_vaddr.
(dwfl_link_map_report) (consider_phdr): Add parameter offset. Set
exec_vaddr when found.
(dwfl_link_map_report): New variable in_ok. Try to read IN from
EXECUTABLE_FOR_CORE. Update consider_phdr caller parameters. Update
report_r_debug caller parameters.
tests/
2013-04-26 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* Makefile.am (EXTRA_DIST): Add test-core-lib.so.bz2,
test-core.core.bz2 and test-core.exec.bz2.
* run-addrname-test.sh: New test for these files.
* run-unstrip-n.sh: Update expected output.
* test-core-lib.so.bz2: New file.
* test-core.core.bz2: New file.
* test-core.exec.bz2: New file.
Signed-off-by: Jan Kratochvil <jan.kratochvil(a)redhat.com>
diff --git a/libdwfl/argp-std.c b/libdwfl/argp-std.c
index e54f720..c884390 100644
--- a/libdwfl/argp-std.c
+++ b/libdwfl/argp-std.c
@@ -295,6 +295,9 @@ parse_opt (int key, char *arg, struct argp_state *state)
if (opt->core)
{
+ if (opt->e)
+ dwfl->executable_for_core = strdup (opt->e);
+
int fd = open64 (opt->core, O_RDONLY);
if (fd < 0)
{
@@ -330,9 +333,6 @@ parse_opt (int key, char *arg, struct argp_state *state)
_("No modules recognized in core file"));
return ENOENT;
}
-
- if (opt->e)
- dwfl->executable_for_core = strdup (opt->e);
}
else if (opt->e)
{
diff --git a/libdwfl/core-file.c b/libdwfl/core-file.c
index ac2aa8c..bf6630b 100644
--- a/libdwfl/core-file.c
+++ b/libdwfl/core-file.c
@@ -431,11 +431,47 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf)
/* Now we have NT_AUXV contents. From here on this processing could be
used for a live process with auxv read from /proc. */
+ GElf_Addr vdso_bias = 0;
+ GElf_Addr vdso_l_ld = 0;
+ GElf_Addr exec_vaddr = 0;
int listed = dwfl_link_map_report (dwfl, auxv, auxv_size,
- dwfl_elf_phdr_memory_callback, elf);
+ dwfl_elf_phdr_memory_callback, elf,
+ &vdso_bias, &vdso_l_ld, &exec_vaddr);
+ if (listed > 0 && (vdso_l_ld != 0 || exec_vaddr != 0))
+ {
+ for (ndx = 0; ndx < (int) phnum; ++ndx)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (elf, ndx, &phdr_mem);
+ if (unlikely (phdr == NULL))
+ {
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return -1;
+ }
+ if (phdr->p_type != PT_LOAD)
+ continue;
+ if (exec_vaddr != 0 && phdr->p_vaddr == exec_vaddr)
+ dwfl_segment_report_module (dwfl, ndx, "[exe]",
+ &dwfl_elf_phdr_memory_callback, elf,
+ core_file_read_eagerly, elf);
+ if (vdso_l_ld != 0 && phdr->p_filesz == phdr->p_memsz
+ && phdr->p_vaddr <= vdso_l_ld
+ && vdso_l_ld < phdr->p_vaddr + phdr->p_memsz)
+ {
+ /* FIXME: Use vdso_bias. Currently dwfl_segment_report_module
+ detects the bias on its own. */
+ dwfl_segment_report_module (dwfl, ndx, "[vdso]",
+ &dwfl_elf_phdr_memory_callback, elf,
+ core_file_read_eagerly, elf);
+ break;
+ }
+ }
+ }
/* Now sniff segment contents for modules. */
int sniffed = 0;
+ if (listed == 0)
+ {
ndx = 0;
do
{
@@ -453,6 +489,7 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf)
++ndx;
}
while (ndx < (int) phnum);
+ }
/* We return the number of modules we found if we found any.
If we found none, we return -1 instead of 0 if there was an
diff --git a/libdwfl/dwfl_module_build_id.c b/libdwfl/dwfl_module_build_id.c
index 98cb9f4..b19bc9c 100644
--- a/libdwfl/dwfl_module_build_id.c
+++ b/libdwfl/dwfl_module_build_id.c
@@ -56,10 +56,17 @@ found_build_id (Dwfl_Module *mod, bool set,
int
internal_function
-__libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
+__libdwfl_find_elf_build_id (Dwfl_Module *mod, Elf *elf,
+ const void **build_id_bits,
+ GElf_Addr *build_id_elfaddr, int *build_id_len)
{
- int
- check_notes (Dwfl_Module *mod, bool set, Elf_Data *data, GElf_Addr data_vaddr)
+ {
+ GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ assert (mod == NULL || ehdr->e_type == mod->e_type);
+ assert (ehdr->e_type != ET_REL || mod != NULL);
+ }
+
+ int check_notes (Elf_Data *data, GElf_Addr data_elfaddr)
{
size_t pos = 0;
GElf_Nhdr nhdr;
@@ -69,10 +76,13 @@ __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
if (nhdr.n_type == NT_GNU_BUILD_ID
&& nhdr.n_namesz == sizeof "GNU" && !memcmp (data->d_buf + name_pos,
"GNU", sizeof "GNU"))
- return found_build_id (mod, set,
- data->d_buf + desc_pos, nhdr.n_descsz,
- data_vaddr == NO_VADDR ? 0
- : data_vaddr + desc_pos);
+ {
+ *build_id_bits = data->d_buf + desc_pos;
+ *build_id_elfaddr = (data_elfaddr == NO_VADDR
+ ? 0 : data_elfaddr + desc_pos);
+ *build_id_len = nhdr.n_descsz;
+ return 1;
+ }
return 0;
}
@@ -98,12 +108,11 @@ __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
GElf_Phdr phdr_mem;
GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
if (likely (phdr != NULL) && phdr->p_type == PT_NOTE)
- result = check_notes (mod, set,
- elf_getdata_rawchunk (elf,
+ result = check_notes (elf_getdata_rawchunk (elf,
phdr->p_offset,
phdr->p_filesz,
ELF_T_NHDR),
- dwfl_adjusted_address (mod, phdr->p_vaddr));
+ phdr->p_vaddr);
}
}
else
@@ -117,12 +126,12 @@ __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
GElf_Addr vaddr = 0;
if (!(shdr->sh_flags & SHF_ALLOC))
vaddr = NO_VADDR;
- else if (mod->e_type != ET_REL)
- vaddr = dwfl_adjusted_address (mod, shdr->sh_addr);
+ else if (mod == NULL || mod->e_type != ET_REL)
+ vaddr = shdr->sh_addr;
else if (__libdwfl_relocate_value (mod, elf, &shstrndx,
elf_ndxscn (scn), &vaddr))
vaddr = NO_VADDR;
- result = check_notes (mod, set, elf_getdata (scn, NULL), vaddr);
+ result = check_notes (elf_getdata (scn, NULL), vaddr);
}
}
while (result == 0 && (scn = elf_nextscn (elf, scn)) != NULL);
@@ -131,6 +140,24 @@ __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
}
int
+internal_function
+__libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
+{
+ const void *build_id_bits;
+ GElf_Addr build_id_elfaddr;
+ int build_id_len;
+
+ int result = __libdwfl_find_elf_build_id (mod, elf, &build_id_bits,
+ &build_id_elfaddr, &build_id_len);
+ if (result <= 0)
+ return result;
+
+ GElf_Addr build_id_vaddr = build_id_elfaddr + (build_id_elfaddr != 0
+ ? mod->main_bias : 0);
+ return found_build_id (mod, set, build_id_bits, build_id_len, build_id_vaddr);
+}
+
+int
dwfl_module_build_id (Dwfl_Module *mod,
const unsigned char **bits, GElf_Addr *vaddr)
{
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index 5aaa778..7777b53 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -362,6 +362,16 @@ extern Dwfl_Error __libdwfl_addrcu (Dwfl_Module *mod, Dwarf_Addr addr,
extern Dwfl_Error __libdwfl_cu_getsrclines (struct dwfl_cu *cu)
internal_function;
+/* Look in ELF for an NT_GNU_BUILD_ID note. Store it to BUILD_ID_BITS,
+ its vaddr in ELF to BUILD_ID_VADDR (it is unrelocated, even if MOD is not
+ NULL) and store length to BUILD_ID_LEN. Returns -1 for errors, 1 if it was
+ stored and 0 if no note is found. MOD may be NULL, MOD must be non-NULL
+ only if ELF is ET_REL. */
+extern int __libdwfl_find_elf_build_id (Dwfl_Module *mod, Elf *elf,
+ const void **build_id_bits,
+ GElf_Addr *build_id_elfaddr,
+ int *build_id_len);
+
/* Look in ELF for an NT_GNU_BUILD_ID note. If SET is true, store it
in MOD and return its length. If SET is false, instead compare it
to that stored in MOD and return 2 if they match, 1 if they do not.
@@ -458,10 +468,18 @@ extern int dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
only find where to begin if the correct executable file was
previously reported and preloaded as with dwfl_report_elf.
+ Return found l_addr and l_ld fields for vDSO in VDSO_BIAS and
+ VDSO_L_LD respectively. Return found memory address of start of the
+ executable in EXEC_VADDR. If either vDSO or executable is not found
+ the referenced addresses are not modified by this function. Any of
+ these parameters can be NULL.
+
Returns the number of modules found, or -1 for errors. */
extern int dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
Dwfl_Memory_Callback *memory_callback,
- void *memory_callback_arg);
+ void *memory_callback_arg,
+ GElf_Addr *vdso_bias, GElf_Addr *vdso_l_ld,
+ GElf_Addr *exec_vaddr);
/* Avoid PLT entries. */
diff --git a/libdwfl/link_map.c b/libdwfl/link_map.c
index c79a2bf..8a81c47 100644
--- a/libdwfl/link_map.c
+++ b/libdwfl/link_map.c
@@ -29,9 +29,11 @@
#include <config.h>
#include "libdwflP.h"
#include "../libdw/memory-access.h"
+#include "system.h"
#include <byteswap.h>
#include <endian.h>
+#include <fcntl.h>
/* This element is always provided and always has a constant value.
This makes it an easy thing to scan for to discern the format. */
@@ -221,7 +223,8 @@ addrsize (uint_fast8_t elfclass)
}
/* Report a module for each struct link_map in the linked list at r_map
- in the struct r_debug at R_DEBUG_VADDR.
+ in the struct r_debug at R_DEBUG_VADDR. For vdso_bias, vdso_l_ld and
+ exec_vaddr descriptions see dwfl_link_map_report in libdwflP.h.
For each link_map entry, if an existing module resides at its address,
this just modifies that module's name and suggested file name. If
@@ -233,7 +236,9 @@ static int
report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata,
Dwfl *dwfl, GElf_Addr r_debug_vaddr,
Dwfl_Memory_Callback *memory_callback,
- void *memory_callback_arg)
+ void *memory_callback_arg,
+ GElf_Addr *vdso_bias, GElf_Addr *vdso_l_ld,
+ GElf_Addr *exec_vaddr)
{
/* Skip r_version, to aligned r_map field. */
GElf_Addr read_vaddr = r_debug_vaddr + addrsize (elfclass);
@@ -351,9 +356,93 @@ report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata,
/* We have to find the file's phdrs to compute along with l_addr
what its runtime address boundaries are. */
- // XXX hook for sysroot
- mod = INTUSE(dwfl_report_elf) (dwfl, basename (name),
- name, -1, l_addr);
+ Dwfl_Module *mod = NULL;
+ if (iterations == 1 && dwfl->executable_for_core != NULL)
+ {
+ /* Find the main executable.
+ FIXME: Try to also find it via build-id. */
+ name = dwfl->executable_for_core;
+ }
+ if (iterations != 2 && name != NULL)
+ {
+ /* Find a shared library or main executable. Do not try to
+ find a file for vDSO (where ITERATIONS equals 2). */
+
+ /* This code is mostly inlined dwfl_report_elf. */
+ // XXX hook for sysroot
+ int fd = open64 (name, O_RDONLY);
+ if (fd >= 0)
+ {
+ Elf *elf;
+ Dwfl_Error error = __libdw_open_file (&fd, &elf, true, false);
+ if (error == DWFL_E_NOERROR)
+ {
+ const void *build_id_bits;
+ GElf_Addr build_id_elfaddr;
+ int build_id_len;
+ bool valid = true;
+
+ /* FIXME: Bias L_ADDR should be computed from the prelink
+ state in memory (when the file got loaded), not against
+ the current on-disk file state as is computed below.
+
+ This verification gives false positive if in-core ELF had
+ build-id but on-disk ELF does not have any. But we cannot
+ reliably find ELF header and/or the ELF build id just from
+ the link map (and checking core segments is also not
+ reliable). */
+
+ if (__libdwfl_find_elf_build_id (NULL, elf, &build_id_bits,
+ &build_id_elfaddr,
+ &build_id_len) > 0
+ && build_id_elfaddr != 0)
+ {
+ GElf_Addr build_id_vaddr = build_id_elfaddr + l_addr;
+ release_buffer (0);
+ int segndx = INTUSE(dwfl_addrsegment) (dwfl,
+ build_id_vaddr,
+ NULL);
+ if (! (*memory_callback) (dwfl, segndx,
+ &buffer, &buffer_available,
+ build_id_vaddr, build_id_len,
+ memory_callback_arg)
+ || memcmp (build_id_bits, buffer, build_id_len) != 0)
+ {
+ /* File has valid build-id which cannot be verified
+ in memory. */
+ valid = false;
+ }
+ }
+
+ if (valid)
+ // XXX hook for sysroot
+ mod = __libdwfl_report_elf (dwfl, basename (name), name,
+ fd, elf, l_addr, true);
+ if (mod == NULL)
+ {
+ elf_end (elf);
+ close (fd);
+ }
+ }
+ }
+ }
+ if (mod != NULL && iterations == 1)
+ {
+ /* Cancel reporting of raw segments for the main executable as
+ its file has been found now. */
+ if (exec_vaddr)
+ *exec_vaddr = 0;
+ }
+ if (iterations == 2)
+ {
+ /* vDSO is only reported to the caller, it is never searched
+ for on the disk. */
+ assert (mod == NULL);
+ if (vdso_bias)
+ *vdso_bias = l_addr;
+ if (vdso_l_ld)
+ *vdso_l_ld = l_ld;
+ }
if (mod != NULL)
{
@@ -569,7 +658,9 @@ find_executable (Dwfl *dwfl, GElf_Addr at_phdr, GElf_Addr at_entry,
int
dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
Dwfl_Memory_Callback *memory_callback,
- void *memory_callback_arg)
+ void *memory_callback_arg,
+ GElf_Addr *vdso_bias, GElf_Addr *vdso_l_ld,
+ GElf_Addr *exec_vaddr)
{
GElf_Addr r_debug_vaddr = 0;
@@ -631,8 +722,8 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
GElf_Xword dyn_filesz = 0;
GElf_Addr dyn_bias = (GElf_Addr) -1;
- inline bool consider_phdr (GElf_Word type,
- GElf_Addr vaddr, GElf_Xword filesz)
+ inline bool consider_phdr (GElf_Word type, GElf_Addr vaddr,
+ GElf_Xword filesz, Elf32_Off offset)
{
switch (type)
{
@@ -643,6 +734,8 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
== (phdr & (dwfl->segment_align - 1))))
{
dyn_bias = phdr - vaddr;
+ if (exec_vaddr)
+ *exec_vaddr = phdr - offset;
return dyn_vaddr != 0;
}
break;
@@ -667,8 +760,65 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
.d_size = phnum * phent,
.d_buf = NULL
};
- if ((*memory_callback) (dwfl, phdr_segndx, &in.d_buf, &in.d_size,
- phdr, phnum * phent, memory_callback_arg))
+ 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)
+ {
+ /* AUXV -> PHDR -> DYNAMIC
+ Both AUXV and DYNAMIC should be always present in a core file.
+ PHDR may be missing in core file, try to read it from
+ EXECUTABLE_FOR_CORE to find where DYNAMIC is located in the
+ core file. */
+
+ int fd = open (dwfl->executable_for_core, O_RDONLY);
+ Elf *elf;
+ Dwfl_Error error = DWFL_E_ERRNO;
+ if (fd != -1)
+ error = __libdw_open_file (&fd, &elf, true, false);
+ if (error != DWFL_E_NOERROR)
+ {
+ __libdwfl_seterrno (error);
+ return false;
+ }
+ GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ if (ehdr == NULL)
+ {
+ elf_end (elf);
+ close (fd);
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return false;
+ }
+ if (ehdr->e_phnum != phnum || ehdr->e_phentsize != phent)
+ {
+ elf_end (elf);
+ close (fd);
+ __libdwfl_seterrno (DWFL_E_BADELF);
+ return false;
+ }
+ off_t off = ehdr->e_phoff;
+ assert (in.d_buf == NULL);
+ assert (in.d_size == phnum * phent);
+ in.d_buf = malloc (in.d_size);
+ if (unlikely (in.d_buf == NULL))
+ {
+ elf_end (elf);
+ close (fd);
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return false;
+ }
+ ssize_t nread = pread_retry (fd, in.d_buf, in.d_size, off);
+ elf_end (elf);
+ close (fd);
+ if (nread != (ssize_t) in.d_size)
+ {
+ free (in.d_buf);
+ __libdwfl_seterrno (DWFL_E_ERRNO);
+ return false;
+ }
+ in_ok = true;
+ }
+ if (in_ok)
{
union
{
@@ -699,7 +849,8 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
for (size_t i = 0; i < phnum; ++i)
if (consider_phdr (u->p32[i].p_type,
u->p32[i].p_vaddr,
- u->p32[i].p_filesz))
+ u->p32[i].p_filesz,
+ u->p32[i].p_offset))
break;
}
else
@@ -707,7 +858,8 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
for (size_t i = 0; i < phnum; ++i)
if (consider_phdr (u->p64[i].p_type,
u->p64[i].p_vaddr,
- u->p64[i].p_filesz))
+ u->p64[i].p_filesz,
+ u->p64[i].p_offset))
break;
}
}
@@ -830,6 +982,7 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size,
/* Now we can follow the dynamic linker's library list. */
return report_r_debug (elfclass, elfdata, dwfl, r_debug_vaddr,
- &integrated_memory_callback, &mcb);
+ &integrated_memory_callback, &mcb,
+ vdso_bias, vdso_l_ld, exec_vaddr);
}
INTDEF (dwfl_link_map_report)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index e10da8a..31c8fba 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -198,7 +198,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
testfile69.core.bz2 testfile69.so.bz2 \
testfile70.core.bz2 testfile70.exec.bz2 \
run-dwfllines.sh run-dwfl-report-elf-align.sh \
- testfile-dwfl-report-elf-align-shlib.so.bz2
+ testfile-dwfl-report-elf-align-shlib.so.bz2 \
+ test-core-lib.so.bz2 test-core.core.bz2 test-core.exec.bz2
if USE_VALGRIND
valgrind_cmd="valgrind -q --trace-children=yes --error-exitcode=1 --run-libc-freeres=no"
diff --git a/tests/run-addrname-test.sh b/tests/run-addrname-test.sh
index 99abf9d..dd6a8f0 100755
--- a/tests/run-addrname-test.sh
+++ b/tests/run-addrname-test.sh
@@ -292,6 +292,12 @@ l0local2
??:0
EOF
+testfiles test-core-lib.so test-core.core test-core.exec
+testrun_compare ../src/addr2line -S -e test-core.exec --core=test-core.core 0x7f67f2aaf619 <<\EOF
+libfunc+0x9
+??:0
+EOF
+
testfiles testfile65
testrun_compare ../src/addr2line -S --core=testfile65 0x7fff94bffa30 <<\EOF
__vdso_time
diff --git a/tests/run-unstrip-n.sh b/tests/run-unstrip-n.sh
index 1353b1d..0d93b29 100755
--- a/tests/run-unstrip-n.sh
+++ b/tests/run-unstrip-n.sh
@@ -35,20 +35,20 @@ testfiles testcore-rtlib testcore-rtlib-ppc
testrun_compare ../src/unstrip -n --core=testcore-rtlib <<\EOF
0x8048000+0x2000 f1c600bc36cb91bf01f9a63a634ecb79aa4c3199@0x8048178 . - [exe]
+0xf75e9000+0x1a000 29a103420abe341e92072fb14274e250e4072148@0xf75e9164 - - [dso]
+0xf7603000+0x1b0000 0b9bf374699e141e5dfc14757ff42b8c2373b4de@0xf7603184 - - [dso]
+0xf77b3000+0x9000 c6c5b5e35ab9589d4762ac85b4bd56b1b2720e37@0xf77b3164 - - [dso]
0xf77d6000+0x1000 676560b1b765cde9c2e53f134f4ee354ea894747@0xf77d6210 . - linux-gate.so.1
-0xf77b3000+0x9000 c6c5b5e35ab9589d4762ac85b4bd56b1b2720e37@0xf77b3164 /lib/librt.so.1 - librt.so.1
-0xf7603000+0x1b0000 0b9bf374699e141e5dfc14757ff42b8c2373b4de@0xf7603184 /lib/libc.so.6 - libc.so.6
-0xf75e9000+0x1a000 29a103420abe341e92072fb14274e250e4072148@0xf75e9164 /lib/libpthread.so.0 - libpthread.so.0
-0xf77d7000+0x21000 6d2cb32650054f1c176d01d48713a4a5e5e84c1a@0xf77d7124 /lib/ld-linux.so.2 - ld-linux.so.2
+0xf77d7000+0x21000 6d2cb32650054f1c176d01d48713a4a5e5e84c1a@0xf77d7124 - - ld-linux.so.2
EOF
testrun_compare ../src/unstrip -n --core=testcore-rtlib-ppc <<\EOF
-0x10000000+0x20000 979b7a26747cc09bd84a42b311b5288c704baea5@0x10000174 . - [exe]
0x100000+0x10000 708b900b05176964512a6b0fe90c2a0c9d73d726@0x100334 . - linux-vdso32.so.1
-0xfd50000+0x30000 3f7d21508470322d2f47acddc20ab10516edba99@0xfd50164 /lib/librt.so.1 - librt.so.1
-0xfdf0000+0x1c0000 edf3dd232e09d01b90683889bd16b9406c52d4de@0xfdf0184 /lib/libc.so.6 - libc.so.6
-0xfdb0000+0x40000 f6ee91d4c629bc7dacc10534cb30056914e7e0b5@0xfdb0164 /lib/libpthread.so.0 - libpthread.so.0
-0xffb0000+0x50000 edec437a85026a1cf8cda94003706202733130c1@0xffb0124 /lib/ld.so.1 - ld.so.1
+0xfd50000+0x30000 3f7d21508470322d2f47acddc20ab10516edba99@0xfd50164 . - librt.so.1
+0xfdb0000+0x40000 f6ee91d4c629bc7dacc10534cb30056914e7e0b5@0xfdb0164 - - libpthread.so.0
+0xfdf0000+0x1c0000 edf3dd232e09d01b90683889bd16b9406c52d4de@0xfdf0184 - - [dso]
+0xffb0000+0x50000 edec437a85026a1cf8cda94003706202733130c1@0xffb0124 - - ld.so.1
+0x10000000+0x20000 979b7a26747cc09bd84a42b311b5288c704baea5@0x10000174 . - [exe]
EOF
test_cleanup
diff --git a/tests/test-core-lib.so.bz2 b/tests/test-core-lib.so.bz2
new file mode 100755
index 0000000..bb2da88
Binary files /dev/null and b/tests/test-core-lib.so.bz2 differ
diff --git a/tests/test-core.core.bz2 b/tests/test-core.core.bz2
new file mode 100644
index 0000000..4d4346b
Binary files /dev/null and b/tests/test-core.core.bz2 differ
diff --git a/tests/test-core.exec.bz2 b/tests/test-core.exec.bz2
new file mode 100755
index 0000000..49ce551
Binary files /dev/null and b/tests/test-core.exec.bz2 differ
10 years, 3 months
[patch 3/4] unwinder: Provide dwfl_core_filename_report
by Jan Kratochvil
Hi,
dependent on:
[patch 2/4] unwinder: Provide EBLHOOKVAR
present in:
jankratochvil/forunwind-baseaddr-hookvars-corereport
but this branch is not suitable for merge as it is unclear in which form get
checked in the patches above. The branch is usable only for "git checkout",
neither diff nor merge should be done on it.
Currently dwfl_core_file_report requires Elf *core. But application cannot
open Elf * on its own, it is elfutils internal function. Various workarounds
I have tried in the code below (the last one works but it is ugly):
#if 0
/* FIXME: Here COREFILE gets errorneously reported into VMA of DWFL. */
Dwfl_Module *mod = dwfl_report_elf (dwfl, "core", corefile, -1, 0 /* base */);
assert (mod != NULL);
Elf *core = dwfl_module_getelf (mod, NULL);
assert (core != NULL);
int result = dwfl_core_file_report (dwfl, core);
assert (result >= 0);
#endif
#if 0
/* FIXME: Here it does not build as __libdw_open_file is private. */
int core_fd = open64 (corefile, O_RDONLY);
assert (core_fd >= 0);
Elf *core;
Dwfl_Error err = __libdw_open_file (&core_fd, &core, true, false);
assert (err == DWFL_E_NOERROR);
int result = dwfl_core_file_report (dwfl, core);
assert (result >= 0);
result = elf_end (core);
assert (result == 0);
result = close (core_fd);
assert (result == 0);
#endif
#if 1
/* FIXME: Here we use different DWFL, reported data would be used after free
if we called dwfl_end (core_dwfl). Therefore we leak CORE_DWFL. */
Dwfl *core_dwfl = dwfl_offline ();
Dwfl_Module *mod = dwfl_report_elf (core_dwfl, "core", corefile, -1,
0 /* base */);
assert (mod != NULL);
GElf_Addr loadbase_ignore;
Elf *core = dwfl_module_getelf (mod, &loadbase_ignore);
assert (core != NULL);
int result = dwfl_core_file_report (dwfl, core);
assert (result >= 0);
// dwfl_end (core_dwfl);
#endif
It is also already suspicious why libdwfl/argp-std.c is using elfutils
internal function, application should be able to implement parsing commandline
parameters on its own.
Thanks,
Jan
libdw/
2012-11-13 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* libdw.map (ELFUTILS_0.156): New block.
libdwfl/
2012-11-13 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* argp-std.c (parse_opt): Remove label nofile. Substitute code by
a dwfl_core_filename_report call. Initialize error from dwfl_errno
now. Check dwfl->modulelist instead of former RESULT.
* core-file.c: Include fcntl.h.
(dwfl_core_filename_report): New function.
* libdwfl.h (dwfl_core_filename_report): New declaration.
diff --git a/libdw/libdw.map b/libdw/libdw.map
index 1f71d03..5a055e8 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -254,3 +254,8 @@ ELFUTILS_0.149 {
dwfl_dwarf_line;
} ELFUTILS_0.148;
+
+ELFUTILS_0.156 {
+ global:
+ dwfl_core_filename_report;
+} ELFUTILS_0.149;
diff --git a/libdwfl/argp-std.c b/libdwfl/argp-std.c
index f0c530f..8aeb80a 100644
--- a/libdwfl/argp-std.c
+++ b/libdwfl/argp-std.c
@@ -205,7 +205,6 @@ parse_opt (int key, char *arg, struct argp_state *state)
{
FILE *f = fopen (arg, "r");
if (f == NULL)
- nofile:
{
int code = errno;
argp_failure (state, EXIT_FAILURE, code,
@@ -291,31 +290,18 @@ parse_opt (int key, char *arg, struct argp_state *state)
if (opt->core)
{
- int fd = open64 (opt->core, O_RDONLY);
- if (fd < 0)
- goto nofile;
-
- Elf *core;
- Dwfl_Error error = __libdw_open_file (&fd, &core, true, false);
- if (error != DWFL_E_NOERROR)
+ if (dwfl_core_filename_report (dwfl, NULL, opt->core) == NULL)
{
+ Dwfl_Error error = dwfl_errno ();
argp_failure (state, EXIT_FAILURE, 0,
_("cannot read ELF core file: %s"),
INTUSE(dwfl_errmsg) (error));
return error == DWFL_E_ERRNO ? errno : EIO;
}
- int result = INTUSE(dwfl_core_file_report) (dwfl, core);
- if (result < 0)
- {
- elf_end (core);
- close (fd);
- return fail (dwfl, result, opt->core);
- }
-
/* From now we leak FD and CORE. */
- if (result == 0)
+ if (dwfl->modulelist == NULL)
{
argp_failure (state, EXIT_FAILURE, 0,
_("No modules recognized in core file"));
diff --git a/libdwfl/core-file.c b/libdwfl/core-file.c
index 1545ca8..f217847 100644
--- a/libdwfl/core-file.c
+++ b/libdwfl/core-file.c
@@ -37,6 +37,7 @@
#include <endian.h>
#include <byteswap.h>
#include "system.h"
+#include <fcntl.h>
/* This is a prototype of what a new libelf interface might be.
@@ -461,3 +462,39 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf)
return sniffed == 0 || listed > sniffed ? listed : sniffed;
}
INTDEF (dwfl_core_file_report)
+
+Elf *
+dwfl_core_filename_report (Dwfl *dwfl, int *fdp, const char *filename)
+{
+ int fd_storage = -1;
+ if (fdp == NULL)
+ fdp = &fd_storage;
+
+ if (*fdp < 0)
+ {
+ *fdp = open64 (filename, O_RDONLY);
+ if (*fdp < 0)
+ {
+ __libdwfl_seterrno (DWFL_E_ERRNO);
+ return NULL;
+ }
+ }
+
+ Elf *core;
+ Dwfl_Error error = __libdw_open_file (fdp, &core, true, false);
+ if (error != DWFL_E_NOERROR)
+ {
+ __libdwfl_seterrno (error);
+ return NULL;
+ }
+
+ int result = INTUSE(dwfl_core_file_report) (dwfl, core);
+ if (result < 0)
+ {
+ elf_end (core);
+ close (*fdp);
+ return NULL;
+ }
+ return core;
+}
+INTDEF (dwfl_core_filename_report)
diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h
index a2ab824..c2b85f4 100644
--- a/libdwfl/libdwfl.h
+++ b/libdwfl/libdwfl.h
@@ -355,6 +355,14 @@ extern int dwfl_linux_kernel_report_offline (Dwfl *dwfl, const char *release,
Returns the number of modules reported, or -1 for errors. */
extern int dwfl_core_file_report (Dwfl *dwfl, Elf *elf);
+/* Call dwfl_core_file_report but providing FILENAME instead. Function returns
+ opened core file Elf * and its *FDP file descriptor which must be held valid
+ until dwfl_end is called for DWFL. FDP can be passed as NULL (but the
+ caller cannot close the file descriptor then). Function returns NULL for
+ errors, check dwfl_errmsg in such case. */
+extern Elf *dwfl_core_filename_report (Dwfl *dwfl, int *fdp,
+ const char *filename);
+
/* Call dwfl_report_module for each file mapped into the address space of PID.
Returns zero on success, -1 if dwfl_report_module failed,
or an errno code if opening the kernel binary failed. */
10 years, 3 months
[patch 2/4] unwinder: Provide EBLHOOKVAR
by Jan Kratochvil
Hi,
dependent on:
[patch 1/4] unwinder: New base address based dwfl_report_elf_baseaddr
present in:
jankratochvil/forunwind-baseaddr-hookvars
but this branch is not suitable for merge as it is unclear in which form get
checked in the patches above. The branch is usable only for "git checkout",
neither diff nor merge should be done on it.
Currently EBL backends support functions via EBLHOOK. Providing variables is
done via a function returning value such as via current x86_64_register_info.
I find it needlessly complicated, backends can provide also
variables/constants.
I can sure easily rework the remaining patches if I really should use a
function doing nothing else than returning a number.
Thanks,
Jan
backends/
2012-11-13 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* libebl_CPU.h (EBLHOOKVAR): New definition.
* libeblP.h (EBLHOOKVAR): New definition and its new undef.
diff --git a/backends/libebl_CPU.h b/backends/libebl_CPU.h
index 36b3a4a..65a9a13 100644
--- a/backends/libebl_CPU.h
+++ b/backends/libebl_CPU.h
@@ -32,6 +32,7 @@
#include <libeblP.h>
#define EBLHOOK(name) EBLHOOK_1(BACKEND, name)
+#define EBLHOOKVAR EBLHOOK
#define EBLHOOK_1(a, b) EBLHOOK_2(a, b)
#define EBLHOOK_2(a, b) a##b
diff --git a/libebl/libeblP.h b/libebl/libeblP.h
index c8196bd..773b6b8 100644
--- a/libebl/libeblP.h
+++ b/libebl/libeblP.h
@@ -54,7 +54,9 @@ struct ebl
/* See ebl-hooks.h for the declarations of the hook functions. */
# define EBLHOOK(name) (*name)
+# define EBLHOOKVAR(name) (name)
# include "ebl-hooks.h"
+# undef EBLHOOKVAR
# undef EBLHOOK
/* Size of entry in Sysv-style hash table. */
10 years, 4 months
[PATCH] readelf: Format first DW_OP_GNU_implicit_pointer argument as DIE offset.
by Mark Wielaard
Make the printing of DW_OP_GNU_implicit_pointer consistent with other
operations that have a DIE offset as argument.
Before:
[ 6e4a] formal_parameter
abstract_origin (ref4) [ 616a]
location (exprloc)
[ 0] GNU_implicit_pointer 0x6dbe, +0
After:
[ 6e4a] formal_parameter
abstract_origin (ref4) [ 616a]
location (exprloc)
[ 0] GNU_implicit_pointer [ 6dbe] +0
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
src/ChangeLog | 5 +++++
src/readelf.c | 2 +-
2 files changed, 6 insertions(+), 1 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index 5c3bf65..8373b70 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,8 @@
+2013-05-06 Mark Wielaard <mjw(a)redhat.com>
+
+ * readelf.c (print_ops): Format first DW_OP_GNU_implicit_pointer
+ argument as DIE offset.
+
2013-04-24 Mark Wielaard <mjw(a)redhat.com>
* Makefile.am: Use AM_CPPFLAGS instead of INCLUDES.
diff --git a/src/readelf.c b/src/readelf.c
index d9527c6..236d088 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -4019,7 +4019,7 @@ print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
/* Byte offset operand. */
get_sleb128 (sleb, data); /* XXX check overrun */
- printf ("%*s[%4" PRIuMAX "] %s %#" PRIxMAX ", %+" PRId64 "\n",
+ printf ("%*s[%4" PRIuMAX "] %s [%6" PRIxMAX "] %+" PRId64 "\n",
indent, "", (intmax_t) offset,
op_name, (uintmax_t) addr, sleb);
CONSUME (data - start);
--
1.7.1
10 years, 4 months
[PATCH] libdw: dwarf_getsrclines don't set end_sequence when there are no lines.
by Mark Wielaard
Some CUs might only have a file list, but no actual source lines in their
statement list. Only set end_sequence at the end of the source lines list
if there are actually lines.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
libdw/ChangeLog | 5 +++++
libdw/dwarf_getsrclines.c | 3 ++-
tests/ChangeLog | 6 ++++++
tests/Makefile.am | 3 ++-
tests/run-get-lines.sh | 31 +++++++++++++++++++++++++++++--
tests/testfilenolines.bz2 | Bin 0 -> 2855 bytes
6 files changed, 44 insertions(+), 4 deletions(-)
create mode 100755 tests/testfilenolines.bz2
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index b4d9dc3..3dc0811 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,8 @@
+2013-05-03 Mark Wielaard <mjw(a)redhat.com>
+
+ * dwarf_getsrclines.c (dwarf_getsrclines): Only set end_sequence
+ when nlinelist > 0.
+
2013-04-24 Mark Wielaard <mjw(a)redhat.com>
* Makefile.am: Use AM_CPPFLAGS instead of INCLUDES.
diff --git a/libdw/dwarf_getsrclines.c b/libdw/dwarf_getsrclines.c
index 5a58b78..fa4dd18 100644
--- a/libdw/dwarf_getsrclines.c
+++ b/libdw/dwarf_getsrclines.c
@@ -715,7 +715,8 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
/* Make sure the highest address for the CU is marked as end_sequence.
This is required by the DWARF spec, but some compilers forget and
dwfl_module_getsrc depends on it. */
- cu->lines->info[nlinelist - 1].end_sequence = 1;
+ if (nlinelist > 0)
+ cu->lines->info[nlinelist - 1].end_sequence = 1;
/* Success. */
res = 0;
diff --git a/tests/ChangeLog b/tests/ChangeLog
index f6c004c..b2ac80f 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,9 @@
+2013-05-03 Mark Wielaard <mjw(a)redhat.com>
+
+ * testfilenolines.bz2: New test file.
+ * Makefile.am (EXTRA_DIST): Add testfilenolines.bz2.
+ * run-get-lines.sh: Run testrun_compare on testfilenolines.
+
2013-04-29 Mark Wielaard <mjw(a)redhat.com>
* test-subr.sh: Don't use pushd, just cd into test-dir.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 4b57703..6327edb 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -198,7 +198,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
testfile69.core.bz2 testfile69.so.bz2 \
testfile70.core.bz2 testfile70.exec.bz2 \
run-dwfllines.sh run-dwfl-report-elf-align.sh \
- testfile-dwfl-report-elf-align-shlib.so.bz2
+ testfile-dwfl-report-elf-align-shlib.so.bz2 \
+ testfilenolines
if USE_VALGRIND
valgrind_cmd='valgrind -q --trace-children=yes --error-exitcode=1 --run-libc-freeres=no'
diff --git a/tests/run-get-lines.sh b/tests/run-get-lines.sh
index 5a220ca..fb48c77 100755
--- a/tests/run-get-lines.sh
+++ b/tests/run-get-lines.sh
@@ -1,5 +1,5 @@
#! /bin/sh
-# Copyright (C) 1999, 2000, 2002, 2004, 2005 Red Hat, Inc.
+# Copyright (C) 1999, 2000, 2002, 2004, 2005, 2013 Red Hat, Inc.
# This file is part of elfutils.
# Written by Ulrich Drepper <drepper(a)redhat.com>, 1999.
#
@@ -18,7 +18,7 @@
. $srcdir/test-subr.sh
-testfiles testfile testfile2
+testfiles testfile testfile2 testfilenolines
testrun_compare ${abs_builddir}/get-lines testfile testfile2 <<\EOF
cuhl = 11, o = 0, asz = 4, osz = 4, ncu = 191
@@ -61,4 +61,31 @@ cuhl = 11, o = 267, asz = 4, osz = 4, ncu = 2680
10000514: /shoggoth/drepper/m.c:8:0: is_stmt:yes, end_seq:yes, bb:no, prologue:no, epilogue:no
EOF
+# - lines.c
+# int ft;
+#
+# int
+# main (int argc, char **argv)
+# {
+# return ft - 42;
+# }
+#
+# - nolines.c
+# int ft = 42;
+#
+# gcc -g -c lines.c
+# gcc -g -c nolines.c
+# gcc -g -o testfilenolines lines.o nolines.o
+
+testrun_compare ${abs_builddir}/get-lines testfilenolines <<\EOF
+cuhl = 11, o = 0, asz = 8, osz = 4, ncu = 169
+ 4 lines
+400474: /home/mark/src/tests/lines.c:5:0: is_stmt:yes, end_seq:no, bb:no, prologue:no, epilogue:no
+40047f: /home/mark/src/tests/lines.c:6:0: is_stmt:yes, end_seq:no, bb:no, prologue:no, epilogue:no
+400488: /home/mark/src/tests/lines.c:7:0: is_stmt:yes, end_seq:no, bb:no, prologue:no, epilogue:no
+40048a: /home/mark/src/tests/lines.c:7:0: is_stmt:yes, end_seq:yes, bb:no, prologue:no, epilogue:no
+cuhl = 11, o = 125, asz = 8, osz = 4, ncu = 243
+ 0 lines
+EOF
+
exit 0
diff --git a/tests/testfilenolines.bz2 b/tests/testfilenolines.bz2
new file mode 100755
index 0000000000000000000000000000000000000000..23cd7223a8e257c2ec1c58d92fcf56c67804b331
GIT binary patch
literal 2855
zcmV+?3)u8RT4*^jL0KkKSx0o)y#NYK|NsC0|NsC0|NsC0|9$`e-+upp>{QNgL2Uoy
zYTS2z?_b~voW$Qwz~--e&wE|%=XlOK-s<b!?cku|d&4Azo|#DUGC}GwpeC9!VFreS
zKxoJ^0B8UJ0BNR;2dDrVXnKPYpwMV!XwYQSMuSF%L7@qVn3`xcGBh-3nKeB}k{Ul$
z(?Os#GypOH7>x}yX^;R0gC?3X000^WjSV!=0MVe(H8K;_nx3iYsguz>rqlyN)EWQ-
zO&S^i86Kbj28MtD003wJ007Vc8V9HW>H|$Q5}8CJRPt(`Q%&k<gUV_8rhwB!%54op
zKze`%fY2EMp`blRo})n8fDHfu&;S4p00002227a%$&dhPf*JtO00xF3rhsGs0iYTN
zLqIeD$PEKc7=uQD01W^Eph%J+NS;qbBW)x<R5Co1X{qQ5k?JyfCImDx9;c+!L6ZV7
zhL2EaWM~6JO#lI)!~oDS115${n#s`a?0ZRS>ac3Ba7}4-6P=13Ole;?eBu8;Z&f&&
zAqoVt#?ZBBx`2|R%ZWlOQC-NY?b_~)oiSV?K(t~o)R3i>F35l+IJS~3mnjUcx**P9
zH6qA)^LAlmEe8unA&(v-Wmjy`U4iwGr*zDy)Padu=ov`5=O~XFs^Cm|id6BbM&qQA
zt^{Rcy0>HTF{s5M6NE~L=?M{m*ePxRU@MJ?NhF}AA34qN97c2X*z}ln-rI~U)`jLt
z=X(@MSh%A$g*KeiNG96<f~?E|v;gy>ESR`19yX5dlS_}7&pC}sNYEXiUhRR|sqebC
z@>kf}TEVNAs&+18-UzCwnauR1XTq0~(B>q3lpxn?;Uif>7F~d@8(@{?ILuu)i@jyj
znf!UE?t6>=jj{n51Oqb;qHA2up^!m8p{C7bVp;@}+5!lOr;(g|*sro=hfesE%ZAZ_
zB&Af4SJrJ*X;V)LpHI{lCs2ZTs0ar{gn<jz5COo}MO@{tDmE8+w@`Ig;<hu;HsYRl
zVNZ8hhm8!U508OYJ$X{cv)p-8DuB`k!|Y;)n9v2=LP5|plCxuE%n-HAKy-`_iGoTP
z7OX_g8b%`&(n%^H*fg6Mm^PYd)G$^ua2Kow&?9KdteuHz$8&^Gk`)$(cF+X|iYyWa
z(iec3R%irZGG;Dyfefa=*tI5x(jI;bMoFMeqbm?0VwS=XDKuFVl0zgx4p{0}_M8#3
zDX8b6M5M?D(3~;Ju_`n!tdgo`lsK)44jRRx-XzRBF08ABjfaiVUCC1N4QHp#*CU(d
zt8I+2w11toYj)5IZj}ab>H`)LEgf})xtEp{FmIWGJTg#)o{J-sR`<x9a$?h%8NLt2
z@@0h}qiSOlX|S}l)SYy=y35kHW8>)@7hdpGxo^gui^WyzOf1of9}1}%nqj3fW<qv(
zdwKcQ>XOV#+6cftrHa!?;f~&%fl7`hrpd%V>eoqeZ7k%L2vQ3~0cWZOqyfqfiUF=;
z15GBg2hXYcTWTyfb~2xw3%v&audmem7W@lDm8kK~27zN*P{T4bT`p=7nKTzNR{1ZI
zREm&p*m}$`Q%5PqzmaytPGn#?_JU0V9AjA2ny<S*Vy;HZ5*(QiVB&FQfxj%4`y@e*
zY2DNVFbGqUNNI{@#j<Ig16tPZARyx`0~zB4F^>aol6smguQz>8idT#+8SQ0IEI6bk
zY|y9-2C)zYQK5dj>c8^98#AdrNzru}xzz=VKLzhv_QML{Z0!ZV-O8e2f&!fgBJ}t&
zFbe{Uh0xI<3?-Tc-l#i);l|kpJ}#sUol1h4y=z-86GxYm@+pNl;gX1gq5^TKW)Wh0
ztZi#yh)^XE=gkfuueMeo%o7ym%WA3?LyiVk&3TzI2;_{FNfB$&pS=SZx=B$odY-oB
zt72G*D4hZle^R3VU7*`uEz@)@*s?W^DH_UQ(MbzmA;lwPla6%Per2qqMbO|*Y<Nkl
zSp|0V4V8rZs>fq^uz1)|B4GhI$`#lvn8X$iR0)7*!igdaQ=-*C<Pegfkc{&_wZF|u
zRpD6IAVr395G_qy9E+Zu_b_MDP!9UNBAd5&&iH8*TPF+oC782Q$zxrltjPq5)CmX}
z(^$9m$gqk7lT?U9Z$4W^8Hu2ExyZ0E6A56s(mO~q3X?18mda)Ek6o#%bc!W8xmK;B
z{aPa+s3iqbu%z2k6oo6sUdY$$O>OeLgSzC3#8=p2tx2jN%FnK@q8;Z;A=CMM7A=Ox
zMl{y~JX1)*u+5xktYvFrZ=i0kue5O#mQV}<Y*!h~ip|P?r(`DtVGupY2a6t72oRcq
z4S9lLkYSLvv;n>v69&B(g6*SbfTlPUvcld`;4x3)@NG*L2HHOjhzKN2Asv9pjT8@M
zn1lt8TnJS?H>#HHWu-9xDbuVC)biy(XLp$9aP-B2p%fM279BDYMx;#%^_quFX^NKi
zZ&j7TQil?eDLRu%Z8S_I=Qk!Q;}OOJAmsT2K%!EN0-Q#z5P8UrVWws_5*fjxhKUo}
zu7lFKqlL4a6d@m9C1S(@SSCZEQ1<ZC;NZgnbpPhK7PHeWL#++X&8jl23^h`RPJo^f
z6lPT?S0D7-%~Vy=!xRRLX$M}XD$dM+8Jc7#i8hNrK^!qiWM7ELl^ds=sdk(hstluk
z%ph=5Vy(>;9O~NLZbnk~(26(@+L#iMzM8lfuF^XwU)5DvS~GLO@R|c7tWmE*BP}n&
zgswz+FlNYgHs)qcch3|UDpJZ=QY_jt9XBc<K#cyx67dSyQUb>Ic1lAWE@$Ph+Pg8f
z;(`^s7tBcQ%9>+ZU>0#Zq%L&gm^gsb4*ELaQr`tfEZqba83-cSi;aOGL>a&s*pMVB
zZUiypc<v3p)_q)y*`dLJy{I(C6AY}5S{)jgDpDzko~Ds&Q8`S*D~iNIEo`)OZG`|~
zizdgI8v2Vp;k+o^`l6sh&gW+ebjb`W1m~z#HZcT923>Z<@S>~>4>(JO9x8<fuw#U-
z=!h~WCa<qBFOSj@#vXMH#Mf@CS8gCuK8Q#}32vJU5GXV@<CA*mOLFSqsMR`JWG~<Z
zy9F8n)r4GP)PyQ9>g@7yMktg;U}v!?pU6rokj6s?L{&pY_|yA|nG49`d`2(~CIuo#
zF%nV;1I9|JN@SY?!D0!?6S`pW!OOLYFr&kju`1%ZOsZJ~DF_AUQF43Fsw~AE$g1_I
zu1b(rgix|ZplApX5O|A8r+fkt0x-xZF44LbgA!lH87UB;>|Z*Rl*1Q9;@Z%T(e^df
z9YcW#Cw=PLz{^4SuaUXW&{;V&<Prl6AY$f55FrpYi%DMw&K3ECVF-@vK&+vWgK&xW
zDhE}uPajh-T(?EJlxV2zLqvjbz%*H}c1|6+<TGm<C|F(WO?II`gA5SsMp4vasCrjd
z;1WVv!HC(Hg%J=Uv*H5OD44R4fvTh>Fo{cle`5YixQ7U@vkMPJ#GB|rWpFhrvi=Q3
ztR$y`I#X?Un6^oZdE!R8Yj&)(t*D<z-GNa8rc8Gxk@vOrLWWqU1PBg!e<Z4A1_I>$
z9@bd0DF`L3B;b?c8qMQ36tJ^2r^7_b6=n~tFFO5&I}$|r_s~P8la<+D`HL-9eIH~P
zm9k+C`pQFt0!I(>i{%1sjB7OLNuv!DUC%CylL{X>WuYM9#Y1j`-@`aoht?To-P~5)
z^=PwhA@2FWUdq#|SdgBX5&j_V#9-dhN`xe#3x-`}hNBXk8(Dfj7oY#d+>uTcBoW;<
FZ$Mh?5)J?W
literal 0
HcmV?d00001
--
1.7.1
10 years, 4 months
[patch 2/2] Add add_p_vaddr to dwfl_report_elf
by Jan Kratochvil
Hi,
jankratochvil/compatversion-baseaddpvaddr
follow-up/repost of:
[patch 1/4] unwinder: New base address based dwfl_report_elf_baseaddr
https://lists.fedorahosted.org/pipermail/elfutils-devel/2013-March/002977...
Message-ID: <20130317160910.GA2731(a)host2.jankratochvil.net>
Implemented the suggestions.
The only "dangerous" change is in linux-kernel-modules.c where the new
parameter is passed incompatibly as 'false' which seems more logical to me.
But it is ignored anyway as kernel modules are (unfortunately) never ET_DYN.
Thanks,
Jan
commit 784b53a1a4b832e27c2e590bddb99393a61edff9
Author: Jan Kratochvil <jan.kratochvil(a)redhat.com>
Date: Sun Apr 28 19:55:48 2013 +0200
Add parameter add_p_vaddr to dwfl_report_elf.
libdwfl/
* dwfl_report_elf.c (__libdwfl_report_elf): Add parameter add_p_vaddr.
Set it to true for ET_EXEC and ET_CORE. Provide alternative
setup of START and BIAS if !ADD_P_VADDR. Set END from BIAS, not BASE.
(dwfl_report_elf): Add parameter add_p_vaddr. Pass it down. Add
NEW_VERSION.
(_compat_without_add_p_vaddr_dwfl_report_elf) <SHARED>: New, with
COMPAT_VERSION.
* libdwfl.h (dwfl_report_elf): Add parameter add_p_vaddr. Describe it.
* libdwflP.h (__libdwfl_report_elf): Add parameter add_p_vaddr.
* link_map.c (report_r_debug): Use true add_p_vaddr for dwfl_report_elf.
* linux-kernel-modules.c (report_kernel): Use false add_p_vaddr for
dwfl_report_elf.
* offline.c (process_elf): Use true add_p_vaddr for dwfl_report_elf.
tests/
* dwfl-report-elf-align.c: Use false add_p_vaddr for dwfl_report_elf.
Signed-off-by: Jan Kratochvil <jan.kratochvil(a)redhat.com>
diff --git a/ChangeLog b/ChangeLog
index 1fdcb58..bffe62d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2013-04-28 Jan Kratochvil <jan.kratochvil(a)redhat.com>
+
+ * NEWS (Version 0.156): New.
+
2013-04-26 Mark Wielaard <mjw(a)redhat.com>
* configure.ac (AM_INIT_AUTOMAKE): Request parallel-tests.
diff --git a/NEWS b/NEWS
index e38e60c..a71a87c 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,7 @@
+Version 0.156
+
+libdwfl: dwfl_report_elf has new parameter add_p_vaddr.
+
Version 0.155
libelf: elf*_xlatetomd now works for cross-endian ELF note data.
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index b4d9dc3..ef9b3ac 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,7 @@
+2013-04-28 Jan Kratochvil <jan.kratochvil(a)redhat.com>
+
+ * libdw.map (ELFUTILS_0.156): New.
+
2013-04-24 Mark Wielaard <mjw(a)redhat.com>
* Makefile.am: Use AM_CPPFLAGS instead of INCLUDES.
diff --git a/libdw/libdw.map b/libdw/libdw.map
index 1f71d03..d38a8ef 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -254,3 +254,9 @@ ELFUTILS_0.149 {
dwfl_dwarf_line;
} ELFUTILS_0.148;
+
+ELFUTILS_0.156 {
+ global:
+ # Replaced ELFUTILS_0.122 version, which has a wrapper without add_p_vaddr.
+ dwfl_report_elf;
+} ELFUTILS_0.149;
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index c1a42d8..912064e 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,5 +1,21 @@
2013-04-28 Jan Kratochvil <jan.kratochvil(a)redhat.com>
+ * dwfl_report_elf.c (__libdwfl_report_elf): Add parameter add_p_vaddr.
+ Set it to true for ET_EXEC and ET_CORE. Provide alternative
+ setup of START and BIAS if !ADD_P_VADDR. Set END from BIAS, not BASE.
+ (dwfl_report_elf): Add parameter add_p_vaddr. Pass it down. Add
+ NEW_VERSION.
+ (_compat_without_add_p_vaddr_dwfl_report_elf) <SHARED>: New, with
+ COMPAT_VERSION.
+ * libdwfl.h (dwfl_report_elf): Add parameter add_p_vaddr. Describe it.
+ * libdwflP.h (__libdwfl_report_elf): Add parameter add_p_vaddr.
+ * link_map.c (report_r_debug): Use true add_p_vaddr for dwfl_report_elf.
+ * linux-kernel-modules.c (report_kernel): Use false add_p_vaddr for
+ dwfl_report_elf.
+ * offline.c (process_elf): Use true add_p_vaddr for dwfl_report_elf.
+
+2013-04-28 Jan Kratochvil <jan.kratochvil(a)redhat.com>
+
* dwfl_module_build_id.c: Use COMPAT_VERSION_SAMEPROTO instead of
COMPAT_VERSION.
diff --git a/libdwfl/dwfl_report_elf.c b/libdwfl/dwfl_report_elf.c
index d706170..1881803 100644
--- a/libdwfl/dwfl_report_elf.c
+++ b/libdwfl/dwfl_report_elf.c
@@ -41,7 +41,8 @@
Dwfl_Module *
internal_function
__libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name,
- int fd, Elf *elf, GElf_Addr base, bool sanity)
+ int fd, Elf *elf, GElf_Addr base, bool add_p_vaddr,
+ bool sanity)
{
GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
if (ehdr == NULL)
@@ -166,6 +167,7 @@ __libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name,
case ET_CORE:
/* An assigned base address is meaningless for these. */
base = 0;
+ add_p_vaddr = true;
case ET_DYN:
default:;
@@ -181,11 +183,19 @@ __libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name,
{
vaddr = ph->p_vaddr & -ph->p_align;
address_sync = ph->p_vaddr + ph->p_memsz;
- start = base + vaddr;
break;
}
}
- bias = base;
+ if (add_p_vaddr)
+ {
+ start = base + vaddr;
+ bias = base;
+ }
+ else
+ {
+ start = base;
+ bias = base - vaddr;
+ }
for (size_t i = phnum; i-- > 0;)
{
@@ -195,7 +205,7 @@ __libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name,
if (ph->p_type == PT_LOAD
&& ph->p_vaddr + ph->p_memsz > 0)
{
- end = base + (ph->p_vaddr + ph->p_memsz);
+ end = bias + (ph->p_vaddr + ph->p_memsz);
break;
}
}
@@ -246,8 +256,8 @@ __libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name,
}
Dwfl_Module *
-dwfl_report_elf (Dwfl *dwfl, const char *name,
- const char *file_name, int fd, GElf_Addr base)
+dwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd,
+ GElf_Addr base, bool add_p_vaddr)
{
bool closefd = false;
if (fd < 0)
@@ -270,7 +280,7 @@ dwfl_report_elf (Dwfl *dwfl, const char *name,
}
Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name,
- fd, elf, base, true);
+ fd, elf, base, add_p_vaddr, true);
if (mod == NULL)
{
elf_end (elf);
@@ -281,3 +291,16 @@ dwfl_report_elf (Dwfl *dwfl, const char *name,
return mod;
}
INTDEF (dwfl_report_elf)
+NEW_VERSION (dwfl_report_elf, ELFUTILS_0.156)
+
+#ifdef SHARED
+COMPAT_VERSION (dwfl_report_elf, ELFUTILS_0.122, without_add_p_vaddr)
+
+Dwfl_Module *
+_compat_without_add_p_vaddr_dwfl_report_elf (Dwfl *dwfl, const char *name,
+ const char *file_name, int fd,
+ GElf_Addr base)
+{
+ return dwfl_report_elf (dwfl, name, file_name, fd, base, true);
+}
+#endif
diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h
index 000d582..572520d 100644
--- a/libdwfl/libdwfl.h
+++ b/libdwfl/libdwfl.h
@@ -141,10 +141,14 @@ extern Dwfl_Module *dwfl_report_module (Dwfl *dwfl, const char *name,
program headers in the given file, plus BASE. For an ET_REL file,
does a simple absolute section layout starting at BASE.
FD may be -1 to open FILE_NAME. On success, FD is consumed by the
- library, and the `find_elf' callback will not be used for this module. */
+ library, and the `find_elf' callback will not be used for this module.
+ ADD_P_VADDR has meaning only for ET_DYN files. With ADD_P_VADDR true BASE
+ then specifies in-memory address with subtracted on-disk file prelinked base
+ address (such as the GNU C library l_addr field). With ADD_P_VADDR false
+ BASE is in-memory absolute address start for any relocatable ELF type. */
extern Dwfl_Module *dwfl_report_elf (Dwfl *dwfl, const char *name,
const char *file_name, int fd,
- GElf_Addr base);
+ GElf_Addr base, bool add_p_vaddr);
/* Similar, but report the module for offline use. All ET_EXEC files
being reported must be reported before any relocatable objects.
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index 5aaa778..37a9dff 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -382,7 +382,8 @@ extern int __libdwfl_crc32_file (int fd, uint32_t *resp) attribute_hidden;
Consumes ELF on success, not on failure. */
extern Dwfl_Module *__libdwfl_report_elf (Dwfl *dwfl, const char *name,
const char *file_name, int fd,
- Elf *elf, GElf_Addr base, bool sanity)
+ Elf *elf, GElf_Addr base,
+ bool add_p_vaddr, bool sanity)
internal_function;
/* Meat of dwfl_report_offline. */
diff --git a/libdwfl/link_map.c b/libdwfl/link_map.c
index c6ec29c..9f1b867 100644
--- a/libdwfl/link_map.c
+++ b/libdwfl/link_map.c
@@ -384,7 +384,7 @@ report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata,
// XXX hook for sysroot
mod = INTUSE(dwfl_report_elf) (dwfl, basename (name),
- name, -1, l_addr);
+ name, -1, l_addr, true);
}
if (mod != NULL)
diff --git a/libdwfl/linux-kernel-modules.c b/libdwfl/linux-kernel-modules.c
index 9bf5f25..dec1a59 100644
--- a/libdwfl/linux-kernel-modules.c
+++ b/libdwfl/linux-kernel-modules.c
@@ -217,7 +217,7 @@ report_kernel (Dwfl *dwfl, const char **release,
if (report)
{
Dwfl_Module *mod = INTUSE(dwfl_report_elf) (dwfl, KERNEL_MODNAME,
- fname, fd, 0);
+ fname, fd, 0, false);
if (mod == NULL)
result = -1;
else
diff --git a/libdwfl/offline.c b/libdwfl/offline.c
index 26a6bd6..28d2782 100644
--- a/libdwfl/offline.c
+++ b/libdwfl/offline.c
@@ -127,7 +127,8 @@ process_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd,
Elf *elf)
{
Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name, fd, elf,
- dwfl->offline_next_address, false);
+ dwfl->offline_next_address, true,
+ false);
if (mod != NULL)
{
/* If this is an ET_EXEC file with fixed addresses, the address range
diff --git a/tests/ChangeLog b/tests/ChangeLog
index e6722da..495926d 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,7 @@
+2013-04-28 Jan Kratochvil <jan.kratochvil(a)redhat.com>
+
+ * dwfl-report-elf-align.c: Use false add_p_vaddr for dwfl_report_elf.
+
2013-04-27 Mark Wielaard <mjw(a)redhat.com>
* update1.c (main): Use unique tempfile name and unlink file.
diff --git a/tests/dwfl-report-elf-align.c b/tests/dwfl-report-elf-align.c
index 0e8bfe3..a4e97d3 100644
--- a/tests/dwfl-report-elf-align.c
+++ b/tests/dwfl-report-elf-align.c
@@ -53,7 +53,7 @@ main (int argc, char **argv)
uintptr_t base = strtoull (argv[2], &endptr, 0);
assert (endptr && !*endptr);
- Dwfl_Module *mod = dwfl_report_elf (dwfl, argv[1], argv[1], -1, base);
+ Dwfl_Module *mod = dwfl_report_elf (dwfl, argv[1], argv[1], -1, base, false);
assert (mod != NULL);
uintptr_t funcaddr = strtoull (argv[3], &endptr, 0);
10 years, 4 months