unwinder: s390 and s390x
authorJan Kratochvil <jan.kratochvil@redhat.com>
Tue, 17 Dec 2013 17:49:54 +0000 (18:49 +0100)
committerJan Kratochvil <jan.kratochvil@redhat.com>
Wed, 18 Dec 2013 12:01:05 +0000 (13:01 +0100)
Signed-off-by: Jan Kratochvil <jan.kratochvil@redhat.com>
24 files changed:
backends/ChangeLog
backends/Makefile.am
backends/s390_corenote.c
backends/s390_init.c
backends/s390_initreg.c [new file with mode: 0644]
backends/s390_unwind.c [new file with mode: 0644]
libdwfl/ChangeLog
libdwfl/dwfl_frame_pc.c
libdwfl/frame_unwind.c
libdwfl/linux-core-attach.c
libebl/ChangeLog
libebl/Makefile.am
libebl/ebl-hooks.h
libebl/eblnormalizepc.c [new file with mode: 0644]
libebl/eblunwind.c [new file with mode: 0644]
libebl/libebl.h
tests/ChangeLog
tests/Makefile.am
tests/backtrace.s390.core.bz2 [new file with mode: 0644]
tests/backtrace.s390.exec.bz2 [new file with mode: 0644]
tests/backtrace.s390x.core.bz2 [new file with mode: 0644]
tests/backtrace.s390x.exec.bz2 [new file with mode: 0644]
tests/run-backtrace-core-s390.sh [new file with mode: 0755]
tests/run-backtrace-core-s390x.sh [new file with mode: 0755]

index dc96048..cb56d21 100644 (file)
@@ -1,3 +1,14 @@
+2013-12-18  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.
+
 2013-12-15  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
        unwinder: ppc and ppc64
index 4752a64..ec9e0a3 100644 (file)
@@ -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)
 
index b88c05c..7ca3516 100644 (file)
 
 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
   };
index 630a2ee..26b20b4 100644 (file)
@@ -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 (file)
index 0000000..62a1531
--- /dev/null
@@ -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 (file)
index 0000000..752bc28
--- /dev/null
@@ -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 stack.  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;
+}
index a025a96..e719df9 100644 (file)
@@ -1,3 +1,15 @@
+2013-12-18  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.
+
 2013-12-17  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
        * frame_unwind.c (handle_cfi): Call ebl_dwarf_to_regno for RA.
index 5462e4c..296c815 100644 (file)
@@ -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?  */
index 3635df1..671c6d1 100644 (file)
@@ -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;
 }
index 11379ba..f55faf7 100644 (file)
@@ -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;
        }
     }
index a27de82..f1aa242 100644 (file)
@@ -1,3 +1,15 @@
+2013-12-18  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.
+
 2013-12-15  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
        unwinder: ppc and ppc64
index 2f3b730..fc4f1b6 100644 (file)
@@ -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)
 
index f721bc4..2243895 100644 (file)
@@ -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 (file)
index 0000000..a5fea77
--- /dev/null
@@ -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 (file)
index 0000000..1251c1b
--- /dev/null
@@ -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);
+}
index 7080b4a..84c2f4c 100644 (file)
@@ -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
index 61c4b84..8561e64 100644 (file)
@@ -1,3 +1,18 @@
+2013-12-18  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.
+
 2013-12-17  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
        * backtrace-dwarf.c (executable, find_elf, dwfl_offline): Remove unused
index 7739623..b58e0f5 100644 (file)
@@ -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/backtrace.s390.core.bz2 b/tests/backtrace.s390.core.bz2
new file mode 100644 (file)
index 0000000..db34694
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 (file)
index 0000000..4c1b4ae
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 (file)
index 0000000..61c23ec
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 (file)
index 0000000..8009239
Binary files /dev/null and b/tests/backtrace.s390x.exec.bz2 differ
diff --git a/tests/run-backtrace-core-s390.sh b/tests/run-backtrace-core-s390.sh
new file mode 100755 (executable)
index 0000000..d3b6dc9
--- /dev/null
@@ -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 (executable)
index 0000000..c3e236d
--- /dev/null
@@ -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