[PATCH] Support powerpc64le-linux platform
This patch adds support for the powerpc64le-linux platform. It consists
of two main features:
- Support little-endian byte order
This is done via a "big_endian" member of struct unw_addr_space,
which is evaluated by common code via the dwarf_is_big_endian
macro, and also in endian-aware code in unw_is_signal_frame.
- Support the ELFv2 ABI
This is done via an "abi" member of struct unw_addr_space. This
is currently only needed in tdep_get_func_addr, since the ELFv2
ABI does not use function descriptors.
Both new members are initialized in unw_create_addr_space and
ppc64_local_addr_space_init, following the mips precedent.
Since ppc32 and ppc64 now no longer share the unw_create_addr_space
implementation, the file is duplicated from the ppc directory into
ppc32/ppc64.
Tested on powerpc64-linux and powerpc64le-linux. Support on LE
seems to be as good as existing BE support; I have not attempted to
fix the existing shortcomings of PPC support that already cause a
number to tests to fail due to unimplemented features.
Signed-off-by: Ulrich Weigand <address@hidden>
---
include/libunwind-ppc64.h | 7 ++++
include/tdep-ppc64/dwarf-config.h | 2 +-
include/tdep-ppc64/libunwind_i.h | 4 ++-
src/Makefile.am | 8 +++--
src/ppc/Gcreate_addr_space.c | 56 ------------------------------
src/ppc/Gis_signal_frame.c | 18 ++++++++--
src/ppc/Lcreate_addr_space.c | 5 ---
src/ppc32/Gcreate_addr_space.c | 56 ++++++++++++++++++++++++++++++
src/ppc32/Lcreate_addr_space.c | 5 +++
src/ppc64/Gcreate_addr_space.c | 71 +++++++++++++++++++++++++++++++++++++++
src/ppc64/Ginit.c | 6 ++++
src/ppc64/Lcreate_addr_space.c | 5 +++
src/ppc64/get_func_addr.c | 26 ++++++++------
13 files changed, 191 insertions(+), 78 deletions(-)
delete mode 100644 src/ppc/Gcreate_addr_space.c
delete mode 100644 src/ppc/Lcreate_addr_space.c
create mode 100644 src/ppc32/Gcreate_addr_space.c
create mode 100644 src/ppc32/Lcreate_addr_space.c
create mode 100644 src/ppc64/Gcreate_addr_space.c
create mode 100644 src/ppc64/Lcreate_addr_space.c
diff --git a/include/libunwind-ppc64.h b/include/libunwind-ppc64.h
index 66420b3..07c72a9 100644
--- a/include/libunwind-ppc64.h
+++ b/include/libunwind-ppc64.h
@@ -222,6 +222,13 @@ typedef enum
}
ppc64_regnum_t;
+typedef enum
+ {
+ UNW_PPC64_ABI_ELFv1,
+ UNW_PPC64_ABI_ELFv2
+ }
+ppc64_abi_t;
+
/*
* According to David Edelsohn, GNU gcc uses R3, R4, R5, and maybe R6 for
* passing parameters to exception handlers.
diff --git a/include/tdep-ppc64/dwarf-config.h
b/include/tdep-ppc64/dwarf-config.h
index e2676a8..7603d18 100644
--- a/include/tdep-ppc64/dwarf-config.h
+++ b/include/tdep-ppc64/dwarf-config.h
@@ -38,7 +38,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. */
#define DWARF_REGNUM_MAP_LENGTH 115
/* Return TRUE if the ADDR_SPACE uses big-endian byte-order. */
-#define dwarf_is_big_endian(addr_space) 1
+#define dwarf_is_big_endian(addr_space) ((addr_space)->big_endian)
/* Convert a pointer to a dwarf_cursor structure to a pointer to
unw_cursor_t. */
diff --git a/include/tdep-ppc64/libunwind_i.h b/include/tdep-ppc64/libunwind_i.h
index 6d72858..6024455 100644
--- a/include/tdep-ppc64/libunwind_i.h
+++ b/include/tdep-ppc64/libunwind_i.h
@@ -51,6 +51,8 @@ unw_tdep_frame_t;
struct unw_addr_space
{
struct unw_accessors acc;
+ int big_endian;
+ ppc64_abi_t abi;
unw_caching_policy_t caching_policy;
#ifdef HAVE_ATOMIC_OPS_H
AO_t cache_generation;
@@ -289,7 +291,7 @@ extern int tdep_fetch_proc_info_post (struct dwarf_cursor
*c, unw_word_t ip,
#define tdep_get_as(c) ((c)->dwarf.as)
#define tdep_get_as_arg(c) ((c)->dwarf.as_arg)
#define tdep_get_ip(c) ((c)->dwarf.ip)
-#define tdep_big_endian(as) 1
+#define tdep_big_endian(as) ((as)->big_endian)
extern int tdep_init_done;
diff --git a/src/Makefile.am b/src/Makefile.am
index cf137c0..84f7e42 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -342,12 +342,12 @@ libunwind_x86_64_la_SOURCES_x86_64 =
$(libunwind_la_SOURCES_x86_64_common) \
x86_64/Gstash_frame.c x86_64/Gstep.c x86_64/Gtrace.c
# The list of local files that go to Power 64 and 32:
-libunwind_la_SOURCES_ppc = ppc/Lcreate_addr_space.c \
+libunwind_la_SOURCES_ppc = \
ppc/Lget_proc_info.c ppc/Lget_save_loc.c ppc/Linit_local.c \
ppc/Linit_remote.c ppc/Lis_signal_frame.c
# The list of generic files that go to Power 64 and 32:
-libunwind_ppc_la_SOURCES_ppc_generic = ppc/Gcreate_addr_space.c
\
+libunwind_ppc_la_SOURCES_ppc_generic = \
ppc/Gget_proc_info.c ppc/Gget_save_loc.c ppc/Ginit_local.c \
ppc/Ginit_remote.c ppc/Gis_signal_frame.c
@@ -360,6 +360,7 @@ libunwind_la_SOURCES_ppc32_common =
$(libunwind_la_SOURCES_common) \
libunwind_la_SOURCES_ppc32 = $(libunwind_la_SOURCES_ppc32_common) \
$(libunwind_la_SOURCES_local) \
$(libunwind_la_SOURCES_ppc) \
+ ppc32/Lcreate_addr_space.c \
ppc32/Lglobal.c ppc32/Linit.c \
ppc32/Lregs.c ppc32/Lresume.c ppc32/Lstep.c
@@ -367,6 +368,7 @@ libunwind_la_SOURCES_ppc32 =
$(libunwind_la_SOURCES_ppc32_common) \
libunwind_ppc32_la_SOURCES_ppc32 = $(libunwind_la_SOURCES_ppc32_common) \
$(libunwind_la_SOURCES_generic) \
$(libunwind_ppc_la_SOURCES_ppc_generic) \
+ ppc32/Gcreate_addr_space.c \
ppc32/Gglobal.c ppc32/Ginit.c \
ppc32/Gregs.c ppc32/Gresume.c ppc32/Gstep.c
@@ -379,6 +381,7 @@ libunwind_la_SOURCES_ppc64_common =
$(libunwind_la_SOURCES_common) \
libunwind_la_SOURCES_ppc64 = $(libunwind_la_SOURCES_ppc64_common) \
$(libunwind_la_SOURCES_local) \
$(libunwind_la_SOURCES_ppc) \
+ ppc64/Lcreate_addr_space.c \
ppc64/Lglobal.c ppc64/Linit.c \
ppc64/Lregs.c ppc64/Lresume.c ppc64/Lstep.c
@@ -386,6 +389,7 @@ libunwind_la_SOURCES_ppc64 =
$(libunwind_la_SOURCES_ppc64_common) \
libunwind_ppc64_la_SOURCES_ppc64 = $(libunwind_la_SOURCES_ppc64_common) \
$(libunwind_la_SOURCES_generic) \
$(libunwind_ppc_la_SOURCES_ppc_generic) \
+ ppc64/Gcreate_addr_space.c \
ppc64/Gglobal.c ppc64/Ginit.c \
ppc64/Gregs.c ppc64/Gresume.c ppc64/Gstep.c
diff --git a/src/ppc/Gcreate_addr_space.c b/src/ppc/Gcreate_addr_space.c
deleted file mode 100644
index 09bde56..0000000
--- a/src/ppc/Gcreate_addr_space.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/* libunwind - a platform-independent unwind library
- Copyright (C) 2006-2007 IBM
- Contributed by
- Corey Ashford <address@hidden>
- Jose Flavio Aguilar Paulino <address@hidden> <address@hidden>
-
-This file is part of libunwind.
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-
-#include <stdlib.h>
-
-#include <libunwind_i.h>
-
-PROTECTED unw_addr_space_t
-unw_create_addr_space (unw_accessors_t *a, int byte_order)
-{
-#ifdef UNW_LOCAL_ONLY
- return NULL;
-#else
- unw_addr_space_t as;
-
- /*
- * Linux ppc64 supports only big-endian.
- */
- if (byte_order != 0 && byte_order != __BIG_ENDIAN)
- return NULL;
-
- as = malloc (sizeof (*as));
- if (!as)
- return NULL;
-
- memset (as, 0, sizeof (*as));
-
- as->acc = *a;
-
- return as;
-#endif
-}
diff --git a/src/ppc/Gis_signal_frame.c b/src/ppc/Gis_signal_frame.c
index 4154429..b5e11e3 100644
--- a/src/ppc/Gis_signal_frame.c
+++ b/src/ppc/Gis_signal_frame.c
@@ -31,7 +31,7 @@ PROTECTED int
unw_is_signal_frame (unw_cursor_t * cursor)
{
struct cursor *c = (struct cursor *) cursor;
- unw_word_t w0, w1, ip;
+ unw_word_t w0, w1, i0, i1, i2, ip;
unw_addr_space_t as;
unw_accessors_t *a;
void *arg;
@@ -60,7 +60,19 @@ unw_is_signal_frame (unw_cursor_t * cursor)
if ((ret = (*a->access_mem) (as, ip, &w0, 0, arg)) < 0
|| (ret = (*a->access_mem) (as, ip + 8, &w1, 0, arg)) < 0)
return 0;
- w1 >>= 32;
- return (w0 == 0x38210080380000ac && w1 == 0x44000002);
+ if (tdep_big_endian (as))
+ {
+ i0 = w0 >> 32;
+ i1 = w0 & 0xffffffffUL;
+ i2 = w1 >> 32;
+ }
+ else
+ {
+ i0 = w0 & 0xffffffffUL;
+ i1 = w0 >> 32;
+ i2 = w1 & 0xffffffffUL;
+ }
+
+ return (i0 == 0x38210080 && i1 == 0x380000ac && i2 == 0x44000002);
}
diff --git a/src/ppc/Lcreate_addr_space.c b/src/ppc/Lcreate_addr_space.c
deleted file mode 100644
index 0f2dc6b..0000000
--- a/src/ppc/Lcreate_addr_space.c
+++ /dev/null
@@ -1,5 +0,0 @@
-#define UNW_LOCAL_ONLY
-#include <libunwind.h>
-#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
-#include "Gcreate_addr_space.c"
-#endif
diff --git a/src/ppc32/Gcreate_addr_space.c b/src/ppc32/Gcreate_addr_space.c
new file mode 100644
index 0000000..f5055ec
--- /dev/null
+++ b/src/ppc32/Gcreate_addr_space.c
@@ -0,0 +1,56 @@
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2006-2007 IBM
+ Contributed by
+ Corey Ashford <address@hidden>
+ Jose Flavio Aguilar Paulino <address@hidden> <address@hidden>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include <stdlib.h>
+
+#include <libunwind_i.h>
+
+PROTECTED unw_addr_space_t
+unw_create_addr_space (unw_accessors_t *a, int byte_order)
+{
+#ifdef UNW_LOCAL_ONLY
+ return NULL;
+#else
+ unw_addr_space_t as;
+
+ /*
+ * We support only big-endian on Linux ppc32.
+ */
+ if (byte_order != 0 && byte_order != __BIG_ENDIAN)
+ return NULL;
+
+ as = malloc (sizeof (*as));
+ if (!as)
+ return NULL;
+
+ memset (as, 0, sizeof (*as));
+
+ as->acc = *a;
+
+ return as;
+#endif
+}
diff --git a/src/ppc32/Lcreate_addr_space.c b/src/ppc32/Lcreate_addr_space.c
new file mode 100644
index 0000000..0f2dc6b
--- /dev/null
+++ b/src/ppc32/Lcreate_addr_space.c
@@ -0,0 +1,5 @@
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Gcreate_addr_space.c"
+#endif
diff --git a/src/ppc64/Gcreate_addr_space.c b/src/ppc64/Gcreate_addr_space.c
new file mode 100644
index 0000000..686653a
--- /dev/null
+++ b/src/ppc64/Gcreate_addr_space.c
@@ -0,0 +1,71 @@
+/* libunwind - a platform-independent unwind library
+ Copyright (C) 2006-2007 IBM
+ Contributed by
+ Corey Ashford <address@hidden>
+ Jose Flavio Aguilar Paulino <address@hidden> <address@hidden>
+
+This file is part of libunwind.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+#include <stdlib.h>
+
+#include <libunwind_i.h>
+
+PROTECTED unw_addr_space_t
+unw_create_addr_space (unw_accessors_t *a, int byte_order)
+{
+#ifdef UNW_LOCAL_ONLY
+ return NULL;
+#else
+ unw_addr_space_t as;
+
+ /*
+ * We support both big- and little-endian on Linux ppc64.
+ */
+ if (byte_order != 0
+ && byte_order != __LITTLE_ENDIAN
+ && byte_order != __BIG_ENDIAN)
+ return NULL;
+
+ as = malloc (sizeof (*as));
+ if (!as)
+ return NULL;
+
+ memset (as, 0, sizeof (*as));
+
+ as->acc = *a;
+
+ if (byte_order == 0)
+ /* use host default: */
+ as->big_endian = (__BYTE_ORDER == __BIG_ENDIAN);
+ else
+ as->big_endian = (byte_order == __BIG_ENDIAN);
+
+ /* FIXME! There is no way to specify the ABI.
+ Default to ELFv1 on big-endian and ELFv2 on little-endian. */
+ if (as->big_endian)
+ as->abi = UNW_PPC64_ABI_ELFv1;
+ else
+ as->abi = UNW_PPC64_ABI_ELFv2;
+
+ return as;
+#endif
+}
diff --git a/src/ppc64/Ginit.c b/src/ppc64/Ginit.c
index 52bf715..c338e4b 100644
--- a/src/ppc64/Ginit.c
+++ b/src/ppc64/Ginit.c
@@ -210,6 +210,12 @@ HIDDEN void
ppc64_local_addr_space_init (void)
{
memset (&local_addr_space, 0, sizeof (local_addr_space));
+ local_addr_space.big_endian = (__BYTE_ORDER == __BIG_ENDIAN);
+#if _CALL_ELF == 2
+ local_addr_space.abi = UNW_PPC64_ABI_ELFv2;
+#else
+ local_addr_space.abi = UNW_PPC64_ABI_ELFv1;
+#endif
local_addr_space.caching_policy = UNW_CACHE_GLOBAL;
local_addr_space.acc.find_proc_info = dwarf_find_proc_info;
local_addr_space.acc.put_unwind_info = put_unwind_info;
diff --git a/src/ppc64/Lcreate_addr_space.c b/src/ppc64/Lcreate_addr_space.c
new file mode 100644
index 0000000..0f2dc6b
--- /dev/null
+++ b/src/ppc64/Lcreate_addr_space.c
@@ -0,0 +1,5 @@
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
+#include "Gcreate_addr_space.c"
+#endif
diff --git a/src/ppc64/get_func_addr.c b/src/ppc64/get_func_addr.c
index a9c828d..846ba4a 100644
--- a/src/ppc64/get_func_addr.c
+++ b/src/ppc64/get_func_addr.c
@@ -31,15 +31,21 @@ int
tdep_get_func_addr (unw_addr_space_t as, unw_word_t addr,
unw_word_t *entry_point)
{
- unw_accessors_t *a;
- int ret;
-
- a = unw_get_accessors (as);
- /* Entry-point is stored in the 1st word of the function descriptor.
- In case that changes in the future, we'd have to update the line
- below and read the word at addr + offset: */
- ret = (*a->access_mem) (as, addr, entry_point, 0, NULL);
- if (ret < 0)
- return ret;
+ if (as->abi == UNW_PPC64_ABI_ELFv1)
+ {
+ unw_accessors_t *a;
+ int ret;
+
+ a = unw_get_accessors (as);
+ /* Entry-point is stored in the 1st word of the function descriptor.
+ In case that changes in the future, we'd have to update the line
+ below and read the word at addr + offset: */
+ ret = (*a->access_mem) (as, addr, entry_point, 0, NULL);
+ if (ret < 0)
+ return ret;
+ }
+ else
+ *entry_point = addr;
+
return 0;
}