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

Reply via email to