[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,
+ ¶m_printf_init, ¶m_printf_next,
+ ¶m_printf_stop, ¶m_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(®);
+- }
+-
+- 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(®);
+- } 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(®);
+- }
+- }
+-
+- /* 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, <e, 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, <e, lib,
++ options.export_filter != NULL) < 0)
+ goto fail;
+
+- if (filter_matches_library(options.static_filter, lib)
+- && populate_symtab(proc, filename, <e, 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, <e, 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