[From nobody Tue Dec 17 18:06:08 2013 From: Jan Kratochvil <jan.kratochvil@redhat.com> Date: Tue, 17 Dec 2013 18:49:54 +0100 Subject: [PATCH] unwinder: s390 and s390x backends/ 2013-12-17 Jan Kratochvil <jan.kratochvil@redhat.com> unwinder: s390 and s390x * Makefile.am (s390_SRCS): Add s390_initreg.c and s390_unwind.c. * s390_corenote.c (prstatus_regs): Set PC_REGISTER. Reindent all the entries. * s390_init.c (s390_init): Initialize frame_nregs, set_initial_registers_tid, normalize_pc and unwind. * s390_initreg.c: New file. * s390_unwind.c: New file. libdwfl/ 2013-12-17 Jan Kratochvil <jan.kratochvil@redhat.com> unwinder: s390 and s390x * dwfl_frame_pc.c (dwfl_frame_pc): Call ebl_normalize_pc. * frame_unwind.c (new_unwound): New function from ... (handle_cfi): ... here. Call it. (setfunc, getfunc, readfunc): New functions. (__libdwfl_frame_unwind): Call ebl_unwind with those functions. * linux-core-attach.c (core_set_initial_registers): Always iterate through the Ebl_Register_Location loop. Call dwfl_thread_state_register_pc there. libebl/ 2013-12-17 Jan Kratochvil <jan.kratochvil@redhat.com> unwinder: s390 and s390x * Makefile.am (gen_SOURCES): Add eblnormalizepc.c and eblunwind.c. * ebl-hooks.h (normalize_pc, unwind): New. * eblnormalizepc.c: New file. * eblunwind.c: New file. * libebl.h (Ebl_Register_Location): Add field pc_register. (ebl_normalize_pc): New declaration. (ebl_tid_registers_get_t, ebl_pid_memory_read_t): New definitions. (ebl_unwind): New declaration. tests/ 2013-12-17 Jan Kratochvil <jan.kratochvil@redhat.com> unwinder: s390 and s390x * Makefile.am (TESTS): Add run-backtrace-core-s390x.sh and run-backtrace-core-s390.sh. (EXTRA_DIST): Add backtrace.s390x.core.bz2, backtrace.s390x.exec.bz2, backtrace.s390.core.bz2, backtrace.s390.exec.bz2, run-backtrace-core-s390x.sh and run-backtrace-core-s390.sh. * 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-core-s390.sh: New file. * run-backtrace-core-s390x.sh: New file. Signed-off-by: Jan Kratochvil <jan.kratochvil@redhat.com> --- backends/Makefile.am | 3 +- backends/s390_corenote.c | 10 +-- backends/s390_init.c | 9 +++ backends/s390_initreg.c | 87 ++++++++++++++++++++++++ backends/s390_unwind.c | 139 ++++++++++++++++++++++++++++++++++++++ libdwfl/dwfl_frame_pc.c | 1 + libdwfl/frame_unwind.c | 93 ++++++++++++++++++++++--- libdwfl/linux-core-attach.c | 14 ++-- libebl/Makefile.am | 3 +- libebl/ebl-hooks.h | 17 +++++ libebl/eblnormalizepc.c | 40 +++++++++++ libebl/eblunwind.c | 43 ++++++++++++ libebl/libebl.h | 29 ++++++++ tests/Makefile.am | 8 ++- tests/backtrace.s390.core.bz2 | Bin 0 -> 7375 bytes tests/backtrace.s390.exec.bz2 | Bin 0 -> 352692 bytes tests/backtrace.s390x.core.bz2 | Bin 0 -> 7740 bytes tests/backtrace.s390x.exec.bz2 | Bin 0 -> 347228 bytes tests/run-backtrace-core-s390.sh | 20 ++++++ tests/run-backtrace-core-s390x.sh | 20 ++++++ 20 files changed, 513 insertions(+), 23 deletions(-) create mode 100644 backends/s390_initreg.c create mode 100644 backends/s390_unwind.c create mode 100644 libebl/eblnormalizepc.c create mode 100644 libebl/eblunwind.c create mode 100644 tests/backtrace.s390.core.bz2 create mode 100644 tests/backtrace.s390.exec.bz2 create mode 100644 tests/backtrace.s390x.core.bz2 create mode 100644 tests/backtrace.s390x.exec.bz2 create mode 100755 tests/run-backtrace-core-s390.sh create mode 100755 tests/run-backtrace-core-s390x.sh diff --git a/backends/Makefile.am b/backends/Makefile.am index 4752a64..ec9e0a3 100644 --- a/backends/Makefile.am +++ b/backends/Makefile.am @@ -106,7 +106,8 @@ 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_cfi.c + s390_corenote.c s390x_corenote.c s390_cfi.c s390_initreg.c \ + s390_unwind.c libebl_s390_pic_a_SOURCES = $(s390_SRCS) am_libebl_s390_pic_a_OBJECTS = $(s390_SRCS:.c=.os) diff --git a/backends/s390_corenote.c b/backends/s390_corenote.c index b88c05c..7ca3516 100644 --- a/backends/s390_corenote.c +++ b/backends/s390_corenote.c @@ -47,13 +47,13 @@ static const Ebl_Register_Location prstatus_regs[] = { -#define GR(at, n, dwreg, b) \ +#define GR(at, n, dwreg, b...) \ { .offset = at * BITS/8, .regno = dwreg, .count = n, .bits = b } - GR ( 0, 1, 64, BITS), /* pswm */ - GR ( 1, 1, 65, BITS), /* pswa */ - GR ( 2, 16, 0, BITS), /* r0-r15 */ - GR (18, 16, 48, 32), /* ar0-ar15 */ + GR ( 0, 1, 64, BITS), /* pswm */ + GR ( 1, 1, 65, BITS, .pc_register = true ), /* pswa */ + GR ( 2, 16, 0, BITS), /* r0-r15 */ + GR (18, 16, 48, 32), /* ar0-ar15 */ #undef GR }; diff --git a/backends/s390_init.c b/backends/s390_init.c index 630a2ee..26b20b4 100644 --- a/backends/s390_init.c +++ b/backends/s390_init.c @@ -62,6 +62,15 @@ s390_init (elf, machine, eh, ehlen) 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_nregs = 32; + HOOK (eh, set_initial_registers_tid); + if (eh->class == ELFCLASS32) + HOOK (eh, normalize_pc); + HOOK (eh, unwind); /* Only the 64-bit format uses the incorrect hash table entry size. */ if (eh->class == ELFCLASS64) diff --git a/backends/s390_initreg.c b/backends/s390_initreg.c new file mode 100644 index 0000000..62a1531 --- /dev/null +++ b/backends/s390_initreg.c @@ -0,0 +1,87 @@ +/* Fetch live process registers from TID. + Copyright (C) 2013 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "system.h" +#include <assert.h> +#ifdef __s390__ +# include <sys/user.h> +# include <asm/ptrace.h> +# include <sys/ptrace.h> +#endif + +#define BACKEND s390_ +#include "libebl_CPU.h" + +bool +s390_set_initial_registers_tid (pid_t tid __attribute__ ((unused)), + ebl_tid_registers_t *setfunc __attribute__ ((unused)), + void *arg __attribute__ ((unused))) +{ +#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. */ + Dwarf_Word dwarf_regs[16]; + for (unsigned u = 0; u < 16; u++) + dwarf_regs[u] = user_regs.regs.gprs[u]; + if (! setfunc (0, 16, dwarf_regs, arg)) + return false; + /* Avoid conversion double -> integer. */ + eu_static_assert (sizeof user_regs.regs.fp_regs.fprs[0] + == sizeof dwarf_regs[0]); + for (unsigned u = 0; u < 16; u++) + dwarf_regs[u] = *((const __typeof (dwarf_regs[0]) *) + &user_regs.regs.fp_regs.fprs[u]); + if (! setfunc (16, 16, dwarf_regs, arg)) + return false; + dwarf_regs[0] = user_regs.regs.psw.addr; + return setfunc (-1, 1, dwarf_regs, arg); +#endif /* __s390__ */ +} + +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_unwind.c b/backends/s390_unwind.c new file mode 100644 index 0000000..6286ee5 --- /dev/null +++ b/backends/s390_unwind.c @@ -0,0 +1,139 @@ +/* Get previous frame state for an existing frame state. + Copyright (C) 2013 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of 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> + +#define BACKEND s390_ +#include "libebl_CPU.h" + +/* s390/s390x do not annotate signal handler frame by CFI. It would be also + difficult as PC points into a stub built on stock. Function below is called + only if unwinder could not find CFI. Function then verifies the register + state for this frame really belongs to a signal frame. In such case it + fetches original registers saved by the signal frame. */ + +bool +s390_unwind (Ebl *ebl, Dwarf_Addr pc, ebl_tid_registers_t *setfunc, + ebl_tid_registers_get_t *getfunc, ebl_pid_memory_read_t *readfunc, + void *arg, bool *signal_framep) +{ + /* 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_Word instr; + if (! readfunc (pc, &instr, arg)) + 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' as the first instruction. */ + if (((instr >> 8) & 0xff) != 0x0a) + return false; + /* Check for 'sigreturn' or 'rt_sigreturn' as the second instruction. */ + if ((instr & 0xff) != 119 && (instr & 0xff) != 173) + return false; + /* See GDB s390_sigtramp_frame_unwind_cache. */ + Dwarf_Word this_sp; + if (! getfunc (0 + 15, 1, &this_sp, arg)) + 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". + Pointer to the array of saved registers is at NEXT_CFA + 8. */ + Dwarf_Word sigreg_ptr; + if (! readfunc (next_cfa + 8, &sigreg_ptr, arg)) + return false; + /* Skip PSW mask. */ + sigreg_ptr += word_size; + /* Read PSW address. */ + Dwarf_Word val; + if (! readfunc (sigreg_ptr, &val, arg)) + return false; + if (! setfunc (-1, 1, &val, arg)) + return false; + sigreg_ptr += word_size; + /* Then the GPRs. */ + Dwarf_Word gprs[16]; + for (int i = 0; i < 16; i++) + { + if (! readfunc (sigreg_ptr, &gprs[i], arg)) + 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. */ + Dwarf_Word fprs[16]; + for (int i = 0; i < 16; i++) + { + if (! readfunc (sigreg_ptr, &val, arg)) + return false; + if (ebl->class == ELFCLASS32) + { + Dwarf_Addr val_low; + if (! readfunc (sigreg_ptr + 4, &val_low, arg)) + return false; + val = (val << 32) | val_low; + } + fprs[i] = val; + sigreg_ptr += 8; + } + /* If we have them, the GPR upper halves are appended at the end. */ + if (ebl->class == ELFCLASS32) + { + /* Skip signal number. */ + sigreg_ptr += 4; + for (int i = 0; i < 16; i++) + { + if (! readfunc (sigreg_ptr, &val, arg)) + return false; + Dwarf_Word val_low = gprs[i]; + val = (val << 32) | val_low; + gprs[i] = val; + sigreg_ptr += 4; + } + } + if (! setfunc (0, 16, gprs, arg)) + return false; + if (! setfunc (16, 16, fprs, arg)) + return false; + *signal_framep = true; + return true; +} diff --git a/libdwfl/dwfl_frame_pc.c b/libdwfl/dwfl_frame_pc.c index 5462e4c..296c815 100644 --- a/libdwfl/dwfl_frame_pc.c +++ b/libdwfl/dwfl_frame_pc.c @@ -37,6 +37,7 @@ dwfl_frame_pc (Dwfl_Frame *state, Dwarf_Addr *pc, bool *isactivation) { assert (state->pc_state == DWFL_FRAME_STATE_PC_SET); *pc = state->pc; + ebl_normalize_pc (state->thread->process->ebl, pc); if (isactivation) { /* Bottom frame? */ diff --git a/libdwfl/frame_unwind.c b/libdwfl/frame_unwind.c index 3635df1..671c6d1 100644 --- a/libdwfl/frame_unwind.c +++ b/libdwfl/frame_unwind.c @@ -494,6 +494,26 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops, return true; } +static void +new_unwound (Dwfl_Frame *state) +{ + assert (state->unwound == NULL); + Dwfl_Thread *thread = state->thread; + Dwfl_Process *process = thread->process; + Ebl *ebl = process->ebl; + size_t nregs = ebl_frame_nregs (ebl); + assert (nregs > 0); + Dwfl_Frame *unwound; + unwound = malloc (sizeof (*unwound) + sizeof (*unwound->regs) * nregs); + state->unwound = unwound; + unwound->thread = thread; + unwound->unwound = NULL; + unwound->signal_frame = false; + unwound->initial_frame = false; + unwound->pc_state = DWFL_FRAME_STATE_ERROR; + memset (unwound->regs_set, 0, sizeof (unwound->regs_set)); +} + /* 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 @@ -508,20 +528,14 @@ handle_cfi (Dwfl_Frame *state, Dwarf_Addr pc, Dwarf_CFI *cfi, Dwarf_Addr bias) __libdwfl_seterrno (DWFL_E_LIBDW); return; } + new_unwound (state); + Dwfl_Frame *unwound = state->unwound; + unwound->signal_frame = frame->fde->cie->signal_frame; Dwfl_Thread *thread = state->thread; Dwfl_Process *process = thread->process; Ebl *ebl = process->ebl; size_t nregs = ebl_frame_nregs (ebl); assert (nregs > 0); - Dwfl_Frame *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->initial_frame = false; - 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; @@ -583,6 +597,47 @@ handle_cfi (Dwfl_Frame *state, Dwarf_Addr pc, Dwarf_CFI *cfi, Dwarf_Addr bias) free (frame); } +static bool +setfunc (int firstreg, unsigned nregs, const Dwarf_Word *regs, void *arg) +{ + Dwfl_Frame *state = arg; + Dwfl_Frame *unwound = state->unwound; + if (firstreg < 0) + { + assert (firstreg == -1); + assert (nregs == 1); + assert (unwound->pc_state == DWFL_FRAME_STATE_PC_UNDEFINED); + unwound->pc = *regs; + unwound->pc_state = DWFL_FRAME_STATE_PC_SET; + return true; + } + while (nregs--) + if (! __libdwfl_frame_reg_set (unwound, firstreg++, *regs++)) + return false; + return true; +} + +static bool +getfunc (int firstreg, unsigned nregs, Dwarf_Word *regs, void *arg) +{ + Dwfl_Frame *state = arg; + assert (firstreg >= 0); + while (nregs--) + if (! __libdwfl_frame_reg_get (state, firstreg++, regs++)) + return false; + return true; +} + +static bool +readfunc (Dwarf_Addr addr, Dwarf_Word *datap, void *arg) +{ + Dwfl_Frame *state = arg; + Dwfl_Thread *thread = state->thread; + Dwfl_Process *process = thread->process; + return process->callbacks->memory_read (process->dwfl, addr, datap, + process->callbacks_arg); +} + void internal_function __libdwfl_frame_unwind (Dwfl_Frame *state) @@ -619,4 +674,24 @@ __libdwfl_frame_unwind (Dwfl_Frame *state) return; } } + assert (state->unwound == NULL); + Dwfl_Thread *thread = state->thread; + Dwfl_Process *process = thread->process; + Ebl *ebl = process->ebl; + new_unwound (state); + state->unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED; + // &Dwfl_Frame.signal_frame cannot be passed as it is a bitfield. + bool signal_frame = false; + if (! ebl_unwind (ebl, pc, setfunc, getfunc, readfunc, state, &signal_frame)) + { + // Discard the unwind attempt. During next __libdwfl_frame_unwind call + // we may have for example the appropriate Dwfl_Module already mapped. + assert (state->unwound->unwound == NULL); + free (state->unwound); + state->unwound = NULL; + // __libdwfl_seterrno has been called above. + return; + } + assert (state->unwound->pc_state == DWFL_FRAME_STATE_PC_SET); + state->unwound->signal_frame = signal_frame; } diff --git a/libdwfl/linux-core-attach.c b/libdwfl/linux-core-attach.c index 11379ba..f55faf7 100644 --- a/libdwfl/linux-core-attach.c +++ b/libdwfl/linux-core-attach.c @@ -228,12 +228,12 @@ core_set_initial_registers (Dwfl_Thread *thread, void *thread_arg_voidp) for (size_t regloci = 0; regloci < nregloc; regloci++) { const Ebl_Register_Location *regloc = reglocs + regloci; - if (regloc->regno >= nregs) + // Iterate even regs out of NREGS range so that we can find pc_register. + if (regloc->bits != 32 && regloc->bits != 64) 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 < regloc->regno + (regloc->count ?: 1U); regno++) { /* PPC provides DWARF register 65 irrelevant for @@ -241,7 +241,8 @@ core_set_initial_registers (Dwfl_Thread *thread, void *thread_arg_voidp) LR (108) is provided earlier (in NT_PRSTATUS) than the # 65. FIXME: It depends now on their order in core notes. FIXME: It uses private function. */ - if (__libdwfl_frame_reg_get (thread->unwound, regno, NULL)) + if (regno < nregs + && __libdwfl_frame_reg_get (thread->unwound, regno, NULL)) continue; Dwarf_Word val; switch (regloc->bits) @@ -266,7 +267,10 @@ core_set_initial_registers (Dwfl_Thread *thread, void *thread_arg_voidp) abort (); } /* Registers not valid for CFI are just ignored. */ - INTUSE(dwfl_thread_state_registers) (thread, regno, 1, &val); + if (regno < nregs) + INTUSE(dwfl_thread_state_registers) (thread, regno, 1, &val); + if (regloc->pc_register) + INTUSE(dwfl_thread_state_register_pc) (thread, val); reg_desc += regloc->pad; } } diff --git a/libebl/Makefile.am b/libebl/Makefile.am index 2f3b730..fc4f1b6 100644 --- a/libebl/Makefile.am +++ b/libebl/Makefile.am @@ -54,7 +54,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 eblinitreg.c ebldwarftoregno.c + eblstother.c eblinitreg.c ebldwarftoregno.c eblnormalizepc.c \ + eblunwind.c libebl_a_SOURCES = $(gen_SOURCES) diff --git a/libebl/ebl-hooks.h b/libebl/ebl-hooks.h index f721bc4..2243895 100644 --- a/libebl/ebl-hooks.h +++ b/libebl/ebl-hooks.h @@ -166,5 +166,22 @@ bool EBLHOOK(set_initial_registers_tid) (pid_t tid, Dwarf_Frame->REGS indexing. */ bool EBLHOOK(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); + +/* Get previous frame state for an existing frame state. Method is called only + if unwinder could not find CFI for current PC. PC is for the + existing frame. SETFUNC sets register in the previous frame. GETFUNC gets + register from the existing frame. Note that GETFUNC vs. SETFUNC act on + a disjunct set of registers. READFUNC reads memory. ARG has to be passed + for SETFUNC, GETFUNC and READFUNC. *SIGNAL_FRAMEP is initialized to false, + it can be set to true if existing frame is a signal frame. SIGNAL_FRAMEP is + never NULL. */ +bool EBLHOOK(unwind) (Ebl *ebl, Dwarf_Addr pc, ebl_tid_registers_t *setfunc, + ebl_tid_registers_get_t *getfunc, + ebl_pid_memory_read_t *readfunc, void *arg, + bool *signal_framep); + /* Destructor for ELF backend handle. */ void EBLHOOK(destr) (struct ebl *); diff --git a/libebl/eblnormalizepc.c b/libebl/eblnormalizepc.c new file mode 100644 index 0000000..a5fea77 --- /dev/null +++ b/libebl/eblnormalizepc.c @@ -0,0 +1,40 @@ +/* Modify PC as fetched from inferior data into valid PC. + Copyright (C) 2013 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of 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/eblunwind.c b/libebl/eblunwind.c new file mode 100644 index 0000000..1251c1b --- /dev/null +++ b/libebl/eblunwind.c @@ -0,0 +1,43 @@ +/* Get previous frame state for an existing frame state. + Copyright (C) 2013 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of 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_unwind (Ebl *ebl, Dwarf_Addr pc, ebl_tid_registers_t *setfunc, + ebl_tid_registers_get_t *getfunc, ebl_pid_memory_read_t *readfunc, + void *arg, bool *signal_framep) +{ + if (ebl == NULL || ebl->unwind == NULL) + return false; + return ebl->unwind (ebl, pc, setfunc, getfunc, readfunc, arg, signal_framep); +} diff --git a/libebl/libebl.h b/libebl/libebl.h index 7080b4a..84c2f4c 100644 --- a/libebl/libebl.h +++ b/libebl/libebl.h @@ -356,6 +356,7 @@ typedef struct uint8_t bits; /* Bits of data for one register. */ uint8_t pad; /* Bytes of padding after register's data. */ Dwarf_Half count; /* Consecutive register numbers here. */ + bool pc_register; } Ebl_Register_Location; /* Non-register data items in core notes. */ @@ -410,6 +411,34 @@ extern size_t ebl_frame_nregs (Ebl *ebl) extern bool ebl_dwarf_to_regno (Ebl *ebl, unsigned *regno) __nonnull_attribute__ (1, 2); +/* Modify PC as fetched from inferior data into valid PC. */ +extern void ebl_normalize_pc (Ebl *ebl, Dwarf_Addr *pc) + __nonnull_attribute__ (1, 2); + +/* Callback type for ebl_unwind's parameter getfunc. */ +typedef bool (ebl_tid_registers_get_t) (int firstreg, unsigned nregs, + Dwarf_Word *regs, void *arg) + __nonnull_attribute__ (3); + +/* Callback type for ebl_unwind's parameter readfunc. */ +typedef bool (ebl_pid_memory_read_t) (Dwarf_Addr addr, Dwarf_Word *data, + void *arg) + __nonnull_attribute__ (3); + +/* Get previous frame state for an existing frame state. Method is called only + if unwinder could not find CFI for current PC. PC is for the + existing frame. SETFUNC sets register in the previous frame. GETFUNC gets + register from the existing frame. Note that GETFUNC vs. SETFUNC act on + a disjunct set of registers. READFUNC reads memory. ARG has to be passed + for SETFUNC, GETFUNC and READFUNC. *SIGNAL_FRAMEP is initialized to false, + it can be set to true if existing frame is a signal frame. SIGNAL_FRAMEP is + never NULL. */ +extern bool ebl_unwind (Ebl *ebl, Dwarf_Addr pc, ebl_tid_registers_t *setfunc, + ebl_tid_registers_get_t *getfunc, + ebl_pid_memory_read_t *readfunc, void *arg, + bool *signal_framep) + __nonnull_attribute__ (1, 3, 4, 5, 7); + #ifdef __cplusplus } #endif diff --git a/tests/Makefile.am b/tests/Makefile.am index 7739623..b58e0f5 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -106,7 +106,8 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \ run-backtrace-native.sh run-backtrace-data.sh run-backtrace-dwarf.sh \ run-backtrace-native-biarch.sh run-backtrace-native-core.sh \ run-backtrace-native-core-biarch.sh run-backtrace-core-x86_64.sh \ - run-backtrace-core-i386.sh run-backtrace-core-ppc.sh + run-backtrace-core-i386.sh run-backtrace-core-ppc.sh \ + run-backtrace-core-s390x.sh run-backtrace-core-s390.sh if !BIARCH export ELFUTILS_DISABLE_BIARCH = 1 @@ -247,7 +248,10 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ backtrace-subr.sh backtrace.i386.core.bz2 backtrace.i386.exec.bz2 \ backtrace.x86_64.core.bz2 backtrace.x86_64.exec.bz2 \ backtrace.ppc.core.bz2 backtrace.ppc.exec.bz2 \ - run-backtrace-core-ppc.sh + run-backtrace-core-ppc.sh \ + backtrace.s390x.core.bz2 backtrace.s390x.exec.bz2 \ + backtrace.s390.core.bz2 backtrace.s390.exec.bz2 \ + run-backtrace-core-s390x.sh run-backtrace-core-s390.sh if USE_VALGRIND valgrind_cmd='valgrind -q --error-exitcode=1 --run-libc-freeres=no' diff --git a/tests/run-backtrace-core-s390.sh b/tests/run-backtrace-core-s390.sh new file mode 100755 index 0000000..d3b6dc9 --- /dev/null +++ b/tests/run-backtrace-core-s390.sh @@ -0,0 +1,20 @@ +#! /bin/bash +# Copyright (C) 2013 Red Hat, Inc. +# This file is part of elfutils. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# elfutils is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +. $srcdir/backtrace-subr.sh + +check_core s390 diff --git a/tests/run-backtrace-core-s390x.sh b/tests/run-backtrace-core-s390x.sh new file mode 100755 index 0000000..c3e236d --- /dev/null +++ b/tests/run-backtrace-core-s390x.sh @@ -0,0 +1,20 @@ +#! /bin/bash +# Copyright (C) 2013 Red Hat, Inc. +# This file is part of elfutils. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# elfutils is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +. $srcdir/backtrace-subr.sh + +check_core s390x -- 1.8.3.1 ]