From da0b1a146f5e6d3273a2719e9b11f3f91d077eb1 Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Tue, 17 Dec 2013 15:01:34 +0100 Subject: [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 --- 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/Gis_signal_frame.c | 18 +++++-- src/{ppc => ppc32}/Gcreate_addr_space.c | 2 +- src/{ppc => ppc32}/Lcreate_addr_space.c | 0 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 | 24 +++++---- 11 files changed, 130 insertions(+), 17 deletions(-) rename src/{ppc => ppc32}/Gcreate_addr_space.c (97%) rename src/{ppc => ppc32}/Lcreate_addr_space.c (100%) 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 66420b33..07c72a9d 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 e2676a82..7603d189 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 6d728586..6024455a 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 cf137c08..84f7e421 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/Gis_signal_frame.c b/src/ppc/Gis_signal_frame.c index 41544290..b5e11e32 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/Gcreate_addr_space.c b/src/ppc32/Gcreate_addr_space.c similarity index 97% rename from src/ppc/Gcreate_addr_space.c rename to src/ppc32/Gcreate_addr_space.c index 09bde565..f5055ecb 100644 --- a/src/ppc/Gcreate_addr_space.c +++ b/src/ppc32/Gcreate_addr_space.c @@ -38,7 +38,7 @@ unw_create_addr_space (unw_accessors_t *a, int byte_order) unw_addr_space_t as; /* - * Linux ppc64 supports only big-endian. + * We support only big-endian on Linux ppc32. */ if (byte_order != 0 && byte_order != __BIG_ENDIAN) return NULL; diff --git a/src/ppc/Lcreate_addr_space.c b/src/ppc32/Lcreate_addr_space.c similarity index 100% rename from src/ppc/Lcreate_addr_space.c rename to src/ppc32/Lcreate_addr_space.c diff --git a/src/ppc64/Gcreate_addr_space.c b/src/ppc64/Gcreate_addr_space.c new file mode 100644 index 00000000..686653a9 --- /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 + Jose Flavio Aguilar Paulino + +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 + +#include + +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 52bf7153..c338e4b4 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 00000000..0f2dc6be --- /dev/null +++ b/src/ppc64/Lcreate_addr_space.c @@ -0,0 +1,5 @@ +#define UNW_LOCAL_ONLY +#include +#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 a9c828dc..846ba4af 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; + 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; - 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; return 0; }