From: Thierry Fauck <thie...@linux.vnet.ibm.com> Signed-off-by: Thierry Fauck <thie...@linux.vnet.ibm.com> --- configure.ac | 3 +- ltrace-elf.c | 2 +- ltrace-elf.h | 1 + sysdeps/linux-gnu/ppc/arch.h | 27 ++- sysdeps/linux-gnu/ppc/fetch.c | 288 ++++++++++++++++++++++++++++--- sysdeps/linux-gnu/ppc/plt.c | 90 +++++++++- sysdeps/linux-gnu/ppc/trace.c | 10 ++ testsuite/ltrace.main/parameters-str.exp | 116 +++++++++++++ testsuite/ltrace.main/system_calls.exp | 2 +- 9 files changed, 502 insertions(+), 37 deletions(-) create mode 100644 testsuite/ltrace.main/parameters-str.exp
diff --git a/configure.ac b/configure.ac index 0e9a124..778fbc3 100644 --- a/configure.ac +++ b/configure.ac @@ -43,7 +43,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" ;; @@ -214,6 +214,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-elf.c b/ltrace-elf.c index 8997518..f638342 100644 --- a/ltrace-elf.c +++ b/ltrace-elf.c @@ -859,7 +859,7 @@ populate_plt(struct process *proc, const char *filename, return 0; } -static void +void delete_symbol_chain(struct library_symbol *libsym) { while (libsym != NULL) { diff --git a/ltrace-elf.h b/ltrace-elf.h index db4ffe9..4a824c4 100644 --- a/ltrace-elf.h +++ b/ltrace-elf.h @@ -166,6 +166,7 @@ int elf_read_next_uleb128(Elf_Data *data, GElf_Xword *offset, uint64_t *retp); /* Return whether there's AMOUNT more bytes after OFFSET in DATA. */ int elf_can_read_next(Elf_Data *data, GElf_Xword offset, GElf_Xword amount); +void delete_symbol_chain(struct library_symbol *); #if __WORDSIZE == 32 #define PRI_ELF_ADDR PRIx32 #define GELF_ADDR_CAST(x) (void *)(uint32_t)(x) diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h index bf9b5dc..ad589e0 100644 --- a/sysdeps/linux-gnu/ppc/arch.h +++ b/sysdeps/linux-gnu/ppc/arch.h @@ -23,8 +23,8 @@ #define LTRACE_PPC_ARCH_H #include <gelf.h> +#include <stdbool.h> -#define BREAKPOINT_VALUE { 0x7f, 0xe0, 0x00, 0x08 } #define BREAKPOINT_LENGTH 4 #define DECR_PC_AFTER_BREAK 0 @@ -34,16 +34,34 @@ #ifdef __powerpc64__ // Says 'ltrace' is 64 bits, says nothing about target. #define LT_ELFCLASS2 ELFCLASS64 #define LT_ELF_MACHINE2 EM_PPC64 -#define ARCH_SUPPORTS_OPD + +#ifdef __LITTLE_ENDIAN__ +# define BREAKPOINT_VALUE { 0x08, 0x00, 0xe0, 0x7f } +# define ARCH_ENDIAN_LITTLE +#else +# define BREAKPOINT_VALUE { 0x7f, 0xe0, 0x00, 0x08 } +# define ARCH_SUPPORTS_OPD +# define ARCH_ENDIAN_BIG #endif +#if _CALL_ELF != 2 +# define ARCH_SUPPORTS_OPD +# define STACK_FRAME_OVERHEAD 112 +#else /* _CALL_ELF == 2 ABIv2 */ +# define STACK_FRAME_OVERHEAD 32 +#endif /* CALL_ELF */ + +#else +#define BREAKPOINT_VALUE { 0x7f, 0xe0, 0x00, 0x08 } +#define ARCH_ENDIAN_BIG +#endif /* __powerpc64__ */ + #define ARCH_HAVE_SW_SINGLESTEP #define ARCH_HAVE_ADD_PLT_ENTRY #define ARCH_HAVE_ADD_FUNC_ENTRY #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 @@ -56,7 +74,8 @@ struct arch_ltelf_data { Elf_Data *opd_data; GElf_Addr opd_base; GElf_Xword opd_size; - int secure_plt; + bool secure_plt : 1; + bool elfv2_abi : 1; Elf_Data *reladyn; size_t reladyn_count; diff --git a/sysdeps/linux-gnu/ppc/fetch.c b/sysdeps/linux-gnu/ppc/fetch.c index ed38336..b42cd95 100644 --- a/sysdeps/linux-gnu/ppc/fetch.c +++ b/sysdeps/linux-gnu/ppc/fetch.c @@ -30,9 +30,11 @@ #include "ptrace.h" #include "proc.h" #include "value.h" +#include "ltrace-elf.h" static int allocate_gpr(struct fetch_context *ctx, struct process *proc, - struct arg_type_info *info, struct value *valuep); + struct arg_type_info *info, struct value *valuep, + size_t off, bool is_hfa_type); /* Floating point registers have the same width on 32-bit as well as * 64-bit PPC, but <ucontext.h> presents a different API depending on @@ -56,13 +58,18 @@ struct fetch_context { int greg; int freg; int ret_struct; + int ret_elmts; + int ret_size; + enum arg_type ret_type; union { gregs32_t r32; gregs64_t r64; } regs; struct fpregs_t fpregs; - + int vgreg; + int struct_hfa_size; + int struct_hfa_count; }; static int @@ -74,7 +81,8 @@ fetch_context_init(struct process *proc, struct fetch_context *context) 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 +115,38 @@ fetch_context_init(struct process *proc, struct fetch_context *context) return 0; } +static int +get_return_info(struct arg_type_info *info, struct process *proc, + struct fetch_context *context) +{ + size_t n_entries = type_aggregate_size(info); + if (n_entries == (size_t)-1) + return -1; + + enum arg_type type = ARGTYPE_VOID; + + while (n_entries-- > 0) { + struct arg_type_info *emt = type_element(info, n_entries); + enum arg_type emt_type = emt->type; + + if (emt_type == ARGTYPE_STRUCT) { + emt_type = get_return_info(emt, proc, context); + } else { + (context->ret_elmts)++; + context->ret_size += type_sizeof(proc, emt); + if (context->ret_type == ARGTYPE_VOID) + context->ret_type = emt_type; + else if (emt_type == ARGTYPE_ARRAY) + context->ret_type = ARGTYPE_ARRAY; + else if (emt_type != context->ret_type + && context->ret_type != ARGTYPE_ARRAY) + context->ret_type += + emt->type * context->ret_elmts * 20 ; + } + } + return type; +} + struct fetch_context * arch_fetch_arg_init(enum tof type, struct process *proc, struct arg_type_info *ret_info) @@ -118,15 +158,59 @@ arch_fetch_arg_init(enum tof type, struct process *proc, return NULL; } + context->vgreg = context->greg; + context->struct_hfa_size = 0; + context->struct_hfa_count = 0; + context->ret_size = 0; + context->ret_elmts = 0; + context->ret_type = ARGTYPE_VOID; + /* 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 * pass the address of this buffer as a hidden first argument * in r3, causing the first explicit argument to be passed in - * r4. */ + * r4. Otherwise first argument located in register is in r3. + * With ABIv2, more case apply to this statements depending + * on the struct return value. + * if the return type is a HFA, it is returned + * in FP/VPF registers; otherwise, if <= 16 bytes, + * it is returned in GPRs, otherwise it is returned + * indirect via pointer. *only* in that latter case, + * all other arguments are shifted by one + * R3 will then points to the buffer for return parameters + * for following cases, R4 pointing to first explicit argument: + * not short string (case character array of <= 8 bytes) + * hfa > 8 bytes in total + */ context->ret_struct = ret_info->type == ARGTYPE_STRUCT; - if (context->ret_struct) + + if (context->ret_struct) { +#if _CALL_ELF == 2 + get_return_info(ret_info, proc, context); + + struct arg_type_info *hfa_info; + size_t hfa_size; + hfa_info = type_get_hfa_type(ret_info, &hfa_size); + + if ((hfa_info != NULL && hfa_size > 8) + || (hfa_info == NULL + && (context->ret_size <= 32 + && (context->ret_type == ARGTYPE_LONG + || (context->ret_size/type_struct_size(ret_info) + == 4 + && context->ret_type != ARGTYPE_INT)))) + || (context->ret_size > 16 + && context->ret_type == ARGTYPE_ARRAY)) { + context->ret_struct = 1; + context->greg++; + context->stack_pointer += 8; + } else + context->ret_struct = 0; +#else context->greg++; +#endif + } return context; } @@ -144,7 +228,8 @@ arch_fetch_arg_clone(struct process *proc, static int allocate_stack_slot(struct fetch_context *ctx, struct process *proc, - struct arg_type_info *info, struct value *valuep) + struct arg_type_info *info, struct value *valuep, + bool is_hfa_type) { size_t sz = type_sizeof(proc, info); if (sz == (size_t)-1) @@ -154,7 +239,14 @@ allocate_stack_slot(struct fetch_context *ctx, struct process *proc, size_t off = 0; if (proc->e_machine == EM_PPC && a < 4) a = 4; +#if _CALL_ELF == 2 + else if (proc->e_machine == EM_PPC64 && sz == 4 && is_hfa_type) + a = 4; + else + a = 8; +#else else if (proc->e_machine == EM_PPC64 && a < 8) +#endif a = 8; /* XXX Remove the two double casts when arch_addr_t @@ -164,7 +256,7 @@ allocate_stack_slot(struct fetch_context *ctx, struct process *proc, if (valuep != NULL) value_in_inferior(valuep, ctx->stack_pointer + off); - ctx->stack_pointer += sz; + ctx->stack_pointer += a; return 0; } @@ -216,19 +308,35 @@ align_small_int(unsigned char *buf, size_t w, size_t sz) static int allocate_gpr(struct fetch_context *ctx, struct process *proc, - struct arg_type_info *info, struct value *valuep) + struct arg_type_info *info, struct value *valuep, + size_t off, bool is_hfa_type) { if (ctx->greg > 10) - return allocate_stack_slot(ctx, proc, info, valuep); + return allocate_stack_slot(ctx, proc, info, valuep, + is_hfa_type); - int reg_num = ctx->greg++; - if (valuep == NULL) - return 0; + int reg_num = ctx->greg; size_t sz = type_sizeof(proc, info); if (sz == (size_t)-1) return -1; assert(sz == 1 || sz == 2 || sz == 4 || sz == 8); +#if _CALL_ELF == 2 + /* Consume the stack slot corresponding to this arg. */ + if ((sz + off) >= 8) + ctx->greg++; + + if (is_hfa_type) + ctx->stack_pointer += sz; + else + ctx->stack_pointer += 8; +#else + ctx->greg++; +#endif + + if (valuep == NULL) + return 0; + if (value_reserve(valuep, sz) == NULL) return -1; @@ -240,13 +348,14 @@ allocate_gpr(struct fetch_context *ctx, struct process *proc, u.i64 = read_gpr(ctx, proc, reg_num); if (proc->e_machine == EM_PPC) align_small_int(u.buf, 8, sz); - memcpy(value_get_raw_data(valuep), u.buf, sz); + memcpy(value_get_raw_data(valuep), u.buf + off, sz); return 0; } 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, + size_t off, bool is_hfa_type) { int pool = proc->e_machine == EM_PPC64 ? 13 : 8; if (ctx->freg <= pool) { @@ -257,8 +366,12 @@ allocate_float(struct fetch_context *ctx, struct process *proc, } u = { .d = ctx->fpregs.fpregs[ctx->freg] }; ctx->freg++; + + if (!is_hfa_type) + ctx->vgreg++; + if (proc->e_machine == EM_PPC64) - allocate_gpr(ctx, proc, info, NULL); + allocate_gpr(ctx, proc, info, NULL, off, is_hfa_type); size_t sz = sizeof(double); if (info->type == ARGTYPE_FLOAT) { @@ -272,7 +385,124 @@ allocate_float(struct fetch_context *ctx, struct process *proc, memcpy(value_get_raw_data(valuep), u.buf, sz); return 0; } - return allocate_stack_slot(ctx, proc, info, valuep); + return allocate_stack_slot(ctx, proc, info, valuep, is_hfa_type); +} + +static int +allocate_hfa(struct fetch_context *ctx, struct process *proc, + struct arg_type_info *info, struct value *valuep, + enum arg_type hfa_type, size_t hfa_count) +{ + size_t sz = type_sizeof(proc, info); + if (sz == (size_t)-1) + return -1; + + ctx->struct_hfa_size += sz; + + /* There are two changes regarding structure return types: + * * HFA 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 heterogeneous) + * 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. + * If an actual parameter is known to correspond to an HFA + * formal parameter, each element is passed in the next + * available floating-point argument register starting at fp1 + * until the fp13. The remaining elements of the aggregate are + * passed on the stack. */ + size_t slot_off = 0; + + unsigned char *buf = value_reserve(valuep, sz); + if (buf == NULL) + return -1; + + struct arg_type_info *hfa_info = type_get_simple(hfa_type); + size_t hfa_sz = type_sizeof(proc, hfa_info); + + if (hfa_count > 8) + ctx->struct_hfa_count += hfa_count; + + while (hfa_count > 0 && ctx->freg <= 13) { + int rc; + struct value tmp; + + value_init(&tmp, proc, NULL, hfa_info, 0); + + if (((hfa_type == ARGTYPE_FLOAT + || hfa_type == ARGTYPE_DOUBLE) + && hfa_count <= 8)) + rc = allocate_float(ctx, proc, hfa_info, &tmp, + slot_off, true); + else + rc = allocate_gpr(ctx, proc, hfa_info, &tmp, + slot_off, true); + + memcpy(buf, value_get_data(&tmp, NULL), hfa_sz); + + slot_off += hfa_sz; + buf += hfa_sz; + hfa_count--; + if (slot_off == 8) { + slot_off = 0; + ctx->vgreg++; + } + + value_destroy(&tmp); + if (rc < 0) + return -1; + } + if (hfa_count == 0) + return 0; + + /* if no remaining FP, GPR corresponding to slot is used + * Mostly it is in part of r10. */ + if (ctx->struct_hfa_size <= 64 && ctx->vgreg == 10) { + while (ctx->vgreg <= 10) { + struct value tmp; + value_init(&tmp, proc, NULL, hfa_info, 0); + union { + uint64_t i64; + unsigned char buf[0]; + } u; + + u.i64 = read_gpr(ctx, proc, ctx->vgreg); + + memcpy(buf, u.buf + slot_off, hfa_sz); + slot_off += hfa_sz; + buf += hfa_sz; + hfa_count--; + ctx->stack_pointer += hfa_sz; + if (slot_off >= 8) { + slot_off = 0; + ctx->vgreg++; + } + value_destroy(&tmp); + } + } + + if (hfa_count == 0) + return 0; + + /* Remaining values are on stack */ + while (hfa_count) { + struct value tmp; + value_init(&tmp, proc, NULL, hfa_info, 0); + + value_in_inferior(&tmp, ctx->stack_pointer); + memcpy(buf, value_get_data(&tmp, NULL), hfa_sz); + ctx->stack_pointer += hfa_sz; + buf += hfa_sz; + hfa_count--; + } + return 0; } static int @@ -287,13 +517,23 @@ allocate_argument(struct fetch_context *ctx, struct process *proc, case ARGTYPE_FLOAT: case ARGTYPE_DOUBLE: - return allocate_float(ctx, proc, info, valuep); + return allocate_float(ctx, proc, info, valuep, + 8 - type_sizeof(proc,info), false); case ARGTYPE_STRUCT: if (proc->e_machine == EM_PPC) { if (value_pass_by_reference(valuep) < 0) return -1; } else { +#if _CALL_ELF == 2 + struct arg_type_info *hfa_info; + size_t hfa_size; + hfa_info = type_get_hfa_type(info, &hfa_size); + if (hfa_info != NULL) { + return allocate_hfa(ctx, proc, info, valuep, + hfa_info->type, hfa_size); + } +#endif /* PPC64: Fixed size aggregates and unions passed by * value are mapped to as many doublewords of the * parameter save area as the value uses in memory. @@ -326,6 +566,7 @@ allocate_argument(struct fetch_context *ctx, struct process *proc, size_t sz = type_sizeof(proc, valuep->type); if (sz == (size_t)-1) return -1; + size_t slots = (sz + width - 1) / width; /* Round up. */ unsigned char *buf = value_reserve(valuep, slots * width); if (buf == NULL) @@ -346,9 +587,11 @@ allocate_argument(struct fetch_context *ctx, struct process *proc, struct arg_type_info *fp_info = type_get_fp_equivalent(valuep->type); if (fp_info != NULL) - rc = allocate_float(ctx, proc, fp_info, &val); + rc = allocate_float(ctx, proc, fp_info, &val, + 8-type_sizeof(proc,info), false); else - rc = allocate_gpr(ctx, proc, long_info, &val); + rc = allocate_gpr(ctx, proc, long_info, &val, + 0, false); if (rc >= 0) { memcpy(ptr, value_get_data(&val, NULL), width); @@ -363,6 +606,7 @@ allocate_argument(struct fetch_context *ctx, struct process *proc, return rc; } +#ifndef __LITTLE_ENDIAN__ /* Small values need post-processing. */ if (sz < width) { switch (info->type) { @@ -394,6 +638,7 @@ allocate_argument(struct fetch_context *ctx, struct process *proc, break; } } +#endif return 0; } @@ -411,6 +656,9 @@ arch_fetch_retval(struct fetch_context *ctx, enum tof type, struct process *proc, struct arg_type_info *info, struct value *valuep) { + if (fetch_context_init(proc, ctx) < 0) + return -1; + if (ctx->ret_struct) { assert(info->type == ARGTYPE_STRUCT); @@ -424,8 +672,6 @@ arch_fetch_retval(struct fetch_context *ctx, enum tof type, return 0; } - if (fetch_context_init(proc, ctx) < 0) - return -1; return allocate_argument(ctx, proc, info, valuep); } diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c index 332daa8..a16e182 100644 --- a/sysdeps/linux-gnu/ppc/plt.c +++ b/sysdeps/linux-gnu/ppc/plt.c @@ -136,7 +136,11 @@ */ #define PPC_PLT_STUB_SIZE 16 -#define PPC64_PLT_STUB_SIZE 8 //xxx +#if _CALL_ELF != 2 +#define PPC64_PLT_STUB_SIZE 8 +#else +#define PPC64_PLT_STUB_SIZE 4 +#endif static inline int host_powerpc64() @@ -186,8 +190,13 @@ ppc32_delayed_symbol(struct library_symbol *libsym) 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) + == (arch_addr_t) (long) libsym->lib->arch.pltgot_addr))) +#else && (ppc_branch_dest(libsym->enter_addr + 4, insn2) == (arch_addr_t) (long) libsym->lib->arch.pltgot_addr))) +#endif { mark_as_resolved(libsym, libsym->arch.resolved_value); } @@ -227,9 +236,15 @@ reloc_is_irelative(int machine, GElf_Rela *rela) { bool irelative = false; if (machine == EM_PPC64) { +#ifdef __LITTLE_ENDIAN__ +#ifdef R_PPC64_IRELATIVE + irelative = GELF_R_TYPE(rela->r_info) == R_PPC64_IRELATIVE; +#endif +#else #ifdef R_PPC64_JMP_IREL irelative = GELF_R_TYPE(rela->r_info) == R_PPC64_JMP_IREL; #endif +#endif } else { assert(machine == EM_PPC); #ifdef R_PPC_IRELATIVE @@ -285,6 +300,7 @@ arch_translate_address_dyn(struct process *proc, arch_addr_t addr, arch_addr_t *ret) { if (proc->e_machine == EM_PPC64) { +#if _CALL_ELF != 2 uint64_t value; if (proc_read_64(proc, addr, &value) < 0) { fprintf(stderr, @@ -296,6 +312,7 @@ arch_translate_address_dyn(struct process *proc, * arch_addr_t becomes integral type. */ *ret = (arch_addr_t)(uintptr_t)value; return 0; +#endif } *ret = addr; @@ -306,7 +323,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->arch.elfv2_abi) { /* XXX The double cast should be removed when * arch_addr_t becomes integral type. */ GElf_Xword offset @@ -368,7 +386,7 @@ get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot, Elf_Data *plt_data) if (ppcgot_sec != NULL) { Elf_Data *data = elf_loaddata(ppcgot_sec, &ppcgot_shdr); - if (data == NULL || data->d_size < 8 ) { + if (data == NULL || data->d_size < 8) { fprintf(stderr, "couldn't read GOT data\n"); } else { // where PPCGOT begins in .got @@ -430,7 +448,12 @@ reloc_copy_if_irelative(GElf_Rela *rela, void *data) int arch_elf_init(struct ltelf *lte, struct library *lib) { + + /* Check for ABIv2 in ELF header processor specific flag. */ + lte->arch.elfv2_abi = ((lte->ehdr.e_flags & EF_PPC64_ABI) == 2); + if (lte->ehdr.e_machine == EM_PPC64 + && !lte->arch.elfv2_abi && load_opd_data(lte, lib) < 0) return -1; @@ -599,7 +622,7 @@ read_plt_slot_value(struct process *proc, GElf_Addr addr, GElf_Addr *valp) uint64_t l; /* XXX double cast. */ if (proc_read_64(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; } @@ -616,7 +639,7 @@ unresolve_plt_slot(struct process *proc, GElf_Addr addr, GElf_Addr value) * pointers intact. Hence the only adjustment that we need to * do is to IP. */ if (ptrace(PTRACE_POKETEXT, proc->pid, addr, value) < 0) { - fprintf(stderr, "failed to unresolve .plt slot: %s\n", + debug(DEBUG_EVENT, "failed to unresolve .plt slot: %s", strerror(errno)); return -1; } @@ -629,9 +652,45 @@ arch_elf_add_func_entry(struct process *proc, struct ltelf *lte, arch_addr_t addr, const char *name, struct library_symbol **ret) { - if (lte->ehdr.e_machine != EM_PPC || lte->ehdr.e_type == ET_DYN) + /* With ABIv2 st_other field contains an offset. */ + if (lte->arch.elfv2_abi) + addr += PPC64_LOCAL_ENTRY_OFFSET(sym->st_other); + + int st_info = GELF_ST_TYPE(sym->st_info); + + if ((lte->ehdr.e_machine != EM_PPC && sym->st_other == 0) + || lte->ehdr.e_type == ET_DYN + || (st_info == STT_FUNC && sym->st_other == 0)) return PLT_DEFAULT; + if (st_info == STT_FUNC) { + /* Put the default symbol to the chain. + * The addr has already been updated with + * symbol offset */ + char *full_name = strdup(name); + if (full_name == NULL) { + fprintf(stderr, "couldn't copy name of %s: %s\n", + name, strerror(errno)); + return PLT_FAIL; + } + + struct library_symbol *libsym = malloc(sizeof *libsym); + if (libsym == NULL + || library_symbol_init(libsym, addr, full_name, 1, + LS_TOPLT_NONE) < 0) { + free(full_name); + free(libsym); + delete_symbol_chain(libsym); + libsym = NULL; + fprintf(stderr, "Couldn't add symbol %s" + "for tracing.\n", name); + return PLT_FAIL; + } + libsym->next = *ret; + *ret = libsym; + return PLT_OK; + } + bool ifunc = false; #ifdef STT_GNU_IFUNC ifunc = GELF_ST_TYPE(sym->st_info) == STT_GNU_IFUNC; @@ -761,9 +820,15 @@ arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, assert(plt_slot_addr >= lte->plt_addr || plt_slot_addr < lte->plt_addr + lte->plt_size); + /* Should avoid to do read if dynamic linker hasn't run yet + * or allow -1 a valid return code. */ GElf_Addr plt_slot_value; - if (read_plt_slot_value(proc, plt_slot_addr, &plt_slot_value) < 0) - goto fail; + if (read_plt_slot_value(proc, plt_slot_addr, &plt_slot_value) < 0) { + if (!lte->arch.elfv2_abi) + goto fail; + else + return PPC_PLT_UNRESOLVED; + } struct library_symbol *libsym = malloc(sizeof(*libsym)); if (libsym == NULL) { @@ -996,7 +1061,10 @@ ppc_plt_bp_continue(struct breakpoint *bp, struct process *proc) continue_after_breakpoint(proc, bp); return; } - +#if _CALL_ELF == 2 + continue_after_breakpoint(proc,bp); + return; +#endif jump_to_entry_point(proc, bp); continue_process(proc->pid); return; @@ -1123,7 +1191,11 @@ arch_library_symbol_init(struct library_symbol *libsym) /* We set type explicitly in the code above, where we have the * necessary context. This is for calls from ltrace-elf.c and * such. */ +#if _CALL_ELF == 2 + libsym->arch.type = PPC_PLT_UNRESOLVED; +#else libsym->arch.type = PPC_DEFAULT; +#endif return 0; } diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c index ee9a6b5..5aab538 100644 --- a/sysdeps/linux-gnu/ppc/trace.c +++ b/sysdeps/linux-gnu/ppc/trace.c @@ -65,9 +65,15 @@ syscall_p(struct process *proc, int status, int *sysnum) if (WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { long pc = (long)get_instruction_pointer(proc); +#ifndef __LITTLE_ENDIAN__ int insn = (int)ptrace(PTRACE_PEEKTEXT, proc->pid, pc - sizeof(long), 0); +#else + int insn = + (int)ptrace(PTRACE_PEEKTEXT, proc->pid, pc - sizeof(int), + 0); +#endif if (insn == SYSCALL_INSN) { *sysnum = @@ -127,7 +133,11 @@ arch_sw_singlestep(struct process *proc, struct breakpoint *sbp, return SWS_FAIL; uint32_t insn; #ifdef __powerpc64__ +# ifdef __LITTLE_ENDIAN__ + insn = (uint32_t) l ; +# else insn = l >> 32; +# endif #else insn = l; #endif diff --git a/testsuite/ltrace.main/parameters-str.exp b/testsuite/ltrace.main/parameters-str.exp new file mode 100644 index 0000000..8f8bf49 --- /dev/null +++ b/testsuite/ltrace.main/parameters-str.exp @@ -0,0 +1,116 @@ +# This file is part of ltrace. +# Copyright (C) 2014 Petr Machata, Red Hat Inc. +# +# This program 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 2 of the +# License, or (at your option) any later version. +# +# This program 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, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +# Test for a number of ways that HFA can be passed. + +ltraceMatch [ltraceLibTest { + string[7] func_str7(string[7]); + string[17] func_str17(string[17]); + + struct(string(array(char,7))) struct_func_str7(struct(string[7])); + struct(string(array(char,8))) struct_func_str8(struct(string[8])); + struct(string(array(char,9))) struct_func_str9(struct(string[9])); + struct(string(array(char,16))) struct_func_str16(struct(string[16])); + struct(string(array(char,17))) struct_func_str17(struct(string[17])); + + struct(struct(string(array(char,16))),int) struct_func_str16_i(string[16]); + + struct(struct(string(array(char,17))),int) struct_func_str17_i(string[17]); + struct(int,struct(string(array(char,17)))) struct_func_str17_k(string[17]); + +} { + char *func_str7(char str[7] ); + char *func_str7_b(char str[17] ); + + struct struct_str { char *str ; }; + struct struct_size7 { char str[7] ; }; + struct struct_size8 { char str[8] ; }; + struct struct_size9 { char str[9] ; }; + struct struct_size16 { char str[16] ; }; + struct struct_size17 { char str[17] ; }; + struct struct_size16_i { char str[16] ; int i;}; + struct struct_size17_i { char str[17] ; int i;}; + struct struct_size17_k { int i; char str[17];}; + + struct struct_size7 struct_func_str7(struct struct_str a); + struct struct_size8 struct_func_str8(struct struct_str a); + struct struct_size9 struct_func_str9(struct struct_str a); + struct struct_size16 struct_func_str16(struct struct_str a); + struct struct_size17 struct_func_str17(struct struct_str a); + struct struct_size16_i struct_func_str16_i(char a[16]); + struct struct_size17_i struct_func_str17_i(char a[17]); + struct struct_size17_k struct_func_str17_k(char a[17]); + +} { + char *func_str7(char str[7]) + { return "ABCDEFG" ; } + char *func_str17(char str[17]) + { return "ABCDEFGIJKabcdefg" ; } + + struct struct_size7 struct_func_str7(struct struct_str a) + { return (struct struct_size7) { "ABCDEFG"} ; } + + struct struct_size8 struct_func_str8(struct struct_str a) + { return (struct struct_size8) { "ABCDEFG8"} ; } + + struct struct_size9 struct_func_str9(struct struct_str a) + { return (struct struct_size9) {"ABCDEFG12"} ; } + + struct struct_size16 struct_func_str16(struct struct_str a) + { return (struct struct_size16) {"a2b4c6d8eafc1e30"} ; } + + struct struct_size17 struct_func_str17(struct struct_str a) + { return (struct struct_size17) {"a2b4c6d8eafc1e305"} ; } + + struct struct_size16_i struct_func_str16_i(char a[16] ) + { return (struct struct_size16_i) {"a2b4c6d8eafc1e30",256} ; } + + struct struct_size17_i struct_func_str17_i(char a[17] ) + { return (struct struct_size17_i) {"a2b4c6d8eafc1e305",512} ; } + + struct struct_size17_k struct_func_str17_k(char a[17] ) + { return (struct struct_size17_k) {768,"a2b4c6d8eafc1e305"} ; } + +} { + func_str7("1234567"); + func_str17("123456789abcdef01"); + + struct_func_str7((struct struct_str){ "1234567" }); + struct_func_str8((struct struct_str){ "12345678" }); + struct_func_str9((struct struct_str){ "1234567ab" }); + struct_func_str16((struct struct_str){ "123456789abcdef0" }); + struct_func_str17((struct struct_str){ "123456789abcdef01" }); + + struct_func_str16_i("123456789abcdef0"); + struct_func_str17_i("123456789abcdef01"); + struct_func_str17_k("12"); +}] { + { {func_str7\("1234567"\).*= "ABCDEFG"} == 1 } + { {func_str17\("123456789abcdef01"\).*= "ABCDEFGIJKabcdefg"} == 1 } + + { {struct_func_str7\({ "1234567" }\).*= { "ABCDEFG" }} == 1 } + { {struct_func_str8\({ "12345678" }\).*= { "ABCDEFG8" }} == 1 } + { {struct_func_str9\({ "1234567ab" }\).*= { "ABCDEFG12" }} == 1 } + { {struct_func_str16\({ "123456789abcdef0" }\).*= { "a2b4c6d8eafc1e30" }} == 1 } + { {struct_func_str17\({ "123456789abcdef01" }\).*= { "a2b4c6d8eafc1e305" }} == 1 } + { {struct_func_str16_i\("123456789abcdef0"\).*= { { "a2b4c6d8eafc1e30" }, 256 }} == 1 } + { {struct_func_str17_i\("123456789abcdef01"\).*= { { "a2b4c6d8eafc1e305" }, 512 }} == 1 } + { {struct_func_str17_k\("12"\).*= { 768, { "a2b4c6d8eafc1e305" } }} == 1 } + +} + diff --git a/testsuite/ltrace.main/system_calls.exp b/testsuite/ltrace.main/system_calls.exp index f60e319..1b64cb0 100644 --- a/testsuite/ltrace.main/system_calls.exp +++ b/testsuite/ltrace.main/system_calls.exp @@ -133,7 +133,7 @@ Match [Diff [Calls [ltraceRun -L -S -- $bin]] \ { {^write$} == 1 } { {^unlink(at)?$} >= 2 } { {^open(at)?$} == 1 } - { {^(new|f)?stat(64)?$} == 1 } + { {^(new|f)?stat(64)?$} >= 1 } { {^close$} == 1 } { {^getcwd$} == 1 } { {^chdir$} == 1 } -- 1.9.1 _______________________________________________ Ltrace-devel mailing list Ltrace-devel@lists.alioth.debian.org http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/ltrace-devel