| diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h |
| index d5ad759..a8b67bb 100644 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 <stdlib.h> |
| #include <string.h> |
| #include <sys/ucontext.h> |
| +#include <stdio.h> |
| |
| #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); |