diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h index d5ad759..a8b67bb 100644 --- a/sysdeps/linux-gnu/ppc/arch.h +++ b/sysdeps/linux-gnu/ppc/arch.h @@ -32,36 +32,45 @@ #define LT_ELF_MACHINE EM_PPC #ifdef __powerpc64__ // Says 'ltrace' is 64 bits, says nothing about target. -#define LT_ELFCLASS2 ELFCLASS64 -#define LT_ELF_MACHINE2 EM_PPC64 +# define LT_ELFCLASS2 ELFCLASS64 +# define LT_ELF_MACHINE2 EM_PPC64 # ifdef __LITTLE_ENDIAN__ -# define BREAKPOINT_VALUE { 0x08, 0x00, 0xe0, 0x7f } -# define ARCH_ENDIAN_LITTLE +# 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 +# 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 +# if !defined(_CALL_ELF) || _CALL_ELF < 2 +# define ARCH_SUPPORTS_OPD +# define STACK_FRAME_OVERHEAD 112 # ifndef EF_PPC64_ABI -# define EF_PPC64_ABI 3 +# define EF_PPC64_ABI 3 # endif -# else /* _CALL_ELF == 2 ABIv2 */ -# define STACK_FRAME_OVERHEAD 32 +# elif _CALL_ELF == 2 /* ELFv2 ABI */ +# define STACK_FRAME_OVERHEAD 32 +# else +# error Unsupported PowerPC64 ABI. # endif /* CALL_ELF */ #else -#define BREAKPOINT_VALUE { 0x7f, 0xe0, 0x00, 0x08 } -#define ARCH_ENDIAN_BIG +# define STACK_FRAME_OVERHEAD 112 +# define BREAKPOINT_VALUE { 0x7f, 0xe0, 0x00, 0x08 } +# define ARCH_ENDIAN_BIG # ifndef EF_PPC64_ABI -# define EF_PPC64_ABI 3 +# define EF_PPC64_ABI 3 # endif #endif /* __powerpc64__ */ +#ifdef _CALL_ELF +enum { ppc64_call_elf_abi = _CALL_ELF }; +#else +enum { ppc64_call_elf_abi = 0 }; +#endif + #define ARCH_HAVE_SW_SINGLESTEP #define ARCH_HAVE_ADD_PLT_ENTRY #define ARCH_HAVE_ADD_FUNC_ENTRY diff --git a/sysdeps/linux-gnu/ppc/fetch.c b/sysdeps/linux-gnu/ppc/fetch.c index c9381c3..c6cbd71 100644 --- a/sysdeps/linux-gnu/ppc/fetch.c +++ b/sysdeps/linux-gnu/ppc/fetch.c @@ -1,6 +1,6 @@ /* * This file is part of ltrace. - * Copyright (C) 2012 Petr Machata, Red Hat Inc. + * Copyright (C) 2012, 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 @@ -23,6 +23,7 @@ #include #include #include +#include #include "backend.h" #include "fetch.h" @@ -57,7 +58,7 @@ struct fetch_context { arch_addr_t stack_pointer; int greg; int freg; - int ret_struct; + bool ret_struct; union { gregs32_t r32; @@ -65,11 +66,29 @@ struct fetch_context { } regs; struct fpregs_t fpregs; int vgreg; - int struct_size; - int struct_hfa_size; - int struct_hfa_count; }; +static bool +is_eligible_hfa(struct arg_type_info *info, + struct arg_type_info **hfa_infop, size_t *hfa_countp) +{ + size_t hfa_count; + struct arg_type_info *hfa_info = type_get_hfa_type(info, &hfa_count); + + if (hfa_info != NULL && hfa_count <= 8 + && (hfa_info->type == ARGTYPE_FLOAT + || hfa_info->type == ARGTYPE_DOUBLE)) { + + if (hfa_infop != NULL) + *hfa_infop = hfa_info; + if (hfa_countp != NULL) + *hfa_countp = hfa_count; + return true; + } + + return false; +} + static int fetch_context_init(struct process *proc, struct fetch_context *context) { @@ -125,30 +144,37 @@ arch_fetch_arg_init(enum tof type, struct process *proc, } context->vgreg = context->greg; - context->struct_size = 0; - context->struct_hfa_size = 0; - context->struct_hfa_count = 0; /* 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. */ - context->ret_struct = ret_info->type == ARGTYPE_STRUCT; - if (context->ret_struct) { -#if _CALL_ELF == 2 - /* if R3 points to stack, parameters will be in R4. */ - uint64_t pstack_end = ptrace(PTRACE_PEEKTEXT, proc->pid, - proc->stack_pointer, 0); - if (((arch_addr_t)context->regs.r64[3] > proc->stack_pointer) - && (context->regs.r64[3] < pstack_end)) { + * r4. + */ + + context->ret_struct = false; + + if (ppc64_call_elf_abi == 2) { + /* With ELFv2 ABI, aggregates that consist + * (recursively) only of members of the same + * floating-point or vector type, are passed in a + * series of floating-point resp. vector registers. + * Additionally, when returning any aggregate of up to + * 16 bytes, general-purpose registers are used. */ + + if (ret_info->type == ARGTYPE_STRUCT + && ! is_eligible_hfa(ret_info, NULL, NULL) + && type_sizeof(proc, ret_info) > 16) { + + context->ret_struct = true; context->greg++; context->stack_pointer += 8; } -#else + + } else if (ret_info->type == ARGTYPE_STRUCT) { + context->ret_struct = true; context->greg++; -#endif } return context; @@ -176,17 +202,16 @@ allocate_stack_slot(struct fetch_context *ctx, struct process *proc, size_t a = type_alignof(proc, info); 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) + if (proc->e_machine == EM_PPC && a < 4) { a = 4; - else - a = 8; -#else - else if (proc->e_machine == EM_PPC64 && a < 8) -#endif + } else if (ppc64_call_elf_abi == 2) { + if (proc->e_machine == EM_PPC64 && sz == 4 && is_hfa_type) { + a = 4; + } else + a = 8; + } else if (proc->e_machine == EM_PPC64 && a < 8) { a = 8; + } /* XXX Remove the two double casts when arch_addr_t * becomes integral type. */ @@ -259,18 +284,19 @@ allocate_gpr(struct fetch_context *ctx, struct process *proc, 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 (ppc64_call_elf_abi == 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++; + } if (valuep == NULL) return 0; @@ -326,7 +352,6 @@ allocate_float(struct fetch_context *ctx, struct process *proc, return allocate_stack_slot(ctx, proc, info, valuep, is_hfa_type); } -#if _CALL_ELF == 2 static int allocate_hfa(struct fetch_context *ctx, struct process *proc, struct arg_type_info *info, struct value *valuep, @@ -336,27 +361,27 @@ allocate_hfa(struct fetch_context *ctx, struct process *proc, if (sz == (size_t)-1) return -1; - ctx->struct_hfa_size += sz; - /* There are two changes regarding structure return types: - * * heterogeneous 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. + * * heterogeneous 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. + * 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. */ + * passed on the stack. + */ size_t slot_off = 0; unsigned char *buf = value_reserve(valuep, sz); @@ -366,26 +391,17 @@ allocate_hfa(struct fetch_context *ctx, struct process *proc, 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); + int rc = allocate_float(ctx, proc, hfa_info, + &tmp, slot_off, true); + if (rc == 0) + memcpy(buf, value_get_data(&tmp, NULL), hfa_sz); + value_destroy(&tmp); - /* Hetereogeneous struct - get value on GPR or stack. */ - 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); + if (rc < 0) + return -1; slot_off += hfa_sz; buf += hfa_sz; @@ -394,17 +410,13 @@ allocate_hfa(struct fetch_context *ctx, struct process *proc, 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) { + * Mostly it is in part of r10. */ + if (ctx->vgreg == 10) { while (ctx->vgreg <= 10) { struct value tmp; value_init(&tmp, proc, NULL, hfa_info, 0); @@ -428,11 +440,8 @@ allocate_hfa(struct fetch_context *ctx, struct process *proc, } } - if (hfa_count == 0) - return 0; - /* Remaining values are on stack */ - while (hfa_count) { + while (hfa_count > 0) { struct value tmp; value_init(&tmp, proc, NULL, hfa_info, 0); @@ -444,7 +453,6 @@ allocate_hfa(struct fetch_context *ctx, struct process *proc, } return 0; } -#endif static int allocate_argument(struct fetch_context *ctx, struct process *proc, @@ -459,24 +467,20 @@ allocate_argument(struct fetch_context *ctx, struct process *proc, case ARGTYPE_FLOAT: case ARGTYPE_DOUBLE: return allocate_float(ctx, proc, info, valuep, - 8 - type_sizeof(proc,info), false); + 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 + } else if (ppc64_call_elf_abi == 2) { struct arg_type_info *hfa_info; - size_t hfa_size; - hfa_info = type_get_hfa_type(info, &hfa_size); - if (hfa_info != NULL ) { - size_t sz = type_sizeof(proc, info); - ctx->struct_size += sz; + size_t hfa_count; + if (is_eligible_hfa(info, &hfa_info, &hfa_count)) { return allocate_hfa(ctx, proc, info, valuep, - hfa_info->type, hfa_size); + hfa_info->type, hfa_count); } -#endif + } else { /* 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. @@ -510,9 +514,6 @@ allocate_argument(struct fetch_context *ctx, struct process *proc, if (sz == (size_t)-1) return -1; - if (ctx->ret_struct) - ctx->struct_size += sz; - size_t slots = (sz + width - 1) / width; /* Round up. */ unsigned char *buf = value_reserve(valuep, slots * width); if (buf == NULL) @@ -605,19 +606,7 @@ arch_fetch_retval(struct fetch_context *ctx, enum tof type, if (fetch_context_init(proc, ctx) < 0) return -1; -#if _CALL_ELF == 2 - void *ptr = (void *)(ctx->regs.r64[1]+32); - uint64_t val = ptrace(PTRACE_PEEKTEXT, proc->pid, ptr, 0); - - if (ctx->ret_struct - && ((ctx->struct_size > 64 - || ctx->struct_hfa_count > 8 - || (ctx->struct_hfa_size == 0 && ctx->struct_size > 56) - || (ctx->regs.r64[3] == ctx->regs.r64[1]+32) - || (ctx->regs.r64[3] == val )))) { -#else if (ctx->ret_struct) { -#endif assert(info->type == ARGTYPE_STRUCT); uint64_t addr = read_gpr(ctx, proc, 3);