[ltrace] Add patches for ARM parameter passing convention
Petr Machata
pmachata at fedoraproject.org
Wed Feb 6 23:16:10 UTC 2013
commit cb7483942c2d8ff8f495d45339113ee4cd694e2d
Author: Petr Machata <pmachata at redhat.com>
Date: Thu Feb 7 00:15:40 2013 +0100
Add patches for ARM parameter passing convention
ltrace-0.7.2-arm.patch | 1251 +++++++++++++++++++++++++++++++++++++++++++++---
ltrace.spec | 10 +-
2 files changed, 1195 insertions(+), 66 deletions(-)
---
diff --git a/ltrace-0.7.2-arm.patch b/ltrace-0.7.2-arm.patch
index a469224..72859a0 100644
--- a/ltrace-0.7.2-arm.patch
+++ b/ltrace-0.7.2-arm.patch
@@ -25,6 +25,40 @@ index c3356de..141ff85 100644
backend.h \
breakpoint.h \
common.h \
+diff --git a/README b/README
+index 3db5bc8..95871d1 100644
+--- a/README
++++ b/README
+@@ -24,6 +24,8 @@ The following targets are currently (at least somewhat) supported.
+ Some of them may be more or less broken in reality, it is not feasible
+ to test each release comprehensively on each target.
+
++ armv6l-*-linux-gnueabi
++ armv7l-*-linux-gnueabihf
+ i[4567]86-*-linux-gnu
+ ia64-*-linux-gnu
+ m68k-*-linux-gnu
+@@ -41,11 +43,6 @@ current status is unknown:
+ sparc64*-*-linux-gnu
+ alpha*-*-linux-gnu
+
+-Support of the following systems is known to be broken and requires
+-fixing:
+-
+- arm-*-linux-gnueabi
+-
+
+ Bug Reports
+ -----------
+@@ -83,7 +80,7 @@ quick one-liner), it is advisable to send an e-mail beforehand.
+
+
+ -------------------------------------------------------------------------------
+-Copyright (C) 2012 Petr Machata <pmachata at redhat.com>
++Copyright (C) 2012,2013 Petr Machata <pmachata at redhat.com>
+ Copyright (C) 1997-2009 Juan Cespedes <cespedes at debian.org>
+ This file is part of ltrace.
+
diff --git a/backend.h b/backend.h
index cfac65e..a9de3b4 100644
--- a/backend.h
@@ -307,6 +341,180 @@ index ed3d0e1..47b8c70 100644
static int
bitvect_lens_format_cb(struct lens *lens, FILE *stream,
struct value *value, struct value_dict *arguments)
+diff --git a/ltrace-elf.c b/ltrace-elf.c
+index 1d0f769..af25f8f 100644
+--- a/ltrace-elf.c
++++ b/ltrace-elf.c
+@@ -1,6 +1,6 @@
+ /*
+ * This file is part of ltrace.
+- * Copyright (C) 2006,2010,2011,2012 Petr Machata, Red Hat Inc.
++ * Copyright (C) 2006,2010,2011,2012,2013 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2010 Zachary T Welch, CodeSourcery
+ * Copyright (C) 2010 Joe Damato
+ * Copyright (C) 1997,1998,2001,2004,2007,2008,2009 Juan Cespedes
+@@ -141,8 +141,9 @@ elf_get_section_if(struct ltelf *lte, Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr,
+ return 0;
+ }
+ }
+- return -1;
+
++ *tgt_sec = NULL;
++ return 0;
+ }
+
+ static int
+@@ -203,23 +204,23 @@ elf_get_section_named(struct ltelf *lte, const char *name,
+ &name_p, &data);
+ }
+
+-static int
+-need_data(Elf_Data *data, GElf_Xword offset, GElf_Xword size)
++int
++elf_can_read_next(Elf_Data *data, GElf_Xword offset, GElf_Xword size)
+ {
+ assert(data != NULL);
+ if (data->d_size < size || offset > data->d_size - size) {
+ debug(1, "Not enough data to read %"PRId64"-byte value"
+ " at offset %"PRId64".", size, offset);
+- return -1;
++ return 0;
+ }
+- return 0;
++ return 1;
+ }
+
+ #define DEF_READER(NAME, SIZE) \
+ int \
+ NAME(Elf_Data *data, GElf_Xword offset, uint##SIZE##_t *retp) \
+ { \
+- if (!need_data(data, offset, SIZE / 8) < 0) \
++ if (!elf_can_read_next(data, offset, SIZE / 8)) \
+ return -1; \
+ \
+ if (data->d_buf == NULL) /* NODATA section */ { \
+@@ -236,12 +237,63 @@ need_data(Elf_Data *data, GElf_Xword offset, GElf_Xword size)
+ return 0; \
+ }
+
++DEF_READER(elf_read_u8, 8)
+ DEF_READER(elf_read_u16, 16)
+ DEF_READER(elf_read_u32, 32)
+ DEF_READER(elf_read_u64, 64)
+
+ #undef DEF_READER
+
++#define DEF_READER(NAME, SIZE) \
++ int \
++ NAME(Elf_Data *data, GElf_Xword *offset, uint##SIZE##_t *retp) \
++ { \
++ int rc = elf_read_u##SIZE(data, *offset, retp); \
++ if (rc < 0) \
++ return rc; \
++ *offset += SIZE / 8; \
++ return 0; \
++ }
++
++DEF_READER(elf_read_next_u8, 8)
++DEF_READER(elf_read_next_u16, 16)
++DEF_READER(elf_read_next_u32, 32)
++DEF_READER(elf_read_next_u64, 64)
++
++#undef DEF_READER
++
++int
++elf_read_next_uleb128(Elf_Data *data, GElf_Xword *offset, uint64_t *retp)
++{
++ uint64_t result = 0;
++ int shift = 0;
++ int size = 8 * sizeof result;
++
++ while (1) {
++ uint8_t byte;
++ if (elf_read_next_u8(data, offset, &byte) < 0)
++ return -1;
++
++ uint8_t payload = byte & 0x7f;
++ result |= (uint64_t)payload << shift;
++ shift += 7;
++ if (shift > size && byte != 0x1)
++ return -1;
++ if ((byte & 0x80) == 0)
++ break;
++ }
++
++ if (retp != NULL)
++ *retp = result;
++ return 0;
++}
++
++int
++elf_read_uleb128(Elf_Data *data, GElf_Xword offset, uint64_t *retp)
++{
++ return elf_read_next_uleb128(data, &offset, retp);
++}
++
+ int
+ open_elf(struct ltelf *lte, const char *filename)
+ {
+diff --git a/ltrace-elf.h b/ltrace-elf.h
+index b76d1eb..178258b 100644
+--- a/ltrace-elf.h
++++ b/ltrace-elf.h
+@@ -1,6 +1,6 @@
+ /*
+ * This file is part of ltrace.
+- * Copyright (C) 2006,2010,2012 Petr Machata, Red Hat Inc.
++ * Copyright (C) 2006,2010,2012,2013 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2010 Zachary T Welch
+ * Copyright (C) 2001,2004,2007,2009 Juan Cespedes
+ * Copyright (C) 2006 Ian Wienand
+@@ -95,6 +95,12 @@ int elf_get_sym_info(struct ltelf *lte, const char *filename,
+ size_t sym_index, GElf_Rela *rela, GElf_Sym *sym);
+
+ Elf_Data *elf_loaddata(Elf_Scn *scn, GElf_Shdr *shdr);
++
++/* The following three look for sections based on various criteria.
++ * They return 0 if there was no error, or a negative value if there
++ * was. If the section was found, it is returned in *TGT_SEC, and the
++ * header is stored te TGT_SHDR. If it wasn't found, *TGT_SEC is set
++ * to NULL. */
+ int elf_get_section_covering(struct ltelf *lte, GElf_Addr addr,
+ Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr);
+ int elf_get_section_type(struct ltelf *lte, GElf_Word type,
+@@ -102,13 +108,29 @@ int elf_get_section_type(struct ltelf *lte, GElf_Word type,
+ int elf_get_section_named(struct ltelf *lte, const char *name,
+ Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr);
+
+-/* Read, respectively, 2, 4, or 8 bytes from Elf data at given OFFSET,
+- * and store it in *RETP. Returns 0 on success or a negative value if
+- * there's not enough data. */
++/* Read, respectively, 1, 2, 4, or 8 bytes from Elf data at given
++ * OFFSET, and store it in *RETP. Returns 0 on success or a negative
++ * value if there's not enough data. */
++int elf_read_u8(Elf_Data *data, GElf_Xword offset, uint8_t *retp);
+ int elf_read_u16(Elf_Data *data, GElf_Xword offset, uint16_t *retp);
+ int elf_read_u32(Elf_Data *data, GElf_Xword offset, uint32_t *retp);
+ int elf_read_u64(Elf_Data *data, GElf_Xword offset, uint64_t *retp);
+
++/* Read at most 64-bit quantity recorded in an ULEB128 variable-length
++ * encoding. */
++int elf_read_uleb128(Elf_Data *data, GElf_Xword offset, uint64_t *retp);
++
++/* These are same as above, but update *OFFSET with the width
++ * of read datum. */
++int elf_read_next_u8(Elf_Data *data, GElf_Xword *offset, uint8_t *retp);
++int elf_read_next_u16(Elf_Data *data, GElf_Xword *offset, uint16_t *retp);
++int elf_read_next_u32(Elf_Data *data, GElf_Xword *offset, uint32_t *retp);
++int elf_read_next_u64(Elf_Data *data, GElf_Xword *offset, uint64_t *retp);
++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);
++
+ #if __WORDSIZE == 32
+ #define PRI_ELF_ADDR PRIx32
+ #define GELF_ADDR_CAST(x) (void *)(uint32_t)(x)
diff --git a/output.c b/output.c
index fe62bb4..f046df8 100644
--- a/output.c
@@ -428,7 +636,7 @@ index c197225..9ccd8f2 100644
- ptrace(PTRACE_POKEUSER, proc->pid, 26 /* RA */ , addr);
-}
diff --git a/sysdeps/linux-gnu/arm/Makefile.am b/sysdeps/linux-gnu/arm/Makefile.am
-index 385424c..4b858ec 100644
+index 385424c..2c180c6 100644
--- a/sysdeps/linux-gnu/arm/Makefile.am
+++ b/sysdeps/linux-gnu/arm/Makefile.am
@@ -1,4 +1,5 @@
@@ -450,7 +658,7 @@ index 385424c..4b858ec 100644
- plt.c \
- regs.c \
- trace.c
-+___libcpu_la_SOURCES = breakpoint.c plt.c regs.c trace.c
++___libcpu_la_SOURCES = breakpoint.c fetch.c plt.c regs.c trace.c
-noinst_HEADERS = \
- arch.h \
@@ -465,7 +673,7 @@ index 385424c..4b858ec 100644
- Makefile.in
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/sysdeps/linux-gnu/arm/arch.h b/sysdeps/linux-gnu/arm/arch.h
-index 291443a..71cfd5e 100644
+index 291443a..58a7fdf 100644
--- a/sysdeps/linux-gnu/arm/arch.h
+++ b/sysdeps/linux-gnu/arm/arch.h
@@ -1,5 +1,6 @@
@@ -475,14 +683,41 @@ index 291443a..71cfd5e 100644
* Copyright (C) 1998,2004,2008 Juan Cespedes
*
* This program is free software; you can redistribute it and/or
-@@ -31,6 +32,7 @@
+@@ -18,6 +19,9 @@
+ * 02110-1301 USA
+ */
+
++#ifndef LTRACE_ARM_ARCH_H
++#define LTRACE_ARM_ARCH_H
++
+ #define ARCH_HAVE_ENABLE_BREAKPOINT 1
+ #define ARCH_HAVE_DISABLE_BREAKPOINT 1
+
+@@ -31,7 +35,24 @@
#define LT_ELFCLASS ELFCLASS32
#define LT_ELF_MACHINE EM_ARM
+#define ARCH_HAVE_SW_SINGLESTEP
++#define ARCH_HAVE_FETCH_ARG
++#define ARCH_HAVE_FETCH_PACK
++#define ARCH_HAVE_SIZEOF
++#define ARCH_HAVE_ALIGNOF
#define ARCH_HAVE_BREAKPOINT_DATA
struct arch_breakpoint_data {
int thumb_mode;
+ };
++
++#define ARCH_HAVE_LTELF_DATA
++struct arch_ltelf_data {
++ /* We have this only for the hooks. */
++};
++
++#define ARCH_HAVE_LIBRARY_DATA
++struct arch_library_data {
++ unsigned int hardfp:1;
++};
++
++#endif /* LTRACE_ARM_ARCH_H */
diff --git a/sysdeps/linux-gnu/arm/breakpoint.c b/sysdeps/linux-gnu/arm/breakpoint.c
index 2fb9578..fcd43a7 100644
--- a/sysdeps/linux-gnu/arm/breakpoint.c
@@ -507,8 +742,543 @@ index 2fb9578..fcd43a7 100644
return 0;
}
+diff --git a/sysdeps/linux-gnu/arm/fetch.c b/sysdeps/linux-gnu/arm/fetch.c
+new file mode 100644
+index 0000000..0064d91
+--- /dev/null
++++ b/sysdeps/linux-gnu/arm/fetch.c
+@@ -0,0 +1,529 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2013 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
++ */
++
++#include <sys/ptrace.h>
++#include <asm/ptrace.h>
++#include <assert.h>
++#include <elf.h>
++#include <libelf.h>
++#include <stdint.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <stdbool.h>
++
++#include "backend.h"
++#include "fetch.h"
++#include "library.h"
++#include "ltrace-elf.h"
++#include "proc.h"
++#include "ptrace.h"
++#include "regs.h"
++#include "type.h"
++#include "value.h"
++
++static int
++get_hardfp(uint64_t abi_vfp_args)
++{
++ if (abi_vfp_args == 2)
++ fprintf(stderr,
++ "Tag_ABI_VFP_args value 2 (tool chain-specific "
++ "conventions) not supported.\n");
++ return abi_vfp_args == 1;
++}
++
++int
++arch_elf_init(struct ltelf *lte, struct library *lib)
++{
++ /* Nothing in this section is strictly critical. It's not
++ * that much of a deal if we fail to guess right whether the
++ * ABI is softfp or hardfp. */
++ unsigned hardfp = 0;
++
++ Elf_Scn *scn;
++ Elf_Data *data;
++ GElf_Shdr shdr;
++ if (elf_get_section_type(lte, SHT_ARM_ATTRIBUTES, &scn, &shdr) < 0
++ || (scn != NULL && (data = elf_loaddata(scn, &shdr)) == NULL)) {
++ fprintf(stderr,
++ "Error when obtaining ARM attribute section: %s\n",
++ elf_errmsg(-1));
++ goto done;
++
++ } else if (scn != NULL && data != NULL) {
++ GElf_Xword offset = 0;
++ uint8_t version;
++ if (elf_read_next_u8(data, &offset, &version) < 0) {
++ goto done;
++ } else if (version != 'A') {
++ fprintf(stderr, "Unsupported ARM attribute section "
++ "version %d ('%c').\n", version, version);
++ goto done;
++ }
++
++ do {
++ const char signature[] = "aeabi";
++ /* N.B. LEN is including the length field
++ * itself. */
++ uint32_t sec_len;
++ if (elf_read_u32(data, offset, &sec_len) < 0
++ || !elf_can_read_next(data, offset, sec_len)) {
++ goto done;
++ }
++ const GElf_Xword next_offset = offset + sec_len;
++ offset += 4;
++
++ if (sec_len < 4 + sizeof signature
++ || strcmp(signature, data->d_buf + offset) != 0)
++ goto skip;
++ offset += sizeof signature;
++
++ const GElf_Xword offset0 = offset;
++ uint64_t tag;
++ uint32_t sub_len;
++ if (elf_read_next_uleb128(data, &offset, &tag) < 0
++ || elf_read_next_u32(data, &offset, &sub_len) < 0
++ || !elf_can_read_next(data, offset0, sub_len))
++ goto done;
++
++ if (tag != 1)
++ /* IHI0045D_ABI_addenda: "section and
++ * symbol attributes are deprecated
++ * [...] consumers are permitted to
++ * ignore them." */
++ goto skip;
++
++ while (offset < offset0 + sub_len) {
++ if (elf_read_next_uleb128(data,
++ &offset, &tag) < 0)
++ goto done;
++
++ switch (tag) {
++ uint64_t v;
++ case 6: /* Tag_CPU_arch */
++ case 7: /* Tag_CPU_arch_profile */
++ case 8: /* Tag_ARM_ISA_use */
++ case 9: /* Tag_THUMB_ISA_use */
++ case 10: /* Tag_FP_arch */
++ case 11: /* Tag_WMMX_arch */
++ case 12: /* Tag_Advanced_SIMD_arch */
++ case 13: /* Tag_PCS_config */
++ case 14: /* Tag_ABI_PCS_R9_use */
++ case 15: /* Tag_ABI_PCS_RW_data */
++ case 16: /* Tag_ABI_PCS_RO_data */
++ case 17: /* Tag_ABI_PCS_GOT_use */
++ case 18: /* Tag_ABI_PCS_wchar_t */
++ case 19: /* Tag_ABI_FP_rounding */
++ case 20: /* Tag_ABI_FP_denormal */
++ case 21: /* Tag_ABI_FP_exceptions */
++ case 22: /* Tag_ABI_FP_user_exceptions */
++ case 23: /* Tag_ABI_FP_number_model */
++ case 24: /* Tag_ABI_align_needed */
++ case 25: /* Tag_ABI_align_preserved */
++ case 26: /* Tag_ABI_enum_size */
++ case 27: /* Tag_ABI_HardFP_use */
++ case 28: /* Tag_ABI_VFP_args */
++ case 29: /* Tag_ABI_WMMX_args */
++ case 30: /* Tag_ABI_optimization_goals */
++ case 31: /* Tag_ABI_FP_optimization_goals */
++ case 32: /* Tag_compatibility */
++ case 34: /* Tag_CPU_unaligned_access */
++ case 36: /* Tag_FP_HP_extension */
++ case 38: /* Tag_ABI_FP_16bit_format */
++ case 42: /* Tag_MPextension_use */
++ case 70: /* Tag_MPextension_use as well */
++ case 44: /* Tag_DIV_use */
++ case 64: /* Tag_nodefaults */
++ case 66: /* Tag_T2EE_use */
++ case 68: /* Tag_Virtualization_use */
++ uleb128:
++ if (elf_read_next_uleb128
++ (data, &offset, &v) < 0)
++ goto done;
++ if (tag == 28)
++ hardfp = get_hardfp(v);
++ if (tag != 32)
++ continue;
++
++ /* Tag 32 has two arguments,
++ * fall through. */
++
++ case 4: /* Tag_CPU_raw_name */
++ case 5: /* Tag_CPU_name */
++ case 65: /* Tag_also_compatible_with */
++ case 67: /* Tag_conformance */
++ ntbs:
++ offset += strlen(data->d_buf
++ + offset) + 1;
++ continue;
++ }
++
++ /* Handle unknown tags in a generic
++ * manner, if possible. */
++ if (tag <= 32) {
++ fprintf(stderr,
++ "Unknown tag %lld "
++ "at offset %#llx "
++ "of ARM attribute section.",
++ tag, offset);
++ goto skip;
++ } else if (tag % 2 == 0) {
++ goto uleb128;
++ } else {
++ goto ntbs;
++ }
++ }
++
++ skip:
++ offset = next_offset;
++
++ } while (elf_can_read_next(data, offset, 1));
++
++ }
++
++done:
++ lib->arch.hardfp = hardfp;
++ return 0;
++}
++
++void
++arch_elf_destroy(struct ltelf *lte)
++{
++}
++
++void
++arch_library_init(struct library *lib)
++{
++}
++
++void
++arch_library_destroy(struct library *lib)
++{
++}
++
++void
++arch_library_clone(struct library *retp, struct library *lib)
++{
++ retp->arch = lib->arch;
++}
++
++enum {
++ /* How many (double) VFP registers the AAPCS uses for
++ * parameter passing. */
++ NUM_VFP_REGS = 8,
++};
++
++struct fetch_context {
++ struct pt_regs regs;
++
++ struct {
++ union {
++ double d[32];
++ float s[64];
++ };
++ uint32_t fpscr;
++ } fpregs;
++
++ /* VFP register allocation. ALLOC.S tracks whether the
++ * corresponding FPREGS.S register is taken, ALLOC.D the same
++ * for FPREGS.D. We only track 8 (16) registers, because
++ * that's what the ABI uses for parameter passing. */
++ union {
++ int16_t d[NUM_VFP_REGS];
++ int8_t s[NUM_VFP_REGS * 2];
++ } alloc;
++
++ unsigned ncrn;
++ arch_addr_t sp;
++ arch_addr_t nsaa;
++ arch_addr_t ret_struct;
++
++ bool hardfp:1;
++ bool in_varargs:1;
++};
++
++static int
++fetch_register_banks(struct process *proc, struct fetch_context *context)
++{
++ if (ptrace(PTRACE_GETREGS, proc->pid, NULL, &context->regs) == -1)
++ return -1;
++
++ if (context->hardfp
++ && ptrace(PTRACE_GETVFPREGS, proc->pid,
++ NULL, &context->fpregs) == -1)
++ return -1;
++
++ context->ncrn = 0;
++ context->nsaa = context->sp = get_stack_pointer(proc);
++ memset(&context->alloc, 0, sizeof(context->alloc));
++
++ return 0;
++}
++
++struct fetch_context *
++arch_fetch_arg_init(enum tof type, struct process *proc,
++ struct arg_type_info *ret_info)
++{
++ struct fetch_context *context = malloc(sizeof(*context));
++
++ {
++ struct process *mainp = proc;
++ while (mainp->libraries == NULL && mainp->parent != NULL)
++ mainp = mainp->parent;
++ context->hardfp = mainp->libraries->arch.hardfp;
++ }
++
++ if (context == NULL
++ || fetch_register_banks(proc, context) < 0) {
++ free(context);
++ return NULL;
++ }
++
++ if (ret_info->type == ARGTYPE_STRUCT
++ || ret_info->type == ARGTYPE_ARRAY) {
++ size_t sz = type_sizeof(proc, ret_info);
++ assert(sz != (size_t)-1);
++ if (sz > 4) {
++ /* XXX double cast */
++ context->ret_struct
++ = (arch_addr_t)context->regs.uregs[0];
++ context->ncrn++;
++ }
++ }
++
++ return context;
++}
++
++struct fetch_context *
++arch_fetch_arg_clone(struct process *proc,
++ struct fetch_context *context)
++{
++ struct fetch_context *clone = malloc(sizeof(*context));
++ if (clone == NULL)
++ return NULL;
++ *clone = *context;
++ return clone;
++}
++
++/* 0 is success, 1 is failure, negative value is an error. */
++static int
++pass_in_vfp(struct fetch_context *ctx, struct process *proc,
++ enum arg_type type, size_t count, struct value *valuep)
++{
++ assert(type == ARGTYPE_FLOAT || type == ARGTYPE_DOUBLE);
++ unsigned max = type == ARGTYPE_DOUBLE ? NUM_VFP_REGS : 2 * NUM_VFP_REGS;
++ if (count > max)
++ return 1;
++
++ size_t i;
++ size_t j;
++ for (i = 0; i < max; ++i) {
++ for (j = i; j < i + count; ++j)
++ if ((type == ARGTYPE_DOUBLE && ctx->alloc.d[j] != 0)
++ || (type == ARGTYPE_FLOAT && ctx->alloc.s[j] != 0))
++ goto next;
++
++ /* Found COUNT consecutive unallocated registers at I. */
++ const size_t sz = (type == ARGTYPE_FLOAT ? 4 : 8) * count;
++ unsigned char *data = value_reserve(valuep, sz);
++ if (data == NULL)
++ return -1;
++
++ for (j = i; j < i + count; ++j)
++ if (type == ARGTYPE_DOUBLE)
++ ctx->alloc.d[j] = -1;
++ else
++ ctx->alloc.s[j] = -1;
++
++ if (type == ARGTYPE_DOUBLE)
++ memcpy(data, ctx->fpregs.d + i, sz);
++ else
++ memcpy(data, ctx->fpregs.s + i, sz);
++
++ return 0;
++
++ next:
++ continue;
++ }
++ return 1;
++}
++
++/* 0 is success, 1 is failure, negative value is an error. */
++static int
++consider_vfp(struct fetch_context *ctx, struct process *proc,
++ struct arg_type_info *info, struct value *valuep)
++{
++ struct arg_type_info *float_info = NULL;
++ size_t hfa_size = 1;
++ if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE)
++ float_info = info;
++ else
++ float_info = type_get_hfa_type(info, &hfa_size);
++
++ if (float_info != NULL && hfa_size <= 4)
++ return pass_in_vfp(ctx, proc, float_info->type,
++ hfa_size, valuep);
++ return 1;
++}
++
++int
++arch_fetch_arg_next(struct fetch_context *ctx, enum tof type,
++ struct process *proc,
++ struct arg_type_info *info, struct value *valuep)
++{
++ const size_t sz = type_sizeof(proc, info);
++ assert(sz != (size_t)-1);
++
++ if (ctx->hardfp && !ctx->in_varargs) {
++ int rc;
++ if ((rc = consider_vfp(ctx, proc, info, valuep)) != 1)
++ return rc;
++ }
++
++ /* IHI0042E_aapcs: If the argument requires double-word
++ * alignment (8-byte), the NCRN is rounded up to the next even
++ * register number. */
++ const size_t al = type_alignof(proc, info);
++ assert(al != (size_t)-1);
++ if (al == 8)
++ ctx->ncrn = ((ctx->ncrn + 1) / 2) * 2;
++
++ /* If the size in words of the argument is not more than r4
++ * minus NCRN, the argument is copied into core registers,
++ * starting at the NCRN. */
++ /* If the NCRN is less than r4 and the NSAA is equal to the
++ * SP, the argument is split between core registers and the
++ * stack. */
++
++ const size_t words = (sz + 3) / 4;
++ if (ctx->ncrn < 4 && ctx->nsaa == ctx->sp) {
++ unsigned char *data = value_reserve(valuep, words * 4);
++ if (data == NULL)
++ return -1;
++ size_t i;
++ for (i = 0; i < words && ctx->ncrn < 4; ++i) {
++ memcpy(data, &ctx->regs.uregs[ctx->ncrn++], 4);
++ data += 4;
++ }
++ const size_t rest = (words - i) * 4;
++ if (rest > 0) {
++ umovebytes(proc, ctx->nsaa, data, rest);
++ ctx->nsaa += rest;
++ }
++ return 0;
++ }
++
++ assert(ctx->ncrn == 4);
++
++ /* If the argument required double-word alignment (8-byte),
++ * then the NSAA is rounded up to the next double-word
++ * address. */
++ if (al == 8)
++ /* XXX double cast. */
++ ctx->nsaa = (arch_addr_t)((((uintptr_t)ctx->nsaa + 7) / 8) * 8);
++ else
++ ctx->nsaa = (arch_addr_t)((((uintptr_t)ctx->nsaa + 3) / 4) * 4);
++
++ value_in_inferior(valuep, ctx->nsaa);
++ ctx->nsaa += sz;
++
++ return 0;
++}
++
++int
++arch_fetch_retval(struct fetch_context *ctx, enum tof type,
++ struct process *proc, struct arg_type_info *info,
++ struct value *valuep)
++{
++ if (fetch_register_banks(proc, ctx) < 0)
++ return -1;
++
++ if (ctx->hardfp && !ctx->in_varargs) {
++ int rc;
++ if ((rc = consider_vfp(ctx, proc, info, valuep)) != 1)
++ return rc;
++ }
++
++ size_t sz = type_sizeof(proc, info);
++ assert(sz != (size_t)-1);
++
++ switch (info->type) {
++ unsigned char *data;
++
++ case ARGTYPE_VOID:
++ return 0;
++
++ case ARGTYPE_FLOAT:
++ case ARGTYPE_DOUBLE:
++ if (ctx->hardfp && !ctx->in_varargs) {
++ unsigned char *data = value_reserve(valuep, sz);
++ if (data == NULL)
++ return -1;
++ memmove(data, &ctx->fpregs, sz);
++ return 0;
++ }
++ goto pass_in_registers;
++
++ case ARGTYPE_ARRAY:
++ case ARGTYPE_STRUCT:
++ if (sz > 4) {
++ value_in_inferior(valuep, ctx->ret_struct);
++ return 0;
++ }
++ /* Fall through. */
++
++ case ARGTYPE_CHAR:
++ case ARGTYPE_SHORT:
++ case ARGTYPE_USHORT:
++ case ARGTYPE_INT:
++ case ARGTYPE_UINT:
++ case ARGTYPE_LONG:
++ case ARGTYPE_ULONG:
++ case ARGTYPE_POINTER:
++ pass_in_registers:
++ if ((data = value_reserve(valuep, sz)) == NULL)
++ return -1;
++ memmove(data, ctx->regs.uregs, sz);
++ return 0;
++ }
++ assert(info->type != info->type);
++ abort();
++}
++
++void
++arch_fetch_arg_done(struct fetch_context *context)
++{
++ free(context);
++}
++
++int
++arch_fetch_param_pack_start(struct fetch_context *context,
++ enum param_pack_flavor ppflavor)
++{
++ if (ppflavor == PARAM_PACK_VARARGS)
++ context->in_varargs = true;
++ return 0;
++}
++
++void
++arch_fetch_param_pack_end(struct fetch_context *context)
++{
++ context->in_varargs = false;
++}
diff --git a/sysdeps/linux-gnu/arm/regs.c b/sysdeps/linux-gnu/arm/regs.c
-index 377df62..bd86370 100644
+index 377df62..e9e825e 100644
--- a/sysdeps/linux-gnu/arm/regs.c
+++ b/sysdeps/linux-gnu/arm/regs.c
@@ -1,5 +1,6 @@
@@ -530,14 +1300,13 @@ index 377df62..bd86370 100644
#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
# define PTRACE_PEEKUSER PTRACE_PEEKUSR
-@@ -37,13 +40,91 @@
+@@ -36,50 +39,119 @@
+ # define PTRACE_POKEUSER PTRACE_POKEUSR
#endif
- #define off_pc ((void *)60)
+-#define off_pc ((void *)60)
-#define off_lr ((void *)56)
- #define off_sp ((void *)52)
-
--void *
+-#define off_sp ((void *)52)
+int
+arm_get_register(struct process *proc, enum arm_register reg, uint32_t *lp)
+{
@@ -548,22 +1317,38 @@ index 377df62..bd86370 100644
+ *lp = (uint32_t)l;
+ return 0;
+}
-+
+
+-void *
+-get_instruction_pointer(struct process *proc)
++int
++arm_set_register(struct process *proc, enum arm_register reg, uint32_t lp)
+ {
+- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0);
++ return ptrace(PTRACE_PEEKUSER, proc->pid,
++ (void *)(reg * 4L), (void *)lp);
+ }
+
+-void
+-set_instruction_pointer(struct process *proc, void *addr)
+int
+arm_get_register_offpc(struct process *proc, enum arm_register reg,
+ uint32_t *lp)
-+{
+ {
+- ptrace(PTRACE_POKEUSER, proc->pid, off_pc, addr);
+ if (arm_get_register(proc, reg, lp) < 0)
+ return -1;
+ if (reg == ARM_REG_PC)
+ *lp += 8;
+ return 0;
-+}
-+
+ }
+
+-void *
+-get_stack_pointer(struct process *proc)
+int
+arm_get_shifted_register(struct process *proc, uint32_t inst, int carry,
+ arch_addr_t pc_val, uint32_t *lp)
-+{
+ {
+- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_sp, 0);
+ enum arm_register rm = BITS(inst, 0, 3);
+ unsigned long shifttype = BITS(inst, 5, 6);
+
@@ -610,30 +1395,15 @@ index 377df62..bd86370 100644
+
+ *lp = res & 0xffffffff;
+ return 0;
-+}
-+
-+arch_addr_t
- get_instruction_pointer(struct process *proc)
- {
-- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0);
-+ uint32_t reg;
-+ if (arm_get_register(proc, ARM_REG_PC, ®) < 0)
-+ /* XXX double cast. */
-+ return (arch_addr_t)-1;
-+ /* XXX double cast. */
-+ return (arch_addr_t)(uintptr_t)reg;
- }
-
- void
-@@ -58,28 +139,13 @@ get_stack_pointer(struct process *proc)
- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_sp, 0);
}
-/* really, this is given the *stack_pointer expecting
- * a CISC architecture; in our case, we don't need that */
-void *
-get_return_addr(struct process *proc, void *stack_pointer)
--{
++static arch_addr_t
++get_register_nocheck(struct process *proc, enum arm_register r)
+ {
- long addr = ptrace(PTRACE_PEEKUSER, proc->pid, off_lr, 0);
-
- /* Remember & unset the thumb mode bit. XXX This is really a
@@ -646,28 +1416,47 @@ index 377df62..bd86370 100644
- addr &= ~1;
-
- return (void *)addr;
--}
--
--void
++ uint32_t reg;
++ if (arm_get_register(proc, r, ®) < 0)
++ /* XXX double cast. */
++ return (arch_addr_t)-1;
++ /* XXX double cast. */
++ return (arch_addr_t)(uintptr_t)reg;
++}
++
++arch_addr_t
++get_instruction_pointer(struct process *proc)
++{
++ return get_register_nocheck(proc, ARM_REG_PC);
+ }
+
+ void
-set_return_addr(struct process *proc, void *addr)
++set_instruction_pointer(struct process *proc, arch_addr_t addr)
++{
++ /* XXX double cast. */
++ arm_set_register(proc, ARM_REG_PC, (uint32_t)addr);
++}
++
++void *
++get_stack_pointer(struct process *proc)
++{
++ return get_register_nocheck(proc, ARM_REG_SP);
++}
++
+arch_addr_t
+get_return_addr(struct process *proc, arch_addr_t stack_pointer)
{
- long iaddr = (int)addr | proc->thumb_mode;
- ptrace(PTRACE_POKEUSER, proc->pid, off_lr, (void *)iaddr);
-+ uint32_t reg;
-+ if (arm_get_register(proc, ARM_REG_LR, ®) < 0)
-+ /* XXX double cast. */
-+ return (arch_addr_t)-1;
-+ /* XXX double cast. */
-+ return (arch_addr_t)(uintptr_t)reg;
++ return get_register_nocheck(proc, ARM_REG_LR);
}
diff --git a/sysdeps/linux-gnu/arm/regs.h b/sysdeps/linux-gnu/arm/regs.h
new file mode 100644
-index 0000000..1566f92
+index 0000000..f9a5a86
--- /dev/null
+++ b/sysdeps/linux-gnu/arm/regs.h
-@@ -0,0 +1,45 @@
+@@ -0,0 +1,47 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
@@ -695,6 +1484,8 @@ index 0000000..1566f92
+ ((long) (BITS(obj,st,fn) | ((long) BIT(obj,fn) * ~ SUBMASK(fn - st))))
+
+enum arm_register {
++ ARM_REG_R7 = 7,
++ ARM_REG_IP = 12,
+ ARM_REG_SP = 13,
+ ARM_REG_LR = 14,
+ ARM_REG_PC = 15,
@@ -714,7 +1505,7 @@ index 0000000..1566f92
+int arm_get_shifted_register(struct process *proc, uint32_t inst, int carry,
+ arch_addr_t pc, uint32_t *lp);
diff --git a/sysdeps/linux-gnu/arm/trace.c b/sysdeps/linux-gnu/arm/trace.c
-index fbbf676..8b0734e 100644
+index fbbf676..5e51e91 100644
--- a/sysdeps/linux-gnu/arm/trace.c
+++ b/sysdeps/linux-gnu/arm/trace.c
@@ -1,6 +1,6 @@
@@ -725,7 +1516,7 @@ index fbbf676..8b0734e 100644
* Copyright (C) 1998,2004,2008,2009 Juan Cespedes
* Copyright (C) 2006 Ian Wienand
*
-@@ -29,10 +29,12 @@
+@@ -29,10 +29,13 @@
#include <sys/ptrace.h>
#include <asm/ptrace.h>
@@ -736,36 +1527,89 @@ index fbbf676..8b0734e 100644
#include "output.h"
#include "ptrace.h"
+#include "regs.h"
++#include "type.h"
#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
# define PTRACE_PEEKUSER PTRACE_PEEKUSR
-@@ -46,6 +48,7 @@
- #define off_r7 ((void *)28)
- #define off_ip ((void *)48)
- #define off_pc ((void *)60)
-+#define off_cpsr ((void *)64)
+@@ -42,11 +45,6 @@
+ # define PTRACE_POKEUSER PTRACE_POKEUSR
+ #endif
+-#define off_r0 ((void *)0)
+-#define off_r7 ((void *)28)
+-#define off_ip ((void *)48)
+-#define off_pc ((void *)60)
+-
void
get_arch_dep(struct process *proc)
-@@ -149,3 +152,560 @@ gimme_arg(enum tof type, struct process *proc, int arg_num,
+ {
+@@ -68,18 +66,24 @@ syscall_p(struct process *proc, int status, int *sysnum)
+ {
+ if (WIFSTOPPED(status)
+ && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) {
+- /* get the user's pc (plus 8) */
+- unsigned pc = ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0);
++ uint32_t pc, ip;
++ if (arm_get_register(proc, ARM_REG_PC, &pc) < 0
++ || arm_get_register(proc, ARM_REG_IP, &ip) < 0)
++ return -1;
++
+ pc = pc - 4;
++
+ /* fetch the SWI instruction */
+ unsigned insn = ptrace(PTRACE_PEEKTEXT, proc->pid,
+ (void *)pc, 0);
+- int ip = ptrace(PTRACE_PEEKUSER, proc->pid, off_ip, 0);
+ if (insn == 0xef000000 || insn == 0x0f000000
+ || (insn & 0xffff0000) == 0xdf000000) {
+ /* EABI syscall */
+- *sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, off_r7, 0);
++ uint32_t r7;
++ if (arm_get_register(proc, ARM_REG_R7, &r7) < 0)
++ return -1;
++ *sysnum = r7;
+ } else if ((insn & 0xfff00000) == 0xef900000) {
+ /* old ABI syscall */
+ *sysnum = insn & 0xfffff;
+@@ -105,47 +109,605 @@ syscall_p(struct process *proc, int status, int *sysnum)
return 0;
}
-+
+
+-long
+-gimme_arg(enum tof type, struct process *proc, int arg_num,
+- struct arg_type_info *info)
+static arch_addr_t
+arm_branch_dest(const arch_addr_t pc, const uint32_t insn)
-+{
+ {
+- proc_archdep *a = (proc_archdep *) proc->arch_ptr;
+ /* Bits 0-23 are signed immediate value. */
+ return pc + ((((insn & 0xffffff) ^ 0x800000) - 0x800000) << 2) + 8;
+}
-+
+
+- if (arg_num == -1) { /* return value */
+- return ptrace(PTRACE_PEEKUSER, proc->pid, off_r0, 0);
+- }
+/* Addresses for calling Thumb functions have the bit 0 set.
+ Here are some macros to test, set, or clear bit 0 of addresses. */
+/* XXX double cast */
+#define IS_THUMB_ADDR(addr) ((uintptr_t)(addr) & 1)
+#define MAKE_THUMB_ADDR(addr) ((arch_addr_t)((uintptr_t)(addr) | 1))
+#define UNMAKE_THUMB_ADDR(addr) ((arch_addr_t)((uintptr_t)(addr) & ~1))
-+
+
+- /* deal with the ARM calling conventions */
+- if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) {
+- if (arg_num < 4) {
+- if (a->valid && type == LT_TOF_FUNCTION)
+- return a->regs.uregs[arg_num];
+- if (a->valid && type == LT_TOF_FUNCTIONR)
+- return a->func_arg[arg_num];
+- return ptrace(PTRACE_PEEKUSER, proc->pid,
+- (void *)(4 * arg_num), 0);
+- } else {
+- return ptrace(PTRACE_PEEKDATA, proc->pid,
+- proc->stack_pointer + 4 * (arg_num - 4),
+- 0);
+enum {
+ COND_ALWAYS = 0xe,
+ COND_NV = 0xf,
@@ -810,7 +1654,19 @@ index fbbf676..8b0734e 100644
+ | (((this_instr >> 24) & 0x1) << 1));
+ next_pcs[nr++] = MAKE_THUMB_ADDR(addr);
+ break;
-+ }
+ }
+- } else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) {
+- if (arg_num < 5) {
+- if (a->valid && type == LT_TOF_SYSCALL)
+- return a->regs.uregs[arg_num];
+- if (a->valid && type == LT_TOF_SYSCALLR)
+- return a->sysc_arg[arg_num];
+- return ptrace(PTRACE_PEEKUSER, proc->pid,
+- (void *)(4 * arg_num), 0);
+- } else {
+- return ptrace(PTRACE_PEEKDATA, proc->pid,
+- proc->stack_pointer + 4 * (arg_num - 5),
+- 0);
+ else
+ switch (opcode) {
+ uint32_t operand1, operand2, result = 0;
@@ -1268,15 +2124,18 @@ index fbbf676..8b0734e 100644
+ return -1;
+
+ next_pcs[nr++] = pc + 2 * length;
-+ }
-+ }
-+
+ }
+- } else {
+- fprintf(stderr, "gimme_arg called with wrong arguments\n");
+- exit(1);
+ }
+
+
+ /* Otherwise take the next instruction. */
+ if (nr == 0)
+ next_pcs[nr++] = pc + thumb_insn_size(inst1);
-+ return 0;
-+}
+ return 0;
+ }
+
+enum sw_singlestep_status
+arch_sw_singlestep(struct process *proc, struct breakpoint *sbp,
@@ -1308,6 +2167,140 @@ index fbbf676..8b0734e 100644
+ ptrace(PTRACE_CONT, proc->pid, 0, 0);
+ return SWS_OK;
+}
++
++size_t
++arch_type_sizeof(struct process *proc, struct arg_type_info *info)
++{
++ if (proc == NULL)
++ return (size_t)-2;
++
++ switch (info->type) {
++ case ARGTYPE_VOID:
++ return 0;
++
++ case ARGTYPE_CHAR:
++ return 1;
++
++ case ARGTYPE_SHORT:
++ case ARGTYPE_USHORT:
++ return 2;
++
++ case ARGTYPE_INT:
++ case ARGTYPE_UINT:
++ case ARGTYPE_LONG:
++ case ARGTYPE_ULONG:
++ case ARGTYPE_POINTER:
++ return 4;
++
++ case ARGTYPE_FLOAT:
++ return 4;
++ case ARGTYPE_DOUBLE:
++ return 8;
++
++ case ARGTYPE_ARRAY:
++ case ARGTYPE_STRUCT:
++ /* Use default value. */
++ return (size_t)-2;
++
++ default:
++ assert(info->type != info->type);
++ abort();
++ }
++}
++
++size_t
++arch_type_alignof(struct process *proc, struct arg_type_info *info)
++{
++ return arch_type_sizeof(proc, info);
++}
+diff --git a/sysdeps/linux-gnu/ia64/fetch.c b/sysdeps/linux-gnu/ia64/fetch.c
+index e90dbed..171c7a2 100644
+--- a/sysdeps/linux-gnu/ia64/fetch.c
++++ b/sysdeps/linux-gnu/ia64/fetch.c
+@@ -1,6 +1,6 @@
+ /*
+ * This file is part of ltrace.
+- * Copyright (C) 2012 Petr Machata, Red Hat Inc.
++ * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2008,2009 Juan Cespedes
+ * Copyright (C) 2006 Steve Fink
+ * Copyright (C) 2006 Ian Wienand
+@@ -249,37 +249,6 @@ allocate_float(struct fetch_context *ctx, struct process *proc,
+ return 0;
+ }
+
+-static enum arg_type
+-get_hfa_type(struct arg_type_info *info, size_t *countp)
+-{
+- size_t n = type_aggregate_size(info);
+- if (n == (size_t)-1)
+- return ARGTYPE_VOID;
+-
+- enum arg_type type = ARGTYPE_VOID;
+- *countp = 0;
+-
+- while (n-- > 0) {
+- struct arg_type_info *emt = type_element(info, n);
+-
+- enum arg_type emt_type = emt->type;
+- size_t emt_count = 1;
+- if (emt_type == ARGTYPE_STRUCT || emt_type == ARGTYPE_ARRAY)
+- emt_type = get_hfa_type(emt, &emt_count);
+-
+- if (type == ARGTYPE_VOID) {
+- if (emt_type != ARGTYPE_FLOAT
+- && emt_type != ARGTYPE_DOUBLE)
+- return ARGTYPE_VOID;
+- type = emt_type;
+- }
+- if (emt_type != type)
+- return ARGTYPE_VOID;
+- *countp += emt_count;
+- }
+- return type;
+-}
+-
+ static int
+ allocate_hfa(struct fetch_context *ctx, struct process *proc,
+ struct arg_type_info *info, struct value *valuep,
+@@ -380,10 +349,11 @@ allocate_ret(struct fetch_context *ctx, struct process *proc,
+ * floating-point registers, beginning with f8. */
+ if (info->type == ARGTYPE_STRUCT || info->type == ARGTYPE_ARRAY) {
+ size_t hfa_size;
+- enum arg_type hfa_type = get_hfa_type(info, &hfa_size);
+- if (hfa_type != ARGTYPE_VOID && hfa_size <= 8)
++ struct arg_type_info *hfa_info
++ = type_get_hfa_type(info, &hfa_size);
++ if (hfa_info != NULL && hfa_size <= 8)
+ return allocate_hfa(ctx, proc, info, valuep,
+- hfa_type, hfa_size);
++ hfa_info->type, hfa_size);
+ }
+
+ /* Integers and pointers are passed in r8. 128-bit integers
+@@ -409,7 +379,7 @@ arch_fetch_arg_next(struct fetch_context *ctx, enum tof type,
+ struct arg_type_info *info, struct value *valuep)
+ {
+ switch (info->type) {
+- enum arg_type hfa_type;
++ struct arg_type_info *hfa_info;
+ size_t hfa_size;
+
+ case ARGTYPE_VOID:
+@@ -421,10 +391,10 @@ arch_fetch_arg_next(struct fetch_context *ctx, enum tof type,
+ return allocate_float(ctx, proc, info, valuep, 1);
+
+ case ARGTYPE_STRUCT:
+- hfa_type = get_hfa_type(info, &hfa_size);
+- if (hfa_type != ARGTYPE_VOID)
++ hfa_info = type_get_hfa_type(info, &hfa_size);
++ if (hfa_info != NULL)
+ return allocate_hfa(ctx, proc, info, valuep,
+- hfa_type, hfa_size);
++ hfa_info->type, hfa_size);
+ /* Fall through. */
+ case ARGTYPE_CHAR:
+ case ARGTYPE_SHORT:
diff --git a/sysdeps/linux-gnu/ia64/regs.c b/sysdeps/linux-gnu/ia64/regs.c
index fb79e8a..67873ce 100644
--- a/sysdeps/linux-gnu/ia64/regs.c
@@ -1372,6 +2365,32 @@ index 19f97cb..d6a7a50 100644
-{
- ptrace(PTRACE_POKEUSER, proc->pid, off_lr, addr);
-}
+diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c
+index 439b8e8..fe1602a 100644
+--- a/sysdeps/linux-gnu/ppc/plt.c
++++ b/sysdeps/linux-gnu/ppc/plt.c
+@@ -262,7 +262,8 @@ load_opd_data(struct ltelf *lte, struct library *lib)
+ {
+ Elf_Scn *sec;
+ GElf_Shdr shdr;
+- if (elf_get_section_named(lte, ".opd", &sec, &shdr) < 0) {
++ if (elf_get_section_named(lte, ".opd", &sec, &shdr) < 0
++ || sec == NULL) {
+ fail:
+ fprintf(stderr, "couldn't find .opd data\n");
+ return -1;
+@@ -290,8 +291,9 @@ get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot, Elf_Data *plt_data)
+ Elf_Scn *ppcgot_sec = NULL;
+ GElf_Shdr ppcgot_shdr;
+ if (ppcgot != 0
+- && elf_get_section_covering(lte, ppcgot,
+- &ppcgot_sec, &ppcgot_shdr) < 0)
++ && (elf_get_section_covering(lte, ppcgot,
++ &ppcgot_sec, &ppcgot_shdr) < 0
++ || ppcgot_sec == NULL))
+ fprintf(stderr,
+ "DT_PPC_GOT=%#"PRIx64", but no such section found\n",
+ ppcgot);
diff --git a/sysdeps/linux-gnu/ppc/regs.c b/sysdeps/linux-gnu/ppc/regs.c
index ed9b398..40d7e7a 100644
--- a/sysdeps/linux-gnu/ppc/regs.c
@@ -1609,6 +2628,36 @@ index 3886e84..0a42c6e 100644
- addr = (void *)((long int)addr & 0xffffffff);
- ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, addr);
-}
+diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp
+index e54086f..b585bc9 100644
+--- a/testsuite/ltrace.main/parameters.exp
++++ b/testsuite/ltrace.main/parameters.exp
+@@ -35,9 +35,6 @@ if [regexp {ELF from incompatible architecture} $exec_output] {
+ return
+ }
+
+-set xfail_spec {"arm*-*" }
+-set xfail_spec_arm {"arm*-*"}
+-
+ # Verify the output
+ set pattern "func_intptr(17)"
+ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+@@ -63,7 +60,6 @@ set pattern "func_ushort(33, 34)"
+ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+ set pattern "func_float(3.40*, -3.40*).*= 3.40*"
+ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+-eval "setup_xfail $xfail_spec"
+ set pattern "func_double(3.40*, -3.40*).*= -3.40*"
+ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+ set pattern "func_typedef(BLUE)"
+@@ -86,7 +82,6 @@ set pattern "func_work(\\\"x\\\")"
+ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+ set pattern "func_struct_2(17, { \\\"ABCDE\\\\\\\\0\\\", 0.250* }, 0.50*).*= { 0.250*, 'B', 'C' }"
+ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+-eval "setup_xfail $xfail_spec_arm"
+ set pattern "<... func_call resumed> \\\"x\\\", \\\"y\\\")"
+ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
diff --git a/testsuite/ltrace.torture/Makefile.am b/testsuite/ltrace.torture/Makefile.am
index daa772f..5a45265 100644
--- a/testsuite/ltrace.torture/Makefile.am
@@ -1682,3 +2731,81 @@ index 0000000..0d633d9
+}
+
+ltraceDone
+diff --git a/type.c b/type.c
+index d80550b..e06a9c2 100644
+--- a/type.c
++++ b/type.c
+@@ -1,6 +1,6 @@
+ /*
+ * This file is part of ltrace.
+- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
++ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2007,2008 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+@@ -568,3 +568,39 @@ type_get_fp_equivalent(struct arg_type_info *info)
+ }
+ abort();
+ }
++
++struct arg_type_info *
++type_get_hfa_type(struct arg_type_info *info, size_t *countp)
++{
++ assert(info != NULL);
++ if (info->type != ARGTYPE_STRUCT
++ && info->type != ARGTYPE_ARRAY)
++ return NULL;
++
++ size_t n = type_aggregate_size(info);
++ if (n == (size_t)-1)
++ return NULL;
++
++ struct arg_type_info *ret = NULL;
++ *countp = 0;
++
++ while (n-- > 0) {
++ struct arg_type_info *emt = type_element(info, n);
++
++ size_t emt_count = 1;
++ if (emt->type == ARGTYPE_STRUCT || emt->type == ARGTYPE_ARRAY)
++ emt = type_get_hfa_type(emt, &emt_count);
++ if (emt == NULL)
++ return NULL;
++ if (ret == NULL) {
++ if (emt->type != ARGTYPE_FLOAT
++ && emt->type != ARGTYPE_DOUBLE)
++ return NULL;
++ ret = emt;
++ }
++ if (emt->type != ret->type)
++ return NULL;
++ *countp += emt_count;
++ }
++ return ret;
++}
+diff --git a/type.h b/type.h
+index b92c1af..3210677 100644
+--- a/type.h
++++ b/type.h
+@@ -1,6 +1,6 @@
+ /*
+ * This file is part of ltrace.
+- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
++ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc.
+ * Copyright (C) 1997-2009 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+@@ -142,4 +142,13 @@ int type_is_signed(enum arg_type type);
+ * type. */
+ struct arg_type_info *type_get_fp_equivalent(struct arg_type_info *info);
+
++/* If INFO is homogeneous floating-point aggregate, return the
++ * corresponding floating point type, and set *COUNTP to number of
++ * fields of the structure. Otherwise return NULL. INFO is a HFA if
++ * it's an aggregate whose each field is either a HFA, or a
++ * floating-point type. */
++struct arg_type_info *type_get_hfa_type(struct arg_type_info *info,
++ size_t *countp);
++
++
+ #endif /* TYPE_H */
diff --git a/ltrace.spec b/ltrace.spec
index adbba90..f6a6795 100644
--- a/ltrace.spec
+++ b/ltrace.spec
@@ -1,7 +1,7 @@
Summary: Tracks runtime library calls from dynamically linked executables
Name: ltrace
Version: 0.7.2
-Release: 3%{?dist}
+Release: 4%{?dist}
URL: http://ltrace.alioth.debian.org/
License: GPLv2+
Group: Development/Debuggers
@@ -18,9 +18,7 @@ Source: http://alioth.debian.org/frs/download.php/3848/ltrace-0.7.2.tar.bz2
# Many small fixes brought from master branch.
Patch0: ltrace-0.7.2-bits.patch
-# Upstream patch which introduces singlestepping support on ARM. This
-# makes ltrace essentially work on ARM, except for parameter passing
-# conventions.
+# Upstream patch which introduces support for ARM architecture.
Patch1: ltrace-0.7.2-arm.patch
# Work around a recently-added GCC warning.
@@ -66,6 +64,10 @@ echo ====================TESTING END=====================
%config(noreplace) %{_sysconfdir}/ltrace.conf
%changelog
+* Wed Feb 6 2013 Petr Machata <pmachata at redhat.com> - 0.7.2-4
+- Update the ARM patch (ltrace-0.7.2-arm.patch) with support for
+ parameter passing conventions.
+
* Thu Jan 31 2013 Petr Machata <pmachata at redhat.com> - 0.7.2-3
- Bring small fixes from master branch
(ltrace-0.7.2-bits.patch; drop ltrace-0.7.2-man.patch)
More information about the scm-commits
mailing list