[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, 1 month
[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 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, 4 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
readelf: Displaying "raw' .debug_aranges
by Mark Wielaard
Hi,
I was chasing a anomaly with .debug_aranges and found the eu-readelf
--debug-dump=aranges output not very helpful. It uses libdw
dwarf_getaranges which hides lots of details and sorts the addresses.
Not helpful if you are trying to see why something might be wrong.
binutils readelf also wasn't a big help because it errored out before
displaying anything. So I implemented a "raw" .debug_aranges for
eu-readelf. The output is as follows:
Table at offset 560:
Length: 60
DWARF version: 2
CU offset: 2bb78
Address size: 8
Segment size: 0
0x000000000043c3c0 <expr_unwind>..0x000000000043e0b1 <evalexp+0xe1>
0x000000000041bba4 <_is_arithop>..0x000000000041bbee <_is_arithop+0x4a>
This did allow me to find what the issue was. But I am not sure yet what
the root cause is. The following comment in the new code summarizes what
is happening:
+ next_table:
+ if (readp != nexthdr)
+ {
+ size_t padding = nexthdr - readp;
+ printf (gettext (" %Zu padding bytes\n"), padding);
+ // To skip, or not to skip, that is the question...
+ // dwarf_getaranges doesn't skip, it assumes that after
+ // seeing the terminator sequence there must be another
+ // table and ignores the length field. Strangely enough
+ // that seems to be the right thing sometimes...
+ readp = nexthdr;
+ }
The above seems to be "correct" according to the DWARF spec. You have to
skip till the next table based on table header length field. But on the
buggy (?) .debug_aranges I am looking at not skipping the padding is the
right thing. This is also what dwarf_getaranges does. So maybe that is
correct? But it also isn't what binutils readelf and dwz seem to do...
I'll try to figure out what is going on. Meanwhile comments on the code
welcome.
Thanks,
Mark
10 years, 6 months
[PATCH] libdw: dwarf_getsrclines mark highest address as end_sequence.
by Mark Wielaard
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. We could reject it as bad DWARF
but the DWARF .debug_lines spec is much stricter than what compilers
seem to output in practice.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
libdw/ChangeLog | 5 +++++
libdw/dwarf_getsrclines.c | 5 +++++
2 files changed, 10 insertions(+)
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 16acf5c..12df9bb 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,8 @@
+2013-03-25 Mark Wielaard <mjw(a)redhat.com>
+
+ * dwarf_getsrclines.c (dwarf_getsrclines): Mark highest address as
+ end_sequence.
+
2013-03-12 Mark Wielaard <mjw(a)redhat.com>
* dwarf_getsrcfiles.c (dwarf_getsrcfiles): Allow DW_TAG_partial_unit.
diff --git a/libdw/dwarf_getsrclines.c b/libdw/dwarf_getsrclines.c
index c24aebb..5a58b78 100644
--- a/libdw/dwarf_getsrclines.c
+++ b/libdw/dwarf_getsrclines.c
@@ -712,6 +712,11 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
cu->lines->info[i].files = files;
}
+ /* 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;
+
/* Success. */
res = 0;
}
--
1.8.1.4
10 years, 6 months
[PATCH] readelf: When highpc is in constant form also print as address.
by Mark Wielaard
When highpc is in constant form it is relative to lowpc. In that case
also show as address.
Suggested-by: Jan Kratochvil <jan.kratochvil(a)redhat.com>
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
src/ChangeLog | 7 +++++++
src/readelf.c | 16 +++++++++++++++-
tests/ChangeLog | 4 ++++
tests/run-readelf-dwz-multi.sh | 12 ++++++------
4 files changed, 32 insertions(+), 7 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index a641d33..24bc6bf 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,10 @@
+2013-03-25 Mark Wielaard <mjw(a)redhat.com>
+
+ * readelf.c (attrcb_args): Add Dwarf_Die.
+ (attr_callback): When highpc is in constant form also print as
+ address.
+ (print_debug_units): Set args.die.
+
2013-03-19 Mark Wielaard <mjw(a)redhat.com>
* readelf.c (print_gdb_index_section): Free format_dwarf_addr results.
diff --git a/src/readelf.c b/src/readelf.c
index 1412bed..d70916a 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -5242,6 +5242,7 @@ struct attrcb_args
{
Dwfl_Module *dwflmod;
Dwarf *dbg;
+ Dwarf_Die *die;
int level;
bool silent;
unsigned int version;
@@ -5437,7 +5438,19 @@ attr_callback (Dwarf_Attribute *attrp, void *arg)
if (cbargs->silent)
break;
- if (valuestr == NULL)
+ /* When highpc is in constant form it is relative to lowpc.
+ In that case also show the address. */
+ Dwarf_Addr highpc;
+ if (attr == DW_AT_high_pc && (dwarf_highpc (cbargs->die, &highpc) == 0))
+ {
+ char *a = format_dwarf_addr (cbargs->dwflmod, cbargs->addrsize,
+ highpc - 1, highpc);
+ printf (" %*s%-20s (%s) %" PRIuMAX " (%s)\n",
+ (int) (level * 2), "", dwarf_attr_name (attr),
+ dwarf_form_name (form), (uintmax_t) num, a);
+ free (a);
+ }
+ else if (valuestr == NULL)
printf (" %*s%-20s (%s) %" PRIuMAX "\n",
(int) (level * 2), "", dwarf_attr_name (attr),
dwarf_form_name (form), (uintmax_t) num);
@@ -5659,6 +5672,7 @@ print_debug_units (Dwfl_Module *dwflmod,
/* Print the attribute values. */
args.level = level;
+ args.die = &dies[level];
(void) dwarf_getattrs (&dies[level], attr_callback, &args, 0);
/* Make room for the next level's DIE. */
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 62b589d..3465777 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,7 @@
+2013-03-25 Mark Wielaard <mjw(a)redhat.com>
+
+ * run-readelf-dwz-multi.sh: Expect high_pc also as address.
+
2013-03-20 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* Makefile.am (check_PROGRAMS): Add dwfl-report-elf-align.
diff --git a/tests/run-readelf-dwz-multi.sh b/tests/run-readelf-dwz-multi.sh
index 20c00c5..95bef55 100755
--- a/tests/run-readelf-dwz-multi.sh
+++ b/tests/run-readelf-dwz-multi.sh
@@ -90,7 +90,7 @@ DWARF section [28] '.debug_info' at offset 0x1078:
name (strp) "main.c"
comp_dir (GNU_strp_alt) "/home/mark/src/tests/dwz"
low_pc (addr) 0x00000000004006ac <main>
- high_pc (udata) 44
+ high_pc (udata) 44 (0x00000000004006d7 <main+0x2b>)
stmt_list (sec_offset) 0
[ 26] imported_unit
import (GNU_ref_alt) [ b]
@@ -105,7 +105,7 @@ DWARF section [28] '.debug_info' at offset 0x1078:
prototyped (flag_present)
type (GNU_ref_alt) [ 3e]
low_pc (addr) 0x00000000004006ac <main>
- high_pc (udata) 44
+ high_pc (udata) 44 (0x00000000004006d7 <main+0x2b>)
frame_base (exprloc)
[ 0] call_frame_cfa
GNU_all_tail_call_sites (flag_present)
@@ -148,7 +148,7 @@ DWARF section [25] '.debug_info' at offset 0x106c:
name (strp) "shared.c"
comp_dir (GNU_strp_alt) "/home/mark/src/tests/dwz"
low_pc (addr) +0x0000000000000670 <call_foo>
- high_pc (udata) 23
+ high_pc (udata) 23 (+0x0000000000000686 <call_foo+0x16>)
stmt_list (sec_offset) 0
[ 26] imported_unit
import (GNU_ref_alt) [ b]
@@ -160,7 +160,7 @@ DWARF section [25] '.debug_info' at offset 0x106c:
prototyped (flag_present)
type (GNU_ref_alt) [ 3e]
low_pc (addr) +0x0000000000000670 <call_foo>
- high_pc (udata) 23
+ high_pc (udata) 23 (+0x0000000000000686 <call_foo+0x16>)
frame_base (exprloc)
[ 0] call_frame_cfa
GNU_all_call_sites (flag_present)
@@ -185,7 +185,7 @@ DWARF section [28] '.debug_info' at offset 0x1088:
name (GNU_strp_alt) "main.c"
comp_dir (GNU_strp_alt) "/home/mark/src/tests"
low_pc (addr) 0x00000000004004ec <main>
- high_pc (udata) 18
+ high_pc (udata) 18 (0x00000000004004fd <main+0x11>)
stmt_list (sec_offset) 0
[ 26] imported_unit
import (GNU_ref_alt) [ b]
@@ -197,7 +197,7 @@ DWARF section [28] '.debug_info' at offset 0x1088:
prototyped (flag_present)
type (GNU_ref_alt) [ 30]
low_pc (addr) 0x00000000004004ec <main>
- high_pc (udata) 18
+ high_pc (udata) 18 (0x00000000004004fd <main+0x11>)
frame_base (exprloc)
[ 0] call_frame_cfa
GNU_all_call_sites (flag_present)
--
1.8.1.4
10 years, 6 months
[patch 1/4] unwinder: New base address based dwfl_report_elf_baseaddr
by Jan Kratochvil
Hi,
dependent on all the patches listed in:
Pending patches ping
https://lists.fedorahosted.org/pipermail/elfutils-devel/2012-November/002...
present in:
jankratochvil/forunwind-baseaddr
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.
Current dwfl_report_elf function has parameter "GElf_Addr base" but it means
bias, not the base address. Some applications (such as Linux kernel perf)
cannot easily find out the bias but they know the base address.
I tried to implement it without this function but I could not.
Thanks,
Jan
libdwfl/
2012-11-13 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* dwfl_report_elf.c (__libdwfl_report_elf): Add parameter base_is_bias.
Set BASE_IS_BIAS to true for ET_EXEC and ET_CORE. Provide alternative
setup of START and BIAS if !BASE_IS_BIAS. Set END from BIAS, not BASE.
(dwfl_report_elf): Rename to ...
(__libdwfl_report_elf_open): ... here. Add parameter base_is_bias,
pass it to __libdwfl_report_elf.
(dwfl_report_elf, dwfl_report_elf_baseaddr): New wrappers.
* libdwfl.h (dwfl_report_elf_baseaddr): New declaration.
* libdwflP.h (__libdwfl_report_elf): Add parameter base_is_bias.
* offline.c (process_elf): Pass true as base_is_bias to
__libdwfl_report_elf.
diff --git a/libdwfl/dwfl_report_elf.c b/libdwfl/dwfl_report_elf.c
index d706170..7a6970e 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 base_is_bias,
+ 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;
+ base_is_bias = 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 (base_is_bias)
+ {
+ 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,9 @@ __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)
+internal_function
+__libdwfl_report_elf_open (Dwfl *dwfl, const char *name, const char *file_name,
+ int fd, GElf_Addr base, bool base_is_bias)
{
bool closefd = false;
if (fd < 0)
@@ -270,7 +281,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, base_is_bias, true);
if (mod == NULL)
{
elf_end (elf);
@@ -280,4 +291,19 @@ dwfl_report_elf (Dwfl *dwfl, const char *name,
return mod;
}
+
+Dwfl_Module *
+dwfl_report_elf (Dwfl *dwfl, const char *name,
+ const char *file_name, int fd, GElf_Addr base)
+{
+ return __libdwfl_report_elf_open (dwfl, name, file_name, fd, base, true);
+}
INTDEF (dwfl_report_elf)
+
+Dwfl_Module *
+dwfl_report_elf_baseaddr (Dwfl *dwfl, const char *name,
+ const char *file_name, int fd, GElf_Addr base)
+{
+ return __libdwfl_report_elf_open (dwfl, name, file_name, fd, base, false);
+}
+INTDEF (dwfl_report_elf_baseaddr)
diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h
index 8498c0c..a2ab824 100644
--- a/libdwfl/libdwfl.h
+++ b/libdwfl/libdwfl.h
@@ -146,6 +146,13 @@ extern Dwfl_Module *dwfl_report_elf (Dwfl *dwfl, const char *name,
const char *file_name, int fd,
GElf_Addr base);
+/* See dwfl_report_elf except that BASE is the page-aligned absolute VMA
+ address where the ELF file should start. Any possible file prelinking of
+ the disk file is compensated. */
+extern Dwfl_Module *dwfl_report_elf_baseaddr (Dwfl *dwfl, const char *name,
+ const char *file_name, int fd,
+ GElf_Addr base);
+
/* Similar, but report the module for offline use. All ET_EXEC files
being reported must be reported before any relocatable objects.
If this is used, dwfl_report_module and dwfl_report_elf may not be
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index ca8be2f..e5e638c 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -353,7 +353,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 base_is_bias, bool sanity)
internal_function;
/* Meat of dwfl_report_offline. */
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
10 years, 6 months