[ltrace] Sync with upstream (ia64 ABI, -l, bug fixes)

Petr Machata pmachata at fedoraproject.org
Mon Oct 1 14:12:32 UTC 2012


commit f09484d326d128b7a93fbad6227239dec8f8cfa7
Author: Petr Machata <pmachata at redhat.com>
Date:   Mon Oct 1 16:12:01 2012 +0200

    Sync with upstream (ia64 ABI, -l, bug fixes)

 ltrace-0.6.0-abi-ia64.patch       | 1539 +++++++++++++++++++++++++++++++++++++
 ltrace-0.6.0-cleanups.patch       |  652 ++++++++++++++++
 ltrace-0.6.0-dash-l.patch         | 1293 +++++++++++++++++++++++++++++++
 ltrace-0.6.0-syscall-time.patch   |   30 +
 ltrace-0.6.0-x86_64-flatten.patch |   88 +++
 ltrace.spec                       |   24 +-
 6 files changed, 3625 insertions(+), 1 deletions(-)
---
diff --git a/ltrace-0.6.0-abi-ia64.patch b/ltrace-0.6.0-abi-ia64.patch
new file mode 100644
index 0000000..f1b6423
--- /dev/null
+++ b/ltrace-0.6.0-abi-ia64.patch
@@ -0,0 +1,1539 @@
+diff --git a/backend.h b/backend.h
+index 29688ea..bae53bd 100644
+--- a/backend.h
++++ b/backend.h
+@@ -180,21 +180,17 @@ size_t umovebytes (struct Process *proc, void *addr, void *buf, size_t count);
+  * XXX the same points as for get_instruction_pointer apply. */
+ void *sym2addr(struct Process *proc, struct library_symbol *sym);
+ 
++/* Obtain address of PLT entry corresponding to relocation RELA in
++ * file LTE.  This is NDX-th PLT entry in the file.
++ *
++ * XXX should this return arch_addr_t?  */
++GElf_Addr arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela);
++
+ /* Called at some point after we have attached to PROC.  This callback
+  * should insert an introspection breakpoint for handling dynamic
+  * linker library loads.  */
+ int linkmap_init(struct Process *proc, arch_addr_t dyn_addr);
+ 
+-/* Called for breakpoints defined over an artificial symbol "".  This
+- * can be used (like it is on Linux/GNU) to add more breakpoints
+- * because a dlopen'ed library was mapped in.
+- *
+- * XXX we should somehow clean up this interface.  For starters,
+- * breakpoints should have their own handler callbacks, so that we can
+- * generalize this to e.g. systemtap SDT probes.  linkmap_init could
+- * perhaps be rolled into some other process init callback.  */
+-void arch_check_dbg(struct Process *proc);
+-
+ /* This should produce and return the next event of one of the traced
+  * processes.  The returned pointer will not be freed by the core and
+  * should be either statically allocated, or the management should be
+@@ -204,47 +200,107 @@ struct Event *next_event(void);
+ /* Called when process PROC was removed.  */
+ void process_removed(struct Process *proc);
+ 
++/* This should extract entry point address and interpreter (dynamic
++ * linker) bias if possible.  Returns 0 if there were no errors, -1
++ * otherwise.  Sets *ENTRYP and *INTERP_BIASP to non-zero values if
++ * the corresponding value is known.  Unknown values are set to 0.
++ *
++ * XXX This is not currently used, but it will be necessary for proper
++ * support of PIE binaries.  */
++int process_get_entry(struct Process *proc,
++		      arch_addr_t *entryp,
++		      arch_addr_t *interp_biasp);
++
++
++/* Optional callbacks
++ *
++ * Some callbacks are only available if backend (arch.h) has a certain
++ * define.  If such a define is not present, default implementation
++ * (most often doing nothing at all) us used instead.  This is used
++ * for gradual extensions of ltrace, so that backends that are not
++ * fully up to date, or that don't need certain functionality, keep
++ * working, while other backends take advantage of the optional
++ * features.  */
++
++/* The following callbacks have to be implemented in backend if arch.h
++ * defines ARCH_HAVE_LTELF_DATA.  Those are used to init and destroy
++ * LTE->arch.  arch_elf_init returns 0 on success or a negative value
++ * on failure.  */
+ int arch_elf_init(struct ltelf *lte, struct library *lib);
+ void arch_elf_destroy(struct ltelf *lte);
+ 
+-enum plt_status {
+-	plt_fail,
+-	plt_ok,
+-	plt_default,
+-};
+-
+-enum plt_status arch_elf_add_plt_entry(struct Process *p, struct ltelf *l,
+-				       const char *n, GElf_Rela *r, size_t i,
+-				       struct library_symbol **ret);
+-
++/* The following callbacks have to be implemented in backend if arch.h
++ * defines ARCH_HAVE_BREAKPOINT_DATA.  Those are used to init,
++ * destroy, and clone SBP->arch.  arch_breakpoint_init and
++ * arch_breakpoint_clone return 0 on success or a negative value on
++ * failure.  */
+ int arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp);
+ void arch_breakpoint_destroy(struct breakpoint *sbp);
+ int arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp);
+ 
++/* The following callbacks have to be implemented in backend if arch.h
++ * defines ARCH_HAVE_LIBRARY_DATA.  Those are used to init, destroy
++ * and clone LIB->arch.  */
+ void arch_library_init(struct library *lib);
+ void arch_library_destroy(struct library *lib);
+ void arch_library_clone(struct library *retp, struct library *lib);
+ 
++/* The following callbacks have to be implemented in backend if arch.h
++ * defines ARCH_HAVE_LIBRARY_SYMBOL_DATA.  Those are used to init,
++ * destroy and clone LIBSYM->arch.  arch_library_symbol_init and
++ * arch_library_symbol_clone return 0 on success or a negative value
++ * on failure.  */
+ int arch_library_symbol_init(struct library_symbol *libsym);
+ void arch_library_symbol_destroy(struct library_symbol *libsym);
+ int arch_library_symbol_clone(struct library_symbol *retp,
+ 			      struct library_symbol *libsym);
+ 
++/* The following callbacks have to be implemented in backend if arch.h
++ * defines ARCH_HAVE_PROCESS_DATA.  Those are used to init, destroy
++ * and clone PROC->arch.  arch_process_exec is called to update
++ * PROC->arch in case that PROC underwent an exec.  See notes at
++ * process_init, process_destroy, process_clone and process_exec in
++ * proc.h.  */
+ int arch_process_init(struct Process *proc);
+ void arch_process_destroy(struct Process *proc);
+ int arch_process_clone(struct Process *retp, struct Process *proc);
+ int arch_process_exec(struct Process *proc);
+ 
+-/* This should extract entry point address and interpreter (dynamic
+- * linker) bias if possible.  Returns 0 if there were no errors, -1
+- * otherwise.  Sets *ENTRYP and *INTERP_BIASP to non-zero values if
+- * the corresponding value is known.  Unknown values are set to 0.  */
+-int process_get_entry(struct Process *proc,
+-		      arch_addr_t *entryp,
+-		      arch_addr_t *interp_biasp);
++enum plt_status {
++	plt_fail,
++	plt_ok,
++	plt_default,
++};
+ 
+-/* This is called after the dynamic linker is done with the
+- * process startup.  */
++/* The following callback has to be implemented in backend if arch.h
++ * defines ARCH_HAVE_ADD_PLT_ENTRY.
++ *
++ * This is called for every PLT relocation R in ELF file LTE, that
++ * ltrace is about to add to a library constructed in process PROC.
++ * The corresponding PLT entry is for symbol called NAME, and it's
++ * I-th relocation in the file.
++ *
++ * If this function returns plt_default, PLT address is obtained by
++ * calling arch_plt_sym_val, and symbol is allocated.  If plt_ok or
++ * plt_default are returned, the chain of symbols passed back in RET
++ * is added to library under construction.  */
++enum plt_status arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte,
++				       const char *name, GElf_Rela *rela,
++				       size_t i, struct library_symbol **ret);
++
++/* This callback needs to be implemented if arch.h defines
++ * ARCH_HAVE_DYNLINK_DONE.  It is called after the dynamic linker is
++ * done with the process startup.  */
+ void arch_dynlink_done(struct Process *proc);
+ 
++/* If arch.h defines ARCH_HAVE_FETCH_ARG, the following callbacks have
++ * to be implemented: arch_fetch_arg_init, arch_fetch_arg_clone,
++ * arch_fetch_arg_done, arch_fetch_arg_next and arch_fetch_retval.
++ * See fetch.h for details.  */
++
++/* If arch.h defines both ARCH_HAVE_FETCH_ARG and
++ * ARCH_HAVE_FETCH_PACK, the following callbacks have to be
++ * implemented: arch_fetch_param_pack_start,
++ * arch_fetch_param_pack_end.  See fetch.h for details.  */
++
+ #endif /* BACKEND_H */
+diff --git a/fetch.c b/fetch.c
+index bce949f..88966a5 100644
+--- a/fetch.c
++++ b/fetch.c
+@@ -22,8 +22,8 @@
+ #include <string.h>
+ 
+ #include "fetch.h"
++#include "sysdep.h"
+ #include "value.h"
+-#include "arch.h"
+ #include "type.h"
+ 
+ #ifdef ARCH_HAVE_FETCH_ARG
+@@ -43,6 +43,13 @@ int arch_fetch_retval(struct fetch_context *ctx, enum tof type,
+ 
+ void arch_fetch_arg_done(struct fetch_context *context);
+ 
++# ifdef ARCH_HAVE_FETCH_PACK
++int arch_fetch_param_pack_start(struct fetch_context *context,
++				enum param_pack_flavor ppflavor);
++
++void arch_fetch_param_pack_end(struct fetch_context *context);
++# endif
++
+ #else
+ /* Fall back to gimme_arg.  */
+ 
+@@ -96,6 +103,20 @@ arch_fetch_arg_done(struct fetch_context *context)
+ }
+ #endif
+ 
++#if !defined(ARCH_HAVE_FETCH_ARG) || !defined(ARCH_HAVE_FETCH_PACK)
++int
++arch_fetch_param_pack_start(struct fetch_context *context,
++			    enum param_pack_flavor ppflavor)
++{
++	return 0;
++}
++
++void
++arch_fetch_param_pack_end(struct fetch_context *context)
++{
++}
++#endif
++
+ struct fetch_context *
+ fetch_arg_init(enum tof type, struct Process *proc,
+ 	       struct arg_type_info *ret_info)
+@@ -130,3 +151,16 @@ fetch_arg_done(struct fetch_context *context)
+ {
+ 	return arch_fetch_arg_done(context);
+ }
++
++int
++fetch_param_pack_start(struct fetch_context *context,
++		       enum param_pack_flavor ppflavor)
++{
++	return arch_fetch_param_pack_start(context, ppflavor);
++}
++
++void
++fetch_param_pack_end(struct fetch_context *context)
++{
++	return arch_fetch_param_pack_end(context);
++}
+diff --git a/fetch.h b/fetch.h
+index 6a5385c..2a13214 100644
+--- a/fetch.h
++++ b/fetch.h
+@@ -22,6 +22,7 @@
+ #define FETCH_H
+ 
+ #include "forward.h"
++#include "param.h"
+ 
+ /* XXX isn't SYSCALL TOF just a different ABI?  Maybe we needed to
+  * support variant ABIs all along.  */
+@@ -61,4 +62,34 @@ int fetch_retval(struct fetch_context *context, enum tof type,
+  * that was passed to fetch_arg_next.  */
+ void fetch_arg_done(struct fetch_context *context);
+ 
++/* Called before fetching arguments that come from parameter packs.
++ * Returns 0 on success or a negative value on failure.  */
++int fetch_param_pack_start(struct fetch_context *context,
++			   enum param_pack_flavor ppflavor);
++
++/* Called after a parameter pack has been fetched.  */
++void fetch_param_pack_end(struct fetch_context *context);
++
++
++/* The following callbacks have to be implemented in backend if arch.h
++ * defines ARCH_HAVE_FETCH_ARG.  These backend callbacks correspond to
++ * above functions.  */
++struct fetch_context *arch_fetch_arg_init(enum tof type, struct Process *proc,
++					  struct arg_type_info *ret_info);
++struct fetch_context *arch_fetch_arg_clone(struct Process *proc,
++					   struct fetch_context *context);
++int arch_fetch_arg_next(struct fetch_context *ctx, enum tof type,
++			struct Process *proc, struct arg_type_info *info,
++			struct value *valuep);
++int arch_fetch_retval(struct fetch_context *ctx, enum tof type,
++		      struct Process *proc, struct arg_type_info *info,
++		      struct value *valuep);
++void arch_fetch_arg_done(struct fetch_context *context);
++
++/* The following callbacks have to be implemented in backend if arch.h
++ * defines ARCH_HAVE_FETCH_ARG and ARCH_HAVE_FETCH_PACK.  */
++int arch_fetch_param_pack_start(struct fetch_context *context,
++				enum param_pack_flavor ppflavor);
++void arch_fetch_param_pack_end(struct fetch_context *context);
++
+ #endif /* FETCH_H */
+diff --git a/lens_enum.c b/lens_enum.c
+index 1af94d2..52edd73 100644
+--- a/lens_enum.c
++++ b/lens_enum.c
+@@ -1,6 +1,6 @@
+ /*
+  * This file is part of ltrace.
+- * Copyright (C) 2011 Petr Machata, Red Hat Inc.
++ * Copyright (C) 2011, 2012 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
+@@ -22,10 +22,10 @@
+ #include <assert.h>
+ #include <string.h>
+ 
+-#include "arch.h"
+ #include "lens_enum.h"
+ #include "lens_default.h"
+ #include "value.h"
++#include "sysdep.h"
+ #include "type.h"
+ 
+ struct enum_entry {
+diff --git a/ltrace-elf.h b/ltrace-elf.h
+index 64d1cb8..c560bb8 100644
+--- a/ltrace-elf.h
++++ b/ltrace-elf.h
+@@ -56,8 +56,6 @@ int ltelf_read_library(struct library *lib, struct Process *proc,
+  * point address is stored to *ENTRYP.  */
+ struct library *ltelf_read_main_binary(struct Process *proc, const char *path);
+ 
+-GElf_Addr arch_plt_sym_val(struct ltelf *, size_t, GElf_Rela *);
+-
+ Elf_Data *elf_loaddata(Elf_Scn *scn, GElf_Shdr *shdr);
+ int elf_get_section_covering(struct ltelf *lte, GElf_Addr addr,
+ 			     Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr);
+diff --git a/output.c b/output.c
+index b138055..8e4e616 100644
+--- a/output.c
++++ b/output.c
+@@ -350,13 +350,19 @@ fetch_one_param(enum tof type, Process *proc, struct fetch_context *context,
+ 		ssize_t *params_leftp)
+ {
+ 	switch (param->flavor) {
++		int rc;
+ 	case PARAM_FLAVOR_TYPE:
+ 		return fetch_simple_param(type, proc, context, arguments,
+ 					  param->u.type.type, NULL);
+ 
+ 	case PARAM_FLAVOR_PACK:
+-		return fetch_param_pack(type, proc, context, arguments,
+-					param, params_leftp);
++		if (fetch_param_pack_start(context,
++					   param->u.pack.ppflavor) < 0)
++			return -1;
++	        rc = fetch_param_pack(type, proc, context, arguments,
++				      param, params_leftp);
++		fetch_param_pack_end(context);
++		return rc;
+ 
+ 	case PARAM_FLAVOR_STOP:
+ 		fetch_param_stop(arguments, params_leftp);
+diff --git a/param.c b/param.c
+index 7715571..a712dad 100644
+--- a/param.c
++++ b/param.c
+@@ -41,7 +41,7 @@ param_init_stop(struct param *param)
+ }
+ 
+ void
+-param_init_pack(struct param *param,
++param_init_pack(struct param *param, enum param_pack_flavor ppflavor,
+ 		struct expr_node *args, size_t nargs, int own_args,
+ 		struct param_enum *(*init)(struct value *cb_args,
+ 					   size_t nargs,
+@@ -57,6 +57,7 @@ param_init_pack(struct param *param,
+ 	param->u.pack.args = args;
+ 	param->u.pack.nargs = nargs;
+ 	param->u.pack.own_args = own_args;
++	param->u.pack.ppflavor = ppflavor;
+ 	param->u.pack.init = init;
+ 	param->u.pack.next = next;
+ 	param->u.pack.stop = stop;
+diff --git a/param.h b/param.h
+index 5882689..d6da96b 100644
+--- a/param.h
++++ b/param.h
+@@ -46,6 +46,18 @@ enum param_flavor {
+ 	PARAM_FLAVOR_STOP,
+ };
+ 
++enum param_pack_flavor {
++	/* This parameter pack expands to a list of ordinary
++	 * arguments.  For example if the last argument is sometimes
++	 * ignored, that would be described by a PARAM_PACK_ARGS
++	 * parameter pack.  ioctl or ptrace are two examples that
++	 * would benefit from this.  */
++	PARAM_PACK_ARGS,
++
++	/* This parameter pack represents a vararg argument.  */
++	PARAM_PACK_VARARGS,
++};
++
+ enum param_status {
+ 	PPCB_ERR = -1,	/* An error occurred.  */
+ 	PPCB_STOP,	/* Stop fetching the arguments.  */
+@@ -69,6 +81,7 @@ struct param {
+ 			struct expr_node *args;
+ 			size_t nargs;
+ 			int own_args;
++			enum param_pack_flavor ppflavor;
+ 
+ 			struct param_enum *(*init)(struct value *cb_args,
+ 						   size_t nargs,
+@@ -112,7 +125,7 @@ void param_init_stop(struct param *param);
+  * if not, whether this argument should be displayed.
+  *
+  * After the enumeration is ended, DONE callback is called.  */
+-void param_init_pack(struct param *param,
++void param_init_pack(struct param *param, enum param_pack_flavor ppflavor,
+ 		     struct expr_node *args, size_t nargs, int own_args,
+ 		     struct param_enum *(*init)(struct value *cb_args,
+ 						size_t nargs,
+diff --git a/printf.c b/printf.c
+index 1fe3025..9a2b4a3 100644
+--- a/printf.c
++++ b/printf.c
+@@ -345,7 +345,7 @@ param_printf_done(struct param_enum *context)
+ void
+ param_pack_init_printf(struct param *param, struct expr_node *arg, int own_arg)
+ {
+-	param_init_pack(param, arg, 1, own_arg,
++	param_init_pack(param, PARAM_PACK_VARARGS, arg, 1, own_arg,
+ 			&param_printf_init, &param_printf_next,
+ 			&param_printf_stop, &param_printf_done);
+ }
+diff --git a/sysdeps/linux-gnu/breakpoint.c b/sysdeps/linux-gnu/breakpoint.c
+index 40a8436..fe336b1 100644
+--- a/sysdeps/linux-gnu/breakpoint.c
++++ b/sysdeps/linux-gnu/breakpoint.c
+@@ -29,7 +29,6 @@
+ 
+ #include "common.h"
+ #include "backend.h"
+-#include "arch.h"
+ #include "sysdep.h"
+ #include "breakpoint.h"
+ #include "proc.h"
+diff --git a/sysdeps/linux-gnu/ia64/Makefile.am b/sysdeps/linux-gnu/ia64/Makefile.am
+index 0fc9ed2..429d013 100644
+--- a/sysdeps/linux-gnu/ia64/Makefile.am
++++ b/sysdeps/linux-gnu/ia64/Makefile.am
+@@ -5,7 +5,8 @@ ___libcpu_la_SOURCES = \
+ 	breakpoint.c \
+ 	plt.c \
+ 	regs.c \
+-	trace.c
++	trace.c \
++	fetch.c
+ 
+ noinst_HEADERS = \
+ 	arch.h \
+diff --git a/sysdeps/linux-gnu/ia64/arch.h b/sysdeps/linux-gnu/ia64/arch.h
+index 00bb077..71a53af 100644
+--- a/sysdeps/linux-gnu/ia64/arch.h
++++ b/sysdeps/linux-gnu/ia64/arch.h
+@@ -21,6 +21,8 @@
+ 
+ #define ARCH_HAVE_DISABLE_BREAKPOINT 1
+ #define ARCH_HAVE_ENABLE_BREAKPOINT 1
++#define ARCH_HAVE_FETCH_ARG
++#define ARCH_HAVE_FETCH_PACK
+ 
+ #define BREAKPOINT_LENGTH 16
+ #define BREAKPOINT_VALUE {0}
+diff --git a/sysdeps/linux-gnu/ia64/fetch.c b/sysdeps/linux-gnu/ia64/fetch.c
+new file mode 100644
+index 0000000..2163801
+--- /dev/null
++++ b/sysdeps/linux-gnu/ia64/fetch.c
+@@ -0,0 +1,503 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2012 Petr Machata, Red Hat Inc.
++ * Copyright (C) 2008,2009 Juan Cespedes
++ * Copyright (C) 2006 Steve Fink
++ * Copyright (C) 2006 Ian Wienand
++ *
++ * 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 <stdlib.h>
++#include <assert.h>
++#include <sys/rse.h>
++#include <ptrace.h>
++#include <string.h>
++#include <errno.h>
++
++#include "backend.h"
++#include "fetch.h"
++#include "type.h"
++#include "proc.h"
++#include "value.h"
++
++struct fetch_context {
++	arch_addr_t stack_pointer;
++	struct pt_all_user_regs regs;
++	enum param_pack_flavor ppflavor;
++
++	/* Return values larger than 256 bits (except HFAs of up to 8
++	 * elements) are returned in a buffer allocated by the
++	 * caller. A pointer to the buffer is passed to the called
++	 * procedure in r8. This register is not guaranteed to be
++	 * preserved by the called procedure.  */
++	unsigned long r8;
++
++	int slot_n;
++	int flt;
++};
++
++union cfm_t {
++	struct {
++		unsigned long sof:7;
++		unsigned long sol:7;
++		unsigned long sor:4;
++		unsigned long rrb_gr:7;
++		unsigned long rrb_fr:7;
++		unsigned long rrb_pr:6;
++	} cfm;
++	unsigned long value;
++};
++
++static int
++fetch_context_init(struct Process *proc, struct fetch_context *context)
++{
++	context->slot_n = 0;
++	context->flt = 8;
++	if (ptrace(PTRACE_GETREGS, proc->pid, 0, &context->regs) < 0)
++		return -1;
++	context->stack_pointer = (void *)(context->regs.gr[12] + 16);
++	context->ppflavor = PARAM_PACK_ARGS;
++
++	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));
++	if (context == NULL
++	    || fetch_context_init(proc, context) < 0) {
++		free(context);
++		return NULL;
++	}
++	context->r8 = context->regs.gr[8];
++
++	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;
++}
++
++int
++allocate_stack_slot(struct fetch_context *ctx, struct Process *proc,
++		    struct arg_type_info *info, struct value *valuep)
++{
++	size_t al = type_alignof(proc, info);
++	size_t sz = type_sizeof(proc, info);
++	if (al == (size_t)-1 || sz == (size_t)-1)
++		return -1;
++
++	errno = 0;
++	long value = ptrace(PTRACE_PEEKDATA, proc->pid, ctx->stack_pointer, 0);
++	if (value == -1 && errno != 0)
++		return -1;
++	ctx->stack_pointer += 8;
++	value_set_word(valuep, value);
++
++	return 0;
++}
++
++static int
++allocate_reg(struct fetch_context *ctx, struct Process *proc,
++	     struct arg_type_info *info, struct value *valuep)
++{
++	if (ctx->slot_n >= 8)
++		return allocate_stack_slot(ctx, proc, info, valuep);
++
++	int reg_num = ctx->slot_n++;
++	if (ctx->slot_n == 8)
++		ctx->flt = 16;
++	if (valuep == NULL)
++		return 0;
++
++	/* This would normally be brought over from asm/ptrace.h, but
++	 * when we do, we get namespace conflicts between asm/fpu.h
++	 * and libunwind.  */
++	enum { PT_AUR_BSP = 17 };
++
++	union cfm_t cfm = { .value = ctx->regs.cfm };
++	unsigned long *bsp = (unsigned long *)ctx->regs.ar[PT_AUR_BSP];
++	unsigned long idx = -cfm.cfm.sof + reg_num;
++	unsigned long *ptr = ia64_rse_skip_regs(bsp, idx);
++	errno = 0;
++	long ret = ptrace(PTRACE_PEEKDATA, proc->pid, ptr, 0);
++	if (ret == -1 && errno != 0)
++		return -1;
++
++	value_set_word(valuep, ret);
++	return 0;
++}
++
++static int
++copy_aggregate_part(struct fetch_context *ctx, struct Process *proc,
++		    unsigned char *buf, size_t size)
++{
++	size_t slots = (size + 7) / 8;
++	struct arg_type_info *long_info = type_get_simple(ARGTYPE_LONG);
++	while (slots-- > 0) {
++		size_t chunk_sz = size > 8 ? 8 : size;
++		size -= 8;
++
++		struct value tmp;
++		value_init(&tmp, proc, NULL, long_info, 0);
++		int rc = allocate_reg(ctx, proc, long_info, &tmp);
++		if (rc >= 0) {
++			memcpy(buf, value_get_data(&tmp, NULL), chunk_sz);
++			buf += 8;
++		}
++		value_destroy(&tmp);
++		if (rc < 0)
++			return -1;
++	}
++	return 0;
++}
++
++static int
++allocate_arg(struct fetch_context *ctx, struct Process *proc,
++	     struct arg_type_info *info, struct value *valuep)
++{
++	size_t sz = type_sizeof(proc, info);
++	size_t align = type_alignof(proc, info);
++	if (sz == (size_t)-1 || align == (size_t)-1)
++		return -1;
++
++	unsigned char *buf = value_reserve(valuep, sz);
++	if (buf == NULL)
++		return -1;
++
++	assert(align == 0 || align == 1 || align == 2 || align == 4
++	       || align == 8 || align == 16);
++
++	/* For aggregates with an external alignment of 16 bytes, the
++	 * Next Even policy is used.  128-bit integers use the Next
++	 * Even policy as well.  */
++	if (align == 16 && ctx->slot_n % 2 != 0)
++		allocate_reg(ctx, proc, info, NULL);
++
++	int rc= copy_aggregate_part(ctx, proc, buf, sz);
++
++	return rc;
++}
++
++/* Stolen from David Mosberger's utrace tool, which he released under
++   the GPL
++   (http://www.gelato.unsw.edu.au/archives/linux-ia64/0104/1405.html) */
++static inline double
++fpreg_to_double (struct ia64_fpreg *fp) {
++	double result;
++	asm ("ldf.fill %0=%1" : "=f"(result) : "m"(*fp));
++	return result;
++}
++
++static int
++allocate_float(struct fetch_context *ctx, struct Process *proc,
++	       struct arg_type_info *info, struct value *valuep,
++	       int take_slot)
++{
++	/* The actual parameter is passed in the next available
++	 * floating-point parameter register, if one is
++	 * available. Floating-point parameter registers are allocated
++	 * as needed from the range f8-f15, starting with f8.  */
++	/* Any register parameters corresponding to a
++	 * variable-argument specification are passed in GRs.  */
++	if (ctx->flt > 15 || ctx->ppflavor == PARAM_PACK_VARARGS)
++		/* If all available floating-point parameter registers
++		 * have been used, the actual parameter is passed in
++		 * the appropriate general register(s).  */
++		return allocate_reg(ctx, proc, info, valuep);
++
++	union {
++		double d;
++		float f;
++		char buf[0];
++	} u = { .d = fpreg_to_double(&ctx->regs.fr[ctx->flt++]) };
++	if (take_slot)
++		allocate_reg(ctx, proc, info, NULL);
++
++	if (info->type == ARGTYPE_FLOAT)
++		u.f = u.d;
++	else
++		assert(info->type == ARGTYPE_DOUBLE);
++
++	if (value_reserve(valuep, sizeof(u)) == NULL)
++		return -1;
++	memmove(value_get_raw_data(valuep), u.buf, sizeof(u));
++
++	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,
++	     enum arg_type hfa_type, size_t hfa_count)
++{
++	size_t sz = type_sizeof(proc, info);
++	if (sz == (size_t)-1)
++		return -1;
++
++	/* 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, until the eight
++	 * argument registers are exhausted. The remaining elements of
++	 * the aggregate are passed in output GRs, according to the
++	 * normal conventions.
++	 *
++	 * Because HFAs are mapped to parameter slots as aggregates,
++	 * single-precision HFAs will be allocated with two
++	 * floating-point values in each parameter slot, but only one
++	 * value per register.
++	 *
++	 * It is possible for the first of two values in a parameter
++	 * slot to occupy the last available floating- point parameter
++	 * register. In this case, the second value is passed in its
++	 * designated GR, but the half of the GR that would have
++	 * contained the first value is undefined.  */
++
++	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);
++
++	/* Pass in register the part that we can.  */
++	while (ctx->flt <= 15 && hfa_count > 0) {
++		struct value tmp;
++		value_init(&tmp, proc, NULL, hfa_info, 0);
++		int rc = allocate_float(ctx, proc, hfa_info, &tmp, 0);
++		if (rc >= 0) {
++			memcpy(buf, value_get_data(&tmp, NULL), hfa_sz);
++			slot_off += hfa_sz;
++			buf += hfa_sz;
++			hfa_count--;
++
++			/* Scratch each fully used slot.  */
++			while (slot_off >= 8) {
++				if (allocate_reg(ctx, proc, info, NULL) < 0)
++					rc = -1;
++				slot_off -= 8;
++			}
++		}
++		value_destroy(&tmp);
++		if (rc < 0)
++			return -1;
++	}
++
++	/* If we have half-slot opened (the case where odd
++	 * ARGTYPE_FLOAT member fits into the last floating point
++	 * register, and the following even member does not), finish
++	 * it first.  */
++	struct arg_type_info *long_info = type_get_simple(ARGTYPE_LONG);
++	if (slot_off != 0 && hfa_count > 0) {
++		struct value tmp;
++		value_init(&tmp, proc, NULL, long_info, 0);
++		int rc = allocate_reg(ctx, proc, long_info, &tmp);
++		if (rc >= 0) {
++			unsigned char *data = value_get_data(&tmp, NULL);
++			memcpy(buf, data, 8 - slot_off);
++			buf += 8 - slot_off;
++			hfa_count--;
++		}
++		value_destroy(&tmp);
++		if (rc < 0) {
++			return -1;
++		}
++	}
++
++	/* The rest is passed in registers and on stack.  */
++	size_t rest = hfa_count * hfa_sz;
++	return copy_aggregate_part(ctx, proc, buf, rest);
++}
++
++static int
++allocate_ret(struct fetch_context *ctx, struct Process *proc,
++	     struct arg_type_info *info, struct value *valuep)
++{
++	size_t sz = type_sizeof(proc, info);
++	if (sz == (size_t)-1)
++		return -1;
++
++	/* Homogeneous floating-point aggregates [...] are returned in
++	 * floating-point registers, provided the array or structure
++	 * contains no more than eight individual values.  The
++	 * elements of the aggregate are placed in successive
++	 * 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)
++			return allocate_hfa(ctx, proc, info, valuep,
++					    hfa_type, hfa_size);
++	}
++
++	/* Integers and pointers are passed in r8.  128-bit integers
++	 * are passed in r8 and r9.  Aggregates of up to 256 bits [32
++	 * bytes] are passed in registers r8...r11.  */
++	if (sz <= 32) {
++		unsigned char *buf = value_reserve(valuep, sz);
++		if (buf == NULL)
++			return -1;
++		memcpy(buf, ctx->regs.gr + 8, sz);
++		return 0;
++	}
++
++	if (value_pass_by_reference(valuep) < 0)
++		return -1;
++	value_set_word(valuep, ctx->r8);
++	return 0;
++}
++
++int
++arch_fetch_arg_next(struct fetch_context *ctx, enum tof type,
++		    struct Process *proc,
++		    struct arg_type_info *info, struct value *valuep)
++{
++	switch (info->type) {
++		enum arg_type hfa_type;
++		size_t hfa_size;
++
++	case ARGTYPE_VOID:
++		value_set_word(valuep, 0);
++		return 0;
++
++	case ARGTYPE_FLOAT:
++	case ARGTYPE_DOUBLE:
++		return allocate_float(ctx, proc, info, valuep, 1);
++
++	case ARGTYPE_STRUCT:
++		hfa_type = get_hfa_type(info, &hfa_size);
++		if (hfa_type != ARGTYPE_VOID)
++			return allocate_hfa(ctx, proc, info, valuep,
++					    hfa_type, hfa_size);
++		/* 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:
++		return allocate_arg(ctx, proc, info, valuep);
++
++	case ARGTYPE_ARRAY:
++		/* Arrays decay into pointers.  XXX Fortran?  */
++		assert(info->type != ARGTYPE_ARRAY);
++		abort();
++	}
++	assert("unhandled type");
++	abort();
++}
++
++int
++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;
++
++	switch (info->type) {
++	case ARGTYPE_VOID:
++	case ARGTYPE_FLOAT:
++	case ARGTYPE_DOUBLE:
++		/* The rules for returning those types are the same as
++		 * for passing them in arguments.  */
++		return arch_fetch_arg_next(ctx, type, proc, info, valuep);
++
++	case ARGTYPE_CHAR:
++	case ARGTYPE_SHORT:
++	case ARGTYPE_USHORT:
++	case ARGTYPE_INT:
++	case ARGTYPE_UINT:
++	case ARGTYPE_LONG:
++	case ARGTYPE_ULONG:
++	case ARGTYPE_POINTER:
++	case ARGTYPE_STRUCT:
++		return allocate_ret(ctx, proc, info, valuep);
++
++	case ARGTYPE_ARRAY:
++		/* Arrays decay into pointers.  XXX Fortran?  */
++		assert(info->type != ARGTYPE_ARRAY);
++		abort();
++	}
++	assert("unhandled type");
++	abort();
++	return arch_fetch_arg_next(ctx, type, proc, info, valuep);
++}
++
++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)
++{
++	context->ppflavor = ppflavor;
++	return 0;
++}
++
++void
++arch_fetch_param_pack_end(struct fetch_context *context)
++{
++	context->ppflavor = PARAM_PACK_ARGS;
++}
+diff --git a/sysdeps/linux-gnu/ia64/trace.c b/sysdeps/linux-gnu/ia64/trace.c
+index d4813e6..e608275 100644
+--- a/sysdeps/linux-gnu/ia64/trace.c
++++ b/sysdeps/linux-gnu/ia64/trace.c
+@@ -140,159 +140,6 @@ syscall_p(Process *proc, int status, int *sysnum) {
+ 	return 0;
+ }
+ 
+-/* Stolen from David Mosberger's utrace tool, which he released under
+-   the GPL
+-   (http://www.gelato.unsw.edu.au/archives/linux-ia64/0104/1405.html) */
+-static inline double
+-fpreg_to_double (struct ia64_fpreg *fp) {
+-  double result;
+-
+-  asm ("ldf.fill %0=%1" : "=f"(result) : "m"(*fp));
+-  return result;
+-}
+-
+-static long
+-gimme_long_arg(enum tof type, Process *proc, int arg_num) {
+-	union cfm_t cfm;
+-	unsigned long bsp;
+-
+-	bsp = ptrace(PTRACE_PEEKUSER, proc->pid, PT_AR_BSP, 0);
+-	cfm.value = ptrace(PTRACE_PEEKUSER, proc->pid, PT_CFM, 0);
+-
+-	if (arg_num == -1)	/* return value */
+-		return ptrace(PTRACE_PEEKUSER, proc->pid, PT_R8, 0);
+-
+-	/* First 8 arguments are passed in registers on the register
+-	 * stack, the following arguments are passed on the stack
+-	 * after a 16 byte scratch area
+-	 *
+-	 * If the function has returned, the ia64 register window has
+-	 * been reverted to the caller's configuration. So although in
+-	 * the callee, the first parameter is in R32, in the caller
+-	 * the first parameter comes in the registers after the local
+-	 * registers (really, input parameters plus locals, but the
+-	 * hardware doesn't track the distinction.) So we have to add
+-	 * in the size of the local area (sol) to find the first
+-	 * parameter passed to the callee. */
+-	if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) {
+-		if (arg_num < 8) {
+-                        if (type == LT_TOF_FUNCTIONR)
+-				arg_num += cfm.cfm.sol;
+-
+-			return ptrace(PTRACE_PEEKDATA, proc->pid,
+-				      (long)ia64_rse_skip_regs((unsigned long *)bsp,
+-							       -cfm.cfm.sof + arg_num),
+-				      0);
+-		} else {
+-			unsigned long sp =
+-			    ptrace(PTRACE_PEEKUSER, proc->pid, PT_R12, 0) + 16;
+-			return ptrace(PTRACE_PEEKDATA, proc->pid,
+-				      sp + (8 * (arg_num - 8)));
+-		}
+-	}
+-
+-	if (type == LT_TOF_SYSCALL || LT_TOF_SYSCALLR)
+-		return ptrace(PTRACE_PEEKDATA, proc->pid,
+-			      (long)ia64_rse_skip_regs((unsigned long *)bsp, arg_num),
+-			      0);
+-
+-	/* error if we get here */
+-	fprintf(stderr, "gimme_arg called with wrong arguments\n");
+-	exit(1);
+-}
+-
+-static long float_regs[8] = { PT_F8, PT_F9, PT_F10, PT_F11,
+-			      PT_F12, PT_F13, PT_F14, PT_F15 };
+-static double
+-gimme_float_arg(enum tof type, Process *proc, int arg_num) {
+-	union cfm_t cfm;
+-	unsigned long bsp;
+-	struct ia64_fpreg reg;
+-
+-	if (arg_num == -1) {	/* return value */
+-		reg.u.bits[0] = ptrace(PTRACE_PEEKUSER, proc->pid,
+-				       PT_F8, 0);
+-		reg.u.bits[1] = ptrace(PTRACE_PEEKUSER, proc->pid,
+-				       PT_F8 + 0x8, 0);
+-		return fpreg_to_double(&reg);
+-	}
+-
+-	bsp = ptrace(PTRACE_PEEKUSER, proc->pid, PT_AR_BSP, 0);
+-	cfm.value = ptrace(PTRACE_PEEKUSER, proc->pid, PT_CFM, 0);
+-
+-	/* The first 8 arguments are passed in regular registers
+-	 * (counting from R32), unless they are floating point values
+-	 * (the case in question here). In that case, up to the first
+-	 * 8 regular registers are still "allocated" for each of the
+-	 * first 8 parameters, but if a parameter is floating point,
+-	 * then the register is left unset and the parameter is passed
+-	 * in the first available floating-point register, counting
+-	 * from F8.
+-	 *
+-	 * Take func(int a, float f, int b, double d), for example.
+-	 *    a - passed in R32
+-	 *    f - R33 left unset, value passed in F8
+-	 *    b - passed in R34
+-	 *    d - R35 left unset, value passed in F9
+-	 *
+-	 * ltrace handles this by counting floating point arguments
+-	 * while parsing declarations. The "arg_num" in this routine
+-	 * (which is only called for floating point values) really
+-	 * means which floating point parameter we're looking for,
+-	 * ignoring everything else.
+-	 *
+-	 * Following the first 8 arguments, the remaining arguments
+-	 * are passed on the stack after a 16 byte scratch area
+-	 */
+-	if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) {
+-		if (arg_num < 8) {
+-			reg.u.bits[0] = ptrace(PTRACE_PEEKUSER, proc->pid,
+-					       float_regs[arg_num], 0);
+-			reg.u.bits[1] = ptrace(PTRACE_PEEKUSER, proc->pid,
+-					       float_regs[arg_num] + 0x8, 0);
+-			return fpreg_to_double(&reg);
+-		} else {
+-			unsigned long sp =
+-			    ptrace(PTRACE_PEEKUSER, proc->pid, PT_R12, 0) + 16;
+-			reg.u.bits[0] = ptrace(PTRACE_PEEKDATA, proc->pid,
+-					       sp + (8 * (arg_num - 8)));
+-			reg.u.bits[0] = ptrace(PTRACE_PEEKDATA, proc->pid,
+-					       sp + (8 * (arg_num - 8)) + 0x8);
+-			return fpreg_to_double(&reg);
+-		}
+-	}
+-
+-	/* error if we get here */
+-	fprintf(stderr, "gimme_arg called with wrong arguments\n");
+-	exit(1);
+-}
+-
+-static unsigned f_index;
+-
+-long
+-gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+-{
+-	union {
+-		long l;
+-		float f;
+-		double d;
+-	} cvt;
+-
+-	if ((type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR)
+-	    && arg_num == 0)
+-		/* See above for the parameter passing convention.  */
+-		f_index = 0;
+-
+-	if (info != NULL && info->type == ARGTYPE_FLOAT)
+-		cvt.f = gimme_float_arg(type, proc, f_index++);
+-	else if (info != NULL && info->type == ARGTYPE_DOUBLE)
+-		cvt.d = gimme_float_arg(type, proc, f_index++);
+-	else
+-		cvt.l = gimme_long_arg(type, proc, arg_num);
+-
+-	return cvt.l;
+-}
+-
+ void
+ get_arch_dep(Process *proc) {
+ }
+diff --git a/testsuite/ltrace.main/parameters-lib.c b/testsuite/ltrace.main/parameters-lib.c
+index 27fe569..750075e 100644
+--- a/testsuite/ltrace.main/parameters-lib.c
++++ b/testsuite/ltrace.main/parameters-lib.c
+@@ -301,3 +301,158 @@ func_struct_size8(struct struct_size8 e)
+ {
+ 	return e;
+ }
++
++struct struct_hfa_f2 { float a; struct flt_eqv1 b; };
++struct struct_hfa_f2
++func_hfa_f2(struct struct_hfa_f2 e)
++{
++	return e;
++}
++
++struct struct_hfa_f3 { float a; struct struct_hfa_f2 b; };
++struct struct_hfa_f3
++func_hfa_f3(struct struct_hfa_f3 e)
++{
++	return e;
++}
++
++struct struct_hfa_f4 { float a; struct struct_hfa_f3 b; };
++struct struct_hfa_f4
++func_hfa_f4(struct struct_hfa_f4 e)
++{
++	return e;
++}
++
++struct struct_hfa_f5 { float a; struct struct_hfa_f4 b; };
++struct struct_hfa_f5
++func_hfa_f5(struct struct_hfa_f5 e)
++{
++	return e;
++}
++
++struct struct_hfa_f6 { float a; struct struct_hfa_f5 b; };
++struct struct_hfa_f6
++func_hfa_f6(struct struct_hfa_f6 e)
++{
++	return e;
++}
++
++struct struct_hfa_f7 { float a; struct struct_hfa_f6 b; };
++struct struct_hfa_f7
++func_hfa_f7(struct struct_hfa_f7 e)
++{
++	return e;
++}
++
++struct struct_hfa_f8 { float a; struct struct_hfa_f7 b; };
++struct struct_hfa_f8
++func_hfa_f8(struct struct_hfa_f8 e)
++{
++	return e;
++}
++
++struct struct_hfa_f9 { float a; struct struct_hfa_f8 b; };
++struct struct_hfa_f9
++func_hfa_f9(struct struct_hfa_f9 e)
++{
++	return e;
++}
++
++struct struct_hfa_f10 { float a; struct struct_hfa_f9 b; };
++struct struct_hfa_f10
++func_hfa_f10(struct struct_hfa_f10 e)
++{
++	return e;
++}
++
++struct struct_hfa_f11 { float a; struct struct_hfa_f10 b; };
++struct struct_hfa_f11
++func_hfa_f11(struct struct_hfa_f11 e)
++{
++	return e;
++}
++
++struct struct_hfa_f12 { float a; struct struct_hfa_f11 b; };
++struct struct_hfa_f12
++func_hfa_f12(struct struct_hfa_f12 e)
++{
++	return e;
++}
++
++
++struct struct_hfa_d2 { double a; struct dbl_eqv1 b; };
++struct struct_hfa_d2
++func_hfa_d2(struct struct_hfa_d2 e)
++{
++	return e;
++}
++
++struct struct_hfa_d3 { double a; struct struct_hfa_d2 b; };
++struct struct_hfa_d3
++func_hfa_d3(struct struct_hfa_d3 e)
++{
++	return e;
++}
++
++struct struct_hfa_d4 { double a; struct struct_hfa_d3 b; };
++struct struct_hfa_d4
++func_hfa_d4(struct struct_hfa_d4 e)
++{
++	return e;
++}
++
++struct struct_hfa_d5 { double a; struct struct_hfa_d4 b; };
++struct struct_hfa_d5
++func_hfa_d5(struct struct_hfa_d5 e)
++{
++	return e;
++}
++
++struct struct_hfa_d6 { double a; struct struct_hfa_d5 b; };
++struct struct_hfa_d6
++func_hfa_d6(struct struct_hfa_d6 e)
++{
++	return e;
++}
++
++struct struct_hfa_d7 { double a; struct struct_hfa_d6 b; };
++struct struct_hfa_d7
++func_hfa_d7(struct struct_hfa_d7 e)
++{
++	return e;
++}
++
++struct struct_hfa_d8 { double a; struct struct_hfa_d7 b; };
++struct struct_hfa_d8
++func_hfa_d8(struct struct_hfa_d8 e)
++{
++	return e;
++}
++
++struct struct_hfa_d9 { double a; struct struct_hfa_d8 b; };
++struct struct_hfa_d9
++func_hfa_d9(struct struct_hfa_d9 e)
++{
++	return e;
++}
++
++struct struct_hfa_d10 { double a; struct struct_hfa_d9 b; };
++struct struct_hfa_d10
++func_hfa_d10(struct struct_hfa_d10 e)
++{
++	return e;
++}
++
++struct struct_hfa_d11 { double a; struct struct_hfa_d10 b; };
++struct struct_hfa_d11
++func_hfa_d11(struct struct_hfa_d11 e)
++{
++	return e;
++}
++
++struct struct_hfa_d12 { double a; struct struct_hfa_d11 b; };
++struct struct_hfa_d12
++func_hfa_d12(struct struct_hfa_d12 e)
++{
++	return e;
++}
+diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c
+index 6318e60..158fdd7 100644
+--- a/testsuite/ltrace.main/parameters.c
++++ b/testsuite/ltrace.main/parameters.c
+@@ -262,5 +262,96 @@ main ()
+   struct struct_size8 func_struct_size8(struct struct_size8 e);
+   func_struct_size8((struct struct_size8){ 5, 6 });
+ 
++  /* Test Itanium Homogeneous Floating-point Aggregates.   */
++
++  struct struct_hfa_f2 { float a; struct flt_eqv1 b; };
++  struct struct_hfa_f2 func_hfa_f2(struct struct_hfa_f2 e);
++  func_hfa_f2((struct struct_hfa_f2){ 1, { 2 } });
++
++  struct struct_hfa_f3 { float a; struct struct_hfa_f2 b; };
++  struct struct_hfa_f3 func_hfa_f3(struct struct_hfa_f3 e);
++  func_hfa_f3((struct struct_hfa_f3){ 3, { 1, { 2 } } });
++
++  struct struct_hfa_f4 { float a; struct struct_hfa_f3 b; };
++  struct struct_hfa_f4 func_hfa_f4(struct struct_hfa_f4 e);
++  func_hfa_f4((struct struct_hfa_f4){ 4, { 3, { 1, { 2 } } } });
++
++  struct struct_hfa_f5 { float a; struct struct_hfa_f4 b; };
++  struct struct_hfa_f5 func_hfa_f5(struct struct_hfa_f5 e);
++  func_hfa_f5((struct struct_hfa_f5){ 5, { 4, { 3, { 1, { 2 } } } } });
++
++  struct struct_hfa_f6 { float a; struct struct_hfa_f5 b; };
++  struct struct_hfa_f6 func_hfa_f6(struct struct_hfa_f6 e);
++  func_hfa_f6((struct struct_hfa_f6){ 6, { 5, { 4, { 3, { 1, { 2 } } } } } });
++
++  struct struct_hfa_f7 { float a; struct struct_hfa_f6 b; };
++  struct struct_hfa_f7 func_hfa_f7(struct struct_hfa_f7 e);
++  func_hfa_f7((struct struct_hfa_f7){ 7, { 6, { 5, { 4, { 3, { 1, { 2 } } } } } } });
++
++  struct struct_hfa_f8 { float a; struct struct_hfa_f7 b; };
++  struct struct_hfa_f8 func_hfa_f8(struct struct_hfa_f8 e);
++  func_hfa_f8((struct struct_hfa_f8){ 8, { 7, { 6, { 5, { 4, { 3, { 1, { 2 } } } } } } } });
++
++  struct struct_hfa_f9 { float a; struct struct_hfa_f8 b; };
++  struct struct_hfa_f9 func_hfa_f9(struct struct_hfa_f9 e);
++  func_hfa_f9((struct struct_hfa_f9){ 9, { 8, { 7, { 6, { 5, { 4, { 3, { 1, { 2 } } } } } } } } });
++
++  struct struct_hfa_f10 { float a; struct struct_hfa_f9 b; };
++  struct struct_hfa_f10 func_hfa_f10(struct struct_hfa_f10 e);
++  func_hfa_f10((struct struct_hfa_f10){ 10, { 9, { 8, { 7, { 6, { 5, { 4, { 3, { 1, { 2 } } } } } } } } } });
++
++  struct struct_hfa_f11 { float a; struct struct_hfa_f10 b; };
++  struct struct_hfa_f11 func_hfa_f11(struct struct_hfa_f11 e);
++  func_hfa_f11((struct struct_hfa_f11){ 11, { 10, { 9, { 8, { 7, { 6, { 5, { 4, { 3, { 1, { 2 } } } } } } } } } } });
++
++  struct struct_hfa_f12 { float a; struct struct_hfa_f11 b; };
++  struct struct_hfa_f12 func_hfa_f12(struct struct_hfa_f12 e);
++  func_hfa_f12((struct struct_hfa_f12){ 12, { 11, { 10, { 9, { 8, { 7, { 6, { 5, { 4, { 3, { 1, { 2 } } } } } } } } } } } });
++
++
++  struct struct_hfa_d2 { double a; struct dbl_eqv1 b; };
++  struct struct_hfa_d2 func_hfa_d2(struct struct_hfa_d2 e);
++  func_hfa_d2((struct struct_hfa_d2){ 1, { 2 } });
++
++  struct struct_hfa_d3 { double a; struct struct_hfa_d2 b; };
++  struct struct_hfa_d3 func_hfa_d3(struct struct_hfa_d3 e);
++  func_hfa_d3((struct struct_hfa_d3){ 3, { 1, { 2 } } });
++
++  struct struct_hfa_d4 { double a; struct struct_hfa_d3 b; };
++  struct struct_hfa_d4 func_hfa_d4(struct struct_hfa_d4 e);
++  func_hfa_d4((struct struct_hfa_d4){ 4, { 3, { 1, { 2 } } } });
++
++  struct struct_hfa_d5 { double a; struct struct_hfa_d4 b; };
++  struct struct_hfa_d5 func_hfa_d5(struct struct_hfa_d5 e);
++  func_hfa_d5((struct struct_hfa_d5){ 5, { 4, { 3, { 1, { 2 } } } } });
++
++  struct struct_hfa_d6 { double a; struct struct_hfa_d5 b; };
++  struct struct_hfa_d6 func_hfa_d6(struct struct_hfa_d6 e);
++  func_hfa_d6((struct struct_hfa_d6){ 6, { 5, { 4, { 3, { 1, { 2 } } } } } });
++
++  struct struct_hfa_d7 { double a; struct struct_hfa_d6 b; };
++  struct struct_hfa_d7 func_hfa_d7(struct struct_hfa_d7 e);
++  func_hfa_d7((struct struct_hfa_d7){ 7, { 6, { 5, { 4, { 3, { 1, { 2 } } } } } } });
++
++  struct struct_hfa_d8 { double a; struct struct_hfa_d7 b; };
++  struct struct_hfa_d8 func_hfa_d8(struct struct_hfa_d8 e);
++  func_hfa_d8((struct struct_hfa_d8){ 8, { 7, { 6, { 5, { 4, { 3, { 1, { 2 } } } } } } } });
++
++  struct struct_hfa_d9 { double a; struct struct_hfa_d8 b; };
++  struct struct_hfa_d9 func_hfa_d9(struct struct_hfa_d9 e);
++  func_hfa_d9((struct struct_hfa_d9){ 9, { 8, { 7, { 6, { 5, { 4, { 3, { 1, { 2 } } } } } } } } });
++
++  struct struct_hfa_d10 { double a; struct struct_hfa_d9 b; };
++  struct struct_hfa_d10 func_hfa_d10(struct struct_hfa_d10 e);
++  func_hfa_d10((struct struct_hfa_d10){ 10, { 9, { 8, { 7, { 6, { 5, { 4, { 3, { 1, { 2 } } } } } } } } } });
++
++  struct struct_hfa_d11 { double a; struct struct_hfa_d10 b; };
++  struct struct_hfa_d11 func_hfa_d11(struct struct_hfa_d11 e);
++  func_hfa_d11((struct struct_hfa_d11){ 11, { 10, { 9, { 8, { 7, { 6, { 5, { 4, { 3, { 1, { 2 } } } } } } } } } } });
++
++  struct struct_hfa_d12 { double a; struct struct_hfa_d11 b; };
++  struct struct_hfa_d12 func_hfa_d12(struct struct_hfa_d12 e);
++  func_hfa_d12((struct struct_hfa_d12){ 12, { 11, { 10, { 9, { 8, { 7, { 6, { 5, { 4, { 3, { 1, { 2 } } } } } } } } } } } });
++
+   return 0;
+ }
+diff --git a/testsuite/ltrace.main/parameters.conf b/testsuite/ltrace.main/parameters.conf
+index ea1c55d..0531a6a 100644
+--- a/testsuite/ltrace.main/parameters.conf
++++ b/testsuite/ltrace.main/parameters.conf
+@@ -36,3 +36,27 @@ struct(char) func_struct_size1(struct(char));
+ struct(short) func_struct_size2(struct(short));
+ struct(int) func_struct_size4(struct(int));
+ struct(int,int) func_struct_size8(struct(int,int));
++
++struct(float,struct(float)) func_hfa_f2(struct(float,struct(float)));
++struct(float,struct(float,struct(float))) func_hfa_f3(struct(float,struct(float,struct(float))));
++struct(float,struct(float,struct(float,struct(float)))) func_hfa_f4(struct(float,struct(float,struct(float,struct(float)))));
++struct(float,struct(float,struct(float,struct(float,struct(float))))) func_hfa_f5(struct(float,struct(float,struct(float,struct(float,struct(float))))));
++struct(float,struct(float,struct(float,struct(float,struct(float,struct(float)))))) func_hfa_f6(struct(float,struct(float,struct(float,struct(float,struct(float,struct(float)))))));
++struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float))))))) func_hfa_f7(struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float))))))));
++struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float)))))))) func_hfa_f8(struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float)))))))));
++struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float))))))))) func_hfa_f9(struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float))))))))));
++struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float)))))))))) func_hfa_f10(struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float)))))))))));
++struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float))))))))))) func_hfa_f11(struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float))))))))))));
++struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float)))))))))))) func_hfa_f12(struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float,struct(float)))))))))))));
++
++struct(double,struct(double)) func_hfa_d2(struct(double,struct(double)));
++struct(double,struct(double,struct(double))) func_hfa_d3(struct(double,struct(double,struct(double))));
++struct(double,struct(double,struct(double,struct(double)))) func_hfa_d4(struct(double,struct(double,struct(double,struct(double)))));
++struct(double,struct(double,struct(double,struct(double,struct(double))))) func_hfa_d5(struct(double,struct(double,struct(double,struct(double,struct(double))))));
++struct(double,struct(double,struct(double,struct(double,struct(double,struct(double)))))) func_hfa_d6(struct(double,struct(double,struct(double,struct(double,struct(double,struct(double)))))));
++struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double))))))) func_hfa_d7(struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double))))))));
++struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double)))))))) func_hfa_d8(struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double)))))))));
++struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double))))))))) func_hfa_d9(struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double))))))))));
++struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double)))))))))) func_hfa_d10(struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double)))))))))));
++struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double))))))))))) func_hfa_d11(struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double))))))))))));
++struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double)))))))))))) func_hfa_d12(struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double,struct(double)))))))))))));
+diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp
+index b62315d..badbe6b 100644
+--- a/testsuite/ltrace.main/parameters.exp
++++ b/testsuite/ltrace.main/parameters.exp
+@@ -156,3 +156,69 @@ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+ 
+ set pattern "func_struct_size8({ 5, 6 }).*= { 5, 6 }"
+ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_f2({ 1.000*, { 2.000* } }).*= { 1.000*, { 2.000* } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_f3({ 3.000*, { 1.000*, { 2.000* } } }).*= { 3.000*, { 1.000*, { 2.000* } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_f4({ 4.000*, { 3.000*, { 1.000*, { 2.000* } } } }).*= { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_f5({ 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } }).*= { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_f6({ 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } }).*= { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_f7({ 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } }).*= { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_f8({ 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } }).*= { 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_f9({ 9.000*, { 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } } }).*= { 9.000*, { 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_f10({ 10.000*, { 9.000*, { 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } } } }).*= { 10.000*, { 9.000*, { 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_f11({ 11.000*, { 10.000*, { 9.000*, { 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } } } } }).*= { 11.000*, { 10.000*, { 9.000*, { 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } } } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_f12({ 12.000*, { 11.000*, { 10.000*, { 9.000*, { 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } } } } } }).*= { 12.000*, { 11.000*, { 10.000*, { 9.000*, { 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } } } } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_d2({ 1.000*, { 2.000* } }).*= { 1.000*, { 2.000* } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_d3({ 3.000*, { 1.000*, { 2.000* } } }).*= { 3.000*, { 1.000*, { 2.000* } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_d4({ 4.000*, { 3.000*, { 1.000*, { 2.000* } } } }).*= { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_d5({ 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } }).*= { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_d6({ 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } }).*= { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_d7({ 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } }).*= { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_d8({ 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } }).*= { 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_d9({ 9.000*, { 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } } }).*= { 9.000*, { 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_d10({ 10.000*, { 9.000*, { 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } } } }).*= { 10.000*, { 9.000*, { 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_d11({ 11.000*, { 10.000*, { 9.000*, { 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } } } } }).*= { 11.000*, { 10.000*, { 9.000*, { 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } } } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
++
++set pattern "func_hfa_d12({ 12.000*, { 11.000*, { 10.000*, { 9.000*, { 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } } } } } }).*= { 12.000*, { 11.000*, { 10.000*, { 9.000*, { 8.000*, { 7.000*, { 6.000*, { 5.000*, { 4.000*, { 3.000*, { 1.000*, { 2.000* } } } } } } } } } } } }"
++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+diff --git a/type.c b/type.c
+index 4b9a645..3ce8563 100644
+--- a/type.c
++++ b/type.c
+@@ -460,6 +460,27 @@ type_element(struct arg_type_info *info, size_t emt)
+ 	}
+ }
+ 
++size_t
++type_aggregate_size(struct arg_type_info *info)
++{
++	assert(info->type == ARGTYPE_STRUCT
++	       || info->type == ARGTYPE_ARRAY);
++
++	switch (info->type) {
++		long ret;
++	case ARGTYPE_ARRAY:
++		if (expr_eval_constant(info->u.array_info.length, &ret) < 0)
++			return (size_t)-1;
++		return (size_t)ret;
++
++	case ARGTYPE_STRUCT:
++		return type_struct_size(info);
++
++	default:
++		abort();
++	}
++}
++
+ int
+ type_is_integral(enum arg_type type)
+ {
+diff --git a/type.h b/type.h
+index 53123b8..e8dec71 100644
+--- a/type.h
++++ b/type.h
+@@ -87,6 +87,11 @@ struct arg_type_info *type_struct_get(struct arg_type_info *info, size_t idx);
+ /* Return number of fields of structure type INFO.  */
+ size_t type_struct_size(struct arg_type_info *info);
+ 
++/* Return number of elements of an aggregate type INFO.  This can be
++ * either ARGTYPE_STRUCT or ARGTYPE_ARRAY of constant length.  If
++ * ARGTYPE_ARRAY does not have a constant length, this returns -1.  */
++size_t type_aggregate_size(struct arg_type_info *info);
++
+ /* Initialize INFO so it becomes ARGTYPE_ARRAY.  The element type is
+  * passed in ELEMENT_INFO, and array length in LENGTH_EXPR.  If,
+  * respectively, OWN_INFO and OWN_LENGTH are true, the pointee and
diff --git a/ltrace-0.6.0-cleanups.patch b/ltrace-0.6.0-cleanups.patch
new file mode 100644
index 0000000..054882d
--- /dev/null
+++ b/ltrace-0.6.0-cleanups.patch
@@ -0,0 +1,652 @@
+diff --git a/lens_default.c b/lens_default.c
+index f59d328..81025b1 100644
+--- a/lens_default.c
++++ b/lens_default.c
+@@ -63,7 +63,7 @@ READER(read_double, double)
+ 		int##BITS##_t i = l;					\
+ 		switch (format) {					\
+ 		case INT_FMT_unknown:					\
+-			if (i < -10000 || i > 10000)			\
++			if (l < -10000 || l > 10000)			\
+ 		case INT_FMT_x:						\
+ 			return fprintf(stream, "%#"PRIx##BITS, i);	\
+ 		case INT_FMT_i:						\
+diff --git a/sysdeps/linux-gnu/ia64/breakpoint.c b/sysdeps/linux-gnu/ia64/breakpoint.c
+index a0bfaf9..a5071b8 100644
+--- a/sysdeps/linux-gnu/ia64/breakpoint.c
++++ b/sysdeps/linux-gnu/ia64/breakpoint.c
+@@ -2,11 +2,12 @@
+  *  -Ian Wienand <ianw at gelato.unsw.edu.au> 10/3/2005
+  */
+ 
+-#include "config.h"
+-
+ #include <sys/ptrace.h>
+ #include <string.h>
+-#include "common.h"
++#include <assert.h>
++
++#include "breakpoint.h"
++#include "debug.h"
+ 
+ static long long
+ extract_bit_field(char *bundle, int from, int len) {
+@@ -161,9 +162,7 @@ arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp)
+ 
+ 	debug(1, "Enable Breakpoint at %p)", sbp->addr);
+ 
+-	if (slotnum > 2)
+-		printf
+-		    ("Can't insert breakpoint for slot numbers greater than 2.");
++	assert(slotnum <= 2);
+ 
+ 	addr &= ~0x0f;
+ 	bundle.ubundle[0] = ptrace(PTRACE_PEEKTEXT, pid, addr, 0);
+diff --git a/sysdeps/linux-gnu/ia64/plt.c b/sysdeps/linux-gnu/ia64/plt.c
+index 323df65..76a4aac 100644
+--- a/sysdeps/linux-gnu/ia64/plt.c
++++ b/sysdeps/linux-gnu/ia64/plt.c
+@@ -1,6 +1,8 @@
+ #include <gelf.h>
++
+ #include "proc.h"
+ #include "common.h"
++#include "library.h"
+ 
+ /* A bundle is 128 bits */
+ #define BUNDLE_SIZE 16
+@@ -36,7 +38,7 @@ arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) {
+ 	unsigned long addr =
+ 	    lte->plt_addr + (4 * BUNDLE_SIZE) + (BUNDLE_SIZE * entries) +
+ 	    (2 * ndx * BUNDLE_SIZE);
+-	debug(3, "Found PLT %d entry at %lx\n", ndx, addr);
++	debug(3, "Found PLT %zd entry at %lx\n", ndx, addr);
+ 
+ 	return addr;
+ }
+diff --git a/breakpoints.c b/breakpoints.c
+index e7120ee..3eee38b 100644
+--- a/breakpoints.c
++++ b/breakpoints.c
+@@ -302,35 +302,6 @@ enable_all_breakpoints(Process *proc)
+ 		dict_apply_to_all(proc->breakpoints, enable_bp_cb,
+ 				  proc);
+ 	}
+-#ifdef __mips__
+-	{
+-		/*
+-		 * I'm sure there is a nicer way to do this. We need to
+-		 * insert breakpoints _after_ the child has been started.
+-		 */
+-		struct library_symbol *sym;
+-		struct library_symbol *new_sym;
+-		sym=proc->list_of_symbols;
+-		while(sym){
+-			void *addr= sym2addr(proc,sym);
+-			if(!addr){
+-				sym=sym->next;
+-				continue;
+-			}
+-			if(dict_find_entry(proc->breakpoints,addr)){
+-				sym=sym->next;
+-				continue;
+-			}
+-			debug(2,"inserting bp %p %s",addr,sym->name);
+-			new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1);
+-			memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1);
+-			new_sym->next=proc->list_of_symbols;
+-			proc->list_of_symbols=new_sym;
+-			insert_breakpoint(proc, addr, new_sym);
+-			sym=sym->next;
+-		}
+-	}
+-#endif
+ }
+ 
+ static void
+diff --git a/handle_event.c b/handle_event.c
+index 5b6cc40..1720cb3 100644
+--- a/handle_event.c
++++ b/handle_event.c
+@@ -232,6 +232,7 @@ pending_new_remove(pid_t pid) {
+ 	debug(DEBUG_FUNCTION, "pending_new_remove(%d)", pid);
+ 
+ 	p = pending_news;
++	pred = NULL;
+ 	if (p->pid == pid) {
+ 		pending_news = p->next;
+ 		free(p);
+@@ -592,25 +593,6 @@ handle_breakpoint(Event *event)
+ 
+ 	for (i = event->proc->callstack_depth - 1; i >= 0; i--) {
+ 		if (brk_addr == event->proc->callstack[i].return_addr) {
+-#if defined(__mips__)
+-			void *addr = NULL;
+-			struct library_symbol *sym= event->proc->callstack[i].c_un.libfunc;
+-			struct library_symbol *new_sym;
+-			assert(sym);
+-			addr = sym2addr(event->proc, sym);
+-			sbp = dict_find_entry(leader->breakpoints, addr);
+-			if (sbp) {
+-				if (addr != sbp->addr) {
+-					insert_breakpoint(event->proc, addr, sym);
+-				}
+-			} else {
+-				new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1);
+-				memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1);
+-				new_sym->next = leader->list_of_symbols;
+-				leader->list_of_symbols = new_sym;
+-				insert_breakpoint(event->proc, addr, new_sym);
+-			}
+-#endif
+ 			for (j = event->proc->callstack_depth - 1; j > i; j--) {
+ 				callstack_pop(event->proc);
+ 			}
+diff --git a/ltrace-elf.c b/ltrace-elf.c
+index c8667a7..bc99c6a 100644
+--- a/ltrace-elf.c
++++ b/ltrace-elf.c
+@@ -50,10 +50,6 @@
+ #include "debug.h"
+ #include "options.h"
+ 
+-#ifdef PLT_REINITALISATION_BP
+-extern char *PLTs_initialized_by_here;
+-#endif
+-
+ #ifndef ARCH_HAVE_LTELF_DATA
+ int
+ arch_elf_init(struct ltelf *lte, struct library *lib)
+diff --git a/ltrace.1 b/ltrace.1
+index 604f4da..fb64289 100644
+--- a/ltrace.1
++++ b/ltrace.1
+@@ -7,7 +7,7 @@ ltrace \- A library call tracer
+ 
+ .SH SYNOPSIS
+ .B ltrace
+-.I "[-bCfghiLrStttV] [-a column] [-A maxelts] [-D level] [-e expr] [-l filename] [-n nr] [-o filename] [-p pid] ... [-s strsize] [-u username] [-w count] [-X extern] [-x extern] ... [--align=column] [--debug=level] [--demangle] [--help] [--indent=nr] [--library=filename] [--no-signals] [--output=filename] [--version] [--where=NR] [command [arg ...]]"
++.I "[-bCfghiLrStttV] [-a column] [-A maxelts] [-D level] [-e expr] [-l filename] [-n nr] [-o filename] [-p pid] ... [-s strsize] [-u username] [-w count] [-x extern] ... [--align=column] [--debug=level] [--demangle] [--help] [--indent=nr] [--library=filename] [--no-signals] [--output=filename] [--version] [--where=NR] [command [arg ...]]"
+ 
+ .SH DESCRIPTION
+ .B ltrace
+@@ -158,14 +158,6 @@ correct execution of setuid and/or setgid binaries.
+ Show backtrace of NR stack frames for each traced function. This option enabled
+ only if libunwind support was enabled at compile time.
+ .TP
+-.I \-X extern
+-Some architectures need to know where to set a breakpoint that will be hit
+-after the dynamic linker has run.  If this flag is used, then the breakpoint
+-is set at
+-.IR extern ,
+-which must be an external function.  By default, '_start' is used.
+-NOTE: this flag is only available on the architectures that need it.
+-.TP
+ .I \-x filter
+ A qualifying expression which modifies which symbol table entry points
+ to trace.  The format of the filter expression is described in the
+diff --git a/options.c b/options.c
+index 8dce7f8..b9472a8 100644
+--- a/options.c
++++ b/options.c
+@@ -50,12 +50,6 @@ struct opt_p_t *opt_p = NULL;	/* attach to process with a given pid */
+ /* List of filenames give to option -F: */
+ struct opt_F_t *opt_F = NULL;	/* alternate configuration file(s) */
+ 
+-#ifdef PLT_REINITALISATION_BP
+-/* Set a break on the routine named here in order to re-initialize breakpoints
+-   after all the PLTs have been initialzed */
+-char *PLTs_initialized_by_here = PLT_REINITALISATION_BP;
+-#endif
+-
+ static void
+ err_usage(void) {
+ 	fprintf(stderr, "Try `%s --help' for more information\n", progname);
+@@ -78,7 +72,6 @@ usage(void) {
+ 		"  -e expr             modify which events to trace.\n"
+ 		"  -f                  trace children (fork() and clone()).\n"
+ 		"  -F, --config=FILE   load alternate configuration file (may be repeated).\n"
+-		"  -g, --no-plt        disable breakpoints on PLT entries.\n"
+ 		"  -h, --help          display this help and exit.\n"
+ 		"  -i                  print instruction pointer at time of library call.\n"
+ 		"  -l, --library=FILE  print library calls from this library only.\n"
+@@ -97,9 +90,6 @@ usage(void) {
+ 		"  -w=NR, --where=NR   print backtrace showing NR stack frames at most.\n"
+ #endif /* defined(HAVE_LIBUNWIND) */
+ 		"  -x NAME             treat the global NAME like a library subroutine.\n"
+-#ifdef PLT_REINITALISATION_BP
+-		"  -X NAME             same as -x; and PLT's will be initialized by here.\n"
+-#endif
+ 		"\nReport bugs to ltrace-devel at lists.alioth.debian.org\n",
+ 		progname);
+ }
+@@ -560,14 +550,6 @@ process_options(int argc, char **argv)
+ 			options.bt_depth = atoi(optarg);
+ 			break;
+ #endif /* defined(HAVE_LIBUNWIND) */
+-		case 'X':
+-#ifdef PLT_REINITALISATION_BP
+-			PLTs_initialized_by_here = optarg;
+-#else
+-			fprintf(stderr, "WARNING: \"-X\" not used for this "
+-				"architecture: assuming you meant \"-x\"\n");
+-#endif
+-			/* Fall Thru */
+ 
+ 		case 'x':
+ 			parse_filter_chain(optarg, &options.static_filter);
+diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h
+index 846961e..2d0ad65 100644
+--- a/sysdeps/linux-gnu/ppc/arch.h
++++ b/sysdeps/linux-gnu/ppc/arch.h
+@@ -37,8 +37,6 @@
+ #define ARCH_SUPPORTS_OPD
+ #endif
+ 
+-#define PLT_REINITALISATION_BP    "_start"
+-
+ #define ARCH_ENDIAN_BIG
+ #define ARCH_HAVE_ATOMIC_SINGLESTEP
+ #define ARCH_HAVE_ADD_PLT_ENTRY
+diff --git a/Makefile.am b/Makefile.am
+index 177a498..47161f8 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -85,6 +85,7 @@ noinst_HEADERS = \
+ 	type.h \
+ 	value.h \
+ 	value_dict.h \
++	callback.h \
+ 	expr.h \
+ 	fetch.h \
+ 	vect.h \
+diff --git a/callback.h b/callback.h
+new file mode 100644
+index 0000000..31e5c8f
+--- /dev/null
++++ b/callback.h
+@@ -0,0 +1,50 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2011,2012 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
++ */
++
++#ifndef _CALLBACK_H_
++#define _CALLBACK_H_
++
++/* Notes on the iteration interface used across ltrace.  Typically the
++ * iteration function looks something like this:
++ *
++ *   foo *each_foo(foo *start_after,
++ *                 enum callback_status (*cb)(foo *f, void *data),
++ *                 void *data);
++ *
++ * The iteration starts after the element designated by START_AFTER,
++ * or at the first element if START_AFTER is NULL.  CB is then called
++ * for each element of the collection.  DATA is passed verbatim to CB.
++ * If CB returns CBS_STOP, the iteration stops and the current element
++ * is returned.  That element can then be passed as START_AFTER to
++ * restart the iteration.  NULL is returned when iteration ends.
++ *
++ * CBS_FAIL is not currently handled, and essentially means the same
++ * thing as CBS_STOP.  There's no provision for returning error
++ * states.  Errors need to be signaled to the caller via DATA,
++ * together with any other data that the callback needs.
++ */
++enum callback_status {
++	CBS_STOP, /* The iteration should stop.  */
++	CBS_CONT, /* The iteration should continue.  */
++	CBS_FAIL, /* There was an error.  The iteration should stop
++		   * and return error.  */
++};
++
++#endif /* _CALLBACK_H_ */
+diff --git a/filter.c b/filter.c
+index 003010d..bf77b76 100644
+--- a/filter.c
++++ b/filter.c
+@@ -25,6 +25,7 @@
+ 
+ #include "filter.h"
+ #include "library.h"
++#include "callback.h"
+ 
+ void
+ filter_init(struct filter *filt)
+diff --git a/forward.h b/forward.h
+index e4233e5..8c03319 100644
+--- a/forward.h
++++ b/forward.h
+@@ -12,3 +12,4 @@ struct param;
+ struct param_enum;
+ struct value;
+ struct value_dict;
++struct filter;
+diff --git a/library.c b/library.c
+index 2ce3427..cbd4a35 100644
+--- a/library.c
++++ b/library.c
+@@ -25,8 +25,9 @@
+ #include <assert.h>
+ 
+ #include "library.h"
+-#include "proc.h" // for enum callback_status
++#include "callback.h"
+ #include "debug.h"
++#include "dict.h"
+ #include "backend.h" // for arch_library_symbol_init, arch_library_init
+ 
+ #ifndef ARCH_HAVE_LIBRARY_DATA
+diff --git a/library.h b/library.h
+index 876a533..f207502 100644
+--- a/library.h
++++ b/library.h
+@@ -23,6 +23,7 @@
+ #define _LIBRARY_H_
+ 
+ #include <stdint.h>
++#include "callback.h"
+ #include "sysdep.h"
+ 
+ struct Process;
+@@ -144,9 +145,8 @@ void library_set_soname(struct library *lib,
+ void library_set_pathname(struct library *lib,
+ 			  const char *new_name, int own_name);
+ 
+-/* Iterate through list of symbols of library LIB.  Restarts are
+- * supported via START_AFTER (see each_process for details of
+- * iteration interface).  */
++/* Iterate through list of symbols of library LIB.  See callback.h for
++ * notes on this interface.  */
+ struct library_symbol *library_each_symbol
+ 	(struct library *lib, struct library_symbol *start_after,
+ 	 enum callback_status (*cb)(struct library_symbol *, void *),
+diff --git a/options.c b/options.c
+index b9472a8..87dddb0 100644
+--- a/options.c
++++ b/options.c
+@@ -52,7 +52,7 @@ struct opt_F_t *opt_F = NULL;	/* alternate configuration file(s) */
+ 
+ static void
+ err_usage(void) {
+-	fprintf(stderr, "Try `%s --help' for more information\n", progname);
++	fprintf(stderr, "Try `%s --help' for more information.\n", progname);
+ 	exit(1);
+ }
+ 
+@@ -127,7 +127,7 @@ search_for_command(char *filename) {
+ 			m = n = strlen(path);
+ 		}
+ 		if (n + strlen(filename) + 1 >= PATH_MAX) {
+-			fprintf(stderr, "Error: filename too long\n");
++			fprintf(stderr, "Error: filename too long.\n");
+ 			exit(1);
+ 		}
+ 		strncpy(pathname, path, n);
+@@ -173,7 +173,7 @@ add_filter_rule(struct filter *filt, const char *expr,
+ 	struct filter_lib_matcher *matcher = malloc(sizeof(*matcher));
+ 
+ 	if (rule == NULL || matcher == NULL) {
+-		fprintf(stderr, "rule near '%s' will be ignored: %s\n",
++		fprintf(stderr, "Rule near '%s' will be ignored: %s.\n",
+ 			expr, strerror(errno));
+ 	fail:
+ 		free(rule);
+@@ -193,7 +193,7 @@ add_filter_rule(struct filter *filt, const char *expr,
+ 		if (status != 0) {
+ 			char buf[100];
+ 			regerror(status, &symbol_re, buf, sizeof buf);
+-			fprintf(stderr, "rule near '%s' will be ignored: %s\n",
++			fprintf(stderr, "Rule near '%s' will be ignored: %s.\n",
+ 				expr, buf);
+ 			goto fail;
+ 		}
+@@ -301,8 +301,8 @@ parse_filter(struct filter *filt, char *expr)
+ 				/* /XXX at YYY/ is the same as
+ 				 * /XXX/@/YYY/.  */
+ 				if (libend[0] != '/')
+-					fprintf(stderr, "unmatched '/'"
+-						" in symbol name\n");
++					fprintf(stderr, "Unmatched '/'"
++						" in symbol name.\n");
+ 				else
+ 					*libend-- = 0;
+ 			}
+@@ -339,7 +339,7 @@ recursive_parse_chain(char *expr)
+ {
+ 	struct filter *filt = malloc(sizeof(*filt));
+ 	if (filt == NULL) {
+-		fprintf(stderr, "(part of) filter will be ignored: '%s': %s\n",
++		fprintf(stderr, "(Part of) filter will be ignored: '%s': %s.\n",
+ 			expr, strerror(errno));
+ 		return NULL;
+ 	}
+@@ -359,7 +359,7 @@ parse_filter_chain(const char *expr, struct filter **retp)
+ {
+ 	char *str = strdup(expr);
+ 	if (str == NULL) {
+-		fprintf(stderr, "filter '%s' will be ignored: %s\n",
++		fprintf(stderr, "Filter '%s' will be ignored: %s.\n",
+ 			expr, strerror(errno));
+ 		return;
+ 	}
+@@ -598,7 +598,8 @@ process_options(int argc, char **argv)
+ 		err_usage();
+ 	}
+ 	if (opt_r && opt_t) {
+-		fprintf(stderr, "%s: Incompatible options -r and -t\n",
++		fprintf(stderr,
++			"%s: Options -r and -t can't be used together\n",
+ 			progname);
+ 		err_usage();
+ 	}
+diff --git a/options.h b/options.h
+index 3ffee71..0806928 100644
+--- a/options.h
++++ b/options.h
+@@ -1,7 +1,7 @@
+ #include <stdio.h>
+ #include <sys/types.h>
+ 
+-struct filter;
++#include "forward.h"
+ 
+ struct options_t {
+ 	int align;      /* -a: default alignment column for results */
+diff --git a/proc.h b/proc.h
+index 64c37a9..b61e420 100644
+--- a/proc.h
++++ b/proc.h
+@@ -34,20 +34,11 @@
+ #include "ltrace.h"
+ #include "dict.h"
+ #include "sysdep.h"
++#include "callback.h"
+ 
+ struct library;
+ struct breakpoint;
+ 
+-/* XXX Move this somewhere where it makes sense.  When the mess in
+- * common.h is disentangled, that would actually be a good place for
+- * this.  */
+-enum callback_status {
+-	CBS_STOP, /* The iteration should stop.  */
+-	CBS_CONT, /* The iteration should continue.  */
+-	CBS_FAIL, /* There was an error.  The iteration should stop
+-		   * and return error.  */
+-};
+-
+ struct event_handler {
+ 	/* Event handler that overrides the default one.  Should
+ 	 * return NULL if the event was handled, otherwise the
+@@ -184,28 +175,16 @@ Process * pid2proc(pid_t pid);
+  * Returns 0 on success or a negative value on failure.  */
+ int process_clone(struct Process *retp, struct Process *proc, pid_t pid);
+ 
+-/* Iterate through the processes that ltrace currently traces.  CB is
+- * called for each process.  Tasks are considered to be processes for
+- * the purpose of this iterator.
+- *
+- * Notes on this iteration interface: The iteration starts after the
+- * process designated by START_AFTER, or at the first process if
+- * START_AFTER is NULL.  DATA is passed verbatim to CB.  If CB returns
+- * CBS_STOP, the iteration stops and the current iterator is returned.
+- * That iterator can then be used to restart the iteration.  NULL is
+- * returned when iteration ends.
+- *
+- * There's no provision for returning error states.  Errors need to be
+- * signaled to the caller via DATA, together with any other data that
+- * the callback needs.  */
++/* Iterate through the processes that ltrace currently traces.  Tasks
++ * are considered to be processes for the purpose of this iterator.
++ * See callback.h for notes on iteration interfaces.  */
+ Process *each_process(Process *start_after,
+ 		      enum callback_status (*cb)(struct Process *proc,
+ 						 void *data),
+ 		      void *data);
+ 
+-/* Iterate through list of tasks of given process PROC.  Restarts are
+- * supported via START_AFTER (see each_process for details of
+- * iteration interface).  */
++/* Iterate through list of tasks of given process PROC.  See
++ * callback.h for notes on iteration interfaces.  */
+ Process *each_task(struct Process *proc, struct Process *start_after,
+ 		   enum callback_status (*cb)(struct Process *proc,
+ 					      void *data),
+@@ -227,8 +206,8 @@ void proc_add_library(struct Process *proc, struct library *lib);
+  * was found and unlinked, otherwise returns a negative value.  */
+ int proc_remove_library(struct Process *proc, struct library *lib);
+ 
+-/* Iterate through the libraries of PROC.  See each_process for
+- * detailed description of the iteration interface.  */
++/* Iterate through the libraries of PROC.  See callback.h for notes on
++ * iteration interfaces.  */
+ struct library *proc_each_library(struct Process *proc, struct library *start,
+ 				  enum callback_status (*cb)(struct Process *p,
+ 							     struct library *l,
+@@ -242,8 +221,8 @@ int proc_add_breakpoint(struct Process *proc, struct breakpoint *bp);
+  * does not find BP in PROC, it's hard error guarded by assertion.  */
+ void proc_remove_breakpoint(struct Process *proc, struct breakpoint *bp);
+ 
+-/* Iterate through the libraries of PROC.  See each_process for
+- * detailed description of the iteration interface.  */
++/* Iterate through the breakpoints of PROC.  See callback.h for notes
++ * on iteration interfaces.  */
+ void *proc_each_breakpoint(struct Process *proc, void *start,
+ 			   enum callback_status (*cb)(struct Process *proc,
+ 						      struct breakpoint *bp,
+diff --git a/sysdeps/linux-gnu/x86/arch.h b/sysdeps/linux-gnu/x86/arch.h
+index 77a09d7..329cfba 100644
+--- a/sysdeps/linux-gnu/x86/arch.h
++++ b/sysdeps/linux-gnu/x86/arch.h
+@@ -1,6 +1,6 @@
+ /*
+  * This file is part of ltrace.
+- * Copyright (C) 2011 Petr Machata
++ * Copyright (C) 2011, 2012 Petr Machata
+  * Copyright (C) 2006 Ian Wienand
+  * Copyright (C) 2004 Juan Cespedes
+  *
+@@ -34,7 +34,3 @@
+ #endif
+ #define LT_ELFCLASS2	ELFCLASS32
+ #define LT_ELF_MACHINE2	EM_386
+-
+-/* __NR_fork, __NR_clone, __NR_clone2, __NR_vfork and __NR_execve
+-   from asm-i386/unistd.h.  */
+-#define FORK_EXEC_SYSCALLS , { 2, 120, -1, 190, 11 }
+diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c
+index 158fdd7..44d54b2 100644
+--- a/testsuite/ltrace.main/parameters.c
++++ b/testsuite/ltrace.main/parameters.c
+@@ -190,7 +190,7 @@ main ()
+ 	 "%c %d %g %d %g %c %d "
+ 	 "%hd %d %c %g %g %g "
+ 	 "%ld %g %g %g %g",
+-	 1, 2, 3, 4.0, '5', 6, 7.0,
++	 1, 2, 3L, 4.0, '5', 6, 7.0,
+ 	 '8', 9, 10.0, 11, 12.0, 'A', 14,
+ 	 (short)15, 16, 'B', 18.0, 19.0, 20.0,
+ 	 21L, 22.0, 23.0, 24.0, 25.0);
+diff --git a/vect.c b/vect.c
+index f2e58b2..7dae847 100644
+--- a/vect.c
++++ b/vect.c
+@@ -134,3 +134,25 @@ vect_destroy(struct vect *vec, void (*dtor)(void *emt, void *data), void *data)
+ 	}
+ 	free(vec->data);
+ }
++
++void *
++vect_each(struct vect *vec, void *start_after,
++	  enum callback_status (*cb)(void *, void *), void *data)
++{
++	size_t i = start_after == NULL ? 0
++		: ((start_after - vec->data) / vec->elt_size) + 1;
++
++	for (; i < vec->size; ++i) {
++		void *slt = slot(vec, i);
++		switch ((*cb)(slt, data)) {
++		case CBS_FAIL:
++			/* XXX handle me */
++		case CBS_STOP:
++			return slt;
++		case CBS_CONT:
++			break;
++		}
++	}
++
++	return NULL;
++}
+diff --git a/vect.h b/vect.h
+index 50401bb..c07235f 100644
+--- a/vect.h
++++ b/vect.h
+@@ -22,6 +22,9 @@
+ #define VECT_H
+ 
+ #include <stddef.h>
++#include <assert.h>
++
++#include "callback.h"
+ 
+ /* Vector is an array that can grow as needed to accommodate the data
+  * that it needs to hold.  ELT_SIZE is also used as an elementary
+@@ -122,4 +125,22 @@ void vect_destroy(struct vect *vec,
+ 			     DATA);					\
+ 	} while (0)
+ 
++/* Iterate through vector VEC.  See callback.h for notes on iteration
++ * interfaces.  */
++void *vect_each(struct vect *vec, void *start_after,
++		enum callback_status (*cb)(void *, void *), void *data);
++
++#define VECT_EACH(VECP, ELT_TYPE, START_AFTER, CB, DATA)		\
++	/* xxx GCC-ism necessary to get in the safety latches.  */	\
++	({								\
++		assert((VECP)->elt_size == sizeof(ELT_TYPE));		\
++		/* Check that CB is typed properly.  */			\
++		enum callback_status (*_cb)(ELT_TYPE *, void *) = CB;	\
++		ELT_TYPE *start_after = (START_AFTER);			\
++		(ELT_TYPE *)vect_each((VECP), start_after,		\
++				      (enum callback_status		\
++				       (*)(void *, void *))_cb,		\
++				      DATA);				\
++	})
++
+ #endif /* VECT_H */
diff --git a/ltrace-0.6.0-dash-l.patch b/ltrace-0.6.0-dash-l.patch
new file mode 100644
index 0000000..09b6fd7
--- /dev/null
+++ b/ltrace-0.6.0-dash-l.patch
@@ -0,0 +1,1293 @@
+diff --git a/backend.h b/backend.h
+index bae53bd..8b4e8fa 100644
+--- a/backend.h
++++ b/backend.h
+@@ -266,6 +266,21 @@ void arch_process_destroy(struct Process *proc);
+ int arch_process_clone(struct Process *retp, struct Process *proc);
+ int arch_process_exec(struct Process *proc);
+ 
++/* The following callback has to be implemented in backend if arch.h
++ * defines ARCH_HAVE_GET_SYM_INFO.
++ *
++ * This is called for every PLT relocation R in ELF file LTE, that
++ * ltrace is about to add to it's internal representation of the
++ * program under trace.
++ * The corresponding PLT entry is for SYM_INDEX-th relocation in the file.
++ *
++ * The callback is responsible for initializing RELA and SYM.
++ *
++ * Return 0 if OK.
++ * Return a negative value if this symbol (SYM_INDEX) should be ignored.  */
++int arch_get_sym_info(struct ltelf *lte, const char *filename,
++		      size_t sym_index, GElf_Rela *rela, GElf_Sym *sym);
++
+ enum plt_status {
+ 	plt_fail,
+ 	plt_ok,
+@@ -293,6 +308,10 @@ enum plt_status arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte,
+  * done with the process startup.  */
+ void arch_dynlink_done(struct Process *proc);
+ 
++/* This callback needs to be implemented if arch.h defines
++ * ARCH_HAVE_SYMBOL_RET.  It is called after a traced call returns.  */
++void arch_symbol_ret(struct Process *proc, struct library_symbol *libsym);
++
+ /* If arch.h defines ARCH_HAVE_FETCH_ARG, the following callbacks have
+  * to be implemented: arch_fetch_arg_init, arch_fetch_arg_clone,
+  * arch_fetch_arg_done, arch_fetch_arg_next and arch_fetch_retval.
+diff --git a/filter.c b/filter.c
+index bf77b76..ba50c40 100644
+--- a/filter.c
++++ b/filter.c
+@@ -147,15 +147,17 @@ filter_matches_library(struct filter *filt, struct library *lib)
+ 	if (filt == NULL)
+ 		return 0;
+ 
+-	struct filter_rule *it;
+-	for (it = filt->rules; it != NULL; it = it->next)
+-		switch (it->type) {
+-		case FR_ADD:
+-			if (matcher_matches_library(it->lib_matcher, lib))
+-				return 1;
+-		case FR_SUBTRACT:
+-			continue;
+-		};
++	for (; filt != NULL; filt = filt->next) {
++		struct filter_rule *it;
++		for (it = filt->rules; it != NULL; it = it->next)
++			switch (it->type) {
++			case FR_ADD:
++				if (matcher_matches_library(it->lib_matcher, lib))
++					return 1;
++			case FR_SUBTRACT:
++				continue;
++			};
++	}
+ 	return 0;
+ }
+ 
+diff --git a/handle_event.c b/handle_event.c
+index 384e868..5793d7f 100644
+--- a/handle_event.c
++++ b/handle_event.c
+@@ -573,6 +573,12 @@ output_right_tos(struct Process *proc)
+ 		output_right(LT_TOF_FUNCTIONR, proc, elem->c_un.libfunc);
+ }
+ 
++#ifndef ARCH_HAVE_SYMBOL_RET
++void arch_symbol_ret(struct Process *proc, struct library_symbol *libsym)
++{
++}
++#endif
++
+ static void
+ handle_breakpoint(Event *event)
+ {
+@@ -606,6 +612,7 @@ handle_breakpoint(Event *event)
+ 			struct library_symbol *libsym =
+ 			    event->proc->callstack[i].c_un.libfunc;
+ 
++			arch_symbol_ret(event->proc, libsym);
+ 			output_right_tos(event->proc);
+ 			callstack_pop(event->proc);
+ 
+@@ -614,7 +621,7 @@ handle_breakpoint(Event *event)
+ 			 * have the same return address, but were made
+ 			 * for different symbols.  This should only
+ 			 * happen for entry point tracing, i.e. for -x
+-			 * everywhere, or -x and -e on PPC64.  */
++			 * everywhere, or -x and -e on MIPS.  */
+ 			while (event->proc->callstack_depth > 0) {
+ 				struct callstack_element *prev;
+ 				size_t d = event->proc->callstack_depth;
+@@ -624,6 +631,8 @@ handle_breakpoint(Event *event)
+ 				    || prev->return_addr != brk_addr)
+ 					break;
+ 
++				arch_symbol_ret(event->proc,
++						prev->c_un.libfunc);
+ 				output_right_tos(event->proc);
+ 				callstack_pop(event->proc);
+ 			}
+diff --git a/library.c b/library.c
+index cbd4a35..84fc0ff 100644
+--- a/library.c
++++ b/library.c
+@@ -113,13 +113,16 @@ static void
+ private_library_symbol_init(struct library_symbol *libsym,
+ 			    arch_addr_t addr,
+ 			    const char *name, int own_name,
+-			    enum toplt type_of_plt)
++			    enum toplt type_of_plt,
++			    int latent, int delayed)
+ {
+ 	libsym->next = NULL;
+ 	libsym->lib = NULL;
+ 	libsym->plt_type = type_of_plt;
+ 	libsym->name = name;
+ 	libsym->own_name = own_name;
++	libsym->latent = latent;
++	libsym->delayed = delayed;
+ 	libsym->enter_addr = (void *)(uintptr_t)addr;
+ }
+ 
+@@ -134,7 +137,8 @@ library_symbol_init(struct library_symbol *libsym,
+ 		    arch_addr_t addr, const char *name, int own_name,
+ 		    enum toplt type_of_plt)
+ {
+-	private_library_symbol_init(libsym, addr, name, own_name, type_of_plt);
++	private_library_symbol_init(libsym, addr, name, own_name,
++				    type_of_plt, 0, 0);
+ 
+ 	/* If arch init fails, we've already set libsym->name and
+ 	 * own_name.  But we return failure, and the client code isn't
+@@ -159,7 +163,8 @@ library_symbol_clone(struct library_symbol *retp, struct library_symbol *libsym)
+ 		return -1;
+ 
+ 	private_library_symbol_init(retp, libsym->enter_addr,
+-				    name, libsym->own_name, libsym->plt_type);
++				    name, libsym->own_name, libsym->plt_type,
++				    libsym->latent, libsym->delayed);
+ 
+ 	if (arch_library_symbol_clone(retp, libsym) < 0) {
+ 		private_library_symbol_destroy(retp);
+@@ -203,6 +208,11 @@ library_symbol_equal_cb(struct library_symbol *libsym, void *u)
+ 	return library_symbol_cmp(libsym, standard) == 0 ? CBS_STOP : CBS_CONT;
+ }
+ 
++enum callback_status
++library_symbol_named_cb(struct library_symbol *libsym, void *name)
++{
++	return strcmp(libsym->name, name) == 0 ? CBS_STOP : CBS_CONT;
++}
+ 
+ static void
+ private_library_init(struct library *lib, enum library_type type)
+@@ -221,6 +231,7 @@ private_library_init(struct library *lib, enum library_type type)
+ 	lib->own_pathname = 0;
+ 
+ 	lib->symbols = NULL;
++	lib->exported_names = NULL;
+ 	lib->type = type;
+ }
+ 
+@@ -231,6 +242,18 @@ library_init(struct library *lib, enum library_type type)
+ 	arch_library_init(lib);
+ }
+ 
++static int
++library_exported_name_clone(struct library_exported_name *retp,
++			    struct library_exported_name *exnm)
++{
++	char *name = exnm->own_name ? strdup(exnm->name) : (char *)exnm->name;
++	if (name == NULL)
++		return -1;
++	retp->name = name;
++	retp->own_name = exnm->own_name;
++	return 0;
++}
++
+ int
+ library_clone(struct library *retp, struct library *lib)
+ {
+@@ -249,20 +272,41 @@ library_clone(struct library *retp, struct library *lib)
+ 	library_set_soname(retp, pathname, lib->own_pathname);
+ 	arch_library_clone(retp, lib);
+ 
+-	struct library_symbol *it;
+-	struct library_symbol **nsymp = &retp->symbols;
+-	for (it = lib->symbols; it != NULL; it = it->next) {
+-		*nsymp = malloc(sizeof(**nsymp));
+-		if (*nsymp == NULL
+-		    || library_symbol_clone(*nsymp, it) < 0) {
+-			/* Release what we managed to allocate.  */
+-			library_destroy(retp);
+-			return -1;
++	/* Clone symbols.  */
++	{
++		struct library_symbol *it;
++		struct library_symbol **nsymp = &retp->symbols;
++		for (it = lib->symbols; it != NULL; it = it->next) {
++			*nsymp = malloc(sizeof(**nsymp));
++			if (*nsymp == NULL
++			    || library_symbol_clone(*nsymp, it) < 0) {
++				free(*nsymp);
++			fail:
++				/* Release what we managed to allocate.  */
++				library_destroy(retp);
++				return -1;
++			}
++
++			(*nsymp)->lib = retp;
++			nsymp = &(*nsymp)->next;
+ 		}
++	}
+ 
+-		(*nsymp)->lib = retp;
+-		nsymp = &(*nsymp)->next;
++	/* Clone exported names.  */
++	{
++		struct library_exported_name *it;
++		struct library_exported_name **nnamep = &retp->exported_names;
++		for (it = lib->exported_names; it != NULL; it = it->next) {
++			*nnamep = malloc(sizeof(**nnamep));
++			if (*nnamep == NULL
++			    || library_exported_name_clone(*nnamep, it) < 0) {
++				free(*nnamep);
++				goto fail;
++			}
++			nnamep = &(*nnamep)->next;
++		}
+ 	}
++
+ 	return 0;
+ }
+ 
+diff --git a/library.h b/library.h
+index f207502..eb986ea 100644
+--- a/library.h
++++ b/library.h
+@@ -38,13 +38,34 @@ enum toplt {
+ unsigned int target_address_hash(const void *key);
+ int target_address_cmp(const void *key1, const void *key2);
+ 
++/* For handling -l.  */
++struct library_exported_name {
++	struct library_exported_name *next;
++	const char *name;
++	int own_name : 1;
++};
++
+ struct library_symbol {
+ 	struct library_symbol *next;
+ 	struct library *lib;
+ 	const char *name;
+ 	arch_addr_t enter_addr;
+ 	enum toplt plt_type;
+-	char own_name;
++	int own_name : 1;
++
++	/* This is relevant for PLT symbols.  Latent PLT symbols are
++	 * those that don't match any of the -e rules, but that might
++	 * potentially become active if a library implementing them
++	 * appears that matches a -l rule.  Ltrace core is responsible
++	 * for clearing latent flag.  */
++	int latent : 1;
++
++	/* Delayed symbols are those for which a breakpoint shouldn't
++	 * be enabled yet.  They are similar to latent symbols, but
++	 * backend is responsible for clearing the delayed flag.  See
++	 * proc_activate_delayed_symbol.  */
++	int delayed : 1;
++
+ 	struct arch_library_symbol_data arch;
+ };
+ 
+@@ -82,6 +103,11 @@ void library_symbol_set_name(struct library_symbol *libsym,
+ enum callback_status library_symbol_equal_cb(struct library_symbol *libsym,
+ 					     void *standard);
+ 
++/* A function that can be used as library_each_symbol callback.  Looks
++ * for a symbol SYM for which strcmp(SYM->name, NAME) == 0.  */
++enum callback_status library_symbol_named_cb(struct library_symbol *libsym,
++					     void *name);
++
+ enum library_type {
+ 	LT_LIBTYPE_MAIN,
+ 	LT_LIBTYPE_DSO,
+@@ -98,8 +124,7 @@ struct library {
+ 	 * they have the same key.  */
+ 	arch_addr_t key;
+ 
+-	/* Address where the library is mapped.  Two library objects
+-	 * are considered equal, if they have the same base.  */
++	/* Address where the library is mapped.  */
+ 	arch_addr_t base;
+ 
+ 	/* Absolute address of the entry point.  Useful for main
+@@ -111,9 +136,16 @@ struct library {
+ 	/* Address of PT_DYNAMIC segment.  */
+ 	arch_addr_t dyn_addr;
+ 
+-	/* Symbols associated with the library.  */
++	/* Symbols associated with the library.  This includes a
++	 * symbols that don't have a breakpoint attached (yet).  */
+ 	struct library_symbol *symbols;
+ 
++	/* List of names that this library implements, and that match
++	 * -l filter.  Each time a new library is mapped, its list of
++	 * exports is examined, and corresponding PLT slots are
++	 * enabled.  */
++	struct library_exported_name *exported_names;
++
+ 	const char *soname;
+ 	const char *pathname;
+ 
+diff --git a/ltrace-elf.c b/ltrace-elf.c
+index bc99c6a..cd88581 100644
+--- a/ltrace-elf.c
++++ b/ltrace-elf.c
+@@ -63,7 +63,7 @@ arch_elf_destroy(struct ltelf *lte)
+ }
+ #endif
+ 
+-int
++static int
+ default_elf_add_plt_entry(struct Process *proc, struct ltelf *lte,
+ 			  const char *a_name, GElf_Rela *rela, size_t ndx,
+ 			  struct library_symbol **ret)
+@@ -91,6 +91,7 @@ default_elf_add_plt_entry(struct Process *proc, struct ltelf *lte,
+ 		goto fail;
+ 	}
+ 
++	libsym->next = *ret;
+ 	*ret = libsym;
+ 	return 0;
+ }
+@@ -510,39 +511,68 @@ do_close_elf(struct ltelf *lte) {
+ 	close(lte->fd);
+ }
+ 
++#ifndef ARCH_HAVE_GET_SYMINFO
++int
++arch_get_sym_info(struct ltelf *lte, const char *filename,
++		  size_t sym_index, GElf_Rela *rela, GElf_Sym *sym)
++{
++	int i = sym_index;
++	GElf_Rel rel;
++	void *ret;
++
++	if (lte->relplt->d_type == ELF_T_REL) {
++		ret = gelf_getrel(lte->relplt, i, &rel);
++		rela->r_offset = rel.r_offset;
++		rela->r_info = rel.r_info;
++		rela->r_addend = 0;
++	} else {
++		ret = gelf_getrela(lte->relplt, i, rela);
++	}
++
++	if (ret == NULL
++	    || ELF64_R_SYM(rela->r_info) >= lte->dynsym_count
++	    || gelf_getsym(lte->dynsym, ELF64_R_SYM(rela->r_info),
++			   sym) == NULL) {
++		fprintf(stderr,
++			"Couldn't get relocation from \"%s\": %s\n",
++			filename, elf_errmsg(-1));
++		exit(EXIT_FAILURE);
++	}
++
++	return 0;
++}
++#endif
++
++static void
++mark_chain_latent(struct library_symbol *libsym)
++{
++	for (; libsym != NULL; libsym = libsym->next) {
++		debug(DEBUG_FUNCTION, "marking %s latent", libsym->name);
++		libsym->latent = 1;
++	}
++}
++
+ static int
+ populate_plt(struct Process *proc, const char *filename,
+-	     struct ltelf *lte, struct library *lib)
++	     struct ltelf *lte, struct library *lib,
++	     int latent_plts)
+ {
+ 	size_t i;
+ 	for (i = 0; i < lte->relplt_count; ++i) {
+-		GElf_Rel rel;
+ 		GElf_Rela rela;
+ 		GElf_Sym sym;
+-		void *ret;
+-
+-		if (lte->relplt->d_type == ELF_T_REL) {
+-			ret = gelf_getrel(lte->relplt, i, &rel);
+-			rela.r_offset = rel.r_offset;
+-			rela.r_info = rel.r_info;
+-			rela.r_addend = 0;
+-		} else {
+-			ret = gelf_getrela(lte->relplt, i, &rela);
+-		}
+ 
+-		if (ret == NULL
+-		    || ELF64_R_SYM(rela.r_info) >= lte->dynsym_count
+-		    || gelf_getsym(lte->dynsym, ELF64_R_SYM(rela.r_info),
+-				   &sym) == NULL) {
+-			fprintf(stderr,
+-				"Couldn't get relocation from \"%s\": %s\n",
+-				filename, elf_errmsg(-1));
+-			exit(EXIT_FAILURE);
+-		}
++		if (arch_get_sym_info(lte, filename, i, &rela, &sym) < 0)
++			continue; /* Skip this entry.  */
+ 
+ 		char const *name = lte->dynstr + sym.st_name;
+ 
+-		if (!filter_matches_symbol(options.plt_filter, name, lib))
++		/* If the symbol wasn't matched, reject it, unless we
++		 * need to keep latent PLT breakpoints for tracing
++		 * exports.  */
++		int matched = filter_matches_symbol(options.plt_filter,
++						    name, lib);
++		if (!matched && !latent_plts)
+ 			continue;
+ 
+ 		struct library_symbol *libsym = NULL;
+@@ -556,8 +586,14 @@ populate_plt(struct Process *proc, const char *filename,
+ 				return -1;
+ 			/* fall-through */
+ 		case plt_ok:
+-			if (libsym != NULL)
++			if (libsym != NULL) {
++				/* If we are adding those symbols just
++				 * for tracing exports, mark them all
++				 * latent.  */
++				if (!matched)
++					mark_chain_latent(libsym);
+ 				library_add_symbol(lib, libsym);
++			}
+ 		}
+ 	}
+ 	return 0;
+@@ -583,11 +619,24 @@ unique_symbol_cmp(const void *key, const void *val)
+ 	return sym_key->addr != sym_val->addr;
+ }
+ 
++static enum callback_status
++symbol_with_address(struct library_symbol *sym, void *addrptr)
++{
++	return sym->enter_addr == *(arch_addr_t *)addrptr
++		? CBS_STOP : CBS_CONT;
++}
++
+ static int
+ populate_this_symtab(struct Process *proc, const char *filename,
+ 		     struct ltelf *lte, struct library *lib,
+-		     Elf_Data *symtab, const char *strtab, size_t size)
++		     Elf_Data *symtab, const char *strtab, size_t size,
++		     struct library_exported_name **names)
+ {
++	/* If a valid NAMES is passed, we pass in *NAMES a list of
++	 * symbol names that this library exports.  */
++	if (names != NULL)
++		*names = NULL;
++
+ 	/* Using sorted array would be arguably better, but this
+ 	 * should be well enough for the number of symbols that we
+ 	 * typically deal with.  */
+@@ -628,6 +677,7 @@ populate_this_symtab(struct Process *proc, const char *filename,
+ 		    || sym.st_shndx == STN_UNDEF)
+ 			continue;
+ 
++		/* Find symbol name and snip version.  */
+ 		const char *orig_name = strtab + sym.st_name;
+ 		const char *version = strchr(orig_name, '@');
+ 		size_t len = version != NULL ? (assert(version > orig_name),
+@@ -637,6 +687,27 @@ populate_this_symtab(struct Process *proc, const char *filename,
+ 		memcpy(name, orig_name, len);
+ 		name[len] = 0;
+ 
++		/* If we are interested in exports, store this name.  */
++		char *name_copy = NULL;
++		if (names != NULL) {
++			struct library_exported_name *export = NULL;
++			name_copy = strdup(name);
++
++			if (name_copy == NULL
++			    || (export = malloc(sizeof(*export))) == NULL) {
++				free(name_copy);
++				fprintf(stderr, "Couldn't store symbol %s.  "
++					"Tracing may be incomplete.\n", name);
++			} else {
++				export->name = name_copy;
++				export->own_name = 1;
++				export->next = *names;
++				*names = export;
++			}
++		}
++
++		/* If the symbol is not matched, skip it.  We already
++		 * stored it to export list above.  */
+ 		if (!filter_matches_symbol(options.static_filter, name, lib))
+ 			continue;
+ 
+@@ -658,15 +729,21 @@ populate_this_symtab(struct Process *proc, const char *filename,
+ 		}
+ 
+ 		char *full_name;
++		int own_full_name = 1;
+ 		if (lib->type != LT_LIBTYPE_MAIN) {
+ 			full_name = malloc(strlen(name) + 1 + lib_len + 1);
+ 			if (full_name == NULL)
+ 				goto fail;
+ 			sprintf(full_name, "%s@%s", name, lib->soname);
+ 		} else {
+-			full_name = strdup(name);
+-			if (full_name == NULL)
+-				goto fail;
++			if (name_copy == NULL) {
++				full_name = strdup(name);
++				if (full_name == NULL)
++					goto fail;
++			} else {
++				full_name = name_copy;
++				own_full_name = 0;
++			}
+ 		}
+ 
+ 		/* Look whether we already have a symbol for this
+@@ -679,8 +756,9 @@ populate_this_symtab(struct Process *proc, const char *filename,
+ 		if (unique->libsym == NULL) {
+ 			struct library_symbol *libsym = malloc(sizeof(*libsym));
+ 			if (libsym == NULL
+-			    || library_symbol_init(libsym, naddr, full_name,
+-						   1, LS_TOPLT_NONE) < 0) {
++			    || library_symbol_init(libsym, naddr,
++						   full_name, own_full_name,
++						   LS_TOPLT_NONE) < 0) {
+ 				--num_symbols;
+ 				goto fail;
+ 			}
+@@ -688,35 +766,60 @@ populate_this_symtab(struct Process *proc, const char *filename,
+ 			unique->addr = naddr;
+ 
+ 		} else if (strlen(full_name) < strlen(unique->libsym->name)) {
+-			library_symbol_set_name(unique->libsym, full_name, 1);
++			library_symbol_set_name(unique->libsym,
++						full_name, own_full_name);
+ 
+-		} else {
++		} else if (own_full_name) {
+ 			free(full_name);
+ 		}
+ 	}
+ 
++	/* Now we do the union of this set of unique symbols with
++	 * what's already in the library.  */
+ 	for (i = 0; i < num_symbols; ++i) {
+-		assert(symbols[i].libsym != NULL);
+-		library_add_symbol(lib, symbols[i].libsym);
++		struct library_symbol *this_sym = symbols[i].libsym;
++		assert(this_sym != NULL);
++		struct library_symbol *other
++			= library_each_symbol(lib, NULL, symbol_with_address,
++					      &this_sym->enter_addr);
++		if (other != NULL) {
++			library_symbol_destroy(this_sym);
++			free(this_sym);
++			symbols[i].libsym = NULL;
++		}
+ 	}
+ 
+-	free(symbols);
++	for (i = 0; i < num_symbols; ++i)
++		if (symbols[i].libsym != NULL)
++			library_add_symbol(lib, symbols[i].libsym);
+ 
++	free(symbols);
+ 	return 0;
+ }
+ 
+ static int
+ populate_symtab(struct Process *proc, const char *filename,
+-		struct ltelf *lte, struct library *lib)
++		struct ltelf *lte, struct library *lib,
++		int symtabs, int exports)
+ {
+-	if (lte->symtab != NULL && lte->strtab != NULL)
+-		return populate_this_symtab(proc, filename, lte, lib,
+-					    lte->symtab, lte->strtab,
+-					    lte->symtab_count);
+-	else
+-		return populate_this_symtab(proc, filename, lte, lib,
+-					    lte->dynsym, lte->dynstr,
+-					    lte->dynsym_count);
++	int status;
++	if (symtabs && lte->symtab != NULL && lte->strtab != NULL
++	    && (status = populate_this_symtab(proc, filename, lte, lib,
++					      lte->symtab, lte->strtab,
++					      lte->symtab_count, NULL)) < 0)
++		return status;
++
++	/* Check whether we want to trace symbols implemented by this
++	 * library (-l).  */
++	struct library_exported_name **names = NULL;
++	if (exports) {
++		debug(DEBUG_FUNCTION, "-l matches %s", lib->soname);
++		names = &lib->exported_names;
++	}
++
++	return populate_this_symtab(proc, filename, lte, lib,
++				    lte->dynsym, lte->dynstr,
++				    lte->dynsym_count, names);
+ }
+ 
+ int
+@@ -776,12 +879,28 @@ ltelf_read_library(struct library *lib, struct Process *proc,
+ 	 * arch_addr_t becomes integral type.  */
+ 	lib->dyn_addr = (arch_addr_t)(uintptr_t)lte.dyn_addr;
+ 
+-	if (filter_matches_library(options.plt_filter, lib)
+-	    && populate_plt(proc, filename, &lte, lib) < 0)
++	/* There are two reasons that we need to inspect symbol tables
++	 * or populate PLT entries.  Either the user requested
++	 * corresponding tracing features (respectively -x and -e), or
++	 * they requested tracing exported symbols (-l).
++	 *
++	 * In the latter case we need to keep even those PLT slots
++	 * that are not requested by -e (but we keep them latent).  We
++	 * also need to inspect .dynsym to find what exports this
++	 * library provide, to turn on existing latent PLT
++	 * entries.  */
++
++	int plts = filter_matches_library(options.plt_filter, lib);
++	if ((plts || options.export_filter != NULL)
++	    && populate_plt(proc, filename, &lte, lib,
++			    options.export_filter != NULL) < 0)
+ 		goto fail;
+ 
+-	if (filter_matches_library(options.static_filter, lib)
+-	    && populate_symtab(proc, filename, &lte, lib) < 0)
++	int exports = filter_matches_library(options.export_filter, lib);
++	int symtabs = filter_matches_library(options.static_filter, lib);
++	if ((symtabs || exports)
++	    && populate_symtab(proc, filename, &lte, lib,
++			       symtabs, exports) < 0)
+ 		goto fail;
+ 
+ done:
+diff --git a/ltrace-elf.h b/ltrace-elf.h
+index c560bb8..7aba933 100644
+--- a/ltrace-elf.h
++++ b/ltrace-elf.h
+@@ -71,10 +71,6 @@ 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);
+ 
+-int default_elf_add_plt_entry(struct Process *proc, struct ltelf *lte,
+-			      const char *a_name, GElf_Rela *rela, size_t ndx,
+-			      struct library_symbol **ret);
+-
+ #if __WORDSIZE == 32
+ #define PRI_ELF_ADDR		PRIx32
+ #define GELF_ADDR_CAST(x)	(void *)(uint32_t)(x)
+diff --git a/ltrace.1 b/ltrace.1
+index fb64289..16cb03b 100644
+--- a/ltrace.1
++++ b/ltrace.1
+@@ -7,7 +7,7 @@ ltrace \- A library call tracer
+ 
+ .SH SYNOPSIS
+ .B ltrace
+-.I "[-bCfghiLrStttV] [-a column] [-A maxelts] [-D level] [-e expr] [-l filename] [-n nr] [-o filename] [-p pid] ... [-s strsize] [-u username] [-w count] [-x extern] ... [--align=column] [--debug=level] [--demangle] [--help] [--indent=nr] [--library=filename] [--no-signals] [--output=filename] [--version] [--where=NR] [command [arg ...]]"
++.I "[-bCfghiLrStttV] [-a column] [-A maxelts] [-D level] [-e expr] [-l library_pattern] [-n nr] [-o filename] [-p pid] ... [-s strsize] [-u username] [-w count] [-x extern] ... [--align=column] [--debug=level] [--demangle] [--help] [--indent=nr] [--library=library_pattern] [--no-signals] [--output=filename] [--version] [--where=NR] [command [arg ...]]"
+ 
+ .SH DESCRIPTION
+ .B ltrace
+@@ -95,16 +95,16 @@ Show a summary of the options to ltrace and exit.
+ .I \-i
+ Print the instruction pointer at the time of the library call.
+ .TP
+-.I \-l, \-\-library filename
+-Display only the symbols included in the library
+-.I filename.
+-Up to 30 library names can be specified with several instances
+-of this option.
++.I \-l, \-\-library library_pattern
++Display only the symbols implemented by libraries that match
++.I library_pattern.
++Multiple library patters can be specified with several instances of
++this option.  Syntax of library_pattern is described in section
++\fBFILTER EXPRESSIONS\fR.
+ .TP
+ .I \-L
+-DON'T display library calls (use it with the
+-.I \-S
+-option).
++When no -e option is given, don't assume the default action of
++\fB at MAIN\fR.
+ .TP
+ .I \-n, \-\-indent nr
+ Indent trace output by
+diff --git a/options.c b/options.c
+index 87dddb0..40bac34 100644
+--- a/options.c
++++ b/options.c
+@@ -74,7 +74,7 @@ usage(void) {
+ 		"  -F, --config=FILE   load alternate configuration file (may be repeated).\n"
+ 		"  -h, --help          display this help and exit.\n"
+ 		"  -i                  print instruction pointer at time of library call.\n"
+-		"  -l, --library=FILE  print library calls from this library only.\n"
++		"  -l, --library=FILE  only trace symbols implemented by this library.\n"
+ 		"  -L                  do NOT display library calls.\n"
+ 		"  -n, --indent=NR     indent output by NR spaces for each call level nesting.\n"
+ 		"  -o, --output=FILE   write the trace output to that file.\n"
+@@ -163,6 +163,34 @@ guess_cols(void) {
+ 	}
+ }
+ 
++static int
++compile_libname(const char *expr, const char *a_lib, int lib_re_p,
++		struct filter_lib_matcher *matcher)
++{
++	if (strcmp(a_lib, "MAIN") == 0) {
++		filter_lib_matcher_main_init(matcher);
++	} else {
++		/* Add ^ and $ to the library expression as well.  */
++		char lib[strlen(a_lib) + 3];
++		sprintf(lib, "^%s$", a_lib);
++
++		enum filter_lib_matcher_type type
++			= lib[0] == '/' ? FLM_PATHNAME : FLM_SONAME;
++
++		regex_t lib_re;
++		int status = (lib_re_p ? regcomp : globcomp)(&lib_re, lib, 0);
++		if (status != 0) {
++			char buf[100];
++			regerror(status, &lib_re, buf, sizeof buf);
++			fprintf(stderr, "Rule near '%s' will be ignored: %s.\n",
++				expr, buf);
++			return -1;
++		}
++		filter_lib_matcher_name_init(matcher, type, lib_re);
++	}
++	return 0;
++}
++
+ static void
+ add_filter_rule(struct filter *filt, const char *expr,
+ 		enum filter_rule_type type,
+@@ -182,14 +210,14 @@ add_filter_rule(struct filter *filt, const char *expr,
+ 	}
+ 
+ 	regex_t symbol_re;
+-	int status;
+ 	{
+ 		/* Add ^ to the start of expression and $ to the end, so that
+ 		 * we match the whole symbol name.  Let the user write the "*"
+ 		 * explicitly if they wish.  */
+ 		char sym[strlen(a_sym) + 3];
+ 		sprintf(sym, "^%s$", a_sym);
+-		status = (sym_re_p ? regcomp : globcomp)(&symbol_re, sym, 0);
++		int status = (sym_re_p ? regcomp : globcomp)
++			(&symbol_re, sym, 0);
+ 		if (status != 0) {
+ 			char buf[100];
+ 			regerror(status, &symbol_re, buf, sizeof buf);
+@@ -199,28 +227,9 @@ add_filter_rule(struct filter *filt, const char *expr,
+ 		}
+ 	}
+ 
+-	if (strcmp(a_lib, "MAIN") == 0) {
+-		filter_lib_matcher_main_init(matcher);
+-	} else {
+-		/* Add ^ and $ to the library expression as well.  */
+-		char lib[strlen(a_lib) + 3];
+-		sprintf(lib, "^%s$", a_lib);
+-
+-		enum filter_lib_matcher_type type
+-			= lib[0] == '/' ? FLM_PATHNAME : FLM_SONAME;
+-
+-		regex_t lib_re;
+-		status = (lib_re_p ? regcomp : globcomp)(&lib_re, lib, 0);
+-		if (status != 0) {
+-			char buf[100];
+-			regerror(status, &lib_re, buf, sizeof buf);
+-			fprintf(stderr, "rule near '%s' will be ignored: %s\n",
+-				expr, buf);
+-
+-			regfree(&symbol_re);
+-			goto fail;
+-		}
+-		filter_lib_matcher_name_init(matcher, type, lib_re);
++	if (compile_libname(expr, a_lib, lib_re_p, matcher) < 0) {
++		regfree(&symbol_re);
++		goto fail;
+ 	}
+ 
+ 	filter_rule_init(rule, type, matcher, symbol_re);
+@@ -228,16 +237,36 @@ add_filter_rule(struct filter *filt, const char *expr,
+ }
+ 
+ static int
+-parse_filter(struct filter *filt, char *expr)
++grok_libname_pattern(char **libnamep, char **libendp)
+ {
+-	/* Filter is a chain of sym at lib rules separated by '-'.  If
+-	 * the filter expression starts with '-', the missing initial
+-	 * rule is implicitly *@*.  */
++	char *libname = *libnamep;
++	char *libend = *libendp;
++
++	if (libend[0] != '/')
++		return 0;
++
++	*libend-- = 0;
++	if (libname != libend && libname[0] == '/')
++		++libname;
++	else
++		fprintf(stderr, "Unmatched '/' in library name.\n");
++
++	*libendp = libend;
++	*libnamep = libname;
++	return 1;
++}
++
++static int
++parse_filter(struct filter *filt, char *expr, int operators)
++{
++	/* Filter is a chain of sym at lib rules separated by '-' or '+'.
++	 * If the filter expression starts with '-', the missing
++	 * initial rule is implicitly *@*.  */
+ 
+ 	enum filter_rule_type type = FR_ADD;
+ 
+ 	while (*expr != 0) {
+-		size_t s = strcspn(expr, "@-+");
++		size_t s = strcspn(expr, "-+@" + (operators ? 0 : 2));
+ 		char *symname = expr;
+ 		char *libname;
+ 		char *next = expr + s + 1;
+@@ -256,7 +285,7 @@ parse_filter(struct filter *filt, char *expr)
+ 		} else {
+ 			assert(expr[s] == '@');
+ 			expr[s] = 0;
+-			s = strcspn(next, "-+");
++			s = strcspn(next, "-+" + (operators ? 0 : 2));
+ 			if (s == 0) {
+ 				libname = "*";
+ 				expr = next;
+@@ -311,15 +340,8 @@ parse_filter(struct filter *filt, char *expr)
+ 		/* If libname ends in '/', then we expect '/' in the
+ 		 * beginning too.  Otherwise the initial '/' is part
+ 		 * of absolute file name.  */
+-		if (!lib_is_re && libend[0] == '/') {
+-			lib_is_re = 1;
+-			*libend-- = 0;
+-			if (libname != libend && libname[0] == '/')
+-				++libname;
+-			else
+-				fprintf(stderr, "unmatched '/'"
+-					" in library name\n");
+-		}
++		if (!lib_is_re)
++			lib_is_re = grok_libname_pattern(&libname, &libend);
+ 
+ 		if (*symname == 0) /* /@AA/ */
+ 			symname = "*";
+@@ -335,7 +357,7 @@ parse_filter(struct filter *filt, char *expr)
+ }
+ 
+ static struct filter *
+-recursive_parse_chain(char *expr)
++recursive_parse_chain(char *expr, int operators)
+ {
+ 	struct filter *filt = malloc(sizeof(*filt));
+ 	if (filt == NULL) {
+@@ -345,7 +367,7 @@ recursive_parse_chain(char *expr)
+ 	}
+ 
+ 	filter_init(filt);
+-	if (parse_filter(filt, expr) < 0) {
++	if (parse_filter(filt, expr, operators) < 0) {
+ 		fprintf(stderr, "Filter '%s' will be ignored.\n", expr);
+ 		free(filt);
+ 		filt = NULL;
+@@ -354,6 +376,14 @@ recursive_parse_chain(char *expr)
+ 	return filt;
+ }
+ 
++static struct filter **
++slist_chase_end(struct filter **begin)
++{
++	for (; *begin != NULL; begin = &(*begin)->next)
++		;
++	return begin;
++}
++
+ static void
+ parse_filter_chain(const char *expr, struct filter **retp)
+ {
+@@ -367,10 +397,7 @@ parse_filter_chain(const char *expr, struct filter **retp)
+ 	if (str[0] == '!')
+ 		str[0] = '-';
+ 
+-	struct filter **tailp;
+-	for (tailp = retp; *tailp != NULL; tailp = &(*tailp)->next)
+-		;
+-	*tailp = recursive_parse_chain(str);
++	*slist_chase_end(retp) = recursive_parse_chain(str, 1);
+ }
+ 
+ char **
+@@ -477,10 +504,16 @@ process_options(int argc, char **argv)
+ 		case 'i':
+ 			opt_i++;
+ 			break;
+-		case 'l':
+-			// XXX TODO
+-			fprintf(stderr, "-l support not yet implemented\n");
++
++		case 'l': {
++			size_t patlen = strlen(optarg);
++			char buf[patlen + 2];
++			sprintf(buf, "@%s", optarg);
++			*slist_chase_end(&options.export_filter)
++				= recursive_parse_chain(buf, 0);
+ 			break;
++		}
++
+ 		case 'L':
+ 			libcalls = 0;
+ 			break;
+@@ -585,13 +618,21 @@ process_options(int argc, char **argv)
+ 		opt_F = egg;
+ 	}
+ 
+-	/* Set default filter.  Use @MAIN for now, as that's what
+-	 * ltrace used to have in the past.  XXX Maybe we should make
+-	 * this "*" instead.  */
+-	if (options.plt_filter == NULL && libcalls) {
++	/* If neither -e, nor -l, nor -L are used, set default -e.
++	 * Use @MAIN for now, as that's what ltrace used to have in
++	 * the past.  XXX Maybe we should make this "*" instead.  */
++	if (libcalls
++	    && options.plt_filter == NULL
++	    && options.export_filter == NULL) {
+ 		parse_filter_chain("@MAIN", &options.plt_filter);
+ 		options.hide_caller = 1;
+ 	}
++	if (!libcalls && options.plt_filter != NULL) {
++		fprintf(stderr,
++			"%s: Option -L can't be used with -e or -l.\n",
++			progname);
++		err_usage();
++	}
+ 
+ 	if (!opt_p && argc < 1) {
+ 		fprintf(stderr, "%s: too few arguments\n", progname);
+diff --git a/options.h b/options.h
+index 0806928..176ce90 100644
+--- a/options.h
++++ b/options.h
+@@ -21,6 +21,11 @@ struct options_t {
+ #endif /* defined(HAVE_LIBUNWIND) */
+ 	struct filter *plt_filter;
+ 	struct filter *static_filter;
++
++	/* A filter matching library names of libraries, whose
++	 * exported symbols we wish to trace.  */
++	struct filter *export_filter;
++
+ 	int hide_caller; /* Whether caller library should be hidden.  */
+ };
+ extern struct options_t options;
+diff --git a/proc.c b/proc.c
+index bf26180..3dab1e2 100644
+--- a/proc.c
++++ b/proc.c
+@@ -629,12 +629,18 @@ destroy_event_handler(Process * proc)
+ 	proc->event_handler = NULL;
+ }
+ 
+-static enum callback_status
+-breakpoint_for_symbol(struct library_symbol *libsym, void *data)
++static int
++breakpoint_for_symbol(struct library_symbol *libsym, struct Process *proc)
+ {
+-	struct Process *proc = data;
++	arch_addr_t bp_addr;
+ 	assert(proc->leader == proc);
+ 
++	/* Don't enable latent or delayed symbols.  */
++	if (libsym->latent || libsym->delayed)
++		return 0;
++
++	bp_addr = sym2addr(proc, libsym);
++
+ 	/* If there is an artificial breakpoint on the same address,
+ 	 * its libsym will be NULL, and we can smuggle our libsym
+ 	 * there.  That artificial breakpoint is there presumably for
+@@ -648,19 +654,19 @@ breakpoint_for_symbol(struct library_symbol *libsym, void *data)
+ 	 * the two: delete the one now in the dictionary, swap values
+ 	 * around, and put the new breakpoint back in.  */
+ 	struct breakpoint *bp = dict_find_entry(proc->breakpoints,
+-						libsym->enter_addr);
++						bp_addr);
+ 	if (bp != NULL) {
+ 		assert(bp->libsym == NULL);
+ 		bp->libsym = libsym;
+-		return CBS_CONT;
++		return 0;
+ 	}
+ 
+ 	bp = malloc(sizeof(*bp));
+ 	if (bp == NULL
+-	    || breakpoint_init(bp, proc, libsym->enter_addr, libsym) < 0) {
++	    || breakpoint_init(bp, proc, bp_addr, libsym) < 0) {
+ 	fail:
+ 		free(bp);
+-		return CBS_FAIL;
++		return -1;
+ 	}
+ 	if (proc_add_breakpoint(proc, bp) < 0) {
+ 		breakpoint_destroy(bp);
+@@ -673,6 +679,47 @@ breakpoint_for_symbol(struct library_symbol *libsym, void *data)
+ 		goto fail;
+ 	}
+ 
++	return 0;
++}
++
++static enum callback_status
++cb_breakpoint_for_symbol(struct library_symbol *libsym, void *data)
++{
++	return breakpoint_for_symbol(libsym, data) < 0 ? CBS_FAIL : CBS_CONT;
++}
++
++static int
++proc_activate_latent_symbol(struct Process *proc,
++			    struct library_symbol *libsym)
++{
++	assert(libsym->latent);
++	libsym->latent = 0;
++	return breakpoint_for_symbol(libsym, proc);
++}
++
++int
++proc_activate_delayed_symbol(struct Process *proc,
++			     struct library_symbol *libsym)
++{
++	assert(libsym->delayed);
++	libsym->delayed = 0;
++	return breakpoint_for_symbol(libsym, proc);
++}
++
++static enum callback_status
++activate_latent_in(struct Process *proc, struct library *lib, void *data)
++{
++	struct library_exported_name *exported;
++	for (exported = data; exported != NULL; exported = exported->next) {
++		struct library_symbol *libsym = NULL;
++		while ((libsym = library_each_symbol(lib, libsym,
++						     library_symbol_named_cb,
++						     (void *)exported->name))
++		       != NULL)
++			if (libsym->latent
++			    && proc_activate_latent_symbol(proc, libsym) < 0)
++				return CBS_FAIL;
++	}
+ 	return CBS_CONT;
+ }
+ 
+@@ -685,10 +732,22 @@ proc_add_library(struct Process *proc, struct library *lib)
+ 	debug(DEBUG_PROCESS, "added library %s@%p (%s) to %d",
+ 	      lib->soname, lib->base, lib->pathname, proc->pid);
+ 
++	/* Insert breakpoints for all active (non-latent) symbols.  */
+ 	struct library_symbol *libsym = NULL;
+-	while ((libsym = library_each_symbol(lib, libsym, breakpoint_for_symbol,
++	while ((libsym = library_each_symbol(lib, libsym,
++					     cb_breakpoint_for_symbol,
+ 					     proc)) != NULL)
+-		fprintf(stderr, "couldn't insert breakpoint for %s to %d: %s",
++		fprintf(stderr, "Couldn't insert breakpoint for %s to %d: %s.",
++			libsym->name, proc->pid, strerror(errno));
++
++	/* Look through export list of the new library and compare it
++	 * with latent symbols of all libraries (including this
++	 * library itself).  */
++	struct library *lib2 = NULL;
++	while ((lib2 = proc_each_library(proc, lib2, activate_latent_in,
++					 lib->exported_names)) != NULL)
++		fprintf(stderr,
++			"Couldn't activate latent symbols for %s in %d: %s.",
+ 			libsym->name, proc->pid, strerror(errno));
+ }
+ 
+diff --git a/proc.h b/proc.h
+index b61e420..9864e1b 100644
+--- a/proc.h
++++ b/proc.h
+@@ -206,6 +206,14 @@ void proc_add_library(struct Process *proc, struct library *lib);
+  * was found and unlinked, otherwise returns a negative value.  */
+ int proc_remove_library(struct Process *proc, struct library *lib);
+ 
++/* Clear a delayed flag.  If a symbol is neither latent, nor delayed,
++ * a breakpoint is inserted for it.  Returns 0 if the activation was
++ * successful or a negative value if it failed.  Note that if a symbol
++ * is both latent and delayed, this will not enable the corresponding
++ * breakpoint.  */
++int proc_activate_delayed_symbol(struct Process *proc,
++				 struct library_symbol *libsym);
++
+ /* Iterate through the libraries of PROC.  See callback.h for notes on
+  * iteration interfaces.  */
+ struct library *proc_each_library(struct Process *proc, struct library *start,
+diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
+index 0829bdb..cdc4062 100644
+--- a/sysdeps/linux-gnu/trace.c
++++ b/sysdeps/linux-gnu/trace.c
+@@ -930,7 +930,7 @@ continue_after_breakpoint(Process *proc, struct breakpoint *sbp)
+ 	if (sbp->enabled == 0) {
+ 		continue_process(proc->pid);
+ 	} else {
+-#if defined __sparc__  || defined __ia64___ || defined __mips__
++#if defined __sparc__  || defined __ia64___
+ 		/* we don't want to singlestep here */
+ 		continue_process(proc->pid);
+ #else
+diff --git a/testsuite/ltrace.main/filters.exp b/testsuite/ltrace.main/filters.exp
+index 1a9a8f7..52b569e 100644
+--- a/testsuite/ltrace.main/filters.exp
++++ b/testsuite/ltrace.main/filters.exp
+@@ -77,3 +77,15 @@ ltrace_runtest $objdir/$subdir $binfile0
+ ltrace_verify_output ${binfile0}.ltrace "filt->func1(" 1
+ ltrace_verify_output ${binfile0}.ltrace "func1 at libfilt1.so(.*)" 1
+ ltrace_verify_output ${binfile0}.ltrace "func1 resumed" 1
++
++# Check that when given -l, we don't trace symbols from other
++# libraries.
++ltrace_options "-llibfilt1.so"
++ltrace_runtest $objdir/$subdir $binfile0
++ltrace_verify_output ${binfile0}.ltrace "filt->func1(.*)" 1
++
++ltrace_options "-llibfilt1.so" "-llibfilt2.so"
++ltrace_runtest $objdir/$subdir $binfile0
++ltrace_verify_output ${binfile0}.ltrace "filt->func1(" 1
++ltrace_verify_output ${binfile0}.ltrace "libfilt1.so->func2(.*)" 1
++ltrace_verify_output ${binfile0}.ltrace "func1 resumed" 1
+diff --git a/testsuite/ltrace.main/main-threaded.exp b/testsuite/ltrace.main/main-threaded.exp
+index 5539805..4f6c25d 100644
+--- a/testsuite/ltrace.main/main-threaded.exp
++++ b/testsuite/ltrace.main/main-threaded.exp
+@@ -19,7 +19,7 @@ if { [ltrace_compile_shlib $libsrc $lib_sl debug ] != ""
+ }
+ 
+ # set options for ltrace.
+-ltrace_options "-l" "$lib_sl" "-f"
++ltrace_options "-l" "lib$testfile.so" "-f"
+ 
+ # Run PUT for ltarce.
+ set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile]
+diff --git a/testsuite/ltrace.main/main-vfork.exp b/testsuite/ltrace.main/main-vfork.exp
+index 299c5e0..989012d 100644
+--- a/testsuite/ltrace.main/main-vfork.exp
++++ b/testsuite/ltrace.main/main-vfork.exp
+@@ -19,7 +19,7 @@ if { [ltrace_compile_shlib $libsrc $lib_sl debug ] != ""
+ }
+ 
+ # set options for ltrace.
+-ltrace_options "-l" "$lib_sl" "-f"
++ltrace_options "-l" "lib$testfile.so" "-f" "-evfork"
+ 
+ # Run PUT for ltarce.
+ set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile]
+diff --git a/testsuite/ltrace.main/main.exp b/testsuite/ltrace.main/main.exp
+index 9e126bc..50c5353 100644
+--- a/testsuite/ltrace.main/main.exp
++++ b/testsuite/ltrace.main/main.exp
+@@ -19,7 +19,7 @@ if { [ltrace_compile_shlib $libsrc $lib_sl debug ] != ""
+ }
+ 
+ # set options for ltrace.
+-ltrace_options "-l" "$objdir/$subdir/libmain.so"
++ltrace_options "-l" "libmain.so"
+ 
+ # Run PUT for ltarce.
+ set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile]
+diff --git a/testsuite/ltrace.main/parameters-lib.c b/testsuite/ltrace.main/parameters-lib.c
+index 750075e..04effaf 100644
+--- a/testsuite/ltrace.main/parameters-lib.c
++++ b/testsuite/ltrace.main/parameters-lib.c
+@@ -456,3 +456,8 @@ func_hfa_d12(struct struct_hfa_d12 e)
+ {
+ 	return e;
+ }
++
++void
++func_printf(char *format, ...)
++{
++}
+diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c
+index 44d54b2..ae8e17b 100644
+--- a/testsuite/ltrace.main/parameters.c
++++ b/testsuite/ltrace.main/parameters.c
+@@ -182,10 +182,11 @@ main ()
+ 		 15, 16, 'B', 18.0, 19.0, 20.0,
+ 		 21, 22.0, 23.0, 24.0, 25.0);
+ 
+-  printf("sotnuh %d %ld %g %c\n", 5, 6L, 1.5, 'X');
+-  printf("sotnuh1 %d %ld %hd\n", 5, 6L, (short)7);
+-  printf("sotnuh2 %s %10s %10s\n", "a string", "a trimmed string", "short");
+-  printf("many_args"
++  void func_printf(char *format, ...);
++  func_printf("sotnuh %d %ld %g %c\n", 5, 6L, 1.5, 'X');
++  func_printf("sotnuh1 %d %ld %hd\n", 5, 6L, (short)7);
++  func_printf("sotnuh2 %s %10s %10s\n", "a string", "a trimmed string", "short");
++  func_printf("many_args"
+ 	 "%d %d %ld %g %c %d %g "
+ 	 "%c %d %g %d %g %c %d "
+ 	 "%hd %d %c %g %g %g "
+@@ -195,7 +196,7 @@ main ()
+ 	 (short)15, 16, 'B', 18.0, 19.0, 20.0,
+ 	 21L, 22.0, 23.0, 24.0, 25.0);
+ 
+-  printf("sotnuh3 %*s\n", 4, "a trimmed string");
++  func_printf("sotnuh3 %*s\n", 4, "a trimmed string");
+ 
+   void func_lens(int, long, short, long);
+   func_lens(22, 23, 24, 25);
+diff --git a/testsuite/ltrace.main/parameters.conf b/testsuite/ltrace.main/parameters.conf
+index 0531a6a..743237f 100644
+--- a/testsuite/ltrace.main/parameters.conf
++++ b/testsuite/ltrace.main/parameters.conf
+@@ -22,7 +22,7 @@ struct(long,long,long,long) func_struct_large(struct(long,long,long,long), struc
+ struct(char,char,long,long) func_struct_large2(struct(char,char,long,long), struct(char,char,long,long));
+ struct(long,long,char,char) func_struct_large3(struct(long,long,char,char), struct(long,long,char,char));
+ void func_many_args(int, int, long, double, char, int, float, char, int, double, int, double, char, int, short, int, char, float, float, double, long, float, float, float, float);
+-int printf(format);
++void func_printf(format);
+ void func_lens(octal, octal(long), hex(short), hex(long));
+ bool(int) func_bool(int, bool(int));
+ void func_hide(int, hide(int), hide(int), int, hide(int), int);
+diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp
+index badbe6b..367214f 100644
+--- a/testsuite/ltrace.main/parameters.exp
++++ b/testsuite/ltrace.main/parameters.exp
+@@ -20,7 +20,7 @@ if { [ltrace_compile_shlib $libsrc $lib_sl debug ] != ""
+ }
+ 
+ # set options for ltrace.
+-ltrace_options "-l" "$objdir/$subdir/libparameters.so" "-F" "$srcdir/$subdir/parameters.conf"
++ltrace_options "-l" "libparameters.so" "-F" "$srcdir/$subdir/parameters.conf"
+ 
+ # Run PUT for ltarce.
+ set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile]
+@@ -100,19 +100,19 @@ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+ set pattern "func_many_args(1, 2, 3, 4.00*, '5', 6, 7.00*, '8', 9, 10.00*, 11, 12.00*, 'A', 14, 15, 16, 'B', 18.00*, 19.00*, 20.00*, 21, 22.00*, 23.00*, 24.00*, 25.00*)"
+ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+ 
+-set pattern "printf(\\\"sotnuh %d %ld %g %c.n\\\", 5, 6, 1.500*, 'X')"
++set pattern "func_printf(\\\"sotnuh %d %ld %g %c.n\\\", 5, 6, 1.500*, 'X')"
+ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+ 
+-set pattern "printf(\\\"sotnuh1 %d %ld %hd.n\\\", 5, 6, 7)"
++set pattern "func_printf(\\\"sotnuh1 %d %ld %hd.n\\\", 5, 6, 7)"
+ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+ 
+-set pattern "printf(\\\"sotnuh2 %s %10s %10s.n\\\", \\\"a string\\\", \\\"a trimmed \\\", \\\"short\\\")"
++set pattern "func_printf(\\\"sotnuh2 %s %10s %10s.n\\\", \\\"a string\\\", \\\"a trimmed \\\", \\\"short\\\")"
+ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+ 
+-set pattern "printf(\\\"sotnuh3 %.s.n\\\", 4, \\\"a tr\\\")"
++set pattern "func_printf(\\\"sotnuh3 %.s.n\\\", 4, \\\"a tr\\\")"
+ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+ 
+-set pattern "printf(\\\"many_args%d %d %ld %g %c %d %g .*, 1, 2, 3, 4.00*, '5', 6, 7.00*, '8', 9, 10.00*, 11, 12.00*, 'A', 14, 15, 16, 'B', 18.00*, 19.00*, 20.00*, 21, 22.00*, 23.00*, 24.00*, 25.00*)"
++set pattern "func_printf(\\\"many_args%d %d %ld %g %c %d %g .*, 1, 2, 3, 4.00*, '5', 6, 7.00*, '8', 9, 10.00*, 11, 12.00*, 'A', 14, 15, 16, 'B', 18.00*, 19.00*, 20.00*, 21, 22.00*, 23.00*, 24.00*, 25.00*)"
+ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+ 
+ set pattern "func_lens(026, 027, 0x18, 0x19)"
diff --git a/ltrace-0.6.0-syscall-time.patch b/ltrace-0.6.0-syscall-time.patch
new file mode 100644
index 0000000..0a8fd3e
--- /dev/null
+++ b/ltrace-0.6.0-syscall-time.patch
@@ -0,0 +1,30 @@
+From 6aa01523f249f9763ccd71db9f46969a6e5d8cfd Mon Sep 17 00:00:00 2001
+From: Paul Buerger <pbuerger at avetec.org>
+Date: Wed, 12 Sep 2012 10:58:52 -0400
+Subject: [PATCH] reported time in system call was too large
+
+when -S and -T are specified and if the system call spans
+a second boundary, the reported time in the system call
+was too large by precisely 2 seconds
+
+Signed-off-by: Paul Buerger <pbuerger at avetec.org>
+---
+ handle_event.c |    2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/handle_event.c b/handle_event.c
+index 1720cb3..384e868 100644
+--- a/handle_event.c
++++ b/handle_event.c
+@@ -522,7 +522,7 @@ calc_time_spent(Process *proc) {
+ 	if (tv.tv_usec >= elem->time_spent.tv_usec) {
+ 		diff.tv_usec = tv.tv_usec - elem->time_spent.tv_usec;
+ 	} else {
+-		diff.tv_sec++;
++		diff.tv_sec--;
+ 		diff.tv_usec = 1000000 + tv.tv_usec - elem->time_spent.tv_usec;
+ 	}
+ 	current_time_spent = diff;
+-- 
+1.7.6.5
+
diff --git a/ltrace-0.6.0-x86_64-flatten.patch b/ltrace-0.6.0-x86_64-flatten.patch
new file mode 100644
index 0000000..e3071f5
--- /dev/null
+++ b/ltrace-0.6.0-x86_64-flatten.patch
@@ -0,0 +1,88 @@
+From 78ed40f161c102a10c6033c28ad9a80e5ffe9550 Mon Sep 17 00:00:00 2001
+From: Petr Machata <pmachata at redhat.com>
+Date: Sat, 22 Sep 2012 18:19:24 +0200
+Subject: [PATCH] Fix passing struct(float,struct(float,float)) on x86_64
+
+The problem was that we assumed that structure elements never overlap
+eightbyte boundary.  This assumption is violated by the above layout,
+where the first two floats should be passed in %xmm0 together.
+
+This case is covered by the Itanium HFA tests func_hfa_f3 and func_hfa_f4.
+---
+ ChangeLog                     |    4 +++
+ sysdeps/linux-gnu/x86/fetch.c |   46 ++++++++++++++++++++++++++++++++++++----
+ 2 files changed, 45 insertions(+), 5 deletions(-)
+
+diff --git a/sysdeps/linux-gnu/x86/fetch.c b/sysdeps/linux-gnu/x86/fetch.c
+index 8df900e..cca1638 100644
+--- a/sysdeps/linux-gnu/x86/fetch.c
++++ b/sysdeps/linux-gnu/x86/fetch.c
+@@ -411,12 +411,34 @@ get_array_field(struct arg_type_info *info, size_t emt)
+ 	return info->u.array_info.elt_type;
+ }
+ 
++static int
++flatten_structure(struct arg_type_info *flattened, struct arg_type_info *info)
++{
++	size_t i;
++	for (i = 0; i < type_struct_size(info); ++i) {
++		struct arg_type_info *field = type_struct_get(info, i);
++		assert(field != NULL);
++		switch (field->type) {
++		case ARGTYPE_STRUCT:
++			if (flatten_structure(flattened, field) < 0)
++				return -1;
++			break;
++
++		default:
++			if (type_struct_add(flattened, field, 0) < 0)
++				return -1;
++		}
++	}
++	return 0;
++}
++
+ static ssize_t
+ classify(struct Process *proc, struct fetch_context *context,
+ 	 struct arg_type_info *info, struct value *valuep, enum arg_class classes[],
+ 	 size_t sz, size_t eightbytes)
+ {
+ 	switch (info->type) {
++		struct arg_type_info flattened;
+ 	case ARGTYPE_VOID:
+ 		return 0;
+ 
+@@ -458,11 +480,25 @@ classify(struct Process *proc, struct fetch_context *context,
+ 					   get_array_field);
+ 
+ 	case ARGTYPE_STRUCT:
+-		/* N.B. "big" structs are dealt with in the
+-		 * caller.  */
+-		return classify_eightbytes(proc, context, info, valuep, classes,
+-					   type_struct_size(info),
+-					   eightbytes, type_struct_get);
++		/* N.B. "big" structs are dealt with in the caller.
++		 *
++		 * First, we need to flatten the structure.  In
++		 * struct(float,struct(float,float)), first two floats
++		 * both belong to the same eightbyte.  */
++		type_init_struct(&flattened);
++
++		ssize_t ret;
++		if (flatten_structure(&flattened, info) < 0) {
++			ret = -1;
++			goto done;
++		}
++		ret = classify_eightbytes(proc, context, &flattened,
++					  valuep, classes,
++					  type_struct_size(&flattened),
++					  eightbytes, type_struct_get);
++	done:
++		type_destroy(&flattened);
++		return ret;
+ 	}
+ 	abort();
+ }
+-- 
+1.7.6.5
+
diff --git a/ltrace.spec b/ltrace.spec
index 0575a16..8a7e433 100644
--- a/ltrace.spec
+++ b/ltrace.spec
@@ -1,7 +1,7 @@
 Summary: Tracks runtime library calls from dynamically linked executables
 Name: ltrace
 Version: 0.6.0
-Release: 18%{?dist}
+Release: 19%{?dist}
 URL: http://ltrace.alioth.debian.org/
 License: GPLv2+
 Group: Development/Debuggers
@@ -37,6 +37,11 @@ Patch21: ltrace-0.6.0-demangle.patch
 Patch22: ltrace-0.6.0-abi.patch
 Patch23: ltrace-0.6.0-abi-s390.patch
 Patch24: ltrace-0.6.0-ppc-flteqv.patch
+Patch25: ltrace-0.6.0-cleanups.patch
+Patch26: ltrace-0.6.0-syscall-time.patch
+Patch27: ltrace-0.6.0-abi-ia64.patch
+Patch28: ltrace-0.6.0-x86_64-flatten.patch
+Patch29: ltrace-0.6.0-dash-l.patch
 
 %description
 Ltrace is a debugging program which runs a specified command until the
@@ -72,6 +77,11 @@ execution of processes.
 %patch22 -p1
 %patch23 -p1
 %patch24 -p1
+%patch25 -p1
+%patch26 -p1
+%patch27 -p1
+%patch28 -p1
+%patch29 -p1
 
 %build
 # This ugly hack is necessary to build and link files for correct
@@ -99,6 +109,18 @@ echo ====================TESTING END=====================
 %config(noreplace) %{_sysconfdir}/ltrace.conf
 
 %changelog
+* Mon Oct  1 2012 Petr Machata <pmachata at redhat.com> - 0.6.0-19
+- Upstream patch for ia64 parameter passing
+  (ltrace-0.6.0-abi-ia64.patch)
+- Upstream fix for a bug in computation of time spent in a syscall
+  (ltrace-0.6.0-syscall-time.patch)
+- Upstream fix for a bug in passing struct(float,struct(float,float))
+  on x86_64 (ltrace-0.6.0-x86_64-flatten.patch)
+- Upstream patch for support of -l option (ltrace-0.6.0-dash-l.patch)
+- Several more upstream patches with random cleanups.  Those were
+  brought to Fedora to make porting of other patches easier.
+  (ltrace-0.6.0-cleanups.patch)
+
 * Thu Aug 30 2012 Petr Machata <pmachata at redhat.com> - 0.6.0-18
 - PPC64 passes floating point equivalent structures in registers
 


More information about the scm-commits mailing list