[ltrace] Bring in code from upstream revamp branch
Petr Machata
pmachata at fedoraproject.org
Mon May 21 15:48:41 UTC 2012
commit 811ce88201b0f4282eac122fb721d3cf4862faa8
Author: Petr Machata <pmachata at redhat.com>
Date: Fri May 18 22:19:12 2012 +0200
Bring in code from upstream revamp branch
Also, run autoreconf, because the changes vs. tarball became impractically
large to patch.
ltrace-0.6.0-abi.patch |13571 ++++++++++++++++++++++++++++++++++++++++++++++++
ltrace.spec | 11 +-
2 files changed, 13579 insertions(+), 3 deletions(-)
---
diff --git a/ltrace-0.6.0-abi.patch b/ltrace-0.6.0-abi.patch
new file mode 100644
index 0000000..ee9e813
--- /dev/null
+++ b/ltrace-0.6.0-abi.patch
@@ -0,0 +1,13571 @@
+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 <gelf.h>
++
++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 <stdlib.h>
+-#include <string.h>
+ #include <assert.h>
+ #include <errno.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
+
+ #ifdef __powerpc__
+ #include <sys/ptrace.h>
+ #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 <config.h>
+
+ #include <sys/types.h>
+@@ -15,6 +37,7 @@
+ #include "ltrace-elf.h"
+ #include "read_config_file.h"
+ #include "proc.h"
++#include "forward.h"
+
+ #if defined HAVE_LIBIBERTY || defined HAVE_LIBSUPC__
+ # define USE_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 <ctype.h>
+-#include <stdio.h>
+-#include <stdlib.h>
+-#include <string.h>
+-#include <limits.h>
+-
+-#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, "<array without address>");
+- case ARGTYPE_ENUM:
+- return display_enum(type, proc, info, value);
+- case ARGTYPE_STRUCT:
+- return fprintf(options.output, "<struct without address>");
+- 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 <pwd.h>
+ #include <grp.h>
+
+-#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 <string.h>
++#include <assert.h>
++#include <errno.h>
++#include <error.h>
++#include <stdlib.h>
++
++#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 <stdlib.h>
++#include <string.h>
++
++#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 <assert.h>
+ #include <errno.h>
+ #include <signal.h>
+@@ -9,10 +34,13 @@
+ #include <string.h>
+ #include <sys/time.h>
+
++#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 <assert.h>
++
++#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 <stdio.h>
++#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 <ctype.h>
++#include <stdlib.h>
++#include <assert.h>
++#include <inttypes.h>
++#include <stdarg.h>
++#include <stdio.h>
++
++#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, "<void>");
++
++ 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 <stdlib.h>
++#include <assert.h>
++#include <string.h>
++
++#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 <sys/param.h>
+@@ -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 <assert.h>
+@@ -13,10 +40,13 @@
+ #include <string.h>
+ #include <unistd.h>
+
+-#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 <stdio.h>
+@@ -7,10 +31,18 @@
+ #include <time.h>
+ #include <sys/time.h>
+ #include <unistd.h>
++#include <errno.h>
++#include <assert.h>
+
+ #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, "<void>");
+- } 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 <assert.h>
++#include <stdlib.h>
++
++#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 <assert.h>
++#include <stdlib.h>
++
++#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 <stddef.h>
++#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 <sys/types.h>
++#include <assert.h>
++#include <errno.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++
+ #if defined(HAVE_LIBUNWIND)
+ #include <libunwind.h>
+ #include <libunwind-ptrace.h>
+ #endif /* defined(HAVE_LIBUNWIND) */
+
+-#include <sys/types.h>
+-#include <string.h>
+-#include <stdio.h>
+-#include <errno.h>
+-#include <stdlib.h>
+-#include <assert.h>
+-
+-#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 <libunwind.h>
+ #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 <string.h>
+ #include <stdlib.h>
+ #include <ctype.h>
++#include <errno.h>
++#include <error.h>
++#include <assert.h>
+
+ #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<type> (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 <sys/ptrace.h>
+@@ -6,6 +28,8 @@
+ #include <stdio.h>
+
+ #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 <stdlib.h>
++#include <sys/ptrace.h>
+ #include <sys/types.h>
+ #include <sys/wait.h>
++#include <assert.h>
+ #include <errno.h>
+ #include <signal.h>
++#include <stdio.h>
++#include <stdlib.h>
+ #include <string.h>
+-#include <sys/ptrace.h>
+-#include <assert.h>
+ #include <unistd.h>
+
+-#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 <gelf.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)
+-{
+- 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 <sys/ptrace.h>
+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 <sys/types.h>
+-#include <sys/ptrace.h>
+-#include <asm/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_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 <sys/ptrace.h>
+-#include <sys/types.h>
+-#include <sys/wait.h>
+-#include <asm/ptrace.h>
+-#include <errno.h>
+-#include <signal.h>
+-#include <stdlib.h>
+-
+-#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 <stdlib.h>
+@@ -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 <assert.h>
++#include <ptrace.h>
++#include <stdint.h>
++#include <stdlib.h>
++#include <string.h>
++#include <sys/ucontext.h>
++
++#include <stdio.h>
++
++#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 <ucontext.h> 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 <sys/ptrace.h>
+-#include <sys/ucontext.h>
+-
+-#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 <sys/types.h>
+-#include <sys/wait.h>
+-#include <signal.h>
+-#include <sys/ptrace.h>
+-#include <asm/ptrace.h>
++#include <assert.h>
+ #include <elf.h>
+ #include <errno.h>
++#include <signal.h>
+ #include <string.h>
+
+-#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 <link.h>
+ #include <signal.h>
+ #include <stdio.h>
++#include <stdlib.h>
+ #include <string.h>
+ #include <unistd.h>
+
+-#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 <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+@@ -14,11 +37,15 @@
+ #include <asm/unistd.h>
+ #include <assert.h>
+
+-#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 <sys/types.h>
++#include <assert.h>
++#include <gelf.h>
++#include <stddef.h>
++#include <stdint.h>
++#include <stdlib.h>
++#include <string.h>
++
++#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 <gelf.h>
++#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 <sys/ptrace.h>
++#include <sys/user.h>
+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 <sys/types.h>
++#include <sys/ptrace.h>
++#include <sys/reg.h>
++#include <errno.h>
++#include <stdio.h>
++#include <string.h>
++
++#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 <sys/reg.h>
++#include <sys/wait.h>
++#include <assert.h>
++#include <errno.h>
++#include <stdlib.h>
++
++#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 <gelf.h>
+-#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 <stdint.h>
+-#include <sys/ptrace.h>
+-#include <sys/user.h>
+-
+-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 <sys/types.h>
+-#include <sys/ptrace.h>
+-#include <sys/reg.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_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 <sys/types.h>
+-#include <sys/wait.h>
+-#include <signal.h>
+-#include <stdlib.h>
+-#include <sys/ptrace.h>
+-#include <sys/reg.h>
+-#include <string.h>
+-#include <assert.h>
+-#include <errno.h>
+-
+-#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( <unfinished ...>"
+ 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 <assert.h>
++#include <stdlib.h>
++#include <limits.h>
++
++#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 <stddef.h>
++#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 <string.h>
++#include <assert.h>
++#include <stdlib.h>
++
++#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 <assert.h>
++#include <string.h>
++#include <stdlib.h>
++
++#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 <stdlib.h>
++#include <assert.h>
++#include <string.h>
++#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 <stddef.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
++ * 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 <error.h>
++#include <errno.h>
++
++#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.spec b/ltrace.spec
index 67fd381..ac08012 100644
--- a/ltrace.spec
+++ b/ltrace.spec
@@ -1,7 +1,7 @@
Summary: Tracks runtime library calls from dynamically linked executables
Name: ltrace
Version: 0.6.0
-Release: 13%{?dist}
+Release: 14%{?dist}
URL: http://ltrace.alioth.debian.org/
License: GPLv2+
Group: Development/Debuggers
@@ -33,6 +33,7 @@ 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-abi.patch
%description
Ltrace is a debugging program which runs a specified command until the
@@ -64,13 +65,13 @@ execution of processes.
%patch18 -p1
%patch19 -p1
%patch20 -p1
-sed -i -e 's/-o root -g root//' Makefile.in
+%patch21 -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"
-# We touch configure.ac up there
+autoreconf -i
%configure CC="$CC"
make %{?_smp_mflags}
@@ -92,6 +93,10 @@ echo ====================TESTING END=====================
%config(noreplace) %{_sysconfdir}/ltrace.conf
%changelog
+* Fri May 18 2012 Petr Machata <pmachata at redhat.com> - 0.6.0-14
+- Add upstream patch that improves parameter passing support (the
+ upstream "revamp" branch) (ltrace-0.6.0-abi.patch)
+
* Thu May 3 2012 Petr Machata <pmachata at redhat.com> - 0.6.0-13
- Check -n argument for validity (ltrace-0.6.0-dash-n.patch)
- Resolves: #818529
More information about the scm-commits
mailing list