From 85e550411cc87c7983a25dce57fccf632fda6f61 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Sat, 10 Nov 2012 15:14:00 +0100 Subject: [PATCH] Upstream 0.7.0 --- .gitignore | 1 + ltrace-0.5-etc-memmove.patch | 13 - ltrace-0.5-ia64-sigill.patch | 84 - ltrace-0.6.0-abi-ia64.patch | 1539 --- ltrace-0.6.0-abi-s390.patch | 1695 ---- ltrace-0.6.0-abi.patch | 13571 --------------------------- ltrace-0.6.0-cleanups.patch | 652 -- ltrace-0.6.0-clone-test.patch | 12 - ltrace-0.6.0-dash-l.patch | 1293 --- ltrace-0.6.0-dash-n.patch | 26 - ltrace-0.6.0-demangle.patch | 86 - ltrace-0.6.0-detach-sleeping.patch | 141 - ltrace-0.6.0-endian.patch | 203 - ltrace-0.6.0-exec-stripped.patch | 63 - ltrace-0.6.0-libs-fixes-1.patch | 39 - ltrace-0.6.0-libs.patch | 10240 -------------------- ltrace-0.6.0-ppc-args.patch | 30 - ltrace-0.6.0-ppc-flteqv.patch | 162 - ltrace-0.6.0-ppc-lwarx.patch | 373 - ltrace-0.6.0-process-start.patch | 1053 --- ltrace-0.6.0-return-string-n.patch | 16 - ltrace-0.6.0-selinux.patch | 212 - ltrace-0.6.0-syscall-time.patch | 30 - ltrace-0.6.0-tail-return.patch | 256 - ltrace-0.6.0-thread-races.patch | 406 - ltrace-0.6.0-threads.patch | 2797 ------ ltrace-0.6.0-vfork.patch | 1130 --- ltrace-0.6.0-x86_64-flatten.patch | 88 - ltrace-0.7.0-man5.patch | 150 + ltrace-0.7.0-ppc-insn.h.patch | 70 + ltrace.spec | 80 +- sources | 2 +- 32 files changed, 240 insertions(+), 36273 deletions(-) delete mode 100644 ltrace-0.5-etc-memmove.patch delete mode 100644 ltrace-0.5-ia64-sigill.patch delete mode 100644 ltrace-0.6.0-abi-ia64.patch delete mode 100644 ltrace-0.6.0-abi-s390.patch delete mode 100644 ltrace-0.6.0-abi.patch delete mode 100644 ltrace-0.6.0-cleanups.patch delete mode 100644 ltrace-0.6.0-clone-test.patch delete mode 100644 ltrace-0.6.0-dash-l.patch delete mode 100644 ltrace-0.6.0-dash-n.patch delete mode 100644 ltrace-0.6.0-demangle.patch delete mode 100644 ltrace-0.6.0-detach-sleeping.patch delete mode 100644 ltrace-0.6.0-endian.patch delete mode 100644 ltrace-0.6.0-exec-stripped.patch delete mode 100644 ltrace-0.6.0-libs-fixes-1.patch delete mode 100644 ltrace-0.6.0-libs.patch delete mode 100644 ltrace-0.6.0-ppc-args.patch delete mode 100644 ltrace-0.6.0-ppc-flteqv.patch delete mode 100644 ltrace-0.6.0-ppc-lwarx.patch delete mode 100644 ltrace-0.6.0-process-start.patch delete mode 100644 ltrace-0.6.0-return-string-n.patch delete mode 100644 ltrace-0.6.0-selinux.patch delete mode 100644 ltrace-0.6.0-syscall-time.patch delete mode 100644 ltrace-0.6.0-tail-return.patch delete mode 100644 ltrace-0.6.0-thread-races.patch delete mode 100644 ltrace-0.6.0-threads.patch delete mode 100644 ltrace-0.6.0-vfork.patch delete mode 100644 ltrace-0.6.0-x86_64-flatten.patch create mode 100644 ltrace-0.7.0-man5.patch create mode 100644 ltrace-0.7.0-ppc-insn.h.patch diff --git a/.gitignore b/.gitignore index 6374c2e..ec5412e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ ltrace-0.5-svn45.tar.gz ltrace-*/ /ltrace-0.6.0.tar.bz2 +/ltrace-0.7.0.tar.bz2 diff --git a/ltrace-0.5-etc-memmove.patch b/ltrace-0.5-etc-memmove.patch deleted file mode 100644 index 1e0ac28..0000000 --- a/ltrace-0.5-etc-memmove.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff -up ltrace-0.5/etc/ltrace.conf\~ ltrace-0.5/etc/ltrace.conf ---- ltrace-0.5/etc/ltrace.conf~ 2006-02-20 22:55:47.000000000 +0100 -+++ ltrace-0.5/etc/ltrace.conf 2010-12-09 12:24:31.000000000 +0100 -@@ -237,6 +237,7 @@ string basename(string); - string index(string,char); - addr memchr(string,char,ulong); - addr memcpy(addr,string3,ulong); -+addr memmove(addr,string3,ulong); - addr memset(addr,char,long); - string rindex(string,char); - addr stpcpy(addr,string); - -Diff finished. Thu Dec 9 12:24:37 2010 diff --git a/ltrace-0.5-ia64-sigill.patch b/ltrace-0.5-ia64-sigill.patch deleted file mode 100644 index 9dab07b..0000000 --- a/ltrace-0.5-ia64-sigill.patch +++ /dev/null @@ -1,84 +0,0 @@ -diff -Nurp ltrace-0.5-orig/testsuite/ltrace.torture/ia64-sigill.exp ltrace-0.5/testsuite/ltrace.torture/ia64-sigill.exp ---- ltrace-0.5-orig/testsuite/ltrace.torture/ia64-sigill.exp 1969-12-31 19:00:00.000000000 -0500 -+++ ltrace-0.5/testsuite/ltrace.torture/ia64-sigill.exp 2007-05-04 13:29:43.000000000 -0400 -@@ -0,0 +1,33 @@ -+# This file was written by Yao Qi . -+ -+set testfile "ia64-sigill" -+set srcfile ${testfile}.s -+set binfile ${testfile} -+ -+if { [istarget ia64-*] } then { -+ verbose "compiling source file now....." -+ # Build the shared libraries this test case needs. -+ if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}.s" "${srcdir}/${subdir}/${binfile}" executable {debug} ] != "" } { -+ send_user "Testcase compile failed, so all tests in this file will automatically fail\n." -+ } -+ -+ # Run PUT for ltarce. -+ set exec_output [ltrace_runtest $srcdir/$subdir $srcdir/$subdir/$binfile] -+ -+ # Check the output of this program. -+ verbose "ltrace runtest output: $exec_output\n" -+ if [regexp {ELF from incompatible architecture} $exec_output] { -+ fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" -+ return -+ } elseif [ regexp {Couldn't get .hash data} $exec_output ] { -+ fail "Couldn't get .hash data!" -+ return -+ } -+ -+ catch "exec sh -c {grep SIGILL ${srcdir}/${subdir}/${testfile}.ltrace | wc -l ;exit}" output -+ if { $output == 0 } then { -+ pass "ltrace did interpret SIGILL as breakpoint." -+ } else { -+ fail "ltrace failed to interpret SIGILL as breakpoint." -+ } -+} -diff -Nurp ltrace-0.5-orig/testsuite/ltrace.torture/ia64-sigill.s ltrace-0.5/testsuite/ltrace.torture/ia64-sigill.s ---- ltrace-0.5-orig/testsuite/ltrace.torture/ia64-sigill.s 1969-12-31 19:00:00.000000000 -0500 -+++ ltrace-0.5/testsuite/ltrace.torture/ia64-sigill.s 2007-05-04 12:51:14.000000000 -0400 -@@ -0,0 +1,43 @@ -+ .file "pokus.c" -+ .pred.safe_across_calls p1-p5,p16-p63 -+ .section .rodata -+ .align 8 -+.LC0: -+ stringz "" -+ .text -+ .align 16 -+ .global main# -+ .proc main# -+main: -+ .prologue 14, 32 -+ .save ar.pfs, r33 -+ alloc r33 = ar.pfs, 0, 4, 1, 0 -+ .vframe r34 -+ mov r34 = r12 -+ mov r35 = r1 -+ .save rp, r32 -+ mov r32 = b0 -+ .body -+ addl r36 = @ltoffx(.LC0), r1 -+ ;; -+ ld8.mov r36 = [r36], .LC0 -+ br.call.sptk.many b0 = printf# -+ nop.b 0x0 -+ nop.b 0x1 -+ nop.b 0x2 -+ nop.b 0x0 -+ nop.b 0x1 -+ nop.b 0x2 -+ mov r1 = r35 -+ addl r14 = 234, r0 -+ ;; -+ mov r8 = r14 -+ mov ar.pfs = r33 -+ mov b0 = r32 -+ .restore sp -+ mov r12 = r34 -+ br.ret.sptk.many b0 -+ ;; -+ .endp main# -+ .section .note.GNU-stack,"",@progbits -+ .ident "GCC: (GNU) 3.4.6 20060404 (Red Hat 3.4.6-3)" diff --git a/ltrace-0.6.0-abi-ia64.patch b/ltrace-0.6.0-abi-ia64.patch deleted file mode 100644 index f1b6423..0000000 --- a/ltrace-0.6.0-abi-ia64.patch +++ /dev/null @@ -1,1539 +0,0 @@ -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-abi-s390.patch b/ltrace-0.6.0-abi-s390.patch deleted file mode 100644 index cfb870d..0000000 --- a/ltrace-0.6.0-abi-s390.patch +++ /dev/null @@ -1,1695 +0,0 @@ -diff --git a/backend.h b/backend.h -index 08306e1..29688ea 100644 ---- a/backend.h -+++ b/backend.h -@@ -22,6 +22,8 @@ - #define BACKEND_H - - #include "forward.h" -+#include "sysdep.h" -+ - #include - - enum process_status { -@@ -33,8 +35,6 @@ enum process_status { - ps_other, /* Necessary other states can be added as needed. */ - }; - --typedef void *target_address_t; -- - /* - * This file contains documentation of back end interface. Some of - * these may be implemented on an OS level (i.e. they are the same -@@ -93,7 +93,7 @@ void get_arch_dep(struct Process *proc); - * XXX note that the IP must fit into an arch pointer. This prevents - * us to use 32-bit ltrace to trace 64-bit process, even on arches - * that would otherwise support this. Above we have a definition of -- * target_address_t. This should be converted to an integral type and -+ * arch_addr_t. This should be converted to an integral type and - * used for target addresses throughout. */ - void *get_instruction_pointer(struct Process *proc); - -@@ -183,7 +183,7 @@ void *sym2addr(struct Process *proc, struct library_symbol *sym); - /* 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, target_address_t dyn_addr); -+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 -@@ -240,8 +240,8 @@ int arch_process_exec(struct Process *proc); - * 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, -- target_address_t *entryp, -- target_address_t *interp_biasp); -+ arch_addr_t *entryp, -+ arch_addr_t *interp_biasp); - - /* This is called after the dynamic linker is done with the - * process startup. */ -diff --git a/breakpoint.h b/breakpoint.h -index 0398072..7cd914e 100644 ---- a/breakpoint.h -+++ b/breakpoint.h -@@ -81,7 +81,7 @@ void breakpoint_on_retract(struct breakpoint *bp, struct Process *proc); - * disabled. orig_value has to be set separately. CBS may be - * NULL. */ - int breakpoint_init(struct breakpoint *bp, struct Process *proc, -- target_address_t addr, struct library_symbol *libsym); -+ arch_addr_t addr, struct library_symbol *libsym); - - /* Make a clone of breakpoint BP into the area of memory pointed to by - * RETP. The original breakpoint was assigned to process OLD_PROC, -diff --git a/breakpoints.c b/breakpoints.c -index 8dc09df..e7120ee 100644 ---- a/breakpoints.c -+++ b/breakpoints.c -@@ -20,7 +20,7 @@ - #ifndef ARCH_HAVE_TRANSLATE_ADDRESS - int - arch_translate_address_dyn(struct Process *proc, -- target_address_t addr, target_address_t *ret) -+ arch_addr_t addr, arch_addr_t *ret) - { - *ret = addr; - return 0; -@@ -29,7 +29,7 @@ arch_translate_address_dyn(struct Process *proc, - struct ltelf; - int - arch_translate_address(struct ltelf *lte, -- target_address_t addr, target_address_t *ret) -+ arch_addr_t addr, arch_addr_t *ret) - { - *ret = addr; - return 0; -@@ -95,7 +95,7 @@ arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp) - - static void - breakpoint_init_base(struct breakpoint *bp, struct Process *proc, -- target_address_t addr, struct library_symbol *libsym) -+ arch_addr_t addr, struct library_symbol *libsym) - { - bp->cbs = NULL; - bp->addr = addr; -@@ -110,7 +110,7 @@ breakpoint_init_base(struct breakpoint *bp, struct Process *proc, - * need process for anything. */ - int - breakpoint_init(struct breakpoint *bp, struct Process *proc, -- target_address_t addr, struct library_symbol *libsym) -+ arch_addr_t addr, struct library_symbol *libsym) - { - breakpoint_init_base(bp, proc, addr, libsym); - return arch_breakpoint_init(proc, bp); -@@ -357,7 +357,7 @@ disable_all_breakpoints(Process *proc) { - * for one structure. */ - struct entry_breakpoint { - struct breakpoint super; -- target_address_t dyn_addr; -+ arch_addr_t dyn_addr; - }; - - static void -@@ -366,7 +366,7 @@ entry_breakpoint_on_hit(struct breakpoint *a, struct Process *proc) - struct entry_breakpoint *bp = (void *)a; - if (proc == NULL || proc->leader == NULL) - return; -- target_address_t dyn_addr = bp->dyn_addr; -+ arch_addr_t dyn_addr = bp->dyn_addr; - delete_breakpoint(proc, bp->super.addr); - linkmap_init(proc, dyn_addr); - arch_dynlink_done(proc); -@@ -374,7 +374,7 @@ entry_breakpoint_on_hit(struct breakpoint *a, struct Process *proc) - - int - entry_breakpoint_init(struct Process *proc, -- struct entry_breakpoint *bp, target_address_t addr, -+ struct entry_breakpoint *bp, arch_addr_t addr, - struct library *lib) - { - int err; -diff --git a/library.c b/library.c -index 2bd7dbb..2ce3427 100644 ---- a/library.c -+++ b/library.c -@@ -71,10 +71,10 @@ target_address_hash(const void *key) - { - /* XXX this assumes that key is passed by value. */ - union { -- target_address_t addr; -- unsigned int ints[sizeof(target_address_t) -+ arch_addr_t addr; -+ unsigned int ints[sizeof(arch_addr_t) - / sizeof(unsigned int)]; -- } u = { .addr = (target_address_t)key }; -+ } u = { .addr = (arch_addr_t)key }; - - size_t i; - unsigned int h = 0; -@@ -87,8 +87,8 @@ int - target_address_cmp(const void *key1, const void *key2) - { - /* XXX this assumes that key is passed by value. */ -- target_address_t addr1 = (target_address_t)key1; -- target_address_t addr2 = (target_address_t)key2; -+ arch_addr_t addr1 = (arch_addr_t)key1; -+ arch_addr_t addr2 = (arch_addr_t)key2; - return addr1 < addr2 ? 1 - : addr1 > addr2 ? -1 : 0; - } -@@ -110,7 +110,7 @@ strdup_if_owned(const char **retp, const char *str, int owned) - - static void - private_library_symbol_init(struct library_symbol *libsym, -- target_address_t addr, -+ arch_addr_t addr, - const char *name, int own_name, - enum toplt type_of_plt) - { -@@ -130,7 +130,7 @@ private_library_symbol_destroy(struct library_symbol *libsym) - - int - library_symbol_init(struct library_symbol *libsym, -- target_address_t addr, const char *name, int own_name, -+ 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); -@@ -358,5 +358,5 @@ library_named_cb(struct Process *proc, struct library *lib, void *name) - enum callback_status - library_with_key_cb(struct Process *proc, struct library *lib, void *keyp) - { -- return lib->key == *(target_address_t *)keyp ? CBS_STOP : CBS_CONT; -+ return lib->key == *(arch_addr_t *)keyp ? CBS_STOP : CBS_CONT; - } -diff --git a/library.h b/library.h -index c387b02..876a533 100644 ---- a/library.h -+++ b/library.h -@@ -33,13 +33,6 @@ enum toplt { - LS_TOPLT_EXEC, /* PLT for this symbol is executable. */ - }; - --/* We should in general be able to trace 64-bit processes with 32-bit -- * ltrace. (At least PPC has several PTRACE requests related to -- * tracing 64-on-32, so presumably it should be possible.) But ltrace -- * is currently hopelessly infested with using void* for host address. -- * So keep with it, for now. */ --typedef void *target_address_t; -- - /* Dict interface. */ - unsigned int target_address_hash(const void *key); - int target_address_cmp(const void *key1, const void *key2); -@@ -48,7 +41,7 @@ struct library_symbol { - struct library_symbol *next; - struct library *lib; - const char *name; -- target_address_t enter_addr; -+ arch_addr_t enter_addr; - enum toplt plt_type; - char own_name; - struct arch_library_symbol_data arch; -@@ -57,7 +50,7 @@ struct library_symbol { - /* Init LIBSYM. NAME will be freed when LIBSYM is destroyed if - * OWN_NAME. ARCH has to be initialized by a separate call. */ - int library_symbol_init(struct library_symbol *libsym, -- target_address_t addr, const char *name, int own_name, -+ arch_addr_t addr, const char *name, int own_name, - enum toplt type_of_plt); - - /* Copy library symbol SYM into the area pointed-to by RETP. Return 0 -@@ -102,20 +95,20 @@ struct library { - - /* Unique key. Two library objects are considered equal, if - * they have the same key. */ -- target_address_t key; -+ arch_addr_t key; - - /* Address where the library is mapped. Two library objects - * are considered equal, if they have the same base. */ -- target_address_t base; -+ arch_addr_t base; - - /* Absolute address of the entry point. Useful for main - * binary, though I suppose the value might be useful for the - * dynamic linker, too (in case we ever want to do early - * process tracing). */ -- target_address_t entry; -+ arch_addr_t entry; - - /* Address of PT_DYNAMIC segment. */ -- target_address_t dyn_addr; -+ arch_addr_t dyn_addr; - - /* Symbols associated with the library. */ - struct library_symbol *symbols; -@@ -171,14 +164,14 @@ enum callback_status library_named_cb(struct Process *proc, - /* A function that can be used as proc_each_library callback. Looks - * for a library with given base. - * -- * NOTE: The key is passed as a POINTER to target_address_t (that -- * because in general, target_address_t doesn't fit in void*). */ -+ * NOTE: The key is passed as a POINTER to arch_addr_t (that -+ * because in general, arch_addr_t doesn't fit in void*). */ - enum callback_status library_with_key_cb(struct Process *proc, - struct library *lib, void *keyp); - - /* XXX this should really be in backend.h (as on pmachata/revamp - * branch), or, on this branch, in common.h. But we need -- * target_address_t (which should also be in backend.h, I reckon), so -+ * arch_addr_t (which should also be in backend.h, I reckon), so - * stuff it here for the time being. */ - /* This function is implemented in the back end. It is called for all - * raw addresses as read from symbol tables etc. If necessary on -@@ -187,10 +180,10 @@ enum callback_status library_with_key_cb(struct Process *proc, - * success and a negative value on failure. */ - struct ltelf; - int arch_translate_address(struct ltelf *lte, -- target_address_t addr, target_address_t *ret); -+ arch_addr_t addr, arch_addr_t *ret); - /* This is the same function as arch_translate_address, except it's - * used at the point that we don't have ELF available anymore. */ - int arch_translate_address_dyn(struct Process *proc, -- target_address_t addr, target_address_t *ret); -+ arch_addr_t addr, arch_addr_t *ret); - - #endif /* _LIBRARY_H_ */ -diff --git a/ltrace-elf.c b/ltrace-elf.c -index e4a61eb..50c4f62 100644 ---- a/ltrace-elf.c -+++ b/ltrace-elf.c -@@ -84,8 +84,8 @@ default_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, - goto fail; - - /* XXX The double cast should be removed when -- * target_address_t becomes integral type. */ -- target_address_t taddr = (target_address_t) -+ * arch_addr_t becomes integral type. */ -+ arch_addr_t taddr = (arch_addr_t) - (uintptr_t)(addr + lte->bias); - - if (library_symbol_init(libsym, taddr, name, 1, LS_TOPLT_EXEC) < 0) { -@@ -573,7 +573,7 @@ populate_plt(struct Process *proc, const char *filename, - * each address, and replace name in libsym with a shorter variant if - * we find it. */ - struct unique_symbol { -- target_address_t addr; -+ arch_addr_t addr; - struct library_symbol *libsym; - }; - -@@ -593,7 +593,8 @@ populate_this_symtab(struct Process *proc, const char *filename, - - /* XXX support IFUNC as well. */ - if (GELF_ST_TYPE(sym.st_info) != STT_FUNC -- || sym.st_value == 0) -+ || sym.st_value == 0 -+ || sym.st_shndx == STN_UNDEF) - continue; - - const char *orig_name = strtab + sym.st_name; -@@ -642,9 +642,9 @@ populate_this_symtab(struct Process *proc, const char *filename, - if (!filter_matches_symbol(options.static_filter, name, lib)) - continue; - -- target_address_t addr = (target_address_t) -+ arch_addr_t addr = (arch_addr_t) - (uintptr_t)(sym.st_value + lte->bias); -- target_address_t naddr; -+ arch_addr_t naddr; - - /* On arches that support OPD, the value of typical - * function symbol will be a pointer to .opd, but some -@@ -734,6 +734,7 @@ ltelf_read_library(struct library *lib, struct Process *proc, - } - - proc->e_machine = lte.ehdr.e_machine; -+ proc->e_class = lte.ehdr.e_ident[EI_CLASS]; - - int status = 0; - if (lib == NULL) -@@ -764,18 +765,18 @@ ltelf_read_library(struct library *lib, struct Process *proc, - } - - /* XXX The double cast should be removed when -- * target_address_t becomes integral type. */ -- target_address_t entry = (target_address_t)(uintptr_t)lte.entry_addr; -+ * arch_addr_t becomes integral type. */ -+ arch_addr_t entry = (arch_addr_t)(uintptr_t)lte.entry_addr; - if (arch_translate_address(<e, entry, &entry) < 0) - goto fail; - - /* XXX The double cast should be removed when -- * target_address_t becomes integral type. */ -- lib->base = (target_address_t)(uintptr_t)lte.base_addr; -+ * arch_addr_t becomes integral type. */ -+ lib->base = (arch_addr_t)(uintptr_t)lte.base_addr; - lib->entry = entry; - /* XXX The double cast should be removed when -- * target_address_t becomes integral type. */ -- lib->dyn_addr = (target_address_t)(uintptr_t)lte.dyn_addr; -+ * 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) -diff --git a/output.c b/output.c -index 8bfe3f0..b138055 100644 ---- a/output.c -+++ b/output.c -@@ -546,12 +546,12 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) - value_init(&retval, proc, NULL, func->return_info, 0); - own_retval = 1; - if (fetch_retval(context, type, proc, func->return_info, -- &retval) == 0) { -- if (stel->arguments != NULL -- && val_dict_push_named(stel->arguments, &retval, -- "retval", 0) == 0) -- own_retval = 0; -- } -+ &retval) < 0) -+ value_set_type(&retval, NULL, 0); -+ else if (stel->arguments != NULL -+ && val_dict_push_named(stel->arguments, &retval, -+ "retval", 0) == 0) -+ own_retval = 0; - } - - if (stel->arguments != NULL) -@@ -563,7 +563,8 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) - tabto(options.align - 1); - fprintf(options.output, "= "); - -- output_one(&retval, stel->arguments); -+ if (context != NULL && retval.type != NULL) -+ output_one(&retval, stel->arguments); - - if (own_retval) - value_destroy(&retval); -diff --git a/proc.c b/proc.c -index b280df8..bf26180 100644 ---- a/proc.c -+++ b/proc.c -@@ -129,8 +129,8 @@ process_bare_destroy(struct Process *pro - static int - process_init_main(struct Process *proc) - { -- target_address_t entry; -- target_address_t interp_bias; -+ arch_addr_t entry; -+ arch_addr_t interp_bias; - if (process_get_entry(proc, &entry, &interp_bias) < 0) { - fprintf(stderr, "Couldn't get entry points of process %d\n", - proc->pid); -@@ -270,6 +270,7 @@ process_clone(struct Process *retp, struct Process *proc, pid_t pid) - - retp->tracesysgood = proc->tracesysgood; - retp->e_machine = proc->e_machine; -+ retp->e_class = proc->e_class; - - /* For non-leader processes, that's all we need to do. */ - if (retp->leader != retp) -diff --git a/proc.h b/proc.h -index fe9048f..5529187 100644 ---- a/proc.h -+++ b/proc.h -@@ -97,7 +97,7 @@ struct Process { - /* Dictionary of breakpoints (which is a mapping - * address->breakpoint). This is NULL for non-leader - * processes. XXX note that we store addresses (keys) by -- * value. That assumes that target_address_t fits in host -+ * value. That assumes that arch_addr_t fits in host - * pointer. */ - Dict * breakpoints; - -@@ -119,7 +119,15 @@ struct Process { - void * stack_pointer; /* To get return addr, args... */ - void * return_addr; - void * arch_ptr; -+ -+ /* XXX We would like to replace this with a pointer to ABI -+ * object that would provide the relevant services, instead of -+ * checking the necessary flags in the back end ad -+ * nauseam. */ - short e_machine; -+ char e_class; -+ -+ /* XXX this shoudl go to ARM's arch_process_data. */ - #ifdef __arm__ - int thumb_mode; /* ARM execution mode: 0: ARM, 1: Thumb */ - #endif -diff --git a/sysdeps/linux-gnu/arm/breakpoint.c b/sysdeps/linux-gnu/arm/breakpoint.c -index 5bcf86e..5748401 100644 ---- a/sysdeps/linux-gnu/arm/breakpoint.c -+++ b/sysdeps/linux-gnu/arm/breakpoint.c -@@ -95,7 +95,7 @@ int - arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp) - { - /* XXX That uintptr_t cast is there temporarily until -- * target_address_t becomes integral type. */ -+ * arch_addr_t becomes integral type. */ - int thumb_mode = ((uintptr_t)sbp->addr) & 1; - if (thumb_mode) - sbp->addr = (void *)((uintptr_t)sbp->addr & ~1); -diff --git a/sysdeps/linux-gnu/ppc/fetch.c b/sysdeps/linux-gnu/ppc/fetch.c -index 370f43e..44cd056 100644 ---- a/sysdeps/linux-gnu/ppc/fetch.c -+++ b/sysdeps/linux-gnu/ppc/fetch.c -@@ -54,7 +54,7 @@ typedef uint32_t gregs32_t[48]; - typedef uint64_t gregs64_t[48]; - - struct fetch_context { -- target_address_t stack_pointer; -+ arch_addr_t stack_pointer; - int greg; - int freg; - int ret_struct; -@@ -159,10 +159,10 @@ allocate_stack_slot(struct fetch_context *ctx, struct Process *proc, - else if (proc->e_machine == EM_PPC64 && a < 8) - a = 8; - -- /* XXX Remove the two double casts when target_address_t -+ /* XXX Remove the two double casts when arch_addr_t - * becomes integral type. */ - uintptr_t tmp = align((uint64_t)(uintptr_t)ctx->stack_pointer, a); -- ctx->stack_pointer = (target_address_t)tmp; -+ ctx->stack_pointer = (arch_addr_t)tmp; - - if (valuep != NULL) { - valuep->where = VAL_LOC_INFERIOR; -@@ -409,9 +409,9 @@ arch_fetch_retval(struct fetch_context *ctx, enum tof type, - value_init(valuep, proc, NULL, info, 0); - - valuep->where = VAL_LOC_INFERIOR; -- /* XXX Remove the double cast when target_address_t -+ /* XXX Remove the double cast when arch_addr_t - * becomes integral type. */ -- valuep->u.address = (target_address_t)(uintptr_t)addr; -+ valuep->u.address = (arch_addr_t)(uintptr_t)addr; - return 0; - } - -diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c -index 944bd6a..c9ca458 100644 ---- a/sysdeps/linux-gnu/ppc/plt.c -+++ b/sysdeps/linux-gnu/ppc/plt.c -@@ -104,7 +104,7 @@ host_powerpc64() - } - - int --read_target_4(struct Process *proc, target_address_t addr, uint32_t *lp) -+read_target_4(struct Process *proc, arch_addr_t addr, uint32_t *lp) - { - unsigned long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); - if (l == -1UL && errno) -@@ -117,7 +117,7 @@ read_target_4(struct Process *proc, target_address_t addr, uint32_t *lp) - } - - static int --read_target_8(struct Process *proc, target_address_t addr, uint64_t *lp) -+read_target_8(struct Process *proc, arch_addr_t addr, uint64_t *lp) - { - unsigned long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); - if (l == -1UL && errno) -@@ -135,7 +135,7 @@ read_target_8(struct Process *proc, target_address_t addr, uint64_t *lp) - } - - int --read_target_long(struct Process *proc, target_address_t addr, uint64_t *lp) -+read_target_long(struct Process *proc, arch_addr_t addr, uint64_t *lp) - { - if (proc->e_machine == EM_PPC) { - uint32_t w; -@@ -223,7 +223,7 @@ arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) - * already. */ - int - arch_translate_address_dyn(struct Process *proc, -- target_address_t addr, target_address_t *ret) -+ arch_addr_t addr, arch_addr_t *ret) - { - if (proc->e_machine == EM_PPC64) { - uint64_t value; -@@ -232,8 +232,8 @@ arch_translate_address_dyn(struct Process *proc, - return -1; - } - /* XXX The double cast should be removed when -- * target_address_t becomes integral type. */ -- *ret = (target_address_t)(uintptr_t)value; -+ * arch_addr_t becomes integral type. */ -+ *ret = (arch_addr_t)(uintptr_t)value; - return 0; - } - -@@ -243,11 +243,11 @@ arch_translate_address_dyn(struct Process *proc, - - int - arch_translate_address(struct ltelf *lte, -- target_address_t addr, target_address_t *ret) -+ arch_addr_t addr, arch_addr_t *ret) - { - if (lte->ehdr.e_machine == EM_PPC64) { - /* XXX The double cast should be removed when -- * target_address_t becomes integral type. */ -+ * arch_addr_t becomes integral type. */ - GElf_Xword offset - = (GElf_Addr)(uintptr_t)addr - lte->arch.opd_base; - uint64_t value; -@@ -256,7 +256,7 @@ arch_translate_address(struct ltelf *lte, - elf_errmsg(-1)); - return -1; - } -- *ret = (target_address_t)(uintptr_t)(value + lte->bias); -+ *ret = (arch_addr_t)(uintptr_t)(value + lte->bias); - return 0; - } - -@@ -509,8 +509,8 @@ arch_elf_init(struct ltelf *lte, struct library *lib) - } - - /* XXX The double cast should be removed when -- * target_address_t becomes integral type. */ -- target_address_t addr = (target_address_t) -+ * arch_addr_t becomes integral type. */ -+ arch_addr_t addr = (arch_addr_t) - (uintptr_t)sym.st_value + lte->bias; - if (library_symbol_init(libsym, addr, sym_name, 1, - LS_TOPLT_EXEC) < 0) -@@ -533,7 +533,7 @@ read_plt_slot_value(struct Process *proc, GElf_Addr addr, GElf_Addr *valp) - * either can change. */ - uint64_t l; - /* XXX double cast. */ -- if (read_target_8(proc, (target_address_t)(uintptr_t)addr, &l) < 0) { -+ if (read_target_8(proc, (arch_addr_t)(uintptr_t)addr, &l) < 0) { - error(0, errno, "ptrace .plt slot value @%#" PRIx64, addr); - return -1; - } -@@ -621,9 +621,9 @@ arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, - } - - /* XXX The double cast should be removed when -- * target_address_t becomes integral type. */ -+ * arch_addr_t becomes integral type. */ - if (library_symbol_init(libsym, -- (target_address_t)(uintptr_t)plt_entry_addr, -+ (arch_addr_t)(uintptr_t)plt_entry_addr, - name, 1, LS_TOPLT_EXEC) < 0) - goto fail; - libsym->arch.plt_slot_addr = plt_slot_addr; -@@ -760,7 +760,7 @@ cb_keep_stepping_p(struct process_stopping_handler *self) - /* We need to install to the next instruction. ADDR points to - * a store instruction, so moving the breakpoint one - * instruction forward is safe. */ -- target_address_t addr = get_instruction_pointer(proc) + 4; -+ arch_addr_t addr = get_instruction_pointer(proc) + 4; - leader->arch.dl_plt_update_bp = insert_breakpoint(proc, addr, NULL); - if (leader->arch.dl_plt_update_bp == NULL) - goto done; -@@ -784,8 +784,8 @@ static void - jump_to_entry_point(struct Process *proc, struct breakpoint *bp) - { - /* XXX The double cast should be removed when -- * target_address_t becomes integral type. */ -- target_address_t rv = (target_address_t) -+ * arch_addr_t becomes integral type. */ -+ arch_addr_t rv = (arch_addr_t) - (uintptr_t)bp->libsym->arch.resolved_value; - set_instruction_pointer(proc, rv); - } -diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c -index 0b734e4..2bb317f 100644 ---- a/sysdeps/linux-gnu/ppc/trace.c -+++ b/sysdeps/linux-gnu/ppc/trace.c -@@ -94,14 +94,14 @@ syscall_p(Process *proc, int status, int *sysnum) { - #define BRANCH_MASK 0xfc000000 - - /* In plt.h. XXX make this official interface. */ --int read_target_4(struct Process *proc, target_address_t addr, uint32_t *lp); -+int read_target_4(struct Process *proc, arch_addr_t addr, uint32_t *lp); - - int - arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, - int (*add_cb)(void *addr, void *data), - void *add_cb_data) - { -- target_address_t ip = get_instruction_pointer(proc); -+ arch_addr_t ip = get_instruction_pointer(proc); - struct breakpoint *other = address2bpstruct(proc->leader, ip); - - debug(1, "arch_atomic_singlestep pid=%d addr=%p %s(%p)", -@@ -129,7 +129,7 @@ arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, - debug(1, "singlestep over atomic block at %p", ip); - - int insn_count; -- target_address_t addr = ip; -+ arch_addr_t addr = ip; - for (insn_count = 0; ; ++insn_count) { - addr += 4; - unsigned long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); -@@ -149,7 +149,7 @@ arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, - int absolute = insn & 2; - - /* XXX drop the following casts. */ -- target_address_t branch_addr; -+ arch_addr_t branch_addr; - if (absolute) - branch_addr = (void *)(uintptr_t)immediate; - else -diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c -index e7556f5..d05da13 100644 ---- a/sysdeps/linux-gnu/proc.c -+++ b/sysdeps/linux-gnu/proc.c -@@ -288,7 +288,7 @@ select_32_64(struct Process *proc, void *p32, void *p64) - } - - static int --fetch_dyn64(struct Process *proc, target_address_t *addr, Elf64_Dyn *ret) -+fetch_dyn64(struct Process *proc, arch_addr_t *addr, Elf64_Dyn *ret) - { - if (umovebytes(proc, *addr, ret, sizeof(*ret)) != sizeof(*ret)) - return -1; -@@ -297,7 +297,7 @@ fetch_dyn64(struct Process *proc, target_address_t *addr, Elf64_Dyn *ret) - } - - static int --fetch_dyn32(struct Process *proc, target_address_t *addr, Elf64_Dyn *ret) -+fetch_dyn32(struct Process *proc, arch_addr_t *addr, Elf64_Dyn *ret) - { - Elf32_Dyn dyn; - if (umovebytes(proc, *addr, &dyn, sizeof(dyn)) != sizeof(dyn)) -@@ -312,14 +312,14 @@ fetch_dyn32(struct Process *proc, target_address_t *addr, Elf64_Dyn *ret) - - static int (* - dyn_fetcher(struct Process *proc))(struct Process *, -- target_address_t *, Elf64_Dyn *) -+ arch_addr_t *, Elf64_Dyn *) - { - return select_32_64(proc, fetch_dyn32, fetch_dyn64); - } - - static int --find_dynamic_entry_addr(struct Process *proc, target_address_t src_addr, -- int d_tag, target_address_t *ret) -+find_dynamic_entry_addr(struct Process *proc, arch_addr_t src_addr, -+ int d_tag, arch_addr_t *ret) - { - debug(DEBUG_FUNCTION, "find_dynamic_entry()"); - -@@ -340,8 +340,8 @@ find_dynamic_entry_addr(struct Process *proc, target_address_t src_addr, - - if (entry.d_tag == d_tag) { - /* XXX The double cast should be removed when -- * target_address_t becomes integral type. */ -- *ret = (target_address_t)(uintptr_t)entry.d_un.d_val; -+ * arch_addr_t becomes integral type. */ -+ *ret = (arch_addr_t)(uintptr_t)entry.d_un.d_val; - debug(2, "found address: %p in dtag %d", *ret, d_tag); - return 0; - } -@@ -364,7 +364,7 @@ struct lt_link_map_32 LT_LINK_MAP(32); - struct lt_link_map_64 LT_LINK_MAP(64); - - static int --fetch_lm64(struct Process *proc, target_address_t addr, -+fetch_lm64(struct Process *proc, arch_addr_t addr, - struct lt_link_map_64 *ret) - { - if (umovebytes(proc, addr, ret, sizeof(*ret)) != sizeof(*ret)) -@@ -373,7 +373,7 @@ fetch_lm64(struct Process *proc, target_address_t addr, - } - - static int --fetch_lm32(struct Process *proc, target_address_t addr, -+fetch_lm32(struct Process *proc, arch_addr_t addr, - struct lt_link_map_64 *ret) - { - struct lt_link_map_32 lm; -@@ -391,7 +391,7 @@ fetch_lm32(struct Process *proc, target_address_t addr, - - static int (* - lm_fetcher(struct Process *proc))(struct Process *, -- target_address_t, struct lt_link_map_64 *) -+ arch_addr_t, struct lt_link_map_64 *) - { - return select_32_64(proc, fetch_lm32, fetch_lm64); - } -@@ -410,7 +410,7 @@ struct lt_r_debug_32 LT_R_DEBUG(32); - struct lt_r_debug_64 LT_R_DEBUG(64); - - static int --fetch_rd64(struct Process *proc, target_address_t addr, -+fetch_rd64(struct Process *proc, arch_addr_t addr, - struct lt_r_debug_64 *ret) - { - if (umovebytes(proc, addr, ret, sizeof(*ret)) != sizeof(*ret)) -@@ -419,7 +419,7 @@ fetch_rd64(struct Process *proc, target_address_t addr, - } - - static int --fetch_rd32(struct Process *proc, target_address_t addr, -+fetch_rd32(struct Process *proc, arch_addr_t addr, - struct lt_r_debug_64 *ret) - { - struct lt_r_debug_32 rd; -@@ -437,7 +437,7 @@ fetch_rd32(struct Process *proc, target_address_t addr, - - static int (* - rdebug_fetcher(struct Process *proc))(struct Process *, -- target_address_t, struct lt_r_debug_64 *) -+ arch_addr_t, struct lt_r_debug_64 *) - { - return select_32_64(proc, fetch_rd32, fetch_rd64); - } -@@ -453,8 +453,8 @@ crawl_linkmap(struct Process *proc, struct lt_r_debug_64 *dbg) - } - - /* XXX The double cast should be removed when -- * target_address_t becomes integral type. */ -- target_address_t addr = (target_address_t)(uintptr_t)dbg->r_map; -+ * arch_addr_t becomes integral type. */ -+ arch_addr_t addr = (arch_addr_t)(uintptr_t)dbg->r_map; - - while (addr != 0) { - struct lt_link_map_64 rlm; -@@ -463,10 +463,10 @@ crawl_linkmap(struct Process *proc, struct lt_r_debug_64 *dbg) - return; - } - -- target_address_t key = addr; -+ arch_addr_t key = addr; - /* XXX The double cast should be removed when -- * target_address_t becomes integral type. */ -- addr = (target_address_t)(uintptr_t)rlm.l_next; -+ * arch_addr_t becomes integral type. */ -+ addr = (arch_addr_t)(uintptr_t)rlm.l_next; - if (rlm.l_name == 0) { - debug(2, "Name of mapped library is NULL"); - return; -@@ -474,8 +474,8 @@ crawl_linkmap(struct Process *proc, struct lt_r_debug_64 *dbg) - - char lib_name[BUFSIZ]; - /* XXX The double cast should be removed when -- * target_address_t becomes integral type. */ -- umovebytes(proc, (target_address_t)(uintptr_t)rlm.l_name, -+ * arch_addr_t becomes integral type. */ -+ umovebytes(proc, (arch_addr_t)(uintptr_t)rlm.l_name, - lib_name, sizeof(lib_name)); - - if (*lib_name == '\0') { -@@ -512,7 +512,7 @@ crawl_linkmap(struct Process *proc, struct lt_r_debug_64 *dbg) - /* A struct stored at proc->debug. */ - struct debug_struct - { -- target_address_t debug_addr; -+ arch_addr_t debug_addr; - int state; - }; - -@@ -561,7 +561,7 @@ rdebug_bp_on_hit(struct breakpoint *bp, struct Process *proc) - } - - int --linkmap_init(struct Process *proc, target_address_t dyn_addr) -+linkmap_init(struct Process *proc, arch_addr_t dyn_addr) - { - debug(DEBUG_FUNCTION, "linkmap_init()"); - -@@ -590,8 +590,8 @@ linkmap_init(struct Process *proc, target_address_t dyn_addr) - } - - /* XXX The double cast should be removed when -- * target_address_t becomes integral type. */ -- target_address_t addr = (target_address_t)(uintptr_t)rdbg.r_brk; -+ * arch_addr_t becomes integral type. */ -+ arch_addr_t addr = (arch_addr_t)(uintptr_t)rdbg.r_brk; - if (arch_translate_address_dyn(proc, addr, &addr) < 0) - goto fail; - -@@ -634,8 +634,8 @@ auxv_fetcher(struct Process *proc))(int, - - int - process_get_entry(struct Process *proc, -- target_address_t *entryp, -- target_address_t *interp_biasp) -+ arch_addr_t *entryp, -+ arch_addr_t *interp_biasp) - { - PROC_PID_FILE(fn, "/proc/%d/auxv", proc->pid); - int fd = open(fn, O_RDONLY); -@@ -648,8 +648,8 @@ process_get_entry(struct Process *proc, - return fd == -1 ? -1 : 0; - } - -- target_address_t at_entry = 0; -- target_address_t at_bias = 0; -+ arch_addr_t at_entry = 0; -+ arch_addr_t at_bias = 0; - while (1) { - Elf64_auxv_t entry; - if (auxv_fetcher(proc)(fd, &entry) < 0) -@@ -658,15 +658,15 @@ process_get_entry(struct Process *proc, - switch (entry.a_type) { - case AT_BASE: - /* XXX The double cast should be removed when -- * target_address_t becomes integral type. */ -- at_bias = (target_address_t) -+ * arch_addr_t becomes integral type. */ -+ at_bias = (arch_addr_t) - (uintptr_t)entry.a_un.a_val; - continue; - - case AT_ENTRY: - /* XXX The double cast should be removed when -- * target_address_t becomes integral type. */ -- at_entry = (target_address_t) -+ * arch_addr_t becomes integral type. */ -+ at_entry = (arch_addr_t) - (uintptr_t)entry.a_un.a_val; - default: - continue; -diff --git a/sysdeps/linux-gnu/s390/Makefile.am b/sysdeps/linux-gnu/s390/Makefile.am -index 19e447f..0235d66 100644 ---- a/sysdeps/linux-gnu/s390/Makefile.am -+++ b/sysdeps/linux-gnu/s390/Makefile.am -@@ -4,7 +4,8 @@ noinst_LTLIBRARIES = \ - ___libcpu_la_SOURCES = \ - plt.c \ - regs.c \ -- trace.c -+ trace.c \ -+ fetch.c - - noinst_HEADERS = \ - arch.h \ -diff --git a/sysdeps/linux-gnu/s390/arch.h b/sysdeps/linux-gnu/s390/arch.h -index 6597355..0d412dc 100644 ---- a/sysdeps/linux-gnu/s390/arch.h -+++ b/sysdeps/linux-gnu/s390/arch.h -@@ -22,18 +22,14 @@ - #define BREAKPOINT_LENGTH 2 - #define DECR_PC_AFTER_BREAK 2 - #define ARCH_ENDIAN_BIG -+#define ARCH_HAVE_FETCH_ARG -+#define ARCH_HAVE_SIZEOF -+#define ARCH_HAVE_ALIGNOF - --#ifdef __s390x__ --#define LT_ELFCLASS ELFCLASS64 --#define LT_ELF_MACHINE EM_S390 --#define LT_ELFCLASS2 ELFCLASS32 --#define LT_ELF_MACHINE2 EM_S390 -- --/* __NR_fork, __NR_clone, __NR_clone2, __NR_vfork and __NR_execve -- from asm-s390/unistd.h. */ --#define FORK_EXEC_SYSCALLS , { 2, 120, -1, 190, 11 } -- --#else - #define LT_ELFCLASS ELFCLASS32 - #define LT_ELF_MACHINE EM_S390 -+ -+#ifdef __s390x__ -+#define LT_ELFCLASS2 ELFCLASS64 -+#define LT_ELF_MACHINE2 EM_S390 - #endif -diff --git a/sysdeps/linux-gnu/s390/fetch.c b/sysdeps/linux-gnu/s390/fetch.c -new file mode 100644 -index 0000000..5d26b35 ---- /dev/null -+++ b/sysdeps/linux-gnu/s390/fetch.c -@@ -0,0 +1,316 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 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 -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "backend.h" -+#include "fetch.h" -+#include "type.h" -+#include "proc.h" -+#include "value.h" -+ -+struct fetch_context { -+ struct user_regs_struct regs; -+ arch_addr_t stack_pointer; -+ int greg; -+ int freg; -+}; -+ -+static int -+s390x(struct fetch_context *ctx) -+{ -+ /* +--------+--------+--------+ -+ * | PSW.31 | PSW.32 | mode | -+ * +--------+--------+--------+ -+ * | 0 | 0 | 24-bit | Not supported in Linux -+ * | 0 | 1 | 31-bit | s390 compatible mode -+ * | 1 | 1 | 64-bit | z/Architecture, "s390x" -+ * +--------+--------+--------+ -+ * (Note: The leftmost bit is PSW.0, rightmost PSW.63.) -+ */ -+ -+#ifdef __s390x__ -+ if ((ctx->regs.psw.mask & 0x180000000UL) == 0x180000000UL) -+ return 1; -+#endif -+ return 0; -+} -+ -+static int -+fp_equivalent(struct arg_type_info *info) -+{ -+ switch (info->type) { -+ case ARGTYPE_VOID: -+ case ARGTYPE_INT: -+ case ARGTYPE_UINT: -+ case ARGTYPE_LONG: -+ case ARGTYPE_ULONG: -+ case ARGTYPE_CHAR: -+ case ARGTYPE_SHORT: -+ case ARGTYPE_USHORT: -+ case ARGTYPE_ARRAY: -+ case ARGTYPE_POINTER: -+ return 0; -+ -+ case ARGTYPE_FLOAT: -+ case ARGTYPE_DOUBLE: -+ return 1; -+ -+ case ARGTYPE_STRUCT: -+ if (type_struct_size(info) != 1) -+ return 0; -+ return fp_equivalent(type_element(info, 0)); -+ } -+ assert(info->type != info->type); -+ abort(); -+} -+ -+static int -+fetch_register_banks(struct Process *proc, struct fetch_context *ctx) -+{ -+ ptrace_area parea; -+ parea.len = sizeof(ctx->regs); -+ parea.process_addr = (uintptr_t)&ctx->regs; -+ parea.kernel_addr = 0; -+ if (ptrace(PTRACE_PEEKUSR_AREA, proc->pid, &parea, NULL) < 0) { -+ fprintf(stderr, "fetch_register_banks GPR: %s\n", -+ strerror(errno)); -+ return -1; -+ } -+ return 0; -+} -+ -+static int -+fetch_context_init(struct Process *proc, struct fetch_context *context) -+{ -+ context->greg = 2; -+ context->freg = 0; -+ return fetch_register_banks(proc, context); -+} -+ -+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) { -+ fprintf(stderr, "arch_fetch_arg_init: %s\n", -+ strerror(errno)); -+ free(context); -+ return NULL; -+ } -+ -+ context->stack_pointer = get_stack_pointer(proc) -+ + (s390x(context) ? 160 : 96); -+ if (ret_info->type == ARGTYPE_STRUCT) -+ ++context->greg; -+ -+ 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; -+} -+ -+static int -+allocate_stack_slot(struct fetch_context *ctx, struct Process *proc, -+ struct arg_type_info *info, struct value *valuep, -+ size_t sz) -+{ -+ /* Note: here we shouldn't see large composite types, those -+ * are passed by reference, which is handled below. Here we -+ * only deal with integers, floats, small structs, etc. */ -+ -+ size_t a; -+ if (s390x(ctx)) { -+ assert(sz <= 8); -+ a = 8; -+ } else { -+ /* Note: double is 8 bytes. */ -+ assert(sz <= 8); -+ a = 4; -+ } -+ -+ size_t off = sz < a ? a - sz : 0; -+ -+ valuep->where = VAL_LOC_INFERIOR; -+ valuep->u.address = ctx->stack_pointer + off; -+ -+ ctx->stack_pointer += sz > a ? sz : a; -+ return 0; -+} -+ -+static void -+copy_gpr(struct fetch_context *ctx, struct value *valuep, int regno) -+{ -+ value_set_word(valuep, ctx->regs.gprs[regno]); -+} -+ -+static int -+allocate_gpr(struct fetch_context *ctx, struct Process *proc, -+ struct arg_type_info *info, struct value *valuep, -+ size_t sz) -+{ -+ if (ctx->greg > 6) -+ return allocate_stack_slot(ctx, proc, info, valuep, sz); -+ -+ copy_gpr(ctx, valuep, ctx->greg++); -+ return 0; -+} -+ -+static int -+allocate_gpr_pair(struct fetch_context *ctx, struct Process *proc, -+ struct arg_type_info *info, struct value *valuep, -+ size_t sz) -+{ -+ assert(!s390x(ctx)); -+ assert(sz <= 8); -+ -+ if (ctx->greg > 5) { -+ ctx->greg = 7; -+ return allocate_stack_slot(ctx, proc, info, valuep, sz); -+ } -+ -+ if (value_reserve(valuep, sz) == NULL) -+ return -1; -+ -+ unsigned char *ptr = value_get_raw_data(valuep); -+ union { -+ struct { -+ uint32_t a; -+ uint32_t b; -+ }; -+ unsigned char buf[8]; -+ } u; -+ u.a = ctx->regs.gprs[ctx->greg++]; -+ u.b = ctx->regs.gprs[ctx->greg++]; -+ memcpy(ptr, u.buf, sz); -+ -+ return 0; -+} -+ -+static int -+allocate_fpr(struct fetch_context *ctx, struct Process *proc, -+ struct arg_type_info *info, struct value *valuep, -+ size_t sz) -+{ -+ int pool = s390x(ctx) ? 6 : 2; -+ -+ if (ctx->freg > pool) -+ return allocate_stack_slot(ctx, proc, info, valuep, sz); -+ -+ if (value_reserve(valuep, sz) == NULL) -+ return -1; -+ -+ memcpy(value_get_raw_data(valuep), -+ &ctx->regs.fp_regs.fprs[ctx->freg], sz); -+ ctx->freg += 2; -+ -+ 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) -+{ -+ size_t sz = type_sizeof(proc, info); -+ if (sz == (size_t)-1) -+ return -1; -+ -+ switch (info->type) { -+ case ARGTYPE_VOID: -+ value_set_word(valuep, 0); -+ return 0; -+ -+ case ARGTYPE_STRUCT: -+ if (fp_equivalent(info)) -+ /* fall through */ -+ case ARGTYPE_FLOAT: -+ case ARGTYPE_DOUBLE: -+ return allocate_fpr(ctx, proc, info, valuep, sz); -+ -+ /* Structures<4 bytes on s390 and structures<8 bytes -+ * on s390x are passed in register. On s390, long -+ * long and structures<8 bytes are passed in two -+ * consecutive registers (if two are available). */ -+ -+ if (sz <= (s390x(ctx) ? 8 : 4)) -+ return allocate_gpr(ctx, proc, info, valuep, sz); -+ else if (sz <= 8) -+ return allocate_gpr_pair(ctx, proc, info, valuep, sz); -+ -+ /* fall through */ -+ -+ case ARGTYPE_ARRAY: -+ if (value_pass_by_reference(valuep) < 0) -+ return -1; -+ /* fall through */ -+ -+ case ARGTYPE_INT: -+ case ARGTYPE_UINT: -+ case ARGTYPE_LONG: -+ case ARGTYPE_ULONG: -+ case ARGTYPE_CHAR: -+ case ARGTYPE_SHORT: -+ case ARGTYPE_USHORT: -+ case ARGTYPE_POINTER: -+ return allocate_gpr(ctx, proc, info, valuep, sz); -+ } -+ return -1; -+} -+ -+int -+arch_fetch_retval(struct fetch_context *ctx, enum tof type, -+ struct Process *proc, struct arg_type_info *info, -+ struct value *valuep) -+{ -+ if (info->type == ARGTYPE_STRUCT) { -+ if (value_pass_by_reference(valuep) < 0) -+ return -1; -+ copy_gpr(ctx, valuep, 2); -+ return 0; -+ } -+ -+ if (fetch_context_init(proc, ctx) < 0) -+ return -1; -+ return arch_fetch_arg_next(ctx, type, proc, info, valuep); -+} -+ -+void -+arch_fetch_arg_done(struct fetch_context *context) -+{ -+ free(context); -+} -diff --git a/sysdeps/linux-gnu/s390/plt.c b/sysdeps/linux-gnu/s390/plt.c -index 754d270..cd6454e 100644 ---- a/sysdeps/linux-gnu/s390/plt.c -+++ b/sysdeps/linux-gnu/s390/plt.c -@@ -1,6 +1,7 @@ - #include - #include "proc.h" - #include "common.h" -+#include "library.h" - - GElf_Addr - arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { -diff --git a/sysdeps/linux-gnu/s390/trace.c b/sysdeps/linux-gnu/s390/trace.c -index 3381ccc..4f6eb8d 100644 ---- a/sysdeps/linux-gnu/s390/trace.c -+++ b/sysdeps/linux-gnu/s390/trace.c -@@ -1,24 +1,38 @@ - /* --** S390 specific part of trace.c --** --** Other routines are in ../trace.c and need to be combined --** at link time with this code. --** --** Copyright (C) 2001,2005 IBM Corp. --*/ -+ * This file is part of ltrace. -+ * Copyright (C) 2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2001,2005 IBM Corp. -+ * -+ * 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 "config.h" - --#include --#include -+#include -+#include - #include - #include -+#include -+#include - #include --#include --#include -+#include - --#include "proc.h" - #include "common.h" -+#include "proc.h" -+#include "type.h" - - #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) - # define PTRACE_PEEKUSER PTRACE_PEEKUSR -@@ -160,44 +174,82 @@ syscall_p(Process *proc, int status, int *sysnum) { - return 0; - } - --long --gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info) -+size_t -+arch_type_sizeof(struct Process *proc, struct arg_type_info *info) - { -- long ret; -+ if (proc == NULL) -+ return (size_t)-2; -+ -+ switch (info->type) { -+ case ARGTYPE_VOID: -+ return 0; -+ -+ case ARGTYPE_CHAR: -+ return 1; -+ -+ case ARGTYPE_SHORT: -+ case ARGTYPE_USHORT: -+ return 2; -+ -+ case ARGTYPE_INT: -+ case ARGTYPE_UINT: -+ return 4; -+ -+ case ARGTYPE_LONG: -+ case ARGTYPE_ULONG: -+ case ARGTYPE_POINTER: -+ return proc->e_class == ELFCLASS64 ? 8 : 4; -+ -+ case ARGTYPE_FLOAT: -+ return 4; -+ case ARGTYPE_DOUBLE: -+ return 8; -+ -+ case ARGTYPE_ARRAY: -+ case ARGTYPE_STRUCT: -+ /* Use default value. */ -+ return (size_t)-2; -+ } -+ assert(info->type != info->type); -+ abort(); -+} - -- switch (arg_num) { -- case -1: /* return value */ -- ret = ptrace(PTRACE_PEEKUSER, proc->pid, PT_GPR2, 0); -- break; -- case 0: -- ret = ptrace(PTRACE_PEEKUSER, proc->pid, PT_ORIGGPR2, 0); -- break; -- case 1: -- ret = ptrace(PTRACE_PEEKUSER, proc->pid, PT_GPR3, 0); -- break; -- case 2: -- ret = ptrace(PTRACE_PEEKUSER, proc->pid, PT_GPR4, 0); -- break; -- case 3: -- ret = ptrace(PTRACE_PEEKUSER, proc->pid, PT_GPR5, 0); -- break; -- case 4: -- ret = ptrace(PTRACE_PEEKUSER, proc->pid, PT_GPR6, 0); -+size_t -+arch_type_alignof(struct Process *proc, struct arg_type_info *info) -+{ -+ if (proc == NULL) -+ return (size_t)-2; -+ -+ switch (info->type) { -+ case ARGTYPE_VOID: -+ assert(info->type != ARGTYPE_VOID); - break; -- default: -- /*Rest of the params saved in stack */ -- if (arg_num >= 5) { -- ret = ptrace(PTRACE_PEEKUSER, proc->pid, -- proc->stack_pointer + 96 + -- 4 * (arg_num - 5), 0); -- } else { -- fprintf(stderr, "gimme_arg called with wrong arguments\n"); -- exit(2); -- } -+ -+ case ARGTYPE_CHAR: -+ return 1; -+ -+ case ARGTYPE_SHORT: -+ case ARGTYPE_USHORT: -+ return 2; -+ -+ case ARGTYPE_INT: -+ case ARGTYPE_UINT: -+ return 4; -+ -+ case ARGTYPE_LONG: -+ case ARGTYPE_ULONG: -+ case ARGTYPE_POINTER: -+ return proc->e_class == ELFCLASS64 ? 8 : 4; -+ -+ case ARGTYPE_FLOAT: -+ return 4; -+ case ARGTYPE_DOUBLE: -+ return 8; -+ -+ case ARGTYPE_ARRAY: -+ case ARGTYPE_STRUCT: -+ /* Use default value. */ -+ return (size_t)-2; - } --#ifdef __s390x__ -- if (proc->mask_32bit) -- ret &= 0xffffffff; --#endif -- return ret; -+ abort(); - } -diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c -index cef8e3d..0829bdb 100644 ---- a/sysdeps/linux-gnu/trace.c -+++ b/sysdeps/linux-gnu/trace.c -@@ -782,7 +782,7 @@ process_stopping_on_event(struct event_handler *super, Event *event) - * reason for the re-enablement. In that case - * handle it. */ - if (event->type == EVENT_BREAKPOINT) { -- target_address_t ip -+ arch_addr_t ip - = get_instruction_pointer(task); - struct breakpoint *other - = address2bpstruct(leader, ip); -diff --git a/sysdeps/linux-gnu/x86/fetch.c b/sysdeps/linux-gnu/x86/fetch.c -index 64f57f3..8df900e 100644 ---- a/sysdeps/linux-gnu/x86/fetch.c -+++ b/sysdeps/linux-gnu/x86/fetch.c -@@ -592,14 +592,14 @@ arch_fetch_retval_32(struct fetch_context *context, enum tof type, - abort(); - } - --static target_address_t -+static arch_addr_t - fetch_stack_pointer(struct fetch_context *context) - { -- target_address_t sp; -+ arch_addr_t sp; - #ifdef __x86_64__ -- sp = (target_address_t)context->iregs.rsp; -+ sp = (arch_addr_t)context->iregs.rsp; - #else -- sp = (target_address_t)context->iregs.esp; -+ sp = (arch_addr_t)context->iregs.esp; - #endif - return sp; - } -diff --git a/sysdeps/linux-gnu/x86/regs.c b/sysdeps/linux-gnu/x86/regs.c -index 477abca..ca6470b 100644 ---- a/sysdeps/linux-gnu/x86/regs.c -+++ b/sysdeps/linux-gnu/x86/regs.c -@@ -47,12 +47,12 @@ - # define XSP (4 * UESP) - #endif - --static target_address_t --conv_32(target_address_t val) -+static arch_addr_t -+conv_32(arch_addr_t val) - { -- /* XXX Drop the multiple double casts when target_address_t -+ /* XXX Drop the multiple double casts when arch_addr_t - * becomes integral. */ -- return (target_address_t)(uintptr_t)(uint32_t)(uintptr_t)val; -+ return (arch_addr_t)(uintptr_t)(uint32_t)(uintptr_t)val; - } - - void * -@@ -65,7 +65,7 @@ get_instruction_pointer(struct Process *proc) - } - - void --set_instruction_pointer(struct Process *proc, target_address_t addr) -+set_instruction_pointer(struct Process *proc, arch_addr_t addr) - { - if (proc->e_machine == EM_386) - addr = conv_32(addr); -@@ -82,9 +82,9 @@ get_stack_pointer(struct Process *proc) - return NULL; - } - -- /* XXX Drop the multiple double casts when target_address_t -+ /* XXX Drop the multiple double casts when arch_addr_t - * becomes integral. */ -- target_address_t ret = (target_address_t)(uintptr_t)sp; -+ arch_addr_t ret = (arch_addr_t)(uintptr_t)sp; - if (proc->e_machine == EM_386) - ret = conv_32(ret); - return ret; -@@ -100,9 +100,9 @@ get_return_addr(struct Process *proc, void *sp) - return NULL; - } - -- /* XXX Drop the multiple double casts when target_address_t -+ /* XXX Drop the multiple double casts when arch_addr_t - * becomes integral. */ -- target_address_t ret = (target_address_t)(uintptr_t)a; -+ arch_addr_t ret = (arch_addr_t)(uintptr_t)a; - if (proc->e_machine == EM_386) - ret = conv_32(ret); - return ret; -diff --git a/sysdeps/sysdep.h b/sysdeps/sysdep.h -index 96b3857..70a4fa7 100644 ---- a/sysdeps/sysdep.h -+++ b/sysdeps/sysdep.h -@@ -28,4 +28,13 @@ struct arch_process_data { - }; - #endif - -+#ifndef ARCH_HAVE_ADDRESS_TYPES -+/* We should in general be able to trace 64-bit processes with 32-bit -+ * ltrace. (At least PPC has several PTRACE requests related to -+ * tracing 64-on-32, so presumably it should be possible.) But ltrace -+ * is currently hopelessly infested with using void* for host address. -+ * So keep with it, for now. */ -+typedef void *arch_addr_t; -+#endif -+ - #endif /* LTRACE_SYSDEP_H */ -diff --git a/testsuite/ltrace.main/parameters-lib.c b/testsuite/ltrace.main/parameters-lib.c -index f9a869d..27fe569 100644 ---- a/testsuite/ltrace.main/parameters-lib.c -+++ b/testsuite/ltrace.main/parameters-lib.c -@@ -241,3 +241,63 @@ void - func_charp_string(char *p) - { - } -+ -+struct dbl_eqv1 { double d; }; -+struct dbl_eqv2 { struct dbl_eqv1 d; }; -+struct dbl_eqv3 { struct dbl_eqv2 d; }; -+struct dbl_eqv4 { struct dbl_eqv3 d; }; -+ -+struct flt_eqv1 { float d; }; -+struct flt_eqv2 { struct flt_eqv1 d; }; -+struct flt_eqv3 { struct flt_eqv2 d; }; -+struct flt_eqv4 { struct flt_eqv3 d; }; -+ -+struct dbl_eqv1 -+func_dbl_eqv(struct dbl_eqv1 a, struct dbl_eqv2 b, -+ struct dbl_eqv3 c, struct dbl_eqv4 d) -+{ -+ return (struct dbl_eqv1){ a.d + b.d.d + c.d.d.d + d.d.d.d.d }; -+} -+ -+struct flt_eqv1 -+func_flt_eqv(struct flt_eqv1 a, struct flt_eqv2 b, -+ struct flt_eqv3 c, struct flt_eqv4 d) -+{ -+ return (struct flt_eqv1){ a.d + b.d.d + c.d.d.d + d.d.d.d.d }; -+} -+ -+struct struct_empty {}; -+struct struct_size1 { char a; }; -+struct struct_size2 { short a; }; -+struct struct_size4 { int a; }; -+struct struct_size8 { int a; int b; }; -+ -+struct struct_empty -+func_struct_empty(struct struct_empty e) -+{ -+ return e; -+} -+ -+struct struct_size1 -+func_struct_size1(struct struct_size1 e) -+{ -+ return e; -+} -+ -+struct struct_size2 -+func_struct_size2(struct struct_size2 e) -+{ -+ return e; -+} -+ -+struct struct_size4 -+func_struct_size4(struct struct_size4 e) -+{ -+ return e; -+} -+ -+struct struct_size8 -+func_struct_size8(struct struct_size8 e) -+{ -+ return e; -+} -diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c -index e8207fe..6318e60 100644 ---- a/testsuite/ltrace.main/parameters.c -+++ b/testsuite/ltrace.main/parameters.c -@@ -218,5 +218,49 @@ main () - void func_charp_string(char *p); - func_charp_string("null-terminated string"); - -+ struct dbl_eqv1 { double d; }; -+ struct dbl_eqv2 { struct dbl_eqv1 d; }; -+ struct dbl_eqv3 { struct dbl_eqv2 d; }; -+ struct dbl_eqv4 { struct dbl_eqv3 d; }; -+ -+ struct flt_eqv1 { float d; }; -+ struct flt_eqv2 { struct flt_eqv1 d; }; -+ struct flt_eqv3 { struct flt_eqv2 d; }; -+ struct flt_eqv4 { struct flt_eqv3 d; }; -+ -+ struct dbl_eqv1 func_dbl_eqv(struct dbl_eqv1 a, struct dbl_eqv2 b, -+ struct dbl_eqv3 c, struct dbl_eqv4 d); -+ func_dbl_eqv((struct dbl_eqv1){ 2.5 }, -+ (struct dbl_eqv2){ { 1.5 } }, -+ (struct dbl_eqv3){ { { 0.5 } } }, -+ (struct dbl_eqv4){ { { { -0.5 } } } }); -+ -+ struct flt_eqv1 func_flt_eqv(struct flt_eqv1 a, struct flt_eqv2 b, -+ struct flt_eqv3 c, struct flt_eqv4 d); -+ func_flt_eqv((struct flt_eqv1){ 2.5 }, -+ (struct flt_eqv2){ { 1.5 } }, -+ (struct flt_eqv3){ { { 0.5 } } }, -+ (struct flt_eqv4){ { { { -0.5 } } } }); -+ -+ struct struct_empty {}; -+ struct struct_empty func_struct_empty(struct struct_empty e); -+ func_struct_empty((struct struct_empty) {}); -+ -+ struct struct_size1 { char a; }; -+ struct struct_size1 func_struct_size1(struct struct_size1 e); -+ func_struct_size1((struct struct_size1){ '5' }); -+ -+ struct struct_size2 { short a; }; -+ struct struct_size2 func_struct_size2(struct struct_size2 e); -+ func_struct_size2((struct struct_size2){ 5 }); -+ -+ struct struct_size4 { int a; }; -+ struct struct_size4 func_struct_size4(struct struct_size4 e); -+ func_struct_size4((struct struct_size4){ 5 }); -+ -+ struct struct_size8 { int a; int b; }; -+ struct struct_size8 func_struct_size8(struct struct_size8 e); -+ func_struct_size8((struct struct_size8){ 5, 6 }); -+ - return 0; - } -diff --git a/testsuite/ltrace.main/parameters.conf b/testsuite/ltrace.main/parameters.conf -index 9e0c967..ea1c55d 100644 ---- a/testsuite/ltrace.main/parameters.conf -+++ b/testsuite/ltrace.main/parameters.conf -@@ -29,3 +29,10 @@ void func_hide(int, hide(int), hide(int), int, hide(int), int); - array(enum[long](A,B), 4) *func_short_enums(array(enum[short](A,B), 4)); - enum[long](A=-1) func_negative_enum(enum[short](A=-1), enum[ushort](A=-1), enum[int](A=-1), enum[uint](A=-1), enum[long](A=-1), enum[ulong](A=-1)); - void func_charp_string(string(char *)); -+struct(double) func_dbl_eqv(struct(double), struct(struct(double)), struct(struct(struct(double))), struct(struct(struct(struct(double))))); -+struct(float) func_flt_eqv(struct(float), struct(struct(float)), struct(struct(struct(float))), struct(struct(struct(struct(float))))); -+struct() func_struct_empty(struct()); -+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)); -diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp -index 8403721..b62315d 100644 ---- a/testsuite/ltrace.main/parameters.exp -+++ b/testsuite/ltrace.main/parameters.exp -@@ -135,3 +135,24 @@ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 - - set pattern "func_charp_string(\\\"null-terminated string\\\")" - ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ -+set pattern "func_dbl_eqv({ 2.500* }, { { 1.50* } }, { { { 0.50* } } }, { { { { -0.50* } } } }).*= { 4.00* }" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ -+set pattern "func_flt_eqv({ 2.500* }, { { 1.50* } }, { { { 0.50* } } }, { { { { -0.50* } } } }).*= { 4.00* }" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ -+set pattern "func_struct_empty({ *}).*= { *}" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ -+set pattern "func_struct_size1({ '5' }).*= { '5' }" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ -+set pattern "func_struct_size2({ 5 }).*= { 5 }" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ -+set pattern "func_struct_size4({ 5 }).*= { 5 }" -+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 diff --git a/ltrace-0.6.0-abi.patch b/ltrace-0.6.0-abi.patch deleted file mode 100644 index 17850bf..0000000 --- a/ltrace-0.6.0-abi.patch +++ /dev/null @@ -1,13571 +0,0 @@ -diff --git a/Makefile.am b/Makefile.am -index a00c8bf..dc9091d 100644 ---- a/Makefile.am -+++ b/Makefile.am -@@ -15,7 +15,6 @@ libltrace_la_SOURCES = \ - debug.c \ - demangle.c \ - dict.c \ -- display_args.c \ - ltrace-elf.c \ - execute_program.c \ - handle_event.c \ -@@ -27,7 +26,19 @@ libltrace_la_SOURCES = \ - summary.c \ - library.c \ - filter.c \ -- glob.c -+ glob.c \ -+ type.c \ -+ value.c \ -+ value_dict.c \ -+ expr.c \ -+ fetch.c \ -+ vect.c \ -+ param.c \ -+ printf.c \ -+ zero.c \ -+ lens.c \ -+ lens_default.c \ -+ lens_enum.c - - libltrace_la_LIBADD = \ - $(libelf_LIBS) \ -@@ -50,6 +61,7 @@ ltrace_LDADD = \ - - - noinst_HEADERS = \ -+ backend.h \ - common.h \ - debug.h \ - defs.h \ -@@ -62,7 +74,20 @@ noinst_HEADERS = \ - read_config_file.h \ - library.h \ - filter.h \ -- glob.h -+ glob.h \ -+ vect.h \ -+ type.h \ -+ value.h \ -+ value_dict.h \ -+ expr.h \ -+ fetch.h \ -+ vect.h \ -+ param.h \ -+ printf.h \ -+ zero.h \ -+ lens.h \ -+ lens_default.h \ -+ lens_enum.h - - dist_man1_MANS = \ - ltrace.1 -diff --git a/backend.h b/backend.h -new file mode 100644 -index 0000000..08306e1 ---- /dev/null -+++ b/backend.h -@@ -0,0 +1,250 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 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 BACKEND_H -+#define BACKEND_H -+ -+#include "forward.h" -+#include -+ -+enum process_status { -+ ps_invalid, /* Failure. */ -+ ps_stop, /* Job-control stop. */ -+ ps_tracing_stop, -+ ps_sleeping, -+ ps_zombie, -+ ps_other, /* Necessary other states can be added as needed. */ -+}; -+ -+typedef void *target_address_t; -+ -+/* -+ * This file contains documentation of back end interface. Some of -+ * these may be implemented on an OS level (i.e. they are the same -+ * e.g. on all Linux architectures), some may differ per architecture -+ * on the same OS (e.g. a way to insert a breakpoint into the process -+ * image is a likely candidate). -+ */ -+ -+/* Convert a PID to a path to the corresponding binary. */ -+char *pid2name(pid_t pid); -+ -+/* Given a PID, find a leader of thread group. */ -+pid_t process_leader(pid_t pid); -+ -+/* Given a PID of leader thread, fill in PIDs of all the tasks. The -+ * function will initialize the pointer *RET_TASKS to a -+ * newly-allocated array, and will store number of elements in that -+ * array to *RET_N. You have to free that buffer when you don't need -+ * it anymore. */ -+int process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n); -+ -+/* Answer whether the process PID is stopped. Returns 0 when not -+ * stopped, 1 when stopped, or -1 when there was an error. */ -+int process_stopped(pid_t pid); -+ -+/* Answer a status of the task PID. See enum process_status. */ -+enum process_status process_status(pid_t pid); -+ -+/* Wait for PID to be ready for tracing. */ -+int wait_for_proc(pid_t pid); -+ -+/* Send a signal SIG to the task PID. */ -+int task_kill(pid_t pid, int sig); -+ -+/* Called after PID is attached, but before it is continued. */ -+void trace_set_options(struct Process *proc); -+ -+/* Called after ltrace forks. Should attach the newly created child, -+ * in whose context this function is called. */ -+void trace_me(void); -+ -+/* Called when ltrace needs to attach to PID, such as when it attaches -+ * to a running process, whose PID is given on the command line. */ -+int trace_pid(pid_t pid); -+ -+/* Stop tracing PID. */ -+void untrace_pid(pid_t pid); -+ -+/* The back end may need to store arbitrary data to a process. This -+ * is a place where it can initialize PROC->arch_dep. XXX this should -+ * be dropped in favor of arhc_process_init on pmachata/libs. */ -+void get_arch_dep(struct Process *proc); -+ -+/* Return current instruction pointer of PROC. -+ * -+ * XXX note that the IP must fit into an arch pointer. This prevents -+ * us to use 32-bit ltrace to trace 64-bit process, even on arches -+ * that would otherwise support this. Above we have a definition of -+ * target_address_t. This should be converted to an integral type and -+ * used for target addresses throughout. */ -+void *get_instruction_pointer(struct Process *proc); -+ -+/* Set instruction pointer of PROC to ADDR. XXX see above. */ -+void set_instruction_pointer(struct Process *proc, void *addr); -+ -+/* Return current stack pointer of PROC. XXX see above. */ -+void *get_stack_pointer(struct Process *proc); -+ -+/* Find and return caller address, i.e. the address where the current -+ * function returns. */ -+void *get_return_addr(struct Process *proc, void *stack_pointer); -+ -+/* Adjust PROC so that when the current function returns, it returns -+ * to ADDR. */ -+void set_return_addr(struct Process *proc, void *addr); -+ -+/* Enable breakpoint SBP in process PROC. */ -+void enable_breakpoint(struct Process *proc, struct breakpoint *sbp); -+ -+/* Disable breakpoint SBP in process PROC. */ -+void disable_breakpoint(struct Process *proc, struct breakpoint *sbp); -+ -+/* Determine whether the event that we have just seen (and that is -+ * recorded in STATUS) was a syscall. If it was, return 1. If it was -+ * a return from syscall, return 2. In both cases, set *SYSNUM to the -+ * number of said syscall. If it wasn't a syscall, return 0. If -+ * there was an error, return -1. */ -+int syscall_p(struct Process *proc, int status, int *sysnum); -+ -+/* Continue execution of the process with given PID. */ -+void continue_process(pid_t pid); -+ -+/* Called after we received a signal SIGNUM. Should do whatever -+ * book-keeping is necessary and continue the process if -+ * necessary. */ -+void continue_after_signal(pid_t pid, int signum); -+ -+/* Called after we received a system call SYSNUM. RET_P is 0 if this -+ * is system call, otherwise it's return from a system call. The -+ * callback should do whatever book-keeping is necessary and continue -+ * the process if necessary. */ -+void continue_after_syscall(struct Process *proc, int sysnum, int ret_p); -+ -+/* Called after we hit a breakpoint SBP. Should do whatever -+ * book-keeping is necessary and then continue the process. */ -+void continue_after_breakpoint(struct Process *proc, struct breakpoint *sbp); -+ -+/* Called after we received a vfork. Should do whatever book-keeping -+ * is necessary and continue the process if necessary. N.B. right -+ * now, with Linux/GNU the only back end, this is not necessary. I -+ * imagine other systems may be different. */ -+void continue_after_vfork(struct Process *proc); -+ -+/* Called when trace_me or primary trace_pid fail. This may plug in -+ * any platform-specific knowledge of why it could be so. */ -+void trace_fail_warning(pid_t pid); -+ -+/* A pair of functions called to initiate a detachment request when -+ * ltrace is about to exit. Their job is to undo any effects that -+ * tracing had and eventually detach process, perhaps by way of -+ * installing a process handler. -+ * -+ * OS_LTRACE_EXITING_SIGHANDLER is called from a signal handler -+ * context right after the signal was captured. It returns 1 if the -+ * request was handled or 0 if it wasn't. -+ * -+ * If the call to OS_LTRACE_EXITING_SIGHANDLER didn't handle the -+ * request, OS_LTRACE_EXITING is called when the next event is -+ * generated. Therefore it's called in "safe" context, without -+ * re-entrancy concerns, but it's only called after an even is -+ * generated. */ -+int os_ltrace_exiting_sighandler(void); -+void os_ltrace_exiting(void); -+ -+/* Should copy COUNT bytes from address ADDR of process PROC to local -+ * buffer BUF. */ -+size_t umovebytes (struct Process *proc, void *addr, void *buf, size_t count); -+ -+/* Find out an address of symbol SYM in process PROC, and return. -+ * Returning NULL delays breakpoint insertion and enables heaps of -+ * arch-specific black magic that we should clean up some day. -+ * -+ * XXX the same points as for get_instruction_pointer apply. */ -+void *sym2addr(struct Process *proc, struct library_symbol *sym); -+ -+/* 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, target_address_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 -+ * done some other way. */ -+struct Event *next_event(void); -+ -+/* Called when process PROC was removed. */ -+void process_removed(struct Process *proc); -+ -+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); -+ -+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); -+ -+void arch_library_init(struct library *lib); -+void arch_library_destroy(struct library *lib); -+void arch_library_clone(struct library *retp, struct library *lib); -+ -+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); -+ -+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, -+ target_address_t *entryp, -+ target_address_t *interp_biasp); -+ -+/* This is called after the dynamic linker is done with the -+ * process startup. */ -+void arch_dynlink_done(struct Process *proc); -+ -+#endif /* BACKEND_H */ -diff --git a/breakpoints.c b/breakpoints.c -index 93ae244..8dc09df 100644 ---- a/breakpoints.c -+++ b/breakpoints.c -@@ -1,18 +1,21 @@ - #include "config.h" - --#include --#include - #include - #include -+#include -+#include -+#include - - #ifdef __powerpc__ - #include - #endif - -+#include "backend.h" - #include "breakpoint.h" --#include "common.h" --#include "proc.h" -+#include "debug.h" - #include "library.h" -+#include "ltrace-elf.h" -+#include "proc.h" - - #ifndef ARCH_HAVE_TRANSLATE_ADDRESS - int -diff --git a/common.h b/common.h -index 7fffa76..959715a 100644 ---- a/common.h -+++ b/common.h -@@ -1,3 +1,27 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2010 Joe Damato -+ * Copyright (C) 2009 Juan Cespedes -+ * -+ * 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 COMMON_H -+#define COMMON_H - #include - - #include -@@ -15,6 +37,7 @@ - #include "ltrace-elf.h" - #include "read_config_file.h" - #include "proc.h" -+#include "forward.h" - - #if defined HAVE_LIBSUPC__ || defined HAVE_LIBSTDC__ - # define USE_CXA_DEMANGLE -@@ -24,93 +47,13 @@ extern char * command; - - extern int exiting; /* =1 if we have to exit ASAP */ - --enum arg_type { -- ARGTYPE_UNKNOWN = -1, -- ARGTYPE_VOID, -- ARGTYPE_INT, -- ARGTYPE_UINT, -- ARGTYPE_LONG, -- ARGTYPE_ULONG, -- ARGTYPE_OCTAL, -- ARGTYPE_CHAR, -- ARGTYPE_SHORT, -- ARGTYPE_USHORT, -- ARGTYPE_FLOAT, /* float value, may require index */ -- ARGTYPE_DOUBLE, /* double value, may require index */ -- ARGTYPE_ADDR, -- ARGTYPE_FILE, -- ARGTYPE_FORMAT, /* printf-like format */ -- ARGTYPE_STRING, /* NUL-terminated string */ -- ARGTYPE_STRING_N, /* String of known maxlen */ -- ARGTYPE_ARRAY, /* Series of values in memory */ -- ARGTYPE_ENUM, /* Enumeration */ -- ARGTYPE_STRUCT, /* Structure of values */ -- ARGTYPE_POINTER, /* Pointer to some other type */ -- ARGTYPE_COUNT /* number of ARGTYPE_* values */ --}; -- --typedef struct arg_type_info_t { -- enum arg_type type; -- union { -- /* ARGTYPE_ENUM */ -- struct { -- size_t entries; -- char ** keys; -- int * values; -- } enum_info; -- -- /* ARGTYPE_ARRAY */ -- struct { -- struct arg_type_info_t * elt_type; -- size_t elt_size; -- int len_spec; -- } array_info; -- -- /* ARGTYPE_STRING_N */ -- struct { -- int size_spec; -- } string_n_info; -- -- /* ARGTYPE_STRUCT */ -- struct { -- struct arg_type_info_t ** fields; /* NULL-terminated */ -- size_t * offset; -- size_t size; -- } struct_info; -- -- /* ARGTYPE_POINTER */ -- struct { -- struct arg_type_info_t * info; -- } ptr_info; -- -- /* ARGTYPE_FLOAT */ -- struct { -- size_t float_index; -- } float_info; -- -- /* ARGTYPE_DOUBLE */ -- struct { -- size_t float_index; -- } double_info; -- } u; --} arg_type_info; -- --enum tof { -- LT_TOF_NONE = 0, -- LT_TOF_FUNCTION, /* A real library function */ -- LT_TOF_FUNCTIONR, /* Return from a real library function */ -- LT_TOF_SYSCALL, /* A syscall */ -- LT_TOF_SYSCALLR, /* Return from a syscall */ -- LT_TOF_STRUCT /* Not a function; read args from struct */ --}; -- - typedef struct Function Function; - struct Function { - const char * name; -- arg_type_info * return_info; -- int num_params; -- arg_type_info * arg_info[MAX_ARGS]; -- int params_right; -+ struct param *params; -+ struct arg_type_info *return_info; -+ int own_return_info; -+ size_t num_params; - Function * next; - }; - -@@ -130,136 +72,22 @@ struct opt_c_struct { - - extern Dict * dict_opt_c; - --enum process_status { -- ps_invalid, /* Failure. */ -- ps_stop, /* Job-control stop. */ -- ps_tracing_stop, -- ps_sleeping, -- ps_zombie, -- ps_other, /* Necessary other states can be added as needed. */ --}; -- - /* Events */ --enum ecb_status { -- ecb_cont, /* The iteration should continue. */ -- ecb_yield, /* The iteration should stop, yielding this -- * event. */ -- ecb_deque, /* Like ecb_stop, but the event should be removed -- * from the queue. */ --}; - extern Event * next_event(void); --extern Event * each_qd_event(enum ecb_status (* cb)(Event * event, void * data), -- void * data); --extern void enque_event(Event * event); - extern void handle_event(Event * event); - - extern pid_t execute_program(const char * command, char ** argv); --extern int display_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); --extern void disable_all_breakpoints(Process * proc); - - extern void show_summary(void); --extern arg_type_info * lookup_prototype(enum arg_type at); - - struct breakpoint; - struct library_symbol; - --/* Arch-dependent stuff: */ --extern char * pid2name(pid_t pid); --extern pid_t process_leader(pid_t pid); --extern int process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n); --extern int process_stopped(pid_t pid); --extern enum process_status process_status(pid_t pid); --extern void trace_set_options(struct Process *proc); --extern int wait_for_proc(pid_t pid); --extern void trace_me(void); --extern int trace_pid(pid_t pid); --extern void untrace_pid(pid_t pid); --extern void get_arch_dep(Process * proc); --extern void * get_instruction_pointer(Process * proc); --extern void set_instruction_pointer(Process * proc, void * addr); --extern void * get_stack_pointer(Process * proc); --extern void * get_return_addr(Process * proc, void * stack_pointer); --extern void set_return_addr(Process * proc, void * addr); --extern void enable_breakpoint(struct Process *proc, struct breakpoint *sbp); --extern void disable_breakpoint(struct Process *proc, struct breakpoint *sbp); --extern int syscall_p(Process * proc, int status, int * sysnum); --extern void continue_process(pid_t pid); --extern void continue_after_signal(pid_t pid, int signum); --extern void continue_after_syscall(Process *proc, int sysnum, int ret_p); --extern void continue_after_breakpoint(struct Process *proc, struct breakpoint *sbp); --extern void continue_after_vfork(Process * proc); --extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); --extern void save_register_args(enum tof type, Process * proc); --extern int umovestr(Process * proc, void * addr, int len, void * laddr); --extern int umovelong (Process * proc, void * addr, long * result, arg_type_info * info); --extern size_t umovebytes (Process *proc, void * addr, void * laddr, size_t count); --extern int ffcheck(void * maddr); --extern void * sym2addr(Process *, struct library_symbol *); --extern int linkmap_init(struct Process *proc, void *dyn_addr); --extern void arch_check_dbg(Process *proc); --extern int task_kill (pid_t pid, int sig); -- --/* Called when trace_me or primary trace_pid fail. This may plug in -- * any platform-specific knowledge of why it could be so. */ --void trace_fail_warning(pid_t pid); -- --/* A pair of functions called to initiate a detachment request when -- * ltrace is about to exit. Their job is to undo any effects that -- * tracing had and eventually detach process, perhaps by way of -- * installing a process handler. -- * -- * OS_LTRACE_EXITING_SIGHANDLER is called from a signal handler -- * context right after the signal was captured. It returns 1 if the -- * request was handled or 0 if it wasn't. -- * -- * If the call to OS_LTRACE_EXITING_SIGHANDLER didn't handle the -- * request, OS_LTRACE_EXITING is called when the next event is -- * generated. Therefore it's called in "safe" context, without -- * re-entrancy concerns, but it's only called after an event is -- * generated. */ --int os_ltrace_exiting_sighandler(void); --void os_ltrace_exiting(void); -- --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); -- --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); -- --void arch_library_init(struct library *lib); --void arch_library_destroy(struct library *lib); --void arch_library_clone(struct library *retp, struct library *lib); -- --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); -- --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); -- --typedef void *target_address_t; --/* 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, -- target_address_t *entryp, -- target_address_t *interp_biasp); -- --/* This is called after the dynamic linker is done with the -- * process startup. */ --void arch_dynlink_done(struct Process *proc); -+/* Format VALUE into STREAM. The dictionary of all arguments is given -+ * for purposes of evaluating array lengths and other dynamic -+ * expressions. Returns number of characters outputted, -1 in case of -+ * failure. */ -+int format_argument(FILE *stream, struct value *value, -+ struct value_dict *arguments); -+ -+#endif /* COMMON_H */ -diff --git a/configure.ac b/configure.ac -index 42d6158..26e1bb6 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -18,10 +18,10 @@ AC_SUBST(HOST_OS) - - case "${host_cpu}" in - arm*|sa110) HOST_CPU="arm" ;; -- i?86) HOST_CPU="i386" ;; - powerpc|powerpc64) HOST_CPU="ppc" ;; - sun4u|sparc64) HOST_CPU="sparc" ;; - s390x) HOST_CPU="s390" ;; -+ i?86|x86_64) HOST_CPU="x86" ;; - *) HOST_CPU="${host_cpu}" ;; - esac - AC_SUBST(HOST_CPU) -@@ -261,14 +261,13 @@ AC_CONFIG_FILES([ - sysdeps/linux-gnu/Makefile - sysdeps/linux-gnu/alpha/Makefile - sysdeps/linux-gnu/arm/Makefile -- sysdeps/linux-gnu/i386/Makefile - sysdeps/linux-gnu/ia64/Makefile - sysdeps/linux-gnu/m68k/Makefile - sysdeps/linux-gnu/mipsel/Makefile - sysdeps/linux-gnu/ppc/Makefile - sysdeps/linux-gnu/s390/Makefile - sysdeps/linux-gnu/sparc/Makefile -- sysdeps/linux-gnu/x86_64/Makefile -+ sysdeps/linux-gnu/x86/Makefile - testsuite/Makefile - testsuite/ltrace.main/Makefile - testsuite/ltrace.minor/Makefile -diff --git a/defs.h b/defs.h -index 1eadb47..e346ccb 100644 ---- a/defs.h -+++ b/defs.h -@@ -3,10 +3,6 @@ - #define DEFAULT_ALIGN 50 /* default alignment column for results */ - #endif /* (-a switch) */ - --#ifndef MAX_ARGS --#define MAX_ARGS 32 /* maximum number of args for a function */ --#endif -- - #ifndef DEFAULT_STRLEN - #define DEFAULT_STRLEN 32 /* default maximum # of bytes printed in */ - #endif /* strings (-s switch) */ -diff --git a/display_args.c b/display_args.c -deleted file mode 100644 -index 5df34ca..0000000 ---- a/display_args.c -+++ /dev/null -@@ -1,461 +0,0 @@ --#include --#include --#include --#include --#include -- --#include "common.h" --#include "proc.h" -- --static int display_char(int what); --static int display_string(enum tof type, Process *proc, -- void* addr, size_t maxlen); --static int display_value(enum tof type, Process *proc, -- long value, arg_type_info *info, -- void *st, arg_type_info* st_info); --static int display_unknown(enum tof type, Process *proc, long value); --static int display_format(enum tof type, Process *proc, int arg_num); -- --static size_t string_maxlength = INT_MAX; --static size_t array_maxlength = INT_MAX; -- --static long --get_length(enum tof type, Process *proc, int len_spec, -- void *st, arg_type_info* st_info) { -- long len; -- arg_type_info info; -- -- if (len_spec > 0) -- return len_spec; -- if (type == LT_TOF_STRUCT) { -- umovelong (proc, st + st_info->u.struct_info.offset[-len_spec-1], -- &len, st_info->u.struct_info.fields[-len_spec-1]); -- return len; -- } -- -- info.type = ARGTYPE_INT; -- return gimme_arg(type, proc, -len_spec-1, &info); --} -- --static int --display_ptrto(enum tof type, Process *proc, long item, -- arg_type_info * info, -- void *st, arg_type_info* st_info) { -- arg_type_info temp; -- temp.type = ARGTYPE_POINTER; -- temp.u.ptr_info.info = info; -- return display_value(type, proc, item, &temp, st, st_info); --} -- --/* -- * addr - A pointer to the first element of the array -- * -- * The function name is used to indicate that we're not actually -- * looking at an 'array', which is a contiguous region of memory -- * containing a sequence of elements of some type; instead, we have a -- * pointer to that region of memory. -- */ --static int --display_arrayptr(enum tof type, Process *proc, -- void *addr, arg_type_info * info, -- void *st, arg_type_info* st_info) { -- int len = 0; -- size_t i; -- size_t array_len; -- -- if (addr == NULL) -- return fprintf(options.output, "NULL"); -- -- array_len = get_length(type, proc, info->u.array_info.len_spec, -- st, st_info); -- len += fprintf(options.output, "[ "); -- for (i = 0; i < options.arraylen && i < array_maxlength && i < array_len; i++) { -- arg_type_info *elt_type = info->u.array_info.elt_type; -- size_t elt_size = info->u.array_info.elt_size; -- if (i != 0) -- len += fprintf(options.output, ", "); -- if (options.debug) -- len += fprintf(options.output, "%p=", addr); -- len += -- display_ptrto(type, proc, (long) addr, elt_type, st, st_info); -- addr += elt_size; -- } -- if (i < array_len) -- len += fprintf(options.output, "..."); -- len += fprintf(options.output, " ]"); -- return len; --} -- --/* addr - A pointer to the beginning of the memory region occupied by -- * the struct (aka a pointer to the struct) -- */ --static int --display_structptr(enum tof type, Process *proc, -- void *addr, arg_type_info * info) { -- int i; -- arg_type_info *field; -- int len = 0; -- -- if (addr == NULL) -- return fprintf(options.output, "NULL"); -- -- len += fprintf(options.output, "{ "); -- for (i = 0; (field = info->u.struct_info.fields[i]) != NULL; i++) { -- if (i != 0) -- len += fprintf(options.output, ", "); -- if (options.debug) -- len += -- fprintf(options.output, "%p=", -- addr + info->u.struct_info.offset[i]); -- len += -- display_ptrto(LT_TOF_STRUCT, proc, -- (long) addr + info->u.struct_info.offset[i], -- field, addr, info); -- } -- len += fprintf(options.output, " }"); -- -- return len; --} -- --static int --display_pointer(enum tof type, Process *proc, long value, -- arg_type_info * info, -- void *st, arg_type_info* st_info) { -- long pointed_to; -- arg_type_info *inner = info->u.ptr_info.info; -- -- if (inner->type == ARGTYPE_ARRAY) { -- return display_arrayptr(type, proc, (void*) value, inner, -- st, st_info); -- } else if (inner->type == ARGTYPE_STRUCT) { -- return display_structptr(type, proc, (void *) value, inner); -- } else { -- if (value == 0) -- return fprintf(options.output, "NULL"); -- else if (umovelong (proc, (void *) value, &pointed_to, -- info->u.ptr_info.info) < 0) -- return fprintf(options.output, "?"); -- else -- return display_value(type, proc, pointed_to, inner, -- st, st_info); -- } --} -- --static int --display_enum(enum tof type, Process *proc, -- arg_type_info* info, long value) { -- size_t ii; -- for (ii = 0; ii < info->u.enum_info.entries; ++ii) { -- if (info->u.enum_info.values[ii] == value) -- return fprintf(options.output, "%s", info->u.enum_info.keys[ii]); -- } -- -- return display_unknown(type, proc, value); --} -- --/* Args: -- type - syscall or shared library function or memory -- proc - information about the traced process -- value - the value to display -- info - the description of the type to display -- st - if the current value is a struct member, the address of the struct -- st_info - type of the above struct -- -- Those last two parameters are used for structs containing arrays or -- strings whose length is given by another structure element. --*/ --int --display_value(enum tof type, Process *proc, -- long value, arg_type_info *info, -- void *st, arg_type_info* st_info) { -- int tmp; -- -- switch (info->type) { -- case ARGTYPE_VOID: -- return 0; -- case ARGTYPE_INT: -- return fprintf(options.output, "%d", (int) value); -- case ARGTYPE_UINT: -- return fprintf(options.output, "%u", (unsigned) value); -- case ARGTYPE_LONG: -- if (proc->mask_32bit) -- return fprintf(options.output, "%d", (int) value); -- else -- return fprintf(options.output, "%ld", value); -- case ARGTYPE_ULONG: -- if (proc->mask_32bit) -- return fprintf(options.output, "%u", (unsigned) value); -- else -- return fprintf(options.output, "%lu", (unsigned long) value); -- case ARGTYPE_OCTAL: -- return fprintf(options.output, "0%o", (unsigned) value); -- case ARGTYPE_CHAR: -- tmp = fprintf(options.output, "'"); -- tmp += display_char(value == -1 ? value : (char) value); -- tmp += fprintf(options.output, "'"); -- return tmp; -- case ARGTYPE_SHORT: -- return fprintf(options.output, "%hd", (short) value); -- case ARGTYPE_USHORT: -- return fprintf(options.output, "%hu", (unsigned short) value); -- case ARGTYPE_FLOAT: { -- union { long l; float f; double d; } cvt; -- cvt.l = value; -- return fprintf(options.output, "%f", cvt.f); -- } -- case ARGTYPE_DOUBLE: { -- union { long l; float f; double d; } cvt; -- cvt.l = value; -- return fprintf(options.output, "%lf", cvt.d); -- } -- case ARGTYPE_ADDR: -- if (!value) -- return fprintf(options.output, "NULL"); -- else -- return fprintf(options.output, "0x%08lx", value); -- case ARGTYPE_FORMAT: -- fprintf(stderr, "Should never encounter a format anywhere but at the top level (for now?)\n"); -- exit(1); -- case ARGTYPE_STRING: -- return display_string(type, proc, (void*) value, -- string_maxlength); -- case ARGTYPE_STRING_N: -- return display_string(type, proc, (void*) value, -- get_length(type, proc, -- info->u.string_n_info.size_spec, st, st_info)); -- case ARGTYPE_ARRAY: -- return fprintf(options.output, ""); -- case ARGTYPE_ENUM: -- return display_enum(type, proc, info, value); -- case ARGTYPE_STRUCT: -- return fprintf(options.output, ""); -- case ARGTYPE_POINTER: -- return display_pointer(type, proc, value, info, -- st, st_info); -- case ARGTYPE_UNKNOWN: -- default: -- return display_unknown(type, proc, value); -- } --} -- --int --display_arg(enum tof type, Process *proc, int arg_num, arg_type_info * info) { -- long arg; -- -- if (info->type == ARGTYPE_VOID) { -- return 0; -- } else if (info->type == ARGTYPE_FORMAT) { -- return display_format(type, proc, arg_num); -- } else { -- arg = gimme_arg(type, proc, arg_num, info); -- return display_value(type, proc, arg, info, NULL, NULL); -- } --} -- --static int --display_char(int what) { -- switch (what) { -- case -1: -- return fprintf(options.output, "EOF"); -- case '\r': -- return fprintf(options.output, "\\r"); -- case '\n': -- return fprintf(options.output, "\\n"); -- case '\t': -- return fprintf(options.output, "\\t"); -- case '\b': -- return fprintf(options.output, "\\b"); -- case '\\': -- return fprintf(options.output, "\\\\"); -- default: -- if (isprint(what)) { -- return fprintf(options.output, "%c", what); -- } else { -- return fprintf(options.output, "\\%03o", (unsigned char)what); -- } -- } --} -- --#define MIN(a,b) (((a)<(b)) ? (a) : (b)) -- --static int --display_string(enum tof type, Process *proc, void *addr, -- size_t maxlength) { -- unsigned char *str1; -- size_t i; -- int len = 0; -- -- if (!addr) { -- return fprintf(options.output, "NULL"); -- } -- -- str1 = malloc(MIN(options.strlen, maxlength) + 3); -- if (!str1) { -- return fprintf(options.output, "???"); -- } -- umovestr(proc, addr, MIN(options.strlen, maxlength) + 1, str1); -- len = fprintf(options.output, "\""); -- for (i = 0; i < MIN(options.strlen, maxlength); i++) { -- if (str1[i]) { -- len += display_char(str1[i]); -- } else { -- break; -- } -- } -- len += fprintf(options.output, "\""); -- if (str1[i] && (options.strlen <= maxlength)) { -- len += fprintf(options.output, "..."); -- } -- free(str1); -- return len; --} -- --static int --display_unknown(enum tof type, Process *proc, long value) { -- if (proc->mask_32bit) { -- if ((int)value < 1000000 && (int)value > -1000000) -- return fprintf(options.output, "%d", (int)value); -- else -- return fprintf(options.output, "%p", (void *)value); -- } else if (value < 1000000 && value > -1000000) { -- return fprintf(options.output, "%ld", value); -- } else { -- return fprintf(options.output, "%p", (void *)value); -- } --} -- --static int --display_format(enum tof type, Process *proc, int arg_num) { -- void *addr; -- unsigned char *str1; -- int i; -- size_t len = 0; -- arg_type_info info; -- -- info.type = ARGTYPE_POINTER; -- addr = (void *)gimme_arg(type, proc, arg_num, &info); -- if (!addr) { -- return fprintf(options.output, "NULL"); -- } -- -- str1 = malloc(MIN(options.strlen, string_maxlength) + 3); -- if (!str1) { -- return fprintf(options.output, "???"); -- } -- umovestr(proc, addr, MIN(options.strlen, string_maxlength) + 1, str1); -- len = fprintf(options.output, "\""); -- for (i = 0; len < MIN(options.strlen, string_maxlength) + 1; i++) { -- if (str1[i]) { -- len += display_char(str1[i]); -- } else { -- break; -- } -- } -- len += fprintf(options.output, "\""); -- if (str1[i] && (options.strlen <= string_maxlength)) { -- len += fprintf(options.output, "..."); -- } -- for (i = 0; str1[i]; i++) { -- if (str1[i] == '%') { -- int is_long = 0; -- while (1) { -- unsigned char c = str1[++i]; -- if (c == '%') { -- break; -- } else if (!c) { -- break; -- } else if (strchr("lzZtj", c)) { -- is_long++; -- if (c == 'j') -- is_long++; -- if (is_long > 1 -- && (sizeof(long) < sizeof(long long) -- || proc->mask_32bit)) { -- len += fprintf(options.output, ", ..."); -- str1[i + 1] = '\0'; -- break; -- } -- } else if (c == 'd' || c == 'i') { -- info.type = ARGTYPE_LONG; -- if (!is_long || proc->mask_32bit) -- len += -- fprintf(options.output, ", %d", -- (int)gimme_arg(type, proc, ++arg_num, &info)); -- else -- len += -- fprintf(options.output, ", %ld", -- gimme_arg(type, proc, ++arg_num, &info)); -- break; -- } else if (c == 'u') { -- info.type = ARGTYPE_LONG; -- if (!is_long || proc->mask_32bit) -- len += -- fprintf(options.output, ", %u", -- (int)gimme_arg(type, proc, ++arg_num, &info)); -- else -- len += -- fprintf(options.output, ", %lu", -- gimme_arg(type, proc, ++arg_num, &info)); -- break; -- } else if (c == 'o') { -- info.type = ARGTYPE_LONG; -- if (!is_long || proc->mask_32bit) -- len += -- fprintf(options.output, ", 0%o", -- (int)gimme_arg(type, proc, ++arg_num, &info)); -- else -- len += -- fprintf(options.output, ", 0%lo", -- gimme_arg(type, proc, ++arg_num, &info)); -- break; -- } else if (c == 'x' || c == 'X') { -- info.type = ARGTYPE_LONG; -- if (!is_long || proc->mask_32bit) -- len += -- fprintf(options.output, ", %#x", -- (int)gimme_arg(type, proc, ++arg_num, &info)); -- else -- len += -- fprintf(options.output, ", %#lx", -- gimme_arg(type, proc, ++arg_num, &info)); -- break; -- } else if (strchr("eEfFgGaACS", c) -- || (is_long -- && (c == 'c' || c == 's'))) { -- len += fprintf(options.output, ", ..."); -- str1[i + 1] = '\0'; -- break; -- } else if (c == 'c') { -- info.type = ARGTYPE_LONG; -- len += fprintf(options.output, ", '"); -- len += -- display_char((int) -- gimme_arg(type, proc, ++arg_num, &info)); -- len += fprintf(options.output, "'"); -- break; -- } else if (c == 's') { -- info.type = ARGTYPE_POINTER; -- len += fprintf(options.output, ", "); -- len += -- display_string(type, proc, -- (void *)gimme_arg(type, proc, ++arg_num, &info), -- string_maxlength); -- break; -- } else if (c == 'p' || c == 'n') { -- info.type = ARGTYPE_POINTER; -- len += -- fprintf(options.output, ", %p", -- (void *)gimme_arg(type, proc, ++arg_num, &info)); -- break; -- } else if (c == '*') { -- info.type = ARGTYPE_LONG; -- len += -- fprintf(options.output, ", %d", -- (int)gimme_arg(type, proc, ++arg_num, &info)); -- } -- } -- } -- } -- free(str1); -- return len; --} -diff --git a/etc/ltrace.conf b/etc/ltrace.conf -index 3caf6b7..6513752 100644 ---- a/etc/ltrace.conf -+++ b/etc/ltrace.conf -@@ -101,7 +101,7 @@ string inet_ntoa(addr); ; It isn't an ADDR but an hexa number... - addr inet_addr(string); - - ; bfd.h --void bfd_init(void); -+void bfd_init(); - int bfd_set_default_target(string); - addr bfd_scan_vma(string, addr, int); - addr bfd_openr(string,string); -@@ -110,9 +110,9 @@ int bfd_check_format(addr,int); - ; ctype.h - char tolower(char); - char toupper(char); --addr __ctype_b_loc(void); --addr __ctype_tolower_loc(void); --addr __ctype_toupper_loc(void); -+addr __ctype_b_loc(); -+addr __ctype_tolower_loc(); -+addr __ctype_toupper_loc(); - - ; curses.h - int waddch(addr, char); -@@ -129,12 +129,12 @@ addr readdir64(addr); - - ; dlfcn.h - addr dlopen(string, int); --string dlerror(void); -+string dlerror(); - addr dlsym(addr, string); - int dlclose(addr); - - ; errno.h --addr __errno_location(void); -+addr __errno_location(); - - ; fcntl.h - int open(string,int,octal); ; WARNING: 3rd argument may not be there -@@ -148,10 +148,10 @@ int getopt_long(int,addr,string,addr,int*); - int getopt_long_only(int,addr,string,addr,addr); - - ; grp.h --void endgrent(void); -+void endgrent(); - addr getgrnam(string); --void setgrent(void); --addr getgrent(void); -+void setgrent(); -+addr getgrent(); - - ; libintl.h - string __dcgettext(string,string,int); -@@ -166,8 +166,8 @@ int _IO_putc(char,file); - string setlocale(int, string); - - ; mcheck.h --void mtrace(void); --void muntrace(void); -+void mtrace(); -+void muntrace(); - - ; mntent.h - int endmntent(file); -@@ -187,28 +187,28 @@ long mq_receive(int, +string0, ulong, addr); - long mq_timedreceive(int, +string0, ulong, addr, addr); - - ; netdb.h --void endhostent(void); --void endnetent(void); --void endnetgrent(void); --void endprotoent(void); --void endservent(void); -+void endhostent(); -+void endnetent(); -+void endnetgrent(); -+void endprotoent(); -+void endservent(); - void freeaddrinfo(addr); - string gai_strerror(int); - int getaddrinfo(string, string, addr, addr); - addr gethostbyaddr(string, uint, int); - addr gethostbyname(string); --addr gethostent(void); -+addr gethostent(); - int getnameinfo(addr, uint, string, uint, string, uint, uint); - addr getnetbyaddr(uint, int); - addr getnetbyname(string); --addr getnetent(void); -+addr getnetent(); - int getnetgrent(addr, addr, addr); - addr getprotobyname(string); - addr getprotobynumber(int); --addr getprotoent(void); -+addr getprotoent(); - addr getservbyname(string, string); - addr getservbyport(int, string); --addr getservent(void); -+addr getservent(); - void herror(string); - string hstrerror(int); - int rcmd(addr, ushort, string, string, string, addr); -@@ -237,9 +237,9 @@ int pcap_compile(addr, addr, string, int, addr); - - ; pwd.h - string getpass(string); --void endpwent(void); -+void endpwent(); - addr getpwnam(string); --void setpwent(void); -+void setpwent(); - - ; readline/readline.h - string readline(string); -@@ -305,7 +305,7 @@ int setenv(string,string,int); - void unsetenv(string); - addr malloc(ulong); - void qsort(addr,ulong,ulong,addr); --int random(void); -+int random(); - addr realloc(addr,ulong); - void srandom(uint); - int system(string); -@@ -364,7 +364,7 @@ int uname(addr); - int statfs(string,addr); - - ; syslog.h --void closelog(void); -+void closelog(); - void openlog(string,int,int); - void syslog(int,format); - -@@ -395,21 +395,21 @@ int dup2(int,int); - int execlp(string,string,addr,addr,addr); - int execv(string,addr); - int fchdir(int); --int fork(void); -+int fork(); - int ftruncate(int,ulong); - string2 getcwd(addr,ulong); - int getdomainname(+string2,ulong); --int geteuid(void); --int getegid(void); --int getgid(void); -+int geteuid(); -+int getegid(); -+int getgid(); - int gethostname(+string2,ulong); --string getlogin(void); -+string getlogin(); - int getopt(int,addr,string); --int getpid(void); --int getppid(void); --int getuid(void); --int getpgrp(void); --int setpgrp(void); -+int getpid(); -+int getppid(); -+int getuid(); -+int getpgrp(); -+int setpgrp(); - int getpgid(int); - int isatty(int); - int link(string,string); -@@ -424,21 +424,21 @@ int setreuid(uint, uint); - int setuid(int); - uint sleep(uint); - int symlink(string,string); --int sync(void); -+int sync(); - int truncate(string,ulong); - string ttyname(int); - int unlink(string); - void usleep(uint); - long write(int, string3, ulong); - addr sbrk(long); --int getpagesize(void); -+int getpagesize(); - long lseek(int,long,int); - int pipe(addr); - - ; utmp.h --void endutent(void); --addr getutent(void); --void setutent(void); -+void endutent(); -+addr getutent(); -+void setutent(); - - ; wchar.h - int fwide(addr, int); -@@ -480,7 +480,7 @@ int acl_set_qualifier(addr,addr); - int acl_set_tag_type(addr,int); - int acl_size(addr); - string acl_to_text(addr,addr); --itn acl_valid(addr); -+int acl_valid(addr); - - ; acl/libacl.h - int acl_check(addr,addr); -@@ -500,9 +500,9 @@ int SYS_close(int); - int SYS_execve(string,addr,addr); - void SYS_exit(int); - void SYS_exit_group(int); --int SYS_fork(void); -+int SYS_fork(); - int SYS_getcwd(+string2,ulong); --int SYS_getpid(void); -+int SYS_getpid(); - ;addr SYS_mmap(addr,ulong,int,int,int,long); - int SYS_munmap(addr,ulong); - int SYS_open(string,int,octal); -@@ -512,7 +512,7 @@ int SYS_stat(string,addr); - octal SYS_umask(octal); - int SYS_uname(addr); - long SYS_write(int,string3,ulong); --int SYS_sync(void); -+int SYS_sync(); - int SYS_setxattr(string,string,addr,uint,int); - int SYS_lsetxattr(string,string,addr,uint,int); - int SYS_fsetxattr(int,string,addr,uint,int); -@@ -545,16 +545,16 @@ int SYS_gettimeofday(addr,addr); - int SYS_settimeofday(addr,addr); - int SYS_setfsgid(int); - int SYS_setfsuid(int); --int SYS_getuid(void); -+int SYS_getuid(); - int SYS_setuid(int); --int SYS_getgid(void); -+int SYS_getgid(); - int SYS_setgid(int); - int SYS_getsid(int); - int SYS_setsid(int); - int SYS_setreuid(int,int); - int SYS_setregid(int,int); --int SYS_geteuid(void); --int SYS_getegid(void); -+int SYS_geteuid(); -+int SYS_getegid(); - int SYS_setpgid(int,int); - int SYS_getresuid(addr,addr,addr); - int SYS_setresuid(int,int,int); -@@ -592,7 +592,7 @@ int SYS_utime(string,addr); - long SYS_lseek(int,long,int); - addr SYS_signal(int,addr); - int SYS_sigaction(int,addr,addr); --int SYS_pause(void); -+int SYS_pause(); - int SYS_sigpending(addr); - int SYS_sigprocmask(int,addr,addr); - int SYS_sigqueue(int,int,addr); -diff --git a/execute_program.c b/execute_program.c -index 55df205..f469b2f 100644 ---- a/execute_program.c -+++ b/execute_program.c -@@ -14,7 +14,9 @@ - #include - #include - --#include "common.h" -+#include "backend.h" -+#include "options.h" -+#include "debug.h" - - static void - change_uid(const char * command) -diff --git a/expr.c b/expr.c -new file mode 100644 -index 0000000..c0ebcef ---- /dev/null -+++ b/expr.c -@@ -0,0 +1,337 @@ -+/* -+ * 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 -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "expr.h" -+ -+static void -+expr_init_common(struct expr_node *node, enum expr_node_kind kind) -+{ -+ node->kind = kind; -+ node->lhs = NULL; -+ node->own_lhs = 0; -+ memset(&node->u, 0, sizeof(node->u)); -+} -+ -+void -+expr_init_self(struct expr_node *node) -+{ -+ expr_init_common(node, EXPR_OP_SELF); -+} -+ -+void -+expr_init_named(struct expr_node *node, -+ const char *name, int own_name) -+{ -+ expr_init_common(node, EXPR_OP_NAMED); -+ node->u.name.s = name; -+ node->u.name.own = own_name; -+} -+ -+void -+expr_init_argno(struct expr_node *node, size_t num) -+{ -+ expr_init_common(node, EXPR_OP_ARGNO); -+ node->u.num = num; -+} -+ -+void -+expr_init_const(struct expr_node *node, struct value *val) -+{ -+ expr_init_common(node, EXPR_OP_CONST); -+ node->u.value = *val; -+} -+ -+void -+expr_init_const_word(struct expr_node *node, long l, -+ struct arg_type_info *type, int own_type) -+{ -+ struct value val; -+ value_init_detached(&val, NULL, type, own_type); -+ value_set_word(&val, l); -+ expr_init_const(node, &val); -+} -+ -+void -+expr_init_index(struct expr_node *node, -+ struct expr_node *lhs, int own_lhs, -+ struct expr_node *rhs, int own_rhs) -+{ -+ expr_init_common(node, EXPR_OP_INDEX); -+ node->lhs = lhs; -+ node->own_lhs = own_lhs; -+ node->u.node.n = rhs; -+ node->u.node.own = own_rhs; -+} -+ -+void -+expr_init_up(struct expr_node *node, struct expr_node *lhs, int own_lhs) -+{ -+ assert(lhs != NULL); -+ expr_init_common(node, EXPR_OP_UP); -+ node->lhs = lhs; -+ node->own_lhs = own_lhs; -+} -+ -+void -+expr_init_cb1(struct expr_node *node, -+ int (*cb)(struct value *ret_value, struct value *value, -+ struct value_dict *arguments, void *data), -+ struct expr_node *lhs, int own_lhs, void *data) -+{ -+ expr_init_common(node, EXPR_OP_CALL1); -+ node->lhs = lhs; -+ node->own_lhs = own_lhs; -+ node->u.call.u.cb1 = cb; -+ node->u.call.data = data; -+} -+ -+void -+expr_init_cb2(struct expr_node *node, -+ int (*cb)(struct value *ret_value, -+ struct value *lhs, struct value *rhs, -+ struct value_dict *arguments, void *data), -+ struct expr_node *lhs, int own_lhs, -+ struct expr_node *rhs, int own_rhs, void *data) -+{ -+ expr_init_common(node, EXPR_OP_CALL2); -+ node->lhs = lhs; -+ node->own_lhs = own_lhs; -+ node->u.call.rhs = rhs; -+ node->u.call.own_rhs = own_rhs; -+ node->u.call.u.cb2 = cb; -+ node->u.call.data = data; -+} -+ -+static void -+release_lhs(struct expr_node *node) -+{ -+ if (node->own_lhs) -+ expr_destroy(node->lhs); -+} -+ -+void -+expr_destroy(struct expr_node *node) -+{ -+ if (node == NULL) -+ return; -+ -+ switch (node->kind) { -+ case EXPR_OP_ARGNO: -+ case EXPR_OP_SELF: -+ return; -+ -+ case EXPR_OP_CONST: -+ value_destroy(&node->u.value); -+ return; -+ -+ case EXPR_OP_NAMED: -+ if (node->u.name.own) -+ free((char *)node->u.name.s); -+ return; -+ -+ case EXPR_OP_INDEX: -+ release_lhs(node); -+ if (node->u.node.own) -+ expr_destroy(node->u.node.n); -+ return; -+ -+ case EXPR_OP_CALL2: -+ if (node->u.call.own_rhs) -+ expr_destroy(node->u.call.rhs); -+ case EXPR_OP_UP: -+ case EXPR_OP_CALL1: -+ release_lhs(node); -+ return; -+ } -+ -+ assert(!"Invalid value of node kind"); -+ abort(); -+} -+ -+int -+expr_is_compile_constant(struct expr_node *node) -+{ -+ return node->kind == EXPR_OP_CONST; -+} -+ -+static int -+eval_up(struct expr_node *node, struct value *context, -+ struct value_dict *arguments, struct value *ret_value) -+{ -+ if (expr_eval(node->lhs, context, arguments, ret_value) < 0) -+ return -1; -+ struct value *parent = value_get_parental_struct(ret_value); -+ if (parent == NULL) { -+ value_destroy(ret_value); -+ return -1; -+ } -+ *ret_value = *parent; -+ return 0; -+} -+ -+static int -+eval_cb1(struct expr_node *node, struct value *context, -+ struct value_dict *arguments, struct value *ret_value) -+{ -+ struct value val; -+ if (expr_eval(node->lhs, context, arguments, &val) < 0) -+ return -1; -+ -+ int ret = 0; -+ if (node->u.call.u.cb1(ret_value, &val, arguments, -+ node->u.call.data) < 0) -+ ret = -1; -+ -+ /* N.B. the callback must return its own value, or somehow -+ * clone the incoming argument. */ -+ value_destroy(&val); -+ return ret; -+} -+ -+static int -+eval_cb2(struct expr_node *node, struct value *context, -+ struct value_dict *arguments, struct value *ret_value) -+{ -+ struct value lhs; -+ if (expr_eval(node->lhs, context, arguments, &lhs) < 0) -+ return -1; -+ -+ struct value rhs; -+ if (expr_eval(node->u.call.rhs, context, arguments, &rhs) < 0) { -+ value_destroy(&lhs); -+ return -1; -+ } -+ -+ int ret = 0; -+ if (node->u.call.u.cb2(ret_value, &lhs, &rhs, arguments, -+ node->u.call.data) < 0) -+ ret = -1; -+ -+ /* N.B. the callback must return its own value, or somehow -+ * clone the incoming argument. */ -+ value_destroy(&lhs); -+ value_destroy(&rhs); -+ return ret; -+} -+ -+int -+eval_index(struct expr_node *node, struct value *context, -+ struct value_dict *arguments, struct value *ret_value) -+{ -+ struct value lhs; -+ if (expr_eval(node->lhs, context, arguments, &lhs) < 0) -+ return -1; -+ -+ long l; -+ if (expr_eval_word(node->u.node.n, context, arguments, &l) < 0) { -+ fail: -+ value_destroy(&lhs); -+ return -1; -+ } -+ -+ if (value_init_element(ret_value, &lhs, (size_t)l) < 0) -+ goto fail; -+ return 0; -+} -+ -+int -+expr_eval(struct expr_node *node, struct value *context, -+ struct value_dict *arguments, struct value *ret_value) -+{ -+ switch (node->kind) { -+ struct value *valp; -+ case EXPR_OP_ARGNO: -+ valp = val_dict_get_num(arguments, node->u.num); -+ if (valp == NULL) -+ return -1; -+ *ret_value = *valp; -+ return 0; -+ -+ case EXPR_OP_NAMED: -+ valp = val_dict_get_name(arguments, node->u.name.s); -+ if (valp == NULL) -+ return -1; -+ *ret_value = *valp; -+ return 0; -+ -+ case EXPR_OP_SELF: -+ *ret_value = *context; -+ return 0; -+ -+ case EXPR_OP_CONST: -+ *ret_value = node->u.value; -+ return 0; -+ -+ case EXPR_OP_INDEX: -+ return eval_index(node, context, arguments, ret_value); -+ -+ case EXPR_OP_UP: -+ return eval_up(node, context, arguments, ret_value); -+ -+ case EXPR_OP_CALL1: -+ return eval_cb1(node, context, arguments, ret_value); -+ -+ case EXPR_OP_CALL2: -+ return eval_cb2(node, context, arguments, ret_value); -+ } -+ -+ assert(!"Unknown node kind."); -+ abort(); -+} -+ -+int -+expr_eval_word(struct expr_node *node, struct value *context, -+ struct value_dict *arguments, long *ret_value) -+{ -+ struct value val; -+ if (expr_eval(node, context, arguments, &val) < 0) -+ return -1; -+ int ret = 0; -+ if (value_extract_word(&val, ret_value, arguments) < 0) -+ ret = -1; -+ value_destroy(&val); -+ return ret; -+} -+ -+int -+expr_eval_constant(struct expr_node *node, long *valuep) -+{ -+ assert(expr_is_compile_constant(node)); -+ return expr_eval_word(node, NULL, NULL, valuep); -+} -+ -+struct expr_node * -+expr_self(void) -+{ -+ static struct expr_node *node = NULL; -+ if (node == NULL) { -+ node = malloc(sizeof(*node)); -+ if (node == NULL) -+ error(1, errno, "malloc expr_self"); -+ expr_init_self(node); -+ } -+ return node; -+} -diff --git a/expr.h b/expr.h -new file mode 100644 -index 0000000..53b75b7 ---- /dev/null -+++ b/expr.h -@@ -0,0 +1,156 @@ -+/* -+ * 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 EXPR_H -+#define EXPR_H -+ -+#include "value.h" -+#include "value_dict.h" -+ -+/* Expressions serve as a way of encoding array lengths. */ -+ -+enum expr_node_kind { -+ EXPR_OP_SELF, /* reference to the variable in question */ -+ EXPR_OP_NAMED, /* value of named argument */ -+ EXPR_OP_ARGNO, /* value of numbered argument */ -+ EXPR_OP_CONST, /* constant value */ -+ EXPR_OP_INDEX, /* A[B] */ -+ EXPR_OP_UP, /* reference to containing structure */ -+ EXPR_OP_CALL1, /* internal callback with one operand */ -+ EXPR_OP_CALL2, /* internal callback with two operands */ -+}; -+ -+struct expr_node { -+ enum expr_node_kind kind; -+ -+ struct expr_node *lhs; -+ int own_lhs; -+ -+ union { -+ struct { -+ const char *s; -+ int own; -+ } name; -+ struct { -+ struct expr_node *n; -+ int own; -+ } node; -+ struct value value; -+ size_t num; -+ struct { -+ union { -+ int (*cb1)(struct value *ret_value, -+ struct value *lhs, -+ struct value_dict *arguments, -+ void *data); -+ int (*cb2)(struct value *ret_value, -+ struct value *lhs, -+ struct value *rhs, -+ struct value_dict *arguments, -+ void *data); -+ } u; -+ void *data; -+ struct expr_node *rhs; -+ int own_rhs; -+ } call; -+ } u; -+}; -+ -+/* Expression of type self just returns the value in consideration. -+ * For example, if what we seek is length of an array, then the value -+ * representing that array is returned by the expression. */ -+void expr_init_self(struct expr_node *node); -+ -+/* Expression that yields the value of an argument named NAME. NAME -+ * is owned if OWN_NAME. */ -+void expr_init_named(struct expr_node *node, -+ const char *name, int own_name); -+ -+/* Expression that yields the value of an argument number NUM. */ -+void expr_init_argno(struct expr_node *node, size_t num); -+ -+/* Constant expression always returns the same value VAL. VAL is -+ * copied into NODE and owned by it. */ -+void expr_init_const(struct expr_node *node, struct value *val); -+void expr_init_const_word(struct expr_node *node, long l, -+ struct arg_type_info *type, int own_type); -+ -+/* Expression LHS[RHS]. LHS and RHS are owned if, respectively, -+ * OWN_LHS and OWN_RHS. */ -+void expr_init_index(struct expr_node *node, -+ struct expr_node *lhs, int own_lhs, -+ struct expr_node *rhs, int own_rhs); -+ -+/* This expression returns the containing value of LHS (^LHS). LHS is -+ * owned if OWN_LHS. */ -+void expr_init_up(struct expr_node *node, struct expr_node *lhs, int own_lhs); -+ -+/* Callback expression calls CB(eval(LHS), DATA). LHS is owned if -+ * OWN_LHS. DATA is passed to callback verbatim. */ -+void expr_init_cb1(struct expr_node *node, -+ int (*cb)(struct value *ret_value, -+ struct value *value, -+ struct value_dict *arguments, -+ void *data), -+ struct expr_node *lhs, int own_lhs, void *data); -+ -+/* Callback expression calls CB(eval(LHS), eval(RHS), DATA). LHS and -+ * RHS are owned if, respectively, OWN_LHS and OWN_RHS. DATA is -+ * passed to callback verbatim. */ -+void expr_init_cb2(struct expr_node *node, -+ int (*cb)(struct value *ret_value, -+ struct value *lhs, struct value *rhs, -+ struct value_dict *arguments, -+ void *data), -+ struct expr_node *lhs, int own_lhs, -+ struct expr_node *rhs, int own_rhs, void *data); -+ -+/* Release the data inside NODE. Doesn't free NODE itself. */ -+void expr_destroy(struct expr_node *node); -+ -+/* Evaluate the expression NODE in context of VALUE. ARGUMENTS is a -+ * dictionary of named and numbered values that NODE may use. Returns -+ * 0 in case of success or a negative value on error. CONTEXT and -+ * ARGUMENTS may be NULL, but then the expression mustn't need them -+ * for evaluation. */ -+int expr_eval(struct expr_node *node, struct value *context, -+ struct value_dict *arguments, struct value *ret_value); -+ -+/* Evaluate compile-time expression. Returns 0 on success or negative -+ * value on failure. Computed value is passed back in *VALUEP. */ -+int expr_eval_constant(struct expr_node *node, long *valuep); -+ -+/* Evaluate expression, whose result should fit into a word. In order -+ * to easily support all the structure and array accesses, we simply -+ * operate on values represented by struct value. But eventually we need -+ * to be able to get out a word-size datum to use it as an index, a -+ * length, etc. */ -+int expr_eval_word(struct expr_node *node, struct value *context, -+ struct value_dict *arguments, long *ret_value); -+ -+/* Returns non-zero value if the expression is a compile-time -+ * constant. Currently this is only EXPR_OP_CONST, but eventually -+ * things like sizeof or simple expressions might be allowed. */ -+int expr_is_compile_constant(struct expr_node *node); -+ -+/* Returns a pre-computed expression "self". */ -+struct expr_node *expr_self(void); -+ -+#endif /* EXPR_H */ -diff --git a/fetch.c b/fetch.c -new file mode 100644 -index 0000000..bce949f ---- /dev/null -+++ b/fetch.c -@@ -0,0 +1,132 @@ -+/* -+ * 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 -+ */ -+ -+#include -+#include -+ -+#include "fetch.h" -+#include "value.h" -+#include "arch.h" -+#include "type.h" -+ -+#ifdef ARCH_HAVE_FETCH_ARG -+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); -+ -+#else -+/* Fall back to gimme_arg. */ -+ -+long gimme_arg(enum tof type, struct Process *proc, int arg_num, -+ struct arg_type_info *info); -+ -+struct fetch_context { -+ int argnum; -+}; -+ -+struct fetch_context * -+arch_fetch_arg_init(enum tof type, struct Process *proc, -+ struct arg_type_info *ret_info) -+{ -+ return calloc(sizeof(struct fetch_context), 1); -+} -+ -+struct fetch_context * -+arch_fetch_arg_clone(struct Process *proc, struct fetch_context *context) -+{ -+ struct fetch_context *ret = malloc(sizeof(*ret)); -+ if (ret == NULL) -+ return NULL; -+ return memcpy(ret, context, sizeof(*ret)); -+} -+ -+int -+arch_fetch_arg_next(struct fetch_context *context, enum tof type, -+ struct Process *proc, -+ struct arg_type_info *info, struct value *valuep) -+{ -+ long l = gimme_arg(type, proc, context->argnum++, info); -+ value_set_word(valuep, l); -+ return 0; -+} -+ -+int -+arch_fetch_retval(struct fetch_context *context, enum tof type, -+ struct Process *proc, -+ struct arg_type_info *info, struct value *valuep) -+{ -+ long l = gimme_arg(type, proc, -1, info); -+ value_set_word(valuep, l); -+ return 0; -+} -+ -+void -+arch_fetch_arg_done(struct fetch_context *context) -+{ -+ free(context); -+} -+#endif -+ -+struct fetch_context * -+fetch_arg_init(enum tof type, struct Process *proc, -+ struct arg_type_info *ret_info) -+{ -+ return arch_fetch_arg_init(type, proc, ret_info); -+} -+ -+struct fetch_context * -+fetch_arg_clone(struct Process *proc, struct fetch_context *context) -+{ -+ return arch_fetch_arg_clone(proc, context); -+} -+ -+int -+fetch_arg_next(struct fetch_context *context, enum tof type, -+ struct Process *proc, -+ struct arg_type_info *info, struct value *valuep) -+{ -+ return arch_fetch_arg_next(context, type, proc, info, valuep); -+} -+ -+int -+fetch_retval(struct fetch_context *context, enum tof type, -+ struct Process *proc, -+ struct arg_type_info *info, struct value *valuep) -+{ -+ return arch_fetch_retval(context, type, proc, info, valuep); -+} -+ -+void -+fetch_arg_done(struct fetch_context *context) -+{ -+ return arch_fetch_arg_done(context); -+} -diff --git a/fetch.h b/fetch.h -new file mode 100644 -index 0000000..6a5385c ---- /dev/null -+++ b/fetch.h -@@ -0,0 +1,64 @@ -+/* -+ * 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 FETCH_H -+#define FETCH_H -+ -+#include "forward.h" -+ -+/* XXX isn't SYSCALL TOF just a different ABI? Maybe we needed to -+ * support variant ABIs all along. */ -+enum tof { -+ LT_TOF_FUNCTION, /* A real library function */ -+ LT_TOF_FUNCTIONR, /* Return from a real library function */ -+ LT_TOF_SYSCALL, /* A syscall */ -+ LT_TOF_SYSCALLR, /* Return from a syscall */ -+}; -+ -+/* The contents of the structure is defined by the back end. */ -+struct fetch_context; -+ -+/* Initialize argument fetching. Returns NULL on failure. RET_INFO -+ * is the return type of the function. */ -+struct fetch_context *fetch_arg_init(enum tof type, struct Process *proc, -+ struct arg_type_info *ret_info); -+ -+/* Make a clone of context. */ -+struct fetch_context *fetch_arg_clone(struct Process *proc, -+ struct fetch_context *context); -+ -+/* Load next argument. The function returns 0 on success or a -+ * negative value on failure. The extracted value is stored in -+ * *VALUEP. */ -+int fetch_arg_next(struct fetch_context *context, enum tof type, -+ struct Process *proc, -+ struct arg_type_info *info, struct value *valuep); -+ -+/* Load return value. The function returns 0 on success or a negative -+ * value on failure. The extracted value is stored in *VALUEP. */ -+int fetch_retval(struct fetch_context *context, enum tof type, -+ struct Process *proc, -+ struct arg_type_info *info, struct value *valuep); -+ -+/* Destroy fetch context. CONTEXT shall be the same memory location -+ * that was passed to fetch_arg_next. */ -+void fetch_arg_done(struct fetch_context *context); -+ -+#endif /* FETCH_H */ -diff --git a/forward.h b/forward.h -new file mode 100644 -index 0000000..e4233e5 ---- /dev/null -+++ b/forward.h -@@ -0,0 +1,14 @@ -+/* Important types defined in other header files are declared -+ here. */ -+struct Event; -+struct Process; -+struct arg_type_info; -+struct breakpoint; -+struct expr_node; -+struct library; -+struct library_symbol; -+struct ltelf; -+struct param; -+struct param_enum; -+struct value; -+struct value_dict; -diff --git a/handle_event.c b/handle_event.c -index 73c118a..5b6cc40 100644 ---- a/handle_event.c -+++ b/handle_event.c -@@ -1,6 +1,31 @@ --#define _GNU_SOURCE -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2010 Arnaud Patard, Mandriva SA -+ * Copyright (C) 1998,2001,2002,2003,2004,2007,2008,2009 Juan Cespedes -+ * Copyright (C) 2008 Luis Machado, IBM Corporation -+ * Copyright (C) 2006 Ian Wienand -+ * Copyright (C) 2006 Paul Gilliam, IBM Corporation -+ * -+ * 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 "config.h" - -+#define _GNU_SOURCE - #include - #include - #include -@@ -9,10 +34,13 @@ - #include - #include - -+#include "backend.h" - #include "breakpoint.h" - #include "common.h" -+#include "fetch.h" - #include "library.h" - #include "proc.h" -+#include "value_dict.h" - - static void handle_signal(Event *event); - static void handle_exit(Event *event); -@@ -228,9 +256,11 @@ handle_clone(Event *event) - if (proc == NULL) { - fail: - free(proc); -- /* XXX proper error handling here, please. */ -- perror("malloc()"); -- exit(1); -+ fprintf(stderr, -+ "Error during init of tracing process %d\n" -+ "This process won't be traced.\n", -+ event->proc->pid); -+ return; - } - - if (process_clone(proc, event->proc, event->e_un.newpid) < 0) -@@ -386,13 +416,13 @@ handle_exit_signal(Event *event) { - } - - static void --output_syscall(struct Process *proc, const char *name, -+output_syscall(struct Process *proc, const char *name, enum tof tof, - void (*output)(enum tof, struct Process *, - struct library_symbol *)) - { - struct library_symbol syscall; - if (library_symbol_init(&syscall, 0, name, 0, LS_TOPLT_NONE) >= 0) { -- (*output)(LT_TOF_SYSCALL, proc, &syscall); -+ (*output)(tof, proc, &syscall); - library_symbol_destroy(&syscall); - } - } -@@ -400,13 +430,13 @@ output_syscall(struct Process *proc, const char *name, - static void - output_syscall_left(struct Process *proc, const char *name) - { -- output_syscall(proc, name, &output_left); -+ output_syscall(proc, name, LT_TOF_SYSCALL, &output_left); - } - - static void - output_syscall_right(struct Process *proc, const char *name) - { -- output_syscall(proc, name, &output_right); -+ output_syscall(proc, name, LT_TOF_SYSCALLR, &output_right); - } - - static void -@@ -670,6 +700,7 @@ callstack_push_syscall(Process *proc, int sysnum) { - } - - elem = &proc->callstack[proc->callstack_depth]; -+ *elem = (struct callstack_element){}; - elem->is_syscall = 1; - elem->c_un.syscall = sysnum; - elem->return_addr = NULL; -@@ -694,6 +725,7 @@ callstack_push_symfunc(Process *proc, struct library_symbol *sym) { - } - - elem = &proc->callstack[proc->callstack_depth++]; -+ *elem = (struct callstack_element){}; - elem->is_syscall = 0; - elem->c_un.libfunc = sym; - -@@ -701,7 +733,6 @@ callstack_push_symfunc(Process *proc, struct library_symbol *sym) { - if (elem->return_addr) - insert_breakpoint(proc, elem->return_addr, NULL); - -- /* handle functions like atexit() on mips which have no return */ - if (opt_T || options.summary) { - struct timezone tz; - gettimeofday(&elem->time_spent, &tz); -@@ -719,9 +750,5 @@ callstack_pop(Process *proc) { - assert(proc->leader != NULL); - delete_breakpoint(proc, elem->return_addr); - } -- if (elem->arch_ptr != NULL) { -- free(elem->arch_ptr); -- elem->arch_ptr = NULL; -- } - proc->callstack_depth--; - } -diff --git a/lens.c b/lens.c -new file mode 100644 -index 0000000..07cdcca ---- /dev/null -+++ b/lens.c -@@ -0,0 +1,62 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011 Petr Machata, Red Hat Inc. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation; either version 2 of the -+ * License, or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -+ * 02110-1301 USA -+ */ -+ -+#include -+ -+#include "lens.h" -+#include "lens_default.h" -+#include "type.h" -+#include "value.h" -+ -+int -+format_argument(FILE *stream, struct value *value, -+ struct value_dict *arguments) -+{ -+ /* Find the closest enclosing parental value whose type has a -+ * lens assigned. */ -+ struct value *parent; -+ for (parent = value; (parent != NULL && parent->type != NULL -+ && parent->type->lens == NULL); -+ parent = parent->parent) -+ ; -+ -+ struct lens *lens = &default_lens; -+ if (parent != NULL && parent->type != NULL -+ && parent->type->lens != NULL) -+ lens = parent->type->lens; -+ -+ return lens_format(lens, stream, value, arguments); -+} -+ -+int -+lens_format(struct lens *lens, FILE *stream, struct value *value, -+ struct value_dict *arguments) -+{ -+ assert(lens->format_cb != NULL); -+ return lens->format_cb(lens, stream, value, arguments); -+} -+ -+void -+lens_destroy(struct lens *lens) -+{ -+ if (lens != NULL -+ && lens->destroy_cb != NULL) -+ lens->destroy_cb(lens); -+} -diff --git a/lens.h b/lens.h -new file mode 100644 -index 0000000..f7de3b5 ---- /dev/null -+++ b/lens.h -@@ -0,0 +1,51 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011 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 LENS_H -+#define LENS_H -+ -+#include -+#include "forward.h" -+ -+struct lens { -+ /* Format VALUE into STREAM. The dictionary of all arguments -+ * is given for purposes of evaluating array lengths and other -+ * dynamic expressions. Returns number of characters -+ * outputted, -1 in case of failure. */ -+ int (*format_cb)(struct lens *lens, FILE *stream, struct value *value, -+ struct value_dict *arguments); -+ -+ /* Erase any memory allocated for LENS. Keep the LENS pointer -+ * itself intact. */ -+ void (*destroy_cb)(struct lens *lens); -+}; -+ -+/* Extracts a lens from VALUE and calls lens_format on that. */ -+int format_argument(FILE *stream, struct value *value, -+ struct value_dict *arguments); -+ -+/* Calls format callback associated with LENS. */ -+int lens_format(struct lens *lens, FILE *stream, struct value *value, -+ struct value_dict *arguments); -+ -+/* Calls destroy callback associated with LENS. */ -+void lens_destroy(struct lens *lens); -+ -+#endif /* LENS_H */ -diff --git a/lens_default.c b/lens_default.c -new file mode 100644 -index 0000000..f59d328 ---- /dev/null -+++ b/lens_default.c -@@ -0,0 +1,499 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 1998,2004,2007,2008,2009 Juan Cespedes -+ * Copyright (C) 2006 Ian Wienand -+ * Copyright (C) 2006 Steve Fink -+ * -+ * 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 "proc.h" -+#include "lens_default.h" -+#include "value.h" -+#include "expr.h" -+#include "type.h" -+#include "common.h" -+#include "zero.h" -+ -+#define READER(NAME, TYPE) \ -+ static int \ -+ NAME(struct value *value, TYPE *ret, struct value_dict *arguments) \ -+ { \ -+ union { \ -+ TYPE val; \ -+ unsigned char buf[0]; \ -+ } u; \ -+ if (value_extract_buf(value, u.buf, arguments) < 0) \ -+ return -1; \ -+ *ret = u.val; \ -+ return 0; \ -+ } -+ -+READER(read_float, float) -+READER(read_double, double) -+ -+#undef READER -+ -+#define HANDLE_WIDTH(BITS) \ -+ do { \ -+ long l; \ -+ if (value_extract_word(value, &l, arguments) < 0) \ -+ return -1; \ -+ int##BITS##_t i = l; \ -+ switch (format) { \ -+ case INT_FMT_unknown: \ -+ if (i < -10000 || i > 10000) \ -+ case INT_FMT_x: \ -+ return fprintf(stream, "%#"PRIx##BITS, i); \ -+ case INT_FMT_i: \ -+ return fprintf(stream, "%"PRIi##BITS, i); \ -+ case INT_FMT_u: \ -+ return fprintf(stream, "%"PRIu##BITS, i); \ -+ case INT_FMT_o: \ -+ return fprintf(stream, "0%"PRIo##BITS, i); \ -+ } \ -+ } while (0) -+ -+enum int_fmt_t -+{ -+ INT_FMT_i, -+ INT_FMT_u, -+ INT_FMT_o, -+ INT_FMT_x, -+ INT_FMT_unknown, -+}; -+ -+static int -+format_integer(FILE *stream, struct value *value, enum int_fmt_t format, -+ struct value_dict *arguments) -+{ -+ switch (type_sizeof(value->inferior, value->type)) { -+ -+ case 1: HANDLE_WIDTH(8); -+ case 2: HANDLE_WIDTH(16); -+ case 4: HANDLE_WIDTH(32); -+ case 8: HANDLE_WIDTH(64); -+ -+ default: -+ assert(!"unsupported integer width"); -+ abort(); -+ -+ case -1: -+ return -1; -+ } -+} -+ -+#undef HANDLE_WIDTH -+ -+static int -+account(int *countp, int c) -+{ -+ if (c >= 0) -+ *countp += c; -+ return c; -+} -+ -+static int -+acc_fprintf(int *countp, FILE *stream, const char *format, ...) -+{ -+ va_list pa; -+ va_start(pa, format); -+ int i = account(countp, vfprintf(stream, format, pa)); -+ va_end(pa); -+ -+ return i; -+} -+ -+static int -+format_char(FILE *stream, struct value *value, struct value_dict *arguments) -+{ -+ long lc; -+ if (value_extract_word(value, &lc, arguments) < 0) -+ return -1; -+ int c = (int)lc; -+ -+ const char *fmt; -+ switch (c) { -+ case -1: -+ fmt = "EOF"; -+ break; -+ case 0: -+ fmt = "\\0"; -+ break; -+ case '\a': -+ fmt = "\\a"; -+ break; -+ case '\b': -+ fmt = "\\b"; -+ break; -+ case '\t': -+ fmt = "\\t"; -+ break; -+ case '\n': -+ fmt = "\\n"; -+ break; -+ case '\v': -+ fmt = "\\v"; -+ break; -+ case '\f': -+ fmt = "\\f"; -+ break; -+ case '\r': -+ fmt = "\\r"; -+ break; -+ case '\\': -+ fmt = "\\\\"; -+ break; -+ default: -+ if (isprint(c) || c == ' ') -+ fmt = "%c"; -+ else -+ fmt = "\\%03o"; -+ } -+ -+ return fprintf(stream, fmt, c); -+} -+ -+static int -+format_naked_char(FILE *stream, struct value *value, -+ struct value_dict *arguments) -+{ -+ int written = 0; -+ if (acc_fprintf(&written, stream, "'") < 0 -+ || account(&written, format_char(stream, value, arguments)) < 0 -+ || acc_fprintf(&written, stream, "'") < 0) -+ return -1; -+ -+ return written; -+} -+ -+static int -+format_floating(FILE *stream, struct value *value, struct value_dict *arguments) -+{ -+ switch (value->type->type) { -+ float f; -+ double d; -+ case ARGTYPE_FLOAT: -+ if (read_float(value, &f, arguments) < 0) -+ return -1; -+ return fprintf(stream, "%f", (double)f); -+ case ARGTYPE_DOUBLE: -+ if (read_double(value, &d, arguments) < 0) -+ return -1; -+ return fprintf(stream, "%f", d); -+ default: -+ abort(); -+ } -+} -+ -+static int -+format_struct(FILE *stream, struct value *value, struct value_dict *arguments) -+{ -+ int written = 0; -+ if (acc_fprintf(&written, stream, "{ ") < 0) -+ return -1; -+ size_t i; -+ for (i = 0; i < type_struct_size(value->type); ++i) { -+ if (i > 0 && acc_fprintf(&written, stream, ", ") < 0) -+ return -1; -+ -+ struct value element; -+ if (value_init_element(&element, value, i) < 0) -+ return -1; -+ int o = format_argument(stream, &element, arguments); -+ if (o < 0) -+ return -1; -+ written += o; -+ } -+ if (acc_fprintf(&written, stream, " }") < 0) -+ return -1; -+ return written; -+} -+ -+int -+format_pointer(FILE *stream, struct value *value, struct value_dict *arguments) -+{ -+ struct value element; -+ if (value_init_deref(&element, value) < 0) -+ return -1; -+ return format_argument(stream, &element, arguments); -+} -+ -+/* -+ * LENGTH is an expression whose evaluation will yield the actual -+ * length of the array. -+ * -+ * MAXLEN is the actual maximum length that we care about -+ * -+ * BEFORE if LENGTH>MAXLEN, we display ellipsis. We display it before -+ * the closing parenthesis if BEFORE, otherwise after it. -+ * -+ * OPEN, CLOSE, DELIM are opening and closing parenthesis and element -+ * delimiter. -+ */ -+int -+format_array(FILE *stream, struct value *value, struct value_dict *arguments, -+ struct expr_node *length, size_t maxlen, int before, -+ const char *open, const char *close, const char *delim) -+{ -+ /* We need "long" to be long enough to cover the whole address -+ * space. */ -+ typedef char assert__long_enough_long[-(sizeof(long) < sizeof(void *))]; -+ long l; -+ if (expr_eval_word(length, value, arguments, &l) < 0) -+ return -1; -+ size_t len = (size_t)l; -+ -+ int written = 0; -+ if (acc_fprintf(&written, stream, "%s", open) < 0) -+ return -1; -+ -+ size_t i; -+ for (i = 0; i < len && i <= maxlen; ++i) { -+ if (i == maxlen) { -+ if (before && acc_fprintf(&written, stream, "...") < 0) -+ return -1; -+ break; -+ } -+ -+ if (i > 0 && acc_fprintf(&written, stream, "%s", delim) < 0) -+ return -1; -+ -+ struct value element; -+ if (value_init_element(&element, value, i) < 0) -+ return -1; -+ int o = format_argument(stream, &element, arguments); -+ if (o < 0) -+ return -1; -+ written += o; -+ } -+ if (acc_fprintf(&written, stream, "%s", close) < 0) -+ return -1; -+ if (i == maxlen && !before && acc_fprintf(&written, stream, "...") < 0) -+ return -1; -+ -+ return written; -+} -+ -+static int -+toplevel_format_lens(struct lens *lens, FILE *stream, -+ struct value *value, struct value_dict *arguments, -+ enum int_fmt_t int_fmt) -+{ -+ switch (value->type->type) { -+ case ARGTYPE_VOID: -+ return fprintf(stream, ""); -+ -+ case ARGTYPE_SHORT: -+ case ARGTYPE_INT: -+ case ARGTYPE_LONG: -+ return format_integer(stream, value, int_fmt, arguments); -+ -+ case ARGTYPE_USHORT: -+ case ARGTYPE_UINT: -+ case ARGTYPE_ULONG: -+ if (int_fmt == INT_FMT_i) -+ int_fmt = INT_FMT_u; -+ return format_integer(stream, value, int_fmt, arguments); -+ -+ case ARGTYPE_CHAR: -+ return format_naked_char(stream, value, arguments); -+ -+ case ARGTYPE_FLOAT: -+ case ARGTYPE_DOUBLE: -+ return format_floating(stream, value, arguments); -+ -+ case ARGTYPE_STRUCT: -+ return format_struct(stream, value, arguments); -+ -+ case ARGTYPE_POINTER: -+ if (value->type->u.array_info.elt_type->type != ARGTYPE_VOID) -+ return format_pointer(stream, value, arguments); -+ return format_integer(stream, value, INT_FMT_x, arguments); -+ -+ case ARGTYPE_ARRAY: -+ return format_array(stream, value, arguments, -+ value->type->u.array_info.length, -+ options.arraylen, 1, "[ ", " ]", ", "); -+ } -+ abort(); -+} -+ -+static int -+default_lens_format_cb(struct lens *lens, FILE *stream, -+ struct value *value, struct value_dict *arguments) -+{ -+ return toplevel_format_lens(lens, stream, value, arguments, INT_FMT_i); -+} -+ -+struct lens default_lens = { -+ .format_cb = default_lens_format_cb, -+}; -+ -+ -+static int -+blind_lens_format_cb(struct lens *lens, FILE *stream, -+ struct value *value, struct value_dict *arguments) -+{ -+ return 0; -+} -+ -+struct lens blind_lens = { -+ .format_cb = blind_lens_format_cb, -+}; -+ -+ -+static int -+octal_lens_format_cb(struct lens *lens, FILE *stream, -+ struct value *value, struct value_dict *arguments) -+{ -+ return toplevel_format_lens(lens, stream, value, arguments, INT_FMT_o); -+} -+ -+struct lens octal_lens = { -+ .format_cb = octal_lens_format_cb, -+}; -+ -+ -+static int -+hex_lens_format_cb(struct lens *lens, FILE *stream, -+ struct value *value, struct value_dict *arguments) -+{ -+ return toplevel_format_lens(lens, stream, value, arguments, INT_FMT_x); -+} -+ -+struct lens hex_lens = { -+ .format_cb = hex_lens_format_cb, -+}; -+ -+ -+static int -+guess_lens_format_cb(struct lens *lens, FILE *stream, -+ struct value *value, struct value_dict *arguments) -+{ -+ return toplevel_format_lens(lens, stream, value, arguments, -+ INT_FMT_unknown); -+} -+ -+struct lens guess_lens = { -+ .format_cb = guess_lens_format_cb, -+}; -+ -+ -+static int -+bool_lens_format_cb(struct lens *lens, FILE *stream, -+ struct value *value, struct value_dict *arguments) -+{ -+ switch (value->type->type) { -+ case ARGTYPE_VOID: -+ case ARGTYPE_FLOAT: -+ case ARGTYPE_DOUBLE: -+ case ARGTYPE_STRUCT: -+ case ARGTYPE_POINTER: -+ case ARGTYPE_ARRAY: -+ return toplevel_format_lens(lens, stream, value, -+ arguments, INT_FMT_i); -+ -+ int zero; -+ case ARGTYPE_SHORT: -+ case ARGTYPE_INT: -+ case ARGTYPE_LONG: -+ case ARGTYPE_USHORT: -+ case ARGTYPE_UINT: -+ case ARGTYPE_ULONG: -+ case ARGTYPE_CHAR: -+ if ((zero = value_is_zero(value, arguments)) < 0) -+ return -1; -+ if (zero) -+ return fprintf(stream, "false"); -+ else -+ return fprintf(stream, "true"); -+ } -+ abort(); -+} -+ -+struct lens bool_lens = { -+ .format_cb = bool_lens_format_cb, -+}; -+ -+ -+static int -+string_lens_format_cb(struct lens *lens, FILE *stream, -+ struct value *value, struct value_dict *arguments) -+{ -+ switch (value->type->type) { -+ case ARGTYPE_POINTER: -+ /* This should really be written as either "string", -+ * or, if lens, then string(array(char, zero)*). But -+ * I suspect people are so used to the char * C idiom, -+ * that string(char *) might actually turn up. So -+ * let's just support it. */ -+ if (value->type->u.ptr_info.info->type == ARGTYPE_CHAR) { -+ struct arg_type_info info[2]; -+ type_init_array(&info[1], -+ value->type->u.ptr_info.info, 0, -+ expr_node_zero(), 0); -+ type_init_pointer(&info[0], &info[1], 0); -+ info->lens = lens; -+ info->own_lens = 0; -+ struct value tmp; -+ if (value_clone(&tmp, value) < 0) -+ return -1; -+ value_set_type(&tmp, info, 0); -+ int ret = string_lens_format_cb(lens, stream, &tmp, -+ arguments); -+ type_destroy(&info[0]); -+ type_destroy(&info[1]); -+ value_destroy(&tmp); -+ return ret; -+ } -+ -+ /* fall-through */ -+ case ARGTYPE_VOID: -+ case ARGTYPE_FLOAT: -+ case ARGTYPE_DOUBLE: -+ case ARGTYPE_STRUCT: -+ case ARGTYPE_SHORT: -+ case ARGTYPE_INT: -+ case ARGTYPE_LONG: -+ case ARGTYPE_USHORT: -+ case ARGTYPE_UINT: -+ case ARGTYPE_ULONG: -+ return toplevel_format_lens(lens, stream, value, -+ arguments, INT_FMT_i); -+ -+ case ARGTYPE_CHAR: -+ return format_char(stream, value, arguments); -+ -+ case ARGTYPE_ARRAY: -+ return format_array(stream, value, arguments, -+ value->type->u.array_info.length, -+ options.strlen, 0, "\"", "\"", ""); -+ } -+ abort(); -+} -+ -+struct lens string_lens = { -+ .format_cb = string_lens_format_cb, -+}; -diff --git a/lens_default.h b/lens_default.h -new file mode 100644 -index 0000000..9a9d0c6 ---- /dev/null -+++ b/lens_default.h -@@ -0,0 +1,49 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011 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 LENS_DEFAULT_H -+#define LENS_DEFAULT_H -+ -+#include "lens.h" -+ -+/* Default lens that does reasonable job for most cases. */ -+extern struct lens default_lens; -+ -+/* A lens that doesn't output anything. */ -+extern struct lens blind_lens; -+ -+/* A lens that formats integers in octal. */ -+extern struct lens octal_lens; -+ -+/* A lens that formats integers in hexadecimal. */ -+extern struct lens hex_lens; -+ -+/* A lens that formats integers as either "true" or "false". */ -+extern struct lens bool_lens; -+ -+/* A lens that tries to guess whether the value is "large" (i.e. a -+ * pointer, and should be formatted in hex), or "small" (and should be -+ * formatted in decimal). */ -+extern struct lens guess_lens; -+ -+/* A lens for strings. */ -+extern struct lens string_lens; -+ -+#endif /* LENS_DEFAULT_H */ -diff --git a/lens_enum.c b/lens_enum.c -new file mode 100644 -index 0000000..1af94d2 ---- /dev/null -+++ b/lens_enum.c -@@ -0,0 +1,163 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011 Petr Machata, Red Hat Inc. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation; either version 2 of the -+ * License, or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -+ * 02110-1301 USA -+ */ -+ -+#include -+#include -+#include -+ -+#include "arch.h" -+#include "lens_enum.h" -+#include "lens_default.h" -+#include "value.h" -+#include "type.h" -+ -+struct enum_entry { -+ char *key; -+ int own_key; -+ struct value *value; -+ int own_value; -+}; -+ -+static void -+enum_entry_dtor(struct enum_entry *entry, void *data) -+{ -+ if (entry->own_key) -+ free(entry->key); -+ if (entry->own_value) { -+ value_destroy(entry->value); -+ free(entry->value); -+ } -+} -+ -+static void -+enum_lens_destroy_cb(struct lens *lens) -+{ -+ struct enum_lens *self = (void *)lens; -+ -+ VECT_DESTROY(&self->entries, struct enum_entry, -+ enum_entry_dtor, NULL); -+} -+ -+enum { -+#ifdef ARCH_ENDIAN_BIG -+ big_endian = 1, -+#elif defined (ARCH_ENDIAN_LITTLE) -+ big_endian = 0, -+#else -+# error Undefined endianness. -+#endif -+}; -+ -+/* Returns 0 if they are not equal, >0 if they are, and <0 if there -+ * was an error. */ -+static int -+enum_values_equal(struct value *inf_value, struct value *enum_value, -+ struct value_dict *arguments) -+{ -+ /* Width may not match between what's defined in config file -+ * and what arrives from the back end. Typically, if there is -+ * a mismatch, the config file size will be larger, as we are -+ * in a situation where 64-bit tracer traces 32-bit process. -+ * But opposite situation can occur e.g. on PPC, where it's -+ * apparently possible for 32-bit tracer to trace 64-bit -+ * inferior, or hypothetically in a x32/x86_64 situation. */ -+ -+ unsigned char *inf_data = value_get_data(inf_value, arguments); -+ size_t inf_sz = value_size(inf_value, arguments); -+ if (inf_data == NULL || inf_sz == (size_t)-1) -+ return -1; -+ -+ assert(inf_value->type->type == enum_value->type->type); -+ -+ unsigned char *enum_data = value_get_data(enum_value, arguments); -+ size_t enum_sz = value_size(enum_value, arguments); -+ if (enum_data == NULL || enum_sz == (size_t)-1) -+ return -1; -+ -+ size_t sz = enum_sz > inf_sz ? inf_sz : enum_sz; -+ -+ if (big_endian) -+ return memcmp(enum_data + enum_sz - sz, -+ inf_data + inf_sz - sz, sz) == 0; -+ else -+ return memcmp(enum_data, inf_data, sz) == 0; -+} -+ -+static const char * -+enum_get(struct enum_lens *lens, struct value *value, -+ struct value_dict *arguments) -+{ -+ size_t i; -+ for (i = 0; i < vect_size(&lens->entries); ++i) { -+ struct enum_entry *entry = VECT_ELEMENT(&lens->entries, -+ struct enum_entry, i); -+ int st = enum_values_equal(value, entry->value, arguments); -+ if (st < 0) -+ return NULL; -+ else if (st != 0) -+ return entry->key; -+ } -+ return NULL; -+} -+ -+static int -+enum_lens_format_cb(struct lens *lens, FILE *stream, -+ struct value *value, struct value_dict *arguments) -+{ -+ struct enum_lens *self = (void *)lens; -+ -+ long l; -+ if (value_extract_word(value, &l, arguments) < 0) -+ return -1; -+ -+ const char *name = enum_get(self, value, arguments); -+ if (name != NULL) -+ return fprintf(stream, "%s", name); -+ -+ return lens_format(&default_lens, stream, value, arguments); -+} -+ -+ -+void -+lens_init_enum(struct enum_lens *lens) -+{ -+ *lens = (struct enum_lens){ -+ { -+ .format_cb = enum_lens_format_cb, -+ .destroy_cb = enum_lens_destroy_cb, -+ }, -+ }; -+ VECT_INIT(&lens->entries, struct enum_entry); -+} -+ -+int -+lens_enum_add(struct enum_lens *lens, -+ const char *key, int own_key, -+ struct value *value, int own_value) -+{ -+ struct enum_entry entry = { (char *)key, own_key, value, own_value }; -+ return VECT_PUSHBACK(&lens->entries, &entry); -+} -+ -+size_t -+lens_enum_size(struct enum_lens *lens) -+{ -+ return vect_size(&lens->entries); -+} -diff --git a/lens_enum.h b/lens_enum.h -new file mode 100644 -index 0000000..de7b386 ---- /dev/null -+++ b/lens_enum.h -@@ -0,0 +1,48 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011 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 LENS_ENUM_H -+#define LENS_ENUM_H -+ -+#include "lens.h" -+#include "vect.h" -+ -+struct enum_lens { -+ struct lens super; -+ struct vect entries; -+ int own; -+}; -+ -+/* Init enumeration LENS. */ -+void lens_init_enum(struct enum_lens *lens); -+ -+/* Push another member of the enumeration, named KEY, with given -+ * VALUE. If OWN_KEY, KEY is owned and released after the type is -+ * destroyed. KEY is typed as const char *, but note that if OWN_KEY, -+ * this value will be freed. If OWN_VALUE, then VALUE is owned and -+ * destroyed by LENS. */ -+int lens_enum_add(struct enum_lens *lens, -+ const char *key, int own_key, -+ struct value *value, int own_value); -+ -+/* Return number of enum elements of type INFO. */ -+size_t lens_enum_size(struct enum_lens *lens); -+ -+#endif /* LENS_ENUM_H */ -diff --git a/libltrace.c b/libltrace.c -index 92f2701..f4bb2c8 100644 ---- a/libltrace.c -+++ b/libltrace.c -@@ -1,3 +1,24 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2009 Juan Cespedes -+ * -+ * 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 "config.h" - - #include -@@ -11,6 +32,8 @@ - - #include "common.h" - #include "proc.h" -+#include "read_config_file.h" -+#include "backend.h" - - char *command = NULL; - -@@ -84,6 +107,7 @@ ltrace_init(int argc, char **argv) { - signal(SIGTERM, signal_exit); /* ... or killed */ - - argv = process_options(argc, argv); -+ init_global_config(); - while (opt_F) { - /* If filename begins with ~, expand it to the user's home */ - /* directory. This does not correctly handle ~yoda, but that */ -diff --git a/library.c b/library.c -index 92fccea..2bd7dbb 100644 ---- a/library.c -+++ b/library.c -@@ -27,7 +27,7 @@ - #include "library.h" - #include "proc.h" // for enum callback_status - #include "debug.h" --#include "common.h" // for arch_library_symbol_init, arch_library_init -+#include "backend.h" // for arch_library_symbol_init, arch_library_init - - #ifndef ARCH_HAVE_LIBRARY_DATA - void -@@ -207,10 +207,18 @@ static void - private_library_init(struct library *lib, enum library_type type) - { - lib->next = NULL; -+ -+ lib->key = 0; -+ lib->base = 0; -+ lib->entry = 0; -+ lib->dyn_addr = 0; -+ - lib->soname = NULL; - lib->own_soname = 0; -+ - lib->pathname = NULL; - lib->own_pathname = 0; -+ - lib->symbols = NULL; - lib->type = type; - } -diff --git a/ltrace-elf.c b/ltrace-elf.c -index b1af070..c40a188 100644 ---- a/ltrace-elf.c -+++ b/ltrace-elf.c -@@ -1,3 +1,30 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2006,2010,2011,2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2010 Zachary T Welch, CodeSourcery -+ * Copyright (C) 2010 Joe Damato -+ * Copyright (C) 1997,1998,2001,2004,2007,2008,2009 Juan Cespedes -+ * Copyright (C) 2006 Olaf Hering, SUSE Linux GmbH -+ * Copyright (C) 2006 Eric Vaitl, Cisco Systems, Inc. -+ * Copyright (C) 2006 Paul Gilliam, IBM Corporation -+ * 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 "config.h" - - #include -@@ -13,10 +40,13 @@ - #include - #include - --#include "common.h" --#include "proc.h" --#include "library.h" -+#include "backend.h" - #include "filter.h" -+#include "library.h" -+#include "ltrace-elf.h" -+#include "proc.h" -+#include "debug.h" -+#include "options.h" - - #ifdef PLT_REINITALISATION_BP - extern char *PLTs_initialized_by_here; -@@ -241,8 +271,11 @@ open_elf(struct ltelf *lte, const char *filename) - exit(EXIT_FAILURE); - } - -- if ((lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS -- || lte->ehdr.e_machine != LT_ELF_MACHINE) -+ if (1 -+#ifdef LT_ELF_MACHINE -+ && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS -+ || lte->ehdr.e_machine != LT_ELF_MACHINE) -+#endif - #ifdef LT_ELF_MACHINE2 - && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS2 - || lte->ehdr.e_machine != LT_ELF_MACHINE2) -diff --git a/output.c b/output.c -index db6e93e..8bfe3f0 100644 ---- a/output.c -+++ b/output.c -@@ -1,3 +1,27 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2010 Joe Damato -+ * Copyright (C) 1997,1998,1999,2001,2002,2003,2004,2007,2008,2009 Juan Cespedes -+ * Copyright (C) 2006 Paul Gilliam -+ * 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 "config.h" - - #include -@@ -7,10 +31,18 @@ - #include - #include - #include -+#include -+#include - - #include "common.h" - #include "proc.h" - #include "library.h" -+#include "type.h" -+#include "value.h" -+#include "value_dict.h" -+#include "param.h" -+#include "fetch.h" -+#include "lens_default.h" - - /* TODO FIXME XXX: include in common.h: */ - extern struct timeval current_time_spent; -@@ -18,7 +50,7 @@ extern struct timeval current_time_spent; - Dict *dict_opt_c = NULL; - - static Process *current_proc = 0; --static int current_depth = 0; -+static size_t current_depth = 0; - static int current_column = 0; - - static void -@@ -29,7 +61,8 @@ output_indent(struct Process *proc) - } - - static void --begin_of_line(enum tof type, Process *proc) { -+begin_of_line(Process *proc, int is_func, int indent) -+{ - current_column = 0; - if (!proc) { - return; -@@ -85,40 +118,89 @@ begin_of_line(enum tof type, Process *proc) { - } - } - if (opt_i) { -- if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { -+ if (is_func) - current_column += fprintf(options.output, "[%p] ", - proc->return_addr); -- } else { -+ else - current_column += fprintf(options.output, "[%p] ", - proc->instruction_pointer); -- } - } -- if (options.indent > 0 && type != LT_TOF_NONE) { -+ if (options.indent > 0 && indent) { - output_indent(proc); - } - } - -+static struct arg_type_info * -+get_unknown_type(void) -+{ -+ static struct arg_type_info *info = NULL; -+ if (info == NULL) { -+ info = malloc(sizeof(*info)); -+ if (info == NULL) { -+ report_global_error("malloc: %s", strerror(errno)); -+ abort(); -+ } -+ *info = *type_get_simple(ARGTYPE_LONG); -+ info->lens = &guess_lens; -+ } -+ return info; -+} -+ -+/* The default prototype is: long X(long, long, long, long). */ -+static Function * -+build_default_prototype(void) -+{ -+ Function *ret = malloc(sizeof(*ret)); -+ size_t i = 0; -+ if (ret == NULL) -+ goto err; -+ memset(ret, 0, sizeof(*ret)); -+ -+ struct arg_type_info *unknown_type = get_unknown_type(); -+ -+ ret->return_info = unknown_type; -+ ret->own_return_info = 0; -+ -+ ret->num_params = 4; -+ ret->params = malloc(sizeof(*ret->params) * ret->num_params); -+ if (ret->params == NULL) -+ goto err; -+ -+ for (i = 0; i < ret->num_params; ++i) -+ param_init_type(&ret->params[i], unknown_type, 0); -+ -+ return ret; -+ -+err: -+ report_global_error("malloc: %s", strerror(errno)); -+ if (ret->params != NULL) { -+ while (i-- > 0) -+ param_destroy(&ret->params[i]); -+ free(ret->params); -+ } -+ -+ free(ret); -+ -+ return NULL; -+} -+ - static Function * - name2func(char const *name) { - Function *tmp; - const char *str1, *str2; - -- tmp = list_of_functions; -- while (tmp) { --#ifdef USE_DEMANGLE -- str1 = options.demangle ? my_demangle(tmp->name) : tmp->name; -- str2 = options.demangle ? my_demangle(name) : name; --#else -+ for (tmp = list_of_functions; tmp != NULL; tmp = tmp->next) { - str1 = tmp->name; - str2 = name; --#endif -- if (!strcmp(str1, str2)) { -- -+ if (!strcmp(str1, str2)) - return tmp; -- } -- tmp = tmp->next; - } -- return NULL; -+ -+ static Function *def = NULL; -+ if (def == NULL) -+ def = build_default_prototype(); -+ -+ return def; - } - - void -@@ -139,7 +221,7 @@ output_line(Process *proc, char *fmt, ...) { - if (!fmt) { - return; - } -- begin_of_line(LT_TOF_NONE, proc); -+ begin_of_line(proc, 0, 0); - - va_start(args, fmt); - vfprintf(options.output, fmt, args); -@@ -155,15 +237,191 @@ tabto(int col) { - } - } - -+static int -+account_output(int o) -+{ -+ if (o < 0) -+ return -1; -+ current_column += o; -+ return 0; -+} -+ -+static int -+output_error(void) -+{ -+ return account_output(fprintf(options.output, "?")); -+} -+ -+static int -+fetch_simple_param(enum tof type, Process *proc, struct fetch_context *context, -+ struct value_dict *arguments, struct arg_type_info *info, -+ struct value *valuep) -+{ -+ /* Arrays decay into pointers per C standard. We check for -+ * this here, because here we also capture arrays that come -+ * from parameter packs. */ -+ int own = 0; -+ if (info->type == ARGTYPE_ARRAY) { -+ struct arg_type_info *tmp = malloc(sizeof(*tmp)); -+ if (tmp != NULL) { -+ type_init_pointer(tmp, info, 0); -+ tmp->lens = info->lens; -+ info = tmp; -+ own = 1; -+ } -+ } -+ -+ struct value value; -+ value_init(&value, proc, NULL, info, own); -+ if (fetch_arg_next(context, type, proc, info, &value) < 0) -+ return -1; -+ -+ if (val_dict_push_next(arguments, &value) < 0) { -+ value_destroy(&value); -+ return -1; -+ } -+ -+ if (valuep != NULL) -+ *valuep = value; -+ -+ return 0; -+} -+ -+static void -+fetch_param_stop(struct value_dict *arguments, ssize_t *params_leftp) -+{ -+ if (*params_leftp == -1) -+ *params_leftp = val_dict_count(arguments); -+} -+ -+static int -+fetch_param_pack(enum tof type, Process *proc, struct fetch_context *context, -+ struct value_dict *arguments, struct param *param, -+ ssize_t *params_leftp) -+{ -+ struct param_enum *e = param_pack_init(param, arguments); -+ if (e == NULL) -+ return -1; -+ -+ int ret = 0; -+ while (1) { -+ int insert_stop = 0; -+ struct arg_type_info *info = malloc(sizeof(*info)); -+ if (info == NULL -+ || param_pack_next(param, e, info, &insert_stop) < 0) { -+ fail: -+ free(info); -+ ret = -1; -+ break; -+ } -+ -+ if (insert_stop) -+ fetch_param_stop(arguments, params_leftp); -+ -+ if (info->type == ARGTYPE_VOID) -+ break; -+ -+ struct value val; -+ if (fetch_simple_param(type, proc, context, arguments, -+ info, &val) < 0) -+ goto fail; -+ -+ int stop = 0; -+ switch (param_pack_stop(param, e, &val)) { -+ case PPCB_ERR: -+ goto fail; -+ case PPCB_STOP: -+ stop = 1; -+ case PPCB_CONT: -+ break; -+ } -+ -+ if (stop) -+ break; -+ } -+ -+ param_pack_done(param, e); -+ return ret; -+} -+ -+static int -+fetch_one_param(enum tof type, Process *proc, struct fetch_context *context, -+ struct value_dict *arguments, struct param *param, -+ ssize_t *params_leftp) -+{ -+ switch (param->flavor) { -+ 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); -+ -+ case PARAM_FLAVOR_STOP: -+ fetch_param_stop(arguments, params_leftp); -+ return 0; -+ } -+ -+ assert(!"Invalid param flavor!"); -+ abort(); -+} -+ -+static int -+fetch_params(enum tof type, Process *proc, struct fetch_context *context, -+ struct value_dict *arguments, Function *func, ssize_t *params_leftp) -+{ -+ size_t i; -+ for (i = 0; i < func->num_params; ++i) -+ if (fetch_one_param(type, proc, context, arguments, -+ &func->params[i], params_leftp) < 0) -+ return -1; -+ -+ /* Implicit stop at the end of parameter list. */ -+ fetch_param_stop(arguments, params_leftp); -+ -+ return 0; -+} -+ -+static int -+output_one(struct value *val, struct value_dict *arguments) -+{ -+ int o = format_argument(options.output, val, arguments); -+ if (account_output(o) < 0) { -+ if (output_error() < 0) -+ return -1; -+ o = 1; -+ } -+ return o; -+} -+ -+static int -+output_params(struct value_dict *arguments, size_t start, size_t end, -+ int *need_delimp) -+{ -+ size_t i; -+ int need_delim = *need_delimp; -+ for (i = start; i < end; ++i) { -+ if (need_delim -+ && account_output(fprintf(options.output, ", ")) < 0) -+ return -1; -+ struct value *value = val_dict_get_num(arguments, i); -+ if (value == NULL) -+ return -1; -+ need_delim = output_one(value, arguments); -+ if (need_delim < 0) -+ return -1; -+ } -+ *need_delimp = need_delim; -+ return 0; -+} -+ - void - output_left(enum tof type, struct Process *proc, - struct library_symbol *libsym) - { - const char *function_name = libsym->name; - Function *func; -- static arg_type_info *arg_unknown = NULL; -- if (arg_unknown == NULL) -- arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN); - - if (options.summary) { - return; -@@ -174,50 +432,47 @@ output_left(enum tof type, struct Process *proc, - } - current_proc = proc; - current_depth = proc->callstack_depth; -- begin_of_line(type, proc); -+ begin_of_line(proc, type == LT_TOF_FUNCTION, 1); - if (!options.hide_caller && libsym->lib != NULL - && libsym->plt_type != LS_TOPLT_NONE) - current_column += fprintf(options.output, "%s->", - libsym->lib->soname); -+ -+ const char *name = function_name; - #ifdef USE_DEMANGLE -- current_column += -- fprintf(options.output, "%s(", -- (options.demangle -- ? my_demangle(function_name) : function_name)); --#else -- current_column += fprintf(options.output, "%s(", function_name); -+ if (options.demangle) -+ name = my_demangle(function_name); - #endif -+ if (account_output(fprintf(options.output, "%s(", name)) < 0) -+ return; - - func = name2func(function_name); -- if (!func) { -- int i; -- for (i = 0; i < 4; i++) { -- current_column += -- display_arg(type, proc, i, arg_unknown); -- current_column += fprintf(options.output, ", "); -- } -- current_column += display_arg(type, proc, 4, arg_unknown); -+ if (func == NULL) - return; -- } else { -- int i; -- for (i = 0; i < func->num_params - func->params_right - 1; i++) { -- current_column += -- display_arg(type, proc, i, func->arg_info[i]); -- current_column += fprintf(options.output, ", "); -- } -- if (func->num_params > func->params_right) { -- current_column += -- display_arg(type, proc, i, func->arg_info[i]); -- if (func->params_right) { -- current_column += fprintf(options.output, ", "); -- } -- } -- if (func->params_right -- || func->return_info->type == ARGTYPE_STRING_N -- || func->return_info->type == ARGTYPE_ARRAY) { -- save_register_args(type, proc); -- } -+ -+ struct fetch_context *context = fetch_arg_init(type, proc, -+ func->return_info); -+ struct value_dict *arguments = malloc(sizeof(*arguments)); -+ if (arguments == NULL) -+ return; -+ val_dict_init(arguments); -+ -+ ssize_t params_left = -1; -+ int need_delim = 0; -+ if (fetch_params(type, proc, context, arguments, func, ¶ms_left) < 0 -+ || output_params(arguments, 0, params_left, &need_delim) < 0) { -+ val_dict_destroy(arguments); -+ fetch_arg_done(context); -+ arguments = NULL; -+ context = NULL; - } -+ -+ struct callstack_element *stel -+ = &proc->callstack[proc->callstack_depth - 1]; -+ stel->fetch_context = context; -+ stel->arguments = arguments; -+ stel->out.params_left = params_left; -+ stel->out.need_delim = need_delim; - } - - void -@@ -225,9 +480,8 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) - { - const char *function_name = libsym->name; - Function *func = name2func(function_name); -- static arg_type_info *arg_unknown = NULL; -- if (arg_unknown == NULL) -- arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN); -+ if (func == NULL) -+ return; - - if (options.summary) { - struct opt_c_struct *st; -@@ -268,7 +522,7 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) - current_proc = 0; - } - if (current_proc != proc) { -- begin_of_line(type, proc); -+ begin_of_line(proc, type == LT_TOF_FUNCTIONR, 1); - #ifdef USE_DEMANGLE - current_column += - fprintf(options.output, "<... %s resumed> ", -@@ -279,32 +533,48 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) - #endif - } - -- if (!func) { -- current_column += fprintf(options.output, ") "); -- tabto(options.align - 1); -- fprintf(options.output, "= "); -- display_arg(type, proc, -1, arg_unknown); -- } else { -- int i; -- for (i = func->num_params - func->params_right; -- i < func->num_params - 1; i++) { -- current_column += -- display_arg(type, proc, i, func->arg_info[i]); -- current_column += fprintf(options.output, ", "); -- } -- if (func->params_right) { -- current_column += -- display_arg(type, proc, i, func->arg_info[i]); -- } -- current_column += fprintf(options.output, ") "); -- tabto(options.align - 1); -- fprintf(options.output, "= "); -- if (func->return_info->type == ARGTYPE_VOID) { -- fprintf(options.output, ""); -- } else { -- display_arg(type, proc, -1, func->return_info); -+ struct callstack_element *stel -+ = &proc->callstack[proc->callstack_depth - 1]; -+ -+ struct fetch_context *context = stel->fetch_context; -+ -+ /* Fetch & enter into dictionary the retval first, so that -+ * other values can use it in expressions. */ -+ struct value retval; -+ int own_retval = 0; -+ if (context != NULL) { -+ value_init(&retval, proc, NULL, func->return_info, 0); -+ own_retval = 1; -+ if (fetch_retval(context, type, proc, func->return_info, -+ &retval) == 0) { -+ if (stel->arguments != NULL -+ && val_dict_push_named(stel->arguments, &retval, -+ "retval", 0) == 0) -+ own_retval = 0; - } - } -+ -+ if (stel->arguments != NULL) -+ output_params(stel->arguments, stel->out.params_left, -+ val_dict_count(stel->arguments), -+ &stel->out.need_delim); -+ -+ current_column += fprintf(options.output, ") "); -+ tabto(options.align - 1); -+ fprintf(options.output, "= "); -+ -+ output_one(&retval, stel->arguments); -+ -+ if (own_retval) -+ value_destroy(&retval); -+ -+ if (stel->arguments != NULL) { -+ val_dict_destroy(stel->arguments); -+ free(stel->arguments); -+ } -+ if (context != NULL) -+ fetch_arg_done(context); -+ - if (opt_T) { - fprintf(options.output, " <%lu.%06d>", - current_time_spent.tv_sec, -@@ -336,3 +606,44 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) - current_proc = 0; - current_column = 0; - } -+ -+static void -+do_report(const char *filename, unsigned line_no, const char *severity, -+ const char *fmt, va_list args) -+{ -+ char buf[128]; -+ vsnprintf(buf, sizeof(buf), fmt, args); -+ buf[sizeof(buf) - 1] = 0; -+ if (filename != NULL) -+ output_line(0, "%s:%d: %s: %s", -+ filename, line_no, severity, buf); -+ else -+ output_line(0, "%s: %s", severity, buf); -+} -+ -+void -+report_error(const char *filename, unsigned line_no, char *fmt, ...) -+{ -+ va_list args; -+ va_start(args, fmt); -+ do_report(filename, line_no, "error", fmt, args); -+ va_end(args); -+} -+ -+void -+report_warning(const char *filename, unsigned line_no, char *fmt, ...) -+{ -+ va_list args; -+ va_start(args, fmt); -+ do_report(filename, line_no, "warning", fmt, args); -+ va_end(args); -+} -+ -+void -+report_global_error(char *fmt, ...) -+{ -+ va_list args; -+ va_start(args, fmt); -+ do_report(NULL, 0, "error", fmt, args); -+ va_end(args); -+} -diff --git a/output.h b/output.h -index 714078f..481a385 100644 ---- a/output.h -+++ b/output.h -@@ -1,7 +1,33 @@ --struct Process; --struct library_symbol; -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2009 Juan Cespedes -+ * -+ * 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 "fetch.h" -+ -+#include "forward.h" -+ - void output_line(struct Process *proc, char *fmt, ...); - void output_left(enum tof type, struct Process *proc, - struct library_symbol *libsym); - void output_right(enum tof type, struct Process *proc, - struct library_symbol *libsym); -+ -+void report_error(char const *file, unsigned line_no, char *fmt, ...); -+void report_warning(char const *file, unsigned line_no, char *fmt, ...); -+void report_global_error(char *fmt, ...); -diff --git a/param.c b/param.c -new file mode 100644 -index 0000000..7715571 ---- /dev/null -+++ b/param.c -@@ -0,0 +1,140 @@ -+/* -+ * 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 -+ */ -+ -+#include -+#include -+ -+#include "param.h" -+#include "type.h" -+#include "value.h" -+#include "expr.h" -+ -+void -+param_init_type(struct param *param, struct arg_type_info *type, int own) -+{ -+ param->flavor = PARAM_FLAVOR_TYPE; -+ param->u.type.type = type; -+ param->u.type.own_type = own; -+} -+ -+void -+param_init_stop(struct param *param) -+{ -+ param->flavor = PARAM_FLAVOR_STOP; -+} -+ -+void -+param_init_pack(struct param *param, -+ struct expr_node *args, size_t nargs, int own_args, -+ struct param_enum *(*init)(struct value *cb_args, -+ size_t nargs, -+ struct value_dict *arguments), -+ int (*next)(struct param_enum *context, -+ struct arg_type_info *infop, -+ int *insert_stop), -+ enum param_status (*stop)(struct param_enum *ctx, -+ struct value *value), -+ void (*done)(struct param_enum *)) -+{ -+ param->flavor = PARAM_FLAVOR_PACK; -+ param->u.pack.args = args; -+ param->u.pack.nargs = nargs; -+ param->u.pack.own_args = own_args; -+ param->u.pack.init = init; -+ param->u.pack.next = next; -+ param->u.pack.stop = stop; -+ param->u.pack.done = done; -+} -+ -+struct param_enum * -+param_pack_init(struct param *param, struct value_dict *fargs) -+{ -+ struct value cb_args[param->u.pack.nargs]; -+ size_t i; -+ -+ /* For evaluation of argument expressions, we pass in this as -+ * a "current" value. */ -+ struct arg_type_info *void_type = type_get_simple(ARGTYPE_VOID); -+ struct value void_val; -+ value_init_detached(&void_val, NULL, void_type, 0); -+ -+ struct param_enum *ret = NULL; -+ for (i = 0; i < param->u.pack.nargs; ++i) { -+ if (expr_eval(¶m->u.pack.args[i], &void_val, -+ fargs, &cb_args[i]) < 0) -+ goto release; -+ } -+ -+ ret = param->u.pack.init(cb_args, param->u.pack.nargs, fargs); -+ -+release: -+ while (i-- > 0) -+ value_destroy(&cb_args[i]); -+ return ret; -+} -+ -+int -+param_pack_next(struct param *param, struct param_enum *context, -+ struct arg_type_info *infop, int *insert_stop) -+{ -+ return param->u.pack.next(context, infop, insert_stop); -+} -+ -+enum param_status -+param_pack_stop(struct param *param, -+ struct param_enum *context, struct value *value) -+{ -+ return param->u.pack.stop(context, value); -+} -+ -+void -+param_pack_done(struct param *param, struct param_enum *context) -+{ -+ return param->u.pack.done(context); -+} -+ -+void -+param_destroy(struct param *param) -+{ -+ if (param == NULL) -+ return; -+ -+ switch (param->flavor) { -+ case PARAM_FLAVOR_TYPE: -+ if (param->u.type.own_type) { -+ type_destroy(param->u.type.type); -+ free(param->u.type.type); -+ } -+ return; -+ -+ case PARAM_FLAVOR_PACK: -+ if (param->u.pack.own_args) { -+ size_t i; -+ for (i = 0; i < param->u.pack.nargs; ++i) -+ expr_destroy(¶m->u.pack.args[i]); -+ free(param->u.pack.args); -+ } -+ case PARAM_FLAVOR_STOP: -+ return; -+ } -+ -+ assert(!"Unknown value of param flavor!"); -+ abort(); -+} -diff --git a/param.h b/param.h -new file mode 100644 -index 0000000..5882689 ---- /dev/null -+++ b/param.h -@@ -0,0 +1,150 @@ -+/* -+ * 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 PARAM_H -+#define PARAM_H -+ -+#include "forward.h" -+ -+/* The structure param holds information about a parameter of a -+ * function. It's used to configure a function prototype. There are -+ * two flavors of parameters: -+ * -+ * - simple types -+ * - parameter packs -+ * -+ * Parameter packs are used to describe various vararg constructs. -+ * They themselves are parametrized by ltrace expressions. Those will -+ * typically be references to other arguments, but constants might -+ * also make sense, and it principle, anything can be used. */ -+ -+enum param_flavor { -+ PARAM_FLAVOR_TYPE, -+ PARAM_FLAVOR_PACK, -+ -+ /* This is for emitting arguments in two bunches. This is -+ * where we should stop emitting "left" bunch. All that's -+ * after this parameter should be emitted in the "right" -+ * bunch. */ -+ PARAM_FLAVOR_STOP, -+}; -+ -+enum param_status { -+ PPCB_ERR = -1, /* An error occurred. */ -+ PPCB_STOP, /* Stop fetching the arguments. */ -+ PPCB_CONT, /* Display this argument and keep going. */ -+}; -+ -+/* Each parameter enumerator defines its own context object. -+ * Definitions of these are in respective .c files of each -+ * enumerator. */ -+struct param_enum; -+ -+/* int printf(string, pack(format, arg1)); */ -+struct param { -+ enum param_flavor flavor; -+ union { -+ struct { -+ struct arg_type_info *type; -+ int own_type; -+ } type; -+ struct { -+ struct expr_node *args; -+ size_t nargs; -+ int own_args; -+ -+ struct param_enum *(*init)(struct value *cb_args, -+ size_t nargs, -+ struct value_dict *arguments); -+ int (*next)(struct param_enum *self, -+ struct arg_type_info *info, -+ int *insert_stop); -+ enum param_status (*stop)(struct param_enum *self, -+ struct value *value); -+ void (*done)(struct param_enum *self); -+ } pack; -+ } u; -+}; -+ -+/* Initialize simple type parameter. TYPE is owned and released by -+ * PARAM if OWN_TYPE. */ -+void param_init_type(struct param *param, -+ struct arg_type_info *type, int own_type); -+ -+/* Initialize a stop. */ -+void param_init_stop(struct param *param); -+ -+/* Initialize parameter pack PARAM. ARGS is an array of expressions -+ * with parameters. ARGS is owned and released by the pack if -+ * OWN_ARGS. NARGS is number of ARGS. -+ * -+ * When the parameter pack should be expanded, those expressions are -+ * evaluated and passed to the INIT callback. This has to return a -+ * non-NULL context object. -+ * -+ * The NEXT callback is then called repeatedly, and should initialize -+ * its INFOP argument to a type of the next parameter in the pack. -+ * When there are no more parameters in the pack, the NEXT callback -+ * will set INFOP to a VOID parameter. If the callback sets -+ * INSERT_STOP to a non-zero value, a stop parameter shall be inserted -+ * before this actual parameter. -+ * -+ * Core then uses the passed-in type to fetch the next argument, which -+ * is in turn passed to STOP callback. This callback then tells -+ * ltrace core what to do next: whether there are more arguments, and -+ * if not, whether this argument should be displayed. -+ * -+ * After the enumeration is ended, DONE callback is called. */ -+void param_init_pack(struct param *param, -+ struct expr_node *args, size_t nargs, int own_args, -+ struct param_enum *(*init)(struct value *cb_args, -+ size_t nargs, -+ struct value_dict *arguments), -+ int (*next)(struct param_enum *self, -+ struct arg_type_info *infop, -+ int *insert_stop), -+ enum param_status (*stop)(struct param_enum *self, -+ struct value *value), -+ void (*done)(struct param_enum *self)); -+ -+/* Start enumerating types in parameter pack. This evaluates the -+ * parameter the pack arguments and calls the init callback. See the -+ * documentation of param_init_pack for details. */ -+struct param_enum *param_pack_init(struct param *param, -+ struct value_dict *fargs); -+ -+/* Ask for next type in enumeration. See the documentation of -+ * param_init_pack for details. */ -+int param_pack_next(struct param *param, struct param_enum *self, -+ struct arg_type_info *infop, int *insert_stop); -+ -+/* Ask whether we should stop enumerating. See the documentation of -+ * param_init_pack for details. */ -+enum param_status param_pack_stop(struct param *param, -+ struct param_enum *self, struct value *value); -+ -+/* Finish enumerating types in parameter pack. See the documentation -+ * of param_init_pack for details. */ -+void param_pack_done(struct param *param, struct param_enum *self); -+ -+/* Destroy data held by PARAM, but not the PARAM pointer itself. */ -+void param_destroy(struct param *param); -+ -+#endif /* PARAM_H */ -diff --git a/printf.c b/printf.c -new file mode 100644 -index 0000000..1fe3025 ---- /dev/null -+++ b/printf.c -@@ -0,0 +1,351 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 1998,2004,2007,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 "printf.h" -+#include "type.h" -+#include "value.h" -+#include "expr.h" -+#include "zero.h" -+#include "param.h" -+#include "lens_default.h" -+ -+struct param_enum { -+ struct value array; -+ int percent; -+ size_t *future_length; -+ char *format; -+ char const *ptr; -+ char const *end; -+}; -+ -+static struct param_enum * -+param_printf_init(struct value *cb_args, size_t nargs, -+ struct value_dict *arguments) -+{ -+ assert(nargs == 1); -+ -+ /* We expect a char array pointer. */ -+ if (cb_args->type->type != ARGTYPE_POINTER -+ || cb_args->type->u.ptr_info.info->type != ARGTYPE_ARRAY -+ || (cb_args->type->u.ptr_info.info->u.array_info.elt_type->type -+ != ARGTYPE_CHAR)) -+ return NULL; -+ -+ struct param_enum *self = malloc(sizeof(*self)); -+ if (self == NULL) { -+ fail: -+ free(self); -+ return NULL; -+ } -+ -+ if (value_init_deref(&self->array, cb_args) < 0) -+ goto fail; -+ -+ assert(self->array.type->type == ARGTYPE_ARRAY); -+ -+ self->format = (char *)value_get_data(&self->array, arguments); -+ if (self->format == NULL) -+ goto fail; -+ -+ size_t size = value_size(&self->array, arguments); -+ if (size == (size_t)-1) -+ goto fail; -+ -+ self->percent = 0; -+ self->ptr = self->format; -+ self->end = self->format + size; -+ self->future_length = NULL; -+ return self; -+} -+ -+static void -+drop_future_length(struct param_enum *self) -+{ -+ if (self->future_length != NULL) { -+ free(self->future_length); -+ self->future_length = NULL; -+ } -+} -+ -+static int -+form_next_param(struct param_enum *self, -+ enum arg_type format_type, enum arg_type elt_type, -+ unsigned hlf, unsigned lng, char *len_buf, size_t len_buf_len, -+ struct arg_type_info *infop) -+{ -+ /* XXX note: Some types are wrong because we lack -+ ARGTYPE_LONGLONG, ARGTYPE_UCHAR and ARGTYPE_SCHAR. */ -+ assert(lng <= 2); -+ assert(hlf <= 2); -+ static enum arg_type ints[] = -+ { ARGTYPE_CHAR, ARGTYPE_SHORT, ARGTYPE_INT, -+ ARGTYPE_LONG, ARGTYPE_ULONG }; -+ static enum arg_type uints[] = -+ { ARGTYPE_CHAR, ARGTYPE_USHORT, ARGTYPE_UINT, -+ ARGTYPE_ULONG, ARGTYPE_ULONG }; -+ -+ struct arg_type_info *elt_info = NULL; -+ if (format_type == ARGTYPE_ARRAY || format_type == ARGTYPE_POINTER) -+ elt_info = type_get_simple(elt_type); -+ else if (format_type == ARGTYPE_INT) -+ format_type = ints[2 + lng - hlf]; -+ else if (format_type == ARGTYPE_UINT) -+ format_type = uints[2 + lng - hlf]; -+ -+ -+ if (format_type == ARGTYPE_ARRAY) { -+ struct expr_node *node = NULL; -+ if (len_buf_len != 0 -+ || self->future_length != NULL) { -+ struct tmp { -+ struct expr_node node; -+ struct arg_type_info type; -+ }; -+ struct tmp *len = malloc(sizeof(*len)); -+ if (len == NULL) { -+ fail: -+ free(len); -+ return -1; -+ } -+ -+ len->type.type = ARGTYPE_LONG; -+ -+ long l; -+ if (self->future_length != NULL) { -+ l = *self->future_length; -+ drop_future_length(self); -+ } else { -+ l = atol(len_buf); -+ } -+ -+ expr_init_const_word(&len->node, l, &len->type, 0); -+ -+ node = build_zero_w_arg(&len->node, 1); -+ if (node == NULL) -+ goto fail; -+ -+ } else { -+ node = expr_node_zero(); -+ } -+ -+ assert(node != NULL); -+ type_init_array(infop, elt_info, 0, node, 1); -+ -+ } else if (format_type == ARGTYPE_POINTER) { -+ type_init_pointer(infop, elt_info, 1); -+ -+ } else { -+ *infop = *type_get_simple(format_type); -+ } -+ -+ return 0; -+} -+ -+static int -+param_printf_next(struct param_enum *self, struct arg_type_info *infop, -+ int *insert_stop) -+{ -+ unsigned hlf = 0; -+ unsigned lng = 0; -+ enum arg_type format_type = ARGTYPE_VOID; -+ enum arg_type elt_type = ARGTYPE_VOID; -+ char len_buf[25] = {}; -+ size_t len_buf_len = 0; -+ struct lens *lens = NULL; -+ -+ for (; self->ptr < self->end; ++self->ptr) { -+ if (!self->percent) { -+ if (*self->ptr == '%') -+ self->percent = 1; -+ continue; -+ } -+ -+ switch (*self->ptr) { -+ case '#': case ' ': case '-': -+ case '+': case 'I': case '\'': -+ /* These are only important for formatting, -+ * not for interpreting the type. */ -+ continue; -+ -+ case '*': -+ /* Length parameter given in the next -+ * argument. */ -+ if (self->future_length == NULL) -+ /* This should really be an assert, -+ * but we can't just fail on invalid -+ * format string. */ -+ self->future_length -+ = malloc(sizeof(*self->future_length)); -+ -+ if (self->future_length != NULL) { -+ ++self->ptr; -+ format_type = ARGTYPE_INT; -+ break; -+ } -+ -+ case '0': -+ case '1': case '2': case '3': -+ case '4': case '5': case '6': -+ case '7': case '8': case '9': -+ /* Field length likewise, but we need to parse -+ * this to attach the appropriate string -+ * length expression. */ -+ if (len_buf_len < sizeof(len_buf) - 1) -+ len_buf[len_buf_len++] = *self->ptr; -+ continue; -+ -+ case 'h': -+ if (hlf < 2) -+ hlf++; -+ continue; -+ -+ case 'l': -+ if (lng < 2) -+ lng++; -+ continue; -+ -+ case 'q': -+ lng = 2; -+ continue; -+ -+ case 'L': /* long double */ -+ lng = 1; -+ continue; -+ -+ case 'j': /* intmax_t */ -+ /* XXX ABI should know */ -+ lng = 2; -+ continue; -+ -+ case 't': /* ptrdiff_t */ -+ case 'Z': case 'z': /* size_t */ -+ lng = 1; /* XXX ABI should tell */ -+ continue; -+ -+ case 'd': -+ case 'i': -+ format_type = ARGTYPE_INT; -+ self->percent = 0; -+ break; -+ -+ case 'o': -+ lens = &octal_lens; -+ goto uint; -+ -+ case 'x': case 'X': -+ lens = &hex_lens; -+ case 'u': -+ uint: -+ format_type = ARGTYPE_UINT; -+ self->percent = 0; -+ break; -+ -+ case 'e': case 'E': -+ case 'f': case 'F': -+ case 'g': case 'G': -+ case 'a': case 'A': -+ format_type = ARGTYPE_DOUBLE; -+ self->percent = 0; -+ break; -+ -+ case 'C': /* like "lc" */ -+ if (lng == 0) -+ lng++; -+ case 'c': -+ /* XXX "lc" means wchar_t string. */ -+ format_type = ARGTYPE_CHAR; -+ self->percent = 0; -+ break; -+ -+ case 'S': /* like "ls" */ -+ if (lng == 0) -+ lng++; -+ case 's': -+ format_type = ARGTYPE_ARRAY; -+ /* XXX "ls" means wchar_t string. */ -+ elt_type = ARGTYPE_CHAR; -+ self->percent = 0; -+ lens = &string_lens; -+ break; -+ -+ case 'p': -+ case 'n': /* int* where to store no. of printed chars. */ -+ format_type = ARGTYPE_POINTER; -+ elt_type = ARGTYPE_VOID; -+ self->percent = 0; -+ break; -+ -+ case 'm': /* (glibc) print argument of errno */ -+ case '%': -+ lng = 0; -+ hlf = 0; -+ self->percent = 0; -+ continue; -+ -+ default: -+ continue; -+ } -+ -+ /* If we got here, the type must have been set. */ -+ assert(format_type != ARGTYPE_VOID); -+ -+ if (form_next_param(self, format_type, elt_type, hlf, lng, -+ len_buf, len_buf_len, infop) < 0) -+ return -1; -+ -+ infop->lens = lens; -+ infop->own_lens = 0; -+ -+ return 0; -+ } -+ -+ infop->type = ARGTYPE_VOID; -+ return 0; -+} -+ -+static enum param_status -+param_printf_stop(struct param_enum *self, struct value *value) -+{ -+ if (self->future_length != NULL -+ && value_extract_word(value, (long *)self->future_length, NULL) < 0) -+ drop_future_length(self); -+ -+ return PPCB_CONT; -+} -+ -+static void -+param_printf_done(struct param_enum *context) -+{ -+ free(context); -+} -+ -+void -+param_pack_init_printf(struct param *param, struct expr_node *arg, int own_arg) -+{ -+ param_init_pack(param, arg, 1, own_arg, -+ ¶m_printf_init, ¶m_printf_next, -+ ¶m_printf_stop, ¶m_printf_done); -+} -diff --git a/printf.h b/printf.h -new file mode 100644 -index 0000000..983c951 ---- /dev/null -+++ b/printf.h -@@ -0,0 +1,34 @@ -+/* -+ * 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 PRINTF_H -+#define PRINTF_H -+ -+#include -+#include "forward.h" -+ -+/* Wrapper around param_init_pack with callbacks to handle -+ * printf-style format strings. ARG should be an expression that -+ * evaluates to a pointer to a character array with the format string -+ * contents. */ -+void param_pack_init_printf(struct param *param, -+ struct expr_node *arg, int own_arg); -+ -+#endif /* PRINTF_H */ -diff --git a/proc.c b/proc.c -index 3e6d5cb..b280df8 100644 ---- a/proc.c -+++ b/proc.c -@@ -1,20 +1,45 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2010 Joe Damato -+ * Copyright (C) 1998,2009 Juan Cespedes -+ * -+ * 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 "config.h" - -+#include -+#include -+#include -+#include -+#include -+#include -+ - #if defined(HAVE_LIBUNWIND) - #include - #include - #endif /* defined(HAVE_LIBUNWIND) */ - --#include --#include --#include --#include --#include --#include -- --#include "common.h" -+#include "backend.h" - #include "breakpoint.h" -+#include "debug.h" -+#include "fetch.h" - #include "proc.h" -+#include "value_dict.h" - - #ifndef ARCH_HAVE_PROCESS_DATA - int -@@ -237,7 +262,7 @@ int - process_clone(struct Process *retp, struct Process *proc, pid_t pid) - { - if (process_bare_init(retp, proc->filename, pid, 0) < 0) { -- fail: -+ fail1: - fprintf(stderr, "failed to clone process %d->%d : %s\n", - proc->pid, pid, strerror(errno)); - return -1; -@@ -268,7 +293,7 @@ process_clone(struct Process *retp, struct Process *proc, pid_t pid) - free(lib); - lib = next; - } -- goto fail; -+ goto fail1; - } - - nlibp = &(*nlibp)->next; -@@ -282,16 +307,57 @@ process_clone(struct Process *retp, struct Process *proc, pid_t pid) - .error = 0, - }; - dict_apply_to_all(proc->breakpoints, &clone_single_bp, &data); -+ if (data.error < 0) -+ goto fail2; - - /* And finally the call stack. */ - memcpy(retp->callstack, proc->callstack, sizeof(retp->callstack)); - retp->callstack_depth = proc->callstack_depth; - -- if (data.error < 0) -- goto fail2; -+ size_t i; -+ for (i = 0; i < retp->callstack_depth; ++i) { -+ struct fetch_context *ctx = retp->callstack[i].fetch_context; -+ if (ctx != NULL) { -+ struct fetch_context *nctx = fetch_arg_clone(retp, ctx); -+ if (nctx == NULL) { -+ size_t j; -+ fail3: -+ for (j = 0; j < i; ++j) { -+ nctx = retp->callstack[i].fetch_context; -+ fetch_arg_done(nctx); -+ retp->callstack[i].fetch_context = NULL; -+ } -+ goto fail2; -+ } -+ retp->callstack[i].fetch_context = nctx; -+ } -+ -+ struct value_dict *args = retp->callstack[i].arguments; -+ if (args != NULL) { -+ struct value_dict *nargs = malloc(sizeof(*nargs)); -+ if (nargs == NULL -+ || val_dict_clone(nargs, args) < 0) { -+ size_t j; -+ fail4: -+ for (j = 0; j < i; ++j) { -+ nargs = retp->callstack[i].arguments; -+ val_dict_destroy(nargs); -+ free(nargs); -+ retp->callstack[i].arguments = NULL; -+ } -+ -+ /* Pretend that this round went well, -+ * so that fail3 frees I-th -+ * fetch_context. */ -+ ++i; -+ goto fail3; -+ } -+ retp->callstack[i].arguments = nargs; -+ } -+ } - - if (arch_process_clone(retp, proc) < 0) -- goto fail2; -+ goto fail4; - - return 0; - } -@@ -528,23 +594,6 @@ clear_leader(struct Process *proc, void *data) - return CBS_CONT; - } - --static enum ecb_status --event_for_proc(Event * event, void * data) --{ -- if (event->proc == data) -- return ecb_deque; -- else -- return ecb_cont; --} -- --static void --delete_events_for(Process * proc) --{ -- Event * event; -- while ((event = each_qd_event(&event_for_proc, proc)) != NULL) -- free(event); --} -- - void - remove_process(Process *proc) - { -@@ -554,7 +603,7 @@ remove_process(Process *proc) - each_task(proc, NULL, &clear_leader, NULL); - - unlist_process(proc); -- delete_events_for(proc); -+ process_removed(proc); - process_destroy(proc); - free(proc); - } -diff --git a/proc.h b/proc.h -index 443bd8e..86bf7da 100644 ---- a/proc.h -+++ b/proc.h -@@ -27,6 +27,8 @@ - #ifndef _PROC_H_ - #define _PROC_H_ - -+#include "config.h" -+ - #if defined(HAVE_LIBUNWIND) - # include - #endif /* defined(HAVE_LIBUNWIND) */ -@@ -60,6 +60,11 @@ enum process_state { - STATE_IGNORED /* ignore this process (it's a fork and no -f was used) */ - }; - -+struct output_state { -+ size_t params_left; -+ int need_delim; -+}; -+ - struct callstack_element { - union { - int syscall; -@@ -68,7 +73,9 @@ struct callstack_element { - int is_syscall; - void * return_addr; - struct timeval time_spent; -- void * arch_ptr; -+ struct fetch_context *fetch_context; -+ struct value_dict *arguments; -+ struct output_state out; - }; - - /* XXX We should get rid of this. */ -@@ -96,7 +103,7 @@ struct Process { - unsigned int personality; - int tracesysgood; /* signal indicating a PTRACE_SYSCALL trap */ - -- int callstack_depth; -+ size_t callstack_depth; - struct callstack_element callstack[MAX_CALLDEPTH]; - - /* Linked list of libraries in backwards order of mapping. -diff --git a/read_config_file.c b/read_config_file.c -index b4b1b56..87e87e7 100644 ---- a/read_config_file.c -+++ b/read_config_file.c -@@ -1,95 +1,100 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 1998,1999,2003,2007,2008,2009 Juan Cespedes -+ * Copyright (C) 2006 Ian Wienand -+ * Copyright (C) 2006 Steve Fink -+ * -+ * 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 "config.h" - - #include - #include - #include -+#include -+#include -+#include - - #include "common.h" -+#include "output.h" -+#include "expr.h" -+#include "param.h" -+#include "printf.h" -+#include "zero.h" -+#include "type.h" -+#include "lens.h" -+#include "lens_default.h" -+#include "lens_enum.h" - - static int line_no; - static char *filename; --static int error_count = 0; - --static arg_type_info *parse_type(char **str); -+static struct arg_type_info *parse_nonpointer_type(char **str, -+ struct param **extra_param, -+ size_t param_num, int *ownp); -+static struct arg_type_info *parse_type(char **str, struct param **extra_param, -+ size_t param_num, int *ownp); -+static struct arg_type_info *parse_lens(char **str, struct param **extra_param, -+ size_t param_num, int *ownp); -+static int parse_enum(char **str, struct arg_type_info **retp, int *ownp); - - Function *list_of_functions = NULL; - --/* Map of strings to type names. These do not need to be in any -- * particular order */ --static struct list_of_pt_t { -- char *name; -- enum arg_type pt; --} list_of_pt[] = { -- { -- "void", ARGTYPE_VOID}, { -- "int", ARGTYPE_INT}, { -- "uint", ARGTYPE_UINT}, { -- "long", ARGTYPE_LONG}, { -- "ulong", ARGTYPE_ULONG}, { -- "octal", ARGTYPE_OCTAL}, { -- "char", ARGTYPE_CHAR}, { -- "short", ARGTYPE_SHORT}, { -- "ushort", ARGTYPE_USHORT}, { -- "float", ARGTYPE_FLOAT}, { -- "double", ARGTYPE_DOUBLE}, { -- "addr", ARGTYPE_ADDR}, { -- "file", ARGTYPE_FILE}, { -- "format", ARGTYPE_FORMAT}, { -- "string", ARGTYPE_STRING}, { -- "array", ARGTYPE_ARRAY}, { -- "struct", ARGTYPE_STRUCT}, { -- "enum", ARGTYPE_ENUM}, { -- NULL, ARGTYPE_UNKNOWN} /* Must finish with NULL */ --}; -- --/* Array of prototype objects for each of the types. The order in this -- * array must exactly match the list of enumerated values in -- * common.h */ --static arg_type_info arg_type_prototypes[] = { -- { ARGTYPE_VOID }, -- { ARGTYPE_INT }, -- { ARGTYPE_UINT }, -- { ARGTYPE_LONG }, -- { ARGTYPE_ULONG }, -- { ARGTYPE_OCTAL }, -- { ARGTYPE_CHAR }, -- { ARGTYPE_SHORT }, -- { ARGTYPE_USHORT }, -- { ARGTYPE_FLOAT }, -- { ARGTYPE_DOUBLE }, -- { ARGTYPE_ADDR }, -- { ARGTYPE_FILE }, -- { ARGTYPE_FORMAT }, -- { ARGTYPE_STRING }, -- { ARGTYPE_STRING_N }, -- { ARGTYPE_ARRAY }, -- { ARGTYPE_ENUM }, -- { ARGTYPE_STRUCT }, -- { ARGTYPE_POINTER }, -- { ARGTYPE_UNKNOWN } --}; -- --arg_type_info * --lookup_prototype(enum arg_type at) { -- if (at >= 0 && at <= ARGTYPE_COUNT) -- return &arg_type_prototypes[at]; -- else -- return &arg_type_prototypes[ARGTYPE_COUNT]; /* UNKNOWN */ --} -- --static arg_type_info * --str2type(char **str) { -- struct list_of_pt_t *tmp = &list_of_pt[0]; -- -- while (tmp->name) { -- if (!strncmp(*str, tmp->name, strlen(tmp->name)) -- && index(" ,()#*;012345[", *(*str + strlen(tmp->name)))) { -- *str += strlen(tmp->name); -- return lookup_prototype(tmp->pt); -- } -- tmp++; -- } -- return lookup_prototype(ARGTYPE_UNKNOWN); -+static int -+parse_arg_type(char **name, enum arg_type *ret) -+{ -+ char *rest = NULL; -+ enum arg_type candidate; -+ -+#define KEYWORD(KWD, TYPE) \ -+ do { \ -+ if (strncmp(*name, KWD, sizeof(KWD) - 1) == 0) { \ -+ rest = *name + sizeof(KWD) - 1; \ -+ candidate = TYPE; \ -+ goto ok; \ -+ } \ -+ } while (0) -+ -+ KEYWORD("void", ARGTYPE_VOID); -+ KEYWORD("int", ARGTYPE_INT); -+ KEYWORD("uint", ARGTYPE_UINT); -+ KEYWORD("long", ARGTYPE_LONG); -+ KEYWORD("ulong", ARGTYPE_ULONG); -+ KEYWORD("char", ARGTYPE_CHAR); -+ KEYWORD("short", ARGTYPE_SHORT); -+ KEYWORD("ushort", ARGTYPE_USHORT); -+ KEYWORD("float", ARGTYPE_FLOAT); -+ KEYWORD("double", ARGTYPE_DOUBLE); -+ KEYWORD("array", ARGTYPE_ARRAY); -+ KEYWORD("struct", ARGTYPE_STRUCT); -+ -+ assert(rest == NULL); -+ return -1; -+ -+#undef KEYWORD -+ -+ok: -+ if (isalnum(*rest)) -+ return -1; -+ -+ *name = rest; -+ *ret = candidate; -+ return 0; - } - - static void -@@ -102,6 +107,10 @@ eat_spaces(char **str) { - static char * - xstrndup(char *str, size_t len) { - char *ret = (char *) malloc(len + 1); -+ if (ret == NULL) { -+ report_global_error("malloc: %s", strerror(errno)); -+ return NULL; -+ } - strncpy(ret, str, len); - ret[len] = 0; - return ret; -@@ -111,10 +120,8 @@ static char * - parse_ident(char **str) { - char *ident = *str; - -- if (!isalnum(**str) && **str != '_') { -- output_line(0, "Syntax error in `%s', line %d: Bad identifier", -- filename, line_no); -- error_count++; -+ if (!isalpha(**str) && **str != '_') { -+ report_error(filename, line_no, "bad identifier"); - return NULL; - } - -@@ -158,61 +165,189 @@ start_of_arg_sig(char *str) { - } - - static int --parse_int(char **str) { -+parse_int(char **str, long *ret) -+{ - char *end; - long n = strtol(*str, &end, 0); - if (end == *str) { -- output_line(0, "Syntax error in `%s', line %d: Bad number (%s)", -- filename, line_no, *str); -- error_count++; -- return 0; -+ report_error(filename, line_no, "bad number"); -+ return -1; - } - - *str = end; -- return n; -+ if (ret != NULL) -+ *ret = n; -+ return 0; -+} -+ -+static int -+check_nonnegative(long l) -+{ -+ if (l < 0) { -+ report_error(filename, line_no, -+ "expected non-negative value, got %ld", l); -+ return -1; -+ } -+ return 0; -+} -+ -+static int -+check_int(long l) -+{ -+ int i = l; -+ if ((long)i != l) { -+ report_error(filename, line_no, -+ "Number too large: %ld", l); -+ return -1; -+ } -+ return 0; -+} -+ -+static int -+parse_char(char **str, char expected) -+{ -+ if (**str != expected) { -+ report_error(filename, line_no, -+ "expected '%c', got '%c'", expected, **str); -+ return -1; -+ } -+ -+ ++*str; -+ return 0; -+} -+ -+static struct expr_node *parse_argnum(char **str, int zero); -+ -+static struct expr_node * -+parse_zero(char **str, struct expr_node *ret) -+{ -+ eat_spaces(str); -+ if (**str == '(') { -+ ++*str; -+ struct expr_node *arg = parse_argnum(str, 0); -+ if (arg == NULL) -+ return NULL; -+ if (parse_char(str, ')') < 0) { -+ fail: -+ expr_destroy(arg); -+ free(arg); -+ return NULL; -+ } -+ -+ struct expr_node *ret = build_zero_w_arg(arg, 1); -+ if (ret == NULL) -+ goto fail; -+ return ret; -+ -+ } else { -+ return expr_node_zero(); -+ } -+} -+ -+static int -+wrap_in_zero(struct expr_node **nodep) -+{ -+ struct expr_node *n = build_zero_w_arg(*nodep, 1); -+ if (n == NULL) -+ return -1; -+ *nodep = n; -+ return 0; - } - - /* - * Input: -- * argN : The value of argument #N, counting from 1 (arg0 = retval) -+ * argN : The value of argument #N, counting from 1 - * eltN : The value of element #N of the containing structure - * retval : The return value -- * 0 : Error -- * N : The numeric value N, if N > 0 -- * -- * Output: -- * > 0 actual numeric value -- * = 0 return value -- * < 0 (arg -n), counting from one -+ * N : The numeric value N - */ --static int --parse_argnum(char **str) { -- int multiplier = 1; -- int n = 0; -- -- if (strncmp(*str, "arg", 3) == 0) { -- (*str) += 3; -- multiplier = -1; -- } else if (strncmp(*str, "elt", 3) == 0) { -- (*str) += 3; -- multiplier = -1; -- } else if (strncmp(*str, "retval", 6) == 0) { -- (*str) += 6; -- return 0; -- } -+static struct expr_node * -+parse_argnum(char **str, int zero) -+{ -+ struct expr_node *expr = malloc(sizeof(*expr)); -+ if (expr == NULL) -+ return NULL; -+ -+ if (isdigit(**str)) { -+ long l; -+ if (parse_int(str, &l) < 0 -+ || check_nonnegative(l) < 0 -+ || check_int(l) < 0) -+ goto fail; - -- n = parse_int(str); -+ expr_init_const_word(expr, l, type_get_simple(ARGTYPE_LONG), 0); - -- return n * multiplier; -+ if (zero && wrap_in_zero(&expr) < 0) -+ goto fail; -+ -+ return expr; -+ -+ } else { -+ char *name = parse_ident(str); -+ if (name == NULL) -+ goto fail; -+ -+ int is_arg = strncmp(name, "arg", 3) == 0; -+ int is_elt = !is_arg && strncmp(name, "elt", 3) == 0; -+ if (is_arg || is_elt) { -+ long l; -+ name += 3; -+ if (parse_int(&name, &l) < 0 -+ || check_int(l) < 0) -+ goto fail; -+ -+ if (is_arg) { -+ expr_init_argno(expr, l - 1); -+ } else { -+ struct expr_node *e_up = malloc(sizeof(*e_up)); -+ struct expr_node *e_ix = malloc(sizeof(*e_ix)); -+ if (e_up == NULL || e_ix == NULL) { -+ free(e_up); -+ free(e_ix); -+ goto fail; -+ } -+ -+ expr_init_up(e_up, expr_self(), 0); -+ struct arg_type_info *ti -+ = type_get_simple(ARGTYPE_LONG); -+ expr_init_const_word(e_ix, l - 1, ti, 0); -+ expr_init_index(expr, e_up, 1, e_ix, 1); -+ } -+ -+ } else if (strcmp(name, "retval") == 0) { -+ expr_init_named(expr, "retval", 0); -+ -+ } else if (strcmp(name, "zero") == 0) { -+ struct expr_node *ret = parse_zero(str, expr); -+ if (ret == NULL) -+ goto fail; -+ return ret; -+ -+ } else { -+ report_error(filename, line_no, -+ "Unknown length specifier: '%s'", name); -+ goto fail; -+ } -+ -+ if (zero && wrap_in_zero(&expr) < 0) -+ goto fail; -+ -+ return expr; -+ } -+ -+fail: -+ free(expr); -+ return NULL; - } - - struct typedef_node_t { - char *name; -- arg_type_info *info; -+ struct arg_type_info *info; -+ int own_type; - struct typedef_node_t *next; - } *typedefs = NULL; - --static arg_type_info * -+static struct arg_type_info * - lookup_typedef(char **str) { - struct typedef_node_t *node; - char *end = *str; -@@ -231,11 +366,22 @@ lookup_typedef(char **str) { - return NULL; - } - -+static struct typedef_node_t * -+insert_typedef(char *name, struct arg_type_info *info, int own_type) -+{ -+ struct typedef_node_t *binding = malloc(sizeof(*binding)); -+ binding->name = name; -+ binding->info = info; -+ binding->own_type = own_type; -+ binding->next = typedefs; -+ typedefs = binding; -+ return binding; -+} -+ - static void - parse_typedef(char **str) { - char *name; -- arg_type_info *info; -- struct typedef_node_t *binding; -+ struct arg_type_info *info; - - (*str) += strlen("typedef"); - eat_spaces(str); -@@ -245,386 +391,689 @@ parse_typedef(char **str) { - - // Skip = sign - eat_spaces(str); -- if (**str != '=') { -- output_line(0, -- "Syntax error in `%s', line %d: expected '=', got '%c'", -- filename, line_no, **str); -- error_count++; -+ if (parse_char(str, '=') < 0) - return; -- } -- (*str)++; - eat_spaces(str); - - // Parse the type -- info = parse_type(str); -+ int own; -+ info = parse_type(str, NULL, 0, &own); - -- // Insert onto beginning of linked list -- binding = malloc(sizeof(*binding)); -- binding->name = name; -- binding->info = info; -- binding->next = typedefs; -- typedefs = binding; -+ insert_typedef(name, info, own); -+} -+ -+static void -+destroy_fun(Function *fun) -+{ -+ size_t i; -+ if (fun == NULL) -+ return; -+ if (fun->own_return_info) { -+ type_destroy(fun->return_info); -+ free(fun->return_info); -+ } -+ for (i = 0; i < fun->num_params; ++i) -+ param_destroy(&fun->params[i]); -+ free(fun->params); - } - --static size_t --arg_sizeof(arg_type_info * arg) { -- if (arg->type == ARGTYPE_CHAR) { -- return sizeof(char); -- } else if (arg->type == ARGTYPE_SHORT || arg->type == ARGTYPE_USHORT) { -- return sizeof(short); -- } else if (arg->type == ARGTYPE_FLOAT) { -- return sizeof(float); -- } else if (arg->type == ARGTYPE_DOUBLE) { -- return sizeof(double); -- } else if (arg->type == ARGTYPE_ENUM) { -- return sizeof(int); -- } else if (arg->type == ARGTYPE_STRUCT) { -- return arg->u.struct_info.size; -- } else if (arg->type == ARGTYPE_POINTER) { -- return sizeof(void*); -- } else if (arg->type == ARGTYPE_ARRAY) { -- if (arg->u.array_info.len_spec > 0) -- return arg->u.array_info.len_spec * arg->u.array_info.elt_size; -+/* Syntax: struct ( type,type,type,... ) */ -+static int -+parse_struct(char **str, struct arg_type_info *info) -+{ -+ eat_spaces(str); -+ if (parse_char(str, '(') < 0) -+ return -1; -+ -+ eat_spaces(str); // Empty arg list with whitespace inside -+ -+ type_init_struct(info); -+ -+ while (1) { -+ eat_spaces(str); -+ if (**str == 0 || **str == ')') { -+ parse_char(str, ')'); -+ return 0; -+ } -+ -+ /* Field delimiter. */ -+ if (type_struct_size(info) > 0) -+ parse_char(str, ','); -+ -+ eat_spaces(str); -+ int own; -+ struct arg_type_info *field = parse_lens(str, NULL, 0, &own); -+ if (field == NULL || type_struct_add(info, field, own)) { -+ type_destroy(info); -+ return -1; -+ } -+ } -+} -+ -+static int -+parse_string(char **str, struct arg_type_info **retp, int *ownp) -+{ -+ struct arg_type_info *info = malloc(sizeof(*info) * 2); -+ if (info == NULL) { -+ fail: -+ free(info); -+ return -1; -+ } -+ -+ struct expr_node *length; -+ int own_length; -+ int with_arg = 0; -+ -+ if (isdigit(**str)) { -+ /* string0 is string[retval], length is zero(retval) -+ * stringN is string[argN], length is zero(argN) */ -+ long l; -+ if (parse_int(str, &l) < 0 -+ || check_int(l) < 0) -+ goto fail; -+ -+ struct expr_node *length_arg = malloc(sizeof(*length_arg)); -+ if (length_arg == NULL) -+ goto fail; -+ -+ if (l == 0) -+ expr_init_named(length_arg, "retval", 0); - else -- return sizeof(void *); -+ expr_init_argno(length_arg, l - 1); -+ -+ length = build_zero_w_arg(length_arg, 1); -+ if (length == NULL) { -+ expr_destroy(length_arg); -+ free(length_arg); -+ goto fail; -+ } -+ own_length = 1; -+ - } else { -- return sizeof(int); -- } --} -- --#undef alignof --#define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st)) --static size_t --arg_align(arg_type_info * arg) { -- struct { char c; char C; } cC; -- struct { char c; short s; } cs; -- struct { char c; int i; } ci; -- struct { char c; long l; } cl; -- struct { char c; void* p; } cp; -- struct { char c; float f; } cf; -- struct { char c; double d; } cd; -- -- static size_t char_alignment = alignof(C, cC); -- static size_t short_alignment = alignof(s, cs); -- static size_t int_alignment = alignof(i, ci); -- static size_t long_alignment = alignof(l, cl); -- static size_t ptr_alignment = alignof(p, cp); -- static size_t float_alignment = alignof(f, cf); -- static size_t double_alignment = alignof(d, cd); -- -- switch (arg->type) { -- case ARGTYPE_LONG: -- case ARGTYPE_ULONG: -- return long_alignment; -- case ARGTYPE_CHAR: -- return char_alignment; -- case ARGTYPE_SHORT: -- case ARGTYPE_USHORT: -- return short_alignment; -- case ARGTYPE_FLOAT: -- return float_alignment; -- case ARGTYPE_DOUBLE: -- return double_alignment; -- case ARGTYPE_ADDR: -- case ARGTYPE_FILE: -- case ARGTYPE_FORMAT: -- case ARGTYPE_STRING: -- case ARGTYPE_STRING_N: -- case ARGTYPE_POINTER: -- return ptr_alignment; -- -- case ARGTYPE_ARRAY: -- return arg_align(&arg->u.array_info.elt_type[0]); -- -- case ARGTYPE_STRUCT: -- return arg_align(arg->u.struct_info.fields[0]); -- -- default: -- return int_alignment; -- } --} -- --static size_t --align_skip(size_t alignment, size_t offset) { -- if (offset % alignment) -- return alignment - (offset % alignment); -- else -- return 0; -+ eat_spaces(str); -+ if (**str == '[') { -+ (*str)++; -+ eat_spaces(str); -+ -+ length = parse_argnum(str, 1); -+ if (length == NULL) -+ goto fail; -+ own_length = 1; -+ -+ eat_spaces(str); -+ parse_char(str, ']'); -+ -+ } else if (**str == '(') { -+ /* Usage of "string" as lens. */ -+ ++*str; -+ -+ free(info); -+ -+ eat_spaces(str); -+ info = parse_type(str, NULL, 0, ownp); -+ if (info == NULL) -+ goto fail; -+ -+ eat_spaces(str); -+ parse_char(str, ')'); -+ -+ with_arg = 1; -+ -+ } else { -+ /* It was just a simple string after all. */ -+ length = expr_node_zero(); -+ own_length = 0; -+ } -+ } -+ -+ /* String is a pointer to array of chars. */ -+ if (!with_arg) { -+ type_init_array(&info[1], type_get_simple(ARGTYPE_CHAR), 0, -+ length, own_length); -+ -+ type_init_pointer(&info[0], &info[1], 0); -+ *ownp = 1; -+ } -+ -+ info->lens = &string_lens; -+ info->own_lens = 0; -+ -+ *retp = info; -+ return 0; - } - --/* I'm sure this isn't completely correct, but just try to get most of -- * them right for now. */ --static void --align_struct(arg_type_info* info) { -- size_t offset; -- int i; -+static int -+build_printf_pack(struct param **packp, size_t param_num) -+{ -+ if (packp == NULL) { -+ report_error(filename, line_no, -+ "'format' type in unexpected context"); -+ return -1; -+ } -+ if (*packp != NULL) { -+ report_error(filename, line_no, -+ "only one 'format' type per function supported"); -+ return -1; -+ } - -- if (info->u.struct_info.size != 0) -- return; // Already done -+ *packp = malloc(sizeof(**packp)); -+ if (*packp == NULL) -+ return -1; - -- // Compute internal padding due to alignment constraints for -- // various types. -- offset = 0; -- for (i = 0; info->u.struct_info.fields[i] != NULL; i++) { -- arg_type_info *field = info->u.struct_info.fields[i]; -- offset += align_skip(arg_align(field), offset); -- info->u.struct_info.offset[i] = offset; -- offset += arg_sizeof(field); -+ struct expr_node *node = malloc(sizeof(*node)); -+ if (node == NULL) { -+ free(*packp); -+ return -1; - } - -- info->u.struct_info.size = offset; -+ expr_init_argno(node, param_num); -+ -+ param_pack_init_printf(*packp, node, 1); -+ -+ return 0; - } - --static arg_type_info * --parse_nonpointer_type(char **str) { -- arg_type_info *simple; -- arg_type_info *info; -+/* Match and consume KWD if it's next in stream, and return 0. -+ * Otherwise return negative number. */ -+static int -+try_parse_kwd(char **str, const char *kwd) -+{ -+ size_t len = strlen(kwd); -+ if (strncmp(*str, kwd, len) == 0 -+ && !isalnum((*str)[len])) { -+ (*str) += len; -+ return 0; -+ } -+ return -1; -+} - -- if (strncmp(*str, "typedef", 7) == 0) { -- parse_typedef(str); -- return lookup_prototype(ARGTYPE_UNKNOWN); -+/* Make a copy of INFO and set the *OWN bit if it's not already -+ * owned. */ -+static int -+unshare_type_info(struct arg_type_info **infop, int *ownp) -+{ -+ if (*ownp) -+ return 0; -+ -+ struct arg_type_info *ninfo = malloc(sizeof(*ninfo)); -+ if (ninfo == NULL) { -+ report_error(filename, line_no, -+ "malloc: %s", strerror(errno)); -+ return -1; - } -+ *ninfo = **infop; -+ *infop = ninfo; -+ *ownp = 1; -+ return 0; -+} - -- simple = str2type(str); -- if (simple->type == ARGTYPE_UNKNOWN) { -- info = lookup_typedef(str); -- if (info) -- return info; -- else -- return simple; // UNKNOWN -+/* XXX extra_param and param_num are a kludge to get in -+ * backward-compatible support for "format" parameter type. The -+ * latter is only valid if the former is non-NULL, which is only in -+ * top-level context. */ -+static int -+parse_alias(char **str, struct arg_type_info **retp, int *ownp, -+ struct param **extra_param, size_t param_num) -+{ -+ /* For backward compatibility, we need to support things like -+ * stringN (which is like string[argN], string[N], and also -+ * bare string. We might, in theory, replace this by -+ * preprocessing configure file sources with M4, but for now, -+ * "string" is syntax. */ -+ if (strncmp(*str, "string", 6) == 0) { -+ (*str) += 6; -+ return parse_string(str, retp, ownp); -+ -+ } else if (try_parse_kwd(str, "format") >= 0 -+ && extra_param != NULL) { -+ /* For backward compatibility, format is parsed as -+ * "string", but it smuggles to the parameter list of -+ * a function a "printf" argument pack with this -+ * parameter as argument. */ -+ if (parse_string(str, retp, ownp) < 0) -+ return -1; -+ -+ return build_printf_pack(extra_param, param_num); -+ -+ } else if (try_parse_kwd(str, "enum") >=0) { -+ -+ return parse_enum(str, retp, ownp); -+ -+ } else { -+ *retp = NULL; -+ return 0; - } -+} - -- info = malloc(sizeof(*info)); -- info->type = simple->type; -+/* Syntax: array ( type, N|argN ) */ -+static int -+parse_array(char **str, struct arg_type_info *info) -+{ -+ eat_spaces(str); -+ if (parse_char(str, '(') < 0) -+ return -1; - -- /* Code to parse parameterized types will go into the following -- switch statement. */ -+ eat_spaces(str); -+ int own; -+ struct arg_type_info *elt_info = parse_lens(str, NULL, 0, &own); -+ if (elt_info == NULL) -+ return -1; - -- switch (info->type) { -+ eat_spaces(str); -+ parse_char(str, ','); - -- /* Syntax: array ( type, N|argN ) */ -- case ARGTYPE_ARRAY: -- (*str)++; // Get past open paren -- eat_spaces(str); -- if ((info->u.array_info.elt_type = parse_type(str)) == NULL) -- return NULL; -- info->u.array_info.elt_size = -- arg_sizeof(info->u.array_info.elt_type); -- (*str)++; // Get past comma -+ eat_spaces(str); -+ struct expr_node *length = parse_argnum(str, 0); -+ if (length == NULL) { -+ if (own) { -+ type_destroy(elt_info); -+ free(elt_info); -+ } -+ return -1; -+ } -+ -+ type_init_array(info, elt_info, own, length, 1); -+ -+ eat_spaces(str); -+ parse_char(str, ')'); -+ return 0; -+} -+ -+/* Syntax: -+ * enum (keyname[=value],keyname[=value],... ) -+ * enum (keyname[=value],keyname[=value],... ) -+ */ -+static int -+parse_enum(char **str, struct arg_type_info **retp, int *ownp) -+{ -+ /* Optional type argument. */ -+ eat_spaces(str); -+ if (**str == '[') { -+ parse_char(str, '['); - eat_spaces(str); -- info->u.array_info.len_spec = parse_argnum(str); -- (*str)++; // Get past close paren -- return info; -- -- /* Syntax: enum ( keyname=value,keyname=value,... ) */ -- case ARGTYPE_ENUM:{ -- struct enum_opt { -- char *key; -- int value; -- struct enum_opt *next; -- }; -- struct enum_opt *list = NULL; -- struct enum_opt *p; -- int entries = 0; -- int ii; -+ *retp = parse_nonpointer_type(str, NULL, 0, ownp); -+ if (*retp == NULL) -+ return -1; -+ -+ if (!type_is_integral((*retp)->type)) { -+ report_error(filename, line_no, -+ "integral type required as enum argument"); -+ fail: -+ if (*ownp) { -+ /* This also releases associated lens -+ * if any was set so far. */ -+ type_destroy(*retp); -+ free(*retp); -+ } -+ return -1; -+ } - - eat_spaces(str); -- (*str)++; // Get past open paren -+ if (parse_char(str, ']') < 0) -+ goto fail; -+ -+ } else { -+ *retp = type_get_simple(ARGTYPE_INT); -+ *ownp = 0; -+ } -+ -+ /* We'll need to set the lens, so unshare. */ -+ if (unshare_type_info(retp, ownp) < 0) -+ goto fail; -+ -+ eat_spaces(str); -+ if (parse_char(str, '(') < 0) -+ goto fail; -+ -+ struct enum_lens *lens = malloc(sizeof(*lens)); -+ if (lens == NULL) { -+ report_error(filename, line_no, -+ "malloc enum lens: %s", strerror(errno)); -+ return -1; -+ } -+ -+ lens_init_enum(lens); -+ (*retp)->lens = &lens->super; -+ (*retp)->own_lens = 1; -+ -+ long last_val = 0; -+ while (1) { - eat_spaces(str); -+ if (**str == 0 || **str == ')') { -+ parse_char(str, ')'); -+ return 0; -+ } - -- while (**str && **str != ')') { -- p = (struct enum_opt *) malloc(sizeof(*p)); -- eat_spaces(str); -- p->key = parse_ident(str); -- if (error_count) { -- free(p); -- return NULL; -- } -- eat_spaces(str); -- if (**str != '=') { -- free(p->key); -- free(p); -- output_line(0, -- "Syntax error in `%s', line %d: expected '=', got '%c'", -- filename, line_no, **str); -- error_count++; -- return NULL; -- } -- ++(*str); -- eat_spaces(str); -- p->value = parse_int(str); -- p->next = list; -- list = p; -- ++entries; -+ /* Field delimiter. XXX should we support the C -+ * syntax, where the enumeration can end in pending -+ * comma? */ -+ if (lens_enum_size(lens) > 0) -+ parse_char(str, ','); - -- // Skip comma -- eat_spaces(str); -- if (**str == ',') { -- (*str)++; -- eat_spaces(str); -- } -+ eat_spaces(str); -+ char *key = parse_ident(str); -+ if (key == NULL) { -+ err: -+ free(key); -+ goto fail; - } - -- info->u.enum_info.entries = entries; -- info->u.enum_info.keys = -- (char **) malloc(entries * sizeof(char *)); -- info->u.enum_info.values = -- (int *) malloc(entries * sizeof(int)); -- for (ii = 0, p = NULL; list; ++ii, list = list->next) { -- if (p) -- free(p); -- info->u.enum_info.keys[ii] = list->key; -- info->u.enum_info.values[ii] = list->value; -- p = list; -+ if (**str == '=') { -+ ++*str; -+ eat_spaces(str); -+ if (parse_int(str, &last_val) < 0) -+ goto err; - } -- if (p) -- free(p); - -- return info; -+ struct value *value = malloc(sizeof(*value)); -+ if (value == NULL) -+ goto err; -+ value_init_detached(value, NULL, *retp, 0); -+ value_set_word(value, last_val); -+ -+ if (lens_enum_add(lens, key, 1, value, 1) < 0) -+ goto err; -+ -+ last_val++; - } - -- case ARGTYPE_STRING: -- if (!isdigit(**str) && **str != '[') { -- /* Oops, was just a simple string after all */ -- free(info); -+ return 0; -+} -+ -+static struct arg_type_info * -+parse_nonpointer_type(char **str, struct param **extra_param, size_t param_num, -+ int *ownp) -+{ -+ enum arg_type type; -+ if (parse_arg_type(str, &type) < 0) { -+ struct arg_type_info *simple; -+ if (parse_alias(str, &simple, ownp, extra_param, param_num) < 0) -+ return NULL; -+ if (simple == NULL) -+ simple = lookup_typedef(str); -+ if (simple != NULL) { -+ *ownp = 0; - return simple; - } - -- info->type = ARGTYPE_STRING_N; -+ report_error(filename, line_no, -+ "unknown type around '%s'", *str); -+ return NULL; -+ } -+ -+ int (*parser) (char **, struct arg_type_info *) = NULL; -+ -+ /* For some types that's all we need. */ -+ switch (type) { -+ case ARGTYPE_VOID: -+ case ARGTYPE_INT: -+ case ARGTYPE_UINT: -+ case ARGTYPE_LONG: -+ case ARGTYPE_ULONG: -+ case ARGTYPE_CHAR: -+ case ARGTYPE_SHORT: -+ case ARGTYPE_USHORT: -+ case ARGTYPE_FLOAT: -+ case ARGTYPE_DOUBLE: -+ *ownp = 0; -+ return type_get_simple(type); -+ -+ case ARGTYPE_ARRAY: -+ parser = parse_array; -+ break; -+ -+ case ARGTYPE_STRUCT: -+ parser = parse_struct; -+ break; -+ -+ case ARGTYPE_POINTER: -+ /* Pointer syntax is not based on keyword, so we -+ * should never get this type. */ -+ assert(type != ARGTYPE_POINTER); -+ abort(); -+ } -+ -+ struct arg_type_info *info = malloc(sizeof(*info)); -+ if (info == NULL) { -+ report_error(filename, line_no, -+ "malloc: %s", strerror(errno)); -+ return NULL; -+ } -+ *ownp = 1; -+ -+ if (parser(str, info) < 0) { -+ free(info); -+ return NULL; -+ } - -- /* Backwards compatibility for string0, string1, ... */ -- if (isdigit(**str)) { -- info->u.string_n_info.size_spec = -parse_int(str); -- return info; -+ return info; -+} -+ -+static struct named_lens { -+ const char *name; -+ struct lens *lens; -+} lenses[] = { -+ { "hide", &blind_lens }, -+ { "octal", &octal_lens }, -+ { "hex", &hex_lens }, -+ { "bool", &bool_lens }, -+ { "guess", &guess_lens }, -+}; -+ -+static struct lens * -+name2lens(char **str, int *own_lensp) -+{ -+ size_t i; -+ for (i = 0; i < sizeof(lenses)/sizeof(*lenses); ++i) -+ if (try_parse_kwd(str, lenses[i].name) == 0) { -+ *own_lensp = 0; -+ return lenses[i].lens; - } - -- (*str)++; // Skip past opening [ -- eat_spaces(str); -- info->u.string_n_info.size_spec = parse_argnum(str); -+ return NULL; -+} -+ -+static struct arg_type_info * -+parse_type(char **str, struct param **extra_param, size_t param_num, int *ownp) -+{ -+ struct arg_type_info *info -+ = parse_nonpointer_type(str, extra_param, param_num, ownp); -+ if (info == NULL) -+ return NULL; -+ -+ while (1) { - eat_spaces(str); -- (*str)++; // Skip past closing ] -- return info; -- -- // Syntax: struct ( type,type,type,... ) -- case ARGTYPE_STRUCT:{ -- int field_num = 0; -- (*str)++; // Get past open paren -- info->u.struct_info.fields = -- malloc((MAX_ARGS + 1) * sizeof(void *)); -- info->u.struct_info.offset = -- malloc((MAX_ARGS + 1) * sizeof(size_t)); -- info->u.struct_info.size = 0; -- eat_spaces(str); // Empty arg list with whitespace inside -- while (**str && **str != ')') { -- if (field_num == MAX_ARGS) { -- output_line(0, -- "Error in `%s', line %d: Too many structure elements", -- filename, line_no); -- error_count++; -+ if (**str == '*') { -+ struct arg_type_info *outer = malloc(sizeof(*outer)); -+ if (outer == NULL) { -+ if (*ownp) { -+ type_destroy(info); -+ free(info); -+ } -+ report_error(filename, line_no, -+ "malloc: %s", strerror(errno)); - return NULL; - } -- eat_spaces(str); -- if (field_num != 0) { -- (*str)++; // Get past comma -- eat_spaces(str); -- } -- if ((info->u.struct_info.fields[field_num++] = -- parse_type(str)) == NULL) -- return NULL; -+ type_init_pointer(outer, info, *ownp); -+ *ownp = 1; -+ (*str)++; -+ info = outer; -+ } else -+ break; -+ } -+ return info; -+} - -- // Must trim trailing spaces so the check for -- // the closing paren is simple -- eat_spaces(str); -+static struct arg_type_info * -+parse_lens(char **str, struct param **extra_param, size_t param_num, int *ownp) -+{ -+ int own_lens; -+ struct lens *lens = name2lens(str, &own_lens); -+ int has_args = 1; -+ struct arg_type_info *info; -+ if (lens != NULL) { -+ eat_spaces(str); -+ -+ /* Octal lens gets special treatment, because of -+ * backward compatibility. */ -+ if (lens == &octal_lens && **str != '(') { -+ has_args = 0; -+ info = type_get_simple(ARGTYPE_INT); -+ *ownp = 0; -+ } else if (parse_char(str, '(') < 0) { -+ report_error(filename, line_no, -+ "expected type argument after the lens"); -+ return NULL; - } -- (*str)++; // Get past closing paren -- info->u.struct_info.fields[field_num] = NULL; -- align_struct(info); -- return info; - } - -- default: -- if (info->type == ARGTYPE_UNKNOWN) { -- output_line(0, "Syntax error in `%s', line %d: " -- "Unknown type encountered", -- filename, line_no); -- free(info); -- error_count++; -+ if (has_args) { -+ eat_spaces(str); -+ info = parse_type(str, extra_param, param_num, ownp); -+ if (info == NULL) { -+ fail: -+ if (own_lens && lens != NULL) -+ lens_destroy(lens); - return NULL; -- } else { -- return info; - } - } --} - --static arg_type_info * --parse_type(char **str) { -- arg_type_info *info = parse_nonpointer_type(str); -- while (**str == '*') { -- arg_type_info *outer = malloc(sizeof(*info)); -- outer->type = ARGTYPE_POINTER; -- outer->u.ptr_info.info = info; -- (*str)++; -- info = outer; -+ if (lens != NULL && has_args) { -+ eat_spaces(str); -+ parse_char(str, ')'); - } -+ -+ /* We can't modify shared types. Make a copy if we have a -+ * lens. */ -+ if (lens != NULL && unshare_type_info(&info, ownp) < 0) -+ goto fail; -+ -+ if (lens != NULL) { -+ info->lens = lens; -+ info->own_lens = own_lens; -+ } -+ - return info; - } - -+static int -+add_param(Function *fun, size_t *allocdp) -+{ -+ size_t allocd = *allocdp; -+ /* XXX +1 is for the extra_param handling hack. */ -+ if ((fun->num_params + 1) >= allocd) { -+ allocd = allocd > 0 ? 2 * allocd : 8; -+ void *na = realloc(fun->params, sizeof(*fun->params) * allocd); -+ if (na == NULL) -+ return -1; -+ -+ fun->params = na; -+ *allocdp = allocd; -+ } -+ return 0; -+} -+ - static Function * - process_line(char *buf) { -- Function fun; -- Function *fun_p; - char *str = buf; - char *tmp; -- int i; -- int float_num = 0; - - line_no++; - debug(3, "Reading line %d of `%s'", line_no, filename); - eat_spaces(&str); -- fun.return_info = parse_type(&str); -- if (fun.return_info == NULL) -+ -+ /* A comment or empty line. */ -+ if (*str == ';' || *str == 0 || *str == '\n') -+ return NULL; -+ -+ if (strncmp(str, "typedef", 7) == 0) { -+ parse_typedef(&str); -+ return NULL; -+ } -+ -+ Function *fun = calloc(1, sizeof(*fun)); -+ if (fun == NULL) { -+ report_error(filename, line_no, -+ "alloc function: %s", strerror(errno)); - return NULL; -- if (fun.return_info->type == ARGTYPE_UNKNOWN) { -+ } -+ -+ fun->return_info = parse_lens(&str, NULL, 0, &fun->own_return_info); -+ if (fun->return_info == NULL) { -+ err: - debug(3, " Skipping line %d", line_no); -+ destroy_fun(fun); - return NULL; - } -- debug(4, " return_type = %d", fun.return_info->type); -+ debug(4, " return_type = %d", fun->return_info->type); -+ - eat_spaces(&str); - tmp = start_of_arg_sig(str); -- if (!tmp) { -- output_line(0, "Syntax error in `%s', line %d", filename, -- line_no); -- error_count++; -- return NULL; -+ if (tmp == NULL) { -+ report_error(filename, line_no, "syntax error"); -+ goto err; - } - *tmp = '\0'; -- fun.name = strdup(str); -+ fun->name = strdup(str); - str = tmp + 1; -- debug(3, " name = %s", fun.name); -- fun.params_right = 0; -- for (i = 0; i < MAX_ARGS; i++) { -+ debug(3, " name = %s", fun->name); -+ -+ size_t allocd = 0; -+ struct param *extra_param = NULL; -+ -+ int have_stop = 0; -+ -+ while (1) { - eat_spaces(&str); -- if (*str == ')') { -+ if (*str == ')') - break; -- } -+ - if (str[0] == '+') { -- fun.params_right++; -+ if (have_stop == 0) { -+ if (add_param(fun, &allocd) < 0) -+ goto add_err; -+ param_init_stop -+ (&fun->params[fun->num_params++]); -+ have_stop = 1; -+ } - str++; -- } else if (fun.params_right) { -- fun.params_right++; - } -- fun.arg_info[i] = parse_type(&str); -- if (fun.arg_info[i] == NULL) { -- output_line(0, "Syntax error in `%s', line %d" -- ": unknown argument type", -- filename, line_no); -- error_count++; -- return NULL; -+ -+ if (add_param(fun, &allocd) < 0) { -+ add_err: -+ report_error(filename, line_no, "(re)alloc params: %s", -+ strerror(errno)); -+ goto err; -+ } -+ -+ int own; -+ struct arg_type_info *type = parse_lens(&str, &extra_param, -+ fun->num_params, &own); -+ if (type == NULL) { -+ report_error(filename, line_no, -+ "unknown argument type"); -+ goto err; -+ } -+ -+ /* XXX We used to allow void parameter as a synonym to -+ * an argument that shouldn't be displayed. We may -+ * wish to re-introduce this when lenses are -+ * implemented, as a synonym, but backends generally -+ * need to know the type, so disallow bare void for -+ * now. */ -+ if (type->type == ARGTYPE_VOID) { -+ report_warning(filename, line_no, -+ "void parameter assumed to be 'int'"); -+ if (own) { -+ type_destroy(type); -+ free(type); -+ } -+ type = type_get_simple(ARGTYPE_INT); -+ own = 0; - } -- if (fun.arg_info[i]->type == ARGTYPE_FLOAT) -- fun.arg_info[i]->u.float_info.float_index = float_num++; -- else if (fun.arg_info[i]->type == ARGTYPE_DOUBLE) -- fun.arg_info[i]->u.double_info.float_index = float_num++; -+ -+ param_init_type(&fun->params[fun->num_params++], type, own); -+ - eat_spaces(&str); - if (*str == ',') { - str++; -@@ -634,20 +1083,35 @@ process_line(char *buf) { - } else { - if (str[strlen(str) - 1] == '\n') - str[strlen(str) - 1] = '\0'; -- output_line(0, "Syntax error in `%s', line %d at ...\"%s\"", -- filename, line_no, str); -- error_count++; -- return NULL; -+ report_error(filename, line_no, -+ "syntax error around \"%s\"", str); -+ goto err; - } - } -- fun.num_params = i; -- fun_p = malloc(sizeof(Function)); -- if (!fun_p) { -- perror("ltrace: malloc"); -- exit(1); -+ -+ if (extra_param != NULL) { -+ assert(fun->num_params < allocd); -+ memcpy(&fun->params[fun->num_params++], extra_param, -+ sizeof(*extra_param)); - } -- memcpy(fun_p, &fun, sizeof(Function)); -- return fun_p; -+ -+ return fun; -+} -+ -+void -+init_global_config(void) -+{ -+ struct arg_type_info *info = malloc(2 * sizeof(*info)); -+ if (info == NULL) -+ error(1, errno, "malloc in init_global_config"); -+ -+ memset(info, 0, 2 * sizeof(*info)); -+ info[0].type = ARGTYPE_POINTER; -+ info[0].u.ptr_info.info = &info[1]; -+ info[1].type = ARGTYPE_VOID; -+ -+ insert_typedef(strdup("addr"), info, 0); -+ insert_typedef(strdup("file"), info, 1); - } - - void -@@ -667,7 +1131,6 @@ read_config_file(char *file) { - while (fgets(buf, 1024, stream)) { - Function *tmp; - -- error_count = 0; - tmp = process_line(buf); - - if (tmp) { -diff --git a/read_config_file.h b/read_config_file.h -index 8000b1c..06215ab 100644 ---- a/read_config_file.h -+++ b/read_config_file.h -@@ -1 +1,2 @@ - extern void read_config_file(char *); -+extern void init_global_config(void); -diff --git a/sysdeps/README b/sysdeps/README -index 0a37909..db51c9e 100644 ---- a/sysdeps/README -+++ b/sysdeps/README -@@ -7,27 +7,5 @@ first target, and must remove "sysdep.o" in this dir. - Files "sysdep.h", "signalent.h" and "syscallent.h" must be present - inside the directory after invoking the first target of the Makefile. - ------------- --"sysdep.o" must export the following functions: -- --Event * next_event(void); --void continue_after_breakpoint(Process * proc, Breakpoint * sbp, int delete_it); --void continue_after_signal(pid_t pid, int signum); --void continue_enabling_breakpoint(pid_t pid, Breakpoint * sbp); --void continue_process(pid_t pid); --void enable_breakpoint(pid_t pid, Breakpoint * sbp); --void disable_breakpoint(pid_t pid, Breakpoint * sbp); --int fork_p(int sysnum); --int exec_p(int sysnum); --int syscall_p(Process * proc, int status, int * sysnum); --void * get_instruction_pointer(pid_t pid); --void * get_stack_pointer(pid_t pid); --void * get_return_addr(pid_t pid, void * stack_pointer); --long gimme_arg(enum tof type, Process * proc, arg_type_info*); --int umovestr(Process * proc, void * addr, int len, void * laddr); --int umovelong(Process * proc, void * addr, long * result); --char * pid2name(pid_t pid); --void trace_me(void); --int trace_pid(pid_t pid); --void untrace_pid(pid_t pid); --void linkmap_init(Process *, struct ltelf *); -+See the file "backend.h" for description of backend interfaces, which -+have to be provided by "sysdep.o". -diff --git a/sysdeps/linux-gnu/Makefile.am b/sysdeps/linux-gnu/Makefile.am -index e6fd7ef..80e6594 100644 ---- a/sysdeps/linux-gnu/Makefile.am -+++ b/sysdeps/linux-gnu/Makefile.am -@@ -1,14 +1,13 @@ - DIST_SUBDIRS = \ - alpha \ - arm \ -- i386 \ - ia64 \ - m68k \ - mipsel \ - ppc \ - s390 \ - sparc \ -- x86_64 -+ x86 - - SUBDIRS = \ - $(HOST_CPU) -@@ -29,7 +28,8 @@ noinst_HEADERS = \ - arch_syscallent.h \ - signalent1.h \ - syscallent1.h \ -- trace.h -+ trace.h \ -+ events.h - - EXTRA_DIST = \ - arch_mksyscallent \ -diff --git a/sysdeps/linux-gnu/arm/arch.h b/sysdeps/linux-gnu/arm/arch.h -index d50e439..291443a 100644 ---- a/sysdeps/linux-gnu/arm/arch.h -+++ b/sysdeps/linux-gnu/arm/arch.h -@@ -1,3 +1,23 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 1998,2004,2008 Juan Cespedes -+ * -+ * 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 -+ */ -+ - #define ARCH_HAVE_ENABLE_BREAKPOINT 1 - #define ARCH_HAVE_DISABLE_BREAKPOINT 1 - -diff --git a/sysdeps/linux-gnu/arm/trace.c b/sysdeps/linux-gnu/arm/trace.c -index 768e1a8..4e4dd25 100644 ---- a/sysdeps/linux-gnu/arm/trace.c -+++ b/sysdeps/linux-gnu/arm/trace.c -@@ -82,7 +82,8 @@ syscall_p(Process *proc, int status, int *sysnum) { - } - - long --gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { -+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info) -+{ - proc_archdep *a = (proc_archdep *) proc->arch_ptr; - - if (arg_num == -1) { /* return value */ -@@ -123,14 +124,3 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { - - return 0; - } -- --void --save_register_args(enum tof type, Process *proc) { -- proc_archdep *a = (proc_archdep *) proc->arch_ptr; -- if (a->valid) { -- if (type == LT_TOF_FUNCTION) -- memcpy(a->func_arg, a->regs.uregs, sizeof(a->func_arg)); -- else -- memcpy(a->sysc_arg, a->regs.uregs, sizeof(a->sysc_arg)); -- } --} -diff --git a/sysdeps/linux-gnu/breakpoint.c b/sysdeps/linux-gnu/breakpoint.c -index e05e730..40a8436 100644 ---- a/sysdeps/linux-gnu/breakpoint.c -+++ b/sysdeps/linux-gnu/breakpoint.c -@@ -1,3 +1,25 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2006 Ian Wienand -+ * Copyright (C) 2002,2008,2009 Juan Cespedes -+ * -+ * 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 "config.h" - - #include -@@ -6,6 +28,8 @@ - #include - - #include "common.h" -+#include "backend.h" -+#include "arch.h" - #include "sysdep.h" - #include "breakpoint.h" - #include "proc.h" -diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c -index 91d873e..d0c1e5c 100644 ---- a/sysdeps/linux-gnu/events.c -+++ b/sysdeps/linux-gnu/events.c -@@ -1,18 +1,42 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2007,2011,2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 1998,2001,2004,2007,2008,2009 Juan Cespedes -+ * -+ * 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 "config.h" - - #define _GNU_SOURCE 1 --#include -+#include - #include - #include -+#include - #include - #include -+#include -+#include - #include --#include --#include - #include - --#include "common.h" -+#include "backend.h" - #include "breakpoint.h" -+#include "debug.h" -+#include "events.h" - #include "proc.h" - - static Event event; -@@ -35,7 +59,7 @@ enque_event(Event * event) - event->proc->pid, event->type); - Event * ne = malloc(sizeof(*ne)); - if (ne == NULL) { -- perror("event will be missed: malloc"); -+ fprintf(stderr, "event will be missed: %s\n", strerror(errno)); - return; - } - -@@ -311,3 +335,20 @@ next_event(void) - - return &event; - } -+ -+static enum ecb_status -+event_for_proc(struct Event *event, void *data) -+{ -+ if (event->proc == data) -+ return ecb_deque; -+ else -+ return ecb_cont; -+} -+ -+void -+delete_events_for(struct Process *proc) -+{ -+ struct Event *event; -+ while ((event = each_qd_event(&event_for_proc, proc)) != NULL) -+ free(event); -+} -diff --git a/sysdeps/linux-gnu/events.h b/sysdeps/linux-gnu/events.h -new file mode 100644 -index 0000000..3802aff ---- /dev/null -+++ b/sysdeps/linux-gnu/events.h -@@ -0,0 +1,41 @@ -+/* -+ * 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 SYSDEPS_LINUX_GNU_EVENTS_H -+#define SYSDEPS_LINUX_GNU_EVENTS_H -+ -+#include "forward.h" -+ -+/* Declarations for event que functions. */ -+ -+enum ecb_status { -+ ecb_cont, /* The iteration should continue. */ -+ ecb_yield, /* The iteration should stop, yielding this -+ * event. */ -+ ecb_deque, /* Like ecb_stop, but the event should be removed -+ * from the queue. */ -+}; -+ -+struct Event *each_qd_event(enum ecb_status (*cb)(struct Event *event, -+ void *data), void *data); -+void delete_events_for(struct Process * proc); -+void enque_event(struct Event *event); -+ -+#endif /* SYSDEPS_LINUX_GNU_EVENTS_H */ -diff --git a/sysdeps/linux-gnu/i386/Makefile.am b/sysdeps/linux-gnu/i386/Makefile.am -deleted file mode 100644 -index a79d2f7..0000000 ---- a/sysdeps/linux-gnu/i386/Makefile.am -+++ /dev/null -@@ -1,16 +0,0 @@ --noinst_LTLIBRARIES = \ -- ../libcpu.la -- --___libcpu_la_SOURCES = \ -- plt.c \ -- regs.c \ -- trace.c -- --noinst_HEADERS = \ -- arch.h \ -- ptrace.h \ -- signalent.h \ -- syscallent.h -- --MAINTAINERCLEANFILES = \ -- Makefile.in -diff --git a/sysdeps/linux-gnu/i386/arch.h b/sysdeps/linux-gnu/i386/arch.h -deleted file mode 100644 -index dc7383f..0000000 ---- a/sysdeps/linux-gnu/i386/arch.h -+++ /dev/null -@@ -1,7 +0,0 @@ --#define BREAKPOINT_VALUE {0xcc} --#define BREAKPOINT_LENGTH 1 --#define DECR_PC_AFTER_BREAK 1 --#define ARCH_ENDIAN_LITTLE -- --#define LT_ELFCLASS ELFCLASS32 --#define LT_ELF_MACHINE EM_386 -diff --git a/sysdeps/linux-gnu/i386/plt.c b/sysdeps/linux-gnu/i386/plt.c -deleted file mode 100644 -index daaf15a..0000000 ---- a/sysdeps/linux-gnu/i386/plt.c -+++ /dev/null -@@ -1,16 +0,0 @@ --#include --#include "proc.h" --#include "library.h" --#include "ltrace-elf.h" -- --GElf_Addr --arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) --{ -- return lte->plt_addr + (ndx + 1) * 16; --} -- --void * --sym2addr(struct Process *proc, struct library_symbol *sym) --{ -- return sym->enter_addr; --} -diff --git a/sysdeps/linux-gnu/i386/ptrace.h b/sysdeps/linux-gnu/i386/ptrace.h -deleted file mode 100644 -index c3cbcb6..0000000 ---- a/sysdeps/linux-gnu/i386/ptrace.h -+++ /dev/null -@@ -1 +0,0 @@ --#include -diff --git a/sysdeps/linux-gnu/i386/regs.c b/sysdeps/linux-gnu/i386/regs.c -deleted file mode 100644 -index a1584ac..0000000 ---- a/sysdeps/linux-gnu/i386/regs.c -+++ /dev/null -@@ -1,40 +0,0 @@ --#include "config.h" -- --#include --#include --#include -- --#include "proc.h" -- --#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) --# define PTRACE_PEEKUSER PTRACE_PEEKUSR --#endif -- --#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) --# define PTRACE_POKEUSER PTRACE_POKEUSR --#endif -- --void * --get_instruction_pointer(Process *proc) { -- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EIP, 0); --} -- --void --set_instruction_pointer(Process *proc, void *addr) { -- ptrace(PTRACE_POKEUSER, proc->pid, 4 * EIP, (long)addr); --} -- --void * --get_stack_pointer(Process *proc) { -- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 4 * UESP, 0); --} -- --void * --get_return_addr(Process *proc, void *stack_pointer) { -- return (void *)ptrace(PTRACE_PEEKTEXT, proc->pid, stack_pointer, 0); --} -- --void --set_return_addr(Process *proc, void *addr) { -- ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, (long)addr); --} -diff --git a/sysdeps/linux-gnu/i386/signalent.h b/sysdeps/linux-gnu/i386/signalent.h -deleted file mode 100644 -index 5395f82..0000000 ---- a/sysdeps/linux-gnu/i386/signalent.h -+++ /dev/null -@@ -1,32 +0,0 @@ -- "SIG_0", /* 0 */ -- "SIGHUP", /* 1 */ -- "SIGINT", /* 2 */ -- "SIGQUIT", /* 3 */ -- "SIGILL", /* 4 */ -- "SIGTRAP", /* 5 */ -- "SIGABRT", /* 6 */ -- "SIGBUS", /* 7 */ -- "SIGFPE", /* 8 */ -- "SIGKILL", /* 9 */ -- "SIGUSR1", /* 10 */ -- "SIGSEGV", /* 11 */ -- "SIGUSR2", /* 12 */ -- "SIGPIPE", /* 13 */ -- "SIGALRM", /* 14 */ -- "SIGTERM", /* 15 */ -- "SIGSTKFLT", /* 16 */ -- "SIGCHLD", /* 17 */ -- "SIGCONT", /* 18 */ -- "SIGSTOP", /* 19 */ -- "SIGTSTP", /* 20 */ -- "SIGTTIN", /* 21 */ -- "SIGTTOU", /* 22 */ -- "SIGURG", /* 23 */ -- "SIGXCPU", /* 24 */ -- "SIGXFSZ", /* 25 */ -- "SIGVTALRM", /* 26 */ -- "SIGPROF", /* 27 */ -- "SIGWINCH", /* 28 */ -- "SIGIO", /* 29 */ -- "SIGPWR", /* 30 */ -- "SIGSYS", /* 31 */ -diff --git a/sysdeps/linux-gnu/i386/syscallent.h b/sysdeps/linux-gnu/i386/syscallent.h -deleted file mode 100644 -index 8f4c887..0000000 ---- a/sysdeps/linux-gnu/i386/syscallent.h -+++ /dev/null -@@ -1,317 +0,0 @@ -- "restart_syscall", /* 0 */ -- "exit", /* 1 */ -- "fork", /* 2 */ -- "read", /* 3 */ -- "write", /* 4 */ -- "open", /* 5 */ -- "close", /* 6 */ -- "waitpid", /* 7 */ -- "creat", /* 8 */ -- "link", /* 9 */ -- "unlink", /* 10 */ -- "execve", /* 11 */ -- "chdir", /* 12 */ -- "time", /* 13 */ -- "mknod", /* 14 */ -- "chmod", /* 15 */ -- "lchown", /* 16 */ -- "break", /* 17 */ -- "oldstat", /* 18 */ -- "lseek", /* 19 */ -- "getpid", /* 20 */ -- "mount", /* 21 */ -- "umount", /* 22 */ -- "setuid", /* 23 */ -- "getuid", /* 24 */ -- "stime", /* 25 */ -- "ptrace", /* 26 */ -- "alarm", /* 27 */ -- "oldfstat", /* 28 */ -- "pause", /* 29 */ -- "utime", /* 30 */ -- "stty", /* 31 */ -- "gtty", /* 32 */ -- "access", /* 33 */ -- "nice", /* 34 */ -- "ftime", /* 35 */ -- "sync", /* 36 */ -- "kill", /* 37 */ -- "rename", /* 38 */ -- "mkdir", /* 39 */ -- "rmdir", /* 40 */ -- "dup", /* 41 */ -- "pipe", /* 42 */ -- "times", /* 43 */ -- "prof", /* 44 */ -- "brk", /* 45 */ -- "setgid", /* 46 */ -- "getgid", /* 47 */ -- "signal", /* 48 */ -- "geteuid", /* 49 */ -- "getegid", /* 50 */ -- "acct", /* 51 */ -- "umount2", /* 52 */ -- "lock", /* 53 */ -- "ioctl", /* 54 */ -- "fcntl", /* 55 */ -- "mpx", /* 56 */ -- "setpgid", /* 57 */ -- "ulimit", /* 58 */ -- "oldolduname", /* 59 */ -- "umask", /* 60 */ -- "chroot", /* 61 */ -- "ustat", /* 62 */ -- "dup2", /* 63 */ -- "getppid", /* 64 */ -- "getpgrp", /* 65 */ -- "setsid", /* 66 */ -- "sigaction", /* 67 */ -- "sgetmask", /* 68 */ -- "ssetmask", /* 69 */ -- "setreuid", /* 70 */ -- "setregid", /* 71 */ -- "sigsuspend", /* 72 */ -- "sigpending", /* 73 */ -- "sethostname", /* 74 */ -- "setrlimit", /* 75 */ -- "getrlimit", /* 76 */ -- "getrusage", /* 77 */ -- "gettimeofday", /* 78 */ -- "settimeofday", /* 79 */ -- "getgroups", /* 80 */ -- "setgroups", /* 81 */ -- "select", /* 82 */ -- "symlink", /* 83 */ -- "oldlstat", /* 84 */ -- "readlink", /* 85 */ -- "uselib", /* 86 */ -- "swapon", /* 87 */ -- "reboot", /* 88 */ -- "readdir", /* 89 */ -- "mmap", /* 90 */ -- "munmap", /* 91 */ -- "truncate", /* 92 */ -- "ftruncate", /* 93 */ -- "fchmod", /* 94 */ -- "fchown", /* 95 */ -- "getpriority", /* 96 */ -- "setpriority", /* 97 */ -- "profil", /* 98 */ -- "statfs", /* 99 */ -- "fstatfs", /* 100 */ -- "ioperm", /* 101 */ -- "socketcall", /* 102 */ -- "syslog", /* 103 */ -- "setitimer", /* 104 */ -- "getitimer", /* 105 */ -- "stat", /* 106 */ -- "lstat", /* 107 */ -- "fstat", /* 108 */ -- "olduname", /* 109 */ -- "iopl", /* 110 */ -- "vhangup", /* 111 */ -- "idle", /* 112 */ -- "vm86old", /* 113 */ -- "wait4", /* 114 */ -- "swapoff", /* 115 */ -- "sysinfo", /* 116 */ -- "ipc", /* 117 */ -- "fsync", /* 118 */ -- "sigreturn", /* 119 */ -- "clone", /* 120 */ -- "setdomainname", /* 121 */ -- "uname", /* 122 */ -- "modify_ldt", /* 123 */ -- "adjtimex", /* 124 */ -- "mprotect", /* 125 */ -- "sigprocmask", /* 126 */ -- "create_module", /* 127 */ -- "init_module", /* 128 */ -- "delete_module", /* 129 */ -- "get_kernel_syms", /* 130 */ -- "quotactl", /* 131 */ -- "getpgid", /* 132 */ -- "fchdir", /* 133 */ -- "bdflush", /* 134 */ -- "sysfs", /* 135 */ -- "personality", /* 136 */ -- "afs_syscall", /* 137 */ -- "setfsuid", /* 138 */ -- "setfsgid", /* 139 */ -- "_llseek", /* 140 */ -- "getdents", /* 141 */ -- "_newselect", /* 142 */ -- "flock", /* 143 */ -- "msync", /* 144 */ -- "readv", /* 145 */ -- "writev", /* 146 */ -- "getsid", /* 147 */ -- "fdatasync", /* 148 */ -- "_sysctl", /* 149 */ -- "mlock", /* 150 */ -- "munlock", /* 151 */ -- "mlockall", /* 152 */ -- "munlockall", /* 153 */ -- "sched_setparam", /* 154 */ -- "sched_getparam", /* 155 */ -- "sched_setscheduler", /* 156 */ -- "sched_getscheduler", /* 157 */ -- "sched_yield", /* 158 */ -- "sched_get_priority_max", /* 159 */ -- "sched_get_priority_min", /* 160 */ -- "sched_rr_get_interval", /* 161 */ -- "nanosleep", /* 162 */ -- "mremap", /* 163 */ -- "setresuid", /* 164 */ -- "getresuid", /* 165 */ -- "vm86", /* 166 */ -- "query_module", /* 167 */ -- "poll", /* 168 */ -- "nfsservctl", /* 169 */ -- "setresgid", /* 170 */ -- "getresgid", /* 171 */ -- "prctl", /* 172 */ -- "rt_sigreturn", /* 173 */ -- "rt_sigaction", /* 174 */ -- "rt_sigprocmask", /* 175 */ -- "rt_sigpending", /* 176 */ -- "rt_sigtimedwait", /* 177 */ -- "rt_sigqueueinfo", /* 178 */ -- "rt_sigsuspend", /* 179 */ -- "pread64", /* 180 */ -- "pwrite64", /* 181 */ -- "chown", /* 182 */ -- "getcwd", /* 183 */ -- "capget", /* 184 */ -- "capset", /* 185 */ -- "sigaltstack", /* 186 */ -- "sendfile", /* 187 */ -- "getpmsg", /* 188 */ -- "putpmsg", /* 189 */ -- "vfork", /* 190 */ -- "ugetrlimit", /* 191 */ -- "mmap2", /* 192 */ -- "truncate64", /* 193 */ -- "ftruncate64", /* 194 */ -- "stat64", /* 195 */ -- "lstat64", /* 196 */ -- "fstat64", /* 197 */ -- "lchown32", /* 198 */ -- "getuid32", /* 199 */ -- "getgid32", /* 200 */ -- "geteuid32", /* 201 */ -- "getegid32", /* 202 */ -- "setreuid32", /* 203 */ -- "setregid32", /* 204 */ -- "getgroups32", /* 205 */ -- "setgroups32", /* 206 */ -- "fchown32", /* 207 */ -- "setresuid32", /* 208 */ -- "getresuid32", /* 209 */ -- "setresgid32", /* 210 */ -- "getresgid32", /* 211 */ -- "chown32", /* 212 */ -- "setuid32", /* 213 */ -- "setgid32", /* 214 */ -- "setfsuid32", /* 215 */ -- "setfsgid32", /* 216 */ -- "pivot_root", /* 217 */ -- "mincore", /* 218 */ -- "madvise1", /* 219 */ -- "getdents64", /* 220 */ -- "fcntl64", /* 221 */ -- "222", /* 222 */ -- "223", /* 223 */ -- "gettid", /* 224 */ -- "readahead", /* 225 */ -- "setxattr", /* 226 */ -- "lsetxattr", /* 227 */ -- "fsetxattr", /* 228 */ -- "getxattr", /* 229 */ -- "lgetxattr", /* 230 */ -- "fgetxattr", /* 231 */ -- "listxattr", /* 232 */ -- "llistxattr", /* 233 */ -- "flistxattr", /* 234 */ -- "removexattr", /* 235 */ -- "lremovexattr", /* 236 */ -- "fremovexattr", /* 237 */ -- "tkill", /* 238 */ -- "sendfile64", /* 239 */ -- "futex", /* 240 */ -- "sched_setaffinity", /* 241 */ -- "sched_getaffinity", /* 242 */ -- "set_thread_area", /* 243 */ -- "get_thread_area", /* 244 */ -- "io_setup", /* 245 */ -- "io_destroy", /* 246 */ -- "io_getevents", /* 247 */ -- "io_submit", /* 248 */ -- "io_cancel", /* 249 */ -- "fadvise64", /* 250 */ -- "251", /* 251 */ -- "exit_group", /* 252 */ -- "lookup_dcookie", /* 253 */ -- "epoll_create", /* 254 */ -- "epoll_ctl", /* 255 */ -- "epoll_wait", /* 256 */ -- "remap_file_pages", /* 257 */ -- "set_tid_address", /* 258 */ -- "timer_create", /* 259 */ -- "260", /* 260 */ -- "261", /* 261 */ -- "262", /* 262 */ -- "263", /* 263 */ -- "264", /* 264 */ -- "265", /* 265 */ -- "266", /* 266 */ -- "267", /* 267 */ -- "statfs64", /* 268 */ -- "fstatfs64", /* 269 */ -- "tgkill", /* 270 */ -- "utimes", /* 271 */ -- "fadvise64_64", /* 272 */ -- "vserver", /* 273 */ -- "mbind", /* 274 */ -- "get_mempolicy", /* 275 */ -- "set_mempolicy", /* 276 */ -- "mq_open", /* 277 */ -- "278", /* 278 */ -- "279", /* 279 */ -- "280", /* 280 */ -- "281", /* 281 */ -- "282", /* 282 */ -- "kexec_load", /* 283 */ -- "waitid", /* 284 */ -- "285", /* 285 */ -- "add_key", /* 286 */ -- "request_key", /* 287 */ -- "keyctl", /* 288 */ -- "ioprio_set", /* 289 */ -- "ioprio_get", /* 290 */ -- "inotify_init", /* 291 */ -- "inotify_add_watch", /* 292 */ -- "inotify_rm_watch", /* 293 */ -- "migrate_pages", /* 294 */ -- "openat", /* 295 */ -- "mkdirat", /* 296 */ -- "mknodat", /* 297 */ -- "fchownat", /* 298 */ -- "futimesat", /* 299 */ -- "fstatat64", /* 300 */ -- "unlinkat", /* 301 */ -- "renameat", /* 302 */ -- "linkat", /* 303 */ -- "symlinkat", /* 304 */ -- "readlinkat", /* 305 */ -- "fchmodat", /* 306 */ -- "faccessat", /* 307 */ -- "pselect6", /* 308 */ -- "ppoll", /* 309 */ -- "unshare", /* 310 */ -- "set_robust_list", /* 311 */ -- "get_robust_list", /* 312 */ -- "splice", /* 313 */ -- "sync_file_range", /* 314 */ -- "tee", /* 315 */ -- "vmsplice", /* 316 */ -diff --git a/sysdeps/linux-gnu/i386/trace.c b/sysdeps/linux-gnu/i386/trace.c -deleted file mode 100644 -index f0c1e50..0000000 ---- a/sysdeps/linux-gnu/i386/trace.c -+++ /dev/null -@@ -1,99 +0,0 @@ --#include "config.h" -- --#include --#include --#include --#include --#include --#include --#include -- --#include "proc.h" --#include "common.h" -- --#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) --# define PTRACE_PEEKUSER PTRACE_PEEKUSR --#endif -- --#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) --# define PTRACE_POKEUSER PTRACE_POKEUSR --#endif -- --void --get_arch_dep(Process *proc) { --} -- --/* Returns 1 if syscall, 2 if sysret, 0 otherwise. -- */ --int --syscall_p(struct Process *proc, int status, int *sysnum) --{ -- if (WIFSTOPPED(status) -- && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { -- struct callstack_element *elem = NULL; -- if (proc->callstack_depth > 0) -- elem = proc->callstack + proc->callstack_depth - 1; -- -- *sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, 4 * ORIG_EAX, 0); -- if (*sysnum == -1) { -- if (errno) -- return -1; -- /* Otherwise, ORIG_EAX == -1 means that the -- * system call should not be restarted. In -- * that case rely on what we have on -- * stack. */ -- if (elem != NULL && elem->is_syscall) -- *sysnum = elem->c_un.syscall; -- } -- -- if (elem != NULL && elem->is_syscall -- && elem->c_un.syscall == *sysnum) -- return 2; -- -- if (*sysnum >= 0) -- return 1; -- } -- return 0; --} -- --long --gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { -- if (arg_num == -1) { /* return value */ -- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EAX, 0); -- } -- -- if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { -- return ptrace(PTRACE_PEEKTEXT, proc->pid, -- proc->stack_pointer + 4 * (arg_num + 1), 0); -- } else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) { --#if 0 -- switch (arg_num) { -- case 0: -- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EBX, 0); -- case 1: -- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * ECX, 0); -- case 2: -- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EDX, 0); -- case 3: -- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * ESI, 0); -- case 4: -- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EDI, 0); -- default: -- fprintf(stderr, -- "gimme_arg called with wrong arguments\n"); -- exit(2); -- } --#else -- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * arg_num, 0); --#endif -- } else { -- fprintf(stderr, "gimme_arg called with wrong arguments\n"); -- exit(1); -- } -- -- return 0; --} -- --void --save_register_args(enum tof type, Process *proc) { --} -diff --git a/sysdeps/linux-gnu/ia64/arch.h b/sysdeps/linux-gnu/ia64/arch.h -index 673047c..00bb077 100644 ---- a/sysdeps/linux-gnu/ia64/arch.h -+++ b/sysdeps/linux-gnu/ia64/arch.h -@@ -1,3 +1,24 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2006,2011 Petr Machata -+ * 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 -+ */ -+ - #define ARCH_HAVE_DISABLE_BREAKPOINT 1 - #define ARCH_HAVE_ENABLE_BREAKPOINT 1 - -diff --git a/sysdeps/linux-gnu/ia64/trace.c b/sysdeps/linux-gnu/ia64/trace.c -index 385fac1..d4813e6 100644 ---- a/sysdeps/linux-gnu/ia64/trace.c -+++ b/sysdeps/linux-gnu/ia64/trace.c -@@ -1,3 +1,25 @@ -+/* -+ * This file is part of ltrace. -+ * 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 "config.h" - - #include -@@ -13,6 +35,7 @@ - - #include "proc.h" - #include "common.h" -+#include "type.h" - - /* What we think of as a bundle, ptrace thinks of it as two unsigned - * longs */ -@@ -244,18 +267,26 @@ gimme_float_arg(enum tof type, Process *proc, int arg_num) { - exit(1); - } - -+static unsigned f_index; -+ - long --gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { -+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info) -+{ - union { - long l; - float f; - double d; - } cvt; - -- if (info->type == ARGTYPE_FLOAT) -- cvt.f = gimme_float_arg(type, proc, info->u.float_info.float_index); -- else if (info->type == ARGTYPE_DOUBLE) -- cvt.d = gimme_float_arg(type, proc, info->u.double_info.float_index); -+ 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); - -@@ -263,9 +294,5 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { - } - - void --save_register_args(enum tof type, Process *proc) { --} -- --void - get_arch_dep(Process *proc) { - } -diff --git a/sysdeps/linux-gnu/m68k/arch.h b/sysdeps/linux-gnu/m68k/arch.h -index 1790d09..9ca08ca 100644 ---- a/sysdeps/linux-gnu/m68k/arch.h -+++ b/sysdeps/linux-gnu/m68k/arch.h -@@ -1,3 +1,23 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 1998,2002,2004 Juan Cespedes -+ * -+ * 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 -+ */ -+ - #define BREAKPOINT_VALUE { 0x4e, 0x4f } - #define BREAKPOINT_LENGTH 2 - #define DECR_PC_AFTER_BREAK 2 -diff --git a/sysdeps/linux-gnu/m68k/trace.c b/sysdeps/linux-gnu/m68k/trace.c -index c63702d..4bec016 100644 ---- a/sysdeps/linux-gnu/m68k/trace.c -+++ b/sysdeps/linux-gnu/m68k/trace.c -@@ -47,7 +47,8 @@ syscall_p(Process *proc, int status, int *sysnum) { - } - - long --gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { -+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info) -+{ - if (arg_num == -1) { /* return value */ - return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_D0, 0); - } -@@ -84,7 +85,3 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { - - return 0; - } -- --void --save_register_args(enum tof type, Process *proc) { --} -diff --git a/sysdeps/linux-gnu/mipsel/arch.h b/sysdeps/linux-gnu/mipsel/arch.h -index f7e2316..9ee29fd 100644 ---- a/sysdeps/linux-gnu/mipsel/arch.h -+++ b/sysdeps/linux-gnu/mipsel/arch.h -@@ -1,3 +1,23 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2006 Eric Vaitl -+ * -+ * 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 LTRACE_MIPS_ARCH_H - #define LTRACE_MIPS_ARCH_H - -diff --git a/sysdeps/linux-gnu/mipsel/trace.c b/sysdeps/linux-gnu/mipsel/trace.c -index 4b999e4..fffaf75 100644 ---- a/sysdeps/linux-gnu/mipsel/trace.c -+++ b/sysdeps/linux-gnu/mipsel/trace.c -@@ -9,6 +9,7 @@ - #include "proc.h" - #include "common.h" - #include "mipsel.h" -+#include "type.h" - #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) - # define PTRACE_PEEKUSER PTRACE_PEEKUSR - #endif -@@ -118,7 +119,8 @@ I'm not doing any floating point support here. - - */ - long --gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { -+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info) -+{ - long ret; - long addr; - debug(2,"type %d arg %d",type,arg_num); -@@ -174,19 +176,4 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { - return 0; - } - --/** -- \param type Type of call/return -- \param proc Process to work with. -- -- Called by \c output_left(), which is called on a syscall or -- function. -- -- The other architectures stub this out, but seems to be the place to -- stash off the arguments on a call so we have them on the return. -- --*/ --void --save_register_args(enum tof type, Process *proc) { --} -- - /**@}*/ -diff --git a/sysdeps/linux-gnu/ppc/Makefile.am b/sysdeps/linux-gnu/ppc/Makefile.am -index 7392452..e302016 100644 ---- a/sysdeps/linux-gnu/ppc/Makefile.am -+++ b/sysdeps/linux-gnu/ppc/Makefile.am -@@ -4,7 +4,8 @@ noinst_LTLIBRARIES = \ - ___libcpu_la_SOURCES = \ - plt.c \ - regs.c \ -- trace.c -+ trace.c \ -+ fetch.c - - noinst_HEADERS = \ - arch.h \ -diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h -index 6e258e7..846961e 100644 ---- a/sysdeps/linux-gnu/ppc/arch.h -+++ b/sysdeps/linux-gnu/ppc/arch.h -@@ -1,3 +1,24 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2012 Petr Machata -+ * Copyright (C) 2006 Paul Gilliam -+ * Copyright (C) 2002,2004 Juan Cespedes -+ * -+ * 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 LTRACE_PPC_ARCH_H - #define LTRACE_PPC_ARCH_H - -@@ -18,6 +39,9 @@ - #define ARCH_HAVE_ADD_PLT_ENTRY - #define ARCH_HAVE_TRANSLATE_ADDRESS - #define ARCH_HAVE_DYNLINK_DONE -+#define ARCH_HAVE_FETCH_ARG -+#define ARCH_HAVE_SIZEOF -+#define ARCH_HAVE_ALIGNOF - - struct library_symbol; - -diff --git a/sysdeps/linux-gnu/ppc/fetch.c b/sysdeps/linux-gnu/ppc/fetch.c -new file mode 100644 -index 0000000..370f43e ---- /dev/null -+++ b/sysdeps/linux-gnu/ppc/fetch.c -@@ -0,0 +1,427 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 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 -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include "backend.h" -+#include "fetch.h" -+#include "type.h" -+#include "proc.h" -+#include "value.h" -+ -+static int allocate_gpr(struct fetch_context *ctx, struct Process *proc, -+ struct arg_type_info *info, struct value *valuep); -+ -+/* Floating point registers have the same width on 32-bit as well as -+ * 64-bit PPC, but presents a different API depending on -+ * whether ltrace is PPC32 or PPC64. -+ * -+ * This is PPC64 definition. The PPC32 is simply an array of 33 -+ * doubles, and doesn't contain the terminating pad. Both seem -+ * compatible enough. */ -+struct fpregs_t -+{ -+ double fpregs[32]; -+ double fpscr; -+ unsigned int _pad[2]; -+}; -+ -+typedef uint32_t gregs32_t[48]; -+typedef uint64_t gregs64_t[48]; -+ -+struct fetch_context { -+ target_address_t stack_pointer; -+ int greg; -+ int freg; -+ int ret_struct; -+ -+ union { -+ gregs32_t r32; -+ gregs64_t r64; -+ } regs; -+ struct fpregs_t fpregs; -+ -+}; -+ -+static int -+fetch_context_init(struct Process *proc, struct fetch_context *context) -+{ -+ context->greg = 3; -+ context->freg = 1; -+ -+ if (proc->e_machine == EM_PPC) -+ context->stack_pointer = proc->stack_pointer + 8; -+ else -+ context->stack_pointer = proc->stack_pointer + 112; -+ -+ /* When ltrace is 64-bit, we might use PTRACE_GETREGS to -+ * obtain 64-bit as well as 32-bit registers. But if we do it -+ * this way, 32-bit ltrace can obtain 64-bit registers. -+ * -+ * XXX this direction is not supported as of this writing, but -+ * should be eventually. */ -+ if (proc->e_machine == EM_PPC64) { -+ if (ptrace(PTRACE_GETREGS64, proc->pid, 0, -+ &context->regs.r64) < 0) -+ return -1; -+ } else { -+#ifdef __powerpc64__ -+ if (ptrace(PTRACE_GETREGS, proc->pid, 0, -+ &context->regs.r64) < 0) -+ return -1; -+ unsigned i; -+ for (i = 0; i < sizeof(context->regs.r64)/8; ++i) -+ context->regs.r32[i] = context->regs.r64[i]; -+#else -+ if (ptrace(PTRACE_GETREGS, proc->pid, 0, -+ &context->regs.r32) < 0) -+ return -1; -+#endif -+ } -+ -+ if (ptrace(PTRACE_GETFPREGS, proc->pid, 0, &context->fpregs) < 0) -+ return -1; -+ -+ 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; -+ } -+ -+ /* Aggregates or unions of any length, and character strings -+ * of length longer than 8 bytes, will be returned in a -+ * storage buffer allocated by the caller. The caller will -+ * pass the address of this buffer as a hidden first argument -+ * in r3, causing the first explicit argument to be passed in -+ * r4. */ -+ context->ret_struct = ret_info->type == ARGTYPE_STRUCT; -+ if (context->ret_struct) -+ context->greg++; -+ -+ 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; -+} -+ -+static int -+allocate_stack_slot(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; -+ -+ size_t a = type_alignof(proc, info); -+ size_t off = 0; -+ if (proc->e_machine == EM_PPC && a < 4) -+ a = 4; -+ else if (proc->e_machine == EM_PPC64 && a < 8) -+ a = 8; -+ -+ /* XXX Remove the two double casts when target_address_t -+ * becomes integral type. */ -+ uintptr_t tmp = align((uint64_t)(uintptr_t)ctx->stack_pointer, a); -+ ctx->stack_pointer = (target_address_t)tmp; -+ -+ if (valuep != NULL) { -+ valuep->where = VAL_LOC_INFERIOR; -+ valuep->u.address = ctx->stack_pointer + off; -+ } -+ -+ ctx->stack_pointer += sz; -+ -+ return 0; -+} -+ -+static uint64_t -+read_gpr(struct fetch_context *ctx, struct Process *proc, int reg_num) -+{ -+ if (proc->e_machine == EM_PPC) -+ return ctx->regs.r32[reg_num]; -+ else -+ return ctx->regs.r64[reg_num]; -+} -+ -+/* The support for little endian PowerPC is in upstream Linux and BFD, -+ * and Unix-like Solaris, which we might well support at some point, -+ * runs PowerPC in little endian as well. This code moves SZ-sized -+ * value to the beginning of W-sized BUF regardless of -+ * endian. */ -+static void -+align_small_int(unsigned char *buf, size_t w, size_t sz) -+{ -+ assert(w == 4 || w == 8); -+ union { -+ uint64_t i64; -+ uint32_t i32; -+ uint16_t i16; -+ uint8_t i8; -+ char buf[0]; -+ } u; -+ memcpy(u.buf, buf, w); -+ if (w == 4) -+ u.i64 = u.i32; -+ -+ switch (sz) { -+ case 1: -+ u.i8 = u.i64; -+ break; -+ case 2: -+ u.i16 = u.i64; -+ break; -+ case 4: -+ u.i32 = u.i64; -+ case 8: -+ break; -+ } -+ -+ memcpy(buf, u.buf, sz); -+} -+ -+static int -+allocate_gpr(struct fetch_context *ctx, struct Process *proc, -+ struct arg_type_info *info, struct value *valuep) -+{ -+ if (ctx->greg > 10) -+ return allocate_stack_slot(ctx, proc, info, valuep); -+ -+ int reg_num = ctx->greg++; -+ if (valuep == NULL) -+ return 0; -+ -+ size_t sz = type_sizeof(proc, info); -+ if (sz == (size_t)-1) -+ return -1; -+ assert(sz == 1 || sz == 2 || sz == 4 || sz == 8); -+ if (value_reserve(valuep, sz) == NULL) -+ return -1; -+ -+ union { -+ uint64_t i64; -+ unsigned char buf[0]; -+ } u; -+ -+ u.i64 = read_gpr(ctx, proc, reg_num); -+ if (proc->e_machine == EM_PPC) -+ align_small_int(u.buf, 8, sz); -+ memcpy(value_get_raw_data(valuep), u.buf, sz); -+ return 0; -+} -+ -+static int -+allocate_float(struct fetch_context *ctx, struct Process *proc, -+ struct arg_type_info *info, struct value *valuep) -+{ -+ int pool = proc->e_machine == EM_PPC64 ? 13 : 8; -+ if (ctx->freg <= pool) { -+ union { -+ double d; -+ float f; -+ char buf[0]; -+ } u = { .d = ctx->fpregs.fpregs[ctx->freg] }; -+ -+ ctx->freg++; -+ if (proc->e_machine == EM_PPC64) -+ allocate_gpr(ctx, proc, info, NULL); -+ -+ size_t sz = sizeof(double); -+ if (info->type == ARGTYPE_FLOAT) { -+ sz = sizeof(float); -+ u.f = (float)u.d; -+ } -+ -+ if (value_reserve(valuep, sz) == NULL) -+ return -1; -+ -+ memcpy(value_get_raw_data(valuep), u.buf, sz); -+ return 0; -+ } -+ return allocate_stack_slot(ctx, proc, info, valuep); -+} -+ -+static int -+allocate_argument(struct fetch_context *ctx, struct Process *proc, -+ struct arg_type_info *info, struct value *valuep) -+{ -+ /* Floating point types and void are handled specially. */ -+ switch (info->type) { -+ case ARGTYPE_VOID: -+ value_set_word(valuep, 0); -+ return 0; -+ -+ case ARGTYPE_FLOAT: -+ case ARGTYPE_DOUBLE: -+ return allocate_float(ctx, proc, info, valuep); -+ -+ case ARGTYPE_STRUCT: -+ if (proc->e_machine == EM_PPC) { -+ if (value_pass_by_reference(valuep) < 0) -+ return -1; -+ } else { -+ /* PPC64: Fixed size aggregates and unions passed by -+ * value are mapped to as many doublewords of the -+ * parameter save area as the value uses in memory. -+ * [...] The first eight doublewords mapped to the -+ * parameter save area correspond to the registers r3 -+ * through r10. */ -+ } -+ /* 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: -+ break; -+ -+ case ARGTYPE_ARRAY: -+ /* Arrays decay into pointers. XXX Fortran? */ -+ assert(info->type != ARGTYPE_ARRAY); -+ abort(); -+ } -+ -+ unsigned width = proc->e_machine == EM_PPC64 ? 8 : 4; -+ -+ /* For other cases (integral types and aggregates), read the -+ * eightbytes comprising the data. */ -+ size_t sz = type_sizeof(proc, valuep->type); -+ if (sz == (size_t)-1) -+ return -1; -+ size_t slots = (sz + width - 1) / width; /* Round up. */ -+ unsigned char *buf = value_reserve(valuep, slots * width); -+ if (buf == NULL) -+ return -1; -+ struct arg_type_info *long_info = type_get_simple(ARGTYPE_LONG); -+ -+ unsigned char *ptr = buf; -+ while (slots-- > 0) { -+ struct value val; -+ value_init(&val, proc, NULL, long_info, 0); -+ int rc = allocate_gpr(ctx, proc, long_info, &val); -+ if (rc >= 0) { -+ memcpy(ptr, value_get_data(&val, NULL), width); -+ ptr += width; -+ } -+ value_destroy(&val); -+ if (rc < 0) -+ return rc; -+ } -+ -+ /* Small values need post-processing. */ -+ if (sz < width) { -+ switch (info->type) { -+ case ARGTYPE_LONG: -+ case ARGTYPE_ULONG: -+ case ARGTYPE_POINTER: -+ case ARGTYPE_DOUBLE: -+ case ARGTYPE_VOID: -+ abort(); -+ -+ /* Simple integer types (char, short, int, long, enum) -+ * are mapped to a single doubleword. Values shorter -+ * than a doubleword are sign or zero extended as -+ * necessary. */ -+ case ARGTYPE_CHAR: -+ case ARGTYPE_SHORT: -+ case ARGTYPE_INT: -+ case ARGTYPE_USHORT: -+ case ARGTYPE_UINT: -+ align_small_int(buf, width, sz); -+ break; -+ -+ /* Single precision floating point values are mapped -+ * to the second word in a single doubleword. -+ * -+ * An aggregate or union smaller than one doubleword -+ * in size is padded so that it appears in the least -+ * significant bits of the doubleword. */ -+ case ARGTYPE_FLOAT: -+ case ARGTYPE_ARRAY: -+ case ARGTYPE_STRUCT: -+ memmove(buf, buf + width - sz, sz); -+ break; -+ } -+ } -+ -+ 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) -+{ -+ return allocate_argument(ctx, proc, info, valuep); -+} -+ -+int -+arch_fetch_retval(struct fetch_context *ctx, enum tof type, -+ struct Process *proc, struct arg_type_info *info, -+ struct value *valuep) -+{ -+ if (ctx->ret_struct) { -+ assert(info->type == ARGTYPE_STRUCT); -+ -+ uint64_t addr = read_gpr(ctx, proc, 3); -+ value_init(valuep, proc, NULL, info, 0); -+ -+ valuep->where = VAL_LOC_INFERIOR; -+ /* XXX Remove the double cast when target_address_t -+ * becomes integral type. */ -+ valuep->u.address = (target_address_t)(uintptr_t)addr; -+ return 0; -+ } -+ -+ if (fetch_context_init(proc, ctx) < 0) -+ return -1; -+ return allocate_argument(ctx, proc, info, valuep); -+} -+ -+void -+arch_fetch_arg_done(struct fetch_context *context) -+{ -+ free(context); -+} -diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c -index 9717738..944bd6a 100644 ---- a/sysdeps/linux-gnu/ppc/plt.c -+++ b/sysdeps/linux-gnu/ppc/plt.c -@@ -11,6 +11,7 @@ - #include "library.h" - #include "breakpoint.h" - #include "linux-gnu/trace.h" -+#include "backend.h" - - /* There are two PLT types on 32-bit PPC: old-style, BSS PLT, and - * new-style "secure" PLT. We can tell one from the other by the -diff --git a/sysdeps/linux-gnu/ppc/ptrace.h b/sysdeps/linux-gnu/ppc/ptrace.h -index c587f40..c3cbcb6 100644 ---- a/sysdeps/linux-gnu/ppc/ptrace.h -+++ b/sysdeps/linux-gnu/ppc/ptrace.h -@@ -1,16 +1 @@ - #include --#include -- --#ifdef __powerpc64__ --#define GET_FPREG(RS, R) ((RS)[R]) --#else --#define GET_FPREG(RS, R) ((RS).fpregs[R]) --#endif -- --typedef struct { -- int valid; -- gregset_t regs; -- fpregset_t fpregs; -- gregset_t regs_copy; -- fpregset_t fpregs_copy; --} proc_archdep; -diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c -index 742785a..0b734e4 100644 ---- a/sysdeps/linux-gnu/ppc/trace.c -+++ b/sysdeps/linux-gnu/ppc/trace.c -@@ -1,18 +1,41 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2010,2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2011 Andreas Schwab -+ * Copyright (C) 2002,2004,2008,2009 Juan Cespedes -+ * Copyright (C) 2008 Luis Machado, IBM Corporation -+ * 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 "config.h" - --#include --#include --#include --#include --#include -+#include - #include - #include -+#include - #include - --#include "proc.h" -+#include "backend.h" -+#include "breakpoint.h" - #include "common.h" -+#include "proc.h" - #include "ptrace.h" --#include "breakpoint.h" -+#include "type.h" - - #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) - # define PTRACE_PEEKUSER PTRACE_PEEKUSR -@@ -24,23 +47,13 @@ - - void - get_arch_dep(Process *proc) { -- if (proc->arch_ptr == NULL) { -- proc->arch_ptr = malloc(sizeof(proc_archdep)); - #ifdef __powerpc64__ -- proc->mask_32bit = (proc->e_machine == EM_PPC); -+ proc->mask_32bit = (proc->e_machine == EM_PPC); - #endif -- } -- -- proc_archdep *a = (proc_archdep *) (proc->arch_ptr); -- a->valid = (ptrace(PTRACE_GETREGS, proc->pid, 0, &a->regs) >= 0) -- && (ptrace(PTRACE_GETFPREGS, proc->pid, 0, &a->fpregs) >= 0); - } - - #define SYSCALL_INSN 0x44000002 - --unsigned int greg = 3; --unsigned int freg = 1; -- - /* Returns 1 if syscall, 2 if sysret, 0 otherwise. */ - int - syscall_p(Process *proc, int status, int *sysnum) { -@@ -66,111 +79,6 @@ syscall_p(Process *proc, int status, int *sysnum) { - return 0; - } - --static long --gimme_arg_regset(enum tof type, Process *proc, int arg_num, arg_type_info *info, -- gregset_t *regs, fpregset_t *fpregs) --{ -- union { long val; float fval; double dval; } cvt; -- -- if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) { -- if (freg <= 13 || (proc->mask_32bit && freg <= 8)) { -- double val = GET_FPREG(*fpregs, freg); -- -- if (info->type == ARGTYPE_FLOAT) -- cvt.fval = val; -- else -- cvt.dval = val; -- -- freg++; -- greg++; -- -- return cvt.val; -- } -- } -- else if (greg <= 10) -- return (*regs)[greg++]; -- else { --#ifdef __powerpc64__ -- if (proc->mask_32bit) -- return ptrace (PTRACE_PEEKDATA, proc->pid, -- proc->stack_pointer + 8 + -- sizeof (int) * (arg_num - 8), 0) >> 32; -- else -- return ptrace (PTRACE_PEEKDATA, proc->pid, -- proc->stack_pointer + 112 + -- sizeof (long) * (arg_num - 8), 0); --#else -- return ptrace (PTRACE_PEEKDATA, proc->pid, -- proc->stack_pointer + 8 + -- sizeof (long) * (arg_num - 8), 0); --#endif -- } -- -- return 0; --} -- --static long --gimme_retval(Process *proc, int arg_num, arg_type_info *info, -- gregset_t *regs, fpregset_t *fpregs) --{ -- union { long val; float fval; double dval; } cvt; -- if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) { -- double val = GET_FPREG(*fpregs, 1); -- -- if (info->type == ARGTYPE_FLOAT) -- cvt.fval = val; -- else -- cvt.dval = val; -- -- return cvt.val; -- } -- else -- return (*regs)[3]; --} -- --/* Grab functions arguments based on the PPC64 ABI. */ --long --gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) --{ -- proc_archdep *arch = (proc_archdep *)proc->arch_ptr; -- if (arch == NULL || !arch->valid) -- return -1; -- -- /* Check if we're entering a new function call to list parameters. If -- so, initialize the register control variables to keep track of where -- the parameters were stored. */ -- if ((type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) -- && arg_num == 0) { -- /* Initialize the set of registrers for parameter passing. */ -- greg = 3; -- freg = 1; -- } -- -- -- if (type == LT_TOF_FUNCTIONR) { -- if (arg_num == -1) -- return gimme_retval(proc, arg_num, info, -- &arch->regs, &arch->fpregs); -- else -- return gimme_arg_regset(type, proc, arg_num, info, -- &arch->regs_copy, -- &arch->fpregs_copy); -- } -- else -- return gimme_arg_regset(type, proc, arg_num, info, -- &arch->regs, &arch->fpregs); --} -- --void --save_register_args(enum tof type, Process *proc) { -- proc_archdep *arch = (proc_archdep *)proc->arch_ptr; -- if (arch == NULL || !arch->valid) -- return; -- -- memcpy(&arch->regs_copy, &arch->regs, sizeof(arch->regs)); -- memcpy(&arch->fpregs_copy, &arch->fpregs, sizeof(arch->fpregs)); --} -- - /* The atomic skip code is mostly taken from GDB. */ - - /* Instruction masks used during single-stepping of atomic -@@ -309,3 +188,77 @@ arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, - ptrace(PTRACE_CONT, proc->pid, 0, 0); - return 0; - } -+ -+size_t -+arch_type_sizeof(struct Process *proc, struct arg_type_info *info) -+{ -+ if (proc == NULL) -+ return (size_t)-2; -+ -+ switch (info->type) { -+ case ARGTYPE_VOID: -+ return 0; -+ -+ case ARGTYPE_CHAR: -+ return 1; -+ -+ case ARGTYPE_SHORT: -+ case ARGTYPE_USHORT: -+ return 2; -+ -+ case ARGTYPE_INT: -+ case ARGTYPE_UINT: -+ return 4; -+ -+ case ARGTYPE_LONG: -+ case ARGTYPE_ULONG: -+ case ARGTYPE_POINTER: -+ return proc->e_machine == EM_PPC64 ? 8 : 4; -+ -+ case ARGTYPE_FLOAT: -+ return 4; -+ case ARGTYPE_DOUBLE: -+ return 8; -+ -+ case ARGTYPE_ARRAY: -+ case ARGTYPE_STRUCT: -+ /* Use default value. */ -+ return (size_t)-2; -+ } -+ assert(info->type != info->type); -+ abort(); -+} -+ -+size_t -+arch_type_alignof(struct Process *proc, struct arg_type_info *info) -+{ -+ if (proc == NULL) -+ return (size_t)-2; -+ -+ switch (info->type) { -+ case ARGTYPE_VOID: -+ assert(info->type != ARGTYPE_VOID); -+ break; -+ -+ 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_FLOAT: -+ case ARGTYPE_DOUBLE: -+ /* On both PPC and PPC64, fundamental types have the -+ * same alignment as size. */ -+ return arch_type_sizeof(proc, info); -+ -+ case ARGTYPE_ARRAY: -+ case ARGTYPE_STRUCT: -+ /* Use default value. */ -+ return (size_t)-2; -+ } -+ assert(info->type != info->type); -+ abort(); -+} -diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c -index c5282e4..e7556f5 100644 ---- a/sysdeps/linux-gnu/proc.c -+++ b/sysdeps/linux-gnu/proc.c -@@ -1,3 +1,26 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2010 Zachary T Welch, CodeSourcery -+ * Copyright (C) 2010 Joe Damato -+ * Copyright (C) 1998,2008,2009 Juan Cespedes -+ * -+ * 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 -+ */ -+ - #define _GNU_SOURCE /* For getline. */ - #include "config.h" - -@@ -12,13 +35,18 @@ - #include - #include - #include -+#include - #include - #include - --#include "common.h" -+#include "backend.h" - #include "breakpoint.h" --#include "proc.h" -+#include "config.h" -+#include "debug.h" -+#include "events.h" - #include "library.h" -+#include "ltrace-elf.h" -+#include "proc.h" - - /* /proc/pid doesn't exist just after the fork, and sometimes `ltrace' - * couldn't open it to find the executable. So it may be necessary to -@@ -88,7 +116,7 @@ each_line_starting(FILE *file, const char *prefix, - char * line; - while ((line = find_line_starting(file, prefix, len)) != NULL) { - enum callback_status st = (*cb)(line, prefix, data); -- free (line); -+ free(line); - if (st == CBS_STOP) - return; - } -@@ -588,3 +616,9 @@ task_kill (pid_t pid, int sig) - ret = syscall (__NR_tkill, pid, sig); - return ret; - } -+ -+void -+process_removed(struct Process *proc) -+{ -+ delete_events_for(proc); -+} -diff --git a/sysdeps/linux-gnu/s390/arch.h b/sysdeps/linux-gnu/s390/arch.h -index 5cf168c..6597355 100644 ---- a/sysdeps/linux-gnu/s390/arch.h -+++ b/sysdeps/linux-gnu/s390/arch.h -@@ -1,8 +1,23 @@ - /* --** S/390 version --** (C) Copyright 2001 IBM Poughkeepsie, IBM Corporation --*/ -+ * This file is part of ltrace. -+ * Copyright (C) 2001 IBM Poughkeepsie, IBM Corporation -+ * -+ * 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 -+ */ - - #define BREAKPOINT_VALUE { 0x00, 0x01 } - #define BREAKPOINT_LENGTH 2 - #define DECR_PC_AFTER_BREAK 2 -diff --git a/sysdeps/linux-gnu/s390/trace.c b/sysdeps/linux-gnu/s390/trace.c -index 8c08f1f..3381ccc 100644 ---- a/sysdeps/linux-gnu/s390/trace.c -+++ b/sysdeps/linux-gnu/s390/trace.c -@@ -161,7 +161,8 @@ syscall_p(Process *proc, int status, int *sysnum) { - } - - long --gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { -+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info) -+{ - long ret; - - switch (arg_num) { -@@ -200,7 +201,3 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { - #endif - return ret; - } -- --void --save_register_args(enum tof type, Process *proc) { --} -diff --git a/sysdeps/linux-gnu/sparc/arch.h b/sysdeps/linux-gnu/sparc/arch.h -index 75251b8..9685d13 100644 ---- a/sysdeps/linux-gnu/sparc/arch.h -+++ b/sysdeps/linux-gnu/sparc/arch.h -@@ -1,3 +1,23 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2004 Juan Cespedes -+ * -+ * 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 -+ */ -+ - #define BREAKPOINT_VALUE {0x91, 0xd0, 0x20, 0x01} - #define BREAKPOINT_LENGTH 4 - #define DECR_PC_AFTER_BREAK 0 -diff --git a/sysdeps/linux-gnu/sparc/trace.c b/sysdeps/linux-gnu/sparc/trace.c -index e05c4d3..83337fc 100644 ---- a/sysdeps/linux-gnu/sparc/trace.c -+++ b/sysdeps/linux-gnu/sparc/trace.c -@@ -45,7 +45,8 @@ syscall_p(Process *proc, int status, int *sysnum) { - } - - long --gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { -+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info) -+{ - proc_archdep *a = (proc_archdep *) proc->arch_ptr; - if (!a->valid) { - fprintf(stderr, "Could not get child registers\n"); -@@ -69,14 +70,3 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { - } - return 0; - } -- --void --save_register_args(enum tof type, Process *proc) { -- proc_archdep *a = (proc_archdep *) proc->arch_ptr; -- if (a->valid) { -- if (type == LT_TOF_FUNCTION) -- memcpy(a->func_arg, &a->regs.u_regs[UREG_G7], sizeof(a->func_arg)); -- else -- memcpy(a->sysc_arg, &a->regs.u_regs[UREG_G7], sizeof(a->sysc_arg)); -- } --} -diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c -index bd5d826..cef8e3d 100644 ---- a/sysdeps/linux-gnu/trace.c -+++ b/sysdeps/linux-gnu/trace.c -@@ -1,3 +1,26 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2007,2011,2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2010 Joe Damato -+ * Copyright (C) 1998,2002,2003,2004,2008,2009 Juan Cespedes -+ * 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 -@@ -14,11 +37,15 @@ - #include - #include - --#include "common.h" -+#include "linux-gnu/trace.h" -+#include "backend.h" --#include "config.h" - #include "breakpoint.h" -+#include "debug.h" -+#include "events.h" -+#include "options.h" - #include "proc.h" --#include "linux-gnu/trace.h" -+#include "ptrace.h" -+#include "type.h" - - #include "config.h" - #ifdef HAVE_LIBSELINUX -@@ -47,44 +74,6 @@ - - #endif /* PTRACE_EVENT_FORK */ - --#ifdef ARCH_HAVE_UMOVELONG --extern int arch_umovelong (Process *, void *, long *, arg_type_info *); --int --umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { -- return arch_umovelong (proc, addr, result, info); --} --#else --/* Read a single long from the process's memory address 'addr' */ --int --umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { -- long pointed_to; -- -- errno = 0; -- pointed_to = ptrace (PTRACE_PEEKTEXT, proc->pid, addr, 0); -- if (pointed_to == -1 && errno) -- return -errno; -- --#if SIZEOF_LONG == 8 -- if (info != NULL -- && (info->type == ARGTYPE_INT -- || (proc->mask_32bit -- && (info->type == ARGTYPE_POINTER -- || info->type == ARGTYPE_STRING)))) { --#if defined (ARCH_ENDIAN_LITTLE) -- pointed_to &= 0x00000000ffffffffUL; --#elif defined (ARCH_ENDIAN_BIG) -- pointed_to = (long)(((unsigned long)pointed_to) >> 32); --#else --# error arch.h has to define endianness --#endif -- } --#endif -- -- *result = pointed_to; -- return 0; --} --#endif -- - void - trace_fail_warning(pid_t pid) - { -@@ -366,15 +363,15 @@ process_stopping_done(struct process_stopping_handler * self, Process * leader) - { - debug(DEBUG_PROCESS, "process stopping done %d", - self->task_enabling_breakpoint->pid); -- size_t i; -+ - if (!self->exiting) { -+ size_t i; - for (i = 0; i < self->pids.count; ++i) - if (self->pids.tasks[i].pid != 0 - && (self->pids.tasks[i].delivered - || self->pids.tasks[i].sysret)) - continue_process(self->pids.tasks[i].pid); - continue_process(self->task_enabling_breakpoint->pid); -- destroy_event_handler(leader); - } - - if (self->exiting) { -@@ -390,6 +387,7 @@ process_stopping_done(struct process_stopping_handler * self, Process * leader) - case CBS_CONT: - goto ugly_workaround; - } -+ destroy_event_handler(leader); - } - } - -@@ -678,8 +676,8 @@ singlestep_error(struct process_stopping_handler *self) - struct Process *teb = self->task_enabling_breakpoint; - struct breakpoint *sbp = self->breakpoint_being_enabled; - fprintf(stderr, "%d couldn't continue when handling %s (%p) at %p\n", -- teb->pid, sbp->libsym != NULL ? sbp->libsym->name : NULL, -- sbp->addr, get_instruction_pointer(teb)); -+ teb->pid, breakpoint_name(sbp), sbp->addr, -+ get_instruction_pointer(teb)); - delete_breakpoint(teb->leader, sbp->addr); - } - -@@ -1233,32 +1231,3 @@ umovebytes(Process *proc, void *addr, void *laddr, size_t len) { - - return bytes_read; - } -- --/* Read a series of bytes starting at the process's memory address -- 'addr' and continuing until a NUL ('\0') is seen or 'len' bytes -- have been read. --*/ --int --umovestr(Process *proc, void *addr, int len, void *laddr) { -- union { -- long a; -- char c[sizeof(long)]; -- } a; -- unsigned i; -- int offset = 0; -- -- while (offset < len) { -- a.a = ptrace(PTRACE_PEEKTEXT, proc->pid, addr + offset, 0); -- for (i = 0; i < sizeof(long); i++) { -- if (a.c[i] && offset + (signed)i < len) { -- *(char *)(laddr + offset + i) = a.c[i]; -- } else { -- *(char *)(laddr + offset + i) = '\0'; -- return 0; -- } -- } -- offset += sizeof(long); -- } -- *(char *)(laddr + offset) = '\0'; -- return 0; --} -diff --git a/sysdeps/linux-gnu/trace.h b/sysdeps/linux-gnu/trace.h -index 0f40709..88ac33d 100644 ---- a/sysdeps/linux-gnu/trace.h -+++ b/sysdeps/linux-gnu/trace.h -@@ -21,6 +21,8 @@ - #ifndef _LTRACE_LINUX_TRACE_H_ - #define _LTRACE_LINUX_TRACE_H_ - -+#include "proc.h" -+ - /* This publishes some Linux-specific data structures used for process - * handling. */ - -diff --git a/sysdeps/linux-gnu/x86/Makefile.am b/sysdeps/linux-gnu/x86/Makefile.am -new file mode 100644 -index 0000000..d5eb6e7 ---- /dev/null -+++ b/sysdeps/linux-gnu/x86/Makefile.am -@@ -0,0 +1,19 @@ -+noinst_LTLIBRARIES = \ -+ ../libcpu.la -+ -+___libcpu_la_SOURCES = \ -+ plt.c \ -+ regs.c \ -+ trace.c \ -+ fetch.c -+ -+noinst_HEADERS = \ -+ arch.h \ -+ ptrace.h \ -+ signalent.h \ -+ signalent1.h \ -+ syscallent.h \ -+ syscallent1.h -+ -+MAINTAINERCLEANFILES = \ -+ Makefile.in -diff --git a/sysdeps/linux-gnu/x86/arch.h b/sysdeps/linux-gnu/x86/arch.h -new file mode 100644 -index 0000000..77a09d7 ---- /dev/null -+++ b/sysdeps/linux-gnu/x86/arch.h -@@ -0,0 +1,40 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011 Petr Machata -+ * Copyright (C) 2006 Ian Wienand -+ * Copyright (C) 2004 Juan Cespedes -+ * -+ * 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 -+ */ -+ -+#define BREAKPOINT_VALUE {0xcc} -+#define BREAKPOINT_LENGTH 1 -+#define DECR_PC_AFTER_BREAK 1 -+#define ARCH_HAVE_FETCH_ARG -+#define ARCH_HAVE_SIZEOF -+#define ARCH_HAVE_ALIGNOF -+#define ARCH_ENDIAN_LITTLE -+ -+#ifdef __x86_64__ -+#define LT_ELFCLASS ELFCLASS64 -+#define LT_ELF_MACHINE EM_X86_64 -+#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/sysdeps/linux-gnu/x86/fetch.c b/sysdeps/linux-gnu/x86/fetch.c -new file mode 100644 -index 0000000..64f57f3 ---- /dev/null -+++ b/sysdeps/linux-gnu/x86/fetch.c -@@ -0,0 +1,809 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011,2012 Petr Machata -+ * -+ * 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 "config.h" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "backend.h" -+#include "expr.h" -+#include "fetch.h" -+#include "proc.h" -+#include "ptrace.h" -+#include "type.h" -+#include "value.h" -+ -+enum arg_class { -+ CLASS_INTEGER, -+ CLASS_SSE, -+ CLASS_NO, -+ CLASS_MEMORY, -+ CLASS_X87, -+}; -+ -+enum reg_pool { -+ POOL_FUNCALL, -+ POOL_SYSCALL, -+ /* A common pool for system call and function call return is -+ * enough, the ABI is similar enough. */ -+ POOL_RETVAL, -+}; -+ -+struct fetch_context -+{ -+ struct user_regs_struct iregs; -+ struct user_fpregs_struct fpregs; -+ -+ void *stack_pointer; -+ size_t ireg; /* Used-up integer registers. */ -+ size_t freg; /* Used-up floating registers. */ -+ -+ union { -+ struct { -+ /* Storage classes for return type. We need -+ * to compute them anyway, so let's keep them -+ * around. */ -+ enum arg_class ret_classes[2]; -+ ssize_t num_ret_classes; -+ } x86_64; -+ struct { -+ struct value retval; -+ } ix86; -+ } u; -+}; -+ -+#ifndef __x86_64__ -+__attribute__((noreturn)) static void -+i386_unreachable(void) -+{ -+ abort(); -+} -+#endif -+ -+static int -+contains_unaligned_fields(struct arg_type_info *info) -+{ -+ /* XXX currently we don't support structure alignment. */ -+ return 0; -+} -+ -+static int -+has_nontrivial_ctor_dtor(struct arg_type_info *info) -+{ -+ /* XXX another unsupported aspect of type info. We might call -+ * these types "class" instead of "struct" in the config -+ * file. */ -+ return 0; -+} -+ -+static void -+copy_int_register(struct fetch_context *context, -+ struct value *valuep, unsigned long val, size_t offset) -+{ -+ if (valuep != NULL) { -+ unsigned char *buf = value_get_raw_data(valuep); -+ memcpy(buf + offset, &val, sizeof(val)); -+ } -+ context->ireg++; -+} -+ -+static void -+copy_sse_register(struct fetch_context *context, struct value *valuep, -+ int half, size_t sz, size_t offset) -+{ -+#ifdef __x86_64__ -+ union { -+ uint32_t sse[4]; -+ long halves[2]; -+ } u; -+ size_t off = 4 * context->freg++; -+ memcpy(u.sse, context->fpregs.xmm_space + off, sizeof(u.sse)); -+ -+ if (valuep != NULL) { -+ unsigned char *buf = value_get_raw_data(valuep); -+ memcpy(buf + offset, u.halves + half, sz); -+ } -+#else -+ i386_unreachable(); -+#endif -+} -+ -+static void -+allocate_stack_slot(struct fetch_context *context, -+ struct value *valuep, size_t sz, size_t offset, -+ size_t archw) -+{ -+ size_t a = type_alignof(valuep->inferior, valuep->type); -+ if (a < archw) -+ a = archw; -+ context->stack_pointer -+ = (void *)align((unsigned long)context->stack_pointer, a); -+ -+ if (valuep != NULL) { -+ valuep->where = VAL_LOC_INFERIOR; -+ valuep->u.address = context->stack_pointer; -+ } -+ context->stack_pointer += sz; -+} -+ -+static enum arg_class -+allocate_x87(struct fetch_context *context, struct value *valuep, -+ size_t sz, size_t offset, enum reg_pool pool, size_t archw) -+{ -+ /* Both i386 and x86_64 ABI only ever really use x87 registers -+ * to return values. Otherwise, the parameter is treated as -+ * if it were CLASS_MEMORY. On x86_64 x87 registers are only -+ * used for returning long double values, which we currently -+ * don't support. */ -+ -+ if (pool != POOL_RETVAL) { -+ allocate_stack_slot(context, valuep, sz, offset, archw); -+ return CLASS_MEMORY; -+ -+ } -+ -+ /* If the class is X87, the value is returned on the X87 stack -+ * in %st0 as 80-bit x87 number. -+ * -+ * If the class is X87UP, the value is returned together with -+ * the previous X87 value in %st0. -+ * -+ * If the class is COMPLEX_X87, the real part of the value is -+ * returned in %st0 and the imaginary part in %st1. */ -+ -+ if (valuep != NULL) { -+ union { -+ long double ld; -+ double d; -+ float f; -+ char buf[0]; -+ } u; -+ -+ /* The x87 floating point value is in long double -+ * format, so we need to convert in to the right type. -+ * Alternatively we might just leave it as is and -+ * smuggle the long double type into the value (via -+ * value_set_type), but for that we first need to -+ * support long double in the first place. */ -+ -+#ifdef __x86_64__ -+ unsigned int *reg; -+#else -+ long int *reg; -+#endif -+ reg = &context->fpregs.st_space[0]; -+ memcpy(&u.ld, reg, sizeof(u)); -+ if (valuep->type->type == ARGTYPE_FLOAT) -+ u.f = (float)u.ld; -+ else if (valuep->type->type == ARGTYPE_DOUBLE) -+ u.d = (double)u.ld; -+ else -+ assert(!"Unexpected floating type!"), abort(); -+ -+ unsigned char *buf = value_get_raw_data(valuep); -+ memcpy(buf + offset, u.buf, sz); -+ } -+ return CLASS_X87; -+} -+ -+static enum arg_class -+allocate_integer(struct fetch_context *context, struct value *valuep, -+ size_t sz, size_t offset, enum reg_pool pool) -+{ -+#define HANDLE(NUM, WHICH) \ -+ case NUM: \ -+ copy_int_register(context, valuep, \ -+ context->iregs.WHICH, offset); \ -+ return CLASS_INTEGER -+ -+ switch (pool) { -+ case POOL_FUNCALL: -+#ifdef __x86_64__ -+ switch (context->ireg) { -+ HANDLE(0, rdi); -+ HANDLE(1, rsi); -+ HANDLE(2, rdx); -+ HANDLE(3, rcx); -+ HANDLE(4, r8); -+ HANDLE(5, r9); -+ default: -+ allocate_stack_slot(context, valuep, sz, offset, 8); -+ return CLASS_MEMORY; -+ } -+#else -+ i386_unreachable(); -+#endif -+ -+ case POOL_SYSCALL: -+#ifdef __x86_64__ -+ switch (context->ireg) { -+ HANDLE(0, rdi); -+ HANDLE(1, rsi); -+ HANDLE(2, rdx); -+ HANDLE(3, r10); -+ HANDLE(4, r8); -+ HANDLE(5, r9); -+ default: -+ assert(!"More than six syscall arguments???"); -+ abort(); -+ } -+#else -+ i386_unreachable(); -+#endif -+ -+ case POOL_RETVAL: -+ switch (context->ireg) { -+#ifdef __x86_64__ -+ HANDLE(0, rax); -+ HANDLE(1, rdx); -+#else -+ HANDLE(0, eax); -+#endif -+ default: -+ assert(!"Too many return value classes."); -+ abort(); -+ } -+ } -+ -+ abort(); -+ -+#undef HANDLE -+} -+ -+static enum arg_class -+allocate_sse(struct fetch_context *context, struct value *valuep, -+ size_t sz, size_t offset, enum reg_pool pool) -+{ -+ size_t num_regs = 0; -+ switch (pool) { -+ case POOL_FUNCALL: -+ num_regs = 8; -+ case POOL_SYSCALL: -+ break; -+ case POOL_RETVAL: -+ num_regs = 2; -+ } -+ -+ if (context->freg >= num_regs) { -+ /* We shouldn't see overflow for RETVAL or SYSCALL -+ * pool. */ -+ assert(pool == POOL_FUNCALL); -+ allocate_stack_slot(context, valuep, sz, offset, 8); -+ return CLASS_MEMORY; -+ } else { -+ copy_sse_register(context, valuep, 0, sz, offset); -+ return CLASS_SSE; -+ } -+} -+ -+/* This allocates registers or stack space for another argument of the -+ * class CLS. */ -+static enum arg_class -+allocate_class(enum arg_class cls, struct fetch_context *context, -+ struct value *valuep, size_t sz, size_t offset, enum reg_pool pool) -+{ -+ switch (cls) { -+ case CLASS_MEMORY: -+ allocate_stack_slot(context, valuep, sz, offset, 8); -+ case CLASS_NO: -+ return cls; -+ -+ case CLASS_INTEGER: -+ return allocate_integer(context, valuep, sz, offset, pool); -+ -+ case CLASS_SSE: -+ return allocate_sse(context, valuep, sz, offset, pool); -+ -+ case CLASS_X87: -+ return allocate_x87(context, valuep, sz, offset, pool, 8); -+ } -+ abort(); -+} -+ -+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); -+ -+/* This classifies one eightbyte part of an array or struct. */ -+static ssize_t -+classify_eightbyte(struct Process *proc, struct fetch_context *context, -+ struct arg_type_info *info, struct value *valuep, -+ enum arg_class *classp, size_t start, size_t end, -+ struct arg_type_info *(*getter)(struct arg_type_info *, -+ size_t)) -+{ -+ size_t i; -+ enum arg_class cls = CLASS_NO; -+ for (i = start; i < end; ++i) { -+ enum arg_class cls2; -+ struct arg_type_info *info2 = getter(info, i); -+ size_t sz = type_sizeof(proc, info2); -+ if (sz == (size_t)-1) -+ return -1; -+ if (classify(proc, context, info2, valuep, &cls2, sz, 1) < 0) -+ return -1; -+ -+ if (cls == CLASS_NO) -+ cls = cls2; -+ else if (cls2 == CLASS_NO || cls == cls2) -+ ; -+ else if (cls == CLASS_MEMORY || cls2 == CLASS_MEMORY) -+ cls = CLASS_MEMORY; -+ else if (cls == CLASS_INTEGER || cls2 == CLASS_INTEGER) -+ cls = CLASS_INTEGER; -+ else -+ cls = CLASS_SSE; -+ } -+ -+ *classp = cls; -+ return 1; -+} -+ -+/* This classifies small arrays and structs. */ -+static ssize_t -+classify_eightbytes(struct Process *proc, struct fetch_context *context, -+ struct arg_type_info *info, struct value *valuep, -+ enum arg_class classes[], size_t elements, -+ size_t eightbytes, -+ struct arg_type_info *(*getter)(struct arg_type_info *, -+ size_t)) -+{ -+ if (eightbytes > 1) { -+ /* Where the second eightbyte starts. Number of the -+ * first element in the structure that belongs to the -+ * second eightbyte. */ -+ size_t start_2nd = 0; -+ size_t i; -+ for (i = 0; i < elements; ++i) -+ if (type_offsetof(proc, info, i) >= 8) { -+ start_2nd = i; -+ break; -+ } -+ -+ enum arg_class cls1, cls2; -+ if (classify_eightbyte(proc, context, info, valuep, &cls1, -+ 0, start_2nd, getter) < 0 -+ || classify_eightbyte(proc, context, info, valuep, &cls2, -+ start_2nd, elements, getter) < 0) -+ return -1; -+ -+ if (cls1 == CLASS_MEMORY || cls2 == CLASS_MEMORY) { -+ classes[0] = CLASS_MEMORY; -+ return 1; -+ } -+ -+ classes[0] = cls1; -+ classes[1] = cls2; -+ return 2; -+ } -+ -+ return classify_eightbyte(proc, context, info, valuep, classes, -+ 0, elements, getter); -+} -+ -+static struct arg_type_info * -+get_array_field(struct arg_type_info *info, size_t emt) -+{ -+ return info->u.array_info.elt_type; -+} -+ -+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) { -+ case ARGTYPE_VOID: -+ return 0; -+ -+ case ARGTYPE_CHAR: -+ case ARGTYPE_SHORT: -+ case ARGTYPE_USHORT: -+ case ARGTYPE_INT: -+ case ARGTYPE_UINT: -+ case ARGTYPE_LONG: -+ case ARGTYPE_ULONG: -+ -+ case ARGTYPE_POINTER: -+ /* and LONGLONG */ -+ /* CLASS_INTEGER */ -+ classes[0] = CLASS_INTEGER; -+ return 1; -+ -+ case ARGTYPE_FLOAT: -+ case ARGTYPE_DOUBLE: -+ /* and DECIMAL, and _m64 */ -+ classes[0] = CLASS_SSE; -+ return 1; -+ -+ case ARGTYPE_ARRAY: -+ /* N.B. this cannot be top-level array, those decay to -+ * pointers. Therefore, it must be inside structure -+ * that's at most 2 eightbytes long. */ -+ -+ /* Structures with flexible array members can't be -+ * passed by value. */ -+ assert(expr_is_compile_constant(info->u.array_info.length)); -+ -+ long l; -+ if (expr_eval_constant(info->u.array_info.length, &l) < 0) -+ return -1; -+ -+ return classify_eightbytes(proc, context, info, valuep, classes, -+ (size_t)l, eightbytes, -+ 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); -+ } -+ abort(); -+} -+ -+static ssize_t -+pass_by_reference(struct value *valuep, enum arg_class classes[]) -+{ -+ if (valuep != NULL && value_pass_by_reference(valuep) < 0) -+ return -1; -+ classes[0] = CLASS_INTEGER; -+ return 1; -+} -+ -+static ssize_t -+classify_argument(struct Process *proc, struct fetch_context *context, -+ struct arg_type_info *info, struct value *valuep, -+ enum arg_class classes[], size_t *sizep) -+{ -+ size_t sz = type_sizeof(proc, info); -+ if (sz == (size_t)-1) -+ return -1; -+ *sizep = sz; -+ -+ size_t eightbytes = (sz + 7) / 8; /* Round up. */ -+ -+ /* Arrays decay into pointers. */ -+ assert(info->type != ARGTYPE_ARRAY); -+ -+ if (info->type == ARGTYPE_STRUCT) { -+ if (eightbytes > 2 || contains_unaligned_fields(info)) { -+ classes[0] = CLASS_MEMORY; -+ return 1; -+ } -+ -+ if (has_nontrivial_ctor_dtor(info)) -+ return pass_by_reference(valuep, classes); -+ } -+ -+ return classify(proc, context, info, valuep, classes, sz, eightbytes); -+} -+ -+static int -+fetch_register_banks(struct Process *proc, struct fetch_context *context, -+ int floating) -+{ -+ if (ptrace(PTRACE_GETREGS, proc->pid, 0, &context->iregs) < 0) -+ return -1; -+ context->ireg = 0; -+ -+ if (floating) { -+ if (ptrace(PTRACE_GETFPREGS, proc->pid, -+ 0, &context->fpregs) < 0) -+ return -1; -+ context->freg = 0; -+ } else { -+ context->freg = -1; -+ } -+ -+ return 0; -+} -+ -+static int -+arch_fetch_arg_next_32(struct fetch_context *context, enum tof type, -+ 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; -+ -+ allocate_stack_slot(context, valuep, sz, 0, 4); -+ -+ return 0; -+} -+ -+static int -+arch_fetch_retval_32(struct fetch_context *context, enum tof type, -+ struct Process *proc, struct arg_type_info *info, -+ struct value *valuep) -+{ -+ if (fetch_register_banks(proc, context, type == LT_TOF_FUNCTIONR) < 0) -+ return -1; -+ -+ struct value *retval = &context->u.ix86.retval; -+ if (retval->type != NULL) { -+ /* Struct return value was extracted when in fetch -+ * init. */ -+ memcpy(valuep, &context->u.ix86.retval, sizeof(*valuep)); -+ return 0; -+ } -+ -+ size_t sz = type_sizeof(proc, info); -+ if (sz == (size_t)-1) -+ return -1; -+ if (value_reserve(valuep, sz) == NULL) -+ return -1; -+ -+ switch (info->type) { -+ enum arg_class cls; -+ case ARGTYPE_VOID: -+ return 0; -+ -+ case ARGTYPE_INT: -+ case ARGTYPE_UINT: -+ case ARGTYPE_LONG: -+ case ARGTYPE_ULONG: -+ case ARGTYPE_CHAR: -+ case ARGTYPE_SHORT: -+ case ARGTYPE_USHORT: -+ case ARGTYPE_POINTER: -+ cls = allocate_integer(context, valuep, sz, 0, POOL_RETVAL); -+ assert(cls == CLASS_INTEGER); -+ return 0; -+ -+ case ARGTYPE_FLOAT: -+ case ARGTYPE_DOUBLE: -+ cls = allocate_x87(context, valuep, sz, 0, POOL_RETVAL, 4); -+ assert(cls == CLASS_X87); -+ return 0; -+ -+ case ARGTYPE_ARRAY: -+ case ARGTYPE_STRUCT: /* Handled above. */ -+ assert(!"Unexpected i386 retval type!"); -+ abort(); -+ } -+ -+ abort(); -+} -+ -+static target_address_t -+fetch_stack_pointer(struct fetch_context *context) -+{ -+ target_address_t sp; -+#ifdef __x86_64__ -+ sp = (target_address_t)context->iregs.rsp; -+#else -+ sp = (target_address_t)context->iregs.esp; -+#endif -+ return sp; -+} -+ -+struct fetch_context * -+arch_fetch_arg_init_32(struct fetch_context *context, -+ enum tof type, struct Process *proc, -+ struct arg_type_info *ret_info) -+{ -+ context->stack_pointer = fetch_stack_pointer(context) + 4; -+ -+ size_t sz = type_sizeof(proc, ret_info); -+ if (sz == (size_t)-1) -+ return NULL; -+ -+ struct value *retval = &context->u.ix86.retval; -+ if (ret_info->type == ARGTYPE_STRUCT) { -+ value_init(retval, proc, NULL, ret_info, 0); -+ -+ enum arg_class dummy[2]; -+ if (pass_by_reference(retval, dummy) < 0) -+ return NULL; -+ allocate_stack_slot(context, retval, 4, 0, 4); -+ -+ } else { -+ value_init_detached(retval, NULL, NULL, 0); -+ } -+ -+ return context; -+} -+ -+struct fetch_context * -+arch_fetch_arg_init_64(struct fetch_context *ctx, enum tof type, -+ struct Process *proc, struct arg_type_info *ret_info) -+{ -+ /* The first stack slot holds a return address. */ -+ ctx->stack_pointer = fetch_stack_pointer(ctx) + 8; -+ -+ size_t size; -+ ctx->u.x86_64.num_ret_classes -+ = classify_argument(proc, ctx, ret_info, NULL, -+ ctx->u.x86_64.ret_classes, &size); -+ if (ctx->u.x86_64.num_ret_classes == -1) -+ return NULL; -+ -+ /* If the class is MEMORY, then the first argument is a hidden -+ * pointer to the allocated storage. */ -+ if (ctx->u.x86_64.num_ret_classes > 0 -+ && ctx->u.x86_64.ret_classes[0] == CLASS_MEMORY) { -+ /* MEMORY should be the sole class. */ -+ assert(ctx->u.x86_64.num_ret_classes == 1); -+ allocate_integer(ctx, NULL, size, 0, POOL_FUNCALL); -+ } -+ -+ return ctx; -+} -+ -+struct fetch_context * -+arch_fetch_arg_init(enum tof type, struct Process *proc, -+ struct arg_type_info *ret_info) -+{ -+ struct fetch_context *ctx = malloc(sizeof(*ctx)); -+ if (ctx == NULL) -+ return NULL; -+ -+ assert(type != LT_TOF_FUNCTIONR -+ && type != LT_TOF_SYSCALLR); -+ if (fetch_register_banks(proc, ctx, type == LT_TOF_FUNCTION) < 0) { -+ fail: -+ free(ctx); -+ return NULL; -+ } -+ -+ struct fetch_context *ret; -+ if (proc->e_machine == EM_386) -+ ret = arch_fetch_arg_init_32(ctx, type, proc, ret_info); -+ else -+ ret = arch_fetch_arg_init_64(ctx, type, proc, ret_info); -+ if (ret == NULL) -+ goto fail; -+ return ret; -+} -+ -+struct fetch_context * -+arch_fetch_arg_clone(struct Process *proc, struct fetch_context *context) -+{ -+ struct fetch_context *ret = malloc(sizeof(*ret)); -+ if (ret == NULL) -+ return NULL; -+ return memcpy(ret, context, sizeof(*ret)); -+} -+ -+static int -+arch_fetch_pool_arg_next(struct fetch_context *context, enum tof type, -+ struct Process *proc, struct arg_type_info *info, -+ struct value *valuep, enum reg_pool pool) -+{ -+ enum arg_class classes[2]; -+ size_t sz, sz1; -+ ssize_t i; -+ ssize_t nclasses = classify_argument(proc, context, info, valuep, -+ classes, &sz); -+ if (nclasses == -1) -+ return -1; -+ if (value_reserve(valuep, sz) == NULL) -+ return -1; -+ -+ /* If there are no registers available for any eightbyte of an -+ * argument, the whole argument is passed on the stack. If -+ * registers have already been assigned for some eightbytes of -+ * such an argument, the assignments get reverted. */ -+ struct fetch_context tmp_context = *context; -+ int revert; -+ if (nclasses == 1) { -+ revert = allocate_class(classes[0], &tmp_context, -+ valuep, sz, 0, pool) != classes[0]; -+ } else { -+ revert = 0; -+ for (i = 0; i < nclasses; ++i) { -+ sz1 = (size_t)(8 * (i + 1)) > sz ? sz - 8 * i : 8; -+ if (allocate_class(classes[i], &tmp_context, valuep, -+ sz1, 8 * i, pool) != classes[i]) -+ revert = 1; -+ } -+ } -+ -+ if (nclasses > 1 && revert) -+ allocate_class(CLASS_MEMORY, context, valuep, sz, 0, pool); -+ else -+ *context = tmp_context; /* Commit. */ -+ -+ return 0; -+} -+ -+int -+arch_fetch_fun_retval(struct fetch_context *context, enum tof type, -+ struct Process *proc, struct arg_type_info *info, -+ struct value *valuep) -+{ -+ assert(type != LT_TOF_FUNCTION -+ && type != LT_TOF_SYSCALL); -+ if (value_reserve(valuep, 8 * context->u.x86_64.num_ret_classes) == NULL -+ || fetch_register_banks(proc, context, -+ type == LT_TOF_FUNCTIONR) < 0) -+ return -1; -+ -+ if (context->u.x86_64.num_ret_classes == 1 -+ && context->u.x86_64.ret_classes[0] == CLASS_MEMORY) -+ pass_by_reference(valuep, context->u.x86_64.ret_classes); -+ -+ size_t sz = type_sizeof(proc, valuep->type); -+ if (sz == (size_t)-1) -+ return -1; -+ -+ ssize_t i; -+ size_t sz1 = context->u.x86_64.num_ret_classes == 1 ? sz : 8; -+ for (i = 0; i < context->u.x86_64.num_ret_classes; ++i) { -+ enum arg_class cls -+ = allocate_class(context->u.x86_64.ret_classes[i], -+ context, valuep, sz1, -+ 8 * i, POOL_RETVAL); -+ assert(cls == context->u.x86_64.ret_classes[i]); -+ } -+ return 0; -+} -+ -+int -+arch_fetch_arg_next(struct fetch_context *context, enum tof type, -+ struct Process *proc, struct arg_type_info *info, -+ struct value *valuep) -+{ -+ if (proc->e_machine == EM_386) -+ return arch_fetch_arg_next_32(context, type, proc, -+ info, valuep); -+ -+ switch (type) { -+ case LT_TOF_FUNCTION: -+ case LT_TOF_FUNCTIONR: -+ return arch_fetch_pool_arg_next(context, type, proc, -+ info, valuep, POOL_FUNCALL); -+ -+ case LT_TOF_SYSCALL: -+ case LT_TOF_SYSCALLR: -+ return arch_fetch_pool_arg_next(context, type, proc, -+ info, valuep, POOL_SYSCALL); -+ } -+ -+ abort(); -+} -+ -+int -+arch_fetch_retval(struct fetch_context *context, enum tof type, -+ struct Process *proc, struct arg_type_info *info, -+ struct value *valuep) -+{ -+ if (proc->e_machine == EM_386) -+ return arch_fetch_retval_32(context, type, proc, info, valuep); -+ -+ return arch_fetch_fun_retval(context, type, proc, info, valuep); -+} -+ -+void -+arch_fetch_arg_done(struct fetch_context *context) -+{ -+ if (context != NULL) -+ free(context); -+} -diff --git a/sysdeps/linux-gnu/x86/plt.c b/sysdeps/linux-gnu/x86/plt.c -new file mode 100644 -index 0000000..bb1b2b1 ---- /dev/null -+++ b/sysdeps/linux-gnu/x86/plt.c -@@ -0,0 +1,14 @@ -+#include -+#include "proc.h" -+#include "common.h" -+#include "library.h" -+ -+GElf_Addr -+arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { -+ return lte->plt_addr + (ndx + 1) * 16; -+} -+ -+void * -+sym2addr(Process *proc, struct library_symbol *sym) { -+ return sym->enter_addr; -+} -diff --git a/sysdeps/linux-gnu/x86/ptrace.h b/sysdeps/linux-gnu/x86/ptrace.h -new file mode 100644 -index 0000000..e0f5261 ---- /dev/null -+++ b/sysdeps/linux-gnu/x86/ptrace.h -@@ -0,0 +1,2 @@ -+#include -+#include -diff --git a/sysdeps/linux-gnu/x86/regs.c b/sysdeps/linux-gnu/x86/regs.c -new file mode 100644 -index 0000000..477abca ---- /dev/null -+++ b/sysdeps/linux-gnu/x86/regs.c -@@ -0,0 +1,116 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 1998,2002,2004,2008,2009 Juan Cespedes -+ * 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 "config.h" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "backend.h" -+#include "proc.h" -+ -+#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) -+# define PTRACE_PEEKUSER PTRACE_PEEKUSR -+#endif -+ -+#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) -+# define PTRACE_POKEUSER PTRACE_POKEUSR -+#endif -+ -+#ifdef __x86_64__ -+# define XIP (8 * RIP) -+# define XSP (8 * RSP) -+#else -+# define XIP (4 * EIP) -+# define XSP (4 * UESP) -+#endif -+ -+static target_address_t -+conv_32(target_address_t val) -+{ -+ /* XXX Drop the multiple double casts when target_address_t -+ * becomes integral. */ -+ return (target_address_t)(uintptr_t)(uint32_t)(uintptr_t)val; -+} -+ -+void * -+get_instruction_pointer(struct Process *proc) -+{ -+ long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, XIP, 0); -+ if (proc->e_machine == EM_386) -+ ret &= 0xffffffff; -+ return (void *)ret; -+} -+ -+void -+set_instruction_pointer(struct Process *proc, target_address_t addr) -+{ -+ if (proc->e_machine == EM_386) -+ addr = conv_32(addr); -+ ptrace(PTRACE_POKEUSER, proc->pid, XIP, addr); -+} -+ -+void * -+get_stack_pointer(struct Process *proc) -+{ -+ long sp = ptrace(PTRACE_PEEKUSER, proc->pid, XSP, 0); -+ if (sp == -1 && errno) { -+ fprintf(stderr, "Couldn't read SP register: %s\n", -+ strerror(errno)); -+ return NULL; -+ } -+ -+ /* XXX Drop the multiple double casts when target_address_t -+ * becomes integral. */ -+ target_address_t ret = (target_address_t)(uintptr_t)sp; -+ if (proc->e_machine == EM_386) -+ ret = conv_32(ret); -+ return ret; -+} -+ -+void * -+get_return_addr(struct Process *proc, void *sp) -+{ -+ long a = ptrace(PTRACE_PEEKTEXT, proc->pid, sp, 0); -+ if (a == -1 && errno) { -+ fprintf(stderr, "Couldn't read return value: %s\n", -+ strerror(errno)); -+ return NULL; -+ } -+ -+ /* XXX Drop the multiple double casts when target_address_t -+ * becomes integral. */ -+ target_address_t ret = (target_address_t)(uintptr_t)a; -+ if (proc->e_machine == EM_386) -+ ret = conv_32(ret); -+ return ret; -+} -+ -+void -+set_return_addr(Process *proc, void *addr) { -+ if (proc->e_machine == EM_386) -+ addr = (void *)((long int)addr & 0xffffffff); -+ ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, addr); -+} -diff --git a/sysdeps/linux-gnu/x86/signalent.h b/sysdeps/linux-gnu/x86/signalent.h -new file mode 100644 -index 0000000..d58a36c ---- /dev/null -+++ b/sysdeps/linux-gnu/x86/signalent.h -@@ -0,0 +1,32 @@ -+"SIG_0", /* 0 */ -+ "SIGHUP", /* 1 */ -+ "SIGINT", /* 2 */ -+ "SIGQUIT", /* 3 */ -+ "SIGILL", /* 4 */ -+ "SIGTRAP", /* 5 */ -+ "SIGABRT", /* 6 */ -+ "SIGBUS", /* 7 */ -+ "SIGFPE", /* 8 */ -+ "SIGKILL", /* 9 */ -+ "SIGUSR1", /* 10 */ -+ "SIGSEGV", /* 11 */ -+ "SIGUSR2", /* 12 */ -+ "SIGPIPE", /* 13 */ -+ "SIGALRM", /* 14 */ -+ "SIGTERM", /* 15 */ -+ "SIGSTKFLT", /* 16 */ -+ "SIGCHLD", /* 17 */ -+ "SIGCONT", /* 18 */ -+ "SIGSTOP", /* 19 */ -+ "SIGTSTP", /* 20 */ -+ "SIGTTIN", /* 21 */ -+ "SIGTTOU", /* 22 */ -+ "SIGURG", /* 23 */ -+ "SIGXCPU", /* 24 */ -+ "SIGXFSZ", /* 25 */ -+ "SIGVTALRM", /* 26 */ -+ "SIGPROF", /* 27 */ -+ "SIGWINCH", /* 28 */ -+ "SIGIO", /* 29 */ -+ "SIGPWR", /* 30 */ -+ "SIGSYS", /* 31 */ -diff --git a/sysdeps/linux-gnu/x86/signalent1.h b/sysdeps/linux-gnu/x86/signalent1.h -new file mode 100644 -index 0000000..5395f82 ---- /dev/null -+++ b/sysdeps/linux-gnu/x86/signalent1.h -@@ -0,0 +1,32 @@ -+ "SIG_0", /* 0 */ -+ "SIGHUP", /* 1 */ -+ "SIGINT", /* 2 */ -+ "SIGQUIT", /* 3 */ -+ "SIGILL", /* 4 */ -+ "SIGTRAP", /* 5 */ -+ "SIGABRT", /* 6 */ -+ "SIGBUS", /* 7 */ -+ "SIGFPE", /* 8 */ -+ "SIGKILL", /* 9 */ -+ "SIGUSR1", /* 10 */ -+ "SIGSEGV", /* 11 */ -+ "SIGUSR2", /* 12 */ -+ "SIGPIPE", /* 13 */ -+ "SIGALRM", /* 14 */ -+ "SIGTERM", /* 15 */ -+ "SIGSTKFLT", /* 16 */ -+ "SIGCHLD", /* 17 */ -+ "SIGCONT", /* 18 */ -+ "SIGSTOP", /* 19 */ -+ "SIGTSTP", /* 20 */ -+ "SIGTTIN", /* 21 */ -+ "SIGTTOU", /* 22 */ -+ "SIGURG", /* 23 */ -+ "SIGXCPU", /* 24 */ -+ "SIGXFSZ", /* 25 */ -+ "SIGVTALRM", /* 26 */ -+ "SIGPROF", /* 27 */ -+ "SIGWINCH", /* 28 */ -+ "SIGIO", /* 29 */ -+ "SIGPWR", /* 30 */ -+ "SIGSYS", /* 31 */ -diff --git a/sysdeps/linux-gnu/x86/syscallent.h b/sysdeps/linux-gnu/x86/syscallent.h -new file mode 100644 -index 0000000..7d746b7 ---- /dev/null -+++ b/sysdeps/linux-gnu/x86/syscallent.h -@@ -0,0 +1,350 @@ -+/* This file is for i386 system call names. */ -+ "restart_syscall", /* 0 */ -+ "exit", /* 1 */ -+ "fork", /* 2 */ -+ "read", /* 3 */ -+ "write", /* 4 */ -+ "open", /* 5 */ -+ "close", /* 6 */ -+ "waitpid", /* 7 */ -+ "creat", /* 8 */ -+ "link", /* 9 */ -+ "unlink", /* 10 */ -+ "execve", /* 11 */ -+ "chdir", /* 12 */ -+ "time", /* 13 */ -+ "mknod", /* 14 */ -+ "chmod", /* 15 */ -+ "lchown", /* 16 */ -+ "break", /* 17 */ -+ "oldstat", /* 18 */ -+ "lseek", /* 19 */ -+ "getpid", /* 20 */ -+ "mount", /* 21 */ -+ "umount", /* 22 */ -+ "setuid", /* 23 */ -+ "getuid", /* 24 */ -+ "stime", /* 25 */ -+ "ptrace", /* 26 */ -+ "alarm", /* 27 */ -+ "oldfstat", /* 28 */ -+ "pause", /* 29 */ -+ "utime", /* 30 */ -+ "stty", /* 31 */ -+ "gtty", /* 32 */ -+ "access", /* 33 */ -+ "nice", /* 34 */ -+ "ftime", /* 35 */ -+ "sync", /* 36 */ -+ "kill", /* 37 */ -+ "rename", /* 38 */ -+ "mkdir", /* 39 */ -+ "rmdir", /* 40 */ -+ "dup", /* 41 */ -+ "pipe", /* 42 */ -+ "times", /* 43 */ -+ "prof", /* 44 */ -+ "brk", /* 45 */ -+ "setgid", /* 46 */ -+ "getgid", /* 47 */ -+ "signal", /* 48 */ -+ "geteuid", /* 49 */ -+ "getegid", /* 50 */ -+ "acct", /* 51 */ -+ "umount2", /* 52 */ -+ "lock", /* 53 */ -+ "ioctl", /* 54 */ -+ "fcntl", /* 55 */ -+ "mpx", /* 56 */ -+ "setpgid", /* 57 */ -+ "ulimit", /* 58 */ -+ "oldolduname", /* 59 */ -+ "umask", /* 60 */ -+ "chroot", /* 61 */ -+ "ustat", /* 62 */ -+ "dup2", /* 63 */ -+ "getppid", /* 64 */ -+ "getpgrp", /* 65 */ -+ "setsid", /* 66 */ -+ "sigaction", /* 67 */ -+ "sgetmask", /* 68 */ -+ "ssetmask", /* 69 */ -+ "setreuid", /* 70 */ -+ "setregid", /* 71 */ -+ "sigsuspend", /* 72 */ -+ "sigpending", /* 73 */ -+ "sethostname", /* 74 */ -+ "setrlimit", /* 75 */ -+ "getrlimit", /* 76 */ -+ "getrusage", /* 77 */ -+ "gettimeofday", /* 78 */ -+ "settimeofday", /* 79 */ -+ "getgroups", /* 80 */ -+ "setgroups", /* 81 */ -+ "select", /* 82 */ -+ "symlink", /* 83 */ -+ "oldlstat", /* 84 */ -+ "readlink", /* 85 */ -+ "uselib", /* 86 */ -+ "swapon", /* 87 */ -+ "reboot", /* 88 */ -+ "readdir", /* 89 */ -+ "mmap", /* 90 */ -+ "munmap", /* 91 */ -+ "truncate", /* 92 */ -+ "ftruncate", /* 93 */ -+ "fchmod", /* 94 */ -+ "fchown", /* 95 */ -+ "getpriority", /* 96 */ -+ "setpriority", /* 97 */ -+ "profil", /* 98 */ -+ "statfs", /* 99 */ -+ "fstatfs", /* 100 */ -+ "ioperm", /* 101 */ -+ "socketcall", /* 102 */ -+ "syslog", /* 103 */ -+ "setitimer", /* 104 */ -+ "getitimer", /* 105 */ -+ "stat", /* 106 */ -+ "lstat", /* 107 */ -+ "fstat", /* 108 */ -+ "olduname", /* 109 */ -+ "iopl", /* 110 */ -+ "vhangup", /* 111 */ -+ "idle", /* 112 */ -+ "vm86old", /* 113 */ -+ "wait4", /* 114 */ -+ "swapoff", /* 115 */ -+ "sysinfo", /* 116 */ -+ "ipc", /* 117 */ -+ "fsync", /* 118 */ -+ "sigreturn", /* 119 */ -+ "clone", /* 120 */ -+ "setdomainname", /* 121 */ -+ "uname", /* 122 */ -+ "modify_ldt", /* 123 */ -+ "adjtimex", /* 124 */ -+ "mprotect", /* 125 */ -+ "sigprocmask", /* 126 */ -+ "create_module", /* 127 */ -+ "init_module", /* 128 */ -+ "delete_module", /* 129 */ -+ "get_kernel_syms", /* 130 */ -+ "quotactl", /* 131 */ -+ "getpgid", /* 132 */ -+ "fchdir", /* 133 */ -+ "bdflush", /* 134 */ -+ "sysfs", /* 135 */ -+ "personality", /* 136 */ -+ "afs_syscall", /* 137 */ -+ "setfsuid", /* 138 */ -+ "setfsgid", /* 139 */ -+ "_llseek", /* 140 */ -+ "getdents", /* 141 */ -+ "_newselect", /* 142 */ -+ "flock", /* 143 */ -+ "msync", /* 144 */ -+ "readv", /* 145 */ -+ "writev", /* 146 */ -+ "getsid", /* 147 */ -+ "fdatasync", /* 148 */ -+ "_sysctl", /* 149 */ -+ "mlock", /* 150 */ -+ "munlock", /* 151 */ -+ "mlockall", /* 152 */ -+ "munlockall", /* 153 */ -+ "sched_setparam", /* 154 */ -+ "sched_getparam", /* 155 */ -+ "sched_setscheduler", /* 156 */ -+ "sched_getscheduler", /* 157 */ -+ "sched_yield", /* 158 */ -+ "sched_get_priority_max", /* 159 */ -+ "sched_get_priority_min", /* 160 */ -+ "sched_rr_get_interval", /* 161 */ -+ "nanosleep", /* 162 */ -+ "mremap", /* 163 */ -+ "setresuid", /* 164 */ -+ "getresuid", /* 165 */ -+ "vm86", /* 166 */ -+ "query_module", /* 167 */ -+ "poll", /* 168 */ -+ "nfsservctl", /* 169 */ -+ "setresgid", /* 170 */ -+ "getresgid", /* 171 */ -+ "prctl", /* 172 */ -+ "rt_sigreturn", /* 173 */ -+ "rt_sigaction", /* 174 */ -+ "rt_sigprocmask", /* 175 */ -+ "rt_sigpending", /* 176 */ -+ "rt_sigtimedwait", /* 177 */ -+ "rt_sigqueueinfo", /* 178 */ -+ "rt_sigsuspend", /* 179 */ -+ "pread64", /* 180 */ -+ "pwrite64", /* 181 */ -+ "chown", /* 182 */ -+ "getcwd", /* 183 */ -+ "capget", /* 184 */ -+ "capset", /* 185 */ -+ "sigaltstack", /* 186 */ -+ "sendfile", /* 187 */ -+ "getpmsg", /* 188 */ -+ "putpmsg", /* 189 */ -+ "vfork", /* 190 */ -+ "ugetrlimit", /* 191 */ -+ "mmap2", /* 192 */ -+ "truncate64", /* 193 */ -+ "ftruncate64", /* 194 */ -+ "stat64", /* 195 */ -+ "lstat64", /* 196 */ -+ "fstat64", /* 197 */ -+ "lchown32", /* 198 */ -+ "getuid32", /* 199 */ -+ "getgid32", /* 200 */ -+ "geteuid32", /* 201 */ -+ "getegid32", /* 202 */ -+ "setreuid32", /* 203 */ -+ "setregid32", /* 204 */ -+ "getgroups32", /* 205 */ -+ "setgroups32", /* 206 */ -+ "fchown32", /* 207 */ -+ "setresuid32", /* 208 */ -+ "getresuid32", /* 209 */ -+ "setresgid32", /* 210 */ -+ "getresgid32", /* 211 */ -+ "chown32", /* 212 */ -+ "setuid32", /* 213 */ -+ "setgid32", /* 214 */ -+ "setfsuid32", /* 215 */ -+ "setfsgid32", /* 216 */ -+ "pivot_root", /* 217 */ -+ "mincore", /* 218 */ -+ "madvise1", /* 219 */ -+ "getdents64", /* 220 */ -+ "fcntl64", /* 221 */ -+ "222", /* 222 */ -+ "223", /* 223 */ -+ "gettid", /* 224 */ -+ "readahead", /* 225 */ -+ "setxattr", /* 226 */ -+ "lsetxattr", /* 227 */ -+ "fsetxattr", /* 228 */ -+ "getxattr", /* 229 */ -+ "lgetxattr", /* 230 */ -+ "fgetxattr", /* 231 */ -+ "listxattr", /* 232 */ -+ "llistxattr", /* 233 */ -+ "flistxattr", /* 234 */ -+ "removexattr", /* 235 */ -+ "lremovexattr", /* 236 */ -+ "fremovexattr", /* 237 */ -+ "tkill", /* 238 */ -+ "sendfile64", /* 239 */ -+ "futex", /* 240 */ -+ "sched_setaffinity", /* 241 */ -+ "sched_getaffinity", /* 242 */ -+ "set_thread_area", /* 243 */ -+ "get_thread_area", /* 244 */ -+ "io_setup", /* 245 */ -+ "io_destroy", /* 246 */ -+ "io_getevents", /* 247 */ -+ "io_submit", /* 248 */ -+ "io_cancel", /* 249 */ -+ "fadvise64", /* 250 */ -+ "251", /* 251 */ -+ "exit_group", /* 252 */ -+ "lookup_dcookie", /* 253 */ -+ "epoll_create", /* 254 */ -+ "epoll_ctl", /* 255 */ -+ "epoll_wait", /* 256 */ -+ "remap_file_pages", /* 257 */ -+ "set_tid_address", /* 258 */ -+ "timer_create", /* 259 */ -+ "260", /* 260 */ -+ "261", /* 261 */ -+ "262", /* 262 */ -+ "263", /* 263 */ -+ "264", /* 264 */ -+ "265", /* 265 */ -+ "266", /* 266 */ -+ "267", /* 267 */ -+ "statfs64", /* 268 */ -+ "fstatfs64", /* 269 */ -+ "tgkill", /* 270 */ -+ "utimes", /* 271 */ -+ "fadvise64_64", /* 272 */ -+ "vserver", /* 273 */ -+ "mbind", /* 274 */ -+ "get_mempolicy", /* 275 */ -+ "set_mempolicy", /* 276 */ -+ "mq_open", /* 277 */ -+ "278", /* 278 */ -+ "279", /* 279 */ -+ "280", /* 280 */ -+ "281", /* 281 */ -+ "282", /* 282 */ -+ "kexec_load", /* 283 */ -+ "waitid", /* 284 */ -+ "285", /* 285 */ -+ "add_key", /* 286 */ -+ "request_key", /* 287 */ -+ "keyctl", /* 288 */ -+ "ioprio_set", /* 289 */ -+ "ioprio_get", /* 290 */ -+ "inotify_init", /* 291 */ -+ "inotify_add_watch", /* 292 */ -+ "inotify_rm_watch", /* 293 */ -+ "migrate_pages", /* 294 */ -+ "openat", /* 295 */ -+ "mkdirat", /* 296 */ -+ "mknodat", /* 297 */ -+ "fchownat", /* 298 */ -+ "futimesat", /* 299 */ -+ "fstatat64", /* 300 */ -+ "unlinkat", /* 301 */ -+ "renameat", /* 302 */ -+ "linkat", /* 303 */ -+ "symlinkat", /* 304 */ -+ "readlinkat", /* 305 */ -+ "fchmodat", /* 306 */ -+ "faccessat", /* 307 */ -+ "pselect6", /* 308 */ -+ "ppoll", /* 309 */ -+ "unshare", /* 310 */ -+ "set_robust_list", /* 311 */ -+ "get_robust_list", /* 312 */ -+ "splice", /* 313 */ -+ "sync_file_range", /* 314 */ -+ "tee", /* 315 */ -+ "vmsplice", /* 316 */ -+ "move_pages", /* 317 */ -+ "getcpu", /* 318 */ -+ "epoll_pwait", /* 319 */ -+ "utimensat", /* 320 */ -+ "signalfd", /* 321 */ -+ "timerfd_create", /* 322 */ -+ "eventfd", /* 323 */ -+ "fallocate", /* 324 */ -+ "timerfd_settime", /* 325 */ -+ "timerfd_gettime", /* 326 */ -+ "signalfd4", /* 327 */ -+ "eventfd2", /* 328 */ -+ "epoll_create1", /* 329 */ -+ "dup3", /* 330 */ -+ "pipe2", /* 331 */ -+ "inotify_init1", /* 332 */ -+ "preadv", /* 333 */ -+ "pwritev", /* 334 */ -+ "rt_tgsigqueueinfo", /* 335 */ -+ "perf_event_open", /* 336 */ -+ "recvmmsg", /* 337 */ -+ "fanotify_init", /* 338 */ -+ "fanotify_mark", /* 339 */ -+ "prlimit64", /* 340 */ -+ "name_to_handle_at", /* 341 */ -+ "open_by_handle_at", /* 342 */ -+ "clock_adjtime", /* 343 */ -+ "syncfs", /* 344 */ -+ "sendmmsg", /* 345 */ -+ "setns", /* 346 */ -+ "process_vm_readv", /* 347 */ -+ "process_vm_writev", /* 348 */ -diff --git a/sysdeps/linux-gnu/x86/syscallent1.h b/sysdeps/linux-gnu/x86/syscallent1.h -new file mode 100644 -index 0000000..c2d9017 ---- /dev/null -+++ b/sysdeps/linux-gnu/x86/syscallent1.h -@@ -0,0 +1,313 @@ -+/* This file is for x86_64 system call names. */ -+ "read", /* 0 */ -+ "write", /* 1 */ -+ "open", /* 2 */ -+ "close", /* 3 */ -+ "stat", /* 4 */ -+ "fstat", /* 5 */ -+ "lstat", /* 6 */ -+ "poll", /* 7 */ -+ "lseek", /* 8 */ -+ "mmap", /* 9 */ -+ "mprotect", /* 10 */ -+ "munmap", /* 11 */ -+ "brk", /* 12 */ -+ "rt_sigaction", /* 13 */ -+ "rt_sigprocmask", /* 14 */ -+ "rt_sigreturn", /* 15 */ -+ "ioctl", /* 16 */ -+ "pread", /* 17 */ -+ "pwrite", /* 18 */ -+ "readv", /* 19 */ -+ "writev", /* 20 */ -+ "access", /* 21 */ -+ "pipe", /* 22 */ -+ "select", /* 23 */ -+ "sched_yield", /* 24 */ -+ "mremap", /* 25 */ -+ "msync", /* 26 */ -+ "mincore", /* 27 */ -+ "madvise", /* 28 */ -+ "shmget", /* 29 */ -+ "shmat", /* 30 */ -+ "shmctl", /* 31 */ -+ "dup", /* 32 */ -+ "dup2", /* 33 */ -+ "pause", /* 34 */ -+ "nanosleep", /* 35 */ -+ "getitimer", /* 36 */ -+ "alarm", /* 37 */ -+ "setitimer", /* 38 */ -+ "getpid", /* 39 */ -+ "sendfile", /* 40 */ -+ "socket", /* 41 */ -+ "connect", /* 42 */ -+ "accept", /* 43 */ -+ "sendto", /* 44 */ -+ "recvfrom", /* 45 */ -+ "sendmsg", /* 46 */ -+ "recvmsg", /* 47 */ -+ "shutdown", /* 48 */ -+ "bind", /* 49 */ -+ "listen", /* 50 */ -+ "getsockname", /* 51 */ -+ "getpeername", /* 52 */ -+ "socketpair", /* 53 */ -+ "setsockopt", /* 54 */ -+ "getsockopt", /* 55 */ -+ "clone", /* 56 */ -+ "fork", /* 57 */ -+ "vfork", /* 58 */ -+ "execve", /* 59 */ -+ "exit", /* 60 */ -+ "wait4", /* 61 */ -+ "kill", /* 62 */ -+ "uname", /* 63 */ -+ "semget", /* 64 */ -+ "semop", /* 65 */ -+ "semctl", /* 66 */ -+ "shmdt", /* 67 */ -+ "msgget", /* 68 */ -+ "msgsnd", /* 69 */ -+ "msgrcv", /* 70 */ -+ "msgctl", /* 71 */ -+ "fcntl", /* 72 */ -+ "flock", /* 73 */ -+ "fsync", /* 74 */ -+ "fdatasync", /* 75 */ -+ "truncate", /* 76 */ -+ "ftruncate", /* 77 */ -+ "getdents", /* 78 */ -+ "getcwd", /* 79 */ -+ "chdir", /* 80 */ -+ "fchdir", /* 81 */ -+ "rename", /* 82 */ -+ "mkdir", /* 83 */ -+ "rmdir", /* 84 */ -+ "creat", /* 85 */ -+ "link", /* 86 */ -+ "unlink", /* 87 */ -+ "symlink", /* 88 */ -+ "readlink", /* 89 */ -+ "chmod", /* 90 */ -+ "fchmod", /* 91 */ -+ "chown", /* 92 */ -+ "fchown", /* 93 */ -+ "lchown", /* 94 */ -+ "umask", /* 95 */ -+ "gettimeofday", /* 96 */ -+ "getrlimit", /* 97 */ -+ "getrusage", /* 98 */ -+ "sysinfo", /* 99 */ -+ "times", /* 100 */ -+ "ptrace", /* 101 */ -+ "getuid", /* 102 */ -+ "syslog", /* 103 */ -+ "getgid", /* 104 */ -+ "setuid", /* 105 */ -+ "setgid", /* 106 */ -+ "geteuid", /* 107 */ -+ "getegid", /* 108 */ -+ "setpgid", /* 109 */ -+ "getppid", /* 110 */ -+ "getpgrp", /* 111 */ -+ "setsid", /* 112 */ -+ "setreuid", /* 113 */ -+ "setregid", /* 114 */ -+ "getgroups", /* 115 */ -+ "setgroups", /* 116 */ -+ "setresuid", /* 117 */ -+ "getresuid", /* 118 */ -+ "setresgid", /* 119 */ -+ "getresgid", /* 120 */ -+ "getpgid", /* 121 */ -+ "setfsuid", /* 122 */ -+ "setfsgid", /* 123 */ -+ "getsid", /* 124 */ -+ "capget", /* 125 */ -+ "capset", /* 126 */ -+ "rt_sigpending", /* 127 */ -+ "rt_sigtimedwait", /* 128 */ -+ "rt_sigqueueinfo", /* 129 */ -+ "rt_sigsuspend", /* 130 */ -+ "sigaltstack", /* 131 */ -+ "utime", /* 132 */ -+ "mknod", /* 133 */ -+ "uselib", /* 134 */ -+ "personality", /* 135 */ -+ "ustat", /* 136 */ -+ "statfs", /* 137 */ -+ "fstatfs", /* 138 */ -+ "sysfs", /* 139 */ -+ "getpriority", /* 140 */ -+ "setpriority", /* 141 */ -+ "sched_setparam", /* 142 */ -+ "sched_getparam", /* 143 */ -+ "sched_setscheduler", /* 144 */ -+ "sched_getscheduler", /* 145 */ -+ "sched_get_priority_max", /* 146 */ -+ "sched_get_priority_min", /* 147 */ -+ "sched_rr_get_interval", /* 148 */ -+ "mlock", /* 149 */ -+ "munlock", /* 150 */ -+ "mlockall", /* 151 */ -+ "munlockall", /* 152 */ -+ "vhangup", /* 153 */ -+ "modify_ldt", /* 154 */ -+ "pivot_root", /* 155 */ -+ "_sysctl", /* 156 */ -+ "prctl", /* 157 */ -+ "arch_prctl", /* 158 */ -+ "adjtimex", /* 159 */ -+ "setrlimit", /* 160 */ -+ "chroot", /* 161 */ -+ "sync", /* 162 */ -+ "acct", /* 163 */ -+ "settimeofday", /* 164 */ -+ "mount", /* 165 */ -+ "umount2", /* 166 */ -+ "swapon", /* 167 */ -+ "swapoff", /* 168 */ -+ "reboot", /* 169 */ -+ "sethostname", /* 170 */ -+ "setdomainname", /* 171 */ -+ "iopl", /* 172 */ -+ "ioperm", /* 173 */ -+ "create_module", /* 174 */ -+ "init_module", /* 175 */ -+ "delete_module", /* 176 */ -+ "get_kernel_syms", /* 177 */ -+ "query_module", /* 178 */ -+ "quotactl", /* 179 */ -+ "nfsservctl", /* 180 */ -+ "getpmsg", /* 181 */ -+ "putpmsg", /* 182 */ -+ "afs_syscall", /* 183 */ -+ "tuxcall", /* 184 */ -+ "security", /* 185 */ -+ "gettid", /* 186 */ -+ "readahead", /* 187 */ -+ "setxattr", /* 188 */ -+ "lsetxattr", /* 189 */ -+ "fsetxattr", /* 190 */ -+ "getxattr", /* 191 */ -+ "lgetxattr", /* 192 */ -+ "fgetxattr", /* 193 */ -+ "listxattr", /* 194 */ -+ "llistxattr", /* 195 */ -+ "flistxattr", /* 196 */ -+ "removexattr", /* 197 */ -+ "lremovexattr", /* 198 */ -+ "fremovexattr", /* 199 */ -+ "tkill", /* 200 */ -+ "time", /* 201 */ -+ "futex", /* 202 */ -+ "sched_setaffinity", /* 203 */ -+ "sched_getaffinity", /* 204 */ -+ "set_thread_area", /* 205 */ -+ "io_setup", /* 206 */ -+ "io_destroy", /* 207 */ -+ "io_getevents", /* 208 */ -+ "io_submit", /* 209 */ -+ "io_cancel", /* 210 */ -+ "get_thread_area", /* 211 */ -+ "lookup_dcookie", /* 212 */ -+ "epoll_create", /* 213 */ -+ "epoll_ctl", /* 214 */ -+ "epoll_wait", /* 215 */ -+ "remap_file_pages", /* 216 */ -+ "getdents64", /* 217 */ -+ "set_tid_address", /* 218 */ -+ "restart_syscall", /* 219 */ -+ "semtimedop", /* 220 */ -+ "fadvise64", /* 221 */ -+ "timer_create", /* 222 */ -+ "timer_settime", /* 223 */ -+ "timer_gettime", /* 224 */ -+ "timer_getoverrun", /* 225 */ -+ "timer_delete", /* 226 */ -+ "clock_settime", /* 227 */ -+ "clock_gettime", /* 228 */ -+ "clock_getres", /* 229 */ -+ "clock_nanosleep", /* 230 */ -+ "exit_group", /* 231 */ -+ "epoll_wait", /* 232 */ -+ "epoll_ctl", /* 233 */ -+ "tgkill", /* 234 */ -+ "utimes", /* 235 */ -+ "vserver", /* 236 */ -+ "mbind", /* 237 */ -+ "set_mempolicy", /* 238 */ -+ "get_mempolicy", /* 239 */ -+ "mq_open", /* 240 */ -+ "mq_unlink", /* 241 */ -+ "mq_timedsend", /* 242 */ -+ "mq_timedreceive", /* 243 */ -+ "mq_notify", /* 244 */ -+ "mq_getsetattr", /* 245 */ -+ "kexec_load", /* 246 */ -+ "waitid", /* 247 */ -+ "add_key", /* 248 */ -+ "request_key", /* 249 */ -+ "keyctl", /* 250 */ -+ "ioprio_set", /* 251 */ -+ "ioprio_get", /* 252 */ -+ "inotify_init", /* 253 */ -+ "inotify_add_watch", /* 254 */ -+ "inotify_rm_watch", /* 255 */ -+ "migrate_pages", /* 256 */ -+ "openat", /* 257 */ -+ "mkdirat", /* 258 */ -+ "mknodat", /* 259 */ -+ "fchownat", /* 260 */ -+ "futimesat", /* 261 */ -+ "newfstatat", /* 262 */ -+ "unlinkat", /* 263 */ -+ "renameat", /* 264 */ -+ "linkat", /* 265 */ -+ "symlinkat", /* 266 */ -+ "readlinkat", /* 267 */ -+ "fchmodat", /* 268 */ -+ "faccessat", /* 269 */ -+ "pselect6", /* 270 */ -+ "ppoll", /* 271 */ -+ "unshare", /* 272 */ -+ "set_robust_list", /* 273 */ -+ "get_robust_list", /* 274 */ -+ "splice", /* 275 */ -+ "tee", /* 276 */ -+ "sync_file_range", /* 277 */ -+ "vmsplice", /* 278 */ -+ "move_pages", /* 279 */ -+ "utimensat", /* 280 */ -+ "epoll_pwait", /* 281 */ -+ "signalfd", /* 282 */ -+ "timerfd_create", /* 283 */ -+ "eventfd", /* 284 */ -+ "fallocate", /* 285 */ -+ "timerfd_settime", /* 286 */ -+ "timerfd_gettime", /* 287 */ -+ "accept4", /* 288 */ -+ "signalfd4", /* 289 */ -+ "eventfd2", /* 290 */ -+ "epoll_create1", /* 291 */ -+ "dup3", /* 292 */ -+ "pipe2", /* 293 */ -+ "inotify_init1", /* 294 */ -+ "preadv", /* 295 */ -+ "pwritev", /* 296 */ -+ "rt_tgsigqueueinfo", /* 297 */ -+ "perf_event_open", /* 298 */ -+ "recvmmsg", /* 299 */ -+ "fanotify_init", /* 300 */ -+ "fanotify_mark", /* 301 */ -+ "prlimit64", /* 302 */ -+ "name_to_handle_at", /* 303 */ -+ "open_by_handle_at", /* 304 */ -+ "clock_adjtime", /* 305 */ -+ "syncfs", /* 306 */ -+ "sendmmsg", /* 307 */ -+ "setns", /* 308 */ -+ "getcpu", /* 309 */ -+ "process_vm_readv", /* 310 */ -+ "process_vm_writev", /* 311 */ -diff --git a/sysdeps/linux-gnu/x86/trace.c b/sysdeps/linux-gnu/x86/trace.c -new file mode 100644 -index 0000000..cc1a6a1 ---- /dev/null -+++ b/sysdeps/linux-gnu/x86/trace.c -@@ -0,0 +1,189 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2010,2011,2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2004,2008,2009 Juan Cespedes -+ * 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 "config.h" -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "backend.h" -+#include "debug.h" -+#include "proc.h" -+#include "ptrace.h" -+#include "type.h" -+ -+#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) -+# define PTRACE_PEEKUSER PTRACE_PEEKUSR -+#endif -+ -+#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) -+# define PTRACE_POKEUSER PTRACE_POKEUSR -+#endif -+ -+#ifdef __x86_64__ -+# define ORIG_XAX (8 * ORIG_RAX) -+#else -+# define ORIG_XAX (4 * ORIG_EAX) -+#endif -+ -+#ifdef __x86_64__ -+static const int x86_64 = 1; -+#else -+static const int x86_64 = 0; -+#endif -+ -+void -+get_arch_dep(struct Process *proc) -+{ -+ /* Unfortunately there are still remnants of mask_32bit uses -+ * around. */ -+ -+ if (proc->e_machine == EM_X86_64) { -+ proc->mask_32bit = 0; -+ proc->personality = 1; -+ } else if (x86_64) { /* x86_64/i386 */ -+ proc->mask_32bit = 1; -+ proc->personality = 0; -+ } else { -+ proc->mask_32bit = 0; -+ proc->personality = 0; -+ } -+} -+ -+/* Returns 1 if syscall, 2 if sysret, 0 otherwise. -+ */ -+int -+syscall_p(struct Process *proc, int status, int *sysnum) -+{ -+ if (WIFSTOPPED(status) -+ && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { -+ struct callstack_element *elem = NULL; -+ if (proc->callstack_depth > 0) -+ elem = proc->callstack + proc->callstack_depth - 1; -+ -+ long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, ORIG_XAX, 0); -+ if (ret == -1) { -+ if (errno) -+ return -1; -+ /* Otherwise, ORIG_RAX == -1 means that the -+ * system call should not be restarted. In -+ * that case rely on what we have on -+ * stack. */ -+ if (elem != NULL && elem->is_syscall) -+ ret = elem->c_un.syscall; -+ } -+ -+ *sysnum = ret; -+ debug(DEBUG_FUNCTION, "sysnum=%ld %p %d\n", ret, -+ get_instruction_pointer(proc), errno); -+ if (elem != NULL && elem->is_syscall -+ && elem->c_un.syscall == *sysnum) -+ return 2; -+ -+ if (*sysnum >= 0) -+ return 1; -+ } -+ return 0; -+} -+ -+size_t -+arch_type_sizeof(struct Process *proc, struct arg_type_info *info) -+{ -+ if (proc == NULL) -+ return (size_t)-2; -+ -+ switch (info->type) { -+ case ARGTYPE_VOID: -+ return 0; -+ -+ case ARGTYPE_CHAR: -+ return 1; -+ -+ case ARGTYPE_SHORT: -+ case ARGTYPE_USHORT: -+ return 2; -+ -+ case ARGTYPE_INT: -+ case ARGTYPE_UINT: -+ return 4; -+ -+ case ARGTYPE_LONG: -+ case ARGTYPE_ULONG: -+ case ARGTYPE_POINTER: -+ return proc->e_machine == EM_X86_64 ? 8 : 4; -+ -+ case ARGTYPE_FLOAT: -+ return 4; -+ case ARGTYPE_DOUBLE: -+ return 8; -+ -+ case ARGTYPE_ARRAY: -+ case ARGTYPE_STRUCT: -+ /* Use default value. */ -+ return (size_t)-2; -+ } -+ assert(info->type != info->type); -+ abort(); -+} -+ -+size_t -+arch_type_alignof(struct Process *proc, struct arg_type_info *info) -+{ -+ if (proc == NULL) -+ return (size_t)-2; -+ -+ switch (info->type) { -+ case ARGTYPE_VOID: -+ assert(info->type != ARGTYPE_VOID); -+ break; -+ -+ case ARGTYPE_CHAR: -+ return 1; -+ -+ case ARGTYPE_SHORT: -+ case ARGTYPE_USHORT: -+ return 2; -+ -+ case ARGTYPE_INT: -+ case ARGTYPE_UINT: -+ return 4; -+ -+ case ARGTYPE_LONG: -+ case ARGTYPE_ULONG: -+ case ARGTYPE_POINTER: -+ return proc->e_machine == EM_X86_64 ? 8 : 4; -+ -+ case ARGTYPE_FLOAT: -+ return 4; -+ case ARGTYPE_DOUBLE: -+ return proc->e_machine == EM_X86_64 ? 8 : 4; -+ -+ case ARGTYPE_ARRAY: -+ case ARGTYPE_STRUCT: -+ /* Use default value. */ -+ return (size_t)-2; -+ } -+ abort(); -+} -diff --git a/sysdeps/linux-gnu/x86_64/Makefile.am b/sysdeps/linux-gnu/x86_64/Makefile.am -deleted file mode 100644 -index 2e476d8..0000000 ---- a/sysdeps/linux-gnu/x86_64/Makefile.am -+++ /dev/null -@@ -1,18 +0,0 @@ --noinst_LTLIBRARIES = \ -- ../libcpu.la -- --___libcpu_la_SOURCES = \ -- plt.c \ -- regs.c \ -- trace.c -- --noinst_HEADERS = \ -- arch.h \ -- ptrace.h \ -- signalent.h \ -- signalent1.h \ -- syscallent.h \ -- syscallent1.h -- --MAINTAINERCLEANFILES = \ -- Makefile.in -diff --git a/sysdeps/linux-gnu/x86_64/arch.h b/sysdeps/linux-gnu/x86_64/arch.h -deleted file mode 100644 -index 255395c..0000000 ---- a/sysdeps/linux-gnu/x86_64/arch.h -+++ /dev/null -@@ -1,13 +0,0 @@ --#define BREAKPOINT_VALUE {0xcc} --#define BREAKPOINT_LENGTH 1 --#define DECR_PC_AFTER_BREAK 1 --#define ARCH_ENDIAN_LITTLE -- --#define LT_ELFCLASS ELFCLASS64 --#define LT_ELF_MACHINE EM_X86_64 --#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/sysdeps/linux-gnu/x86_64/plt.c b/sysdeps/linux-gnu/x86_64/plt.c -deleted file mode 100644 -index bb1b2b1..0000000 ---- a/sysdeps/linux-gnu/x86_64/plt.c -+++ /dev/null -@@ -1,14 +0,0 @@ --#include --#include "proc.h" --#include "common.h" --#include "library.h" -- --GElf_Addr --arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { -- return lte->plt_addr + (ndx + 1) * 16; --} -- --void * --sym2addr(Process *proc, struct library_symbol *sym) { -- return sym->enter_addr; --} -diff --git a/sysdeps/linux-gnu/x86_64/ptrace.h b/sysdeps/linux-gnu/x86_64/ptrace.h -deleted file mode 100644 -index d92771f..0000000 ---- a/sysdeps/linux-gnu/x86_64/ptrace.h -+++ /dev/null -@@ -1,14 +0,0 @@ --#include --#include --#include -- --typedef struct { -- int valid; -- struct user_regs_struct regs; -- struct user_fpregs_struct fpregs; --} proc_archdep; -- --typedef struct { -- struct user_regs_struct regs_copy; -- struct user_fpregs_struct fpregs_copy; --} callstack_achdep; -diff --git a/sysdeps/linux-gnu/x86_64/regs.c b/sysdeps/linux-gnu/x86_64/regs.c -deleted file mode 100644 -index 0ff3281..0000000 ---- a/sysdeps/linux-gnu/x86_64/regs.c -+++ /dev/null -@@ -1,54 +0,0 @@ --#include "config.h" -- --#include --#include --#include -- --#include "proc.h" -- --#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) --# define PTRACE_PEEKUSER PTRACE_PEEKUSR --#endif -- --#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) --# define PTRACE_POKEUSER PTRACE_POKEUSR --#endif -- --void * --get_instruction_pointer(Process *proc) { -- long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RIP, 0); -- if (proc->mask_32bit) -- ret &= 0xffffffff; -- return (void *)ret; --} -- --void --set_instruction_pointer(Process *proc, void *addr) { -- if (proc->mask_32bit) -- addr = (void *)((long int)addr & 0xffffffff); -- ptrace(PTRACE_POKEUSER, proc->pid, 8 * RIP, addr); --} -- --void * --get_stack_pointer(Process *proc) { -- long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RSP, 0); -- if (proc->mask_32bit) -- ret &= 0xffffffff; -- return (void *)ret; --} -- --void * --get_return_addr(Process *proc, void *stack_pointer) { -- unsigned long int ret; -- ret = ptrace(PTRACE_PEEKTEXT, proc->pid, stack_pointer, 0); -- if (proc->mask_32bit) -- ret &= 0xffffffff; -- return (void *)ret; --} -- --void --set_return_addr(Process *proc, void *addr) { -- if (proc->mask_32bit) -- addr = (void *)((long int)addr & 0xffffffff); -- ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, addr); --} -diff --git a/sysdeps/linux-gnu/x86_64/signalent.h b/sysdeps/linux-gnu/x86_64/signalent.h -deleted file mode 100644 -index d58a36c..0000000 ---- a/sysdeps/linux-gnu/x86_64/signalent.h -+++ /dev/null -@@ -1,32 +0,0 @@ --"SIG_0", /* 0 */ -- "SIGHUP", /* 1 */ -- "SIGINT", /* 2 */ -- "SIGQUIT", /* 3 */ -- "SIGILL", /* 4 */ -- "SIGTRAP", /* 5 */ -- "SIGABRT", /* 6 */ -- "SIGBUS", /* 7 */ -- "SIGFPE", /* 8 */ -- "SIGKILL", /* 9 */ -- "SIGUSR1", /* 10 */ -- "SIGSEGV", /* 11 */ -- "SIGUSR2", /* 12 */ -- "SIGPIPE", /* 13 */ -- "SIGALRM", /* 14 */ -- "SIGTERM", /* 15 */ -- "SIGSTKFLT", /* 16 */ -- "SIGCHLD", /* 17 */ -- "SIGCONT", /* 18 */ -- "SIGSTOP", /* 19 */ -- "SIGTSTP", /* 20 */ -- "SIGTTIN", /* 21 */ -- "SIGTTOU", /* 22 */ -- "SIGURG", /* 23 */ -- "SIGXCPU", /* 24 */ -- "SIGXFSZ", /* 25 */ -- "SIGVTALRM", /* 26 */ -- "SIGPROF", /* 27 */ -- "SIGWINCH", /* 28 */ -- "SIGIO", /* 29 */ -- "SIGPWR", /* 30 */ -- "SIGSYS", /* 31 */ -diff --git a/sysdeps/linux-gnu/x86_64/signalent1.h b/sysdeps/linux-gnu/x86_64/signalent1.h -deleted file mode 100644 -index 5ead946..0000000 ---- a/sysdeps/linux-gnu/x86_64/signalent1.h -+++ /dev/null -@@ -1 +0,0 @@ --#include "i386/signalent.h" -diff --git a/sysdeps/linux-gnu/x86_64/syscallent.h b/sysdeps/linux-gnu/x86_64/syscallent.h -deleted file mode 100644 -index 5e5f88a..0000000 ---- a/sysdeps/linux-gnu/x86_64/syscallent.h -+++ /dev/null -@@ -1,256 +0,0 @@ --"read", /* 0 */ -- "write", /* 1 */ -- "open", /* 2 */ -- "close", /* 3 */ -- "stat", /* 4 */ -- "fstat", /* 5 */ -- "lstat", /* 6 */ -- "poll", /* 7 */ -- "lseek", /* 8 */ -- "mmap", /* 9 */ -- "mprotect", /* 10 */ -- "munmap", /* 11 */ -- "brk", /* 12 */ -- "rt_sigaction", /* 13 */ -- "rt_sigprocmask", /* 14 */ -- "rt_sigreturn", /* 15 */ -- "ioctl", /* 16 */ -- "pread", /* 17 */ -- "pwrite", /* 18 */ -- "readv", /* 19 */ -- "writev", /* 20 */ -- "access", /* 21 */ -- "pipe", /* 22 */ -- "select", /* 23 */ -- "sched_yield", /* 24 */ -- "mremap", /* 25 */ -- "msync", /* 26 */ -- "mincore", /* 27 */ -- "madvise", /* 28 */ -- "shmget", /* 29 */ -- "shmat", /* 30 */ -- "shmctl", /* 31 */ -- "dup", /* 32 */ -- "dup2", /* 33 */ -- "pause", /* 34 */ -- "nanosleep", /* 35 */ -- "getitimer", /* 36 */ -- "alarm", /* 37 */ -- "setitimer", /* 38 */ -- "getpid", /* 39 */ -- "sendfile", /* 40 */ -- "socket", /* 41 */ -- "connect", /* 42 */ -- "accept", /* 43 */ -- "sendto", /* 44 */ -- "recvfrom", /* 45 */ -- "sendmsg", /* 46 */ -- "recvmsg", /* 47 */ -- "shutdown", /* 48 */ -- "bind", /* 49 */ -- "listen", /* 50 */ -- "getsockname", /* 51 */ -- "getpeername", /* 52 */ -- "socketpair", /* 53 */ -- "setsockopt", /* 54 */ -- "getsockopt", /* 55 */ -- "clone", /* 56 */ -- "fork", /* 57 */ -- "vfork", /* 58 */ -- "execve", /* 59 */ -- "exit", /* 60 */ -- "wait4", /* 61 */ -- "kill", /* 62 */ -- "uname", /* 63 */ -- "semget", /* 64 */ -- "semop", /* 65 */ -- "semctl", /* 66 */ -- "shmdt", /* 67 */ -- "msgget", /* 68 */ -- "msgsnd", /* 69 */ -- "msgrcv", /* 70 */ -- "msgctl", /* 71 */ -- "fcntl", /* 72 */ -- "flock", /* 73 */ -- "fsync", /* 74 */ -- "fdatasync", /* 75 */ -- "truncate", /* 76 */ -- "ftruncate", /* 77 */ -- "getdents", /* 78 */ -- "getcwd", /* 79 */ -- "chdir", /* 80 */ -- "fchdir", /* 81 */ -- "rename", /* 82 */ -- "mkdir", /* 83 */ -- "rmdir", /* 84 */ -- "creat", /* 85 */ -- "link", /* 86 */ -- "unlink", /* 87 */ -- "symlink", /* 88 */ -- "readlink", /* 89 */ -- "chmod", /* 90 */ -- "fchmod", /* 91 */ -- "chown", /* 92 */ -- "fchown", /* 93 */ -- "lchown", /* 94 */ -- "umask", /* 95 */ -- "gettimeofday", /* 96 */ -- "getrlimit", /* 97 */ -- "getrusage", /* 98 */ -- "sysinfo", /* 99 */ -- "times", /* 100 */ -- "ptrace", /* 101 */ -- "getuid", /* 102 */ -- "syslog", /* 103 */ -- "getgid", /* 104 */ -- "setuid", /* 105 */ -- "setgid", /* 106 */ -- "geteuid", /* 107 */ -- "getegid", /* 108 */ -- "setpgid", /* 109 */ -- "getppid", /* 110 */ -- "getpgrp", /* 111 */ -- "setsid", /* 112 */ -- "setreuid", /* 113 */ -- "setregid", /* 114 */ -- "getgroups", /* 115 */ -- "setgroups", /* 116 */ -- "setresuid", /* 117 */ -- "getresuid", /* 118 */ -- "setresgid", /* 119 */ -- "getresgid", /* 120 */ -- "getpgid", /* 121 */ -- "setfsuid", /* 122 */ -- "setfsgid", /* 123 */ -- "getsid", /* 124 */ -- "capget", /* 125 */ -- "capset", /* 126 */ -- "rt_sigpending", /* 127 */ -- "rt_sigtimedwait", /* 128 */ -- "rt_sigqueueinfo", /* 129 */ -- "rt_sigsuspend", /* 130 */ -- "sigaltstack", /* 131 */ -- "utime", /* 132 */ -- "mknod", /* 133 */ -- "uselib", /* 134 */ -- "personality", /* 135 */ -- "ustat", /* 136 */ -- "statfs", /* 137 */ -- "fstatfs", /* 138 */ -- "sysfs", /* 139 */ -- "getpriority", /* 140 */ -- "setpriority", /* 141 */ -- "sched_setparam", /* 142 */ -- "sched_getparam", /* 143 */ -- "sched_setscheduler", /* 144 */ -- "sched_getscheduler", /* 145 */ -- "sched_get_priority_max", /* 146 */ -- "sched_get_priority_min", /* 147 */ -- "sched_rr_get_interval", /* 148 */ -- "mlock", /* 149 */ -- "munlock", /* 150 */ -- "mlockall", /* 151 */ -- "munlockall", /* 152 */ -- "vhangup", /* 153 */ -- "modify_ldt", /* 154 */ -- "pivot_root", /* 155 */ -- "_sysctl", /* 156 */ -- "prctl", /* 157 */ -- "arch_prctl", /* 158 */ -- "adjtimex", /* 159 */ -- "setrlimit", /* 160 */ -- "chroot", /* 161 */ -- "sync", /* 162 */ -- "acct", /* 163 */ -- "settimeofday", /* 164 */ -- "mount", /* 165 */ -- "umount2", /* 166 */ -- "swapon", /* 167 */ -- "swapoff", /* 168 */ -- "reboot", /* 169 */ -- "sethostname", /* 170 */ -- "setdomainname", /* 171 */ -- "iopl", /* 172 */ -- "ioperm", /* 173 */ -- "create_module", /* 174 */ -- "init_module", /* 175 */ -- "delete_module", /* 176 */ -- "get_kernel_syms", /* 177 */ -- "query_module", /* 178 */ -- "quotactl", /* 179 */ -- "nfsservctl", /* 180 */ -- "getpmsg", /* 181 */ -- "putpmsg", /* 182 */ -- "afs_syscall", /* 183 */ -- "tuxcall", /* 184 */ -- "security", /* 185 */ -- "gettid", /* 186 */ -- "readahead", /* 187 */ -- "setxattr", /* 188 */ -- "lsetxattr", /* 189 */ -- "fsetxattr", /* 190 */ -- "getxattr", /* 191 */ -- "lgetxattr", /* 192 */ -- "fgetxattr", /* 193 */ -- "listxattr", /* 194 */ -- "llistxattr", /* 195 */ -- "flistxattr", /* 196 */ -- "removexattr", /* 197 */ -- "lremovexattr", /* 198 */ -- "fremovexattr", /* 199 */ -- "tkill", /* 200 */ -- "time", /* 201 */ -- "futex", /* 202 */ -- "sched_setaffinity", /* 203 */ -- "sched_getaffinity", /* 204 */ -- "set_thread_area", /* 205 */ -- "io_setup", /* 206 */ -- "io_destroy", /* 207 */ -- "io_getevents", /* 208 */ -- "io_submit", /* 209 */ -- "io_cancel", /* 210 */ -- "get_thread_area", /* 211 */ -- "lookup_dcookie", /* 212 */ -- "epoll_create", /* 213 */ -- "epoll_ctl", /* 214 */ -- "epoll_wait", /* 215 */ -- "remap_file_pages", /* 216 */ -- "getdents64", /* 217 */ -- "set_tid_address", /* 218 */ -- "restart_syscall", /* 219 */ -- "semtimedop", /* 220 */ -- "fadvise64", /* 221 */ -- "timer_create", /* 222 */ -- "timer_settime", /* 223 */ -- "timer_gettime", /* 224 */ -- "timer_getoverrun", /* 225 */ -- "timer_delete", /* 226 */ -- "clock_settime", /* 227 */ -- "clock_gettime", /* 228 */ -- "clock_getres", /* 229 */ -- "clock_nanosleep", /* 230 */ -- "exit_group", /* 231 */ -- "epoll_wait", /* 232 */ -- "epoll_ctl", /* 233 */ -- "tgkill", /* 234 */ -- "utimes", /* 235 */ -- "vserver", /* 236 */ -- "mbind", /* 237 */ -- "set_mempolicy", /* 238 */ -- "get_mempolicy", /* 239 */ -- "mq_open", /* 240 */ -- "mq_unlink", /* 241 */ -- "mq_timedsend", /* 242 */ -- "mq_timedreceive", /* 243 */ -- "mq_notify", /* 244 */ -- "mq_getsetattr", /* 245 */ -- "kexec_load", /* 246 */ -- "waitid", /* 247 */ -- "add_key", /* 248 */ -- "request_key", /* 249 */ -- "keyctl", /* 250 */ -- "ioprio_set", /* 251 */ -- "ioprio_get", /* 252 */ -- "inotify_init", /* 253 */ -- "inotify_add_watch", /* 254 */ -- "inotify_rm_watch", /* 255 */ -diff --git a/sysdeps/linux-gnu/x86_64/syscallent1.h b/sysdeps/linux-gnu/x86_64/syscallent1.h -deleted file mode 100644 -index d8dd9f7..0000000 ---- a/sysdeps/linux-gnu/x86_64/syscallent1.h -+++ /dev/null -@@ -1 +0,0 @@ --#include "i386/syscallent.h" -diff --git a/sysdeps/linux-gnu/x86_64/trace.c b/sysdeps/linux-gnu/x86_64/trace.c -deleted file mode 100644 -index 0d3f693..0000000 ---- a/sysdeps/linux-gnu/x86_64/trace.c -+++ /dev/null -@@ -1,201 +0,0 @@ --#include "config.h" -- --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "common.h" --#include "ptrace.h" --#include "proc.h" -- --#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) --# define PTRACE_PEEKUSER PTRACE_PEEKUSR --#endif -- --#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) --# define PTRACE_POKEUSER PTRACE_POKEUSR --#endif -- --void --get_arch_dep(Process *proc) { -- proc_archdep *a; -- -- if (!proc->arch_ptr) -- proc->arch_ptr = (void *)malloc(sizeof(proc_archdep)); -- -- a = (proc_archdep *) (proc->arch_ptr); -- a->valid = (ptrace(PTRACE_GETREGS, proc->pid, 0, &a->regs) >= 0); -- if (a->valid) { -- a->valid = (ptrace(PTRACE_GETFPREGS, proc->pid, 0, &a->fpregs) >= 0); -- } -- if (a->regs.cs == 0x23) { -- proc->mask_32bit = 1; -- proc->personality = 1; -- } --} -- --/* Returns 1 if syscall, 2 if sysret, 0 otherwise. -- */ --int --syscall_p(struct Process *proc, int status, int *sysnum) --{ -- if (WIFSTOPPED(status) -- && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { -- struct callstack_element *elem = NULL; -- if (proc->callstack_depth > 0) -- elem = proc->callstack + proc->callstack_depth - 1; -- -- long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * ORIG_RAX, 0); -- if (ret == -1) { -- if (errno) -- return -1; -- /* Otherwise, ORIG_RAX == -1 means that the -- * system call should not be restarted. In -- * that case rely on what we have on -- * stack. */ -- if (elem != NULL && elem->is_syscall) -- ret = elem->c_un.syscall; -- } -- -- *sysnum = ret; -- debug(DEBUG_FUNCTION, "sysnum=%ld %p %d\n", ret, -- get_instruction_pointer(proc), errno); -- if (elem != NULL && elem->is_syscall -- && elem->c_un.syscall == *sysnum) -- return 2; -- -- if (*sysnum >= 0) -- return 1; -- } -- return 0; --} -- --static unsigned int --gimme_arg32(enum tof type, Process *proc, int arg_num) { -- proc_archdep *a = (proc_archdep *) proc->arch_ptr; -- -- if (arg_num == -1) { /* return value */ -- return a->regs.rax; -- } -- -- if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { -- return ptrace(PTRACE_PEEKTEXT, proc->pid, -- proc->stack_pointer + 4 * (arg_num + 1), 0); -- } else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) { -- switch (arg_num) { -- case 0: -- return a->regs.rbx; -- case 1: -- return a->regs.rcx; -- case 2: -- return a->regs.rdx; -- case 3: -- return a->regs.rsi; -- case 4: -- return a->regs.rdi; -- case 5: -- return a->regs.rbp; -- default: -- fprintf(stderr, -- "gimme_arg32 called with wrong arguments\n"); -- exit(2); -- } -- } -- fprintf(stderr, "gimme_arg called with wrong arguments\n"); -- exit(1); --} -- --static long --gimme_arg_regset(Process *proc, int arg_num, arg_type_info *info, -- struct user_regs_struct *regs, -- struct user_fpregs_struct *fpregs) --{ -- union { -- uint32_t sse[4]; -- long lval; -- float fval; -- double dval; -- } cvt; -- -- if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) { -- memcpy(cvt.sse, fpregs->xmm_space + 4*arg_num, -- sizeof(cvt.sse)); -- return cvt.lval; -- } -- -- switch (arg_num) { -- case 0: -- return regs->rdi; -- case 1: -- return regs->rsi; -- case 2: -- return regs->rdx; -- case 3: -- return regs->rcx; -- case 4: -- return regs->r8; -- case 5: -- return regs->r9; -- default: -- return ptrace(PTRACE_PEEKTEXT, proc->pid, -- proc->stack_pointer + 8 * (arg_num - 6 + 1), 0); -- } --} --static long --gimme_retval(Process *proc, int arg_num, arg_type_info *info, -- struct user_regs_struct *regs, struct user_fpregs_struct *fpregs) --{ -- if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) -- return gimme_arg_regset(proc, 0, info, regs, fpregs); -- else -- return regs->rax; --} -- --long --gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { -- if (proc->mask_32bit) -- return (unsigned int)gimme_arg32(type, proc, arg_num); -- -- proc_archdep *arch = (proc_archdep *)proc->arch_ptr; -- -- if (arch == NULL || !arch->valid) -- return -1; -- -- if (type == LT_TOF_FUNCTIONR) { -- if (arg_num == -1) -- return gimme_retval(proc, arg_num, info, -- &arch->regs, &arch->fpregs); -- else { -- struct callstack_element *elem -- = proc->callstack + proc->callstack_depth - 1; -- callstack_achdep *csad = elem->arch_ptr; -- assert(csad != NULL); -- return gimme_arg_regset(proc, arg_num, info, -- &csad->regs_copy, -- &csad->fpregs_copy); -- } -- } -- else -- return gimme_arg_regset(proc, arg_num, info, -- &arch->regs, &arch->fpregs); --} -- --void --save_register_args(enum tof type, Process *proc) { -- proc_archdep *arch = (proc_archdep *)proc->arch_ptr; -- if (arch == NULL || !arch->valid) -- return; -- -- callstack_achdep *csad = malloc(sizeof(*csad)); -- memset(csad, 0, sizeof(*csad)); -- memcpy(&csad->regs_copy, &arch->regs, sizeof(arch->regs)); -- memcpy(&csad->fpregs_copy, &arch->fpregs, sizeof(arch->fpregs)); -- -- proc->callstack[proc->callstack_depth - 1].arch_ptr = csad; --} -diff --git a/testsuite/ltrace.main/parameters-lib.c b/testsuite/ltrace.main/parameters-lib.c -index ef98143..f9a869d 100644 ---- a/testsuite/ltrace.main/parameters-lib.c -+++ b/testsuite/ltrace.main/parameters-lib.c -@@ -27,6 +27,11 @@ void func_strfixed(char* p) - strcpy(p, "Hello world"); - } - -+void func_string(char* p) -+{ -+ printf("%s\n", p); -+} -+ - void func_ppp(int*** ppp) - { - printf("%d\n", ***ppp); -@@ -133,3 +138,106 @@ void func_call (char *x, char* y, void (*cb) (char *)) - cb (y); - *x = (*y)++; - } -+ -+struct S2 { -+ float f; -+ char a; -+ char b; -+}; -+ -+struct S3 { -+ char a[6]; -+ float f; -+}; -+ -+struct S2 -+func_struct_2(int i, struct S3 s3, double d) -+{ -+ return (struct S2){ s3.f, s3.a[1], s3.a[2] }; -+} -+ -+struct S4 { -+ long a; -+ long b; -+ long c; -+ long d; -+}; -+ -+struct S4 -+func_struct_large(struct S4 a, struct S4 b) -+{ -+ return (struct S4){ a.a + b.a, a.b + b.b, a.c + b.c, a.d + b.d }; -+} -+ -+struct S5 { -+ char a; -+ char b; -+ long c; -+ long d; -+}; -+ -+struct S5 -+func_struct_large2(struct S5 a, struct S5 b) -+{ -+ return (struct S5){ a.a + b.a, a.b + b.b, a.c + b.c, a.d + b.d }; -+} -+ -+struct S6 { -+ long a; -+ long b; -+ char c; -+ char d; -+}; -+ -+struct S6 -+func_struct_large3(struct S6 a, struct S6 b) -+{ -+ return (struct S6){ a.a + b.a, a.b + b.b, a.c + b.c, a.d + b.d }; -+} -+ -+void -+func_many_args(int a, int b, long c, double d, char e, int f, float g, char h, -+ int i, double j, int k, double l, char m, int n, short o, int p, -+ char q, float r, float s, double t, long u, float v, float w, -+ float x, float y) -+{ -+} -+ -+void -+func_lens(int a, long b, short c, long d) -+{ -+} -+ -+int -+func_bool(int a, int b) -+{ -+ return !b; -+} -+ -+void -+func_hide(int a, int b, int c, int d, int e, int f) -+{ -+} -+ -+long * -+func_short_enums(short values[]) -+{ -+ static long retvals[4]; -+ retvals[0] = values[0]; -+ retvals[1] = values[1]; -+ retvals[2] = values[2]; -+ retvals[3] = values[3]; -+ return retvals; -+} -+ -+long -+func_negative_enum(short a, unsigned short b, int c, unsigned d, -+ long e, unsigned long f) -+{ -+ return -1; -+} -+ -+void -+func_charp_string(char *p) -+{ -+} -diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c -index fb46dfe..e8207fe 100644 ---- a/testsuite/ltrace.main/parameters.c -+++ b/testsuite/ltrace.main/parameters.c -@@ -59,6 +59,7 @@ main () - - func_intptr_ret(&x); - -+ func_string("zero\0xxxxxxxxxxxxxx"); - func_strlen(buf); - printf("%s\n", buf); - -@@ -133,5 +134,89 @@ main () - func_call(x, y, call_func_work); - } - -+ struct S2 { -+ float f; -+ char a; -+ char b; -+ }; -+ struct S3 { -+ char a[6]; -+ float f; -+ }; -+ struct S2 func_struct_2(int, struct S3 s3, double d); -+ func_struct_2(17, (struct S3){ "ABCDE", 0.25 }, 0.5); -+ -+ struct S4 { -+ long a; -+ long b; -+ long c; -+ long d; -+ }; -+ struct S4 func_struct_large(struct S4 a, struct S4 b); -+ func_struct_large((struct S4){ 1, 2, 3, 4 }, (struct S4){ 5, 6, 7, 8 }); -+ -+ struct S5 { -+ char a; -+ char b; -+ long c; -+ long d; -+ }; -+ struct S5 func_struct_large2(struct S5 a, struct S5 b); -+ func_struct_large2((struct S5){ '0', '1', 3, 4 }, (struct S5){ '2', '3', 7, 8 }); -+ -+ struct S6 { -+ long a; -+ long b; -+ char c; -+ char d; -+ }; -+ struct S6 func_struct_large3(struct S6 a, struct S6 b); -+ func_struct_large3((struct S6){ 3, 4, '0', '1' }, (struct S6){ 7, 8 ,'2', '3' }); -+ -+ void func_many_args(int a, int b, long c, double d, char e, int f, float g, -+ char h, int i, double j, int k, double l, char m, int n, -+ short o, int p, char q, float r, float s, double t, -+ long u, float v, float w, float x, float y); -+ func_many_args(1, 2, 3, 4.0, '5', 6, 7.0, -+ '8', 9, 10.0, 11, 12.0, 'A', 14, -+ 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" -+ "%d %d %ld %g %c %d %g " -+ "%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, -+ '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); -+ -+ printf("sotnuh3 %*s\n", 4, "a trimmed string"); -+ -+ void func_lens(int, long, short, long); -+ func_lens(22, 23, 24, 25); -+ -+ int func_bool(int a, int b); -+ func_bool(1, 10); -+ func_bool(2, 0); -+ -+ void func_hide(int a, int b, int c, int d, int e, int f); -+ func_hide(1, 2, 3, 4, 5, 6); -+ -+ enum ab { A, B }; -+ long *func_short_enums(short abs[]); -+ func_short_enums((short[]){ A, B, A, A }); -+ -+ long func_negative_enum(short a, unsigned short b, int c, unsigned d, -+ long e, unsigned long f); -+ func_negative_enum(-1, -1, -1, -1, -1, -1); -+ -+ void func_charp_string(char *p); -+ func_charp_string("null-terminated string"); -+ - return 0; - } -diff --git a/testsuite/ltrace.main/parameters.conf b/testsuite/ltrace.main/parameters.conf -index e94ce2c..9e0c967 100644 ---- a/testsuite/ltrace.main/parameters.conf -+++ b/testsuite/ltrace.main/parameters.conf -@@ -1,8 +1,9 @@ - void func_intptr(int*) - void func_intptr_ret(+int*) -+void func_string(string[10]) - int func_strlen(+string[retval]) - void func_strfixed(string[4]) --void func_ppp(int***) -+void func_ppp(int * **) - void func_stringp(string*) - void func_enum(enum (RED=0,GREEN=1,BLUE=2,CHARTREUSE=3,PETUNIA=4)) - void func_short(short,short) -@@ -11,8 +12,20 @@ float func_float(float,float) - double func_double(double,double) - typedef color = enum (RED=0,GREEN=1,BLUE=2,CHARTREUSE=3,PETUNIA=4) - void func_typedef(color) --void func_arrayi(array(int,arg2)*,void) --void func_arrayf(array(float,arg2)*,void) -+void func_arrayi(array(int,arg2)*,int) -+void func_arrayf(array(float,arg2)*,int) - void func_struct(struct(int,int,int,array(struct(int,int),elt2)*,array(struct(int,int),3),string[elt3])*) - void func_work(+string); - void func_call(+string, string); -+struct(float,char,char) func_struct_2(int, struct(string(array(char, 6)),float), double); -+struct(long,long,long,long) func_struct_large(struct(long,long,long,long), struct(long,long,long,long)); -+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_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); -+array(enum[long](A,B), 4) *func_short_enums(array(enum[short](A,B), 4)); -+enum[long](A=-1) func_negative_enum(enum[short](A=-1), enum[ushort](A=-1), enum[int](A=-1), enum[uint](A=-1), enum[long](A=-1), enum[ulong](A=-1)); -+void func_charp_string(string(char *)); -diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp -index 924afaf..8403721 100644 ---- a/testsuite/ltrace.main/parameters.exp -+++ b/testsuite/ltrace.main/parameters.exp -@@ -35,7 +35,8 @@ if [regexp {ELF from incompatible architecture} $exec_output] { - return - } - --set xfail_spec {"arm*-*" "i*86-*"} -+set xfail_spec {"arm*-*" } -+set xfail_spec_arm {"arm*-*"} - - # Verify the output - set pattern "func_intptr(17)" -@@ -50,6 +51,8 @@ set pattern "func_ppp(80)" - ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 - set pattern "func_stringp(\\\"Dude\\\")" - ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+set pattern "func_string(\\\"zero\\\")" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 - set pattern "func_enum(BLUE)" - ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 - set pattern "func_short(-8, -9)" -@@ -63,13 +66,13 @@ set pattern "func_double(3.40*, -3.40*).*= -3.40*" - ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 - set pattern "func_typedef(BLUE)" - ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 --set pattern "func_arrayi(. 10, 11, 12, 13\\.\\.\\. ., )" -+set pattern "func_arrayi(. 10, 11, 12, 13\\.\\.\\. ., 8)" - ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 --set pattern "func_arrayi(. 10, 11 ., )" -+set pattern "func_arrayi(. 10, 11 ., 2)" - ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 --set pattern "func_arrayf(. 10.10*, 11.10*, 12.10*, 13.10*\\.\\.\\. ., )" -+set pattern "func_arrayf(. 10.10*, 11.10*, 12.10*, 13.10*\\.\\.\\. ., 8)" - ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 --set pattern "func_arrayf(. 10.10*, 11.10* ., )" -+set pattern "func_arrayf(. 10.10*, 11.10* ., 2)" - ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 - set pattern "exited (status 0)" - ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -@@ -79,6 +82,56 @@ set pattern "func_call( " - ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 - set pattern "func_work(\\\"x\\\")" - ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 --eval "setup_xfail $xfail_spec" -+set pattern "func_struct_2(17, { \\\"ABCDE\\\\\\\\0\\\", 0.250* }, 0.50*).*= { 0.250*, 'B', 'C' }" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+eval "setup_xfail $xfail_spec_arm" - set pattern "<... func_call resumed> \\\"x\\\", \\\"y\\\")" - ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ -+set pattern "func_struct_large({ 1, 2, 3, 4 }, { 5, 6, 7, 8 }).*= { 6, 8, 10, 12 }" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ -+set pattern "func_struct_large2({ '0', '1', 3, 4 }, { '2', '3', 7, 8 }).*= { 'b', 'd', 10, 12 }" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ -+set pattern "func_struct_large3({ 3, 4, '0', '1' }, { 7, 8, '2', '3' }).*= { 10, 12, 'b', 'd' }" -+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')" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ -+set pattern "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\\\")" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ -+set pattern "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*)" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ -+set pattern "func_lens(026, 027, 0x18, 0x19)" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ -+set pattern "func_bool(1, true).*= false" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ -+set pattern "func_bool(2, false).*= true" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ -+set pattern "func_hide(1, 4, 6)" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ -+set pattern "func_short_enums(. A, B, A, A .).*= . A, B, A, A ." -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ -+set pattern "func_negative_enum(A, A, A, A, A, A).*= A" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ -+set pattern "func_charp_string(\\\"null-terminated string\\\")" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -diff --git a/type.c b/type.c -new file mode 100644 -index 0000000..6341042 ---- /dev/null -+++ b/type.c -@@ -0,0 +1,515 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2007,2008 Juan Cespedes -+ * -+ * 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 "type.h" -+#include "sysdep.h" -+#include "expr.h" -+#include "lens.h" -+ -+struct arg_type_info * -+type_get_simple(enum arg_type type) -+{ -+#define HANDLE(T) { \ -+ static struct arg_type_info t = { T }; \ -+ case T: \ -+ return &t; \ -+ } -+ -+ switch (type) { -+ HANDLE(ARGTYPE_VOID) -+ HANDLE(ARGTYPE_INT) -+ HANDLE(ARGTYPE_UINT) -+ HANDLE(ARGTYPE_LONG) -+ HANDLE(ARGTYPE_ULONG) -+ HANDLE(ARGTYPE_CHAR) -+ HANDLE(ARGTYPE_SHORT) -+ HANDLE(ARGTYPE_USHORT) -+ HANDLE(ARGTYPE_FLOAT) -+ HANDLE(ARGTYPE_DOUBLE) -+ -+#undef HANDLE -+ -+ case ARGTYPE_ARRAY: -+ case ARGTYPE_STRUCT: -+ case ARGTYPE_POINTER: -+ assert(!"Not a simple type!"); -+ }; -+ abort(); -+} -+ -+static void -+type_init_common(struct arg_type_info *info, enum arg_type type) -+{ -+ info->type = type; -+ info->lens = NULL; -+ info->own_lens = 0; -+} -+ -+struct struct_field { -+ struct arg_type_info *info; -+ int own_info; -+}; -+ -+void -+type_init_struct(struct arg_type_info *info) -+{ -+ type_init_common(info, ARGTYPE_STRUCT); -+ VECT_INIT(&info->u.entries, struct struct_field); -+} -+ -+int -+type_struct_add(struct arg_type_info *info, -+ struct arg_type_info *field_info, int own) -+{ -+ assert(info->type == ARGTYPE_STRUCT); -+ struct struct_field field = { field_info, own }; -+ return VECT_PUSHBACK(&info->u.entries, &field); -+} -+ -+struct arg_type_info * -+type_struct_get(struct arg_type_info *info, size_t idx) -+{ -+ assert(info->type == ARGTYPE_STRUCT); -+ struct struct_field *field = VECT_ELEMENT(&info->u.entries, -+ struct struct_field, idx); -+ if (field == NULL) -+ return NULL; -+ return field->info; -+} -+ -+size_t -+type_struct_size(struct arg_type_info *info) -+{ -+ assert(info->type == ARGTYPE_STRUCT); -+ return vect_size(&info->u.entries); -+} -+ -+static void -+struct_field_dtor(struct struct_field *field, void *data) -+{ -+ if (field->own_info) { -+ type_destroy(field->info); -+ free(field->info); -+ } -+} -+ -+static void -+type_struct_destroy(struct arg_type_info *info) -+{ -+ VECT_DESTROY(&info->u.entries, struct struct_field, -+ struct_field_dtor, NULL); -+} -+ -+static int -+layout_struct(struct Process *proc, struct arg_type_info *info, -+ size_t *sizep, size_t *alignmentp, size_t *offsetofp) -+{ -+ size_t sz = 0; -+ size_t max_alignment = 0; -+ size_t i; -+ size_t offsetof_field = (size_t)-1; -+ if (offsetofp != NULL) -+ offsetof_field = *offsetofp; -+ -+ assert(info->type == ARGTYPE_STRUCT); -+ for (i = 0; i < vect_size(&info->u.entries); ++i) { -+ struct struct_field *field -+ = VECT_ELEMENT(&info->u.entries, -+ struct struct_field, i); -+ -+ size_t alignment = type_alignof(proc, field->info); -+ if (alignment == (size_t)-1) -+ return -1; -+ -+ /* Add padding to SZ to align the next element. */ -+ sz = align(sz, alignment); -+ if (i == offsetof_field) { -+ *offsetofp = sz; -+ if (sizep == NULL && alignmentp == NULL) -+ return 0; -+ } -+ -+ size_t size = type_sizeof(proc, field->info); -+ if (size == (size_t)-1) -+ return -1; -+ sz += size; -+ -+ if (alignment > max_alignment) -+ max_alignment = alignment; -+ } -+ -+ if (max_alignment > 0) -+ sz = align(sz, max_alignment); -+ -+ if (sizep != NULL) -+ *sizep = sz; -+ -+ if (alignmentp != NULL) -+ *alignmentp = max_alignment; -+ -+ return 0; -+} -+ -+void -+type_init_array(struct arg_type_info *info, -+ struct arg_type_info *element_info, int own_info, -+ struct expr_node *length_expr, int own_length) -+{ -+ type_init_common(info, ARGTYPE_ARRAY); -+ info->u.array_info.elt_type = element_info; -+ info->u.array_info.own_info = own_info; -+ info->u.array_info.length = length_expr; -+ info->u.array_info.own_length = own_length; -+} -+ -+static void -+type_array_destroy(struct arg_type_info *info) -+{ -+ if (info->u.array_info.own_info) { -+ type_destroy(info->u.array_info.elt_type); -+ free(info->u.array_info.elt_type); -+ } -+ if (info->u.array_info.own_length) { -+ expr_destroy(info->u.array_info.length); -+ free(info->u.array_info.length); -+ } -+} -+ -+void -+type_init_pointer(struct arg_type_info *info, -+ struct arg_type_info *pointee_info, int own_info) -+{ -+ type_init_common(info, ARGTYPE_POINTER); -+ info->u.ptr_info.info = pointee_info; -+ info->u.ptr_info.own_info = own_info; -+} -+ -+static void -+type_pointer_destroy(struct arg_type_info *info) -+{ -+ if (info->u.ptr_info.own_info) { -+ type_destroy(info->u.ptr_info.info); -+ free(info->u.ptr_info.info); -+ } -+} -+ -+void -+type_destroy(struct arg_type_info *info) -+{ -+ if (info == NULL) -+ return; -+ -+ switch (info->type) { -+ case ARGTYPE_STRUCT: -+ type_struct_destroy(info); -+ break; -+ -+ case ARGTYPE_ARRAY: -+ type_array_destroy(info); -+ break; -+ -+ case ARGTYPE_POINTER: -+ type_pointer_destroy(info); -+ break; -+ -+ case ARGTYPE_VOID: -+ case ARGTYPE_INT: -+ case ARGTYPE_UINT: -+ case ARGTYPE_LONG: -+ case ARGTYPE_ULONG: -+ case ARGTYPE_CHAR: -+ case ARGTYPE_SHORT: -+ case ARGTYPE_USHORT: -+ case ARGTYPE_FLOAT: -+ case ARGTYPE_DOUBLE: -+ break; -+ } -+ -+ if (info->own_lens) { -+ lens_destroy(info->lens); -+ free(info->lens); -+ } -+} -+ -+#ifdef ARCH_HAVE_SIZEOF -+size_t arch_type_sizeof(struct Process *proc, struct arg_type_info * arg); -+#else -+size_t -+arch_type_sizeof(struct Process *proc, struct arg_type_info * arg) -+{ -+ /* Use default value. */ -+ return (size_t)-2; -+} -+#endif -+ -+#ifdef ARCH_HAVE_ALIGNOF -+size_t arch_type_alignof(struct Process *proc, struct arg_type_info * arg); -+#else -+size_t -+arch_type_alignof(struct Process *proc, struct arg_type_info * arg) -+{ -+ /* Use default value. */ -+ return (size_t)-2; -+} -+#endif -+ -+/* We need to support alignments that are not power of two. E.g. long -+ * double on x86 has alignment of 12. */ -+size_t -+align(size_t sz, size_t alignment) -+{ -+ assert(alignment != 0); -+ -+ if ((sz % alignment) != 0) -+ sz = ((sz / alignment) + 1) * alignment; -+ -+ return sz; -+} -+ -+size_t -+type_sizeof(struct Process *proc, struct arg_type_info *type) -+{ -+ size_t arch_size = arch_type_sizeof(proc, type); -+ if (arch_size != (size_t)-2) -+ return arch_size; -+ -+ switch (type->type) { -+ size_t size; -+ case ARGTYPE_CHAR: -+ return sizeof(char); -+ -+ case ARGTYPE_SHORT: -+ case ARGTYPE_USHORT: -+ return sizeof(short); -+ -+ case ARGTYPE_INT: -+ case ARGTYPE_UINT: -+ return sizeof(int); -+ -+ case ARGTYPE_LONG: -+ case ARGTYPE_ULONG: -+ return sizeof(long); -+ -+ case ARGTYPE_FLOAT: -+ return sizeof(float); -+ -+ case ARGTYPE_DOUBLE: -+ return sizeof(double); -+ -+ case ARGTYPE_STRUCT: -+ if (layout_struct(proc, type, &size, NULL, NULL) < 0) -+ return (size_t)-1; -+ return size; -+ -+ case ARGTYPE_POINTER: -+ return sizeof(void *); -+ -+ case ARGTYPE_ARRAY: -+ if (expr_is_compile_constant(type->u.array_info.length)) { -+ long l; -+ if (expr_eval_constant(type->u.array_info.length, -+ &l) < 0) -+ return -1; -+ -+ struct arg_type_info *elt_ti -+ = type->u.array_info.elt_type; -+ -+ size_t elt_size = type_sizeof(proc, elt_ti); -+ if (elt_size == (size_t)-1) -+ return (size_t)-1; -+ -+ return ((size_t)l) * elt_size; -+ -+ } else { -+ /* Flexible arrays don't count into the -+ * sizeof. */ -+ return 0; -+ } -+ -+ case ARGTYPE_VOID: -+ return 0; -+ } -+ -+ abort(); -+} -+ -+#undef alignof -+#define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st)) -+ -+size_t -+type_alignof(struct Process *proc, struct arg_type_info *type) -+{ -+ size_t arch_alignment = arch_type_alignof(proc, type); -+ if (arch_alignment != (size_t)-2) -+ return arch_alignment; -+ -+ struct { char c; char C; } cC; -+ struct { char c; short s; } cs; -+ struct { char c; int i; } ci; -+ struct { char c; long l; } cl; -+ struct { char c; void* p; } cp; -+ struct { char c; float f; } cf; -+ struct { char c; double d; } cd; -+ -+ static size_t char_alignment = alignof(C, cC); -+ static size_t short_alignment = alignof(s, cs); -+ static size_t int_alignment = alignof(i, ci); -+ static size_t long_alignment = alignof(l, cl); -+ static size_t ptr_alignment = alignof(p, cp); -+ static size_t float_alignment = alignof(f, cf); -+ static size_t double_alignment = alignof(d, cd); -+ -+ switch (type->type) { -+ size_t alignment; -+ case ARGTYPE_LONG: -+ case ARGTYPE_ULONG: -+ return long_alignment; -+ case ARGTYPE_CHAR: -+ return char_alignment; -+ case ARGTYPE_SHORT: -+ case ARGTYPE_USHORT: -+ return short_alignment; -+ case ARGTYPE_FLOAT: -+ return float_alignment; -+ case ARGTYPE_DOUBLE: -+ return double_alignment; -+ case ARGTYPE_POINTER: -+ return ptr_alignment; -+ -+ case ARGTYPE_ARRAY: -+ return type_alignof(proc, type->u.array_info.elt_type); -+ -+ case ARGTYPE_STRUCT: -+ if (layout_struct(proc, type, NULL, &alignment, NULL) < 0) -+ return (size_t)-1; -+ return alignment; -+ -+ default: -+ return int_alignment; -+ } -+} -+ -+size_t -+type_offsetof(struct Process *proc, struct arg_type_info *type, size_t emt) -+{ -+ assert(type->type == ARGTYPE_STRUCT -+ || type->type == ARGTYPE_ARRAY); -+ -+ switch (type->type) { -+ size_t alignment; -+ size_t size; -+ case ARGTYPE_ARRAY: -+ alignment = type_alignof(proc, type->u.array_info.elt_type); -+ if (alignment == (size_t)-1) -+ return (size_t)-1; -+ -+ size = type_sizeof(proc, type->u.array_info.elt_type); -+ if (size == (size_t)-1) -+ return (size_t)-1; -+ -+ return emt * align(size, alignment); -+ -+ case ARGTYPE_STRUCT: -+ if (layout_struct(proc, type, NULL, NULL, &emt) < 0) -+ return (size_t)-1; -+ return emt; -+ -+ default: -+ abort(); -+ } -+} -+ -+struct arg_type_info * -+type_element(struct arg_type_info *info, size_t emt) -+{ -+ assert(info->type == ARGTYPE_STRUCT -+ || info->type == ARGTYPE_ARRAY); -+ -+ switch (info->type) { -+ case ARGTYPE_ARRAY: -+ return info->u.array_info.elt_type; -+ -+ case ARGTYPE_STRUCT: -+ assert(emt < type_struct_size(info)); -+ return type_struct_get(info, emt); -+ -+ default: -+ abort(); -+ } -+} -+ -+int -+type_is_integral(enum arg_type type) -+{ -+ switch (type) { -+ case ARGTYPE_INT: -+ case ARGTYPE_UINT: -+ case ARGTYPE_LONG: -+ case ARGTYPE_ULONG: -+ case ARGTYPE_CHAR: -+ case ARGTYPE_SHORT: -+ case ARGTYPE_USHORT: -+ return 1; -+ -+ case ARGTYPE_VOID: -+ case ARGTYPE_FLOAT: -+ case ARGTYPE_DOUBLE: -+ case ARGTYPE_ARRAY: -+ case ARGTYPE_STRUCT: -+ case ARGTYPE_POINTER: -+ return 0; -+ } -+ abort(); -+} -+ -+int -+type_is_signed(enum arg_type type) -+{ -+ assert(type_is_integral(type)); -+ -+ switch (type) { -+ case ARGTYPE_CHAR: -+ return CHAR_MIN != 0; -+ -+ case ARGTYPE_SHORT: -+ case ARGTYPE_INT: -+ case ARGTYPE_LONG: -+ return 1; -+ -+ case ARGTYPE_UINT: -+ case ARGTYPE_ULONG: -+ case ARGTYPE_USHORT: -+ return 0; -+ -+ case ARGTYPE_VOID: -+ case ARGTYPE_FLOAT: -+ case ARGTYPE_DOUBLE: -+ case ARGTYPE_ARRAY: -+ case ARGTYPE_STRUCT: -+ case ARGTYPE_POINTER: -+ abort(); -+ } -+ abort(); -+} -diff --git a/type.h b/type.h -new file mode 100644 -index 0000000..545173c ---- /dev/null -+++ b/type.h -@@ -0,0 +1,133 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 1997-2009 Juan Cespedes -+ * -+ * 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 TYPE_H -+#define TYPE_H -+ -+#include -+#include "forward.h" -+#include "vect.h" -+ -+enum arg_type { -+ ARGTYPE_VOID, -+ ARGTYPE_INT, -+ ARGTYPE_UINT, -+ ARGTYPE_LONG, -+ ARGTYPE_ULONG, -+ ARGTYPE_CHAR, -+ ARGTYPE_SHORT, -+ ARGTYPE_USHORT, -+ ARGTYPE_FLOAT, -+ ARGTYPE_DOUBLE, -+ ARGTYPE_ARRAY, /* Series of values in memory */ -+ ARGTYPE_STRUCT, /* Structure of values */ -+ ARGTYPE_POINTER, /* Pointer to some other type */ -+}; -+ -+struct arg_type_info { -+ enum arg_type type; -+ union { -+ struct vect entries; -+ -+ /* ARGTYPE_ARRAY */ -+ struct { -+ struct arg_type_info *elt_type; -+ struct expr_node *length; -+ int own_info:1; -+ int own_length:1; -+ } array_info; -+ -+ /* ARGTYPE_POINTER */ -+ struct { -+ struct arg_type_info *info; -+ int own_info:1; -+ } ptr_info; -+ } u; -+ -+ struct lens *lens; -+ int own_lens; -+}; -+ -+/* Return a type info for simple type TYPE (which shall not be array, -+ * struct, or pointer. Each call with the same TYPE yields the same -+ * arg_type_info pointer. */ -+struct arg_type_info *type_get_simple(enum arg_type type); -+ -+/* Initialize INFO so it becomes ARGTYPE_STRUCT. The created -+ * structure contains no fields. Use type_struct_add to populate the -+ * structure. */ -+void type_init_struct(struct arg_type_info *info); -+ -+/* Add a new field of type FIELD_INFO to a structure INFO. If OWN, -+ * the field type is owned and destroyed together with INFO. */ -+int type_struct_add(struct arg_type_info *info, -+ struct arg_type_info *field_info, int own); -+ -+/* Get IDX-th field of structure type INFO. */ -+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); -+ -+/* 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 -+ * length are owned and destroyed together with INFO. */ -+void type_init_array(struct arg_type_info *info, -+ struct arg_type_info *element_info, int own_info, -+ struct expr_node *length_expr, int own_length); -+ -+/* Initialize INFO so it becomes ARGTYPE_POINTER. The pointee type is -+ * passed in POINTEE_INFO. If OWN_INFO, the pointee type is owned and -+ * destroyed together with INFO. */ -+void type_init_pointer(struct arg_type_info *info, -+ struct arg_type_info *pointee_info, int own_info); -+ -+/* Release any memory associated with INFO. Doesn't free INFO -+ * itself. */ -+void type_destroy(struct arg_type_info *info); -+ -+/* Compute a size of given type. Return (size_t)-1 for error. */ -+size_t type_sizeof(struct Process *proc, struct arg_type_info *type); -+ -+/* Compute an alignment necessary for elements of this type. Return -+ * (size_t)-1 for error. */ -+size_t type_alignof(struct Process *proc, struct arg_type_info *type); -+ -+/* Align value SZ to ALIGNMENT and return the result. */ -+size_t align(size_t sz, size_t alignment); -+ -+/* Return ELT-th element of compound type TYPE. This is useful for -+ * arrays and structures. */ -+struct arg_type_info *type_element(struct arg_type_info *type, size_t elt); -+ -+/* Compute an offset of EMT-th element of type TYPE. This works for -+ * arrays and structures. Return (size_t)-1 for error. */ -+size_t type_offsetof(struct Process *proc, -+ struct arg_type_info *type, size_t elt); -+ -+/* Whether TYPE is an integral type as defined by the C standard. */ -+int type_is_integral(enum arg_type type); -+ -+/* Whether TYPE, which shall be integral, is a signed type. */ -+int type_is_signed(enum arg_type type); -+ -+#endif /* TYPE_H */ -diff --git a/value.c b/value.c -new file mode 100644 -index 0000000..62466f2 ---- /dev/null -+++ b/value.c -@@ -0,0 +1,455 @@ -+/* -+ * 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 -+ */ -+ -+#include -+#include -+#include -+ -+#include "value.h" -+#include "type.h" -+#include "common.h" -+#include "expr.h" -+#include "backend.h" -+ -+static void -+value_common_init(struct value *valp, struct Process *inferior, -+ struct value *parent, struct arg_type_info *type, -+ int own_type) -+{ -+ valp->type = type; -+ valp->own_type = own_type; -+ valp->inferior = inferior; -+ memset(&valp->u, 0, sizeof(valp->u)); -+ valp->where = VAL_LOC_NODATA; -+ valp->parent = parent; -+ valp->size = (size_t)-1; -+} -+ -+void -+value_init(struct value *valp, struct Process *inferior, struct value *parent, -+ struct arg_type_info *type, int own_type) -+{ -+ assert(inferior != NULL); -+ value_common_init(valp, inferior, parent, type, own_type); -+} -+ -+void -+value_init_detached(struct value *valp, struct value *parent, -+ struct arg_type_info *type, int own_type) -+{ -+ value_common_init(valp, NULL, parent, type, own_type); -+} -+ -+void -+value_set_type(struct value *value, struct arg_type_info *type, int own_type) -+{ -+ if (value->own_type) -+ type_destroy(value->type); -+ value->type = type; -+ value->own_type = own_type; -+} -+ -+void -+value_take_type(struct value *value, struct arg_type_info **type, -+ int *own_type) -+{ -+ *type = value->type; -+ *own_type = value->own_type; -+ value->own_type = 0; -+} -+ -+void -+value_release(struct value *val) -+{ -+ if (val == NULL) -+ return; -+ if (val->where == VAL_LOC_COPY) { -+ free(val->u.address); -+ val->where = VAL_LOC_NODATA; -+ } -+} -+ -+void -+value_destroy(struct value *val) -+{ -+ if (val == NULL) -+ return; -+ value_release(val); -+ value_set_type(val, NULL, 0); -+} -+ -+unsigned char * -+value_reserve(struct value *valp, size_t size) -+{ -+ if (size <= sizeof(valp->u.value)) { -+ valp->where = VAL_LOC_WORD; -+ valp->u.value = 0; -+ } else { -+ valp->where = VAL_LOC_COPY; -+ valp->u.address = calloc(size, 1); -+ if (valp->u.address == 0) -+ return NULL; -+ } -+ return value_get_raw_data(valp); -+} -+ -+int -+value_reify(struct value *val, struct value_dict *arguments) -+{ -+ if (val->where != VAL_LOC_INFERIOR) -+ return 0; -+ assert(val->inferior != NULL); -+ -+ size_t size = value_size(val, arguments); -+ if (size == (size_t)-1) -+ return -1; -+ -+ void *data; -+ enum value_location_t nloc; -+ if (size <= sizeof(val->u.value)) { -+ data = &val->u.value; -+ nloc = VAL_LOC_WORD; -+ } else { -+ data = malloc(size); -+ if (data == NULL) -+ return -1; -+ nloc = VAL_LOC_COPY; -+ } -+ -+ if (umovebytes(val->inferior, val->u.address, data, size) < size) { -+ if (nloc == VAL_LOC_COPY) -+ free(data); -+ return -1; -+ } -+ -+ val->where = nloc; -+ if (nloc == VAL_LOC_COPY) -+ val->u.address = data; -+ -+ return 0; -+} -+ -+unsigned char * -+value_get_data(struct value *val, struct value_dict *arguments) -+{ -+ if (value_reify(val, arguments) < 0) -+ return NULL; -+ return value_get_raw_data(val); -+} -+ -+unsigned char * -+value_get_raw_data(struct value *val) -+{ -+ switch (val->where) { -+ case VAL_LOC_INFERIOR: -+ abort(); -+ case VAL_LOC_NODATA: -+ return NULL; -+ case VAL_LOC_COPY: -+ case VAL_LOC_SHARED: -+ return val->u.address; -+ case VAL_LOC_WORD: -+ return val->u.buf; -+ } -+ -+ assert(!"Unexpected value of val->where"); -+ abort(); -+} -+ -+int -+value_clone(struct value *retp, struct value *val) -+{ -+ *retp = *val; -+ if (val->where == VAL_LOC_COPY) { -+ assert(val->inferior != NULL); -+ size_t size = type_sizeof(val->inferior, val->type); -+ if (size == (size_t)-1) -+ return -1; -+ -+ retp->u.address = malloc(size); -+ if (retp->u.address == NULL) -+ return -1; -+ -+ memcpy(retp->u.address, val->u.address, size); -+ } -+ -+ return 0; -+} -+ -+size_t -+value_size(struct value *val, struct value_dict *arguments) -+{ -+ if (val->size != (size_t)-1) -+ return val->size; -+ -+ if (val->type->type != ARGTYPE_ARRAY) -+ return val->size = type_sizeof(val->inferior, val->type); -+ -+ struct value length; -+ if (expr_eval(val->type->u.array_info.length, val, -+ arguments, &length) < 0) -+ return (size_t)-1; -+ -+ size_t l; -+ int o = value_extract_word(&length, (long *)&l, arguments); -+ value_destroy(&length); -+ -+ if (o < 0) -+ return (size_t)-1; -+ -+ size_t elt_size = type_sizeof(val->inferior, -+ val->type->u.array_info.elt_type); -+ if (elt_size == (size_t)-1) -+ return (size_t)-1; -+ -+ return val->size = elt_size * l; -+} -+ -+int -+value_init_element(struct value *ret_val, struct value *val, size_t element) -+{ -+ size_t off = type_offsetof(val->inferior, val->type, element); -+ if (off == (size_t)-1) -+ return -1; -+ -+ struct arg_type_info *e_info = type_element(val->type, element); -+ if (e_info == NULL) -+ return -1; -+ -+ value_common_init(ret_val, val->inferior, val, e_info, 0); -+ -+ switch (val->where) { -+ case VAL_LOC_COPY: -+ case VAL_LOC_SHARED: -+ ret_val->u.address = val->u.address + off; -+ ret_val->where = VAL_LOC_SHARED; -+ return 0; -+ -+ case VAL_LOC_WORD: -+ ret_val->u.address = value_get_raw_data(val) + off; -+ ret_val->where = VAL_LOC_SHARED; -+ return 0; -+ -+ case VAL_LOC_INFERIOR: -+ ret_val->u.address = val->u.address + off; -+ ret_val->where = VAL_LOC_INFERIOR; -+ return 0; -+ -+ case VAL_LOC_NODATA: -+ assert(!"Can't offset NODATA."); -+ abort(); -+ } -+ abort(); -+} -+ -+int -+value_init_deref(struct value *ret_val, struct value *valp) -+{ -+ assert(valp->type->type == ARGTYPE_POINTER); -+ -+ /* Note: extracting a pointer value should not need value_dict -+ * with function arguments. */ -+ long l; -+ if (value_extract_word(valp, &l, NULL) < 0) -+ return -1; -+ -+ /* We need "long" to be long enough to hold platform -+ * pointers. */ -+ typedef char assert__long_enough_long[-(sizeof(l) < sizeof(void *))]; -+ -+ value_common_init(ret_val, valp->inferior, valp, -+ valp->type->u.ptr_info.info, 0); -+ ret_val->u.value = l; /* Set the address. */ -+ ret_val->where = VAL_LOC_INFERIOR; -+ return 0; -+} -+ -+/* The functions value_extract_buf and value_extract_word assume that -+ * data in VALUE is stored at the start of the internal buffer. For -+ * value_extract_buf in particular there's no other reasonable -+ * default. If we need to copy out four bytes, they need to be the -+ * bytes pointed to by the buffer pointer. -+ * -+ * But actually the situation is similar for value_extract_word as -+ * well. This function is used e.g. to extract characters from -+ * strings. Those weren't stored by value_set_word, they might still -+ * be in client for all we know. So value_extract_word has to assume -+ * that the whole of data is data is stored at the buffer pointer. -+ * -+ * This is a problem on big endian machines, where 2-byte quantity -+ * carried in 4- or 8-byte long is stored at the end of that long. -+ * (Though that quantity itself is still big endian.) So we need to -+ * make a little dance to shift the value to the right part of the -+ * buffer. */ -+ -+union word_data { -+ uint8_t u8; -+ uint16_t u16; -+ uint32_t u32; -+ uint64_t u64; -+ long l; -+ unsigned char buf[0]; -+} u; -+ -+void -+value_set_word(struct value *value, long word) -+{ -+ size_t sz = type_sizeof(value->inferior, value->type); -+ assert(sz != (size_t)-1); -+ assert(sz <= sizeof(value->u.value)); -+ -+ value->where = VAL_LOC_WORD; -+ -+ union word_data u = {}; -+ -+ switch (sz) { -+ case 0: -+ u.l = 0; -+ break; -+ case 1: -+ u.u8 = word; -+ break; -+ case 2: -+ u.u16 = word; -+ break; -+ case 4: -+ u.u32 = word; -+ break; -+ case 8: -+ u.u64 = word; -+ break; -+ default: -+ assert(sz != sz); -+ abort(); -+ } -+ -+ value->u.value = u.l; -+} -+ -+static int -+value_extract_buf_sz(struct value *value, unsigned char *tgt, size_t sz, -+ struct value_dict *arguments) -+{ -+ unsigned char *data = value_get_data(value, arguments); -+ if (data == NULL) -+ return -1; -+ -+ memcpy(tgt, data, sz); -+ return 0; -+} -+ -+int -+value_extract_word(struct value *value, long *retp, -+ struct value_dict *arguments) -+{ -+ size_t sz = type_sizeof(value->inferior, value->type); -+ if (sz == (size_t)-1) -+ return -1; -+ assert(sz <= sizeof(value->u.value)); -+ -+ if (sz == 0) { -+ *retp = 0; -+ return 0; -+ } -+ -+ union word_data u = {}; -+ if (value_extract_buf_sz(value, u.buf, sz, arguments) < 0) -+ return -1; -+ -+ switch (sz) { -+ case 1: -+ *retp = (long)u.u8; -+ return 0; -+ case 2: -+ *retp = (long)u.u16; -+ return 0; -+ case 4: -+ *retp = (long)u.u32; -+ return 0; -+ case 8: -+ *retp = (long)u.u64; -+ return 0; -+ default: -+ assert(sz != sz); -+ abort(); -+ } -+} -+ -+int -+value_extract_buf(struct value *value, unsigned char *tgt, -+ struct value_dict *arguments) -+{ -+ size_t sz = type_sizeof(value->inferior, value->type); -+ if (sz == (size_t)-1) -+ return -1; -+ -+ return value_extract_buf_sz(value, tgt, sz, arguments); -+} -+ -+struct value * -+value_get_parental_struct(struct value *val) -+{ -+ struct value *parent; -+ for (parent = val->parent; parent != NULL; parent = parent->parent) -+ if (parent->type->type == ARGTYPE_STRUCT) -+ return parent; -+ return NULL; -+} -+ -+int -+value_is_zero(struct value *val, struct value_dict *arguments) -+{ -+ unsigned char *data = value_get_data(val, arguments); -+ if (data == NULL) -+ return -1; -+ size_t sz = type_sizeof(val->inferior, val->type); -+ if (sz == (size_t)-1) -+ return -1; -+ -+ int zero = 1; -+ size_t j; -+ for (j = 0; j < sz; ++j) { -+ if (data[j] != 0) { -+ zero = 0; -+ break; -+ } -+ } -+ return zero; -+} -+ -+int -+value_pass_by_reference(struct value *value) -+{ -+ assert(value != NULL); -+ assert(value->type->type == ARGTYPE_STRUCT); -+ -+ struct arg_type_info *new_info = calloc(sizeof(*new_info), 1); -+ if (new_info == NULL) -+ return -1; -+ -+ int own; -+ struct arg_type_info *orig; -+ value_take_type(value, &orig, &own); -+ type_init_pointer(new_info, orig, own); -+ new_info->lens = orig->lens; -+ value_set_type(value, new_info, 1); -+ -+ return 0; -+} -diff --git a/value.h b/value.h -new file mode 100644 -index 0000000..1ba8a6c ---- /dev/null -+++ b/value.h -@@ -0,0 +1,155 @@ -+/* -+ * 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 VALUE_H -+#define VALUE_H -+ -+#include "forward.h" -+ -+/* Values are objects that capture data fetched from an inferior. -+ * Typically a value is attached to a single inferior where it was -+ * extracted from, but it is possible to create a detached value. -+ * Each value is typed. Values support a number of routines, such as -+ * dereferencing if the value is of pointer type, array or structure -+ * access, etc. -+ * -+ * A value can be uninitialized, abstract or reified. Abstract values -+ * are just references into inferior, no transfer has taken place yet. -+ * Reified values have been copied out of the corresponding inferior, -+ * or otherwise set to some value. */ -+ -+enum value_location_t { -+ VAL_LOC_NODATA = 0, /* Uninitialized. */ -+ VAL_LOC_INFERIOR, /* Value is in the inferior process. */ -+ VAL_LOC_COPY, /* Value was copied out of the inferior. */ -+ VAL_LOC_SHARED, /* Like VAL_LOC_COPY, but don't free. */ -+ VAL_LOC_WORD, /* Like VAL_LOC_COPY, but small enough. */ -+}; -+ -+struct value { -+ struct arg_type_info *type; -+ struct Process *inferior; -+ struct value *parent; -+ size_t size; -+ union { -+ void *address; /* VAL_LOC_CLIENT, VAL_LOC_COPY, -+ VAL_LOC_SHARED */ -+ long value; /* VAL_LOC_WORD */ -+ unsigned char buf[0]; -+ } u; -+ enum value_location_t where; -+ int own_type; -+}; -+ -+/* Initialize VALUE. INFERIOR must not be NULL. PARENT is parental -+ * value, in case of compound types. It may be NULL. TYPE is a type -+ * of the value. It may be NULL if the type is not yet known. If -+ * OWN_TYPE, the passed-in type is owned and released by value. */ -+void value_init(struct value *value, struct Process *inferior, -+ struct value *parent, struct arg_type_info *type, -+ int own_type); -+ -+/* Initialize VALUE. This is like value_init, except that inferior is -+ * NULL. VALP is initialized as a detached value, without assigned -+ * process. You have to be careful not to use VAL_LOC_INFERIOR -+ * values if the value is detached. */ -+void value_init_detached(struct value *value, struct value *parent, -+ struct arg_type_info *type, int own_type); -+ -+/* Set TYPE. This releases old type if it was owned. TYPE is owned -+ * and released if OWN_TYPE. */ -+void value_set_type(struct value *value, -+ struct arg_type_info *type, int own_type); -+ -+/* Transfers the ownership of VALUE's type, if any, to the caller. -+ * This doesn't reset the VALUE's type, but gives up ownership if -+ * there was one. Previous ownership is passed in OWN_TYPE. */ -+void value_take_type(struct value *value, -+ struct arg_type_info **type, int *own_type); -+ -+/* Release the data held by VALP, if any, but not the type. */ -+void value_release(struct value *valp); -+ -+/* Destroy the value. This is like value_release, but it additionally -+ * frees the value type, if it's own_type. It doesn't free the VAL -+ * pointer itself. */ -+void value_destroy(struct value *val); -+ -+/* Set the data held by VALP to VALUE. This also sets the value's -+ * where to VAL_LOC_WORD. */ -+void value_set_word(struct value *valp, long value); -+ -+/* Set the data held by VALP to a buffer of size SIZE. This buffer -+ * may be allocated by malloc. Returns NULL on failure. */ -+unsigned char *value_reserve(struct value *valp, size_t size); -+ -+/* Access ELEMENT-th field of the compound value VALP, and store the -+ * result into the value RET_VAL. Returns 0 on success, or negative -+ * value on failure. */ -+int value_init_element(struct value *ret_val, struct value *valp, size_t element); -+ -+/* De-reference pointer value, and store the result into the value -+ * RET_VAL. Returns 0 on success, or negative value on failure. */ -+int value_init_deref(struct value *ret_val, struct value *valp); -+ -+/* If value is in inferior, copy it over to ltrace. Return 0 for -+ * success or negative value for failure. */ -+int value_reify(struct value *val, struct value_dict *arguments); -+ -+/* Return a pointer to the data of the value. This copies the data -+ * from the inferior to the tracer. Returns NULL on failure. */ -+unsigned char *value_get_data(struct value *val, struct value_dict *arguments); -+ -+/* Return a pointer to the raw data of the value. This shall not be -+ * called on a VAL_LOC_INFERIOR value. */ -+unsigned char *value_get_raw_data(struct value *val); -+ -+/* Copy value VAL into the area pointed-to by RETP. Return 0 on -+ * success or a negative value on failure. */ -+int value_clone(struct value *retp, struct value *val); -+ -+/* Give a size of given value. Return (size_t)-1 for error. This is -+ * a full size of the value. In particular for arrays, it returns -+ * actual length of the array, as computed by the length -+ * expression. */ -+size_t value_size(struct value *val, struct value_dict *arguments); -+ -+/* Extract at most word-sized datum from the value. Return 0 on -+ * success or negative value on failure. */ -+int value_extract_word(struct value *val, long *retp, -+ struct value_dict *arguments); -+ -+/* Copy contents of VAL to DATA. The buffer must be large enough to -+ * hold all the data inside. */ -+int value_extract_buf(struct value *val, unsigned char *data, -+ struct value_dict *arguments); -+ -+/* Find the most enclosing parental value that is a struct. Return -+ * NULL when there is no such parental value. */ -+struct value *value_get_parental_struct(struct value *val); -+ -+/* Determine whether this is all-zero value. Returns >0 if it is, ==0 -+ * if it isn't, <0 on error. */ -+int value_is_zero(struct value *val, struct value_dict *arguments); -+ -+/* Convert a structure type to pointer to that structure type. */ -+int value_pass_by_reference(struct value *value); -+ -+#endif /* VALUE_H */ -diff --git a/value_dict.c b/value_dict.c -new file mode 100644 -index 0000000..3f3880a ---- /dev/null -+++ b/value_dict.c -@@ -0,0 +1,147 @@ -+/* -+ * 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 -+ */ -+ -+#include -+#include -+#include -+ -+#include "value_dict.h" -+#include "value.h" -+ -+struct named_value -+{ -+ const char *name; -+ struct value value; -+ int own_name; -+}; -+ -+void -+val_dict_init(struct value_dict *dict) -+{ -+ VECT_INIT(&dict->numbered, struct value); -+ VECT_INIT(&dict->named, struct named_value); -+} -+ -+static int -+value_clone_cb(struct value *tgt, struct value *src, void *data) -+{ -+ return value_clone(tgt, src); -+} -+ -+static void -+value_dtor(struct value *val, void *data) -+{ -+ value_destroy(val); -+} -+ -+static int -+named_value_clone(struct named_value *tgt, struct named_value *src, void *data) -+{ -+ tgt->name = strdup(src->name); -+ if (tgt->name == NULL) -+ return -1; -+ tgt->own_name = 1; -+ if (value_clone(&tgt->value, &src->value) < 0) { -+ free((char *)tgt->name); -+ return -1; -+ } -+ return 0; -+} -+ -+static void -+named_value_dtor(struct named_value *named, void *data) -+{ -+ if (named->own_name) -+ free((char *)named->name); -+ value_destroy(&named->value); -+} -+ -+int -+val_dict_clone(struct value_dict *target, struct value_dict *source) -+{ -+ if (VECT_CLONE(&target->numbered, &source->numbered, struct value, -+ value_clone_cb, value_dtor, NULL) < 0) -+ return -1; -+ -+ if (VECT_CLONE(&target->named, &source->named, struct named_value, -+ named_value_clone, named_value_dtor, NULL) < 0) { -+ VECT_DESTROY(&target->numbered, struct value, value_dtor, NULL); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+int -+val_dict_push_next(struct value_dict *dict, struct value *val) -+{ -+ return VECT_PUSHBACK(&dict->numbered, val); -+} -+ -+int -+val_dict_push_named(struct value_dict *dict, struct value *val, -+ const char *name, int own_name) -+{ -+ if (own_name && (name = strdup(name)) == NULL) -+ return -1; -+ struct named_value element = { name, *val, own_name }; -+ if (VECT_PUSHBACK(&dict->named, &element) < 0) { -+ if (own_name) -+ free((char *)name); -+ return -1; -+ } -+ return 0; -+} -+ -+size_t -+val_dict_count(struct value_dict *dict) -+{ -+ return vect_size(&dict->numbered); -+} -+ -+struct value * -+val_dict_get_num(struct value_dict *dict, size_t num) -+{ -+ assert(num < vect_size(&dict->numbered)); -+ return VECT_ELEMENT(&dict->numbered, struct value, num); -+} -+ -+struct value * -+val_dict_get_name(struct value_dict *dict, const char *name) -+{ -+ size_t i; -+ for (i = 0; i < vect_size(&dict->named); ++i) { -+ struct named_value *element -+ = VECT_ELEMENT(&dict->named, struct named_value, i); -+ if (strcmp(element->name, name) == 0) -+ return &element->value; -+ } -+ return NULL; -+} -+ -+void -+val_dict_destroy(struct value_dict *dict) -+{ -+ if (dict == NULL) -+ return; -+ -+ VECT_DESTROY(&dict->numbered, struct value, value_dtor, NULL); -+ VECT_DESTROY(&dict->named, struct named_value, named_value_dtor, NULL); -+} -diff --git a/value_dict.h b/value_dict.h -new file mode 100644 -index 0000000..f75ee37 ---- /dev/null -+++ b/value_dict.h -@@ -0,0 +1,67 @@ -+/* -+ * 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 ARGS_H -+#define ARGS_H -+ -+#include "forward.h" -+#include "vect.h" -+ -+/* Value dictionary is used to store actual function arguments. It -+ * supports both numbered and named arguments. */ -+struct value_dict -+{ -+ struct vect numbered; -+ struct vect named; -+}; -+ -+/* Initialize DICT. */ -+void val_dict_init(struct value_dict *dict); -+ -+/* Clone SOURCE into TARGET. Return 0 on success or a negative value -+ * on failure. */ -+int val_dict_clone(struct value_dict *target, struct value_dict *source); -+ -+/* Push next numbered value, VAL. The value is copied over and the -+ * dictionary becomes its owner, and is responsible for destroying it -+ * later. Returns 0 on success and a negative value on failure. */ -+int val_dict_push_next(struct value_dict *dict, struct value *val); -+ -+/* Return count of numbered arguments. */ -+size_t val_dict_count(struct value_dict *dict); -+ -+/* Push value VAL named NAME. See notes at val_dict_push_next about -+ * value ownership. The name is owned and freed if OWN_NAME is -+ * non-zero. */ -+int val_dict_push_named(struct value_dict *dict, struct value *val, -+ const char *name, int own_name); -+ -+/* Get NUM-th numbered argument, or NULL if there's not that much -+ * arguments. */ -+struct value *val_dict_get_num(struct value_dict *dict, size_t num); -+ -+/* Get argument named NAME, or NULL if there's no such argument. */ -+struct value *val_dict_get_name(struct value_dict *dict, const char *name); -+ -+/* Destroy the dictionary and all the values in it. Note that DICT -+ * itself (the pointer) is not freed. */ -+void val_dict_destroy(struct value_dict *dict); -+ -+#endif /* ARGS_H */ -diff --git a/vect.c b/vect.c -new file mode 100644 -index 0000000..f2e58b2 ---- /dev/null -+++ b/vect.c -@@ -0,0 +1,136 @@ -+/* -+ * 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 -+ */ -+ -+#include -+#include -+#include -+#include "vect.h" -+ -+static void * -+slot(struct vect *vec, size_t i) -+{ -+ return ((char *)vec->data) + vec->elt_size * i; -+} -+ -+void -+vect_init(struct vect *vec, size_t elt_size) -+{ -+ *vec = (struct vect){ NULL, 0, 0, elt_size }; -+} -+ -+static int -+copy_elt(void *tgt, void *src, void *data) -+{ -+ struct vect *target = data; -+ memcpy(tgt, src, target->elt_size); -+ return 0; -+} -+ -+int -+vect_clone(struct vect *target, struct vect *source, -+ int (*clone)(void *tgt, void *src, void *data), -+ void (*dtor)(void *elt, void *data), -+ void *data) -+{ -+ vect_init(target, source->elt_size); -+ if (vect_reserve(target, source->size) < 0) -+ return -1; -+ -+ if (clone == NULL) { -+ assert(dtor == NULL); -+ clone = copy_elt; -+ data = target; -+ } else { -+ assert(dtor != NULL); -+ } -+ -+ size_t i; -+ for (i = 0; i < source->size; ++i) -+ if (clone(slot(target, i), slot(source, i), data) < 0) -+ goto fail; -+ -+ target->size = source->size; -+ return 0; -+ -+fail: -+ /* N.B. destroy the elements in opposite order. */ -+ if (dtor != NULL) -+ while (i-- != 0) -+ dtor(slot(target, i), data); -+ vect_destroy(target, NULL, NULL); -+ return -1; -+} -+ -+int -+vect_reserve(struct vect *vec, size_t count) -+{ -+ if (count > vec->allocated) { -+ size_t na = vec->allocated != 0 ? 2 * vec->allocated : 4; -+ void *n = realloc(vec->data, na * vec->elt_size); -+ if (n == NULL) -+ return -1; -+ vec->data = n; -+ vec->allocated = na; -+ } -+ assert(count <= vec->allocated); -+ return 0; -+} -+ -+size_t -+vect_size(struct vect *vec) -+{ -+ return vec->size; -+} -+ -+int -+vect_empty(struct vect *vec) -+{ -+ return vec->size == 0; -+} -+ -+int -+vect_reserve_additional(struct vect *vec, size_t count) -+{ -+ return vect_reserve(vec, vect_size(vec) + count); -+} -+ -+int -+vect_pushback(struct vect *vec, void *eltp) -+{ -+ if (vect_reserve_additional(vec, 1) < 0) -+ return -1; -+ memcpy(slot(vec, vec->size++), eltp, vec->elt_size); -+ return 0; -+} -+ -+void -+vect_destroy(struct vect *vec, void (*dtor)(void *emt, void *data), void *data) -+{ -+ if (vec == NULL) -+ return; -+ -+ if (dtor != NULL) { -+ size_t i; -+ size_t sz = vect_size(vec); -+ for (i = 0; i < sz; ++i) -+ dtor(slot(vec, i), data); -+ } -+ free(vec->data); -+} -diff --git a/vect.h b/vect.h -new file mode 100644 -index 0000000..50401bb ---- /dev/null -+++ b/vect.h -@@ -0,0 +1,125 @@ -+/* -+ * 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 VECT_H -+#define VECT_H -+ -+#include -+ -+/* 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 -+ * sanity check, because the array itself is not typed. */ -+ -+struct vect -+{ -+ void *data; -+ size_t size; /* In elements. */ -+ size_t allocated; /* In elements. */ -+ size_t elt_size; /* In bytes. */ -+}; -+ -+/* Initialize VEC, which will hold elements of size ELT_SIZE. */ -+void vect_init(struct vect *vec, size_t elt_size); -+ -+/* Initialize VECP, which will hold elements of type ELT_TYPE. */ -+#define VECT_INIT(VECP, ELT_TYPE) \ -+ (vect_init(VECP, sizeof(ELT_TYPE))) -+ -+/* Initialize TARGET by copying over contents of vector SOURCE. If -+ * CLONE is non-NULL, it's evoked on each element, and should clone -+ * SRC into TGT. It should return 0 on success or negative value on -+ * failure. DATA is passed to CLONE verbatim. This function returns -+ * 0 on success or negative value on failure. In case of failure, if -+ * DTOR is non-NULL, it is invoked on all hitherto created elements -+ * with the same DATA. If one of CLONE, DTOR is non-NULL, then both -+ * have to be. */ -+int vect_clone(struct vect *target, struct vect *source, -+ int (*clone)(void *tgt, void *src, void *data), -+ void (*dtor)(void *elt, void *data), -+ void *data); -+ -+/* Destroy VEC, which holds elements of type ELT_TYPE, using DTOR. */ -+#define VECT_CLONE(TGT_VEC, SRC_VEC, ELT_TYPE, CLONE, DTOR, DATA) \ -+ /* xxx GCC-ism necessary to get in the safety latches. */ \ -+ ({ \ -+ struct vect *_source_vec = (SRC_VEC); \ -+ assert(_source_vec->elt_size == sizeof(ELT_TYPE)); \ -+ /* Check that callbacks are typed properly. */ \ -+ void (*_dtor_callback)(ELT_TYPE *, void *) = DTOR; \ -+ int (*_clone_callback)(ELT_TYPE *, \ -+ ELT_TYPE *, void *) = CLONE; \ -+ vect_clone((TGT_VEC), _source_vec, \ -+ (int (*)(void *, void *, void *))_clone_callback, \ -+ (void (*)(void *, void *))_dtor_callback, \ -+ DATA); \ -+ }) -+ -+/* Return number of elements in VEC. */ -+size_t vect_size(struct vect *vec); -+ -+/* Emptiness predicate. */ -+int vect_empty(struct vect *vec); -+ -+/* Accessor. Fetch ELT_NUM-th argument of type ELT_TYPE from the -+ * vector referenced by VECP. */ -+#define VECT_ELEMENT(VECP, ELT_TYPE, ELT_NUM) \ -+ (assert((VECP)->elt_size == sizeof(ELT_TYPE)), \ -+ assert((ELT_NUM) < (VECP)->size), \ -+ ((ELT_TYPE *)(VECP)->data) + (ELT_NUM)) -+ -+#define VECT_BACK(VECP, ELT_TYPE) \ -+ VECT_ELEMENT(VECP, ELT_TYPE, (VECP)->size) -+ -+/* Copy element referenced by ELTP to the end of VEC. The object -+ * referenced by ELTP is now owned by VECT. Returns 0 if the -+ * operation was successful, or negative value on error. */ -+int vect_pushback(struct vect *vec, void *eltp); -+ -+/* Copy element referenced by ELTP to the end of VEC. See -+ * vect_pushback for details. In addition, make a check whether VECP -+ * holds elements of the right size. */ -+#define VECT_PUSHBACK(VECP, ELTP) \ -+ (assert((VECP)->elt_size == sizeof(*(ELTP))), \ -+ vect_pushback((VECP), (ELTP))) -+ -+/* Make sure that VEC can hold at least COUNT elements. Return 0 on -+ * success, negative value on failure. */ -+int vect_reserve(struct vect *vec, size_t count); -+ -+/* Make sure that VEC can accommodate COUNT additional elements. */ -+int vect_reserve_additional(struct vect *vec, size_t count); -+ -+/* Destroy VEC. If DTOR is non-NULL, then it's called on each element -+ * of the vector. DATA is passed to DTOR verbatim. The memory -+ * pointed-to by VEC is not freed. */ -+void vect_destroy(struct vect *vec, -+ void (*dtor)(void *emt, void *data), void *data); -+ -+/* Destroy VEC, which holds elements of type ELT_TYPE, using DTOR. */ -+#define VECT_DESTROY(VECP, ELT_TYPE, DTOR, DATA) \ -+ do { \ -+ assert((VECP)->elt_size == sizeof(ELT_TYPE)); \ -+ /* Check that DTOR is typed properly. */ \ -+ void (*_dtor_callback)(ELT_TYPE *, void *) = DTOR; \ -+ vect_destroy((VECP), (void (*)(void *, void *))_dtor_callback, \ -+ DATA); \ -+ } while (0) -+ -+#endif /* VECT_H */ -diff --git a/zero.c b/zero.c -new file mode 100644 -index 0000000..bc119ee ---- /dev/null -+++ b/zero.c -@@ -0,0 +1,105 @@ -+/* -+ * 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 -+ */ -+ -+#include -+#include -+ -+#include "zero.h" -+#include "common.h" -+#include "type.h" -+#include "value.h" -+#include "expr.h" -+ -+static int -+zero_callback_max(struct value *ret_value, struct value *lhs, -+ struct value_dict *arguments, -+ size_t max, void *data) -+{ -+ size_t i; -+ for (i = 0; i < max; ++i) { -+ struct value element; -+ if (value_init_element(&element, lhs, i) < 0) -+ return -1; -+ -+ int zero = value_is_zero(&element, arguments); -+ -+ value_destroy(&element); -+ -+ if (zero) -+ break; -+ } -+ -+ struct arg_type_info *long_type = type_get_simple(ARGTYPE_LONG); -+ value_init_detached(ret_value, NULL, long_type, 0); -+ value_set_word(ret_value, i); -+ return 0; -+} -+ -+/* LHS->zero(RHS). Looks for a length of zero-terminated array, but -+ * looks no further than first RHS bytes. */ -+static int -+zero_callback(struct value *ret_value, struct value *lhs, -+ struct value *rhs, struct value_dict *arguments, void *data) -+{ -+ long l; -+ if (value_extract_word(rhs, &l, arguments) < 0) -+ return -1; -+ if (l < 0) -+ /* It might just be a positive value >2GB, but that's -+ * not likely. */ -+ report_global_error("maximum array length seems negative"); -+ size_t max = (size_t)l; -+ return zero_callback_max(ret_value, lhs, arguments, max, data); -+} -+ -+/* LHS->zero. Looks for a length of zero-terminated array, without -+ * limit. */ -+static int -+zero1_callback(struct value *ret_value, struct value *lhs, -+ struct value_dict *arguments, void *data) -+{ -+ return zero_callback_max(ret_value, lhs, arguments, (size_t)-1, data); -+} -+ -+struct expr_node * -+build_zero_w_arg(struct expr_node *expr, int own) -+{ -+ struct expr_node *e_z = malloc(sizeof(*e_z)); -+ if (e_z == NULL) -+ return NULL; -+ -+ expr_init_cb2(e_z, &zero_callback, -+ expr_self(), 0, expr, own, NULL); -+ return e_z; -+} -+ -+struct expr_node * -+expr_node_zero(void) -+{ -+ static struct expr_node *node = NULL; -+ if (node == NULL) { -+ node = malloc(sizeof(*node)); -+ if (node == NULL) -+ error(1, errno, "malloc expr_node_zero"); -+ expr_init_cb1(node, &zero1_callback, -+ expr_self(), 0, (void *)-1); -+ } -+ return node; -+} -diff --git a/zero.h b/zero.h -new file mode 100644 -index 0000000..45d2771 ---- /dev/null -+++ b/zero.h -@@ -0,0 +1,34 @@ -+/* -+ * 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 ZERO_H -+#define ZERO_H -+ -+#include "forward.h" -+ -+/* This returns a pre-built "zero" node without argument. Share, but -+ don't free. */ -+struct expr_node *expr_node_zero(void); -+ -+/* This builds a new "zero" node with EXPR as argument. EXPR is owned -+ * by the built node if OWN. Returns NULL if something failed. */ -+struct expr_node *build_zero_w_arg(struct expr_node *expr, int own); -+ -+#endif /* ZERO_H */ diff --git a/ltrace-0.6.0-cleanups.patch b/ltrace-0.6.0-cleanups.patch deleted file mode 100644 index 054882d..0000000 --- a/ltrace-0.6.0-cleanups.patch +++ /dev/null @@ -1,652 +0,0 @@ -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-clone-test.patch b/ltrace-0.6.0-clone-test.patch deleted file mode 100644 index c01221b..0000000 --- a/ltrace-0.6.0-clone-test.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/testsuite/ltrace.minor/trace-clone.c b/testsuite/ltrace.minor/trace-clone.c -index a1ccb22..6e1c809 100644 ---- a/testsuite/ltrace.minor/trace-clone.c -+++ b/testsuite/ltrace.minor/trace-clone.c -@@ -4,6 +4,7 @@ - - This file was written by Yao Qi . */ - -+#define _GNU_SOURCE - #include - #include - #include diff --git a/ltrace-0.6.0-dash-l.patch b/ltrace-0.6.0-dash-l.patch deleted file mode 100644 index 09b6fd7..0000000 --- a/ltrace-0.6.0-dash-l.patch +++ /dev/null @@ -1,1293 +0,0 @@ -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-dash-n.patch b/ltrace-0.6.0-dash-n.patch deleted file mode 100644 index 461b91d..0000000 --- a/ltrace-0.6.0-dash-n.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/options.c b/options.c -index d5edc1a..8dce7f8 100644 ---- a/options.c -+++ b/options.c -@@ -494,9 +494,19 @@ process_options(int argc, char **argv) - case 'L': - libcalls = 0; - break; -- case 'n': -- options.indent = atoi(optarg); -+ case 'n': { -+ char *endptr; -+ long int l = strtol(optarg, &endptr, 0); -+ /* Arbitrary cut-off. Nobody needs to indent -+ * more than, say, 8, anyway. */ -+ if (l < 0 || l > 20 || *optarg == 0 || *endptr != 0) { -+ fprintf(stderr, "Invalid argument to -n: '%s'." -+ " Use integer 0..20.\n", optarg); -+ exit(1); -+ } -+ options.indent = (int)l; - break; -+ } - case 'o': - options.output = fopen(optarg, "w"); - if (!options.output) { diff --git a/ltrace-0.6.0-demangle.patch b/ltrace-0.6.0-demangle.patch deleted file mode 100644 index 20e414d..0000000 --- a/ltrace-0.6.0-demangle.patch +++ /dev/null @@ -1,86 +0,0 @@ -diff --git a/Makefile.am b/Makefile.am -index 256c940..593e943 100644 ---- a/Makefile.am -+++ b/Makefile.am -@@ -35,6 +35,7 @@ libltrace_la_LIBADD = \ - $(libelf_LIBS) \ - $(liberty_LIBS) \ - $(libsupcxx_LIBS) \ -+ $(libstdcxx_LIBS) \ - $(libunwind_LIBS) \ - $(libunwind_ptrace_LIBS) \ - $(libunwind_arch_LIBS) \ -diff --git a/common.h b/common.h -index 7fffa76..b84a5d1 100644 ---- a/common.h -+++ b/common.h -@@ -16,7 +16,10 @@ - #include "read_config_file.h" - #include "proc.h" - --#if defined HAVE_LIBIBERTY || defined HAVE_LIBSUPC__ -+#if defined HAVE_LIBSUPC__ || defined HAVE_LIBSTDC__ -+# define USE_CXA_DEMANGLE -+#endif -+#if defined HAVE_LIBIBERTY || defined USE_CXA_DEMANGLE - # define USE_DEMANGLE - #endif - -diff --git a/configure.ac b/configure.ac -index 707f8c5..1ec7242 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -82,6 +82,14 @@ AC_CHECK_LIB([supc++], [__cxa_demangle], [ - AC_SUBST(libsupcxx_LIBS) - - -+# HAVE_LIBSTDC__ -+AC_CHECK_LIB([stdc++], [__cxa_demangle], [ -+ AC_DEFINE([HAVE_LIBSTDC__], [1], [we have libstdc++]) -+ libstdcxx_LIBS="-lstdc++"], [ -+ libstdcxx_LIBS=""]) -+AC_SUBST(libstdcxx_LIBS) -+ -+ - dnl Check security_get_boolean_active availability. - AC_CHECK_HEADERS(selinux/selinux.h) - AC_CHECK_LIB(selinux, security_get_boolean_active) -diff --git a/demangle.c b/demangle.c -index 5825e28..26a5c7a 100644 ---- a/demangle.c -+++ b/demangle.c -@@ -15,9 +15,8 @@ static Dict *d = NULL; - const char * - my_demangle(const char *function_name) { - const char *tmp, *fn_copy; --#if !defined HAVE_LIBIBERTY && defined HAVE_LIBSUPC__ -+#ifdef USE_CXA_DEMANGLE - extern char *__cxa_demangle(const char *, char *, size_t *, int *); -- int status = 0; - #endif - - debug(DEBUG_FUNCTION, "my_demangle(name=%s)", function_name); -@@ -30,7 +29,8 @@ my_demangle(const char *function_name) { - fn_copy = strdup(function_name); - #ifdef HAVE_LIBIBERTY - tmp = cplus_demangle(function_name, DMGL_ANSI | DMGL_PARAMS); --#elif defined HAVE_LIBSUPC__ -+#elif defined USE_CXA_DEMANGLE -+ int status = 0; - tmp = __cxa_demangle(function_name, NULL, NULL, &status); - #endif - if (!tmp) -diff --git a/testsuite/ltrace.minor/demangle.exp b/testsuite/ltrace.minor/demangle.exp -index c2d3aeb..9dd7694 100644 ---- a/testsuite/ltrace.minor/demangle.exp -+++ b/testsuite/ltrace.minor/demangle.exp -@@ -32,6 +32,9 @@ if [regexp {ELF from incompatible architecture} $exec_output] { - } elseif [ regexp {Couldn't get .hash data} $exec_output ] { - fail "Couldn't get .hash data!" - return -+} elseif [ regexp {invalid option} $exec_output ] { -+ unsupported "Demangle support not compiled in." -+ return - } - - # read function declarations from demangle.cpp and verify them in demangle.ltrace. diff --git a/ltrace-0.6.0-detach-sleeping.patch b/ltrace-0.6.0-detach-sleeping.patch deleted file mode 100644 index bac1b7e..0000000 --- a/ltrace-0.6.0-detach-sleeping.patch +++ /dev/null @@ -1,141 +0,0 @@ -diff --git a/common.h b/common.h -index 2399e29..b6e10f2 100644 ---- a/common.h -+++ b/common.h -@@ -347,7 +347,6 @@ extern void continue_after_signal(pid_t pid, int signum); - extern void continue_after_syscall(Process *proc, int sysnum, int ret_p); - extern void continue_after_breakpoint(Process * proc, struct breakpoint *sbp); - extern void continue_after_vfork(Process * proc); --extern void ltrace_exiting(void); - extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); - extern void save_register_args(enum tof type, Process * proc); - extern int umovestr(Process * proc, void * addr, int len, void * laddr); -@@ -363,5 +362,21 @@ extern int task_kill (pid_t pid, int sig); - * any platform-specific knowledge of why it could be so. */ - void trace_fail_warning(pid_t pid); - -+/* A pair of functions called to initiate a detachment request when -+ * ltrace is about to exit. Their job is to undo any effects that -+ * tracing had and eventually detach process, perhaps by way of -+ * installing a process handler. -+ * -+ * OS_LTRACE_EXITING_SIGHANDLER is called from a signal handler -+ * context right after the signal was captured. It returns 1 if the -+ * request was handled or 0 if it wasn't. -+ * -+ * If the call to OS_LTRACE_EXITING_SIGHANDLER didn't handle the -+ * request, OS_LTRACE_EXITING is called when the next event is -+ * generated. Therefore it's called in "safe" context, without -+ * re-entrancy concerns, but it's only called after an even is -+ * generated. */ -+int os_ltrace_exiting_sighandler(void); -+void os_ltrace_exiting(void); - - extern struct ltelf main_lte; -diff --git a/handle_event.c b/handle_event.c -index e3d3d0a..725f50d 100644 ---- a/handle_event.c -+++ b/handle_event.c -@@ -49,11 +49,12 @@ call_handler(Process * proc, Event * event) - } - - void --handle_event(Event *event) { -+handle_event(Event *event) -+{ - if (exiting == 1) { -- exiting = 2; - debug(1, "ltrace about to exit"); -- ltrace_exiting(); -+ os_ltrace_exiting(); -+ exiting = 2; - } - debug(DEBUG_FUNCTION, "handle_event(pid=%d, type=%d)", - event->proc ? event->proc->pid : -1, event->type); -diff --git a/libltrace.c b/libltrace.c -index 19bfafd..777ad1b 100644 ---- a/libltrace.c -+++ b/libltrace.c -@@ -48,9 +48,14 @@ signal_alarm(int sig) { - } - - static void --signal_exit(int sig) { -- exiting = 1; -+signal_exit(int sig) -+{ - debug(1, "Received interrupt signal; exiting..."); -+ if (exiting != 0) -+ return; -+ -+ exiting = 1 + !!os_ltrace_exiting_sighandler(); -+ - signal(SIGINT, SIG_IGN); - signal(SIGTERM, SIG_IGN); - signal(SIGALRM, signal_alarm); -diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c -index 021192f..b6c12ef 100644 ---- a/sysdeps/linux-gnu/events.c -+++ b/sysdeps/linux-gnu/events.c -@@ -104,6 +104,8 @@ next_qd_event(void) - return each_qd_event(&event_process_not_reenabling, NULL); - } - -+int linux_in_waitpid = 0; -+ - Event * - next_event(void) - { -@@ -124,7 +126,11 @@ next_event(void) - debug(DEBUG_EVENT, "event: No more traced programs: exiting"); - exit(0); - } -+ -+ linux_in_waitpid = 1; - pid = waitpid(-1, &status, __WALL); -+ linux_in_waitpid = 0; -+ - if (pid == -1) { - if (errno == ECHILD) { - debug(DEBUG_EVENT, "event: No more traced programs: exiting"); -diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c -index 82a4154..9ecea1e 100644 ---- a/sysdeps/linux-gnu/trace.c -+++ b/sysdeps/linux-gnu/trace.c -@@ -314,8 +314,8 @@ task_stopped(Process * task, void * data) - case ps_invalid: - case ps_tracing_stop: - case ps_zombie: -- case ps_sleeping: - return pcb_cont; -+ case ps_sleeping: - case ps_stop: - case ps_other: - return pcb_stop; -@@ -1005,7 +1005,7 @@ continue_after_syscall(Process * proc, int sysnum, int ret_p) - * detaches. - */ - void --ltrace_exiting(void) -+os_ltrace_exiting(void) - { - struct opt_p_t * it; - for (it = opt_p; it != NULL; it = it->next) { -@@ -1019,6 +1019,17 @@ ltrace_exiting(void) - } - } - -+int -+os_ltrace_exiting_sighandler(void) -+{ -+ extern int linux_in_waitpid; -+ if (linux_in_waitpid) { -+ os_ltrace_exiting(); -+ return 1; -+ } -+ return 0; -+} -+ - size_t - umovebytes(Process *proc, void *addr, void *laddr, size_t len) { - diff --git a/ltrace-0.6.0-endian.patch b/ltrace-0.6.0-endian.patch deleted file mode 100644 index 7c12043..0000000 --- a/ltrace-0.6.0-endian.patch +++ /dev/null @@ -1,203 +0,0 @@ -diff --git a/sysdeps/linux-gnu/alpha/arch.h b/sysdeps/linux-gnu/alpha/arch.h -index 1107b5f..2dfeec7 100644 ---- a/sysdeps/linux-gnu/alpha/arch.h -+++ b/sysdeps/linux-gnu/alpha/arch.h -@@ -1,6 +1,7 @@ - #define BREAKPOINT_VALUE { 0x80, 0x00, 0x00, 0x00 } - #define BREAKPOINT_LENGTH 4 - #define DECR_PC_AFTER_BREAK 4 -+#define ARCH_ENDIAN_LITTLE - - #define LT_ELFCLASS ELFCLASS64 - #define LT_ELF_MACHINE EM_ALPHA -diff --git a/sysdeps/linux-gnu/arm/arch.h b/sysdeps/linux-gnu/arm/arch.h -index 8f2dfb3..e2a62ab 100644 ---- a/sysdeps/linux-gnu/arm/arch.h -+++ b/sysdeps/linux-gnu/arm/arch.h -@@ -6,6 +6,7 @@ - #define THUMB_BREAKPOINT_VALUE { 0x01, 0xde } - #define THUMB_BREAKPOINT_LENGTH 2 - #define DECR_PC_AFTER_BREAK 0 -+#define ARCH_ENDIAN_LITTLE - - #define LT_ELFCLASS ELFCLASS32 - #define LT_ELF_MACHINE EM_ARM -diff --git a/sysdeps/linux-gnu/i386/arch.h b/sysdeps/linux-gnu/i386/arch.h -index dc7383f..15f0d78 100644 ---- a/sysdeps/linux-gnu/i386/arch.h -+++ b/sysdeps/linux-gnu/i386/arch.h -@@ -1,6 +1,7 @@ - #define BREAKPOINT_VALUE {0xcc} - #define BREAKPOINT_LENGTH 1 - #define DECR_PC_AFTER_BREAK 1 -+#define ARCH_ENDIAN_LITTLE - - #define LT_ELFCLASS ELFCLASS32 - #define LT_ELF_MACHINE EM_386 -diff --git a/sysdeps/linux-gnu/ia64/arch.h b/sysdeps/linux-gnu/ia64/arch.h -index 673047c..d7e0d23 100644 ---- a/sysdeps/linux-gnu/ia64/arch.h -+++ b/sysdeps/linux-gnu/ia64/arch.h -@@ -4,6 +4,7 @@ - #define BREAKPOINT_LENGTH 16 - #define BREAKPOINT_VALUE {0} - #define DECR_PC_AFTER_BREAK 0 -+#define ARCH_ENDIAN_LITTLE - - #define LT_ELFCLASS ELFCLASS64 - #define LT_ELF_MACHINE EM_IA_64 -diff --git a/sysdeps/linux-gnu/m68k/arch.h b/sysdeps/linux-gnu/m68k/arch.h -index 1790d09..44fad89 100644 ---- a/sysdeps/linux-gnu/m68k/arch.h -+++ b/sysdeps/linux-gnu/m68k/arch.h -@@ -1,6 +1,7 @@ - #define BREAKPOINT_VALUE { 0x4e, 0x4f } - #define BREAKPOINT_LENGTH 2 - #define DECR_PC_AFTER_BREAK 2 -+#define ARCH_ENDIAN_BIG - - #define LT_ELFCLASS ELFCLASS32 - #define LT_ELF_MACHINE EM_68K -diff --git a/sysdeps/linux-gnu/mipsel/arch.h b/sysdeps/linux-gnu/mipsel/arch.h -index dd0ca35..45c4598 100644 ---- a/sysdeps/linux-gnu/mipsel/arch.h -+++ b/sysdeps/linux-gnu/mipsel/arch.h -@@ -1,6 +1,7 @@ - #define BREAKPOINT_VALUE { 0x0d, 0x00, 0x00, 0x00 } - #define BREAKPOINT_LENGTH 4 - #define DECR_PC_AFTER_BREAK 0 -+#define ARCH_ENDIAN_LITTLE - - #define LT_ELFCLASS ELFCLASS32 - #define LT_ELF_MACHINE EM_MIPS -diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h -index 711b4a3..8b955e8 100644 ---- a/sysdeps/linux-gnu/ppc/arch.h -+++ b/sysdeps/linux-gnu/ppc/arch.h -@@ -14,7 +14,7 @@ - #define PLT_REINITALISATION_BP "_start" - - /* Start of arch-specific functions. */ --#define ARCH_HAVE_UMOVELONG -+#define ARCH_ENDIAN_BIG - - #define PPC_NOP { 0x60, 0x00, 0x00, 0x00 } - #define PPC_NOP_LENGTH 4 -diff -up ltrace-0.6.0/sysdeps/linux-gnu/ppc/trace.c\~ ltrace-0.6.0/sysdeps/linux-gnu/ppc/trace.c ---- ltrace-0.6.0/sysdeps/linux-gnu/ppc/trace.c~ 2011-02-14 16:48:25.000000000 +0100 -+++ ltrace-0.6.0/sysdeps/linux-gnu/ppc/trace.c 2011-09-03 00:18:14.518317665 +0200 -@@ -156,34 +156,3 @@ save_register_args(enum tof type, Proces - memcpy(&arch->regs_copy, &arch->regs, sizeof(arch->regs)); - memcpy(&arch->fpregs_copy, &arch->fpregs, sizeof(arch->fpregs)); - } -- --/* Read a single long from the process's memory address 'addr'. */ --int --arch_umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { -- long pointed_to; -- -- errno = 0; -- -- pointed_to = ptrace (PTRACE_PEEKTEXT, proc->pid, addr, 0); -- -- if (pointed_to == -1 && errno) -- return -errno; -- -- /* Since int's are 4-bytes (long is 8-bytes) in length for ppc64, we -- need to shift the long values returned by ptrace to end up with -- the correct value. */ -- -- if (info) { -- if (info->type == ARGTYPE_INT || (proc->mask_32bit && (info->type == ARGTYPE_POINTER -- || info->type == ARGTYPE_STRING))) { -- pointed_to = pointed_to >> 32; -- -- /* Make sure we have nothing in the upper word so we can -- do a explicit cast from long to int later in the code. */ -- pointed_to &= 0x00000000ffffffff; -- } -- } -- -- *result = pointed_to; -- return 0; --} -diff --git a/sysdeps/linux-gnu/s390/arch.h b/sysdeps/linux-gnu/s390/arch.h -index 5cf168c..c6fed74 100644 ---- a/sysdeps/linux-gnu/s390/arch.h -+++ b/sysdeps/linux-gnu/s390/arch.h -@@ -6,6 +6,7 @@ - #define BREAKPOINT_VALUE { 0x00, 0x01 } - #define BREAKPOINT_LENGTH 2 - #define DECR_PC_AFTER_BREAK 2 -+#define ARCH_ENDIAN_BIG - - #ifdef __s390x__ - #define LT_ELFCLASS ELFCLASS64 -diff --git a/sysdeps/linux-gnu/sparc/arch.h b/sysdeps/linux-gnu/sparc/arch.h -index 75251b8..bec22ce 100644 ---- a/sysdeps/linux-gnu/sparc/arch.h -+++ b/sysdeps/linux-gnu/sparc/arch.h -@@ -1,6 +1,7 @@ - #define BREAKPOINT_VALUE {0x91, 0xd0, 0x20, 0x01} - #define BREAKPOINT_LENGTH 4 - #define DECR_PC_AFTER_BREAK 0 -+#define ARCH_ENDIAN_BIG - - #define LT_ELFCLASS ELFCLASS32 - #define LT_ELF_MACHINE EM_SPARC -diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c -index e4be465..9b0b980 100644 ---- a/sysdeps/linux-gnu/trace.c -+++ b/sysdeps/linux-gnu/trace.c -@@ -10,6 +10,7 @@ - #include - - #include "common.h" -+#include "config.h" - - /* If the system headers did not provide the constants, hard-code the normal - values. */ -@@ -54,15 +55,23 @@ umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { - if (pointed_to == -1 && errno) - return -errno; - -- *result = pointed_to; -- if (info) { -- switch(info->type) { -- case ARGTYPE_INT: -- *result &= 0x00000000ffffffffUL; -- default: -- break; -- }; -+#if SIZEOF_LONG == 8 -+ if (info != NULL -+ && (info->type == ARGTYPE_INT -+ || (proc->mask_32bit -+ && (info->type == ARGTYPE_POINTER -+ || info->type == ARGTYPE_STRING)))) { -+#if defined (ARCH_ENDIAN_LITTLE) -+ pointed_to &= 0x00000000ffffffffUL; -+#elif defined (ARCH_ENDIAN_BIG) -+ pointed_to = (long)(((unsigned long)pointed_to) >> 32); -+#else -+# error arch.h has to define endianness -+#endif - } -+#endif -+ -+ *result = pointed_to; - return 0; - } - #endif -diff --git a/sysdeps/linux-gnu/x86_64/arch.h b/sysdeps/linux-gnu/x86_64/arch.h -index 255395c..34376c4 100644 ---- a/sysdeps/linux-gnu/x86_64/arch.h -+++ b/sysdeps/linux-gnu/x86_64/arch.h -@@ -1,6 +1,7 @@ - #define BREAKPOINT_VALUE {0xcc} - #define BREAKPOINT_LENGTH 1 - #define DECR_PC_AFTER_BREAK 1 -+#define ARCH_ENDIAN_LITTLE - - #define LT_ELFCLASS ELFCLASS64 - #define LT_ELF_MACHINE EM_X86_64 diff --git a/ltrace-0.6.0-exec-stripped.patch b/ltrace-0.6.0-exec-stripped.patch deleted file mode 100644 index 25bd121..0000000 --- a/ltrace-0.6.0-exec-stripped.patch +++ /dev/null @@ -1,63 +0,0 @@ -diff -up ltrace-0.6.0/ltrace-elf.c\~ ltrace-0.6.0/ltrace-elf.c ---- ltrace-0.6.0/ltrace-elf.c~ 2011-02-14 16:48:25.000000000 +0100 -+++ ltrace-0.6.0/ltrace-elf.c 2011-02-14 18:13:03.000000000 +0100 -@@ -609,6 +609,7 @@ read_elf(Process *proc) { - struct ltelf lte[MAX_LIBRARIES + 1]; - size_t i; - struct opt_x_t *xptr; -+ struct opt_x_t *opt_x_loc = opt_x; - struct library_symbol **lib_tail = NULL; - int exit_out = 0; - int count = 0; -@@ -722,11 +723,11 @@ read_elf(Process *proc) { - main_cheat = (struct opt_x_t *)malloc(sizeof(struct opt_x_t)); - if (main_cheat == NULL) - error(EXIT_FAILURE, 0, "Couldn't allocate memory"); -- main_cheat->next = opt_x; -+ main_cheat->next = opt_x_loc; - main_cheat->found = 0; - main_cheat->name = PLTs_initialized_by_here; - -- for (xptr = opt_x; xptr; xptr = xptr->next) -+ for (xptr = opt_x_loc; xptr; xptr = xptr->next) - if (strcmp(xptr->name, PLTs_initialized_by_here) == 0 - && main_cheat) { - free(main_cheat); -@@ -734,7 +735,7 @@ read_elf(Process *proc) { - break; - } - if (main_cheat) -- opt_x = main_cheat; -+ opt_x_loc = main_cheat; - } - #endif - } else { -@@ -756,7 +757,7 @@ read_elf(Process *proc) { - if (!addr) - continue; - -- for (xptr = opt_x; xptr; xptr = xptr->next) -+ for (xptr = opt_x_loc; xptr; xptr = xptr->next) - if (xptr->name && strcmp(xptr->name, name) == 0) { - /* FIXME: Should be able to use &library_symbols as above. But - when you do, none of the real library symbols cause breaks. */ -@@ -769,7 +770,7 @@ read_elf(Process *proc) { - - unsigned found_count = 0; - -- for (xptr = opt_x; xptr; xptr = xptr->next) { -+ for (xptr = opt_x_loc; xptr; xptr = xptr->next) { - if (xptr->found) - continue; - -@@ -791,7 +792,7 @@ read_elf(Process *proc) { - } - } - -- for (xptr = opt_x; xptr; xptr = xptr->next) -+ for (xptr = opt_x_loc; xptr; xptr = xptr->next) - if ( ! xptr->found) { - char *badthing = "WARNING"; - #ifdef PLT_REINITALISATION_BP - -Diff finished. Mon Feb 14 18:13:11 2011 diff --git a/ltrace-0.6.0-libs-fixes-1.patch b/ltrace-0.6.0-libs-fixes-1.patch deleted file mode 100644 index f10c339..0000000 --- a/ltrace-0.6.0-libs-fixes-1.patch +++ /dev/null @@ -1,39 +0,0 @@ -diff --git a/output.c b/output.c -index ac8c9d0..db6e93e 100644 ---- a/output.c -+++ b/output.c -@@ -22,9 +22,10 @@ static int current_depth = 0; - static int current_column = 0; - - static void --output_indent(Process *proc) { -- current_column += -- fprintf(options.output, "%*s", options.indent * proc->callstack_depth, ""); -+output_indent(struct Process *proc) -+{ -+ int d = options.indent * (proc->callstack_depth - 1); -+ current_column += fprintf(options.output, "%*s", d, ""); - } - - static void -diff --git a/proc.c b/proc.c -index 51833fe..54afbe0 100644 ---- a/proc.c -+++ b/proc.c -@@ -49,6 +49,7 @@ arch_dynlink_done(struct Process *proc) - #endif - - static void add_process(struct Process *proc, int was_exec); -+static void unlist_process(struct Process *proc); - - static int - process_bare_init(struct Process *proc, const char *filename, -@@ -96,7 +97,7 @@ process_bare_destroy(struct Process *proc, int was_exec) - dict_clear(proc->breakpoints); - if (!was_exec) { - free(proc->filename); -- remove_process(proc); -+ unlist_process(proc); - } - } - diff --git a/ltrace-0.6.0-libs.patch b/ltrace-0.6.0-libs.patch deleted file mode 100644 index a433ee2..0000000 --- a/ltrace-0.6.0-libs.patch +++ /dev/null @@ -1,10240 +0,0 @@ -diff --git a/Makefile.am b/Makefile.am -index 6c299d8..a00c8bf 100644 ---- a/Makefile.am -+++ b/Makefile.am -@@ -24,7 +24,10 @@ libltrace_la_SOURCES = \ - output.c \ - proc.c \ - read_config_file.c \ -- summary.c -+ summary.c \ -+ library.c \ -+ filter.c \ -+ glob.c - - libltrace_la_LIBADD = \ - $(libelf_LIBS) \ -@@ -56,7 +59,10 @@ noinst_HEADERS = \ - ltrace.h \ - options.h \ - output.h \ -- read_config_file.h -+ read_config_file.h \ -+ library.h \ -+ filter.h \ -+ glob.h - - dist_man1_MANS = \ - ltrace.1 -diff --git a/breakpoint.h b/breakpoint.h -index ce6f501..0398072 100644 ---- a/breakpoint.h -+++ b/breakpoint.h -@@ -33,74 +33,106 @@ - * enable. Realized tracepoints enable breakpoints, which are a - * low-level realization of high-level tracepoint. - * -- * Tracepoints are provided by the main binary as well as by any -- * opened libraries: every time an ELF file is mapped into the address -- * space, a new set of tracepoints is extracted, and filtered -- * according to user settings. Those tracepoints that are left are -- * then realized, and the tracing starts. -- * -- * A scheme like this would take care of gradually introducing -- * breakpoints when the library is mapped, and therefore ready, and -- * would avoid certain hacks. For example on PPC64, we don't actually -- * add breakpoints to PLT. Instead, we read the PLT (which contains -- * addresses, not code), to figure out where to put the breakpoints. -- * In prelinked code, that address is non-zero, and points to an -- * address that's not yet mapped. ptrace then fails when we try to -- * add the breakpoint. -- * -- * Ideally, return breakpoints would be just a special kind of -- * tracepoint that has attached some magic. Or a feature of a -- * tracepoint. Service breakpoints like the handling of dlopen would -- * be a low-level breakpoint, likely without tracepoint attached. -+ * Service breakpoints like the handling of dlopen would be a -+ * low-level breakpoint, likely without tracepoint attached. - * - * So that's for sometimes. - */ - --#include "arch.h" -+#include "sysdep.h" -+#include "library.h" - - struct Process; - struct breakpoint; - - struct bp_callbacks { -- void (*on_hit) (struct breakpoint *bp, struct Process *proc); -- void (*on_destroy) (struct breakpoint *bp); -+ void (*on_hit)(struct breakpoint *bp, struct Process *proc); -+ void (*on_continue)(struct breakpoint *bp, struct Process *proc); -+ void (*on_retract)(struct breakpoint *bp, struct Process *proc); - }; - - struct breakpoint { - struct bp_callbacks *cbs; -+ struct library_symbol *libsym; - void *addr; - unsigned char orig_value[BREAKPOINT_LENGTH]; - int enabled; -- struct library_symbol *libsym; --#ifdef __arm__ -- int thumb_mode; --#endif -+ struct arch_breakpoint_data arch; - }; - - /* Call on-hit handler of BP, if any is set. */ - void breakpoint_on_hit(struct breakpoint *bp, struct Process *proc); - --/* Call on-destroy handler of BP, if any is set. */ --void breakpoint_on_destroy(struct breakpoint *bp); -+/* Call on-continue handler of BP. If none is set, call -+ * continue_after_breakpoint. */ -+void breakpoint_on_continue(struct breakpoint *bp, struct Process *proc); -+ -+/* Call on-retract handler of BP, if any is set. This should be -+ * called before the breakpoints are destroyed. The reason for a -+ * separate interface is that breakpoint_destroy has to be callable -+ * without PROC. ON_DISABLE might be useful as well, but that would -+ * be called every time we disable the breakpoint, which is too often -+ * (a breakpoint has to be disabled every time that we need to execute -+ * the instruction underneath it). */ -+void breakpoint_on_retract(struct breakpoint *bp, struct Process *proc); -+ -+/* Initialize a breakpoint structure. That doesn't actually realize -+ * the breakpoint. The breakpoint is initially assumed to be -+ * disabled. orig_value has to be set separately. CBS may be -+ * NULL. */ -+int breakpoint_init(struct breakpoint *bp, struct Process *proc, -+ target_address_t addr, struct library_symbol *libsym); -+ -+/* Make a clone of breakpoint BP into the area of memory pointed to by -+ * RETP. The original breakpoint was assigned to process OLD_PROC, -+ * the cloned breakpoint will be attached to process NEW_PROC. -+ * Returns 0 on success or a negative value on failure. */ -+int breakpoint_clone(struct breakpoint *retp, struct Process *new_proc, -+ struct breakpoint *bp, struct Process *old_proc); -+ -+/* Set callbacks. If CBS is non-NULL, then BP->cbs shall be NULL. */ -+void breakpoint_set_callbacks(struct breakpoint *bp, struct bp_callbacks *cbs); - --/* This is actually three functions rolled in one: -- * - breakpoint_init -- * - proc_insert_breakpoint -- * - breakpoint_enable -- * XXX I think it should be broken up somehow. */ -+/* Destroy a breakpoint structure. */ -+void breakpoint_destroy(struct breakpoint *bp); -+ -+/* Call enable_breakpoint the first time it's called. Returns 0 on -+ * success and a negative value on failure. */ -+int breakpoint_turn_on(struct breakpoint *bp, struct Process *proc); -+ -+/* Call disable_breakpoint when turned off the same number of times -+ * that it was turned on. Returns 0 on success and a negative value -+ * on failure. */ -+int breakpoint_turn_off(struct breakpoint *bp, struct Process *proc); -+ -+/* Utility function that does what typically needs to be done when a -+ * breakpoint is to be inserted. It checks whether there is another -+ * breakpoint in PROC->LEADER for given ADDR. If not, it allocates -+ * memory for a new breakpoint on the heap, initializes it, and calls -+ * PROC_ADD_BREAKPOINT to add the newly-created breakpoint. For newly -+ * added as well as preexisting breakpoints, it then calls -+ * BREAKPOINT_TURN_ON. If anything fails, it cleans up and returns -+ * NULL. Otherwise it returns the breakpoint for ADDR. */ - struct breakpoint *insert_breakpoint(struct Process *proc, void *addr, -- struct library_symbol *libsym, int enable); -+ struct library_symbol *libsym); -+ -+/* Name of a symbol associated with BP. May be NULL. */ -+const char *breakpoint_name(const struct breakpoint *bp); - --/* */ -+/* A library that this breakpoint comes from. May be NULL. */ -+struct library *breakpoint_library(const struct breakpoint *bp); -+ -+/* Again, this seems to be several interfaces rolled into one: -+ * - breakpoint_disable -+ * - proc_remove_breakpoint -+ * - breakpoint_destroy -+ * XXX */ - void delete_breakpoint(struct Process *proc, void *addr); - - /* XXX some of the following belongs to proc.h/proc.c. */ - struct breakpoint *address2bpstruct(struct Process *proc, void *addr); - void enable_all_breakpoints(struct Process *proc); - void disable_all_breakpoints(struct Process *proc); --int breakpoints_init(struct Process *proc, int enable); -- --void reinitialize_breakpoints(struct Process *proc); -- -+int breakpoints_init(struct Process *proc); - - #endif /* BREAKPOINT_H */ -diff --git a/breakpoints.c b/breakpoints.c -index 5713fe4..9536266 100644 ---- a/breakpoints.c -+++ b/breakpoints.c -@@ -3,6 +3,7 @@ - #include - #include - #include -+#include - - #ifdef __powerpc__ - #include -@@ -10,21 +11,52 @@ - - #include "breakpoint.h" - #include "common.h" -+#include "proc.h" -+#include "library.h" -+ -+#ifndef ARCH_HAVE_TRANSLATE_ADDRESS -+int -+arch_translate_address_dyn(struct Process *proc, -+ target_address_t addr, target_address_t *ret) -+{ -+ *ret = addr; -+ return 0; -+} -+ -+struct ltelf; -+int -+arch_translate_address(struct ltelf *lte, -+ target_address_t addr, target_address_t *ret) -+{ -+ *ret = addr; -+ return 0; -+} -+#endif - - void - breakpoint_on_hit(struct breakpoint *bp, struct Process *proc) - { - assert(bp != NULL); - if (bp->cbs != NULL && bp->cbs->on_hit != NULL) -- (bp->cbs->on_hit) (bp, proc); -+ (bp->cbs->on_hit)(bp, proc); -+} -+ -+void -+breakpoint_on_continue(struct breakpoint *bp, struct Process *proc) -+{ -+ assert(bp != NULL); -+ if (bp->cbs != NULL && bp->cbs->on_continue != NULL) -+ (bp->cbs->on_continue)(bp, proc); -+ else -+ continue_after_breakpoint(proc, bp); - } - - void --breakpoint_on_destroy(struct breakpoint *bp) -+breakpoint_on_retract(struct breakpoint *bp, struct Process *proc) - { - assert(bp != NULL); -- if (bp->cbs != NULL && bp->cbs->on_destroy != NULL) -- (bp->cbs->on_destroy) (bp); -+ if (bp->cbs != NULL && bp->cbs->on_retract != NULL) -+ (bp->cbs->on_retract)(bp, proc); - } - - /*****************************************************************************/ -@@ -39,52 +71,171 @@ address2bpstruct(Process *proc, void *addr) - return dict_find_entry(proc->breakpoints, addr); - } - --struct breakpoint * --insert_breakpoint(Process *proc, void *addr, -- struct library_symbol *libsym, int enable) -+#ifndef ARCH_HAVE_BREAKPOINT_DATA -+int -+arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp) - { -- struct breakpoint *sbp; -+ return 0; -+} - -- Process * leader = proc->leader; -+void -+arch_breakpoint_destroy(struct breakpoint *sbp) -+{ -+} -+ -+int -+arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp) -+{ -+ return 0; -+} -+#endif -+ -+static void -+breakpoint_init_base(struct breakpoint *bp, struct Process *proc, -+ target_address_t addr, struct library_symbol *libsym) -+{ -+ bp->cbs = NULL; -+ bp->addr = addr; -+ memset(bp->orig_value, 0, sizeof(bp->orig_value)); -+ bp->enabled = 0; -+ bp->libsym = libsym; -+} -+ -+/* On second thought, I don't think we need PROC. All the translation -+ * (arch_translate_address in particular) should be doable using -+ * static lookups of various sections in the ELF file. We shouldn't -+ * need process for anything. */ -+int -+breakpoint_init(struct breakpoint *bp, struct Process *proc, -+ target_address_t addr, struct library_symbol *libsym) -+{ -+ breakpoint_init_base(bp, proc, addr, libsym); -+ return arch_breakpoint_init(proc, bp); -+} -+ -+void -+breakpoint_set_callbacks(struct breakpoint *bp, struct bp_callbacks *cbs) -+{ -+ if (bp->cbs != NULL) -+ assert(bp->cbs == NULL); -+ bp->cbs = cbs; -+} -+ -+void -+breakpoint_destroy(struct breakpoint *bp) -+{ -+ if (bp == NULL) -+ return; -+ arch_breakpoint_destroy(bp); -+} -+ -+struct find_symbol_data { -+ struct library_symbol *old_libsym; -+ struct library_symbol *found_libsym; -+}; -+ -+static enum callback_status -+find_sym_in_lib(struct Process *proc, struct library *lib, void *u) -+{ -+ struct find_symbol_data *fs = u; -+ fs->found_libsym -+ = library_each_symbol(lib, NULL, library_symbol_equal_cb, -+ fs->old_libsym); -+ return fs->found_libsym != NULL ? CBS_STOP : CBS_CONT; -+} -+ -+int -+breakpoint_clone(struct breakpoint *retp, struct Process *new_proc, -+ struct breakpoint *bp, struct Process *old_proc) -+{ -+ /* Find library and symbol that this breakpoint was linked to. */ -+ struct library_symbol *libsym = bp->libsym; -+ struct library *lib = NULL; -+ if (libsym != NULL) { -+ struct find_symbol_data f_data = { -+ .old_libsym = libsym, -+ }; -+ lib = proc_each_library(old_proc, NULL, -+ find_sym_in_lib, &f_data); -+ assert(lib != NULL); -+ libsym = f_data.found_libsym; -+ } -+ -+ /* LIB and LIBSYM now hold the new library and symbol that -+ * correspond to the original breakpoint. Now we can do the -+ * clone itself. */ -+ breakpoint_init_base(retp, new_proc, bp->addr, libsym); -+ memcpy(retp->orig_value, bp->orig_value, sizeof(bp->orig_value)); -+ retp->enabled = bp->enabled; -+ if (arch_breakpoint_clone(retp, bp) < 0) -+ return -1; -+ breakpoint_set_callbacks(retp, bp->cbs); -+ return 0; -+} -+ -+int -+breakpoint_turn_on(struct breakpoint *bp, struct Process *proc) -+{ -+ bp->enabled++; -+ if (bp->enabled == 1) { -+ assert(proc->pid != 0); -+ enable_breakpoint(proc, bp); -+ } -+ return 0; -+} -+ -+int -+breakpoint_turn_off(struct breakpoint *bp, struct Process *proc) -+{ -+ bp->enabled--; -+ if (bp->enabled == 0) -+ disable_breakpoint(proc, bp); -+ assert(bp->enabled >= 0); -+ return 0; -+} -+ -+struct breakpoint * -+insert_breakpoint(struct Process *proc, void *addr, -+ struct library_symbol *libsym) -+{ -+ Process *leader = proc->leader; - - /* Only the group leader should be getting the breakpoints and - * thus have ->breakpoint initialized. */ - assert(leader != NULL); - assert(leader->breakpoints != NULL); - --#ifdef __arm__ -- int thumb_mode = (int)addr & 1; -- if (thumb_mode) -- addr = (void *)((int)addr & ~1); --#endif -- -- debug(DEBUG_FUNCTION, "insert_breakpoint(pid=%d, addr=%p, symbol=%s)", proc->pid, addr, libsym ? libsym->name : "NULL"); -- debug(1, "symbol=%s, addr=%p", libsym?libsym->name:"(nil)", addr); -- -- if (!addr) -- return NULL; -+ debug(DEBUG_FUNCTION, "insert_breakpoint(pid=%d, addr=%p, symbol=%s)", -+ proc->pid, addr, libsym ? libsym->name : "NULL"); - -- if (libsym) -- libsym->needs_init = 0; -+ assert(addr != 0); - -- sbp = dict_find_entry(leader->breakpoints, addr); -+ /* XXX what we need to do instead is have a list of -+ * breakpoints that are enabled at this address. The -+ * following works if every breakpoint is the same and there's -+ * no extra data, but that doesn't hold anymore. For now it -+ * will suffice, about the only realistic case where we need -+ * to have more than one breakpoint per address is return from -+ * a recursive library call. */ -+ struct breakpoint *sbp = dict_find_entry(leader->breakpoints, addr); - if (sbp == NULL) { -- sbp = calloc(1, sizeof(*sbp)); -- if (sbp == NULL) { -- return NULL; /* TODO FIXME XXX: error_mem */ -+ sbp = malloc(sizeof(*sbp)); -+ if (sbp == NULL -+ || breakpoint_init(sbp, proc, addr, libsym) < 0) { -+ free(sbp); -+ return NULL; -+ } -+ if (proc_add_breakpoint(leader, sbp) < 0) { -+ fail: -+ breakpoint_destroy(sbp); -+ free(sbp); -+ return NULL; - } -- dict_enter(leader->breakpoints, addr, sbp); -- sbp->addr = addr; -- sbp->libsym = libsym; - } --#ifdef __arm__ -- sbp->thumb_mode = thumb_mode | proc->thumb_mode; -- proc->thumb_mode = 0; --#endif -- sbp->enabled++; -- if (sbp->enabled == 1 && enable) { -- assert(proc->pid != 0); -- enable_breakpoint(proc, sbp); -+ -+ if (breakpoint_turn_on(sbp, proc) < 0) { -+ proc_remove_breakpoint(leader, sbp); -+ goto fail; - } - - return sbp; -@@ -93,23 +244,41 @@ insert_breakpoint(Process *proc, void *addr, - void - delete_breakpoint(Process *proc, void *addr) - { -- struct breakpoint *sbp; -- - debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr); - - Process * leader = proc->leader; - assert(leader != NULL); - -- sbp = dict_find_entry(leader->breakpoints, addr); -- assert(sbp); /* FIXME: remove after debugging has been done. */ -+ struct breakpoint *sbp = dict_find_entry(leader->breakpoints, addr); -+ assert(sbp != NULL); - /* This should only happen on out-of-memory conditions. */ - if (sbp == NULL) - return; - -- sbp->enabled--; -- if (sbp->enabled == 0) -- disable_breakpoint(proc, sbp); -- assert(sbp->enabled >= 0); -+ if (breakpoint_turn_off(sbp, proc) < 0) { -+ fprintf(stderr, "Couldn't turn off the breakpoint %s@%p\n", -+ breakpoint_name(sbp), sbp->addr); -+ return; -+ } -+ if (sbp->enabled == 0) { -+ proc_remove_breakpoint(leader, sbp); -+ breakpoint_destroy(sbp); -+ free(sbp); -+ } -+} -+ -+const char * -+breakpoint_name(const struct breakpoint *bp) -+{ -+ assert(bp != NULL); -+ return bp->libsym != NULL ? bp->libsym->name : NULL; -+} -+ -+struct library * -+breakpoint_library(const struct breakpoint *bp) -+{ -+ assert(bp != NULL); -+ return bp->libsym != NULL ? bp->libsym->lib : NULL; - } - - static void -@@ -176,108 +345,97 @@ disable_all_breakpoints(Process *proc) { - dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc); - } - --static void --free_bp_cb(void *addr, void *sbp, void *data) { -- debug(DEBUG_FUNCTION, "free_bp_cb(sbp=%p)", sbp); -- assert(sbp); -- free(sbp); --} -+/* XXX This is not currently properly supported. On clone, this is -+ * just sliced. Hopefully at the point that clone is done, this -+ * breakpoint is not necessary anymore. If this use case ends up -+ * being important, we need to add a clone and destroy callbacks to -+ * breakpoints, and we should also probably drop arch_breakpoint_data -+ * so that we don't end up with two different customization mechanisms -+ * for one structure. */ -+struct entry_breakpoint { -+ struct breakpoint super; -+ target_address_t dyn_addr; -+}; - - static void --entry_callback_hit(struct breakpoint *bp, struct Process *proc) -+entry_breakpoint_on_hit(struct breakpoint *a, struct Process *proc) - { -+ struct entry_breakpoint *bp = (void *)a; - if (proc == NULL || proc->leader == NULL) - return; -- delete_breakpoint(proc, bp->addr); // xxx -- reinitialize_breakpoints(proc->leader); -+ target_address_t dyn_addr = bp->dyn_addr; -+ delete_breakpoint(proc, bp->super.addr); -+ linkmap_init(proc, dyn_addr); -+ arch_dynlink_done(proc); - } - - int --breakpoints_init(Process *proc, int enable) -+entry_breakpoint_init(struct Process *proc, -+ struct entry_breakpoint *bp, target_address_t addr, -+ struct library *lib) - { -- debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid); -- if (proc->breakpoints) { /* let's remove that struct */ -- dict_apply_to_all(proc->breakpoints, free_bp_cb, NULL); -- dict_clear(proc->breakpoints); -- proc->breakpoints = NULL; -- } -+ int err; -+ if ((err = breakpoint_init(&bp->super, proc, addr, NULL)) < 0) -+ return err; - -- /* Only the thread group leader should hold the breakpoints. -- * (N.B. PID may be set to 0 temporarily when called by -- * handle_exec). */ -- assert(proc->leader == proc); -+ static struct bp_callbacks entry_callbacks = { -+ .on_hit = entry_breakpoint_on_hit, -+ }; -+ bp->super.cbs = &entry_callbacks; -+ bp->dyn_addr = lib->dyn_addr; -+ return 0; -+} - -- proc->breakpoints = dict_init(dict_key2hash_int, -- dict_key_cmp_int); -+int -+breakpoints_init(Process *proc) -+{ -+ debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid); - -- destroy_library_symbol_chain(proc->list_of_symbols); -- proc->list_of_symbols = NULL; -+ /* XXX breakpoint dictionary should be initialized -+ * outside. Here we just put in breakpoints. */ -+ assert(proc->breakpoints != NULL); - -- GElf_Addr entry; -- if (options.libcalls && proc->filename) { -- proc->list_of_symbols = read_elf(proc, &entry); -- if (proc->list_of_symbols == NULL) { -- fail: -- /* XXX leak breakpoints */ -- return -1; -- } -+ /* Only the thread group leader should hold the breakpoints. */ -+ assert(proc->leader == proc); - -- if (opt_e) { -- struct library_symbol **tmp1 = &proc->list_of_symbols; -- while (*tmp1) { -- struct opt_e_t *tmp2 = opt_e; -- int keep = !opt_e_enable; -- -- while (tmp2) { -- if (!strcmp((*tmp1)->name, -- tmp2->name)) { -- keep = opt_e_enable; -- } -- tmp2 = tmp2->next; -- } -- if (!keep) { -- *tmp1 = (*tmp1)->next; -- } else { -- tmp1 = &((*tmp1)->next); -- } -- } -+ /* N.B. the following used to be conditional on this, and -+ * maybe it still needs to be. */ -+ assert(proc->filename != NULL); -+ -+ struct library *lib = ltelf_read_main_binary(proc, proc->filename); -+ struct entry_breakpoint *entry_bp = NULL; -+ int bp_state = 0; -+ int result = -1; -+ switch (lib != NULL) { -+ fail: -+ switch (bp_state) { -+ case 2: -+ proc_remove_library(proc, lib); -+ proc_remove_breakpoint(proc, &entry_bp->super); -+ case 1: -+ breakpoint_destroy(&entry_bp->super); - } -+ library_destroy(lib); -+ free(entry_bp); -+ case 0: -+ return result; - } - -- struct breakpoint *entry_bp -- = insert_breakpoint(proc, (void *)(uintptr_t)entry, NULL, 1); -- if (entry_bp == NULL) { -- fprintf(stderr, "fail!\n"); -+ entry_bp = malloc(sizeof(*entry_bp)); -+ if (entry_bp == NULL -+ || (result = entry_breakpoint_init(proc, entry_bp, -+ lib->entry, lib)) < 0) - goto fail; -- } -+ ++bp_state; - -- static struct bp_callbacks entry_callbacks = { -- .on_hit = entry_callback_hit, -- }; -- entry_bp->cbs = &entry_callbacks; -+ if ((result = proc_add_breakpoint(proc, &entry_bp->super)) < 0) -+ goto fail; -+ ++bp_state; -+ -+ if ((result = breakpoint_turn_on(&entry_bp->super, proc)) < 0) -+ goto fail; -+ proc_add_library(proc, lib); - - proc->callstack_depth = 0; - return 0; - } -- --void --reinitialize_breakpoints(Process *proc) { -- struct library_symbol *sym; -- -- debug(DEBUG_FUNCTION, "reinitialize_breakpoints(pid=%d)", proc->pid); -- -- sym = proc->list_of_symbols; -- -- while (sym) { -- if (sym->needs_init) { -- insert_breakpoint(proc, sym2addr(proc, sym), sym, 1); -- if (sym->needs_init && !sym->is_weak) { -- fprintf(stderr, -- "could not re-initialize breakpoint for \"%s\" in file \"%s\"\n", -- sym->name, proc->filename); -- exit(1); -- } -- } -- sym = sym->next; -- } --} -diff --git a/common.h b/common.h -index ed618b5..04e095c 100644 ---- a/common.h -+++ b/common.h -@@ -2,7 +2,4 @@ - #include --#if defined(HAVE_LIBUNWIND) --#include --#endif /* defined(HAVE_LIBUNWIND) */ - - #include - #include -@@ -17,6 +14,7 @@ - #include "debug.h" - #include "ltrace-elf.h" - #include "read_config_file.h" -+#include "proc.h" - - #if defined HAVE_LIBIBERTY || defined HAVE_LIBSUPC__ - # define USE_DEMANGLE -@@ -116,116 +114,9 @@ struct Function { - Function * next; - }; - --enum toplt { -- LS_TOPLT_NONE = 0, /* PLT not used for this symbol. */ -- LS_TOPLT_EXEC, /* PLT for this symbol is executable. */ -- LS_TOPLT_POINT /* PLT for this symbol is a non-executable. */ --}; -- - extern Function * list_of_functions; - extern char *PLTs_initialized_by_here; - --struct library_symbol { -- char * name; -- void * enter_addr; -- char needs_init; -- enum toplt plt_type; -- char is_weak; -- struct library_symbol * next; --}; -- --struct callstack_element { -- union { -- int syscall; -- struct library_symbol * libfunc; -- } c_un; -- int is_syscall; -- void * return_addr; -- struct timeval time_spent; -- void * arch_ptr; --}; -- --#define MAX_CALLDEPTH 64 -- --typedef enum Process_State Process_State; --enum Process_State { -- STATE_ATTACHED = 0, -- STATE_BEING_CREATED, -- STATE_IGNORED /* ignore this process (it's a fork and no -f was used) */ --}; -- --typedef struct Event_Handler Event_Handler; --struct Event_Handler { -- /* Event handler that overrides the default one. Should -- * return NULL if the event was handled, otherwise the -- * returned event is passed to the default handler. */ -- Event * (* on_event)(Event_Handler * self, Event * event); -- -- /* Called when the event handler removal is requested. */ -- void (* destroy)(Event_Handler * self); --}; -- --/* XXX We would rather have this all organized a little differently, -- * have Process for the whole group and Task for what's there for -- * per-thread stuff. But for now this is the less invasive way of -- * structuring it. */ --struct Process { -- Process_State state; -- Process * parent; /* needed by STATE_BEING_CREATED */ -- char * filename; -- pid_t pid; -- -- /* Dictionary of breakpoints (which is a mapping -- * address->breakpoint). This is NULL for non-leader -- * processes. */ -- Dict * breakpoints; -- -- int mask_32bit; /* 1 if 64-bit ltrace is tracing 32-bit process */ -- unsigned int personality; -- int tracesysgood; /* signal indicating a PTRACE_SYSCALL trap */ -- -- int callstack_depth; -- struct callstack_element callstack[MAX_CALLDEPTH]; -- struct library_symbol * list_of_symbols; -- -- int libdl_hooked; -- /* Arch-dependent: */ -- void * debug; /* arch-dep process debug struct */ -- long debug_state; /* arch-dep debug state */ -- void * instruction_pointer; -- void * stack_pointer; /* To get return addr, args... */ -- void * return_addr; -- void * arch_ptr; -- short e_machine; -- short need_to_reinitialize_breakpoints; --#ifdef __arm__ -- int thumb_mode; /* ARM execution mode: 0: ARM, 1: Thumb */ --#endif -- --#if defined(HAVE_LIBUNWIND) -- /* libunwind address space */ -- unw_addr_space_t unwind_as; -- void *unwind_priv; --#endif /* defined(HAVE_LIBUNWIND) */ -- -- /* Set in leader. */ -- Event_Handler * event_handler; -- -- -- /** -- * Process chaining. -- **/ -- Process * next; -- -- /* LEADER points to the leader thread of the POSIX.1 process. -- If X->LEADER == X, then X is the leader thread and the -- Process structures chained by NEXT represent other threads, -- up until, but not including, the next leader thread. -- LEADER may be NULL after the leader has already exited. In -- that case this process is waiting to be collected. */ -- Process * leader; --}; -- - struct opt_c_struct { - int count; - struct timeval tv; -@@ -248,23 +139,6 @@ enum process_status { - ps_other, /* Necessary other states can be added as needed. */ - }; - --enum pcb_status { -- pcb_stop, /* The iteration should stop. */ -- pcb_cont, /* The iteration should continue. */ --}; -- --/* Process list */ --extern Process * pid2proc(pid_t pid); --extern void add_process(Process * proc); --extern void remove_process(Process * proc); --extern void change_process_leader(Process * proc, Process * leader); --extern Process *each_process(Process * start, -- enum pcb_status (* cb)(Process * proc, void * data), -- void * data); --extern Process *each_task(Process * start, -- enum pcb_status (* cb)(Process * proc, void * data), -- void * data); -- - /* Events */ - enum ecb_status { - ecb_cont, /* The iteration should continue. */ -@@ -279,31 +153,15 @@ extern Event * each_qd_event(enum ecb_status (* cb)(Event * event, void * data), - extern void enque_event(Event * event); - extern void handle_event(Event * event); - --extern void install_event_handler(Process * proc, Event_Handler * handler); --extern void destroy_event_handler(Process * proc); -- - extern pid_t execute_program(const char * command, char ** argv); - extern int display_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); - extern void disable_all_breakpoints(Process * proc); - --extern Process * open_program(char * filename, pid_t pid, int init_breakpoints); --extern void open_pid(pid_t pid); - extern void show_summary(void); - extern arg_type_info * lookup_prototype(enum arg_type at); - --extern int do_init_elf(struct ltelf *lte, const char *filename); --extern void do_close_elf(struct ltelf *lte); --extern int in_load_libraries(const char *name, struct ltelf *lte, size_t count, GElf_Sym *sym); --extern struct library_symbol *library_symbols; --extern void add_library_symbol(GElf_Addr addr, const char *name, -- struct library_symbol **library_symbolspp, -- enum toplt type_of_plt, int is_weak); -- --extern struct library_symbol * clone_library_symbol(struct library_symbol * s); --extern void destroy_library_symbol(struct library_symbol * s); --extern void destroy_library_symbol_chain(struct library_symbol * chain); -- - struct breakpoint; -+struct library_symbol; - - /* Arch-dependent stuff: */ - extern char * pid2name(pid_t pid); -@@ -311,8 +169,8 @@ extern pid_t process_leader(pid_t pid); - extern int process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n); - extern int process_stopped(pid_t pid); - extern enum process_status process_status(pid_t pid); --extern void trace_set_options(Process * proc, pid_t pid); --extern void wait_for_proc(pid_t pid); -+extern void trace_set_options(struct Process *proc); -+extern int wait_for_proc(pid_t pid); - extern void trace_me(void); - extern int trace_pid(pid_t pid); - extern void untrace_pid(pid_t pid); -@@ -322,13 +180,13 @@ extern void set_instruction_pointer(Process * proc, void * addr); - extern void * get_stack_pointer(Process * proc); - extern void * get_return_addr(Process * proc, void * stack_pointer); - extern void set_return_addr(Process * proc, void * addr); --extern void enable_breakpoint(Process * proc, struct breakpoint *sbp); --extern void disable_breakpoint(Process * proc, struct breakpoint *sbp); -+extern void enable_breakpoint(struct Process *proc, struct breakpoint *sbp); -+extern void disable_breakpoint(struct Process *proc, struct breakpoint *sbp); - extern int syscall_p(Process * proc, int status, int * sysnum); - extern void continue_process(pid_t pid); - extern void continue_after_signal(pid_t pid, int signum); - extern void continue_after_syscall(Process *proc, int sysnum, int ret_p); --extern void continue_after_breakpoint(Process * proc, struct breakpoint *sbp); -+extern void continue_after_breakpoint(struct Process *proc, struct breakpoint *sbp); - extern void continue_after_vfork(Process * proc); - extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); - extern void save_register_args(enum tof type, Process * proc); -@@ -337,7 +195,7 @@ extern int umovelong (Process * proc, void * addr, long * result, arg_type_info - extern size_t umovebytes (Process *proc, void * addr, void * laddr, size_t count); - extern int ffcheck(void * maddr); - extern void * sym2addr(Process *, struct library_symbol *); --extern int linkmap_init(Process *, struct ltelf *); -+extern int linkmap_init(struct Process *proc, void *dyn_addr); - extern void arch_check_dbg(Process *proc); - extern int task_kill (pid_t pid, int sig); - -@@ -357,9 +215,51 @@ void trace_fail_warning(pid_t pid); - * If the call to OS_LTRACE_EXITING_SIGHANDLER didn't handle the - * request, OS_LTRACE_EXITING is called when the next event is - * generated. Therefore it's called in "safe" context, without -- * re-entrancy concerns, but it's only called after an even is -+ * re-entrancy concerns, but it's only called after an event is - * generated. */ - int os_ltrace_exiting_sighandler(void); - void os_ltrace_exiting(void); - --extern struct ltelf main_lte; -+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); -+ -+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); -+ -+void arch_library_init(struct library *lib); -+void arch_library_destroy(struct library *lib); -+void arch_library_clone(struct library *retp, struct library *lib); -+ -+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); -+ -+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); -+ -+typedef void *target_address_t; -+/* 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, -+ target_address_t *entryp, -+ target_address_t *interp_biasp); -+ -+/* This is called after the dynamic linker is done with the -+ * process startup. */ -+void arch_dynlink_done(struct Process *proc); -diff --git a/debug.c b/debug.c -index 1be873b..5fb9feb 100644 ---- a/debug.c -+++ b/debug.c -@@ -16,6 +16,7 @@ debug_(int level, const char *file, int line, const char *fmt, ...) { - va_end(args); - - output_line(NULL, "DEBUG: %s:%d: %s", file, line, buf); -+ fflush(options.output); - } - - /* -diff --git a/defs.h b/defs.h -index dea000b..1eadb47 100644 ---- a/defs.h -+++ b/defs.h -@@ -14,5 +14,3 @@ - #ifndef DEFAULT_ARRAYLEN - #define DEFAULT_ARRAYLEN 4 /* default maximum # array elements */ - #endif /* (-A switch) */ -- --#define MAX_LIBRARIES 200 -diff --git a/dict.c b/dict.c -index ba318cd..b32ef8e 100644 ---- a/dict.c -+++ b/dict.c -@@ -24,13 +24,14 @@ struct dict_entry { - - struct dict { - struct dict_entry *buckets[DICTTABLESIZE]; -- unsigned int (*key2hash) (void *); -- int (*key_cmp) (void *, void *); -+ unsigned int (*key2hash) (const void *); -+ int (*key_cmp) (const void *, const void *); - }; - - Dict * --dict_init(unsigned int (*key2hash) (void *), -- int (*key_cmp) (void *, void *)) { -+dict_init(unsigned int (*key2hash) (const void *), -+ int (*key_cmp) (const void *, const void *)) -+{ - Dict *d; - int i; - -@@ -103,7 +104,31 @@ dict_enter(Dict *d, void *key, void *value) { - } - - void * --dict_find_entry(Dict *d, void *key) { -+dict_remove(Dict *d, void *key) -+{ -+ assert(d != NULL); -+ debug(DEBUG_FUNCTION, "dict_remove(%p)", key); -+ -+ unsigned int hash = d->key2hash(key); -+ unsigned int bucketpos = hash % DICTTABLESIZE; -+ -+ struct dict_entry **entryp; -+ for (entryp = &d->buckets[bucketpos]; (*entryp) != NULL; -+ entryp = &(*entryp)->next) { -+ struct dict_entry *entry = *entryp; -+ if (hash != entry->hash) -+ continue; -+ if (d->key_cmp(key, entry->key) == 0) { -+ *entryp = entry->next; -+ return entry->value; -+ } -+ } -+ return NULL; -+} -+ -+void * -+dict_find_entry(Dict *d, const void *key) -+{ - unsigned int hash; - unsigned int bucketpos; - struct dict_entry *entry; -@@ -147,7 +172,8 @@ dict_apply_to_all(Dict *d, - /*****************************************************************************/ - - unsigned int --dict_key2hash_string(void *key) { -+dict_key2hash_string(const void *key) -+{ - const char *s = (const char *)key; - unsigned int total = 0, shift = 0; - -@@ -163,19 +189,22 @@ dict_key2hash_string(void *key) { - } - - int --dict_key_cmp_string(void *key1, void *key2) { -+dict_key_cmp_string(const void *key1, const void *key2) -+{ - assert(key1); - assert(key2); - return strcmp((const char *)key1, (const char *)key2); - } - - unsigned int --dict_key2hash_int(void *key) { -+dict_key2hash_int(const void *key) -+{ - return (unsigned long)key; - } - - int --dict_key_cmp_int(void *key1, void *key2) { -+dict_key_cmp_int(const void *key1, const void *key2) -+{ - return key1 - key2; - } - -diff --git a/dict.h b/dict.h -index 27dc7bf..f41011a 100644 ---- a/dict.h -+++ b/dict.h -@@ -4,19 +4,22 @@ - - typedef struct dict Dict; - --extern Dict *dict_init(unsigned int (*key2hash) (void *), -- int (*key_cmp) (void *, void *)); -+extern Dict *dict_init(unsigned int (*key2hash) (const void *), -+ int (*key_cmp) (const void *, const void *)); - extern void dict_clear(Dict *d); - extern int dict_enter(Dict *d, void *key, void *value); --extern void *dict_find_entry(Dict *d, void *key); -+extern void *dict_remove(Dict *d, void *key); -+extern void *dict_find_entry(Dict *d, const void *key); - extern void dict_apply_to_all(Dict *d, - void (*func) (void *key, void *value, void *data), - void *data); - --extern unsigned int dict_key2hash_string(void *key); --extern int dict_key_cmp_string(void *key1, void *key2); --extern unsigned int dict_key2hash_int(void *key); --extern int dict_key_cmp_int(void *key1, void *key2); -+extern unsigned int dict_key2hash_string(const void *key); -+extern int dict_key_cmp_string(const void *key1, const void *key2); -+ -+extern unsigned int dict_key2hash_int(const void *key); -+extern int dict_key_cmp_int(const void *key1, const void *key2); -+ - extern Dict * dict_clone(Dict *old, void * (*key_clone)(void*), void * (*value_clone)(void*)); - extern Dict * dict_clone2(Dict * old, - void * (* key_clone)(void * key, void * data), -diff --git a/display_args.c b/display_args.c -index c639c88..5df34ca 100644 ---- a/display_args.c -+++ b/display_args.c -@@ -5,6 +5,7 @@ - #include - - #include "common.h" -+#include "proc.h" - - static int display_char(int what); - static int display_string(enum tof type, Process *proc, -diff --git a/execute_program.c b/execute_program.c -index 859f32c..55df205 100644 ---- a/execute_program.c -+++ b/execute_program.c -@@ -78,6 +78,7 @@ execute_program(const char * command, char **argv) - - pid = fork(); - if (pid < 0) { -+ fail: - perror("ltrace: fork"); - exit(1); - } else if (!pid) { /* child */ -@@ -89,9 +90,9 @@ execute_program(const char * command, char **argv) - _exit(1); - } - -- wait_for_proc(pid); -+ if (wait_for_proc(pid) < 0) -+ goto fail; - - debug(1, "PID=%d", pid); -- - return pid; - } -diff --git a/filter.c b/filter.c -new file mode 100644 -index 0000000..003010d ---- /dev/null -+++ b/filter.c -@@ -0,0 +1,188 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 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 -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#include "filter.h" -+#include "library.h" -+ -+void -+filter_init(struct filter *filt) -+{ -+ filt->rules = NULL; -+ filt->next = NULL; -+} -+ -+void -+filter_destroy(struct filter *filt) -+{ -+ struct filter_rule *it; -+ for (it = filt->rules; it != NULL; ) { -+ struct filter_rule *next = it->next; -+ filter_rule_destroy(it); -+ it = next; -+ } -+} -+ -+void -+filter_rule_init(struct filter_rule *rule, enum filter_rule_type type, -+ struct filter_lib_matcher *matcher, -+ regex_t symbol_re) -+{ -+ rule->type = type; -+ rule->lib_matcher = matcher; -+ rule->symbol_re = symbol_re; -+ rule->next = NULL; -+} -+ -+void -+filter_rule_destroy(struct filter_rule *rule) -+{ -+ filter_lib_matcher_destroy(rule->lib_matcher); -+ regfree(&rule->symbol_re); -+} -+ -+void -+filter_add_rule(struct filter *filt, struct filter_rule *rule) -+{ -+ struct filter_rule **rulep; -+ for (rulep = &filt->rules; *rulep != NULL; rulep = &(*rulep)->next) -+ ; -+ *rulep = rule; -+} -+ -+void -+filter_lib_matcher_name_init(struct filter_lib_matcher *matcher, -+ enum filter_lib_matcher_type type, -+ regex_t libname_re) -+{ -+ switch (type) { -+ case FLM_MAIN: -+ assert(type != type); -+ abort(); -+ -+ case FLM_SONAME: -+ case FLM_PATHNAME: -+ matcher->type = type; -+ matcher->libname_re = libname_re; -+ } -+} -+ -+void -+filter_lib_matcher_main_init(struct filter_lib_matcher *matcher) -+{ -+ matcher->type = FLM_MAIN; -+} -+ -+void -+filter_lib_matcher_destroy(struct filter_lib_matcher *matcher) -+{ -+ switch (matcher->type) { -+ case FLM_SONAME: -+ case FLM_PATHNAME: -+ regfree(&matcher->libname_re); -+ break; -+ case FLM_MAIN: -+ break; -+ } -+} -+ -+static int -+re_match_or_error(regex_t *re, const char *name, const char *what) -+{ -+ int status = regexec(re, name, 0, NULL, 0); -+ if (status == 0) -+ return 1; -+ if (status == REG_NOMATCH) -+ return 0; -+ -+ char buf[200]; -+ regerror(status, re, buf, sizeof buf); -+ fprintf(stderr, "Error when matching %s: %s\n", name, buf); -+ -+ return 0; -+} -+ -+static int -+matcher_matches_library(struct filter_lib_matcher *matcher, struct library *lib) -+{ -+ switch (matcher->type) { -+ case FLM_SONAME: -+ return re_match_or_error(&matcher->libname_re, lib->soname, -+ "library soname"); -+ case FLM_PATHNAME: -+ return re_match_or_error(&matcher->libname_re, lib->pathname, -+ "library pathname"); -+ case FLM_MAIN: -+ return lib->type == LT_LIBTYPE_MAIN; -+ } -+ assert(matcher->type != matcher->type); -+ abort(); -+} -+ -+int -+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; -+ }; -+ return 0; -+} -+ -+int -+filter_matches_symbol(struct filter *filt, -+ const char *sym_name, struct library *lib) -+{ -+ for (; filt != NULL; filt = filt->next) { -+ int matches = 0; -+ struct filter_rule *it; -+ for (it = filt->rules; it != NULL; it = it->next) { -+ switch (it->type) { -+ case FR_ADD: -+ if (matches) -+ continue; -+ break; -+ case FR_SUBTRACT: -+ if (!matches) -+ continue; -+ } -+ -+ if (matcher_matches_library(it->lib_matcher, lib) -+ && re_match_or_error(&it->symbol_re, sym_name, -+ "symbol name")) -+ matches = !matches; -+ } -+ if (matches) -+ return 1; -+ } -+ return 0; -+} -diff --git a/filter.h b/filter.h -new file mode 100644 -index 0000000..65c575a ---- /dev/null -+++ b/filter.h -@@ -0,0 +1,99 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 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 -+ */ -+ -+/* This file contains declarations and types for working with symbol -+ * filters. */ -+ -+#ifndef FILTER_H -+#define FILTER_H -+ -+#include -+#include -+ -+struct library; -+struct library_symbol; -+ -+enum filter_lib_matcher_type { -+ /* Match by soname. */ -+ FLM_SONAME, -+ /* Match by path name. */ -+ FLM_PATHNAME, -+ /* Match main binary. */ -+ FLM_MAIN, -+}; -+ -+struct filter_lib_matcher { -+ enum filter_lib_matcher_type type; -+ regex_t libname_re; -+}; -+ -+enum filter_rule_type { -+ FR_ADD, -+ FR_SUBTRACT, -+}; -+ -+struct filter_rule { -+ struct filter_rule *next; -+ struct filter_lib_matcher *lib_matcher; -+ regex_t symbol_re; /* Regex for matching symbol name. */ -+ enum filter_rule_type type; -+}; -+ -+struct filter { -+ struct filter *next; -+ struct filter_rule *rules; -+}; -+ -+void filter_init(struct filter *filt); -+void filter_destroy(struct filter *filt); -+ -+/* Both SYMBOL_RE and MATCHER are owned and destroyed by RULE. */ -+void filter_rule_init(struct filter_rule *rule, enum filter_rule_type type, -+ struct filter_lib_matcher *matcher, -+ regex_t symbol_re); -+ -+void filter_rule_destroy(struct filter_rule *rule); -+ -+/* RULE is added to FILT and owned and destroyed by it. */ -+void filter_add_rule(struct filter *filt, struct filter_rule *rule); -+ -+/* Create a matcher that matches library name. RE is owned and -+ * destroyed by MATCHER. TYPE shall be FLM_SONAME or -+ * FLM_PATHNAME. */ -+void filter_lib_matcher_name_init(struct filter_lib_matcher *matcher, -+ enum filter_lib_matcher_type type, -+ regex_t re); -+ -+/* Create a matcher that matches main binary. */ -+void filter_lib_matcher_main_init(struct filter_lib_matcher *matcher); -+ -+void filter_lib_matcher_destroy(struct filter_lib_matcher *matcher); -+ -+/* Ask whether FILTER might match a symbol in LIB. 0 if no, non-0 if -+ * yes. Note that positive answer doesn't mean that anything will -+ * actually be matched, just that potentially it could. */ -+int filter_matches_library(struct filter *filt, struct library *lib); -+ -+/* Ask whether FILTER matches this symbol. Returns 0 if it doesn't, -+ * or non-0 value if it does. */ -+int filter_matches_symbol(struct filter *filt, const char *sym_name, -+ struct library *lib); -+ -+#endif /* FILTER_H */ -diff --git a/glob.c b/glob.c -new file mode 100644 -index 0000000..6c5c9ef ---- /dev/null -+++ b/glob.c -@@ -0,0 +1,275 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2007, 2008, 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 -+ */ -+ -+#include -+#include -+#include -+#include -+ -+static ssize_t -+match_character_class(const char *glob, size_t length, size_t from) -+{ -+ size_t i; -+ if (length > 0) -+ for (i = from + 2; i < length - 1 && glob[++i] != ':'; ) -+ ; -+ if (i >= length || glob[++i] != ']') -+ return -1; -+ return i; -+} -+ -+static ssize_t -+match_brack(const char *glob, size_t length, size_t from, int *exclmp) -+{ -+ size_t i = from + 1; -+ -+ if (i >= length) -+ return -1; -+ -+ /* Complement operator. */ -+ *exclmp = 0; -+ if (glob[i] == '^' || glob[i] == '!') { -+ *exclmp = glob[i++] == '!'; -+ if (i >= length) -+ return -1; -+ } -+ -+ /* On first character, both [ and ] are legal. But when [ is -+ * followed with :, it's character class. */ -+ if (glob[i] == '[' && glob[i + 1] == ':') { -+ ssize_t j = match_character_class(glob, length, i); -+ if (j < 0) -+ fail: -+ return -1; -+ i = j; -+ } -+ ++i; /* skip any character, including [ or ] */ -+ -+ int escape = 0; -+ for (; i < length; ++i) { -+ char c = glob[i]; -+ if (escape) { -+ ++i; -+ escape = 0; -+ -+ } else if (c == '[' && glob[i + 1] == ':') { -+ ssize_t j = match_character_class(glob, length, i); -+ if (j < 0) -+ goto fail; -+ i = j; -+ -+ } else if (c == ']') { -+ return i; -+ } -+ } -+ return -1; -+} -+ -+static int -+append(char **bufp, const char *str, size_t str_size, -+ size_t *sizep, size_t *allocp) -+{ -+ if (str_size == 0) -+ str_size = strlen(str); -+ size_t nsize = *sizep + str_size; -+ if (nsize > *allocp) { -+ size_t nalloc = nsize * 2; -+ char *nbuf = realloc(*bufp, nalloc); -+ if (nbuf == NULL) -+ return -1; -+ *allocp = nalloc; -+ *bufp = nbuf; -+ } -+ -+ memcpy(*bufp + *sizep, str, str_size); -+ *sizep = nsize; -+ return 0; -+} -+ -+static int -+glob_to_regex(const char *glob, char **retp) -+{ -+ size_t allocd = 0; -+ size_t size = 0; -+ char *buf = NULL; -+ -+ size_t length = strlen(glob); -+ int escape = 0; -+ size_t i; -+ for(i = 0; i < length; ++i) { -+ char c = glob[i]; -+ if (escape) { -+ if (c == '\\') { -+ if (append(&buf, "\\\\", 0, -+ &size, &allocd) < 0) { -+ fail: -+ free(buf); -+ return REG_ESPACE; -+ } -+ -+ } else if (c == '*') { -+ if (append(&buf, "\\*", 0, &size, &allocd) < 0) -+ goto fail; -+ } else if (c == '?') { -+ if (append(&buf, "?", 0, &size, &allocd) < 0) -+ goto fail; -+ } else if (append(&buf, (char[]){ '\\', c }, 2, -+ &size, &allocd) < 0) -+ goto fail; -+ escape = 0; -+ } else { -+ if (c == '\\') -+ escape = 1; -+ else if (c == '[') { -+ int exclm; -+ ssize_t j = match_brack(glob, length, i, &exclm); -+ if (j < 0) -+ return REG_EBRACK; -+ if (exclm -+ && append(&buf, "[^", 2, -+ &size, &allocd) < 0) -+ goto fail; -+ if (append(&buf, glob + i + 2*exclm, -+ j - i + 1 - 2*exclm, -+ &size, &allocd) < 0) -+ goto fail; -+ i = j; -+ -+ } else if (c == '*') { -+ if (append(&buf, ".*", 0, &size, &allocd) < 0) -+ goto fail; -+ } else if (c == '?') { -+ if (append(&buf, ".", 0, &size, &allocd) < 0) -+ goto fail; -+ } else if (c == '.') { -+ if (append(&buf, "\\.", 0, &size, &allocd) < 0) -+ goto fail; -+ } else if (append(&buf, &c, 1, &size, &allocd) < 0) -+ goto fail; -+ } -+ } -+ -+ if (escape) { -+ free(buf); -+ return REG_EESCAPE; -+ } -+ -+ { -+ char c = 0; -+ if (append(&buf, &c, 1, &size, &allocd) < 0) -+ goto fail; -+ } -+ *retp = buf; -+ return 0; -+} -+ -+int -+globcomp(regex_t *preg, const char *glob, int cflags) -+{ -+ char *regex; -+ int status = glob_to_regex(glob, ®ex); -+ if (status != 0) -+ return status; -+ status = regcomp(preg, regex, cflags); -+ free(regex); -+ return status; -+} -+ -+#ifdef TEST -+#include -+#include -+ -+static void -+translate(const char *glob, int exp_status, const char *expect) -+{ -+ char *pattern = NULL; -+ int status = glob_to_regex(glob, &pattern); -+ if (status != exp_status) { -+ fprintf(stderr, "translating %s, expected status %d, got %d\n", -+ glob, exp_status, status); -+ return; -+ } -+ -+ if (status == 0) { -+ assert(pattern != NULL); -+ if (strcmp(pattern, expect) != 0) -+ fprintf(stderr, "translating %s, expected %s, got %s\n", -+ glob, expect, pattern); -+ free(pattern); -+ } else { -+ assert(pattern == NULL); -+ } -+} -+ -+static void -+try_match(const char *glob, const char *str, int expect) -+{ -+ regex_t preg; -+ int status = globcomp(&preg, glob, 0); -+ assert(status == 0); -+ status = regexec(&preg, str, 0, NULL, 0); -+ assert(status == expect); -+ regfree(&preg); -+} -+ -+int -+main(void) -+{ -+ translate("*", 0, ".*"); -+ translate("?", 0, "."); -+ translate(".*", 0, "\\..*"); -+ translate("*.*", 0, ".*\\..*"); -+ translate("*a*", 0, ".*a.*"); -+ translate("[abc]", 0, "[abc]"); -+ translate("[^abc]", 0, "[^abc]"); -+ translate("[!abc]", 0, "[^abc]"); -+ translate("[]]", 0, "[]]"); -+ translate("[[]", 0, "[[]"); -+ translate("[^]]", 0, "[^]]"); -+ translate("[^a-z]", 0, "[^a-z]"); -+ translate("[abc\\]]", 0, "[abc\\]]"); -+ translate("[abc\\]def]", 0, "[abc\\]def]"); -+ translate("[[:space:]]", 0, "[[:space:]]"); -+ translate("[^[:space:]]", 0, "[^[:space:]]"); -+ translate("[![:space:]]", 0, "[^[:space:]]"); -+ translate("[^a-z]*", 0, "[^a-z].*"); -+ translate("[^a-z]bar*", 0, "[^a-z]bar.*"); -+ translate("*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.", 0, -+ ".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\." -+ ".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\."); -+ -+ translate("\\", REG_EESCAPE, NULL); -+ translate("[^[:naotuh\\", REG_EBRACK, NULL); -+ translate("[^[:", REG_EBRACK, NULL); -+ translate("[^[", REG_EBRACK, NULL); -+ translate("[^", REG_EBRACK, NULL); -+ translate("[\\", REG_EBRACK, NULL); -+ translate("[", REG_EBRACK, NULL); -+ -+ try_match("abc*def", "abc012def", 0); -+ try_match("abc*def", "ab012def", REG_NOMATCH); -+ try_match("[abc]*def", "a1def", 0); -+ try_match("[abc]*def", "b1def", 0); -+ try_match("[abc]*def", "d1def", REG_NOMATCH); -+ -+ return 0; -+} -+ -+#endif -diff --git a/glob.h b/glob.h -new file mode 100644 -index 0000000..d60c0a2 ---- /dev/null -+++ b/glob.h -@@ -0,0 +1,32 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2007, 2008, 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 _GLOB_H_ -+#define _GLOB_H_ -+ -+#include -+#include -+ -+/* This is akin to regcomp(3), except it compiles a glob expression -+ * passed in GLOB. See glob(7) for more information about the syntax -+ * supported by globcomp. */ -+int globcomp(regex_t *preg, const char *glob, int cflags); -+ -+#endif /* _GLOB_H_ */ -diff --git a/handle_event.c b/handle_event.c -index ec4c9f3..73c118a 100644 ---- a/handle_event.c -+++ b/handle_event.c -@@ -1,20 +1,18 @@ - #define _GNU_SOURCE - #include "config.h" - -+#include -+#include -+#include - #include --#include - #include --#include --#include -+#include - #include --#include -- --#ifdef __powerpc__ --#include --#endif - --#include "common.h" - #include "breakpoint.h" -+#include "common.h" -+#include "library.h" -+#include "proc.h" - - static void handle_signal(Event *event); - static void handle_exit(Event *event); -@@ -42,7 +40,7 @@ call_handler(Process * proc, Event * event) - { - assert(proc != NULL); - -- Event_Handler * handler = proc->event_handler; -+ struct event_handler *handler = proc->event_handler; - if (handler == NULL) - return event; - -@@ -86,60 +84,72 @@ handle_event(Event *event) - debug(1, "event: none"); - return; - case EVENT_SIGNAL: -- debug(1, "event: signal (%s [%d])", -+ debug(1, "[%d] event: signal (%s [%d])", -+ event->proc->pid, - shortsignal(event->proc, event->e_un.signum), - event->e_un.signum); - handle_signal(event); - return; - case EVENT_EXIT: -- debug(1, "event: exit (%d)", event->e_un.ret_val); -+ debug(1, "[%d] event: exit (%d)", -+ event->proc->pid, -+ event->e_un.ret_val); - handle_exit(event); - return; - case EVENT_EXIT_SIGNAL: -- debug(1, "event: exit signal (%s [%d])", -+ debug(1, "[%d] event: exit signal (%s [%d])", -+ event->proc->pid, - shortsignal(event->proc, event->e_un.signum), - event->e_un.signum); - handle_exit_signal(event); - return; - case EVENT_SYSCALL: -- debug(1, "event: syscall (%s [%d])", -+ debug(1, "[%d] event: syscall (%s [%d])", -+ event->proc->pid, - sysname(event->proc, event->e_un.sysnum), - event->e_un.sysnum); - handle_syscall(event); - return; - case EVENT_SYSRET: -- debug(1, "event: sysret (%s [%d])", -+ debug(1, "[%d] event: sysret (%s [%d])", -+ event->proc->pid, - sysname(event->proc, event->e_un.sysnum), - event->e_un.sysnum); - handle_sysret(event); - return; - case EVENT_ARCH_SYSCALL: -- debug(1, "event: arch_syscall (%s [%d])", -- arch_sysname(event->proc, event->e_un.sysnum), -- event->e_un.sysnum); -+ debug(1, "[%d] event: arch_syscall (%s [%d])", -+ event->proc->pid, -+ arch_sysname(event->proc, event->e_un.sysnum), -+ event->e_un.sysnum); - handle_arch_syscall(event); - return; - case EVENT_ARCH_SYSRET: -- debug(1, "event: arch_sysret (%s [%d])", -- arch_sysname(event->proc, event->e_un.sysnum), -- event->e_un.sysnum); -+ debug(1, "[%d] event: arch_sysret (%s [%d])", -+ event->proc->pid, -+ arch_sysname(event->proc, event->e_un.sysnum), -+ event->e_un.sysnum); - handle_arch_sysret(event); - return; - case EVENT_CLONE: - case EVENT_VFORK: -- debug(1, "event: clone (%u)", event->e_un.newpid); -+ debug(1, "[%d] event: clone (%u)", -+ event->proc->pid, event->e_un.newpid); - handle_clone(event); - return; - case EVENT_EXEC: -- debug(1, "event: exec()"); -+ debug(1, "[%d] event: exec()", -+ event->proc->pid); - handle_exec(event); - return; - case EVENT_BREAKPOINT: -- debug(1, "event: breakpoint"); -+ debug(1, "[%d] event: breakpoint %p", -+ event->proc->pid, event->e_un.brk_addr); - handle_breakpoint(event); - return; - case EVENT_NEW: -- debug(1, "event: new process"); -+ debug(1, "[%d] event: new process", -+ event->e_un.newpid); - handle_new(event); - return; - default: -@@ -148,37 +158,6 @@ handle_event(Event *event) - } - } - --/* TODO */ --static void * --address_clone(void * addr, void * data) --{ -- debug(DEBUG_FUNCTION, "address_clone(%p)", addr); -- return addr; --} -- --static void * --breakpoint_clone(void *bp, void *data) --{ -- Dict *map = data; -- debug(DEBUG_FUNCTION, "breakpoint_clone(%p)", bp); -- struct breakpoint *b = malloc(sizeof(*b)); -- if (!b) { -- perror("malloc()"); -- exit(1); -- } -- memcpy(b, bp, sizeof(*b)); -- if (b->libsym != NULL) { -- struct library_symbol *sym = dict_find_entry(map, b->libsym); -- if (b->libsym == NULL) { -- fprintf(stderr, "Can't find cloned symbol %s.\n", -- b->libsym->name); -- return NULL; -- } -- b->libsym = sym; -- } -- return b; --} -- - typedef struct Pending_New Pending_New; - struct Pending_New { - pid_t pid; -@@ -240,83 +219,45 @@ pending_new_remove(pid_t pid) { - } - } - --static int --clone_breakpoints(Process * proc, Process * orig_proc) --{ -- /* When copying breakpoints, we also have to copy the -- * referenced symbols, and link them properly. */ -- Dict * map = dict_init(&dict_key2hash_int, &dict_key_cmp_int); -- struct library_symbol * it = proc->list_of_symbols; -- proc->list_of_symbols = NULL; -- for (; it != NULL; it = it->next) { -- struct library_symbol * libsym = clone_library_symbol(it); -- if (libsym == NULL) { -- int save_errno; -- err: -- save_errno = errno; -- destroy_library_symbol_chain(proc->list_of_symbols); -- dict_clear(map); -- errno = save_errno; -- return -1; -- } -- libsym->next = proc->list_of_symbols; -- proc->list_of_symbols = libsym; -- if (dict_enter(map, it, libsym) != 0) -- goto err; -- } -- -- proc->breakpoints = dict_clone2(orig_proc->breakpoints, -- address_clone, breakpoint_clone, map); -- if (proc->breakpoints == NULL) -- goto err; -- -- dict_clear(map); -- return 0; --} -- - static void --handle_clone(Event * event) { -- Process *p; -- -+handle_clone(Event *event) -+{ - debug(DEBUG_FUNCTION, "handle_clone(pid=%d)", event->proc->pid); - -- p = malloc(sizeof(Process)); -- if (!p) { -+ struct Process *proc = malloc(sizeof(*proc)); -+ if (proc == NULL) { -+ fail: -+ free(proc); -+ /* XXX proper error handling here, please. */ - perror("malloc()"); - exit(1); - } -- memcpy(p, event->proc, sizeof(Process)); -- p->pid = event->e_un.newpid; -- p->parent = event->proc; -+ -+ if (process_clone(proc, event->proc, event->e_un.newpid) < 0) -+ goto fail; -+ proc->parent = event->proc; - - /* We save register values to the arch pointer, and these need - to be per-thread. */ -- p->arch_ptr = NULL; -- -- if (pending_new(p->pid)) { -- pending_new_remove(p->pid); -- if (p->event_handler != NULL) -- destroy_event_handler(p); -- if (event->proc->state == STATE_ATTACHED && options.follow) { -- p->state = STATE_ATTACHED; -- } else { -- p->state = STATE_IGNORED; -- } -- continue_process(p->pid); -- add_process(p); -+ proc->arch_ptr = NULL; -+ -+ if (pending_new(proc->pid)) { -+ pending_new_remove(proc->pid); -+ /* XXX this used to be destroy_event_handler call, but -+ * I don't think we want to call that on a shared -+ * state. */ -+ proc->event_handler = NULL; -+ if (event->proc->state == STATE_ATTACHED && options.follow) -+ proc->state = STATE_ATTACHED; -+ else -+ proc->state = STATE_IGNORED; -+ continue_process(proc->pid); - } else { -- p->state = STATE_BEING_CREATED; -- add_process(p); -+ proc->state = STATE_BEING_CREATED; - } - -- if (p->leader == p) -- clone_breakpoints(p, event->proc->leader); -- else -- /* Thread groups share breakpoints. */ -- p->breakpoints = NULL; -- - if (event->type == EVENT_VFORK) -- continue_after_vfork(p); -+ continue_after_vfork(proc); - else - continue_process(event->proc->pid); - } -@@ -445,14 +386,38 @@ handle_exit_signal(Event *event) { - } - - static void -+output_syscall(struct Process *proc, const char *name, -+ void (*output)(enum tof, struct Process *, -+ struct library_symbol *)) -+{ -+ struct library_symbol syscall; -+ if (library_symbol_init(&syscall, 0, name, 0, LS_TOPLT_NONE) >= 0) { -+ (*output)(LT_TOF_SYSCALL, proc, &syscall); -+ library_symbol_destroy(&syscall); -+ } -+} -+ -+static void -+output_syscall_left(struct Process *proc, const char *name) -+{ -+ output_syscall(proc, name, &output_left); -+} -+ -+static void -+output_syscall_right(struct Process *proc, const char *name) -+{ -+ output_syscall(proc, name, &output_right); -+} -+ -+static void - handle_syscall(Event *event) { - debug(DEBUG_FUNCTION, "handle_syscall(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); - if (event->proc->state != STATE_IGNORED) { - callstack_push_syscall(event->proc, event->e_un.sysnum); -- if (options.syscalls) { -- output_left(LT_TOF_SYSCALL, event->proc, -- sysname(event->proc, event->e_un.sysnum)); -- } -+ if (options.syscalls) -+ output_syscall_left(event->proc, -+ sysname(event->proc, -+ event->e_un.sysnum)); - } - continue_after_syscall(event->proc, event->e_un.sysnum, 0); - } -@@ -461,20 +426,36 @@ static void - handle_exec(Event * event) { - Process * proc = event->proc; - -+ /* Save the PID so that we can use it after unsuccessful -+ * process_exec. */ -+ pid_t pid = proc->pid; -+ - debug(DEBUG_FUNCTION, "handle_exec(pid=%d)", proc->pid); - if (proc->state == STATE_IGNORED) { -- untrace_pid(proc->pid); -+ untrace: -+ untrace_pid(pid); - remove_process(proc); - return; - } - output_line(proc, "--- Called exec() ---"); -- proc->mask_32bit = 0; -- proc->personality = 0; -- proc->arch_ptr = NULL; -- free(proc->filename); -- proc->filename = pid2name(proc->pid); -- breakpoints_init(proc, 0); -- proc->callstack_depth = 0; -+ -+ if (process_exec(proc) < 0) { -+ fprintf(stderr, -+ "couldn't reinitialize process %d after exec\n", pid); -+ goto untrace; -+ } -+ -+ continue_process(proc->pid); -+ -+ /* After the exec, we expect to hit the first executable -+ * instruction. -+ * -+ * XXX TODO It would be nice to have this removed, but then we -+ * need to do that also for initial call to wait_for_proc in -+ * execute_program. In that case we could generate a -+ * EVENT_FIRST event or something, or maybe this could somehow -+ * be rolled into EVENT_NEW. */ -+ wait_for_proc(proc->pid); - continue_process(proc->pid); - } - -@@ -484,8 +465,9 @@ handle_arch_syscall(Event *event) { - if (event->proc->state != STATE_IGNORED) { - callstack_push_syscall(event->proc, 0xf0000 + event->e_un.sysnum); - if (options.syscalls) { -- output_left(LT_TOF_SYSCALL, event->proc, -- arch_sysname(event->proc, event->e_un.sysnum)); -+ output_syscall_left(event->proc, -+ arch_sysname(event->proc, -+ event->e_un.sysnum)); - } - } - continue_process(event->proc->pid); -@@ -522,10 +504,11 @@ handle_sysret(Event *event) { - if (opt_T || options.summary) { - calc_time_spent(event->proc); - } -- if (options.syscalls) { -- output_right(LT_TOF_SYSCALLR, event->proc, -- sysname(event->proc, event->e_un.sysnum)); -- } -+ if (options.syscalls) -+ output_syscall_right(event->proc, -+ sysname(event->proc, -+ event->e_un.sysnum)); -+ - assert(event->proc->callstack_depth > 0); - unsigned d = event->proc->callstack_depth - 1; - assert(event->proc->callstack[d].is_syscall); -@@ -541,10 +524,10 @@ handle_arch_sysret(Event *event) { - if (opt_T || options.summary) { - calc_time_spent(event->proc); - } -- if (options.syscalls) { -- output_right(LT_TOF_SYSCALLR, event->proc, -- arch_sysname(event->proc, event->e_un.sysnum)); -- } -+ if (options.syscalls) -+ output_syscall_right(event->proc, -+ arch_sysname(event->proc, -+ event->e_un.sysnum)); - callstack_pop(event->proc); - } - continue_process(event->proc->pid); -@@ -556,7 +539,7 @@ output_right_tos(struct Process *proc) - size_t d = proc->callstack_depth; - struct callstack_element *elem = &proc->callstack[d - 1]; - if (proc->state != STATE_IGNORED) -- output_right(LT_TOF_FUNCTIONR, proc, elem->c_un.libfunc->name); -+ output_right(LT_TOF_FUNCTIONR, proc, elem->c_un.libfunc); - } - - static void -@@ -564,43 +564,7 @@ handle_breakpoint(Event *event) - - for (i = event->proc->callstack_depth - 1; i >= 0; i--) { - if (brk_addr == event->proc->callstack[i].return_addr) { -- struct library_symbol *libsym = -- event->proc->callstack[i].c_un.libfunc; --#ifdef __powerpc__ -- /* -- * PPC HACK! (XXX FIXME TODO) -- * The PLT gets modified during the first call, -- * so be sure to re-enable the breakpoint. -- */ -- unsigned long a; -- void *addr = sym2addr(event->proc, libsym); -- -- if (libsym->plt_type != LS_TOPLT_POINT) { -- unsigned char break_insn[] = BREAKPOINT_VALUE; -- -- sbp = address2bpstruct(leader, addr); -- assert(sbp); -- a = ptrace(PTRACE_PEEKTEXT, event->proc->pid, -- addr); -- -- if (memcmp(&a, break_insn, BREAKPOINT_LENGTH)) { -- sbp->enabled--; -- insert_breakpoint(event->proc, addr, -- libsym, 1); -- } -- } else { -- sbp = dict_find_entry(leader->breakpoints, addr); -- /* On powerpc, the breakpoint address -- may end up being actual entry point -- of the library symbol, not the PLT -- address we computed. In that case, -- sbp is NULL. */ -- if (sbp == NULL || addr != sbp->addr) { -- insert_breakpoint(event->proc, addr, -- libsym, 1); -- } -- } --#elif defined(__mips__) -+#if defined(__mips__) - void *addr = NULL; - struct library_symbol *sym= event->proc->callstack[i].c_un.libfunc; - struct library_symbol *new_sym; -@@ -624,14 +571,14 @@ handle_breakpoint(Event *event) - sbp = dict_find_entry(leader->breakpoints, addr); - if (sbp) { - if (addr != sbp->addr) { -- insert_breakpoint(event->proc, addr, sym, 1); -+ 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, 1); -+ insert_breakpoint(event->proc, addr, new_sym); - } - #endif - for (j = event->proc->callstack_depth - 1; j > i; j--) { -@@ -644,6 +591,9 @@ handle_breakpoint(Event *event) - } - event->proc->return_addr = brk_addr; - -+ struct library_symbol *libsym = -+ event->proc->callstack[i].c_un.libfunc; -+ - output_right_tos(event->proc); - callstack_pop(event->proc); - -@@ -666,41 +616,44 @@ handle_breakpoint(Event *event) - callstack_pop(event->proc); - } - -- sbp = address2bpstruct(leader, brk_addr); -- continue_after_breakpoint(event->proc, sbp); -+ /* Maybe the previous callstack_pop's got rid -+ * of the breakpoint, but if we are in a -+ * recursive call, it's still enabled. In -+ * that case we need to skip it properly. */ -+ if ((sbp = address2bpstruct(leader, brk_addr)) != NULL) { -+ continue_after_breakpoint(event->proc, sbp); -+ } else { -+ set_instruction_pointer(event->proc, brk_addr); -+ continue_process(event->proc->pid); -+ } - return; - } - } - -- if ((sbp = address2bpstruct(leader, brk_addr))) { -+ if ((sbp = address2bpstruct(leader, brk_addr)) != NULL) - breakpoint_on_hit(sbp, event->proc); -- -- if (sbp->libsym == NULL) { -- continue_after_breakpoint(event->proc, sbp); -- return; -- } -- -- if (strcmp(sbp->libsym->name, "") == 0) { -- debug(DEBUG_PROCESS, "Hit _dl_debug_state breakpoint!\n"); -- arch_check_dbg(leader); -- } -- -- if (event->proc->state != STATE_IGNORED) { -+ else if (event->proc->state != STATE_IGNORED) -+ output_line(event->proc, -+ "unexpected breakpoint at %p", brk_addr); -+ -+ /* breakpoint_on_hit may delete its own breakpoint, so we have -+ * to look it up again. */ -+ if ((sbp = address2bpstruct(leader, brk_addr)) != NULL) { -+ if (event->proc->state != STATE_IGNORED -+ && sbp->libsym != NULL) { - event->proc->stack_pointer = get_stack_pointer(event->proc); - event->proc->return_addr = - get_return_addr(event->proc, event->proc->stack_pointer); - callstack_push_symfunc(event->proc, sbp->libsym); -- output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym->name); -+ output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym); - } - -- continue_after_breakpoint(event->proc, sbp); -+ breakpoint_on_continue(sbp, event->proc); - return; -+ } else { -+ set_instruction_pointer(event->proc, brk_addr); - } - -- if (event->proc->state != STATE_IGNORED && !options.no_plt) { -- output_line(event->proc, "unexpected breakpoint at %p", -- brk_addr); -- } - continue_process(event->proc->pid); - } - -@@ -745,9 +698,8 @@ callstack_push_symfunc(Process *proc, struct library_symbol *sym) { - elem->c_un.libfunc = sym; - - elem->return_addr = proc->return_addr; -- if (elem->return_addr) { -- insert_breakpoint(proc, elem->return_addr, NULL, 1); -- } -+ if (elem->return_addr) -+ insert_breakpoint(proc, elem->return_addr, NULL); - - /* handle functions like atexit() on mips which have no return */ - if (opt_T || options.summary) { -diff --git a/libltrace.c b/libltrace.c -index 777ad1b..92f2701 100644 ---- a/libltrace.c -+++ b/libltrace.c -@@ -1,22 +1,23 @@ - #include "config.h" - -+#include -+#include -+#include -+#include - #include - #include --#include - #include --#include --#include --#include --#include -+#include - - #include "common.h" -+#include "proc.h" - - char *command = NULL; - - int exiting = 0; /* =1 if a SIGINT or SIGTERM has been received */ - --static enum pcb_status --stop_non_p_processes (Process * proc, void * data) -+static enum callback_status -+stop_non_p_processes(Process *proc, void *data) - { - int stop = 1; - -@@ -38,7 +39,7 @@ stop_non_p_processes (Process * proc, void * data) - kill(proc->pid, SIGSTOP); - } - -- return pcb_cont; -+ return CBS_CONT; - } - - static void -@@ -104,20 +105,22 @@ ltrace_init(int argc, char **argv) { - } - opt_F = opt_F->next; - } -- if (opt_e) { -- struct opt_e_t *tmp = opt_e; -- while (tmp) { -- debug(1, "Option -e: %s\n", tmp->name); -- tmp = tmp->next; -- } -- } - if (command) { - /* Check that the binary ABI is supported before - * calling execute_program. */ - struct ltelf lte = {}; - open_elf(<e, command); - -- open_program(command, execute_program(command, argv), 0); -+ pid_t pid = execute_program(command, argv); -+ struct Process *proc = open_program(command, pid); -+ if (proc == NULL) { -+ fprintf(stderr, "couldn't open program '%s': %s\n", -+ command, strerror(errno)); -+ exit(EXIT_FAILURE); -+ } -+ -+ trace_set_options(proc); -+ continue_process(pid); - } - opt_p_tmp = opt_p; - while (opt_p_tmp) { -diff --git a/library.c b/library.c -new file mode 100644 -index 0000000..92fccea ---- /dev/null -+++ b/library.c -@@ -0,0 +1,354 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2001,2009 Juan Cespedes -+ * 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 "library.h" -+#include "proc.h" // for enum callback_status -+#include "debug.h" -+#include "common.h" // for arch_library_symbol_init, arch_library_init -+ -+#ifndef ARCH_HAVE_LIBRARY_DATA -+void -+arch_library_init(struct library *lib) -+{ -+} -+ -+void -+arch_library_destroy(struct library *lib) -+{ -+} -+ -+void -+arch_library_clone(struct library *retp, struct library *lib) -+{ -+} -+#endif -+ -+#ifndef ARCH_HAVE_LIBRARY_SYMBOL_DATA -+int -+arch_library_symbol_init(struct library_symbol *libsym) -+{ -+ return 0; -+} -+ -+void -+arch_library_symbol_destroy(struct library_symbol *libsym) -+{ -+} -+ -+int -+arch_library_symbol_clone(struct library_symbol *retp, -+ struct library_symbol *libsym) -+{ -+ return 0; -+} -+#endif -+ -+unsigned int -+target_address_hash(const void *key) -+{ -+ /* XXX this assumes that key is passed by value. */ -+ union { -+ target_address_t addr; -+ unsigned int ints[sizeof(target_address_t) -+ / sizeof(unsigned int)]; -+ } u = { .addr = (target_address_t)key }; -+ -+ size_t i; -+ unsigned int h = 0; -+ for (i = 0; i < sizeof(u.ints) / sizeof(*u.ints); ++i) -+ h ^= dict_key2hash_int((void *)(uintptr_t)u.ints[i]); -+ return h; -+} -+ -+int -+target_address_cmp(const void *key1, const void *key2) -+{ -+ /* XXX this assumes that key is passed by value. */ -+ target_address_t addr1 = (target_address_t)key1; -+ target_address_t addr2 = (target_address_t)key2; -+ return addr1 < addr2 ? 1 -+ : addr1 > addr2 ? -1 : 0; -+} -+ -+/* If the other symbol owns the name, we need to make the copy, so -+ * that the life-times of the two symbols are not dependent on each -+ * other. */ -+static int -+strdup_if_owned(const char **retp, const char *str, int owned) -+{ -+ if (!owned || str == NULL) { -+ *retp = str; -+ return 0; -+ } else { -+ *retp = strdup(str); -+ return *retp != NULL ? 0 : -1; -+ } -+} -+ -+static void -+private_library_symbol_init(struct library_symbol *libsym, -+ target_address_t addr, -+ const char *name, int own_name, -+ enum toplt type_of_plt) -+{ -+ libsym->next = NULL; -+ libsym->lib = NULL; -+ libsym->plt_type = type_of_plt; -+ libsym->name = name; -+ libsym->own_name = own_name; -+ libsym->enter_addr = (void *)(uintptr_t)addr; -+} -+ -+static void -+private_library_symbol_destroy(struct library_symbol *libsym) -+{ -+ library_symbol_set_name(libsym, NULL, 0); -+} -+ -+int -+library_symbol_init(struct library_symbol *libsym, -+ target_address_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); -+ -+ /* If arch init fails, we've already set libsym->name and -+ * own_name. But we return failure, and the client code isn't -+ * supposed to call library_symbol_destroy in such a case. */ -+ return arch_library_symbol_init(libsym); -+} -+ -+void -+library_symbol_destroy(struct library_symbol *libsym) -+{ -+ if (libsym != NULL) { -+ private_library_symbol_destroy(libsym); -+ arch_library_symbol_destroy(libsym); -+ } -+} -+ -+int -+library_symbol_clone(struct library_symbol *retp, struct library_symbol *libsym) -+{ -+ const char *name; -+ if (strdup_if_owned(&name, libsym->name, libsym->own_name) < 0) -+ return -1; -+ -+ private_library_symbol_init(retp, libsym->enter_addr, -+ name, libsym->own_name, libsym->plt_type); -+ -+ if (arch_library_symbol_clone(retp, libsym) < 0) { -+ private_library_symbol_destroy(retp); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+int -+library_symbol_cmp(struct library_symbol *a, struct library_symbol *b) -+{ -+ if (a->enter_addr < b->enter_addr) -+ return -1; -+ if (a->enter_addr > b->enter_addr) -+ return 1; -+ if (a->name != NULL && b->name != NULL) -+ return strcmp(a->name, b->name); -+ if (a->name == NULL) { -+ if (b->name == NULL) -+ return 0; -+ return -1; -+ } -+ return 1; -+} -+ -+void -+library_symbol_set_name(struct library_symbol *libsym, -+ const char *name, int own_name) -+{ -+ if (libsym->own_name) -+ free((char *)libsym->name); -+ libsym->name = name; -+ libsym->own_name = own_name; -+} -+ -+enum callback_status -+library_symbol_equal_cb(struct library_symbol *libsym, void *u) -+{ -+ struct library_symbol *standard = u; -+ return library_symbol_cmp(libsym, standard) == 0 ? CBS_STOP : CBS_CONT; -+} -+ -+ -+static void -+private_library_init(struct library *lib, enum library_type type) -+{ -+ lib->next = NULL; -+ lib->soname = NULL; -+ lib->own_soname = 0; -+ lib->pathname = NULL; -+ lib->own_pathname = 0; -+ lib->symbols = NULL; -+ lib->type = type; -+} -+ -+void -+library_init(struct library *lib, enum library_type type) -+{ -+ private_library_init(lib, type); -+ arch_library_init(lib); -+} -+ -+int -+library_clone(struct library *retp, struct library *lib) -+{ -+ const char *soname = NULL; -+ const char *pathname; -+ if (strdup_if_owned(&soname, lib->soname, lib->own_soname) < 0 -+ || strdup_if_owned(&pathname, -+ lib->pathname, lib->own_pathname) < 0) { -+ if (lib->own_soname) -+ free((char *)soname); -+ return -1; -+ } -+ -+ private_library_init(retp, lib->type); -+ library_set_soname(retp, soname, lib->own_soname); -+ 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; -+ } -+ -+ (*nsymp)->lib = retp; -+ nsymp = &(*nsymp)->next; -+ } -+ return 0; -+} -+ -+void -+library_destroy(struct library *lib) -+{ -+ if (lib == NULL) -+ return; -+ -+ arch_library_destroy(lib); -+ library_set_soname(lib, NULL, 0); -+ library_set_pathname(lib, NULL, 0); -+ -+ struct library_symbol *sym; -+ for (sym = lib->symbols; sym != NULL; ) { -+ struct library_symbol *next = sym->next; -+ library_symbol_destroy(sym); -+ free(sym); -+ sym = next; -+ } -+} -+ -+void -+library_set_soname(struct library *lib, const char *new_name, int own_name) -+{ -+ if (lib->own_soname) -+ free((char *)lib->soname); -+ lib->soname = new_name; -+ lib->own_soname = own_name; -+} -+ -+void -+library_set_pathname(struct library *lib, const char *new_name, int own_name) -+{ -+ if (lib->own_pathname) -+ free((char *)lib->pathname); -+ lib->pathname = new_name; -+ lib->own_pathname = own_name; -+} -+ -+struct library_symbol * -+library_each_symbol(struct library *lib, struct library_symbol *start_after, -+ enum callback_status (*cb)(struct library_symbol *, void *), -+ void *data) -+{ -+ struct library_symbol *it = start_after == NULL ? lib->symbols -+ : start_after->next; -+ -+ while (it != NULL) { -+ struct library_symbol *next = it->next; -+ -+ switch ((*cb)(it, data)) { -+ case CBS_FAIL: -+ /* XXX handle me */ -+ case CBS_STOP: -+ return it; -+ case CBS_CONT: -+ break; -+ } -+ -+ it = next; -+ } -+ -+ return NULL; -+} -+ -+void -+library_add_symbol(struct library *lib, struct library_symbol *first) -+{ -+ struct library_symbol *last; -+ for (last = first; last != NULL; ) { -+ last->lib = lib; -+ if (last->next != NULL) -+ last = last->next; -+ else -+ break; -+ } -+ -+ assert(last->next == NULL); -+ last->next = lib->symbols; -+ lib->symbols = first; -+} -+ -+enum callback_status -+library_named_cb(struct Process *proc, struct library *lib, void *name) -+{ -+ if (name == lib->soname -+ || strcmp(lib->soname, (char *)name) == 0) -+ return CBS_STOP; -+ else -+ return CBS_CONT; -+} -+ -+enum callback_status -+library_with_key_cb(struct Process *proc, struct library *lib, void *keyp) -+{ -+ return lib->key == *(target_address_t *)keyp ? CBS_STOP : CBS_CONT; -+} -diff --git a/library.h b/library.h -new file mode 100644 -index 0000000..c387b02 ---- /dev/null -+++ b/library.h -@@ -0,0 +1,196 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2006 Paul Gilliam -+ * -+ * 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 _LIBRARY_H_ -+#define _LIBRARY_H_ -+ -+#include -+#include "sysdep.h" -+ -+struct Process; -+struct library; -+ -+enum toplt { -+ LS_TOPLT_NONE = 0, /* PLT not used for this symbol. */ -+ LS_TOPLT_EXEC, /* PLT for this symbol is executable. */ -+}; -+ -+/* We should in general be able to trace 64-bit processes with 32-bit -+ * ltrace. (At least PPC has several PTRACE requests related to -+ * tracing 64-on-32, so presumably it should be possible.) But ltrace -+ * is currently hopelessly infested with using void* for host address. -+ * So keep with it, for now. */ -+typedef void *target_address_t; -+ -+/* Dict interface. */ -+unsigned int target_address_hash(const void *key); -+int target_address_cmp(const void *key1, const void *key2); -+ -+struct library_symbol { -+ struct library_symbol *next; -+ struct library *lib; -+ const char *name; -+ target_address_t enter_addr; -+ enum toplt plt_type; -+ char own_name; -+ struct arch_library_symbol_data arch; -+}; -+ -+/* Init LIBSYM. NAME will be freed when LIBSYM is destroyed if -+ * OWN_NAME. ARCH has to be initialized by a separate call. */ -+int library_symbol_init(struct library_symbol *libsym, -+ target_address_t addr, const char *name, int own_name, -+ enum toplt type_of_plt); -+ -+/* Copy library symbol SYM into the area pointed-to by RETP. Return 0 -+ * on success or a negative value on failure. */ -+int library_symbol_clone(struct library_symbol *retp, -+ struct library_symbol *sym); -+ -+/* Destroy library symbol. This essentially just frees name if it's -+ * owned. It doesn't free the memory associated with SYM pointer -+ * itself. Returns 0 on success or a negative value in case of an -+ * error (which would be an out of memory condition). */ -+void library_symbol_destroy(struct library_symbol *sym); -+ -+/* Compare two library symbols. Returns a negative value, 0, or a -+ * positive value, much like strcmp. The function compares symbol -+ * addresses, and if those are equal, it compares symbol names. If -+ * those are equal, too, the symbols are considered equal. */ -+int library_symbol_cmp(struct library_symbol *a, struct library_symbol *b); -+ -+/* Set a name for library symbol. This frees the old name, if -+ * that is owned. */ -+void library_symbol_set_name(struct library_symbol *libsym, -+ const char *name, int own_name); -+ -+/* A function that can be used as library_each_symbol callback. Looks -+ * for a symbol SYM for which library_symbol_cmp(SYM, STANDARD) -+ * returns 0. */ -+enum callback_status library_symbol_equal_cb(struct library_symbol *libsym, -+ void *standard); -+ -+enum library_type { -+ LT_LIBTYPE_MAIN, -+ LT_LIBTYPE_DSO, -+}; -+ -+/* XXX we might consider sharing libraries across processes. Things -+ * like libc will be opened by every single process, no point cloning -+ * these everywhere. But for now, keep the ownership structure -+ * simple. */ -+struct library { -+ struct library *next; -+ -+ /* Unique key. Two library objects are considered equal, if -+ * they have the same key. */ -+ target_address_t key; -+ -+ /* Address where the library is mapped. Two library objects -+ * are considered equal, if they have the same base. */ -+ target_address_t base; -+ -+ /* Absolute address of the entry point. Useful for main -+ * binary, though I suppose the value might be useful for the -+ * dynamic linker, too (in case we ever want to do early -+ * process tracing). */ -+ target_address_t entry; -+ -+ /* Address of PT_DYNAMIC segment. */ -+ target_address_t dyn_addr; -+ -+ /* Symbols associated with the library. */ -+ struct library_symbol *symbols; -+ -+ const char *soname; -+ const char *pathname; -+ -+ enum library_type type; -+ -+ char own_soname : 1; -+ char own_pathname : 1; -+ -+ struct arch_library_data arch; -+}; -+ -+/* Init LIB. */ -+void library_init(struct library *lib, enum library_type type); -+ -+/* Initialize RETP to a library identical to LIB. Symbols are not -+ * shared, but copied over. Returns 0 on success and a negative value -+ * in case of failure. */ -+int library_clone(struct library *retp, struct library *lib); -+ -+/* Destroy library. Doesn't free LIB itself. Symbols are destroyed -+ * and freed. */ -+void library_destroy(struct library *lib); -+ -+/* Set library soname. Frees the old name if necessary. */ -+void library_set_soname(struct library *lib, -+ const char *new_name, int own_name); -+ -+/* Set library pathname. Frees the old name if necessary. */ -+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). */ -+struct library_symbol *library_each_symbol -+ (struct library *lib, struct library_symbol *start_after, -+ enum callback_status (*cb)(struct library_symbol *, void *), -+ void *data); -+ -+/* Add a new symbol SYM to LIB. SYM is assumed owned, we need to -+ * overwrite SYM->next. */ -+void library_add_symbol(struct library *lib, struct library_symbol *sym); -+ -+/* A function that can be used as proc_each_library callback. Looks -+ * for a library with the name passed in DATA. PROC is ignored. */ -+enum callback_status library_named_cb(struct Process *proc, -+ struct library *lib, void *name); -+ -+/* A function that can be used as proc_each_library callback. Looks -+ * for a library with given base. -+ * -+ * NOTE: The key is passed as a POINTER to target_address_t (that -+ * because in general, target_address_t doesn't fit in void*). */ -+enum callback_status library_with_key_cb(struct Process *proc, -+ struct library *lib, void *keyp); -+ -+/* XXX this should really be in backend.h (as on pmachata/revamp -+ * branch), or, on this branch, in common.h. But we need -+ * target_address_t (which should also be in backend.h, I reckon), so -+ * stuff it here for the time being. */ -+/* This function is implemented in the back end. It is called for all -+ * raw addresses as read from symbol tables etc. If necessary on -+ * given architecture, this function should translate the address -+ * according to .opd or other indirection mechanism. Returns 0 on -+ * success and a negative value on failure. */ -+struct ltelf; -+int arch_translate_address(struct ltelf *lte, -+ target_address_t addr, target_address_t *ret); -+/* This is the same function as arch_translate_address, except it's -+ * used at the point that we don't have ELF available anymore. */ -+int arch_translate_address_dyn(struct Process *proc, -+ target_address_t addr, target_address_t *ret); -+ -+#endif /* _LIBRARY_H_ */ -diff --git a/ltrace-elf.c b/ltrace-elf.c -index f7fc239..a311c5f 100644 ---- a/ltrace-elf.c -+++ b/ltrace-elf.c -@@ -1,68 +1,96 @@ - #include "config.h" - -+#include - #include - #include --#include - #include - #include - #include -+#include - #include -+#include - #include - #include - #include --#include - - #include "common.h" -- --void do_close_elf(struct ltelf *lte); --void add_library_symbol(GElf_Addr addr, const char *name, -- struct library_symbol **library_symbolspp, -- enum toplt type_of_plt, int is_weak); --int in_load_libraries(const char *name, struct ltelf *lte, size_t count, GElf_Sym *sym); --static GElf_Addr opd2addr(struct ltelf *ltc, GElf_Addr addr); -- --struct library_symbol *library_symbols = NULL; --struct ltelf main_lte; -+#include "proc.h" -+#include "library.h" -+#include "filter.h" - - #ifdef PLT_REINITALISATION_BP - extern char *PLTs_initialized_by_here; - #endif - --#ifndef DT_PPC_GOT --# define DT_PPC_GOT (DT_LOPROC + 0) --#endif -- --#define PPC_PLT_STUB_SIZE 16 -- --static Elf_Data *loaddata(Elf_Scn *scn, GElf_Shdr *shdr) -+#ifndef ARCH_HAVE_LTELF_DATA -+int -+arch_elf_init(struct ltelf *lte, struct library *lib) - { -- Elf_Data *data = elf_getdata(scn, NULL); -- if (data == NULL || elf_getdata(scn, data) != NULL -- || data->d_off || data->d_size != shdr->sh_size) -- return NULL; -- return data; -+ return 0; - } - --static int inside(GElf_Addr addr, GElf_Shdr *shdr) -+void -+arch_elf_destroy(struct ltelf *lte) - { -- return addr >= shdr->sh_addr -- && addr < shdr->sh_addr + shdr->sh_size; - } -+#endif - --static int maybe_pick_section(GElf_Addr addr, -- Elf_Scn *in_sec, GElf_Shdr *in_shdr, -- Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr) -+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 (inside (addr, in_shdr)) { -- *tgt_sec = in_sec; -- *tgt_shdr = *in_shdr; -- return 1; -+ char *name = strdup(a_name); -+ if (name == NULL) { -+ fail: -+ free(name); -+ return -1; -+ } -+ -+ GElf_Addr addr = arch_plt_sym_val(lte, ndx, rela); -+ -+ struct library_symbol *libsym = malloc(sizeof(*libsym)); -+ if (libsym == NULL) -+ goto fail; -+ -+ /* XXX The double cast should be removed when -+ * target_address_t becomes integral type. */ -+ target_address_t taddr = (target_address_t) -+ (uintptr_t)(addr + lte->bias); -+ -+ if (library_symbol_init(libsym, taddr, name, 1, LS_TOPLT_EXEC) < 0) { -+ free(libsym); -+ goto fail; - } -+ -+ *ret = libsym; - return 0; - } - --static int get_section_covering(struct ltelf *lte, GElf_Addr addr, -- Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr) -+#ifndef ARCH_HAVE_ADD_PLT_ENTRY -+enum plt_status -+arch_elf_add_plt_entry(struct Process *proc, struct ltelf *lte, -+ const char *a_name, GElf_Rela *rela, size_t ndx, -+ struct library_symbol **ret) -+{ -+ return plt_default; -+} -+#endif -+ -+Elf_Data * -+elf_loaddata(Elf_Scn *scn, GElf_Shdr *shdr) -+{ -+ Elf_Data *data = elf_getdata(scn, NULL); -+ if (data == NULL || elf_getdata(scn, data) != NULL -+ || data->d_off || data->d_size != shdr->sh_size) -+ return NULL; -+ return data; -+} -+ -+static int -+elf_get_section_if(struct ltelf *lte, Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr, -+ int (*predicate)(Elf_Scn *, GElf_Shdr *, void *data), -+ void *data) - { - int i; - for (i = 1; i < lte->ehdr.e_shnum; ++i) { -@@ -72,69 +100,115 @@ static int get_section_covering(struct ltelf *lte, GElf_Addr addr, - scn = elf_getscn(lte->elf, i); - if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) { - debug(1, "Couldn't read section or header."); -+ return -1; -+ } -+ if (predicate(scn, &shdr, data)) { -+ *tgt_sec = scn; -+ *tgt_shdr = shdr; - return 0; - } -- -- if (maybe_pick_section(addr, scn, &shdr, tgt_sec, tgt_shdr)) -- return 1; - } -+ return -1; - -- return 0; - } - --static GElf_Addr read32be(Elf_Data *data, size_t offset) -+static int -+inside_p(Elf_Scn *scn, GElf_Shdr *shdr, void *data) - { -- if (data->d_size < offset + 4) { -- debug(1, "Not enough data to read 32bit value at offset %zd.", -- offset); -- return 0; -- } -+ GElf_Addr addr = *(GElf_Addr *)data; -+ return addr >= shdr->sh_addr -+ && addr < shdr->sh_addr + shdr->sh_size; -+} -+ -+int -+elf_get_section_covering(struct ltelf *lte, GElf_Addr addr, -+ Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr) -+{ -+ return elf_get_section_if(lte, tgt_sec, tgt_shdr, -+ &inside_p, &addr); -+} - -- unsigned char const *buf = data->d_buf + offset; -- return ((Elf32_Word)buf[0] << 24) -- | ((Elf32_Word)buf[1] << 16) -- | ((Elf32_Word)buf[2] << 8) -- | ((Elf32_Word)buf[3]); -+static int -+type_p(Elf_Scn *scn, GElf_Shdr *shdr, void *data) -+{ -+ GElf_Word type = *(GElf_Word *)data; -+ return shdr->sh_type == type; - } - --static GElf_Addr get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot, -- Elf_Data *plt_data) -+int -+elf_get_section_type(struct ltelf *lte, GElf_Word type, -+ Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr) - { -- Elf_Scn *ppcgot_sec = NULL; -- GElf_Shdr ppcgot_shdr; -- if (ppcgot != 0 -- && !get_section_covering(lte, ppcgot, &ppcgot_sec, &ppcgot_shdr)) -- // xxx should be the log out -- fprintf(stderr, -- "DT_PPC_GOT=%#" PRIx64 ", but no such section found.\n", -- ppcgot); -- -- if (ppcgot_sec != NULL) { -- Elf_Data *data = loaddata(ppcgot_sec, &ppcgot_shdr); -- if (data == NULL -- || data->d_size < 8 ) -- debug(1, "Couldn't read GOT data."); -- else { -- // where PPCGOT begins in .got -- size_t offset = ppcgot - ppcgot_shdr.sh_addr; -- GElf_Addr glink_vma = read32be(data, offset + 4); -- if (glink_vma != 0) { -- debug(1, "PPC GOT glink_vma address: %#" PRIx64, -- glink_vma); -- return glink_vma; -- } -- } -- } -+ return elf_get_section_if(lte, tgt_sec, tgt_shdr, -+ &type_p, &type); -+} - -- if (plt_data != NULL) { -- GElf_Addr glink_vma = read32be(plt_data, 0); -- debug(1, ".plt glink_vma address: %#" PRIx64, glink_vma); -- return glink_vma; -- } -+struct section_named_data { -+ struct ltelf *lte; -+ const char *name; -+}; -+ -+static int -+name_p(Elf_Scn *scn, GElf_Shdr *shdr, void *d) -+{ -+ struct section_named_data *data = d; -+ const char *name = elf_strptr(data->lte->elf, -+ data->lte->ehdr.e_shstrndx, -+ shdr->sh_name); -+ return strcmp(name, data->name) == 0; -+} - -+int -+elf_get_section_named(struct ltelf *lte, const char *name, -+ Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr) -+{ -+ struct section_named_data data = { -+ .lte = lte, -+ .name = name, -+ }; -+ return elf_get_section_if(lte, tgt_sec, tgt_shdr, -+ &name_p, &data); -+} -+ -+static int -+need_data(Elf_Data *data, GElf_Xword offset, GElf_Xword size) -+{ -+ assert(data != NULL); -+ if (data->d_size < size || offset > data->d_size - size) { -+ debug(1, "Not enough data to read %zd-byte value" -+ " at offset %zd.", size, offset); -+ return -1; -+ } - return 0; - } - -+#define DEF_READER(NAME, SIZE) \ -+ int \ -+ NAME(Elf_Data *data, GElf_Xword offset, uint##SIZE##_t *retp) \ -+ { \ -+ if (!need_data(data, offset, SIZE / 8) < 0) \ -+ return -1; \ -+ \ -+ if (data->d_buf == NULL) /* NODATA section */ { \ -+ *retp = 0; \ -+ return 0; \ -+ } \ -+ \ -+ union { \ -+ uint##SIZE##_t dst; \ -+ char buf[0]; \ -+ } u; \ -+ memcpy(u.buf, data->d_buf + offset, sizeof(u.dst)); \ -+ *retp = u.dst; \ -+ return 0; \ -+ } -+ -+DEF_READER(elf_read_u16, 16) -+DEF_READER(elf_read_u32, 32) -+DEF_READER(elf_read_u64, 64) -+ -+#undef DEF_READER -+ - int - open_elf(struct ltelf *lte, const char *filename) - { -@@ -150,17 +224,22 @@ open_elf(struct ltelf *lte, const char *filename) - lte->elf = elf_begin(lte->fd, ELF_C_READ, NULL); - #endif - -- if (lte->elf == NULL || elf_kind(lte->elf) != ELF_K_ELF) -- error(EXIT_FAILURE, 0, "Can't open ELF file \"%s\"", filename); -+ if (lte->elf == NULL || elf_kind(lte->elf) != ELF_K_ELF) { -+ fprintf(stderr, "\"%s\" is not an ELF file\n", filename); -+ exit(EXIT_FAILURE); -+ } - -- if (gelf_getehdr(lte->elf, <e->ehdr) == NULL) -- error(EXIT_FAILURE, 0, "Can't read ELF header of \"%s\"", -- filename); -+ if (gelf_getehdr(lte->elf, <e->ehdr) == NULL) { -+ fprintf(stderr, "can't read ELF header of \"%s\": %s\n", -+ filename, elf_errmsg(-1)); -+ exit(EXIT_FAILURE); -+ } - -- if (lte->ehdr.e_type != ET_EXEC && lte->ehdr.e_type != ET_DYN) -- error(EXIT_FAILURE, 0, -- "\"%s\" is not an ELF executable nor shared library", -- filename); -+ if (lte->ehdr.e_type != ET_EXEC && lte->ehdr.e_type != ET_DYN) { -+ fprintf(stderr, "\"%s\" is neither an ELF executable" -+ " nor a shared library\n", filename); -+ exit(EXIT_FAILURE); -+ } - - if ((lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS - || lte->ehdr.e_machine != LT_ELF_MACHINE) -@@ -172,18 +251,58 @@ open_elf(struct ltelf *lte, const char *filename) - && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS3 - || lte->ehdr.e_machine != LT_ELF_MACHINE3) - #endif -- ) -- error(EXIT_FAILURE, 0, -- "\"%s\" is ELF from incompatible architecture", filename); -+ ) { -+ fprintf(stderr, -+ "\"%s\" is ELF from incompatible architecture\n", -+ filename); -+ exit(EXIT_FAILURE); -+ } - - return 0; - } - --int --do_init_elf(struct ltelf *lte, const char *filename) { -+static void -+read_symbol_table(struct ltelf *lte, const char *filename, -+ Elf_Scn *scn, GElf_Shdr *shdr, const char *name, -+ Elf_Data **datap, size_t *countp, const char **strsp) -+{ -+ *datap = elf_getdata(scn, NULL); -+ *countp = shdr->sh_size / shdr->sh_entsize; -+ if ((*datap == NULL || elf_getdata(scn, *datap) != NULL) -+ && options.static_filter != NULL) { -+ fprintf(stderr, "Couldn't get data of section" -+ " %s from \"%s\": %s\n", -+ name, filename, elf_errmsg(-1)); -+ exit(EXIT_FAILURE); -+ } -+ -+ scn = elf_getscn(lte->elf, shdr->sh_link); -+ GElf_Shdr shdr2; -+ if (scn == NULL || gelf_getshdr(scn, &shdr2) == NULL) { -+ fprintf(stderr, "Couldn't get header of section" -+ " #%d from \"%s\": %s\n", -+ shdr2.sh_link, filename, elf_errmsg(-1)); -+ exit(EXIT_FAILURE); -+ } -+ -+ Elf_Data *data = elf_getdata(scn, NULL); -+ if (data == NULL || elf_getdata(scn, data) != NULL -+ || shdr2.sh_size != data->d_size || data->d_off) { -+ fprintf(stderr, "Couldn't get data of section" -+ " #%d from \"%s\": %s\n", -+ shdr2.sh_link, filename, elf_errmsg(-1)); -+ exit(EXIT_FAILURE); -+ } -+ -+ *strsp = data->d_buf; -+} -+ -+static int -+do_init_elf(struct ltelf *lte, const char *filename, GElf_Addr bias) -+{ - int i; - GElf_Addr relplt_addr = 0; -- size_t relplt_size = 0; -+ GElf_Addr soname_offset = 0; - - debug(DEBUG_FUNCTION, "do_init_elf(filename=%s)", filename); - debug(1, "Reading ELF from %s...", filename); -@@ -191,8 +310,25 @@ do_init_elf(struct ltelf *lte, const char *filename) { - if (open_elf(lte, filename) < 0) - return -1; - -- Elf_Data *plt_data = NULL; -- GElf_Addr ppcgot = 0; -+ /* Find out the base address. */ -+ { -+ GElf_Phdr phdr; -+ for (i = 0; gelf_getphdr (lte->elf, i, &phdr) != NULL; ++i) { -+ if (phdr.p_type == PT_LOAD) { -+ lte->base_addr = phdr.p_vaddr + bias; -+ break; -+ } -+ } -+ } -+ -+ if (lte->base_addr == 0) { -+ fprintf(stderr, "Couldn't determine base address of %s\n", -+ filename); -+ return -1; -+ } -+ -+ lte->bias = bias; -+ lte->entry_addr = lte->ehdr.e_entry + lte->bias; - - for (i = 1; i < lte->ehdr.e_shnum; ++i) { - Elf_Scn *scn; -@@ -200,68 +336,29 @@ do_init_elf(struct ltelf *lte, const char *filename) { - const char *name; - - scn = elf_getscn(lte->elf, i); -- if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) -- error(EXIT_FAILURE, 0, -- "Couldn't get section header from \"%s\"", -- filename); -+ if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) { -+ fprintf(stderr, "Couldn't get section #%d from" -+ " \"%s\": %s\n", i, filename, elf_errmsg(-1)); -+ exit(EXIT_FAILURE); -+ } - - name = elf_strptr(lte->elf, lte->ehdr.e_shstrndx, shdr.sh_name); -- if (name == NULL) -- error(EXIT_FAILURE, 0, -- "Couldn't get section header from \"%s\"", -- filename); -+ if (name == NULL) { -+ fprintf(stderr, "Couldn't get name of section #%d from" -+ " \"%s\": %s\n", i, filename, elf_errmsg(-1)); -+ exit(EXIT_FAILURE); -+ } - - if (shdr.sh_type == SHT_SYMTAB) { -- Elf_Data *data; -- -- lte->symtab = elf_getdata(scn, NULL); -- lte->symtab_count = shdr.sh_size / shdr.sh_entsize; -- if ((lte->symtab == NULL -- || elf_getdata(scn, lte->symtab) != NULL) -- && opt_x != NULL) -- error(EXIT_FAILURE, 0, -- "Couldn't get .symtab data from \"%s\"", -- filename); -- -- scn = elf_getscn(lte->elf, shdr.sh_link); -- if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) -- error(EXIT_FAILURE, 0, -- "Couldn't get section header from \"%s\"", -- filename); -+ read_symbol_table(lte, filename, -+ scn, &shdr, name, <e->symtab, -+ <e->symtab_count, <e->strtab); - -- data = elf_getdata(scn, NULL); -- if (data == NULL || elf_getdata(scn, data) != NULL -- || shdr.sh_size != data->d_size || data->d_off) -- error(EXIT_FAILURE, 0, -- "Couldn't get .strtab data from \"%s\"", -- filename); -- -- lte->strtab = data->d_buf; - } else if (shdr.sh_type == SHT_DYNSYM) { -- Elf_Data *data; -- -- lte->dynsym = elf_getdata(scn, NULL); -- lte->dynsym_count = shdr.sh_size / shdr.sh_entsize; -- if (lte->dynsym == NULL -- || elf_getdata(scn, lte->dynsym) != NULL) -- error(EXIT_FAILURE, 0, -- "Couldn't get .dynsym data from \"%s\"", -- filename); -+ read_symbol_table(lte, filename, -+ scn, &shdr, name, <e->dynsym, -+ <e->dynsym_count, <e->dynstr); - -- scn = elf_getscn(lte->elf, shdr.sh_link); -- if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) -- error(EXIT_FAILURE, 0, -- "Couldn't get section header from \"%s\"", -- filename); -- -- data = elf_getdata(scn, NULL); -- if (data == NULL || elf_getdata(scn, data) != NULL -- || shdr.sh_size != data->d_size || data->d_off) -- error(EXIT_FAILURE, 0, -- "Couldn't get .dynstr data from \"%s\"", -- filename); -- -- lte->dynstr = data->d_buf; - } else if (shdr.sh_type == SHT_DYNAMIC) { - Elf_Data *data; - size_t j; -@@ -270,135 +367,39 @@ do_init_elf(struct ltelf *lte, const char *filename) { - lte->dyn_sz = shdr.sh_size; - - data = elf_getdata(scn, NULL); -- if (data == NULL || elf_getdata(scn, data) != NULL) -- error(EXIT_FAILURE, 0, -- "Couldn't get .dynamic data from \"%s\"", -- filename); -+ if (data == NULL || elf_getdata(scn, data) != NULL) { -+ fprintf(stderr, "Couldn't get .dynamic data" -+ " from \"%s\": %s\n", -+ filename, strerror(errno)); -+ exit(EXIT_FAILURE); -+ } - - for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) { - GElf_Dyn dyn; - -- if (gelf_getdyn(data, j, &dyn) == NULL) -- error(EXIT_FAILURE, 0, -- "Couldn't get .dynamic data from \"%s\"", -- filename); --#ifdef __mips__ --/** -- MIPS ABI Supplement: -- -- DT_PLTGOT This member holds the address of the .got section. -- -- DT_MIPS_SYMTABNO This member holds the number of entries in the -- .dynsym section. -- -- DT_MIPS_LOCAL_GOTNO This member holds the number of local global -- offset table entries. -- -- DT_MIPS_GOTSYM This member holds the index of the first dyamic -- symbol table entry that corresponds to an entry in the gobal offset -- table. -- -- */ -- if(dyn.d_tag==DT_PLTGOT){ -- lte->pltgot_addr=dyn.d_un.d_ptr; -- } -- if(dyn.d_tag==DT_MIPS_LOCAL_GOTNO){ -- lte->mips_local_gotno=dyn.d_un.d_val; -+ if (gelf_getdyn(data, j, &dyn) == NULL) { -+ fprintf(stderr, "Couldn't get .dynamic" -+ " data from \"%s\": %s\n", -+ filename, strerror(errno)); -+ exit(EXIT_FAILURE); - } -- if(dyn.d_tag==DT_MIPS_GOTSYM){ -- lte->mips_gotsym=dyn.d_un.d_val; -- } --#endif // __mips__ - if (dyn.d_tag == DT_JMPREL) - relplt_addr = dyn.d_un.d_ptr; - else if (dyn.d_tag == DT_PLTRELSZ) -- relplt_size = dyn.d_un.d_val; -- else if (dyn.d_tag == DT_PPC_GOT) { -- ppcgot = dyn.d_un.d_val; -- debug(1, "ppcgot %#" PRIx64, ppcgot); -- } -- } -- } else if (shdr.sh_type == SHT_HASH) { -- Elf_Data *data; -- size_t j; -- -- lte->hash_type = SHT_HASH; -- -- data = elf_getdata(scn, NULL); -- if (data == NULL || elf_getdata(scn, data) != NULL -- || data->d_off || data->d_size != shdr.sh_size) -- error(EXIT_FAILURE, 0, -- "Couldn't get .hash data from \"%s\"", -- filename); -- -- if (shdr.sh_entsize == 4) { -- /* Standard conforming ELF. */ -- if (data->d_type != ELF_T_WORD) -- error(EXIT_FAILURE, 0, -- "Couldn't get .hash data from \"%s\"", -- filename); -- lte->hash = (Elf32_Word *) data->d_buf; -- } else if (shdr.sh_entsize == 8) { -- /* Alpha or s390x. */ -- Elf32_Word *dst, *src; -- size_t hash_count = data->d_size / 8; -- -- lte->hash = (Elf32_Word *) -- malloc(hash_count * sizeof(Elf32_Word)); -- if (lte->hash == NULL) -- error(EXIT_FAILURE, 0, -- "Couldn't convert .hash section from \"%s\"", -- filename); -- lte->lte_flags |= LTE_HASH_MALLOCED; -- dst = lte->hash; -- src = (Elf32_Word *) data->d_buf; -- if ((data->d_type == ELF_T_WORD -- && __BYTE_ORDER == __BIG_ENDIAN) -- || (data->d_type == ELF_T_XWORD -- && lte->ehdr.e_ident[EI_DATA] == -- ELFDATA2MSB)) -- ++src; -- for (j = 0; j < hash_count; ++j, src += 2) -- *dst++ = *src; -- } else -- error(EXIT_FAILURE, 0, -- "Unknown .hash sh_entsize in \"%s\"", -- filename); -- } else if (shdr.sh_type == SHT_GNU_HASH -- && lte->hash == NULL) { -- Elf_Data *data; -- -- lte->hash_type = SHT_GNU_HASH; -- -- if (shdr.sh_entsize != 0 -- && shdr.sh_entsize != 4) { -- error(EXIT_FAILURE, 0, -- ".gnu.hash sh_entsize in \"%s\" " -- "should be 4, but is %#" PRIx64, -- filename, shdr.sh_entsize); -+ lte->relplt_size = dyn.d_un.d_val; -+ else if (dyn.d_tag == DT_SONAME) -+ soname_offset = dyn.d_un.d_val; - } -- -- data = loaddata(scn, &shdr); -- if (data == NULL) -- error(EXIT_FAILURE, 0, -- "Couldn't get .gnu.hash data from \"%s\"", -- filename); -- -- lte->hash = (Elf32_Word *) data->d_buf; - } else if (shdr.sh_type == SHT_PROGBITS - || shdr.sh_type == SHT_NOBITS) { - if (strcmp(name, ".plt") == 0) { - lte->plt_addr = shdr.sh_addr; - lte->plt_size = shdr.sh_size; -- if (shdr.sh_flags & SHF_EXECINSTR) { -- lte->lte_flags |= LTE_PLT_EXECUTABLE; -- } -- if (lte->ehdr.e_machine == EM_PPC) { -- plt_data = loaddata(scn, &shdr); -- if (plt_data == NULL) -- fprintf(stderr, -- "Can't load .plt data\n"); -- } -+ lte->plt_data = elf_loaddata(scn, &shdr); -+ if (lte->plt_data == NULL) -+ fprintf(stderr, -+ "Can't load .plt data\n"); -+ lte->plt_flags = shdr.sh_flags; - } - #ifdef ARCH_SUPPORTS_OPD - else if (strcmp(name, ".opd") == 0) { -@@ -410,449 +411,376 @@ do_init_elf(struct ltelf *lte, const char *filename) { - } - } - -- if (lte->dynsym == NULL || lte->dynstr == NULL) -- error(EXIT_FAILURE, 0, -- "Couldn't find .dynsym or .dynstr in \"%s\"", filename); -+ if (lte->dynsym == NULL || lte->dynstr == NULL) { -+ fprintf(stderr, "Couldn't find .dynsym or .dynstr in \"%s\"\n", -+ filename); -+ exit(EXIT_FAILURE); -+ } - - if (!relplt_addr || !lte->plt_addr) { - debug(1, "%s has no PLT relocations", filename); - lte->relplt = NULL; - lte->relplt_count = 0; -- } else if (relplt_size == 0) { -+ } else if (lte->relplt_size == 0) { - debug(1, "%s has unknown PLT size", filename); - lte->relplt = NULL; - lte->relplt_count = 0; - } else { -- if (lte->ehdr.e_machine == EM_PPC) { -- GElf_Addr glink_vma -- = get_glink_vma(lte, ppcgot, plt_data); -- -- assert (relplt_size % 12 == 0); -- size_t count = relplt_size / 12; // size of RELA entry -- lte->plt_stub_vma = glink_vma -- - (GElf_Addr)count * PPC_PLT_STUB_SIZE; -- debug(1, "stub_vma is %#" PRIx64, lte->plt_stub_vma); -- } - - for (i = 1; i < lte->ehdr.e_shnum; ++i) { - Elf_Scn *scn; - GElf_Shdr shdr; - - scn = elf_getscn(lte->elf, i); -- if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) -- error(EXIT_FAILURE, 0, -- "Couldn't get section header from \"%s\"", -- filename); -+ if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) { -+ fprintf(stderr, "Couldn't get section header" -+ " from \"%s\": %s\n", -+ filename, elf_errmsg(-1)); -+ exit(EXIT_FAILURE); -+ } - if (shdr.sh_addr == relplt_addr -- && shdr.sh_size == relplt_size) { -+ && shdr.sh_size == lte->relplt_size) { - lte->relplt = elf_getdata(scn, NULL); - lte->relplt_count = - shdr.sh_size / shdr.sh_entsize; - if (lte->relplt == NULL -- || elf_getdata(scn, lte->relplt) != NULL) -- error(EXIT_FAILURE, 0, -- "Couldn't get .rel*.plt data from \"%s\"", -- filename); -+ || elf_getdata(scn, lte->relplt) != NULL) { -+ fprintf(stderr, "Couldn't get .rel*.plt" -+ " data from \"%s\": %s\n", -+ filename, elf_errmsg(-1)); -+ exit(EXIT_FAILURE); -+ } - break; - } - } - -- if (i == lte->ehdr.e_shnum) -- error(EXIT_FAILURE, 0, -- "Couldn't find .rel*.plt section in \"%s\"", -- filename); -+ if (i == lte->ehdr.e_shnum) { -+ fprintf(stderr, -+ "Couldn't find .rel*.plt section in \"%s\"\n", -+ filename); -+ exit(EXIT_FAILURE); -+ } - - debug(1, "%s %zd PLT relocations", filename, lte->relplt_count); - } -+ -+ if (soname_offset != 0) -+ lte->soname = lte->dynstr + soname_offset; -+ - return 0; - } - -+/* XXX temporarily non-static */ - void - do_close_elf(struct ltelf *lte) { - debug(DEBUG_FUNCTION, "do_close_elf()"); -- if (lte->lte_flags & LTE_HASH_MALLOCED) -- free((char *)lte->hash); -+ arch_elf_destroy(lte); - elf_end(lte->elf); - close(lte->fd); - } - --static struct library_symbol * --create_library_symbol(const char * name, GElf_Addr addr) --{ -- size_t namel = strlen(name) + 1; -- struct library_symbol * sym = calloc(sizeof(*sym) + namel, 1); -- if (sym == NULL) { -- perror("create_library_symbol"); -- return NULL; -- } -- sym->name = (char *)(sym + 1); -- memcpy(sym->name, name, namel); -- sym->enter_addr = (void *)(uintptr_t) addr; -- return sym; --} -- --void --add_library_symbol(GElf_Addr addr, const char *name, -- struct library_symbol **library_symbolspp, -- enum toplt type_of_plt, int is_weak) -+static int -+populate_plt(struct Process *proc, const char *filename, -+ struct ltelf *lte, struct library *lib) - { -- struct library_symbol *s; -- -- debug(DEBUG_FUNCTION, "add_library_symbol()"); -- -- s = create_library_symbol(name, addr); -- if (s == NULL) -- error(EXIT_FAILURE, errno, "add_library_symbol failed"); -+ size_t i; -+ for (i = 0; i < lte->relplt_count; ++i) { -+ GElf_Rel rel; -+ GElf_Rela rela; -+ GElf_Sym sym; -+ void *ret; - -- s->needs_init = 1; -- s->is_weak = is_weak; -- s->plt_type = type_of_plt; -+ 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); -+ } - -- s->next = *library_symbolspp; -- *library_symbolspp = s; -+ 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); -+ } - -- debug(2, "addr: %p, symbol: \"%s\"", (void *)(uintptr_t) addr, name); --} -+ char const *name = lte->dynstr + sym.st_name; - --struct library_symbol * --clone_library_symbol(struct library_symbol * sym) --{ -- struct library_symbol * copy -- = create_library_symbol(sym->name, -- (GElf_Addr)(uintptr_t)sym->enter_addr); -- if (copy == NULL) -- return NULL; -- -- copy->needs_init = sym->needs_init; -- copy->is_weak = sym->is_weak; -- copy->plt_type = sym->plt_type; -+ if (!filter_matches_symbol(options.plt_filter, name, lib)) -+ continue; - -- return copy; -+ struct library_symbol *libsym = NULL; -+ switch (arch_elf_add_plt_entry(proc, lte, name, -+ &rela, i, &libsym)) { -+ case plt_default: -+ if (default_elf_add_plt_entry(proc, lte, name, -+ &rela, i, &libsym) < 0) -+ /* fall-through */ -+ case plt_fail: -+ return -1; -+ /* fall-through */ -+ case plt_ok: -+ if (libsym != NULL) -+ library_add_symbol(lib, libsym); -+ } -+ } -+ return 0; - } - --void --destroy_library_symbol(struct library_symbol * sym) --{ -- free(sym); --} -+/* When -x rules result in request to trace several aliases, we only -+ * want to add such symbol once. The only way that those symbols -+ * differ in is their name, e.g. in glibc you have __GI___libc_free, -+ * __cfree, __free, __libc_free, cfree and free all defined on the -+ * same address. So instead we keep this unique symbol struct for -+ * each address, and replace name in libsym with a shorter variant if -+ * we find it. */ -+struct unique_symbol { -+ target_address_t addr; -+ struct library_symbol *libsym; -+}; - --void --destroy_library_symbol_chain(struct library_symbol * sym) -+static int -+unique_symbol_cmp(const void *key, const void *val) - { -- while (sym != NULL) { -- struct library_symbol * next = sym->next; -- destroy_library_symbol(sym); -- sym = next; -- } --} -- --/* stolen from elfutils-0.123 */ --static unsigned long --private_elf_gnu_hash(const char *name) { -- unsigned long h = 5381; -- const unsigned char *string = (const unsigned char *)name; -- unsigned char c; -- for (c = *string; c; c = *++string) -- h = h * 33 + c; -- return h & 0xffffffff; -+ const struct unique_symbol *sym_key = key; -+ const struct unique_symbol *sym_val = val; -+ return sym_key->addr != sym_val->addr; - } - - static int --symbol_matches(struct ltelf *lte, size_t lte_i, GElf_Sym *sym, -- size_t symidx, const char *name) -+populate_this_symtab(struct Process *proc, const char *filename, -+ struct ltelf *lte, struct library *lib, -+ Elf_Data *symtab, const char *strtab, size_t size) - { -- GElf_Sym tmp_sym; -- GElf_Sym *tmp; -- -- tmp = (sym) ? (sym) : (&tmp_sym); -- -- if (gelf_getsym(lte[lte_i].dynsym, symidx, tmp) == NULL) -- error(EXIT_FAILURE, 0, "Couldn't get symbol from .dynsym"); -- else { -- tmp->st_value += lte[lte_i].base_addr; -- debug(2, "symbol found: %s, %zd, %#" PRIx64, -- name, lte_i, tmp->st_value); -+ /* Using sorted array would be arguably better, but this -+ * should be well enough for the number of symbols that we -+ * typically deal with. */ -+ size_t num_symbols = 0; -+ struct unique_symbol *symbols = malloc(sizeof(*symbols) * size); -+ if (symbols == NULL) { -+ fprintf(stderr, "couldn't insert symbols for -x: %s\n", -+ strerror(errno)); -+ return -1; - } -- return tmp->st_value != 0 -- && tmp->st_shndx != SHN_UNDEF -- && strcmp(name, lte[lte_i].dynstr + tmp->st_name) == 0; --} - --int --in_load_libraries(const char *name, struct ltelf *lte, size_t count, GElf_Sym *sym) { -+ GElf_Word secflags[lte->ehdr.e_shnum]; - size_t i; -- unsigned long hash; -- unsigned long gnu_hash; -- -- if (!count) -- return 1; -- --#ifdef ELF_HASH_TAKES_SIGNED_CHAR -- hash = elf_hash(name); --#else -- hash = elf_hash((const unsigned char *)name); --#endif -- gnu_hash = private_elf_gnu_hash(name); -- -- for (i = 0; i < count; ++i) { -- if (lte[i].hash == NULL) -+ for (i = 1; i < lte->ehdr.e_shnum; ++i) { -+ Elf_Scn *scn = elf_getscn(lte->elf, i); -+ if (scn == NULL) - continue; -- -- if (lte[i].hash_type == SHT_GNU_HASH) { -- Elf32_Word * hashbase = lte[i].hash; -- Elf32_Word nbuckets = *hashbase++; -- Elf32_Word symbias = *hashbase++; -- Elf32_Word bitmask_nwords = *hashbase++; -- Elf32_Word * buckets; -- Elf32_Word * chain_zero; -- Elf32_Word bucket; -- -- // +1 for skipped `shift' -- hashbase += lte[i].ehdr.e_ident[EI_CLASS] * bitmask_nwords + 1; -- buckets = hashbase; -- hashbase += nbuckets; -- chain_zero = hashbase - symbias; -- bucket = buckets[gnu_hash % nbuckets]; -- -- if (bucket != 0) { -- const Elf32_Word *hasharr = &chain_zero[bucket]; -- do -- if ((*hasharr & ~1u) == (gnu_hash & ~1u)) { -- int symidx = hasharr - chain_zero; -- if (symbol_matches(lte, i, -- sym, symidx, -- name)) -- return 1; -- } -- while ((*hasharr++ & 1u) == 0); -- } -- } else { -- Elf32_Word nbuckets, symndx; -- Elf32_Word *buckets, *chain; -- nbuckets = lte[i].hash[0]; -- buckets = <e[i].hash[2]; -- chain = <e[i].hash[2 + nbuckets]; -- -- for (symndx = buckets[hash % nbuckets]; -- symndx != STN_UNDEF; symndx = chain[symndx]) -- if (symbol_matches(lte, i, sym, symndx, name)) -- return 1; -- } -+ GElf_Shdr shdr; -+ if (gelf_getshdr(scn, &shdr) == NULL) -+ continue; -+ secflags[i] = shdr.sh_flags; - } -- return 0; --} -- --static GElf_Addr --opd2addr(struct ltelf *lte, GElf_Addr addr) { --#ifdef ARCH_SUPPORTS_OPD -- unsigned long base, offset; -- -- if (!lte->opd) -- return addr; -- -- base = (unsigned long)lte->opd->d_buf; -- offset = (unsigned long)addr - (unsigned long)lte->opd_addr; -- if (offset > lte->opd_size) -- error(EXIT_FAILURE, 0, "static plt not in .opd"); -- -- return *(GElf_Addr*)(base + offset); --#else //!ARCH_SUPPORTS_OPD -- return addr; --#endif --} - --struct library_symbol * --read_elf(Process *proc, GElf_Addr *entryp) --{ -- struct ltelf lte[MAX_LIBRARIES + 1]; -- size_t i; -- struct opt_x_t *xptr; -- struct opt_x_t *opt_x_loc = opt_x; -- struct library_symbol **lib_tail = NULL; -- int exit_out = 0; -- int count = 0; -+ size_t lib_len = strlen(lib->soname); -+ for (i = 0; i < size; ++i) { -+ GElf_Sym sym; -+ if (gelf_getsym(symtab, i, &sym) == NULL) { -+ fail: -+ fprintf(stderr, -+ "couldn't get symbol #%zd from %s: %s\n", -+ i, filename, elf_errmsg(-1)); -+ continue; -+ } - -- debug(DEBUG_FUNCTION, "read_elf(file=%s)", proc->filename); -+ /* XXX support IFUNC as well. */ -+ if (GELF_ST_TYPE(sym.st_info) != STT_FUNC -+ || sym.st_value == 0) -+ continue; - -- memset(lte, 0, sizeof(*lte)); -- library_symbols = NULL; -- library_num = 0; -- proc->libdl_hooked = 0; -+ const char *orig_name = strtab + sym.st_name; -+ const char *version = strchr(orig_name, '@'); -+ size_t len = version != NULL ? (assert(version > orig_name), -+ (size_t)(version - orig_name)) -+ : strlen(orig_name); -+ char name[len + 1]; -+ memcpy(name, orig_name, len); -+ name[len] = 0; - -- if (do_init_elf(lte, proc->filename)) -- return NULL; -+ if (!filter_matches_symbol(options.static_filter, name, lib)) -+ continue; - -- memcpy(&main_lte, lte, sizeof(struct ltelf)); -+ target_address_t addr = (target_address_t) -+ (uintptr_t)(sym.st_value + lte->bias); -+ target_address_t naddr; -+ -+ /* On arches that support OPD, the value of typical -+ * function symbol will be a pointer to .opd, but some -+ * will point directly to .text. We don't want to -+ * translate those. */ -+ if (secflags[sym.st_shndx] & SHF_EXECINSTR) { -+ naddr = addr; -+ } else if (arch_translate_address(lte, addr, &naddr) < 0) { -+ fprintf(stderr, -+ "couldn't translate address of %s@%s: %s\n", -+ name, lib->soname, strerror(errno)); -+ continue; -+ } - -- if (opt_p && opt_p->pid > 0) { -- linkmap_init(proc, lte); -- proc->libdl_hooked = 1; -- } -+ char *full_name; -+ 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; -+ } - -- proc->e_machine = lte->ehdr.e_machine; -+ /* Look whether we already have a symbol for this -+ * address. If not, add this one. */ -+ struct unique_symbol key = { naddr, NULL }; -+ struct unique_symbol *unique -+ = lsearch(&key, symbols, &num_symbols, -+ sizeof(*symbols), &unique_symbol_cmp); -+ -+ 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) { -+ --num_symbols; -+ goto fail; -+ } -+ unique->libsym = libsym; -+ unique->addr = naddr; - -- for (i = 0; i < library_num; ++i) { -- if (do_init_elf(<e[i + 1], library[i])) -- error(EXIT_FAILURE, errno, "Can't open \"%s\"", -- library[i]); -- } -+ } else if (strlen(full_name) < strlen(unique->libsym->name)) { -+ library_symbol_set_name(unique->libsym, full_name, 1); - -- if (!options.no_plt) { --#ifdef __mips__ -- // MIPS doesn't use the PLT and the GOT entries get changed -- // on startup. -- for(i=lte->mips_gotsym; idynsym_count;i++){ -- GElf_Sym sym; -- const char *name; -- GElf_Addr addr = arch_plt_sym_val(lte, i, 0); -- if (gelf_getsym(lte->dynsym, i, &sym) == NULL){ -- error(EXIT_FAILURE, 0, -- "Couldn't get relocation from \"%s\"", -- proc->filename); -- } -- name=lte->dynstr+sym.st_name; -- if(ELF64_ST_TYPE(sym.st_info) != STT_FUNC){ -- debug(2,"sym %s not a function",name); -- continue; -- } -- add_library_symbol(addr, name, &library_symbols, 0, -- ELF64_ST_BIND(sym.st_info) != 0); -- if (!lib_tail) -- lib_tail = &(library_symbols->next); -+ } else { -+ free(full_name); - } --#else -- for (i = 0; i < lte->relplt_count; ++i) { -- GElf_Rel rel; -- GElf_Rela rela; -- GElf_Sym sym; -- GElf_Addr addr; -- void *ret; -- const char *name; -- -- 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) -- error(EXIT_FAILURE, 0, -- "Couldn't get relocation from \"%s\"", -- proc->filename); -- -- name = lte->dynstr + sym.st_name; -- count = library_num ? library_num+1 : 0; -- -- if (in_load_libraries(name, lte, count, NULL)) { -- enum toplt pltt; -- if (sym.st_value == 0 && lte->plt_stub_vma != 0) { -- pltt = LS_TOPLT_EXEC; -- addr = lte->plt_stub_vma + PPC_PLT_STUB_SIZE * i; -- } -- else { -- pltt = PLTS_ARE_EXECUTABLE(lte) -- ? LS_TOPLT_EXEC : LS_TOPLT_POINT; -- addr = arch_plt_sym_val(lte, i, &rela); -- } -+ } - -- add_library_symbol(addr, name, &library_symbols, pltt, -- ELF64_ST_BIND(sym.st_info) == STB_WEAK); -- if (!lib_tail) -- lib_tail = &(library_symbols->next); -- } -- } --#endif // !__mips__ -- } else { -- lib_tail = &library_symbols; -+ for (i = 0; i < num_symbols; ++i) { -+ assert(symbols[i].libsym != NULL); -+ library_add_symbol(lib, symbols[i].libsym); - } - -- for (i = 0; i < lte->symtab_count; ++i) { -- GElf_Sym sym; -- GElf_Addr addr; -- const char *name; -+ free(symbols); - -- if (gelf_getsym(lte->symtab, i, &sym) == NULL) -- error(EXIT_FAILURE, 0, -- "Couldn't get symbol from \"%s\"", -- proc->filename); -+ return 0; -+} - -- name = lte->strtab + sym.st_name; -- addr = sym.st_value; -- if (!addr) -- continue; -+static int -+populate_symtab(struct Process *proc, const char *filename, -+ struct ltelf *lte, struct library *lib) -+{ -+ 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); -+} - -- for (xptr = opt_x_loc; xptr; xptr = xptr->next) -- if (xptr->name && strcmp(xptr->name, name) == 0) { -- /* FIXME: Should be able to use &library_symbols as above. But -- when you do, none of the real library symbols cause breaks. */ -- add_library_symbol(opd2addr(lte, addr), -- name, lib_tail, LS_TOPLT_NONE, 0); -- xptr->found = 1; -- break; -- } -+int -+ltelf_read_library(struct library *lib, struct Process *proc, -+ const char *filename, GElf_Addr bias) -+{ -+ struct ltelf lte = {}; -+ if (do_init_elf(<e, filename, bias) < 0) -+ return -1; -+ if (arch_elf_init(<e, lib) < 0) { -+ fprintf(stderr, "Backend initialization failed.\n"); -+ return -1; - } - -- unsigned found_count = 0; -+ proc->e_machine = lte.ehdr.e_machine; - -- for (xptr = opt_x_loc; xptr; xptr = xptr->next) { -- if (xptr->found) -- continue; -+ int status = 0; -+ if (lib == NULL) -+ goto fail; - -- GElf_Sym sym; -- GElf_Addr addr; -- if (in_load_libraries(xptr->name, lte, library_num+1, &sym)) { -- debug(2, "found symbol %s @ %#" PRIx64 ", adding it.", -- xptr->name, sym.st_value); -- addr = sym.st_value; -- if (ELF32_ST_TYPE (sym.st_info) == STT_FUNC) { -- add_library_symbol(addr, xptr->name, lib_tail, LS_TOPLT_NONE, 0); -- xptr->found = 1; -- found_count++; -- } -- } -- if (found_count == opt_x_cnt){ -- debug(2, "done, found everything: %d\n", found_count); -- break; -- } -+ /* Note that we set soname and pathname as soon as they are -+ * allocated, so in case of further errors, this get released -+ * when LIB is release, which should happen in the caller when -+ * we return error. */ -+ -+ if (lib->pathname == NULL) { -+ char *pathname = strdup(filename); -+ if (pathname == NULL) -+ goto fail; -+ library_set_pathname(lib, pathname, 1); - } - -- if (lte->ehdr.e_entry != 0) { -- *entryp = opd2addr(lte, lte->ehdr.e_entry); -+ if (lte.soname != NULL) { -+ char *soname = strdup(lte.soname); -+ if (soname == NULL) -+ goto fail; -+ library_set_soname(lib, soname, 1); - } else { -+ const char *soname = rindex(lib->pathname, '/') + 1; -+ if (soname == NULL) -+ soname = lib->pathname; -+ library_set_soname(lib, soname, 0); - } - -- for (xptr = opt_x_loc; xptr; xptr = xptr->next) -- if ( ! xptr->found) { -- char *badthing = "WARNING"; --#ifdef PLT_REINITALISATION_BP -- if (strcmp(xptr->name, PLTs_initialized_by_here) == 0) { -- if (lte->ehdr.e_entry) { -- fprintf (stderr, "WARNING: Using e_ent" -- "ry from elf header (%p) for " -- "address of \"%s\"\n", (void*) -- (long) lte->ehdr.e_entry, -- PLTs_initialized_by_here); -- continue; -- } -- badthing = "ERROR"; -- exit_out = 1; -- } --#endif -- fprintf (stderr, -- "%s: Couldn't find symbol \"%s\" in file \"%s\" assuming it will be loaded by libdl!" -- "\n", badthing, xptr->name, proc->filename); -- } -- if (exit_out) { -- exit (1); -- } -+ /* XXX The double cast should be removed when -+ * target_address_t becomes integral type. */ -+ target_address_t entry = (target_address_t)(uintptr_t)lte.entry_addr; -+ if (arch_translate_address(<e, entry, &entry) < 0) -+ goto fail; -+ -+ /* XXX The double cast should be removed when -+ * target_address_t becomes integral type. */ -+ lib->base = (target_address_t)(uintptr_t)lte.base_addr; -+ lib->entry = entry; -+ /* XXX The double cast should be removed when -+ * target_address_t becomes integral type. */ -+ lib->dyn_addr = (target_address_t)(uintptr_t)lte.dyn_addr; -+ -+ if (filter_matches_library(options.plt_filter, lib) -+ && populate_plt(proc, filename, <e, lib) < 0) -+ goto fail; -+ -+ if (filter_matches_library(options.static_filter, lib) -+ && populate_symtab(proc, filename, <e, lib) < 0) -+ goto fail; -+ -+done: -+ do_close_elf(<e); -+ return status; -+ -+fail: -+ status = -1; -+ goto done; -+} - -- for (i = 0; i < library_num + 1; ++i) -- do_close_elf(<e[i]); -+struct library * -+ltelf_read_main_binary(struct Process *proc, const char *path) -+{ -+ struct library *lib = malloc(sizeof(*lib)); -+ if (lib == NULL) -+ return NULL; -+ library_init(lib, LT_LIBTYPE_MAIN); -+ library_set_pathname(lib, path, 0); -+ -+ /* There is a race between running the process and reading its -+ * binary for internal consumption. So open the binary from -+ * the /proc filesystem. XXX Note that there is similar race -+ * for libraries, but there we don't have a nice answer like -+ * that. Presumably we could read the DSOs from the process -+ * memory image, but that's not currently done. */ -+ char *fname = pid2name(proc->pid); -+ if (ltelf_read_library(lib, proc, fname, 0) < 0) { -+ library_destroy(lib); -+ free(lib); -+ return NULL; -+ } - -- return library_symbols; -+ return lib; - } -diff --git a/ltrace-elf.h b/ltrace-elf.h -index 4da8a0a..64d1cb8 100644 ---- a/ltrace-elf.h -+++ b/ltrace-elf.h -@@ -3,7 +3,18 @@ - - #include - #include -+#include "sysdep.h" - -+struct Process; -+struct library; -+struct library_symbol; -+ -+/* XXX Ok, the original idea was to separate the low-level ELF data -+ * from the abstract "struct library" object, but we use some of the -+ * following extensively in the back end. Not all though. So what we -+ * use should be move to struct library, and the rest of this -+ * structure maybe could be safely hidden in .c. How to integrate the -+ * arch-specific bits into struct library is unclear as of now. */ - struct ltelf { - int fd; - Elf *elf; -@@ -12,46 +23,59 @@ struct ltelf { - size_t dynsym_count; - const char *dynstr; - GElf_Addr plt_addr; -+ GElf_Word plt_flags; - size_t plt_size; - Elf_Data *relplt; -+ Elf_Data *plt_data; - size_t relplt_count; - Elf_Data *symtab; - const char *strtab; -+ const char *soname; - size_t symtab_count; - Elf_Data *opd; - GElf_Addr *opd_addr; - size_t opd_size; -- Elf32_Word *hash; -- int hash_type; -- int lte_flags; - GElf_Addr dyn_addr; - size_t dyn_sz; -+ size_t relplt_size; -+ GElf_Addr bias; -+ GElf_Addr entry_addr; - GElf_Addr base_addr; --#ifdef __mips__ -- size_t pltgot_addr; -- size_t mips_local_gotno; -- size_t mips_gotsym; --#endif // __mips__ -- GElf_Addr plt_stub_vma; -+ struct arch_ltelf_data arch; - }; - --#define ELF_MAX_SEGMENTS 50 --#define LTE_HASH_MALLOCED 1 --#define LTE_PLT_EXECUTABLE 2 -+int open_elf(struct ltelf *lte, const char *filename); - --#define PLTS_ARE_EXECUTABLE(lte) ((lte->lte_flags & LTE_PLT_EXECUTABLE) != 0) -+/* XXX is it possible to put breakpoints in VDSO and VSYSCALL -+ * pseudo-libraries? For now we assume that all libraries can be -+ * opened via a filesystem. BASE is ignored for ET_EXEC files. */ -+int ltelf_read_library(struct library *lib, struct Process *proc, -+ const char *filename, GElf_Addr bias); - --extern size_t library_num; --extern char *library[MAX_LIBRARIES]; -+/* Create a library object representing the main binary. The entry -+ * point address is stored to *ENTRYP. */ -+struct library *ltelf_read_main_binary(struct Process *proc, const char *path); - --extern int open_elf(struct ltelf *lte, const char *filename); --extern struct library_symbol *read_elf(Process *proc, GElf_Addr *entryp); -+GElf_Addr arch_plt_sym_val(struct ltelf *, size_t, GElf_Rela *); - --extern 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); -+int elf_get_section_type(struct ltelf *lte, GElf_Word type, -+ Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr); -+int elf_get_section_named(struct ltelf *lte, const char *name, -+ Elf_Scn **tgt_sec, GElf_Shdr *tgt_shdr); - --#ifndef SHT_GNU_HASH --#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ --#endif -+/* Read, respectively, 2, 4, or 8 bytes from Elf data at given OFFSET, -+ * and store it in *RETP. Returns 0 on success or a negative value if -+ * there's not enough data. */ -+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 -diff --git a/ltrace.1 b/ltrace.1 -index 4d320e6..3a908be 100644 ---- a/ltrace.1 -+++ b/ltrace.1 -@@ -1,3 +1,4 @@ -+.\" Copyright (c) 2012 Petr Machata, Red Hat Inc. - .\" Copyright (c) 1997-2005 Juan Cespedes - .\" This file is covered by the GNU GPL - .TH ltrace 1 -@@ -64,22 +65,12 @@ carries upon a traced process - DEBUG_FUNCTION. Shows every entry to internal functions - .RE - .TP --.I \-e expr --A qualifying expression which modifies which events to trace. --The format of the expression is: --.br --[!]value1[,value2]... --.br --where the values are the functions to trace. Using an exclamation --mark negates the set of values. For example --.I \-e printf --means to trace only the printf library call. By contrast, --.I \-e !printf --means to trace every library call except printf. --.IP --Note that some shells use the exclamation point for history --expansion; even inside quoted arguments. If so, you must escape --the exclamation point with a backslash. -+.I \-e filter -+A qualifying expression which modifies which library calls to trace. -+The format of the filter expression is described in the section -+\fBFILTER EXPRESSIONS\fR. If more than one \-e option appears on the -+command line, the library calls that match any of them are traced. If -+no \-e is given, \fB@MAIN\fR is assumed as a default. - .TP - .I \-f - Trace child processes as they are created by -@@ -175,16 +166,72 @@ is set at - 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 extern --Trace the external function --.IR extern . --This option will search the symbol table and lib-dl loaded libraries when --attempting to match the given symbol name. --This option may be repeated. -+.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 -+section \fBFILTER EXPRESSIONS\fR. If more than one \-x option appears -+on the command line, the symbols that match any of them are traced. -+No entry points are traced if no \-x is given. - .TP - .I \-V, \-\-version - Show the version number of ltrace and exit. - -+.SH FILTER EXPRESSIONS -+ -+Filter expression is a chain of glob- or regexp-based rules that are -+used to pick symbols for tracing from libraries that the process uses. -+Most of it is intuitive, so as an example, the following would trace -+calls to malloc and free, except those done by libc: -+ -+-e malloc+free-@libc.so* -+ -+This reads: trace malloc and free, but don't trace anything that comes -+from libc. Semi-formally, the syntax of the above example looks -+approximately like this: -+ -+{[+-][\fIsymbol pattern\fR][@\fIlibrary pattern\fR]} -+ -+\fISymbol pattern\fR is used to match symbol names, \fIlibrary -+pattern\fR to match library SONAMEs. Both are implicitly globs, but -+can be regular expressions as well (see below). The glob syntax -+supports meta-characters \fB*\fR and \fB?\fR and character classes, -+similarly to what basic bash globs support. \fB^\fR and \fB$\fR are -+recognized to mean, respectively, start and end of given name. -+ -+Both \fIsymbol pattern\fR and \fIlibrary pattern\fR have to match the -+whole name. If you want to match only a part of name, surround it -+with one or two *'s as appropriate. The exception is if the pattern -+is not mentioned at all, in which case it's as if the corresponding -+pattern were \fB*\fR. (So \fBmalloc\fR is really \fBmalloc@*\fR and -+\fB@libc.*\fR is really \fB*@libc.*\fR.) -+ -+In libraries that don't have an explicit SONAME, basename is taken for -+SONAME. That holds for main binary as well: \fB/bin/echo\fR has an -+implicit SONAME of \fBecho\fR. In addition to that, special library -+pattern \fBMAIN\fR always matches symbols in the main binary and never -+a library with actual SONAME \fBMAIN\fR (use e.g. \fB^MAIN\fR or -+\fB[M]AIN\fR for that). -+ -+If the symbol or library pattern is surrounded in slashes (/like -+this/), then it is considered a regular expression instead. As a -+shorthand, instead of writing \fB/x/@/y/\fR, you can write -+\fB/x@y/\fR. -+ -+If the library pattern starts with a slash, it is not a SONAME -+expression, but a path expression, and is matched against the library -+path name. -+ -+The first rule may lack a sign, in which case \fB+\fR is assumed. If, -+on the other hand, the first rule has a \fB-\fR sign, it is as if -+there was another rule \fB@*\fR in front of it. -+ -+The above rules are used to construct the set of traced symbols. Each -+candidate symbol is passed through the chain of above rules. -+Initially, the symbol is \fIunmarked\fR. If it symbol matches a -+\fB+\fR rule, it becomes \fImarked\fR, if it matches a \fB-\fR rule, -+it becomes \fIunmarked\fR. If, after applying all rules, the symbol -+is \fImarked\fR, it will be traced. -+ - .SH BUGS - It has most of the bugs stated in - .BR strace(1) . -@@ -212,6 +259,8 @@ Personal config file, overrides - - .SH AUTHOR - Juan Cespedes -+.br -+Petr Machata - - .SH "SEE ALSO" - .BR strace(1) , -diff --git a/ltrace.h b/ltrace.h -index 194704d..fe3d6ed 100644 ---- a/ltrace.h -+++ b/ltrace.h -@@ -1,3 +1,6 @@ -+#ifndef _LTRACE_H_ -+#define _LTRACE_H_ -+ - typedef enum Event_type Event_type; - enum Event_type { - EVENT_NONE=0, -@@ -18,11 +21,10 @@ enum Event_type { - EVENT_MAX - }; - --typedef struct Process Process; - typedef struct Event Event; - struct Event { - struct Event * next; -- Process * proc; -+ struct Process * proc; - Event_type type; - union { - int ret_val; /* EVENT_EXIT */ -@@ -38,3 +40,5 @@ typedef void (*callback_func) (Event *); - extern void ltrace_init(int argc, char **argv); - extern void ltrace_add_callback(callback_func f, Event_type type); - extern void ltrace_main(void); -+ -+#endif /* _LTRACE_H_ */ -diff --git a/options.c b/options.c -index 74c28bd..d5edc1a 100644 ---- a/options.c -+++ b/options.c -@@ -1,16 +1,19 @@ - #include "config.h" - --#include --#include --#include --#include --#include --#include - #include -- -+#include -+#include -+#include - #include -+#include -+#include -+#include -+#include -+#include - - #include "common.h" -+#include "filter.h" -+#include "glob.h" - - #ifndef SYSCONFDIR - #define SYSCONFDIR "/etc" -@@ -23,7 +26,6 @@ struct options_t options = { - .align = DEFAULT_ALIGN, /* alignment column for results */ - .user = NULL, /* username to run command as */ - .syscalls = 0, /* display syscalls */ -- .libcalls = 1, /* display library calls */ - #ifdef USE_DEMANGLE - .demangle = 0, /* Demangle low-level symbol names */ - #endif -@@ -36,8 +38,6 @@ struct options_t options = { - .follow = 0, /* trace child processes */ - }; - --char *library[MAX_LIBRARIES]; --size_t library_num = 0; - static char *progname; /* Program name (`ltrace') */ - int opt_i = 0; /* instruction pointer */ - int opt_r = 0; /* print relative timestamp */ -@@ -47,14 +47,6 @@ int opt_T = 0; /* show the time spent inside each call */ - /* List of pids given to option -p: */ - struct opt_p_t *opt_p = NULL; /* attach to process with a given pid */ - --/* List of function names given to option -e: */ --struct opt_e_t *opt_e = NULL; --int opt_e_enable = 1; -- --/* List of global function names given to -x: */ --struct opt_x_t *opt_x = NULL; --unsigned int opt_x_cnt = 0; -- - /* List of filenames give to option -F: */ - struct opt_F_t *opt_F = NULL; /* alternate configuration file(s) */ - -@@ -181,11 +173,221 @@ guess_cols(void) { - } - } - -+static void -+add_filter_rule(struct filter *filt, const char *expr, -+ enum filter_rule_type type, -+ const char *a_sym, int sym_re_p, -+ const char *a_lib, int lib_re_p) -+{ -+ struct filter_rule *rule = malloc(sizeof(*rule)); -+ struct filter_lib_matcher *matcher = malloc(sizeof(*matcher)); -+ -+ if (rule == NULL || matcher == NULL) { -+ fprintf(stderr, "rule near '%s' will be ignored: %s\n", -+ expr, strerror(errno)); -+ fail: -+ free(rule); -+ free(matcher); -+ return; -+ } -+ -+ 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); -+ if (status != 0) { -+ char buf[100]; -+ regerror(status, &symbol_re, buf, sizeof buf); -+ fprintf(stderr, "rule near '%s' will be ignored: %s\n", -+ expr, buf); -+ goto fail; -+ } -+ } -+ -+ 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); -+ } -+ -+ filter_rule_init(rule, type, matcher, symbol_re); -+ filter_add_rule(filt, rule); -+} -+ -+static int -+parse_filter(struct filter *filt, char *expr) -+{ -+ /* Filter is a chain of sym@lib rules separated by '-'. 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, "@-+"); -+ char *symname = expr; -+ char *libname; -+ char *next = expr + s + 1; -+ enum filter_rule_type this_type = type; -+ -+ if (expr[s] == 0) { -+ libname = "*"; -+ expr = next - 1; -+ -+ } else if (expr[s] == '-' || expr[s] == '+') { -+ type = expr[s] == '-' ? FR_SUBTRACT : FR_ADD; -+ expr[s] = 0; -+ libname = "*"; -+ expr = next; -+ -+ } else { -+ assert(expr[s] == '@'); -+ expr[s] = 0; -+ s = strcspn(next, "-+"); -+ if (s == 0) { -+ libname = "*"; -+ expr = next; -+ } else if (next[s] == 0) { -+ expr = next + s; -+ libname = next; -+ } else { -+ assert(next[s] == '-' || next[s] == '+'); -+ type = next[s] == '-' ? FR_SUBTRACT : FR_ADD; -+ next[s] = 0; -+ expr = next + s + 1; -+ libname = next; -+ } -+ } -+ -+ assert(*libname != 0); -+ char *symend = symname + strlen(symname) - 1; -+ char *libend = libname + strlen(libname) - 1; -+ int sym_is_re = 0; -+ int lib_is_re = 0; -+ -+ /* -+ * /xxx/@... and ...@/xxx/ means that xxx are regular -+ * expressions. They are globs otherwise. -+ * -+ * /xxx@yyy/ is the same as /xxx/@/yyy/ -+ * -+ * @/xxx matches library path name -+ * @.xxx matches library relative path name -+ */ -+ if (symname[0] == '/') { -+ if (symname != symend && symend[0] == '/') { -+ ++symname; -+ *symend-- = 0; -+ sym_is_re = 1; -+ -+ } else { -+ sym_is_re = 1; -+ lib_is_re = 1; -+ ++symname; -+ -+ /* /XXX@YYY/ is the same as -+ * /XXX/@/YYY/. */ -+ if (libend[0] != '/') -+ fprintf(stderr, "unmatched '/'" -+ " in symbol name\n"); -+ else -+ *libend-- = 0; -+ } -+ } -+ -+ /* 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 (*symname == 0) /* /@AA/ */ -+ symname = "*"; -+ if (*libname == 0) /* /aa@/ */ -+ libname = "*"; -+ -+ add_filter_rule(filt, expr, this_type, -+ symname, sym_is_re, -+ libname, lib_is_re); -+ } -+ -+ return 0; -+} -+ -+static struct filter * -+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", -+ expr, strerror(errno)); -+ return NULL; -+ } -+ -+ filter_init(filt); -+ if (parse_filter(filt, expr) < 0) { -+ fprintf(stderr, "Filter '%s' will be ignored.\n", expr); -+ free(filt); -+ filt = NULL; -+ } -+ -+ return filt; -+} -+ -+static void -+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", -+ expr, strerror(errno)); -+ return; -+ } -+ /* Support initial '!' for backward compatibility. */ -+ if (str[0] == '!') -+ str[0] = '-'; -+ -+ struct filter **tailp; -+ for (tailp = retp; *tailp != NULL; tailp = &(*tailp)->next) -+ ; -+ *tailp = recursive_parse_chain(str); -+} -+ - char ** --process_options(int argc, char **argv) { -+process_options(int argc, char **argv) -+{ - progname = argv[0]; - options.output = stderr; -- options.no_plt = 0; - options.no_signals = 0; - #if defined(HAVE_LIBUNWIND) - options.bt_depth = -1; -@@ -193,6 +395,8 @@ process_options(int argc, char **argv) { - - guess_cols(); - -+ int libcalls = 1; -+ - while (1) { - int c; - char *p; -@@ -209,14 +413,13 @@ process_options(int argc, char **argv) { - {"library", 1, 0, 'l'}, - {"output", 1, 0, 'o'}, - {"version", 0, 0, 'V'}, -- {"no-plt", 0, 0, 'g'}, - {"no-signals", 0, 0, 'b'}, - #if defined(HAVE_LIBUNWIND) - {"where", 1, 0, 'w'}, - #endif /* defined(HAVE_LIBUNWIND) */ - {0, 0, 0, 0} - }; -- c = getopt_long(argc, argv, "+cfhiLrStTVgb" -+ c = getopt_long(argc, argv, "+cfhiLrStTVb" - # ifdef USE_DEMANGLE - "C" - # endif -@@ -258,39 +461,11 @@ process_options(int argc, char **argv) { - err_usage(); - } - break; -+ - case 'e': -- { -- char *str_e = strdup(optarg); -- if (!str_e) { -- perror("ltrace: strdup"); -- exit(1); -- } -- if (str_e[0] == '!') { -- opt_e_enable = 0; -- str_e++; -- } -- while (*str_e) { -- struct opt_e_t *tmp; -- char *str2 = strchr(str_e, ','); -- if (str2) { -- *str2 = '\0'; -- } -- tmp = malloc(sizeof(struct opt_e_t)); -- if (!tmp) { -- perror("ltrace: malloc"); -- exit(1); -- } -- tmp->name = str_e; -- tmp->next = opt_e; -- opt_e = tmp; -- if (str2) { -- str_e = str2 + 1; -- } else { -- break; -- } -- } -- break; -- } -+ parse_filter_chain(optarg, &options.plt_filter); -+ break; -+ - case 'f': - options.follow = 1; - break; -@@ -306,9 +481,6 @@ process_options(int argc, char **argv) { - opt_F = tmp; - break; - } -- case 'g': -- options.no_plt = 1; -- break; - case 'h': - usage(); - exit(0); -@@ -316,16 +488,11 @@ process_options(int argc, char **argv) { - opt_i++; - break; - case 'l': -- if (library_num == MAX_LIBRARIES) { -- fprintf(stderr, -- "Too many libraries. Maximum is %i.\n", -- MAX_LIBRARIES); -- exit(1); -- } -- library[library_num++] = optarg; -+ // XXX TODO -+ fprintf(stderr, "-l support not yet implemented\n"); - break; - case 'L': -- options.libcalls = 0; -+ libcalls = 0; - break; - case 'n': - options.indent = atoi(optarg); -@@ -334,7 +501,7 @@ process_options(int argc, char **argv) { - options.output = fopen(optarg, "w"); - if (!options.output) { - fprintf(stderr, -- "Can't open %s for output: %s\n", -+ "can't open %s for writing: %s\n", - optarg, strerror(errno)); - exit(1); - } -@@ -393,31 +560,8 @@ process_options(int argc, char **argv) { - /* Fall Thru */ - - case 'x': -- { -- struct opt_x_t *p = opt_x; -- -- /* First, check for duplicate. */ -- while (p && strcmp(p->name, optarg)) { -- p = p->next; -- } -- if (p) { -- break; -- } -- -- /* If not duplicate, add to list. */ -- p = malloc(sizeof(struct opt_x_t)); -- if (!p) { -- perror("ltrace: malloc"); -- exit(1); -- } -- opt_x_cnt++; -- p->name = optarg; -- p->found = 0; -- p->next = opt_x; -- p->hash = ~(0UL); -- opt_x = p; -- break; -- } -+ parse_filter_chain(optarg, &options.static_filter); -+ break; - - default: - err_usage(); -@@ -449,6 +593,14 @@ 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) { -+ parse_filter_chain("@MAIN", &options.plt_filter); -+ options.hide_caller = 1; -+ } -+ - if (!opt_p && argc < 1) { - fprintf(stderr, "%s: too few arguments\n", progname); - err_usage(); -diff --git a/options.h b/options.h -index 9a00629..3ffee71 100644 ---- a/options.h -+++ b/options.h -@@ -1,11 +1,12 @@ - #include - #include - -+struct filter; -+ - struct options_t { - int align; /* -a: default alignment column for results */ - char * user; /* -u: username to run command as */ - int syscalls; /* -S: display system calls */ -- int libcalls; /* -L: display library calls */ - int demangle; /* -C: demangle low-level names into user-level names */ - int indent; /* -n: indent trace output according to program flow */ - FILE *output; /* output to a specific file */ -@@ -14,11 +15,13 @@ struct options_t { - size_t arraylen; /* default maximum # of array elements printed */ - size_t strlen; /* default maximum # of bytes printed in strings */ - int follow; /* trace child processes */ -- int no_plt; /* set bps on PLT entries */ - int no_signals; /* don't print signals */ - #if defined(HAVE_LIBUNWIND) - int bt_depth; /* how may levels of stack frames to show */ - #endif /* defined(HAVE_LIBUNWIND) */ -+ struct filter *plt_filter; -+ struct filter *static_filter; -+ int hide_caller; /* Whether caller library should be hidden. */ - }; - extern struct options_t options; - -@@ -32,31 +35,13 @@ struct opt_p_t { - struct opt_p_t *next; - }; - --struct opt_e_t { -- char *name; -- struct opt_e_t *next; --}; -- - struct opt_F_t { - char *filename; - struct opt_F_t *next; - }; - --struct opt_x_t { -- char *name; -- int found; -- unsigned long hash; -- struct opt_x_t *next; --}; -- - extern struct opt_p_t *opt_p; /* attach to process with a given pid */ - --extern struct opt_e_t *opt_e; /* list of function names to display */ --extern int opt_e_enable; /* 0 if '!' is used, 1 otherwise */ -- - extern struct opt_F_t *opt_F; /* alternate configuration file(s) */ - --extern struct opt_x_t *opt_x; /* list of functions to break at */ --extern unsigned int opt_x_cnt; -- - extern char **process_options(int argc, char **argv); -diff --git a/output.c b/output.c -index 1e2e709..ac8c9d0 100644 ---- a/output.c -+++ b/output.c -@@ -9,6 +9,8 @@ - #include - - #include "common.h" -+#include "proc.h" -+#include "library.h" - - /* TODO FIXME XXX: include in common.h: */ - extern struct timeval current_time_spent; -@@ -153,7 +155,10 @@ tabto(int col) { - } - - void --output_left(enum tof type, Process *proc, char const *function_name) { -+output_left(enum tof type, struct Process *proc, -+ struct library_symbol *libsym) -+{ -+ const char *function_name = libsym->name; - Function *func; - static arg_type_info *arg_unknown = NULL; - if (arg_unknown == NULL) -@@ -169,10 +174,15 @@ output_left(enum tof type, Process *proc, char const *function_name) { - current_proc = proc; - current_depth = proc->callstack_depth; - begin_of_line(type, proc); -+ if (!options.hide_caller && libsym->lib != NULL -+ && libsym->plt_type != LS_TOPLT_NONE) -+ current_column += fprintf(options.output, "%s->", -+ libsym->lib->soname); - #ifdef USE_DEMANGLE - current_column += -- fprintf(options.output, "%s(", -- options.demangle ? my_demangle(function_name) : function_name); -+ fprintf(options.output, "%s(", -+ (options.demangle -+ ? my_demangle(function_name) : function_name)); - #else - current_column += fprintf(options.output, "%s(", function_name); - #endif -@@ -210,7 +220,9 @@ output_left(enum tof type, Process *proc, char const *function_name) { - } - - void --output_right(enum tof type, Process *proc, char *function_name) { -+output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) -+{ -+ const char *function_name = libsym->name; - Function *func = name2func(function_name); - static arg_type_info *arg_unknown = NULL; - if (arg_unknown == NULL) -diff --git a/output.h b/output.h -index fa840c7..714078f 100644 ---- a/output.h -+++ b/output.h -@@ -1,3 +1,7 @@ --void output_line(Process *proc, char *fmt, ...); --void output_left(enum tof type, Process *proc, char const *function_name); --void output_right(enum tof type, Process *proc, char *function_name); -+struct Process; -+struct library_symbol; -+void output_line(struct Process *proc, char *fmt, ...); -+void output_left(enum tof type, struct Process *proc, -+ struct library_symbol *libsym); -+void output_right(enum tof type, struct Process *proc, -+ struct library_symbol *libsym); -diff --git a/proc.c b/proc.c -index 47086b4..51833fe 100644 ---- a/proc.c -+++ b/proc.c -@@ -11,45 +11,296 @@ - #include - #include - #include --#include - - #include "common.h" - #include "breakpoint.h" -+#include "proc.h" - --Process * --open_program(char *filename, pid_t pid, int enable) { -- Process *proc; -- assert(pid != 0); -- proc = calloc(sizeof(Process), 1); -- if (!proc) { -- perror("malloc"); -- exit(1); -+#ifndef ARCH_HAVE_PROCESS_DATA -+int -+arch_process_init(struct Process *proc) -+{ -+ return 0; -+} -+ -+void -+arch_process_destroy(struct Process *proc) -+{ -+} -+ -+int -+arch_process_clone(struct Process *retp, struct Process *proc) -+{ -+ return 0; -+} -+ -+int -+arch_process_exec(struct Process *proc) -+{ -+ return 0; -+} -+#endif -+ -+#ifndef ARCH_HAVE_DYNLINK_DONE -+void -+arch_dynlink_done(struct Process *proc) -+{ -+} -+#endif -+ -+static void add_process(struct Process *proc, int was_exec); -+ -+static int -+process_bare_init(struct Process *proc, const char *filename, -+ pid_t pid, int was_exec) -+{ -+ if (!was_exec) { -+ memset(proc, 0, sizeof(*proc)); -+ -+ proc->filename = strdup(filename); -+ if (proc->filename == NULL) { -+ fail: -+ free(proc->filename); -+ if (proc->breakpoints != NULL) -+ dict_clear(proc->breakpoints); -+ return -1; -+ } - } - -- proc->filename = strdup(filename); -+ /* Add process so that we know who the leader is. */ - proc->pid = pid; -+ add_process(proc, was_exec); -+ if (proc->leader == NULL) -+ goto fail; -+ -+ if (proc->leader == proc) { -+ proc->breakpoints = dict_init(target_address_hash, -+ target_address_cmp); -+ if (proc->breakpoints == NULL) -+ goto fail; -+ } else { -+ proc->breakpoints = NULL; -+ } -+ - #if defined(HAVE_LIBUNWIND) - proc->unwind_priv = _UPT_create(pid); - proc->unwind_as = unw_create_addr_space(&_UPT_accessors, 0); - #endif /* defined(HAVE_LIBUNWIND) */ - -- add_process(proc); -- if (proc->leader == NULL) { -+ return 0; -+} -+ -+static void -+process_bare_destroy(struct Process *proc, int was_exec) -+{ -+ dict_clear(proc->breakpoints); -+ if (!was_exec) { -+ free(proc->filename); -+ remove_process(proc); -+ } -+} -+ -+static int -+process_init_main(struct Process *proc) -+{ -+ target_address_t entry; -+ target_address_t interp_bias; -+ if (process_get_entry(proc, &entry, &interp_bias) < 0) { -+ fprintf(stderr, "Couldn't get entry points of process %d\n", -+ proc->pid); -+ return -1; -+ } -+ -+ if (breakpoints_init(proc) < 0) { -+ fprintf(stderr, "failed to init breakpoints %d\n", -+ proc->pid); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+int -+process_init(struct Process *proc, const char *filename, pid_t pid) -+{ -+ if (process_bare_init(proc, filename, pid, 0) < 0) { -+ fail: -+ fprintf(stderr, "failed to initialize process %d: %s\n", -+ pid, strerror(errno)); -+ return -1; -+ } -+ -+ if (arch_process_init(proc) < 0) { -+ process_bare_destroy(proc, 0); -+ goto fail; -+ } -+ -+ if (proc->leader != proc) -+ return 0; -+ if (process_init_main(proc) < 0) { -+ process_bare_destroy(proc, 0); -+ goto fail; -+ } -+ return 0; -+} -+ -+static enum callback_status -+destroy_breakpoint_cb(struct Process *proc, struct breakpoint *bp, void *data) -+{ -+ breakpoint_destroy(bp); -+ free(bp); -+ return CBS_CONT; -+} -+ -+static void -+private_process_destroy(struct Process *proc, int keep_filename) -+{ -+ if (!keep_filename) -+ free(proc->filename); -+ -+ /* Libraries and symbols. This is only relevant in -+ * leader. */ -+ struct library *lib; -+ for (lib = proc->libraries; lib != NULL; ) { -+ struct library *next = lib->next; -+ library_destroy(lib); -+ free(lib); -+ lib = next; -+ } -+ proc->libraries = NULL; -+ -+ /* Breakpoints. */ -+ if (proc->breakpoints != NULL) { -+ proc_each_breakpoint(proc, NULL, destroy_breakpoint_cb, NULL); -+ dict_clear(proc->breakpoints); -+ proc->breakpoints = NULL; -+ } -+} -+ -+void -+process_destroy(struct Process *proc) -+{ -+ private_process_destroy(proc, 0); -+ arch_process_destroy(proc); -+} -+ -+int -+process_exec(struct Process *proc) -+{ -+ /* Call exec first, before we destroy the main state. */ -+ if (arch_process_exec(proc) < 0) -+ return -1; -+ -+ private_process_destroy(proc, 1); -+ if (process_bare_init(proc, NULL, proc->pid, 1) < 0) -+ return -1; -+ if (process_init_main(proc) < 0) { -+ process_bare_destroy(proc, 1); -+ return -1; -+ } -+ return 0; -+} -+ -+struct Process * -+open_program(const char *filename, pid_t pid) -+{ -+ assert(pid != 0); -+ struct Process *proc = malloc(sizeof(*proc)); -+ if (proc == NULL || process_init(proc, filename, pid) < 0) { - free(proc); - return NULL; - } -+ return proc; -+} - -- if (proc->leader == proc) { -- trace_set_options(proc, proc->pid); -- if (breakpoints_init(proc, enable)) { -- fprintf(stderr, "failed to init breakpoints %d\n", -- proc->pid); -- remove_process(proc); -- return NULL; -+struct clone_single_bp_data { -+ struct Process *old_proc; -+ struct Process *new_proc; -+ int error; -+}; -+ -+static void -+clone_single_bp(void *key, void *value, void *u) -+{ -+ struct breakpoint *bp = value; -+ struct clone_single_bp_data *data = u; -+ -+ data->error = 0; -+ struct breakpoint *clone = malloc(sizeof(*clone)); -+ if (clone == NULL -+ || breakpoint_clone(clone, data->new_proc, -+ bp, data->old_proc) < 0) { -+ fail: -+ free(clone); -+ data->error = -1; -+ } -+ if (proc_add_breakpoint(data->new_proc->leader, clone) < 0) { -+ breakpoint_destroy(clone); -+ goto fail; -+ } -+} -+ -+int -+process_clone(struct Process *retp, struct Process *proc, pid_t pid) -+{ -+ if (process_bare_init(retp, proc->filename, pid, 0) < 0) { -+ fail: -+ fprintf(stderr, "failed to clone process %d->%d : %s\n", -+ proc->pid, pid, strerror(errno)); -+ return -1; -+ } -+ -+ retp->tracesysgood = proc->tracesysgood; -+ retp->e_machine = proc->e_machine; -+ -+ /* For non-leader processes, that's all we need to do. */ -+ if (retp->leader != retp) -+ return 0; -+ -+ /* Clone symbols first so that we can clone and relink -+ * breakpoints. */ -+ struct library *lib; -+ struct library **nlibp = &retp->libraries; -+ for (lib = proc->libraries; lib != NULL; lib = lib->next) { -+ *nlibp = malloc(sizeof(**nlibp)); -+ if (*nlibp == NULL -+ || library_clone(*nlibp, lib) < 0) { -+ fail2: -+ process_bare_destroy(retp, 0); -+ -+ /* Error when cloning. Unroll what was done. */ -+ for (lib = retp->libraries; lib != NULL; ) { -+ struct library *next = lib->next; -+ library_destroy(lib); -+ free(lib); -+ lib = next; -+ } -+ goto fail; - } -+ -+ nlibp = &(*nlibp)->next; - } - -- return proc; -+ /* Now clone breakpoints. Symbol relinking is done in -+ * clone_single_bp. */ -+ struct clone_single_bp_data data = { -+ .old_proc = proc, -+ .new_proc = retp, -+ .error = 0, -+ }; -+ dict_apply_to_all(proc->breakpoints, &clone_single_bp, &data); -+ -+ /* And finally the call stack. */ -+ memcpy(retp->callstack, proc->callstack, sizeof(retp->callstack)); -+ retp->callstack_depth = proc->callstack_depth; -+ -+ if (data.error < 0) -+ goto fail2; -+ -+ if (arch_process_clone(retp, proc) < 0) -+ goto fail2; -+ -+ return 0; - } - - static int -@@ -67,19 +318,19 @@ open_one_pid(pid_t pid) - return -1; - } - -- proc = open_program(filename, pid, 0); -+ proc = open_program(filename, pid); - if (proc == NULL) - return -1; -- trace_set_options(proc, pid); -+ trace_set_options(proc); - - return 0; - } - --static enum pcb_status -+static enum callback_status - start_one_pid(Process * proc, void * data) - { - continue_process(proc->pid); -- return pcb_cont; -+ return CBS_CONT; - } - - void -@@ -134,20 +385,21 @@ open_pid(pid_t pid) - old_ntasks = ntasks; - } - -- /* Done. Now initialize breakpoints and then continue -- * everyone. */ -- Process * leader; -- leader = pid2proc(pid)->leader; -- enable_all_breakpoints(leader); -+ struct Process *leader = pid2proc(pid)->leader; - -- each_task(pid2proc(pid)->leader, start_one_pid, NULL); -+ /* XXX Is there a way to figure out whether _start has -+ * actually already been hit? */ -+ arch_dynlink_done(leader); -+ -+ /* Done. Continue everyone. */ -+ each_task(leader, NULL, start_one_pid, NULL); - } - --static enum pcb_status -+static enum callback_status - find_proc(Process * proc, void * data) - { - pid_t pid = (pid_t)(uintptr_t)data; -- return proc->pid == pid ? pcb_stop : pcb_cont; -+ return proc->pid == pid ? CBS_STOP : CBS_CONT; - } - - Process * -@@ -179,41 +431,60 @@ unlist_process(Process * proc) - } - } - --Process * --each_process(Process * proc, -- enum pcb_status (* cb)(Process * proc, void * data), -- void * data) -+struct Process * -+each_process(struct Process *start_after, -+ enum callback_status(*cb)(struct Process *proc, void *data), -+ void *data) - { -- Process * it = proc ?: list_of_processes; -- for (; it != NULL; ) { -+ struct Process *it = start_after == NULL ? list_of_processes -+ : start_after->next; -+ -+ while (it != NULL) { - /* Callback might call remove_process. */ -- Process * next = it->next; -- if ((*cb) (it, data) == pcb_stop) -+ struct Process *next = it->next; -+ switch ((*cb)(it, data)) { -+ case CBS_FAIL: -+ /* XXX handle me */ -+ case CBS_STOP: - return it; -+ case CBS_CONT: -+ break; -+ } - it = next; - } - return NULL; - } - - Process * --each_task(Process * it, enum pcb_status (* cb)(Process * proc, void * data), -- void * data) -+each_task(struct Process *proc, struct Process *start_after, -+ enum callback_status(*cb)(struct Process *proc, void *data), -+ void *data) - { -+ assert(proc != NULL); -+ struct Process *it = start_after == NULL ? proc->leader -+ : start_after->next; -+ - if (it != NULL) { -- Process * leader = it->leader; -- for (; it != NULL && it->leader == leader; ) { -+ struct Process *leader = it->leader; -+ while (it != NULL && it->leader == leader) { - /* Callback might call remove_process. */ -- Process * next = it->next; -- if ((*cb) (it, data) == pcb_stop) -+ struct Process *next = it->next; -+ switch ((*cb)(it, data)) { -+ case CBS_FAIL: -+ /* XXX handle me */ -+ case CBS_STOP: - return it; -+ case CBS_CONT: -+ break; -+ } - it = next; - } - } - return NULL; - } - --void --add_process(Process * proc) -+static void -+add_process(struct Process *proc, int was_exec) - { - Process ** leaderp = &list_of_processes; - if (proc->pid) { -@@ -231,8 +502,11 @@ add_process(Process * proc) - leaderp = &leader->next; - } - } -- proc->next = *leaderp; -- *leaderp = proc; -+ -+ if (!was_exec) { -+ proc->next = *leaderp; -+ *leaderp = proc; -+ } - } - - void -@@ -252,13 +526,13 @@ change_process_leader(Process * proc, Process * leader) - *leaderp = proc; - } - --static enum pcb_status --clear_leader(Process * proc, void * data) -+static enum callback_status -+clear_leader(struct Process *proc, void *data) - { - debug(DEBUG_FUNCTION, "detach_task %d from leader %d", - proc->pid, proc->leader->pid); - proc->leader = NULL; -- return pcb_cont; -+ return CBS_CONT; - } - - static enum ecb_status -@@ -284,15 +558,16 @@ remove_process(Process *proc) - debug(DEBUG_FUNCTION, "remove_proc(pid=%d)", proc->pid); - - if (proc->leader == proc) -- each_task(proc, &clear_leader, NULL); -+ each_task(proc, NULL, &clear_leader, NULL); - - unlist_process(proc); - delete_events_for(proc); -+ process_destroy(proc); - free(proc); - } - - void --install_event_handler(Process * proc, Event_Handler * handler) -+install_event_handler(Process *proc, struct event_handler *handler) - { - debug(DEBUG_FUNCTION, "install_event_handler(pid=%d, %p)", proc->pid, handler); - assert(proc->event_handler == NULL); -@@ -302,7 +577,7 @@ install_event_handler(Process * proc, Event_Handler * handler) - void - destroy_event_handler(Process * proc) - { -- Event_Handler * handler = proc->event_handler; -+ struct event_handler *handler = proc->event_handler; - debug(DEBUG_FUNCTION, "destroy_event_handler(pid=%d, %p)", proc->pid, handler); - assert(handler != NULL); - if (handler->destroy != NULL) -@@ -310,3 +585,197 @@ destroy_event_handler(Process * proc) - free(handler); - proc->event_handler = NULL; - } -+ -+static enum callback_status -+breakpoint_for_symbol(struct library_symbol *libsym, void *data) -+{ -+ struct Process *proc = data; -+ assert(proc->leader == proc); -+ -+ /* 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 -+ * the callbacks, which we don't touch. If there is a real -+ * breakpoint, then this is a bug. ltrace-elf.c should filter -+ * symbols and ignore extra symbol aliases. -+ * -+ * The other direction is more complicated and currently not -+ * supported. If a breakpoint has custom callbacks, it might -+ * be also custom-allocated, and we would really need to swap -+ * 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); -+ if (bp != NULL) { -+ assert(bp->libsym == NULL); -+ bp->libsym = libsym; -+ return CBS_CONT; -+ } -+ -+ bp = malloc(sizeof(*bp)); -+ if (bp == NULL -+ || breakpoint_init(bp, proc, libsym->enter_addr, libsym) < 0) { -+ fail: -+ free(bp); -+ return CBS_FAIL; -+ } -+ if (proc_add_breakpoint(proc, bp) < 0) { -+ breakpoint_destroy(bp); -+ goto fail; -+ } -+ -+ if (breakpoint_turn_on(bp, proc) < 0) { -+ proc_remove_breakpoint(proc, bp); -+ breakpoint_destroy(bp); -+ goto fail; -+ } -+ -+ return CBS_CONT; -+} -+ -+void -+proc_add_library(struct Process *proc, struct library *lib) -+{ -+ assert(lib->next == NULL); -+ lib->next = proc->libraries; -+ proc->libraries = lib; -+ debug(DEBUG_PROCESS, "added library %s@%p (%s) to %d", -+ lib->soname, lib->base, lib->pathname, proc->pid); -+ -+ struct library_symbol *libsym = NULL; -+ while ((libsym = library_each_symbol(lib, libsym, breakpoint_for_symbol, -+ proc)) != NULL) -+ fprintf(stderr, "couldn't insert breakpoint for %s to %d: %s", -+ libsym->name, proc->pid, strerror(errno)); -+} -+ -+int -+proc_remove_library(struct Process *proc, struct library *lib) -+{ -+ struct library **libp; -+ for (libp = &proc->libraries; *libp != NULL; libp = &(*libp)->next) -+ if (*libp == lib) { -+ *libp = lib->next; -+ return 0; -+ } -+ return -1; -+} -+ -+struct library * -+proc_each_library(struct Process *proc, struct library *it, -+ enum callback_status (*cb)(struct Process *proc, -+ struct library *lib, void *data), -+ void *data) -+{ -+ if (it == NULL) -+ it = proc->libraries; -+ -+ while (it != NULL) { -+ struct library *next = it->next; -+ -+ switch (cb(proc, it, data)) { -+ case CBS_FAIL: -+ /* XXX handle me */ -+ case CBS_STOP: -+ return it; -+ case CBS_CONT: -+ break; -+ } -+ -+ it = next; -+ } -+ -+ return NULL; -+} -+ -+static void -+check_leader(struct Process *proc) -+{ -+ /* Only the group leader should be getting the breakpoints and -+ * thus have ->breakpoint initialized. */ -+ assert(proc->leader != NULL); -+ assert(proc->leader == proc); -+ assert(proc->breakpoints != NULL); -+} -+ -+int -+proc_add_breakpoint(struct Process *proc, struct breakpoint *bp) -+{ -+ debug(DEBUG_FUNCTION, "proc_add_breakpoint(pid=%d, %s@%p)", -+ proc->pid, breakpoint_name(bp), bp->addr); -+ check_leader(proc); -+ -+ /* XXX We might merge bp->libsym instead of the following -+ * assert, but that's not necessary right now. Read the -+ * comment in breakpoint_for_symbol. */ -+ assert(dict_find_entry(proc->breakpoints, bp->addr) == NULL); -+ -+ if (dict_enter(proc->breakpoints, bp->addr, bp) < 0) { -+ fprintf(stderr, -+ "couldn't enter breakpoint %s@%p to dictionary: %s\n", -+ breakpoint_name(bp), bp->addr, strerror(errno)); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+void -+proc_remove_breakpoint(struct Process *proc, struct breakpoint *bp) -+{ -+ debug(DEBUG_FUNCTION, "proc_remove_breakpoint(pid=%d, %s@%p)", -+ proc->pid, breakpoint_name(bp), bp->addr); -+ check_leader(proc); -+ struct breakpoint *removed = dict_remove(proc->breakpoints, bp->addr); -+ assert(removed == bp); -+} -+ -+/* Dict doesn't support iteration restarts, so here's this contraption -+ * for now. XXX add restarts to dict. */ -+struct each_breakpoint_data -+{ -+ void *start; -+ void *end; -+ struct Process *proc; -+ enum callback_status (*cb)(struct Process *proc, -+ struct breakpoint *bp, -+ void *data); -+ void *cb_data; -+}; -+ -+static void -+each_breakpoint_cb(void *key, void *value, void *d) -+{ -+ struct each_breakpoint_data *data = d; -+ if (data->end != NULL) -+ return; -+ if (data->start == key) -+ data->start = NULL; -+ -+ if (data->start == NULL) { -+ switch (data->cb(data->proc, value, data->cb_data)) { -+ case CBS_FAIL: -+ /* XXX handle me */ -+ case CBS_STOP: -+ data->end = key; -+ case CBS_CONT: -+ return; -+ } -+ } -+} -+ -+void * -+proc_each_breakpoint(struct Process *proc, void *start, -+ enum callback_status (*cb)(struct Process *proc, -+ struct breakpoint *bp, -+ void *data), void *data) -+{ -+ struct each_breakpoint_data dd = { -+ .start = start, -+ .proc = proc, -+ .cb = cb, -+ .cb_data = data, -+ }; -+ dict_apply_to_all(proc->breakpoints, &each_breakpoint_cb, &dd); -+ return dd.end; -+} -diff --git a/proc.h b/proc.h -new file mode 100644 -index 0000000..443bd8e ---- /dev/null -+++ b/proc.h -@@ -0,0 +1,234 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2010,2011,2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2010 Joe Damato -+ * Copyright (C) 1998,2001,2008,2009 Juan Cespedes -+ * -+ * 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 _PROC_H_ -+#define _PROC_H_ -+ -+#if defined(HAVE_LIBUNWIND) -+# include -+#endif /* defined(HAVE_LIBUNWIND) */ -+ -+#include "ltrace.h" -+#include "dict.h" -+#include "sysdep.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 -+ * returned event is passed to the default handler. */ -+ Event *(*on_event)(struct event_handler *self, Event *event); -+ -+ /* Called when the event handler removal is requested. */ -+ void (*destroy)(struct event_handler *self); -+}; -+ -+enum process_state { -+ STATE_ATTACHED = 0, -+ STATE_BEING_CREATED, -+ STATE_IGNORED /* ignore this process (it's a fork and no -f was used) */ -+}; -+ -+struct callstack_element { -+ union { -+ int syscall; -+ struct library_symbol * libfunc; -+ } c_un; -+ int is_syscall; -+ void * return_addr; -+ struct timeval time_spent; -+ void * arch_ptr; -+}; -+ -+/* XXX We should get rid of this. */ -+#define MAX_CALLDEPTH 64 -+ -+/* XXX We would rather have this all organized a little differently, -+ * have Process for the whole group and Task for what's there for -+ * per-thread stuff. But for now this is the less invasive way of -+ * structuring it. */ -+typedef struct Process Process; -+struct Process { -+ enum process_state state; -+ Process * parent; /* needed by STATE_BEING_CREATED */ -+ char * filename; -+ pid_t pid; -+ -+ /* Dictionary of breakpoints (which is a mapping -+ * address->breakpoint). This is NULL for non-leader -+ * processes. XXX note that we store addresses (keys) by -+ * value. That assumes that target_address_t fits in host -+ * pointer. */ -+ Dict * breakpoints; -+ -+ int mask_32bit; /* 1 if 64-bit ltrace is tracing 32-bit process */ -+ unsigned int personality; -+ int tracesysgood; /* signal indicating a PTRACE_SYSCALL trap */ -+ -+ int callstack_depth; -+ struct callstack_element callstack[MAX_CALLDEPTH]; -+ -+ /* Linked list of libraries in backwards order of mapping. -+ * The last element is the executed binary itself. */ -+ struct library *libraries; -+ -+ /* Arch-dependent: */ -+ void *debug; /* arch-dep process debug struct XXX move to -+ * os_process_data after it's invented. */ -+ void * instruction_pointer; -+ void * stack_pointer; /* To get return addr, args... */ -+ void * return_addr; -+ void * arch_ptr; -+ short e_machine; -+#ifdef __arm__ -+ int thumb_mode; /* ARM execution mode: 0: ARM, 1: Thumb */ -+#endif -+ -+#if defined(HAVE_LIBUNWIND) -+ /* libunwind address space */ -+ unw_addr_space_t unwind_as; -+ void *unwind_priv; -+#endif /* defined(HAVE_LIBUNWIND) */ -+ -+ /* Set in leader. */ -+ struct event_handler *event_handler; -+ -+ /** -+ * Process chaining. -+ **/ -+ Process * next; -+ -+ /* LEADER points to the leader thread of the POSIX.1 process. -+ If X->LEADER == X, then X is the leader thread and the -+ Process structures chained by NEXT represent other threads, -+ up until, but not including, the next leader thread. -+ LEADER may be NULL after the leader has already exited. In -+ that case this process is waiting to be collected. */ -+ Process * leader; -+ -+ struct arch_process_data arch; -+}; -+ -+/* Initialize a process given a path to binary FILENAME, with a PID, -+ * and add the process to an internal chain of traced processes. */ -+int process_init(struct Process *proc, const char *filename, pid_t pid); -+ -+/* PROC underwent an exec. This is a bit like process_destroy -+ * followed by process_init, except that some state is kept and the -+ * process doesn't lose it's place in the list of processes. */ -+int process_exec(struct Process *proc); -+ -+/* Release any memory allocated for PROC (but not PROC itself). Does -+ * NOT remove PROC from internal chain. -+ * -+ * XXX clearly this init/destroy pair is different than others and -+ * should be fixed. process_init should presumably be separate from -+ * process_add. */ -+void process_destroy(struct Process *proc); -+ -+struct Process *open_program(const char *filename, pid_t pid); -+void open_pid(pid_t pid); -+Process * pid2proc(pid_t pid); -+ -+/* Clone the contents of PROC into the memory referenced by RETP. -+ * 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. */ -+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). */ -+Process *each_task(struct Process *proc, struct Process *start_after, -+ enum callback_status (*cb)(struct Process *proc, -+ void *data), -+ void *data); -+ -+void change_process_leader(Process *proc, Process *leader); -+ -+/* Remove process from the list of traced processes, drop any events -+ * in the event queue, destroy it and free memory. */ -+void remove_process(struct Process *proc); -+ -+void install_event_handler(Process *proc, struct event_handler *handler); -+void destroy_event_handler(Process *proc); -+ -+/* Add a library LIB to the list of PROC's libraries. */ -+void proc_add_library(struct Process *proc, struct library *lib); -+ -+/* Remove LIB from list of PROC's libraries. Returns 0 if the library -+ * 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. */ -+struct library *proc_each_library(struct Process *proc, struct library *start, -+ enum callback_status (*cb)(struct Process *p, -+ struct library *l, -+ void *data), -+ void *data); -+ -+/* Insert BP into PROC. */ -+int proc_add_breakpoint(struct Process *proc, struct breakpoint *bp); -+ -+/* Remove BP from PROC. This has no reason to fail in runtime. If it -+ * 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. */ -+void *proc_each_breakpoint(struct Process *proc, void *start, -+ enum callback_status (*cb)(struct Process *proc, -+ struct breakpoint *bp, -+ void *data), -+ void *data); -+ -+#endif /* _PROC_H_ */ -diff --git a/sysdeps/linux-gnu/Makefile.am b/sysdeps/linux-gnu/Makefile.am -index bd52092..e6fd7ef 100644 ---- a/sysdeps/linux-gnu/Makefile.am -+++ b/sysdeps/linux-gnu/Makefile.am -@@ -28,7 +28,8 @@ ___libos_la_LIBADD = \ - noinst_HEADERS = \ - arch_syscallent.h \ - signalent1.h \ -- syscallent1.h -+ syscallent1.h \ -+ trace.h - - EXTRA_DIST = \ - arch_mksyscallent \ -diff --git a/sysdeps/linux-gnu/alpha/plt.c b/sysdeps/linux-gnu/alpha/plt.c -index 83337b2..8ef456e 100644 ---- a/sysdeps/linux-gnu/alpha/plt.c -+++ b/sysdeps/linux-gnu/alpha/plt.c -@@ -1,4 +1,5 @@ - #include -+#include "proc.h" - #include "common.h" - - GElf_Addr -diff --git a/sysdeps/linux-gnu/alpha/regs.c b/sysdeps/linux-gnu/alpha/regs.c -index 9554e48..3c02a5d 100644 ---- a/sysdeps/linux-gnu/alpha/regs.c -+++ b/sysdeps/linux-gnu/alpha/regs.c -@@ -4,6 +4,7 @@ - #include - #include - -+#include "proc.h" - #include "common.h" - - #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) -diff --git a/sysdeps/linux-gnu/alpha/trace.c b/sysdeps/linux-gnu/alpha/trace.c -index e4d4063..18fe395 100644 ---- a/sysdeps/linux-gnu/alpha/trace.c -+++ b/sysdeps/linux-gnu/alpha/trace.c -@@ -6,6 +6,7 @@ - #include - #include - -+#include "proc.h" - #include "common.h" - #include "debug.h" - -diff --git a/sysdeps/linux-gnu/arm/arch.h b/sysdeps/linux-gnu/arm/arch.h -index 8f2dfb3..d50e439 100644 ---- a/sysdeps/linux-gnu/arm/arch.h -+++ b/sysdeps/linux-gnu/arm/arch.h -@@ -9,3 +9,8 @@ - - #define LT_ELFCLASS ELFCLASS32 - #define LT_ELF_MACHINE EM_ARM -+ -+#define ARCH_HAVE_BREAKPOINT_DATA -+struct arch_breakpoint_data { -+ int thumb_mode; -+}; -diff --git a/sysdeps/linux-gnu/arm/breakpoint.c b/sysdeps/linux-gnu/arm/breakpoint.c -index 493f973..324ff07 100644 ---- a/sysdeps/linux-gnu/arm/breakpoint.c -+++ b/sysdeps/linux-gnu/arm/breakpoint.c -@@ -82,3 +82,29 @@ arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) - ptrace(PTRACE_POKETEXT, pid, sbp->addr + i * sizeof(long), a); - } - } -+ -+int -+arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp) -+{ -+ /* XXX That uintptr_t cast is there temporarily until -+ * target_address_t becomes integral type. */ -+ int thumb_mode = ((uintptr_t)sbp->addr) & 1; -+ if (thumb_mode) -+ sbp->addr = (void *)((uintptr_t)sbp->addr & ~1); -+ sbp->arch.thumb_mode = thumb_mode | proc->thumb_mode; -+ /* XXX This doesn't seem like it belongs here. */ -+ proc->thumb_mode = 0; -+ return 0; -+} -+ -+void -+arch_breakpoint_destroy(struct breakpoint *sbp) -+{ -+} -+ -+int -+arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp) -+{ -+ retp->arch.thumb_mode = sbp->arch.thumb_mode; -+ return 0; -+} -diff --git a/sysdeps/linux-gnu/arm/plt.c b/sysdeps/linux-gnu/arm/plt.c -index 76f4f4c..fb98d7b 100644 ---- a/sysdeps/linux-gnu/arm/plt.c -+++ b/sysdeps/linux-gnu/arm/plt.c -@@ -1,4 +1,5 @@ - #include -+#include "proc.h" - #include "common.h" - - static int -diff --git a/sysdeps/linux-gnu/arm/regs.c b/sysdeps/linux-gnu/arm/regs.c -index b8aed6e..22bc4bf 100644 ---- a/sysdeps/linux-gnu/arm/regs.c -+++ b/sysdeps/linux-gnu/arm/regs.c -@@ -4,6 +4,7 @@ - #include - #include - -+#include "proc.h" - #include "common.h" - - #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) -@@ -39,9 +40,15 @@ void * - get_return_addr(Process *proc, void *stack_pointer) { - long addr = ptrace(PTRACE_PEEKUSER, proc->pid, off_lr, 0); - -+ /* Remember & unset the thumb mode bit. XXX This is really a -+ * bit of a hack, as we assume that the following -+ * insert_breakpoint call will be related to this address. -+ * This interface should really be get_return_breakpoint, or -+ * maybe install_return_breakpoint. */ - proc->thumb_mode = addr & 1; - if (proc->thumb_mode) - addr &= ~1; -+ - return (void *)addr; - } - -diff --git a/sysdeps/linux-gnu/arm/trace.c b/sysdeps/linux-gnu/arm/trace.c -index 39b8264..f465b72 100644 ---- a/sysdeps/linux-gnu/arm/trace.c -+++ b/sysdeps/linux-gnu/arm/trace.c -@@ -7,6 +7,7 @@ - #include - #include - -+#include "proc.h" - #include "common.h" - #include "output.h" - #include "ptrace.h" -diff --git a/sysdeps/linux-gnu/breakpoint.c b/sysdeps/linux-gnu/breakpoint.c -index b98374b..e05e730 100644 ---- a/sysdeps/linux-gnu/breakpoint.c -+++ b/sysdeps/linux-gnu/breakpoint.c -@@ -1,11 +1,15 @@ - #include "config.h" - - #include -+#include - #include -+#include - - #include "common.h" --#include "arch.h" -+#include "sysdep.h" - #include "breakpoint.h" -+#include "proc.h" -+#include "library.h" - - #ifdef ARCH_HAVE_ENABLE_BREAKPOINT - extern void arch_enable_breakpoint(pid_t, struct breakpoint *); -@@ -16,15 +20,20 @@ arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp) - static unsigned char break_insn[] = BREAKPOINT_VALUE; - unsigned int i, j; - -- if (sbp->libsym) { -- debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p, symbol=%s", pid, sbp->addr, sbp->libsym->name); -- } else { -- debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p", pid, sbp->addr); -- } -+ debug(DEBUG_PROCESS, -+ "arch_enable_breakpoint: pid=%d, addr=%p, symbol=%s", -+ pid, sbp->addr, breakpoint_name(sbp)); - - for (i = 0; i < 1 + ((BREAKPOINT_LENGTH - 1) / sizeof(long)); i++) { - long a = ptrace(PTRACE_PEEKTEXT, pid, - sbp->addr + i * sizeof(long), 0); -+ if (a == -1 && errno) { -+ fprintf(stderr, "enable_breakpoint" -+ " pid=%d, addr=%p, symbol=%s: %s\n", -+ pid, sbp->addr, breakpoint_name(sbp), -+ strerror(errno)); -+ return; -+ } - for (j = 0; - j < sizeof(long) - && i * sizeof(long) + j < BREAKPOINT_LENGTH; j++) { -@@ -33,7 +42,15 @@ arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp) - sbp->orig_value[i * sizeof(long) + j] = bytes[j]; - bytes[j] = break_insn[i * sizeof(long) + j]; - } -- ptrace(PTRACE_POKETEXT, pid, sbp->addr + i * sizeof(long), a); -+ a = ptrace(PTRACE_POKETEXT, pid, -+ sbp->addr + i * sizeof(long), a); -+ if (a == -1) { -+ fprintf(stderr, "enable_breakpoint" -+ " pid=%d, addr=%p, symbol=%s: %s\n", -+ pid, sbp->addr, breakpoint_name(sbp), -+ strerror(errno)); -+ return; -+ } - } - } - #endif /* ARCH_HAVE_ENABLE_BREAKPOINT */ -@@ -41,11 +58,8 @@ arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp) - void - enable_breakpoint(Process *proc, struct breakpoint *sbp) - { -- if (sbp->libsym) { -- debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p, symbol=%s", proc->pid, sbp->addr, sbp->libsym->name); -- } else { -- debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p", proc->pid, sbp->addr); -- } -+ debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p, symbol=%s", -+ proc->pid, sbp->addr, breakpoint_name(sbp)); - arch_enable_breakpoint(proc->pid, sbp); - } - -@@ -57,16 +71,19 @@ arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) - { - unsigned int i, j; - -- if (sbp->libsym) { -- debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p, symbol=%s", pid, sbp->addr, sbp->libsym->name); -- } else { -- debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p", pid, sbp->addr); -- } -+ debug(DEBUG_PROCESS, -+ "arch_disable_breakpoint: pid=%d, addr=%p, symbol=%s", -+ pid, sbp->addr, breakpoint_name(sbp)); - - for (i = 0; i < 1 + ((BREAKPOINT_LENGTH - 1) / sizeof(long)); i++) { -- long a = -- ptrace(PTRACE_PEEKTEXT, pid, sbp->addr + i * sizeof(long), -- 0); -+ long a = ptrace(PTRACE_PEEKTEXT, pid, -+ sbp->addr + i * sizeof(long), 0); -+ if (a == -1 && errno) { -+ fprintf(stderr, -+ "disable_breakpoint pid=%d, addr=%p: %s\n", -+ pid, sbp->addr, strerror(errno)); -+ return; -+ } - for (j = 0; - j < sizeof(long) - && i * sizeof(long) + j < BREAKPOINT_LENGTH; j++) { -@@ -74,7 +91,14 @@ arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) - - bytes[j] = sbp->orig_value[i * sizeof(long) + j]; - } -- ptrace(PTRACE_POKETEXT, pid, sbp->addr + i * sizeof(long), a); -+ a = ptrace(PTRACE_POKETEXT, pid, -+ sbp->addr + i * sizeof(long), a); -+ if (a == -1 && errno) { -+ fprintf(stderr, -+ "disable_breakpoint pid=%d, addr=%p: %s\n", -+ pid, sbp->addr, strerror(errno)); -+ return; -+ } - } - } - #endif /* ARCH_HAVE_DISABLE_BREAKPOINT */ -@@ -82,10 +106,7 @@ arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) - void - disable_breakpoint(Process *proc, struct breakpoint *sbp) - { -- if (sbp->libsym) { -- debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p, symbol=%s", proc->pid, sbp->addr, sbp->libsym->name); -- } else { -- debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p", proc->pid, sbp->addr); -- } -+ debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p, symbol=%s", -+ proc->pid, sbp->addr, breakpoint_name(sbp)); - arch_disable_breakpoint(proc->pid, sbp); - } -diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c -index 0167049..91d873e 100644 ---- a/sysdeps/linux-gnu/events.c -+++ b/sysdeps/linux-gnu/events.c -@@ -13,6 +13,7 @@ - - #include "common.h" - #include "breakpoint.h" -+#include "proc.h" - - static Event event; - -@@ -21,10 +22,10 @@ static Event event; - static Event * delayed_events = NULL; - static Event * end_delayed_events = NULL; - --static enum pcb_status -+static enum callback_status - first (Process * proc, void * data) - { -- return pcb_stop; -+ return CBS_STOP; - } - - void -@@ -174,14 +175,6 @@ next_event(void) - get_arch_dep(event.proc); - debug(3, "event from pid %u", pid); - Process *leader = event.proc->leader; -- if (leader == event.proc) { -- if (!event.proc->libdl_hooked) { -- /* debug struct may not have been written yet.. */ -- if (linkmap_init(event.proc, &main_lte) == 0) { -- event.proc->libdl_hooked = 1; -- } -- } -- } - - /* The process should be stopped after the waitpid call. But - * when the whole thread group is terminated, we see -diff --git a/sysdeps/linux-gnu/i386/plt.c b/sysdeps/linux-gnu/i386/plt.c -index b53ff44..daaf15a 100644 ---- a/sysdeps/linux-gnu/i386/plt.c -+++ b/sysdeps/linux-gnu/i386/plt.c -@@ -1,12 +1,16 @@ - #include --#include "common.h" -+#include "proc.h" -+#include "library.h" -+#include "ltrace-elf.h" - - GElf_Addr --arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { -+arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) -+{ - return lte->plt_addr + (ndx + 1) * 16; - } - - void * --sym2addr(Process *proc, struct library_symbol *sym) { -+sym2addr(struct Process *proc, struct library_symbol *sym) -+{ - return sym->enter_addr; - } -diff --git a/sysdeps/linux-gnu/i386/regs.c b/sysdeps/linux-gnu/i386/regs.c -index 6777f17..a1584ac 100644 ---- a/sysdeps/linux-gnu/i386/regs.c -+++ b/sysdeps/linux-gnu/i386/regs.c -@@ -4,7 +4,7 @@ - #include - #include - --#include "common.h" -+#include "proc.h" - - #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) - # define PTRACE_PEEKUSER PTRACE_PEEKUSR -diff --git a/sysdeps/linux-gnu/i386/trace.c b/sysdeps/linux-gnu/i386/trace.c -index 76f1105..f0c1e50 100644 ---- a/sysdeps/linux-gnu/i386/trace.c -+++ b/sysdeps/linux-gnu/i386/trace.c -@@ -1,12 +1,14 @@ - #include "config.h" - --#include -+#include - #include - #include --#include --#include - #include -+#include -+#include -+#include - -+#include "proc.h" - #include "common.h" - - #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) -@@ -24,20 +26,32 @@ get_arch_dep(Process *proc) { - /* Returns 1 if syscall, 2 if sysret, 0 otherwise. - */ - int --syscall_p(Process *proc, int status, int *sysnum) { -+syscall_p(struct Process *proc, int status, int *sysnum) -+{ - if (WIFSTOPPED(status) - && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { -+ struct callstack_element *elem = NULL; -+ if (proc->callstack_depth > 0) -+ elem = proc->callstack + proc->callstack_depth - 1; -+ - *sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, 4 * ORIG_EAX, 0); -+ if (*sysnum == -1) { -+ if (errno) -+ return -1; -+ /* Otherwise, ORIG_EAX == -1 means that the -+ * system call should not be restarted. In -+ * that case rely on what we have on -+ * stack. */ -+ if (elem != NULL && elem->is_syscall) -+ *sysnum = elem->c_un.syscall; -+ } - -- if (proc->callstack_depth > 0 && -- proc->callstack[proc->callstack_depth - 1].is_syscall && -- proc->callstack[proc->callstack_depth - 1].c_un.syscall == *sysnum) { -+ if (elem != NULL && elem->is_syscall -+ && elem->c_un.syscall == *sysnum) - return 2; -- } - -- if (*sysnum >= 0) { -+ if (*sysnum >= 0) - return 1; -- } - } - return 0; - } -diff --git a/sysdeps/linux-gnu/ia64/plt.c b/sysdeps/linux-gnu/ia64/plt.c -index 7fd451b..323df65 100644 ---- a/sysdeps/linux-gnu/ia64/plt.c -+++ b/sysdeps/linux-gnu/ia64/plt.c -@@ -1,4 +1,5 @@ - #include -+#include "proc.h" - #include "common.h" - - /* A bundle is 128 bits */ -diff --git a/sysdeps/linux-gnu/ia64/regs.c b/sysdeps/linux-gnu/ia64/regs.c -index 3f5d951..64c0164 100644 ---- a/sysdeps/linux-gnu/ia64/regs.c -+++ b/sysdeps/linux-gnu/ia64/regs.c -@@ -8,6 +8,7 @@ - #include - - #include -+#include "proc.h" - #include "common.h" - - void * -diff --git a/sysdeps/linux-gnu/ia64/trace.c b/sysdeps/linux-gnu/ia64/trace.c -index 079ed55..385fac1 100644 ---- a/sysdeps/linux-gnu/ia64/trace.c -+++ b/sysdeps/linux-gnu/ia64/trace.c -@@ -11,6 +11,7 @@ - #include - #include - -+#include "proc.h" - #include "common.h" - - /* What we think of as a bundle, ptrace thinks of it as two unsigned -diff --git a/sysdeps/linux-gnu/m68k/plt.c b/sysdeps/linux-gnu/m68k/plt.c -index 508d7fc..a1c2604 100644 ---- a/sysdeps/linux-gnu/m68k/plt.c -+++ b/sysdeps/linux-gnu/m68k/plt.c -@@ -1,4 +1,5 @@ - #include -+#include "proc.h" - #include "common.h" - - GElf_Addr -diff --git a/sysdeps/linux-gnu/m68k/regs.c b/sysdeps/linux-gnu/m68k/regs.c -index 959a60e..1542b5a 100644 ---- a/sysdeps/linux-gnu/m68k/regs.c -+++ b/sysdeps/linux-gnu/m68k/regs.c -@@ -4,6 +4,7 @@ - #include - #include - -+#include "proc.h" - #include "common.h" - - #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) -diff --git a/sysdeps/linux-gnu/m68k/trace.c b/sysdeps/linux-gnu/m68k/trace.c -index 2f89fdf..c63702d 100644 ---- a/sysdeps/linux-gnu/m68k/trace.c -+++ b/sysdeps/linux-gnu/m68k/trace.c -@@ -6,6 +6,7 @@ - #include - #include - -+#include "proc.h" - #include "common.h" - - #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) -diff --git a/sysdeps/linux-gnu/mipsel/arch.h b/sysdeps/linux-gnu/mipsel/arch.h -index dd0ca35..f7e2316 100644 ---- a/sysdeps/linux-gnu/mipsel/arch.h -+++ b/sysdeps/linux-gnu/mipsel/arch.h -@@ -1,3 +1,8 @@ -+#ifndef LTRACE_MIPS_ARCH_H -+#define LTRACE_MIPS_ARCH_H -+ -+#include -+ - #define BREAKPOINT_VALUE { 0x0d, 0x00, 0x00, 0x00 } - #define BREAKPOINT_LENGTH 4 - #define DECR_PC_AFTER_BREAK 0 -@@ -7,3 +12,12 @@ - - #define PLTs_INIT_BY_HERE "_start" - #define E_ENTRY_NAME "_start" -+ -+#define ARCH_HAVE_LTELF_DATA -+struct arch_ltelf_data { -+ size_t pltgot_addr; -+ size_t mips_local_gotno; -+ size_t mips_gotsym; -+}; -+ -+#endif /* LTRACE_MIPS_ARCH_H */ -diff --git a/sysdeps/linux-gnu/mipsel/plt.c b/sysdeps/linux-gnu/mipsel/plt.c -index 57dfb9a..3ffaddf 100644 ---- a/sysdeps/linux-gnu/mipsel/plt.c -+++ b/sysdeps/linux-gnu/mipsel/plt.c -@@ -1,6 +1,8 @@ - #include "debug.h" - #include - #include -+#include -+#include "proc.h" - #include "common.h" - - /** -@@ -69,4 +71,62 @@ sym2addr(Process *proc, struct library_symbol *sym) { - return (void *)ret;; - } - -+/** -+ MIPS ABI Supplement: -+ -+ DT_PLTGOT This member holds the address of the .got section. -+ -+ DT_MIPS_SYMTABNO This member holds the number of entries in the -+ .dynsym section. -+ -+ DT_MIPS_LOCAL_GOTNO This member holds the number of local global -+ offset table entries. -+ -+ DT_MIPS_GOTSYM This member holds the index of the first dyamic -+ symbol table entry that corresponds to an entry in the gobal offset -+ table. -+ -+ */ -+int -+arch_elf_init(struct ltelf *lte) -+{ -+ Elf_Scn *scn; -+ GElf_Shdr shdr; -+ if (elf_get_section_type(lte, SHT_DYNAMIC, &scn, &shdr) < 0 -+ || scn == NULL) { -+ fail: -+ error(0, 0, "Couldn't get SHT_DYNAMIC: %s", -+ elf_errmsg(-1)); -+ return -1; -+ } -+ -+ Elf_Data *data = elf_loaddata(scn, &shdr); -+ if (data == NULL) -+ goto fail; -+ -+ size_t j; -+ for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) { -+ GElf_Dyn dyn; -+ if (gelf_getdyn(data, j, &dyn) == NULL) -+ goto fail; -+ -+ if(dyn.d_tag == DT_PLTGOT) { -+ lte->arch.pltgot_addr = dyn.d_un.d_ptr; -+ } -+ if(dyn.d_tag == DT_MIPS_LOCAL_GOTNO){ -+ lte->arch.mips_local_gotno = dyn.d_un.d_val; -+ } -+ if(dyn.d_tag == DT_MIPS_GOTSYM){ -+ lte->arch.mips_gotsym = dyn.d_un.d_val; -+ } -+ } -+ -+ return 0; -+} -+ -+void -+arch_elf_destroy(struct ltelf *lte) -+{ -+} -+ - /**@}*/ -diff --git a/sysdeps/linux-gnu/mipsel/regs.c b/sysdeps/linux-gnu/mipsel/regs.c -index badbb10..a8a9b10 100644 ---- a/sysdeps/linux-gnu/mipsel/regs.c -+++ b/sysdeps/linux-gnu/mipsel/regs.c -@@ -5,6 +5,7 @@ - #include - #include - -+#include "proc.h" - #include "common.h" - #include "mipsel.h" - -diff --git a/sysdeps/linux-gnu/mipsel/trace.c b/sysdeps/linux-gnu/mipsel/trace.c -index 6553967..4b999e4 100644 ---- a/sysdeps/linux-gnu/mipsel/trace.c -+++ b/sysdeps/linux-gnu/mipsel/trace.c -@@ -6,6 +6,7 @@ - #include - #include - #include "debug.h" -+#include "proc.h" - #include "common.h" - #include "mipsel.h" - #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) -diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h -index 64c1821..6e258e7 100644 ---- a/sysdeps/linux-gnu/ppc/arch.h -+++ b/sysdeps/linux-gnu/ppc/arch.h -@@ -1,3 +1,8 @@ -+#ifndef LTRACE_PPC_ARCH_H -+#define LTRACE_PPC_ARCH_H -+ -+#include -+ - #define BREAKPOINT_VALUE { 0x7f, 0xe0, 0x00, 0x08 } - #define BREAKPOINT_LENGTH 4 - #define DECR_PC_AFTER_BREAK 0 -@@ -13,13 +18,68 @@ - - #define PLT_REINITALISATION_BP "_start" - --/* Start of arch-specific functions. */ - #define ARCH_ENDIAN_BIG - #define ARCH_HAVE_ATOMIC_SINGLESTEP -+#define ARCH_HAVE_ADD_PLT_ENTRY -+#define ARCH_HAVE_TRANSLATE_ADDRESS -+#define ARCH_HAVE_DYNLINK_DONE - --#define PPC_NOP { 0x60, 0x00, 0x00, 0x00 } --#define PPC_NOP_LENGTH 4 -+struct library_symbol; - --#if (PPC_NOP_LENGTH != BREAKPOINT_LENGTH) --#error "Length of the breakpoint value not equal to the length of a nop instruction" --#endif -+#define ARCH_HAVE_LTELF_DATA -+struct arch_ltelf_data { -+ GElf_Addr plt_stub_vma; -+ struct library_symbol *stubs; -+ Elf_Data *opd_data; -+ GElf_Addr opd_base; -+ GElf_Xword opd_size; -+ int secure_plt; -+}; -+ -+#define ARCH_HAVE_LIBRARY_DATA -+struct arch_library_data { -+ int bss_plt_prelinked; -+}; -+ -+enum ppc64_plt_type { -+ /* Either a non-PLT symbol, or PPC32 symbol. */ -+ PPC_DEFAULT = 0, -+ -+ /* PPC64 STUB, never resolved. */ -+ PPC64_PLT_STUB, -+ -+ /* Unresolved PLT symbol (.plt contains PLT address). */ -+ PPC_PLT_UNRESOLVED, -+ -+ /* Resolved PLT symbol. The corresponding .plt slot contained -+ * target address, which was changed to the address of -+ * corresponding PLT entry. The original is now saved in -+ * RESOLVED_VALUE. */ -+ PPC_PLT_RESOLVED, -+}; -+ -+#define ARCH_HAVE_LIBRARY_SYMBOL_DATA -+struct arch_library_symbol_data { -+ enum ppc64_plt_type type; -+ GElf_Addr resolved_value; -+ -+ /* Address of corresponding slot in .plt. */ -+ GElf_Addr plt_slot_addr; -+}; -+ -+#define ARCH_HAVE_BREAKPOINT_DATA -+struct arch_breakpoint_data { -+ /* We need this just for arch_breakpoint_init. */ -+}; -+ -+#define ARCH_HAVE_PROCESS_DATA -+struct arch_process_data { -+ /* Breakpoint that hits when the dynamic linker is about to -+ * update a .plt slot. NULL before that address is known. */ -+ struct breakpoint *dl_plt_update_bp; -+ -+ /* PLT update breakpoint looks here for the handler. */ -+ struct process_stopping_handler *handler; -+}; -+ -+#endif /* LTRACE_PPC_ARCH_H */ -diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c -index 668f63d..3b6a25f 100644 ---- a/sysdeps/linux-gnu/ppc/plt.c -+++ b/sysdeps/linux-gnu/ppc/plt.c -@@ -1,55 +1,986 @@ - #include - #include -+#include -+#include -+#include -+#include -+#include -+ -+#include "proc.h" - #include "common.h" -+#include "library.h" -+#include "breakpoint.h" -+#include "linux-gnu/trace.h" -+ -+/* There are two PLT types on 32-bit PPC: old-style, BSS PLT, and -+ * new-style "secure" PLT. We can tell one from the other by the -+ * flags on the .plt section. If it's +X (executable), it's BSS PLT, -+ * otherwise it's secure. -+ * -+ * BSS PLT works the same way as most architectures: the .plt section -+ * contains trampolines and we put breakpoints to those. If not -+ * prelinked, .plt contains zeroes, and dynamic linker fills in the -+ * initial set of trampolines, which means that we need to delay -+ * enabling breakpoints until after binary entry point is hit. -+ * Additionally, after first call, dynamic linker updates .plt with -+ * branch to resolved address. That means that on first hit, we must -+ * do something similar to the PPC64 gambit described below. -+ * -+ * With secure PLT, the .plt section doesn't contain instructions but -+ * addresses. The real PLT table is stored in .text. Addresses of -+ * those PLT entries can be computed, and apart from the fact that -+ * they are in .text, they are ordinary PLT entries. -+ * -+ * 64-bit PPC is more involved. Program linker creates for each -+ * library call a _stub_ symbol named xxxxxxxx.plt_call. -+ * (where xxxxxxxx is a hexadecimal number). That stub does the call -+ * dispatch: it loads an address of a function to call from the -+ * section .plt, and branches. PLT entries themselves are essentially -+ * a curried call to the resolver. When the symbol is resolved, the -+ * resolver updates the value stored in .plt, and the next time -+ * around, the stub calls the library function directly. So we make -+ * at most one trip (none if the binary is prelinked) through each PLT -+ * entry, and correspondingly that is useless as a breakpoint site. -+ * -+ * Note the three confusing terms: stubs (that play the role of PLT -+ * entries), PLT entries, .plt section. -+ * -+ * We first check symbol tables and see if we happen to have stub -+ * symbols available. If yes we just put breakpoints to those, and -+ * treat them as usual breakpoints. The only tricky part is realizing -+ * that there can be more than one breakpoint per symbol. -+ * -+ * The case that we don't have the stub symbols available is harder. -+ * The following scheme uses two kinds of PLT breakpoints: unresolved -+ * and resolved (to some address). When the process starts (or when -+ * we attach), we distribute unresolved PLT breakpoints to the PLT -+ * entries (not stubs). Then we look in .plt, and for each entry -+ * whose value is different than the corresponding PLT entry address, -+ * we assume it was already resolved, and convert the breakpoint to -+ * resolved. We also rewrite the resolved value in .plt back to the -+ * PLT address. -+ * -+ * When a PLT entry hits a resolved breakpoint (which happens because -+ * we rewrite .plt with the original unresolved addresses), we move -+ * the instruction pointer to the corresponding address and continue -+ * the process as if nothing happened. -+ * -+ * When unresolved PLT entry is called for the first time, we need to -+ * catch the new value that the resolver will write to a .plt slot. -+ * We also need to prevent another thread from racing through and -+ * taking the branch without ltrace noticing. So when unresolved PLT -+ * entry hits, we have to stop all threads. We then single-step -+ * through the resolver, until the .plt slot changes. When it does, -+ * we treat it the same way as above: convert the PLT breakpoint to -+ * resolved, and rewrite the .plt value back to PLT address. We then -+ * start all threads again. -+ * -+ * As an optimization, we remember the address where the address was -+ * resolved, and put a breakpoint there. The next time around (when -+ * the next PLT entry is to be resolved), instead of single-stepping -+ * through half the dynamic linker, we just let the thread run and hit -+ * this breakpoint. When it hits, we know the PLT entry was resolved. -+ * -+ * XXX TODO If we have hardware watch point, we might put a read watch -+ * on .plt slot, and discover the offenders this way. I don't know -+ * the details, but I assume at most a handful (like, one or two, if -+ * available at all) addresses may be watched at a time, and thus this -+ * would be used as an amendment of the above rather than full-on -+ * solution to PLT tracing on PPC. -+ */ -+ -+#define PPC_PLT_STUB_SIZE 16 -+#define PPC64_PLT_STUB_SIZE 8 //xxx -+ -+static inline int -+host_powerpc64() -+{ -+#ifdef __powerpc64__ -+ return 1; -+#else -+ return 0; -+#endif -+} -+ -+int -+read_target_4(struct Process *proc, target_address_t addr, uint32_t *lp) -+{ -+ unsigned long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); -+ if (l == -1UL && errno) -+ return -1; -+#ifdef __powerpc64__ -+ l >>= 32; -+#endif -+ *lp = l; -+ return 0; -+} -+ -+static int -+read_target_8(struct Process *proc, target_address_t addr, uint64_t *lp) -+{ -+ unsigned long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); -+ if (l == -1UL && errno) -+ return -1; -+ if (host_powerpc64()) { -+ *lp = l; -+ } else { -+ unsigned long l2 = ptrace(PTRACE_PEEKTEXT, proc->pid, -+ addr + 4, 0); -+ if (l2 == -1UL && errno) -+ return -1; -+ *lp = ((uint64_t)l << 32) | l2; -+ } -+ return 0; -+} -+ -+int -+read_target_long(struct Process *proc, target_address_t addr, uint64_t *lp) -+{ -+ if (proc->e_machine == EM_PPC) { -+ uint32_t w; -+ int ret = read_target_4(proc, addr, &w); -+ if (ret >= 0) -+ *lp = (uint64_t)w; -+ return ret; -+ } else { -+ return read_target_8(proc, addr, lp); -+ } -+} -+ -+static enum callback_status -+reenable_breakpoint(struct Process *proc, struct breakpoint *bp, void *data) -+{ -+ /* We don't need to re-enable non-PLT breakpoints and -+ * breakpoints that are not PPC32 BSS unprelinked. */ -+ if (bp->libsym == NULL -+ || bp->libsym->plt_type == LS_TOPLT_NONE -+ || bp->libsym->lib->arch.bss_plt_prelinked != 0) -+ return CBS_CONT; -+ -+ debug(DEBUG_PROCESS, "pid=%d reenable_breakpoint %s", -+ proc->pid, breakpoint_name(bp)); -+ -+ assert(proc->e_machine == EM_PPC); -+ uint64_t l; -+ if (read_target_8(proc, bp->addr, &l) < 0) { -+ error(0, errno, "couldn't read PLT value for %s(%p)", -+ breakpoint_name(bp), bp->addr); -+ return CBS_CONT; -+ } -+ -+ /* XXX double cast */ -+ bp->libsym->arch.plt_slot_addr = (GElf_Addr)(uintptr_t)bp->addr; -+ -+ /* If necessary, re-enable the breakpoint if it was -+ * overwritten by the dynamic linker. */ -+ union { -+ uint32_t insn; -+ char buf[4]; -+ } u = { .buf = BREAKPOINT_VALUE }; -+ if (l >> 32 == u.insn) -+ debug(DEBUG_PROCESS, "pid=%d, breakpoint still present" -+ " at %p, avoiding reenable", proc->pid, bp->addr); -+ else -+ enable_breakpoint(proc, bp); -+ -+ bp->libsym->arch.resolved_value = l; -+ -+ return CBS_CONT; -+} -+ -+void -+arch_dynlink_done(struct Process *proc) -+{ -+ /* On PPC32, .plt of objects that use BSS PLT are overwritten -+ * by the dynamic linker (unless that object was prelinked). -+ * We need to re-enable breakpoints in those objects. */ -+ proc_each_breakpoint(proc, NULL, reenable_breakpoint, NULL); -+} - - GElf_Addr --arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { -- return rela->r_offset; -+arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) -+{ -+ if (lte->ehdr.e_machine == EM_PPC && lte->arch.secure_plt) { -+ assert(lte->arch.plt_stub_vma != 0); -+ return lte->arch.plt_stub_vma + PPC_PLT_STUB_SIZE * ndx; -+ -+ } else if (lte->ehdr.e_machine == EM_PPC) { -+ return rela->r_offset; -+ -+ } else { -+ /* If we get here, we don't have stub symbols. In -+ * that case we put brakpoints to PLT entries the same -+ * as the PPC32 secure PLT case does. */ -+ assert(lte->arch.plt_stub_vma != 0); -+ return lte->arch.plt_stub_vma + PPC64_PLT_STUB_SIZE * ndx; -+ } -+} -+ -+/* This entry point is called when ltelf is not available -+ * anymore--during runtime. At that point we don't have to concern -+ * ourselves with bias, as the values in OPD have been resolved -+ * already. */ -+int -+arch_translate_address_dyn(struct Process *proc, -+ target_address_t addr, target_address_t *ret) -+{ -+ if (proc->e_machine == EM_PPC64) { -+ uint64_t value; -+ if (read_target_8(proc, addr, &value) < 0) { -+ error(0, errno, "dynamic .opd translation of %p", addr); -+ return -1; -+ } -+ *ret = (target_address_t)value; -+ return 0; -+ } -+ -+ *ret = addr; -+ return 0; -+} -+ -+int -+arch_translate_address(struct ltelf *lte, -+ target_address_t addr, target_address_t *ret) -+{ -+ if (lte->ehdr.e_machine == EM_PPC64) { -+ GElf_Xword offset = (GElf_Addr)addr - lte->arch.opd_base; -+ uint64_t value; -+ if (elf_read_u64(lte->arch.opd_data, offset, &value) < 0) { -+ error(0, 0, "static .opd translation of %p: %s", addr, -+ elf_errmsg(-1)); -+ return -1; -+ } -+ *ret = (target_address_t)(value + lte->bias); -+ return 0; -+ } -+ -+ *ret = addr; -+ return 0; -+} -+ -+static int -+load_opd_data(struct ltelf *lte, struct library *lib) -+{ -+ Elf_Scn *sec; -+ GElf_Shdr shdr; -+ if (elf_get_section_named(lte, ".opd", &sec, &shdr) < 0) { -+ fail: -+ fprintf(stderr, "couldn't find .opd data\n"); -+ return -1; -+ } -+ -+ lte->arch.opd_data = elf_rawdata(sec, NULL); -+ if (lte->arch.opd_data == NULL) -+ goto fail; -+ -+ lte->arch.opd_base = shdr.sh_addr + lte->bias; -+ lte->arch.opd_size = shdr.sh_size; -+ -+ return 0; - } - - void * --sym2addr(Process *proc, struct library_symbol *sym) { -- void *addr = sym->enter_addr; -- long pt_ret; -+sym2addr(struct Process *proc, struct library_symbol *sym) -+{ -+ return sym->enter_addr; -+} -+ -+static GElf_Addr -+get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot, Elf_Data *plt_data) -+{ -+ Elf_Scn *ppcgot_sec = NULL; -+ GElf_Shdr ppcgot_shdr; -+ if (ppcgot != 0 -+ && elf_get_section_covering(lte, ppcgot, -+ &ppcgot_sec, &ppcgot_shdr) < 0) -+ error(0, 0, "DT_PPC_GOT=%#"PRIx64", but no such section found", -+ ppcgot); -+ -+ if (ppcgot_sec != NULL) { -+ Elf_Data *data = elf_loaddata(ppcgot_sec, &ppcgot_shdr); -+ if (data == NULL || data->d_size < 8 ) { -+ error(0, 0, "couldn't read GOT data"); -+ } else { -+ // where PPCGOT begins in .got -+ size_t offset = ppcgot - ppcgot_shdr.sh_addr; -+ assert(offset % 4 == 0); -+ uint32_t glink_vma; -+ if (elf_read_u32(data, offset + 4, &glink_vma) < 0) { -+ error(0, 0, "couldn't read glink VMA address" -+ " at %zd@GOT", offset); -+ return 0; -+ } -+ if (glink_vma != 0) { -+ debug(1, "PPC GOT glink_vma address: %#" PRIx32, -+ glink_vma); -+ return (GElf_Addr)glink_vma; -+ } -+ } -+ } -+ -+ if (plt_data != NULL) { -+ uint32_t glink_vma; -+ if (elf_read_u32(plt_data, 0, &glink_vma) < 0) { -+ error(0, 0, "couldn't read glink VMA address"); -+ return 0; -+ } -+ debug(1, ".plt glink_vma address: %#" PRIx32, glink_vma); -+ return (GElf_Addr)glink_vma; -+ } -+ -+ return 0; -+} -+ -+static int -+load_dynamic_entry(struct ltelf *lte, int tag, GElf_Addr *valuep) -+{ -+ Elf_Scn *scn; -+ GElf_Shdr shdr; -+ if (elf_get_section_type(lte, SHT_DYNAMIC, &scn, &shdr) < 0 -+ || scn == NULL) { -+ fail: -+ error(0, 0, "Couldn't get SHT_DYNAMIC: %s", -+ elf_errmsg(-1)); -+ return -1; -+ } - -- debug(3, 0); -+ Elf_Data *data = elf_loaddata(scn, &shdr); -+ if (data == NULL) -+ goto fail; - -- if (sym->plt_type != LS_TOPLT_POINT) { -- return addr; -+ size_t j; -+ for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) { -+ GElf_Dyn dyn; -+ if (gelf_getdyn(data, j, &dyn) == NULL) -+ goto fail; -+ -+ if(dyn.d_tag == tag) { -+ *valuep = dyn.d_un.d_ptr; -+ return 0; -+ } - } - -- if (proc->pid == 0) { -+ return -1; -+} -+ -+static int -+load_ppcgot(struct ltelf *lte, GElf_Addr *ppcgotp) -+{ -+ return load_dynamic_entry(lte, DT_PPC_GOT, ppcgotp); -+} -+ -+static int -+load_ppc64_glink(struct ltelf *lte, GElf_Addr *glinkp) -+{ -+ return load_dynamic_entry(lte, DT_PPC64_GLINK, glinkp); -+} -+ -+static int -+nonzero_data(Elf_Data *data) -+{ -+ /* We are not supposed to get here if there's no PLT. */ -+ assert(data != NULL); -+ -+ unsigned char *buf = data->d_buf; -+ if (buf == NULL) - return 0; -+ -+ size_t i; -+ for (i = 0; i < data->d_size; ++i) -+ if (buf[i] != 0) -+ return 1; -+ return 0; -+} -+ -+int -+arch_elf_init(struct ltelf *lte, struct library *lib) -+{ -+ if (lte->ehdr.e_machine == EM_PPC64 -+ && load_opd_data(lte, lib) < 0) -+ return -1; -+ -+ lte->arch.secure_plt = !(lte->plt_flags & SHF_EXECINSTR); -+ -+ /* For PPC32 BSS, it is important whether the binary was -+ * prelinked. If .plt section is NODATA, or if it contains -+ * zeroes, then this library is not prelinked, and we need to -+ * delay breakpoints. */ -+ if (lte->ehdr.e_machine == EM_PPC && !lte->arch.secure_plt) -+ lib->arch.bss_plt_prelinked = nonzero_data(lte->plt_data); -+ else -+ /* For cases where it's irrelevant, initialize the -+ * value to something conspicuous. */ -+ lib->arch.bss_plt_prelinked = -1; -+ -+ if (lte->ehdr.e_machine == EM_PPC && lte->arch.secure_plt) { -+ GElf_Addr ppcgot; -+ if (load_ppcgot(lte, &ppcgot) < 0) { -+ error(0, 0, "couldn't find DT_PPC_GOT"); -+ return -1; -+ } -+ GElf_Addr glink_vma = get_glink_vma(lte, ppcgot, lte->plt_data); -+ -+ assert (lte->relplt_size % 12 == 0); -+ size_t count = lte->relplt_size / 12; // size of RELA entry -+ lte->arch.plt_stub_vma = glink_vma -+ - (GElf_Addr)count * PPC_PLT_STUB_SIZE; -+ debug(1, "stub_vma is %#" PRIx64, lte->arch.plt_stub_vma); -+ -+ } else if (lte->ehdr.e_machine == EM_PPC64) { -+ GElf_Addr glink_vma; -+ if (load_ppc64_glink(lte, &glink_vma) < 0) { -+ error(0, 0, "couldn't find DT_PPC64_GLINK"); -+ return -1; -+ } -+ -+ /* The first glink stub starts at offset 32. */ -+ lte->arch.plt_stub_vma = glink_vma + 32; - } - -- if (options.debug >= 3) { -- xinfdump(proc->pid, (void *)(((long)addr-32)&0xfffffff0), -- sizeof(void*)*8); -+ /* On PPC64, look for stub symbols in symbol table. These are -+ * called: xxxxxxxx.plt_call.callee_name@version+addend. */ -+ if (lte->ehdr.e_machine == EM_PPC64 -+ && lte->symtab != NULL && lte->strtab != NULL) { -+ -+ /* N.B. We can't simply skip the symbols that we fail -+ * to read or malloc. There may be more than one stub -+ * per symbol name, and if we failed in one but -+ * succeeded in another, the PLT enabling code would -+ * have no way to tell that something is missing. We -+ * could work around that, of course, but it doesn't -+ * seem worth the trouble. So if anything fails, we -+ * just pretend that we don't have stub symbols at -+ * all, as if the binary is stripped. */ -+ -+ size_t i; -+ for (i = 0; i < lte->symtab_count; ++i) { -+ GElf_Sym sym; -+ if (gelf_getsym(lte->symtab, i, &sym) == NULL) { -+ struct library_symbol *sym, *next; -+ fail: -+ for (sym = lte->arch.stubs; sym != NULL; ) { -+ next = sym->next; -+ library_symbol_destroy(sym); -+ free(sym); -+ sym = next; -+ } -+ lte->arch.stubs = NULL; -+ break; -+ } -+ -+ const char *name = lte->strtab + sym.st_name; -+ -+#define STUBN ".plt_call." -+ if ((name = strstr(name, STUBN)) == NULL) -+ continue; -+ name += sizeof(STUBN) - 1; -+#undef STUBN -+ -+ size_t len; -+ const char *ver = strchr(name, '@'); -+ if (ver != NULL) { -+ len = ver - name; -+ -+ } else { -+ /* If there is "+" at all, check that -+ * the symbol name ends in "+0". */ -+ const char *add = strrchr(name, '+'); -+ if (add != NULL) { -+ assert(strcmp(add, "+0") == 0); -+ len = add - name; -+ } else { -+ len = strlen(name); -+ } -+ } -+ -+ char *sym_name = strndup(name, len); -+ struct library_symbol *libsym = malloc(sizeof(*libsym)); -+ if (sym_name == NULL || libsym == NULL) { -+ fail2: -+ free(sym_name); -+ free(libsym); -+ goto fail; -+ } -+ -+ /* XXX The double cast should be removed when -+ * target_address_t becomes integral type. */ -+ target_address_t addr = (target_address_t) -+ (uintptr_t)sym.st_value + lte->bias; -+ if (library_symbol_init(libsym, addr, sym_name, 1, -+ LS_TOPLT_EXEC) < 0) -+ goto fail2; -+ libsym->arch.type = PPC64_PLT_STUB; -+ libsym->next = lte->arch.stubs; -+ lte->arch.stubs = libsym; -+ } -+ } -+ -+ return 0; -+} -+ -+static int -+read_plt_slot_value(struct Process *proc, GElf_Addr addr, GElf_Addr *valp) -+{ -+ /* On PPC64, we read from .plt, which contains 8 byte -+ * addresses. On PPC32 we read from .plt, which contains 4 -+ * byte instructions, but the PLT is two instructions, and -+ * either can change. */ -+ uint64_t l; -+ /* XXX double cast. */ -+ if (read_target_8(proc, (target_address_t)(uintptr_t)addr, &l) < 0) { -+ error(0, errno, "ptrace .plt slot value @%#" PRIx64, addr); -+ return -1; - } - -- // On a PowerPC-64 system, a plt is three 64-bit words: the first is the -- // 64-bit address of the routine. Before the PLT has been initialized, -- // this will be 0x0. In fact, the symbol table won't have the plt's -- // address even. Ater the PLT has been initialized, but before it has -- // been resolved, the first word will be the address of the function in -- // the dynamic linker that will reslove the PLT. After the PLT is -- // resolved, this will will be the address of the routine whose symbol -- // is in the symbol table. -+ *valp = (GElf_Addr)l; -+ return 0; -+} -+ -+static int -+unresolve_plt_slot(struct Process *proc, GElf_Addr addr, GElf_Addr value) -+{ -+ /* We only modify plt_entry[0], which holds the resolved -+ * address of the routine. We keep the TOC and environment -+ * pointers intact. Hence the only adjustment that we need to -+ * do is to IP. */ -+ if (ptrace(PTRACE_POKETEXT, proc->pid, addr, value) < 0) { -+ error(0, errno, "unresolve .plt slot"); -+ return -1; -+ } -+ return 0; -+} -+ -+static void -+mark_as_resolved(struct library_symbol *libsym, GElf_Addr value) -+{ -+ libsym->arch.type = PPC_PLT_RESOLVED; -+ libsym->arch.resolved_value = value; -+} - -- // On a PowerPC-32 system, there are two types of PLTs: secure (new) and -- // non-secure (old). For the secure case, the PLT is simply a pointer -- // and we can treat it much as we do for the PowerPC-64 case. For the -- // non-secure case, the PLT is executable code and we can put the -- // break-point right in the PLT. -+enum plt_status -+arch_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 (lte->ehdr.e_machine == EM_PPC) -+ return plt_default; - -- pt_ret = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); -+ /* PPC64. If we have stubs, we return a chain of breakpoint -+ * sites, one for each stub that corresponds to this PLT -+ * entry. */ -+ struct library_symbol *chain = NULL; -+ struct library_symbol **symp; -+ for (symp = <e->arch.stubs; *symp != NULL; ) { -+ struct library_symbol *sym = *symp; -+ if (strcmp(sym->name, a_name) != 0) { -+ symp = &(*symp)->next; -+ continue; -+ } -+ -+ /* Re-chain the symbol from stubs to CHAIN. */ -+ *symp = sym->next; -+ sym->next = chain; -+ chain = sym; -+ } -+ -+ if (chain != NULL) { -+ *ret = chain; -+ return plt_ok; -+ } -+ -+ /* We don't have stub symbols. Find corresponding .plt slot, -+ * and check whether it contains the corresponding PLT address -+ * (or 0 if the dynamic linker hasn't run yet). N.B. we don't -+ * want read this from ELF file, but from process image. That -+ * makes a difference if we are attaching to a running -+ * process. */ -+ -+ GElf_Addr plt_entry_addr = arch_plt_sym_val(lte, ndx, rela); -+ GElf_Addr plt_slot_addr = rela->r_offset; -+ assert(plt_slot_addr >= lte->plt_addr -+ || plt_slot_addr < lte->plt_addr + lte->plt_size); -+ -+ GElf_Addr plt_slot_value; -+ if (read_plt_slot_value(proc, plt_slot_addr, &plt_slot_value) < 0) -+ return plt_fail; -+ -+ char *name = strdup(a_name); -+ struct library_symbol *libsym = malloc(sizeof(*libsym)); -+ if (name == NULL || libsym == NULL) { -+ error(0, errno, "allocation for .plt slot"); -+ fail: -+ free(name); -+ free(libsym); -+ return plt_fail; -+ } -+ -+ /* XXX The double cast should be removed when -+ * target_address_t becomes integral type. */ -+ if (library_symbol_init(libsym, -+ (target_address_t)(uintptr_t)plt_entry_addr, -+ name, 1, LS_TOPLT_EXEC) < 0) -+ goto fail; -+ libsym->arch.plt_slot_addr = plt_slot_addr; -+ -+ if (plt_slot_value == plt_entry_addr || plt_slot_value == 0) { -+ libsym->arch.type = PPC_PLT_UNRESOLVED; -+ libsym->arch.resolved_value = plt_entry_addr; - -- if (proc->mask_32bit) { -- // Assume big-endian. -- addr = (void *)((pt_ret >> 32) & 0xffffffff); - } else { -- addr = (void *)pt_ret; -+ /* Unresolve the .plt slot. If the binary was -+ * prelinked, this makes the code invalid, because in -+ * case of prelinked binary, the dynamic linker -+ * doesn't update .plt[0] and .plt[1] with addresses -+ * of the resover. But we don't care, we will never -+ * need to enter the resolver. That just means that -+ * we have to un-un-resolve this back before we -+ * detach. */ -+ -+ if (unresolve_plt_slot(proc, plt_slot_addr, plt_entry_addr) < 0) { -+ library_symbol_destroy(libsym); -+ goto fail; -+ } -+ mark_as_resolved(libsym, plt_slot_value); - } - -- return addr; -+ *ret = libsym; -+ return plt_ok; -+} -+ -+void -+arch_elf_destroy(struct ltelf *lte) -+{ -+ struct library_symbol *sym; -+ for (sym = lte->arch.stubs; sym != NULL; ) { -+ struct library_symbol *next = sym->next; -+ library_symbol_destroy(sym); -+ free(sym); -+ sym = next; -+ } -+} -+ -+static void -+dl_plt_update_bp_on_hit(struct breakpoint *bp, struct Process *proc) -+{ -+ debug(DEBUG_PROCESS, "pid=%d dl_plt_update_bp_on_hit %s(%p)", -+ proc->pid, breakpoint_name(bp), bp->addr); -+ struct process_stopping_handler *self = proc->arch.handler; -+ assert(self != NULL); -+ -+ struct library_symbol *libsym = self->breakpoint_being_enabled->libsym; -+ GElf_Addr value; -+ if (read_plt_slot_value(proc, libsym->arch.plt_slot_addr, &value) < 0) -+ return; -+ -+ /* On PPC64, we rewrite the slot value. */ -+ if (proc->e_machine == EM_PPC64) -+ unresolve_plt_slot(proc, libsym->arch.plt_slot_addr, -+ libsym->arch.resolved_value); -+ /* We mark the breakpoint as resolved on both arches. */ -+ mark_as_resolved(libsym, value); -+ -+ /* cb_on_all_stopped looks if HANDLER is set to NULL as a way -+ * to check that this was run. It's an error if it -+ * wasn't. */ -+ proc->arch.handler = NULL; -+ -+ breakpoint_turn_off(bp, proc); -+} -+ -+static void -+cb_on_all_stopped(struct process_stopping_handler *self) -+{ -+ /* Put that in for dl_plt_update_bp_on_hit to see. */ -+ assert(self->task_enabling_breakpoint->arch.handler == NULL); -+ self->task_enabling_breakpoint->arch.handler = self; -+ -+ linux_ptrace_disable_and_continue(self); -+} -+ -+static enum callback_status -+cb_keep_stepping_p(struct process_stopping_handler *self) -+{ -+ struct Process *proc = self->task_enabling_breakpoint; -+ struct library_symbol *libsym = self->breakpoint_being_enabled->libsym; -+ -+ GElf_Addr value; -+ if (read_plt_slot_value(proc, libsym->arch.plt_slot_addr, &value) < 0) -+ return CBS_FAIL; -+ -+ /* In UNRESOLVED state, the RESOLVED_VALUE in fact contains -+ * the PLT entry value. */ -+ if (value == libsym->arch.resolved_value) -+ return CBS_CONT; -+ -+ debug(DEBUG_PROCESS, "pid=%d PLT got resolved to value %#"PRIx64, -+ proc->pid, value); -+ -+ /* The .plt slot got resolved! We can migrate the breakpoint -+ * to RESOLVED and stop single-stepping. */ -+ if (proc->e_machine == EM_PPC64 -+ && unresolve_plt_slot(proc, libsym->arch.plt_slot_addr, -+ libsym->arch.resolved_value) < 0) -+ return CBS_FAIL; -+ -+ /* Resolving on PPC64 consists of overwriting a doubleword in -+ * .plt. That doubleword is than read back by a stub, and -+ * jumped on. Hopefully we can assume that double word update -+ * is done on a single place only, as it contains a final -+ * address. We still need to look around for any sync -+ * instruction, but essentially it is safe to optimize away -+ * the single stepping next time and install a post-update -+ * breakpoint. -+ * -+ * The situation on PPC32 BSS is more complicated. The -+ * dynamic linker here updates potentially several -+ * instructions (XXX currently we assume two) and the rules -+ * are more complicated. Sometimes it's enough to adjust just -+ * one of the addresses--the logic for generating optimal -+ * dispatch depends on relative addresses of the .plt entry -+ * and the jump destination. We can't assume that the some -+ * instruction block does the update every time. So on PPC32, -+ * we turn the optimization off and just step through it each -+ * time. */ -+ if (proc->e_machine == EM_PPC) -+ goto done; -+ -+ /* Install breakpoint to the address where the change takes -+ * place. If we fail, then that just means that we'll have to -+ * singlestep the next time around as well. */ -+ struct Process *leader = proc->leader; -+ if (leader == NULL || leader->arch.dl_plt_update_bp != NULL) -+ goto done; -+ -+ /* We need to install to the next instruction. ADDR points to -+ * a store instruction, so moving the breakpoint one -+ * instruction forward is safe. */ -+ target_address_t addr = get_instruction_pointer(proc) + 4; -+ leader->arch.dl_plt_update_bp = insert_breakpoint(proc, addr, NULL); -+ if (leader->arch.dl_plt_update_bp == NULL) -+ goto done; -+ -+ static struct bp_callbacks dl_plt_update_cbs = { -+ .on_hit = dl_plt_update_bp_on_hit, -+ }; -+ leader->arch.dl_plt_update_bp->cbs = &dl_plt_update_cbs; -+ -+ /* Turn it off for now. We will turn it on again when we hit -+ * the PLT entry that needs this. */ -+ breakpoint_turn_off(leader->arch.dl_plt_update_bp, proc); -+ -+done: -+ mark_as_resolved(libsym, value); -+ -+ return CBS_STOP; -+} -+ -+static void -+jump_to_entry_point(struct Process *proc, struct breakpoint *bp) -+{ -+ /* XXX The double cast should be removed when -+ * target_address_t becomes integral type. */ -+ target_address_t rv = (target_address_t) -+ (uintptr_t)bp->libsym->arch.resolved_value; -+ set_instruction_pointer(proc, rv); -+} -+ -+static void -+ppc_plt_bp_continue(struct breakpoint *bp, struct Process *proc) -+{ -+ switch (bp->libsym->arch.type) { -+ struct Process *leader; -+ void (*on_all_stopped)(struct process_stopping_handler *); -+ enum callback_status (*keep_stepping_p) -+ (struct process_stopping_handler *); -+ -+ case PPC_DEFAULT: -+ assert(proc->e_machine == EM_PPC); -+ assert(bp->libsym != NULL); -+ assert(bp->libsym->lib->arch.bss_plt_prelinked == 0); -+ /* fall-through */ -+ -+ case PPC_PLT_UNRESOLVED: -+ on_all_stopped = NULL; -+ keep_stepping_p = NULL; -+ leader = proc->leader; -+ -+ if (leader != NULL && leader->arch.dl_plt_update_bp != NULL -+ && breakpoint_turn_on(leader->arch.dl_plt_update_bp, -+ proc) >= 0) -+ on_all_stopped = cb_on_all_stopped; -+ else -+ keep_stepping_p = cb_keep_stepping_p; -+ -+ if (process_install_stopping_handler -+ (proc, bp, on_all_stopped, keep_stepping_p, NULL) < 0) { -+ error(0, 0, "ppc_plt_bp_continue: couldn't install" -+ " event handler"); -+ continue_after_breakpoint(proc, bp); -+ } -+ return; -+ -+ case PPC_PLT_RESOLVED: -+ if (proc->e_machine == EM_PPC) { -+ continue_after_breakpoint(proc, bp); -+ return; -+ } -+ -+ jump_to_entry_point(proc, bp); -+ continue_process(proc->pid); -+ return; -+ -+ case PPC64_PLT_STUB: -+ /* These should never hit here. */ -+ break; -+ } -+ -+ assert(bp->libsym->arch.type != bp->libsym->arch.type); -+ abort(); -+} -+ -+/* When a process is in a PLT stub, it may have already read the data -+ * in .plt that we changed. If we detach now, it will jump to PLT -+ * entry and continue to the dynamic linker, where it will SIGSEGV, -+ * because zeroth .plt slot is not filled in prelinked binaries, and -+ * the dynamic linker needs that data. Moreover, the process may -+ * actually have hit the breakpoint already. This functions tries to -+ * detect both cases and do any fix-ups necessary to mend this -+ * situation. */ -+static enum callback_status -+detach_task_cb(struct Process *task, void *data) -+{ -+ struct breakpoint *bp = data; -+ -+ if (get_instruction_pointer(task) == bp->addr) { -+ debug(DEBUG_PROCESS, "%d at %p, which is PLT slot", -+ task->pid, bp->addr); -+ jump_to_entry_point(task, bp); -+ return CBS_CONT; -+ } -+ -+ /* XXX There's still a window of several instructions where we -+ * might catch the task inside a stub such that it has already -+ * read destination address from .plt, but hasn't jumped yet, -+ * thus avoiding the breakpoint. */ -+ -+ return CBS_CONT; -+} -+ -+static void -+ppc_plt_bp_retract(struct breakpoint *bp, struct Process *proc) -+{ -+ /* On PPC64, we rewrite .plt with PLT entry addresses. This -+ * needs to be undone. Unfortunately, the program may have -+ * made decisions based on that value */ -+ if (proc->e_machine == EM_PPC64 -+ && bp->libsym != NULL -+ && bp->libsym->arch.type == PPC_PLT_RESOLVED) { -+ each_task(proc->leader, NULL, detach_task_cb, bp); -+ unresolve_plt_slot(proc, bp->libsym->arch.plt_slot_addr, -+ bp->libsym->arch.resolved_value); -+ } -+} -+ -+void -+arch_library_init(struct library *lib) -+{ -+} -+ -+void -+arch_library_destroy(struct library *lib) -+{ -+} -+ -+void -+arch_library_clone(struct library *retp, struct library *lib) -+{ -+} -+ -+int -+arch_library_symbol_init(struct library_symbol *libsym) -+{ -+ /* We set type explicitly in the code above, where we have the -+ * necessary context. This is for calls from ltrace-elf.c and -+ * such. */ -+ libsym->arch.type = PPC_DEFAULT; -+ return 0; -+} -+ -+void -+arch_library_symbol_destroy(struct library_symbol *libsym) -+{ -+} -+ -+int -+arch_library_symbol_clone(struct library_symbol *retp, -+ struct library_symbol *libsym) -+{ -+ retp->arch = libsym->arch; -+ return 0; -+} -+ -+/* For some symbol types, we need to set up custom callbacks. XXX we -+ * don't need PROC here, we can store the data in BP if it is of -+ * interest to us. */ -+int -+arch_breakpoint_init(struct Process *proc, struct breakpoint *bp) -+{ -+ /* Artificial and entry-point breakpoints are plain. */ -+ if (bp->libsym == NULL || bp->libsym->plt_type != LS_TOPLT_EXEC) -+ return 0; -+ -+ /* On PPC, secure PLT and prelinked BSS PLT are plain. */ -+ if (proc->e_machine == EM_PPC -+ && bp->libsym->lib->arch.bss_plt_prelinked != 0) -+ return 0; -+ -+ /* On PPC64, stub PLT breakpoints are plain. */ -+ if (proc->e_machine == EM_PPC64 -+ && bp->libsym->arch.type == PPC64_PLT_STUB) -+ return 0; -+ -+ static struct bp_callbacks cbs = { -+ .on_continue = ppc_plt_bp_continue, -+ .on_retract = ppc_plt_bp_retract, -+ }; -+ breakpoint_set_callbacks(bp, &cbs); -+ return 0; -+} -+ -+void -+arch_breakpoint_destroy(struct breakpoint *bp) -+{ -+} -+ -+int -+arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp) -+{ -+ retp->arch = sbp->arch; -+ return 0; -+} -+ -+int -+arch_process_init(struct Process *proc) -+{ -+ proc->arch.dl_plt_update_bp = NULL; -+ proc->arch.handler = NULL; -+ return 0; -+} -+ -+void -+arch_process_destroy(struct Process *proc) -+{ -+} -+ -+int -+arch_process_clone(struct Process *retp, struct Process *proc) -+{ -+ retp->arch = proc->arch; -+ return 0; -+} -+ -+int -+arch_process_exec(struct Process *proc) -+{ -+ return arch_process_init(proc); - } -diff --git a/sysdeps/linux-gnu/ppc/regs.c b/sysdeps/linux-gnu/ppc/regs.c -index eca58ff..26e962f 100644 ---- a/sysdeps/linux-gnu/ppc/regs.c -+++ b/sysdeps/linux-gnu/ppc/regs.c -@@ -3,7 +3,10 @@ - #include - #include - #include -+#include -+#include - -+#include "proc.h" - #include "common.h" - - #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) -@@ -20,8 +23,10 @@ get_instruction_pointer(Process *proc) { - } - - void --set_instruction_pointer(Process *proc, void *addr) { -- ptrace(PTRACE_POKEUSER, proc->pid, sizeof(long)*PT_NIP, addr); -+set_instruction_pointer(Process *proc, void *addr) -+{ -+ if (ptrace(PTRACE_POKEUSER, proc->pid, sizeof(long)*PT_NIP, addr) != 0) -+ error(0, errno, "set_instruction_pointer"); - } - - void * -diff --git a/sysdeps/linux-gnu/ppc/syscallent.h b/sysdeps/linux-gnu/ppc/syscallent.h -index 5ce5739..7537b3d 100644 ---- a/sysdeps/linux-gnu/ppc/syscallent.h -+++ b/sysdeps/linux-gnu/ppc/syscallent.h -@@ -1,4 +1,4 @@ --"0", /* 0 */ -+ "restart_syscall", /* 0 */ - "exit", /* 1 */ - "fork", /* 2 */ - "read", /* 3 */ -@@ -177,8 +177,8 @@ - "rt_sigtimedwait", /* 176 */ - "rt_sigqueueinfo", /* 177 */ - "rt_sigsuspend", /* 178 */ -- "pread", /* 179 */ -- "pwrite", /* 180 */ -+ "pread64", /* 179 */ -+ "pwrite64", /* 180 */ - "chown", /* 181 */ - "getcwd", /* 182 */ - "capget", /* 183 */ -@@ -254,6 +254,12 @@ - "fstatfs64", /* 253 */ - "fadvise64_64", /* 254 */ - "rtas", /* 255 */ -+ "sys_debug_setcontext", /* 256 */ -+ "server", /* 257 */ -+ "migrate_pages", /* 258 */ -+ "mbind", /* 259 */ -+ "get_mempolicy", /* 260 */ -+ "set_mempolicy", /* 261 */ - "mq_open", /* 262 */ - "mq_unlink", /* 263 */ - "mq_timedsend", /* 264 */ -@@ -270,3 +276,78 @@ - "inotify_init", /* 275 */ - "inotify_add_watch", /* 276 */ - "inotify_rm_watch", /* 277 */ -+ "spu_run", /* 278 */ -+ "spu_create", /* 279 */ -+ "pselect6", /* 280 */ -+ "ppoll", /* 281 */ -+ "unshare", /* 282 */ -+ "splice", /* 283 */ -+ "tee", /* 284 */ -+ "vmsplice", /* 285 */ -+ "openat", /* 286 */ -+ "mkdirat", /* 287 */ -+ "mknodat", /* 288 */ -+ "fchownat", /* 289 */ -+ "futimesat", /* 290 */ -+ "fstatat64", /* 291 */ -+ "unlinkat", /* 292 */ -+ "renameat", /* 293 */ -+ "linkat", /* 294 */ -+ "symlinkat", /* 295 */ -+ "readlinkat", /* 296 */ -+ "fchmodat", /* 297 */ -+ "faccessat", /* 298 */ -+ "get_robust_list", /* 299 */ -+ "set_robust_list", /* 300 */ -+ "move_pages", /* 301 */ -+ "getcpu", /* 302 */ -+ "epoll_pwait", /* 303 */ -+ "utimensat", /* 304 */ -+ "signalfd", /* 305 */ -+ "timerfd_create", /* 306 */ -+ "eventfd", /* 307 */ -+ "sync_file_range2", /* 308 */ -+ "fallocate", /* 309 */ -+ "subpage_prot", /* 310 */ -+ "timerfd_settime", /* 311 */ -+ "timerfd_gettime", /* 312 */ -+ "signalfd4", /* 313 */ -+ "eventfd2", /* 314 */ -+ "epoll_create1", /* 315 */ -+ "dup3", /* 316 */ -+ "pipe2", /* 317 */ -+ "inotify_init1", /* 318 */ -+ "perf_event_open", /* 319 */ -+ "preadv", /* 320 */ -+ "pwritev", /* 321 */ -+ "rt_tgsigqueueinfo", /* 322 */ -+ "fanotify_init", /* 323 */ -+ "fanotify_mark", /* 324 */ -+ "prlimit64", /* 325 */ -+ "socket", /* 326 */ -+ "bind", /* 327 */ -+ "connect", /* 328 */ -+ "listen", /* 329 */ -+ "accept", /* 330 */ -+ "getsockname", /* 331 */ -+ "getpeername", /* 332 */ -+ "socketpair", /* 333 */ -+ "send", /* 334 */ -+ "sendto", /* 335 */ -+ "recv", /* 336 */ -+ "recvfrom", /* 337 */ -+ "shutdown", /* 338 */ -+ "setsockopt", /* 339 */ -+ "getsockopt", /* 340 */ -+ "sendmsg", /* 341 */ -+ "recvmsg", /* 342 */ -+ "recvmmsg", /* 343 */ -+ "accept4", /* 344 */ -+ "name_to_handle_at", /* 345 */ -+ "open_by_handle_at", /* 346 */ -+ "clock_adjtime", /* 347 */ -+ "syncfs", /* 348 */ -+ "sendmmsg", /* 349 */ -+ "setns", /* 350 */ -+ "process_vm_readv", /* 351 */ -+ "process_writev", /* 352 */ -diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c -index 05993de..742785a 100644 ---- a/sysdeps/linux-gnu/ppc/trace.c -+++ b/sysdeps/linux-gnu/ppc/trace.c -@@ -9,6 +9,7 @@ - #include - #include - -+#include "proc.h" - #include "common.h" - #include "ptrace.h" - #include "breakpoint.h" -@@ -209,30 +211,46 @@ arch_umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { - #define STWCX_INSTRUCTION 0x7c00012d - #define STDCX_INSTRUCTION 0x7c0001ad - #define BC_MASK 0xfc000000 --#define BC_INSTRUCTION 0x40000000 -+#define BC_INSN 0x40000000 -+#define BRANCH_MASK 0xfc000000 -+ -+/* In plt.h. XXX make this official interface. */ -+int read_target_4(struct Process *proc, target_address_t addr, uint32_t *lp); - - int - arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, - int (*add_cb)(void *addr, void *data), - void *add_cb_data) - { -- void *addr = sbp->addr; -- debug(1, "pid=%d addr=%p", proc->pid, addr); -+ target_address_t ip = get_instruction_pointer(proc); -+ struct breakpoint *other = address2bpstruct(proc->leader, ip); -+ -+ debug(1, "arch_atomic_singlestep pid=%d addr=%p %s(%p)", -+ proc->pid, ip, breakpoint_name(sbp), sbp->addr); - - /* If the original instruction was lwarx/ldarx, we can't - * single-step over it, instead we have to execute the whole - * atomic block at once. */ - union { - uint32_t insn; -- char buf[4]; -+ char buf[BREAKPOINT_LENGTH]; - } u; -- memcpy(u.buf, sbp->orig_value, BREAKPOINT_LENGTH); -+ if (other != NULL) { -+ memcpy(u.buf, sbp->orig_value, BREAKPOINT_LENGTH); -+ } else if (read_target_4(proc, ip, &u.insn) < 0) { -+ fprintf(stderr, "couldn't read instruction at IP %p\n", ip); -+ /* Do the normal singlestep. */ -+ return 1; -+ } - - if ((u.insn & LWARX_MASK) != LWARX_INSTRUCTION - && (u.insn & LWARX_MASK) != LDARX_INSTRUCTION) - return 1; - -+ debug(1, "singlestep over atomic block at %p", ip); -+ - int insn_count; -+ target_address_t addr = ip; - for (insn_count = 0; ; ++insn_count) { - addr += 4; - unsigned long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); -@@ -245,27 +263,39 @@ arch_atomic_singlestep(struct Process *proc, Breakpoint *sbp, - insn = l; - #endif - -- /* If we hit a branch instruction, give up. The -- * computation could escape that way and we'd have to -- * treat that case specially. */ -- if ((insn & BC_MASK) == BC_INSTRUCTION) { -- debug(1, "pid=%d, found branch at %p, giving up", -- proc->pid, addr); -- return -1; -+ /* If a conditional branch is found, put a breakpoint -+ * in its destination address. */ -+ if ((insn & BRANCH_MASK) == BC_INSN) { -+ int immediate = ((insn & 0xfffc) ^ 0x8000) - 0x8000; -+ int absolute = insn & 2; -+ -+ /* XXX drop the following casts. */ -+ target_address_t branch_addr; -+ if (absolute) -+ branch_addr = (void *)(uintptr_t)immediate; -+ else -+ branch_addr = addr + (uintptr_t)immediate; -+ -+ debug(1, "pid=%d, branch in atomic block from %p to %p", -+ proc->pid, addr, branch_addr); -+ if (add_cb(branch_addr, add_cb_data) < 0) -+ return -1; - } - -+ /* Assume that the atomic sequence ends with a -+ * stwcx/stdcx instruction. */ - if ((insn & STWCX_MASK) == STWCX_INSTRUCTION - || (insn & STWCX_MASK) == STDCX_INSTRUCTION) { -- debug(1, "pid=%d, found end of atomic block at %p", -- proc->pid, addr); -+ debug(1, "pid=%d, found end of atomic block %p at %p", -+ proc->pid, ip, addr); - break; - } - - /* Arbitrary cut-off. If we didn't find the - * terminating instruction by now, just give up. */ - if (insn_count > 16) { -- debug(1, "pid=%d, couldn't find end of atomic block", -- proc->pid); -+ fprintf(stderr, "[%d] couldn't find end of atomic block" -+ " at %p\n", proc->pid, ip); - return -1; - } - } -diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c -index 3350117..b5123fe 100644 ---- a/sysdeps/linux-gnu/proc.c -+++ b/sysdeps/linux-gnu/proc.c -@@ -1,23 +1,24 @@ - #define _GNU_SOURCE /* For getline. */ - #include "config.h" - --#include - #include -+#include -+#include -+#include -+#include -+#include - #include - #include - #include -+#include - #include - #include --#include - #include --#include --#include --#include --#include --#include - - #include "common.h" - #include "breakpoint.h" -+#include "proc.h" -+#include "library.h" - - /* /proc/pid doesn't exist just after the fork, and sometimes `ltrace' - * couldn't open it to find the executable. So it may be necessary to -@@ -77,27 +78,28 @@ find_line_starting(FILE * file, const char * prefix, size_t len) - } - - static void --each_line_starting(FILE * file, const char *prefix, -- enum pcb_status (*cb)(const char * line, const char * prefix, -- void * data), -- void * data) -+each_line_starting(FILE *file, const char *prefix, -+ enum callback_status (*cb)(const char *line, -+ const char *prefix, -+ void *data), -+ void *data) - { - size_t len = strlen(prefix); - char * line; - while ((line = find_line_starting(file, prefix, len)) != NULL) { -- enum pcb_status st = (*cb)(line, prefix, data); -+ enum callback_status st = (*cb)(line, prefix, data); - free (line); -- if (st == pcb_stop) -+ if (st == CBS_STOP) - return; - } - } - --static enum pcb_status --process_leader_cb(const char * line, const char * prefix, void * data) -+static enum callback_status -+process_leader_cb(const char *line, const char *prefix, void *data) - { - pid_t * pidp = data; - *pidp = atoi(line + strlen(prefix)); -- return pcb_stop; -+ return CBS_STOP; - } - - pid_t -@@ -113,13 +115,13 @@ process_leader(pid_t pid) - return tgid; - } - --static enum pcb_status --process_stopped_cb(const char * line, const char * prefix, void * data) -+static enum callback_status -+process_stopped_cb(const char *line, const char *prefix, void *data) - { - char c = line[strlen(prefix)]; - // t:tracing stop, T:job control stop - *(int *)data = (c == 't' || c == 'T'); -- return pcb_stop; -+ return CBS_STOP; - } - - int -@@ -135,15 +137,15 @@ process_stopped(pid_t pid) - return is_stopped; - } - --static enum pcb_status --process_status_cb(const char * line, const char * prefix, void * data) -+static enum callback_status -+process_status_cb(const char *line, const char *prefix, void *data) - { - const char * status = line + strlen(prefix); - const char c = *status; - - #define RETURN(C) do { \ - *(enum process_status *)data = C; \ -- return pcb_stop; \ -+ return CBS_STOP; \ - } while (0) - - switch (c) { -@@ -179,7 +181,8 @@ process_status(pid_t pid) - each_line_starting(file, "State:\t", &process_status_cb, &ret); - fclose(file); - if (ret == ps_invalid) -- error(0, errno, "process_status %d", pid); -+ fprintf(stderr, "process_status %d: %s", pid, -+ strerror(errno)); - } else - /* If the file is not present, the process presumably - * exited already. */ -@@ -242,51 +245,178 @@ process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n) - return 0; - } - -+/* On native 64-bit system, we need to be careful when handling cross -+ * tracing. This select appropriate pointer depending on host and -+ * target architectures. XXX Really we should abstract this into the -+ * ABI object, as theorized about somewhere on pmachata/revamp -+ * branch. */ -+static void * -+select_32_64(struct Process *proc, void *p32, void *p64) -+{ -+ if (sizeof(long) == 4 || proc->mask_32bit) -+ return p32; -+ else -+ return p64; -+} -+ -+static int -+fetch_dyn64(struct Process *proc, target_address_t *addr, Elf64_Dyn *ret) -+{ -+ if (umovebytes(proc, *addr, ret, sizeof(*ret)) != sizeof(*ret)) -+ return -1; -+ *addr += sizeof(*ret); -+ return 0; -+} -+ - static int --find_dynamic_entry_addr(Process *proc, void *pvAddr, int d_tag, void **addr) { -- int i = 0, done = 0; -- ElfW(Dyn) entry; -+fetch_dyn32(struct Process *proc, target_address_t *addr, Elf64_Dyn *ret) -+{ -+ Elf32_Dyn dyn; -+ if (umovebytes(proc, *addr, &dyn, sizeof(dyn)) != sizeof(dyn)) -+ return -1; -+ -+ *addr += sizeof(dyn); -+ ret->d_tag = dyn.d_tag; -+ ret->d_un.d_val = dyn.d_un.d_val; -+ -+ return 0; -+} - -+static int (* -+dyn_fetcher(struct Process *proc))(struct Process *, -+ target_address_t *, Elf64_Dyn *) -+{ -+ return select_32_64(proc, fetch_dyn32, fetch_dyn64); -+} -+ -+static int -+find_dynamic_entry_addr(struct Process *proc, target_address_t src_addr, -+ int d_tag, target_address_t *ret) -+{ - debug(DEBUG_FUNCTION, "find_dynamic_entry()"); - -- if (addr == NULL || pvAddr == NULL || d_tag < 0 || d_tag > DT_NUM) { -+ if (ret == NULL || src_addr == 0 || d_tag < 0 || d_tag > DT_NUM) - return -1; -- } - -- while ((!done) && (i < ELF_MAX_SEGMENTS) && -- (sizeof(entry) == umovebytes(proc, pvAddr, &entry, sizeof(entry))) && -- (entry.d_tag != DT_NULL)) { -+ int i = 0; -+ while (1) { -+ Elf64_Dyn entry; -+ if (dyn_fetcher(proc)(proc, &src_addr, &entry) < 0 -+ || entry.d_tag == DT_NULL -+ || i++ > 100) { /* Arbitrary cut-off so that we -+ * don't loop forever if the -+ * binary is corrupted. */ -+ debug(2, "Couldn't find address for dtag!"); -+ return -1; -+ } -+ - if (entry.d_tag == d_tag) { -- done = 1; -- *addr = (void *)entry.d_un.d_val; -+ /* XXX The double cast should be removed when -+ * target_address_t becomes integral type. */ -+ *ret = (target_address_t)(uintptr_t)entry.d_un.d_val; -+ debug(2, "found address: %p in dtag %d", *ret, d_tag); -+ return 0; - } -- pvAddr += sizeof(entry); -- i++; - } -+} - -- if (done) { -- debug(2, "found address: 0x%p in dtag %d\n", *addr, d_tag); -- return 0; -+/* Our own type for representing 32-bit linkmap. We can't rely on the -+ * definition in link.h, because that's only accurate for our host -+ * architecture, not for target architecture (where the traced process -+ * runs). */ -+#define LT_LINK_MAP(BITS) \ -+ { \ -+ Elf##BITS##_Addr l_addr; \ -+ Elf##BITS##_Addr l_name; \ -+ Elf##BITS##_Addr l_ld; \ -+ Elf##BITS##_Addr l_next; \ -+ Elf##BITS##_Addr l_prev; \ - } -- else { -- debug(2, "Couldn't address for dtag!\n"); -+struct lt_link_map_32 LT_LINK_MAP(32); -+struct lt_link_map_64 LT_LINK_MAP(64); -+ -+static int -+fetch_lm64(struct Process *proc, target_address_t addr, -+ struct lt_link_map_64 *ret) -+{ -+ if (umovebytes(proc, addr, ret, sizeof(*ret)) != sizeof(*ret)) -+ return -1; -+ return 0; -+} -+ -+static int -+fetch_lm32(struct Process *proc, target_address_t addr, -+ struct lt_link_map_64 *ret) -+{ -+ struct lt_link_map_32 lm; -+ if (umovebytes(proc, addr, &lm, sizeof(lm)) != sizeof(lm)) - return -1; -+ -+ ret->l_addr = lm.l_addr; -+ ret->l_name = lm.l_name; -+ ret->l_ld = lm.l_ld; -+ ret->l_next = lm.l_next; -+ ret->l_prev = lm.l_prev; -+ -+ return 0; -+} -+ -+static int (* -+lm_fetcher(struct Process *proc))(struct Process *, -+ target_address_t, struct lt_link_map_64 *) -+{ -+ return select_32_64(proc, fetch_lm32, fetch_lm64); -+} -+ -+/* The same as above holds for struct r_debug. */ -+#define LT_R_DEBUG(BITS) \ -+ { \ -+ int r_version; \ -+ Elf##BITS##_Addr r_map; \ -+ Elf##BITS##_Addr r_brk; \ -+ int r_state; \ -+ Elf##BITS##_Addr r_ldbase; \ - } -+ -+struct lt_r_debug_32 LT_R_DEBUG(32); -+struct lt_r_debug_64 LT_R_DEBUG(64); -+ -+static int -+fetch_rd64(struct Process *proc, target_address_t addr, -+ struct lt_r_debug_64 *ret) -+{ -+ if (umovebytes(proc, addr, ret, sizeof(*ret)) != sizeof(*ret)) -+ return -1; -+ return 0; - } - --struct cb_data { -- const char *lib_name; -- struct ltelf *lte; -- ElfW(Addr) addr; -- Process *proc; --}; -+static int -+fetch_rd32(struct Process *proc, target_address_t addr, -+ struct lt_r_debug_64 *ret) -+{ -+ struct lt_r_debug_32 rd; -+ if (umovebytes(proc, addr, &rd, sizeof(rd)) != sizeof(rd)) -+ return -1; - --static void --crawl_linkmap(Process *proc, struct r_debug *dbg, void (*callback)(void *), struct cb_data *data) { -- struct link_map rlm; -- char lib_name[BUFSIZ]; -- struct link_map *lm = NULL; -+ ret->r_version = rd.r_version; -+ ret->r_map = rd.r_map; -+ ret->r_brk = rd.r_brk; -+ ret->r_state = rd.r_state; -+ ret->r_ldbase = rd.r_ldbase; -+ -+ return 0; -+} - -+static int (* -+rdebug_fetcher(struct Process *proc))(struct Process *, -+ target_address_t, struct lt_r_debug_64 *) -+{ -+ return select_32_64(proc, fetch_rd32, fetch_rd64); -+} -+ -+static void -+crawl_linkmap(struct Process *proc, struct lt_r_debug_64 *dbg) -+{ - debug (DEBUG_FUNCTION, "crawl_linkmap()"); - - if (!dbg || !dbg->r_map) { -@@ -294,201 +424,234 @@ crawl_linkmap(Process *proc, struct r_debug *dbg, void (*callback)(void *), stru - return; - } - -- lm = dbg->r_map; -+ /* XXX The double cast should be removed when -+ * target_address_t becomes integral type. */ -+ target_address_t addr = (target_address_t)(uintptr_t)dbg->r_map; - -- while (lm) { -- if (umovebytes(proc, lm, &rlm, sizeof(rlm)) != sizeof(rlm)) { -- debug(2, "Unable to read link map\n"); -+ while (addr != 0) { -+ struct lt_link_map_64 rlm; -+ if (lm_fetcher(proc)(proc, addr, &rlm) < 0) { -+ debug(2, "Unable to read link map"); - return; - } - -- lm = rlm.l_next; -- if (rlm.l_name == NULL) { -- debug(2, "Invalid library name referenced in dynamic linker map\n"); -+ target_address_t key = addr; -+ /* XXX The double cast should be removed when -+ * target_address_t becomes integral type. */ -+ addr = (target_address_t)(uintptr_t)rlm.l_next; -+ if (rlm.l_name == 0) { -+ debug(2, "Name of mapped library is NULL"); - return; - } - -- umovebytes(proc, rlm.l_name, lib_name, sizeof(lib_name)); -+ char lib_name[BUFSIZ]; -+ /* XXX The double cast should be removed when -+ * target_address_t becomes integral type. */ -+ umovebytes(proc, (target_address_t)(uintptr_t)rlm.l_name, -+ lib_name, sizeof(lib_name)); - -- if (lib_name[0] == '\0') { -- debug(2, "Library name is an empty string"); -+ if (*lib_name == '\0') { -+ /* VDSO. No associated file, XXX but we might -+ * load it from the address space of the -+ * process. */ - continue; - } - -- if (callback) { -- debug(2, "Dispatching callback for: %s, " -- "Loaded at 0x%" PRI_ELF_ADDR "\n", -- lib_name, rlm.l_addr); -- data->addr = rlm.l_addr; -- data->lib_name = lib_name; -- callback(data); -+ /* Do we have that library already? */ -+ if (proc_each_library(proc, NULL, library_with_key_cb, &key)) -+ continue; -+ -+ struct library *lib = malloc(sizeof(*lib)); -+ if (lib == NULL) { -+ fail: -+ if (lib != NULL) -+ library_destroy(lib); -+ fprintf(stderr, "Couldn't load ELF object %s: %s\n", -+ lib_name, strerror(errno)); -+ continue; - } -+ library_init(lib, LT_LIBTYPE_DSO); -+ -+ if (ltelf_read_library(lib, proc, lib_name, rlm.l_addr) < 0) -+ goto fail; -+ -+ lib->key = key; -+ proc_add_library(proc, lib); - } - return; - } - --static struct r_debug * --load_debug_struct(Process *proc) { -- struct r_debug *rdbg = NULL; -+/* A struct stored at proc->debug. */ -+struct debug_struct -+{ -+ target_address_t debug_addr; -+ int state; -+}; - -+static int -+load_debug_struct(struct Process *proc, struct lt_r_debug_64 *ret) -+{ - debug(DEBUG_FUNCTION, "load_debug_struct"); - -- rdbg = malloc(sizeof(*rdbg)); -- if (!rdbg) { -- return NULL; -- } -+ struct debug_struct *debug = proc->debug; - -- if (umovebytes(proc, proc->debug, rdbg, sizeof(*rdbg)) != sizeof(*rdbg)) { -+ if (rdebug_fetcher(proc)(proc, debug->debug_addr, ret) < 0) { - debug(2, "This process does not have a debug structure!\n"); -- free(rdbg); -- return NULL; -+ return -1; - } - -- return rdbg; -+ return 0; - } - - static void --linkmap_add_cb(void *data) { //const char *lib_name, ElfW(Addr) addr) { -- size_t i = 0; -- struct cb_data *lm_add = data; -- struct ltelf lte; -- struct opt_x_t *xptr; -- -- debug(DEBUG_FUNCTION, "linkmap_add_cb"); -- -- /* -- XXX -- iterate through library[i]'s to see if this lib is in the list. -- if not, add it -- */ -- for(;i < library_num;i++) { -- if (strcmp(library[i], lm_add->lib_name) == 0) { -- /* found it, so its not new */ -- return; -- } -- } -- -- /* new library linked! */ -- debug(2, "New libdl loaded library found: %s\n", lm_add->lib_name); -- -- if (library_num < MAX_LIBRARIES) { -- library[library_num++] = strdup(lm_add->lib_name); -- memset(<e, 0, sizeof(struct ltelf)); -- lte.base_addr = lm_add->addr; -- do_init_elf(<e, library[library_num-1]); -- /* add bps */ -- for (xptr = opt_x; xptr; xptr = xptr->next) { -- if (xptr->found) -- continue; -- -- GElf_Sym sym; -- GElf_Addr addr; -- -- if (in_load_libraries(xptr->name, <e, 1, &sym)) { -- debug(2, "found symbol %s @ %#" PRIx64 -- ", adding it.", -- xptr->name, sym.st_value); -- addr = sym.st_value; -- add_library_symbol(addr, xptr->name, &library_symbols, LS_TOPLT_NONE, 0); -- xptr->found = 1; -- insert_breakpoint(lm_add->proc, -- sym2addr(lm_add->proc, -- library_symbols), -- library_symbols, 1); -- } -- } -- do_close_elf(<e); -- } --} -- --void --arch_check_dbg(Process *proc) { -- struct r_debug *dbg = NULL; -- struct cb_data data; -- -+rdebug_bp_on_hit(struct breakpoint *bp, struct Process *proc) -+{ - debug(DEBUG_FUNCTION, "arch_check_dbg"); - -- if (!(dbg = load_debug_struct(proc))) { -+ struct lt_r_debug_64 rdbg; -+ if (load_debug_struct(proc, &rdbg) < 0) { - debug(2, "Unable to load debug structure!"); - return; - } - -- if (dbg->r_state == RT_CONSISTENT) { -+ struct debug_struct *debug = proc->debug; -+ if (rdbg.r_state == RT_CONSISTENT) { - debug(2, "Linkmap is now consistent"); -- if (proc->debug_state == RT_ADD) { -+ if (debug->state == RT_ADD) { - debug(2, "Adding DSO to linkmap"); -- data.proc = proc; -- crawl_linkmap(proc, dbg, linkmap_add_cb, &data); -- } else if (proc->debug_state == RT_DELETE) { -+ //data.proc = proc; -+ crawl_linkmap(proc, &rdbg); -+ //&data); -+ } else if (debug->state == RT_DELETE) { - debug(2, "Removing DSO from linkmap"); - } else { - debug(2, "Unexpected debug state!"); - } - } - -- proc->debug_state = dbg->r_state; -- -- return; -+ debug->state = rdbg.r_state; - } - --static void --hook_libdl_cb(void *data) { -- struct cb_data *hook_data = data; -- const char *lib_name = NULL; -- ElfW(Addr) addr; -- struct ltelf *lte = NULL; -- -- debug(DEBUG_FUNCTION, "add_library_cb"); -+int -+linkmap_init(struct Process *proc, target_address_t dyn_addr) -+{ -+ debug(DEBUG_FUNCTION, "linkmap_init()"); - -- if (!data) { -- debug(2, "No callback data"); -- return; -+ struct debug_struct *debug = malloc(sizeof(*debug)); -+ if (debug == NULL) { -+ fprintf(stderr, "couldn't allocate debug struct: %s\n", -+ strerror(errno)); -+ fail: -+ proc->debug = NULL; -+ free(debug); -+ return -1; - } -+ proc->debug = debug; - -- lib_name = hook_data->lib_name; -- addr = hook_data->addr; -- lte = hook_data->lte; -- -- if (library_num < MAX_LIBRARIES) { -- library[library_num++] = strdup(lib_name); -- lte[library_num].base_addr = addr; -+ if (find_dynamic_entry_addr(proc, dyn_addr, DT_DEBUG, -+ &debug->debug_addr) == -1) { -+ debug(2, "Couldn't find debug structure!"); -+ goto fail; - } -- else { -- fprintf (stderr, "MAX LIBS REACHED\n"); -- exit(EXIT_FAILURE); -+ -+ int status; -+ struct lt_r_debug_64 rdbg; -+ if ((status = load_debug_struct(proc, &rdbg)) < 0) { -+ debug(2, "No debug structure or no memory to allocate one!"); -+ return status; - } --} - --int --linkmap_init(Process *proc, struct ltelf *lte) { -- void *dbg_addr = NULL, *dyn_addr = GELF_ADDR_CAST(lte->dyn_addr); -- struct r_debug *rdbg = NULL; -- struct cb_data data; -+ /* XXX The double cast should be removed when -+ * target_address_t becomes integral type. */ -+ target_address_t addr = (target_address_t)(uintptr_t)rdbg.r_brk; -+ if (arch_translate_address_dyn(proc, addr, &addr) < 0) -+ goto fail; - -- debug(DEBUG_FUNCTION, "linkmap_init()"); -+ struct breakpoint *rdebug_bp = insert_breakpoint(proc, addr, NULL); -+ static struct bp_callbacks rdebug_callbacks = { -+ .on_hit = rdebug_bp_on_hit, -+ }; -+ rdebug_bp->cbs = &rdebug_callbacks; - -- if (find_dynamic_entry_addr(proc, dyn_addr, DT_DEBUG, &dbg_addr) == -1) { -- debug(2, "Couldn't find debug structure!"); -- return -1; -- } -+ crawl_linkmap(proc, &rdbg); - -- proc->debug = dbg_addr; -+ return 0; -+} - -- if (!(rdbg = load_debug_struct(proc))) { -- debug(2, "No debug structure or no memory to allocate one!"); -+static int -+fetch_auxv64_entry(int fd, Elf64_auxv_t *ret) -+{ -+ /* Reaching EOF is as much problem as not reading whole -+ * entry. */ -+ return read(fd, ret, sizeof(*ret)) == sizeof(*ret) ? 0 : -1; -+} -+ -+static int -+fetch_auxv32_entry(int fd, Elf64_auxv_t *ret) -+{ -+ Elf32_auxv_t auxv; -+ if (read(fd, &auxv, sizeof(auxv)) != sizeof(auxv)) - return -1; -+ -+ ret->a_type = auxv.a_type; -+ ret->a_un.a_val = auxv.a_un.a_val; -+ return 0; -+} -+ -+static int (* -+auxv_fetcher(struct Process *proc))(int, Elf64_auxv_t *) -+{ -+ return select_32_64(proc, fetch_auxv32_entry, fetch_auxv64_entry); -+} -+ -+int -+process_get_entry(struct Process *proc, -+ target_address_t *entryp, -+ target_address_t *interp_biasp) -+{ -+ PROC_PID_FILE(fn, "/proc/%d/auxv", proc->pid); -+ int fd = open(fn, O_RDONLY); -+ if (fd == -1) { -+ fail: -+ fprintf(stderr, "couldn't read %s: %s", fn, strerror(errno)); -+ done: -+ if (fd != -1) -+ close(fd); -+ return fd == -1 ? -1 : 0; - } - -- data.lte = lte; -+ target_address_t at_entry = 0; -+ target_address_t at_bias = 0; -+ while (1) { -+ Elf64_auxv_t entry; -+ if (auxv_fetcher(proc)(fd, &entry) < 0) -+ goto fail; -+ -+ switch (entry.a_type) { -+ case AT_BASE: -+ /* XXX The double cast should be removed when -+ * target_address_t becomes integral type. */ -+ at_bias = (target_address_t) -+ (uintptr_t)entry.a_un.a_val; -+ continue; - -- add_library_symbol(rdbg->r_brk, "", &library_symbols, LS_TOPLT_NONE, 0); -- insert_breakpoint(proc, sym2addr(proc, library_symbols), -- library_symbols, 1); -+ case AT_ENTRY: -+ /* XXX The double cast should be removed when -+ * target_address_t becomes integral type. */ -+ at_entry = (target_address_t) -+ (uintptr_t)entry.a_un.a_val; -+ default: -+ continue; - -- crawl_linkmap(proc, rdbg, hook_libdl_cb, &data); -+ case AT_NULL: -+ break; -+ } -+ break; -+ } - -- free(rdbg); -- return 0; -+ *entryp = at_entry; -+ *interp_biasp = at_bias; -+ goto done; - } - - int -diff --git a/sysdeps/linux-gnu/s390/plt.c b/sysdeps/linux-gnu/s390/plt.c -index 85a1dd1..754d270 100644 ---- a/sysdeps/linux-gnu/s390/plt.c -+++ b/sysdeps/linux-gnu/s390/plt.c -@@ -1,4 +1,5 @@ - #include -+#include "proc.h" - #include "common.h" - - GElf_Addr -diff --git a/sysdeps/linux-gnu/s390/regs.c b/sysdeps/linux-gnu/s390/regs.c -index 169893e..a45dd9b 100644 ---- a/sysdeps/linux-gnu/s390/regs.c -+++ b/sysdeps/linux-gnu/s390/regs.c -@@ -9,6 +9,7 @@ - #include - #include - -+#include "proc.h" - #include "common.h" - - #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) -diff --git a/sysdeps/linux-gnu/s390/trace.c b/sysdeps/linux-gnu/s390/trace.c -index 63935de..8c08f1f 100644 ---- a/sysdeps/linux-gnu/s390/trace.c -+++ b/sysdeps/linux-gnu/s390/trace.c -@@ -17,6 +17,7 @@ - #include - #include - -+#include "proc.h" - #include "common.h" - - #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) -diff --git a/sysdeps/linux-gnu/sparc/plt.c b/sysdeps/linux-gnu/sparc/plt.c -index f9e6d80..658e549 100644 ---- a/sysdeps/linux-gnu/sparc/plt.c -+++ b/sysdeps/linux-gnu/sparc/plt.c -@@ -1,4 +1,5 @@ - #include -+#include "proc.h" - #include "common.h" - - GElf_Addr -diff --git a/sysdeps/linux-gnu/sparc/regs.c b/sysdeps/linux-gnu/sparc/regs.c -index 49d2729..d7ee114 100644 ---- a/sysdeps/linux-gnu/sparc/regs.c -+++ b/sysdeps/linux-gnu/sparc/regs.c -@@ -2,6 +2,7 @@ - - #include - #include "ptrace.h" -+#include "proc.h" - #include "common.h" - - void * -diff --git a/sysdeps/linux-gnu/sparc/trace.c b/sysdeps/linux-gnu/sparc/trace.c -index 7f05b55..e05c4d3 100644 ---- a/sysdeps/linux-gnu/sparc/trace.c -+++ b/sysdeps/linux-gnu/sparc/trace.c -@@ -6,6 +6,7 @@ - #include - #include - #include "ptrace.h" -+#include "proc.h" - #include "common.h" - - void -diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c -index fe64a28..d5c5262 100644 ---- a/sysdeps/linux-gnu/trace.c -+++ b/sysdeps/linux-gnu/trace.c -@@ -17,6 +17,8 @@ - #include "common.h" - #include "config.h" - #include "breakpoint.h" -+#include "proc.h" -+#include "linux-gnu/trace.h" - - #include "config.h" - #ifdef HAVE_LIBSELINUX -@@ -107,26 +109,21 @@ trace_me(void) - } - - /* There's a (hopefully) brief period of time after the child process -- * exec's when we can't trace it yet. Here we wait for kernel to -+ * forks when we can't trace it yet. Here we wait for kernel to - * prepare the process. */ --void -+int - wait_for_proc(pid_t pid) - { -- size_t i; -- for (i = 0; i < 100; ++i) { -- /* We read from memory address 0, but that shouldn't -- * be a problem: the reading will just fail. We are -- * looking for a particular reason of failure. */ -- if (ptrace(PTRACE_PEEKTEXT, pid, 0, 0) != -1 -- || errno != ESRCH) -- return; -- -- usleep(1000); -+ /* man ptrace: PTRACE_ATTACH attaches to the process specified -+ in pid. The child is sent a SIGSTOP, but will not -+ necessarily have stopped by the completion of this call; -+ use wait() to wait for the child to stop. */ -+ if (waitpid(pid, NULL, __WALL) != pid) { -+ perror ("trace_pid: waitpid"); -+ return -1; - } - -- fprintf(stderr, "\ --I consistently fail to read a word from the freshly launched process.\n\ --I'll now try to proceed with tracing, but this shouldn't be happening.\n"); -+ return 0; - } - - int -@@ -139,23 +136,16 @@ trace_pid(pid_t pid) - if (ptrace(PTRACE_ATTACH, pid, 1, 0) < 0) - return -1; - -- /* man ptrace: PTRACE_ATTACH attaches to the process specified -- in pid. The child is sent a SIGSTOP, but will not -- necessarily have stopped by the completion of this call; -- use wait() to wait for the child to stop. */ -- if (waitpid (pid, NULL, __WALL) != pid) { -- perror ("trace_pid: waitpid"); -- return -1; -- } -- -- return 0; -+ return wait_for_proc(pid); - } - - void --trace_set_options(Process *proc, pid_t pid) { -+trace_set_options(struct Process *proc) -+{ - if (proc->tracesysgood & 0x80) - return; - -+ pid_t pid = proc->pid; - debug(DEBUG_PROCESS, "trace_set_options: pid=%d", pid); - - long options = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK | -@@ -212,67 +202,6 @@ continue_process(pid_t pid) - "putting off the continue, events in que."); - } - --/** -- * This is used for bookkeeping related to PIDs that the event -- * handlers work with. -- */ --struct pid_task { -- pid_t pid; /* This may be 0 for tasks that exited -- * mid-handling. */ -- int sigstopped : 1; -- int got_event : 1; -- int delivered : 1; -- int vforked : 1; -- int sysret : 1; --} * pids; -- --struct pid_set { -- struct pid_task * tasks; -- size_t count; -- size_t alloc; --}; -- --/** -- * Breakpoint re-enablement. When we hit a breakpoint, we must -- * disable it, single-step, and re-enable it. That single-step can be -- * done only by one task in a task group, while others are stopped, -- * otherwise the processes would race for who sees the breakpoint -- * disabled and who doesn't. The following is to keep track of it -- * all. -- */ --struct process_stopping_handler --{ -- Event_Handler super; -- -- /* The task that is doing the re-enablement. */ -- Process * task_enabling_breakpoint; -- -- /* The pointer being re-enabled. */ -- struct breakpoint *breakpoint_being_enabled; -- -- /* Artificial atomic skip breakpoint, if any needed. */ -- void *atomic_skip_bp_addr; -- -- enum { -- /* We are waiting for everyone to land in t/T. */ -- psh_stopping = 0, -- -- /* We are doing the PTRACE_SINGLESTEP. */ -- psh_singlestep, -- -- /* We are waiting for all the SIGSTOPs to arrive so -- * that we can sink them. */ -- psh_sinking, -- -- /* This is for tracking the ugly workaround. */ -- psh_ugly_workaround, -- } state; -- -- int exiting; -- -- struct pid_set pids; --}; -- - static struct pid_task * - get_task_info(struct pid_set * pids, pid_t pid) - { -@@ -303,8 +232,8 @@ add_task_info(struct pid_set * pids, pid_t pid) - return task_info; - } - --static enum pcb_status --task_stopped(Process * task, void * data) -+static enum callback_status -+task_stopped(struct Process *task, void *data) - { - enum process_status st = process_status(task->pid); - if (data != NULL) -@@ -318,48 +247,48 @@ task_stopped(Process * task, void * data) - case ps_invalid: - case ps_tracing_stop: - case ps_zombie: -- return pcb_cont; -+ return CBS_CONT; - case ps_sleeping: - case ps_stop: - case ps_other: -- return pcb_stop; -+ return CBS_STOP; - } - - abort (); - } - - /* Task is blocked if it's stopped, or if it's a vfork parent. */ --static enum pcb_status --task_blocked(Process * task, void * data) -+static enum callback_status -+task_blocked(struct Process *task, void *data) - { - struct pid_set * pids = data; - struct pid_task * task_info = get_task_info(pids, task->pid); - if (task_info != NULL - && task_info->vforked) -- return pcb_cont; -+ return CBS_CONT; - - return task_stopped(task, NULL); - } - --static Event * process_vfork_on_event(Event_Handler * super, Event * event); -+static Event *process_vfork_on_event(struct event_handler *super, Event *event); - --static enum pcb_status --task_vforked(Process * task, void * data) -+static enum callback_status -+task_vforked(struct Process *task, void *data) - { - if (task->event_handler != NULL - && task->event_handler->on_event == &process_vfork_on_event) -- return pcb_stop; -- return pcb_cont; -+ return CBS_STOP; -+ return CBS_CONT; - } - - static int --is_vfork_parent(Process * task) -+is_vfork_parent(struct Process *task) - { -- return each_task(task->leader, &task_vforked, NULL) != NULL; -+ return each_task(task->leader, NULL, &task_vforked, NULL) != NULL; - } - --static enum pcb_status --send_sigstop(Process * task, void * data) -+static enum callback_status -+send_sigstop(struct Process *task, void *data) - { - Process * leader = task->leader; - struct pid_set * pids = data; -@@ -372,24 +301,24 @@ send_sigstop(Process * task, void * data) - perror("send_sigstop: add_task_info"); - destroy_event_handler(leader); - /* Signal failure upwards. */ -- return pcb_stop; -+ return CBS_STOP; - } - - /* This task still has not been attached to. It should be - stopped by the kernel. */ - if (task->state == STATE_BEING_CREATED) -- return pcb_cont; -+ return CBS_CONT; - - /* Don't bother sending SIGSTOP if we are already stopped, or - * if we sent the SIGSTOP already, which happens when we are - * handling "onexit" and inherited the handler from breakpoint - * re-enablement. */ - enum process_status st; -- if (task_stopped(task, &st) == pcb_cont) -- return pcb_cont; -+ if (task_stopped(task, &st) == CBS_CONT) -+ return CBS_CONT; - if (task_info->sigstopped) { - if (!task_info->delivered) -- return pcb_cont; -+ return CBS_CONT; - task_info->delivered = 0; - } - -@@ -400,7 +329,7 @@ send_sigstop(Process * task, void * data) - if (st == ps_sleeping - && is_vfork_parent (task)) { - task_info->vforked = 1; -- return pcb_cont; -+ return CBS_CONT; - } - - if (task_kill(task->pid, SIGSTOP) >= 0) { -@@ -410,7 +339,7 @@ send_sigstop(Process * task, void * data) - fprintf(stderr, - "Warning: couldn't send SIGSTOP to %d\n", task->pid); - -- return pcb_cont; -+ return CBS_CONT; - } - - /* On certain kernels, detaching right after a singlestep causes the -@@ -426,7 +355,7 @@ ugly_workaround(Process * proc) - if (sbp != NULL) - enable_breakpoint(proc, sbp); - else -- insert_breakpoint(proc, ip, NULL, 1); -+ insert_breakpoint(proc, ip, NULL); - ptrace(PTRACE_CONT, proc->pid, 0, 0); - } - -@@ -444,9 +373,21 @@ process_stopping_done(struct process_stopping_handler * self, Process * leader) - continue_process(self->pids.tasks[i].pid); - continue_process(self->task_enabling_breakpoint->pid); - destroy_event_handler(leader); -- } else { -+ } -+ -+ if (self->exiting) { -+ ugly_workaround: - self->state = psh_ugly_workaround; - ugly_workaround(self->task_enabling_breakpoint); -+ } else { -+ switch ((self->ugly_workaround_p)(self)) { -+ case CBS_FAIL: -+ /* xxx handle me */ -+ case CBS_STOP: -+ break; -+ case CBS_CONT: -+ goto ugly_workaround; -+ } - } - } - -@@ -464,21 +405,28 @@ undo_breakpoint(Event * event, void * data) - return ecb_cont; - } - --static enum pcb_status --untrace_task(Process * task, void * data) -+static enum callback_status -+untrace_task(struct Process *task, void *data) - { - if (task != data) - untrace_pid(task->pid); -- return pcb_cont; -+ return CBS_CONT; - } - --static enum pcb_status --remove_task(Process * task, void * data) -+static enum callback_status -+remove_task(struct Process *task, void *data) - { - /* Don't untrace leader just yet. */ - if (task != data) - remove_process(task); -- return pcb_cont; -+ return CBS_CONT; -+} -+ -+static enum callback_status -+retract_breakpoint_cb(struct Process *proc, struct breakpoint *bp, void *data) -+{ -+ breakpoint_on_retract(bp, proc); -+ return CBS_CONT; - } - - static void -@@ -486,6 +434,7 @@ detach_process(Process * leader) - { - each_qd_event(&undo_breakpoint, leader); - disable_all_breakpoints(leader); -+ proc_each_breakpoint(leader, NULL, retract_breakpoint_cb, NULL); - - /* Now untrace the process, if it was attached to by -p. */ - struct opt_p_t * it; -@@ -494,11 +443,11 @@ detach_process(Process * leader) - if (proc == NULL) - continue; - if (proc->leader == leader) { -- each_task(leader, &untrace_task, NULL); -+ each_task(leader, NULL, &untrace_task, NULL); - break; - } - } -- each_task(leader, &remove_task, leader); -+ each_task(leader, NULL, &remove_task, leader); - destroy_event_handler(leader); - remove_task(leader, NULL); - } -@@ -632,19 +581,56 @@ arch_atomic_singlestep(struct Process *proc, Breakpoint *sbp, - } - #endif - -+static Event *process_stopping_on_event(struct event_handler *super, -+ Event *event); -+ -+static void -+remove_atomic_breakpoints(struct Process *proc) -+{ -+ struct process_stopping_handler *self -+ = (void *)proc->leader->event_handler; -+ assert(self != NULL); -+ assert(self->super.on_event == process_stopping_on_event); -+ -+ int ct = sizeof(self->atomic_skip_bp_addrs) -+ / sizeof(*self->atomic_skip_bp_addrs); -+ int i; -+ for (i = 0; i < ct; ++i) -+ if (self->atomic_skip_bp_addrs[i] != 0) { -+ delete_breakpoint(proc, self->atomic_skip_bp_addrs[i]); -+ self->atomic_skip_bp_addrs[i] = 0; -+ } -+} -+ -+static void -+atomic_singlestep_bp_on_hit(struct breakpoint *bp, struct Process *proc) -+{ -+ remove_atomic_breakpoints(proc); -+} -+ - static int - atomic_singlestep_add_bp(void *addr, void *data) - { - struct process_stopping_handler *self = data; - struct Process *proc = self->task_enabling_breakpoint; - -- /* Only support single address as of now. */ -- assert(self->atomic_skip_bp_addr == NULL); -- -- self->atomic_skip_bp_addr = addr + 4; -- insert_breakpoint(proc->leader, self->atomic_skip_bp_addr, NULL, 1); -+ int ct = sizeof(self->atomic_skip_bp_addrs) -+ / sizeof(*self->atomic_skip_bp_addrs); -+ int i; -+ for (i = 0; i < ct; ++i) -+ if (self->atomic_skip_bp_addrs[i] == 0) { -+ self->atomic_skip_bp_addrs[i] = addr; -+ static struct bp_callbacks cbs = { -+ .on_hit = atomic_singlestep_bp_on_hit, -+ }; -+ struct breakpoint *bp -+ = insert_breakpoint(proc, addr, NULL); -+ breakpoint_set_callbacks(bp, &cbs); -+ return 0; -+ } - -- return 0; -+ assert(!"Too many atomic singlestep breakpoints!"); -+ abort(); - } - - static int -@@ -670,30 +656,68 @@ singlestep(struct process_stopping_handler *self) *** - } - - static void --post_singlestep(struct process_stopping_handler *self, Event **eventp) -+post_singlestep(struct process_stopping_handler *self, -+ struct Event **eventp) - { - continue_for_sigstop_delivery(&self->pids); - -- if ((*eventp)->type == EVENT_BREAKPOINT) -+ if (*eventp != NULL && (*eventp)->type == EVENT_BREAKPOINT) - *eventp = NULL; // handled - -- if (self->atomic_skip_bp_addr != 0) -- delete_breakpoint(self->task_enabling_breakpoint->leader, -- self->atomic_skip_bp_addr); -+ struct Process *proc = self->task_enabling_breakpoint; - -+ remove_atomic_breakpoints(proc); - self->breakpoint_being_enabled = NULL; - } - - static void --singlestep_error(struct process_stopping_handler *self, Event **eventp) -+singlestep_error(struct process_stopping_handler *self) - { - struct Process *teb = self->task_enabling_breakpoint; - struct breakpoint *sbp = self->breakpoint_being_enabled; -- fprintf(stderr, "%d couldn't singlestep over %s (%p)\n", -+ fprintf(stderr, "%d couldn't continue when handling %s (%p) at %p\n", - teb->pid, sbp->libsym != NULL ? sbp->libsym->name : NULL, -- sbp->addr); -+ sbp->addr, get_instruction_pointer(teb)); - delete_breakpoint(teb->leader, sbp->addr); -- post_singlestep(self, eventp); -+} -+ -+static void -+pt_continue(struct process_stopping_handler *self) -+{ -+ struct Process *teb = self->task_enabling_breakpoint; -+ debug(1, "PTRACE_CONT"); -+ ptrace(PTRACE_CONT, teb->pid, 0, 0); -+} -+ -+static void -+pt_singlestep(struct process_stopping_handler *self) -+{ -+ if (singlestep(self) < 0) -+ singlestep_error(self); -+} -+ -+static void -+disable_and(struct process_stopping_handler *self, -+ void (*do_this)(struct process_stopping_handler *self)) -+{ -+ struct Process *teb = self->task_enabling_breakpoint; -+ debug(DEBUG_PROCESS, "all stopped, now singlestep/cont %d", teb->pid); -+ if (self->breakpoint_being_enabled->enabled) -+ disable_breakpoint(teb, self->breakpoint_being_enabled); -+ (do_this)(self); -+ self->state = psh_singlestep; -+} -+ -+void -+linux_ptrace_disable_and_singlestep(struct process_stopping_handler *self) -+{ -+ disable_and(self, &pt_singlestep); -+} -+ -+void -+linux_ptrace_disable_and_continue(struct process_stopping_handler *self) -+{ -+ disable_and(self, &pt_continue); - } - - /* This event handler is installed when we are in the process of -@@ -703,16 +727,15 @@ singlestep_error(struct process_stopping_handler *self, Event **eventp) - * happens, we let the re-enablement thread to PTRACE_SINGLESTEP, - * re-enable, and continue everyone. */ - static Event * --process_stopping_on_event(Event_Handler * super, Event * event) -+process_stopping_on_event(struct event_handler *super, Event *event) - { - struct process_stopping_handler * self = (void *)super; - Process * task = event->proc; - Process * leader = task->leader; -- struct breakpoint *sbp = self->breakpoint_being_enabled; - Process * teb = self->task_enabling_breakpoint; - - debug(DEBUG_PROCESS, -- "pid %d; event type %d; state %d", -+ "process_stopping_on_event: pid %d; event type %d; state %d", - task->pid, event->type, self->state); - - struct pid_task * task_info = get_task_info(&self->pids, task->pid); -@@ -741,17 +764,10 @@ process_stopping_on_event(Event_Handler * super, Event * event) - switch (state) { - case psh_stopping: - /* If everyone is stopped, singlestep. */ -- if (each_task(leader, &task_blocked, &self->pids) == NULL) { -- debug(DEBUG_PROCESS, "all stopped, now SINGLESTEP %d", -- teb->pid); -- if (sbp->enabled) -- disable_breakpoint(teb, sbp); -- if (singlestep(self) < 0) { -- singlestep_error(self, &event); -- goto psh_sinking; -- } -- -- self->state = state = psh_singlestep; -+ if (each_task(leader, NULL, &task_blocked, -+ &self->pids) == NULL) { -+ (self->on_all_stopped)(self); -+ state = self->state; - } - break; - -@@ -760,18 +776,47 @@ process_stopping_on_event(Event_Handler * super, Event * event) - * have now stepped, and can re-enable the breakpoint. */ - if (event != NULL && task == teb) { - -- /* This is not the singlestep that we are waiting for. */ -+ /* If this was caused by a real breakpoint, as -+ * opposed to a singlestep, assume that it's -+ * an artificial breakpoint installed for some -+ * reason for the re-enablement. In that case -+ * handle it. */ -+ if (event->type == EVENT_BREAKPOINT) { -+ target_address_t ip -+ = get_instruction_pointer(task); -+ struct breakpoint *other -+ = address2bpstruct(leader, ip); -+ if (other != NULL) -+ breakpoint_on_hit(other, task); -+ } -+ -+ /* If we got SIGNAL instead of BREAKPOINT, -+ * then this is not singlestep at all. */ - if (event->type == EVENT_SIGNAL) { -+ do_singlestep: - if (singlestep(self) < 0) { -- singlestep_error(self, &event); -+ singlestep_error(self); -+ post_singlestep(self, &event); - goto psh_sinking; - } - break; -+ } else { -+ switch ((self->keep_stepping_p)(self)) { -+ case CBS_FAIL: -+ /* XXX handle me */ -+ case CBS_STOP: -+ break; -+ case CBS_CONT: -+ /* Sink singlestep event. */ -+ if (event->type == EVENT_BREAKPOINT) -+ event = NULL; -+ goto do_singlestep; -+ } - } - -- /* Essentially we don't care what event caused -- * the thread to stop. We can do the -- * re-enablement now. */ -+ /* Re-enable the breakpoint that we are -+ * stepping over. */ -+ struct breakpoint *sbp = self->breakpoint_being_enabled; - if (sbp->enabled) - enable_breakpoint(teb, sbp); - -@@ -812,52 +857,89 @@ process_stopping_on_event(Event_Handler * super, Event * event) - } - - static void --process_stopping_destroy(Event_Handler * super) -+process_stopping_destroy(struct event_handler *super) - { - struct process_stopping_handler * self = (void *)super; - free(self->pids.tasks); - } - -+static enum callback_status -+no(struct process_stopping_handler *self) -+{ -+ return CBS_STOP; -+} -+ -+int -+process_install_stopping_handler(struct Process *proc, struct breakpoint *sbp, -+ void (*as)(struct process_stopping_handler *), -+ enum callback_status (*ks) -+ (struct process_stopping_handler *), -+ enum callback_status (*uw) -+ (struct process_stopping_handler *)) -+{ -+ debug(DEBUG_FUNCTION, -+ "process_install_stopping_handler: pid=%d", proc->pid); -+ -+ struct process_stopping_handler *handler = calloc(sizeof(*handler), 1); -+ if (handler == NULL) -+ return -1; -+ -+ if (as == NULL) -+ as = &linux_ptrace_disable_and_singlestep; -+ if (ks == NULL) -+ ks = &no; -+ if (uw == NULL) -+ uw = &no; -+ -+ handler->super.on_event = process_stopping_on_event; -+ handler->super.destroy = process_stopping_destroy; -+ handler->task_enabling_breakpoint = proc; -+ handler->breakpoint_being_enabled = sbp; -+ handler->on_all_stopped = as; -+ handler->keep_stepping_p = ks; -+ handler->ugly_workaround_p = uw; -+ -+ install_event_handler(proc->leader, &handler->super); -+ -+ if (each_task(proc->leader, NULL, &send_sigstop, -+ &handler->pids) != NULL) { -+ destroy_event_handler(proc); -+ return -1; -+ } -+ -+ /* And deliver the first fake event, in case all the -+ * conditions are already fulfilled. */ -+ Event ev = { -+ .type = EVENT_NONE, -+ .proc = proc, -+ }; -+ process_stopping_on_event(&handler->super, &ev); -+ -+ return 0; -+} -+ - void - continue_after_breakpoint(Process *proc, struct breakpoint *sbp) - { -+ debug(DEBUG_PROCESS, -+ "continue_after_breakpoint: pid=%d, addr=%p", -+ proc->pid, sbp->addr); -+ - set_instruction_pointer(proc, sbp->addr); -+ - if (sbp->enabled == 0) { - continue_process(proc->pid); - } else { -- debug(DEBUG_PROCESS, -- "continue_after_breakpoint: pid=%d, addr=%p", -- proc->pid, sbp->addr); - #if defined __sparc__ || defined __ia64___ || defined __mips__ - /* we don't want to singlestep here */ - continue_process(proc->pid); - #else -- struct process_stopping_handler * handler -- = calloc(sizeof(*handler), 1); -- if (handler == NULL) { -- perror("malloc breakpoint disable handler"); -- fatal: -+ if (process_install_stopping_handler -+ (proc, sbp, NULL, NULL, NULL) < 0) { -+ perror("process_stopping_handler_create"); - /* Carry on not bothering to re-enable. */ - continue_process(proc->pid); -- return; - } -- -- handler->super.on_event = process_stopping_on_event; -- handler->super.destroy = process_stopping_destroy; -- handler->task_enabling_breakpoint = proc; -- handler->breakpoint_being_enabled = sbp; -- install_event_handler(proc->leader, &handler->super); -- -- if (each_task(proc->leader, &send_sigstop, -- &handler->pids) != NULL) -- goto fatal; -- -- /* And deliver the first fake event, in case all the -- * conditions are already fulfilled. */ -- Event ev; -- ev.type = EVENT_NONE; -- ev.proc = proc; -- process_stopping_on_event(&handler->super, &ev); - #endif - } - } -@@ -872,18 +954,20 @@ continue_after_breakpoint(Process *proc, struct breakpoint *sbp) - */ - struct ltrace_exiting_handler - { -- Event_Handler super; -+ struct event_handler super; - struct pid_set pids; - }; - - static Event * --ltrace_exiting_on_event(Event_Handler * super, Event * event) -+ltrace_exiting_on_event(struct event_handler *super, Event *event) - { - struct ltrace_exiting_handler * self = (void *)super; - Process * task = event->proc; - Process * leader = task->leader; - -- debug(DEBUG_PROCESS, "pid %d; event type %d", task->pid, event->type); -+ debug(DEBUG_PROCESS, -+ "ltrace_exiting_on_event: pid %d; event type %d", -+ task->pid, event->type); - - struct pid_task * task_info = get_task_info(&self->pids, task->pid); - handle_stopping_event(task_info, &event); -@@ -904,7 +988,7 @@ ltrace_exiting_on_event(Event_Handler * super, Event * event) - } - - static void --ltrace_exiting_destroy(Event_Handler * super) -+ltrace_exiting_destroy(struct event_handler *super) - { - struct ltrace_exiting_handler * self = (void *)super; - free(self->pids.tasks); -@@ -947,7 +1031,7 @@ ltrace_exiting_install_handler(Process * proc) - handler->super.destroy = ltrace_exiting_destroy; - install_event_handler(proc->leader, &handler->super); - -- if (each_task(proc->leader, &send_sigstop, -+ if (each_task(proc->leader, NULL, &send_sigstop, - &handler->pids) != NULL) - goto fatal; - -@@ -975,13 +1059,17 @@ ltrace_exiting_install_handler(Process * proc) - - struct process_vfork_handler - { -- Event_Handler super; -+ struct event_handler super; - void * bp_addr; - }; - - static Event * --process_vfork_on_event(Event_Handler * super, Event * event) -+process_vfork_on_event(struct event_handler *super, Event *event) - { -+ debug(DEBUG_PROCESS, -+ "process_vfork_on_event: pid %d; event type %d", -+ event->proc->pid, event->type); -+ - struct process_vfork_handler * self = (void *)super; - struct breakpoint *sbp; - assert(self != NULL); -@@ -989,7 +1077,7 @@ process_vfork_on_event(Event_Handler * super, Event * event) - switch (event->type) { - case EVENT_BREAKPOINT: - /* Remember the vfork return breakpoint. */ -- if (self->bp_addr == NULL) -+ if (self->bp_addr == 0) - self->bp_addr = event->e_un.brk_addr; - break; - -@@ -998,13 +1086,15 @@ process_vfork_on_event(Event_Handler * super, Event * event) - case EVENT_EXEC: - /* Smuggle back in the vfork return breakpoint, so - * that our parent can trip over it once again. */ -- if (self->bp_addr != NULL) { -+ if (self->bp_addr != 0) { - sbp = dict_find_entry(event->proc->leader->breakpoints, - self->bp_addr); - if (sbp != NULL) -- insert_breakpoint(event->proc->parent, -- self->bp_addr, -- sbp->libsym, 1); -+ assert(sbp->libsym == NULL); -+ /* We don't mind failing that, it's not a big -+ * deal to not display one extra vfork return. */ -+ insert_breakpoint(event->proc->parent, -+ self->bp_addr, NULL); - } - - continue_process(event->proc->parent->pid); -diff --git a/sysdeps/linux-gnu/trace.h b/sysdeps/linux-gnu/trace.h -new file mode 100644 -index 0000000..0f40709 ---- /dev/null -+++ b/sysdeps/linux-gnu/trace.h -@@ -0,0 +1,119 @@ -+/* -+ * 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 _LTRACE_LINUX_TRACE_H_ -+#define _LTRACE_LINUX_TRACE_H_ -+ -+/* This publishes some Linux-specific data structures used for process -+ * handling. */ -+ -+/** -+ * This is used for bookkeeping related to PIDs that the event -+ * handlers work with. -+ */ -+struct pid_task { -+ pid_t pid; /* This may be 0 for tasks that exited -+ * mid-handling. */ -+ int sigstopped : 1; -+ int got_event : 1; -+ int delivered : 1; -+ int vforked : 1; -+ int sysret : 1; -+}; -+ -+struct pid_set { -+ struct pid_task *tasks; -+ size_t count; -+ size_t alloc; -+}; -+ -+/** -+ * Breakpoint re-enablement. When we hit a breakpoint, we must -+ * disable it, single-step, and re-enable it. That single-step can be -+ * done only by one task in a task group, while others are stopped, -+ * otherwise the processes would race for who sees the breakpoint -+ * disabled and who doesn't. The following is to keep track of it -+ * all. -+ */ -+struct process_stopping_handler -+{ -+ struct event_handler super; -+ -+ /* The task that is doing the re-enablement. */ -+ struct Process *task_enabling_breakpoint; -+ -+ /* The pointer being re-enabled. */ -+ struct breakpoint *breakpoint_being_enabled; -+ -+ /* Artificial atomic skip breakpoint, if any needed. */ -+ void *atomic_skip_bp_addrs[2]; -+ -+ /* When all tasks are stopped, this callback gets called. */ -+ void (*on_all_stopped)(struct process_stopping_handler *); -+ -+ /* When we get a singlestep event, this is called to decide -+ * whether to stop stepping, or whether to enable the -+ * brakpoint, sink remaining signals, and continue -+ * everyone. */ -+ enum callback_status (*keep_stepping_p) -+ (struct process_stopping_handler *); -+ -+ /* Whether we need to use ugly workaround to get around -+ * various problems with singlestepping. */ -+ enum callback_status (*ugly_workaround_p) -+ (struct process_stopping_handler *); -+ -+ enum { -+ /* We are waiting for everyone to land in t/T. */ -+ psh_stopping = 0, -+ -+ /* We are doing the PTRACE_SINGLESTEP. */ -+ psh_singlestep, -+ -+ /* We are waiting for all the SIGSTOPs to arrive so -+ * that we can sink them. */ -+ psh_sinking, -+ -+ /* This is for tracking the ugly workaround. */ -+ psh_ugly_workaround, -+ } state; -+ -+ int exiting; -+ -+ struct pid_set pids; -+}; -+ -+/* Allocate a process stopping handler, initialize it and install it. -+ * Return 0 on success or a negative value on failure. Pass NULL for -+ * each callback to use a default instead. The default for -+ * ON_ALL_STOPPED is LINUX_PTRACE_DISABLE_AND_SINGLESTEP, the default -+ * for KEEP_STEPPING_P and UGLY_WORKAROUND_P is "no". */ -+int process_install_stopping_handler -+ (struct Process *proc, struct breakpoint *sbp, -+ void (*on_all_stopped)(struct process_stopping_handler *), -+ enum callback_status (*keep_stepping_p) -+ (struct process_stopping_handler *), -+ enum callback_status (*ugly_workaround_p) -+ (struct process_stopping_handler *)); -+ -+void linux_ptrace_disable_and_singlestep(struct process_stopping_handler *self); -+void linux_ptrace_disable_and_continue(struct process_stopping_handler *self); -+ -+#endif /* _LTRACE_LINUX_TRACE_H_ */ -diff --git a/sysdeps/linux-gnu/x86_64/plt.c b/sysdeps/linux-gnu/x86_64/plt.c -index b53ff44..bb1b2b1 100644 ---- a/sysdeps/linux-gnu/x86_64/plt.c -+++ b/sysdeps/linux-gnu/x86_64/plt.c -@@ -1,5 +1,7 @@ - #include -+#include "proc.h" - #include "common.h" -+#include "library.h" - - GElf_Addr - arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { -diff --git a/sysdeps/linux-gnu/x86_64/regs.c b/sysdeps/linux-gnu/x86_64/regs.c -index ed1f118..0ff3281 100644 ---- a/sysdeps/linux-gnu/x86_64/regs.c -+++ b/sysdeps/linux-gnu/x86_64/regs.c -@@ -4,7 +4,7 @@ - #include - #include - --#include "common.h" -+#include "proc.h" - - #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) - # define PTRACE_PEEKUSER PTRACE_PEEKUSR -diff --git a/sysdeps/linux-gnu/x86_64/trace.c b/sysdeps/linux-gnu/x86_64/trace.c -index d0299d9..0d3f693 100644 ---- a/sysdeps/linux-gnu/x86_64/trace.c -+++ b/sysdeps/linux-gnu/x86_64/trace.c -@@ -12,6 +12,7 @@ - - #include "common.h" - #include "ptrace.h" -+#include "proc.h" - - #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) - # define PTRACE_PEEKUSER PTRACE_PEEKUSR -@@ -42,23 +43,35 @@ get_arch_dep(Process *proc) { - /* Returns 1 if syscall, 2 if sysret, 0 otherwise. - */ - int --syscall_p(Process *proc, int status, int *sysnum) { -+syscall_p(struct Process *proc, int status, int *sysnum) -+{ - if (WIFSTOPPED(status) - && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { -+ struct callstack_element *elem = NULL; -+ if (proc->callstack_depth > 0) -+ elem = proc->callstack + proc->callstack_depth - 1; -+ - long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * ORIG_RAX, 0); -- if (ret == -1 && errno) -- return -1; -+ if (ret == -1) { -+ if (errno) -+ return -1; -+ /* Otherwise, ORIG_RAX == -1 means that the -+ * system call should not be restarted. In -+ * that case rely on what we have on -+ * stack. */ -+ if (elem != NULL && elem->is_syscall) -+ ret = elem->c_un.syscall; -+ } - - *sysnum = ret; -- if (proc->callstack_depth > 0 && -- proc->callstack[proc->callstack_depth - 1].is_syscall && -- proc->callstack[proc->callstack_depth - 1].c_un.syscall == *sysnum) { -+ debug(DEBUG_FUNCTION, "sysnum=%ld %p %d\n", ret, -+ get_instruction_pointer(proc), errno); -+ if (elem != NULL && elem->is_syscall -+ && elem->c_un.syscall == *sysnum) - return 2; -- } - -- if (*sysnum >= 0) { -+ if (*sysnum >= 0) - return 1; -- } - } - return 0; - } -diff --git a/sysdeps/sysdep.h b/sysdeps/sysdep.h -index 1d19c6f..96b3857 100644 ---- a/sysdeps/sysdep.h -+++ b/sysdeps/sysdep.h -@@ -1 +1,31 @@ -+#ifndef LTRACE_SYSDEP_H -+#define LTRACE_SYSDEP_H -+ - #include -+ -+#ifndef ARCH_HAVE_LTELF_DATA -+struct arch_ltelf_data { -+}; -+#endif -+ -+#ifndef ARCH_HAVE_BREAKPOINT_DATA -+struct arch_breakpoint_data { -+}; -+#endif -+ -+#ifndef ARCH_HAVE_LIBRARY_SYMBOL_DATA -+struct arch_library_symbol_data { -+}; -+#endif -+ -+#ifndef ARCH_HAVE_LIBRARY_DATA -+struct arch_library_data { -+}; -+#endif -+ -+#ifndef ARCH_HAVE_PROCESS_DATA -+struct arch_process_data { -+}; -+#endif -+ -+#endif /* LTRACE_SYSDEP_H */ -diff --git a/testsuite/ltrace.main/filt.c b/testsuite/ltrace.main/filt.c -new file mode 100644 -index 0000000..f31a30d ---- /dev/null -+++ b/testsuite/ltrace.main/filt.c -@@ -0,0 +1,8 @@ -+void func1(void); -+ -+int -+main(int argc, char *argv[]) -+{ -+ func1(); -+ return 0; -+} -diff --git a/testsuite/ltrace.main/filt1.c b/testsuite/ltrace.main/filt1.c -new file mode 100644 -index 0000000..a0eb906 ---- /dev/null -+++ b/testsuite/ltrace.main/filt1.c -@@ -0,0 +1,7 @@ -+void func2(void); -+ -+void -+func1(void) -+{ -+ func2(); -+} -diff --git a/testsuite/ltrace.main/filt2.c b/testsuite/ltrace.main/filt2.c -new file mode 100644 -index 0000000..24999a3 ---- /dev/null -+++ b/testsuite/ltrace.main/filt2.c -@@ -0,0 +1,5 @@ -+void -+func2(void) -+{ -+ puts("func2"); -+} -diff --git a/testsuite/ltrace.main/filters.exp b/testsuite/ltrace.main/filters.exp -new file mode 100644 -index 0000000..1a9a8f7 ---- /dev/null -+++ b/testsuite/ltrace.main/filters.exp -@@ -0,0 +1,79 @@ -+# Copyright (C) 2012 Petr Machata, Red Hat Inc. -+ -+set testfile "filt" -+set srcfile0 $srcdir/$subdir/$testfile.c -+set binfile0 $objdir/$subdir/$testfile -+set base1 "filt1" -+set srcfile1 $srcdir/$subdir/$base1.c -+set binfile1 $objdir/$subdir/lib$base1.so -+set base2 "filt2" -+set srcfile2 $srcdir/$subdir/$base2.c -+set binfile2 $objdir/$subdir/lib$base2.so -+ -+if [get_compiler_info $binfile0] { -+ return -1 -+} -+ -+verbose "compiling source file now....." -+if { [ltrace_compile_shlib $srcfile2 $binfile2 debug ] != "" -+ || [ltrace_compile_shlib $srcfile1 $binfile1 debug ] != "" -+ || [ltrace_compile $srcfile0 $binfile0 executable [list debug shlib=$binfile1 shlib=$binfile2] ] != ""} { -+ send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" -+} -+ -+ltrace_options "-e*" -+set exec_output [ltrace_runtest $objdir/$subdir $binfile0] -+ -+# Check the output of this program. -+verbose "ltrace runtest output: $exec_output\n" -+if [regexp {ELF from incompatible architecture} $exec_output] { -+ fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" -+ return -+} elseif [ regexp {Couldn't get .hash data} $exec_output ] { -+ fail "Couldn't get .hash data!" -+ return -+} -+ -+ltrace_verify_output ${binfile0}.ltrace "filt->func1" 1 -+ltrace_verify_output ${binfile0}.ltrace "libfilt1.so->func2" 1 -+ltrace_verify_output ${binfile0}.ltrace "libfilt2.so->puts" 1 -+ltrace_verify_output ${binfile0}.ltrace "func2 resumed" 1 -+ltrace_verify_output ${binfile0}.ltrace "func1 resumed" 1 -+ -+# I simply can't figure out how to pass an empty string to -+# ltrace_options without that getting interpreted as {}, so pass -+# something harmless instead. -+ltrace_options "-b" -+ltrace_runtest $objdir/$subdir $binfile0 -+ltrace_verify_output ${binfile0}.ltrace "^func1(.*)" 1 -+ -+ltrace_options "-e@MAIN" -+ltrace_runtest $objdir/$subdir $binfile0 -+ltrace_verify_output ${binfile0}.ltrace "filt->func1(.*)" 1 -+ -+ltrace_options "-e@libfilt1.so" -+ltrace_runtest $objdir/$subdir $binfile0 -+ltrace_verify_output ${binfile0}.ltrace "libfilt1.so->func2(.*)" 1 -+ -+ltrace_options "-e@libfilt2.so" -+ltrace_runtest $objdir/$subdir $binfile0 -+ltrace_verify_output ${binfile0}.ltrace "libfilt2.so->puts(.*)" 1 -+ -+ltrace_options "-e@libfilt*" -+ltrace_runtest $objdir/$subdir $binfile0 -+ltrace_verify_output ${binfile0}.ltrace "libfilt1.so->func2(" 1 -+ltrace_verify_output ${binfile0}.ltrace "libfilt2.so->puts(.*)" 1 -+ltrace_verify_output ${binfile0}.ltrace "func2 resumed" 1 -+ -+ltrace_options "-efunc*" -+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 -+ -+# Check that we handle breakpoint on both PLT entry and entry point -+ltrace_options "-efunc1" "-xfunc1" -+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 -diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c -index 154de84..fb46dfe 100644 ---- a/testsuite/ltrace.main/parameters.c -+++ b/testsuite/ltrace.main/parameters.c -@@ -36,10 +36,16 @@ typedef enum { - void func_enum(color_t); - void func_typedef(color_t); - --void func_work (char *x); --void func_call (char *x, char* y, void (*cb) (char *)); -+void func_work(char *x); -+void func_call(char *x, char *y, void (*cb)(char *)); - --int -+void -+call_func_work (char *x) -+{ -+ func_work(x); -+} -+ -+int - main () - { - int x = 17; -@@ -124,7 +130,7 @@ main () - { - char x[10] = {}; - char y[10] = {}; -- func_call (x, y, func_work); -+ func_call(x, y, call_func_work); - } - - return 0; -diff --git a/testsuite/ltrace.minor/libdl-simple.exp b/testsuite/ltrace.minor/libdl-simple.exp -index 9957001..91af5bd 100644 ---- a/testsuite/ltrace.minor/libdl-simple.exp -+++ b/testsuite/ltrace.minor/libdl-simple.exp -@@ -28,25 +28,5 @@ if [regexp {ELF from incompatible architecture} $exec_output] { - return - } - --# Verify the time for calling sleep. --set fd [ open $objdir/$subdir/$binfile.ltrace r] --set FOUND 0 --while { [gets $fd line] >= 0 } { -- # match the line with sleep and extract the spent time in sleep and sleep argument. -- if [ regexp {(test_libdl)\(} $line match tester ] then { -- verbose "test_libdl = $tester" -- -- if { $tester == "test_libdl" } then { -- pass "Successfully traced libdl loaded function." -- } else { -- fail "Failed to trace libdl loaded function." -- } -- set FOUND 1 -- break -- } --} --close $fd -- --if {$FOUND != 1} then { -- fail "Fail to trace libdl loaded function!" --} -+set pattern "test_libdl@liblibdl-simple.so" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -diff -urp ltrace-0.6.0-orig/Makefile.in ltrace-0.6.0/Makefile.in ---- ltrace-0.6.0-orig/Makefile.in 2012-05-01 00:24:18.824433420 +0200 -+++ ltrace-0.6.0/Makefile.in 2012-05-01 00:25:01.285738780 +0200 -@@ -65,7 +65,7 @@ libltrace_la_DEPENDENCIES = $(am__DEPEND - am_libltrace_la_OBJECTS = breakpoints.lo debug.lo demangle.lo dict.lo \ - display_args.lo ltrace-elf.lo execute_program.lo \ - handle_event.lo libltrace.lo options.lo output.lo proc.lo \ -- read_config_file.lo summary.lo -+ read_config_file.lo summary.lo library.lo filter.lo glob.lo - libltrace_la_OBJECTS = $(am_libltrace_la_OBJECTS) - am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" \ - "$(DESTDIR)$(docdir)" "$(DESTDIR)$(sysconfdir)" -@@ -311,7 +314,10 @@ libltrace_la_SOURCES = \ - output.c \ - proc.c \ - read_config_file.c \ -- summary.c -+ summary.c \ -+ library.c \ -+ filter.c \ -+ glob.c - - libltrace_la_LIBADD = \ - $(libelf_LIBS) \ -@@ -338,7 +344,10 @@ noinst_HEADERS = \ - ltrace.h \ - options.h \ - output.h \ -- read_config_file.h -+ read_config_file.h \ -+ library.h \ -+ filter.h \ -+ glob.h - - dist_man1_MANS = \ - ltrace.1 -diff -urp ltrace-0.6.0-orig/sysdeps/linux-gnu/Makefile.in ltrace-0.6.0/sysdeps/linux-gnu/Makefile.in ---- ltrace-0.6.0-orig/sysdeps/linux-gnu/Makefile.in 2012-05-01 00:24:18.864434649 +0200 -+++ ltrace-0.6.0/sysdeps/linux-gnu/Makefile.in 2012-05-01 00:24:59.835694202 +0200 -@@ -260,7 +263,8 @@ ___libos_la_LIBADD = \ - noinst_HEADERS = \ - arch_syscallent.h \ - signalent1.h \ -- syscallent1.h -+ syscallent1.h \ -+ trace.h - - EXTRA_DIST = \ - arch_mksyscallent \ -diff --git a/ltrace-elf.c b/ltrace-elf.c -index a311c5f..b1af070 100644 ---- a/ltrace-elf.c -+++ b/ltrace-elf.c -@@ -175,8 +175,8 @@ need_data(Elf_Data *data, GElf_Xword offset, GElf_Xword size) - { - assert(data != NULL); - if (data->d_size < size || offset > data->d_size - size) { -- debug(1, "Not enough data to read %zd-byte value" -- " at offset %zd.", size, offset); -+ debug(1, "Not enough data to read %"PRId64"-byte value" -+ " at offset %"PRId64".", size, offset); - return -1; - } - return 0; -diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c -index 3b6a25f..9717738 100644 ---- a/sysdeps/linux-gnu/ppc/plt.c -+++ b/sysdeps/linux-gnu/ppc/plt.c -@@ -230,7 +230,9 @@ arch_translate_address_dyn(struct Process *proc, - error(0, errno, "dynamic .opd translation of %p", addr); - return -1; - } -- *ret = (target_address_t)value; -+ /* XXX The double cast should be removed when -+ * target_address_t becomes integral type. */ -+ *ret = (target_address_t)(uintptr_t)value; - return 0; - } - -@@ -243,14 +245,17 @@ arch_translate_address(struct ltelf *lte, - target_address_t addr, target_address_t *ret) - { - if (lte->ehdr.e_machine == EM_PPC64) { -- GElf_Xword offset = (GElf_Addr)addr - lte->arch.opd_base; -+ /* XXX The double cast should be removed when -+ * target_address_t becomes integral type. */ -+ GElf_Xword offset -+ = (GElf_Addr)(uintptr_t)addr - lte->arch.opd_base; - uint64_t value; - if (elf_read_u64(lte->arch.opd_data, offset, &value) < 0) { - error(0, 0, "static .opd translation of %p: %s", addr, - elf_errmsg(-1)); - return -1; - } -- *ret = (target_address_t)(value + lte->bias); -+ *ret = (target_address_t)(uintptr_t)(value + lte->bias); - return 0; - } - diff --git a/ltrace-0.6.0-ppc-args.patch b/ltrace-0.6.0-ppc-args.patch deleted file mode 100644 index 43bfa2e..0000000 --- a/ltrace-0.6.0-ppc-args.patch +++ /dev/null @@ -1,30 +0,0 @@ -diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c -index 20b3f5d..321e6ec 100644 ---- a/sysdeps/linux-gnu/ppc/trace.c -+++ b/sysdeps/linux-gnu/ppc/trace.c -@@ -87,10 +87,22 @@ gimme_arg_regset(enum tof type, Process *proc, int arg_num, arg_type_info *info, - } - else if (greg <= 10) - return (*regs)[greg++]; -- else -+ else { -+#ifdef __powerpc64__ -+ if (proc->mask_32bit) -+ return ptrace (PTRACE_PEEKDATA, proc->pid, -+ proc->stack_pointer + 8 + -+ sizeof (int) * (arg_num - 8), 0) >> 32; -+ else -+ return ptrace (PTRACE_PEEKDATA, proc->pid, -+ proc->stack_pointer + 112 + -+ sizeof (long) * (arg_num - 8), 0); -+#else - return ptrace (PTRACE_PEEKDATA, proc->pid, -- proc->stack_pointer + sizeof (long) * -- (arg_num - 8), 0); -+ proc->stack_pointer + 8 + -+ sizeof (long) * (arg_num - 8), 0); -+#endif -+ } - - return 0; - } diff --git a/ltrace-0.6.0-ppc-flteqv.patch b/ltrace-0.6.0-ppc-flteqv.patch deleted file mode 100644 index 758e935..0000000 --- a/ltrace-0.6.0-ppc-flteqv.patch +++ /dev/null @@ -1,162 +0,0 @@ -From fcf256ceeab4b0b74cf1e18122e894aafce94fdc Mon Sep 17 00:00:00 2001 -From: Petr Machata -Date: Thu, 30 Aug 2012 01:37:23 +0200 -Subject: [PATCH] PPC64 passes floating point equivalent structures in - registers as well - -Extract the structure unpacking code from s390 back end into the generic -type module. Call this from both s390 and ppc back ends. ---- - sysdeps/linux-gnu/ppc/fetch.c | 21 +++++++++++++++++++-- - sysdeps/linux-gnu/s390/fetch.c | 31 +------------------------------ - type.c | 34 ++++++++++++++++++++++++++++++++++ - type.h | 7 +++++++ - 4 files changed, 61 insertions(+), 32 deletions(-) - -diff --git a/sysdeps/linux-gnu/ppc/fetch.c b/sysdeps/linux-gnu/ppc/fetch.c -index 44cd056..40c91d9 100644 ---- a/sysdeps/linux-gnu/ppc/fetch.c -+++ b/sysdeps/linux-gnu/ppc/fetch.c -@@ -340,13 +340,30 @@ allocate_argument(struct fetch_context *ctx, struct Process *proc, - while (slots-- > 0) { - struct value val; - value_init(&val, proc, NULL, long_info, 0); -- int rc = allocate_gpr(ctx, proc, long_info, &val); -+ -+ /* Floating point registers [...] are used [...] to -+ pass [...] one member aggregates passed by value -+ containing a floating point value[.] Note that for -+ one member aggregates, "containing" extends to -+ aggregates within aggregates ad infinitum. */ -+ int rc; -+ struct arg_type_info *fp_info -+ = type_get_fp_equivalent(valuep->type); -+ if (fp_info != NULL) -+ rc = allocate_float(ctx, proc, fp_info, &val); -+ else -+ rc = allocate_gpr(ctx, proc, long_info, &val); -+ - if (rc >= 0) { - memcpy(ptr, value_get_data(&val, NULL), width); - ptr += width; - } - value_destroy(&val); -- if (rc < 0) -+ -+ /* Bail out if we failed or if we are dealing with -+ * FP-equivalent. Those don't need the adjustments -+ * made below. */ -+ if (rc < 0 || fp_info != NULL) - return rc; - } - -diff --git a/sysdeps/linux-gnu/s390/fetch.c b/sysdeps/linux-gnu/s390/fetch.c -index 5d26b35..c46ef7c 100644 ---- a/sysdeps/linux-gnu/s390/fetch.c -+++ b/sysdeps/linux-gnu/s390/fetch.c -@@ -61,35 +61,6 @@ s390x(struct fetch_context *ctx) - } - - static int --fp_equivalent(struct arg_type_info *info) --{ -- switch (info->type) { -- case ARGTYPE_VOID: -- case ARGTYPE_INT: -- case ARGTYPE_UINT: -- case ARGTYPE_LONG: -- case ARGTYPE_ULONG: -- case ARGTYPE_CHAR: -- case ARGTYPE_SHORT: -- case ARGTYPE_USHORT: -- case ARGTYPE_ARRAY: -- case ARGTYPE_POINTER: -- return 0; -- -- case ARGTYPE_FLOAT: -- case ARGTYPE_DOUBLE: -- return 1; -- -- case ARGTYPE_STRUCT: -- if (type_struct_size(info) != 1) -- return 0; -- return fp_equivalent(type_element(info, 0)); -- } -- assert(info->type != info->type); -- abort(); --} -- --static int - fetch_register_banks(struct Process *proc, struct fetch_context *ctx) - { - ptrace_area parea; -@@ -256,7 +227,7 @@ arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, - return 0; - - case ARGTYPE_STRUCT: -- if (fp_equivalent(info)) -+ if (type_get_fp_equivalent(info) != NULL) - /* fall through */ - case ARGTYPE_FLOAT: - case ARGTYPE_DOUBLE: -diff --git a/type.c b/type.c -index 6341042..4b9a645 100644 ---- a/type.c -+++ b/type.c -@@ -513,3 +513,37 @@ type_is_signed(enum arg_type type) - } - abort(); - } -+ -+struct arg_type_info * -+type_get_fp_equivalent(struct arg_type_info *info) -+{ -+ /* Extract innermost structure. Give up early if any -+ * component has more than one element. */ -+ while (info->type == ARGTYPE_STRUCT) { -+ if (type_struct_size(info) != 1) -+ return NULL; -+ info = type_element(info, 0); -+ } -+ -+ switch (info->type) { -+ case ARGTYPE_CHAR: -+ case ARGTYPE_SHORT: -+ case ARGTYPE_INT: -+ case ARGTYPE_LONG: -+ case ARGTYPE_UINT: -+ case ARGTYPE_ULONG: -+ case ARGTYPE_USHORT: -+ case ARGTYPE_VOID: -+ case ARGTYPE_ARRAY: -+ case ARGTYPE_POINTER: -+ return NULL; -+ -+ case ARGTYPE_FLOAT: -+ case ARGTYPE_DOUBLE: -+ return info; -+ -+ case ARGTYPE_STRUCT: -+ abort(); -+ } -+ abort(); -+} -diff --git a/type.h b/type.h -index 545173c..53123b8 100644 ---- a/type.h -+++ b/type.h -@@ -130,4 +130,11 @@ int type_is_integral(enum arg_type type); - /* Whether TYPE, which shall be integral, is a signed type. */ - int type_is_signed(enum arg_type type); - -+/* If INFO is floating point equivalent type, return the corresponding -+ * floating point type. Otherwise return NULL. Floating point -+ * equivalent types are either ARGTYPE_FLOAT, or ARGTYPE_DOUBLE, or -+ * ARGTYPE_STRUCT whose sole member is a floating point equivalent -+ * type. */ -+struct arg_type_info *type_get_fp_equivalent(struct arg_type_info *info); -+ - #endif /* TYPE_H */ --- -1.7.6.5 - diff --git a/ltrace-0.6.0-ppc-lwarx.patch b/ltrace-0.6.0-ppc-lwarx.patch deleted file mode 100644 index cf64123..0000000 --- a/ltrace-0.6.0-ppc-lwarx.patch +++ /dev/null @@ -1,373 +0,0 @@ -diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h -index 711b4a3..64c1821 100644 ---- a/sysdeps/linux-gnu/ppc/arch.h -+++ b/sysdeps/linux-gnu/ppc/arch.h -@@ -15,6 +15,7 @@ - - /* Start of arch-specific functions. */ - #define ARCH_ENDIAN_BIG -+#define ARCH_HAVE_ATOMIC_SINGLESTEP - - #define PPC_NOP { 0x60, 0x00, 0x00, 0x00 } - #define PPC_NOP_LENGTH 4 -diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c -index 8642157..05993de 100644 ---- a/sysdeps/linux-gnu/ppc/trace.c -+++ b/sysdeps/linux-gnu/ppc/trace.c -@@ -11,6 +11,7 @@ - - #include "common.h" - #include "ptrace.h" -+#include "breakpoint.h" - - #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) - # define PTRACE_PEEKUSER PTRACE_PEEKUSR -@@ -197,3 +197,85 @@ arch_umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { - memcpy(&arch->regs_copy, &arch->regs, sizeof(arch->regs)); - memcpy(&arch->fpregs_copy, &arch->fpregs, sizeof(arch->fpregs)); - } -+ -+/* The atomic skip code is mostly taken from GDB. */ -+ -+/* Instruction masks used during single-stepping of atomic -+ * sequences. This was lifted from GDB. */ -+#define LWARX_MASK 0xfc0007fe -+#define LWARX_INSTRUCTION 0x7c000028 -+#define LDARX_INSTRUCTION 0x7c0000A8 -+#define STWCX_MASK 0xfc0007ff -+#define STWCX_INSTRUCTION 0x7c00012d -+#define STDCX_INSTRUCTION 0x7c0001ad -+#define BC_MASK 0xfc000000 -+#define BC_INSTRUCTION 0x40000000 -+ -+int -+arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, -+ int (*add_cb)(void *addr, void *data), -+ void *add_cb_data) -+{ -+ void *addr = sbp->addr; -+ debug(1, "pid=%d addr=%p", proc->pid, addr); -+ -+ /* If the original instruction was lwarx/ldarx, we can't -+ * single-step over it, instead we have to execute the whole -+ * atomic block at once. */ -+ union { -+ uint32_t insn; -+ char buf[4]; -+ } u; -+ memcpy(u.buf, sbp->orig_value, BREAKPOINT_LENGTH); -+ -+ if ((u.insn & LWARX_MASK) != LWARX_INSTRUCTION -+ && (u.insn & LWARX_MASK) != LDARX_INSTRUCTION) -+ return 1; -+ -+ int insn_count; -+ for (insn_count = 0; ; ++insn_count) { -+ addr += 4; -+ unsigned long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); -+ if (l == (unsigned long)-1 && errno) -+ return -1; -+ uint32_t insn; -+#ifdef __powerpc64__ -+ insn = l >> 32; -+#else -+ insn = l; -+#endif -+ -+ /* If we hit a branch instruction, give up. The -+ * computation could escape that way and we'd have to -+ * treat that case specially. */ -+ if ((insn & BC_MASK) == BC_INSTRUCTION) { -+ debug(1, "pid=%d, found branch at %p, giving up", -+ proc->pid, addr); -+ return -1; -+ } -+ -+ if ((insn & STWCX_MASK) == STWCX_INSTRUCTION -+ || (insn & STWCX_MASK) == STDCX_INSTRUCTION) { -+ debug(1, "pid=%d, found end of atomic block at %p", -+ proc->pid, addr); -+ break; -+ } -+ -+ /* Arbitrary cut-off. If we didn't find the -+ * terminating instruction by now, just give up. */ -+ if (insn_count > 16) { -+ debug(1, "pid=%d, couldn't find end of atomic block", -+ proc->pid); -+ return -1; -+ } -+ } -+ -+ /* Put the breakpoint to the next instruction. */ -+ addr += 4; -+ if (add_cb(addr, add_cb_data) < 0) -+ return -1; -+ -+ debug(1, "PTRACE_CONT"); -+ ptrace(PTRACE_CONT, proc->pid, 0, 0); -+ return 0; -+} -diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c -index 9ecea1e..d962048 100644 ---- a/sysdeps/linux-gnu/trace.c -+++ b/sysdeps/linux-gnu/trace.c -@@ -249,6 +249,9 @@ struct process_stopping_handler - /* The pointer being re-enabled. */ - struct breakpoint *breakpoint_being_enabled; - -+ /* Artificial atomic skip breakpoint, if any needed. */ -+ void *atomic_skip_bp_addr; -+ - enum { - /* We are waiting for everyone to land in t/T. */ - psh_stopping = 0, -@@ -612,12 +615,84 @@ all_stops_accountable(struct pid_set * pids) - return 1; - } - --static void --singlestep(Process * proc) -+/* The protocol is: 0 for success, negative for failure, positive if -+ * default singlestep is to be used. */ -+int arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, -+ int (*add_cb)(void *addr, void *data), -+ void *add_cb_data); -+ -+#ifndef ARCH_HAVE_ATOMIC_SINGLESTEP -+int -+arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, -+ int (*add_cb)(void *addr, void *data), -+ void *add_cb_data) -+{ -+ return 1; -+} -+#endif -+ -+static int -+atomic_singlestep_add_bp(void *addr, void *data) -+{ -+ struct process_stopping_handler *self = data; -+ struct Process *proc = self->task_enabling_breakpoint; -+ -+ /* Only support single address as of now. */ -+ assert(self->atomic_skip_bp_addr == NULL); -+ -+ self->atomic_skip_bp_addr = addr + 4; -+ insert_breakpoint(proc->leader, self->atomic_skip_bp_addr, NULL, 1); -+ -+ return 0; -+} -+ -+static int -+singlestep(struct process_stopping_handler *self) - { -+ struct Process *proc = self->task_enabling_breakpoint; -+ -+ int status = arch_atomic_singlestep(self->task_enabling_breakpoint, -+ self->breakpoint_being_enabled, -+ &atomic_singlestep_add_bp, self); -+ -+ /* Propagate failure and success. */ -+ if (status <= 0) -+ return status; -+ -+ /* Otherwise do the default action: singlestep. */ - debug(1, "PTRACE_SINGLESTEP"); -- if (ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0)) -+ if (ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0)) { - perror("PTRACE_SINGLESTEP"); -+ return -1; -+ } -+ return 0; -+} -+ -+static void -+post_singlestep(struct process_stopping_handler *self, Event **eventp) -+{ -+ continue_for_sigstop_delivery(&self->pids); -+ -+ if ((*eventp)->type == EVENT_BREAKPOINT) -+ *eventp = NULL; // handled -+ -+ if (self->atomic_skip_bp_addr != 0) -+ delete_breakpoint(self->task_enabling_breakpoint->leader, -+ self->atomic_skip_bp_addr); -+ -+ self->breakpoint_being_enabled = NULL; -+} -+ -+static void -+singlestep_error(struct process_stopping_handler *self, Event **eventp) -+{ -+ struct Process *teb = self->task_enabling_breakpoint; -+ struct breakpoint *sbp = self->breakpoint_being_enabled; -+ fprintf(stderr, "%d couldn't singlestep over %s (%p)\n", -+ teb->pid, sbp->libsym != NULL ? sbp->libsym->name : NULL, -+ sbp->addr); -+ delete_breakpoint(teb->leader, sbp->addr); -+ post_singlestep(self, eventp); - } - - /* This event handler is installed when we are in the process of -@@ -670,7 +745,11 @@ process_stopping_on_event(Event_Handler * super, Event * event) - teb->pid); - if (sbp->enabled) - disable_breakpoint(teb, sbp); -- singlestep(teb); -+ if (singlestep(self) < 0) { -+ singlestep_error(self, &event); -+ goto psh_sinking; -+ } -+ - self->state = state = psh_singlestep; - } - break; -@@ -682,7 +761,10 @@ process_stopping_on_event(Event_Handler * super, Event * event) - - /* This is not the singlestep that we are waiting for. */ - if (event->type == EVENT_SIGNAL) { -- singlestep(task); -+ if (singlestep(self) < 0) { -+ singlestep_error(self, &event); -+ goto psh_sinking; -+ } - break; - } - -@@ -692,18 +774,13 @@ process_stopping_on_event(Event_Handler * super, Event * event) - if (sbp->enabled) - enable_breakpoint(teb, sbp); - -- continue_for_sigstop_delivery(&self->pids); -- -- self->breakpoint_being_enabled = NULL; -- self->state = state = psh_sinking; -- -- if (event->type == EVENT_BREAKPOINT) -- event = NULL; // handled -- } else -- break; -- -- /* fall-through */ -+ post_singlestep(self, &event); -+ goto psh_sinking; -+ } -+ break; - -+ psh_sinking: -+ state = self->state = psh_sinking; - case psh_sinking: - if (await_sigstop_delivery(&self->pids, task_info, event)) - process_stopping_done(self, leader); -diff --git a/testsuite/ltrace.torture/ppc-lwarx.c b/testsuite/ltrace.torture/ppc-lwarx.c -new file mode 100644 -index 0000000..0716407 ---- /dev/null -+++ b/testsuite/ltrace.torture/ppc-lwarx.c -@@ -0,0 +1,44 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 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 -+ */ -+ -+#include -+ -+__attribute__((noinline, optimize(3))) void -+atomic_add(uint32_t *a, uint32_t b) -+{ -+ __asm__ volatile("lwarx 9,0,%0\n" -+ "add 9,9,%2\n" -+ "stwcx. 9,0,%0\n" -+ "bne- atomic_add\n" -+ : "=r"(a) -+ : "0"(a), "r"(b) -+ : "%r9"); -+} -+ -+uint32_t a = 0; -+ -+__attribute__((optimize(0))) int -+main(int argc, char **argv) -+{ -+ atomic_add(&a, 5); -+ atomic_add(&a, 10); -+ atomic_add(&a, 15); -+ return a; -+} -diff --git a/testsuite/ltrace.torture/ppc-lwarx.exp b/testsuite/ltrace.torture/ppc-lwarx.exp -new file mode 100644 -index 0000000..bc2eba4 ---- /dev/null -+++ b/testsuite/ltrace.torture/ppc-lwarx.exp -@@ -0,0 +1,55 @@ -+# This file is part of ltrace. -+# Copyright (C) 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 -+ -+set testfile "ppc-lwarx" -+set srcfile ${testfile}.c -+set binfile ${testfile} -+ -+if [get_compiler_info $binfile] { -+ return -1 -+} -+ -+if { [istarget powerpc*-*] } then { -+ verbose "compiling source file now....." -+ if { [ltrace_compile $srcdir/$subdir/$srcfile $objdir/$subdir/$binfile executable {debug} ] != "" } { -+ send_user "Testcase compile failed, so all tests in this file will automatically fail\n." -+ } -+ -+ # set options for ltrace. -+ ltrace_options "-x" "atomic_add" "-e" "!atoi" -+ -+ # Run PUT for ltarce. -+ set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile] -+ -+ # Check the output of this program. -+ verbose "ltrace runtest output: $exec_output\n" -+ if [regexp {ELF from incompatible architecture} $exec_output] { -+ fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" -+ return -+ } elseif [ regexp {Couldn't get .hash data} $exec_output ] { -+ fail "Couldn't get .hash data!" -+ return -+ } -+ -+ set pattern "atomic_add(.*, 5,.*)" -+ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ set pattern "atomic_add(.*, 10,.*)" -+ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+ set pattern "atomic_add(.*, 15,.*)" -+ ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 -+} diff --git a/ltrace-0.6.0-process-start.patch b/ltrace-0.6.0-process-start.patch deleted file mode 100644 index 1a95e25..0000000 --- a/ltrace-0.6.0-process-start.patch +++ /dev/null @@ -1,1053 +0,0 @@ -diff --git a/breakpoint.h b/breakpoint.h -new file mode 100644 -index 0000000..ce6f501 ---- /dev/null -+++ b/breakpoint.h -@@ -0,0 +1,106 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2009 Juan Cespedes -+ * -+ * 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 BREAKPOINT_H -+#define BREAKPOINT_H -+ -+/* XXX This is currently a very weak abstraction. We would like to -+ * much expand this to allow things like breakpoints on SDT probes and -+ * such. -+ * -+ * In particular, we would like to add a tracepoint abstraction. -+ * Tracepoint is a traceable feature--e.g. an exact address, a DWARF -+ * symbol, an ELF symbol, a PLT entry, or an SDT probe. Tracepoints -+ * are named and the user can configure which of them he wants to -+ * enable. Realized tracepoints enable breakpoints, which are a -+ * low-level realization of high-level tracepoint. -+ * -+ * Tracepoints are provided by the main binary as well as by any -+ * opened libraries: every time an ELF file is mapped into the address -+ * space, a new set of tracepoints is extracted, and filtered -+ * according to user settings. Those tracepoints that are left are -+ * then realized, and the tracing starts. -+ * -+ * A scheme like this would take care of gradually introducing -+ * breakpoints when the library is mapped, and therefore ready, and -+ * would avoid certain hacks. For example on PPC64, we don't actually -+ * add breakpoints to PLT. Instead, we read the PLT (which contains -+ * addresses, not code), to figure out where to put the breakpoints. -+ * In prelinked code, that address is non-zero, and points to an -+ * address that's not yet mapped. ptrace then fails when we try to -+ * add the breakpoint. -+ * -+ * Ideally, return breakpoints would be just a special kind of -+ * tracepoint that has attached some magic. Or a feature of a -+ * tracepoint. Service breakpoints like the handling of dlopen would -+ * be a low-level breakpoint, likely without tracepoint attached. -+ * -+ * So that's for sometimes. -+ */ -+ -+#include "arch.h" -+ -+struct Process; -+struct breakpoint; -+ -+struct bp_callbacks { -+ void (*on_hit) (struct breakpoint *bp, struct Process *proc); -+ void (*on_destroy) (struct breakpoint *bp); -+}; -+ -+struct breakpoint { -+ struct bp_callbacks *cbs; -+ void *addr; -+ unsigned char orig_value[BREAKPOINT_LENGTH]; -+ int enabled; -+ struct library_symbol *libsym; -+#ifdef __arm__ -+ int thumb_mode; -+#endif -+}; -+ -+/* Call on-hit handler of BP, if any is set. */ -+void breakpoint_on_hit(struct breakpoint *bp, struct Process *proc); -+ -+/* Call on-destroy handler of BP, if any is set. */ -+void breakpoint_on_destroy(struct breakpoint *bp); -+ -+/* This is actually three functions rolled in one: -+ * - breakpoint_init -+ * - proc_insert_breakpoint -+ * - breakpoint_enable -+ * XXX I think it should be broken up somehow. */ -+struct breakpoint *insert_breakpoint(struct Process *proc, void *addr, -+ struct library_symbol *libsym, int enable); -+ -+/* */ -+void delete_breakpoint(struct Process *proc, void *addr); -+ -+/* XXX some of the following belongs to proc.h/proc.c. */ -+struct breakpoint *address2bpstruct(struct Process *proc, void *addr); -+void enable_all_breakpoints(struct Process *proc); -+void disable_all_breakpoints(struct Process *proc); -+int breakpoints_init(struct Process *proc, int enable); -+ -+void reinitialize_breakpoints(struct Process *proc); -+ -+ -+#endif /* BREAKPOINT_H */ -diff --git a/breakpoints.c b/breakpoints.c -index 387b2a5..5713fe4 100644 ---- a/breakpoints.c -+++ b/breakpoints.c -@@ -8,12 +8,30 @@ - #include - #endif - -+#include "breakpoint.h" - #include "common.h" - -+void -+breakpoint_on_hit(struct breakpoint *bp, struct Process *proc) -+{ -+ assert(bp != NULL); -+ if (bp->cbs != NULL && bp->cbs->on_hit != NULL) -+ (bp->cbs->on_hit) (bp, proc); -+} -+ -+void -+breakpoint_on_destroy(struct breakpoint *bp) -+{ -+ assert(bp != NULL); -+ if (bp->cbs != NULL && bp->cbs->on_destroy != NULL) -+ (bp->cbs->on_destroy) (bp); -+} -+ - /*****************************************************************************/ - --Breakpoint * --address2bpstruct(Process *proc, void *addr) { -+struct breakpoint * -+address2bpstruct(Process *proc, void *addr) -+{ - assert(proc != NULL); - assert(proc->breakpoints != NULL); - assert(proc->leader == proc); -@@ -21,10 +39,11 @@ address2bpstruct(Process *proc, void *addr) { - return dict_find_entry(proc->breakpoints, addr); - } - --void -+struct breakpoint * - insert_breakpoint(Process *proc, void *addr, -- struct library_symbol *libsym, int enable) { -- Breakpoint *sbp; -+ struct library_symbol *libsym, int enable) -+{ -+ struct breakpoint *sbp; - - Process * leader = proc->leader; - -@@ -43,16 +62,16 @@ insert_breakpoint(Process *proc, void *addr, - debug(1, "symbol=%s, addr=%p", libsym?libsym->name:"(nil)", addr); - - if (!addr) -- return; -+ return NULL; - - if (libsym) - libsym->needs_init = 0; - - sbp = dict_find_entry(leader->breakpoints, addr); -- if (!sbp) { -- sbp = calloc(1, sizeof(Breakpoint)); -- if (!sbp) { -- return; /* TODO FIXME XXX: error_mem */ -+ if (sbp == NULL) { -+ sbp = calloc(1, sizeof(*sbp)); -+ if (sbp == NULL) { -+ return NULL; /* TODO FIXME XXX: error_mem */ - } - dict_enter(leader->breakpoints, addr, sbp); - sbp->addr = addr; -@@ -67,11 +86,14 @@ insert_breakpoint(Process *proc, void *addr, - assert(proc->pid != 0); - enable_breakpoint(proc, sbp); - } -+ -+ return sbp; - } - - void --delete_breakpoint(Process *proc, void *addr) { -- Breakpoint *sbp; -+delete_breakpoint(Process *proc, void *addr) -+{ -+ struct breakpoint *sbp; - - debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr); - -@@ -91,89 +113,67 @@ delete_breakpoint(Process *proc, void *addr) { - } - - static void --enable_bp_cb(void *addr, void *sbp, void *proc) { -+enable_bp_cb(void *addr, void *sbp, void *proc) -+{ - debug(DEBUG_FUNCTION, "enable_bp_cb(pid=%d)", ((Process *)proc)->pid); -- if (((Breakpoint *)sbp)->enabled) { -+ if (((struct breakpoint *)sbp)->enabled) - enable_breakpoint(proc, sbp); -- } - } - - void --enable_all_breakpoints(Process *proc) { -+enable_all_breakpoints(Process *proc) -+{ - debug(DEBUG_FUNCTION, "enable_all_breakpoints(pid=%d)", proc->pid); -- if (proc->breakpoints_enabled <= 0) { --#ifdef __powerpc__ -- unsigned long a; - -+ debug(1, "Enabling breakpoints for pid %u...", proc->pid); -+ if (proc->breakpoints) { -+ dict_apply_to_all(proc->breakpoints, enable_bp_cb, -+ proc); -+ } -+#ifdef __mips__ -+ { - /* -- * PPC HACK! (XXX FIXME TODO) -- * If the dynamic linker hasn't populated the PLT then -- * dont enable the breakpoints -+ * I'm sure there is a nicer way to do this. We need to -+ * insert breakpoints _after_ the child has been started. - */ -- if (options.libcalls) { -- a = ptrace(PTRACE_PEEKTEXT, proc->pid, -- sym2addr(proc, proc->list_of_symbols), -- 0); -- if (a == 0x0) -- return; -- } --#endif -- -- debug(1, "Enabling breakpoints for pid %u...", proc->pid); -- if (proc->breakpoints) { -- 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); -+ 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 - } -- proc->breakpoints_enabled = 1; -+#endif - } - - static void --disable_bp_cb(void *addr, void *sbp, void *proc) { -+disable_bp_cb(void *addr, void *sbp, void *proc) -+{ - debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid); -- if (((Breakpoint *)sbp)->enabled) { -+ if (((struct breakpoint *)sbp)->enabled) - disable_breakpoint(proc, sbp); -- } - } - - void - disable_all_breakpoints(Process *proc) { - debug(DEBUG_FUNCTION, "disable_all_breakpoints(pid=%d)", proc->pid); - assert(proc->leader == proc); -- if (proc->breakpoints_enabled) { -- debug(1, "Disabling breakpoints for pid %u...", proc->pid); -- dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc); -- } -- proc->breakpoints_enabled = 0; -+ dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc); - } - - static void -@@ -183,11 +183,18 @@ free_bp_cb(void *addr, void *sbp, void *data) { - free(sbp); - } - -+static void -+entry_callback_hit(struct breakpoint *bp, struct Process *proc) -+{ -+ if (proc == NULL || proc->leader == NULL) -+ return; -+ delete_breakpoint(proc, bp->addr); // xxx -+ reinitialize_breakpoints(proc->leader); -+} -+ - int - breakpoints_init(Process *proc, int enable) - { -- struct library_symbol *sym; -- - debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid); - if (proc->breakpoints) { /* let's remove that struct */ - dict_apply_to_all(proc->breakpoints, free_bp_cb, NULL); -@@ -206,9 +213,11 @@ breakpoints_init(Process *proc, int enable) - destroy_library_symbol_chain(proc->list_of_symbols); - proc->list_of_symbols = NULL; - -+ GElf_Addr entry; - if (options.libcalls && proc->filename) { -- proc->list_of_symbols = read_elf(proc); -+ proc->list_of_symbols = read_elf(proc, &entry); - if (proc->list_of_symbols == NULL) { -+ fail: - /* XXX leak breakpoints */ - return -1; - } -@@ -235,11 +244,19 @@ breakpoints_init(Process *proc, int enable) - } - } - -- for (sym = proc->list_of_symbols; sym; sym = sym->next) -- insert_breakpoint(proc, sym2addr(proc, sym), sym, enable); -+ struct breakpoint *entry_bp -+ = insert_breakpoint(proc, (void *)(uintptr_t)entry, NULL, 1); -+ if (entry_bp == NULL) { -+ fprintf(stderr, "fail!\n"); -+ goto fail; -+ } -+ -+ static struct bp_callbacks entry_callbacks = { -+ .on_hit = entry_callback_hit, -+ }; -+ entry_bp->cbs = &entry_callbacks; - - proc->callstack_depth = 0; -- proc->breakpoints_enabled = -1; - return 0; - } - -diff --git a/common.h b/common.h -index 715898d..a47cd89 100644 ---- a/common.h -+++ b/common.h -@@ -26,17 +26,6 @@ extern char * command; - - extern int exiting; /* =1 if we have to exit ASAP */ - --typedef struct Breakpoint Breakpoint; --struct Breakpoint { -- void * addr; -- unsigned char orig_value[BREAKPOINT_LENGTH]; -- int enabled; -- struct library_symbol * libsym; --#ifdef __arm__ -- int thumb_mode; --#endif --}; -- - enum arg_type { - ARGTYPE_UNKNOWN = -1, - ARGTYPE_VOID, -@@ -187,11 +176,10 @@ struct Process { - pid_t pid; - - /* Dictionary of breakpoints (which is a mapping -- * address->Breakpoint). This is NULL for non-leader -+ * address->breakpoint). This is NULL for non-leader - * processes. */ - Dict * breakpoints; - -- int breakpoints_enabled; /* -1:not enabled yet, 0:disabled, 1:enabled */ - int mask_32bit; /* 1 if 64-bit ltrace is tracing 32-bit process */ - unsigned int personality; - int tracesysgood; /* signal indicating a PTRACE_SYSCALL trap */ -@@ -296,14 +284,7 @@ extern void destroy_event_handler(Process * proc); - - extern pid_t execute_program(const char * command, char ** argv); - extern int display_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); --extern Breakpoint * address2bpstruct(Process * proc, void * addr); --extern int breakpoints_init(Process * proc, int enable); --extern void insert_breakpoint(Process * proc, void * addr, -- struct library_symbol * libsym, int enable); --extern void delete_breakpoint(Process * proc, void * addr); --extern void enable_all_breakpoints(Process * proc); - extern void disable_all_breakpoints(Process * proc); --extern void reinitialize_breakpoints(Process *); - - extern Process * open_program(char * filename, pid_t pid, int init_breakpoints); - extern void open_pid(pid_t pid); -@@ -322,6 +303,8 @@ extern struct library_symbol * clone_library_symbol(struct library_symbol * s); - extern void destroy_library_symbol(struct library_symbol * s); - extern void destroy_library_symbol_chain(struct library_symbol * chain); - -+struct breakpoint; -+ - /* Arch-dependent stuff: */ - extern char * pid2name(pid_t pid); - extern pid_t process_leader(pid_t pid); -@@ -329,6 +312,7 @@ extern int process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n); - extern int process_stopped(pid_t pid); - extern enum process_status process_status(pid_t pid); - extern void trace_set_options(Process * proc, pid_t pid); -+extern void wait_for_proc(pid_t pid); - extern void trace_me(void); - extern int trace_pid(pid_t pid); - extern void untrace_pid(pid_t pid); -@@ -338,13 +322,13 @@ extern void set_instruction_pointer(Process * proc, void * addr); - extern void * get_stack_pointer(Process * proc); - extern void * get_return_addr(Process * proc, void * stack_pointer); - extern void set_return_addr(Process * proc, void * addr); --extern void enable_breakpoint(Process * proc, Breakpoint * sbp); --extern void disable_breakpoint(Process * proc, Breakpoint * sbp); -+extern void enable_breakpoint(Process * proc, struct breakpoint *sbp); -+extern void disable_breakpoint(Process * proc, struct breakpoint *sbp); - extern int syscall_p(Process * proc, int status, int * sysnum); - extern void continue_process(pid_t pid); - extern void continue_after_signal(pid_t pid, int signum); - extern void continue_after_syscall(Process *proc, int sysnum, int ret_p); --extern void continue_after_breakpoint(Process * proc, Breakpoint * sbp); -+extern void continue_after_breakpoint(Process * proc, struct breakpoint *sbp); - extern void continue_after_vfork(Process * proc); - extern void ltrace_exiting(void); - extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); -diff --git a/execute_program.c b/execute_program.c -index 47f514d..859f32c 100644 ---- a/execute_program.c -+++ b/execute_program.c -@@ -89,6 +89,8 @@ execute_program(const char * command, char **argv) - _exit(1); - } - -+ wait_for_proc(pid); -+ - debug(1, "PID=%d", pid); - - return pid; -diff --git a/handle_event.c b/handle_event.c -index 203459c..c146eb9 100644 ---- a/handle_event.c -+++ b/handle_event.c -@@ -1,6 +1,6 @@ -+#define _GNU_SOURCE - #include "config.h" - --#define _GNU_SOURCE - #include - #include - #include -@@ -9,12 +9,13 @@ - #include - #include - --#include "common.h" -- - #ifdef __powerpc__ - #include - #endif - -+#include "common.h" -+#include "breakpoint.h" -+ - static void handle_signal(Event *event); - static void handle_exit(Event *event); - static void handle_exit_signal(Event *event); -@@ -155,19 +156,18 @@ address_clone(void * addr, void * data) - } - - static void * --breakpoint_clone(void * bp, void * data) -+breakpoint_clone(void *bp, void *data) - { -- Breakpoint * b; -- Dict * map = data; -+ Dict *map = data; - debug(DEBUG_FUNCTION, "breakpoint_clone(%p)", bp); -- b = malloc(sizeof(Breakpoint)); -+ struct breakpoint *b = malloc(sizeof(*b)); - if (!b) { - perror("malloc()"); - exit(1); - } -- memcpy(b, bp, sizeof(Breakpoint)); -+ memcpy(b, bp, sizeof(*b)); - if (b->libsym != NULL) { -- struct library_symbol * sym = dict_find_entry(map, b->libsym); -+ struct library_symbol *sym = dict_find_entry(map, b->libsym); - if (b->libsym == NULL) { - fprintf(stderr, "Can't find cloned symbol %s.\n", - b->libsym->name); -@@ -452,9 +452,6 @@ handle_syscall(Event *event) { - output_left(LT_TOF_SYSCALL, event->proc, - sysname(event->proc, event->e_un.sysnum)); - } -- if (event->proc->breakpoints_enabled == 0) { -- enable_all_breakpoints(event->proc); -- } - } - continue_after_syscall(event->proc, event->e_un.sysnum, 0); - } -@@ -462,7 +459,6 @@ handle_syscall(Event *event) { - static void - handle_exec(Event * event) { - Process * proc = event->proc; -- pid_t saved_pid; - - debug(DEBUG_FUNCTION, "handle_exec(pid=%d)", proc->pid); - if (proc->state == STATE_IGNORED) { -@@ -476,10 +472,7 @@ handle_exec(Event * event) { - proc->arch_ptr = NULL; - free(proc->filename); - proc->filename = pid2name(proc->pid); -- saved_pid = proc->pid; -- proc->pid = 0; - breakpoints_init(proc, 0); -- proc->pid = saved_pid; - proc->callstack_depth = 0; - continue_process(proc->pid); - } -@@ -493,9 +486,6 @@ handle_arch_syscall(Event *event) { - output_left(LT_TOF_SYSCALL, event->proc, - arch_sysname(event->proc, event->e_un.sysnum)); - } -- if (event->proc->breakpoints_enabled == 0) { -- enable_all_breakpoints(event->proc); -- } - } - continue_process(event->proc->pid); - } -@@ -559,14 +549,11 @@ handle_arch_sysret(Event *event) { - continue_process(event->proc->pid); - } - --#ifdef __powerpc__ --void *get_count_register (Process *proc); --#endif -- - static void --handle_breakpoint(Event *event) { -+handle_breakpoint(Event *event) -+{ - int i, j; -- Breakpoint *sbp; -+ struct breakpoint *sbp; - Process *leader = event->proc->leader; - - /* The leader has terminated. */ -@@ -578,28 +565,6 @@ handle_breakpoint(Event *event) { - debug(DEBUG_FUNCTION, "handle_breakpoint(pid=%d, addr=%p)", event->proc->pid, event->e_un.brk_addr); - debug(2, "event: breakpoint (%p)", event->e_un.brk_addr); - --#ifdef __powerpc__ -- /* Need to skip following NOP's to prevent a fake function from being stacked. */ -- long stub_addr = (long) get_count_register(event->proc); -- Breakpoint *stub_bp = NULL; -- char nop_instruction[] = PPC_NOP; -- -- stub_bp = address2bpstruct(leader, event->e_un.brk_addr); -- -- if (stub_bp) { -- unsigned char *bp_instruction = stub_bp->orig_value; -- -- if (memcmp(bp_instruction, nop_instruction, -- PPC_NOP_LENGTH) == 0) { -- if (stub_addr != (long) event->e_un.brk_addr) { -- set_instruction_pointer (event->proc, event->e_un.brk_addr + 4); -- continue_process(event->proc->pid); -- return; -- } -- } -- } --#endif -- - for (i = event->proc->callstack_depth - 1; i >= 0; i--) { - if (event->e_un.brk_addr == - event->proc->callstack[i].return_addr) { -@@ -679,6 +644,8 @@ handle_breakpoint(Event *event) { - } - - if ((sbp = address2bpstruct(leader, event->e_un.brk_addr))) { -+ breakpoint_on_hit(sbp, event->proc); -+ - if (sbp->libsym == NULL) { - continue_after_breakpoint(event->proc, sbp); - return; -@@ -696,12 +663,6 @@ handle_breakpoint(Event *event) { - callstack_push_symfunc(event->proc, sbp->libsym); - output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym->name); - } --#ifdef PLT_REINITALISATION_BP -- if (event->proc->need_to_reinitialize_breakpoints -- && (strcmp(sbp->libsym->name, PLTs_initialized_by_here) == -- 0)) -- reinitialize_breakpoints(leader); --#endif - - continue_after_breakpoint(event->proc, sbp); - return; -diff --git a/ltrace-elf.c b/ltrace-elf.c -index 8dbc298..f7fc239 100644 ---- a/ltrace-elf.c -+++ b/ltrace-elf.c -@@ -662,7 +662,8 @@ opd2addr(struct ltelf *lte, GElf_Addr addr) { - } - - struct library_symbol * --read_elf(Process *proc) { -+read_elf(Process *proc, GElf_Addr *entryp) -+{ - struct ltelf lte[MAX_LIBRARIES + 1]; - size_t i; - struct opt_x_t *xptr; -@@ -700,7 +701,6 @@ read_elf(Process *proc) { - #ifdef __mips__ - // MIPS doesn't use the PLT and the GOT entries get changed - // on startup. -- proc->need_to_reinitialize_breakpoints = 1; - for(i=lte->mips_gotsym; idynsym_count;i++){ - GElf_Sym sym; - const char *name; -@@ -745,11 +745,6 @@ read_elf(Process *proc) { - "Couldn't get relocation from \"%s\"", - proc->filename); - --#ifdef PLT_REINITALISATION_BP -- if (!sym.st_value && PLTs_initialized_by_here) -- proc->need_to_reinitialize_breakpoints = 1; --#endif -- - name = lte->dynstr + sym.st_name; - count = library_num ? library_num+1 : 0; - -@@ -772,30 +767,6 @@ read_elf(Process *proc) { - } - } - #endif // !__mips__ --#ifdef PLT_REINITALISATION_BP -- struct opt_x_t *main_cheat; -- -- if (proc->need_to_reinitialize_breakpoints) { -- /* Add "PLTs_initialized_by_here" to opt_x list, if not -- already there. */ -- main_cheat = (struct opt_x_t *)malloc(sizeof(struct opt_x_t)); -- if (main_cheat == NULL) -- error(EXIT_FAILURE, 0, "Couldn't allocate memory"); -- main_cheat->next = opt_x_loc; -- main_cheat->found = 0; -- main_cheat->name = PLTs_initialized_by_here; -- -- for (xptr = opt_x_loc; xptr; xptr = xptr->next) -- if (strcmp(xptr->name, PLTs_initialized_by_here) == 0 -- && main_cheat) { -- free(main_cheat); -- main_cheat = NULL; -- break; -- } -- if (main_cheat) -- opt_x_loc = main_cheat; -- } --#endif - } else { - lib_tail = &library_symbols; - } -@@ -850,16 +821,17 @@ read_elf(Process *proc) { - } - } - -+ if (lte->ehdr.e_entry != 0) { -+ *entryp = opd2addr(lte, lte->ehdr.e_entry); -+ } else { -+ } -+ - for (xptr = opt_x_loc; xptr; xptr = xptr->next) - if ( ! xptr->found) { - char *badthing = "WARNING"; - #ifdef PLT_REINITALISATION_BP - if (strcmp(xptr->name, PLTs_initialized_by_here) == 0) { - if (lte->ehdr.e_entry) { -- add_library_symbol ( -- opd2addr (lte, lte->ehdr.e_entry), -- PLTs_initialized_by_here, -- lib_tail, 1, 0); - fprintf (stderr, "WARNING: Using e_ent" - "ry from elf header (%p) for " - "address of \"%s\"\n", (void*) -diff --git a/ltrace-elf.h b/ltrace-elf.h -index 3b675c5..4da8a0a 100644 ---- a/ltrace-elf.h -+++ b/ltrace-elf.h -@@ -45,7 +45,7 @@ extern size_t library_num; - extern char *library[MAX_LIBRARIES]; - - extern int open_elf(struct ltelf *lte, const char *filename); --extern struct library_symbol *read_elf(Process *); -+extern struct library_symbol *read_elf(Process *proc, GElf_Addr *entryp); - - extern GElf_Addr arch_plt_sym_val(struct ltelf *, size_t, GElf_Rela *); - -diff --git a/proc.c b/proc.c -index f4d3396..5febc3f 100644 ---- a/proc.c -+++ b/proc.c -@@ -14,6 +14,7 @@ - #include - - #include "common.h" -+#include "breakpoint.h" - - Process * - open_program(char *filename, pid_t pid, int enable) { -@@ -26,7 +27,6 @@ open_program(char *filename, pid_t pid, int enable) { - } - - proc->filename = strdup(filename); -- proc->breakpoints_enabled = -1; - proc->pid = pid; - #if defined(HAVE_LIBUNWIND) - proc->unwind_priv = _UPT_create(pid); -@@ -39,13 +39,15 @@ open_program(char *filename, pid_t pid, int enable) { - return NULL; - } - -- if (proc->leader == proc) -+ if (proc->leader == proc) { -+ trace_set_options(proc, proc->pid); - if (breakpoints_init(proc, enable)) { - fprintf(stderr, "failed to init breakpoints %d\n", - proc->pid); - remove_process(proc); - return NULL; - } -+ } - - return proc; - } -@@ -73,11 +75,10 @@ open_one_pid(pid_t pid) - return 0; - } - --enum pcb_status -+static enum pcb_status - start_one_pid(Process * proc, void * data) - { - continue_process(proc->pid); -- proc->breakpoints_enabled = 1; - return pcb_cont; - } - -@@ -116,7 +117,7 @@ open_pid(pid_t pid) - if (process_tasks(pid, &tasks, &ntasks) < 0) { - fprintf(stderr, "Cannot obtain tasks of pid %u: %s\n", - pid, strerror(errno)); -- goto start; -+ break; - } - - have_all = 1; -@@ -135,7 +136,6 @@ open_pid(pid_t pid) - /* Done. Now initialize breakpoints and then continue - * everyone. */ - Process * leader; --start: - leader = pid2proc(pid)->leader; - enable_all_breakpoints(leader); - -diff --git a/sysdeps/linux-gnu/arm/breakpoint.c b/sysdeps/linux-gnu/arm/breakpoint.c -index 4e17940..493f973 100644 ---- a/sysdeps/linux-gnu/arm/breakpoint.c -+++ b/sysdeps/linux-gnu/arm/breakpoint.c -@@ -27,7 +27,8 @@ - #include "common.h" - - void --arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) { -+arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp) -+{ - unsigned int i, j; - const unsigned char break_insn[] = BREAKPOINT_VALUE; - const unsigned char thumb_break_insn[] = THUMB_BREAKPOINT_VALUE; -@@ -59,7 +60,8 @@ arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) { - } - - void --arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp) { -+arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) -+{ - unsigned int i, j; - - debug(1, "arch_disable_breakpoint(%d,%p)", pid, sbp->addr); -diff --git a/sysdeps/linux-gnu/breakpoint.c b/sysdeps/linux-gnu/breakpoint.c -index 5a49e9d..b98374b 100644 ---- a/sysdeps/linux-gnu/breakpoint.c -+++ b/sysdeps/linux-gnu/breakpoint.c -@@ -5,12 +5,13 @@ - - #include "common.h" - #include "arch.h" -+#include "breakpoint.h" - - #ifdef ARCH_HAVE_ENABLE_BREAKPOINT --extern void arch_enable_breakpoint(pid_t, Breakpoint *); -+extern void arch_enable_breakpoint(pid_t, struct breakpoint *); - #else /* ARCH_HAVE_ENABLE_BREAKPOINT */ - void --arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) -+arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp) - { - static unsigned char break_insn[] = BREAKPOINT_VALUE; - unsigned int i, j; -@@ -38,7 +39,8 @@ arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) - #endif /* ARCH_HAVE_ENABLE_BREAKPOINT */ - - void --enable_breakpoint(Process * proc, Breakpoint *sbp) { -+enable_breakpoint(Process *proc, struct breakpoint *sbp) -+{ - if (sbp->libsym) { - debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p, symbol=%s", proc->pid, sbp->addr, sbp->libsym->name); - } else { -@@ -48,10 +50,10 @@ enable_breakpoint(Process * proc, Breakpoint *sbp) { - } - - #ifdef ARCH_HAVE_DISABLE_BREAKPOINT --extern void arch_disable_breakpoint(pid_t, const Breakpoint *sbp); -+extern void arch_disable_breakpoint(pid_t, const struct breakpoint *sbp); - #else /* ARCH_HAVE_DISABLE_BREAKPOINT */ - void --arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp) -+arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) - { - unsigned int i, j; - -@@ -78,7 +80,8 @@ arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp) - #endif /* ARCH_HAVE_DISABLE_BREAKPOINT */ - - void --disable_breakpoint(Process * proc, Breakpoint *sbp) { -+disable_breakpoint(Process *proc, struct breakpoint *sbp) -+{ - if (sbp->libsym) { - debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p, symbol=%s", proc->pid, sbp->addr, sbp->libsym->name); - } else { -diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c -index 021192f..3174c1a 100644 ---- a/sysdeps/linux-gnu/events.c -+++ b/sysdeps/linux-gnu/events.c -@@ -12,6 +12,7 @@ - #include - - #include "common.h" -+#include "breakpoint.h" - - static Event event; - -@@ -166,19 +167,9 @@ next_event(void) - } - get_arch_dep(event.proc); - debug(3, "event from pid %u", pid); -- if (event.proc->breakpoints_enabled == -1) -- trace_set_options(event.proc, event.proc->pid); - Process *leader = event.proc->leader; - if (leader == event.proc) { -- if (event.proc->breakpoints_enabled == -1) { -- event.type = EVENT_NONE; -- enable_all_breakpoints(event.proc); -- continue_process(event.proc->pid); -- debug(DEBUG_EVENT, -- "event: NONE: pid=%d (enabling breakpoints)", -- pid); -- return &event; -- } else if (!event.proc->libdl_hooked) { -+ if (!event.proc->libdl_hooked) { - /* debug struct may not have been written yet.. */ - if (linkmap_init(event.proc, &main_lte) == 0) { - event.proc->libdl_hooked = 1; -diff --git a/sysdeps/linux-gnu/ia64/breakpoint.c b/sysdeps/linux-gnu/ia64/breakpoint.c -index 45ee11e..a0bfaf9 100644 ---- a/sysdeps/linux-gnu/ia64/breakpoint.c -+++ b/sysdeps/linux-gnu/ia64/breakpoint.c -@@ -150,7 +150,8 @@ union bundle_t { - }; - - void --arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) { -+arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp) -+{ - - unsigned long addr = (unsigned long)sbp->addr; - union bundle_t bundle; -@@ -187,7 +188,8 @@ arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) { - } - - void --arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp) { -+arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) -+{ - - unsigned long addr = (unsigned long)sbp->addr; - int slotnum = (int)(addr & 0x0f) & 0x3; -diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c -index a99593c..3350117 100644 ---- a/sysdeps/linux-gnu/proc.c -+++ b/sysdeps/linux-gnu/proc.c -@@ -1,6 +1,5 @@ - #define _GNU_SOURCE /* For getline. */ - #include "config.h" --#include "common.h" - - #include - #include -@@ -17,6 +16,8 @@ - #include - #include - -+#include "common.h" -+#include "breakpoint.h" - - /* /proc/pid doesn't exist just after the fork, and sometimes `ltrace' - * couldn't open it to find the executable. So it may be necessary to -diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c -index db18df0..b4d8fd1 100644 ---- a/sysdeps/linux-gnu/trace.c -+++ b/sysdeps/linux-gnu/trace.c -@@ -11,6 +11,7 @@ - - #include "common.h" - #include "config.h" -+#include "breakpoint.h" - - /* If the system headers did not provide the constants, hard-code the normal - values. */ -@@ -77,6 +78,29 @@ trace_me(void) { - } - } - -+/* There's a (hopefully) brief period of time after the child process -+ * exec's when we can't trace it yet. Here we wait for kernel to -+ * prepare the process. */ -+void -+wait_for_proc(pid_t pid) -+{ -+ size_t i; -+ for (i = 0; i < 100; ++i) { -+ /* We read from memory address 0, but that shouldn't -+ * be a problem: the reading will just fail. We are -+ * looking for a particular reason of failure. */ -+ if (ptrace(PTRACE_PEEKTEXT, pid, 0, 0) != -1 -+ || errno != ESRCH) -+ return; -+ -+ usleep(1000); -+ } -+ -+ fprintf(stderr, "\ -+I consistently fail to read a word from the freshly launched process.\n\ -+I'll now try to proceed with tracing, but this shouldn't be happening.\n"); -+} -+ - int - trace_pid(pid_t pid) { - debug(DEBUG_PROCESS, "trace_pid: pid=%d", pid); -@@ -193,7 +217,7 @@ struct process_stopping_handler - Process * task_enabling_breakpoint; - - /* The pointer being re-enabled. */ -- Breakpoint * breakpoint_being_enabled; -+ struct breakpoint *breakpoint_being_enabled; - - enum { - /* We are waiting for everyone to land in t/T. */ -@@ -364,7 +388,7 @@ static void - ugly_workaround(Process * proc) - { - void * ip = get_instruction_pointer(proc); -- Breakpoint * sbp = dict_find_entry(proc->leader->breakpoints, ip); -+ struct breakpoint *sbp = dict_find_entry(proc->leader->breakpoints, ip); - if (sbp != NULL) - enable_breakpoint(proc, sbp); - else -@@ -578,7 +602,7 @@ process_stopping_on_event(Event_Handler * super, Event * event) - struct process_stopping_handler * self = (void *)super; - Process * task = event->proc; - Process * leader = task->leader; -- Breakpoint * sbp = self->breakpoint_being_enabled; -+ struct breakpoint *sbp = self->breakpoint_being_enabled; - Process * teb = self->task_enabling_breakpoint; - - debug(DEBUG_PROCESS, -@@ -687,7 +711,7 @@ process_stopping_destroy(Event_Handler * super) - } - - void --continue_after_breakpoint(Process *proc, Breakpoint *sbp) -+continue_after_breakpoint(Process *proc, struct breakpoint *sbp) - { - set_instruction_pointer(proc, sbp->addr); - if (sbp->enabled == 0) { -@@ -851,7 +875,7 @@ static Event * - process_vfork_on_event(Event_Handler * super, Event * event) - { - struct process_vfork_handler * self = (void *)super; -- Breakpoint * sbp; -+ struct breakpoint *sbp; - assert(self != NULL); - - switch (event->type) { diff --git a/ltrace-0.6.0-return-string-n.patch b/ltrace-0.6.0-return-string-n.patch deleted file mode 100644 index a3c8320..0000000 --- a/ltrace-0.6.0-return-string-n.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff -up ltrace-0.6.0/output.c\~ ltrace-0.6.0/output.c ---- ltrace-0.6.0/output.c~ 2011-02-14 16:48:25.000000000 +0100 -+++ ltrace-0.6.0/output.c 2011-02-15 00:35:39.000000000 +0100 -@@ -202,7 +204,9 @@ output_left(enum tof type, Process *proc - current_column += fprintf(options.output, ", "); - } - } -- if (func->params_right) { -+ if (func->params_right -+ || func->return_info->type == ARGTYPE_STRING_N -+ || func->return_info->type == ARGTYPE_ARRAY) { - save_register_args(type, proc); - } - } - -Diff finished. Tue Feb 15 00:35:44 2011 diff --git a/ltrace-0.6.0-selinux.patch b/ltrace-0.6.0-selinux.patch deleted file mode 100644 index 4b85e22..0000000 --- a/ltrace-0.6.0-selinux.patch +++ /dev/null @@ -1,212 +0,0 @@ -From cec06ec8282c538a40bde968ae36fe8356daffaa Mon Sep 17 00:00:00 2001 -From: Petr Machata -Date: Tue, 10 Apr 2012 13:31:55 +0200 -Subject: [PATCH] Warn when we fail to trace and SELinux boolean deny_ptrace - is in effect - ---- - ChangeLog | 7 ++++++ - common.h | 4 +++ - configure.ac | 5 ++++ - proc.c | 1 + - sysdeps/linux-gnu/trace.c | 51 ++++++++++++++++++++++++++++++++++++-------- - 5 files changed, 58 insertions(+), 10 deletions(-) - -diff --git a/common.h b/common.h -index fa80076..2399e29 100644 ---- a/common.h -+++ b/common.h -@@ -359,2 +359,6 @@ extern int linkmap_init(Process *, struct ltelf *); - -+/* Called when trace_me or primary trace_pid fail. This may plug in -+ * any platform-specific knowledge of why it could be so. */ -+void trace_fail_warning(pid_t pid); -+ - -diff --git a/configure.ac b/configure.ac -index 7fcfda5..42d6158 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -82,6 +82,11 @@ AC_CHECK_LIB([supc++], [__cxa_demangle], [ - AC_SUBST(libsupcxx_LIBS) - - -+dnl Check security_get_boolean_active availability. -+AC_CHECK_HEADERS(selinux/selinux.h) -+AC_CHECK_LIB(selinux, security_get_boolean_active) -+ -+ - # HAVE_LIBUNWIND - AC_ARG_WITH(libunwind, - AS_HELP_STRING([--with-libunwind], [Use libunwind frame unwinding support]), -diff -up ltrace-0.6.0/configure\~ ltrace-0.6.0/configure ---- ltrace-0.6.0/configure~ 2012-04-11 12:56:00.688263027 +0200 -+++ ltrace-0.6.0/configure 2012-04-11 12:55:49.787935890 +0200 -@@ -11029,6 +11689,65 @@ fi - - - -+for ac_header in selinux/selinux.h -+do : -+ ac_fn_c_check_header_mongrel "$LINENO" "selinux/selinux.h" "ac_cv_header_selinux_selinux_h" "$ac_includes_default" -+if test "x$ac_cv_header_selinux_selinux_h" = xyes; then : -+ cat >>confdefs.h <<_ACEOF -+#define HAVE_SELINUX_SELINUX_H 1 -+_ACEOF -+ -+fi -+ -+done -+ -+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for security_get_boolean_active in -lselinux" >&5 -+$as_echo_n "checking for security_get_boolean_active in -lselinux... " >&6; } -+if ${ac_cv_lib_selinux_security_get_boolean_active+:} false; then : -+ $as_echo_n "(cached) " >&6 -+else -+ ac_check_lib_save_LIBS=$LIBS -+LIBS="-lselinux $LIBS" -+cat confdefs.h - <<_ACEOF >conftest.$ac_ext -+/* end confdefs.h. */ -+ -+/* Override any GCC internal prototype to avoid an error. -+ Use char because int might match the return type of a GCC -+ builtin and then its argument prototype would still apply. */ -+#ifdef __cplusplus -+extern "C" -+#endif -+char security_get_boolean_active (); -+int -+main () -+{ -+return security_get_boolean_active (); -+ ; -+ return 0; -+} -+_ACEOF -+if ac_fn_c_try_link "$LINENO"; then : -+ ac_cv_lib_selinux_security_get_boolean_active=yes -+else -+ ac_cv_lib_selinux_security_get_boolean_active=no -+fi -+rm -f core conftest.err conftest.$ac_objext \ -+ conftest$ac_exeext conftest.$ac_ext -+LIBS=$ac_check_lib_save_LIBS -+fi -+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_selinux_security_get_boolean_active" >&5 -+$as_echo "$ac_cv_lib_selinux_security_get_boolean_active" >&6; } -+if test "x$ac_cv_lib_selinux_security_get_boolean_active" = xyes; then : -+ cat >>confdefs.h <<_ACEOF -+#define HAVE_LIBSELINUX 1 -+_ACEOF -+ -+ LIBS="-lselinux $LIBS" -+ -+fi -+ -+ -+ - # HAVE_LIBUNWIND - - # Check whether --with-libunwind was given. -diff --git a/proc.c b/proc.c -index 106b6a0..ded0c95 100644 ---- a/proc.c -+++ b/proc.c -@@ -94,6 +94,7 @@ open_pid(pid_t pid) - if (open_one_pid(pid)) { - fprintf(stderr, "Cannot attach to pid %u: %s\n", - pid, strerror(errno)); -+ trace_fail_warning(pid); - return; - } - -diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c -index 67e1f93..82a4154 100644 ---- a/sysdeps/linux-gnu/trace.c -+++ b/sysdeps/linux-gnu/trace.c -@@ -10,2 +10,7 @@ - -+#include "config.h" -+#ifdef HAVE_LIBSELINUX -+# include -+#endif -+ - /* If the system headers did not provide the constants, hard-code the normal -@@ -69,10 +75,32 @@ umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { - #endif - - void --trace_me(void) { -+trace_fail_warning(pid_t pid) -+{ -+ /* This was adapted from GDB. */ -+#ifdef HAVE_LIBSELINUX -+ static int checked = 0; -+ if (checked) -+ return; -+ checked = 1; -+ -+ /* -1 is returned for errors, 0 if it has no effect, 1 if -+ * PTRACE_ATTACH is forbidden. */ -+ if (security_get_boolean_active("deny_ptrace") == 1) -+ fprintf(stderr, -+"The SELinux boolean 'deny_ptrace' is enabled, which may prevent ltrace from\n" -+"tracing other processes. You can disable this process attach protection by\n" -+"issuing 'setsebool deny_ptrace=0' in the superuser context.\n"); -+#endif /* HAVE_LIBSELINUX */ -+} -+ -+void -+trace_me(void) -+{ - debug(DEBUG_PROCESS, "trace_me: pid=%d", getpid()); - if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) { - perror("PTRACE_TRACEME"); -+ trace_fail_warning(getpid()); - exit(1); - } - } -@@ -101,11 +129,14 @@ I'll now try to proceed with tracing, but this shouldn't be happening.\n"); - } - - int --trace_pid(pid_t pid) { -+trace_pid(pid_t pid) -+{ - debug(DEBUG_PROCESS, "trace_pid: pid=%d", pid); -- if (ptrace(PTRACE_ATTACH, pid, 1, 0) < 0) { -+ /* This shouldn't emit error messages, as there are legitimate -+ * reasons that the PID can't be attached: like it may have -+ * already ended. */ -+ if (ptrace(PTRACE_ATTACH, pid, 1, 0) < 0) - return -1; -- } - - /* man ptrace: PTRACE_ATTACH attaches to the process specified - in pid. The child is sent a SIGSTOP, but will not --- -1.7.7.6 - -diff -up ./config.h.in~ ./config.h.in ---- ./config.h.in~ 2011-02-14 17:01:18.000000000 +0100 -+++ ./config.h.in 2012-04-11 13:19:10.000000000 +0200 -@@ -45,6 +45,9 @@ - /* we have libiberty */ - #undef HAVE_LIBIBERTY - -+/* Define to 1 if you have the `selinux' library (-lselinux). */ -+#undef HAVE_LIBSELINUX -+ - /* we have libsupc++ */ - #undef HAVE_LIBSUPC__ - -@@ -72,6 +75,9 @@ - /* Define to 1 if you have the `rmdir' function. */ - #undef HAVE_RMDIR - -+/* Define to 1 if you have the header file. */ -+#undef HAVE_SELINUX_SELINUX_H -+ - /* Define to 1 if you have the header file. */ - #undef HAVE_STDDEF_H - diff --git a/ltrace-0.6.0-syscall-time.patch b/ltrace-0.6.0-syscall-time.patch deleted file mode 100644 index 0a8fd3e..0000000 --- a/ltrace-0.6.0-syscall-time.patch +++ /dev/null @@ -1,30 +0,0 @@ -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-tail-return.patch b/ltrace-0.6.0-tail-return.patch deleted file mode 100644 index 1af5e61..0000000 --- a/ltrace-0.6.0-tail-return.patch +++ /dev/null @@ -1,256 +0,0 @@ -diff --git a/handle_event.c b/handle_event.c -index 725f50d..8c3c7ce 100644 ---- a/handle_event.c -+++ b/handle_event.c -@@ -565,11 +565,21 @@ void *get_count_register (Process *proc); - } - - static void -+output_right_tos(struct Process *proc) -+{ -+ size_t d = proc->callstack_depth; -+ struct callstack_element *elem = &proc->callstack[d - 1]; -+ if (proc->state != STATE_IGNORED) -+ output_right(LT_TOF_FUNCTIONR, proc, elem->c_un.libfunc->name); -+} -+ -+static void - handle_breakpoint(Event *event) - { - int i, j; - struct breakpoint *sbp; - Process *leader = event->proc->leader; -+ void *brk_addr = event->e_un.brk_addr; - - /* The leader has terminated. */ - if (leader == NULL) { -@@ -577,12 +587,14 @@ handle_breakpoint(Event *event) - return; - } - -- debug(DEBUG_FUNCTION, "handle_breakpoint(pid=%d, addr=%p)", event->proc->pid, event->e_un.brk_addr); -- debug(2, "event: breakpoint (%p)", event->e_un.brk_addr); -+ debug(DEBUG_FUNCTION, "handle_breakpoint(pid=%d, addr=%p)", -+ event->proc->pid, brk_addr); -+ debug(2, "event: breakpoint (%p)", brk_addr); - - for (i = event->proc->callstack_depth - 1; i >= 0; i--) { -- if (event->e_un.brk_addr == -- event->proc->callstack[i].return_addr) { -+ if (brk_addr == event->proc->callstack[i].return_addr) { -+ struct library_symbol *libsym = -+ event->proc->callstack[i].c_un.libfunc; - #ifdef __powerpc__ - /* - * PPC HACK! (XXX FIXME TODO) -@@ -623,8 +625,6 @@ handle_breakpoint(Event *event) - * so be sure to re-enable the breakpoint. - */ - unsigned long a; -- struct library_symbol *libsym = -- event->proc->callstack[i].c_un.libfunc; - void *addr = sym2addr(event->proc, libsym); - - if (libsym->plt_type != LS_TOPLT_POINT) { -@@ -668,19 +679,37 @@ handle_breakpoint(Event *event) - calc_time_spent(event->proc); - } - } -- event->proc->return_addr = event->e_un.brk_addr; -- if (event->proc->state != STATE_IGNORED) { -- output_right(LT_TOF_FUNCTIONR, event->proc, -- event->proc->callstack[i].c_un.libfunc->name); -- } -+ event->proc->return_addr = brk_addr; -+ -+ output_right_tos(event->proc); - callstack_pop(event->proc); -- sbp = address2bpstruct(leader, event->e_un.brk_addr); -+ -+ /* Pop also any other entries that seem like -+ * they are linked to the current one: they -+ * 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. */ -+ while (event->proc->callstack_depth > 0) { -+ struct callstack_element *prev; -+ size_t d = event->proc->callstack_depth; -+ prev = &event->proc->callstack[d - 1]; -+ -+ if (prev->c_un.libfunc == libsym -+ || prev->return_addr != brk_addr) -+ break; -+ -+ output_right_tos(event->proc); -+ callstack_pop(event->proc); -+ } -+ -+ sbp = address2bpstruct(leader, brk_addr); - continue_after_breakpoint(event->proc, sbp); - return; - } - } - -- if ((sbp = address2bpstruct(leader, event->e_un.brk_addr))) { -+ if ((sbp = address2bpstruct(leader, brk_addr))) { - breakpoint_on_hit(sbp, event->proc); - - if (sbp->libsym == NULL) { -@@ -711,7 +740,7 @@ handle_breakpoint(Event *event) - - if (event->proc->state != STATE_IGNORED && !options.no_plt) { - output_line(event->proc, "unexpected breakpoint at %p", -- (void *)event->e_un.brk_addr); -+ brk_addr); - } - continue_process(event->proc->pid); - } -@@ -742,7 +771,7 @@ callstack_push_syscall(Process *proc, int sysnum) { - - static void - callstack_push_symfunc(Process *proc, struct library_symbol *sym) { -- struct callstack_element *elem, *prev; -+ struct callstack_element *elem; - - debug(DEBUG_FUNCTION, "callstack_push_symfunc(pid=%d, symbol=%s)", proc->pid, sym->name); - /* FIXME: not good -- should use dynamic allocation. 19990703 mortene. */ -@@ -752,8 +781,7 @@ callstack_push_symfunc(Process *proc, struct library_symbol *sym) { - return; - } - -- prev = &proc->callstack[proc->callstack_depth-1]; -- elem = &proc->callstack[proc->callstack_depth]; -+ elem = &proc->callstack[proc->callstack_depth++]; - elem->is_syscall = 0; - elem->c_un.libfunc = sym; - -@@ -763,8 +791,6 @@ callstack_push_symfunc(Process *proc, struct library_symbol *sym) { - } - - /* handle functions like atexit() on mips which have no return */ -- if (elem->return_addr != prev->return_addr) -- proc->callstack_depth++; - if (opt_T || options.summary) { - struct timezone tz; - gettimeofday(&elem->time_spent, &tz); -diff --git a/testsuite/ltrace.main/branch_func.c b/testsuite/ltrace.main/branch_func.c -new file mode 100644 -index 0000000..0ce311d ---- /dev/null -+++ b/testsuite/ltrace.main/branch_func.c -@@ -0,0 +1,51 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 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 -+ */ -+ -+/* This is specially made to produce tail calls. We then trace them -+ * and see if ltrace handles it well, or whether its internal stack -+ * overflows. */ -+ -+__attribute__((noinline, optimize(3))) int -+func3(int i) -+{ -+ return i + 1; -+} -+ -+__attribute__((noinline, optimize(3))) int -+func2(int i) -+{ -+ return func3(i * 3); -+} -+ -+__attribute__((noinline, optimize(3))) int -+func1(int i) -+{ -+ return func2(i + 2); -+} -+ -+__attribute__((optimize(0))) int -+main(int argc, char **argv) -+{ -+ int counter = 0; -+ int i; -+ for (i = 0; i < 100; ++i) -+ counter += func1(i); -+ return counter; -+} -diff --git a/testsuite/ltrace.main/branch_func.exp b/testsuite/ltrace.main/branch_func.exp -new file mode 100644 -index 0000000..fec5700 ---- /dev/null -+++ b/testsuite/ltrace.main/branch_func.exp -@@ -0,0 +1,57 @@ -+# This file is part of ltrace. -+# Copyright (C) 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 -+ -+set testfile "branch_func" -+set srcfile ${testfile}.c -+set binfile ${testfile} -+ -+if [get_compiler_info $binfile] { -+ return -1 -+} -+ -+verbose "compiling source file now....." -+if { [ltrace_compile $srcdir/$subdir/$srcfile $objdir/$subdir/$binfile executable {debug} ] != "" } { -+ send_user "Testcase compile failed, so all tests in this file will automatically fail\n." -+} -+ -+# set options for ltrace. -+ltrace_options "-x" "func1" "-x" "func2" "-x" "func3" -+ -+# Run PUT for ltarce. -+set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile] -+ -+# Check the output of this program. -+verbose "ltrace runtest output: $exec_output\n" -+if [regexp {ELF from incompatible architecture} $exec_output] { -+ fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" -+ return -+} elseif [ regexp {Couldn't get .hash data} $exec_output ] { -+ fail "Couldn't get .hash data!" -+ return -+} -+ -+set pattern "func1(.*unfinished" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 100 -+set pattern "func2(.*unfinished" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 100 -+set pattern "func3(.*)" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 100 -+set pattern "func2.resumed" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 100 -+set pattern "func1.resumed" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 100 diff --git a/ltrace-0.6.0-thread-races.patch b/ltrace-0.6.0-thread-races.patch deleted file mode 100644 index 8250aae..0000000 --- a/ltrace-0.6.0-thread-races.patch +++ /dev/null @@ -1,406 +0,0 @@ -diff --git a/common.h b/common.h -index 2fff8dd..715898d 100644 ---- a/common.h -+++ b/common.h -@@ -343,6 +343,7 @@ extern void disable_breakpoint(Process * proc, Breakpoint * sbp); - extern int syscall_p(Process * proc, int status, int * sysnum); - extern void continue_process(pid_t pid); - extern void continue_after_signal(pid_t pid, int signum); -+extern void continue_after_syscall(Process *proc, int sysnum, int ret_p); - extern void continue_after_breakpoint(Process * proc, Breakpoint * sbp); - extern void continue_after_vfork(Process * proc); - extern void ltrace_exiting(void); -diff --git a/handle_event.c b/handle_event.c -index f56c537..203459c 100644 ---- a/handle_event.c -+++ b/handle_event.c -@@ -70,7 +70,9 @@ handle_event(Event *event) { - - /* Note: the previous handler has a chance to alter - * the event. */ -- if (event->proc->leader != NULL) { -+ if (event->proc != NULL -+ && event->proc->leader != NULL -+ && event->proc != event->proc->leader) { - event = call_handler(event->proc->leader, event); - if (event == NULL) - return; -@@ -454,7 +456,7 @@ handle_syscall(Event *event) { - enable_all_breakpoints(event->proc); - } - } -- continue_process(event->proc->pid); -+ continue_after_syscall(event->proc, event->e_un.sysnum, 0); - } - - static void -@@ -533,9 +535,12 @@ handle_sysret(Event *event) { - output_right(LT_TOF_SYSCALLR, event->proc, - sysname(event->proc, event->e_un.sysnum)); - } -+ assert(event->proc->callstack_depth > 0); -+ unsigned d = event->proc->callstack_depth - 1; -+ assert(event->proc->callstack[d].is_syscall); - callstack_pop(event->proc); - } -- continue_process(event->proc->pid); -+ continue_after_syscall(event->proc, event->e_un.sysnum, 1); - } - - static void -@@ -639,7 +644,7 @@ handle_breakpoint(Event *event) { - struct library_symbol *sym= event->proc->callstack[i].c_un.libfunc; - struct library_symbol *new_sym; - assert(sym); -- addr = sym2addr(leader, sym); -+ addr = sym2addr(event->proc, sym); - sbp = dict_find_entry(leader->breakpoints, addr); - if (sbp) { - if (addr != sbp->addr) { -diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c -index 0685342..021192f 100644 ---- a/sysdeps/linux-gnu/events.c -+++ b/sysdeps/linux-gnu/events.c -@@ -9,6 +9,7 @@ - #include - #include - #include -+#include - - #include "common.h" - -@@ -138,6 +139,26 @@ next_event(void) - } - event.proc = pid2proc(pid); - if (!event.proc || event.proc->state == STATE_BEING_CREATED) { -+ /* Work around (presumably) a bug on some kernels, -+ * where we are seeing a waitpid event even though the -+ * process is still reported to be running. Wait for -+ * the tracing stop to propagate. But don't get stuck -+ * here forever. -+ * -+ * We need the process in T, because there's a lot of -+ * ptracing going on all over the place, and these -+ * calls fail when the process is not in T. -+ * -+ * N.B. This was observed on RHEL 5 Itanium, but I'm -+ * turning this on globally, to save some poor soul -+ * down the road (which could well be me a year from -+ * now) the pain of figuring this out all over again. -+ * Petr Machata 2011-11-22. */ -+ int i = 0; -+ for (; i < 100 && process_status(pid) != ps_tracing_stop; ++i) { -+ debug(2, "waiting for %d to stop", pid); -+ usleep(10000); -+ } - event.type = EVENT_NEW; - event.e_un.newpid = pid; - debug(DEBUG_EVENT, "event: NEW: pid=%d", pid); -diff --git a/sysdeps/linux-gnu/ia64/regs.c b/sysdeps/linux-gnu/ia64/regs.c -index 00df572..3f5d951 100644 ---- a/sysdeps/linux-gnu/ia64/regs.c -+++ b/sysdeps/linux-gnu/ia64/regs.c -@@ -2,6 +2,7 @@ - - #include - #include -+#include - - #include - #include -@@ -36,12 +37,18 @@ set_instruction_pointer(Process *proc, void *addr) { - - void * - get_stack_pointer(Process *proc) { -- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, PT_R12, 0); -+ long l = ptrace(PTRACE_PEEKUSER, proc->pid, PT_R12, 0); -+ if (l == -1 && errno) -+ return NULL; -+ return (void *)l; - } - - void * - get_return_addr(Process *proc, void *stack_pointer) { -- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, PT_B0, 0); -+ long l = ptrace(PTRACE_PEEKUSER, proc->pid, PT_B0, 0); -+ if (l == -1 && errno) -+ return NULL; -+ return (void *)l; - } - - void -diff --git a/sysdeps/linux-gnu/ia64/trace.c b/sysdeps/linux-gnu/ia64/trace.c -index 799e0ff..079ed55 100644 ---- a/sysdeps/linux-gnu/ia64/trace.c -+++ b/sysdeps/linux-gnu/ia64/trace.c -@@ -9,6 +9,7 @@ - #include - #include - #include -+#include - - #include "common.h" - -@@ -48,9 +49,10 @@ int - syscall_p(Process *proc, int status, int *sysnum) { - if (WIFSTOPPED(status) - && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { -- unsigned long slot = -- (ptrace(PTRACE_PEEKUSER, proc->pid, PT_CR_IPSR, 0) >> 41) & -- 0x3; -+ long l = ptrace(PTRACE_PEEKUSER, proc->pid, PT_CR_IPSR, 0); -+ if (l == -1 && errno) -+ return -1; -+ unsigned long slot = ((unsigned long)l >> 41) & 0x3; - unsigned long ip = - ptrace(PTRACE_PEEKUSER, proc->pid, PT_CR_IIP, 0); - -diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c -index ba3806d..db18df0 100644 ---- a/sysdeps/linux-gnu/trace.c -+++ b/sysdeps/linux-gnu/trace.c -@@ -146,8 +146,8 @@ continue_process(pid_t pid) - debug(DEBUG_PROCESS, "continue_process: pid=%d", pid); - - /* Only really continue the process if there are no events in -- the queue for this process. Otherwise just for the other -- events to arrive. */ -+ the queue for this process. Otherwise just wait for the -+ other events to arrive. */ - if (!have_events_for(pid)) - /* We always trace syscalls to control fork(), - * clone(), execve()... */ -@@ -168,6 +168,7 @@ struct pid_task { - int got_event : 1; - int delivered : 1; - int vforked : 1; -+ int sysret : 1; - } * pids; - - struct pid_set { -@@ -259,10 +260,14 @@ task_stopped(Process * task, void * data) - case ps_invalid: - case ps_tracing_stop: - case ps_zombie: -+ case ps_sleeping: - return pcb_cont; -- default: -+ case ps_stop: -+ case ps_other: - return pcb_stop; - } -+ -+ abort (); - } - - /* Task is blocked if it's stopped, or if it's a vfork parent. */ -@@ -376,7 +381,8 @@ process_stopping_done(struct process_stopping_handler * self, Process * leader) - if (!self->exiting) { - for (i = 0; i < self->pids.count; ++i) - if (self->pids.tasks[i].pid != 0 -- && self->pids.tasks[i].delivered) -+ && (self->pids.tasks[i].delivered -+ || self->pids.tasks[i].sysret)) - continue_process(self->pids.tasks[i].pid); - continue_process(self->task_enabling_breakpoint->pid); - destroy_event_handler(leader); -@@ -469,7 +475,10 @@ handle_stopping_event(struct pid_task * task_info, Event ** eventp) - /* Some SIGSTOPs may have not been delivered to their respective tasks - * yet. They are still in the queue. If we have seen an event for - * that process, continue it, so that the SIGSTOP can be delivered and -- * caught by ltrace. */ -+ * caught by ltrace. We don't mind that the process is after -+ * breakpoint (and therefore potentially doesn't have aligned IP), -+ * because the signal will be delivered without the process actually -+ * starting. */ - static void - continue_for_sigstop_delivery(struct pid_set * pids) - { -@@ -549,6 +558,14 @@ all_stops_accountable(struct pid_set * pids) - return 1; - } - -+static void -+singlestep(Process * proc) -+{ -+ debug(1, "PTRACE_SINGLESTEP"); -+ if (ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0)) -+ perror("PTRACE_SINGLESTEP"); -+} -+ - /* This event handler is installed when we are in the process of - * stopping the whole thread group to do the pointer re-enablement for - * one of the threads. We pump all events to the queue for later -@@ -580,6 +597,17 @@ process_stopping_on_event(Event_Handler * super, Event * event) - if (event_exit_p(event) && task_info != NULL) - task_info->pid = 0; - -+ /* Always handle sysrets. Whether sysret occurred and what -+ * sys it rets from may need to be determined based on process -+ * stack, so we need to keep that in sync with reality. Note -+ * that we don't continue the process after the sysret is -+ * handled. See continue_after_syscall. */ -+ if (event != NULL && event->type == EVENT_SYSRET) { -+ debug(1, "%d LT_EV_SYSRET", event->proc->pid); -+ event_to_queue = 0; -+ task_info->sysret = 1; -+ } -+ - switch (state) { - case psh_stopping: - /* If everyone is stopped, singlestep. */ -@@ -588,16 +616,22 @@ process_stopping_on_event(Event_Handler * super, Event * event) - teb->pid); - if (sbp->enabled) - disable_breakpoint(teb, sbp); -- if (ptrace(PTRACE_SINGLESTEP, teb->pid, 0, 0)) -- perror("PTRACE_SINGLESTEP"); -+ singlestep(teb); - self->state = state = psh_singlestep; - } - break; - -- case psh_singlestep: { -+ case psh_singlestep: - /* In singlestep state, breakpoint signifies that we - * have now stepped, and can re-enable the breakpoint. */ - if (event != NULL && task == teb) { -+ -+ /* This is not the singlestep that we are waiting for. */ -+ if (event->type == EVENT_SIGNAL) { -+ singlestep(task); -+ break; -+ } -+ - /* Essentially we don't care what event caused - * the thread to stop. We can do the - * re-enablement now. */ -@@ -613,7 +647,6 @@ process_stopping_on_event(Event_Handler * super, Event * event) - event = NULL; // handled - } else - break; -- } - - /* fall-through */ - -@@ -806,9 +839,6 @@ ltrace_exiting_install_handler(Process * proc) - * with its parent, and handle it as a multi-threaded case, with the - * exception that we know that the parent is blocked, and don't - * attempt to stop it. When the child execs, we undo the setup. -- * -- * XXX The parent process could be un-suspended before ltrace gets -- * child exec/exit event. Make sure this is taken care of. - */ - - struct process_vfork_handler -@@ -840,9 +870,9 @@ process_vfork_on_event(Event_Handler * super, Event * event) - sbp = dict_find_entry(event->proc->leader->breakpoints, - self->bp_addr); - if (sbp != NULL) -- insert_breakpoint(event->proc->leader, -- self->bp_addr, sbp->libsym, -- 1); -+ insert_breakpoint(event->proc->parent, -+ self->bp_addr, -+ sbp->libsym, 1); - } - - continue_process(event->proc->parent->pid); -@@ -852,11 +882,6 @@ process_vfork_on_event(Event_Handler * super, Event * event) - change_process_leader(event->proc, event->proc); - destroy_event_handler(event->proc); - -- /* XXXXX this could happen in the middle of handling -- * multi-threaded breakpoint. We must be careful to -- * undo the effects that we introduced above (vforked -- * = 1 et.al.). */ -- - default: - ; - } -@@ -893,6 +918,27 @@ continue_after_vfork(Process * proc) - change_process_leader(proc, proc->parent->leader); - } - -+static int -+is_mid_stopping(Process *proc) -+{ -+ return proc != NULL -+ && proc->event_handler != NULL -+ && proc->event_handler->on_event == &process_stopping_on_event; -+} -+ -+void -+continue_after_syscall(Process * proc, int sysnum, int ret_p) -+{ -+ /* Don't continue if we are mid-stopping. */ -+ if (ret_p && (is_mid_stopping(proc) || is_mid_stopping(proc->leader))) { -+ debug(DEBUG_PROCESS, -+ "continue_after_syscall: don't continue %d", -+ proc->pid); -+ return; -+ } -+ continue_process(proc->pid); -+} -+ - /* If ltrace gets SIGINT, the processes directly or indirectly run by - * ltrace get it too. We just have to wait long enough for the signal - * to be delivered and the process terminated, which we notice and -diff --git a/testsuite/ltrace.main/hello-vfork.c b/testsuite/ltrace.main/hello-vfork.c -new file mode 100644 -index 0000000..228c052 ---- /dev/null -+++ b/testsuite/ltrace.main/hello-vfork.c -@@ -0,0 +1,11 @@ -+/* Copyright (C) 2008, Red Hat, Inc. -+ * Written by Denys Vlasenko */ -+#include -+#include -+ -+int main() { -+ int r = vfork(); -+ fprintf(stderr, "vfork():%d\n", r); -+ _exit(0); -+} -+ -diff --git a/testsuite/ltrace.main/hello-vfork.exp b/testsuite/ltrace.main/hello-vfork.exp -new file mode 100644 -index 0000000..12c9ca3 ---- /dev/null -+++ b/testsuite/ltrace.main/hello-vfork.exp -@@ -0,0 +1,35 @@ -+# This file was written by Yao Qi . -+ -+set testfile "hello-vfork" -+set srcfile ${testfile}.c -+set binfile ${testfile} -+ -+ -+if [get_compiler_info $binfile] { -+ return -1 -+} -+ -+verbose "compiling source file now....." -+if { [ltrace_compile $srcdir/$subdir/$srcfile $objdir/$subdir/$binfile executable debug ] != ""} { -+ send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" -+} -+ -+# set options for ltrace. -+ltrace_options "-f" -+ -+# Run PUT for ltarce. -+set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile] -+ -+# Check the output of this program. -+verbose "ltrace runtest output: $exec_output\n" -+if [regexp {ELF from incompatible architecture} $exec_output] { -+ fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" -+ return -+} elseif [ regexp {Couldn't get .hash data} $exec_output ] { -+ fail "Couldn't get .hash data!" -+ return -+} -+ -+# Verify the output by checking numbers of print in main-vfork.ltrace. -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace "_exit" 2 -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace "vfork resumed" 2 diff --git a/ltrace-0.6.0-threads.patch b/ltrace-0.6.0-threads.patch deleted file mode 100644 index bf48fda..0000000 --- a/ltrace-0.6.0-threads.patch +++ /dev/null @@ -1,2797 +0,0 @@ -diff --git a/breakpoints.c b/breakpoints.c -index 1ea406a..1eff8b0 100644 ---- a/breakpoints.c -+++ b/breakpoints.c -@@ -14,15 +14,25 @@ - - Breakpoint * - address2bpstruct(Process *proc, void *addr) { -+ assert(proc != NULL); -+ assert(proc->breakpoints != NULL); -+ assert(proc->leader == proc); - debug(DEBUG_FUNCTION, "address2bpstruct(pid=%d, addr=%p)", proc->pid, addr); - return dict_find_entry(proc->breakpoints, addr); - } - - void - insert_breakpoint(Process *proc, void *addr, -- struct library_symbol *libsym) { -+ struct library_symbol *libsym, int enable) { - Breakpoint *sbp; - -+ Process * leader = proc->leader; -+ -+ /* Only the group leader should be getting the breakpoints and -+ * thus have ->breakpoint initialized. */ -+ assert(leader != NULL); -+ assert(leader->breakpoints != NULL); -+ - #ifdef __arm__ - int thumb_mode = (int)addr & 1; - if (thumb_mode) -@@ -38,13 +48,13 @@ insert_breakpoint(Process *proc, void *addr, - if (libsym) - libsym->needs_init = 0; - -- sbp = dict_find_entry(proc->breakpoints, addr); -+ sbp = dict_find_entry(leader->breakpoints, addr); - if (!sbp) { - sbp = calloc(1, sizeof(Breakpoint)); - if (!sbp) { - return; /* TODO FIXME XXX: error_mem */ - } -- dict_enter(proc->breakpoints, addr, sbp); -+ dict_enter(leader->breakpoints, addr, sbp); - sbp->addr = addr; - sbp->libsym = libsym; - } -@@ -53,8 +63,10 @@ insert_breakpoint(Process *proc, void *addr, - proc->thumb_mode = 0; - #endif - sbp->enabled++; -- if (sbp->enabled == 1 && proc->pid) -- enable_breakpoint(proc->pid, sbp); -+ if (sbp->enabled == 1 && enable) { -+ assert(proc->pid != 0); -+ enable_breakpoint(proc, sbp); -+ } - } - - void -@@ -63,7 +75,10 @@ delete_breakpoint(Process *proc, void *addr) { - - debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr); - -- sbp = dict_find_entry(proc->breakpoints, addr); -+ Process * leader = proc->leader; -+ assert(leader != NULL); -+ -+ sbp = dict_find_entry(leader->breakpoints, addr); - assert(sbp); /* FIXME: remove after debugging has been done. */ - /* This should only happen on out-of-memory conditions. */ - if (sbp == NULL) -@@ -71,7 +86,7 @@ delete_breakpoint(Process *proc, void *addr) { - - sbp->enabled--; - if (sbp->enabled == 0) -- disable_breakpoint(proc->pid, sbp); -+ disable_breakpoint(proc, sbp); - assert(sbp->enabled >= 0); - } - -@@ -79,7 +94,7 @@ static void - enable_bp_cb(void *addr, void *sbp, void *proc) { - debug(DEBUG_FUNCTION, "enable_bp_cb(pid=%d)", ((Process *)proc)->pid); - if (((Breakpoint *)sbp)->enabled) { -- enable_breakpoint(((Process *)proc)->pid, sbp); -+ enable_breakpoint(proc, sbp); - } - } - -@@ -146,13 +161,14 @@ static void - disable_bp_cb(void *addr, void *sbp, void *proc) { - debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid); - if (((Breakpoint *)sbp)->enabled) { -- disable_breakpoint(((Process *)proc)->pid, sbp); -+ disable_breakpoint(proc, sbp); - } - } - - void - disable_all_breakpoints(Process *proc) { - debug(DEBUG_FUNCTION, "disable_all_breakpoints(pid=%d)", proc->pid); -+ assert(proc->leader == proc); - if (proc->breakpoints_enabled) { - debug(1, "Disabling breakpoints for pid %u...", proc->pid); - dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc); -@@ -167,8 +183,9 @@ free_bp_cb(void *addr, void *sbp, void *data) { - free(sbp); - } - --void --breakpoints_init(Process *proc) { -+int -+breakpoints_init(Process *proc, int enable) -+{ - struct library_symbol *sym; - - debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid); -@@ -177,19 +194,41 @@ breakpoints_init(Process *proc) { - dict_clear(proc->breakpoints); - proc->breakpoints = NULL; - } -- proc->breakpoints = dict_init(dict_key2hash_int, dict_key_cmp_int); -+ -+ /* Only the thread group leader should hold the breakpoints. -+ * (N.B. PID may be set to 0 temporarily when called by -+ * handle_exec). */ -+ assert(proc->leader == proc); -+ -+ proc->breakpoints = dict_init(dict_key2hash_int, -+ dict_key_cmp_int); -+ -+ if (proc->list_of_symbols != NULL) { -+ struct library_symbol * sym = proc->list_of_symbols; -+ while (sym != NULL) { -+ struct library_symbol * next = sym->next; -+ free(sym); -+ sym = next; -+ } -+ } -+ proc->list_of_symbols = NULL; - - if (options.libcalls && proc->filename) { -- /* FIXME: memory leak when called by exec(): */ - proc->list_of_symbols = read_elf(proc); -+ if (proc->list_of_symbols == NULL) { -+ /* XXX leak breakpoints */ -+ return -1; -+ } -+ - if (opt_e) { -- struct library_symbol **tmp1 = &(proc->list_of_symbols); -+ struct library_symbol **tmp1 = &proc->list_of_symbols; - while (*tmp1) { - struct opt_e_t *tmp2 = opt_e; - int keep = !opt_e_enable; - - while (tmp2) { -- if (!strcmp((*tmp1)->name, tmp2->name)) { -+ if (!strcmp((*tmp1)->name, -+ tmp2->name)) { - keep = opt_e_enable; - } - tmp2 = tmp2->next; -@@ -201,15 +240,14 @@ breakpoints_init(Process *proc) { - } - } - } -- } else { -- proc->list_of_symbols = NULL; -- } -- for (sym = proc->list_of_symbols; sym; sym = sym->next) { -- /* proc->pid==0 delays enabling. */ -- insert_breakpoint(proc, sym2addr(proc, sym), sym); - } -+ -+ for (sym = proc->list_of_symbols; sym; sym = sym->next) -+ insert_breakpoint(proc, sym2addr(proc, sym), sym, enable); -+ - proc->callstack_depth = 0; - proc->breakpoints_enabled = -1; -+ return 0; - } - - void -@@ -222,8 +260,7 @@ reinitialize_breakpoints(Process *proc) { - - while (sym) { - if (sym->needs_init) { -- insert_breakpoint(proc, sym2addr(proc, sym), -- sym); -+ insert_breakpoint(proc, sym2addr(proc, sym), sym, 1); - if (sym->needs_init && !sym->is_weak) { - fprintf(stderr, - "could not re-initialize breakpoint for \"%s\" in file \"%s\"\n", -diff --git a/common.h b/common.h -index 70e4a5a..49861cf 100644 ---- a/common.h -+++ b/common.h -@@ -1,3 +1,4 @@ -+#include - #if defined(HAVE_LIBUNWIND) - #include - #endif /* defined(HAVE_LIBUNWIND) */ -@@ -161,12 +162,32 @@ enum Process_State { - STATE_IGNORED /* ignore this process (it's a fork and no -f was used) */ - }; - -+typedef struct Event_Handler Event_Handler; -+struct Event_Handler { -+ /* Event handler that overrides the default one. Should -+ * return NULL if the event was handled, otherwise the -+ * returned event is passed to the default handler. */ -+ Event * (* on_event)(Event_Handler * self, Event * event); -+ -+ /* Called when the event handler removal is requested. */ -+ void (* destroy)(Event_Handler * self); -+}; -+ -+/* XXX We would rather have this all organized a little differently, -+ * have Process for the whole group and Task for what's there for -+ * per-thread stuff. But for now this is the less invasive way of -+ * structuring it. */ - struct Process { - Process_State state; - Process * parent; /* needed by STATE_BEING_CREATED */ - char * filename; - pid_t pid; -+ -+ /* Dictionary of breakpoints (which is a mapping -+ * address->Breakpoint). This is NULL for non-leader -+ * processes. */ - Dict * breakpoints; -+ - int breakpoints_enabled; /* -1:not enabled yet, 0:disabled, 1:enabled */ - int mask_32bit; /* 1 if 64-bit ltrace is tracing 32-bit process */ - unsigned int personality; -@@ -183,7 +204,6 @@ struct Process { - void * instruction_pointer; - void * stack_pointer; /* To get return addr, args... */ - void * return_addr; -- Breakpoint * breakpoint_being_enabled; - void * arch_ptr; - short e_machine; - short need_to_reinitialize_breakpoints; -@@ -191,16 +211,28 @@ struct Process { - int thumb_mode; /* ARM execution mode: 0: ARM, 1: Thumb */ - #endif - -- /* output: */ -- enum tof type_being_displayed; -- - #if defined(HAVE_LIBUNWIND) - /* libunwind address space */ - unw_addr_space_t unwind_as; - void *unwind_priv; - #endif /* defined(HAVE_LIBUNWIND) */ - -+ /* Set in leader. */ -+ Event_Handler * event_handler; -+ -+ -+ /** -+ * Process chaining. -+ **/ - Process * next; -+ -+ /* LEADER points to the leader thread of the POSIX.1 process. -+ If X->LEADER == X, then X is the leader thread and the -+ Process structures chained by NEXT represent other threads, -+ up until, but not including, the next leader thread. -+ LEADER may be NULL after the leader has already exited. In -+ that case this process is waiting to be collected. */ -+ Process * leader; - }; - - struct opt_c_struct { -@@ -216,27 +248,64 @@ struct opt_c_struct { - - extern Dict * dict_opt_c; - --extern Process * list_of_processes; -+enum process_status { -+ ps_invalid, /* Failure. */ -+ ps_stop, /* Job-control stop. */ -+ ps_tracing_stop, -+ ps_zombie, -+ ps_other, /* Necessary other states can be added as needed. */ -+}; - --extern Event * next_event(void); -+enum pcb_status { -+ pcb_stop, /* The iteration should stop. */ -+ pcb_cont, /* The iteration should continue. */ -+}; -+ -+/* Process list */ - extern Process * pid2proc(pid_t pid); -+extern void add_process(Process * proc); -+extern void remove_process(Process * proc); -+extern Process *each_process(Process * start, -+ enum pcb_status (* cb)(Process * proc, void * data), -+ void * data); -+extern Process *each_task(Process * start, -+ enum pcb_status (* cb)(Process * proc, void * data), -+ void * data); -+ -+/* Events */ -+enum ecb_status { -+ ecb_cont, /* The iteration should continue. */ -+ ecb_yield, /* The iteration should stop, yielding this -+ * event. */ -+ ecb_deque, /* Like ecb_stop, but the event should be removed -+ * from the queue. */ -+}; -+extern Event * next_event(void); -+extern Event * each_qd_event(enum ecb_status (* cb)(Event * event, void * data), -+ void * data); -+extern void enque_event(Event * event); - extern void handle_event(Event * event); --extern void execute_program(Process *, char **); -+ -+extern void install_event_handler(Process * proc, Event_Handler * handler); -+extern void destroy_event_handler(Process * proc); -+ -+extern pid_t execute_program(const char * command, char ** argv); - extern int display_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); - extern Breakpoint * address2bpstruct(Process * proc, void * addr); --extern void breakpoints_init(Process * proc); --extern void insert_breakpoint(Process * proc, void * addr, struct library_symbol * libsym); -+extern int breakpoints_init(Process * proc, int enable); -+extern void insert_breakpoint(Process * proc, void * addr, -+ struct library_symbol * libsym, int enable); - extern void delete_breakpoint(Process * proc, void * addr); - extern void enable_all_breakpoints(Process * proc); - extern void disable_all_breakpoints(Process * proc); - extern void reinitialize_breakpoints(Process *); - --extern Process * open_program(char * filename, pid_t pid); -+extern Process * open_program(char * filename, pid_t pid, int init_breakpoints); - extern void open_pid(pid_t pid); - extern void show_summary(void); - extern arg_type_info * lookup_prototype(enum arg_type at); - --extern void do_init_elf(struct ltelf *lte, const char *filename); -+extern int do_init_elf(struct ltelf *lte, const char *filename); - extern void do_close_elf(struct ltelf *lte); - extern int in_load_libraries(const char *name, struct ltelf *lte, size_t count, GElf_Sym *sym); - extern struct library_symbol *library_symbols; -@@ -246,6 +315,10 @@ extern void add_library_symbol(GElf_Addr addr, const char *name, - - /* Arch-dependent stuff: */ - extern char * pid2name(pid_t pid); -+extern pid_t process_leader(pid_t pid); -+extern int process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n); -+extern int process_stopped(pid_t pid); -+extern enum process_status process_status(pid_t pid); - extern void trace_set_options(Process * proc, pid_t pid); - extern void trace_me(void); - extern int trace_pid(pid_t pid); -@@ -256,13 +329,13 @@ extern void set_instruction_pointer(Process * proc, void * addr); - extern void * get_stack_pointer(Process * proc); - extern void * get_return_addr(Process * proc, void * stack_pointer); - extern void set_return_addr(Process * proc, void * addr); --extern void enable_breakpoint(pid_t pid, Breakpoint * sbp); --extern void disable_breakpoint(pid_t pid, const Breakpoint * sbp); -+extern void enable_breakpoint(Process * proc, Breakpoint * sbp); -+extern void disable_breakpoint(Process * proc, Breakpoint * sbp); - extern int syscall_p(Process * proc, int status, int * sysnum); - extern void continue_process(pid_t pid); - extern void continue_after_signal(pid_t pid, int signum); - extern void continue_after_breakpoint(Process * proc, Breakpoint * sbp); --extern void continue_enabling_breakpoint(pid_t pid, Breakpoint * sbp); -+extern void ltrace_exiting(void); - extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); - extern void save_register_args(enum tof type, Process * proc); - extern int umovestr(Process * proc, void * addr, int len, void * laddr); -@@ -272,5 +345,7 @@ extern int ffcheck(void * maddr); - extern void * sym2addr(Process *, struct library_symbol *); - extern int linkmap_init(Process *, struct ltelf *); - extern void arch_check_dbg(Process *proc); -+extern int task_kill (pid_t pid, int sig); -+ - - extern struct ltelf main_lte; -diff --git a/execute_program.c b/execute_program.c -index 3651b66..47f514d 100644 ---- a/execute_program.c -+++ b/execute_program.c -@@ -17,7 +17,8 @@ - #include "common.h" - - static void --change_uid(Process *proc) { -+change_uid(const char * command) -+{ - uid_t run_uid, run_euid; - gid_t run_gid, run_egid; - -@@ -49,7 +50,7 @@ change_uid(Process *proc) { - run_euid = run_uid; - run_egid = run_gid; - -- if (!stat(proc->filename, &statbuf)) { -+ if (!stat(command, &statbuf)) { - if (statbuf.st_mode & S_ISUID) { - run_euid = statbuf.st_uid; - } -@@ -68,32 +69,27 @@ change_uid(Process *proc) { - } - } - --void --execute_program(Process *sp, char **argv) { -+pid_t -+execute_program(const char * command, char **argv) -+{ - pid_t pid; - -- debug(1, "Executing `%s'...", sp->filename); -+ debug(1, "Executing `%s'...", command); - - pid = fork(); - if (pid < 0) { - perror("ltrace: fork"); - exit(1); - } else if (!pid) { /* child */ -- change_uid(sp); -+ change_uid(command); - trace_me(); -- execvp(sp->filename, argv); -- fprintf(stderr, "Can't execute `%s': %s\n", sp->filename, -+ execvp(command, argv); -+ fprintf(stderr, "Can't execute `%s': %s\n", command, - strerror(errno)); - _exit(1); - } - - debug(1, "PID=%d", pid); - -- sp->pid = pid; -- --#if defined(HAVE_LIBUNWIND) -- sp->unwind_priv = _UPT_create(pid); --#endif /* defined(HAVE_LIBUNWIND) */ -- -- return; -+ return pid; - } -diff --git a/handle_event.c b/handle_event.c -index 01309ff..0aa40f7 100644 ---- a/handle_event.c -+++ b/handle_event.c -@@ -25,7 +25,6 @@ static void handle_clone(Event *event); - static void handle_exec(Event *event); - static void handle_breakpoint(Event *event); - static void handle_new(Event *event); --static void remove_proc(Process *proc); - - static void callstack_push_syscall(Process *proc, int sysnum); - static void callstack_push_symfunc(Process *proc, -@@ -38,7 +37,26 @@ static char * arch_sysname(Process *proc, int sysnum); - - void - handle_event(Event *event) { -- debug(DEBUG_FUNCTION, "handle_event(pid=%d, type=%d)", event->proc ? event->proc->pid : -1, event->type); -+ if (exiting == 1) { -+ exiting = 2; -+ debug(1, "ltrace about to exit"); -+ ltrace_exiting(); -+ } -+ debug(DEBUG_FUNCTION, "handle_event(pid=%d, type=%d)", -+ event->proc ? event->proc->pid : -1, event->type); -+ /* If the thread group defines an overriding event handler, -+ give it a chance to kick in. */ -+ if (event->proc != NULL -+ && event->proc->leader != NULL) { -+ Event_Handler * handler = event->proc->leader->event_handler; -+ if (handler != NULL) { -+ event = (*handler->on_event) (handler, event); -+ if (event == NULL) -+ /* It was handled. */ -+ return; -+ } -+ } -+ - switch (event->type) { - case EVENT_NONE: - debug(1, "event: none"); -@@ -202,24 +220,24 @@ handle_clone(Event * event) { - p->pid = event->e_un.newpid; - p->parent = event->proc; - -+ /* We save register values to the arch pointer, and these need -+ to be per-thread. */ -+ p->arch_ptr = NULL; -+ - if (pending_new(p->pid)) { - pending_new_remove(p->pid); -- if (p->breakpoint_being_enabled) { -- enable_breakpoint(p->pid, p->breakpoint_being_enabled); -- p->breakpoint_being_enabled = NULL; -- } -+ if (p->event_handler != NULL) -+ destroy_event_handler(p); - if (event->proc->state == STATE_ATTACHED && options.follow) { - p->state = STATE_ATTACHED; - } else { - p->state = STATE_IGNORED; - } - continue_process(p->pid); -- p->next = list_of_processes; -- list_of_processes = p; -+ add_process(p); - } else { - p->state = STATE_BEING_CREATED; -- p->next = list_of_processes; -- list_of_processes = p; -+ add_process(p); - } - continue_process(event->proc->pid); - } -@@ -235,10 +253,8 @@ handle_new(Event * event) { - pending_new_insert(event->e_un.newpid); - } else { - assert(proc->state == STATE_BEING_CREATED); -- if (proc->breakpoint_being_enabled) { -- enable_breakpoint(proc->pid, proc->breakpoint_being_enabled); -- proc->breakpoint_being_enabled = NULL; -- } -+ if (proc->event_handler != NULL) -+ destroy_event_handler(proc); - if (options.follow) { - proc->state = STATE_ATTACHED; - } else { -@@ -323,13 +339,6 @@ arch_sysname(Process *proc, int sysnum) { - static void - handle_signal(Event *event) { - debug(DEBUG_FUNCTION, "handle_signal(pid=%d, signum=%d)", event->proc->pid, event->e_un.signum); -- if (exiting && event->e_un.signum == SIGSTOP) { -- pid_t pid = event->proc->pid; -- disable_all_breakpoints(event->proc); -- untrace_pid(pid); -- remove_proc(event->proc); -- return; -- } - if (event->proc->state != STATE_IGNORED && !options.no_signals) { - output_line(event->proc, "--- %s (%s) ---", - shortsignal(event->proc, event->e_un.signum), -@@ -345,7 +354,7 @@ handle_exit(Event *event) { - output_line(event->proc, "+++ exited (status %d) +++", - event->e_un.ret_val); - } -- remove_proc(event->proc); -+ remove_process(event->proc); - } - - static void -@@ -355,31 +364,7 @@ handle_exit_signal(Event *event) { - output_line(event->proc, "+++ killed by %s +++", - shortsignal(event->proc, event->e_un.signum)); - } -- remove_proc(event->proc); --} -- --static void --remove_proc(Process *proc) { -- Process *tmp, *tmp2; -- -- debug(DEBUG_FUNCTION, "remove_proc(pid=%d)", proc->pid); -- -- if (list_of_processes == proc) { -- tmp = list_of_processes; -- list_of_processes = list_of_processes->next; -- free(tmp); -- return; -- } -- tmp = list_of_processes; -- while (tmp->next) { -- if (tmp->next == proc) { -- tmp2 = tmp->next; -- tmp->next = tmp->next->next; -- free(tmp2); -- continue; -- } -- tmp = tmp->next; -- } -+ remove_process(event->proc); - } - - static void -@@ -389,7 +374,7 @@ handle_syscall(Event *event) { - callstack_push_syscall(event->proc, event->e_un.sysnum); - if (options.syscalls) { - output_left(LT_TOF_SYSCALL, event->proc, -- sysname(event->proc, event->e_un.sysnum)); -+ sysname(event->proc, event->e_un.sysnum)); - } - if (event->proc->breakpoints_enabled == 0) { - enable_all_breakpoints(event->proc); -@@ -406,7 +391,7 @@ handle_exec(Event * event) { - debug(DEBUG_FUNCTION, "handle_exec(pid=%d)", proc->pid); - if (proc->state == STATE_IGNORED) { - untrace_pid(proc->pid); -- remove_proc(proc); -+ remove_process(proc); - return; - } - output_line(proc, "--- Called exec() ---"); -@@ -417,7 +402,7 @@ handle_exec(Event * event) { - proc->filename = pid2name(proc->pid); - saved_pid = proc->pid; - proc->pid = 0; -- breakpoints_init(proc); -+ breakpoints_init(proc, 0); - proc->pid = saved_pid; - proc->callstack_depth = 0; - continue_process(proc->pid); -@@ -503,6 +488,13 @@ static void - handle_breakpoint(Event *event) { - int i, j; - Breakpoint *sbp; -+ Process *leader = event->proc->leader; -+ -+ /* The leader has terminated. */ -+ if (leader == NULL) { -+ continue_process(event->proc->pid); -+ return; -+ } - - debug(DEBUG_FUNCTION, "handle_breakpoint(pid=%d, addr=%p)", event->proc->pid, event->e_un.brk_addr); - debug(2, "event: breakpoint (%p)", event->e_un.brk_addr); -@@ -513,7 +505,7 @@ handle_breakpoint(Event *event) { - Breakpoint *stub_bp = NULL; - char nop_instruction[] = PPC_NOP; - -- stub_bp = address2bpstruct (event->proc, event->e_un.brk_addr); -+ stub_bp = address2bpstruct(leader, event->e_un.brk_addr); - - if (stub_bp) { - unsigned char *bp_instruction = stub_bp->orig_value; -@@ -528,14 +520,6 @@ handle_breakpoint(Event *event) { - } - } - #endif -- if ((sbp = event->proc->breakpoint_being_enabled) != 0) { -- /* Reinsert breakpoint */ -- continue_enabling_breakpoint(event->proc->pid, -- event->proc-> -- breakpoint_being_enabled); -- event->proc->breakpoint_being_enabled = NULL; -- return; -- } - - for (i = event->proc->callstack_depth - 1; i >= 0; i--) { - if (event->e_un.brk_addr == -@@ -554,7 +538,7 @@ handle_breakpoint(Event *event) { - if (libsym->plt_type != LS_TOPLT_POINT) { - unsigned char break_insn[] = BREAKPOINT_VALUE; - -- sbp = address2bpstruct(event->proc, addr); -+ sbp = address2bpstruct(leader, addr); - assert(sbp); - a = ptrace(PTRACE_PEEKTEXT, event->proc->pid, - addr); -@@ -562,10 +546,10 @@ handle_breakpoint(Event *event) { - if (memcmp(&a, break_insn, BREAKPOINT_LENGTH)) { - sbp->enabled--; - insert_breakpoint(event->proc, addr, -- libsym); -+ libsym, 1); - } - } else { -- sbp = dict_find_entry(event->proc->breakpoints, addr); -+ sbp = dict_find_entry(leader->breakpoints, addr); - /* On powerpc, the breakpoint address - may end up being actual entry point - of the library symbol, not the PLT -@@ -573,7 +557,7 @@ handle_breakpoint(Event *event) { - sbp is NULL. */ - if (sbp == NULL || addr != sbp->addr) { - insert_breakpoint(event->proc, addr, -- libsym); -+ libsym, 1); - } - } - #elif defined(__mips__) -@@ -581,18 +565,18 @@ handle_breakpoint(Event *event) { - 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(event->proc->breakpoints, addr); -+ addr = sym2addr(leader, sym); -+ sbp = dict_find_entry(leader->breakpoints, addr); - if (sbp) { - if (addr != sbp->addr) { -- insert_breakpoint(event->proc, addr, sym); -+ insert_breakpoint(event->proc, addr, sym, 1); - } - } 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=event->proc->list_of_symbols; -- event->proc->list_of_symbols=new_sym; -- insert_breakpoint(event->proc, addr, new_sym); -+ new_sym->next = leader->list_of_symbols; -+ leader->list_of_symbols = new_sym; -+ insert_breakpoint(event->proc, addr, new_sym, 1); - } - #endif - for (j = event->proc->callstack_depth - 1; j > i; j--) { -@@ -609,18 +593,23 @@ handle_breakpoint(Event *event) { - event->proc->callstack[i].c_un.libfunc->name); - } - callstack_pop(event->proc); -- continue_after_breakpoint(event->proc, -- address2bpstruct(event->proc, -- event->e_un.brk_addr)); -+ sbp = address2bpstruct(leader, event->e_un.brk_addr); -+ continue_after_breakpoint(event->proc, sbp); - return; - } - } - -- if ((sbp = address2bpstruct(event->proc, event->e_un.brk_addr))) { -+ if ((sbp = address2bpstruct(leader, event->e_un.brk_addr))) { -+ if (sbp->libsym == NULL) { -+ continue_after_breakpoint(event->proc, sbp); -+ return; -+ } -+ - if (strcmp(sbp->libsym->name, "") == 0) { -- debug(2, "Hit _dl_debug_state breakpoint!\n"); -- arch_check_dbg(event->proc); -+ debug(DEBUG_PROCESS, "Hit _dl_debug_state breakpoint!\n"); -+ arch_check_dbg(leader); - } -+ - if (event->proc->state != STATE_IGNORED) { - event->proc->stack_pointer = get_stack_pointer(event->proc); - event->proc->return_addr = -@@ -632,7 +621,7 @@ handle_breakpoint(Event *event) { - if (event->proc->need_to_reinitialize_breakpoints - && (strcmp(sbp->libsym->name, PLTs_initialized_by_here) == - 0)) -- reinitialize_breakpoints(event->proc); -+ reinitialize_breakpoints(leader); - #endif - - continue_after_breakpoint(event->proc, sbp); -@@ -689,7 +678,7 @@ callstack_push_symfunc(Process *proc, struct library_symbol *sym) { - - elem->return_addr = proc->return_addr; - if (elem->return_addr) { -- insert_breakpoint(proc, elem->return_addr, 0); -+ insert_breakpoint(proc, elem->return_addr, NULL, 1); - } - - /* handle functions like atexit() on mips which have no return */ -@@ -709,6 +698,7 @@ callstack_pop(Process *proc) { - debug(DEBUG_FUNCTION, "callstack_pop(pid=%d)", proc->pid); - elem = &proc->callstack[proc->callstack_depth - 1]; - if (!elem->is_syscall && elem->return_addr) { -+ assert(proc->leader != NULL); - delete_breakpoint(proc, elem->return_addr); - } - if (elem->arch_ptr != NULL) { -diff --git a/libltrace.c b/libltrace.c -index 0f48d11..e731fe1 100644 ---- a/libltrace.c -+++ b/libltrace.c -@@ -12,32 +12,39 @@ - #include "common.h" - - char *command = NULL; --Process *list_of_processes = NULL; - - int exiting = 0; /* =1 if a SIGINT or SIGTERM has been received */ - --static void --signal_alarm(int sig) { -- Process *tmp = list_of_processes; -+static enum pcb_status -+stop_non_p_processes (Process * proc, void * data) -+{ -+ int stop = 1; - -- signal(SIGALRM, SIG_DFL); -- while (tmp) { -- struct opt_p_t *tmp2 = opt_p; -- while (tmp2) { -- if (tmp->pid == tmp2->pid) { -- tmp = tmp->next; -- if (!tmp) { -- return; -- } -- tmp2 = opt_p; -- continue; -- } -- tmp2 = tmp2->next; -+ struct opt_p_t *it; -+ for (it = opt_p; it != NULL; it = it->next) { -+ Process * p_proc = pid2proc(it->pid); -+ if (p_proc == NULL) { -+ printf("stop_non_p_processes: %d terminated?\n", it->pid); -+ continue; -+ } -+ if (p_proc == proc || p_proc->leader == proc->leader) { -+ stop = 0; -+ break; - } -- debug(2, "Sending SIGSTOP to process %u\n", tmp->pid); -- kill(tmp->pid, SIGSTOP); -- tmp = tmp->next; - } -+ -+ if (stop) { -+ debug(2, "Sending SIGSTOP to process %u", proc->pid); -+ kill(proc->pid, SIGSTOP); -+ } -+ -+ return pcb_cont; -+} -+ -+static void -+signal_alarm(int sig) { -+ signal(SIGALRM, SIG_DFL); -+ each_process(NULL, &stop_non_p_processes, NULL); - } - - static void -@@ -47,15 +54,7 @@ signal_exit(int sig) { - signal(SIGINT, SIG_IGN); - signal(SIGTERM, SIG_IGN); - signal(SIGALRM, signal_alarm); -- if (opt_p) { -- struct opt_p_t *tmp = opt_p; -- while (tmp) { -- debug(2, "Sending SIGSTOP to process %u\n", tmp->pid); -- kill(tmp->pid, SIGSTOP); -- tmp = tmp->next; -- } -- } -- alarm(1); -+ //alarm(1); - } - - static void -@@ -108,7 +107,7 @@ ltrace_init(int argc, char **argv) { - } - } - if (command) { -- execute_program(open_program(command, 0), argv); -+ open_program(command, execute_program(command, argv), 0); - } - opt_p_tmp = opt_p; - while (opt_p_tmp) { -diff --git a/ltrace-elf.c b/ltrace-elf.c -index 1a33ec3..d88d5a6 100644 ---- a/ltrace-elf.c -+++ b/ltrace-elf.c -@@ -14,7 +14,6 @@ - - #include "common.h" - --void do_init_elf(struct ltelf *lte, const char *filename); - void do_close_elf(struct ltelf *lte); - void add_library_symbol(GElf_Addr addr, const char *name, - struct library_symbol **library_symbolspp, -@@ -136,7 +135,7 @@ static GElf_Addr get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot, - return 0; - } - --void -+int - do_init_elf(struct ltelf *lte, const char *filename) { - int i; - GElf_Addr relplt_addr = 0; -@@ -147,7 +146,7 @@ do_init_elf(struct ltelf *lte, const char *filename) { - - lte->fd = open(filename, O_RDONLY); - if (lte->fd == -1) -- error(EXIT_FAILURE, errno, "Can't open \"%s\"", filename); -+ return 1; - - #ifdef HAVE_ELF_C_READ_MMAP - lte->elf = elf_begin(lte->fd, ELF_C_READ_MMAP, NULL); -@@ -454,6 +453,7 @@ do_init_elf(struct ltelf *lte, const char *filename) { - - debug(1, "%s %zd PLT relocations", filename, lte->relplt_count); - } -+ return 0; - } - - void -@@ -622,7 +622,8 @@ read_elf(Process *proc) { - - elf_version(EV_CURRENT); - -- do_init_elf(lte, proc->filename); -+ if (do_init_elf(lte, proc->filename)) -+ return NULL; - - memcpy(&main_lte, lte, sizeof(struct ltelf)); - -@@ -634,7 +635,9 @@ read_elf(Process *proc) { - proc->e_machine = lte->ehdr.e_machine; - - for (i = 0; i < library_num; ++i) { -- do_init_elf(<e[i + 1], library[i]); -+ if (do_init_elf(<e[i + 1], library[i])) -+ error(EXIT_FAILURE, errno, "Can't open \"%s\"", -+ proc->filename); - } - - if (!options.no_plt) { -diff --git a/ltrace.h b/ltrace.h -index 5e43ba5..0ff4572 100644 ---- a/ltrace.h -+++ b/ltrace.h -@@ -20,6 +20,7 @@ enum Event_type { - typedef struct Process Process; - typedef struct Event Event; - struct Event { -+ struct Event * next; - Process * proc; - Event_type type; - union { -diff --git a/output.c b/output.c -index de2a836..945dd52 100644 ---- a/output.c -+++ b/output.c -@@ -96,7 +96,7 @@ begin_of_line(enum tof type, Process *proc) { - } - - static Function * --name2func(char *name) { -+name2func(char const *name) { - Function *tmp; - const char *str1, *str2; - -@@ -153,7 +153,7 @@ tabto(int col) { - } - - void --output_left(enum tof type, Process *proc, char *function_name) { -+output_left(enum tof type, Process *proc, char const *function_name) { - Function *func; - static arg_type_info *arg_unknown = NULL; - if (arg_unknown == NULL) -@@ -168,7 +168,6 @@ output_left(enum tof type, Process *proc, char *function_name) { - } - current_proc = proc; - current_depth = proc->callstack_depth; -- proc->type_being_displayed = type; - begin_of_line(type, proc); - #ifdef USE_DEMANGLE - current_column += -diff --git a/output.h b/output.h -index c58577a..fa840c7 100644 ---- a/output.h -+++ b/output.h -@@ -1,3 +1,3 @@ - void output_line(Process *proc, char *fmt, ...); --void output_left(enum tof type, Process *proc, char *function_name); -+void output_left(enum tof type, Process *proc, char const *function_name); - void output_right(enum tof type, Process *proc, char *function_name); -diff --git a/proc.c b/proc.c -index 1c57532..0425e09 100644 ---- a/proc.c -+++ b/proc.c -@@ -10,73 +10,280 @@ - #include - #include - #include -+#include -+#include - - #include "common.h" - - Process * --open_program(char *filename, pid_t pid) { -+open_program(char *filename, pid_t pid, int enable) { - Process *proc; -+ assert(pid != 0); - proc = calloc(sizeof(Process), 1); - if (!proc) { - perror("malloc"); - exit(1); - } -+ - proc->filename = strdup(filename); - proc->breakpoints_enabled = -1; -- if (pid) { -- proc->pid = pid; -+ proc->pid = pid; - #if defined(HAVE_LIBUNWIND) -- proc->unwind_priv = _UPT_create(pid); -- } else { -- proc->unwind_priv = NULL; -+ proc->unwind_priv = _UPT_create(pid); -+ proc->unwind_as = unw_create_addr_space(&_UPT_accessors, 0); - #endif /* defined(HAVE_LIBUNWIND) */ -- } - -- breakpoints_init(proc); -+ add_process(proc); -+ if (proc->leader == NULL) { -+ free(proc); -+ return NULL; -+ } - -- proc->next = list_of_processes; -- list_of_processes = proc; -+ if (proc->leader == proc) -+ if (breakpoints_init(proc, enable)) { -+ fprintf(stderr, "failed to init breakpoints %d\n", -+ proc->pid); -+ remove_process(proc); -+ return NULL; -+ } - --#if defined(HAVE_LIBUNWIND) -- proc->unwind_as = unw_create_addr_space(&_UPT_accessors, 0); --#endif /* defined(HAVE_LIBUNWIND) */ - return proc; - } - --void --open_pid(pid_t pid) { -+static int -+open_one_pid(pid_t pid) -+{ - Process *proc; - char *filename; -+ debug(DEBUG_PROCESS, "open_one_pid(pid=%d)", pid); - -- if (trace_pid(pid) < 0) { -- fprintf(stderr, "Cannot attach to pid %u: %s\n", pid, -- strerror(errno)); -- return; -+ /* Get the filename first. Should the trace_pid fail, we can -+ * easily free it, untracing is more work. */ -+ if ((filename = pid2name(pid)) == NULL -+ || trace_pid(pid) < 0) { -+ free(filename); -+ return -1; - } - -- filename = pid2name(pid); -+ proc = open_program(filename, pid, 0); -+ if (proc == NULL) -+ return -1; -+ trace_set_options(proc, pid); -+ -+ return 0; -+} -+ -+enum pcb_status -+start_one_pid(Process * proc, void * data) -+{ -+ continue_process(proc->pid); -+ proc->breakpoints_enabled = 1; -+ return pcb_cont; -+} -+ -+void -+open_pid(pid_t pid) -+{ -+ debug(DEBUG_PROCESS, "open_pid(pid=%d)", pid); -+ /* If we are already tracing this guy, we should be seeing all -+ * his children via normal tracing route. */ -+ if (pid2proc(pid) != NULL) -+ return; - -- if (!filename) { -- fprintf(stderr, "Cannot trace pid %u: %s\n", pid, -- strerror(errno)); -+ /* First, see if we can attach the requested PID itself. */ -+ if (open_one_pid(pid)) { -+ fprintf(stderr, "Cannot attach to pid %u: %s\n", -+ pid, strerror(errno)); - return; - } - -- proc = open_program(filename, pid); -- continue_process(pid); -- proc->breakpoints_enabled = 1; -+ /* Now attach to all tasks that belong to that PID. There's a -+ * race between process_tasks and open_one_pid. So when we -+ * fail in open_one_pid below, we just do another round. -+ * Chances are that by then that PID will have gone away, and -+ * that's why we have seen the failure. The processes that we -+ * manage to open_one_pid are stopped, so we should eventually -+ * reach a point where process_tasks doesn't give any new -+ * processes (because there's nobody left to produce -+ * them). */ -+ size_t old_ntasks = 0; -+ int have_all; -+ while (1) { -+ pid_t *tasks; -+ size_t ntasks; -+ size_t i; -+ -+ if (process_tasks(pid, &tasks, &ntasks) < 0) { -+ fprintf(stderr, "Cannot obtain tasks of pid %u: %s\n", -+ pid, strerror(errno)); -+ goto start; -+ } -+ -+ have_all = 1; -+ for (i = 0; i < ntasks; ++i) -+ if (pid2proc(tasks[i]) == NULL -+ && open_one_pid(tasks[i])) -+ have_all = 0; -+ -+ free(tasks); -+ -+ if (have_all && old_ntasks == ntasks) -+ break; -+ old_ntasks = ntasks; -+ } -+ -+ /* Done. Now initialize breakpoints and then continue -+ * everyone. */ -+ Process * leader; -+start: -+ leader = pid2proc(pid)->leader; -+ enable_all_breakpoints(leader); -+ -+ each_task(pid2proc(pid)->leader, start_one_pid, NULL); -+} -+ -+static enum pcb_status -+find_proc(Process * proc, void * data) -+{ -+ pid_t pid = (pid_t)(uintptr_t)data; -+ return proc->pid == pid ? pcb_stop : pcb_cont; - } - - Process * - pid2proc(pid_t pid) { -- Process *tmp; -+ return each_process(NULL, &find_proc, (void *)(uintptr_t)pid); -+} - -+ -+static Process * list_of_processes = NULL; -+ -+Process * -+each_process(Process * proc, -+ enum pcb_status (* cb)(Process * proc, void * data), -+ void * data) -+{ -+ Process * it = proc ?: list_of_processes; -+ for (; it != NULL; ) { -+ /* Callback might call remove_process. */ -+ Process * next = it->next; -+ if ((*cb) (it, data) == pcb_stop) -+ return it; -+ it = next; -+ } -+ return NULL; -+} -+ -+Process * -+each_task(Process * it, enum pcb_status (* cb)(Process * proc, void * data), -+ void * data) -+{ -+ if (it != NULL) { -+ Process * leader = it->leader; -+ for (; it != NULL && it->leader == leader; ) { -+ /* Callback might call remove_process. */ -+ Process * next = it->next; -+ if ((*cb) (it, data) == pcb_stop) -+ return it; -+ it = next; -+ } -+ } -+ return NULL; -+} -+ -+void -+add_process(Process * proc) -+{ -+ Process ** leaderp = &list_of_processes; -+ if (proc->pid) { -+ pid_t tgid = process_leader(proc->pid); -+ if (tgid == 0) -+ /* Must have been terminated before we managed -+ * to fully attach. */ -+ return; -+ if (tgid == proc->pid) -+ proc->leader = proc; -+ else { -+ Process * leader = pid2proc(tgid); -+ proc->leader = leader; -+ if (leader != NULL) -+ leaderp = &leader->next; -+ } -+ } -+ proc->next = *leaderp; -+ *leaderp = proc; -+} -+ -+static enum pcb_status -+clear_leader(Process * proc, void * data) -+{ -+ debug(DEBUG_FUNCTION, "detach_task %d from leader %d", -+ proc->pid, proc->leader->pid); -+ proc->leader = NULL; -+ return pcb_cont; -+} -+ -+static enum ecb_status -+event_for_proc(Event * event, void * data) -+{ -+ if (event->proc == data) -+ return ecb_deque; -+ else -+ return ecb_cont; -+} -+ -+static void -+delete_events_for(Process * proc) -+{ -+ Event * event; -+ while ((event = each_qd_event(&event_for_proc, proc)) != NULL) -+ free(event); -+} -+ -+void -+remove_process(Process *proc) -+{ -+ Process *tmp, *tmp2; -+ -+ debug(DEBUG_FUNCTION, "remove_proc(pid=%d)", proc->pid); -+ -+ if (proc->leader == proc) -+ each_task(proc, &clear_leader, NULL); -+ -+ if (list_of_processes == proc) { -+ tmp = list_of_processes; -+ list_of_processes = list_of_processes->next; -+ delete_events_for(tmp); -+ free(tmp); -+ return; -+ } - tmp = list_of_processes; -- while (tmp) { -- if (pid == tmp->pid) { -- return tmp; -+ while (tmp->next) { -+ if (tmp->next == proc) { -+ tmp2 = tmp->next; -+ tmp->next = tmp->next->next; -+ delete_events_for(tmp2); -+ free(tmp2); -+ return; - } - tmp = tmp->next; - } -- return NULL; -+} -+ -+void -+install_event_handler(Process * proc, Event_Handler * handler) -+{ -+ debug(DEBUG_FUNCTION, "install_event_handler(pid=%d, %p)", proc->pid, handler); -+ assert(proc->event_handler == NULL); -+ proc->event_handler = handler; -+} -+ -+void -+destroy_event_handler(Process * proc) -+{ -+ Event_Handler * handler = proc->event_handler; -+ debug(DEBUG_FUNCTION, "destroy_event_handler(pid=%d, %p)", proc->pid, handler); -+ assert(handler != NULL); -+ handler->destroy(handler); -+ free(handler); -+ proc->event_handler = NULL; - } -diff --git a/sysdeps/linux-gnu/breakpoint.c b/sysdeps/linux-gnu/breakpoint.c -index 9104189..5a49e9d 100644 ---- a/sysdeps/linux-gnu/breakpoint.c -+++ b/sysdeps/linux-gnu/breakpoint.c -@@ -8,21 +8,11 @@ - - #ifdef ARCH_HAVE_ENABLE_BREAKPOINT - extern void arch_enable_breakpoint(pid_t, Breakpoint *); -+#else /* ARCH_HAVE_ENABLE_BREAKPOINT */ - void --enable_breakpoint(pid_t pid, Breakpoint *sbp) { -- if (sbp->libsym) { -- debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p, symbol=%s", pid, sbp->addr, sbp->libsym->name); -- } else { -- debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p", pid, sbp->addr); -- } -- arch_enable_breakpoint(pid, sbp); --} --#else -- --static unsigned char break_insn[] = BREAKPOINT_VALUE; -- --void --enable_breakpoint(pid_t pid, Breakpoint *sbp) { -+arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) -+{ -+ static unsigned char break_insn[] = BREAKPOINT_VALUE; - unsigned int i, j; - - if (sbp->libsym) { -@@ -32,9 +22,8 @@ enable_breakpoint(pid_t pid, Breakpoint *sbp) { - } - - for (i = 0; i < 1 + ((BREAKPOINT_LENGTH - 1) / sizeof(long)); i++) { -- long a = -- ptrace(PTRACE_PEEKTEXT, pid, sbp->addr + i * sizeof(long), -- 0); -+ long a = ptrace(PTRACE_PEEKTEXT, pid, -+ sbp->addr + i * sizeof(long), 0); - for (j = 0; - j < sizeof(long) - && i * sizeof(long) + j < BREAKPOINT_LENGTH; j++) { -@@ -48,20 +37,22 @@ enable_breakpoint(pid_t pid, Breakpoint *sbp) { - } - #endif /* ARCH_HAVE_ENABLE_BREAKPOINT */ - --#ifdef ARCH_HAVE_DISABLE_BREAKPOINT --extern void arch_disable_breakpoint(pid_t, const Breakpoint *sbp); - void --disable_breakpoint(pid_t pid, const Breakpoint *sbp) { -+enable_breakpoint(Process * proc, Breakpoint *sbp) { - if (sbp->libsym) { -- debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p, symbol=%s", pid, sbp->addr, sbp->libsym->name); -+ debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p, symbol=%s", proc->pid, sbp->addr, sbp->libsym->name); - } else { -- debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p", pid, sbp->addr); -+ debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p", proc->pid, sbp->addr); - } -- arch_disable_breakpoint(pid, sbp); -+ arch_enable_breakpoint(proc->pid, sbp); - } --#else -+ -+#ifdef ARCH_HAVE_DISABLE_BREAKPOINT -+extern void arch_disable_breakpoint(pid_t, const Breakpoint *sbp); -+#else /* ARCH_HAVE_DISABLE_BREAKPOINT */ - void --disable_breakpoint(pid_t pid, const Breakpoint *sbp) { -+arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp) -+{ - unsigned int i, j; - - if (sbp->libsym) { -@@ -85,3 +76,13 @@ disable_breakpoint(pid_t pid, const Breakpoint *sbp) { - } - } - #endif /* ARCH_HAVE_DISABLE_BREAKPOINT */ -+ -+void -+disable_breakpoint(Process * proc, Breakpoint *sbp) { -+ if (sbp->libsym) { -+ debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p, symbol=%s", proc->pid, sbp->addr, sbp->libsym->name); -+ } else { -+ debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p", proc->pid, sbp->addr); -+ } -+ arch_disable_breakpoint(proc->pid, sbp); -+} -diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c -index fd19e71..8a79583 100644 ---- a/sysdeps/linux-gnu/events.c -+++ b/sysdeps/linux-gnu/events.c -@@ -8,20 +8,118 @@ - #include - #include - #include -+#include - - #include "common.h" - - static Event event; - -+/* A queue of events that we missed while enabling the -+ * breakpoint in one of tasks. */ -+static Event * delayed_events = NULL; -+static Event * end_delayed_events = NULL; -+ -+static enum pcb_status -+first (Process * proc, void * data) -+{ -+ return pcb_stop; -+} -+ -+void -+enque_event(Event * event) -+{ -+ debug(DEBUG_FUNCTION, "%d: queuing event %d for later", -+ event->proc->pid, event->type); -+ Event * ne = malloc(sizeof(*ne)); -+ if (ne == NULL) { -+ perror("event will be missed: malloc"); -+ return; -+ } -+ -+ *ne = *event; -+ ne->next = NULL; -+ if (end_delayed_events == NULL) { -+ assert(delayed_events == NULL); -+ end_delayed_events = delayed_events = ne; -+ } -+ else { -+ assert(delayed_events != NULL); -+ end_delayed_events = end_delayed_events->next = ne; -+ } -+} -+ -+Event * -+each_qd_event(enum ecb_status (*pred)(Event *, void *), void * data) -+{ -+ Event * prev = delayed_events; -+ Event * event; -+ for (event = prev; event != NULL; ) { -+ switch ((*pred)(event, data)) { -+ case ecb_cont: -+ prev = event; -+ event = event->next; -+ continue; -+ -+ case ecb_deque: -+ debug(DEBUG_FUNCTION, "dequeuing event %d for %d", -+ event->type, -+ event->proc != NULL ? event->proc->pid : -1); -+ /* -+ printf("dequeuing event %d for %d\n", event->type, -+ event->proc != NULL ? event->proc->pid : -1) ; -+ */ -+ if (end_delayed_events == event) -+ end_delayed_events = prev; -+ if (delayed_events == event) -+ delayed_events = event->next; -+ else -+ prev->next = event->next; -+ if (delayed_events == NULL) -+ end_delayed_events = NULL; -+ /* fall-through */ -+ -+ case ecb_yield: -+ return event; -+ } -+ } -+ -+ return NULL; -+} -+ -+static enum ecb_status -+event_process_not_reenabling(Event * event, void * data) -+{ -+ if (event->proc == NULL -+ || event->proc->leader == NULL -+ || event->proc->leader->event_handler == NULL) -+ return ecb_deque; -+ else -+ return ecb_cont; -+} -+ -+static Event * -+next_qd_event(void) -+{ -+ return each_qd_event(&event_process_not_reenabling, NULL); -+} -+ - Event * --next_event(void) { -+next_event(void) -+{ - pid_t pid; - int status; - int tmp; - int stop_signal; - - debug(DEBUG_FUNCTION, "next_event()"); -- if (!list_of_processes) { -+ Event * ev; -+ if ((ev = next_qd_event()) != NULL) { -+ event = *ev; -+ free(ev); -+ return &event; -+ } -+ -+ if (!each_process(NULL, &first, NULL)) { - debug(DEBUG_EVENT, "event: No more traced programs: exiting"); - exit(0); - } -@@ -46,26 +144,76 @@ next_event(void) { - return &event; - } - get_arch_dep(event.proc); -- event.proc->instruction_pointer = NULL; - debug(3, "event from pid %u", pid); -- if (event.proc->breakpoints_enabled == -1) { -- event.type = EVENT_NONE; -+ if (event.proc->breakpoints_enabled == -1) - trace_set_options(event.proc, event.proc->pid); -- enable_all_breakpoints(event.proc); -- continue_process(event.proc->pid); -- debug(DEBUG_EVENT, "event: NONE: pid=%d (enabling breakpoints)", pid); -- return &event; -- } else if (!event.proc->libdl_hooked) { -- /* debug struct may not have been written yet.. */ -- if (linkmap_init(event.proc, &main_lte) == 0) { -- event.proc->libdl_hooked = 1; -+ Process *leader = event.proc->leader; -+ if (leader == event.proc) { -+ if (event.proc->breakpoints_enabled == -1) { -+ event.type = EVENT_NONE; -+ enable_all_breakpoints(event.proc); -+ continue_process(event.proc->pid); -+ debug(DEBUG_EVENT, -+ "event: NONE: pid=%d (enabling breakpoints)", -+ pid); -+ return &event; -+ } else if (!event.proc->libdl_hooked) { -+ /* debug struct may not have been written yet.. */ -+ if (linkmap_init(event.proc, &main_lte) == 0) { -+ event.proc->libdl_hooked = 1; -+ } - } - } - -- if (opt_i) { -- event.proc->instruction_pointer = -- get_instruction_pointer(event.proc); -+ /* The process should be stopped after the waitpid call. But -+ * when the whole thread group is terminated, we see -+ * individual tasks spontaneously transitioning from 't' to -+ * 'R' and 'Z'. Calls to ptrace fail and /proc/pid/status may -+ * not even be available anymore, so we can't check in -+ * advance. So we just drop the error checking around ptrace -+ * calls. We check for termination ex post when it fails, -+ * suppress the event, and let the event loop collect the -+ * termination in the next iteration. */ -+#define CHECK_PROCESS_TERMINATED \ -+ do { \ -+ int errno_save = errno; \ -+ switch (process_stopped(pid)) \ -+ case 0: \ -+ case -1: { \ -+ debug(DEBUG_EVENT, \ -+ "process not stopped, is it terminating?"); \ -+ event.type = EVENT_NONE; \ -+ continue_process(event.proc->pid); \ -+ return &event; \ -+ } \ -+ errno = errno_save; \ -+ } while (0) -+ -+ event.proc->instruction_pointer = (void *)(uintptr_t)-1; -+ -+ /* Check for task termination now, before we have a need to -+ * call CHECK_PROCESS_TERMINATED later. That would suppress -+ * the event that we are processing. */ -+ if (WIFSIGNALED(status)) { -+ event.type = EVENT_EXIT_SIGNAL; -+ event.e_un.signum = WTERMSIG(status); -+ debug(DEBUG_EVENT, "event: EXIT_SIGNAL: pid=%d, signum=%d", pid, event.e_un.signum); -+ return &event; -+ } -+ if (WIFEXITED(status)) { -+ event.type = EVENT_EXIT; -+ event.e_un.ret_val = WEXITSTATUS(status); -+ debug(DEBUG_EVENT, "event: EXIT: pid=%d, status=%d", pid, event.e_un.ret_val); -+ return &event; -+ } -+ -+ event.proc->instruction_pointer = get_instruction_pointer(event.proc); -+ if (event.proc->instruction_pointer == (void *)(uintptr_t)-1) { -+ CHECK_PROCESS_TERMINATED; -+ if (errno != 0) -+ perror("get_instruction_pointer"); - } -+ - switch (syscall_p(event.proc, status, &tmp)) { - case 1: - event.type = EVENT_SYSCALL; -@@ -88,10 +236,9 @@ next_event(void) { - debug(DEBUG_EVENT, "event: ARCH_SYSRET: pid=%d, sysnum=%d", pid, tmp); - return &event; - case -1: -- event.type = EVENT_NONE; -- continue_process(event.proc->pid); -- debug(DEBUG_EVENT, "event: NONE: pid=%d (syscall_p returned -1)", pid); -- return &event; -+ CHECK_PROCESS_TERMINATED; -+ if (errno != 0) -+ perror("syscall_p"); - } - if (WIFSTOPPED(status) && ((status>>16 == PTRACE_EVENT_FORK) || (status>>16 == PTRACE_EVENT_VFORK) || (status>>16 == PTRACE_EVENT_CLONE))) { - unsigned long data; -@@ -106,18 +253,6 @@ next_event(void) { - debug(DEBUG_EVENT, "event: EXEC: pid=%d", pid); - return &event; - } -- if (WIFEXITED(status)) { -- event.type = EVENT_EXIT; -- event.e_un.ret_val = WEXITSTATUS(status); -- debug(DEBUG_EVENT, "event: EXIT: pid=%d, status=%d", pid, event.e_un.ret_val); -- return &event; -- } -- if (WIFSIGNALED(status)) { -- event.type = EVENT_EXIT_SIGNAL; -- event.e_un.signum = WTERMSIG(status); -- debug(DEBUG_EVENT, "event: EXIT_SIGNAL: pid=%d, signum=%d", pid, event.e_un.signum); -- return &event; -- } - if (!WIFSTOPPED(status)) { - /* should never happen */ - event.type = EVENT_NONE; -@@ -128,22 +263,19 @@ next_event(void) { - stop_signal = WSTOPSIG(status); - - /* On some targets, breakpoints are signalled not using -- SIGTRAP, but also with SIGILL, SIGSEGV or SIGEMT. Check -- for these. (TODO: is this true?) */ -- if (stop_signal == SIGSEGV -- || stop_signal == SIGILL --#ifdef SIGEMT -- || stop_signal == SIGEMT --#endif -- ) { -- if (!event.proc->instruction_pointer) { -- event.proc->instruction_pointer = -- get_instruction_pointer(event.proc); -- } -+ SIGTRAP, but also with SIGILL, SIGSEGV or SIGEMT. SIGEMT -+ is not defined on Linux, but check for the others. - -- if (address2bpstruct(event.proc, event.proc->instruction_pointer)) -+ N.B. see comments in GDB's infrun.c for details. I've -+ actually seen this on an Itanium machine on RHEL 5, I don't -+ remember the exact kernel version anymore. ia64-sigill.s -+ in the test suite tests this. Petr Machata 2011-06-08. */ -+ void * break_address -+ = event.proc->instruction_pointer - DECR_PC_AFTER_BREAK; -+ if ((stop_signal == SIGSEGV || stop_signal == SIGILL) -+ && leader != NULL -+ && address2bpstruct(leader, break_address)) - stop_signal = SIGTRAP; -- } - - if (stop_signal != (SIGTRAP | event.proc->tracesysgood) - && stop_signal != SIGTRAP) { -@@ -156,12 +288,8 @@ next_event(void) { - /* last case [by exhaustion] */ - event.type = EVENT_BREAKPOINT; - -- if (!event.proc->instruction_pointer) { -- event.proc->instruction_pointer = -- get_instruction_pointer(event.proc); -- } -- event.e_un.brk_addr = -- event.proc->instruction_pointer - DECR_PC_AFTER_BREAK; -+ event.e_un.brk_addr = break_address; - debug(DEBUG_EVENT, "event: BREAKPOINT: pid=%d, addr=%p", pid, event.e_un.brk_addr); -+ - return &event; - } -diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c -index e1cadf7..e3b71e5 100644 ---- a/sysdeps/linux-gnu/proc.c -+++ b/sysdeps/linux-gnu/proc.c -@@ -1,13 +1,22 @@ -+#define _GNU_SOURCE /* For getline. */ - #include "config.h" - #include "common.h" - - #include -+#include -+#include - #include - #include - #include - #include - #include - #include -+#include -+#include -+#include -+#include -+#include -+ - - /* /proc/pid doesn't exist just after the fork, and sometimes `ltrace' - * couldn't open it to find the executable. So it may be necessary to -@@ -16,17 +25,19 @@ - - #define MAX_DELAY 100000 /* 100000 microseconds = 0.1 seconds */ - -+#define PROC_PID_FILE(VAR, FORMAT, PID) \ -+ char VAR[strlen(FORMAT) + 6]; \ -+ sprintf(VAR, FORMAT, PID) -+ - /* - * Returns a (malloc'd) file name corresponding to a running pid - */ - char * - pid2name(pid_t pid) { -- char proc_exe[1024]; -- - if (!kill(pid, 0)) { - int delay = 0; - -- sprintf(proc_exe, "/proc/%d/exe", pid); -+ PROC_PID_FILE(proc_exe, "/proc/%d/exe", pid); - - while (delay < MAX_DELAY) { - if (!access(proc_exe, F_OK)) { -@@ -38,6 +49,197 @@ pid2name(pid_t pid) { - return NULL; - } - -+static FILE * -+open_status_file(pid_t pid) -+{ -+ PROC_PID_FILE(fn, "/proc/%d/status", pid); -+ /* Don't complain if we fail. This would typically happen -+ when the process is about to terminate, and these files are -+ not available anymore. This function is called from the -+ event loop, and we don't want to clutter the output just -+ because the process terminates. */ -+ return fopen(fn, "r"); -+} -+ -+static char * -+find_line_starting(FILE * file, const char * prefix, size_t len) -+{ -+ char * line = NULL; -+ size_t line_len = 0; -+ while (!feof(file)) { -+ if (getline(&line, &line_len, file) < 0) -+ return NULL; -+ if (strncmp(line, prefix, len) == 0) -+ return line; -+ } -+ return NULL; -+} -+ -+static void -+each_line_starting(FILE * file, const char *prefix, -+ enum pcb_status (*cb)(const char * line, const char * prefix, -+ void * data), -+ void * data) -+{ -+ size_t len = strlen(prefix); -+ char * line; -+ while ((line = find_line_starting(file, prefix, len)) != NULL) { -+ enum pcb_status st = (*cb)(line, prefix, data); -+ free (line); -+ if (st == pcb_stop) -+ return; -+ } -+} -+ -+static enum pcb_status -+process_leader_cb(const char * line, const char * prefix, void * data) -+{ -+ pid_t * pidp = data; -+ *pidp = atoi(line + strlen(prefix)); -+ return pcb_stop; -+} -+ -+pid_t -+process_leader(pid_t pid) -+{ -+ pid_t tgid = 0; -+ FILE * file = open_status_file(pid); -+ if (file != NULL) { -+ each_line_starting(file, "Tgid:\t", &process_leader_cb, &tgid); -+ fclose(file); -+ } -+ -+ return tgid; -+} -+ -+static enum pcb_status -+process_stopped_cb(const char * line, const char * prefix, void * data) -+{ -+ char c = line[strlen(prefix)]; -+ // t:tracing stop, T:job control stop -+ *(int *)data = (c == 't' || c == 'T'); -+ return pcb_stop; -+} -+ -+int -+process_stopped(pid_t pid) -+{ -+ int is_stopped = -1; -+ FILE * file = open_status_file(pid); -+ if (file != NULL) { -+ each_line_starting(file, "State:\t", &process_stopped_cb, -+ &is_stopped); -+ fclose(file); -+ } -+ return is_stopped; -+} -+ -+static enum pcb_status -+process_status_cb(const char * line, const char * prefix, void * data) -+{ -+ const char * status = line + strlen(prefix); -+ const char c = *status; -+ -+#define RETURN(C) do { \ -+ *(enum process_status *)data = C; \ -+ return pcb_stop; \ -+ } while (0) -+ -+ switch (c) { -+ case 'Z': RETURN(ps_zombie); -+ case 't': RETURN(ps_tracing_stop); -+ case 'T': { -+ /* This can be either "T (stopped)" or, for older -+ * kernels, "T (tracing stop)". */ -+ if (!strcmp(status, "T (stopped)\n")) -+ RETURN(ps_stop); -+ else if (!strcmp(status, "T (tracing stop)\n")) -+ RETURN(ps_tracing_stop); -+ else { -+ fprintf(stderr, "Unknown process status: %s", -+ status); -+ RETURN(ps_stop); /* Some sort of stop -+ * anyway. */ -+ } -+ } -+ } -+ -+ RETURN(ps_other); -+#undef RETURN -+} -+ -+enum process_status -+process_status(pid_t pid) -+{ -+ enum process_status ret = ps_invalid; -+ FILE * file = open_status_file(pid); -+ if (file != NULL) { -+ each_line_starting(file, "State:\t", &process_status_cb, &ret); -+ fclose(file); -+ if (ret == ps_invalid) -+ error(0, errno, "process_status %d", pid); -+ } else -+ /* If the file is not present, the process presumably -+ * exited already. */ -+ ret = ps_zombie; -+ -+ return ret; -+} -+ -+static int -+all_digits(const char *str) -+{ -+ while (isdigit(*str)) -+ str++; -+ return !*str; -+} -+ -+int -+process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n) -+{ -+ PROC_PID_FILE(fn, "/proc/%d/task", pid); -+ DIR * d = opendir(fn); -+ if (d == NULL) -+ return -1; -+ -+ pid_t *tasks = NULL; -+ size_t n = 0; -+ size_t alloc = 0; -+ -+ while (1) { -+ struct dirent entry; -+ struct dirent *result; -+ if (readdir_r(d, &entry, &result) != 0) { -+ free(tasks); -+ return -1; -+ } -+ if (result == NULL) -+ break; -+ if (result->d_type == DT_DIR && all_digits(result->d_name)) { -+ pid_t npid = atoi(result->d_name); -+ if (n >= alloc) { -+ alloc = alloc > 0 ? (2 * alloc) : 8; -+ pid_t *ntasks = realloc(tasks, -+ sizeof(*tasks) * alloc); -+ if (ntasks == NULL) { -+ free(tasks); -+ return -1; -+ } -+ tasks = ntasks; -+ } -+ if (n >= alloc) -+ abort(); -+ tasks[n++] = npid; -+ } -+ } -+ -+ closedir(d); -+ -+ *ret_tasks = tasks; -+ *ret_n = n; -+ return 0; -+} -+ - static int - find_dynamic_entry_addr(Process *proc, void *pvAddr, int d_tag, void **addr) { - int i = 0, done = 0; -@@ -187,7 +389,10 @@ linkmap_add_cb(void *data) { //const char *lib_name, ElfW(Addr) addr) { - addr = sym.st_value; - add_library_symbol(addr, xptr->name, &library_symbols, LS_TOPLT_NONE, 0); - xptr->found = 1; -- insert_breakpoint(lm_add->proc, sym2addr(lm_add->proc, library_symbols), library_symbols); -+ insert_breakpoint(lm_add->proc, -+ sym2addr(lm_add->proc, -+ library_symbols), -+ library_symbols, 1); - } - } - do_close_elf(<e); -@@ -275,10 +480,22 @@ linkmap_init(Process *proc, struct ltelf *lte) { - data.lte = lte; - - add_library_symbol(rdbg->r_brk, "", &library_symbols, LS_TOPLT_NONE, 0); -- insert_breakpoint(proc, sym2addr(proc, library_symbols), library_symbols); -+ insert_breakpoint(proc, sym2addr(proc, library_symbols), -+ library_symbols, 1); - - crawl_linkmap(proc, rdbg, hook_libdl_cb, &data); - - free(rdbg); - return 0; - } -+ -+int -+task_kill (pid_t pid, int sig) -+{ -+ // Taken from GDB -+ int ret; -+ -+ errno = 0; -+ ret = syscall (__NR_tkill, pid, sig); -+ return ret; -+} -diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c -index e4be465..3800fad 100644 ---- a/sysdeps/linux-gnu/trace.c -+++ b/sysdeps/linux-gnu/trace.c -@@ -7,6 +7,7 @@ - #include - #include "ptrace.h" - #include -+#include - - #include "common.h" - -@@ -69,7 +70,7 @@ umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { - - void - trace_me(void) { -- debug(DEBUG_PROCESS, "trace_me: pid=%d\n", getpid()); -+ debug(DEBUG_PROCESS, "trace_me: pid=%d", getpid()); - if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) { - perror("PTRACE_TRACEME"); - exit(1); -@@ -78,7 +79,7 @@ trace_me(void) { - - int - trace_pid(pid_t pid) { -- debug(DEBUG_PROCESS, "trace_pid: pid=%d\n", pid); -+ debug(DEBUG_PROCESS, "trace_pid: pid=%d", pid); - if (ptrace(PTRACE_ATTACH, pid, 1, 0) < 0) { - return -1; - } -@@ -87,9 +88,9 @@ trace_pid(pid_t pid) { - in pid. The child is sent a SIGSTOP, but will not - necessarily have stopped by the completion of this call; - use wait() to wait for the child to stop. */ -- if (waitpid (pid, NULL, 0) != pid) { -+ if (waitpid (pid, NULL, __WALL) != pid) { - perror ("trace_pid: waitpid"); -- exit (1); -+ return -1; - } - - return 0; -@@ -100,7 +101,7 @@ trace_set_options(Process *proc, pid_t pid) { - if (proc->tracesysgood & 0x80) - return; - -- debug(DEBUG_PROCESS, "trace_set_options: pid=%d\n", pid); -+ debug(DEBUG_PROCESS, "trace_set_options: pid=%d", pid); - - long options = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK | - PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE | -@@ -115,7 +116,7 @@ trace_set_options(Process *proc, pid_t pid) { - - void - untrace_pid(pid_t pid) { -- debug(DEBUG_PROCESS, "untrace_pid: pid=%d\n", pid); -+ debug(DEBUG_PROCESS, "untrace_pid: pid=%d", pid); - ptrace(PTRACE_DETACH, pid, 1, 0); - } - -@@ -126,56 +127,652 @@ continue_after_signal(pid_t pid, int signum) { - - void - continue_after_signal(pid_t pid, int signum) { -- Process *proc; -- - debug(DEBUG_PROCESS, "continue_after_signal: pid=%d, signum=%d", pid, signum); -- -- proc = pid2proc(pid); -- if (proc && proc->breakpoint_being_enabled) { --#if defined __sparc__ || defined __ia64___ || defined __mips__ -- ptrace(PTRACE_SYSCALL, pid, 0, signum); --#else -- ptrace(PTRACE_SINGLESTEP, pid, 0, signum); --#endif -+ ptrace(PTRACE_SYSCALL, pid, 0, signum); -+} -+ -+static enum ecb_status -+event_for_pid(Event * event, void * data) -+{ -+ if (event->proc != NULL && event->proc->pid == (pid_t)(uintptr_t)data) -+ return ecb_yield; -+ return ecb_cont; -+} -+ -+static int -+have_events_for(pid_t pid) -+{ -+ return each_qd_event(event_for_pid, (void *)(uintptr_t)pid) != NULL; -+} -+ -+void -+continue_process(pid_t pid) -+{ -+ debug(DEBUG_PROCESS, "continue_process: pid=%d", pid); -+ -+ /* Only really continue the process if there are no events in -+ the queue for this process. Otherwise just for the other -+ events to arrive. */ -+ if (!have_events_for(pid)) -+ /* We always trace syscalls to control fork(), -+ * clone(), execve()... */ -+ ptrace(PTRACE_SYSCALL, pid, 0, 0); -+ else -+ debug(DEBUG_PROCESS, -+ "putting off the continue, events in que."); -+} -+ -+/** -+ * This is used for bookkeeping related to PIDs that the event -+ * handlers work with. -+ */ -+struct pid_task { -+ pid_t pid; /* This may be 0 for tasks that exited -+ * mid-handling. */ -+ int sigstopped; -+ int got_event; -+ int delivered; -+} * pids; -+ -+struct pid_set { -+ struct pid_task * tasks; -+ size_t count; -+ size_t alloc; -+}; -+ -+/** -+ * Breakpoint re-enablement. When we hit a breakpoint, we must -+ * disable it, single-step, and re-enable it. That single-step can be -+ * done only by one task in a task group, while others are stopped, -+ * otherwise the processes would race for who sees the breakpoint -+ * disabled and who doesn't. The following is to keep track of it -+ * all. -+ */ -+struct process_stopping_handler -+{ -+ Event_Handler super; -+ -+ /* The task that is doing the re-enablement. */ -+ Process * task_enabling_breakpoint; -+ -+ /* The pointer being re-enabled. */ -+ Breakpoint * breakpoint_being_enabled; -+ -+ enum { -+ /* We are waiting for everyone to land in t/T. */ -+ psh_stopping = 0, -+ -+ /* We are doing the PTRACE_SINGLESTEP. */ -+ psh_singlestep, -+ -+ /* We are waiting for all the SIGSTOPs to arrive so -+ * that we can sink them. */ -+ psh_sinking, -+ -+ /* This is for tracking the ugly workaround. */ -+ psh_ugly_workaround, -+ } state; -+ -+ int exiting; -+ -+ struct pid_set pids; -+}; -+ -+static enum pcb_status -+task_stopped(Process * task, void * data) -+{ -+ /* If the task is already stopped, don't worry about it. -+ * Likewise if it managed to become a zombie or terminate in -+ * the meantime. This can happen when the whole thread group -+ * is terminating. */ -+ switch (process_status(task->pid)) { -+ case ps_invalid: -+ case ps_tracing_stop: -+ case ps_zombie: -+ return pcb_cont; -+ default: -+ return pcb_stop; -+ } -+} -+ -+static struct pid_task * -+get_task_info(struct pid_set * pids, pid_t pid) -+{ -+ assert(pid != 0); -+ size_t i; -+ for (i = 0; i < pids->count; ++i) -+ if (pids->tasks[i].pid == pid) -+ return &pids->tasks[i]; -+ -+ return NULL; -+} -+ -+static struct pid_task * -+add_task_info(struct pid_set * pids, pid_t pid) -+{ -+ if (pids->count == pids->alloc) { -+ size_t ns = (2 * pids->alloc) ?: 4; -+ struct pid_task * n = realloc(pids->tasks, -+ sizeof(*pids->tasks) * ns); -+ if (n == NULL) -+ return NULL; -+ pids->tasks = n; -+ pids->alloc = ns; -+ } -+ struct pid_task * task_info = &pids->tasks[pids->count++]; -+ memset(task_info, 0, sizeof(*task_info)); -+ task_info->pid = pid; -+ return task_info; -+} -+ -+static enum pcb_status -+send_sigstop(Process * task, void * data) -+{ -+ Process * leader = task->leader; -+ struct pid_set * pids = data; -+ -+ /* Look for pre-existing task record, or add new. */ -+ struct pid_task * task_info = get_task_info(pids, task->pid); -+ if (task_info == NULL) -+ task_info = add_task_info(pids, task->pid); -+ if (task_info == NULL) { -+ perror("send_sigstop: add_task_info"); -+ destroy_event_handler(leader); -+ /* Signal failure upwards. */ -+ return pcb_stop; -+ } -+ -+ /* This task still has not been attached to. It should be -+ stopped by the kernel. */ -+ if (task->state == STATE_BEING_CREATED) -+ return pcb_cont; -+ -+ /* Don't bother sending SIGSTOP if we are already stopped, or -+ * if we sent the SIGSTOP already, which happens when we -+ * inherit the handler from breakpoint re-enablement. */ -+ if (task_stopped(task, NULL) == pcb_cont) -+ return pcb_cont; -+ if (task_info->sigstopped) { -+ if (!task_info->delivered) -+ return pcb_cont; -+ task_info->delivered = 0; -+ } -+ -+ if (task_kill(task->pid, SIGSTOP) >= 0) { -+ debug(DEBUG_PROCESS, "send SIGSTOP to %d", task->pid); -+ task_info->sigstopped = 1; -+ } else -+ fprintf(stderr, -+ "Warning: couldn't send SIGSTOP to %d\n", task->pid); -+ -+ return pcb_cont; -+} -+ -+/* On certain kernels, detaching right after a singlestep causes the -+ tracee to be killed with a SIGTRAP (that even though the singlestep -+ was properly caught by waitpid. The ugly workaround is to put a -+ breakpoint where IP points and let the process continue. After -+ this the breakpoint can be retracted and the process detached. */ -+static void -+ugly_workaround(Process * proc) -+{ -+ void * ip = get_instruction_pointer(proc); -+ Breakpoint * sbp = dict_find_entry(proc->leader->breakpoints, ip); -+ if (sbp != NULL) -+ enable_breakpoint(proc, sbp); -+ else -+ insert_breakpoint(proc, ip, NULL, 1); -+ ptrace(PTRACE_CONT, proc->pid, 0, 0); -+} -+ -+static void -+process_stopping_done(struct process_stopping_handler * self, Process * leader) -+{ -+ debug(DEBUG_PROCESS, "process stopping done %d", -+ self->task_enabling_breakpoint->pid); -+ size_t i; -+ if (!self->exiting) { -+ for (i = 0; i < self->pids.count; ++i) -+ if (self->pids.tasks[i].pid != 0 -+ && self->pids.tasks[i].delivered) -+ continue_process(self->pids.tasks[i].pid); -+ continue_process(self->task_enabling_breakpoint->pid); -+ destroy_event_handler(leader); - } else { -- ptrace(PTRACE_SYSCALL, pid, 0, signum); -+ self->state = psh_ugly_workaround; -+ ugly_workaround(self->task_enabling_breakpoint); - } - } - --void --continue_process(pid_t pid) { -- /* We always trace syscalls to control fork(), clone(), execve()... */ -+/* Before we detach, we need to make sure that task's IP is on the -+ * edge of an instruction. So for tasks that have a breakpoint event -+ * in the queue, we adjust the instruction pointer, just like -+ * continue_after_breakpoint does. */ -+static enum ecb_status -+undo_breakpoint(Event * event, void * data) -+{ -+ if (event != NULL -+ && event->proc->leader == data -+ && event->type == EVENT_BREAKPOINT) -+ set_instruction_pointer(event->proc, event->e_un.brk_addr); -+ return ecb_cont; -+} - -- debug(DEBUG_PROCESS, "continue_process: pid=%d", pid); -+static enum pcb_status -+untrace_task(Process * task, void * data) -+{ -+ if (task != data) -+ untrace_pid(task->pid); -+ return pcb_cont; -+} - -- ptrace(PTRACE_SYSCALL, pid, 0, 0); -+static enum pcb_status -+remove_task(Process * task, void * data) -+{ -+ /* Don't untrace leader just yet. */ -+ if (task != data) -+ remove_process(task); -+ return pcb_cont; - } - --void --continue_enabling_breakpoint(pid_t pid, Breakpoint *sbp) { -- enable_breakpoint(pid, sbp); -- continue_process(pid); -+static void -+detach_process(Process * leader) -+{ -+ each_qd_event(&undo_breakpoint, leader); -+ disable_all_breakpoints(leader); -+ -+ /* Now untrace the process, if it was attached to by -p. */ -+ struct opt_p_t * it; -+ for (it = opt_p; it != NULL; it = it->next) { -+ Process * proc = pid2proc(it->pid); -+ if (proc == NULL) -+ continue; -+ if (proc->leader == leader) { -+ each_task(leader, &untrace_task, NULL); -+ break; -+ } -+ } -+ each_task(leader, &remove_task, leader); -+ destroy_event_handler(leader); -+ remove_task(leader, NULL); -+} -+ -+static void -+handle_stopping_event(struct pid_task * task_info, Event ** eventp) -+{ -+ /* Mark all events, so that we know whom to SIGCONT later. */ -+ if (task_info != NULL) -+ task_info->got_event = 1; -+ -+ Event * event = *eventp; -+ -+ /* In every state, sink SIGSTOP events for tasks that it was -+ * sent to. */ -+ if (task_info != NULL -+ && event->type == EVENT_SIGNAL -+ && event->e_un.signum == SIGSTOP) { -+ debug(DEBUG_PROCESS, "SIGSTOP delivered to %d", task_info->pid); -+ if (task_info->sigstopped -+ && !task_info->delivered) { -+ task_info->delivered = 1; -+ *eventp = NULL; // sink the event -+ } else -+ fprintf(stderr, "suspicious: %d got SIGSTOP, but " -+ "sigstopped=%d and delivered=%d\n", -+ task_info->pid, task_info->sigstopped, -+ task_info->delivered); -+ } -+} -+ -+/* Some SIGSTOPs may have not been delivered to their respective tasks -+ * yet. They are still in the queue. If we have seen an event for -+ * that process, continue it, so that the SIGSTOP can be delivered and -+ * caught by ltrace. */ -+static void -+continue_for_sigstop_delivery(struct pid_set * pids) -+{ -+ size_t i; -+ for (i = 0; i < pids->count; ++i) { -+ if (pids->tasks[i].pid != 0 -+ && pids->tasks[i].sigstopped -+ && !pids->tasks[i].delivered -+ && pids->tasks[i].got_event) { -+ debug(DEBUG_PROCESS, "continue %d for SIGSTOP delivery", -+ pids->tasks[i].pid); -+ ptrace(PTRACE_SYSCALL, pids->tasks[i].pid, 0, 0); -+ } -+ } -+} -+ -+static int -+event_exit_p(Event * event) -+{ -+ return event != NULL && (event->type == EVENT_EXIT -+ || event->type == EVENT_EXIT_SIGNAL); -+} -+ -+static int -+event_exit_or_none_p(Event * event) -+{ -+ return event == NULL || event_exit_p(event) -+ || event->type == EVENT_NONE; -+} -+ -+static int -+await_sigstop_delivery(struct pid_set * pids, struct pid_task * task_info, -+ Event * event) -+{ -+ /* If we still didn't get our SIGSTOP, continue the process -+ * and carry on. */ -+ if (event != NULL && !event_exit_or_none_p(event) -+ && task_info != NULL && task_info->sigstopped) { -+ debug(DEBUG_PROCESS, "continue %d for SIGSTOP delivery", -+ task_info->pid); -+ /* We should get the signal the first thing -+ * after this, so it should be OK to continue -+ * even if we are over a breakpoint. */ -+ ptrace(PTRACE_SYSCALL, task_info->pid, 0, 0); -+ -+ } else { -+ /* If all SIGSTOPs were delivered, uninstall the -+ * handler and continue everyone. */ -+ /* XXX I suspect that we should check tasks that are -+ * still around. Is things are now, there should be a -+ * race between waiting for everyone to stop and one -+ * of the tasks exiting. */ -+ int all_clear = 1; -+ size_t i; -+ for (i = 0; i < pids->count; ++i) -+ if (pids->tasks[i].pid != 0 -+ && pids->tasks[i].sigstopped -+ && !pids->tasks[i].delivered) { -+ all_clear = 0; -+ break; -+ } -+ return all_clear; -+ } -+ -+ return 0; -+} -+ -+static int -+all_stops_accountable(struct pid_set * pids) -+{ -+ size_t i; -+ for (i = 0; i < pids->count; ++i) -+ if (pids->tasks[i].pid != 0 -+ && !pids->tasks[i].got_event -+ && !have_events_for(pids->tasks[i].pid)) -+ return 0; -+ return 1; -+} -+ -+/* This event handler is installed when we are in the process of -+ * stopping the whole thread group to do the pointer re-enablement for -+ * one of the threads. We pump all events to the queue for later -+ * processing while we wait for all the threads to stop. When this -+ * happens, we let the re-enablement thread to PTRACE_SINGLESTEP, -+ * re-enable, and continue everyone. */ -+static Event * -+process_stopping_on_event(Event_Handler * super, Event * event) -+{ -+ struct process_stopping_handler * self = (void *)super; -+ Process * task = event->proc; -+ Process * leader = task->leader; -+ Breakpoint * sbp = self->breakpoint_being_enabled; -+ Process * teb = self->task_enabling_breakpoint; -+ -+ debug(DEBUG_PROCESS, -+ "pid %d; event type %d; state %d", -+ task->pid, event->type, self->state); -+ -+ struct pid_task * task_info = get_task_info(&self->pids, task->pid); -+ if (task_info == NULL) -+ fprintf(stderr, "new task??? %d\n", task->pid); -+ handle_stopping_event(task_info, &event); -+ -+ int state = self->state; -+ int event_to_queue = !event_exit_or_none_p(event); -+ -+ /* Deactivate the entry if the task exits. */ -+ if (event_exit_p(event) && task_info != NULL) -+ task_info->pid = 0; -+ -+ switch (state) { -+ case psh_stopping: -+ /* If everyone is stopped, singlestep. */ -+ if (each_task(leader, &task_stopped, NULL) == NULL) { -+ debug(DEBUG_PROCESS, "all stopped, now SINGLESTEP %d", -+ teb->pid); -+ if (sbp->enabled) -+ disable_breakpoint(teb, sbp); -+ if (ptrace(PTRACE_SINGLESTEP, teb->pid, 0, 0)) -+ perror("PTRACE_SINGLESTEP"); -+ self->state = state = psh_singlestep; -+ } -+ break; -+ -+ case psh_singlestep: { -+ /* In singlestep state, breakpoint signifies that we -+ * have now stepped, and can re-enable the breakpoint. */ -+ if (event != NULL && task == teb) { -+ /* Essentially we don't care what event caused -+ * the thread to stop. We can do the -+ * re-enablement now. */ -+ if (sbp->enabled) -+ enable_breakpoint(teb, sbp); -+ -+ continue_for_sigstop_delivery(&self->pids); -+ -+ self->breakpoint_being_enabled = NULL; -+ self->state = state = psh_sinking; -+ -+ if (event->type == EVENT_BREAKPOINT) -+ event = NULL; // handled -+ } else -+ break; -+ } -+ -+ /* fall-through */ -+ -+ case psh_sinking: -+ if (await_sigstop_delivery(&self->pids, task_info, event)) -+ process_stopping_done(self, leader); -+ break; -+ -+ case psh_ugly_workaround: -+ if (event == NULL) -+ break; -+ if (event->type == EVENT_BREAKPOINT) { -+ undo_breakpoint(event, leader); -+ if (task == teb) -+ self->task_enabling_breakpoint = NULL; -+ } -+ if (self->task_enabling_breakpoint == NULL -+ && all_stops_accountable(&self->pids)) { -+ undo_breakpoint(event, leader); -+ detach_process(leader); -+ event = NULL; // handled -+ } -+ } -+ -+ if (event != NULL && event_to_queue) { -+ enque_event(event); -+ event = NULL; // sink the event -+ } -+ -+ return event; -+} -+ -+static void -+process_stopping_destroy(Event_Handler * super) -+{ -+ struct process_stopping_handler * self = (void *)super; -+ free(self->pids.tasks); - } - - void --continue_after_breakpoint(Process *proc, Breakpoint *sbp) { -- if (sbp->enabled) -- disable_breakpoint(proc->pid, sbp); -+continue_after_breakpoint(Process *proc, Breakpoint *sbp) -+{ - set_instruction_pointer(proc, sbp->addr); - if (sbp->enabled == 0) { - continue_process(proc->pid); - } else { -- debug(DEBUG_PROCESS, "continue_after_breakpoint: pid=%d, addr=%p", proc->pid, sbp->addr); -- proc->breakpoint_being_enabled = sbp; -+ debug(DEBUG_PROCESS, -+ "continue_after_breakpoint: pid=%d, addr=%p", -+ proc->pid, sbp->addr); - #if defined __sparc__ || defined __ia64___ || defined __mips__ - /* we don't want to singlestep here */ - continue_process(proc->pid); - #else -- ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0); -+ struct process_stopping_handler * handler -+ = calloc(sizeof(*handler), 1); -+ if (handler == NULL) { -+ perror("malloc breakpoint disable handler"); -+ fatal: -+ /* Carry on not bothering to re-enable. */ -+ continue_process(proc->pid); -+ return; -+ } -+ -+ handler->super.on_event = process_stopping_on_event; -+ handler->super.destroy = process_stopping_destroy; -+ handler->task_enabling_breakpoint = proc; -+ handler->breakpoint_being_enabled = sbp; -+ install_event_handler(proc->leader, &handler->super); -+ -+ if (each_task(proc->leader, &send_sigstop, -+ &handler->pids) != NULL) -+ goto fatal; -+ -+ /* And deliver the first fake event, in case all the -+ * conditions are already fulfilled. */ -+ Event ev; -+ ev.type = EVENT_NONE; -+ ev.proc = proc; -+ process_stopping_on_event(&handler->super, &ev); - #endif - } - } - -+/** -+ * Ltrace exit. When we are about to exit, we have to go through all -+ * the processes, stop them all, remove all the breakpoints, and then -+ * detach the processes that we attached to using -p. If we left the -+ * other tasks running, they might hit stray return breakpoints and -+ * produce artifacts, so we better stop everyone, even if it's a bit -+ * of extra work. -+ */ -+struct ltrace_exiting_handler -+{ -+ Event_Handler super; -+ struct pid_set pids; -+}; -+ -+static Event * -+ltrace_exiting_on_event(Event_Handler * super, Event * event) -+{ -+ struct ltrace_exiting_handler * self = (void *)super; -+ Process * task = event->proc; -+ Process * leader = task->leader; -+ -+ debug(DEBUG_PROCESS, "pid %d; event type %d", task->pid, event->type); -+ -+ struct pid_task * task_info = get_task_info(&self->pids, task->pid); -+ handle_stopping_event(task_info, &event); -+ -+ if (event != NULL && event->type == EVENT_BREAKPOINT) -+ undo_breakpoint(event, leader); -+ -+ if (await_sigstop_delivery(&self->pids, task_info, event) -+ && all_stops_accountable(&self->pids)) -+ detach_process(leader); -+ -+ /* Sink all non-exit events. We are about to exit, so we -+ * don't bother with queuing them. */ -+ if (event_exit_or_none_p(event)) -+ return event; -+ -+ return NULL; -+} -+ -+static void -+ltrace_exiting_destroy(Event_Handler * super) -+{ -+ struct ltrace_exiting_handler * self = (void *)super; -+ free(self->pids.tasks); -+} -+ -+static int -+ltrace_exiting_install_handler(Process * proc) -+{ -+ /* Only install to leader. */ -+ if (proc->leader != proc) -+ return 0; -+ -+ /* Perhaps we are already installed, if the user passed -+ * several -p options that are tasks of one process. */ -+ if (proc->event_handler != NULL -+ && proc->event_handler->on_event == <race_exiting_on_event) -+ return 0; -+ -+ /* If stopping handler is already present, let it do the -+ * work. */ -+ if (proc->event_handler != NULL) { -+ assert(proc->event_handler->on_event -+ == &process_stopping_on_event); -+ struct process_stopping_handler * other -+ = (void *)proc->event_handler; -+ other->exiting = 1; -+ return 0; -+ } -+ -+ struct ltrace_exiting_handler * handler -+ = calloc(sizeof(*handler), 1); -+ if (handler == NULL) { -+ perror("malloc exiting handler"); -+ fatal: -+ /* XXXXXXXXXXXXXXXXXXX fixme */ -+ return -1; -+ } -+ -+ handler->super.on_event = ltrace_exiting_on_event; -+ handler->super.destroy = ltrace_exiting_destroy; -+ install_event_handler(proc->leader, &handler->super); -+ -+ if (each_task(proc->leader, &send_sigstop, -+ &handler->pids) != NULL) -+ goto fatal; -+ -+ return 0; -+} -+ -+/* If ltrace gets SIGINT, the processes directly or indirectly run by -+ * ltrace get it too. We just have to wait long enough for the signal -+ * to be delivered and the process terminated, which we notice and -+ * exit ltrace, too. So there's not much we need to do there. We -+ * want to keep tracing those processes as usual, in case they just -+ * SIG_IGN the SIGINT to do their shutdown etc. -+ * -+ * For processes ran on the background, we want to install an exit -+ * handler that stops all the threads, removes all breakpoints, and -+ * detaches. -+ */ -+void -+ltrace_exiting(void) -+{ -+ struct opt_p_t * it; -+ for (it = opt_p; it != NULL; it = it->next) { -+ Process * proc = pid2proc(it->pid); -+ if (proc == NULL || proc->leader == NULL) -+ continue; -+ if (ltrace_exiting_install_handler(proc->leader) < 0) -+ fprintf(stderr, -+ "Couldn't install exiting handler for %d.\n", -+ proc->pid); -+ } -+} -+ - size_t - umovebytes(Process *proc, void *addr, void *laddr, size_t len) { - -diff --git a/sysdeps/linux-gnu/x86_64/trace.c b/sysdeps/linux-gnu/x86_64/trace.c -index e8581af..d0299d9 100644 ---- a/sysdeps/linux-gnu/x86_64/trace.c -+++ b/sysdeps/linux-gnu/x86_64/trace.c -@@ -8,6 +8,7 @@ - #include - #include - #include -+#include - - #include "common.h" - #include "ptrace.h" -@@ -44,8 +45,11 @@ int - syscall_p(Process *proc, int status, int *sysnum) { - if (WIFSTOPPED(status) - && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { -- *sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * ORIG_RAX, 0); -+ long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * ORIG_RAX, 0); -+ if (ret == -1 && errno) -+ return -1; - -+ *sysnum = ret; - if (proc->callstack_depth > 0 && - proc->callstack[proc->callstack_depth - 1].is_syscall && - proc->callstack[proc->callstack_depth - 1].c_un.syscall == *sysnum) { -diff --git a/testsuite/ltrace.main/main-threaded.c b/testsuite/ltrace.main/main-threaded.c -new file mode 100644 -index 0000000..a183966 ---- /dev/null -+++ b/testsuite/ltrace.main/main-threaded.c -@@ -0,0 +1,29 @@ -+#include -+ -+extern void print (char *); -+ -+#define PRINT_LOOP 10 -+ -+void * -+th_main (void *arg) -+{ -+ int i; -+ for (i=0; i. -+ -+set testfile "main-threaded" -+set srcfile ${testfile}.c -+set binfile ${testfile} -+set libfile "main-lib" -+set libsrc $srcdir/$subdir/$libfile.c -+set lib_sl $objdir/$subdir/lib$testfile.so -+ -+ -+if [get_compiler_info $binfile] { -+ return -1 -+} -+ -+verbose "compiling source file now....." -+if { [ltrace_compile_shlib $libsrc $lib_sl debug ] != "" -+ || [ltrace_compile $srcdir/$subdir/$srcfile $objdir/$subdir/$binfile executable [list debug shlib=$lib_sl ldflags=-pthread] ] != ""} { -+ send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" -+} -+ -+# set options for ltrace. -+ltrace_options "-l" "$objdir/$subdir/libmain.so" "-f" -+ -+# Run PUT for ltarce. -+set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile] -+ -+# Check the output of this program. -+verbose "ltrace runtest output: $exec_output\n" -+if [regexp {ELF from incompatible architecture} $exec_output] { -+ fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" -+ return -+} elseif [ regexp {Couldn't get .hash data} $exec_output ] { -+ fail "Couldn't get .hash data!" -+ return -+} -+ -+# Verify the output by checking numbers of print in main-threaded.ltrace. -+set pattern "print(" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 30 diff --git a/ltrace-0.6.0-vfork.patch b/ltrace-0.6.0-vfork.patch deleted file mode 100644 index 8f56414..0000000 --- a/ltrace-0.6.0-vfork.patch +++ /dev/null @@ -1,1130 +0,0 @@ -diff --git a/breakpoints.c b/breakpoints.c -index 1eff8b0..387b2a5 100644 ---- a/breakpoints.c -+++ b/breakpoints.c -@@ -203,14 +203,7 @@ breakpoints_init(Process *proc, int enable) - proc->breakpoints = dict_init(dict_key2hash_int, - dict_key_cmp_int); - -- if (proc->list_of_symbols != NULL) { -- struct library_symbol * sym = proc->list_of_symbols; -- while (sym != NULL) { -- struct library_symbol * next = sym->next; -- free(sym); -- sym = next; -- } -- } -+ destroy_library_symbol_chain(proc->list_of_symbols); - proc->list_of_symbols = NULL; - - if (options.libcalls && proc->filename) { -diff --git a/common.h b/common.h -index 49861cf..c0b24e5 100644 ---- a/common.h -+++ b/common.h -@@ -252,6 +252,7 @@ enum process_status { - ps_invalid, /* Failure. */ - ps_stop, /* Job-control stop. */ - ps_tracing_stop, -+ ps_sleeping, - ps_zombie, - ps_other, /* Necessary other states can be added as needed. */ - }; -@@ -265,6 +266,7 @@ enum pcb_status { - extern Process * pid2proc(pid_t pid); - extern void add_process(Process * proc); - extern void remove_process(Process * proc); -+extern void change_process_leader(Process * proc, Process * leader); - extern Process *each_process(Process * start, - enum pcb_status (* cb)(Process * proc, void * data), - void * data); -@@ -313,6 +315,10 @@ extern void add_library_symbol(GElf_Addr addr, const char *name, - struct library_symbol **library_symbolspp, - enum toplt type_of_plt, int is_weak); - -+extern struct library_symbol * clone_library_symbol(struct library_symbol * s); -+extern void destroy_library_symbol(struct library_symbol * s); -+extern void destroy_library_symbol_chain(struct library_symbol * chain); -+ - /* Arch-dependent stuff: */ - extern char * pid2name(pid_t pid); - extern pid_t process_leader(pid_t pid); -@@ -335,6 +341,7 @@ extern int syscall_p(Process * proc, int status, int * sysnum); - extern void continue_process(pid_t pid); - extern void continue_after_signal(pid_t pid, int signum); - extern void continue_after_breakpoint(Process * proc, Breakpoint * sbp); -+extern void continue_after_vfork(Process * proc); - extern void ltrace_exiting(void); - extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); - extern void save_register_args(enum tof type, Process * proc); -diff --git a/dict.c b/dict.c -index 486a461..ba318cd 100644 ---- a/dict.c -+++ b/dict.c -@@ -180,7 +180,9 @@ dict_key_cmp_int(void *key1, void *key2) { - } - - Dict * --dict_clone(Dict *old, void * (*key_clone)(void*), void * (*value_clone)(void*)) { -+dict_clone2(Dict * old, void * (*key_clone)(void *, void *), -+ void * (*value_clone)(void *, void *), void * data) -+{ - Dict *d; - int i; - -@@ -199,17 +201,64 @@ dict_clone(Dict *old, void * (*key_clone)(void*), void * (*value_clone)(void*)) - de_old = old->buckets[i]; - de_new = &d->buckets[i]; - while (de_old) { -+ void * nkey, * nval; - *de_new = malloc(sizeof(struct dict_entry)); - if (!*de_new) { - perror("malloc()"); - exit(1); - } - memcpy(*de_new, de_old, sizeof(struct dict_entry)); -- (*de_new)->key = key_clone(de_old->key); -- (*de_new)->value = value_clone(de_old->value); -+ -+ /* The error detection is rather weak :-/ */ -+ nkey = key_clone(de_old->key, data); -+ if (nkey == NULL && de_old->key != NULL) { -+ perror("key_clone"); -+ err: -+ /* XXX Will this actually work? We -+ * simply memcpy the old dictionary -+ * over up there. */ -+ dict_clear(d); -+ free(de_new); -+ return NULL; -+ } -+ -+ nval = value_clone(de_old->value, data); -+ if (nval == NULL && de_old->value != NULL) { -+ perror("value_clone"); -+ goto err; -+ } -+ -+ (*de_new)->key = nkey; -+ (*de_new)->value = nval; - de_new = &(*de_new)->next; - de_old = de_old->next; - } - } - return d; - } -+ -+struct wrap_clone_cb -+{ -+ void * (*key_clone)(void *); -+ void * (*value_clone)(void *); -+}; -+ -+static void * -+value_clone_1(void * arg, void * data) -+{ -+ return ((struct wrap_clone_cb *)data)->value_clone(arg); -+} -+ -+static void * -+key_clone_1(void * arg, void * data) -+{ -+ return ((struct wrap_clone_cb *)data)->key_clone(arg); -+} -+ -+Dict * -+dict_clone(Dict * old, void * (*key_clone)(void *), -+ void * (*value_clone)(void *)) -+{ -+ struct wrap_clone_cb cb = { key_clone, value_clone }; -+ return dict_clone2(old, &key_clone_1, &value_clone_1, &cb); -+} -diff --git a/dict.h b/dict.h -index a70c3d5..27dc7bf 100644 ---- a/dict.h -+++ b/dict.h -@@ -18,3 +18,7 @@ extern int dict_key_cmp_string(void *key1, void *key2); - extern unsigned int dict_key2hash_int(void *key); - extern int dict_key_cmp_int(void *key1, void *key2); - extern Dict * dict_clone(Dict *old, void * (*key_clone)(void*), void * (*value_clone)(void*)); -+extern Dict * dict_clone2(Dict * old, -+ void * (* key_clone)(void * key, void * data), -+ void * (* value_clone)(void * value, void * data), -+ void * data); -diff --git a/handle_event.c b/handle_event.c -index 0aa40f7..f56c537 100644 ---- a/handle_event.c -+++ b/handle_event.c -@@ -7,6 +7,7 @@ - #include - #include - #include -+#include - - #include "common.h" - -@@ -35,6 +36,18 @@ static char * shortsignal(Process *proc, int signum); - static char * sysname(Process *proc, int sysnum); - static char * arch_sysname(Process *proc, int sysnum); - -+static Event * -+call_handler(Process * proc, Event * event) -+{ -+ assert(proc != NULL); -+ -+ Event_Handler * handler = proc->event_handler; -+ if (handler == NULL) -+ return event; -+ -+ return (*handler->on_event) (handler, event); -+} -+ - void - handle_event(Event *event) { - if (exiting == 1) { -@@ -44,15 +57,22 @@ handle_event(Event *event) { - } - debug(DEBUG_FUNCTION, "handle_event(pid=%d, type=%d)", - event->proc ? event->proc->pid : -1, event->type); -- /* If the thread group defines an overriding event handler, -- give it a chance to kick in. */ -- if (event->proc != NULL -- && event->proc->leader != NULL) { -- Event_Handler * handler = event->proc->leader->event_handler; -- if (handler != NULL) { -- event = (*handler->on_event) (handler, event); -+ -+ /* If the thread group or an individual task define an -+ overriding event handler, give them a chance to kick in. -+ We will end up calling both handlers, if the first one -+ doesn't sink the event. */ -+ if (event->proc != NULL) { -+ event = call_handler(event->proc, event); -+ if (event == NULL) -+ /* It was handled. */ -+ return; -+ -+ /* Note: the previous handler has a chance to alter -+ * the event. */ -+ if (event->proc->leader != NULL) { -+ event = call_handler(event->proc->leader, event); - if (event == NULL) -- /* It was handled. */ - return; - } - } -@@ -102,6 +122,7 @@ handle_event(Event *event) { - handle_arch_sysret(event); - return; - case EVENT_CLONE: -+ case EVENT_VFORK: - debug(1, "event: clone (%u)", event->e_un.newpid); - handle_clone(event); - return; -@@ -125,14 +146,17 @@ handle_event(Event *event) { - - /* TODO */ - static void * --address_clone(void * addr) { -+address_clone(void * addr, void * data) -+{ - debug(DEBUG_FUNCTION, "address_clone(%p)", addr); - return addr; - } - - static void * --breakpoint_clone(void * bp) { -+breakpoint_clone(void * bp, void * data) -+{ - Breakpoint * b; -+ Dict * map = data; - debug(DEBUG_FUNCTION, "breakpoint_clone(%p)", bp); - b = malloc(sizeof(Breakpoint)); - if (!b) { -@@ -140,6 +164,15 @@ breakpoint_clone(void * bp) { - exit(1); - } - memcpy(b, bp, sizeof(Breakpoint)); -+ if (b->libsym != NULL) { -+ struct library_symbol * sym = dict_find_entry(map, b->libsym); -+ if (b->libsym == NULL) { -+ fprintf(stderr, "Can't find cloned symbol %s.\n", -+ b->libsym->name); -+ return NULL; -+ } -+ b->libsym = sym; -+ } - return b; - } - -@@ -204,6 +237,40 @@ pending_new_remove(pid_t pid) { - } - } - -+static int -+clone_breakpoints(Process * proc, Process * orig_proc) -+{ -+ /* When copying breakpoints, we also have to copy the -+ * referenced symbols, and link them properly. */ -+ Dict * map = dict_init(&dict_key2hash_int, &dict_key_cmp_int); -+ struct library_symbol * it = proc->list_of_symbols; -+ proc->list_of_symbols = NULL; -+ for (; it != NULL; it = it->next) { -+ struct library_symbol * libsym = clone_library_symbol(it); -+ if (libsym == NULL) { -+ int save_errno; -+ err: -+ save_errno = errno; -+ destroy_library_symbol_chain(proc->list_of_symbols); -+ dict_clear(map); -+ errno = save_errno; -+ return -1; -+ } -+ libsym->next = proc->list_of_symbols; -+ proc->list_of_symbols = libsym; -+ if (dict_enter(map, it, libsym) != 0) -+ goto err; -+ } -+ -+ proc->breakpoints = dict_clone2(orig_proc->breakpoints, -+ address_clone, breakpoint_clone, map); -+ if (proc->breakpoints == NULL) -+ goto err; -+ -+ dict_clear(map); -+ return 0; -+} -+ - static void - handle_clone(Event * event) { - Process *p; -@@ -216,7 +283,6 @@ handle_clone(Event * event) { - exit(1); - } - memcpy(p, event->proc, sizeof(Process)); -- p->breakpoints = dict_clone(event->proc->breakpoints, address_clone, breakpoint_clone); - p->pid = event->e_un.newpid; - p->parent = event->proc; - -@@ -239,7 +305,17 @@ handle_clone(Event * event) { - p->state = STATE_BEING_CREATED; - add_process(p); - } -- continue_process(event->proc->pid); -+ -+ if (p->leader == p) -+ clone_breakpoints(p, event->proc->leader); -+ else -+ /* Thread groups share breakpoints. */ -+ p->breakpoints = NULL; -+ -+ if (event->type == EVENT_VFORK) -+ continue_after_vfork(p); -+ else -+ continue_process(event->proc->pid); - } - - static void -@@ -253,8 +329,6 @@ handle_new(Event * event) { - pending_new_insert(event->e_un.newpid); - } else { - assert(proc->state == STATE_BEING_CREATED); -- if (proc->event_handler != NULL) -- destroy_event_handler(proc); - if (options.follow) { - proc->state = STATE_ATTACHED; - } else { -diff --git a/libltrace.c b/libltrace.c -index e731fe1..19bfafd 100644 ---- a/libltrace.c -+++ b/libltrace.c -@@ -107,6 +107,11 @@ ltrace_init(int argc, char **argv) { - } - } - if (command) { -+ /* Check that the binary ABI is supported before -+ * calling execute_program. */ -+ struct ltelf lte = {}; -+ open_elf(<e, command); -+ - open_program(command, execute_program(command, argv), 0); - } - opt_p_tmp = opt_p; -diff --git a/ltrace-elf.c b/ltrace-elf.c -index d88d5a6..9aea4a9 100644 ---- a/ltrace-elf.c -+++ b/ltrace-elf.c -@@ -136,18 +136,14 @@ static GElf_Addr get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot, - } - - int --do_init_elf(struct ltelf *lte, const char *filename) { -- int i; -- GElf_Addr relplt_addr = 0; -- size_t relplt_size = 0; -- -- debug(DEBUG_FUNCTION, "do_init_elf(filename=%s)", filename); -- debug(1, "Reading ELF from %s...", filename); -- -+open_elf(struct ltelf *lte, const char *filename) -+{ - lte->fd = open(filename, O_RDONLY); - if (lte->fd == -1) - return 1; - -+ elf_version(EV_CURRENT); -+ - #ifdef HAVE_ELF_C_READ_MMAP - lte->elf = elf_begin(lte->fd, ELF_C_READ_MMAP, NULL); - #else -@@ -180,6 +176,21 @@ do_init_elf(struct ltelf *lte, const char *filename) { - error(EXIT_FAILURE, 0, - "\"%s\" is ELF from incompatible architecture", filename); - -+ return 0; -+} -+ -+int -+do_init_elf(struct ltelf *lte, const char *filename) { -+ int i; -+ GElf_Addr relplt_addr = 0; -+ size_t relplt_size = 0; -+ -+ debug(DEBUG_FUNCTION, "do_init_elf(filename=%s)", filename); -+ debug(1, "Reading ELF from %s...", filename); -+ -+ if (open_elf(lte, filename) < 0) -+ return -1; -+ - Elf_Data *plt_data = NULL; - GElf_Addr ppcgot = 0; - -@@ -465,30 +476,76 @@ do_close_elf(struct ltelf *lte) { - close(lte->fd); - } - -+static struct library_symbol * -+create_library_symbol(const char * name, GElf_Addr addr) -+{ -+ size_t namel = strlen(name) + 1; -+ struct library_symbol * sym = calloc(sizeof(*sym) + namel, 1); -+ if (sym == NULL) { -+ perror("create_library_symbol"); -+ return NULL; -+ } -+ sym->name = (char *)(sym + 1); -+ memcpy(sym->name, name, namel); -+ sym->enter_addr = (void *)(uintptr_t) addr; -+ return sym; -+} -+ - void - add_library_symbol(GElf_Addr addr, const char *name, - struct library_symbol **library_symbolspp, -- enum toplt type_of_plt, int is_weak) { -+ enum toplt type_of_plt, int is_weak) -+{ - struct library_symbol *s; - - debug(DEBUG_FUNCTION, "add_library_symbol()"); - -- s = malloc(sizeof(struct library_symbol) + strlen(name) + 1); -+ s = create_library_symbol(name, addr); - if (s == NULL) - error(EXIT_FAILURE, errno, "add_library_symbol failed"); - - s->needs_init = 1; - s->is_weak = is_weak; - s->plt_type = type_of_plt; -+ - s->next = *library_symbolspp; -- s->enter_addr = (void *)(uintptr_t) addr; -- s->name = (char *)(s + 1); -- strcpy(s->name, name); - *library_symbolspp = s; - - debug(2, "addr: %p, symbol: \"%s\"", (void *)(uintptr_t) addr, name); - } - -+struct library_symbol * -+clone_library_symbol(struct library_symbol * sym) -+{ -+ struct library_symbol * copy -+ = create_library_symbol(sym->name, -+ (GElf_Addr)(uintptr_t)sym->enter_addr); -+ if (copy == NULL) -+ return NULL; -+ -+ copy->needs_init = sym->needs_init; -+ copy->is_weak = sym->is_weak; -+ copy->plt_type = sym->plt_type; -+ -+ return copy; -+} -+ -+void -+destroy_library_symbol(struct library_symbol * sym) -+{ -+ free(sym); -+} -+ -+void -+destroy_library_symbol_chain(struct library_symbol * sym) -+{ -+ while (sym != NULL) { -+ struct library_symbol * next = sym->next; -+ destroy_library_symbol(sym); -+ sym = next; -+ } -+} -+ - /* stolen from elfutils-0.123 */ - static unsigned long - private_elf_gnu_hash(const char *name) { -@@ -620,8 +677,6 @@ read_elf(Process *proc) { - library_num = 0; - proc->libdl_hooked = 0; - -- elf_version(EV_CURRENT); -- - if (do_init_elf(lte, proc->filename)) - return NULL; - -@@ -637,7 +692,7 @@ read_elf(Process *proc) { - for (i = 0; i < library_num; ++i) { - if (do_init_elf(<e[i + 1], library[i])) - error(EXIT_FAILURE, errno, "Can't open \"%s\"", -- proc->filename); -+ library[i]); - } - - if (!options.no_plt) { -diff --git a/ltrace-elf.h b/ltrace-elf.h -index a29fe2c..3b675c5 100644 ---- a/ltrace-elf.h -+++ b/ltrace-elf.h -@@ -44,6 +44,7 @@ struct ltelf { - extern size_t library_num; - extern char *library[MAX_LIBRARIES]; - -+extern int open_elf(struct ltelf *lte, const char *filename); - extern struct library_symbol *read_elf(Process *); - - extern GElf_Addr arch_plt_sym_val(struct ltelf *, size_t, GElf_Rela *); -diff --git a/ltrace.h b/ltrace.h -index 0ff4572..194704d 100644 ---- a/ltrace.h -+++ b/ltrace.h -@@ -9,6 +9,7 @@ enum Event_type { - EVENT_ARCH_SYSCALL, - EVENT_ARCH_SYSRET, - EVENT_CLONE, -+ EVENT_VFORK, - EVENT_EXEC, - EVENT_BREAKPOINT, - EVENT_LIBCALL, -diff --git a/proc.c b/proc.c -index 0425e09..f4d3396 100644 ---- a/proc.c -+++ b/proc.c -@@ -154,9 +154,30 @@ pid2proc(pid_t pid) { - return each_process(NULL, &find_proc, (void *)(uintptr_t)pid); - } - -- - static Process * list_of_processes = NULL; - -+static void -+unlist_process(Process * proc) -+{ -+ Process *tmp; -+ -+ if (list_of_processes == proc) { -+ list_of_processes = list_of_processes->next; -+ return; -+ } -+ -+ for (tmp = list_of_processes; ; tmp = tmp->next) { -+ /* If the following assert fails, the process wasn't -+ * in the list. */ -+ assert(tmp->next != NULL); -+ -+ if (tmp->next == proc) { -+ tmp->next = tmp->next->next; -+ return; -+ } -+ } -+} -+ - Process * - each_process(Process * proc, - enum pcb_status (* cb)(Process * proc, void * data), -@@ -213,6 +234,23 @@ add_process(Process * proc) - *leaderp = proc; - } - -+void -+change_process_leader(Process * proc, Process * leader) -+{ -+ Process ** leaderp = &list_of_processes; -+ if (proc->leader == leader) -+ return; -+ -+ assert(leader != NULL); -+ unlist_process(proc); -+ if (proc != leader) -+ leaderp = &leader->next; -+ -+ proc->leader = leader; -+ proc->next = *leaderp; -+ *leaderp = proc; -+} -+ - static enum pcb_status - clear_leader(Process * proc, void * data) - { -@@ -242,31 +280,14 @@ delete_events_for(Process * proc) - void - remove_process(Process *proc) - { -- Process *tmp, *tmp2; -- - debug(DEBUG_FUNCTION, "remove_proc(pid=%d)", proc->pid); - - if (proc->leader == proc) - each_task(proc, &clear_leader, NULL); - -- if (list_of_processes == proc) { -- tmp = list_of_processes; -- list_of_processes = list_of_processes->next; -- delete_events_for(tmp); -- free(tmp); -- return; -- } -- tmp = list_of_processes; -- while (tmp->next) { -- if (tmp->next == proc) { -- tmp2 = tmp->next; -- tmp->next = tmp->next->next; -- delete_events_for(tmp2); -- free(tmp2); -- return; -- } -- tmp = tmp->next; -- } -+ unlist_process(proc); -+ delete_events_for(proc); -+ free(proc); - } - - void -@@ -283,7 +304,8 @@ destroy_event_handler(Process * proc) - Event_Handler * handler = proc->event_handler; - debug(DEBUG_FUNCTION, "destroy_event_handler(pid=%d, %p)", proc->pid, handler); - assert(handler != NULL); -- handler->destroy(handler); -+ if (handler->destroy != NULL) -+ handler->destroy(handler); - free(handler); - proc->event_handler = NULL; - } -diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c -index 8a79583..0685342 100644 ---- a/sysdeps/linux-gnu/events.c -+++ b/sysdeps/linux-gnu/events.c -@@ -240,13 +240,20 @@ next_event(void) - if (errno != 0) - perror("syscall_p"); - } -- if (WIFSTOPPED(status) && ((status>>16 == PTRACE_EVENT_FORK) || (status>>16 == PTRACE_EVENT_VFORK) || (status>>16 == PTRACE_EVENT_CLONE))) { -- unsigned long data; -- ptrace(PTRACE_GETEVENTMSG, pid, NULL, &data); -- event.type = EVENT_CLONE; -- event.e_un.newpid = data; -- debug(DEBUG_EVENT, "event: CLONE: pid=%d, newpid=%d", pid, (int)data); -- return &event; -+ if (WIFSTOPPED(status)) { -+ int what = status >> 16; -+ if (what == PTRACE_EVENT_VFORK -+ || what == PTRACE_EVENT_FORK -+ || what == PTRACE_EVENT_CLONE) { -+ unsigned long data; -+ event.type = what == PTRACE_EVENT_VFORK -+ ? EVENT_VFORK : EVENT_CLONE; -+ ptrace(PTRACE_GETEVENTMSG, pid, NULL, &data); -+ event.e_un.newpid = data; -+ debug(DEBUG_EVENT, "event: CLONE: pid=%d, newpid=%d", -+ pid, (int)data); -+ return &event; -+ } - } - if (WIFSTOPPED(status) && (status>>16 == PTRACE_EVENT_EXEC)) { - event.type = EVENT_EXEC; -diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c -index e3b71e5..a99593c 100644 ---- a/sysdeps/linux-gnu/proc.c -+++ b/sysdeps/linux-gnu/proc.c -@@ -148,7 +148,7 @@ process_status_cb(const char * line, const char * prefix, void * data) - switch (c) { - case 'Z': RETURN(ps_zombie); - case 't': RETURN(ps_tracing_stop); -- case 'T': { -+ case 'T': - /* This can be either "T (stopped)" or, for older - * kernels, "T (tracing stop)". */ - if (!strcmp(status, "T (stopped)\n")) -@@ -161,7 +161,8 @@ process_status_cb(const char * line, const char * prefix, void * data) - RETURN(ps_stop); /* Some sort of stop - * anyway. */ - } -- } -+ case 'D': -+ case 'S': RETURN(ps_sleeping); - } - - RETURN(ps_other); -diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c -index f8a1779..ba3806d 100644 ---- a/sysdeps/linux-gnu/trace.c -+++ b/sysdeps/linux-gnu/trace.c -@@ -164,9 +164,10 @@ continue_process(pid_t pid) - struct pid_task { - pid_t pid; /* This may be 0 for tasks that exited - * mid-handling. */ -- int sigstopped; -- int got_event; -- int delivered; -+ int sigstopped : 1; -+ int got_event : 1; -+ int delivered : 1; -+ int vforked : 1; - } * pids; - - struct pid_set { -@@ -213,23 +214,6 @@ struct process_stopping_handler - struct pid_set pids; - }; - --static enum pcb_status --task_stopped(Process * task, void * data) --{ -- /* If the task is already stopped, don't worry about it. -- * Likewise if it managed to become a zombie or terminate in -- * the meantime. This can happen when the whole thread group -- * is terminating. */ -- switch (process_status(task->pid)) { -- case ps_invalid: -- case ps_tracing_stop: -- case ps_zombie: -- return pcb_cont; -- default: -- return pcb_stop; -- } --} -- - static struct pid_task * - get_task_info(struct pid_set * pids, pid_t pid) - { -@@ -261,6 +245,57 @@ add_task_info(struct pid_set * pids, pid_t pid) - } - - static enum pcb_status -+task_stopped(Process * task, void * data) -+{ -+ enum process_status st = process_status(task->pid); -+ if (data != NULL) -+ *(enum process_status *)data = st; -+ -+ /* If the task is already stopped, don't worry about it. -+ * Likewise if it managed to become a zombie or terminate in -+ * the meantime. This can happen when the whole thread group -+ * is terminating. */ -+ switch (st) { -+ case ps_invalid: -+ case ps_tracing_stop: -+ case ps_zombie: -+ return pcb_cont; -+ default: -+ return pcb_stop; -+ } -+} -+ -+/* Task is blocked if it's stopped, or if it's a vfork parent. */ -+static enum pcb_status -+task_blocked(Process * task, void * data) -+{ -+ struct pid_set * pids = data; -+ struct pid_task * task_info = get_task_info(pids, task->pid); -+ if (task_info != NULL -+ && task_info->vforked) -+ return pcb_cont; -+ -+ return task_stopped(task, NULL); -+} -+ -+static Event * process_vfork_on_event(Event_Handler * super, Event * event); -+ -+static enum pcb_status -+task_vforked(Process * task, void * data) -+{ -+ if (task->event_handler != NULL -+ && task->event_handler->on_event == &process_vfork_on_event) -+ return pcb_stop; -+ return pcb_cont; -+} -+ -+static int -+is_vfork_parent(Process * task) -+{ -+ return each_task(task->leader, &task_vforked, NULL) != NULL; -+} -+ -+static enum pcb_status - send_sigstop(Process * task, void * data) - { - Process * leader = task->leader; -@@ -283,9 +318,11 @@ send_sigstop(Process * task, void * data) - return pcb_cont; - - /* Don't bother sending SIGSTOP if we are already stopped, or -- * if we sent the SIGSTOP already, which happens when we -- * inherit the handler from breakpoint re-enablement. */ -- if (task_stopped(task, NULL) == pcb_cont) -+ * if we sent the SIGSTOP already, which happens when we are -+ * handling "onexit" and inherited the handler from breakpoint -+ * re-enablement. */ -+ enum process_status st; -+ if (task_stopped(task, &st) == pcb_cont) - return pcb_cont; - if (task_info->sigstopped) { - if (!task_info->delivered) -@@ -293,6 +330,16 @@ send_sigstop(Process * task, void * data) - task_info->delivered = 0; - } - -+ /* Also don't attempt to stop the process if it's a parent of -+ * vforked process. We set up event handler specially to hint -+ * us. In that case parent is in D state, which we use to -+ * weed out unnecessary looping. */ -+ if (st == ps_sleeping -+ && is_vfork_parent (task)) { -+ task_info->vforked = 1; -+ return pcb_cont; -+ } -+ - if (task_kill(task->pid, SIGSTOP) >= 0) { - debug(DEBUG_PROCESS, "send SIGSTOP to %d", task->pid); - task_info->sigstopped = 1; -@@ -536,7 +583,7 @@ process_stopping_on_event(Event_Handler * super, Event * event) - switch (state) { - case psh_stopping: - /* If everyone is stopped, singlestep. */ -- if (each_task(leader, &task_stopped, NULL) == NULL) { -+ if (each_task(leader, &task_blocked, &self->pids) == NULL) { - debug(DEBUG_PROCESS, "all stopped, now SINGLESTEP %d", - teb->pid); - if (sbp->enabled) -@@ -742,6 +789,110 @@ ltrace_exiting_install_handler(Process * proc) - return 0; - } - -+/* -+ * When the traced process vforks, it's suspended until the child -+ * process calls _exit or exec*. In the meantime, the two share the -+ * address space. -+ * -+ * The child process should only ever call _exit or exec*, but we -+ * can't count on that (it's not the role of ltrace to policy, but to -+ * observe). In any case, we will _at least_ have to deal with -+ * removal of vfork return breakpoint (which we have to smuggle back -+ * in, so that the parent can see it, too), and introduction of exec* -+ * return breakpoint. Since we already have both breakpoint actions -+ * to deal with, we might as well support it all. -+ * -+ * The gist is that we pretend that the child is in a thread group -+ * with its parent, and handle it as a multi-threaded case, with the -+ * exception that we know that the parent is blocked, and don't -+ * attempt to stop it. When the child execs, we undo the setup. -+ * -+ * XXX The parent process could be un-suspended before ltrace gets -+ * child exec/exit event. Make sure this is taken care of. -+ */ -+ -+struct process_vfork_handler -+{ -+ Event_Handler super; -+ void * bp_addr; -+}; -+ -+static Event * -+process_vfork_on_event(Event_Handler * super, Event * event) -+{ -+ struct process_vfork_handler * self = (void *)super; -+ Breakpoint * sbp; -+ assert(self != NULL); -+ -+ switch (event->type) { -+ case EVENT_BREAKPOINT: -+ /* Remember the vfork return breakpoint. */ -+ if (self->bp_addr == NULL) -+ self->bp_addr = event->e_un.brk_addr; -+ break; -+ -+ case EVENT_EXIT: -+ case EVENT_EXIT_SIGNAL: -+ case EVENT_EXEC: -+ /* Smuggle back in the vfork return breakpoint, so -+ * that our parent can trip over it once again. */ -+ if (self->bp_addr != NULL) { -+ sbp = dict_find_entry(event->proc->leader->breakpoints, -+ self->bp_addr); -+ if (sbp != NULL) -+ insert_breakpoint(event->proc->leader, -+ self->bp_addr, sbp->libsym, -+ 1); -+ } -+ -+ continue_process(event->proc->parent->pid); -+ -+ /* Remove the leader that we artificially set up -+ * earlier. */ -+ change_process_leader(event->proc, event->proc); -+ destroy_event_handler(event->proc); -+ -+ /* XXXXX this could happen in the middle of handling -+ * multi-threaded breakpoint. We must be careful to -+ * undo the effects that we introduced above (vforked -+ * = 1 et.al.). */ -+ -+ default: -+ ; -+ } -+ -+ return event; -+} -+ -+void -+continue_after_vfork(Process * proc) -+{ -+ debug(DEBUG_PROCESS, "continue_after_vfork: pid=%d", proc->pid); -+ struct process_vfork_handler * handler = calloc(sizeof(*handler), 1); -+ if (handler == NULL) { -+ perror("malloc vfork handler"); -+ /* Carry on not bothering to treat the process as -+ * necessary. */ -+ continue_process(proc->parent->pid); -+ return; -+ } -+ -+ /* We must set up custom event handler, so that we see -+ * exec/exit events for the task itself. */ -+ handler->super.on_event = process_vfork_on_event; -+ install_event_handler(proc, &handler->super); -+ -+ /* Make sure that the child is sole thread. */ -+ assert(proc->leader == proc); -+ assert(proc->next == NULL || proc->next->leader != proc); -+ -+ /* Make sure that the child's parent is properly set up. */ -+ assert(proc->parent != NULL); -+ assert(proc->parent->leader != NULL); -+ -+ change_process_leader(proc, proc->parent->leader); -+} -+ - /* If ltrace gets SIGINT, the processes directly or indirectly run by - * ltrace get it too. We just have to wait long enough for the signal - * to be delivered and the process terminated, which we notice and -diff --git a/testsuite/ltrace.main/main-threaded.exp b/testsuite/ltrace.main/main-threaded.exp -index 0157797..5539805 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" "$objdir/$subdir/libmain.so" "-f" -+ltrace_options "-l" "$lib_sl" "-f" - - # Run PUT for ltarce. - set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile] -@@ -35,5 +35,5 @@ if [regexp {ELF from incompatible architecture} $exec_output] { - } - - # Verify the output by checking numbers of print in main-threaded.ltrace. --set pattern "print(" -+set pattern "print" - ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 30 -diff --git a/testsuite/ltrace.main/main-vfork.c b/testsuite/ltrace.main/main-vfork.c -new file mode 100644 -index 0000000..a5f6c40 ---- /dev/null -+++ b/testsuite/ltrace.main/main-vfork.c -@@ -0,0 +1,28 @@ -+#include -+ -+extern void print (char *); -+ -+#define PRINT_LOOP 10 -+ -+void -+th_main (char * arg) -+{ -+ int i; -+ for (i=0; i. -+ -+set testfile "main-vfork" -+set srcfile ${testfile}.c -+set binfile ${testfile} -+set libfile "main-lib" -+set libsrc $srcdir/$subdir/$libfile.c -+set lib_sl $objdir/$subdir/lib$testfile.so -+ -+ -+if [get_compiler_info $binfile] { -+ return -1 -+} -+ -+verbose "compiling source file now....." -+if { [ltrace_compile_shlib $libsrc $lib_sl debug ] != "" -+ || [ltrace_compile $srcdir/$subdir/$srcfile $objdir/$subdir/$binfile executable [list debug shlib=$lib_sl] ] != ""} { -+ send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" -+} -+ -+# set options for ltrace. -+ltrace_options "-l" "$lib_sl" "-f" -+ -+# Run PUT for ltarce. -+set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile] -+ -+# Check the output of this program. -+verbose "ltrace runtest output: $exec_output\n" -+if [regexp {ELF from incompatible architecture} $exec_output] { -+ fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" -+ return -+} elseif [ regexp {Couldn't get .hash data} $exec_output ] { -+ fail "Couldn't get .hash data!" -+ return -+} -+ -+# Verify the output by checking numbers of print in main-vfork.ltrace. -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace "print" 20 -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace "vfork resumed" 2 -diff --git a/testsuite/ltrace.torture/vfork-thread.c b/testsuite/ltrace.torture/vfork-thread.c -new file mode 100644 -index 0000000..f909bd3 ---- /dev/null -+++ b/testsuite/ltrace.torture/vfork-thread.c -@@ -0,0 +1,50 @@ -+#include -+#include -+#include -+#include -+ -+ -+void * -+routine (void *data) -+{ -+ int i; -+ for (i = 0; i < 6; ++i) -+ { -+ puts ("bleble"); -+ sleep (1); -+ } -+} -+ -+ -+void * -+routine2 (void *data) -+{ -+ pid_t child = vfork (); -+ if (child == 0) -+ { -+ int i, j; -+ puts ("vforked"); -+ for (i = 0; i < 100000; ++i) -+ for (j = 0; j < 10000; ++j) -+ ; -+ puts ("vforked child exiting"); -+ _exit (0); -+ } -+ puts ("parent continuing"); -+ return NULL; -+} -+ -+int -+main(int argc, char *argv[]) -+{ -+ pthread_t thread; -+ pthread_create (&thread, NULL, &routine, NULL); -+ -+ sleep (1); -+ -+ pthread_t thread2; -+ pthread_create (&thread2, NULL, &routine2, NULL); -+ pthread_join (thread2, NULL); -+ pthread_join (thread, NULL); -+ return 0; -+} -diff --git a/testsuite/ltrace.torture/vfork-thread.exp b/testsuite/ltrace.torture/vfork-thread.exp -new file mode 100644 -index 0000000..bd01319 ---- /dev/null -+++ b/testsuite/ltrace.torture/vfork-thread.exp -@@ -0,0 +1,32 @@ -+# This file was written by Yao Qi . -+ -+set testfile "vfork-thread" -+set srcfile ${testfile}.c -+set binfile ${testfile} -+ -+ -+verbose "compiling source file now....." -+# Build the shared libraries this test case needs. -+if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}.c" "${objdir}/${subdir}/${binfile}" executable [list debug ldflags=-pthread] ] != "" } { -+ send_user "Testcase compile failed, so all tests in this file will automatically fail\n." -+} -+ -+ltrace_options "-f" -+ -+# Run PUT for ltarce. -+set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile] -+ -+# Check the output of this program. -+verbose "ltrace runtest output: $exec_output\n" -+if [regexp {ELF from incompatible architecture} $exec_output] { -+ fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" -+ return -+} elseif [ regexp {Couldn't get .hash data} $exec_output ] { -+ fail "Couldn't get .hash data!" -+ return -+} -+ -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace "puts" 9 -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace "sleep" 7 -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace "vfork resumed" 2 -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace "_exit" 1 diff --git a/ltrace-0.6.0-x86_64-flatten.patch b/ltrace-0.6.0-x86_64-flatten.patch deleted file mode 100644 index e3071f5..0000000 --- a/ltrace-0.6.0-x86_64-flatten.patch +++ /dev/null @@ -1,88 +0,0 @@ -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-0.7.0-man5.patch b/ltrace-0.7.0-man5.patch new file mode 100644 index 0000000..c026367 --- /dev/null +++ b/ltrace-0.7.0-man5.patch @@ -0,0 +1,150 @@ +diff -urp ltrace-0.7.0/Makefile.am ltrace-0.7.0/Makefile.am +--- ltrace-0.7.0/Makefile.am 2012-11-10 13:43:55.000000000 +0100 ++++ ltrace-0.7.0/Makefile.am 2012-11-10 14:57:39.141288149 +0100 +@@ -116,7 +116,8 @@ noinst_HEADERS = \ + lens_default.h \ + lens_enum.h + +-dist_man1_MANS = ltrace.1 ltrace.conf.5 ++dist_man1_MANS = ltrace.1 ++dist_man5_MANS = ltrace.conf.5 + + dist_doc_DATA = COPYING CREDITS INSTALL README TODO + +diff -urp ltrace-0.7.0/Makefile.in ltrace-0.7.0/Makefile.in +--- ltrace-0.7.0/Makefile.in 2012-11-10 13:46:03.000000000 +0100 ++++ ltrace-0.7.0/Makefile.in 2012-11-10 15:00:05.854225673 +0100 +@@ -60,8 +60,8 @@ host_triplet = @host@ + bin_PROGRAMS = ltrace$(EXEEXT) + subdir = . + DIST_COMMON = README $(am__configure_deps) $(dist_doc_DATA) \ +- $(dist_man1_MANS) $(dist_sysconf_DATA) $(noinst_HEADERS) \ +- $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ ++ $(dist_man1_MANS) $(dist_man5_MANS) $(dist_sysconf_DATA) \ ++ $(noinst_HEADERS) $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/config.h.in $(top_srcdir)/configure COPYING INSTALL \ + NEWS TODO config/autoconf/config.guess \ + config/autoconf/config.sub config/autoconf/depcomp \ +@@ -96,7 +96,8 @@ am_libltrace_la_OBJECTS = breakpoints.lo + lens_default.lo lens_enum.lo + libltrace_la_OBJECTS = $(am_libltrace_la_OBJECTS) + am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" \ +- "$(DESTDIR)$(docdir)" "$(DESTDIR)$(sysconfdir)" ++ "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(docdir)" \ ++ "$(DESTDIR)$(sysconfdir)" + PROGRAMS = $(bin_PROGRAMS) + am_ltrace_OBJECTS = main.$(OBJEXT) + ltrace_OBJECTS = $(am_ltrace_OBJECTS) +@@ -145,8 +146,9 @@ am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' + man1dir = $(mandir)/man1 ++man5dir = $(mandir)/man5 + NROFF = nroff +-MANS = $(dist_man1_MANS) ++MANS = $(dist_man1_MANS) $(dist_man5_MANS) + DATA = $(dist_doc_DATA) $(dist_sysconf_DATA) + HEADERS = $(noinst_HEADERS) + RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ +@@ -410,7 +412,8 @@ noinst_HEADERS = \ + lens_default.h \ + lens_enum.h + +-dist_man1_MANS = ltrace.1 ltrace.conf.5 ++dist_man1_MANS = ltrace.1 ++dist_man5_MANS = ltrace.conf.5 + dist_doc_DATA = COPYING CREDITS INSTALL README TODO + dist_sysconf_DATA = \ + etc/ltrace.conf +@@ -658,6 +661,40 @@ uninstall-man1: + test -z "$$files" || { \ + echo " ( cd '$(DESTDIR)$(man1dir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(man1dir)" && rm -f $$files; } ++install-man5: $(dist_man5_MANS) ++ @$(NORMAL_INSTALL) ++ test -z "$(man5dir)" || $(MKDIR_P) "$(DESTDIR)$(man5dir)" ++ @list='$(dist_man5_MANS)'; test -n "$(man5dir)" || exit 0; \ ++ { for i in $$list; do echo "$$i"; done; \ ++ } | while read p; do \ ++ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ ++ echo "$$d$$p"; echo "$$p"; \ ++ done | \ ++ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ ++ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ ++ sed 'N;N;s,\n, ,g' | { \ ++ list=; while read file base inst; do \ ++ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ ++ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \ ++ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst" || exit $$?; \ ++ fi; \ ++ done; \ ++ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ ++ while read files; do \ ++ test -z "$$files" || { \ ++ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man5dir)'"; \ ++ $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \ ++ done; } ++ ++uninstall-man5: ++ @$(NORMAL_UNINSTALL) ++ @list='$(dist_man5_MANS)'; test -n "$(man5dir)" || exit 0; \ ++ files=`{ for i in $$list; do echo "$$i"; done; \ ++ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ ++ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ ++ test -z "$$files" || { \ ++ echo " ( cd '$(DESTDIR)$(man5dir)' && rm -f" $$files ")"; \ ++ cd "$(DESTDIR)$(man5dir)" && rm -f $$files; } + install-dist_docDATA: $(dist_doc_DATA) + @$(NORMAL_INSTALL) + test -z "$(docdir)" || $(MKDIR_P) "$(DESTDIR)$(docdir)" +@@ -1028,7 +1065,7 @@ all-am: Makefile $(LTLIBRARIES) $(PROGRA + config.h + installdirs: installdirs-recursive + installdirs-am: +- for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(sysconfdir)"; do \ ++ for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(sysconfdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done + install: install-recursive +@@ -1097,7 +1134,7 @@ install-info: install-info-recursive + + install-info-am: + +-install-man: install-man1 ++install-man: install-man1 install-man5 + + install-pdf: install-pdf-recursive + +@@ -1133,7 +1170,7 @@ ps-am: + uninstall-am: uninstall-binPROGRAMS uninstall-dist_docDATA \ + uninstall-dist_sysconfDATA uninstall-man + +-uninstall-man: uninstall-man1 ++uninstall-man: uninstall-man1 uninstall-man5 + + .MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) all \ + ctags-recursive install-am install-strip tags-recursive +@@ -1150,14 +1187,16 @@ uninstall-man: uninstall-man1 + install-data-am install-dist_docDATA install-dist_sysconfDATA \ + install-dvi install-dvi-am install-exec install-exec-am \ + install-html install-html-am install-info install-info-am \ +- install-man install-man1 install-pdf install-pdf-am install-ps \ +- install-ps-am install-strip installcheck installcheck-am \ +- installdirs installdirs-am maintainer-clean \ +- maintainer-clean-generic maintainer-clean-local mostlyclean \ +- mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ +- pdf pdf-am ps ps-am tags tags-recursive uninstall uninstall-am \ ++ install-man install-man1 install-man5 install-pdf \ ++ install-pdf-am install-ps install-ps-am install-strip \ ++ installcheck installcheck-am installdirs installdirs-am \ ++ maintainer-clean maintainer-clean-generic \ ++ maintainer-clean-local mostlyclean mostlyclean-compile \ ++ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ ++ tags tags-recursive uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-dist_docDATA \ +- uninstall-dist_sysconfDATA uninstall-man uninstall-man1 ++ uninstall-dist_sysconfDATA uninstall-man uninstall-man1 \ ++ uninstall-man5 + + + maintainer-clean-local: diff --git a/ltrace-0.7.0-ppc-insn.h.patch b/ltrace-0.7.0-ppc-insn.h.patch new file mode 100644 index 0000000..0fc6f0c --- /dev/null +++ b/ltrace-0.7.0-ppc-insn.h.patch @@ -0,0 +1,70 @@ +diff -urp ltrace-0.7.0/sysdeps/linux-gnu/ppc/Makefile.am ltrace-0.7.0/sysdeps/linux-gnu/ppc/Makefile.am +--- ltrace-0.7.0/sysdeps/linux-gnu/ppc/Makefile.am 2012-11-09 23:45:23.192038441 +0100 ++++ ltrace-0.7.0/sysdeps/linux-gnu/ppc/Makefile.am 2012-11-10 13:43:55.000000000 +0100 +@@ -27,6 +27,7 @@ ___libcpu_la_SOURCES = \ + + noinst_HEADERS = \ + arch.h \ ++ insn.h \ + ptrace.h \ + signalent.h \ + syscallent.h +diff -urp ltrace-0.7.0/sysdeps/linux-gnu/ppc/Makefile.in ltrace-0.7.0/sysdeps/linux-gnu/ppc/Makefile.in +--- ltrace-0.7.0/sysdeps/linux-gnu/ppc/Makefile.in 2012-11-10 14:41:22.453856321 +0100 ++++ ltrace-0.7.0/sysdeps/linux-gnu/ppc/Makefile.in 2012-11-10 13:46:03.000000000 +0100 +@@ -232,6 +232,7 @@ ___libcpu_la_SOURCES = \ + + noinst_HEADERS = \ + arch.h \ ++ insn.h \ + ptrace.h \ + signalent.h \ + syscallent.h +--- ltrace-0.7.0/sysdeps/linux-gnu/ppc/insn.h 1970-01-01 01:00:00.000000000 +0100 ++++ ltrace-0.7.0/sysdeps/linux-gnu/ppc/insn.h 2012-11-07 17:09:31.784520698 +0100 +@@ -0,0 +1,45 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 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 ++ */ ++ ++/* Instruction masks used during single-stepping of atomic ++ * sequences. This was lifted from GDB. */ ++#define LWARX_MASK 0xfc0007fe ++#define LWARX_INSTRUCTION 0x7c000028 ++#define LDARX_INSTRUCTION 0x7c0000A8 ++#define STWCX_MASK 0xfc0007ff ++#define STWCX_INSTRUCTION 0x7c00012d ++#define STDCX_INSTRUCTION 0x7c0001ad ++#define BRANCH_MASK 0xfc000000 ++#define BC_MASK 0xfc000000 ++#define BC_INSN 0x40000000 ++#define B_INSN 0x48000000 ++ ++static inline arch_addr_t ++ppc_branch_dest(arch_addr_t addr, uint32_t insn) ++{ ++ int immediate = ((insn & 0xfffc) ^ 0x8000) - 0x8000; ++ int absolute = insn & 2; ++ ++ /* XXX drop the following double casts. */ ++ if (absolute) ++ return (arch_addr_t)(uintptr_t)immediate; ++ else ++ return addr + (uintptr_t)immediate; ++} diff --git a/ltrace.spec b/ltrace.spec index 8a7e433..df58545 100644 --- a/ltrace.spec +++ b/ltrace.spec @@ -1,47 +1,20 @@ Summary: Tracks runtime library calls from dynamically linked executables Name: ltrace -Version: 0.6.0 -Release: 19%{?dist} +Version: 0.7.0 +Release: 1%{?dist} URL: http://ltrace.alioth.debian.org/ License: GPLv2+ Group: Development/Debuggers BuildRequires: elfutils-libelf-devel dejagnu -BuildRequires: autoconf automake libtool BuildRequires: libselinux-devel -# Tarball generated from git checkout. To regenerate: -# git clone http://github.com/ice799/ltrace.git -# cd ltrace && ./autogen.sh && ./configure && make dist -Source: %{name}-%{version}.tar.bz2 +Source: http://alioth.debian.org/frs/download.php/3822/%{name}-%{version}.tar.bz2 -Patch1: ltrace-0.5-ia64-sigill.patch -Patch2: ltrace-0.6.0-exec-stripped.patch -Patch4: ltrace-0.5-etc-memmove.patch -Patch5: ltrace-0.6.0-return-string-n.patch -Patch6: ltrace-0.6.0-threads.patch -Patch7: ltrace-0.6.0-endian.patch -Patch8: ltrace-0.6.0-clone-test.patch -Patch9: ltrace-0.6.0-ppc-args.patch -Patch11: ltrace-0.6.0-vfork.patch -Patch12: ltrace-0.6.0-thread-races.patch -Patch13: ltrace-0.6.0-process-start.patch -Patch14: ltrace-0.6.0-selinux.patch -Patch15: ltrace-0.6.0-detach-sleeping.patch -Patch16: ltrace-0.6.0-tail-return.patch -Patch17: ltrace-0.6.0-ppc-lwarx.patch -Patch18: ltrace-0.6.0-libs.patch -Patch19: ltrace-0.6.0-libs-fixes-1.patch -Patch20: ltrace-0.6.0-dash-n.patch -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 +# Upstream ccbdb91f +Patch0: ltrace-0.7.0-ppc-insn.h.patch +# Upstream 808d64b9 +Patch1: ltrace-0.7.0-man5.patch %description Ltrace is a debugging program which runs a specified command until the @@ -55,39 +28,13 @@ execution of processes. %prep %setup -q +%patch0 -p1 %patch1 -p1 -%patch2 -p1 -%patch4 -p1 -%patch5 -p1 -%patch6 -p1 -%patch7 -p1 -%patch8 -p1 -%patch9 -p1 -%patch11 -p1 -%patch12 -p1 -%patch13 -p1 -%patch14 -p1 -%patch15 -p1 -%patch16 -p1 -%patch17 -p1 -%patch18 -p1 -%patch19 -p1 -%patch20 -p1 -%patch21 -p1 -%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 # architecture. It makes a difference on ppc. export CC="gcc`echo $RPM_OPT_FLAGS | sed -n 's/^.*\(-m[36][124]\).*$/ \1/p'` -D_LARGEFILE64_SOURCE" -autoreconf -i %configure CC="$CC" make %{?_smp_mflags} @@ -103,12 +50,21 @@ echo ====================TESTING END===================== %files %defattr(-,root,root) -%doc COPYING README TODO BUGS ChangeLog +%doc COPYING README NEWS %{_bindir}/ltrace %{_mandir}/man1/ltrace.1* +%{_mandir}/man5/ltrace.conf.5* %config(noreplace) %{_sysconfdir}/ltrace.conf %changelog +* Sat Nov 10 2012 Petr Machata - 0.7.0-1 +- Upstream 0.7.0 + - Drop all the patches + - Upstream patch for missing sysdeps/linux-gnu/ppc/insn.h + (ltrace-0.7.0-ppc-insn.h.patch) + - Upstream patch for installing ltrace.conf.5 to man5 + (ltrace-0.7.0-man5.patch) + * Mon Oct 1 2012 Petr Machata - 0.6.0-19 - Upstream patch for ia64 parameter passing (ltrace-0.6.0-abi-ia64.patch) diff --git a/sources b/sources index 2914d2c..7677f11 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -eca4ffd76293b2cae871b890b1cd4ddf ltrace-0.6.0.tar.bz2 +68ad1cdf594fc6819e55519f36092916 ltrace-0.7.0.tar.bz2