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 + + #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 + #include + +-#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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 10/3/2005 + */ + +-#include "config.h" +- + #include + #include +-#include "common.h" ++#include ++ ++#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 ++ + #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@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 + + #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 ++#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@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 + #include + +-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 ++#include ++ ++#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@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@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@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@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 +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 +--- + 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 +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 - 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 - 0.6.0-18 - PPC64 passes floating point equivalent structures in registers