Here after find the 3 patches produced to enable ppc64el arch with ABIv2 applying on ltrace 0.7.2/0.7.3/0.7.3.4 trees
1st patch is mostly handling endianess and was checked on RH ABIv1 2nd patch enable ABIv2 and was tested on Suze 3rd patch fix Debian/Ubuntu dynamic library load Of course comments are welcome, knowing that the change in the dev. tree would allow to rework and clean the code if needed. -- Thierry Fauck @ linux.vnet.ibm.com
From: Thierry Fauck <[email protected]> Date: Thu, 30 Jan 2014 14:44:10 +0100 First set of changes for ltrace and ppc64le arch for ABI V1 Signed-off-by: Thierry Fauck <[email protected]> {ltrace-0.7.3.orig => ltrace-0.7.3}/configure.ac | 3 ++- {ltrace-0.7.3.orig => ltrace-0.7.3}/sysdeps/linux-gnu/ppc/arch.h | 8 +++++++- {ltrace-0.7.3.orig => ltrace-0.7.3}/sysdeps/linux-gnu/ppc/fetch.c | 2 ++ {ltrace-0.7.3.orig => ltrace-0.7.3}/sysdeps/linux-gnu/ppc/plt.c | 4 ++++ {ltrace-0.7.3.orig => ltrace-0.7.3}/sysdeps/linux-gnu/ppc/trace.c | 8 ++++++++ 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/ltrace-0.7.3.orig/configure.ac b/ltrace-0.7.3/configure.ac index 95027d5..dabdd11 100644 --- a/ltrace-0.7.3.orig/configure.ac +++ b/ltrace-0.7.3/configure.ac @@ -42,7 +42,7 @@ case "${host_cpu}" in arm*|sa110) HOST_CPU="arm" ;; cris*) HOST_CPU="cris" ;; mips*) HOST_CPU="mips" ;; - powerpc|powerpc64) HOST_CPU="ppc" ;; + powerpc|powerpc64|powerpc64le) HOST_CPU="ppc" ;; sun4u|sparc64) HOST_CPU="sparc" ;; s390x) HOST_CPU="s390" ;; i?86|x86_64) HOST_CPU="x86" ;; @@ -168,6 +168,7 @@ if test x"$enable_libunwind" = xyes; then i?86) UNWIND_ARCH="x86" ;; powerpc) UNWIND_ARCH="ppc32" ;; powerpc64) UNWIND_ARCH="ppc64" ;; + powerpc64le) UNWIND_ARCH="ppc64" ;; mips*) UNWIND_ARCH="mips" ;; *) UNWIND_ARCH="${host_cpu}" ;; esac diff --git a/ltrace-0.7.3.orig/sysdeps/linux-gnu/ppc/arch.h b/ltrace-0.7.3/sysdeps/linux-gnu/ppc/arch.h index fb8768a..edabe2e 100644 --- a/ltrace-0.7.3.orig/sysdeps/linux-gnu/ppc/arch.h +++ b/ltrace-0.7.3/sysdeps/linux-gnu/ppc/arch.h @@ -27,6 +27,7 @@ #define BREAKPOINT_VALUE { 0x7f, 0xe0, 0x00, 0x08 } #define BREAKPOINT_LENGTH 4 #define DECR_PC_AFTER_BREAK 0 +#define ARCH_ENDIAN_BIG #define LT_ELFCLASS ELFCLASS32 #define LT_ELF_MACHINE EM_PPC @@ -35,6 +36,12 @@ #define LT_ELFCLASS2 ELFCLASS64 #define LT_ELF_MACHINE2 EM_PPC64 #define ARCH_SUPPORTS_OPD +#ifdef __LITTLE_ENDIAN__ +#undef BREAKPOINT_VALUE +#define BREAKPOINT_VALUE { 0x08, 0x00, 0xe0, 0x7f } +#define ARCH_ENDIAN_LITTLE +#undef ARCH_ENDIAN_BIG +#endif #endif #define ARCH_HAVE_ATOMIC_SINGLESTEP @@ -42,7 +49,6 @@ #define ARCH_HAVE_TRANSLATE_ADDRESS #define ARCH_HAVE_DYNLINK_DONE #define ARCH_HAVE_FETCH_ARG -#define ARCH_ENDIAN_BIG #define ARCH_HAVE_SIZEOF #define ARCH_HAVE_ALIGNOF diff --git a/ltrace-0.7.3.orig/sysdeps/linux-gnu/ppc/fetch.c b/ltrace-0.7.3/sysdeps/linux-gnu/ppc/fetch.c index 9963a1e..3b62fa0 100644 --- a/ltrace-0.7.3.orig/sysdeps/linux-gnu/ppc/fetch.c +++ b/ltrace-0.7.3/sysdeps/linux-gnu/ppc/fetch.c @@ -364,6 +364,7 @@ allocate_argument(struct fetch_context *ctx, struct Process *proc, } /* Small values need post-processing. */ +#ifndef __LITTLE_ENDIAN__ if (sz < width) { switch (info->type) { default: @@ -394,6 +395,7 @@ allocate_argument(struct fetch_context *ctx, struct Process *proc, break; } } +#endif return 0; } diff --git a/ltrace-0.7.3.orig/sysdeps/linux-gnu/ppc/plt.c b/ltrace-0.7.3/sysdeps/linux-gnu/ppc/plt.c index f83f087..9097c27 100644 --- a/ltrace-0.7.3.orig/sysdeps/linux-gnu/ppc/plt.c +++ b/ltrace-0.7.3/sysdeps/linux-gnu/ppc/plt.c @@ -221,7 +221,11 @@ arch_dynlink_done(struct Process *proc) if ((insn1 & BRANCH_MASK) == B_INSN || ((insn2 & BRANCH_MASK) == B_INSN /* XXX double cast */ +#ifdef __LITTLE_ENDIAN__ + && (ppc_branch_dest(libsym->enter_addr + 4, insn1) +#else && (ppc_branch_dest(libsym->enter_addr + 4, insn2) +#endif == (void*)(long)libsym->lib->arch.pltgot_addr))) mark_as_resolved(libsym, libsym->arch.resolved_value); diff --git a/ltrace-0.7.3.orig/sysdeps/linux-gnu/ppc/trace.c b/ltrace-0.7.3/sysdeps/linux-gnu/ppc/trace.c index 4357a1e..df53578 100644 --- a/ltrace-0.7.3.orig/sysdeps/linux-gnu/ppc/trace.c +++ b/ltrace-0.7.3/sysdeps/linux-gnu/ppc/trace.c @@ -64,7 +64,11 @@ syscall_p(Process *proc, int status, int *sysnum) { && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { long pc = (long)get_instruction_pointer(proc); int insn = +#ifdef __LITTLE_ENDIAN__ + (int)ptrace(PTRACE_PEEKTEXT, proc->pid, pc - sizeof(int), +#else (int)ptrace(PTRACE_PEEKTEXT, proc->pid, pc - sizeof(long), +#endif 0); if (insn == SYSCALL_INSN) { @@ -128,7 +132,11 @@ arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, return -1; uint32_t insn; #ifdef __powerpc64__ +#ifdef __LITTLE_ENDIAN__ + insn = (uint32_t) l ; +#else insn = l >> 32; +#endif #else insn = l; #endif
>From 09e03e58e58325c5099188a798c8e119277314cf Mon Sep 17 00:00:00 2001 From: Michel Normand <[email protected]> Date: Thu, 20 Feb 2014 14:44:10 +0100 Subject: [PATCH 2/3] ltrace ppc64le ABIv2 From: Thierry Fauck <[email protected]> Patches to enable ppc64el arch and ABI V2 . Remove use of .opd . Correct parameters location Signed-off-by: Thierry Fauck <[email protected]> Signed-off-by: Guy Menanteau <[email protected]> {ltrace.0.7.3-v1 => ltrace-0.7.3}/ltrace-elf.c | 4 + .../sysdeps/linux-gnu/ppc/arch.h | 3 + .../sysdeps/linux-gnu/ppc/fetch.c | 193 ++++++++++++++++++++- .../sysdeps/linux-gnu/ppc/plt.c | 14 +- .../sysdeps/linux-gnu/ppc/trace.c | 2 + 5 files changed, 212 insertions(+), 4 deletions(-) diff --git a/ltrace.0.7.3-v1/ltrace-elf.c b/ltrace-0.7.3/ltrace-elf.c index c571d2a..4b8b0bc 100644 --- a/ltrace.0.7.3-v1/ltrace-elf.c +++ b/ltrace-0.7.3/ltrace-elf.c @@ -714,6 +714,10 @@ populate_this_symtab(struct Process *proc, const char *filename, continue; } +#if defined(__powerpc64__) && _CALL_ELF == 2 + naddr += PPC64_LOCAL_ENTRY_OFFSET (sym.st_other); +#endif + char *full_name; int own_full_name = 1; if (name_copy == NULL) { diff --git a/ltrace.0.7.3-v1/sysdeps/linux-gnu/ppc/arch.h b/ltrace-0.7.3/sysdeps/linux-gnu/ppc/arch.h index edabe2e..3505cd4 100644 --- a/ltrace.0.7.3-v1/sysdeps/linux-gnu/ppc/arch.h +++ b/ltrace-0.7.3/sysdeps/linux-gnu/ppc/arch.h @@ -41,6 +41,9 @@ #define BREAKPOINT_VALUE { 0x08, 0x00, 0xe0, 0x7f } #define ARCH_ENDIAN_LITTLE #undef ARCH_ENDIAN_BIG +#if _CALL_ELF == 2 +#undef ARCH_SUPPORTS_OPD +#endif #endif #endif diff --git a/ltrace.0.7.3-v1/sysdeps/linux-gnu/ppc/fetch.c b/ltrace-0.7.3/sysdeps/linux-gnu/ppc/fetch.c index 3b62fa0..1e5a584 100644 --- a/ltrace.0.7.3-v1/sysdeps/linux-gnu/ppc/fetch.c +++ b/ltrace-0.7.3/sysdeps/linux-gnu/ppc/fetch.c @@ -65,16 +65,38 @@ struct fetch_context { }; +enum homogeneous_type { + NOINIT = 0, + HETEROGENEOUS, + HOMOGENEOUS, + HOMOGENEOUS_NESTED_FLOAT, +}; + +struct struct_attributes { + struct arg_type_info *info; + enum arg_type type; + size_t nb_elements; + enum homogeneous_type homogeneous; +}; + + static int fetch_context_init(struct Process *proc, struct fetch_context *context) { context->greg = 3; context->freg = 1; +#if _CALL_ELF == 2 +#define STACK_FRAME_OVERHEAD 96 +#else +#define STACK_FRAME_OVERHEAD 112 +#endif + if (proc->e_machine == EM_PPC) context->stack_pointer = proc->stack_pointer + 8; else - context->stack_pointer = proc->stack_pointer + 112; + context->stack_pointer = proc->stack_pointer + + STACK_FRAME_OVERHEAD; /* When ltrace is 64-bit, we might use PTRACE_GETREGS to * obtain 64-bit as well as 32-bit registers. But if we do it @@ -107,6 +129,57 @@ fetch_context_init(struct Process *proc, struct fetch_context *context) return 0; } +#if _CALL_ELF == 2 +static int +get_struct_attribut(struct arg_type_info *info, struct Process *proc, + size_t *size_of, struct struct_attributes *struct_attr) +{ + size_t n = type_aggregate_size(info); + if (n == (size_t)-1) + return ARGTYPE_VOID; + + enum arg_type type = ARGTYPE_VOID; + + while (n-- > 0) { + struct arg_type_info *emt = type_element(info, n); + enum arg_type emt_type = emt->type; + + if (emt_type != ARGTYPE_STRUCT) { + (struct_attr->nb_elements)++; + if (struct_attr->homogeneous == NOINIT) { + struct_attr->info = emt; + struct_attr->type = emt_type; + + if (emt_type == ARGTYPE_DOUBLE || + emt_type == ARGTYPE_FLOAT) + struct_attr->homogeneous = + HOMOGENEOUS_NESTED_FLOAT; + else + struct_attr->homogeneous = HOMOGENEOUS; + } else { + if (struct_attr->type != emt_type) + struct_attr->homogeneous = HETEROGENEOUS; + } + } + + if (emt_type == ARGTYPE_STRUCT) { + emt_type = get_struct_attribut(emt, proc, size_of, + struct_attr); + } else { + size_t size = type_sizeof(proc, emt); + if (size == (size_t)-1) + return -1; + *size_of += size; + } + + type = emt_type; + } + + assert(type != ARGTYPE_STRUCT); + return type; +} +#endif + struct fetch_context * arch_fetch_arg_init(enum tof type, struct Process *proc, struct arg_type_info *ret_info) @@ -118,6 +191,7 @@ arch_fetch_arg_init(enum tof type, struct Process *proc, return NULL; } +#if _CALL_ELF != 2 /* Aggregates or unions of any length, and character strings * of length longer than 8 bytes, will be returned in a * storage buffer allocated by the caller. The caller will @@ -125,6 +199,41 @@ arch_fetch_arg_init(enum tof type, struct Process *proc, * in r3, causing the first explicit argument to be passed in * r4. */ context->ret_struct = ret_info->type == ARGTYPE_STRUCT; +#else + /* There are two changes regarding structure return types: + * * homogeneous float/vector structs are returned + * in (multiple) FP/vector registers, + * instead of via implicit reference. + * * small structs (up to 16 bytes) are return + * in one or two GPRs, instead of via implicit reference. + + * Other structures (larger than 16 bytes, not homogeneous) + * are still returned via implicit reference (i.e. a pointer + * to memory where to return the struct being passed in r3). + * Of course, whether or not an implicit reference pointer + * is present will shift the remaining arguments, + * so you need to get this right for ELFv2 in order + * to get the arguments correct. */ + + struct struct_attributes struct_attr; + struct_attr.homogeneous = NOINIT; + struct_attr.nb_elements = 0; + size_t ret_size = 0; + context->ret_struct = 0; + + if (ret_info->type == ARGTYPE_STRUCT) { + get_struct_attribut(ret_info, proc, &ret_size, &struct_attr); + + if (((struct_attr.homogeneous == HOMOGENEOUS_NESTED_FLOAT) && + (struct_attr.nb_elements > 8)) || + (((struct_attr.homogeneous == HOMOGENEOUS) || + (struct_attr.homogeneous == HETEROGENEOUS)) && + (ret_size > 16))) + + context->ret_struct = 1; + } +#endif + if (context->ret_struct) context->greg++; @@ -246,7 +355,7 @@ allocate_gpr(struct fetch_context *ctx, struct Process *proc, static int allocate_float(struct fetch_context *ctx, struct Process *proc, - struct arg_type_info *info, struct value *valuep) + struct arg_type_info *info, struct value *valuep) { int pool = proc->e_machine == EM_PPC64 ? 13 : 8; if (ctx->freg <= pool) { @@ -275,6 +384,58 @@ allocate_float(struct fetch_context *ctx, struct Process *proc, return allocate_stack_slot(ctx, proc, info, valuep); } +struct arg_type_info * +arch_type_get_fp_equivalent(struct arg_type_info *info, struct Process *proc) +{ +#if _CALL_ELF != 2 + info = type_get_fp_equivalent(info); + return info; +#else + struct struct_attributes struct_attr; + struct_attr.homogeneous = NOINIT; + struct_attr.nb_elements = 0; + size_t ret_size = 0; + + if (info->type == ARGTYPE_STRUCT) { + enum arg_type elm_type = + get_struct_attribut(info, proc, &ret_size, &struct_attr); + + if ((struct_attr.nb_elements <= 8) && + ((struct_attr.homogeneous == HOMOGENEOUS) || + (struct_attr.homogeneous == HOMOGENEOUS_NESTED_FLOAT)) && + ((elm_type == ARGTYPE_FLOAT) || + (elm_type == ARGTYPE_DOUBLE))) + info = struct_attr.info; + (elm_type == ARGTYPE_DOUBLE))) + info = struct_attr.info; + else + return NULL; + } + + switch (info->type) { + case ARGTYPE_CHAR: + case ARGTYPE_SHORT: + case ARGTYPE_INT: + case ARGTYPE_LONG: + case ARGTYPE_UINT: + case ARGTYPE_ULONG: + case ARGTYPE_USHORT: + case ARGTYPE_VOID: + case ARGTYPE_ARRAY: + case ARGTYPE_POINTER: + return NULL; + + case ARGTYPE_FLOAT: + case ARGTYPE_DOUBLE: + return info; + + case ARGTYPE_STRUCT: + abort(); + } + abort(); +#endif +} + static int allocate_argument(struct fetch_context *ctx, struct Process *proc, struct arg_type_info *info, struct value *valuep) @@ -327,10 +488,32 @@ allocate_argument(struct fetch_context *ctx, struct Process *proc, if (sz == (size_t)-1) return -1; size_t slots = (sz + width - 1) / width; /* Round up. */ +#if _CALL_ELF == 2 + if (info->type == ARGTYPE_STRUCT) { + enum arg_type elm_type; + struct struct_attributes struct_attr; + struct_attr.homogeneous = NOINIT; + struct_attr.nb_elements = 0; + size_t dummy = 0; + + elm_type = + get_struct_attribut(info, proc, &dummy, &struct_attr); + if ((struct_attr.nb_elements <= 8) && + ((struct_attr.homogeneous == HOMOGENEOUS) || + (struct_attr.homogeneous == HOMOGENEOUS_NESTED_FLOAT)) && + (elm_type == ARGTYPE_FLOAT)) { + /* adjust sizes for float */ + width = 4; + sz = width * struct_attr.nb_elements; + slots = (size_t)struct_attr.nb_elements; + } + } +#endif + unsigned char *buf = value_reserve(valuep, slots * width); if (buf == NULL) return -1; - struct arg_type_info *long_info = type_get_simple(ARGTYPE_LONG); + struct arg_type_info *long_info = arch_type_get_simple(ARGTYPE_LONG,proc); unsigned char *ptr = buf; while (slots-- > 0) { @@ -359,7 +542,11 @@ allocate_argument(struct fetch_context *ctx, struct Process *proc, /* Bail out if we failed or if we are dealing with * FP-equivalent. Those don't need the adjustments * made below. */ +#if _CALL_ELF != 2 if (rc < 0 || fp_info != NULL) +#else + if (rc < 0 ) +#endif return rc; } diff --git a/ltrace.0.7.3-v1/sysdeps/linux-gnu/ppc/plt.c b/ltrace-0.7.3/sysdeps/linux-gnu/ppc/plt.c index 9097c27..2189cc3 100644 --- a/ltrace.0.7.3-v1/sysdeps/linux-gnu/ppc/plt.c +++ b/ltrace-0.7.3/sysdeps/linux-gnu/ppc/plt.c @@ -113,7 +113,11 @@ */ #define PPC_PLT_STUB_SIZE 16 +#if _CALL_ELF != 2 #define PPC64_PLT_STUB_SIZE 8 //xxx +#else +#define PPC64_PLT_STUB_SIZE 4 //xxx +#endif static inline int host_powerpc64() @@ -253,7 +257,11 @@ arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) * that case we put brakpoints to PLT entries the same * as the PPC32 secure PLT case does. */ assert(lte->arch.plt_stub_vma != 0); +#if _CALL_ELF != 2 return lte->arch.plt_stub_vma + PPC64_PLT_STUB_SIZE * ndx; +#else + return lte->arch.plt_stub_vma - 4 + PPC64_PLT_STUB_SIZE * ndx; +#endif } } @@ -265,6 +273,7 @@ int arch_translate_address_dyn(struct Process *proc, arch_addr_t addr, arch_addr_t *ret) { +#if _CALL_ELF != 2 if (proc->e_machine == EM_PPC64) { uint64_t value; if (read_target_8(proc, addr, &value) < 0) { @@ -278,6 +287,7 @@ arch_translate_address_dyn(struct Process *proc, *ret = (arch_addr_t)(uintptr_t)value; return 0; } +#endif *ret = addr; return 0; @@ -287,7 +297,8 @@ int arch_translate_address(struct ltelf *lte, arch_addr_t addr, arch_addr_t *ret) { - if (lte->ehdr.e_machine == EM_PPC64) { + if (lte->ehdr.e_machine == EM_PPC64 + && (lte->ehdr.e_flags & 3) != 2 ) { /* XXX The double cast should be removed when * arch_addr_t becomes integral type. */ GElf_Xword offset @@ -433,6 +444,7 @@ int arch_elf_init(struct ltelf *lte, struct library *lib) { if (lte->ehdr.e_machine == EM_PPC64 + && (lte->ehdr.e_flags & 3) != 2 && load_opd_data(lte, lib) < 0) return -1; diff --git a/ltrace.0.7.3-v1/sysdeps/linux-gnu/ppc/trace.c b/ltrace-0.7.3/sysdeps/linux-gnu/ppc/trace.c index df53578..baf12ad 100644 --- a/ltrace.0.7.3-v1/sysdeps/linux-gnu/ppc/trace.c +++ b/ltrace-0.7.3/sysdeps/linux-gnu/ppc/trace.c @@ -126,7 +126,9 @@ arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, int insn_count; arch_addr_t addr = ip; for (insn_count = 0; ; ++insn_count) { +#if (!defined(__LITTLE_ENDIAN__)) || (_CALL_ELF == 2) addr += 4; +#endif unsigned long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); if (l == (unsigned long)-1 && errno) return -1;
From: Thierry Fauck <[email protected]> Date: Thu, 20 Feb 2014 14:44:10 +0100 Subject: [PATCH 2/2] ltrace ppc64le ABIv2 . For Ubuntu/Debian the ld64.so and libc.so may not have stub initialized when the libraries are dynamically loaded .../sysdeps/linux-gnu/ppc/plt.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/ltrace-0.7.3-v2/sysdeps/linux-gnu/ppc/plt.c b/ltrace-0.7.3/sysdeps/linux-gnu/ppc/plt.c index 2189cc3..8e03283 100644 --- a/ltrace-0.7.3-v2/sysdeps/linux-gnu/ppc/plt.c +++ b/ltrace-0.7.3/sysdeps/linux-gnu/ppc/plt.c @@ -585,7 +585,7 @@ read_plt_slot_value(struct Process *proc, GElf_Addr addr, GElf_Addr *valp) uint64_t l; /* XXX double cast. */ if (read_target_8(proc, (arch_addr_t)(uintptr_t)addr, &l) < 0) { - fprintf(stderr, "ptrace .plt slot value @%#" PRIx64": %s\n", + debug(DEBUG_EVENT, "ptrace .plt slot value @%#" PRIx64": %s", addr, strerror(errno)); return -1; } @@ -668,8 +668,18 @@ arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, || plt_slot_addr < lte->plt_addr + lte->plt_size); GElf_Addr plt_slot_value; - if (read_plt_slot_value(proc, plt_slot_addr, &plt_slot_value) < 0) + if (read_plt_slot_value(proc, plt_slot_addr, &plt_slot_value) < 0) { +#if _CALL_ELF != 2 return plt_fail; +#else + /* With ABIv2, as we don't have .odp section, + depending on how libc.so and ld.so are + loaded stub area may not be initialized/dynamically + loaded requiring the common process to do it + usually making symbols latent */ + return plt_default; +#endif + } char *name = strdup(a_name); struct library_symbol *libsym = malloc(sizeof(*libsym)); @@ -862,9 +872,13 @@ ppc_plt_bp_continue(struct breakpoint *bp, struct Process *proc) (struct process_stopping_handler *); case PPC_DEFAULT: +#if _CALL_ELF != 2 assert(proc->e_machine == EM_PPC); assert(bp->libsym != NULL); assert(bp->libsym->lib->arch.bss_plt_prelinked == 0); +#else + /* ABIv2 uses plt and may set the flag */ +#endif /* Fall through. */ case PPC_PLT_UNRESOLVED:
_______________________________________________ Ltrace-devel mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/ltrace-devel
