[patch 5/4] unwinder: non-x86* parts of the unwinder [resent]

Jan Kratochvil jan.kratochvil at redhat.com
Wed Nov 14 19:55:08 UTC 2012


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 at 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 at 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 at 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 at 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, &regno))
+    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, &regno))
+    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


More information about the elfutils-devel mailing list