[ltrace] Bring in code from upstream revamp branch

Petr Machata pmachata at fedoraproject.org
Mon May 21 15:48:41 UTC 2012


commit 811ce88201b0f4282eac122fb721d3cf4862faa8
Author: Petr Machata <pmachata at redhat.com>
Date:   Fri May 18 22:19:12 2012 +0200

    Bring in code from upstream revamp branch
    
    Also, run autoreconf, because the changes vs. tarball became impractically
    large to patch.

 ltrace-0.6.0-abi.patch |13571 ++++++++++++++++++++++++++++++++++++++++++++++++
 ltrace.spec            |   11 +-
 2 files changed, 13579 insertions(+), 3 deletions(-)
---
diff --git a/ltrace-0.6.0-abi.patch b/ltrace-0.6.0-abi.patch
new file mode 100644
index 0000000..ee9e813
--- /dev/null
+++ b/ltrace-0.6.0-abi.patch
@@ -0,0 +1,13571 @@
+diff --git a/Makefile.am b/Makefile.am
+index a00c8bf..dc9091d 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -15,7 +15,6 @@ libltrace_la_SOURCES = \
+ 	debug.c \
+ 	demangle.c \
+ 	dict.c \
+-	display_args.c \
+ 	ltrace-elf.c \
+ 	execute_program.c \
+ 	handle_event.c \
+@@ -27,7 +26,19 @@ libltrace_la_SOURCES = \
+ 	summary.c \
+ 	library.c \
+ 	filter.c \
+-	glob.c
++	glob.c \
++	type.c \
++	value.c \
++	value_dict.c \
++	expr.c \
++	fetch.c \
++	vect.c \
++	param.c \
++	printf.c \
++	zero.c \
++	lens.c \
++	lens_default.c \
++	lens_enum.c
+ 
+ libltrace_la_LIBADD = \
+ 	$(libelf_LIBS) \
+@@ -50,6 +61,7 @@ ltrace_LDADD = \
+ 
+ 
+ noinst_HEADERS = \
++	backend.h \
+ 	common.h \
+ 	debug.h \
+ 	defs.h \
+@@ -62,7 +74,20 @@ noinst_HEADERS = \
+ 	read_config_file.h \
+ 	library.h \
+ 	filter.h \
+-	glob.h
++	glob.h \
++	vect.h \
++	type.h \
++	value.h \
++	value_dict.h \
++	expr.h \
++	fetch.h \
++	vect.h \
++	param.h \
++	printf.h \
++	zero.h \
++	lens.h \
++	lens_default.h \
++	lens_enum.h
+ 
+ dist_man1_MANS = \
+ 	ltrace.1
+diff --git a/backend.h b/backend.h
+new file mode 100644
+index 0000000..08306e1
+--- /dev/null
++++ b/backend.h
+@@ -0,0 +1,250 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2012 Petr Machata, Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ */
++
++#ifndef BACKEND_H
++#define BACKEND_H
++
++#include "forward.h"
++#include <gelf.h>
++
++enum process_status {
++	ps_invalid,	/* Failure.  */
++	ps_stop,	/* Job-control stop.  */
++	ps_tracing_stop,
++	ps_sleeping,
++	ps_zombie,
++	ps_other,	/* Necessary other states can be added as needed.  */
++};
++
++typedef void *target_address_t;
++
++/*
++ * This file contains documentation of back end interface.  Some of
++ * these may be implemented on an OS level (i.e. they are the same
++ * e.g. on all Linux architectures), some may differ per architecture
++ * on the same OS (e.g. a way to insert a breakpoint into the process
++ * image is a likely candidate).
++ */
++
++/* Convert a PID to a path to the corresponding binary.  */
++char *pid2name(pid_t pid);
++
++/* Given a PID, find a leader of thread group.  */
++pid_t process_leader(pid_t pid);
++
++/* Given a PID of leader thread, fill in PIDs of all the tasks.  The
++ * function will initialize the pointer *RET_TASKS to a
++ * newly-allocated array, and will store number of elements in that
++ * array to *RET_N.  You have to free that buffer when you don't need
++ * it anymore.  */
++int process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n);
++
++/* Answer whether the process PID is stopped.  Returns 0 when not
++ * stopped, 1 when stopped, or -1 when there was an error.  */
++int process_stopped(pid_t pid);
++
++/* Answer a status of the task PID.  See enum process_status.  */
++enum process_status process_status(pid_t pid);
++
++/* Wait for PID to be ready for tracing.  */
++int wait_for_proc(pid_t pid);
++
++/* Send a signal SIG to the task PID.  */
++int task_kill(pid_t pid, int sig);
++
++/* Called after PID is attached, but before it is continued.  */
++void trace_set_options(struct Process *proc);
++
++/* Called after ltrace forks.  Should attach the newly created child,
++ * in whose context this function is called.  */
++void trace_me(void);
++
++/* Called when ltrace needs to attach to PID, such as when it attaches
++ * to a running process, whose PID is given on the command line.  */
++int trace_pid(pid_t pid);
++
++/* Stop tracing PID.  */
++void untrace_pid(pid_t pid);
++
++/* The back end may need to store arbitrary data to a process.  This
++ * is a place where it can initialize PROC->arch_dep.  XXX this should
++ * be dropped in favor of arhc_process_init on pmachata/libs.  */
++void get_arch_dep(struct Process *proc);
++
++/* Return current instruction pointer of PROC.
++ *
++ * XXX note that the IP must fit into an arch pointer.  This prevents
++ * us to use 32-bit ltrace to trace 64-bit process, even on arches
++ * that would otherwise support this.  Above we have a definition of
++ * target_address_t.  This should be converted to an integral type and
++ * used for target addresses throughout.  */
++void *get_instruction_pointer(struct Process *proc);
++
++/* Set instruction pointer of PROC to ADDR.  XXX see above.  */
++void set_instruction_pointer(struct Process *proc, void *addr);
++
++/* Return current stack pointer of PROC.  XXX see above.  */
++void *get_stack_pointer(struct Process *proc);
++
++/* Find and return caller address, i.e. the address where the current
++ * function returns.  */
++void *get_return_addr(struct Process *proc, void *stack_pointer);
++
++/* Adjust PROC so that when the current function returns, it returns
++ * to ADDR.  */
++void set_return_addr(struct Process *proc, void *addr);
++
++/* Enable breakpoint SBP in process PROC.  */
++void enable_breakpoint(struct Process *proc, struct breakpoint *sbp);
++
++/* Disable breakpoint SBP in process PROC.  */
++void disable_breakpoint(struct Process *proc, struct breakpoint *sbp);
++
++/* Determine whether the event that we have just seen (and that is
++ * recorded in STATUS) was a syscall.  If it was, return 1.  If it was
++ * a return from syscall, return 2.  In both cases, set *SYSNUM to the
++ * number of said syscall.  If it wasn't a syscall, return 0.  If
++ * there was an error, return -1.  */
++int syscall_p(struct Process *proc, int status, int *sysnum);
++
++/* Continue execution of the process with given PID.  */
++void continue_process(pid_t pid);
++
++/* Called after we received a signal SIGNUM.  Should do whatever
++ * book-keeping is necessary and continue the process if
++ * necessary.  */
++void continue_after_signal(pid_t pid, int signum);
++
++/* Called after we received a system call SYSNUM.  RET_P is 0 if this
++ * is system call, otherwise it's return from a system call.  The
++ * callback should do whatever book-keeping is necessary and continue
++ * the process if necessary.  */
++void continue_after_syscall(struct Process *proc, int sysnum, int ret_p);
++
++/* Called after we hit a breakpoint SBP.  Should do whatever
++ * book-keeping is necessary and then continue the process.  */
++void continue_after_breakpoint(struct Process *proc, struct breakpoint *sbp);
++
++/* Called after we received a vfork.  Should do whatever book-keeping
++ * is necessary and continue the process if necessary.  N.B. right
++ * now, with Linux/GNU the only back end, this is not necessary.  I
++ * imagine other systems may be different.  */
++void continue_after_vfork(struct Process *proc);
++
++/* Called when trace_me or primary trace_pid fail.  This may plug in
++ * any platform-specific knowledge of why it could be so.  */
++void trace_fail_warning(pid_t pid);
++
++/* A pair of functions called to initiate a detachment request when
++ * ltrace is about to exit.  Their job is to undo any effects that
++ * tracing had and eventually detach process, perhaps by way of
++ * installing a process handler.
++ *
++ * OS_LTRACE_EXITING_SIGHANDLER is called from a signal handler
++ * context right after the signal was captured.  It returns 1 if the
++ * request was handled or 0 if it wasn't.
++ *
++ * If the call to OS_LTRACE_EXITING_SIGHANDLER didn't handle the
++ * request, OS_LTRACE_EXITING is called when the next event is
++ * generated.  Therefore it's called in "safe" context, without
++ * re-entrancy concerns, but it's only called after an even is
++ * generated.  */
++int os_ltrace_exiting_sighandler(void);
++void os_ltrace_exiting(void);
++
++/* Should copy COUNT bytes from address ADDR of process PROC to local
++ * buffer BUF.  */
++size_t umovebytes (struct Process *proc, void *addr, void *buf, size_t count);
++
++/* Find out an address of symbol SYM in process PROC, and return.
++ * Returning NULL delays breakpoint insertion and enables heaps of
++ * arch-specific black magic that we should clean up some day.
++ *
++ * XXX the same points as for get_instruction_pointer apply. */
++void *sym2addr(struct Process *proc, struct library_symbol *sym);
++
++/* Called at some point after we have attached to PROC.  This callback
++ * should insert an introspection breakpoint for handling dynamic
++ * linker library loads.  */
++int linkmap_init(struct Process *proc, target_address_t dyn_addr);
++
++/* Called for breakpoints defined over an artificial symbol "".  This
++ * can be used (like it is on Linux/GNU) to add more breakpoints
++ * because a dlopen'ed library was mapped in.
++ *
++ * XXX we should somehow clean up this interface.  For starters,
++ * breakpoints should have their own handler callbacks, so that we can
++ * generalize this to e.g. systemtap SDT probes.  linkmap_init could
++ * perhaps be rolled into some other process init callback.  */
++void arch_check_dbg(struct Process *proc);
++
++/* This should produce and return the next event of one of the traced
++ * processes.  The returned pointer will not be freed by the core and
++ * should be either statically allocated, or the management should be
++ * done some other way.  */
++struct Event *next_event(void);
++
++/* Called when process PROC was removed.  */
++void process_removed(struct Process *proc);
++
++int arch_elf_init(struct ltelf *lte, struct library *lib);
++void arch_elf_destroy(struct ltelf *lte);
++
++enum plt_status {
++	plt_fail,
++	plt_ok,
++	plt_default,
++};
++
++enum plt_status arch_elf_add_plt_entry(struct Process *p, struct ltelf *l,
++				       const char *n, GElf_Rela *r, size_t i,
++				       struct library_symbol **ret);
++
++int arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp);
++void arch_breakpoint_destroy(struct breakpoint *sbp);
++int arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp);
++
++void arch_library_init(struct library *lib);
++void arch_library_destroy(struct library *lib);
++void arch_library_clone(struct library *retp, struct library *lib);
++
++int arch_library_symbol_init(struct library_symbol *libsym);
++void arch_library_symbol_destroy(struct library_symbol *libsym);
++int arch_library_symbol_clone(struct library_symbol *retp,
++			      struct library_symbol *libsym);
++
++int arch_process_init(struct Process *proc);
++void arch_process_destroy(struct Process *proc);
++int arch_process_clone(struct Process *retp, struct Process *proc);
++int arch_process_exec(struct Process *proc);
++
++/* This should extract entry point address and interpreter (dynamic
++ * linker) bias if possible.  Returns 0 if there were no errors, -1
++ * otherwise.  Sets *ENTRYP and *INTERP_BIASP to non-zero values if
++ * the corresponding value is known.  Unknown values are set to 0.  */
++int process_get_entry(struct Process *proc,
++		      target_address_t *entryp,
++		      target_address_t *interp_biasp);
++
++/* This is called after the dynamic linker is done with the
++ * process startup.  */
++void arch_dynlink_done(struct Process *proc);
++
++#endif /* BACKEND_H */
+diff --git a/breakpoints.c b/breakpoints.c
+index 93ae244..8dc09df 100644
+--- a/breakpoints.c
++++ b/breakpoints.c
+@@ -1,18 +1,21 @@
+ #include "config.h"
+ 
+-#include <stdlib.h>
+-#include <string.h>
+ #include <assert.h>
+ #include <errno.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
+ 
+ #ifdef __powerpc__
+ #include <sys/ptrace.h>
+ #endif
+ 
++#include "backend.h"
+ #include "breakpoint.h"
+-#include "common.h"
+-#include "proc.h"
++#include "debug.h"
+ #include "library.h"
++#include "ltrace-elf.h"
++#include "proc.h"
+ 
+ #ifndef ARCH_HAVE_TRANSLATE_ADDRESS
+ int
+diff --git a/common.h b/common.h
+index 7fffa76..959715a 100644
+--- a/common.h
++++ b/common.h
+@@ -1,3 +1,27 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
++ * Copyright (C) 2010 Joe Damato
++ * Copyright (C) 2009 Juan Cespedes
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ */
++
++#ifndef COMMON_H
++#define COMMON_H
+ #include <config.h>
+ 
+ #include <sys/types.h>
+@@ -15,6 +37,7 @@
+ #include "ltrace-elf.h"
+ #include "read_config_file.h"
+ #include "proc.h"
++#include "forward.h"
+ 
+ #if defined HAVE_LIBIBERTY || defined HAVE_LIBSUPC__
+ # define USE_DEMANGLE
+@@ -24,93 +47,13 @@ extern char * command;
+ 
+ extern int exiting;  /* =1 if we have to exit ASAP */
+ 
+-enum arg_type {
+-	ARGTYPE_UNKNOWN = -1,
+-	ARGTYPE_VOID,
+-	ARGTYPE_INT,
+-	ARGTYPE_UINT,
+-	ARGTYPE_LONG,
+-	ARGTYPE_ULONG,
+-	ARGTYPE_OCTAL,
+-	ARGTYPE_CHAR,
+-	ARGTYPE_SHORT,
+-	ARGTYPE_USHORT,
+-	ARGTYPE_FLOAT,		/* float value, may require index */
+-	ARGTYPE_DOUBLE,		/* double value, may require index */
+-	ARGTYPE_ADDR,
+-	ARGTYPE_FILE,
+-	ARGTYPE_FORMAT,		/* printf-like format */
+-	ARGTYPE_STRING,		/* NUL-terminated string */
+-	ARGTYPE_STRING_N,	/* String of known maxlen */
+-	ARGTYPE_ARRAY,		/* Series of values in memory */
+-	ARGTYPE_ENUM,		/* Enumeration */
+-	ARGTYPE_STRUCT,		/* Structure of values */
+-	ARGTYPE_POINTER,	/* Pointer to some other type */
+-	ARGTYPE_COUNT		/* number of ARGTYPE_* values */
+-};
+-
+-typedef struct arg_type_info_t {
+-	enum arg_type type;
+-	union {
+-		/* ARGTYPE_ENUM */
+-		struct {
+-			size_t entries;
+-			char ** keys;
+-			int * values;
+-		} enum_info;
+-
+-		/* ARGTYPE_ARRAY */
+-		struct {
+-			struct arg_type_info_t * elt_type;
+-			size_t elt_size;
+-			int len_spec;
+-		} array_info;
+-
+-		/* ARGTYPE_STRING_N */
+-		struct {
+-			int size_spec;
+-		} string_n_info;
+-
+-		/* ARGTYPE_STRUCT */
+-		struct {
+-			struct arg_type_info_t ** fields;	/* NULL-terminated */
+-			size_t * offset;
+-			size_t size;
+-		} struct_info;
+-
+-		/* ARGTYPE_POINTER */
+-		struct {
+-			struct arg_type_info_t * info;
+-		} ptr_info;
+-
+-		/* ARGTYPE_FLOAT */
+-		struct {
+-			size_t float_index;
+-		} float_info;
+-
+-		/* ARGTYPE_DOUBLE */
+-		struct {
+-			size_t float_index;
+-		} double_info;
+-	} u;
+-} arg_type_info;
+-
+-enum tof {
+-	LT_TOF_NONE = 0,
+-	LT_TOF_FUNCTION,	/* A real library function */
+-	LT_TOF_FUNCTIONR,	/* Return from a real library function */
+-	LT_TOF_SYSCALL,		/* A syscall */
+-	LT_TOF_SYSCALLR,	/* Return from a syscall */
+-	LT_TOF_STRUCT		/* Not a function; read args from struct */
+-};
+-
+ typedef struct Function Function;
+ struct Function {
+ 	const char * name;
+-	arg_type_info * return_info;
+-	int num_params;
+-	arg_type_info * arg_info[MAX_ARGS];
+-	int params_right;
++	struct param *params;
++	struct arg_type_info *return_info;
++	int own_return_info;
++	size_t num_params;
+ 	Function * next;
+ };
+ 
+@@ -130,136 +72,22 @@ struct opt_c_struct {
+ 
+ extern Dict * dict_opt_c;
+ 
+-enum process_status {
+-	ps_invalid,	/* Failure.  */
+-	ps_stop,	/* Job-control stop.  */
+-	ps_tracing_stop,
+-	ps_sleeping,
+-	ps_zombie,
+-	ps_other,	/* Necessary other states can be added as needed.  */
+-};
+-
+ /* Events  */
+-enum ecb_status {
+-	ecb_cont, /* The iteration should continue.  */
+-	ecb_yield, /* The iteration should stop, yielding this
+-		    * event.  */
+-	ecb_deque, /* Like ecb_stop, but the event should be removed
+-		    * from the queue.  */
+-};
+ extern Event * next_event(void);
+-extern Event * each_qd_event(enum ecb_status (* cb)(Event * event, void * data),
+-			     void * data);
+-extern void enque_event(Event * event);
+ extern void handle_event(Event * event);
+ 
+ extern pid_t execute_program(const char * command, char ** argv);
+-extern int display_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info);
+-extern void disable_all_breakpoints(Process * proc);
+ 
+ extern void show_summary(void);
+-extern arg_type_info * lookup_prototype(enum arg_type at);
+ 
+ struct breakpoint;
+ struct library_symbol;
+ 
+-/* Arch-dependent stuff: */
+-extern char * pid2name(pid_t pid);
+-extern pid_t process_leader(pid_t pid);
+-extern int process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n);
+-extern int process_stopped(pid_t pid);
+-extern enum process_status process_status(pid_t pid);
+-extern void trace_set_options(struct Process *proc);
+-extern int wait_for_proc(pid_t pid);
+-extern void trace_me(void);
+-extern int trace_pid(pid_t pid);
+-extern void untrace_pid(pid_t pid);
+-extern void get_arch_dep(Process * proc);
+-extern void * get_instruction_pointer(Process * proc);
+-extern void set_instruction_pointer(Process * proc, void * addr);
+-extern void * get_stack_pointer(Process * proc);
+-extern void * get_return_addr(Process * proc, void * stack_pointer);
+-extern void set_return_addr(Process * proc, void * addr);
+-extern void enable_breakpoint(struct Process *proc, struct breakpoint *sbp);
+-extern void disable_breakpoint(struct Process *proc, struct breakpoint *sbp);
+-extern int syscall_p(Process * proc, int status, int * sysnum);
+-extern void continue_process(pid_t pid);
+-extern void continue_after_signal(pid_t pid, int signum);
+-extern void continue_after_syscall(Process *proc, int sysnum, int ret_p);
+-extern void continue_after_breakpoint(struct Process *proc, struct breakpoint *sbp);
+-extern void continue_after_vfork(Process * proc);
+-extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info);
+-extern void save_register_args(enum tof type, Process * proc);
+-extern int umovestr(Process * proc, void * addr, int len, void * laddr);
+-extern int umovelong (Process * proc, void * addr, long * result, arg_type_info * info);
+-extern size_t umovebytes (Process *proc, void * addr, void * laddr, size_t count);
+-extern int ffcheck(void * maddr);
+-extern void * sym2addr(Process *, struct library_symbol *);
+-extern int linkmap_init(struct Process *proc, void *dyn_addr);
+-extern void arch_check_dbg(Process *proc);
+-extern int task_kill (pid_t pid, int sig);
+-
+-/* Called when trace_me or primary trace_pid fail.  This may plug in
+- * any platform-specific knowledge of why it could be so.  */
+-void trace_fail_warning(pid_t pid);
+-
+-/* A pair of functions called to initiate a detachment request when
+- * ltrace is about to exit.  Their job is to undo any effects that
+- * tracing had and eventually detach process, perhaps by way of
+- * installing a process handler.
+- *
+- * OS_LTRACE_EXITING_SIGHANDLER is called from a signal handler
+- * context right after the signal was captured.  It returns 1 if the
+- * request was handled or 0 if it wasn't.
+- *
+- * If the call to OS_LTRACE_EXITING_SIGHANDLER didn't handle the
+- * request, OS_LTRACE_EXITING is called when the next event is
+- * generated.  Therefore it's called in "safe" context, without
+- * re-entrancy concerns, but it's only called after an event is
+- * generated.  */
+-int os_ltrace_exiting_sighandler(void);
+-void os_ltrace_exiting(void);
+-
+-int arch_elf_init(struct ltelf *lte, struct library *lib);
+-void arch_elf_destroy(struct ltelf *lte);
+-
+-enum plt_status {
+-	plt_fail,
+-	plt_ok,
+-	plt_default,
+-};
+-
+-enum plt_status arch_elf_add_plt_entry(struct Process *p, struct ltelf *l,
+-				       const char *n, GElf_Rela *r, size_t i,
+-				       struct library_symbol **ret);
+-
+-int arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp);
+-void arch_breakpoint_destroy(struct breakpoint *sbp);
+-int arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp);
+-
+-void arch_library_init(struct library *lib);
+-void arch_library_destroy(struct library *lib);
+-void arch_library_clone(struct library *retp, struct library *lib);
+-
+-int arch_library_symbol_init(struct library_symbol *libsym);
+-void arch_library_symbol_destroy(struct library_symbol *libsym);
+-int arch_library_symbol_clone(struct library_symbol *retp,
+-			      struct library_symbol *libsym);
+-
+-int arch_process_init(struct Process *proc);
+-void arch_process_destroy(struct Process *proc);
+-int arch_process_clone(struct Process *retp, struct Process *proc);
+-int arch_process_exec(struct Process *proc);
+-
+-typedef void *target_address_t;
+-/* This should extract entry point address and interpreter (dynamic
+- * linker) bias if possible.  Returns 0 if there were no errors, -1
+- * otherwise.  Sets *ENTRYP and *INTERP_BIASP to non-zero values if
+- * the corresponding value is known.  Unknown values are set to 0.  */
+-int process_get_entry(struct Process *proc,
+-		      target_address_t *entryp,
+-		      target_address_t *interp_biasp);
+-
+-/* This is called after the dynamic linker is done with the
+- * process startup.  */
+-void arch_dynlink_done(struct Process *proc);
++/* Format VALUE into STREAM.  The dictionary of all arguments is given
++ * for purposes of evaluating array lengths and other dynamic
++ * expressions.  Returns number of characters outputted, -1 in case of
++ * failure.  */
++int format_argument(FILE *stream, struct value *value,
++		    struct value_dict *arguments);
++
++#endif /* COMMON_H */
+diff --git a/configure.ac b/configure.ac
+index 42d6158..26e1bb6 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -18,10 +18,10 @@ AC_SUBST(HOST_OS)
+ 
+ case "${host_cpu}" in
+     arm*|sa110)		HOST_CPU="arm" ;;
+-    i?86)		HOST_CPU="i386" ;;
+     powerpc|powerpc64)	HOST_CPU="ppc" ;;
+     sun4u|sparc64)	HOST_CPU="sparc" ;;
+     s390x)		HOST_CPU="s390" ;;
++    i?86|x86_64)	HOST_CPU="x86" ;;
+     *)			HOST_CPU="${host_cpu}" ;;
+ esac
+ AC_SUBST(HOST_CPU)
+@@ -261,14 +261,13 @@ AC_CONFIG_FILES([
+ 	sysdeps/linux-gnu/Makefile
+ 	sysdeps/linux-gnu/alpha/Makefile
+ 	sysdeps/linux-gnu/arm/Makefile
+-	sysdeps/linux-gnu/i386/Makefile
+ 	sysdeps/linux-gnu/ia64/Makefile
+ 	sysdeps/linux-gnu/m68k/Makefile
+ 	sysdeps/linux-gnu/mipsel/Makefile
+ 	sysdeps/linux-gnu/ppc/Makefile
+ 	sysdeps/linux-gnu/s390/Makefile
+ 	sysdeps/linux-gnu/sparc/Makefile
+-	sysdeps/linux-gnu/x86_64/Makefile
++	sysdeps/linux-gnu/x86/Makefile
+ 	testsuite/Makefile
+ 	testsuite/ltrace.main/Makefile
+ 	testsuite/ltrace.minor/Makefile
+diff --git a/defs.h b/defs.h
+index 1eadb47..e346ccb 100644
+--- a/defs.h
++++ b/defs.h
+@@ -3,10 +3,6 @@
+ #define DEFAULT_ALIGN 50	/* default alignment column for results */
+ #endif				/* (-a switch) */
+ 
+-#ifndef MAX_ARGS
+-#define MAX_ARGS        32	/* maximum number of args for a function */
+-#endif
+-
+ #ifndef DEFAULT_STRLEN
+ #define DEFAULT_STRLEN  32	/* default maximum # of bytes printed in */
+ #endif				/* strings (-s switch) */
+diff --git a/display_args.c b/display_args.c
+deleted file mode 100644
+index 5df34ca..0000000
+--- a/display_args.c
++++ /dev/null
+@@ -1,461 +0,0 @@
+-#include <ctype.h>
+-#include <stdio.h>
+-#include <stdlib.h>
+-#include <string.h>
+-#include <limits.h>
+-
+-#include "common.h"
+-#include "proc.h"
+-
+-static int display_char(int what);
+-static int display_string(enum tof type, Process *proc,
+-			  void* addr, size_t maxlen);
+-static int display_value(enum tof type, Process *proc,
+-			 long value, arg_type_info *info,
+-			 void *st, arg_type_info* st_info);
+-static int display_unknown(enum tof type, Process *proc, long value);
+-static int display_format(enum tof type, Process *proc, int arg_num);
+-
+-static size_t string_maxlength = INT_MAX;
+-static size_t array_maxlength = INT_MAX;
+-
+-static long
+-get_length(enum tof type, Process *proc, int len_spec,
+-		       void *st, arg_type_info* st_info) {
+-	long len;
+-	arg_type_info info;
+-
+-	if (len_spec > 0)
+-		return len_spec;
+-	if (type == LT_TOF_STRUCT) {
+-		umovelong (proc, st + st_info->u.struct_info.offset[-len_spec-1],
+-			   &len, st_info->u.struct_info.fields[-len_spec-1]);
+-		return len;
+-	}
+-
+-	info.type = ARGTYPE_INT;
+-	return gimme_arg(type, proc, -len_spec-1, &info);
+-}
+-
+-static int
+-display_ptrto(enum tof type, Process *proc, long item,
+-			 arg_type_info * info,
+-			 void *st, arg_type_info* st_info) {
+-	arg_type_info temp;
+-	temp.type = ARGTYPE_POINTER;
+-	temp.u.ptr_info.info = info;
+-	return display_value(type, proc, item, &temp, st, st_info);
+-}
+-
+-/*
+- * addr - A pointer to the first element of the array
+- *
+- * The function name is used to indicate that we're not actually
+- * looking at an 'array', which is a contiguous region of memory
+- * containing a sequence of elements of some type; instead, we have a
+- * pointer to that region of memory.
+- */
+-static int
+-display_arrayptr(enum tof type, Process *proc,
+-			    void *addr, arg_type_info * info,
+-			    void *st, arg_type_info* st_info) {
+-	int len = 0;
+-	size_t i;
+-	size_t array_len;
+-
+-	if (addr == NULL)
+-		return fprintf(options.output, "NULL");
+-
+-	array_len = get_length(type, proc, info->u.array_info.len_spec,
+-			st, st_info);
+-	len += fprintf(options.output, "[ ");
+-	for (i = 0; i < options.arraylen && i < array_maxlength && i < array_len; i++) {
+-		arg_type_info *elt_type = info->u.array_info.elt_type;
+-		size_t elt_size = info->u.array_info.elt_size;
+-		if (i != 0)
+-			len += fprintf(options.output, ", ");
+-		if (options.debug)
+-			len += fprintf(options.output, "%p=", addr);
+-		len +=
+-			display_ptrto(type, proc, (long) addr, elt_type, st, st_info);
+-		addr += elt_size;
+-	}
+-	if (i < array_len)
+-		len += fprintf(options.output, "...");
+-	len += fprintf(options.output, " ]");
+-	return len;
+-}
+-
+-/* addr - A pointer to the beginning of the memory region occupied by
+- *        the struct (aka a pointer to the struct)
+- */
+-static int
+-display_structptr(enum tof type, Process *proc,
+-			     void *addr, arg_type_info * info) {
+-	int i;
+-	arg_type_info *field;
+-	int len = 0;
+-
+-	if (addr == NULL)
+-		return fprintf(options.output, "NULL");
+-
+-	len += fprintf(options.output, "{ ");
+-	for (i = 0; (field = info->u.struct_info.fields[i]) != NULL; i++) {
+-		if (i != 0)
+-			len += fprintf(options.output, ", ");
+-		if (options.debug)
+-			len +=
+-				fprintf(options.output, "%p=",
+-						addr + info->u.struct_info.offset[i]);
+-		len +=
+-			display_ptrto(LT_TOF_STRUCT, proc,
+-					(long) addr + info->u.struct_info.offset[i],
+-					field, addr, info);
+-	}
+-	len += fprintf(options.output, " }");
+-
+-	return len;
+-}
+-
+-static int
+-display_pointer(enum tof type, Process *proc, long value,
+-			   arg_type_info * info,
+-			   void *st, arg_type_info* st_info) {
+-	long pointed_to;
+-	arg_type_info *inner = info->u.ptr_info.info;
+-
+-	if (inner->type == ARGTYPE_ARRAY) {
+-		return display_arrayptr(type, proc, (void*) value, inner,
+-				st, st_info);
+-	} else if (inner->type == ARGTYPE_STRUCT) {
+-		return display_structptr(type, proc, (void *) value, inner);
+-	} else {
+-		if (value == 0)
+-			return fprintf(options.output, "NULL");
+-		else if (umovelong (proc, (void *) value, &pointed_to,
+-				    info->u.ptr_info.info) < 0)
+-			return fprintf(options.output, "?");
+-		else
+-			return display_value(type, proc, pointed_to, inner,
+-					st, st_info);
+-	}
+-}
+-
+-static int
+-display_enum(enum tof type, Process *proc,
+-		arg_type_info* info, long value) {
+-	size_t ii;
+-	for (ii = 0; ii < info->u.enum_info.entries; ++ii) {
+-		if (info->u.enum_info.values[ii] == value)
+-			return fprintf(options.output, "%s", info->u.enum_info.keys[ii]);
+-	}
+-
+-	return display_unknown(type, proc, value);
+-}
+-
+-/* Args:
+-   type - syscall or shared library function or memory
+-   proc - information about the traced process
+-   value - the value to display
+-   info - the description of the type to display
+-   st - if the current value is a struct member, the address of the struct
+-   st_info - type of the above struct
+-
+-   Those last two parameters are used for structs containing arrays or
+-   strings whose length is given by another structure element.
+-*/
+-int
+-display_value(enum tof type, Process *proc,
+-		long value, arg_type_info *info,
+-		void *st, arg_type_info* st_info) {
+-	int tmp;
+-
+-	switch (info->type) {
+-	case ARGTYPE_VOID:
+-		return 0;
+-	case ARGTYPE_INT:
+-		return fprintf(options.output, "%d", (int) value);
+-	case ARGTYPE_UINT:
+-		return fprintf(options.output, "%u", (unsigned) value);
+-	case ARGTYPE_LONG:
+-		if (proc->mask_32bit)
+-			return fprintf(options.output, "%d", (int) value);
+-		else
+-			return fprintf(options.output, "%ld", value);
+-	case ARGTYPE_ULONG:
+-		if (proc->mask_32bit)
+-			return fprintf(options.output, "%u", (unsigned) value);
+-		else
+-			return fprintf(options.output, "%lu", (unsigned long) value);
+-	case ARGTYPE_OCTAL:
+-		return fprintf(options.output, "0%o", (unsigned) value);
+-	case ARGTYPE_CHAR:
+-		tmp = fprintf(options.output, "'");
+-		tmp += display_char(value == -1 ? value : (char) value);
+-		tmp += fprintf(options.output, "'");
+-		return tmp;
+-	case ARGTYPE_SHORT:
+-		return fprintf(options.output, "%hd", (short) value);
+-	case ARGTYPE_USHORT:
+-		return fprintf(options.output, "%hu", (unsigned short) value);
+-	case ARGTYPE_FLOAT: {
+-		union { long l; float f; double d; } cvt;
+-		cvt.l = value;
+-		return fprintf(options.output, "%f", cvt.f);
+-	}
+-	case ARGTYPE_DOUBLE: {
+-		union { long l; float f; double d; } cvt;
+-		cvt.l = value;
+-		return fprintf(options.output, "%lf", cvt.d);
+-	}
+-	case ARGTYPE_ADDR:
+-		if (!value)
+-			return fprintf(options.output, "NULL");
+-		else
+-			return fprintf(options.output, "0x%08lx", value);
+-	case ARGTYPE_FORMAT:
+-		fprintf(stderr, "Should never encounter a format anywhere but at the top level (for now?)\n");
+-		exit(1);
+-	case ARGTYPE_STRING:
+-		return display_string(type, proc, (void*) value,
+-				      string_maxlength);
+-	case ARGTYPE_STRING_N:
+-		return display_string(type, proc, (void*) value,
+-				      get_length(type, proc,
+-						 info->u.string_n_info.size_spec, st, st_info));
+-	case ARGTYPE_ARRAY:
+-		return fprintf(options.output, "<array without address>");
+-	case ARGTYPE_ENUM:
+-		return display_enum(type, proc, info, value);
+-	case ARGTYPE_STRUCT:
+-		return fprintf(options.output, "<struct without address>");
+-	case ARGTYPE_POINTER:
+-		return display_pointer(type, proc, value, info,
+-				       st, st_info);
+-	case ARGTYPE_UNKNOWN:
+-	default:
+-		return display_unknown(type, proc, value);
+-	}
+-}
+-
+-int
+-display_arg(enum tof type, Process *proc, int arg_num, arg_type_info * info) {
+-	long arg;
+-
+-	if (info->type == ARGTYPE_VOID) {
+-		return 0;
+-	} else if (info->type == ARGTYPE_FORMAT) {
+-		return display_format(type, proc, arg_num);
+-	} else {
+-		arg = gimme_arg(type, proc, arg_num, info);
+-		return display_value(type, proc, arg, info, NULL, NULL);
+-	}
+-}
+-
+-static int
+-display_char(int what) {
+-	switch (what) {
+-	case -1:
+-		return fprintf(options.output, "EOF");
+-	case '\r':
+-		return fprintf(options.output, "\\r");
+-	case '\n':
+-		return fprintf(options.output, "\\n");
+-	case '\t':
+-		return fprintf(options.output, "\\t");
+-	case '\b':
+-		return fprintf(options.output, "\\b");
+-	case '\\':
+-		return fprintf(options.output, "\\\\");
+-	default:
+-		if (isprint(what)) {
+-			return fprintf(options.output, "%c", what);
+-		} else {
+-			return fprintf(options.output, "\\%03o", (unsigned char)what);
+-		}
+-	}
+-}
+-
+-#define MIN(a,b) (((a)<(b)) ? (a) : (b))
+-
+-static int
+-display_string(enum tof type, Process *proc, void *addr,
+-			  size_t maxlength) {
+-	unsigned char *str1;
+-	size_t i;
+-	int len = 0;
+-
+-	if (!addr) {
+-		return fprintf(options.output, "NULL");
+-	}
+-
+-	str1 = malloc(MIN(options.strlen, maxlength) + 3);
+-	if (!str1) {
+-		return fprintf(options.output, "???");
+-	}
+-	umovestr(proc, addr, MIN(options.strlen, maxlength) + 1, str1);
+-	len = fprintf(options.output, "\"");
+-	for (i = 0; i < MIN(options.strlen, maxlength); i++) {
+-		if (str1[i]) {
+-			len += display_char(str1[i]);
+-		} else {
+-			break;
+-		}
+-	}
+-	len += fprintf(options.output, "\"");
+-	if (str1[i] && (options.strlen <= maxlength)) {
+-		len += fprintf(options.output, "...");
+-	}
+-	free(str1);
+-	return len;
+-}
+-
+-static int
+-display_unknown(enum tof type, Process *proc, long value) {
+-	if (proc->mask_32bit) {
+-		if ((int)value < 1000000 && (int)value > -1000000)
+-			return fprintf(options.output, "%d", (int)value);
+-		else
+-			return fprintf(options.output, "%p", (void *)value);
+-	} else if (value < 1000000 && value > -1000000) {
+-		return fprintf(options.output, "%ld", value);
+-	} else {
+-		return fprintf(options.output, "%p", (void *)value);
+-	}
+-}
+-
+-static int
+-display_format(enum tof type, Process *proc, int arg_num) {
+-	void *addr;
+-	unsigned char *str1;
+-	int i;
+-	size_t len = 0;
+-	arg_type_info info;
+-
+-	info.type = ARGTYPE_POINTER;
+-	addr = (void *)gimme_arg(type, proc, arg_num, &info);
+-	if (!addr) {
+-		return fprintf(options.output, "NULL");
+-	}
+-
+-	str1 = malloc(MIN(options.strlen, string_maxlength) + 3);
+-	if (!str1) {
+-		return fprintf(options.output, "???");
+-	}
+-	umovestr(proc, addr, MIN(options.strlen, string_maxlength) + 1, str1);
+-	len = fprintf(options.output, "\"");
+-	for (i = 0; len < MIN(options.strlen, string_maxlength) + 1; i++) {
+-		if (str1[i]) {
+-			len += display_char(str1[i]);
+-		} else {
+-			break;
+-		}
+-	}
+-	len += fprintf(options.output, "\"");
+-	if (str1[i] && (options.strlen <= string_maxlength)) {
+-		len += fprintf(options.output, "...");
+-	}
+-	for (i = 0; str1[i]; i++) {
+-		if (str1[i] == '%') {
+-			int is_long = 0;
+-			while (1) {
+-				unsigned char c = str1[++i];
+-				if (c == '%') {
+-					break;
+-				} else if (!c) {
+-					break;
+-				} else if (strchr("lzZtj", c)) {
+-					is_long++;
+-					if (c == 'j')
+-						is_long++;
+-					if (is_long > 1
+-					    && (sizeof(long) < sizeof(long long)
+-						|| proc->mask_32bit)) {
+-						len += fprintf(options.output, ", ...");
+-						str1[i + 1] = '\0';
+-						break;
+-					}
+-				} else if (c == 'd' || c == 'i') {
+-					info.type = ARGTYPE_LONG;
+-					if (!is_long || proc->mask_32bit)
+-						len +=
+-						    fprintf(options.output, ", %d",
+-							    (int)gimme_arg(type, proc, ++arg_num, &info));
+-					else
+-						len +=
+-						    fprintf(options.output, ", %ld",
+-							    gimme_arg(type, proc, ++arg_num, &info));
+-					break;
+-				} else if (c == 'u') {
+-					info.type = ARGTYPE_LONG;
+-					if (!is_long || proc->mask_32bit)
+-						len +=
+-						    fprintf(options.output, ", %u",
+-							    (int)gimme_arg(type, proc, ++arg_num, &info));
+-					else
+-						len +=
+-						    fprintf(options.output, ", %lu",
+-							    gimme_arg(type, proc, ++arg_num, &info));
+-					break;
+-				} else if (c == 'o') {
+-					info.type = ARGTYPE_LONG;
+-					if (!is_long || proc->mask_32bit)
+-						len +=
+-						    fprintf(options.output, ", 0%o",
+-							    (int)gimme_arg(type, proc, ++arg_num, &info));
+-					else
+-						len +=
+-						    fprintf(options.output, ", 0%lo",
+-							    gimme_arg(type, proc, ++arg_num, &info));
+-					break;
+-				} else if (c == 'x' || c == 'X') {
+-					info.type = ARGTYPE_LONG;
+-					if (!is_long || proc->mask_32bit)
+-						len +=
+-						    fprintf(options.output, ", %#x",
+-							    (int)gimme_arg(type, proc, ++arg_num, &info));
+-					else
+-						len +=
+-						    fprintf(options.output, ", %#lx",
+-							    gimme_arg(type, proc, ++arg_num, &info));
+-					break;
+-				} else if (strchr("eEfFgGaACS", c)
+-					   || (is_long
+-					       && (c == 'c' || c == 's'))) {
+-					len += fprintf(options.output, ", ...");
+-					str1[i + 1] = '\0';
+-					break;
+-				} else if (c == 'c') {
+-					info.type = ARGTYPE_LONG;
+-					len += fprintf(options.output, ", '");
+-					len +=
+-					    display_char((int)
+-							 gimme_arg(type, proc, ++arg_num, &info));
+-					len += fprintf(options.output, "'");
+-					break;
+-				} else if (c == 's') {
+-					info.type = ARGTYPE_POINTER;
+-					len += fprintf(options.output, ", ");
+-					len +=
+-					    display_string(type, proc,
+-							   (void *)gimme_arg(type, proc, ++arg_num, &info),
+-							   string_maxlength);
+-					break;
+-				} else if (c == 'p' || c == 'n') {
+-					info.type = ARGTYPE_POINTER;
+-					len +=
+-					    fprintf(options.output, ", %p",
+-						    (void *)gimme_arg(type, proc, ++arg_num, &info));
+-					break;
+-				} else if (c == '*') {
+-					info.type = ARGTYPE_LONG;
+-					len +=
+-					    fprintf(options.output, ", %d",
+-						    (int)gimme_arg(type, proc, ++arg_num, &info));
+-				}
+-			}
+-		}
+-	}
+-	free(str1);
+-	return len;
+-}
+diff --git a/etc/ltrace.conf b/etc/ltrace.conf
+index 3caf6b7..6513752 100644
+--- a/etc/ltrace.conf
++++ b/etc/ltrace.conf
+@@ -101,7 +101,7 @@ string inet_ntoa(addr);			; It isn't an ADDR but an hexa number...
+ addr inet_addr(string);
+ 
+ ; bfd.h
+-void bfd_init(void);
++void bfd_init();
+ int bfd_set_default_target(string);
+ addr bfd_scan_vma(string, addr, int);
+ addr bfd_openr(string,string);
+@@ -110,9 +110,9 @@ int bfd_check_format(addr,int);
+ ; ctype.h
+ char tolower(char);
+ char toupper(char);
+-addr __ctype_b_loc(void);
+-addr __ctype_tolower_loc(void);
+-addr __ctype_toupper_loc(void);
++addr __ctype_b_loc();
++addr __ctype_tolower_loc();
++addr __ctype_toupper_loc();
+ 
+ ; curses.h
+ int waddch(addr, char);
+@@ -129,12 +129,12 @@ addr readdir64(addr);
+ 
+ ; dlfcn.h
+ addr  dlopen(string, int);
+-string dlerror(void);
++string dlerror();
+ addr  dlsym(addr, string);
+ int dlclose(addr);
+ 
+ ; errno.h
+-addr __errno_location(void);
++addr __errno_location();
+ 
+ ; fcntl.h
+ int open(string,int,octal);		; WARNING: 3rd argument may not be there
+@@ -148,10 +148,10 @@ int getopt_long(int,addr,string,addr,int*);
+ int getopt_long_only(int,addr,string,addr,addr);
+ 
+ ; grp.h
+-void endgrent(void);
++void endgrent();
+ addr getgrnam(string);
+-void setgrent(void);
+-addr getgrent(void);
++void setgrent();
++addr getgrent();
+ 
+ ; libintl.h
+ string __dcgettext(string,string,int);
+@@ -166,8 +166,8 @@ int _IO_putc(char,file);
+ string setlocale(int, string);
+ 
+ ; mcheck.h
+-void mtrace(void);
+-void muntrace(void);
++void mtrace();
++void muntrace();
+ 
+ ; mntent.h
+ int endmntent(file);
+@@ -187,28 +187,28 @@ long mq_receive(int, +string0, ulong, addr);
+ long mq_timedreceive(int, +string0, ulong, addr, addr);
+ 
+ ; netdb.h
+-void endhostent(void);
+-void endnetent(void);
+-void endnetgrent(void);
+-void endprotoent(void);
+-void endservent(void);
++void endhostent();
++void endnetent();
++void endnetgrent();
++void endprotoent();
++void endservent();
+ void freeaddrinfo(addr);
+ string gai_strerror(int);
+ int getaddrinfo(string, string, addr, addr);
+ addr gethostbyaddr(string, uint, int);
+ addr gethostbyname(string);
+-addr gethostent(void);
++addr gethostent();
+ int getnameinfo(addr, uint, string, uint, string, uint, uint);
+ addr getnetbyaddr(uint, int);
+ addr getnetbyname(string);
+-addr getnetent(void);
++addr getnetent();
+ int getnetgrent(addr, addr, addr);
+ addr getprotobyname(string);
+ addr getprotobynumber(int);
+-addr getprotoent(void);
++addr getprotoent();
+ addr getservbyname(string, string);
+ addr getservbyport(int, string);
+-addr getservent(void);
++addr getservent();
+ void herror(string);
+ string hstrerror(int);
+ int rcmd(addr, ushort, string, string, string, addr);
+@@ -237,9 +237,9 @@ int pcap_compile(addr, addr, string, int, addr);
+ 
+ ; pwd.h
+ string getpass(string);
+-void endpwent(void);
++void endpwent();
+ addr getpwnam(string);
+-void setpwent(void);
++void setpwent();
+ 
+ ; readline/readline.h
+ string readline(string);
+@@ -305,7 +305,7 @@ int setenv(string,string,int);
+ void unsetenv(string);
+ addr malloc(ulong);
+ void qsort(addr,ulong,ulong,addr);
+-int random(void);
++int random();
+ addr realloc(addr,ulong);
+ void srandom(uint);
+ int system(string);
+@@ -364,7 +364,7 @@ int uname(addr);
+ int statfs(string,addr);
+ 
+ ; syslog.h
+-void closelog(void);
++void closelog();
+ void openlog(string,int,int);
+ void syslog(int,format);
+ 
+@@ -395,21 +395,21 @@ int dup2(int,int);
+ int execlp(string,string,addr,addr,addr);
+ int execv(string,addr);
+ int fchdir(int);
+-int fork(void);
++int fork();
+ int ftruncate(int,ulong);
+ string2 getcwd(addr,ulong);
+ int getdomainname(+string2,ulong);
+-int geteuid(void);
+-int getegid(void);
+-int getgid(void);
++int geteuid();
++int getegid();
++int getgid();
+ int gethostname(+string2,ulong);
+-string getlogin(void);
++string getlogin();
+ int getopt(int,addr,string);
+-int getpid(void);
+-int getppid(void);
+-int getuid(void);
+-int getpgrp(void);
+-int setpgrp(void);
++int getpid();
++int getppid();
++int getuid();
++int getpgrp();
++int setpgrp();
+ int getpgid(int);
+ int isatty(int);
+ int link(string,string);
+@@ -424,21 +424,21 @@ int setreuid(uint, uint);
+ int setuid(int);
+ uint sleep(uint);
+ int symlink(string,string);
+-int sync(void);
++int sync();
+ int truncate(string,ulong);
+ string ttyname(int);
+ int unlink(string);
+ void usleep(uint);
+ long write(int, string3, ulong);
+ addr sbrk(long);
+-int getpagesize(void);
++int getpagesize();
+ long lseek(int,long,int);
+ int pipe(addr);
+ 
+ ; utmp.h
+-void endutent(void);
+-addr getutent(void);
+-void setutent(void);
++void endutent();
++addr getutent();
++void setutent();
+ 
+ ; wchar.h
+ int fwide(addr, int);
+@@ -480,7 +480,7 @@ int acl_set_qualifier(addr,addr);
+ int acl_set_tag_type(addr,int);
+ int acl_size(addr);
+ string acl_to_text(addr,addr);
+-itn acl_valid(addr);
++int acl_valid(addr);
+ 
+ ; acl/libacl.h
+ int acl_check(addr,addr);
+@@ -500,9 +500,9 @@ int   SYS_close(int);
+ int   SYS_execve(string,addr,addr);
+ void  SYS_exit(int);
+ void  SYS_exit_group(int);
+-int   SYS_fork(void);
++int   SYS_fork();
+ int   SYS_getcwd(+string2,ulong);
+-int   SYS_getpid(void);
++int   SYS_getpid();
+ ;addr SYS_mmap(addr,ulong,int,int,int,long);
+ int   SYS_munmap(addr,ulong);
+ int   SYS_open(string,int,octal);
+@@ -512,7 +512,7 @@ int   SYS_stat(string,addr);
+ octal SYS_umask(octal);
+ int   SYS_uname(addr);
+ long  SYS_write(int,string3,ulong);
+-int   SYS_sync(void);
++int   SYS_sync();
+ int   SYS_setxattr(string,string,addr,uint,int);
+ int   SYS_lsetxattr(string,string,addr,uint,int);
+ int   SYS_fsetxattr(int,string,addr,uint,int);
+@@ -545,16 +545,16 @@ int   SYS_gettimeofday(addr,addr);
+ int   SYS_settimeofday(addr,addr);
+ int   SYS_setfsgid(int);
+ int   SYS_setfsuid(int);
+-int   SYS_getuid(void);
++int   SYS_getuid();
+ int   SYS_setuid(int);
+-int   SYS_getgid(void);
++int   SYS_getgid();
+ int   SYS_setgid(int);
+ int   SYS_getsid(int);
+ int   SYS_setsid(int);
+ int   SYS_setreuid(int,int);
+ int   SYS_setregid(int,int);
+-int   SYS_geteuid(void);
+-int   SYS_getegid(void);
++int   SYS_geteuid();
++int   SYS_getegid();
+ int   SYS_setpgid(int,int);
+ int   SYS_getresuid(addr,addr,addr);
+ int   SYS_setresuid(int,int,int);
+@@ -592,7 +592,7 @@ int   SYS_utime(string,addr);
+ long  SYS_lseek(int,long,int);
+ addr  SYS_signal(int,addr);
+ int   SYS_sigaction(int,addr,addr);
+-int   SYS_pause(void);
++int   SYS_pause();
+ int   SYS_sigpending(addr);
+ int   SYS_sigprocmask(int,addr,addr);
+ int   SYS_sigqueue(int,int,addr);
+diff --git a/execute_program.c b/execute_program.c
+index 55df205..f469b2f 100644
+--- a/execute_program.c
++++ b/execute_program.c
+@@ -14,7 +14,9 @@
+ #include <pwd.h>
+ #include <grp.h>
+ 
+-#include "common.h"
++#include "backend.h"
++#include "options.h"
++#include "debug.h"
+ 
+ static void
+ change_uid(const char * command)
+diff --git a/expr.c b/expr.c
+new file mode 100644
+index 0000000..c0ebcef
+--- /dev/null
++++ b/expr.c
+@@ -0,0 +1,337 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ */
++
++#include <string.h>
++#include <assert.h>
++#include <errno.h>
++#include <error.h>
++#include <stdlib.h>
++
++#include "expr.h"
++
++static void
++expr_init_common(struct expr_node *node, enum expr_node_kind kind)
++{
++	node->kind = kind;
++	node->lhs = NULL;
++	node->own_lhs = 0;
++	memset(&node->u, 0, sizeof(node->u));
++}
++
++void
++expr_init_self(struct expr_node *node)
++{
++	expr_init_common(node, EXPR_OP_SELF);
++}
++
++void
++expr_init_named(struct expr_node *node,
++		const char *name, int own_name)
++{
++	expr_init_common(node, EXPR_OP_NAMED);
++	node->u.name.s = name;
++	node->u.name.own = own_name;
++}
++
++void
++expr_init_argno(struct expr_node *node, size_t num)
++{
++	expr_init_common(node, EXPR_OP_ARGNO);
++	node->u.num = num;
++}
++
++void
++expr_init_const(struct expr_node *node, struct value *val)
++{
++	expr_init_common(node, EXPR_OP_CONST);
++	node->u.value = *val;
++}
++
++void
++expr_init_const_word(struct expr_node *node, long l,
++		     struct arg_type_info *type, int own_type)
++{
++	struct value val;
++	value_init_detached(&val, NULL, type, own_type);
++	value_set_word(&val, l);
++	expr_init_const(node, &val);
++}
++
++void
++expr_init_index(struct expr_node *node,
++		struct expr_node *lhs, int own_lhs,
++		struct expr_node *rhs, int own_rhs)
++{
++	expr_init_common(node, EXPR_OP_INDEX);
++	node->lhs = lhs;
++	node->own_lhs = own_lhs;
++	node->u.node.n = rhs;
++	node->u.node.own = own_rhs;
++}
++
++void
++expr_init_up(struct expr_node *node, struct expr_node *lhs, int own_lhs)
++{
++	assert(lhs != NULL);
++	expr_init_common(node, EXPR_OP_UP);
++	node->lhs = lhs;
++	node->own_lhs = own_lhs;
++}
++
++void
++expr_init_cb1(struct expr_node *node,
++	      int (*cb)(struct value *ret_value, struct value *value,
++			struct value_dict *arguments, void *data),
++	      struct expr_node *lhs, int own_lhs, void *data)
++{
++	expr_init_common(node, EXPR_OP_CALL1);
++	node->lhs = lhs;
++	node->own_lhs = own_lhs;
++	node->u.call.u.cb1 = cb;
++	node->u.call.data = data;
++}
++
++void
++expr_init_cb2(struct expr_node *node,
++	      int (*cb)(struct value *ret_value,
++			struct value *lhs, struct value *rhs,
++			struct value_dict *arguments, void *data),
++	      struct expr_node *lhs, int own_lhs,
++	      struct expr_node *rhs, int own_rhs, void *data)
++{
++	expr_init_common(node, EXPR_OP_CALL2);
++	node->lhs = lhs;
++	node->own_lhs = own_lhs;
++	node->u.call.rhs = rhs;
++	node->u.call.own_rhs = own_rhs;
++	node->u.call.u.cb2 = cb;
++	node->u.call.data = data;
++}
++
++static void
++release_lhs(struct expr_node *node)
++{
++	if (node->own_lhs)
++		expr_destroy(node->lhs);
++}
++
++void
++expr_destroy(struct expr_node *node)
++{
++	if (node == NULL)
++		return;
++
++	switch (node->kind) {
++	case EXPR_OP_ARGNO:
++	case EXPR_OP_SELF:
++		return;
++
++	case EXPR_OP_CONST:
++		value_destroy(&node->u.value);
++		return;
++
++	case EXPR_OP_NAMED:
++		if (node->u.name.own)
++			free((char *)node->u.name.s);
++		return;
++
++	case EXPR_OP_INDEX:
++		release_lhs(node);
++		if (node->u.node.own)
++			expr_destroy(node->u.node.n);
++		return;
++
++	case EXPR_OP_CALL2:
++		if (node->u.call.own_rhs)
++			expr_destroy(node->u.call.rhs);
++	case EXPR_OP_UP:
++	case EXPR_OP_CALL1:
++		release_lhs(node);
++		return;
++	}
++
++	assert(!"Invalid value of node kind");
++	abort();
++}
++
++int
++expr_is_compile_constant(struct expr_node *node)
++{
++	return node->kind == EXPR_OP_CONST;
++}
++
++static int
++eval_up(struct expr_node *node, struct value *context,
++	struct value_dict *arguments, struct value *ret_value)
++{
++	if (expr_eval(node->lhs, context, arguments, ret_value) < 0)
++		return -1;
++	struct value *parent = value_get_parental_struct(ret_value);
++	if (parent == NULL) {
++		value_destroy(ret_value);
++		return -1;
++	}
++	*ret_value = *parent;
++	return 0;
++}
++
++static int
++eval_cb1(struct expr_node *node, struct value *context,
++	 struct value_dict *arguments, struct value *ret_value)
++{
++	struct value val;
++	if (expr_eval(node->lhs, context, arguments, &val) < 0)
++		return -1;
++
++	int ret = 0;
++	if (node->u.call.u.cb1(ret_value, &val, arguments,
++			       node->u.call.data) < 0)
++		ret = -1;
++
++	/* N.B. the callback must return its own value, or somehow
++	 * clone the incoming argument.  */
++	value_destroy(&val);
++	return ret;
++}
++
++static int
++eval_cb2(struct expr_node *node, struct value *context,
++	 struct value_dict *arguments, struct value *ret_value)
++{
++	struct value lhs;
++	if (expr_eval(node->lhs, context, arguments, &lhs) < 0)
++		return -1;
++
++	struct value rhs;
++	if (expr_eval(node->u.call.rhs, context, arguments, &rhs) < 0) {
++		value_destroy(&lhs);
++		return -1;
++	}
++
++	int ret = 0;
++	if (node->u.call.u.cb2(ret_value, &lhs, &rhs, arguments,
++			       node->u.call.data) < 0)
++		ret = -1;
++
++	/* N.B. the callback must return its own value, or somehow
++	 * clone the incoming argument.  */
++	value_destroy(&lhs);
++	value_destroy(&rhs);
++	return ret;
++}
++
++int
++eval_index(struct expr_node *node, struct value *context,
++	   struct value_dict *arguments, struct value *ret_value)
++{
++	struct value lhs;
++	if (expr_eval(node->lhs, context, arguments, &lhs) < 0)
++		return -1;
++
++	long l;
++	if (expr_eval_word(node->u.node.n, context, arguments, &l) < 0) {
++	fail:
++		value_destroy(&lhs);
++		return -1;
++	}
++
++	if (value_init_element(ret_value, &lhs, (size_t)l) < 0)
++		goto fail;
++	return 0;
++}
++
++int
++expr_eval(struct expr_node *node, struct value *context,
++	  struct value_dict *arguments, struct value *ret_value)
++{
++	switch (node->kind) {
++		struct value *valp;
++	case EXPR_OP_ARGNO:
++		valp = val_dict_get_num(arguments, node->u.num);
++		if (valp == NULL)
++			return -1;
++		*ret_value = *valp;
++		return 0;
++
++	case EXPR_OP_NAMED:
++		valp = val_dict_get_name(arguments, node->u.name.s);
++		if (valp == NULL)
++			return -1;
++		*ret_value = *valp;
++		return 0;
++
++	case EXPR_OP_SELF:
++		*ret_value = *context;
++		return 0;
++
++	case EXPR_OP_CONST:
++		*ret_value = node->u.value;
++		return 0;
++
++	case EXPR_OP_INDEX:
++		return eval_index(node, context, arguments, ret_value);
++
++	case EXPR_OP_UP:
++		return eval_up(node, context, arguments, ret_value);
++
++	case EXPR_OP_CALL1:
++		return eval_cb1(node, context, arguments, ret_value);
++
++	case EXPR_OP_CALL2:
++		return eval_cb2(node, context, arguments, ret_value);
++	}
++
++	assert(!"Unknown node kind.");
++	abort();
++}
++
++int
++expr_eval_word(struct expr_node *node, struct value *context,
++	       struct value_dict *arguments, long *ret_value)
++{
++	struct value val;
++	if (expr_eval(node, context, arguments, &val) < 0)
++		return -1;
++	int ret = 0;
++	if (value_extract_word(&val, ret_value, arguments) < 0)
++		ret = -1;
++	value_destroy(&val);
++	return ret;
++}
++
++int
++expr_eval_constant(struct expr_node *node, long *valuep)
++{
++	assert(expr_is_compile_constant(node));
++	return expr_eval_word(node, NULL, NULL, valuep);
++}
++
++struct expr_node *
++expr_self(void)
++{
++	static struct expr_node *node = NULL;
++	if (node == NULL) {
++		node = malloc(sizeof(*node));
++		if (node == NULL)
++			error(1, errno, "malloc expr_self");
++		expr_init_self(node);
++	}
++	return node;
++}
+diff --git a/expr.h b/expr.h
+new file mode 100644
+index 0000000..53b75b7
+--- /dev/null
++++ b/expr.h
+@@ -0,0 +1,156 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ */
++
++#ifndef EXPR_H
++#define EXPR_H
++
++#include "value.h"
++#include "value_dict.h"
++
++/* Expressions serve as a way of encoding array lengths.  */
++
++enum expr_node_kind {
++	EXPR_OP_SELF,	/* reference to the variable in question  */
++	EXPR_OP_NAMED,	/* value of named argument */
++	EXPR_OP_ARGNO,	/* value of numbered argument */
++	EXPR_OP_CONST,	/* constant value */
++	EXPR_OP_INDEX,	/* A[B] */
++	EXPR_OP_UP,	/* reference to containing structure */
++	EXPR_OP_CALL1,	/* internal callback with one operand */
++	EXPR_OP_CALL2,	/* internal callback with two operands */
++};
++
++struct expr_node {
++	enum expr_node_kind kind;
++
++	struct expr_node *lhs;
++	int own_lhs;
++
++	union {
++		struct {
++			const char *s;
++			int own;
++		} name;
++		struct {
++			struct expr_node *n;
++			int own;
++		} node;
++		struct value value;
++		size_t num;
++		struct {
++			union {
++				int (*cb1)(struct value *ret_value,
++					   struct value *lhs,
++					   struct value_dict *arguments,
++					   void *data);
++				int (*cb2)(struct value *ret_value,
++					   struct value *lhs,
++					   struct value *rhs,
++					   struct value_dict *arguments,
++					   void *data);
++			} u;
++			void *data;
++			struct expr_node *rhs;
++			int own_rhs;
++		} call;
++	} u;
++};
++
++/* Expression of type self just returns the value in consideration.
++ * For example, if what we seek is length of an array, then the value
++ * representing that array is returned by the expression.  */
++void expr_init_self(struct expr_node *node);
++
++/* Expression that yields the value of an argument named NAME.  NAME
++ * is owned if OWN_NAME.  */
++void expr_init_named(struct expr_node *node,
++		     const char *name, int own_name);
++
++/* Expression that yields the value of an argument number NUM.  */
++void expr_init_argno(struct expr_node *node, size_t num);
++
++/* Constant expression always returns the same value VAL.  VAL is
++ * copied into NODE and owned by it.  */
++void expr_init_const(struct expr_node *node, struct value *val);
++void expr_init_const_word(struct expr_node *node, long l,
++			  struct arg_type_info *type, int own_type);
++
++/* Expression LHS[RHS].  LHS and RHS are owned if, respectively,
++ * OWN_LHS and OWN_RHS.  */
++void expr_init_index(struct expr_node *node,
++		     struct expr_node *lhs, int own_lhs,
++		     struct expr_node *rhs, int own_rhs);
++
++/* This expression returns the containing value of LHS (^LHS).  LHS is
++ * owned if OWN_LHS.  */
++void expr_init_up(struct expr_node *node, struct expr_node *lhs, int own_lhs);
++
++/* Callback expression calls CB(eval(LHS), DATA).  LHS is owned if
++ * OWN_LHS.  DATA is passed to callback verbatim.  */
++void expr_init_cb1(struct expr_node *node,
++		   int (*cb)(struct value *ret_value,
++			     struct value *value,
++			     struct value_dict *arguments,
++			     void *data),
++		   struct expr_node *lhs, int own_lhs, void *data);
++
++/* Callback expression calls CB(eval(LHS), eval(RHS), DATA).  LHS and
++ * RHS are owned if, respectively, OWN_LHS and OWN_RHS.  DATA is
++ * passed to callback verbatim.  */
++void expr_init_cb2(struct expr_node *node,
++		   int (*cb)(struct value *ret_value,
++			     struct value *lhs, struct value *rhs,
++			     struct value_dict *arguments,
++			     void *data),
++		   struct expr_node *lhs, int own_lhs,
++		   struct expr_node *rhs, int own_rhs, void *data);
++
++/* Release the data inside NODE.  Doesn't free NODE itself.  */
++void expr_destroy(struct expr_node *node);
++
++/* Evaluate the expression NODE in context of VALUE.  ARGUMENTS is a
++ * dictionary of named and numbered values that NODE may use.  Returns
++ * 0 in case of success or a negative value on error.  CONTEXT and
++ * ARGUMENTS may be NULL, but then the expression mustn't need them
++ * for evaluation.  */
++int expr_eval(struct expr_node *node, struct value *context,
++	      struct value_dict *arguments, struct value *ret_value);
++
++/* Evaluate compile-time expression.  Returns 0 on success or negative
++ * value on failure.  Computed value is passed back in *VALUEP.  */
++int expr_eval_constant(struct expr_node *node, long *valuep);
++
++/* Evaluate expression, whose result should fit into a word.  In order
++ * to easily support all the structure and array accesses, we simply
++ * operate on values represented by struct value.  But eventually we need
++ * to be able to get out a word-size datum to use it as an index, a
++ * length, etc.  */
++int expr_eval_word(struct expr_node *node, struct value *context,
++		   struct value_dict *arguments, long *ret_value);
++
++/* Returns non-zero value if the expression is a compile-time
++ * constant.  Currently this is only EXPR_OP_CONST, but eventually
++ * things like sizeof or simple expressions might be allowed.  */
++int expr_is_compile_constant(struct expr_node *node);
++
++/* Returns a pre-computed expression "self".  */
++struct expr_node *expr_self(void);
++
++#endif /* EXPR_H */
+diff --git a/fetch.c b/fetch.c
+new file mode 100644
+index 0000000..bce949f
+--- /dev/null
++++ b/fetch.c
+@@ -0,0 +1,132 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ */
++
++#include <stdlib.h>
++#include <string.h>
++
++#include "fetch.h"
++#include "value.h"
++#include "arch.h"
++#include "type.h"
++
++#ifdef ARCH_HAVE_FETCH_ARG
++struct fetch_context *arch_fetch_arg_init(enum tof type, struct Process *proc,
++					  struct arg_type_info *ret_info);
++
++struct fetch_context *arch_fetch_arg_clone(struct Process *proc,
++					   struct fetch_context *context);
++
++int arch_fetch_arg_next(struct fetch_context *ctx, enum tof type,
++			struct Process *proc, struct arg_type_info *info,
++			struct value *valuep);
++
++int arch_fetch_retval(struct fetch_context *ctx, enum tof type,
++		      struct Process *proc, struct arg_type_info *info,
++		      struct value *valuep);
++
++void arch_fetch_arg_done(struct fetch_context *context);
++
++#else
++/* Fall back to gimme_arg.  */
++
++long gimme_arg(enum tof type, struct Process *proc, int arg_num,
++	       struct arg_type_info *info);
++
++struct fetch_context {
++	int argnum;
++};
++
++struct fetch_context *
++arch_fetch_arg_init(enum tof type, struct Process *proc,
++		    struct arg_type_info *ret_info)
++{
++	return calloc(sizeof(struct fetch_context), 1);
++}
++
++struct fetch_context *
++arch_fetch_arg_clone(struct Process *proc, struct fetch_context *context)
++{
++	struct fetch_context *ret = malloc(sizeof(*ret));
++	if (ret == NULL)
++		return NULL;
++	return memcpy(ret, context, sizeof(*ret));
++}
++
++int
++arch_fetch_arg_next(struct fetch_context *context, enum tof type,
++		    struct Process *proc,
++		    struct arg_type_info *info, struct value *valuep)
++{
++	long l = gimme_arg(type, proc, context->argnum++, info);
++	value_set_word(valuep, l);
++	return 0;
++}
++
++int
++arch_fetch_retval(struct fetch_context *context, enum tof type,
++		  struct Process *proc,
++		  struct arg_type_info *info, struct value *valuep)
++{
++	long l = gimme_arg(type, proc, -1, info);
++	value_set_word(valuep, l);
++	return 0;
++}
++
++void
++arch_fetch_arg_done(struct fetch_context *context)
++{
++	free(context);
++}
++#endif
++
++struct fetch_context *
++fetch_arg_init(enum tof type, struct Process *proc,
++	       struct arg_type_info *ret_info)
++{
++	return arch_fetch_arg_init(type, proc, ret_info);
++}
++
++struct fetch_context *
++fetch_arg_clone(struct Process *proc, struct fetch_context *context)
++{
++	return arch_fetch_arg_clone(proc, context);
++}
++
++int
++fetch_arg_next(struct fetch_context *context, enum tof type,
++	       struct Process *proc,
++	       struct arg_type_info *info, struct value *valuep)
++{
++	return arch_fetch_arg_next(context, type, proc, info, valuep);
++}
++
++int
++fetch_retval(struct fetch_context *context, enum tof type,
++	     struct Process *proc,
++	     struct arg_type_info *info, struct value *valuep)
++{
++	return arch_fetch_retval(context, type, proc, info, valuep);
++}
++
++void
++fetch_arg_done(struct fetch_context *context)
++{
++	return arch_fetch_arg_done(context);
++}
+diff --git a/fetch.h b/fetch.h
+new file mode 100644
+index 0000000..6a5385c
+--- /dev/null
++++ b/fetch.h
+@@ -0,0 +1,64 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ */
++
++#ifndef FETCH_H
++#define FETCH_H
++
++#include "forward.h"
++
++/* XXX isn't SYSCALL TOF just a different ABI?  Maybe we needed to
++ * support variant ABIs all along.  */
++enum tof {
++	LT_TOF_FUNCTION,	/* A real library function */
++	LT_TOF_FUNCTIONR,	/* Return from a real library function */
++	LT_TOF_SYSCALL,		/* A syscall */
++	LT_TOF_SYSCALLR,	/* Return from a syscall */
++};
++
++/* The contents of the structure is defined by the back end.  */
++struct fetch_context;
++
++/* Initialize argument fetching.  Returns NULL on failure.  RET_INFO
++ * is the return type of the function.  */
++struct fetch_context *fetch_arg_init(enum tof type, struct Process *proc,
++				     struct arg_type_info *ret_info);
++
++/* Make a clone of context.  */
++struct fetch_context *fetch_arg_clone(struct Process *proc,
++				      struct fetch_context *context);
++
++/* Load next argument.  The function returns 0 on success or a
++ * negative value on failure.  The extracted value is stored in
++ * *VALUEP.  */
++int fetch_arg_next(struct fetch_context *context, enum tof type,
++		   struct Process *proc,
++		   struct arg_type_info *info, struct value *valuep);
++
++/* Load return value.  The function returns 0 on success or a negative
++ * value on failure.  The extracted value is stored in *VALUEP.  */
++int fetch_retval(struct fetch_context *context, enum tof type,
++		 struct Process *proc,
++		 struct arg_type_info *info, struct value *valuep);
++
++/* Destroy fetch context.  CONTEXT shall be the same memory location
++ * that was passed to fetch_arg_next.  */
++void fetch_arg_done(struct fetch_context *context);
++
++#endif /* FETCH_H */
+diff --git a/forward.h b/forward.h
+new file mode 100644
+index 0000000..e4233e5
+--- /dev/null
++++ b/forward.h
+@@ -0,0 +1,14 @@
++/* Important types defined in other header files are declared
++   here.  */
++struct Event;
++struct Process;
++struct arg_type_info;
++struct breakpoint;
++struct expr_node;
++struct library;
++struct library_symbol;
++struct ltelf;
++struct param;
++struct param_enum;
++struct value;
++struct value_dict;
+diff --git a/handle_event.c b/handle_event.c
+index 73c118a..5b6cc40 100644
+--- a/handle_event.c
++++ b/handle_event.c
+@@ -1,6 +1,31 @@
+-#define _GNU_SOURCE
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
++ * Copyright (C) 2010 Arnaud Patard, Mandriva SA
++ * Copyright (C) 1998,2001,2002,2003,2004,2007,2008,2009 Juan Cespedes
++ * Copyright (C) 2008 Luis Machado, IBM Corporation
++ * Copyright (C) 2006 Ian Wienand
++ * Copyright (C) 2006 Paul Gilliam, IBM Corporation
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ */
++
+ #include "config.h"
+ 
++#define _GNU_SOURCE
+ #include <assert.h>
+ #include <errno.h>
+ #include <signal.h>
+@@ -9,10 +34,13 @@
+ #include <string.h>
+ #include <sys/time.h>
+ 
++#include "backend.h"
+ #include "breakpoint.h"
+ #include "common.h"
++#include "fetch.h"
+ #include "library.h"
+ #include "proc.h"
++#include "value_dict.h"
+ 
+ static void handle_signal(Event *event);
+ static void handle_exit(Event *event);
+@@ -228,9 +256,11 @@ handle_clone(Event *event)
+ 	if (proc == NULL) {
+ 	fail:
+ 		free(proc);
+-		/* XXX proper error handling here, please.  */
+-		perror("malloc()");
+-		exit(1);
++		fprintf(stderr,
++			"Error during init of tracing process %d\n"
++			"This process won't be traced.\n",
++			event->proc->pid);
++		return;
+ 	}
+ 
+ 	if (process_clone(proc, event->proc, event->e_un.newpid) < 0)
+@@ -386,13 +416,13 @@ handle_exit_signal(Event *event) {
+ }
+ 
+ static void
+-output_syscall(struct Process *proc, const char *name,
++output_syscall(struct Process *proc, const char *name, enum tof tof,
+ 	       void (*output)(enum tof, struct Process *,
+ 			      struct library_symbol *))
+ {
+ 	struct library_symbol syscall;
+ 	if (library_symbol_init(&syscall, 0, name, 0, LS_TOPLT_NONE) >= 0) {
+-		(*output)(LT_TOF_SYSCALL, proc, &syscall);
++		(*output)(tof, proc, &syscall);
+ 		library_symbol_destroy(&syscall);
+ 	}
+ }
+@@ -400,13 +430,13 @@ output_syscall(struct Process *proc, const char *name,
+ static void
+ output_syscall_left(struct Process *proc, const char *name)
+ {
+-	output_syscall(proc, name, &output_left);
++	output_syscall(proc, name, LT_TOF_SYSCALL, &output_left);
+ }
+ 
+ static void
+ output_syscall_right(struct Process *proc, const char *name)
+ {
+-	output_syscall(proc, name, &output_right);
++	output_syscall(proc, name, LT_TOF_SYSCALLR, &output_right);
+ }
+ 
+ static void
+@@ -670,6 +700,7 @@ callstack_push_syscall(Process *proc, int sysnum) {
+ 	}
+ 
+ 	elem = &proc->callstack[proc->callstack_depth];
++	*elem = (struct callstack_element){};
+ 	elem->is_syscall = 1;
+ 	elem->c_un.syscall = sysnum;
+ 	elem->return_addr = NULL;
+@@ -694,6 +725,7 @@ callstack_push_symfunc(Process *proc, struct library_symbol *sym) {
+ 	}
+ 
+ 	elem = &proc->callstack[proc->callstack_depth++];
++	*elem = (struct callstack_element){};
+ 	elem->is_syscall = 0;
+ 	elem->c_un.libfunc = sym;
+ 
+@@ -701,7 +733,6 @@ callstack_push_symfunc(Process *proc, struct library_symbol *sym) {
+ 	if (elem->return_addr)
+ 		insert_breakpoint(proc, elem->return_addr, NULL);
+ 
+-	/* handle functions like atexit() on mips which have no return */
+ 	if (opt_T || options.summary) {
+ 		struct timezone tz;
+ 		gettimeofday(&elem->time_spent, &tz);
+@@ -719,9 +750,5 @@ callstack_pop(Process *proc) {
+ 		assert(proc->leader != NULL);
+ 		delete_breakpoint(proc, elem->return_addr);
+ 	}
+-	if (elem->arch_ptr != NULL) {
+-		free(elem->arch_ptr);
+-		elem->arch_ptr = NULL;
+-	}
+ 	proc->callstack_depth--;
+ }
+diff --git a/lens.c b/lens.c
+new file mode 100644
+index 0000000..07cdcca
+--- /dev/null
++++ b/lens.c
+@@ -0,0 +1,62 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2011 Petr Machata, Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ */
++
++#include <assert.h>
++
++#include "lens.h"
++#include "lens_default.h"
++#include "type.h"
++#include "value.h"
++
++int
++format_argument(FILE *stream, struct value *value,
++		struct value_dict *arguments)
++{
++	/* Find the closest enclosing parental value whose type has a
++	 * lens assigned.  */
++	struct value *parent;
++	for (parent = value; (parent != NULL && parent->type != NULL
++			      && parent->type->lens == NULL);
++	     parent = parent->parent)
++		;
++
++	struct lens *lens = &default_lens;
++	if (parent != NULL && parent->type != NULL
++	    && parent->type->lens != NULL)
++		lens = parent->type->lens;
++
++	return lens_format(lens, stream, value, arguments);
++}
++
++int
++lens_format(struct lens *lens, FILE *stream, struct value *value,
++	    struct value_dict *arguments)
++{
++	assert(lens->format_cb != NULL);
++	return lens->format_cb(lens, stream, value, arguments);
++}
++
++void
++lens_destroy(struct lens *lens)
++{
++	if (lens != NULL
++	    && lens->destroy_cb != NULL)
++		lens->destroy_cb(lens);
++}
+diff --git a/lens.h b/lens.h
+new file mode 100644
+index 0000000..f7de3b5
+--- /dev/null
++++ b/lens.h
+@@ -0,0 +1,51 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2011 Petr Machata, Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ */
++
++#ifndef LENS_H
++#define LENS_H
++
++#include <stdio.h>
++#include "forward.h"
++
++struct lens {
++	/* Format VALUE into STREAM.  The dictionary of all arguments
++	 * is given for purposes of evaluating array lengths and other
++	 * dynamic expressions.  Returns number of characters
++	 * outputted, -1 in case of failure.  */
++	int (*format_cb)(struct lens *lens, FILE *stream, struct value *value,
++			 struct value_dict *arguments);
++
++	/* Erase any memory allocated for LENS.  Keep the LENS pointer
++	 * itself intact.  */
++	void (*destroy_cb)(struct lens *lens);
++};
++
++/* Extracts a lens from VALUE and calls lens_format on that.  */
++int format_argument(FILE *stream, struct value *value,
++		    struct value_dict *arguments);
++
++/* Calls format callback associated with LENS.  */
++int lens_format(struct lens *lens, FILE *stream, struct value *value,
++		struct value_dict *arguments);
++
++/* Calls destroy callback associated with LENS.  */
++void lens_destroy(struct lens *lens);
++
++#endif /* LENS_H */
+diff --git a/lens_default.c b/lens_default.c
+new file mode 100644
+index 0000000..f59d328
+--- /dev/null
++++ b/lens_default.c
+@@ -0,0 +1,499 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
++ * Copyright (C) 1998,2004,2007,2008,2009 Juan Cespedes
++ * Copyright (C) 2006 Ian Wienand
++ * Copyright (C) 2006 Steve Fink
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ */
++
++#include <ctype.h>
++#include <stdlib.h>
++#include <assert.h>
++#include <inttypes.h>
++#include <stdarg.h>
++#include <stdio.h>
++
++#include "proc.h"
++#include "lens_default.h"
++#include "value.h"
++#include "expr.h"
++#include "type.h"
++#include "common.h"
++#include "zero.h"
++
++#define READER(NAME, TYPE)						\
++	static int							\
++	NAME(struct value *value, TYPE *ret, struct value_dict *arguments) \
++	{								\
++		union {							\
++			TYPE val;					\
++			unsigned char buf[0];				\
++		} u;							\
++		if (value_extract_buf(value, u.buf, arguments) < 0)	\
++			return -1;					\
++		*ret = u.val;						\
++		return 0;						\
++	}
++
++READER(read_float, float)
++READER(read_double, double)
++
++#undef READER
++
++#define HANDLE_WIDTH(BITS)						\
++	do {								\
++		long l;							\
++		if (value_extract_word(value, &l, arguments) < 0)	\
++			return -1;					\
++		int##BITS##_t i = l;					\
++		switch (format) {					\
++		case INT_FMT_unknown:					\
++			if (i < -10000 || i > 10000)			\
++		case INT_FMT_x:						\
++			return fprintf(stream, "%#"PRIx##BITS, i);	\
++		case INT_FMT_i:						\
++			return fprintf(stream, "%"PRIi##BITS, i);	\
++		case INT_FMT_u:						\
++			return fprintf(stream, "%"PRIu##BITS, i);	\
++		case INT_FMT_o:						\
++			return fprintf(stream, "0%"PRIo##BITS, i);	\
++		}							\
++	} while (0)
++
++enum int_fmt_t
++{
++	INT_FMT_i,
++	INT_FMT_u,
++	INT_FMT_o,
++	INT_FMT_x,
++	INT_FMT_unknown,
++};
++
++static int
++format_integer(FILE *stream, struct value *value, enum int_fmt_t format,
++	       struct value_dict *arguments)
++{
++	switch (type_sizeof(value->inferior, value->type)) {
++
++	case 1: HANDLE_WIDTH(8);
++	case 2: HANDLE_WIDTH(16);
++	case 4: HANDLE_WIDTH(32);
++	case 8: HANDLE_WIDTH(64);
++
++	default:
++		assert(!"unsupported integer width");
++		abort();
++
++	case -1:
++		return -1;
++	}
++}
++
++#undef HANDLE_WIDTH
++
++static int
++account(int *countp, int c)
++{
++	if (c >= 0)
++		*countp += c;
++	return c;
++}
++
++static int
++acc_fprintf(int *countp, FILE *stream, const char *format, ...)
++{
++	va_list pa;
++	va_start(pa, format);
++	int i = account(countp, vfprintf(stream, format, pa));
++	va_end(pa);
++
++	return i;
++}
++
++static int
++format_char(FILE *stream, struct value *value, struct value_dict *arguments)
++{
++	long lc;
++	if (value_extract_word(value, &lc, arguments) < 0)
++		return -1;
++	int c = (int)lc;
++
++	const char *fmt;
++	switch (c) {
++	case -1:
++		fmt = "EOF";
++		break;
++	case 0:
++		fmt = "\\0";
++		break;
++	case '\a':
++		fmt = "\\a";
++		break;
++	case '\b':
++		fmt = "\\b";
++		break;
++	case '\t':
++		fmt = "\\t";
++		break;
++	case '\n':
++		fmt = "\\n";
++		break;
++	case '\v':
++		fmt = "\\v";
++		break;
++	case '\f':
++		fmt = "\\f";
++		break;
++	case '\r':
++		fmt = "\\r";
++		break;
++	case '\\':
++		fmt = "\\\\";
++		break;
++	default:
++		if (isprint(c) || c == ' ')
++			fmt = "%c";
++		else
++			fmt = "\\%03o";
++	}
++
++	return fprintf(stream, fmt, c);
++}
++
++static int
++format_naked_char(FILE *stream, struct value *value,
++		  struct value_dict *arguments)
++{
++	int written = 0;
++	if (acc_fprintf(&written, stream, "'") < 0
++	    || account(&written, format_char(stream, value, arguments)) < 0
++	    || acc_fprintf(&written, stream, "'") < 0)
++		return -1;
++
++	return written;
++}
++
++static int
++format_floating(FILE *stream, struct value *value, struct value_dict *arguments)
++{
++	switch (value->type->type) {
++		float f;
++		double d;
++	case ARGTYPE_FLOAT:
++		if (read_float(value, &f, arguments) < 0)
++			return -1;
++		return fprintf(stream, "%f", (double)f);
++	case ARGTYPE_DOUBLE:
++		if (read_double(value, &d, arguments) < 0)
++			return -1;
++		return fprintf(stream, "%f", d);
++	default:
++		abort();
++	}
++}
++
++static int
++format_struct(FILE *stream, struct value *value, struct value_dict *arguments)
++{
++	int written = 0;
++	if (acc_fprintf(&written, stream, "{ ") < 0)
++		return -1;
++	size_t i;
++	for (i = 0; i < type_struct_size(value->type); ++i) {
++		if (i > 0 && acc_fprintf(&written, stream, ", ") < 0)
++			return -1;
++
++		struct value element;
++		if (value_init_element(&element, value, i) < 0)
++			return -1;
++		int o = format_argument(stream, &element, arguments);
++		if (o < 0)
++			return -1;
++		written += o;
++	}
++	if (acc_fprintf(&written, stream, " }") < 0)
++		return -1;
++	return written;
++}
++
++int
++format_pointer(FILE *stream, struct value *value, struct value_dict *arguments)
++{
++	struct value element;
++	if (value_init_deref(&element, value) < 0)
++		return -1;
++	return format_argument(stream, &element, arguments);
++}
++
++/*
++ * LENGTH is an expression whose evaluation will yield the actual
++ *    length of the array.
++ *
++ * MAXLEN is the actual maximum length that we care about
++ *
++ * BEFORE if LENGTH>MAXLEN, we display ellipsis.  We display it before
++ *    the closing parenthesis if BEFORE, otherwise after it.
++ *
++ * OPEN, CLOSE, DELIM are opening and closing parenthesis and element
++ *    delimiter.
++ */
++int
++format_array(FILE *stream, struct value *value, struct value_dict *arguments,
++	     struct expr_node *length, size_t maxlen, int before,
++	     const char *open, const char *close, const char *delim)
++{
++	/* We need "long" to be long enough to cover the whole address
++	 * space.  */
++	typedef char assert__long_enough_long[-(sizeof(long) < sizeof(void *))];
++	long l;
++	if (expr_eval_word(length, value, arguments, &l) < 0)
++		return -1;
++	size_t len = (size_t)l;
++
++	int written = 0;
++	if (acc_fprintf(&written, stream, "%s", open) < 0)
++		return -1;
++
++	size_t i;
++	for (i = 0; i < len && i <= maxlen; ++i) {
++		if (i == maxlen) {
++			if (before && acc_fprintf(&written, stream, "...") < 0)
++				return -1;
++			break;
++		}
++
++		if (i > 0 && acc_fprintf(&written, stream, "%s", delim) < 0)
++			return -1;
++
++		struct value element;
++		if (value_init_element(&element, value, i) < 0)
++			return -1;
++		int o = format_argument(stream, &element, arguments);
++		if (o < 0)
++			return -1;
++		written += o;
++	}
++	if (acc_fprintf(&written, stream, "%s", close) < 0)
++		return -1;
++	if (i == maxlen && !before && acc_fprintf(&written, stream, "...") < 0)
++		return -1;
++
++	return written;
++}
++
++static int
++toplevel_format_lens(struct lens *lens, FILE *stream,
++		     struct value *value, struct value_dict *arguments,
++		     enum int_fmt_t int_fmt)
++{
++	switch (value->type->type) {
++	case ARGTYPE_VOID:
++		return fprintf(stream, "<void>");
++
++	case ARGTYPE_SHORT:
++	case ARGTYPE_INT:
++	case ARGTYPE_LONG:
++		return format_integer(stream, value, int_fmt, arguments);
++
++	case ARGTYPE_USHORT:
++	case ARGTYPE_UINT:
++	case ARGTYPE_ULONG:
++		if (int_fmt == INT_FMT_i)
++			int_fmt = INT_FMT_u;
++		return format_integer(stream, value, int_fmt, arguments);
++
++	case ARGTYPE_CHAR:
++		return format_naked_char(stream, value, arguments);
++
++	case ARGTYPE_FLOAT:
++	case ARGTYPE_DOUBLE:
++		return format_floating(stream, value, arguments);
++
++	case ARGTYPE_STRUCT:
++		return format_struct(stream, value, arguments);
++
++	case ARGTYPE_POINTER:
++		if (value->type->u.array_info.elt_type->type != ARGTYPE_VOID)
++			return format_pointer(stream, value, arguments);
++		return format_integer(stream, value, INT_FMT_x, arguments);
++
++	case ARGTYPE_ARRAY:
++		return format_array(stream, value, arguments,
++				    value->type->u.array_info.length,
++				    options.arraylen, 1, "[ ", " ]", ", ");
++	}
++	abort();
++}
++
++static int
++default_lens_format_cb(struct lens *lens, FILE *stream,
++		       struct value *value, struct value_dict *arguments)
++{
++	return toplevel_format_lens(lens, stream, value, arguments, INT_FMT_i);
++}
++
++struct lens default_lens = {
++	.format_cb = default_lens_format_cb,
++};
++
++
++static int
++blind_lens_format_cb(struct lens *lens, FILE *stream,
++		     struct value *value, struct value_dict *arguments)
++{
++	return 0;
++}
++
++struct lens blind_lens = {
++	.format_cb = blind_lens_format_cb,
++};
++
++
++static int
++octal_lens_format_cb(struct lens *lens, FILE *stream,
++		     struct value *value, struct value_dict *arguments)
++{
++	return toplevel_format_lens(lens, stream, value, arguments, INT_FMT_o);
++}
++
++struct lens octal_lens = {
++	.format_cb = octal_lens_format_cb,
++};
++
++
++static int
++hex_lens_format_cb(struct lens *lens, FILE *stream,
++		   struct value *value, struct value_dict *arguments)
++{
++	return toplevel_format_lens(lens, stream, value, arguments, INT_FMT_x);
++}
++
++struct lens hex_lens = {
++	.format_cb = hex_lens_format_cb,
++};
++
++
++static int
++guess_lens_format_cb(struct lens *lens, FILE *stream,
++		     struct value *value, struct value_dict *arguments)
++{
++	return toplevel_format_lens(lens, stream, value, arguments,
++				    INT_FMT_unknown);
++}
++
++struct lens guess_lens = {
++	.format_cb = guess_lens_format_cb,
++};
++
++
++static int
++bool_lens_format_cb(struct lens *lens, FILE *stream,
++		    struct value *value, struct value_dict *arguments)
++{
++	switch (value->type->type) {
++	case ARGTYPE_VOID:
++	case ARGTYPE_FLOAT:
++	case ARGTYPE_DOUBLE:
++	case ARGTYPE_STRUCT:
++	case ARGTYPE_POINTER:
++	case ARGTYPE_ARRAY:
++		return toplevel_format_lens(lens, stream, value,
++					    arguments, INT_FMT_i);
++
++		int zero;
++	case ARGTYPE_SHORT:
++	case ARGTYPE_INT:
++	case ARGTYPE_LONG:
++	case ARGTYPE_USHORT:
++	case ARGTYPE_UINT:
++	case ARGTYPE_ULONG:
++	case ARGTYPE_CHAR:
++		if ((zero = value_is_zero(value, arguments)) < 0)
++			return -1;
++		if (zero)
++			return fprintf(stream, "false");
++		else
++			return fprintf(stream, "true");
++	}
++	abort();
++}
++
++struct lens bool_lens = {
++	.format_cb = bool_lens_format_cb,
++};
++
++
++static int
++string_lens_format_cb(struct lens *lens, FILE *stream,
++		      struct value *value, struct value_dict *arguments)
++{
++	switch (value->type->type) {
++	case ARGTYPE_POINTER:
++		/* This should really be written as either "string",
++		 * or, if lens, then string(array(char, zero)*).  But
++		 * I suspect people are so used to the char * C idiom,
++		 * that string(char *) might actually turn up.  So
++		 * let's just support it.  */
++		if (value->type->u.ptr_info.info->type == ARGTYPE_CHAR) {
++			struct arg_type_info info[2];
++			type_init_array(&info[1],
++					value->type->u.ptr_info.info, 0,
++					expr_node_zero(), 0);
++			type_init_pointer(&info[0], &info[1], 0);
++			info->lens = lens;
++			info->own_lens = 0;
++			struct value tmp;
++			if (value_clone(&tmp, value) < 0)
++				return -1;
++			value_set_type(&tmp, info, 0);
++			int ret = string_lens_format_cb(lens, stream, &tmp,
++							arguments);
++			type_destroy(&info[0]);
++			type_destroy(&info[1]);
++			value_destroy(&tmp);
++			return ret;
++		}
++
++		/* fall-through */
++	case ARGTYPE_VOID:
++	case ARGTYPE_FLOAT:
++	case ARGTYPE_DOUBLE:
++	case ARGTYPE_STRUCT:
++	case ARGTYPE_SHORT:
++	case ARGTYPE_INT:
++	case ARGTYPE_LONG:
++	case ARGTYPE_USHORT:
++	case ARGTYPE_UINT:
++	case ARGTYPE_ULONG:
++		return toplevel_format_lens(lens, stream, value,
++					    arguments, INT_FMT_i);
++
++	case ARGTYPE_CHAR:
++		return format_char(stream, value, arguments);
++
++	case ARGTYPE_ARRAY:
++		return format_array(stream, value, arguments,
++				    value->type->u.array_info.length,
++				    options.strlen, 0, "\"", "\"", "");
++	}
++	abort();
++}
++
++struct lens string_lens = {
++	.format_cb = string_lens_format_cb,
++};
+diff --git a/lens_default.h b/lens_default.h
+new file mode 100644
+index 0000000..9a9d0c6
+--- /dev/null
++++ b/lens_default.h
+@@ -0,0 +1,49 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2011 Petr Machata, Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ */
++
++#ifndef LENS_DEFAULT_H
++#define LENS_DEFAULT_H
++
++#include "lens.h"
++
++/* Default lens that does reasonable job for most cases.  */
++extern struct lens default_lens;
++
++/* A lens that doesn't output anything.  */
++extern struct lens blind_lens;
++
++/* A lens that formats integers in octal.  */
++extern struct lens octal_lens;
++
++/* A lens that formats integers in hexadecimal.  */
++extern struct lens hex_lens;
++
++/* A lens that formats integers as either "true" or "false".  */
++extern struct lens bool_lens;
++
++/* A lens that tries to guess whether the value is "large" (i.e. a
++ * pointer, and should be formatted in hex), or "small" (and should be
++ * formatted in decimal).  */
++extern struct lens guess_lens;
++
++/* A lens for strings.  */
++extern struct lens string_lens;
++
++#endif /* LENS_DEFAULT_H */
+diff --git a/lens_enum.c b/lens_enum.c
+new file mode 100644
+index 0000000..1af94d2
+--- /dev/null
++++ b/lens_enum.c
+@@ -0,0 +1,163 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2011 Petr Machata, Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ */
++
++#include <stdlib.h>
++#include <assert.h>
++#include <string.h>
++
++#include "arch.h"
++#include "lens_enum.h"
++#include "lens_default.h"
++#include "value.h"
++#include "type.h"
++
++struct enum_entry {
++	char *key;
++	int own_key;
++	struct value *value;
++	int own_value;
++};
++
++static void
++enum_entry_dtor(struct enum_entry *entry, void *data)
++{
++	if (entry->own_key)
++		free(entry->key);
++	if (entry->own_value) {
++		value_destroy(entry->value);
++		free(entry->value);
++	}
++}
++
++static void
++enum_lens_destroy_cb(struct lens *lens)
++{
++	struct enum_lens *self = (void *)lens;
++
++	VECT_DESTROY(&self->entries, struct enum_entry,
++		     enum_entry_dtor, NULL);
++}
++
++enum {
++#ifdef ARCH_ENDIAN_BIG
++	big_endian = 1,
++#elif defined (ARCH_ENDIAN_LITTLE)
++	big_endian = 0,
++#else
++# error Undefined endianness.
++#endif
++};
++
++/* Returns 0 if they are not equal, >0 if they are, and <0 if there
++ * was an error.  */
++static int
++enum_values_equal(struct value *inf_value, struct value *enum_value,
++		  struct value_dict *arguments)
++{
++	/* Width may not match between what's defined in config file
++	 * and what arrives from the back end.  Typically, if there is
++	 * a mismatch, the config file size will be larger, as we are
++	 * in a situation where 64-bit tracer traces 32-bit process.
++	 * But opposite situation can occur e.g. on PPC, where it's
++	 * apparently possible for 32-bit tracer to trace 64-bit
++	 * inferior, or hypothetically in a x32/x86_64 situation.  */
++
++	unsigned char *inf_data = value_get_data(inf_value, arguments);
++	size_t inf_sz = value_size(inf_value, arguments);
++	if (inf_data == NULL || inf_sz == (size_t)-1)
++		return -1;
++
++	assert(inf_value->type->type == enum_value->type->type);
++
++	unsigned char *enum_data = value_get_data(enum_value, arguments);
++	size_t enum_sz = value_size(enum_value, arguments);
++	if (enum_data == NULL || enum_sz == (size_t)-1)
++		return -1;
++
++	size_t sz = enum_sz > inf_sz ? inf_sz : enum_sz;
++
++	if (big_endian)
++		return memcmp(enum_data + enum_sz - sz,
++			      inf_data + inf_sz - sz, sz) == 0;
++	else
++		return memcmp(enum_data, inf_data, sz) == 0;
++}
++
++static const char *
++enum_get(struct enum_lens *lens, struct value *value,
++	 struct value_dict *arguments)
++{
++	size_t i;
++	for (i = 0; i < vect_size(&lens->entries); ++i) {
++		struct enum_entry *entry = VECT_ELEMENT(&lens->entries,
++							struct enum_entry, i);
++		int st = enum_values_equal(value, entry->value, arguments);
++		if (st < 0)
++			return NULL;
++		else if (st != 0)
++			return entry->key;
++	}
++	return NULL;
++}
++
++static int
++enum_lens_format_cb(struct lens *lens, FILE *stream,
++		    struct value *value, struct value_dict *arguments)
++{
++	struct enum_lens *self = (void *)lens;
++
++	long l;
++	if (value_extract_word(value, &l, arguments) < 0)
++		return -1;
++
++	const char *name = enum_get(self, value, arguments);
++	if (name != NULL)
++		return fprintf(stream, "%s", name);
++
++	return lens_format(&default_lens, stream, value, arguments);
++}
++
++
++void
++lens_init_enum(struct enum_lens *lens)
++{
++	*lens = (struct enum_lens){
++		{
++			.format_cb = enum_lens_format_cb,
++			.destroy_cb = enum_lens_destroy_cb,
++		},
++	};
++	VECT_INIT(&lens->entries, struct enum_entry);
++}
++
++int
++lens_enum_add(struct enum_lens *lens,
++	      const char *key, int own_key,
++	      struct value *value, int own_value)
++{
++	struct enum_entry entry = { (char *)key, own_key, value, own_value };
++	return VECT_PUSHBACK(&lens->entries, &entry);
++}
++
++size_t
++lens_enum_size(struct enum_lens *lens)
++{
++	return vect_size(&lens->entries);
++}
+diff --git a/lens_enum.h b/lens_enum.h
+new file mode 100644
+index 0000000..de7b386
+--- /dev/null
++++ b/lens_enum.h
+@@ -0,0 +1,48 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2011 Petr Machata, Red Hat Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ */
++
++#ifndef LENS_ENUM_H
++#define LENS_ENUM_H
++
++#include "lens.h"
++#include "vect.h"
++
++struct enum_lens {
++	struct lens super;
++	struct vect entries;
++	int own;
++};
++
++/* Init enumeration LENS.  */
++void lens_init_enum(struct enum_lens *lens);
++
++/* Push another member of the enumeration, named KEY, with given
++ * VALUE.  If OWN_KEY, KEY is owned and released after the type is
++ * destroyed.  KEY is typed as const char *, but note that if OWN_KEY,
++ * this value will be freed.  If OWN_VALUE, then VALUE is owned and
++ * destroyed by LENS.  */
++int lens_enum_add(struct enum_lens *lens,
++		  const char *key, int own_key,
++		  struct value *value, int own_value);
++
++/* Return number of enum elements of type INFO.  */
++size_t lens_enum_size(struct enum_lens *lens);
++
++#endif /* LENS_ENUM_H */
+diff --git a/libltrace.c b/libltrace.c
+index 92f2701..f4bb2c8 100644
+--- a/libltrace.c
++++ b/libltrace.c
+@@ -1,3 +1,24 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2011 Petr Machata, Red Hat Inc.
++ * Copyright (C) 2009 Juan Cespedes
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ */
++
+ #include "config.h"
+ 
+ #include <sys/param.h>
+@@ -11,6 +32,8 @@
+ 
+ #include "common.h"
+ #include "proc.h"
++#include "read_config_file.h"
++#include "backend.h"
+ 
+ char *command = NULL;
+ 
+@@ -84,6 +107,7 @@ ltrace_init(int argc, char **argv) {
+ 	signal(SIGTERM, signal_exit);	/*  ... or killed */
+ 
+ 	argv = process_options(argc, argv);
++	init_global_config();
+ 	while (opt_F) {
+ 		/* If filename begins with ~, expand it to the user's home */
+ 		/* directory. This does not correctly handle ~yoda, but that */
+diff --git a/library.c b/library.c
+index 92fccea..2bd7dbb 100644
+--- a/library.c
++++ b/library.c
+@@ -27,7 +27,7 @@
+ #include "library.h"
+ #include "proc.h" // for enum callback_status
+ #include "debug.h"
+-#include "common.h" // for arch_library_symbol_init, arch_library_init
++#include "backend.h" // for arch_library_symbol_init, arch_library_init
+ 
+ #ifndef ARCH_HAVE_LIBRARY_DATA
+ void
+@@ -207,10 +207,18 @@ static void
+ private_library_init(struct library *lib, enum library_type type)
+ {
+ 	lib->next = NULL;
++
++	lib->key = 0;
++	lib->base = 0;
++	lib->entry = 0;
++	lib->dyn_addr = 0;
++
+ 	lib->soname = NULL;
+ 	lib->own_soname = 0;
++
+ 	lib->pathname = NULL;
+ 	lib->own_pathname = 0;
++
+ 	lib->symbols = NULL;
+ 	lib->type = type;
+ }
+diff --git a/ltrace-elf.c b/ltrace-elf.c
+index b1af070..c40a188 100644
+--- a/ltrace-elf.c
++++ b/ltrace-elf.c
+@@ -1,3 +1,30 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2006,2010,2011,2012 Petr Machata, Red Hat Inc.
++ * Copyright (C) 2010 Zachary T Welch, CodeSourcery
++ * Copyright (C) 2010 Joe Damato
++ * Copyright (C) 1997,1998,2001,2004,2007,2008,2009 Juan Cespedes
++ * Copyright (C) 2006 Olaf Hering, SUSE Linux GmbH
++ * Copyright (C) 2006 Eric Vaitl, Cisco Systems, Inc.
++ * Copyright (C) 2006 Paul Gilliam, IBM Corporation
++ * Copyright (C) 2006 Ian Wienand
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ */
++
+ #include "config.h"
+ 
+ #include <assert.h>
+@@ -13,10 +40,13 @@
+ #include <string.h>
+ #include <unistd.h>
+ 
+-#include "common.h"
+-#include "proc.h"
+-#include "library.h"
++#include "backend.h"
+ #include "filter.h"
++#include "library.h"
++#include "ltrace-elf.h"
++#include "proc.h"
++#include "debug.h"
++#include "options.h"
+ 
+ #ifdef PLT_REINITALISATION_BP
+ extern char *PLTs_initialized_by_here;
+@@ -241,8 +271,11 @@ open_elf(struct ltelf *lte, const char *filename)
+ 		exit(EXIT_FAILURE);
+ 	}
+ 
+-	if ((lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS
+-	     || lte->ehdr.e_machine != LT_ELF_MACHINE)
++	if (1
++#ifdef LT_ELF_MACHINE
++	    && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS
++		|| lte->ehdr.e_machine != LT_ELF_MACHINE)
++#endif
+ #ifdef LT_ELF_MACHINE2
+ 	    && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS2
+ 		|| lte->ehdr.e_machine != LT_ELF_MACHINE2)
+diff --git a/output.c b/output.c
+index db6e93e..8bfe3f0 100644
+--- a/output.c
++++ b/output.c
+@@ -1,3 +1,27 @@
++/*
++ * This file is part of ltrace.
++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
++ * Copyright (C) 2010 Joe Damato
++ * Copyright (C) 1997,1998,1999,2001,2002,2003,2004,2007,2008,2009 Juan Cespedes
++ * Copyright (C) 2006 Paul Gilliam
++ * Copyright (C) 2006 Ian Wienand
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ */
++
+ #include "config.h"
+ 
+ #include <stdio.h>
+@@ -7,10 +31,18 @@
+ #include <time.h>
+ #include <sys/time.h>
+ #include <unistd.h>
++#include <errno.h>
++#include <assert.h>
+ 
+ #include "common.h"
+ #include "proc.h"
+ #include "library.h"
++#include "type.h"
++#include "value.h"
++#include "value_dict.h"
++#include "param.h"
++#include "fetch.h"
++#include "lens_default.h"
+ 
+ /* TODO FIXME XXX: include in common.h: */
+ extern struct timeval current_time_spent;
+@@ -18,7 +50,7 @@ extern struct timeval current_time_spent;
+ Dict *dict_opt_c = NULL;
+ 
+ static Process *current_proc = 0;
+-static int current_depth = 0;
++static size_t current_depth = 0;
+ static int current_column = 0;
+ 
+ static void
+@@ -29,7 +61,8 @@ output_indent(struct Process *proc)
+ }
+ 
+ static void
+-begin_of_line(enum tof type, Process *proc) {
++begin_of_line(Process *proc, int is_func, int indent)
++{
+ 	current_column = 0;
+ 	if (!proc) {
+ 		return;
+@@ -85,40 +118,89 @@ begin_of_line(enum tof type, Process *proc) {
+ 		}
+ 	}
+ 	if (opt_i) {
+-		if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) {
++		if (is_func)
+ 			current_column += fprintf(options.output, "[%p] ",
+ 						  proc->return_addr);
+-		} else {
++		else
+ 			current_column += fprintf(options.output, "[%p] ",
+ 						  proc->instruction_pointer);
+-		}
+ 	}
+-	if (options.indent > 0 && type != LT_TOF_NONE) {
++	if (options.indent > 0 && indent) {
+ 		output_indent(proc);
+ 	}
+ }
+ 
++static struct arg_type_info *
++get_unknown_type(void)
++{
++	static struct arg_type_info *info = NULL;
++	if (info == NULL) {
++		info = malloc(sizeof(*info));
++		if (info == NULL) {
++			report_global_error("malloc: %s", strerror(errno));
++			abort();
++		}
++		*info = *type_get_simple(ARGTYPE_LONG);
++		info->lens = &guess_lens;
++	}
++	return info;
++}
++
++/* The default prototype is: long X(long, long, long, long).  */
++static Function *
++build_default_prototype(void)
++{
++	Function *ret = malloc(sizeof(*ret));
++	size_t i = 0;
++	if (ret == NULL)
++		goto err;
++	memset(ret, 0, sizeof(*ret));
++
++	struct arg_type_info *unknown_type = get_unknown_type();
++
++	ret->return_info = unknown_type;
++	ret->own_return_info = 0;
++
++	ret->num_params = 4;
++	ret->params = malloc(sizeof(*ret->params) * ret->num_params);
++	if (ret->params == NULL)
++		goto err;
++
++	for (i = 0; i < ret->num_params; ++i)
++		param_init_type(&ret->params[i], unknown_type, 0);
++
++	return ret;
++
++err:
++	report_global_error("malloc: %s", strerror(errno));
++	if (ret->params != NULL) {
++		while (i-- > 0)
++			param_destroy(&ret->params[i]);
++		free(ret->params);
++	}
++
++	free(ret);
++
++	return NULL;
++}
++
+ static Function *
+ name2func(char const *name) {
+ 	Function *tmp;
+ 	const char *str1, *str2;
+ 
+-	tmp = list_of_functions;
+-	while (tmp) {
+-#ifdef USE_DEMANGLE
+-		str1 = options.demangle ? my_demangle(tmp->name) : tmp->name;
+-		str2 = options.demangle ? my_demangle(name) : name;
+-#else
++	for (tmp = list_of_functions; tmp != NULL; tmp = tmp->next) {
+ 		str1 = tmp->name;
+ 		str2 = name;
+-#endif
+-		if (!strcmp(str1, str2)) {
+-
++		if (!strcmp(str1, str2))
+ 			return tmp;
+-		}
+-		tmp = tmp->next;
+ 	}
+-	return NULL;
++
++	static Function *def = NULL;
++	if (def == NULL)
++		def = build_default_prototype();
++
++	return def;
+ }
+ 
+ void
+@@ -139,7 +221,7 @@ output_line(Process *proc, char *fmt, ...) {
+ 	if (!fmt) {
+ 		return;
+ 	}
+-	begin_of_line(LT_TOF_NONE, proc);
++	begin_of_line(proc, 0, 0);
+ 
+ 	va_start(args, fmt);
+ 	vfprintf(options.output, fmt, args);
+@@ -155,15 +237,191 @@ tabto(int col) {
+ 	}
+ }
+ 
++static int
++account_output(int o)
++{
++	if (o < 0)
++		return -1;
++	current_column += o;
++	return 0;
++}
++
++static int
++output_error(void)
++{
++	return account_output(fprintf(options.output, "?"));
++}
++
++static int
++fetch_simple_param(enum tof type, Process *proc, struct fetch_context *context,
++		   struct value_dict *arguments, struct arg_type_info *info,
++		   struct value *valuep)
++{
++	/* Arrays decay into pointers per C standard.  We check for
++	 * this here, because here we also capture arrays that come
++	 * from parameter packs.  */
++	int own = 0;
++	if (info->type == ARGTYPE_ARRAY) {
++		struct arg_type_info *tmp = malloc(sizeof(*tmp));
++		if (tmp != NULL) {
++			type_init_pointer(tmp, info, 0);
++			tmp->lens = info->lens;
++			info = tmp;
++			own = 1;
++		}
++	}
++
++	struct value value;
++	value_init(&value, proc, NULL, info, own);
++	if (fetch_arg_next(context, type, proc, info, &value) < 0)
++		return -1;
++
++	if (val_dict_push_next(arguments, &value) < 0) {
++		value_destroy(&value);
++		return -1;
++	}
++
++	if (valuep != NULL)
++		*valuep = value;
++
++	return 0;
++}
++
++static void
++fetch_param_stop(struct value_dict *arguments, ssize_t *params_leftp)
++{
++	if (*params_leftp == -1)
++		*params_leftp = val_dict_count(arguments);
++}
++
++static int
++fetch_param_pack(enum tof type, Process *proc, struct fetch_context *context,
++		 struct value_dict *arguments, struct param *param,
++		 ssize_t *params_leftp)
++{
++	struct param_enum *e = param_pack_init(param, arguments);
++	if (e == NULL)
++		return -1;
++
++	int ret = 0;
++	while (1) {
++		int insert_stop = 0;
++		struct arg_type_info *info = malloc(sizeof(*info));
++		if (info == NULL
++		    || param_pack_next(param, e, info, &insert_stop) < 0) {
++		fail:
++			free(info);
++			ret = -1;
++			break;
++		}
++
++		if (insert_stop)
++			fetch_param_stop(arguments, params_leftp);
++
++		if (info->type == ARGTYPE_VOID)
++			break;
++
++		struct value val;
++		if (fetch_simple_param(type, proc, context, arguments,
++				       info, &val) < 0)
++			goto fail;
++
++		int stop = 0;
++		switch (param_pack_stop(param, e, &val)) {
++		case PPCB_ERR:
++			goto fail;
++		case PPCB_STOP:
++			stop = 1;
++		case PPCB_CONT:
++			break;
++		}
++
++		if (stop)
++			break;
++	}
++
++	param_pack_done(param, e);
++	return ret;
++}
++
++static int
++fetch_one_param(enum tof type, Process *proc, struct fetch_context *context,
++		struct value_dict *arguments, struct param *param,
++		ssize_t *params_leftp)
++{
++	switch (param->flavor) {
++	case PARAM_FLAVOR_TYPE:
++		return fetch_simple_param(type, proc, context, arguments,
++					  param->u.type.type, NULL);
++
++	case PARAM_FLAVOR_PACK:
++		return fetch_param_pack(type, proc, context, arguments,
++					param, params_leftp);
++
++	case PARAM_FLAVOR_STOP:
++		fetch_param_stop(arguments, params_leftp);
++		return 0;
++	}
++
++	assert(!"Invalid param flavor!");
++	abort();
++}
++
++static int
++fetch_params(enum tof type, Process *proc, struct fetch_context *context,
++	     struct value_dict *arguments, Function *func, ssize_t *params_leftp)
++{
++	size_t i;
++	for (i = 0; i < func->num_params; ++i)
++		if (fetch_one_param(type, proc, context, arguments,
++				    &func->params[i], params_leftp) < 0)
++			return -1;
++
++	/* Implicit stop at the end of parameter list.  */
++	fetch_param_stop(arguments, params_leftp);
++
++	return 0;
++}
++
++static int
++output_one(struct value *val, struct value_dict *arguments)
++{
++	int o = format_argument(options.output, val, arguments);
++	if (account_output(o) < 0) {
++		if (output_error() < 0)
++			return -1;
++		o = 1;
++	}
++	return o;
++}
++
++static int
++output_params(struct value_dict *arguments, size_t start, size_t end,
++	      int *need_delimp)
++{
++	size_t i;
++	int need_delim = *need_delimp;
++	for (i = start; i < end; ++i) {
++		if (need_delim
++		    && account_output(fprintf(options.output, ", ")) < 0)
++			return -1;
++		struct value *value = val_dict_get_num(arguments, i);
++		if (value == NULL)
++			return -1;
++		need_delim = output_one(value, arguments);
++		if (need_delim < 0)
++			return -1;
++	}
++	*need_delimp = need_delim;
++	return 0;
++}
++
+ void
+ output_left(enum tof type, struct Process *proc,
+ 	    struct library_symbol *libsym)
+ {
+ 	const char *function_name = libsym->name;
+ 	Function *func;
+-	static arg_type_info *arg_unknown = NULL;
+-	if (arg_unknown == NULL)
+-	    arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN);
+ 
+ 	if (options.summary) {
+ 		return;
+@@ -174,50 +432,47 @@ output_left(enum tof type, struct Process *proc,
+ 	}
+ 	current_proc = proc;
+ 	current_depth = proc->callstack_depth;
+-	begin_of_line(type, proc);
++	begin_of_line(proc, type == LT_TOF_FUNCTION, 1);
+ 	if (!options.hide_caller && libsym->lib != NULL
+ 	    && libsym->plt_type != LS_TOPLT_NONE)
+ 		current_column += fprintf(options.output, "%s->",
+ 					  libsym->lib->soname);
++
++	const char *name = function_name;
+ #ifdef USE_DEMANGLE
+-	current_column +=
+-		fprintf(options.output, "%s(",
+-			(options.demangle
+-			 ? my_demangle(function_name) : function_name));
+-#else
+-	current_column += fprintf(options.output, "%s(", function_name);
++	if (options.demangle)
++		name = my_demangle(function_name);
+ #endif
++	if (account_output(fprintf(options.output, "%s(", name)) < 0)
++		return;
+ 
+ 	func = name2func(function_name);
+-	if (!func) {
+-		int i;
+-		for (i = 0; i < 4; i++) {
+-			current_column +=
+-			    display_arg(type, proc, i, arg_unknown);
+-			current_column += fprintf(options.output, ", ");
+-		}
+-		current_column += display_arg(type, proc, 4, arg_unknown);
++	if (func == NULL)
+ 		return;
+-	} else {
+-		int i;
+-		for (i = 0; i < func->num_params - func->params_right - 1; i++) {
+-			current_column +=
+-			    display_arg(type, proc, i, func->arg_info[i]);
+-			current_column += fprintf(options.output, ", ");
+-		}
+-		if (func->num_params > func->params_right) {
+-			current_column +=
+-			    display_arg(type, proc, i, func->arg_info[i]);
+-			if (func->params_right) {
+-				current_column += fprintf(options.output, ", ");
+-			}
+-		}
+-		if (func->params_right
+-		    || func->return_info->type == ARGTYPE_STRING_N
+-		    || func->return_info->type == ARGTYPE_ARRAY) {
+-			save_register_args(type, proc);
+-		}
++
++	struct fetch_context *context = fetch_arg_init(type, proc,
++						       func->return_info);
++	struct value_dict *arguments = malloc(sizeof(*arguments));
++	if (arguments == NULL)
++		return;
++	val_dict_init(arguments);
++
++	ssize_t params_left = -1;
++	int need_delim = 0;
++	if (fetch_params(type, proc, context, arguments, func, &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 */
diff --git a/ltrace.spec b/ltrace.spec
index 67fd381..ac08012 100644
--- a/ltrace.spec
+++ b/ltrace.spec
@@ -1,7 +1,7 @@
 Summary: Tracks runtime library calls from dynamically linked executables
 Name: ltrace
 Version: 0.6.0
-Release: 13%{?dist}
+Release: 14%{?dist}
 URL: http://ltrace.alioth.debian.org/
 License: GPLv2+
 Group: Development/Debuggers
@@ -33,6 +33,7 @@ Patch17: ltrace-0.6.0-ppc-lwarx.patch
 Patch18: ltrace-0.6.0-libs.patch
 Patch19: ltrace-0.6.0-libs-fixes-1.patch
 Patch20: ltrace-0.6.0-dash-n.patch
+Patch21: ltrace-0.6.0-abi.patch
 
 %description
 Ltrace is a debugging program which runs a specified command until the
@@ -64,13 +65,13 @@ execution of processes.
 %patch18 -p1
 %patch19 -p1
 %patch20 -p1
-sed -i -e 's/-o root -g root//' Makefile.in
+%patch21 -p1
 
 %build
 # This ugly hack is necessary to build and link files for correct
 # architecture.  It makes a difference on ppc.
 export CC="gcc`echo $RPM_OPT_FLAGS | sed -n 's/^.*\(-m[36][124]\).*$/ \1/p'` -D_LARGEFILE64_SOURCE"
-# We touch configure.ac up there
+autoreconf -i
 %configure CC="$CC"
 make %{?_smp_mflags}
 
@@ -92,6 +93,10 @@ echo ====================TESTING END=====================
 %config(noreplace) %{_sysconfdir}/ltrace.conf
 
 %changelog
+* Fri May 18 2012 Petr Machata <pmachata at redhat.com> - 0.6.0-14
+- Add upstream patch that improves parameter passing support (the
+  upstream "revamp" branch) (ltrace-0.6.0-abi.patch)
+
 * Thu May  3 2012 Petr Machata <pmachata at redhat.com> - 0.6.0-13
 - Check -n argument for validity (ltrace-0.6.0-dash-n.patch)
 - Resolves: #818529


More information about the scm-commits mailing list