[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, &reg) < 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, &reg) < 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, &reg) < 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