Blob Blame History Raw
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, &params_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(&param->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(&param->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,
+			&param_printf_init, &param_printf_next,
+			&param_printf_stop, &param_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 */