diff --git a/Makefile.am b/Makefile.am
index a00c8bf..dc9091d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -15,7 +15,6 @@ libltrace_la_SOURCES = \
debug.c \
demangle.c \
dict.c \
- display_args.c \
ltrace-elf.c \
execute_program.c \
handle_event.c \
@@ -27,7 +26,19 @@ libltrace_la_SOURCES = \
summary.c \
library.c \
filter.c \
- glob.c
+ glob.c \
+ type.c \
+ value.c \
+ value_dict.c \
+ expr.c \
+ fetch.c \
+ vect.c \
+ param.c \
+ printf.c \
+ zero.c \
+ lens.c \
+ lens_default.c \
+ lens_enum.c
libltrace_la_LIBADD = \
$(libelf_LIBS) \
@@ -50,6 +61,7 @@ ltrace_LDADD = \
noinst_HEADERS = \
+ backend.h \
common.h \
debug.h \
defs.h \
@@ -62,7 +74,20 @@ noinst_HEADERS = \
read_config_file.h \
library.h \
filter.h \
- glob.h
+ glob.h \
+ vect.h \
+ type.h \
+ value.h \
+ value_dict.h \
+ expr.h \
+ fetch.h \
+ vect.h \
+ param.h \
+ printf.h \
+ zero.h \
+ lens.h \
+ lens_default.h \
+ lens_enum.h
dist_man1_MANS = \
ltrace.1
diff --git a/backend.h b/backend.h
new file mode 100644
index 0000000..08306e1
--- /dev/null
+++ b/backend.h
@@ -0,0 +1,250 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef BACKEND_H
+#define BACKEND_H
+
+#include "forward.h"
+#include <gelf.h>
+
+enum process_status {
+ ps_invalid, /* Failure. */
+ ps_stop, /* Job-control stop. */
+ ps_tracing_stop,
+ ps_sleeping,
+ ps_zombie,
+ ps_other, /* Necessary other states can be added as needed. */
+};
+
+typedef void *target_address_t;
+
+/*
+ * This file contains documentation of back end interface. Some of
+ * these may be implemented on an OS level (i.e. they are the same
+ * e.g. on all Linux architectures), some may differ per architecture
+ * on the same OS (e.g. a way to insert a breakpoint into the process
+ * image is a likely candidate).
+ */
+
+/* Convert a PID to a path to the corresponding binary. */
+char *pid2name(pid_t pid);
+
+/* Given a PID, find a leader of thread group. */
+pid_t process_leader(pid_t pid);
+
+/* Given a PID of leader thread, fill in PIDs of all the tasks. The
+ * function will initialize the pointer *RET_TASKS to a
+ * newly-allocated array, and will store number of elements in that
+ * array to *RET_N. You have to free that buffer when you don't need
+ * it anymore. */
+int process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n);
+
+/* Answer whether the process PID is stopped. Returns 0 when not
+ * stopped, 1 when stopped, or -1 when there was an error. */
+int process_stopped(pid_t pid);
+
+/* Answer a status of the task PID. See enum process_status. */
+enum process_status process_status(pid_t pid);
+
+/* Wait for PID to be ready for tracing. */
+int wait_for_proc(pid_t pid);
+
+/* Send a signal SIG to the task PID. */
+int task_kill(pid_t pid, int sig);
+
+/* Called after PID is attached, but before it is continued. */
+void trace_set_options(struct Process *proc);
+
+/* Called after ltrace forks. Should attach the newly created child,
+ * in whose context this function is called. */
+void trace_me(void);
+
+/* Called when ltrace needs to attach to PID, such as when it attaches
+ * to a running process, whose PID is given on the command line. */
+int trace_pid(pid_t pid);
+
+/* Stop tracing PID. */
+void untrace_pid(pid_t pid);
+
+/* The back end may need to store arbitrary data to a process. This
+ * is a place where it can initialize PROC->arch_dep. XXX this should
+ * be dropped in favor of arhc_process_init on pmachata/libs. */
+void get_arch_dep(struct Process *proc);
+
+/* Return current instruction pointer of PROC.
+ *
+ * XXX note that the IP must fit into an arch pointer. This prevents
+ * us to use 32-bit ltrace to trace 64-bit process, even on arches
+ * that would otherwise support this. Above we have a definition of
+ * target_address_t. This should be converted to an integral type and
+ * used for target addresses throughout. */
+void *get_instruction_pointer(struct Process *proc);
+
+/* Set instruction pointer of PROC to ADDR. XXX see above. */
+void set_instruction_pointer(struct Process *proc, void *addr);
+
+/* Return current stack pointer of PROC. XXX see above. */
+void *get_stack_pointer(struct Process *proc);
+
+/* Find and return caller address, i.e. the address where the current
+ * function returns. */
+void *get_return_addr(struct Process *proc, void *stack_pointer);
+
+/* Adjust PROC so that when the current function returns, it returns
+ * to ADDR. */
+void set_return_addr(struct Process *proc, void *addr);
+
+/* Enable breakpoint SBP in process PROC. */
+void enable_breakpoint(struct Process *proc, struct breakpoint *sbp);
+
+/* Disable breakpoint SBP in process PROC. */
+void disable_breakpoint(struct Process *proc, struct breakpoint *sbp);
+
+/* Determine whether the event that we have just seen (and that is
+ * recorded in STATUS) was a syscall. If it was, return 1. If it was
+ * a return from syscall, return 2. In both cases, set *SYSNUM to the
+ * number of said syscall. If it wasn't a syscall, return 0. If
+ * there was an error, return -1. */
+int syscall_p(struct Process *proc, int status, int *sysnum);
+
+/* Continue execution of the process with given PID. */
+void continue_process(pid_t pid);
+
+/* Called after we received a signal SIGNUM. Should do whatever
+ * book-keeping is necessary and continue the process if
+ * necessary. */
+void continue_after_signal(pid_t pid, int signum);
+
+/* Called after we received a system call SYSNUM. RET_P is 0 if this
+ * is system call, otherwise it's return from a system call. The
+ * callback should do whatever book-keeping is necessary and continue
+ * the process if necessary. */
+void continue_after_syscall(struct Process *proc, int sysnum, int ret_p);
+
+/* Called after we hit a breakpoint SBP. Should do whatever
+ * book-keeping is necessary and then continue the process. */
+void continue_after_breakpoint(struct Process *proc, struct breakpoint *sbp);
+
+/* Called after we received a vfork. Should do whatever book-keeping
+ * is necessary and continue the process if necessary. N.B. right
+ * now, with Linux/GNU the only back end, this is not necessary. I
+ * imagine other systems may be different. */
+void continue_after_vfork(struct Process *proc);
+
+/* Called when trace_me or primary trace_pid fail. This may plug in
+ * any platform-specific knowledge of why it could be so. */
+void trace_fail_warning(pid_t pid);
+
+/* A pair of functions called to initiate a detachment request when
+ * ltrace is about to exit. Their job is to undo any effects that
+ * tracing had and eventually detach process, perhaps by way of
+ * installing a process handler.
+ *
+ * OS_LTRACE_EXITING_SIGHANDLER is called from a signal handler
+ * context right after the signal was captured. It returns 1 if the
+ * request was handled or 0 if it wasn't.
+ *
+ * If the call to OS_LTRACE_EXITING_SIGHANDLER didn't handle the
+ * request, OS_LTRACE_EXITING is called when the next event is
+ * generated. Therefore it's called in "safe" context, without
+ * re-entrancy concerns, but it's only called after an even is
+ * generated. */
+int os_ltrace_exiting_sighandler(void);
+void os_ltrace_exiting(void);
+
+/* Should copy COUNT bytes from address ADDR of process PROC to local
+ * buffer BUF. */
+size_t umovebytes (struct Process *proc, void *addr, void *buf, size_t count);
+
+/* Find out an address of symbol SYM in process PROC, and return.
+ * Returning NULL delays breakpoint insertion and enables heaps of
+ * arch-specific black magic that we should clean up some day.
+ *
+ * XXX the same points as for get_instruction_pointer apply. */
+void *sym2addr(struct Process *proc, struct library_symbol *sym);
+
+/* Called at some point after we have attached to PROC. This callback
+ * should insert an introspection breakpoint for handling dynamic
+ * linker library loads. */
+int linkmap_init(struct Process *proc, target_address_t dyn_addr);
+
+/* Called for breakpoints defined over an artificial symbol "". This
+ * can be used (like it is on Linux/GNU) to add more breakpoints
+ * because a dlopen'ed library was mapped in.
+ *
+ * XXX we should somehow clean up this interface. For starters,
+ * breakpoints should have their own handler callbacks, so that we can
+ * generalize this to e.g. systemtap SDT probes. linkmap_init could
+ * perhaps be rolled into some other process init callback. */
+void arch_check_dbg(struct Process *proc);
+
+/* This should produce and return the next event of one of the traced
+ * processes. The returned pointer will not be freed by the core and
+ * should be either statically allocated, or the management should be
+ * done some other way. */
+struct Event *next_event(void);
+
+/* Called when process PROC was removed. */
+void process_removed(struct Process *proc);
+
+int arch_elf_init(struct ltelf *lte, struct library *lib);
+void arch_elf_destroy(struct ltelf *lte);
+
+enum plt_status {
+ plt_fail,
+ plt_ok,
+ plt_default,
+};
+
+enum plt_status arch_elf_add_plt_entry(struct Process *p, struct ltelf *l,
+ const char *n, GElf_Rela *r, size_t i,
+ struct library_symbol **ret);
+
+int arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp);
+void arch_breakpoint_destroy(struct breakpoint *sbp);
+int arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp);
+
+void arch_library_init(struct library *lib);
+void arch_library_destroy(struct library *lib);
+void arch_library_clone(struct library *retp, struct library *lib);
+
+int arch_library_symbol_init(struct library_symbol *libsym);
+void arch_library_symbol_destroy(struct library_symbol *libsym);
+int arch_library_symbol_clone(struct library_symbol *retp,
+ struct library_symbol *libsym);
+
+int arch_process_init(struct Process *proc);
+void arch_process_destroy(struct Process *proc);
+int arch_process_clone(struct Process *retp, struct Process *proc);
+int arch_process_exec(struct Process *proc);
+
+/* This should extract entry point address and interpreter (dynamic
+ * linker) bias if possible. Returns 0 if there were no errors, -1
+ * otherwise. Sets *ENTRYP and *INTERP_BIASP to non-zero values if
+ * the corresponding value is known. Unknown values are set to 0. */
+int process_get_entry(struct Process *proc,
+ target_address_t *entryp,
+ target_address_t *interp_biasp);
+
+/* This is called after the dynamic linker is done with the
+ * process startup. */
+void arch_dynlink_done(struct Process *proc);
+
+#endif /* BACKEND_H */
diff --git a/breakpoints.c b/breakpoints.c
index 93ae244..8dc09df 100644
--- a/breakpoints.c
+++ b/breakpoints.c
@@ -1,18 +1,21 @@
#include "config.h"
-#include <stdlib.h>
-#include <string.h>
#include <assert.h>
#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#ifdef __powerpc__
#include <sys/ptrace.h>
#endif
+#include "backend.h"
#include "breakpoint.h"
-#include "common.h"
-#include "proc.h"
+#include "debug.h"
#include "library.h"
+#include "ltrace-elf.h"
+#include "proc.h"
#ifndef ARCH_HAVE_TRANSLATE_ADDRESS
int
diff --git a/common.h b/common.h
index 7fffa76..959715a 100644
--- a/common.h
+++ b/common.h
@@ -1,3 +1,27 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2010 Joe Damato
+ * Copyright (C) 2009 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
#include <config.h>
#include <sys/types.h>
@@ -15,6 +37,7 @@
#include "ltrace-elf.h"
#include "read_config_file.h"
#include "proc.h"
+#include "forward.h"
#if defined HAVE_LIBIBERTY || defined HAVE_LIBSUPC__
# define USE_DEMANGLE
@@ -24,93 +47,13 @@ extern char * command;
extern int exiting; /* =1 if we have to exit ASAP */
-enum arg_type {
- ARGTYPE_UNKNOWN = -1,
- ARGTYPE_VOID,
- ARGTYPE_INT,
- ARGTYPE_UINT,
- ARGTYPE_LONG,
- ARGTYPE_ULONG,
- ARGTYPE_OCTAL,
- ARGTYPE_CHAR,
- ARGTYPE_SHORT,
- ARGTYPE_USHORT,
- ARGTYPE_FLOAT, /* float value, may require index */
- ARGTYPE_DOUBLE, /* double value, may require index */
- ARGTYPE_ADDR,
- ARGTYPE_FILE,
- ARGTYPE_FORMAT, /* printf-like format */
- ARGTYPE_STRING, /* NUL-terminated string */
- ARGTYPE_STRING_N, /* String of known maxlen */
- ARGTYPE_ARRAY, /* Series of values in memory */
- ARGTYPE_ENUM, /* Enumeration */
- ARGTYPE_STRUCT, /* Structure of values */
- ARGTYPE_POINTER, /* Pointer to some other type */
- ARGTYPE_COUNT /* number of ARGTYPE_* values */
-};
-
-typedef struct arg_type_info_t {
- enum arg_type type;
- union {
- /* ARGTYPE_ENUM */
- struct {
- size_t entries;
- char ** keys;
- int * values;
- } enum_info;
-
- /* ARGTYPE_ARRAY */
- struct {
- struct arg_type_info_t * elt_type;
- size_t elt_size;
- int len_spec;
- } array_info;
-
- /* ARGTYPE_STRING_N */
- struct {
- int size_spec;
- } string_n_info;
-
- /* ARGTYPE_STRUCT */
- struct {
- struct arg_type_info_t ** fields; /* NULL-terminated */
- size_t * offset;
- size_t size;
- } struct_info;
-
- /* ARGTYPE_POINTER */
- struct {
- struct arg_type_info_t * info;
- } ptr_info;
-
- /* ARGTYPE_FLOAT */
- struct {
- size_t float_index;
- } float_info;
-
- /* ARGTYPE_DOUBLE */
- struct {
- size_t float_index;
- } double_info;
- } u;
-} arg_type_info;
-
-enum tof {
- LT_TOF_NONE = 0,
- LT_TOF_FUNCTION, /* A real library function */
- LT_TOF_FUNCTIONR, /* Return from a real library function */
- LT_TOF_SYSCALL, /* A syscall */
- LT_TOF_SYSCALLR, /* Return from a syscall */
- LT_TOF_STRUCT /* Not a function; read args from struct */
-};
-
typedef struct Function Function;
struct Function {
const char * name;
- arg_type_info * return_info;
- int num_params;
- arg_type_info * arg_info[MAX_ARGS];
- int params_right;
+ struct param *params;
+ struct arg_type_info *return_info;
+ int own_return_info;
+ size_t num_params;
Function * next;
};
@@ -130,136 +72,22 @@ struct opt_c_struct {
extern Dict * dict_opt_c;
-enum process_status {
- ps_invalid, /* Failure. */
- ps_stop, /* Job-control stop. */
- ps_tracing_stop,
- ps_sleeping,
- ps_zombie,
- ps_other, /* Necessary other states can be added as needed. */
-};
-
/* Events */
-enum ecb_status {
- ecb_cont, /* The iteration should continue. */
- ecb_yield, /* The iteration should stop, yielding this
- * event. */
- ecb_deque, /* Like ecb_stop, but the event should be removed
- * from the queue. */
-};
extern Event * next_event(void);
-extern Event * each_qd_event(enum ecb_status (* cb)(Event * event, void * data),
- void * data);
-extern void enque_event(Event * event);
extern void handle_event(Event * event);
extern pid_t execute_program(const char * command, char ** argv);
-extern int display_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info);
-extern void disable_all_breakpoints(Process * proc);
extern void show_summary(void);
-extern arg_type_info * lookup_prototype(enum arg_type at);
struct breakpoint;
struct library_symbol;
-/* Arch-dependent stuff: */
-extern char * pid2name(pid_t pid);
-extern pid_t process_leader(pid_t pid);
-extern int process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n);
-extern int process_stopped(pid_t pid);
-extern enum process_status process_status(pid_t pid);
-extern void trace_set_options(struct Process *proc);
-extern int wait_for_proc(pid_t pid);
-extern void trace_me(void);
-extern int trace_pid(pid_t pid);
-extern void untrace_pid(pid_t pid);
-extern void get_arch_dep(Process * proc);
-extern void * get_instruction_pointer(Process * proc);
-extern void set_instruction_pointer(Process * proc, void * addr);
-extern void * get_stack_pointer(Process * proc);
-extern void * get_return_addr(Process * proc, void * stack_pointer);
-extern void set_return_addr(Process * proc, void * addr);
-extern void enable_breakpoint(struct Process *proc, struct breakpoint *sbp);
-extern void disable_breakpoint(struct Process *proc, struct breakpoint *sbp);
-extern int syscall_p(Process * proc, int status, int * sysnum);
-extern void continue_process(pid_t pid);
-extern void continue_after_signal(pid_t pid, int signum);
-extern void continue_after_syscall(Process *proc, int sysnum, int ret_p);
-extern void continue_after_breakpoint(struct Process *proc, struct breakpoint *sbp);
-extern void continue_after_vfork(Process * proc);
-extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info);
-extern void save_register_args(enum tof type, Process * proc);
-extern int umovestr(Process * proc, void * addr, int len, void * laddr);
-extern int umovelong (Process * proc, void * addr, long * result, arg_type_info * info);
-extern size_t umovebytes (Process *proc, void * addr, void * laddr, size_t count);
-extern int ffcheck(void * maddr);
-extern void * sym2addr(Process *, struct library_symbol *);
-extern int linkmap_init(struct Process *proc, void *dyn_addr);
-extern void arch_check_dbg(Process *proc);
-extern int task_kill (pid_t pid, int sig);
-
-/* Called when trace_me or primary trace_pid fail. This may plug in
- * any platform-specific knowledge of why it could be so. */
-void trace_fail_warning(pid_t pid);
-
-/* A pair of functions called to initiate a detachment request when
- * ltrace is about to exit. Their job is to undo any effects that
- * tracing had and eventually detach process, perhaps by way of
- * installing a process handler.
- *
- * OS_LTRACE_EXITING_SIGHANDLER is called from a signal handler
- * context right after the signal was captured. It returns 1 if the
- * request was handled or 0 if it wasn't.
- *
- * If the call to OS_LTRACE_EXITING_SIGHANDLER didn't handle the
- * request, OS_LTRACE_EXITING is called when the next event is
- * generated. Therefore it's called in "safe" context, without
- * re-entrancy concerns, but it's only called after an event is
- * generated. */
-int os_ltrace_exiting_sighandler(void);
-void os_ltrace_exiting(void);
-
-int arch_elf_init(struct ltelf *lte, struct library *lib);
-void arch_elf_destroy(struct ltelf *lte);
-
-enum plt_status {
- plt_fail,
- plt_ok,
- plt_default,
-};
-
-enum plt_status arch_elf_add_plt_entry(struct Process *p, struct ltelf *l,
- const char *n, GElf_Rela *r, size_t i,
- struct library_symbol **ret);
-
-int arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp);
-void arch_breakpoint_destroy(struct breakpoint *sbp);
-int arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp);
-
-void arch_library_init(struct library *lib);
-void arch_library_destroy(struct library *lib);
-void arch_library_clone(struct library *retp, struct library *lib);
-
-int arch_library_symbol_init(struct library_symbol *libsym);
-void arch_library_symbol_destroy(struct library_symbol *libsym);
-int arch_library_symbol_clone(struct library_symbol *retp,
- struct library_symbol *libsym);
-
-int arch_process_init(struct Process *proc);
-void arch_process_destroy(struct Process *proc);
-int arch_process_clone(struct Process *retp, struct Process *proc);
-int arch_process_exec(struct Process *proc);
-
-typedef void *target_address_t;
-/* This should extract entry point address and interpreter (dynamic
- * linker) bias if possible. Returns 0 if there were no errors, -1
- * otherwise. Sets *ENTRYP and *INTERP_BIASP to non-zero values if
- * the corresponding value is known. Unknown values are set to 0. */
-int process_get_entry(struct Process *proc,
- target_address_t *entryp,
- target_address_t *interp_biasp);
-
-/* This is called after the dynamic linker is done with the
- * process startup. */
-void arch_dynlink_done(struct Process *proc);
+/* Format VALUE into STREAM. The dictionary of all arguments is given
+ * for purposes of evaluating array lengths and other dynamic
+ * expressions. Returns number of characters outputted, -1 in case of
+ * failure. */
+int format_argument(FILE *stream, struct value *value,
+ struct value_dict *arguments);
+
+#endif /* COMMON_H */
diff --git a/configure.ac b/configure.ac
index 42d6158..26e1bb6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -18,10 +18,10 @@ AC_SUBST(HOST_OS)
case "${host_cpu}" in
arm*|sa110) HOST_CPU="arm" ;;
- i?86) HOST_CPU="i386" ;;
powerpc|powerpc64) HOST_CPU="ppc" ;;
sun4u|sparc64) HOST_CPU="sparc" ;;
s390x) HOST_CPU="s390" ;;
+ i?86|x86_64) HOST_CPU="x86" ;;
*) HOST_CPU="${host_cpu}" ;;
esac
AC_SUBST(HOST_CPU)
@@ -261,14 +261,13 @@ AC_CONFIG_FILES([
sysdeps/linux-gnu/Makefile
sysdeps/linux-gnu/alpha/Makefile
sysdeps/linux-gnu/arm/Makefile
- sysdeps/linux-gnu/i386/Makefile
sysdeps/linux-gnu/ia64/Makefile
sysdeps/linux-gnu/m68k/Makefile
sysdeps/linux-gnu/mipsel/Makefile
sysdeps/linux-gnu/ppc/Makefile
sysdeps/linux-gnu/s390/Makefile
sysdeps/linux-gnu/sparc/Makefile
- sysdeps/linux-gnu/x86_64/Makefile
+ sysdeps/linux-gnu/x86/Makefile
testsuite/Makefile
testsuite/ltrace.main/Makefile
testsuite/ltrace.minor/Makefile
diff --git a/defs.h b/defs.h
index 1eadb47..e346ccb 100644
--- a/defs.h
+++ b/defs.h
@@ -3,10 +3,6 @@
#define DEFAULT_ALIGN 50 /* default alignment column for results */
#endif /* (-a switch) */
-#ifndef MAX_ARGS
-#define MAX_ARGS 32 /* maximum number of args for a function */
-#endif
-
#ifndef DEFAULT_STRLEN
#define DEFAULT_STRLEN 32 /* default maximum # of bytes printed in */
#endif /* strings (-s switch) */
diff --git a/display_args.c b/display_args.c
deleted file mode 100644
index 5df34ca..0000000
--- a/display_args.c
+++ /dev/null
@@ -1,461 +0,0 @@
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-
-#include "common.h"
-#include "proc.h"
-
-static int display_char(int what);
-static int display_string(enum tof type, Process *proc,
- void* addr, size_t maxlen);
-static int display_value(enum tof type, Process *proc,
- long value, arg_type_info *info,
- void *st, arg_type_info* st_info);
-static int display_unknown(enum tof type, Process *proc, long value);
-static int display_format(enum tof type, Process *proc, int arg_num);
-
-static size_t string_maxlength = INT_MAX;
-static size_t array_maxlength = INT_MAX;
-
-static long
-get_length(enum tof type, Process *proc, int len_spec,
- void *st, arg_type_info* st_info) {
- long len;
- arg_type_info info;
-
- if (len_spec > 0)
- return len_spec;
- if (type == LT_TOF_STRUCT) {
- umovelong (proc, st + st_info->u.struct_info.offset[-len_spec-1],
- &len, st_info->u.struct_info.fields[-len_spec-1]);
- return len;
- }
-
- info.type = ARGTYPE_INT;
- return gimme_arg(type, proc, -len_spec-1, &info);
-}
-
-static int
-display_ptrto(enum tof type, Process *proc, long item,
- arg_type_info * info,
- void *st, arg_type_info* st_info) {
- arg_type_info temp;
- temp.type = ARGTYPE_POINTER;
- temp.u.ptr_info.info = info;
- return display_value(type, proc, item, &temp, st, st_info);
-}
-
-/*
- * addr - A pointer to the first element of the array
- *
- * The function name is used to indicate that we're not actually
- * looking at an 'array', which is a contiguous region of memory
- * containing a sequence of elements of some type; instead, we have a
- * pointer to that region of memory.
- */
-static int
-display_arrayptr(enum tof type, Process *proc,
- void *addr, arg_type_info * info,
- void *st, arg_type_info* st_info) {
- int len = 0;
- size_t i;
- size_t array_len;
-
- if (addr == NULL)
- return fprintf(options.output, "NULL");
-
- array_len = get_length(type, proc, info->u.array_info.len_spec,
- st, st_info);
- len += fprintf(options.output, "[ ");
- for (i = 0; i < options.arraylen && i < array_maxlength && i < array_len; i++) {
- arg_type_info *elt_type = info->u.array_info.elt_type;
- size_t elt_size = info->u.array_info.elt_size;
- if (i != 0)
- len += fprintf(options.output, ", ");
- if (options.debug)
- len += fprintf(options.output, "%p=", addr);
- len +=
- display_ptrto(type, proc, (long) addr, elt_type, st, st_info);
- addr += elt_size;
- }
- if (i < array_len)
- len += fprintf(options.output, "...");
- len += fprintf(options.output, " ]");
- return len;
-}
-
-/* addr - A pointer to the beginning of the memory region occupied by
- * the struct (aka a pointer to the struct)
- */
-static int
-display_structptr(enum tof type, Process *proc,
- void *addr, arg_type_info * info) {
- int i;
- arg_type_info *field;
- int len = 0;
-
- if (addr == NULL)
- return fprintf(options.output, "NULL");
-
- len += fprintf(options.output, "{ ");
- for (i = 0; (field = info->u.struct_info.fields[i]) != NULL; i++) {
- if (i != 0)
- len += fprintf(options.output, ", ");
- if (options.debug)
- len +=
- fprintf(options.output, "%p=",
- addr + info->u.struct_info.offset[i]);
- len +=
- display_ptrto(LT_TOF_STRUCT, proc,
- (long) addr + info->u.struct_info.offset[i],
- field, addr, info);
- }
- len += fprintf(options.output, " }");
-
- return len;
-}
-
-static int
-display_pointer(enum tof type, Process *proc, long value,
- arg_type_info * info,
- void *st, arg_type_info* st_info) {
- long pointed_to;
- arg_type_info *inner = info->u.ptr_info.info;
-
- if (inner->type == ARGTYPE_ARRAY) {
- return display_arrayptr(type, proc, (void*) value, inner,
- st, st_info);
- } else if (inner->type == ARGTYPE_STRUCT) {
- return display_structptr(type, proc, (void *) value, inner);
- } else {
- if (value == 0)
- return fprintf(options.output, "NULL");
- else if (umovelong (proc, (void *) value, &pointed_to,
- info->u.ptr_info.info) < 0)
- return fprintf(options.output, "?");
- else
- return display_value(type, proc, pointed_to, inner,
- st, st_info);
- }
-}
-
-static int
-display_enum(enum tof type, Process *proc,
- arg_type_info* info, long value) {
- size_t ii;
- for (ii = 0; ii < info->u.enum_info.entries; ++ii) {
- if (info->u.enum_info.values[ii] == value)
- return fprintf(options.output, "%s", info->u.enum_info.keys[ii]);
- }
-
- return display_unknown(type, proc, value);
-}
-
-/* Args:
- type - syscall or shared library function or memory
- proc - information about the traced process
- value - the value to display
- info - the description of the type to display
- st - if the current value is a struct member, the address of the struct
- st_info - type of the above struct
-
- Those last two parameters are used for structs containing arrays or
- strings whose length is given by another structure element.
-*/
-int
-display_value(enum tof type, Process *proc,
- long value, arg_type_info *info,
- void *st, arg_type_info* st_info) {
- int tmp;
-
- switch (info->type) {
- case ARGTYPE_VOID:
- return 0;
- case ARGTYPE_INT:
- return fprintf(options.output, "%d", (int) value);
- case ARGTYPE_UINT:
- return fprintf(options.output, "%u", (unsigned) value);
- case ARGTYPE_LONG:
- if (proc->mask_32bit)
- return fprintf(options.output, "%d", (int) value);
- else
- return fprintf(options.output, "%ld", value);
- case ARGTYPE_ULONG:
- if (proc->mask_32bit)
- return fprintf(options.output, "%u", (unsigned) value);
- else
- return fprintf(options.output, "%lu", (unsigned long) value);
- case ARGTYPE_OCTAL:
- return fprintf(options.output, "0%o", (unsigned) value);
- case ARGTYPE_CHAR:
- tmp = fprintf(options.output, "'");
- tmp += display_char(value == -1 ? value : (char) value);
- tmp += fprintf(options.output, "'");
- return tmp;
- case ARGTYPE_SHORT:
- return fprintf(options.output, "%hd", (short) value);
- case ARGTYPE_USHORT:
- return fprintf(options.output, "%hu", (unsigned short) value);
- case ARGTYPE_FLOAT: {
- union { long l; float f; double d; } cvt;
- cvt.l = value;
- return fprintf(options.output, "%f", cvt.f);
- }
- case ARGTYPE_DOUBLE: {
- union { long l; float f; double d; } cvt;
- cvt.l = value;
- return fprintf(options.output, "%lf", cvt.d);
- }
- case ARGTYPE_ADDR:
- if (!value)
- return fprintf(options.output, "NULL");
- else
- return fprintf(options.output, "0x%08lx", value);
- case ARGTYPE_FORMAT:
- fprintf(stderr, "Should never encounter a format anywhere but at the top level (for now?)\n");
- exit(1);
- case ARGTYPE_STRING:
- return display_string(type, proc, (void*) value,
- string_maxlength);
- case ARGTYPE_STRING_N:
- return display_string(type, proc, (void*) value,
- get_length(type, proc,
- info->u.string_n_info.size_spec, st, st_info));
- case ARGTYPE_ARRAY:
- return fprintf(options.output, "<array without address>");
- case ARGTYPE_ENUM:
- return display_enum(type, proc, info, value);
- case ARGTYPE_STRUCT:
- return fprintf(options.output, "<struct without address>");
- case ARGTYPE_POINTER:
- return display_pointer(type, proc, value, info,
- st, st_info);
- case ARGTYPE_UNKNOWN:
- default:
- return display_unknown(type, proc, value);
- }
-}
-
-int
-display_arg(enum tof type, Process *proc, int arg_num, arg_type_info * info) {
- long arg;
-
- if (info->type == ARGTYPE_VOID) {
- return 0;
- } else if (info->type == ARGTYPE_FORMAT) {
- return display_format(type, proc, arg_num);
- } else {
- arg = gimme_arg(type, proc, arg_num, info);
- return display_value(type, proc, arg, info, NULL, NULL);
- }
-}
-
-static int
-display_char(int what) {
- switch (what) {
- case -1:
- return fprintf(options.output, "EOF");
- case '\r':
- return fprintf(options.output, "\\r");
- case '\n':
- return fprintf(options.output, "\\n");
- case '\t':
- return fprintf(options.output, "\\t");
- case '\b':
- return fprintf(options.output, "\\b");
- case '\\':
- return fprintf(options.output, "\\\\");
- default:
- if (isprint(what)) {
- return fprintf(options.output, "%c", what);
- } else {
- return fprintf(options.output, "\\%03o", (unsigned char)what);
- }
- }
-}
-
-#define MIN(a,b) (((a)<(b)) ? (a) : (b))
-
-static int
-display_string(enum tof type, Process *proc, void *addr,
- size_t maxlength) {
- unsigned char *str1;
- size_t i;
- int len = 0;
-
- if (!addr) {
- return fprintf(options.output, "NULL");
- }
-
- str1 = malloc(MIN(options.strlen, maxlength) + 3);
- if (!str1) {
- return fprintf(options.output, "???");
- }
- umovestr(proc, addr, MIN(options.strlen, maxlength) + 1, str1);
- len = fprintf(options.output, "\"");
- for (i = 0; i < MIN(options.strlen, maxlength); i++) {
- if (str1[i]) {
- len += display_char(str1[i]);
- } else {
- break;
- }
- }
- len += fprintf(options.output, "\"");
- if (str1[i] && (options.strlen <= maxlength)) {
- len += fprintf(options.output, "...");
- }
- free(str1);
- return len;
-}
-
-static int
-display_unknown(enum tof type, Process *proc, long value) {
- if (proc->mask_32bit) {
- if ((int)value < 1000000 && (int)value > -1000000)
- return fprintf(options.output, "%d", (int)value);
- else
- return fprintf(options.output, "%p", (void *)value);
- } else if (value < 1000000 && value > -1000000) {
- return fprintf(options.output, "%ld", value);
- } else {
- return fprintf(options.output, "%p", (void *)value);
- }
-}
-
-static int
-display_format(enum tof type, Process *proc, int arg_num) {
- void *addr;
- unsigned char *str1;
- int i;
- size_t len = 0;
- arg_type_info info;
-
- info.type = ARGTYPE_POINTER;
- addr = (void *)gimme_arg(type, proc, arg_num, &info);
- if (!addr) {
- return fprintf(options.output, "NULL");
- }
-
- str1 = malloc(MIN(options.strlen, string_maxlength) + 3);
- if (!str1) {
- return fprintf(options.output, "???");
- }
- umovestr(proc, addr, MIN(options.strlen, string_maxlength) + 1, str1);
- len = fprintf(options.output, "\"");
- for (i = 0; len < MIN(options.strlen, string_maxlength) + 1; i++) {
- if (str1[i]) {
- len += display_char(str1[i]);
- } else {
- break;
- }
- }
- len += fprintf(options.output, "\"");
- if (str1[i] && (options.strlen <= string_maxlength)) {
- len += fprintf(options.output, "...");
- }
- for (i = 0; str1[i]; i++) {
- if (str1[i] == '%') {
- int is_long = 0;
- while (1) {
- unsigned char c = str1[++i];
- if (c == '%') {
- break;
- } else if (!c) {
- break;
- } else if (strchr("lzZtj", c)) {
- is_long++;
- if (c == 'j')
- is_long++;
- if (is_long > 1
- && (sizeof(long) < sizeof(long long)
- || proc->mask_32bit)) {
- len += fprintf(options.output, ", ...");
- str1[i + 1] = '\0';
- break;
- }
- } else if (c == 'd' || c == 'i') {
- info.type = ARGTYPE_LONG;
- if (!is_long || proc->mask_32bit)
- len +=
- fprintf(options.output, ", %d",
- (int)gimme_arg(type, proc, ++arg_num, &info));
- else
- len +=
- fprintf(options.output, ", %ld",
- gimme_arg(type, proc, ++arg_num, &info));
- break;
- } else if (c == 'u') {
- info.type = ARGTYPE_LONG;
- if (!is_long || proc->mask_32bit)
- len +=
- fprintf(options.output, ", %u",
- (int)gimme_arg(type, proc, ++arg_num, &info));
- else
- len +=
- fprintf(options.output, ", %lu",
- gimme_arg(type, proc, ++arg_num, &info));
- break;
- } else if (c == 'o') {
- info.type = ARGTYPE_LONG;
- if (!is_long || proc->mask_32bit)
- len +=
- fprintf(options.output, ", 0%o",
- (int)gimme_arg(type, proc, ++arg_num, &info));
- else
- len +=
- fprintf(options.output, ", 0%lo",
- gimme_arg(type, proc, ++arg_num, &info));
- break;
- } else if (c == 'x' || c == 'X') {
- info.type = ARGTYPE_LONG;
- if (!is_long || proc->mask_32bit)
- len +=
- fprintf(options.output, ", %#x",
- (int)gimme_arg(type, proc, ++arg_num, &info));
- else
- len +=
- fprintf(options.output, ", %#lx",
- gimme_arg(type, proc, ++arg_num, &info));
- break;
- } else if (strchr("eEfFgGaACS", c)
- || (is_long
- && (c == 'c' || c == 's'))) {
- len += fprintf(options.output, ", ...");
- str1[i + 1] = '\0';
- break;
- } else if (c == 'c') {
- info.type = ARGTYPE_LONG;
- len += fprintf(options.output, ", '");
- len +=
- display_char((int)
- gimme_arg(type, proc, ++arg_num, &info));
- len += fprintf(options.output, "'");
- break;
- } else if (c == 's') {
- info.type = ARGTYPE_POINTER;
- len += fprintf(options.output, ", ");
- len +=
- display_string(type, proc,
- (void *)gimme_arg(type, proc, ++arg_num, &info),
- string_maxlength);
- break;
- } else if (c == 'p' || c == 'n') {
- info.type = ARGTYPE_POINTER;
- len +=
- fprintf(options.output, ", %p",
- (void *)gimme_arg(type, proc, ++arg_num, &info));
- break;
- } else if (c == '*') {
- info.type = ARGTYPE_LONG;
- len +=
- fprintf(options.output, ", %d",
- (int)gimme_arg(type, proc, ++arg_num, &info));
- }
- }
- }
- }
- free(str1);
- return len;
-}
diff --git a/etc/ltrace.conf b/etc/ltrace.conf
index 3caf6b7..6513752 100644
--- a/etc/ltrace.conf
+++ b/etc/ltrace.conf
@@ -101,7 +101,7 @@ string inet_ntoa(addr); ; It isn't an ADDR but an hexa number...
addr inet_addr(string);
; bfd.h
-void bfd_init(void);
+void bfd_init();
int bfd_set_default_target(string);
addr bfd_scan_vma(string, addr, int);
addr bfd_openr(string,string);
@@ -110,9 +110,9 @@ int bfd_check_format(addr,int);
; ctype.h
char tolower(char);
char toupper(char);
-addr __ctype_b_loc(void);
-addr __ctype_tolower_loc(void);
-addr __ctype_toupper_loc(void);
+addr __ctype_b_loc();
+addr __ctype_tolower_loc();
+addr __ctype_toupper_loc();
; curses.h
int waddch(addr, char);
@@ -129,12 +129,12 @@ addr readdir64(addr);
; dlfcn.h
addr dlopen(string, int);
-string dlerror(void);
+string dlerror();
addr dlsym(addr, string);
int dlclose(addr);
; errno.h
-addr __errno_location(void);
+addr __errno_location();
; fcntl.h
int open(string,int,octal); ; WARNING: 3rd argument may not be there
@@ -148,10 +148,10 @@ int getopt_long(int,addr,string,addr,int*);
int getopt_long_only(int,addr,string,addr,addr);
; grp.h
-void endgrent(void);
+void endgrent();
addr getgrnam(string);
-void setgrent(void);
-addr getgrent(void);
+void setgrent();
+addr getgrent();
; libintl.h
string __dcgettext(string,string,int);
@@ -166,8 +166,8 @@ int _IO_putc(char,file);
string setlocale(int, string);
; mcheck.h
-void mtrace(void);
-void muntrace(void);
+void mtrace();
+void muntrace();
; mntent.h
int endmntent(file);
@@ -187,28 +187,28 @@ long mq_receive(int, +string0, ulong, addr);
long mq_timedreceive(int, +string0, ulong, addr, addr);
; netdb.h
-void endhostent(void);
-void endnetent(void);
-void endnetgrent(void);
-void endprotoent(void);
-void endservent(void);
+void endhostent();
+void endnetent();
+void endnetgrent();
+void endprotoent();
+void endservent();
void freeaddrinfo(addr);
string gai_strerror(int);
int getaddrinfo(string, string, addr, addr);
addr gethostbyaddr(string, uint, int);
addr gethostbyname(string);
-addr gethostent(void);
+addr gethostent();
int getnameinfo(addr, uint, string, uint, string, uint, uint);
addr getnetbyaddr(uint, int);
addr getnetbyname(string);
-addr getnetent(void);
+addr getnetent();
int getnetgrent(addr, addr, addr);
addr getprotobyname(string);
addr getprotobynumber(int);
-addr getprotoent(void);
+addr getprotoent();
addr getservbyname(string, string);
addr getservbyport(int, string);
-addr getservent(void);
+addr getservent();
void herror(string);
string hstrerror(int);
int rcmd(addr, ushort, string, string, string, addr);
@@ -237,9 +237,9 @@ int pcap_compile(addr, addr, string, int, addr);
; pwd.h
string getpass(string);
-void endpwent(void);
+void endpwent();
addr getpwnam(string);
-void setpwent(void);
+void setpwent();
; readline/readline.h
string readline(string);
@@ -305,7 +305,7 @@ int setenv(string,string,int);
void unsetenv(string);
addr malloc(ulong);
void qsort(addr,ulong,ulong,addr);
-int random(void);
+int random();
addr realloc(addr,ulong);
void srandom(uint);
int system(string);
@@ -364,7 +364,7 @@ int uname(addr);
int statfs(string,addr);
; syslog.h
-void closelog(void);
+void closelog();
void openlog(string,int,int);
void syslog(int,format);
@@ -395,21 +395,21 @@ int dup2(int,int);
int execlp(string,string,addr,addr,addr);
int execv(string,addr);
int fchdir(int);
-int fork(void);
+int fork();
int ftruncate(int,ulong);
string2 getcwd(addr,ulong);
int getdomainname(+string2,ulong);
-int geteuid(void);
-int getegid(void);
-int getgid(void);
+int geteuid();
+int getegid();
+int getgid();
int gethostname(+string2,ulong);
-string getlogin(void);
+string getlogin();
int getopt(int,addr,string);
-int getpid(void);
-int getppid(void);
-int getuid(void);
-int getpgrp(void);
-int setpgrp(void);
+int getpid();
+int getppid();
+int getuid();
+int getpgrp();
+int setpgrp();
int getpgid(int);
int isatty(int);
int link(string,string);
@@ -424,21 +424,21 @@ int setreuid(uint, uint);
int setuid(int);
uint sleep(uint);
int symlink(string,string);
-int sync(void);
+int sync();
int truncate(string,ulong);
string ttyname(int);
int unlink(string);
void usleep(uint);
long write(int, string3, ulong);
addr sbrk(long);
-int getpagesize(void);
+int getpagesize();
long lseek(int,long,int);
int pipe(addr);
; utmp.h
-void endutent(void);
-addr getutent(void);
-void setutent(void);
+void endutent();
+addr getutent();
+void setutent();
; wchar.h
int fwide(addr, int);
@@ -480,7 +480,7 @@ int acl_set_qualifier(addr,addr);
int acl_set_tag_type(addr,int);
int acl_size(addr);
string acl_to_text(addr,addr);
-itn acl_valid(addr);
+int acl_valid(addr);
; acl/libacl.h
int acl_check(addr,addr);
@@ -500,9 +500,9 @@ int SYS_close(int);
int SYS_execve(string,addr,addr);
void SYS_exit(int);
void SYS_exit_group(int);
-int SYS_fork(void);
+int SYS_fork();
int SYS_getcwd(+string2,ulong);
-int SYS_getpid(void);
+int SYS_getpid();
;addr SYS_mmap(addr,ulong,int,int,int,long);
int SYS_munmap(addr,ulong);
int SYS_open(string,int,octal);
@@ -512,7 +512,7 @@ int SYS_stat(string,addr);
octal SYS_umask(octal);
int SYS_uname(addr);
long SYS_write(int,string3,ulong);
-int SYS_sync(void);
+int SYS_sync();
int SYS_setxattr(string,string,addr,uint,int);
int SYS_lsetxattr(string,string,addr,uint,int);
int SYS_fsetxattr(int,string,addr,uint,int);
@@ -545,16 +545,16 @@ int SYS_gettimeofday(addr,addr);
int SYS_settimeofday(addr,addr);
int SYS_setfsgid(int);
int SYS_setfsuid(int);
-int SYS_getuid(void);
+int SYS_getuid();
int SYS_setuid(int);
-int SYS_getgid(void);
+int SYS_getgid();
int SYS_setgid(int);
int SYS_getsid(int);
int SYS_setsid(int);
int SYS_setreuid(int,int);
int SYS_setregid(int,int);
-int SYS_geteuid(void);
-int SYS_getegid(void);
+int SYS_geteuid();
+int SYS_getegid();
int SYS_setpgid(int,int);
int SYS_getresuid(addr,addr,addr);
int SYS_setresuid(int,int,int);
@@ -592,7 +592,7 @@ int SYS_utime(string,addr);
long SYS_lseek(int,long,int);
addr SYS_signal(int,addr);
int SYS_sigaction(int,addr,addr);
-int SYS_pause(void);
+int SYS_pause();
int SYS_sigpending(addr);
int SYS_sigprocmask(int,addr,addr);
int SYS_sigqueue(int,int,addr);
diff --git a/execute_program.c b/execute_program.c
index 55df205..f469b2f 100644
--- a/execute_program.c
+++ b/execute_program.c
@@ -14,7 +14,9 @@
#include <pwd.h>
#include <grp.h>
-#include "common.h"
+#include "backend.h"
+#include "options.h"
+#include "debug.h"
static void
change_uid(const char * command)
diff --git a/expr.c b/expr.c
new file mode 100644
index 0000000..c0ebcef
--- /dev/null
+++ b/expr.c
@@ -0,0 +1,337 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <stdlib.h>
+
+#include "expr.h"
+
+static void
+expr_init_common(struct expr_node *node, enum expr_node_kind kind)
+{
+ node->kind = kind;
+ node->lhs = NULL;
+ node->own_lhs = 0;
+ memset(&node->u, 0, sizeof(node->u));
+}
+
+void
+expr_init_self(struct expr_node *node)
+{
+ expr_init_common(node, EXPR_OP_SELF);
+}
+
+void
+expr_init_named(struct expr_node *node,
+ const char *name, int own_name)
+{
+ expr_init_common(node, EXPR_OP_NAMED);
+ node->u.name.s = name;
+ node->u.name.own = own_name;
+}
+
+void
+expr_init_argno(struct expr_node *node, size_t num)
+{
+ expr_init_common(node, EXPR_OP_ARGNO);
+ node->u.num = num;
+}
+
+void
+expr_init_const(struct expr_node *node, struct value *val)
+{
+ expr_init_common(node, EXPR_OP_CONST);
+ node->u.value = *val;
+}
+
+void
+expr_init_const_word(struct expr_node *node, long l,
+ struct arg_type_info *type, int own_type)
+{
+ struct value val;
+ value_init_detached(&val, NULL, type, own_type);
+ value_set_word(&val, l);
+ expr_init_const(node, &val);
+}
+
+void
+expr_init_index(struct expr_node *node,
+ struct expr_node *lhs, int own_lhs,
+ struct expr_node *rhs, int own_rhs)
+{
+ expr_init_common(node, EXPR_OP_INDEX);
+ node->lhs = lhs;
+ node->own_lhs = own_lhs;
+ node->u.node.n = rhs;
+ node->u.node.own = own_rhs;
+}
+
+void
+expr_init_up(struct expr_node *node, struct expr_node *lhs, int own_lhs)
+{
+ assert(lhs != NULL);
+ expr_init_common(node, EXPR_OP_UP);
+ node->lhs = lhs;
+ node->own_lhs = own_lhs;
+}
+
+void
+expr_init_cb1(struct expr_node *node,
+ int (*cb)(struct value *ret_value, struct value *value,
+ struct value_dict *arguments, void *data),
+ struct expr_node *lhs, int own_lhs, void *data)
+{
+ expr_init_common(node, EXPR_OP_CALL1);
+ node->lhs = lhs;
+ node->own_lhs = own_lhs;
+ node->u.call.u.cb1 = cb;
+ node->u.call.data = data;
+}
+
+void
+expr_init_cb2(struct expr_node *node,
+ int (*cb)(struct value *ret_value,
+ struct value *lhs, struct value *rhs,
+ struct value_dict *arguments, void *data),
+ struct expr_node *lhs, int own_lhs,
+ struct expr_node *rhs, int own_rhs, void *data)
+{
+ expr_init_common(node, EXPR_OP_CALL2);
+ node->lhs = lhs;
+ node->own_lhs = own_lhs;
+ node->u.call.rhs = rhs;
+ node->u.call.own_rhs = own_rhs;
+ node->u.call.u.cb2 = cb;
+ node->u.call.data = data;
+}
+
+static void
+release_lhs(struct expr_node *node)
+{
+ if (node->own_lhs)
+ expr_destroy(node->lhs);
+}
+
+void
+expr_destroy(struct expr_node *node)
+{
+ if (node == NULL)
+ return;
+
+ switch (node->kind) {
+ case EXPR_OP_ARGNO:
+ case EXPR_OP_SELF:
+ return;
+
+ case EXPR_OP_CONST:
+ value_destroy(&node->u.value);
+ return;
+
+ case EXPR_OP_NAMED:
+ if (node->u.name.own)
+ free((char *)node->u.name.s);
+ return;
+
+ case EXPR_OP_INDEX:
+ release_lhs(node);
+ if (node->u.node.own)
+ expr_destroy(node->u.node.n);
+ return;
+
+ case EXPR_OP_CALL2:
+ if (node->u.call.own_rhs)
+ expr_destroy(node->u.call.rhs);
+ case EXPR_OP_UP:
+ case EXPR_OP_CALL1:
+ release_lhs(node);
+ return;
+ }
+
+ assert(!"Invalid value of node kind");
+ abort();
+}
+
+int
+expr_is_compile_constant(struct expr_node *node)
+{
+ return node->kind == EXPR_OP_CONST;
+}
+
+static int
+eval_up(struct expr_node *node, struct value *context,
+ struct value_dict *arguments, struct value *ret_value)
+{
+ if (expr_eval(node->lhs, context, arguments, ret_value) < 0)
+ return -1;
+ struct value *parent = value_get_parental_struct(ret_value);
+ if (parent == NULL) {
+ value_destroy(ret_value);
+ return -1;
+ }
+ *ret_value = *parent;
+ return 0;
+}
+
+static int
+eval_cb1(struct expr_node *node, struct value *context,
+ struct value_dict *arguments, struct value *ret_value)
+{
+ struct value val;
+ if (expr_eval(node->lhs, context, arguments, &val) < 0)
+ return -1;
+
+ int ret = 0;
+ if (node->u.call.u.cb1(ret_value, &val, arguments,
+ node->u.call.data) < 0)
+ ret = -1;
+
+ /* N.B. the callback must return its own value, or somehow
+ * clone the incoming argument. */
+ value_destroy(&val);
+ return ret;
+}
+
+static int
+eval_cb2(struct expr_node *node, struct value *context,
+ struct value_dict *arguments, struct value *ret_value)
+{
+ struct value lhs;
+ if (expr_eval(node->lhs, context, arguments, &lhs) < 0)
+ return -1;
+
+ struct value rhs;
+ if (expr_eval(node->u.call.rhs, context, arguments, &rhs) < 0) {
+ value_destroy(&lhs);
+ return -1;
+ }
+
+ int ret = 0;
+ if (node->u.call.u.cb2(ret_value, &lhs, &rhs, arguments,
+ node->u.call.data) < 0)
+ ret = -1;
+
+ /* N.B. the callback must return its own value, or somehow
+ * clone the incoming argument. */
+ value_destroy(&lhs);
+ value_destroy(&rhs);
+ return ret;
+}
+
+int
+eval_index(struct expr_node *node, struct value *context,
+ struct value_dict *arguments, struct value *ret_value)
+{
+ struct value lhs;
+ if (expr_eval(node->lhs, context, arguments, &lhs) < 0)
+ return -1;
+
+ long l;
+ if (expr_eval_word(node->u.node.n, context, arguments, &l) < 0) {
+ fail:
+ value_destroy(&lhs);
+ return -1;
+ }
+
+ if (value_init_element(ret_value, &lhs, (size_t)l) < 0)
+ goto fail;
+ return 0;
+}
+
+int
+expr_eval(struct expr_node *node, struct value *context,
+ struct value_dict *arguments, struct value *ret_value)
+{
+ switch (node->kind) {
+ struct value *valp;
+ case EXPR_OP_ARGNO:
+ valp = val_dict_get_num(arguments, node->u.num);
+ if (valp == NULL)
+ return -1;
+ *ret_value = *valp;
+ return 0;
+
+ case EXPR_OP_NAMED:
+ valp = val_dict_get_name(arguments, node->u.name.s);
+ if (valp == NULL)
+ return -1;
+ *ret_value = *valp;
+ return 0;
+
+ case EXPR_OP_SELF:
+ *ret_value = *context;
+ return 0;
+
+ case EXPR_OP_CONST:
+ *ret_value = node->u.value;
+ return 0;
+
+ case EXPR_OP_INDEX:
+ return eval_index(node, context, arguments, ret_value);
+
+ case EXPR_OP_UP:
+ return eval_up(node, context, arguments, ret_value);
+
+ case EXPR_OP_CALL1:
+ return eval_cb1(node, context, arguments, ret_value);
+
+ case EXPR_OP_CALL2:
+ return eval_cb2(node, context, arguments, ret_value);
+ }
+
+ assert(!"Unknown node kind.");
+ abort();
+}
+
+int
+expr_eval_word(struct expr_node *node, struct value *context,
+ struct value_dict *arguments, long *ret_value)
+{
+ struct value val;
+ if (expr_eval(node, context, arguments, &val) < 0)
+ return -1;
+ int ret = 0;
+ if (value_extract_word(&val, ret_value, arguments) < 0)
+ ret = -1;
+ value_destroy(&val);
+ return ret;
+}
+
+int
+expr_eval_constant(struct expr_node *node, long *valuep)
+{
+ assert(expr_is_compile_constant(node));
+ return expr_eval_word(node, NULL, NULL, valuep);
+}
+
+struct expr_node *
+expr_self(void)
+{
+ static struct expr_node *node = NULL;
+ if (node == NULL) {
+ node = malloc(sizeof(*node));
+ if (node == NULL)
+ error(1, errno, "malloc expr_self");
+ expr_init_self(node);
+ }
+ return node;
+}
diff --git a/expr.h b/expr.h
new file mode 100644
index 0000000..53b75b7
--- /dev/null
+++ b/expr.h
@@ -0,0 +1,156 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef EXPR_H
+#define EXPR_H
+
+#include "value.h"
+#include "value_dict.h"
+
+/* Expressions serve as a way of encoding array lengths. */
+
+enum expr_node_kind {
+ EXPR_OP_SELF, /* reference to the variable in question */
+ EXPR_OP_NAMED, /* value of named argument */
+ EXPR_OP_ARGNO, /* value of numbered argument */
+ EXPR_OP_CONST, /* constant value */
+ EXPR_OP_INDEX, /* A[B] */
+ EXPR_OP_UP, /* reference to containing structure */
+ EXPR_OP_CALL1, /* internal callback with one operand */
+ EXPR_OP_CALL2, /* internal callback with two operands */
+};
+
+struct expr_node {
+ enum expr_node_kind kind;
+
+ struct expr_node *lhs;
+ int own_lhs;
+
+ union {
+ struct {
+ const char *s;
+ int own;
+ } name;
+ struct {
+ struct expr_node *n;
+ int own;
+ } node;
+ struct value value;
+ size_t num;
+ struct {
+ union {
+ int (*cb1)(struct value *ret_value,
+ struct value *lhs,
+ struct value_dict *arguments,
+ void *data);
+ int (*cb2)(struct value *ret_value,
+ struct value *lhs,
+ struct value *rhs,
+ struct value_dict *arguments,
+ void *data);
+ } u;
+ void *data;
+ struct expr_node *rhs;
+ int own_rhs;
+ } call;
+ } u;
+};
+
+/* Expression of type self just returns the value in consideration.
+ * For example, if what we seek is length of an array, then the value
+ * representing that array is returned by the expression. */
+void expr_init_self(struct expr_node *node);
+
+/* Expression that yields the value of an argument named NAME. NAME
+ * is owned if OWN_NAME. */
+void expr_init_named(struct expr_node *node,
+ const char *name, int own_name);
+
+/* Expression that yields the value of an argument number NUM. */
+void expr_init_argno(struct expr_node *node, size_t num);
+
+/* Constant expression always returns the same value VAL. VAL is
+ * copied into NODE and owned by it. */
+void expr_init_const(struct expr_node *node, struct value *val);
+void expr_init_const_word(struct expr_node *node, long l,
+ struct arg_type_info *type, int own_type);
+
+/* Expression LHS[RHS]. LHS and RHS are owned if, respectively,
+ * OWN_LHS and OWN_RHS. */
+void expr_init_index(struct expr_node *node,
+ struct expr_node *lhs, int own_lhs,
+ struct expr_node *rhs, int own_rhs);
+
+/* This expression returns the containing value of LHS (^LHS). LHS is
+ * owned if OWN_LHS. */
+void expr_init_up(struct expr_node *node, struct expr_node *lhs, int own_lhs);
+
+/* Callback expression calls CB(eval(LHS), DATA). LHS is owned if
+ * OWN_LHS. DATA is passed to callback verbatim. */
+void expr_init_cb1(struct expr_node *node,
+ int (*cb)(struct value *ret_value,
+ struct value *value,
+ struct value_dict *arguments,
+ void *data),
+ struct expr_node *lhs, int own_lhs, void *data);
+
+/* Callback expression calls CB(eval(LHS), eval(RHS), DATA). LHS and
+ * RHS are owned if, respectively, OWN_LHS and OWN_RHS. DATA is
+ * passed to callback verbatim. */
+void expr_init_cb2(struct expr_node *node,
+ int (*cb)(struct value *ret_value,
+ struct value *lhs, struct value *rhs,
+ struct value_dict *arguments,
+ void *data),
+ struct expr_node *lhs, int own_lhs,
+ struct expr_node *rhs, int own_rhs, void *data);
+
+/* Release the data inside NODE. Doesn't free NODE itself. */
+void expr_destroy(struct expr_node *node);
+
+/* Evaluate the expression NODE in context of VALUE. ARGUMENTS is a
+ * dictionary of named and numbered values that NODE may use. Returns
+ * 0 in case of success or a negative value on error. CONTEXT and
+ * ARGUMENTS may be NULL, but then the expression mustn't need them
+ * for evaluation. */
+int expr_eval(struct expr_node *node, struct value *context,
+ struct value_dict *arguments, struct value *ret_value);
+
+/* Evaluate compile-time expression. Returns 0 on success or negative
+ * value on failure. Computed value is passed back in *VALUEP. */
+int expr_eval_constant(struct expr_node *node, long *valuep);
+
+/* Evaluate expression, whose result should fit into a word. In order
+ * to easily support all the structure and array accesses, we simply
+ * operate on values represented by struct value. But eventually we need
+ * to be able to get out a word-size datum to use it as an index, a
+ * length, etc. */
+int expr_eval_word(struct expr_node *node, struct value *context,
+ struct value_dict *arguments, long *ret_value);
+
+/* Returns non-zero value if the expression is a compile-time
+ * constant. Currently this is only EXPR_OP_CONST, but eventually
+ * things like sizeof or simple expressions might be allowed. */
+int expr_is_compile_constant(struct expr_node *node);
+
+/* Returns a pre-computed expression "self". */
+struct expr_node *expr_self(void);
+
+#endif /* EXPR_H */
diff --git a/fetch.c b/fetch.c
new file mode 100644
index 0000000..bce949f
--- /dev/null
+++ b/fetch.c
@@ -0,0 +1,132 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "fetch.h"
+#include "value.h"
+#include "arch.h"
+#include "type.h"
+
+#ifdef ARCH_HAVE_FETCH_ARG
+struct fetch_context *arch_fetch_arg_init(enum tof type, struct Process *proc,
+ struct arg_type_info *ret_info);
+
+struct fetch_context *arch_fetch_arg_clone(struct Process *proc,
+ struct fetch_context *context);
+
+int arch_fetch_arg_next(struct fetch_context *ctx, enum tof type,
+ struct Process *proc, struct arg_type_info *info,
+ struct value *valuep);
+
+int arch_fetch_retval(struct fetch_context *ctx, enum tof type,
+ struct Process *proc, struct arg_type_info *info,
+ struct value *valuep);
+
+void arch_fetch_arg_done(struct fetch_context *context);
+
+#else
+/* Fall back to gimme_arg. */
+
+long gimme_arg(enum tof type, struct Process *proc, int arg_num,
+ struct arg_type_info *info);
+
+struct fetch_context {
+ int argnum;
+};
+
+struct fetch_context *
+arch_fetch_arg_init(enum tof type, struct Process *proc,
+ struct arg_type_info *ret_info)
+{
+ return calloc(sizeof(struct fetch_context), 1);
+}
+
+struct fetch_context *
+arch_fetch_arg_clone(struct Process *proc, struct fetch_context *context)
+{
+ struct fetch_context *ret = malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ return memcpy(ret, context, sizeof(*ret));
+}
+
+int
+arch_fetch_arg_next(struct fetch_context *context, enum tof type,
+ struct Process *proc,
+ struct arg_type_info *info, struct value *valuep)
+{
+ long l = gimme_arg(type, proc, context->argnum++, info);
+ value_set_word(valuep, l);
+ return 0;
+}
+
+int
+arch_fetch_retval(struct fetch_context *context, enum tof type,
+ struct Process *proc,
+ struct arg_type_info *info, struct value *valuep)
+{
+ long l = gimme_arg(type, proc, -1, info);
+ value_set_word(valuep, l);
+ return 0;
+}
+
+void
+arch_fetch_arg_done(struct fetch_context *context)
+{
+ free(context);
+}
+#endif
+
+struct fetch_context *
+fetch_arg_init(enum tof type, struct Process *proc,
+ struct arg_type_info *ret_info)
+{
+ return arch_fetch_arg_init(type, proc, ret_info);
+}
+
+struct fetch_context *
+fetch_arg_clone(struct Process *proc, struct fetch_context *context)
+{
+ return arch_fetch_arg_clone(proc, context);
+}
+
+int
+fetch_arg_next(struct fetch_context *context, enum tof type,
+ struct Process *proc,
+ struct arg_type_info *info, struct value *valuep)
+{
+ return arch_fetch_arg_next(context, type, proc, info, valuep);
+}
+
+int
+fetch_retval(struct fetch_context *context, enum tof type,
+ struct Process *proc,
+ struct arg_type_info *info, struct value *valuep)
+{
+ return arch_fetch_retval(context, type, proc, info, valuep);
+}
+
+void
+fetch_arg_done(struct fetch_context *context)
+{
+ return arch_fetch_arg_done(context);
+}
diff --git a/fetch.h b/fetch.h
new file mode 100644
index 0000000..6a5385c
--- /dev/null
+++ b/fetch.h
@@ -0,0 +1,64 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef FETCH_H
+#define FETCH_H
+
+#include "forward.h"
+
+/* XXX isn't SYSCALL TOF just a different ABI? Maybe we needed to
+ * support variant ABIs all along. */
+enum tof {
+ LT_TOF_FUNCTION, /* A real library function */
+ LT_TOF_FUNCTIONR, /* Return from a real library function */
+ LT_TOF_SYSCALL, /* A syscall */
+ LT_TOF_SYSCALLR, /* Return from a syscall */
+};
+
+/* The contents of the structure is defined by the back end. */
+struct fetch_context;
+
+/* Initialize argument fetching. Returns NULL on failure. RET_INFO
+ * is the return type of the function. */
+struct fetch_context *fetch_arg_init(enum tof type, struct Process *proc,
+ struct arg_type_info *ret_info);
+
+/* Make a clone of context. */
+struct fetch_context *fetch_arg_clone(struct Process *proc,
+ struct fetch_context *context);
+
+/* Load next argument. The function returns 0 on success or a
+ * negative value on failure. The extracted value is stored in
+ * *VALUEP. */
+int fetch_arg_next(struct fetch_context *context, enum tof type,
+ struct Process *proc,
+ struct arg_type_info *info, struct value *valuep);
+
+/* Load return value. The function returns 0 on success or a negative
+ * value on failure. The extracted value is stored in *VALUEP. */
+int fetch_retval(struct fetch_context *context, enum tof type,
+ struct Process *proc,
+ struct arg_type_info *info, struct value *valuep);
+
+/* Destroy fetch context. CONTEXT shall be the same memory location
+ * that was passed to fetch_arg_next. */
+void fetch_arg_done(struct fetch_context *context);
+
+#endif /* FETCH_H */
diff --git a/forward.h b/forward.h
new file mode 100644
index 0000000..e4233e5
--- /dev/null
+++ b/forward.h
@@ -0,0 +1,14 @@
+/* Important types defined in other header files are declared
+ here. */
+struct Event;
+struct Process;
+struct arg_type_info;
+struct breakpoint;
+struct expr_node;
+struct library;
+struct library_symbol;
+struct ltelf;
+struct param;
+struct param_enum;
+struct value;
+struct value_dict;
diff --git a/handle_event.c b/handle_event.c
index 73c118a..5b6cc40 100644
--- a/handle_event.c
+++ b/handle_event.c
@@ -1,6 +1,31 @@
-#define _GNU_SOURCE
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2010 Arnaud Patard, Mandriva SA
+ * Copyright (C) 1998,2001,2002,2003,2004,2007,2008,2009 Juan Cespedes
+ * Copyright (C) 2008 Luis Machado, IBM Corporation
+ * Copyright (C) 2006 Ian Wienand
+ * Copyright (C) 2006 Paul Gilliam, IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
#include "config.h"
+#define _GNU_SOURCE
#include <assert.h>
#include <errno.h>
#include <signal.h>
@@ -9,10 +34,13 @@
#include <string.h>
#include <sys/time.h>
+#include "backend.h"
#include "breakpoint.h"
#include "common.h"
+#include "fetch.h"
#include "library.h"
#include "proc.h"
+#include "value_dict.h"
static void handle_signal(Event *event);
static void handle_exit(Event *event);
@@ -228,9 +256,11 @@ handle_clone(Event *event)
if (proc == NULL) {
fail:
free(proc);
- /* XXX proper error handling here, please. */
- perror("malloc()");
- exit(1);
+ fprintf(stderr,
+ "Error during init of tracing process %d\n"
+ "This process won't be traced.\n",
+ event->proc->pid);
+ return;
}
if (process_clone(proc, event->proc, event->e_un.newpid) < 0)
@@ -386,13 +416,13 @@ handle_exit_signal(Event *event) {
}
static void
-output_syscall(struct Process *proc, const char *name,
+output_syscall(struct Process *proc, const char *name, enum tof tof,
void (*output)(enum tof, struct Process *,
struct library_symbol *))
{
struct library_symbol syscall;
if (library_symbol_init(&syscall, 0, name, 0, LS_TOPLT_NONE) >= 0) {
- (*output)(LT_TOF_SYSCALL, proc, &syscall);
+ (*output)(tof, proc, &syscall);
library_symbol_destroy(&syscall);
}
}
@@ -400,13 +430,13 @@ output_syscall(struct Process *proc, const char *name,
static void
output_syscall_left(struct Process *proc, const char *name)
{
- output_syscall(proc, name, &output_left);
+ output_syscall(proc, name, LT_TOF_SYSCALL, &output_left);
}
static void
output_syscall_right(struct Process *proc, const char *name)
{
- output_syscall(proc, name, &output_right);
+ output_syscall(proc, name, LT_TOF_SYSCALLR, &output_right);
}
static void
@@ -670,6 +700,7 @@ callstack_push_syscall(Process *proc, int sysnum) {
}
elem = &proc->callstack[proc->callstack_depth];
+ *elem = (struct callstack_element){};
elem->is_syscall = 1;
elem->c_un.syscall = sysnum;
elem->return_addr = NULL;
@@ -694,6 +725,7 @@ callstack_push_symfunc(Process *proc, struct library_symbol *sym) {
}
elem = &proc->callstack[proc->callstack_depth++];
+ *elem = (struct callstack_element){};
elem->is_syscall = 0;
elem->c_un.libfunc = sym;
@@ -701,7 +733,6 @@ callstack_push_symfunc(Process *proc, struct library_symbol *sym) {
if (elem->return_addr)
insert_breakpoint(proc, elem->return_addr, NULL);
- /* handle functions like atexit() on mips which have no return */
if (opt_T || options.summary) {
struct timezone tz;
gettimeofday(&elem->time_spent, &tz);
@@ -719,9 +750,5 @@ callstack_pop(Process *proc) {
assert(proc->leader != NULL);
delete_breakpoint(proc, elem->return_addr);
}
- if (elem->arch_ptr != NULL) {
- free(elem->arch_ptr);
- elem->arch_ptr = NULL;
- }
proc->callstack_depth--;
}
diff --git a/lens.c b/lens.c
new file mode 100644
index 0000000..07cdcca
--- /dev/null
+++ b/lens.c
@@ -0,0 +1,62 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <assert.h>
+
+#include "lens.h"
+#include "lens_default.h"
+#include "type.h"
+#include "value.h"
+
+int
+format_argument(FILE *stream, struct value *value,
+ struct value_dict *arguments)
+{
+ /* Find the closest enclosing parental value whose type has a
+ * lens assigned. */
+ struct value *parent;
+ for (parent = value; (parent != NULL && parent->type != NULL
+ && parent->type->lens == NULL);
+ parent = parent->parent)
+ ;
+
+ struct lens *lens = &default_lens;
+ if (parent != NULL && parent->type != NULL
+ && parent->type->lens != NULL)
+ lens = parent->type->lens;
+
+ return lens_format(lens, stream, value, arguments);
+}
+
+int
+lens_format(struct lens *lens, FILE *stream, struct value *value,
+ struct value_dict *arguments)
+{
+ assert(lens->format_cb != NULL);
+ return lens->format_cb(lens, stream, value, arguments);
+}
+
+void
+lens_destroy(struct lens *lens)
+{
+ if (lens != NULL
+ && lens->destroy_cb != NULL)
+ lens->destroy_cb(lens);
+}
diff --git a/lens.h b/lens.h
new file mode 100644
index 0000000..f7de3b5
--- /dev/null
+++ b/lens.h
@@ -0,0 +1,51 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef LENS_H
+#define LENS_H
+
+#include <stdio.h>
+#include "forward.h"
+
+struct lens {
+ /* Format VALUE into STREAM. The dictionary of all arguments
+ * is given for purposes of evaluating array lengths and other
+ * dynamic expressions. Returns number of characters
+ * outputted, -1 in case of failure. */
+ int (*format_cb)(struct lens *lens, FILE *stream, struct value *value,
+ struct value_dict *arguments);
+
+ /* Erase any memory allocated for LENS. Keep the LENS pointer
+ * itself intact. */
+ void (*destroy_cb)(struct lens *lens);
+};
+
+/* Extracts a lens from VALUE and calls lens_format on that. */
+int format_argument(FILE *stream, struct value *value,
+ struct value_dict *arguments);
+
+/* Calls format callback associated with LENS. */
+int lens_format(struct lens *lens, FILE *stream, struct value *value,
+ struct value_dict *arguments);
+
+/* Calls destroy callback associated with LENS. */
+void lens_destroy(struct lens *lens);
+
+#endif /* LENS_H */
diff --git a/lens_default.c b/lens_default.c
new file mode 100644
index 0000000..f59d328
--- /dev/null
+++ b/lens_default.c
@@ -0,0 +1,499 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 1998,2004,2007,2008,2009 Juan Cespedes
+ * Copyright (C) 2006 Ian Wienand
+ * Copyright (C) 2006 Steve Fink
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "proc.h"
+#include "lens_default.h"
+#include "value.h"
+#include "expr.h"
+#include "type.h"
+#include "common.h"
+#include "zero.h"
+
+#define READER(NAME, TYPE) \
+ static int \
+ NAME(struct value *value, TYPE *ret, struct value_dict *arguments) \
+ { \
+ union { \
+ TYPE val; \
+ unsigned char buf[0]; \
+ } u; \
+ if (value_extract_buf(value, u.buf, arguments) < 0) \
+ return -1; \
+ *ret = u.val; \
+ return 0; \
+ }
+
+READER(read_float, float)
+READER(read_double, double)
+
+#undef READER
+
+#define HANDLE_WIDTH(BITS) \
+ do { \
+ long l; \
+ if (value_extract_word(value, &l, arguments) < 0) \
+ return -1; \
+ int##BITS##_t i = l; \
+ switch (format) { \
+ case INT_FMT_unknown: \
+ if (i < -10000 || i > 10000) \
+ case INT_FMT_x: \
+ return fprintf(stream, "%#"PRIx##BITS, i); \
+ case INT_FMT_i: \
+ return fprintf(stream, "%"PRIi##BITS, i); \
+ case INT_FMT_u: \
+ return fprintf(stream, "%"PRIu##BITS, i); \
+ case INT_FMT_o: \
+ return fprintf(stream, "0%"PRIo##BITS, i); \
+ } \
+ } while (0)
+
+enum int_fmt_t
+{
+ INT_FMT_i,
+ INT_FMT_u,
+ INT_FMT_o,
+ INT_FMT_x,
+ INT_FMT_unknown,
+};
+
+static int
+format_integer(FILE *stream, struct value *value, enum int_fmt_t format,
+ struct value_dict *arguments)
+{
+ switch (type_sizeof(value->inferior, value->type)) {
+
+ case 1: HANDLE_WIDTH(8);
+ case 2: HANDLE_WIDTH(16);
+ case 4: HANDLE_WIDTH(32);
+ case 8: HANDLE_WIDTH(64);
+
+ default:
+ assert(!"unsupported integer width");
+ abort();
+
+ case -1:
+ return -1;
+ }
+}
+
+#undef HANDLE_WIDTH
+
+static int
+account(int *countp, int c)
+{
+ if (c >= 0)
+ *countp += c;
+ return c;
+}
+
+static int
+acc_fprintf(int *countp, FILE *stream, const char *format, ...)
+{
+ va_list pa;
+ va_start(pa, format);
+ int i = account(countp, vfprintf(stream, format, pa));
+ va_end(pa);
+
+ return i;
+}
+
+static int
+format_char(FILE *stream, struct value *value, struct value_dict *arguments)
+{
+ long lc;
+ if (value_extract_word(value, &lc, arguments) < 0)
+ return -1;
+ int c = (int)lc;
+
+ const char *fmt;
+ switch (c) {
+ case -1:
+ fmt = "EOF";
+ break;
+ case 0:
+ fmt = "\\0";
+ break;
+ case '\a':
+ fmt = "\\a";
+ break;
+ case '\b':
+ fmt = "\\b";
+ break;
+ case '\t':
+ fmt = "\\t";
+ break;
+ case '\n':
+ fmt = "\\n";
+ break;
+ case '\v':
+ fmt = "\\v";
+ break;
+ case '\f':
+ fmt = "\\f";
+ break;
+ case '\r':
+ fmt = "\\r";
+ break;
+ case '\\':
+ fmt = "\\\\";
+ break;
+ default:
+ if (isprint(c) || c == ' ')
+ fmt = "%c";
+ else
+ fmt = "\\%03o";
+ }
+
+ return fprintf(stream, fmt, c);
+}
+
+static int
+format_naked_char(FILE *stream, struct value *value,
+ struct value_dict *arguments)
+{
+ int written = 0;
+ if (acc_fprintf(&written, stream, "'") < 0
+ || account(&written, format_char(stream, value, arguments)) < 0
+ || acc_fprintf(&written, stream, "'") < 0)
+ return -1;
+
+ return written;
+}
+
+static int
+format_floating(FILE *stream, struct value *value, struct value_dict *arguments)
+{
+ switch (value->type->type) {
+ float f;
+ double d;
+ case ARGTYPE_FLOAT:
+ if (read_float(value, &f, arguments) < 0)
+ return -1;
+ return fprintf(stream, "%f", (double)f);
+ case ARGTYPE_DOUBLE:
+ if (read_double(value, &d, arguments) < 0)
+ return -1;
+ return fprintf(stream, "%f", d);
+ default:
+ abort();
+ }
+}
+
+static int
+format_struct(FILE *stream, struct value *value, struct value_dict *arguments)
+{
+ int written = 0;
+ if (acc_fprintf(&written, stream, "{ ") < 0)
+ return -1;
+ size_t i;
+ for (i = 0; i < type_struct_size(value->type); ++i) {
+ if (i > 0 && acc_fprintf(&written, stream, ", ") < 0)
+ return -1;
+
+ struct value element;
+ if (value_init_element(&element, value, i) < 0)
+ return -1;
+ int o = format_argument(stream, &element, arguments);
+ if (o < 0)
+ return -1;
+ written += o;
+ }
+ if (acc_fprintf(&written, stream, " }") < 0)
+ return -1;
+ return written;
+}
+
+int
+format_pointer(FILE *stream, struct value *value, struct value_dict *arguments)
+{
+ struct value element;
+ if (value_init_deref(&element, value) < 0)
+ return -1;
+ return format_argument(stream, &element, arguments);
+}
+
+/*
+ * LENGTH is an expression whose evaluation will yield the actual
+ * length of the array.
+ *
+ * MAXLEN is the actual maximum length that we care about
+ *
+ * BEFORE if LENGTH>MAXLEN, we display ellipsis. We display it before
+ * the closing parenthesis if BEFORE, otherwise after it.
+ *
+ * OPEN, CLOSE, DELIM are opening and closing parenthesis and element
+ * delimiter.
+ */
+int
+format_array(FILE *stream, struct value *value, struct value_dict *arguments,
+ struct expr_node *length, size_t maxlen, int before,
+ const char *open, const char *close, const char *delim)
+{
+ /* We need "long" to be long enough to cover the whole address
+ * space. */
+ typedef char assert__long_enough_long[-(sizeof(long) < sizeof(void *))];
+ long l;
+ if (expr_eval_word(length, value, arguments, &l) < 0)
+ return -1;
+ size_t len = (size_t)l;
+
+ int written = 0;
+ if (acc_fprintf(&written, stream, "%s", open) < 0)
+ return -1;
+
+ size_t i;
+ for (i = 0; i < len && i <= maxlen; ++i) {
+ if (i == maxlen) {
+ if (before && acc_fprintf(&written, stream, "...") < 0)
+ return -1;
+ break;
+ }
+
+ if (i > 0 && acc_fprintf(&written, stream, "%s", delim) < 0)
+ return -1;
+
+ struct value element;
+ if (value_init_element(&element, value, i) < 0)
+ return -1;
+ int o = format_argument(stream, &element, arguments);
+ if (o < 0)
+ return -1;
+ written += o;
+ }
+ if (acc_fprintf(&written, stream, "%s", close) < 0)
+ return -1;
+ if (i == maxlen && !before && acc_fprintf(&written, stream, "...") < 0)
+ return -1;
+
+ return written;
+}
+
+static int
+toplevel_format_lens(struct lens *lens, FILE *stream,
+ struct value *value, struct value_dict *arguments,
+ enum int_fmt_t int_fmt)
+{
+ switch (value->type->type) {
+ case ARGTYPE_VOID:
+ return fprintf(stream, "<void>");
+
+ case ARGTYPE_SHORT:
+ case ARGTYPE_INT:
+ case ARGTYPE_LONG:
+ return format_integer(stream, value, int_fmt, arguments);
+
+ case ARGTYPE_USHORT:
+ case ARGTYPE_UINT:
+ case ARGTYPE_ULONG:
+ if (int_fmt == INT_FMT_i)
+ int_fmt = INT_FMT_u;
+ return format_integer(stream, value, int_fmt, arguments);
+
+ case ARGTYPE_CHAR:
+ return format_naked_char(stream, value, arguments);
+
+ case ARGTYPE_FLOAT:
+ case ARGTYPE_DOUBLE:
+ return format_floating(stream, value, arguments);
+
+ case ARGTYPE_STRUCT:
+ return format_struct(stream, value, arguments);
+
+ case ARGTYPE_POINTER:
+ if (value->type->u.array_info.elt_type->type != ARGTYPE_VOID)
+ return format_pointer(stream, value, arguments);
+ return format_integer(stream, value, INT_FMT_x, arguments);
+
+ case ARGTYPE_ARRAY:
+ return format_array(stream, value, arguments,
+ value->type->u.array_info.length,
+ options.arraylen, 1, "[ ", " ]", ", ");
+ }
+ abort();
+}
+
+static int
+default_lens_format_cb(struct lens *lens, FILE *stream,
+ struct value *value, struct value_dict *arguments)
+{
+ return toplevel_format_lens(lens, stream, value, arguments, INT_FMT_i);
+}
+
+struct lens default_lens = {
+ .format_cb = default_lens_format_cb,
+};
+
+
+static int
+blind_lens_format_cb(struct lens *lens, FILE *stream,
+ struct value *value, struct value_dict *arguments)
+{
+ return 0;
+}
+
+struct lens blind_lens = {
+ .format_cb = blind_lens_format_cb,
+};
+
+
+static int
+octal_lens_format_cb(struct lens *lens, FILE *stream,
+ struct value *value, struct value_dict *arguments)
+{
+ return toplevel_format_lens(lens, stream, value, arguments, INT_FMT_o);
+}
+
+struct lens octal_lens = {
+ .format_cb = octal_lens_format_cb,
+};
+
+
+static int
+hex_lens_format_cb(struct lens *lens, FILE *stream,
+ struct value *value, struct value_dict *arguments)
+{
+ return toplevel_format_lens(lens, stream, value, arguments, INT_FMT_x);
+}
+
+struct lens hex_lens = {
+ .format_cb = hex_lens_format_cb,
+};
+
+
+static int
+guess_lens_format_cb(struct lens *lens, FILE *stream,
+ struct value *value, struct value_dict *arguments)
+{
+ return toplevel_format_lens(lens, stream, value, arguments,
+ INT_FMT_unknown);
+}
+
+struct lens guess_lens = {
+ .format_cb = guess_lens_format_cb,
+};
+
+
+static int
+bool_lens_format_cb(struct lens *lens, FILE *stream,
+ struct value *value, struct value_dict *arguments)
+{
+ switch (value->type->type) {
+ case ARGTYPE_VOID:
+ case ARGTYPE_FLOAT:
+ case ARGTYPE_DOUBLE:
+ case ARGTYPE_STRUCT:
+ case ARGTYPE_POINTER:
+ case ARGTYPE_ARRAY:
+ return toplevel_format_lens(lens, stream, value,
+ arguments, INT_FMT_i);
+
+ int zero;
+ case ARGTYPE_SHORT:
+ case ARGTYPE_INT:
+ case ARGTYPE_LONG:
+ case ARGTYPE_USHORT:
+ case ARGTYPE_UINT:
+ case ARGTYPE_ULONG:
+ case ARGTYPE_CHAR:
+ if ((zero = value_is_zero(value, arguments)) < 0)
+ return -1;
+ if (zero)
+ return fprintf(stream, "false");
+ else
+ return fprintf(stream, "true");
+ }
+ abort();
+}
+
+struct lens bool_lens = {
+ .format_cb = bool_lens_format_cb,
+};
+
+
+static int
+string_lens_format_cb(struct lens *lens, FILE *stream,
+ struct value *value, struct value_dict *arguments)
+{
+ switch (value->type->type) {
+ case ARGTYPE_POINTER:
+ /* This should really be written as either "string",
+ * or, if lens, then string(array(char, zero)*). But
+ * I suspect people are so used to the char * C idiom,
+ * that string(char *) might actually turn up. So
+ * let's just support it. */
+ if (value->type->u.ptr_info.info->type == ARGTYPE_CHAR) {
+ struct arg_type_info info[2];
+ type_init_array(&info[1],
+ value->type->u.ptr_info.info, 0,
+ expr_node_zero(), 0);
+ type_init_pointer(&info[0], &info[1], 0);
+ info->lens = lens;
+ info->own_lens = 0;
+ struct value tmp;
+ if (value_clone(&tmp, value) < 0)
+ return -1;
+ value_set_type(&tmp, info, 0);
+ int ret = string_lens_format_cb(lens, stream, &tmp,
+ arguments);
+ type_destroy(&info[0]);
+ type_destroy(&info[1]);
+ value_destroy(&tmp);
+ return ret;
+ }
+
+ /* fall-through */
+ case ARGTYPE_VOID:
+ case ARGTYPE_FLOAT:
+ case ARGTYPE_DOUBLE:
+ case ARGTYPE_STRUCT:
+ case ARGTYPE_SHORT:
+ case ARGTYPE_INT:
+ case ARGTYPE_LONG:
+ case ARGTYPE_USHORT:
+ case ARGTYPE_UINT:
+ case ARGTYPE_ULONG:
+ return toplevel_format_lens(lens, stream, value,
+ arguments, INT_FMT_i);
+
+ case ARGTYPE_CHAR:
+ return format_char(stream, value, arguments);
+
+ case ARGTYPE_ARRAY:
+ return format_array(stream, value, arguments,
+ value->type->u.array_info.length,
+ options.strlen, 0, "\"", "\"", "");
+ }
+ abort();
+}
+
+struct lens string_lens = {
+ .format_cb = string_lens_format_cb,
+};
diff --git a/lens_default.h b/lens_default.h
new file mode 100644
index 0000000..9a9d0c6
--- /dev/null
+++ b/lens_default.h
@@ -0,0 +1,49 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef LENS_DEFAULT_H
+#define LENS_DEFAULT_H
+
+#include "lens.h"
+
+/* Default lens that does reasonable job for most cases. */
+extern struct lens default_lens;
+
+/* A lens that doesn't output anything. */
+extern struct lens blind_lens;
+
+/* A lens that formats integers in octal. */
+extern struct lens octal_lens;
+
+/* A lens that formats integers in hexadecimal. */
+extern struct lens hex_lens;
+
+/* A lens that formats integers as either "true" or "false". */
+extern struct lens bool_lens;
+
+/* A lens that tries to guess whether the value is "large" (i.e. a
+ * pointer, and should be formatted in hex), or "small" (and should be
+ * formatted in decimal). */
+extern struct lens guess_lens;
+
+/* A lens for strings. */
+extern struct lens string_lens;
+
+#endif /* LENS_DEFAULT_H */
diff --git a/lens_enum.c b/lens_enum.c
new file mode 100644
index 0000000..1af94d2
--- /dev/null
+++ b/lens_enum.c
@@ -0,0 +1,163 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include "arch.h"
+#include "lens_enum.h"
+#include "lens_default.h"
+#include "value.h"
+#include "type.h"
+
+struct enum_entry {
+ char *key;
+ int own_key;
+ struct value *value;
+ int own_value;
+};
+
+static void
+enum_entry_dtor(struct enum_entry *entry, void *data)
+{
+ if (entry->own_key)
+ free(entry->key);
+ if (entry->own_value) {
+ value_destroy(entry->value);
+ free(entry->value);
+ }
+}
+
+static void
+enum_lens_destroy_cb(struct lens *lens)
+{
+ struct enum_lens *self = (void *)lens;
+
+ VECT_DESTROY(&self->entries, struct enum_entry,
+ enum_entry_dtor, NULL);
+}
+
+enum {
+#ifdef ARCH_ENDIAN_BIG
+ big_endian = 1,
+#elif defined (ARCH_ENDIAN_LITTLE)
+ big_endian = 0,
+#else
+# error Undefined endianness.
+#endif
+};
+
+/* Returns 0 if they are not equal, >0 if they are, and <0 if there
+ * was an error. */
+static int
+enum_values_equal(struct value *inf_value, struct value *enum_value,
+ struct value_dict *arguments)
+{
+ /* Width may not match between what's defined in config file
+ * and what arrives from the back end. Typically, if there is
+ * a mismatch, the config file size will be larger, as we are
+ * in a situation where 64-bit tracer traces 32-bit process.
+ * But opposite situation can occur e.g. on PPC, where it's
+ * apparently possible for 32-bit tracer to trace 64-bit
+ * inferior, or hypothetically in a x32/x86_64 situation. */
+
+ unsigned char *inf_data = value_get_data(inf_value, arguments);
+ size_t inf_sz = value_size(inf_value, arguments);
+ if (inf_data == NULL || inf_sz == (size_t)-1)
+ return -1;
+
+ assert(inf_value->type->type == enum_value->type->type);
+
+ unsigned char *enum_data = value_get_data(enum_value, arguments);
+ size_t enum_sz = value_size(enum_value, arguments);
+ if (enum_data == NULL || enum_sz == (size_t)-1)
+ return -1;
+
+ size_t sz = enum_sz > inf_sz ? inf_sz : enum_sz;
+
+ if (big_endian)
+ return memcmp(enum_data + enum_sz - sz,
+ inf_data + inf_sz - sz, sz) == 0;
+ else
+ return memcmp(enum_data, inf_data, sz) == 0;
+}
+
+static const char *
+enum_get(struct enum_lens *lens, struct value *value,
+ struct value_dict *arguments)
+{
+ size_t i;
+ for (i = 0; i < vect_size(&lens->entries); ++i) {
+ struct enum_entry *entry = VECT_ELEMENT(&lens->entries,
+ struct enum_entry, i);
+ int st = enum_values_equal(value, entry->value, arguments);
+ if (st < 0)
+ return NULL;
+ else if (st != 0)
+ return entry->key;
+ }
+ return NULL;
+}
+
+static int
+enum_lens_format_cb(struct lens *lens, FILE *stream,
+ struct value *value, struct value_dict *arguments)
+{
+ struct enum_lens *self = (void *)lens;
+
+ long l;
+ if (value_extract_word(value, &l, arguments) < 0)
+ return -1;
+
+ const char *name = enum_get(self, value, arguments);
+ if (name != NULL)
+ return fprintf(stream, "%s", name);
+
+ return lens_format(&default_lens, stream, value, arguments);
+}
+
+
+void
+lens_init_enum(struct enum_lens *lens)
+{
+ *lens = (struct enum_lens){
+ {
+ .format_cb = enum_lens_format_cb,
+ .destroy_cb = enum_lens_destroy_cb,
+ },
+ };
+ VECT_INIT(&lens->entries, struct enum_entry);
+}
+
+int
+lens_enum_add(struct enum_lens *lens,
+ const char *key, int own_key,
+ struct value *value, int own_value)
+{
+ struct enum_entry entry = { (char *)key, own_key, value, own_value };
+ return VECT_PUSHBACK(&lens->entries, &entry);
+}
+
+size_t
+lens_enum_size(struct enum_lens *lens)
+{
+ return vect_size(&lens->entries);
+}
diff --git a/lens_enum.h b/lens_enum.h
new file mode 100644
index 0000000..de7b386
--- /dev/null
+++ b/lens_enum.h
@@ -0,0 +1,48 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef LENS_ENUM_H
+#define LENS_ENUM_H
+
+#include "lens.h"
+#include "vect.h"
+
+struct enum_lens {
+ struct lens super;
+ struct vect entries;
+ int own;
+};
+
+/* Init enumeration LENS. */
+void lens_init_enum(struct enum_lens *lens);
+
+/* Push another member of the enumeration, named KEY, with given
+ * VALUE. If OWN_KEY, KEY is owned and released after the type is
+ * destroyed. KEY is typed as const char *, but note that if OWN_KEY,
+ * this value will be freed. If OWN_VALUE, then VALUE is owned and
+ * destroyed by LENS. */
+int lens_enum_add(struct enum_lens *lens,
+ const char *key, int own_key,
+ struct value *value, int own_value);
+
+/* Return number of enum elements of type INFO. */
+size_t lens_enum_size(struct enum_lens *lens);
+
+#endif /* LENS_ENUM_H */
diff --git a/libltrace.c b/libltrace.c
index 92f2701..f4bb2c8 100644
--- a/libltrace.c
+++ b/libltrace.c
@@ -1,3 +1,24 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2009 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
#include "config.h"
#include <sys/param.h>
@@ -11,6 +32,8 @@
#include "common.h"
#include "proc.h"
+#include "read_config_file.h"
+#include "backend.h"
char *command = NULL;
@@ -84,6 +107,7 @@ ltrace_init(int argc, char **argv) {
signal(SIGTERM, signal_exit); /* ... or killed */
argv = process_options(argc, argv);
+ init_global_config();
while (opt_F) {
/* If filename begins with ~, expand it to the user's home */
/* directory. This does not correctly handle ~yoda, but that */
diff --git a/library.c b/library.c
index 92fccea..2bd7dbb 100644
--- a/library.c
+++ b/library.c
@@ -27,7 +27,7 @@
#include "library.h"
#include "proc.h" // for enum callback_status
#include "debug.h"
-#include "common.h" // for arch_library_symbol_init, arch_library_init
+#include "backend.h" // for arch_library_symbol_init, arch_library_init
#ifndef ARCH_HAVE_LIBRARY_DATA
void
@@ -207,10 +207,18 @@ static void
private_library_init(struct library *lib, enum library_type type)
{
lib->next = NULL;
+
+ lib->key = 0;
+ lib->base = 0;
+ lib->entry = 0;
+ lib->dyn_addr = 0;
+
lib->soname = NULL;
lib->own_soname = 0;
+
lib->pathname = NULL;
lib->own_pathname = 0;
+
lib->symbols = NULL;
lib->type = type;
}
diff --git a/ltrace-elf.c b/ltrace-elf.c
index b1af070..c40a188 100644
--- a/ltrace-elf.c
+++ b/ltrace-elf.c
@@ -1,3 +1,30 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2006,2010,2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2010 Zachary T Welch, CodeSourcery
+ * Copyright (C) 2010 Joe Damato
+ * Copyright (C) 1997,1998,2001,2004,2007,2008,2009 Juan Cespedes
+ * Copyright (C) 2006 Olaf Hering, SUSE Linux GmbH
+ * Copyright (C) 2006 Eric Vaitl, Cisco Systems, Inc.
+ * Copyright (C) 2006 Paul Gilliam, IBM Corporation
+ * Copyright (C) 2006 Ian Wienand
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
#include "config.h"
#include <assert.h>
@@ -13,10 +40,13 @@
#include <string.h>
#include <unistd.h>
-#include "common.h"
-#include "proc.h"
-#include "library.h"
+#include "backend.h"
#include "filter.h"
+#include "library.h"
+#include "ltrace-elf.h"
+#include "proc.h"
+#include "debug.h"
+#include "options.h"
#ifdef PLT_REINITALISATION_BP
extern char *PLTs_initialized_by_here;
@@ -241,8 +271,11 @@ open_elf(struct ltelf *lte, const char *filename)
exit(EXIT_FAILURE);
}
- if ((lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS
- || lte->ehdr.e_machine != LT_ELF_MACHINE)
+ if (1
+#ifdef LT_ELF_MACHINE
+ && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS
+ || lte->ehdr.e_machine != LT_ELF_MACHINE)
+#endif
#ifdef LT_ELF_MACHINE2
&& (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS2
|| lte->ehdr.e_machine != LT_ELF_MACHINE2)
diff --git a/output.c b/output.c
index db6e93e..8bfe3f0 100644
--- a/output.c
+++ b/output.c
@@ -1,3 +1,27 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2010 Joe Damato
+ * Copyright (C) 1997,1998,1999,2001,2002,2003,2004,2007,2008,2009 Juan Cespedes
+ * Copyright (C) 2006 Paul Gilliam
+ * Copyright (C) 2006 Ian Wienand
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
#include "config.h"
#include <stdio.h>
@@ -7,10 +31,18 @@
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
#include "common.h"
#include "proc.h"
#include "library.h"
+#include "type.h"
+#include "value.h"
+#include "value_dict.h"
+#include "param.h"
+#include "fetch.h"
+#include "lens_default.h"
/* TODO FIXME XXX: include in common.h: */
extern struct timeval current_time_spent;
@@ -18,7 +50,7 @@ extern struct timeval current_time_spent;
Dict *dict_opt_c = NULL;
static Process *current_proc = 0;
-static int current_depth = 0;
+static size_t current_depth = 0;
static int current_column = 0;
static void
@@ -29,7 +61,8 @@ output_indent(struct Process *proc)
}
static void
-begin_of_line(enum tof type, Process *proc) {
+begin_of_line(Process *proc, int is_func, int indent)
+{
current_column = 0;
if (!proc) {
return;
@@ -85,40 +118,89 @@ begin_of_line(enum tof type, Process *proc) {
}
}
if (opt_i) {
- if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) {
+ if (is_func)
current_column += fprintf(options.output, "[%p] ",
proc->return_addr);
- } else {
+ else
current_column += fprintf(options.output, "[%p] ",
proc->instruction_pointer);
- }
}
- if (options.indent > 0 && type != LT_TOF_NONE) {
+ if (options.indent > 0 && indent) {
output_indent(proc);
}
}
+static struct arg_type_info *
+get_unknown_type(void)
+{
+ static struct arg_type_info *info = NULL;
+ if (info == NULL) {
+ info = malloc(sizeof(*info));
+ if (info == NULL) {
+ report_global_error("malloc: %s", strerror(errno));
+ abort();
+ }
+ *info = *type_get_simple(ARGTYPE_LONG);
+ info->lens = &guess_lens;
+ }
+ return info;
+}
+
+/* The default prototype is: long X(long, long, long, long). */
+static Function *
+build_default_prototype(void)
+{
+ Function *ret = malloc(sizeof(*ret));
+ size_t i = 0;
+ if (ret == NULL)
+ goto err;
+ memset(ret, 0, sizeof(*ret));
+
+ struct arg_type_info *unknown_type = get_unknown_type();
+
+ ret->return_info = unknown_type;
+ ret->own_return_info = 0;
+
+ ret->num_params = 4;
+ ret->params = malloc(sizeof(*ret->params) * ret->num_params);
+ if (ret->params == NULL)
+ goto err;
+
+ for (i = 0; i < ret->num_params; ++i)
+ param_init_type(&ret->params[i], unknown_type, 0);
+
+ return ret;
+
+err:
+ report_global_error("malloc: %s", strerror(errno));
+ if (ret->params != NULL) {
+ while (i-- > 0)
+ param_destroy(&ret->params[i]);
+ free(ret->params);
+ }
+
+ free(ret);
+
+ return NULL;
+}
+
static Function *
name2func(char const *name) {
Function *tmp;
const char *str1, *str2;
- tmp = list_of_functions;
- while (tmp) {
-#ifdef USE_DEMANGLE
- str1 = options.demangle ? my_demangle(tmp->name) : tmp->name;
- str2 = options.demangle ? my_demangle(name) : name;
-#else
+ for (tmp = list_of_functions; tmp != NULL; tmp = tmp->next) {
str1 = tmp->name;
str2 = name;
-#endif
- if (!strcmp(str1, str2)) {
-
+ if (!strcmp(str1, str2))
return tmp;
- }
- tmp = tmp->next;
}
- return NULL;
+
+ static Function *def = NULL;
+ if (def == NULL)
+ def = build_default_prototype();
+
+ return def;
}
void
@@ -139,7 +221,7 @@ output_line(Process *proc, char *fmt, ...) {
if (!fmt) {
return;
}
- begin_of_line(LT_TOF_NONE, proc);
+ begin_of_line(proc, 0, 0);
va_start(args, fmt);
vfprintf(options.output, fmt, args);
@@ -155,15 +237,191 @@ tabto(int col) {
}
}
+static int
+account_output(int o)
+{
+ if (o < 0)
+ return -1;
+ current_column += o;
+ return 0;
+}
+
+static int
+output_error(void)
+{
+ return account_output(fprintf(options.output, "?"));
+}
+
+static int
+fetch_simple_param(enum tof type, Process *proc, struct fetch_context *context,
+ struct value_dict *arguments, struct arg_type_info *info,
+ struct value *valuep)
+{
+ /* Arrays decay into pointers per C standard. We check for
+ * this here, because here we also capture arrays that come
+ * from parameter packs. */
+ int own = 0;
+ if (info->type == ARGTYPE_ARRAY) {
+ struct arg_type_info *tmp = malloc(sizeof(*tmp));
+ if (tmp != NULL) {
+ type_init_pointer(tmp, info, 0);
+ tmp->lens = info->lens;
+ info = tmp;
+ own = 1;
+ }
+ }
+
+ struct value value;
+ value_init(&value, proc, NULL, info, own);
+ if (fetch_arg_next(context, type, proc, info, &value) < 0)
+ return -1;
+
+ if (val_dict_push_next(arguments, &value) < 0) {
+ value_destroy(&value);
+ return -1;
+ }
+
+ if (valuep != NULL)
+ *valuep = value;
+
+ return 0;
+}
+
+static void
+fetch_param_stop(struct value_dict *arguments, ssize_t *params_leftp)
+{
+ if (*params_leftp == -1)
+ *params_leftp = val_dict_count(arguments);
+}
+
+static int
+fetch_param_pack(enum tof type, Process *proc, struct fetch_context *context,
+ struct value_dict *arguments, struct param *param,
+ ssize_t *params_leftp)
+{
+ struct param_enum *e = param_pack_init(param, arguments);
+ if (e == NULL)
+ return -1;
+
+ int ret = 0;
+ while (1) {
+ int insert_stop = 0;
+ struct arg_type_info *info = malloc(sizeof(*info));
+ if (info == NULL
+ || param_pack_next(param, e, info, &insert_stop) < 0) {
+ fail:
+ free(info);
+ ret = -1;
+ break;
+ }
+
+ if (insert_stop)
+ fetch_param_stop(arguments, params_leftp);
+
+ if (info->type == ARGTYPE_VOID)
+ break;
+
+ struct value val;
+ if (fetch_simple_param(type, proc, context, arguments,
+ info, &val) < 0)
+ goto fail;
+
+ int stop = 0;
+ switch (param_pack_stop(param, e, &val)) {
+ case PPCB_ERR:
+ goto fail;
+ case PPCB_STOP:
+ stop = 1;
+ case PPCB_CONT:
+ break;
+ }
+
+ if (stop)
+ break;
+ }
+
+ param_pack_done(param, e);
+ return ret;
+}
+
+static int
+fetch_one_param(enum tof type, Process *proc, struct fetch_context *context,
+ struct value_dict *arguments, struct param *param,
+ ssize_t *params_leftp)
+{
+ switch (param->flavor) {
+ case PARAM_FLAVOR_TYPE:
+ return fetch_simple_param(type, proc, context, arguments,
+ param->u.type.type, NULL);
+
+ case PARAM_FLAVOR_PACK:
+ return fetch_param_pack(type, proc, context, arguments,
+ param, params_leftp);
+
+ case PARAM_FLAVOR_STOP:
+ fetch_param_stop(arguments, params_leftp);
+ return 0;
+ }
+
+ assert(!"Invalid param flavor!");
+ abort();
+}
+
+static int
+fetch_params(enum tof type, Process *proc, struct fetch_context *context,
+ struct value_dict *arguments, Function *func, ssize_t *params_leftp)
+{
+ size_t i;
+ for (i = 0; i < func->num_params; ++i)
+ if (fetch_one_param(type, proc, context, arguments,
+ &func->params[i], params_leftp) < 0)
+ return -1;
+
+ /* Implicit stop at the end of parameter list. */
+ fetch_param_stop(arguments, params_leftp);
+
+ return 0;
+}
+
+static int
+output_one(struct value *val, struct value_dict *arguments)
+{
+ int o = format_argument(options.output, val, arguments);
+ if (account_output(o) < 0) {
+ if (output_error() < 0)
+ return -1;
+ o = 1;
+ }
+ return o;
+}
+
+static int
+output_params(struct value_dict *arguments, size_t start, size_t end,
+ int *need_delimp)
+{
+ size_t i;
+ int need_delim = *need_delimp;
+ for (i = start; i < end; ++i) {
+ if (need_delim
+ && account_output(fprintf(options.output, ", ")) < 0)
+ return -1;
+ struct value *value = val_dict_get_num(arguments, i);
+ if (value == NULL)
+ return -1;
+ need_delim = output_one(value, arguments);
+ if (need_delim < 0)
+ return -1;
+ }
+ *need_delimp = need_delim;
+ return 0;
+}
+
void
output_left(enum tof type, struct Process *proc,
struct library_symbol *libsym)
{
const char *function_name = libsym->name;
Function *func;
- static arg_type_info *arg_unknown = NULL;
- if (arg_unknown == NULL)
- arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN);
if (options.summary) {
return;
@@ -174,50 +432,47 @@ output_left(enum tof type, struct Process *proc,
}
current_proc = proc;
current_depth = proc->callstack_depth;
- begin_of_line(type, proc);
+ begin_of_line(proc, type == LT_TOF_FUNCTION, 1);
if (!options.hide_caller && libsym->lib != NULL
&& libsym->plt_type != LS_TOPLT_NONE)
current_column += fprintf(options.output, "%s->",
libsym->lib->soname);
+
+ const char *name = function_name;
#ifdef USE_DEMANGLE
- current_column +=
- fprintf(options.output, "%s(",
- (options.demangle
- ? my_demangle(function_name) : function_name));
-#else
- current_column += fprintf(options.output, "%s(", function_name);
+ if (options.demangle)
+ name = my_demangle(function_name);
#endif
+ if (account_output(fprintf(options.output, "%s(", name)) < 0)
+ return;
func = name2func(function_name);
- if (!func) {
- int i;
- for (i = 0; i < 4; i++) {
- current_column +=
- display_arg(type, proc, i, arg_unknown);
- current_column += fprintf(options.output, ", ");
- }
- current_column += display_arg(type, proc, 4, arg_unknown);
+ if (func == NULL)
return;
- } else {
- int i;
- for (i = 0; i < func->num_params - func->params_right - 1; i++) {
- current_column +=
- display_arg(type, proc, i, func->arg_info[i]);
- current_column += fprintf(options.output, ", ");
- }
- if (func->num_params > func->params_right) {
- current_column +=
- display_arg(type, proc, i, func->arg_info[i]);
- if (func->params_right) {
- current_column += fprintf(options.output, ", ");
- }
- }
- if (func->params_right
- || func->return_info->type == ARGTYPE_STRING_N
- || func->return_info->type == ARGTYPE_ARRAY) {
- save_register_args(type, proc);
- }
+
+ struct fetch_context *context = fetch_arg_init(type, proc,
+ func->return_info);
+ struct value_dict *arguments = malloc(sizeof(*arguments));
+ if (arguments == NULL)
+ return;
+ val_dict_init(arguments);
+
+ ssize_t params_left = -1;
+ int need_delim = 0;
+ if (fetch_params(type, proc, context, arguments, func, ¶ms_left) < 0
+ || output_params(arguments, 0, params_left, &need_delim) < 0) {
+ val_dict_destroy(arguments);
+ fetch_arg_done(context);
+ arguments = NULL;
+ context = NULL;
}
+
+ struct callstack_element *stel
+ = &proc->callstack[proc->callstack_depth - 1];
+ stel->fetch_context = context;
+ stel->arguments = arguments;
+ stel->out.params_left = params_left;
+ stel->out.need_delim = need_delim;
}
void
@@ -225,9 +480,8 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym)
{
const char *function_name = libsym->name;
Function *func = name2func(function_name);
- static arg_type_info *arg_unknown = NULL;
- if (arg_unknown == NULL)
- arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN);
+ if (func == NULL)
+ return;
if (options.summary) {
struct opt_c_struct *st;
@@ -268,7 +522,7 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym)
current_proc = 0;
}
if (current_proc != proc) {
- begin_of_line(type, proc);
+ begin_of_line(proc, type == LT_TOF_FUNCTIONR, 1);
#ifdef USE_DEMANGLE
current_column +=
fprintf(options.output, "<... %s resumed> ",
@@ -279,32 +533,48 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym)
#endif
}
- if (!func) {
- current_column += fprintf(options.output, ") ");
- tabto(options.align - 1);
- fprintf(options.output, "= ");
- display_arg(type, proc, -1, arg_unknown);
- } else {
- int i;
- for (i = func->num_params - func->params_right;
- i < func->num_params - 1; i++) {
- current_column +=
- display_arg(type, proc, i, func->arg_info[i]);
- current_column += fprintf(options.output, ", ");
- }
- if (func->params_right) {
- current_column +=
- display_arg(type, proc, i, func->arg_info[i]);
- }
- current_column += fprintf(options.output, ") ");
- tabto(options.align - 1);
- fprintf(options.output, "= ");
- if (func->return_info->type == ARGTYPE_VOID) {
- fprintf(options.output, "<void>");
- } else {
- display_arg(type, proc, -1, func->return_info);
+ struct callstack_element *stel
+ = &proc->callstack[proc->callstack_depth - 1];
+
+ struct fetch_context *context = stel->fetch_context;
+
+ /* Fetch & enter into dictionary the retval first, so that
+ * other values can use it in expressions. */
+ struct value retval;
+ int own_retval = 0;
+ if (context != NULL) {
+ value_init(&retval, proc, NULL, func->return_info, 0);
+ own_retval = 1;
+ if (fetch_retval(context, type, proc, func->return_info,
+ &retval) == 0) {
+ if (stel->arguments != NULL
+ && val_dict_push_named(stel->arguments, &retval,
+ "retval", 0) == 0)
+ own_retval = 0;
}
}
+
+ if (stel->arguments != NULL)
+ output_params(stel->arguments, stel->out.params_left,
+ val_dict_count(stel->arguments),
+ &stel->out.need_delim);
+
+ current_column += fprintf(options.output, ") ");
+ tabto(options.align - 1);
+ fprintf(options.output, "= ");
+
+ output_one(&retval, stel->arguments);
+
+ if (own_retval)
+ value_destroy(&retval);
+
+ if (stel->arguments != NULL) {
+ val_dict_destroy(stel->arguments);
+ free(stel->arguments);
+ }
+ if (context != NULL)
+ fetch_arg_done(context);
+
if (opt_T) {
fprintf(options.output, " <%lu.%06d>",
current_time_spent.tv_sec,
@@ -336,3 +606,44 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym)
current_proc = 0;
current_column = 0;
}
+
+static void
+do_report(const char *filename, unsigned line_no, const char *severity,
+ const char *fmt, va_list args)
+{
+ char buf[128];
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ buf[sizeof(buf) - 1] = 0;
+ if (filename != NULL)
+ output_line(0, "%s:%d: %s: %s",
+ filename, line_no, severity, buf);
+ else
+ output_line(0, "%s: %s", severity, buf);
+}
+
+void
+report_error(const char *filename, unsigned line_no, char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ do_report(filename, line_no, "error", fmt, args);
+ va_end(args);
+}
+
+void
+report_warning(const char *filename, unsigned line_no, char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ do_report(filename, line_no, "warning", fmt, args);
+ va_end(args);
+}
+
+void
+report_global_error(char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ do_report(NULL, 0, "error", fmt, args);
+ va_end(args);
+}
diff --git a/output.h b/output.h
index 714078f..481a385 100644
--- a/output.h
+++ b/output.h
@@ -1,7 +1,33 @@
-struct Process;
-struct library_symbol;
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2009 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include "fetch.h"
+
+#include "forward.h"
+
void output_line(struct Process *proc, char *fmt, ...);
void output_left(enum tof type, struct Process *proc,
struct library_symbol *libsym);
void output_right(enum tof type, struct Process *proc,
struct library_symbol *libsym);
+
+void report_error(char const *file, unsigned line_no, char *fmt, ...);
+void report_warning(char const *file, unsigned line_no, char *fmt, ...);
+void report_global_error(char *fmt, ...);
diff --git a/param.c b/param.c
new file mode 100644
index 0000000..7715571
--- /dev/null
+++ b/param.c
@@ -0,0 +1,140 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "param.h"
+#include "type.h"
+#include "value.h"
+#include "expr.h"
+
+void
+param_init_type(struct param *param, struct arg_type_info *type, int own)
+{
+ param->flavor = PARAM_FLAVOR_TYPE;
+ param->u.type.type = type;
+ param->u.type.own_type = own;
+}
+
+void
+param_init_stop(struct param *param)
+{
+ param->flavor = PARAM_FLAVOR_STOP;
+}
+
+void
+param_init_pack(struct param *param,
+ struct expr_node *args, size_t nargs, int own_args,
+ struct param_enum *(*init)(struct value *cb_args,
+ size_t nargs,
+ struct value_dict *arguments),
+ int (*next)(struct param_enum *context,
+ struct arg_type_info *infop,
+ int *insert_stop),
+ enum param_status (*stop)(struct param_enum *ctx,
+ struct value *value),
+ void (*done)(struct param_enum *))
+{
+ param->flavor = PARAM_FLAVOR_PACK;
+ param->u.pack.args = args;
+ param->u.pack.nargs = nargs;
+ param->u.pack.own_args = own_args;
+ param->u.pack.init = init;
+ param->u.pack.next = next;
+ param->u.pack.stop = stop;
+ param->u.pack.done = done;
+}
+
+struct param_enum *
+param_pack_init(struct param *param, struct value_dict *fargs)
+{
+ struct value cb_args[param->u.pack.nargs];
+ size_t i;
+
+ /* For evaluation of argument expressions, we pass in this as
+ * a "current" value. */
+ struct arg_type_info *void_type = type_get_simple(ARGTYPE_VOID);
+ struct value void_val;
+ value_init_detached(&void_val, NULL, void_type, 0);
+
+ struct param_enum *ret = NULL;
+ for (i = 0; i < param->u.pack.nargs; ++i) {
+ if (expr_eval(¶m->u.pack.args[i], &void_val,
+ fargs, &cb_args[i]) < 0)
+ goto release;
+ }
+
+ ret = param->u.pack.init(cb_args, param->u.pack.nargs, fargs);
+
+release:
+ while (i-- > 0)
+ value_destroy(&cb_args[i]);
+ return ret;
+}
+
+int
+param_pack_next(struct param *param, struct param_enum *context,
+ struct arg_type_info *infop, int *insert_stop)
+{
+ return param->u.pack.next(context, infop, insert_stop);
+}
+
+enum param_status
+param_pack_stop(struct param *param,
+ struct param_enum *context, struct value *value)
+{
+ return param->u.pack.stop(context, value);
+}
+
+void
+param_pack_done(struct param *param, struct param_enum *context)
+{
+ return param->u.pack.done(context);
+}
+
+void
+param_destroy(struct param *param)
+{
+ if (param == NULL)
+ return;
+
+ switch (param->flavor) {
+ case PARAM_FLAVOR_TYPE:
+ if (param->u.type.own_type) {
+ type_destroy(param->u.type.type);
+ free(param->u.type.type);
+ }
+ return;
+
+ case PARAM_FLAVOR_PACK:
+ if (param->u.pack.own_args) {
+ size_t i;
+ for (i = 0; i < param->u.pack.nargs; ++i)
+ expr_destroy(¶m->u.pack.args[i]);
+ free(param->u.pack.args);
+ }
+ case PARAM_FLAVOR_STOP:
+ return;
+ }
+
+ assert(!"Unknown value of param flavor!");
+ abort();
+}
diff --git a/param.h b/param.h
new file mode 100644
index 0000000..5882689
--- /dev/null
+++ b/param.h
@@ -0,0 +1,150 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef PARAM_H
+#define PARAM_H
+
+#include "forward.h"
+
+/* The structure param holds information about a parameter of a
+ * function. It's used to configure a function prototype. There are
+ * two flavors of parameters:
+ *
+ * - simple types
+ * - parameter packs
+ *
+ * Parameter packs are used to describe various vararg constructs.
+ * They themselves are parametrized by ltrace expressions. Those will
+ * typically be references to other arguments, but constants might
+ * also make sense, and it principle, anything can be used. */
+
+enum param_flavor {
+ PARAM_FLAVOR_TYPE,
+ PARAM_FLAVOR_PACK,
+
+ /* This is for emitting arguments in two bunches. This is
+ * where we should stop emitting "left" bunch. All that's
+ * after this parameter should be emitted in the "right"
+ * bunch. */
+ PARAM_FLAVOR_STOP,
+};
+
+enum param_status {
+ PPCB_ERR = -1, /* An error occurred. */
+ PPCB_STOP, /* Stop fetching the arguments. */
+ PPCB_CONT, /* Display this argument and keep going. */
+};
+
+/* Each parameter enumerator defines its own context object.
+ * Definitions of these are in respective .c files of each
+ * enumerator. */
+struct param_enum;
+
+/* int printf(string, pack(format, arg1)); */
+struct param {
+ enum param_flavor flavor;
+ union {
+ struct {
+ struct arg_type_info *type;
+ int own_type;
+ } type;
+ struct {
+ struct expr_node *args;
+ size_t nargs;
+ int own_args;
+
+ struct param_enum *(*init)(struct value *cb_args,
+ size_t nargs,
+ struct value_dict *arguments);
+ int (*next)(struct param_enum *self,
+ struct arg_type_info *info,
+ int *insert_stop);
+ enum param_status (*stop)(struct param_enum *self,
+ struct value *value);
+ void (*done)(struct param_enum *self);
+ } pack;
+ } u;
+};
+
+/* Initialize simple type parameter. TYPE is owned and released by
+ * PARAM if OWN_TYPE. */
+void param_init_type(struct param *param,
+ struct arg_type_info *type, int own_type);
+
+/* Initialize a stop. */
+void param_init_stop(struct param *param);
+
+/* Initialize parameter pack PARAM. ARGS is an array of expressions
+ * with parameters. ARGS is owned and released by the pack if
+ * OWN_ARGS. NARGS is number of ARGS.
+ *
+ * When the parameter pack should be expanded, those expressions are
+ * evaluated and passed to the INIT callback. This has to return a
+ * non-NULL context object.
+ *
+ * The NEXT callback is then called repeatedly, and should initialize
+ * its INFOP argument to a type of the next parameter in the pack.
+ * When there are no more parameters in the pack, the NEXT callback
+ * will set INFOP to a VOID parameter. If the callback sets
+ * INSERT_STOP to a non-zero value, a stop parameter shall be inserted
+ * before this actual parameter.
+ *
+ * Core then uses the passed-in type to fetch the next argument, which
+ * is in turn passed to STOP callback. This callback then tells
+ * ltrace core what to do next: whether there are more arguments, and
+ * if not, whether this argument should be displayed.
+ *
+ * After the enumeration is ended, DONE callback is called. */
+void param_init_pack(struct param *param,
+ struct expr_node *args, size_t nargs, int own_args,
+ struct param_enum *(*init)(struct value *cb_args,
+ size_t nargs,
+ struct value_dict *arguments),
+ int (*next)(struct param_enum *self,
+ struct arg_type_info *infop,
+ int *insert_stop),
+ enum param_status (*stop)(struct param_enum *self,
+ struct value *value),
+ void (*done)(struct param_enum *self));
+
+/* Start enumerating types in parameter pack. This evaluates the
+ * parameter the pack arguments and calls the init callback. See the
+ * documentation of param_init_pack for details. */
+struct param_enum *param_pack_init(struct param *param,
+ struct value_dict *fargs);
+
+/* Ask for next type in enumeration. See the documentation of
+ * param_init_pack for details. */
+int param_pack_next(struct param *param, struct param_enum *self,
+ struct arg_type_info *infop, int *insert_stop);
+
+/* Ask whether we should stop enumerating. See the documentation of
+ * param_init_pack for details. */
+enum param_status param_pack_stop(struct param *param,
+ struct param_enum *self, struct value *value);
+
+/* Finish enumerating types in parameter pack. See the documentation
+ * of param_init_pack for details. */
+void param_pack_done(struct param *param, struct param_enum *self);
+
+/* Destroy data held by PARAM, but not the PARAM pointer itself. */
+void param_destroy(struct param *param);
+
+#endif /* PARAM_H */
diff --git a/printf.c b/printf.c
new file mode 100644
index 0000000..1fe3025
--- /dev/null
+++ b/printf.c
@@ -0,0 +1,351 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 1998,2004,2007,2008,2009 Juan Cespedes
+ * Copyright (C) 2006 Steve Fink
+ * Copyright (C) 2006 Ian Wienand
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "printf.h"
+#include "type.h"
+#include "value.h"
+#include "expr.h"
+#include "zero.h"
+#include "param.h"
+#include "lens_default.h"
+
+struct param_enum {
+ struct value array;
+ int percent;
+ size_t *future_length;
+ char *format;
+ char const *ptr;
+ char const *end;
+};
+
+static struct param_enum *
+param_printf_init(struct value *cb_args, size_t nargs,
+ struct value_dict *arguments)
+{
+ assert(nargs == 1);
+
+ /* We expect a char array pointer. */
+ if (cb_args->type->type != ARGTYPE_POINTER
+ || cb_args->type->u.ptr_info.info->type != ARGTYPE_ARRAY
+ || (cb_args->type->u.ptr_info.info->u.array_info.elt_type->type
+ != ARGTYPE_CHAR))
+ return NULL;
+
+ struct param_enum *self = malloc(sizeof(*self));
+ if (self == NULL) {
+ fail:
+ free(self);
+ return NULL;
+ }
+
+ if (value_init_deref(&self->array, cb_args) < 0)
+ goto fail;
+
+ assert(self->array.type->type == ARGTYPE_ARRAY);
+
+ self->format = (char *)value_get_data(&self->array, arguments);
+ if (self->format == NULL)
+ goto fail;
+
+ size_t size = value_size(&self->array, arguments);
+ if (size == (size_t)-1)
+ goto fail;
+
+ self->percent = 0;
+ self->ptr = self->format;
+ self->end = self->format + size;
+ self->future_length = NULL;
+ return self;
+}
+
+static void
+drop_future_length(struct param_enum *self)
+{
+ if (self->future_length != NULL) {
+ free(self->future_length);
+ self->future_length = NULL;
+ }
+}
+
+static int
+form_next_param(struct param_enum *self,
+ enum arg_type format_type, enum arg_type elt_type,
+ unsigned hlf, unsigned lng, char *len_buf, size_t len_buf_len,
+ struct arg_type_info *infop)
+{
+ /* XXX note: Some types are wrong because we lack
+ ARGTYPE_LONGLONG, ARGTYPE_UCHAR and ARGTYPE_SCHAR. */
+ assert(lng <= 2);
+ assert(hlf <= 2);
+ static enum arg_type ints[] =
+ { ARGTYPE_CHAR, ARGTYPE_SHORT, ARGTYPE_INT,
+ ARGTYPE_LONG, ARGTYPE_ULONG };
+ static enum arg_type uints[] =
+ { ARGTYPE_CHAR, ARGTYPE_USHORT, ARGTYPE_UINT,
+ ARGTYPE_ULONG, ARGTYPE_ULONG };
+
+ struct arg_type_info *elt_info = NULL;
+ if (format_type == ARGTYPE_ARRAY || format_type == ARGTYPE_POINTER)
+ elt_info = type_get_simple(elt_type);
+ else if (format_type == ARGTYPE_INT)
+ format_type = ints[2 + lng - hlf];
+ else if (format_type == ARGTYPE_UINT)
+ format_type = uints[2 + lng - hlf];
+
+
+ if (format_type == ARGTYPE_ARRAY) {
+ struct expr_node *node = NULL;
+ if (len_buf_len != 0
+ || self->future_length != NULL) {
+ struct tmp {
+ struct expr_node node;
+ struct arg_type_info type;
+ };
+ struct tmp *len = malloc(sizeof(*len));
+ if (len == NULL) {
+ fail:
+ free(len);
+ return -1;
+ }
+
+ len->type.type = ARGTYPE_LONG;
+
+ long l;
+ if (self->future_length != NULL) {
+ l = *self->future_length;
+ drop_future_length(self);
+ } else {
+ l = atol(len_buf);
+ }
+
+ expr_init_const_word(&len->node, l, &len->type, 0);
+
+ node = build_zero_w_arg(&len->node, 1);
+ if (node == NULL)
+ goto fail;
+
+ } else {
+ node = expr_node_zero();
+ }
+
+ assert(node != NULL);
+ type_init_array(infop, elt_info, 0, node, 1);
+
+ } else if (format_type == ARGTYPE_POINTER) {
+ type_init_pointer(infop, elt_info, 1);
+
+ } else {
+ *infop = *type_get_simple(format_type);
+ }
+
+ return 0;
+}
+
+static int
+param_printf_next(struct param_enum *self, struct arg_type_info *infop,
+ int *insert_stop)
+{
+ unsigned hlf = 0;
+ unsigned lng = 0;
+ enum arg_type format_type = ARGTYPE_VOID;
+ enum arg_type elt_type = ARGTYPE_VOID;
+ char len_buf[25] = {};
+ size_t len_buf_len = 0;
+ struct lens *lens = NULL;
+
+ for (; self->ptr < self->end; ++self->ptr) {
+ if (!self->percent) {
+ if (*self->ptr == '%')
+ self->percent = 1;
+ continue;
+ }
+
+ switch (*self->ptr) {
+ case '#': case ' ': case '-':
+ case '+': case 'I': case '\'':
+ /* These are only important for formatting,
+ * not for interpreting the type. */
+ continue;
+
+ case '*':
+ /* Length parameter given in the next
+ * argument. */
+ if (self->future_length == NULL)
+ /* This should really be an assert,
+ * but we can't just fail on invalid
+ * format string. */
+ self->future_length
+ = malloc(sizeof(*self->future_length));
+
+ if (self->future_length != NULL) {
+ ++self->ptr;
+ format_type = ARGTYPE_INT;
+ break;
+ }
+
+ case '0':
+ case '1': case '2': case '3':
+ case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ /* Field length likewise, but we need to parse
+ * this to attach the appropriate string
+ * length expression. */
+ if (len_buf_len < sizeof(len_buf) - 1)
+ len_buf[len_buf_len++] = *self->ptr;
+ continue;
+
+ case 'h':
+ if (hlf < 2)
+ hlf++;
+ continue;
+
+ case 'l':
+ if (lng < 2)
+ lng++;
+ continue;
+
+ case 'q':
+ lng = 2;
+ continue;
+
+ case 'L': /* long double */
+ lng = 1;
+ continue;
+
+ case 'j': /* intmax_t */
+ /* XXX ABI should know */
+ lng = 2;
+ continue;
+
+ case 't': /* ptrdiff_t */
+ case 'Z': case 'z': /* size_t */
+ lng = 1; /* XXX ABI should tell */
+ continue;
+
+ case 'd':
+ case 'i':
+ format_type = ARGTYPE_INT;
+ self->percent = 0;
+ break;
+
+ case 'o':
+ lens = &octal_lens;
+ goto uint;
+
+ case 'x': case 'X':
+ lens = &hex_lens;
+ case 'u':
+ uint:
+ format_type = ARGTYPE_UINT;
+ self->percent = 0;
+ break;
+
+ case 'e': case 'E':
+ case 'f': case 'F':
+ case 'g': case 'G':
+ case 'a': case 'A':
+ format_type = ARGTYPE_DOUBLE;
+ self->percent = 0;
+ break;
+
+ case 'C': /* like "lc" */
+ if (lng == 0)
+ lng++;
+ case 'c':
+ /* XXX "lc" means wchar_t string. */
+ format_type = ARGTYPE_CHAR;
+ self->percent = 0;
+ break;
+
+ case 'S': /* like "ls" */
+ if (lng == 0)
+ lng++;
+ case 's':
+ format_type = ARGTYPE_ARRAY;
+ /* XXX "ls" means wchar_t string. */
+ elt_type = ARGTYPE_CHAR;
+ self->percent = 0;
+ lens = &string_lens;
+ break;
+
+ case 'p':
+ case 'n': /* int* where to store no. of printed chars. */
+ format_type = ARGTYPE_POINTER;
+ elt_type = ARGTYPE_VOID;
+ self->percent = 0;
+ break;
+
+ case 'm': /* (glibc) print argument of errno */
+ case '%':
+ lng = 0;
+ hlf = 0;
+ self->percent = 0;
+ continue;
+
+ default:
+ continue;
+ }
+
+ /* If we got here, the type must have been set. */
+ assert(format_type != ARGTYPE_VOID);
+
+ if (form_next_param(self, format_type, elt_type, hlf, lng,
+ len_buf, len_buf_len, infop) < 0)
+ return -1;
+
+ infop->lens = lens;
+ infop->own_lens = 0;
+
+ return 0;
+ }
+
+ infop->type = ARGTYPE_VOID;
+ return 0;
+}
+
+static enum param_status
+param_printf_stop(struct param_enum *self, struct value *value)
+{
+ if (self->future_length != NULL
+ && value_extract_word(value, (long *)self->future_length, NULL) < 0)
+ drop_future_length(self);
+
+ return PPCB_CONT;
+}
+
+static void
+param_printf_done(struct param_enum *context)
+{
+ free(context);
+}
+
+void
+param_pack_init_printf(struct param *param, struct expr_node *arg, int own_arg)
+{
+ param_init_pack(param, arg, 1, own_arg,
+ ¶m_printf_init, ¶m_printf_next,
+ ¶m_printf_stop, ¶m_printf_done);
+}
diff --git a/printf.h b/printf.h
new file mode 100644
index 0000000..983c951
--- /dev/null
+++ b/printf.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef PRINTF_H
+#define PRINTF_H
+
+#include <stddef.h>
+#include "forward.h"
+
+/* Wrapper around param_init_pack with callbacks to handle
+ * printf-style format strings. ARG should be an expression that
+ * evaluates to a pointer to a character array with the format string
+ * contents. */
+void param_pack_init_printf(struct param *param,
+ struct expr_node *arg, int own_arg);
+
+#endif /* PRINTF_H */
diff --git a/proc.c b/proc.c
index 3e6d5cb..b280df8 100644
--- a/proc.c
+++ b/proc.c
@@ -1,20 +1,45 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2010 Joe Damato
+ * Copyright (C) 1998,2009 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
#include "config.h"
+#include <sys/types.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
#if defined(HAVE_LIBUNWIND)
#include <libunwind.h>
#include <libunwind-ptrace.h>
#endif /* defined(HAVE_LIBUNWIND) */
-#include <sys/types.h>
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <assert.h>
-
-#include "common.h"
+#include "backend.h"
#include "breakpoint.h"
+#include "debug.h"
+#include "fetch.h"
#include "proc.h"
+#include "value_dict.h"
#ifndef ARCH_HAVE_PROCESS_DATA
int
@@ -237,7 +262,7 @@ int
process_clone(struct Process *retp, struct Process *proc, pid_t pid)
{
if (process_bare_init(retp, proc->filename, pid, 0) < 0) {
- fail:
+ fail1:
fprintf(stderr, "failed to clone process %d->%d : %s\n",
proc->pid, pid, strerror(errno));
return -1;
@@ -268,7 +293,7 @@ process_clone(struct Process *retp, struct Process *proc, pid_t pid)
free(lib);
lib = next;
}
- goto fail;
+ goto fail1;
}
nlibp = &(*nlibp)->next;
@@ -282,16 +307,57 @@ process_clone(struct Process *retp, struct Process *proc, pid_t pid)
.error = 0,
};
dict_apply_to_all(proc->breakpoints, &clone_single_bp, &data);
+ if (data.error < 0)
+ goto fail2;
/* And finally the call stack. */
memcpy(retp->callstack, proc->callstack, sizeof(retp->callstack));
retp->callstack_depth = proc->callstack_depth;
- if (data.error < 0)
- goto fail2;
+ size_t i;
+ for (i = 0; i < retp->callstack_depth; ++i) {
+ struct fetch_context *ctx = retp->callstack[i].fetch_context;
+ if (ctx != NULL) {
+ struct fetch_context *nctx = fetch_arg_clone(retp, ctx);
+ if (nctx == NULL) {
+ size_t j;
+ fail3:
+ for (j = 0; j < i; ++j) {
+ nctx = retp->callstack[i].fetch_context;
+ fetch_arg_done(nctx);
+ retp->callstack[i].fetch_context = NULL;
+ }
+ goto fail2;
+ }
+ retp->callstack[i].fetch_context = nctx;
+ }
+
+ struct value_dict *args = retp->callstack[i].arguments;
+ if (args != NULL) {
+ struct value_dict *nargs = malloc(sizeof(*nargs));
+ if (nargs == NULL
+ || val_dict_clone(nargs, args) < 0) {
+ size_t j;
+ fail4:
+ for (j = 0; j < i; ++j) {
+ nargs = retp->callstack[i].arguments;
+ val_dict_destroy(nargs);
+ free(nargs);
+ retp->callstack[i].arguments = NULL;
+ }
+
+ /* Pretend that this round went well,
+ * so that fail3 frees I-th
+ * fetch_context. */
+ ++i;
+ goto fail3;
+ }
+ retp->callstack[i].arguments = nargs;
+ }
+ }
if (arch_process_clone(retp, proc) < 0)
- goto fail2;
+ goto fail4;
return 0;
}
@@ -528,23 +594,6 @@ clear_leader(struct Process *proc, void *data)
return CBS_CONT;
}
-static enum ecb_status
-event_for_proc(Event * event, void * data)
-{
- if (event->proc == data)
- return ecb_deque;
- else
- return ecb_cont;
-}
-
-static void
-delete_events_for(Process * proc)
-{
- Event * event;
- while ((event = each_qd_event(&event_for_proc, proc)) != NULL)
- free(event);
-}
-
void
remove_process(Process *proc)
{
@@ -554,7 +603,7 @@ remove_process(Process *proc)
each_task(proc, NULL, &clear_leader, NULL);
unlist_process(proc);
- delete_events_for(proc);
+ process_removed(proc);
process_destroy(proc);
free(proc);
}
diff --git a/proc.h b/proc.h
index 443bd8e..86bf7da 100644
--- a/proc.h
+++ b/proc.h
@@ -27,6 +27,8 @@
#ifndef _PROC_H_
#define _PROC_H_
+#include "config.h"
+
#if defined(HAVE_LIBUNWIND)
# include <libunwind.h>
#endif /* defined(HAVE_LIBUNWIND) */
@@ -60,6 +60,11 @@ enum process_state {
STATE_IGNORED /* ignore this process (it's a fork and no -f was used) */
};
+struct output_state {
+ size_t params_left;
+ int need_delim;
+};
+
struct callstack_element {
union {
int syscall;
@@ -68,7 +73,9 @@ struct callstack_element {
int is_syscall;
void * return_addr;
struct timeval time_spent;
- void * arch_ptr;
+ struct fetch_context *fetch_context;
+ struct value_dict *arguments;
+ struct output_state out;
};
/* XXX We should get rid of this. */
@@ -96,7 +103,7 @@ struct Process {
unsigned int personality;
int tracesysgood; /* signal indicating a PTRACE_SYSCALL trap */
- int callstack_depth;
+ size_t callstack_depth;
struct callstack_element callstack[MAX_CALLDEPTH];
/* Linked list of libraries in backwards order of mapping.
diff --git a/read_config_file.c b/read_config_file.c
index b4b1b56..87e87e7 100644
--- a/read_config_file.c
+++ b/read_config_file.c
@@ -1,95 +1,100 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 1998,1999,2003,2007,2008,2009 Juan Cespedes
+ * Copyright (C) 2006 Ian Wienand
+ * Copyright (C) 2006 Steve Fink
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
+#include <errno.h>
+#include <error.h>
+#include <assert.h>
#include "common.h"
+#include "output.h"
+#include "expr.h"
+#include "param.h"
+#include "printf.h"
+#include "zero.h"
+#include "type.h"
+#include "lens.h"
+#include "lens_default.h"
+#include "lens_enum.h"
static int line_no;
static char *filename;
-static int error_count = 0;
-static arg_type_info *parse_type(char **str);
+static struct arg_type_info *parse_nonpointer_type(char **str,
+ struct param **extra_param,
+ size_t param_num, int *ownp);
+static struct arg_type_info *parse_type(char **str, struct param **extra_param,
+ size_t param_num, int *ownp);
+static struct arg_type_info *parse_lens(char **str, struct param **extra_param,
+ size_t param_num, int *ownp);
+static int parse_enum(char **str, struct arg_type_info **retp, int *ownp);
Function *list_of_functions = NULL;
-/* Map of strings to type names. These do not need to be in any
- * particular order */
-static struct list_of_pt_t {
- char *name;
- enum arg_type pt;
-} list_of_pt[] = {
- {
- "void", ARGTYPE_VOID}, {
- "int", ARGTYPE_INT}, {
- "uint", ARGTYPE_UINT}, {
- "long", ARGTYPE_LONG}, {
- "ulong", ARGTYPE_ULONG}, {
- "octal", ARGTYPE_OCTAL}, {
- "char", ARGTYPE_CHAR}, {
- "short", ARGTYPE_SHORT}, {
- "ushort", ARGTYPE_USHORT}, {
- "float", ARGTYPE_FLOAT}, {
- "double", ARGTYPE_DOUBLE}, {
- "addr", ARGTYPE_ADDR}, {
- "file", ARGTYPE_FILE}, {
- "format", ARGTYPE_FORMAT}, {
- "string", ARGTYPE_STRING}, {
- "array", ARGTYPE_ARRAY}, {
- "struct", ARGTYPE_STRUCT}, {
- "enum", ARGTYPE_ENUM}, {
- NULL, ARGTYPE_UNKNOWN} /* Must finish with NULL */
-};
-
-/* Array of prototype objects for each of the types. The order in this
- * array must exactly match the list of enumerated values in
- * common.h */
-static arg_type_info arg_type_prototypes[] = {
- { ARGTYPE_VOID },
- { ARGTYPE_INT },
- { ARGTYPE_UINT },
- { ARGTYPE_LONG },
- { ARGTYPE_ULONG },
- { ARGTYPE_OCTAL },
- { ARGTYPE_CHAR },
- { ARGTYPE_SHORT },
- { ARGTYPE_USHORT },
- { ARGTYPE_FLOAT },
- { ARGTYPE_DOUBLE },
- { ARGTYPE_ADDR },
- { ARGTYPE_FILE },
- { ARGTYPE_FORMAT },
- { ARGTYPE_STRING },
- { ARGTYPE_STRING_N },
- { ARGTYPE_ARRAY },
- { ARGTYPE_ENUM },
- { ARGTYPE_STRUCT },
- { ARGTYPE_POINTER },
- { ARGTYPE_UNKNOWN }
-};
-
-arg_type_info *
-lookup_prototype(enum arg_type at) {
- if (at >= 0 && at <= ARGTYPE_COUNT)
- return &arg_type_prototypes[at];
- else
- return &arg_type_prototypes[ARGTYPE_COUNT]; /* UNKNOWN */
-}
-
-static arg_type_info *
-str2type(char **str) {
- struct list_of_pt_t *tmp = &list_of_pt[0];
-
- while (tmp->name) {
- if (!strncmp(*str, tmp->name, strlen(tmp->name))
- && index(" ,()#*;012345[", *(*str + strlen(tmp->name)))) {
- *str += strlen(tmp->name);
- return lookup_prototype(tmp->pt);
- }
- tmp++;
- }
- return lookup_prototype(ARGTYPE_UNKNOWN);
+static int
+parse_arg_type(char **name, enum arg_type *ret)
+{
+ char *rest = NULL;
+ enum arg_type candidate;
+
+#define KEYWORD(KWD, TYPE) \
+ do { \
+ if (strncmp(*name, KWD, sizeof(KWD) - 1) == 0) { \
+ rest = *name + sizeof(KWD) - 1; \
+ candidate = TYPE; \
+ goto ok; \
+ } \
+ } while (0)
+
+ KEYWORD("void", ARGTYPE_VOID);
+ KEYWORD("int", ARGTYPE_INT);
+ KEYWORD("uint", ARGTYPE_UINT);
+ KEYWORD("long", ARGTYPE_LONG);
+ KEYWORD("ulong", ARGTYPE_ULONG);
+ KEYWORD("char", ARGTYPE_CHAR);
+ KEYWORD("short", ARGTYPE_SHORT);
+ KEYWORD("ushort", ARGTYPE_USHORT);
+ KEYWORD("float", ARGTYPE_FLOAT);
+ KEYWORD("double", ARGTYPE_DOUBLE);
+ KEYWORD("array", ARGTYPE_ARRAY);
+ KEYWORD("struct", ARGTYPE_STRUCT);
+
+ assert(rest == NULL);
+ return -1;
+
+#undef KEYWORD
+
+ok:
+ if (isalnum(*rest))
+ return -1;
+
+ *name = rest;
+ *ret = candidate;
+ return 0;
}
static void
@@ -102,6 +107,10 @@ eat_spaces(char **str) {
static char *
xstrndup(char *str, size_t len) {
char *ret = (char *) malloc(len + 1);
+ if (ret == NULL) {
+ report_global_error("malloc: %s", strerror(errno));
+ return NULL;
+ }
strncpy(ret, str, len);
ret[len] = 0;
return ret;
@@ -111,10 +120,8 @@ static char *
parse_ident(char **str) {
char *ident = *str;
- if (!isalnum(**str) && **str != '_') {
- output_line(0, "Syntax error in `%s', line %d: Bad identifier",
- filename, line_no);
- error_count++;
+ if (!isalpha(**str) && **str != '_') {
+ report_error(filename, line_no, "bad identifier");
return NULL;
}
@@ -158,61 +165,189 @@ start_of_arg_sig(char *str) {
}
static int
-parse_int(char **str) {
+parse_int(char **str, long *ret)
+{
char *end;
long n = strtol(*str, &end, 0);
if (end == *str) {
- output_line(0, "Syntax error in `%s', line %d: Bad number (%s)",
- filename, line_no, *str);
- error_count++;
- return 0;
+ report_error(filename, line_no, "bad number");
+ return -1;
}
*str = end;
- return n;
+ if (ret != NULL)
+ *ret = n;
+ return 0;
+}
+
+static int
+check_nonnegative(long l)
+{
+ if (l < 0) {
+ report_error(filename, line_no,
+ "expected non-negative value, got %ld", l);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+check_int(long l)
+{
+ int i = l;
+ if ((long)i != l) {
+ report_error(filename, line_no,
+ "Number too large: %ld", l);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+parse_char(char **str, char expected)
+{
+ if (**str != expected) {
+ report_error(filename, line_no,
+ "expected '%c', got '%c'", expected, **str);
+ return -1;
+ }
+
+ ++*str;
+ return 0;
+}
+
+static struct expr_node *parse_argnum(char **str, int zero);
+
+static struct expr_node *
+parse_zero(char **str, struct expr_node *ret)
+{
+ eat_spaces(str);
+ if (**str == '(') {
+ ++*str;
+ struct expr_node *arg = parse_argnum(str, 0);
+ if (arg == NULL)
+ return NULL;
+ if (parse_char(str, ')') < 0) {
+ fail:
+ expr_destroy(arg);
+ free(arg);
+ return NULL;
+ }
+
+ struct expr_node *ret = build_zero_w_arg(arg, 1);
+ if (ret == NULL)
+ goto fail;
+ return ret;
+
+ } else {
+ return expr_node_zero();
+ }
+}
+
+static int
+wrap_in_zero(struct expr_node **nodep)
+{
+ struct expr_node *n = build_zero_w_arg(*nodep, 1);
+ if (n == NULL)
+ return -1;
+ *nodep = n;
+ return 0;
}
/*
* Input:
- * argN : The value of argument #N, counting from 1 (arg0 = retval)
+ * argN : The value of argument #N, counting from 1
* eltN : The value of element #N of the containing structure
* retval : The return value
- * 0 : Error
- * N : The numeric value N, if N > 0
- *
- * Output:
- * > 0 actual numeric value
- * = 0 return value
- * < 0 (arg -n), counting from one
+ * N : The numeric value N
*/
-static int
-parse_argnum(char **str) {
- int multiplier = 1;
- int n = 0;
-
- if (strncmp(*str, "arg", 3) == 0) {
- (*str) += 3;
- multiplier = -1;
- } else if (strncmp(*str, "elt", 3) == 0) {
- (*str) += 3;
- multiplier = -1;
- } else if (strncmp(*str, "retval", 6) == 0) {
- (*str) += 6;
- return 0;
- }
+static struct expr_node *
+parse_argnum(char **str, int zero)
+{
+ struct expr_node *expr = malloc(sizeof(*expr));
+ if (expr == NULL)
+ return NULL;
+
+ if (isdigit(**str)) {
+ long l;
+ if (parse_int(str, &l) < 0
+ || check_nonnegative(l) < 0
+ || check_int(l) < 0)
+ goto fail;
- n = parse_int(str);
+ expr_init_const_word(expr, l, type_get_simple(ARGTYPE_LONG), 0);
- return n * multiplier;
+ if (zero && wrap_in_zero(&expr) < 0)
+ goto fail;
+
+ return expr;
+
+ } else {
+ char *name = parse_ident(str);
+ if (name == NULL)
+ goto fail;
+
+ int is_arg = strncmp(name, "arg", 3) == 0;
+ int is_elt = !is_arg && strncmp(name, "elt", 3) == 0;
+ if (is_arg || is_elt) {
+ long l;
+ name += 3;
+ if (parse_int(&name, &l) < 0
+ || check_int(l) < 0)
+ goto fail;
+
+ if (is_arg) {
+ expr_init_argno(expr, l - 1);
+ } else {
+ struct expr_node *e_up = malloc(sizeof(*e_up));
+ struct expr_node *e_ix = malloc(sizeof(*e_ix));
+ if (e_up == NULL || e_ix == NULL) {
+ free(e_up);
+ free(e_ix);
+ goto fail;
+ }
+
+ expr_init_up(e_up, expr_self(), 0);
+ struct arg_type_info *ti
+ = type_get_simple(ARGTYPE_LONG);
+ expr_init_const_word(e_ix, l - 1, ti, 0);
+ expr_init_index(expr, e_up, 1, e_ix, 1);
+ }
+
+ } else if (strcmp(name, "retval") == 0) {
+ expr_init_named(expr, "retval", 0);
+
+ } else if (strcmp(name, "zero") == 0) {
+ struct expr_node *ret = parse_zero(str, expr);
+ if (ret == NULL)
+ goto fail;
+ return ret;
+
+ } else {
+ report_error(filename, line_no,
+ "Unknown length specifier: '%s'", name);
+ goto fail;
+ }
+
+ if (zero && wrap_in_zero(&expr) < 0)
+ goto fail;
+
+ return expr;
+ }
+
+fail:
+ free(expr);
+ return NULL;
}
struct typedef_node_t {
char *name;
- arg_type_info *info;
+ struct arg_type_info *info;
+ int own_type;
struct typedef_node_t *next;
} *typedefs = NULL;
-static arg_type_info *
+static struct arg_type_info *
lookup_typedef(char **str) {
struct typedef_node_t *node;
char *end = *str;
@@ -231,11 +366,22 @@ lookup_typedef(char **str) {
return NULL;
}
+static struct typedef_node_t *
+insert_typedef(char *name, struct arg_type_info *info, int own_type)
+{
+ struct typedef_node_t *binding = malloc(sizeof(*binding));
+ binding->name = name;
+ binding->info = info;
+ binding->own_type = own_type;
+ binding->next = typedefs;
+ typedefs = binding;
+ return binding;
+}
+
static void
parse_typedef(char **str) {
char *name;
- arg_type_info *info;
- struct typedef_node_t *binding;
+ struct arg_type_info *info;
(*str) += strlen("typedef");
eat_spaces(str);
@@ -245,386 +391,689 @@ parse_typedef(char **str) {
// Skip = sign
eat_spaces(str);
- if (**str != '=') {
- output_line(0,
- "Syntax error in `%s', line %d: expected '=', got '%c'",
- filename, line_no, **str);
- error_count++;
+ if (parse_char(str, '=') < 0)
return;
- }
- (*str)++;
eat_spaces(str);
// Parse the type
- info = parse_type(str);
+ int own;
+ info = parse_type(str, NULL, 0, &own);
- // Insert onto beginning of linked list
- binding = malloc(sizeof(*binding));
- binding->name = name;
- binding->info = info;
- binding->next = typedefs;
- typedefs = binding;
+ insert_typedef(name, info, own);
+}
+
+static void
+destroy_fun(Function *fun)
+{
+ size_t i;
+ if (fun == NULL)
+ return;
+ if (fun->own_return_info) {
+ type_destroy(fun->return_info);
+ free(fun->return_info);
+ }
+ for (i = 0; i < fun->num_params; ++i)
+ param_destroy(&fun->params[i]);
+ free(fun->params);
}
-static size_t
-arg_sizeof(arg_type_info * arg) {
- if (arg->type == ARGTYPE_CHAR) {
- return sizeof(char);
- } else if (arg->type == ARGTYPE_SHORT || arg->type == ARGTYPE_USHORT) {
- return sizeof(short);
- } else if (arg->type == ARGTYPE_FLOAT) {
- return sizeof(float);
- } else if (arg->type == ARGTYPE_DOUBLE) {
- return sizeof(double);
- } else if (arg->type == ARGTYPE_ENUM) {
- return sizeof(int);
- } else if (arg->type == ARGTYPE_STRUCT) {
- return arg->u.struct_info.size;
- } else if (arg->type == ARGTYPE_POINTER) {
- return sizeof(void*);
- } else if (arg->type == ARGTYPE_ARRAY) {
- if (arg->u.array_info.len_spec > 0)
- return arg->u.array_info.len_spec * arg->u.array_info.elt_size;
+/* Syntax: struct ( type,type,type,... ) */
+static int
+parse_struct(char **str, struct arg_type_info *info)
+{
+ eat_spaces(str);
+ if (parse_char(str, '(') < 0)
+ return -1;
+
+ eat_spaces(str); // Empty arg list with whitespace inside
+
+ type_init_struct(info);
+
+ while (1) {
+ eat_spaces(str);
+ if (**str == 0 || **str == ')') {
+ parse_char(str, ')');
+ return 0;
+ }
+
+ /* Field delimiter. */
+ if (type_struct_size(info) > 0)
+ parse_char(str, ',');
+
+ eat_spaces(str);
+ int own;
+ struct arg_type_info *field = parse_lens(str, NULL, 0, &own);
+ if (field == NULL || type_struct_add(info, field, own)) {
+ type_destroy(info);
+ return -1;
+ }
+ }
+}
+
+static int
+parse_string(char **str, struct arg_type_info **retp, int *ownp)
+{
+ struct arg_type_info *info = malloc(sizeof(*info) * 2);
+ if (info == NULL) {
+ fail:
+ free(info);
+ return -1;
+ }
+
+ struct expr_node *length;
+ int own_length;
+ int with_arg = 0;
+
+ if (isdigit(**str)) {
+ /* string0 is string[retval], length is zero(retval)
+ * stringN is string[argN], length is zero(argN) */
+ long l;
+ if (parse_int(str, &l) < 0
+ || check_int(l) < 0)
+ goto fail;
+
+ struct expr_node *length_arg = malloc(sizeof(*length_arg));
+ if (length_arg == NULL)
+ goto fail;
+
+ if (l == 0)
+ expr_init_named(length_arg, "retval", 0);
else
- return sizeof(void *);
+ expr_init_argno(length_arg, l - 1);
+
+ length = build_zero_w_arg(length_arg, 1);
+ if (length == NULL) {
+ expr_destroy(length_arg);
+ free(length_arg);
+ goto fail;
+ }
+ own_length = 1;
+
} else {
- return sizeof(int);
- }
-}
-
-#undef alignof
-#define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st))
-static size_t
-arg_align(arg_type_info * arg) {
- struct { char c; char C; } cC;
- struct { char c; short s; } cs;
- struct { char c; int i; } ci;
- struct { char c; long l; } cl;
- struct { char c; void* p; } cp;
- struct { char c; float f; } cf;
- struct { char c; double d; } cd;
-
- static size_t char_alignment = alignof(C, cC);
- static size_t short_alignment = alignof(s, cs);
- static size_t int_alignment = alignof(i, ci);
- static size_t long_alignment = alignof(l, cl);
- static size_t ptr_alignment = alignof(p, cp);
- static size_t float_alignment = alignof(f, cf);
- static size_t double_alignment = alignof(d, cd);
-
- switch (arg->type) {
- case ARGTYPE_LONG:
- case ARGTYPE_ULONG:
- return long_alignment;
- case ARGTYPE_CHAR:
- return char_alignment;
- case ARGTYPE_SHORT:
- case ARGTYPE_USHORT:
- return short_alignment;
- case ARGTYPE_FLOAT:
- return float_alignment;
- case ARGTYPE_DOUBLE:
- return double_alignment;
- case ARGTYPE_ADDR:
- case ARGTYPE_FILE:
- case ARGTYPE_FORMAT:
- case ARGTYPE_STRING:
- case ARGTYPE_STRING_N:
- case ARGTYPE_POINTER:
- return ptr_alignment;
-
- case ARGTYPE_ARRAY:
- return arg_align(&arg->u.array_info.elt_type[0]);
-
- case ARGTYPE_STRUCT:
- return arg_align(arg->u.struct_info.fields[0]);
-
- default:
- return int_alignment;
- }
-}
-
-static size_t
-align_skip(size_t alignment, size_t offset) {
- if (offset % alignment)
- return alignment - (offset % alignment);
- else
- return 0;
+ eat_spaces(str);
+ if (**str == '[') {
+ (*str)++;
+ eat_spaces(str);
+
+ length = parse_argnum(str, 1);
+ if (length == NULL)
+ goto fail;
+ own_length = 1;
+
+ eat_spaces(str);
+ parse_char(str, ']');
+
+ } else if (**str == '(') {
+ /* Usage of "string" as lens. */
+ ++*str;
+
+ free(info);
+
+ eat_spaces(str);
+ info = parse_type(str, NULL, 0, ownp);
+ if (info == NULL)
+ goto fail;
+
+ eat_spaces(str);
+ parse_char(str, ')');
+
+ with_arg = 1;
+
+ } else {
+ /* It was just a simple string after all. */
+ length = expr_node_zero();
+ own_length = 0;
+ }
+ }
+
+ /* String is a pointer to array of chars. */
+ if (!with_arg) {
+ type_init_array(&info[1], type_get_simple(ARGTYPE_CHAR), 0,
+ length, own_length);
+
+ type_init_pointer(&info[0], &info[1], 0);
+ *ownp = 1;
+ }
+
+ info->lens = &string_lens;
+ info->own_lens = 0;
+
+ *retp = info;
+ return 0;
}
-/* I'm sure this isn't completely correct, but just try to get most of
- * them right for now. */
-static void
-align_struct(arg_type_info* info) {
- size_t offset;
- int i;
+static int
+build_printf_pack(struct param **packp, size_t param_num)
+{
+ if (packp == NULL) {
+ report_error(filename, line_no,
+ "'format' type in unexpected context");
+ return -1;
+ }
+ if (*packp != NULL) {
+ report_error(filename, line_no,
+ "only one 'format' type per function supported");
+ return -1;
+ }
- if (info->u.struct_info.size != 0)
- return; // Already done
+ *packp = malloc(sizeof(**packp));
+ if (*packp == NULL)
+ return -1;
- // Compute internal padding due to alignment constraints for
- // various types.
- offset = 0;
- for (i = 0; info->u.struct_info.fields[i] != NULL; i++) {
- arg_type_info *field = info->u.struct_info.fields[i];
- offset += align_skip(arg_align(field), offset);
- info->u.struct_info.offset[i] = offset;
- offset += arg_sizeof(field);
+ struct expr_node *node = malloc(sizeof(*node));
+ if (node == NULL) {
+ free(*packp);
+ return -1;
}
- info->u.struct_info.size = offset;
+ expr_init_argno(node, param_num);
+
+ param_pack_init_printf(*packp, node, 1);
+
+ return 0;
}
-static arg_type_info *
-parse_nonpointer_type(char **str) {
- arg_type_info *simple;
- arg_type_info *info;
+/* Match and consume KWD if it's next in stream, and return 0.
+ * Otherwise return negative number. */
+static int
+try_parse_kwd(char **str, const char *kwd)
+{
+ size_t len = strlen(kwd);
+ if (strncmp(*str, kwd, len) == 0
+ && !isalnum((*str)[len])) {
+ (*str) += len;
+ return 0;
+ }
+ return -1;
+}
- if (strncmp(*str, "typedef", 7) == 0) {
- parse_typedef(str);
- return lookup_prototype(ARGTYPE_UNKNOWN);
+/* Make a copy of INFO and set the *OWN bit if it's not already
+ * owned. */
+static int
+unshare_type_info(struct arg_type_info **infop, int *ownp)
+{
+ if (*ownp)
+ return 0;
+
+ struct arg_type_info *ninfo = malloc(sizeof(*ninfo));
+ if (ninfo == NULL) {
+ report_error(filename, line_no,
+ "malloc: %s", strerror(errno));
+ return -1;
}
+ *ninfo = **infop;
+ *infop = ninfo;
+ *ownp = 1;
+ return 0;
+}
- simple = str2type(str);
- if (simple->type == ARGTYPE_UNKNOWN) {
- info = lookup_typedef(str);
- if (info)
- return info;
- else
- return simple; // UNKNOWN
+/* XXX extra_param and param_num are a kludge to get in
+ * backward-compatible support for "format" parameter type. The
+ * latter is only valid if the former is non-NULL, which is only in
+ * top-level context. */
+static int
+parse_alias(char **str, struct arg_type_info **retp, int *ownp,
+ struct param **extra_param, size_t param_num)
+{
+ /* For backward compatibility, we need to support things like
+ * stringN (which is like string[argN], string[N], and also
+ * bare string. We might, in theory, replace this by
+ * preprocessing configure file sources with M4, but for now,
+ * "string" is syntax. */
+ if (strncmp(*str, "string", 6) == 0) {
+ (*str) += 6;
+ return parse_string(str, retp, ownp);
+
+ } else if (try_parse_kwd(str, "format") >= 0
+ && extra_param != NULL) {
+ /* For backward compatibility, format is parsed as
+ * "string", but it smuggles to the parameter list of
+ * a function a "printf" argument pack with this
+ * parameter as argument. */
+ if (parse_string(str, retp, ownp) < 0)
+ return -1;
+
+ return build_printf_pack(extra_param, param_num);
+
+ } else if (try_parse_kwd(str, "enum") >=0) {
+
+ return parse_enum(str, retp, ownp);
+
+ } else {
+ *retp = NULL;
+ return 0;
}
+}
- info = malloc(sizeof(*info));
- info->type = simple->type;
+/* Syntax: array ( type, N|argN ) */
+static int
+parse_array(char **str, struct arg_type_info *info)
+{
+ eat_spaces(str);
+ if (parse_char(str, '(') < 0)
+ return -1;
- /* Code to parse parameterized types will go into the following
- switch statement. */
+ eat_spaces(str);
+ int own;
+ struct arg_type_info *elt_info = parse_lens(str, NULL, 0, &own);
+ if (elt_info == NULL)
+ return -1;
- switch (info->type) {
+ eat_spaces(str);
+ parse_char(str, ',');
- /* Syntax: array ( type, N|argN ) */
- case ARGTYPE_ARRAY:
- (*str)++; // Get past open paren
- eat_spaces(str);
- if ((info->u.array_info.elt_type = parse_type(str)) == NULL)
- return NULL;
- info->u.array_info.elt_size =
- arg_sizeof(info->u.array_info.elt_type);
- (*str)++; // Get past comma
+ eat_spaces(str);
+ struct expr_node *length = parse_argnum(str, 0);
+ if (length == NULL) {
+ if (own) {
+ type_destroy(elt_info);
+ free(elt_info);
+ }
+ return -1;
+ }
+
+ type_init_array(info, elt_info, own, length, 1);
+
+ eat_spaces(str);
+ parse_char(str, ')');
+ return 0;
+}
+
+/* Syntax:
+ * enum (keyname[=value],keyname[=value],... )
+ * enum<type> (keyname[=value],keyname[=value],... )
+ */
+static int
+parse_enum(char **str, struct arg_type_info **retp, int *ownp)
+{
+ /* Optional type argument. */
+ eat_spaces(str);
+ if (**str == '[') {
+ parse_char(str, '[');
eat_spaces(str);
- info->u.array_info.len_spec = parse_argnum(str);
- (*str)++; // Get past close paren
- return info;
-
- /* Syntax: enum ( keyname=value,keyname=value,... ) */
- case ARGTYPE_ENUM:{
- struct enum_opt {
- char *key;
- int value;
- struct enum_opt *next;
- };
- struct enum_opt *list = NULL;
- struct enum_opt *p;
- int entries = 0;
- int ii;
+ *retp = parse_nonpointer_type(str, NULL, 0, ownp);
+ if (*retp == NULL)
+ return -1;
+
+ if (!type_is_integral((*retp)->type)) {
+ report_error(filename, line_no,
+ "integral type required as enum argument");
+ fail:
+ if (*ownp) {
+ /* This also releases associated lens
+ * if any was set so far. */
+ type_destroy(*retp);
+ free(*retp);
+ }
+ return -1;
+ }
eat_spaces(str);
- (*str)++; // Get past open paren
+ if (parse_char(str, ']') < 0)
+ goto fail;
+
+ } else {
+ *retp = type_get_simple(ARGTYPE_INT);
+ *ownp = 0;
+ }
+
+ /* We'll need to set the lens, so unshare. */
+ if (unshare_type_info(retp, ownp) < 0)
+ goto fail;
+
+ eat_spaces(str);
+ if (parse_char(str, '(') < 0)
+ goto fail;
+
+ struct enum_lens *lens = malloc(sizeof(*lens));
+ if (lens == NULL) {
+ report_error(filename, line_no,
+ "malloc enum lens: %s", strerror(errno));
+ return -1;
+ }
+
+ lens_init_enum(lens);
+ (*retp)->lens = &lens->super;
+ (*retp)->own_lens = 1;
+
+ long last_val = 0;
+ while (1) {
eat_spaces(str);
+ if (**str == 0 || **str == ')') {
+ parse_char(str, ')');
+ return 0;
+ }
- while (**str && **str != ')') {
- p = (struct enum_opt *) malloc(sizeof(*p));
- eat_spaces(str);
- p->key = parse_ident(str);
- if (error_count) {
- free(p);
- return NULL;
- }
- eat_spaces(str);
- if (**str != '=') {
- free(p->key);
- free(p);
- output_line(0,
- "Syntax error in `%s', line %d: expected '=', got '%c'",
- filename, line_no, **str);
- error_count++;
- return NULL;
- }
- ++(*str);
- eat_spaces(str);
- p->value = parse_int(str);
- p->next = list;
- list = p;
- ++entries;
+ /* Field delimiter. XXX should we support the C
+ * syntax, where the enumeration can end in pending
+ * comma? */
+ if (lens_enum_size(lens) > 0)
+ parse_char(str, ',');
- // Skip comma
- eat_spaces(str);
- if (**str == ',') {
- (*str)++;
- eat_spaces(str);
- }
+ eat_spaces(str);
+ char *key = parse_ident(str);
+ if (key == NULL) {
+ err:
+ free(key);
+ goto fail;
}
- info->u.enum_info.entries = entries;
- info->u.enum_info.keys =
- (char **) malloc(entries * sizeof(char *));
- info->u.enum_info.values =
- (int *) malloc(entries * sizeof(int));
- for (ii = 0, p = NULL; list; ++ii, list = list->next) {
- if (p)
- free(p);
- info->u.enum_info.keys[ii] = list->key;
- info->u.enum_info.values[ii] = list->value;
- p = list;
+ if (**str == '=') {
+ ++*str;
+ eat_spaces(str);
+ if (parse_int(str, &last_val) < 0)
+ goto err;
}
- if (p)
- free(p);
- return info;
+ struct value *value = malloc(sizeof(*value));
+ if (value == NULL)
+ goto err;
+ value_init_detached(value, NULL, *retp, 0);
+ value_set_word(value, last_val);
+
+ if (lens_enum_add(lens, key, 1, value, 1) < 0)
+ goto err;
+
+ last_val++;
}
- case ARGTYPE_STRING:
- if (!isdigit(**str) && **str != '[') {
- /* Oops, was just a simple string after all */
- free(info);
+ return 0;
+}
+
+static struct arg_type_info *
+parse_nonpointer_type(char **str, struct param **extra_param, size_t param_num,
+ int *ownp)
+{
+ enum arg_type type;
+ if (parse_arg_type(str, &type) < 0) {
+ struct arg_type_info *simple;
+ if (parse_alias(str, &simple, ownp, extra_param, param_num) < 0)
+ return NULL;
+ if (simple == NULL)
+ simple = lookup_typedef(str);
+ if (simple != NULL) {
+ *ownp = 0;
return simple;
}
- info->type = ARGTYPE_STRING_N;
+ report_error(filename, line_no,
+ "unknown type around '%s'", *str);
+ return NULL;
+ }
+
+ int (*parser) (char **, struct arg_type_info *) = NULL;
+
+ /* For some types that's all we need. */
+ switch (type) {
+ case ARGTYPE_VOID:
+ case ARGTYPE_INT:
+ case ARGTYPE_UINT:
+ case ARGTYPE_LONG:
+ case ARGTYPE_ULONG:
+ case ARGTYPE_CHAR:
+ case ARGTYPE_SHORT:
+ case ARGTYPE_USHORT:
+ case ARGTYPE_FLOAT:
+ case ARGTYPE_DOUBLE:
+ *ownp = 0;
+ return type_get_simple(type);
+
+ case ARGTYPE_ARRAY:
+ parser = parse_array;
+ break;
+
+ case ARGTYPE_STRUCT:
+ parser = parse_struct;
+ break;
+
+ case ARGTYPE_POINTER:
+ /* Pointer syntax is not based on keyword, so we
+ * should never get this type. */
+ assert(type != ARGTYPE_POINTER);
+ abort();
+ }
+
+ struct arg_type_info *info = malloc(sizeof(*info));
+ if (info == NULL) {
+ report_error(filename, line_no,
+ "malloc: %s", strerror(errno));
+ return NULL;
+ }
+ *ownp = 1;
+
+ if (parser(str, info) < 0) {
+ free(info);
+ return NULL;
+ }
- /* Backwards compatibility for string0, string1, ... */
- if (isdigit(**str)) {
- info->u.string_n_info.size_spec = -parse_int(str);
- return info;
+ return info;
+}
+
+static struct named_lens {
+ const char *name;
+ struct lens *lens;
+} lenses[] = {
+ { "hide", &blind_lens },
+ { "octal", &octal_lens },
+ { "hex", &hex_lens },
+ { "bool", &bool_lens },
+ { "guess", &guess_lens },
+};
+
+static struct lens *
+name2lens(char **str, int *own_lensp)
+{
+ size_t i;
+ for (i = 0; i < sizeof(lenses)/sizeof(*lenses); ++i)
+ if (try_parse_kwd(str, lenses[i].name) == 0) {
+ *own_lensp = 0;
+ return lenses[i].lens;
}
- (*str)++; // Skip past opening [
- eat_spaces(str);
- info->u.string_n_info.size_spec = parse_argnum(str);
+ return NULL;
+}
+
+static struct arg_type_info *
+parse_type(char **str, struct param **extra_param, size_t param_num, int *ownp)
+{
+ struct arg_type_info *info
+ = parse_nonpointer_type(str, extra_param, param_num, ownp);
+ if (info == NULL)
+ return NULL;
+
+ while (1) {
eat_spaces(str);
- (*str)++; // Skip past closing ]
- return info;
-
- // Syntax: struct ( type,type,type,... )
- case ARGTYPE_STRUCT:{
- int field_num = 0;
- (*str)++; // Get past open paren
- info->u.struct_info.fields =
- malloc((MAX_ARGS + 1) * sizeof(void *));
- info->u.struct_info.offset =
- malloc((MAX_ARGS + 1) * sizeof(size_t));
- info->u.struct_info.size = 0;
- eat_spaces(str); // Empty arg list with whitespace inside
- while (**str && **str != ')') {
- if (field_num == MAX_ARGS) {
- output_line(0,
- "Error in `%s', line %d: Too many structure elements",
- filename, line_no);
- error_count++;
+ if (**str == '*') {
+ struct arg_type_info *outer = malloc(sizeof(*outer));
+ if (outer == NULL) {
+ if (*ownp) {
+ type_destroy(info);
+ free(info);
+ }
+ report_error(filename, line_no,
+ "malloc: %s", strerror(errno));
return NULL;
}
- eat_spaces(str);
- if (field_num != 0) {
- (*str)++; // Get past comma
- eat_spaces(str);
- }
- if ((info->u.struct_info.fields[field_num++] =
- parse_type(str)) == NULL)
- return NULL;
+ type_init_pointer(outer, info, *ownp);
+ *ownp = 1;
+ (*str)++;
+ info = outer;
+ } else
+ break;
+ }
+ return info;
+}
- // Must trim trailing spaces so the check for
- // the closing paren is simple
- eat_spaces(str);
+static struct arg_type_info *
+parse_lens(char **str, struct param **extra_param, size_t param_num, int *ownp)
+{
+ int own_lens;
+ struct lens *lens = name2lens(str, &own_lens);
+ int has_args = 1;
+ struct arg_type_info *info;
+ if (lens != NULL) {
+ eat_spaces(str);
+
+ /* Octal lens gets special treatment, because of
+ * backward compatibility. */
+ if (lens == &octal_lens && **str != '(') {
+ has_args = 0;
+ info = type_get_simple(ARGTYPE_INT);
+ *ownp = 0;
+ } else if (parse_char(str, '(') < 0) {
+ report_error(filename, line_no,
+ "expected type argument after the lens");
+ return NULL;
}
- (*str)++; // Get past closing paren
- info->u.struct_info.fields[field_num] = NULL;
- align_struct(info);
- return info;
}
- default:
- if (info->type == ARGTYPE_UNKNOWN) {
- output_line(0, "Syntax error in `%s', line %d: "
- "Unknown type encountered",
- filename, line_no);
- free(info);
- error_count++;
+ if (has_args) {
+ eat_spaces(str);
+ info = parse_type(str, extra_param, param_num, ownp);
+ if (info == NULL) {
+ fail:
+ if (own_lens && lens != NULL)
+ lens_destroy(lens);
return NULL;
- } else {
- return info;
}
}
-}
-static arg_type_info *
-parse_type(char **str) {
- arg_type_info *info = parse_nonpointer_type(str);
- while (**str == '*') {
- arg_type_info *outer = malloc(sizeof(*info));
- outer->type = ARGTYPE_POINTER;
- outer->u.ptr_info.info = info;
- (*str)++;
- info = outer;
+ if (lens != NULL && has_args) {
+ eat_spaces(str);
+ parse_char(str, ')');
}
+
+ /* We can't modify shared types. Make a copy if we have a
+ * lens. */
+ if (lens != NULL && unshare_type_info(&info, ownp) < 0)
+ goto fail;
+
+ if (lens != NULL) {
+ info->lens = lens;
+ info->own_lens = own_lens;
+ }
+
return info;
}
+static int
+add_param(Function *fun, size_t *allocdp)
+{
+ size_t allocd = *allocdp;
+ /* XXX +1 is for the extra_param handling hack. */
+ if ((fun->num_params + 1) >= allocd) {
+ allocd = allocd > 0 ? 2 * allocd : 8;
+ void *na = realloc(fun->params, sizeof(*fun->params) * allocd);
+ if (na == NULL)
+ return -1;
+
+ fun->params = na;
+ *allocdp = allocd;
+ }
+ return 0;
+}
+
static Function *
process_line(char *buf) {
- Function fun;
- Function *fun_p;
char *str = buf;
char *tmp;
- int i;
- int float_num = 0;
line_no++;
debug(3, "Reading line %d of `%s'", line_no, filename);
eat_spaces(&str);
- fun.return_info = parse_type(&str);
- if (fun.return_info == NULL)
+
+ /* A comment or empty line. */
+ if (*str == ';' || *str == 0 || *str == '\n')
+ return NULL;
+
+ if (strncmp(str, "typedef", 7) == 0) {
+ parse_typedef(&str);
+ return NULL;
+ }
+
+ Function *fun = calloc(1, sizeof(*fun));
+ if (fun == NULL) {
+ report_error(filename, line_no,
+ "alloc function: %s", strerror(errno));
return NULL;
- if (fun.return_info->type == ARGTYPE_UNKNOWN) {
+ }
+
+ fun->return_info = parse_lens(&str, NULL, 0, &fun->own_return_info);
+ if (fun->return_info == NULL) {
+ err:
debug(3, " Skipping line %d", line_no);
+ destroy_fun(fun);
return NULL;
}
- debug(4, " return_type = %d", fun.return_info->type);
+ debug(4, " return_type = %d", fun->return_info->type);
+
eat_spaces(&str);
tmp = start_of_arg_sig(str);
- if (!tmp) {
- output_line(0, "Syntax error in `%s', line %d", filename,
- line_no);
- error_count++;
- return NULL;
+ if (tmp == NULL) {
+ report_error(filename, line_no, "syntax error");
+ goto err;
}
*tmp = '\0';
- fun.name = strdup(str);
+ fun->name = strdup(str);
str = tmp + 1;
- debug(3, " name = %s", fun.name);
- fun.params_right = 0;
- for (i = 0; i < MAX_ARGS; i++) {
+ debug(3, " name = %s", fun->name);
+
+ size_t allocd = 0;
+ struct param *extra_param = NULL;
+
+ int have_stop = 0;
+
+ while (1) {
eat_spaces(&str);
- if (*str == ')') {
+ if (*str == ')')
break;
- }
+
if (str[0] == '+') {
- fun.params_right++;
+ if (have_stop == 0) {
+ if (add_param(fun, &allocd) < 0)
+ goto add_err;
+ param_init_stop
+ (&fun->params[fun->num_params++]);
+ have_stop = 1;
+ }
str++;
- } else if (fun.params_right) {
- fun.params_right++;
}
- fun.arg_info[i] = parse_type(&str);
- if (fun.arg_info[i] == NULL) {
- output_line(0, "Syntax error in `%s', line %d"
- ": unknown argument type",
- filename, line_no);
- error_count++;
- return NULL;
+
+ if (add_param(fun, &allocd) < 0) {
+ add_err:
+ report_error(filename, line_no, "(re)alloc params: %s",
+ strerror(errno));
+ goto err;
+ }
+
+ int own;
+ struct arg_type_info *type = parse_lens(&str, &extra_param,
+ fun->num_params, &own);
+ if (type == NULL) {
+ report_error(filename, line_no,
+ "unknown argument type");
+ goto err;
+ }
+
+ /* XXX We used to allow void parameter as a synonym to
+ * an argument that shouldn't be displayed. We may
+ * wish to re-introduce this when lenses are
+ * implemented, as a synonym, but backends generally
+ * need to know the type, so disallow bare void for
+ * now. */
+ if (type->type == ARGTYPE_VOID) {
+ report_warning(filename, line_no,
+ "void parameter assumed to be 'int'");
+ if (own) {
+ type_destroy(type);
+ free(type);
+ }
+ type = type_get_simple(ARGTYPE_INT);
+ own = 0;
}
- if (fun.arg_info[i]->type == ARGTYPE_FLOAT)
- fun.arg_info[i]->u.float_info.float_index = float_num++;
- else if (fun.arg_info[i]->type == ARGTYPE_DOUBLE)
- fun.arg_info[i]->u.double_info.float_index = float_num++;
+
+ param_init_type(&fun->params[fun->num_params++], type, own);
+
eat_spaces(&str);
if (*str == ',') {
str++;
@@ -634,20 +1083,35 @@ process_line(char *buf) {
} else {
if (str[strlen(str) - 1] == '\n')
str[strlen(str) - 1] = '\0';
- output_line(0, "Syntax error in `%s', line %d at ...\"%s\"",
- filename, line_no, str);
- error_count++;
- return NULL;
+ report_error(filename, line_no,
+ "syntax error around \"%s\"", str);
+ goto err;
}
}
- fun.num_params = i;
- fun_p = malloc(sizeof(Function));
- if (!fun_p) {
- perror("ltrace: malloc");
- exit(1);
+
+ if (extra_param != NULL) {
+ assert(fun->num_params < allocd);
+ memcpy(&fun->params[fun->num_params++], extra_param,
+ sizeof(*extra_param));
}
- memcpy(fun_p, &fun, sizeof(Function));
- return fun_p;
+
+ return fun;
+}
+
+void
+init_global_config(void)
+{
+ struct arg_type_info *info = malloc(2 * sizeof(*info));
+ if (info == NULL)
+ error(1, errno, "malloc in init_global_config");
+
+ memset(info, 0, 2 * sizeof(*info));
+ info[0].type = ARGTYPE_POINTER;
+ info[0].u.ptr_info.info = &info[1];
+ info[1].type = ARGTYPE_VOID;
+
+ insert_typedef(strdup("addr"), info, 0);
+ insert_typedef(strdup("file"), info, 1);
}
void
@@ -667,7 +1131,6 @@ read_config_file(char *file) {
while (fgets(buf, 1024, stream)) {
Function *tmp;
- error_count = 0;
tmp = process_line(buf);
if (tmp) {
diff --git a/read_config_file.h b/read_config_file.h
index 8000b1c..06215ab 100644
--- a/read_config_file.h
+++ b/read_config_file.h
@@ -1 +1,2 @@
extern void read_config_file(char *);
+extern void init_global_config(void);
diff --git a/sysdeps/README b/sysdeps/README
index 0a37909..db51c9e 100644
--- a/sysdeps/README
+++ b/sysdeps/README
@@ -7,27 +7,5 @@ first target, and must remove "sysdep.o" in this dir.
Files "sysdep.h", "signalent.h" and "syscallent.h" must be present
inside the directory after invoking the first target of the Makefile.
------------
-"sysdep.o" must export the following functions:
-
-Event * next_event(void);
-void continue_after_breakpoint(Process * proc, Breakpoint * sbp, int delete_it);
-void continue_after_signal(pid_t pid, int signum);
-void continue_enabling_breakpoint(pid_t pid, Breakpoint * sbp);
-void continue_process(pid_t pid);
-void enable_breakpoint(pid_t pid, Breakpoint * sbp);
-void disable_breakpoint(pid_t pid, Breakpoint * sbp);
-int fork_p(int sysnum);
-int exec_p(int sysnum);
-int syscall_p(Process * proc, int status, int * sysnum);
-void * get_instruction_pointer(pid_t pid);
-void * get_stack_pointer(pid_t pid);
-void * get_return_addr(pid_t pid, void * stack_pointer);
-long gimme_arg(enum tof type, Process * proc, arg_type_info*);
-int umovestr(Process * proc, void * addr, int len, void * laddr);
-int umovelong(Process * proc, void * addr, long * result);
-char * pid2name(pid_t pid);
-void trace_me(void);
-int trace_pid(pid_t pid);
-void untrace_pid(pid_t pid);
-void linkmap_init(Process *, struct ltelf *);
+See the file "backend.h" for description of backend interfaces, which
+have to be provided by "sysdep.o".
diff --git a/sysdeps/linux-gnu/Makefile.am b/sysdeps/linux-gnu/Makefile.am
index e6fd7ef..80e6594 100644
--- a/sysdeps/linux-gnu/Makefile.am
+++ b/sysdeps/linux-gnu/Makefile.am
@@ -1,14 +1,13 @@
DIST_SUBDIRS = \
alpha \
arm \
- i386 \
ia64 \
m68k \
mipsel \
ppc \
s390 \
sparc \
- x86_64
+ x86
SUBDIRS = \
$(HOST_CPU)
@@ -29,7 +28,8 @@ noinst_HEADERS = \
arch_syscallent.h \
signalent1.h \
syscallent1.h \
- trace.h
+ trace.h \
+ events.h
EXTRA_DIST = \
arch_mksyscallent \
diff --git a/sysdeps/linux-gnu/arm/arch.h b/sysdeps/linux-gnu/arm/arch.h
index d50e439..291443a 100644
--- a/sysdeps/linux-gnu/arm/arch.h
+++ b/sysdeps/linux-gnu/arm/arch.h
@@ -1,3 +1,23 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 1998,2004,2008 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
#define ARCH_HAVE_ENABLE_BREAKPOINT 1
#define ARCH_HAVE_DISABLE_BREAKPOINT 1
diff --git a/sysdeps/linux-gnu/arm/trace.c b/sysdeps/linux-gnu/arm/trace.c
index 768e1a8..4e4dd25 100644
--- a/sysdeps/linux-gnu/arm/trace.c
+++ b/sysdeps/linux-gnu/arm/trace.c
@@ -82,7 +82,8 @@ syscall_p(Process *proc, int status, int *sysnum) {
}
long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
proc_archdep *a = (proc_archdep *) proc->arch_ptr;
if (arg_num == -1) { /* return value */
@@ -123,14 +124,3 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
return 0;
}
-
-void
-save_register_args(enum tof type, Process *proc) {
- proc_archdep *a = (proc_archdep *) proc->arch_ptr;
- if (a->valid) {
- if (type == LT_TOF_FUNCTION)
- memcpy(a->func_arg, a->regs.uregs, sizeof(a->func_arg));
- else
- memcpy(a->sysc_arg, a->regs.uregs, sizeof(a->sysc_arg));
- }
-}
diff --git a/sysdeps/linux-gnu/breakpoint.c b/sysdeps/linux-gnu/breakpoint.c
index e05e730..40a8436 100644
--- a/sysdeps/linux-gnu/breakpoint.c
+++ b/sysdeps/linux-gnu/breakpoint.c
@@ -1,3 +1,25 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2006 Ian Wienand
+ * Copyright (C) 2002,2008,2009 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
#include "config.h"
#include <sys/ptrace.h>
@@ -6,6 +28,8 @@
#include <stdio.h>
#include "common.h"
+#include "backend.h"
+#include "arch.h"
#include "sysdep.h"
#include "breakpoint.h"
#include "proc.h"
diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c
index 91d873e..d0c1e5c 100644
--- a/sysdeps/linux-gnu/events.c
+++ b/sysdeps/linux-gnu/events.c
@@ -1,18 +1,42 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2007,2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 1998,2001,2004,2007,2008,2009 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
#include "config.h"
#define _GNU_SOURCE 1
-#include <stdlib.h>
+#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <assert.h>
#include <errno.h>
#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include <sys/ptrace.h>
-#include <assert.h>
#include <unistd.h>
-#include "common.h"
+#include "backend.h"
#include "breakpoint.h"
+#include "debug.h"
+#include "events.h"
#include "proc.h"
static Event event;
@@ -35,7 +59,7 @@ enque_event(Event * event)
event->proc->pid, event->type);
Event * ne = malloc(sizeof(*ne));
if (ne == NULL) {
- perror("event will be missed: malloc");
+ fprintf(stderr, "event will be missed: %s\n", strerror(errno));
return;
}
@@ -311,3 +335,20 @@ next_event(void)
return &event;
}
+
+static enum ecb_status
+event_for_proc(struct Event *event, void *data)
+{
+ if (event->proc == data)
+ return ecb_deque;
+ else
+ return ecb_cont;
+}
+
+void
+delete_events_for(struct Process *proc)
+{
+ struct Event *event;
+ while ((event = each_qd_event(&event_for_proc, proc)) != NULL)
+ free(event);
+}
diff --git a/sysdeps/linux-gnu/events.h b/sysdeps/linux-gnu/events.h
new file mode 100644
index 0000000..3802aff
--- /dev/null
+++ b/sysdeps/linux-gnu/events.h
@@ -0,0 +1,41 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef SYSDEPS_LINUX_GNU_EVENTS_H
+#define SYSDEPS_LINUX_GNU_EVENTS_H
+
+#include "forward.h"
+
+/* Declarations for event que functions. */
+
+enum ecb_status {
+ ecb_cont, /* The iteration should continue. */
+ ecb_yield, /* The iteration should stop, yielding this
+ * event. */
+ ecb_deque, /* Like ecb_stop, but the event should be removed
+ * from the queue. */
+};
+
+struct Event *each_qd_event(enum ecb_status (*cb)(struct Event *event,
+ void *data), void *data);
+void delete_events_for(struct Process * proc);
+void enque_event(struct Event *event);
+
+#endif /* SYSDEPS_LINUX_GNU_EVENTS_H */
diff --git a/sysdeps/linux-gnu/i386/Makefile.am b/sysdeps/linux-gnu/i386/Makefile.am
deleted file mode 100644
index a79d2f7..0000000
--- a/sysdeps/linux-gnu/i386/Makefile.am
+++ /dev/null
@@ -1,16 +0,0 @@
-noinst_LTLIBRARIES = \
- ../libcpu.la
-
-___libcpu_la_SOURCES = \
- plt.c \
- regs.c \
- trace.c
-
-noinst_HEADERS = \
- arch.h \
- ptrace.h \
- signalent.h \
- syscallent.h
-
-MAINTAINERCLEANFILES = \
- Makefile.in
diff --git a/sysdeps/linux-gnu/i386/arch.h b/sysdeps/linux-gnu/i386/arch.h
deleted file mode 100644
index dc7383f..0000000
--- a/sysdeps/linux-gnu/i386/arch.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#define BREAKPOINT_VALUE {0xcc}
-#define BREAKPOINT_LENGTH 1
-#define DECR_PC_AFTER_BREAK 1
-#define ARCH_ENDIAN_LITTLE
-
-#define LT_ELFCLASS ELFCLASS32
-#define LT_ELF_MACHINE EM_386
diff --git a/sysdeps/linux-gnu/i386/plt.c b/sysdeps/linux-gnu/i386/plt.c
deleted file mode 100644
index daaf15a..0000000
--- a/sysdeps/linux-gnu/i386/plt.c
+++ /dev/null
@@ -1,16 +0,0 @@
-#include <gelf.h>
-#include "proc.h"
-#include "library.h"
-#include "ltrace-elf.h"
-
-GElf_Addr
-arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela)
-{
- return lte->plt_addr + (ndx + 1) * 16;
-}
-
-void *
-sym2addr(struct Process *proc, struct library_symbol *sym)
-{
- return sym->enter_addr;
-}
diff --git a/sysdeps/linux-gnu/i386/ptrace.h b/sysdeps/linux-gnu/i386/ptrace.h
deleted file mode 100644
index c3cbcb6..0000000
--- a/sysdeps/linux-gnu/i386/ptrace.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <sys/ptrace.h>
diff --git a/sysdeps/linux-gnu/i386/regs.c b/sysdeps/linux-gnu/i386/regs.c
deleted file mode 100644
index a1584ac..0000000
--- a/sysdeps/linux-gnu/i386/regs.c
+++ /dev/null
@@ -1,40 +0,0 @@
-#include "config.h"
-
-#include <sys/types.h>
-#include <sys/ptrace.h>
-#include <asm/ptrace.h>
-
-#include "proc.h"
-
-#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
-# define PTRACE_PEEKUSER PTRACE_PEEKUSR
-#endif
-
-#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR))
-# define PTRACE_POKEUSER PTRACE_POKEUSR
-#endif
-
-void *
-get_instruction_pointer(Process *proc) {
- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EIP, 0);
-}
-
-void
-set_instruction_pointer(Process *proc, void *addr) {
- ptrace(PTRACE_POKEUSER, proc->pid, 4 * EIP, (long)addr);
-}
-
-void *
-get_stack_pointer(Process *proc) {
- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 4 * UESP, 0);
-}
-
-void *
-get_return_addr(Process *proc, void *stack_pointer) {
- return (void *)ptrace(PTRACE_PEEKTEXT, proc->pid, stack_pointer, 0);
-}
-
-void
-set_return_addr(Process *proc, void *addr) {
- ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, (long)addr);
-}
diff --git a/sysdeps/linux-gnu/i386/signalent.h b/sysdeps/linux-gnu/i386/signalent.h
deleted file mode 100644
index 5395f82..0000000
--- a/sysdeps/linux-gnu/i386/signalent.h
+++ /dev/null
@@ -1,32 +0,0 @@
- "SIG_0", /* 0 */
- "SIGHUP", /* 1 */
- "SIGINT", /* 2 */
- "SIGQUIT", /* 3 */
- "SIGILL", /* 4 */
- "SIGTRAP", /* 5 */
- "SIGABRT", /* 6 */
- "SIGBUS", /* 7 */
- "SIGFPE", /* 8 */
- "SIGKILL", /* 9 */
- "SIGUSR1", /* 10 */
- "SIGSEGV", /* 11 */
- "SIGUSR2", /* 12 */
- "SIGPIPE", /* 13 */
- "SIGALRM", /* 14 */
- "SIGTERM", /* 15 */
- "SIGSTKFLT", /* 16 */
- "SIGCHLD", /* 17 */
- "SIGCONT", /* 18 */
- "SIGSTOP", /* 19 */
- "SIGTSTP", /* 20 */
- "SIGTTIN", /* 21 */
- "SIGTTOU", /* 22 */
- "SIGURG", /* 23 */
- "SIGXCPU", /* 24 */
- "SIGXFSZ", /* 25 */
- "SIGVTALRM", /* 26 */
- "SIGPROF", /* 27 */
- "SIGWINCH", /* 28 */
- "SIGIO", /* 29 */
- "SIGPWR", /* 30 */
- "SIGSYS", /* 31 */
diff --git a/sysdeps/linux-gnu/i386/syscallent.h b/sysdeps/linux-gnu/i386/syscallent.h
deleted file mode 100644
index 8f4c887..0000000
--- a/sysdeps/linux-gnu/i386/syscallent.h
+++ /dev/null
@@ -1,317 +0,0 @@
- "restart_syscall", /* 0 */
- "exit", /* 1 */
- "fork", /* 2 */
- "read", /* 3 */
- "write", /* 4 */
- "open", /* 5 */
- "close", /* 6 */
- "waitpid", /* 7 */
- "creat", /* 8 */
- "link", /* 9 */
- "unlink", /* 10 */
- "execve", /* 11 */
- "chdir", /* 12 */
- "time", /* 13 */
- "mknod", /* 14 */
- "chmod", /* 15 */
- "lchown", /* 16 */
- "break", /* 17 */
- "oldstat", /* 18 */
- "lseek", /* 19 */
- "getpid", /* 20 */
- "mount", /* 21 */
- "umount", /* 22 */
- "setuid", /* 23 */
- "getuid", /* 24 */
- "stime", /* 25 */
- "ptrace", /* 26 */
- "alarm", /* 27 */
- "oldfstat", /* 28 */
- "pause", /* 29 */
- "utime", /* 30 */
- "stty", /* 31 */
- "gtty", /* 32 */
- "access", /* 33 */
- "nice", /* 34 */
- "ftime", /* 35 */
- "sync", /* 36 */
- "kill", /* 37 */
- "rename", /* 38 */
- "mkdir", /* 39 */
- "rmdir", /* 40 */
- "dup", /* 41 */
- "pipe", /* 42 */
- "times", /* 43 */
- "prof", /* 44 */
- "brk", /* 45 */
- "setgid", /* 46 */
- "getgid", /* 47 */
- "signal", /* 48 */
- "geteuid", /* 49 */
- "getegid", /* 50 */
- "acct", /* 51 */
- "umount2", /* 52 */
- "lock", /* 53 */
- "ioctl", /* 54 */
- "fcntl", /* 55 */
- "mpx", /* 56 */
- "setpgid", /* 57 */
- "ulimit", /* 58 */
- "oldolduname", /* 59 */
- "umask", /* 60 */
- "chroot", /* 61 */
- "ustat", /* 62 */
- "dup2", /* 63 */
- "getppid", /* 64 */
- "getpgrp", /* 65 */
- "setsid", /* 66 */
- "sigaction", /* 67 */
- "sgetmask", /* 68 */
- "ssetmask", /* 69 */
- "setreuid", /* 70 */
- "setregid", /* 71 */
- "sigsuspend", /* 72 */
- "sigpending", /* 73 */
- "sethostname", /* 74 */
- "setrlimit", /* 75 */
- "getrlimit", /* 76 */
- "getrusage", /* 77 */
- "gettimeofday", /* 78 */
- "settimeofday", /* 79 */
- "getgroups", /* 80 */
- "setgroups", /* 81 */
- "select", /* 82 */
- "symlink", /* 83 */
- "oldlstat", /* 84 */
- "readlink", /* 85 */
- "uselib", /* 86 */
- "swapon", /* 87 */
- "reboot", /* 88 */
- "readdir", /* 89 */
- "mmap", /* 90 */
- "munmap", /* 91 */
- "truncate", /* 92 */
- "ftruncate", /* 93 */
- "fchmod", /* 94 */
- "fchown", /* 95 */
- "getpriority", /* 96 */
- "setpriority", /* 97 */
- "profil", /* 98 */
- "statfs", /* 99 */
- "fstatfs", /* 100 */
- "ioperm", /* 101 */
- "socketcall", /* 102 */
- "syslog", /* 103 */
- "setitimer", /* 104 */
- "getitimer", /* 105 */
- "stat", /* 106 */
- "lstat", /* 107 */
- "fstat", /* 108 */
- "olduname", /* 109 */
- "iopl", /* 110 */
- "vhangup", /* 111 */
- "idle", /* 112 */
- "vm86old", /* 113 */
- "wait4", /* 114 */
- "swapoff", /* 115 */
- "sysinfo", /* 116 */
- "ipc", /* 117 */
- "fsync", /* 118 */
- "sigreturn", /* 119 */
- "clone", /* 120 */
- "setdomainname", /* 121 */
- "uname", /* 122 */
- "modify_ldt", /* 123 */
- "adjtimex", /* 124 */
- "mprotect", /* 125 */
- "sigprocmask", /* 126 */
- "create_module", /* 127 */
- "init_module", /* 128 */
- "delete_module", /* 129 */
- "get_kernel_syms", /* 130 */
- "quotactl", /* 131 */
- "getpgid", /* 132 */
- "fchdir", /* 133 */
- "bdflush", /* 134 */
- "sysfs", /* 135 */
- "personality", /* 136 */
- "afs_syscall", /* 137 */
- "setfsuid", /* 138 */
- "setfsgid", /* 139 */
- "_llseek", /* 140 */
- "getdents", /* 141 */
- "_newselect", /* 142 */
- "flock", /* 143 */
- "msync", /* 144 */
- "readv", /* 145 */
- "writev", /* 146 */
- "getsid", /* 147 */
- "fdatasync", /* 148 */
- "_sysctl", /* 149 */
- "mlock", /* 150 */
- "munlock", /* 151 */
- "mlockall", /* 152 */
- "munlockall", /* 153 */
- "sched_setparam", /* 154 */
- "sched_getparam", /* 155 */
- "sched_setscheduler", /* 156 */
- "sched_getscheduler", /* 157 */
- "sched_yield", /* 158 */
- "sched_get_priority_max", /* 159 */
- "sched_get_priority_min", /* 160 */
- "sched_rr_get_interval", /* 161 */
- "nanosleep", /* 162 */
- "mremap", /* 163 */
- "setresuid", /* 164 */
- "getresuid", /* 165 */
- "vm86", /* 166 */
- "query_module", /* 167 */
- "poll", /* 168 */
- "nfsservctl", /* 169 */
- "setresgid", /* 170 */
- "getresgid", /* 171 */
- "prctl", /* 172 */
- "rt_sigreturn", /* 173 */
- "rt_sigaction", /* 174 */
- "rt_sigprocmask", /* 175 */
- "rt_sigpending", /* 176 */
- "rt_sigtimedwait", /* 177 */
- "rt_sigqueueinfo", /* 178 */
- "rt_sigsuspend", /* 179 */
- "pread64", /* 180 */
- "pwrite64", /* 181 */
- "chown", /* 182 */
- "getcwd", /* 183 */
- "capget", /* 184 */
- "capset", /* 185 */
- "sigaltstack", /* 186 */
- "sendfile", /* 187 */
- "getpmsg", /* 188 */
- "putpmsg", /* 189 */
- "vfork", /* 190 */
- "ugetrlimit", /* 191 */
- "mmap2", /* 192 */
- "truncate64", /* 193 */
- "ftruncate64", /* 194 */
- "stat64", /* 195 */
- "lstat64", /* 196 */
- "fstat64", /* 197 */
- "lchown32", /* 198 */
- "getuid32", /* 199 */
- "getgid32", /* 200 */
- "geteuid32", /* 201 */
- "getegid32", /* 202 */
- "setreuid32", /* 203 */
- "setregid32", /* 204 */
- "getgroups32", /* 205 */
- "setgroups32", /* 206 */
- "fchown32", /* 207 */
- "setresuid32", /* 208 */
- "getresuid32", /* 209 */
- "setresgid32", /* 210 */
- "getresgid32", /* 211 */
- "chown32", /* 212 */
- "setuid32", /* 213 */
- "setgid32", /* 214 */
- "setfsuid32", /* 215 */
- "setfsgid32", /* 216 */
- "pivot_root", /* 217 */
- "mincore", /* 218 */
- "madvise1", /* 219 */
- "getdents64", /* 220 */
- "fcntl64", /* 221 */
- "222", /* 222 */
- "223", /* 223 */
- "gettid", /* 224 */
- "readahead", /* 225 */
- "setxattr", /* 226 */
- "lsetxattr", /* 227 */
- "fsetxattr", /* 228 */
- "getxattr", /* 229 */
- "lgetxattr", /* 230 */
- "fgetxattr", /* 231 */
- "listxattr", /* 232 */
- "llistxattr", /* 233 */
- "flistxattr", /* 234 */
- "removexattr", /* 235 */
- "lremovexattr", /* 236 */
- "fremovexattr", /* 237 */
- "tkill", /* 238 */
- "sendfile64", /* 239 */
- "futex", /* 240 */
- "sched_setaffinity", /* 241 */
- "sched_getaffinity", /* 242 */
- "set_thread_area", /* 243 */
- "get_thread_area", /* 244 */
- "io_setup", /* 245 */
- "io_destroy", /* 246 */
- "io_getevents", /* 247 */
- "io_submit", /* 248 */
- "io_cancel", /* 249 */
- "fadvise64", /* 250 */
- "251", /* 251 */
- "exit_group", /* 252 */
- "lookup_dcookie", /* 253 */
- "epoll_create", /* 254 */
- "epoll_ctl", /* 255 */
- "epoll_wait", /* 256 */
- "remap_file_pages", /* 257 */
- "set_tid_address", /* 258 */
- "timer_create", /* 259 */
- "260", /* 260 */
- "261", /* 261 */
- "262", /* 262 */
- "263", /* 263 */
- "264", /* 264 */
- "265", /* 265 */
- "266", /* 266 */
- "267", /* 267 */
- "statfs64", /* 268 */
- "fstatfs64", /* 269 */
- "tgkill", /* 270 */
- "utimes", /* 271 */
- "fadvise64_64", /* 272 */
- "vserver", /* 273 */
- "mbind", /* 274 */
- "get_mempolicy", /* 275 */
- "set_mempolicy", /* 276 */
- "mq_open", /* 277 */
- "278", /* 278 */
- "279", /* 279 */
- "280", /* 280 */
- "281", /* 281 */
- "282", /* 282 */
- "kexec_load", /* 283 */
- "waitid", /* 284 */
- "285", /* 285 */
- "add_key", /* 286 */
- "request_key", /* 287 */
- "keyctl", /* 288 */
- "ioprio_set", /* 289 */
- "ioprio_get", /* 290 */
- "inotify_init", /* 291 */
- "inotify_add_watch", /* 292 */
- "inotify_rm_watch", /* 293 */
- "migrate_pages", /* 294 */
- "openat", /* 295 */
- "mkdirat", /* 296 */
- "mknodat", /* 297 */
- "fchownat", /* 298 */
- "futimesat", /* 299 */
- "fstatat64", /* 300 */
- "unlinkat", /* 301 */
- "renameat", /* 302 */
- "linkat", /* 303 */
- "symlinkat", /* 304 */
- "readlinkat", /* 305 */
- "fchmodat", /* 306 */
- "faccessat", /* 307 */
- "pselect6", /* 308 */
- "ppoll", /* 309 */
- "unshare", /* 310 */
- "set_robust_list", /* 311 */
- "get_robust_list", /* 312 */
- "splice", /* 313 */
- "sync_file_range", /* 314 */
- "tee", /* 315 */
- "vmsplice", /* 316 */
diff --git a/sysdeps/linux-gnu/i386/trace.c b/sysdeps/linux-gnu/i386/trace.c
deleted file mode 100644
index f0c1e50..0000000
--- a/sysdeps/linux-gnu/i386/trace.c
+++ /dev/null
@@ -1,99 +0,0 @@
-#include "config.h"
-
-#include <sys/ptrace.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <asm/ptrace.h>
-#include <errno.h>
-#include <signal.h>
-#include <stdlib.h>
-
-#include "proc.h"
-#include "common.h"
-
-#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
-# define PTRACE_PEEKUSER PTRACE_PEEKUSR
-#endif
-
-#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR))
-# define PTRACE_POKEUSER PTRACE_POKEUSR
-#endif
-
-void
-get_arch_dep(Process *proc) {
-}
-
-/* Returns 1 if syscall, 2 if sysret, 0 otherwise.
- */
-int
-syscall_p(struct Process *proc, int status, int *sysnum)
-{
- if (WIFSTOPPED(status)
- && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) {
- struct callstack_element *elem = NULL;
- if (proc->callstack_depth > 0)
- elem = proc->callstack + proc->callstack_depth - 1;
-
- *sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, 4 * ORIG_EAX, 0);
- if (*sysnum == -1) {
- if (errno)
- return -1;
- /* Otherwise, ORIG_EAX == -1 means that the
- * system call should not be restarted. In
- * that case rely on what we have on
- * stack. */
- if (elem != NULL && elem->is_syscall)
- *sysnum = elem->c_un.syscall;
- }
-
- if (elem != NULL && elem->is_syscall
- && elem->c_un.syscall == *sysnum)
- return 2;
-
- if (*sysnum >= 0)
- return 1;
- }
- return 0;
-}
-
-long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
- if (arg_num == -1) { /* return value */
- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EAX, 0);
- }
-
- if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) {
- return ptrace(PTRACE_PEEKTEXT, proc->pid,
- proc->stack_pointer + 4 * (arg_num + 1), 0);
- } else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) {
-#if 0
- switch (arg_num) {
- case 0:
- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EBX, 0);
- case 1:
- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * ECX, 0);
- case 2:
- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EDX, 0);
- case 3:
- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * ESI, 0);
- case 4:
- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EDI, 0);
- default:
- fprintf(stderr,
- "gimme_arg called with wrong arguments\n");
- exit(2);
- }
-#else
- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * arg_num, 0);
-#endif
- } else {
- fprintf(stderr, "gimme_arg called with wrong arguments\n");
- exit(1);
- }
-
- return 0;
-}
-
-void
-save_register_args(enum tof type, Process *proc) {
-}
diff --git a/sysdeps/linux-gnu/ia64/arch.h b/sysdeps/linux-gnu/ia64/arch.h
index 673047c..00bb077 100644
--- a/sysdeps/linux-gnu/ia64/arch.h
+++ b/sysdeps/linux-gnu/ia64/arch.h
@@ -1,3 +1,24 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2006,2011 Petr Machata
+ * Copyright (C) 2006 Ian Wienand
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
#define ARCH_HAVE_DISABLE_BREAKPOINT 1
#define ARCH_HAVE_ENABLE_BREAKPOINT 1
diff --git a/sysdeps/linux-gnu/ia64/trace.c b/sysdeps/linux-gnu/ia64/trace.c
index 385fac1..d4813e6 100644
--- a/sysdeps/linux-gnu/ia64/trace.c
+++ b/sysdeps/linux-gnu/ia64/trace.c
@@ -1,3 +1,25 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2008,2009 Juan Cespedes
+ * Copyright (C) 2006 Steve Fink
+ * Copyright (C) 2006 Ian Wienand
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
#include "config.h"
#include <stdlib.h>
@@ -13,6 +35,7 @@
#include "proc.h"
#include "common.h"
+#include "type.h"
/* What we think of as a bundle, ptrace thinks of it as two unsigned
* longs */
@@ -244,18 +267,26 @@ gimme_float_arg(enum tof type, Process *proc, int arg_num) {
exit(1);
}
+static unsigned f_index;
+
long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
union {
long l;
float f;
double d;
} cvt;
- if (info->type == ARGTYPE_FLOAT)
- cvt.f = gimme_float_arg(type, proc, info->u.float_info.float_index);
- else if (info->type == ARGTYPE_DOUBLE)
- cvt.d = gimme_float_arg(type, proc, info->u.double_info.float_index);
+ if ((type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR)
+ && arg_num == 0)
+ /* See above for the parameter passing convention. */
+ f_index = 0;
+
+ if (info != NULL && info->type == ARGTYPE_FLOAT)
+ cvt.f = gimme_float_arg(type, proc, f_index++);
+ else if (info != NULL && info->type == ARGTYPE_DOUBLE)
+ cvt.d = gimme_float_arg(type, proc, f_index++);
else
cvt.l = gimme_long_arg(type, proc, arg_num);
@@ -263,9 +294,5 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
}
void
-save_register_args(enum tof type, Process *proc) {
-}
-
-void
get_arch_dep(Process *proc) {
}
diff --git a/sysdeps/linux-gnu/m68k/arch.h b/sysdeps/linux-gnu/m68k/arch.h
index 1790d09..9ca08ca 100644
--- a/sysdeps/linux-gnu/m68k/arch.h
+++ b/sysdeps/linux-gnu/m68k/arch.h
@@ -1,3 +1,23 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 1998,2002,2004 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
#define BREAKPOINT_VALUE { 0x4e, 0x4f }
#define BREAKPOINT_LENGTH 2
#define DECR_PC_AFTER_BREAK 2
diff --git a/sysdeps/linux-gnu/m68k/trace.c b/sysdeps/linux-gnu/m68k/trace.c
index c63702d..4bec016 100644
--- a/sysdeps/linux-gnu/m68k/trace.c
+++ b/sysdeps/linux-gnu/m68k/trace.c
@@ -47,7 +47,8 @@ syscall_p(Process *proc, int status, int *sysnum) {
}
long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
if (arg_num == -1) { /* return value */
return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_D0, 0);
}
@@ -84,7 +85,3 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
return 0;
}
-
-void
-save_register_args(enum tof type, Process *proc) {
-}
diff --git a/sysdeps/linux-gnu/mipsel/arch.h b/sysdeps/linux-gnu/mipsel/arch.h
index f7e2316..9ee29fd 100644
--- a/sysdeps/linux-gnu/mipsel/arch.h
+++ b/sysdeps/linux-gnu/mipsel/arch.h
@@ -1,3 +1,23 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2006 Eric Vaitl
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
#ifndef LTRACE_MIPS_ARCH_H
#define LTRACE_MIPS_ARCH_H
diff --git a/sysdeps/linux-gnu/mipsel/trace.c b/sysdeps/linux-gnu/mipsel/trace.c
index 4b999e4..fffaf75 100644
--- a/sysdeps/linux-gnu/mipsel/trace.c
+++ b/sysdeps/linux-gnu/mipsel/trace.c
@@ -9,6 +9,7 @@
#include "proc.h"
#include "common.h"
#include "mipsel.h"
+#include "type.h"
#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
# define PTRACE_PEEKUSER PTRACE_PEEKUSR
#endif
@@ -118,7 +119,8 @@ I'm not doing any floating point support here.
*/
long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
long ret;
long addr;
debug(2,"type %d arg %d",type,arg_num);
@@ -174,19 +176,4 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
return 0;
}
-/**
- \param type Type of call/return
- \param proc Process to work with.
-
- Called by \c output_left(), which is called on a syscall or
- function.
-
- The other architectures stub this out, but seems to be the place to
- stash off the arguments on a call so we have them on the return.
-
-*/
-void
-save_register_args(enum tof type, Process *proc) {
-}
-
/**@}*/
diff --git a/sysdeps/linux-gnu/ppc/Makefile.am b/sysdeps/linux-gnu/ppc/Makefile.am
index 7392452..e302016 100644
--- a/sysdeps/linux-gnu/ppc/Makefile.am
+++ b/sysdeps/linux-gnu/ppc/Makefile.am
@@ -4,7 +4,8 @@ noinst_LTLIBRARIES = \
___libcpu_la_SOURCES = \
plt.c \
regs.c \
- trace.c
+ trace.c \
+ fetch.c
noinst_HEADERS = \
arch.h \
diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h
index 6e258e7..846961e 100644
--- a/sysdeps/linux-gnu/ppc/arch.h
+++ b/sysdeps/linux-gnu/ppc/arch.h
@@ -1,3 +1,24 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2012 Petr Machata
+ * Copyright (C) 2006 Paul Gilliam
+ * Copyright (C) 2002,2004 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
#ifndef LTRACE_PPC_ARCH_H
#define LTRACE_PPC_ARCH_H
@@ -18,6 +39,9 @@
#define ARCH_HAVE_ADD_PLT_ENTRY
#define ARCH_HAVE_TRANSLATE_ADDRESS
#define ARCH_HAVE_DYNLINK_DONE
+#define ARCH_HAVE_FETCH_ARG
+#define ARCH_HAVE_SIZEOF
+#define ARCH_HAVE_ALIGNOF
struct library_symbol;
diff --git a/sysdeps/linux-gnu/ppc/fetch.c b/sysdeps/linux-gnu/ppc/fetch.c
new file mode 100644
index 0000000..370f43e
--- /dev/null
+++ b/sysdeps/linux-gnu/ppc/fetch.c
@@ -0,0 +1,427 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <assert.h>
+#include <ptrace.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ucontext.h>
+
+#include <stdio.h>
+
+#include "backend.h"
+#include "fetch.h"
+#include "type.h"
+#include "proc.h"
+#include "value.h"
+
+static int allocate_gpr(struct fetch_context *ctx, struct Process *proc,
+ struct arg_type_info *info, struct value *valuep);
+
+/* Floating point registers have the same width on 32-bit as well as
+ * 64-bit PPC, but <ucontext.h> presents a different API depending on
+ * whether ltrace is PPC32 or PPC64.
+ *
+ * This is PPC64 definition. The PPC32 is simply an array of 33
+ * doubles, and doesn't contain the terminating pad. Both seem
+ * compatible enough. */
+struct fpregs_t
+{
+ double fpregs[32];
+ double fpscr;
+ unsigned int _pad[2];
+};
+
+typedef uint32_t gregs32_t[48];
+typedef uint64_t gregs64_t[48];
+
+struct fetch_context {
+ target_address_t stack_pointer;
+ int greg;
+ int freg;
+ int ret_struct;
+
+ union {
+ gregs32_t r32;
+ gregs64_t r64;
+ } regs;
+ struct fpregs_t fpregs;
+
+};
+
+static int
+fetch_context_init(struct Process *proc, struct fetch_context *context)
+{
+ context->greg = 3;
+ context->freg = 1;
+
+ if (proc->e_machine == EM_PPC)
+ context->stack_pointer = proc->stack_pointer + 8;
+ else
+ context->stack_pointer = proc->stack_pointer + 112;
+
+ /* When ltrace is 64-bit, we might use PTRACE_GETREGS to
+ * obtain 64-bit as well as 32-bit registers. But if we do it
+ * this way, 32-bit ltrace can obtain 64-bit registers.
+ *
+ * XXX this direction is not supported as of this writing, but
+ * should be eventually. */
+ if (proc->e_machine == EM_PPC64) {
+ if (ptrace(PTRACE_GETREGS64, proc->pid, 0,
+ &context->regs.r64) < 0)
+ return -1;
+ } else {
+#ifdef __powerpc64__
+ if (ptrace(PTRACE_GETREGS, proc->pid, 0,
+ &context->regs.r64) < 0)
+ return -1;
+ unsigned i;
+ for (i = 0; i < sizeof(context->regs.r64)/8; ++i)
+ context->regs.r32[i] = context->regs.r64[i];
+#else
+ if (ptrace(PTRACE_GETREGS, proc->pid, 0,
+ &context->regs.r32) < 0)
+ return -1;
+#endif
+ }
+
+ if (ptrace(PTRACE_GETFPREGS, proc->pid, 0, &context->fpregs) < 0)
+ return -1;
+
+ return 0;
+}
+
+struct fetch_context *
+arch_fetch_arg_init(enum tof type, struct Process *proc,
+ struct arg_type_info *ret_info)
+{
+ struct fetch_context *context = malloc(sizeof(*context));
+ if (context == NULL
+ || fetch_context_init(proc, context) < 0) {
+ free(context);
+ return NULL;
+ }
+
+ /* Aggregates or unions of any length, and character strings
+ * of length longer than 8 bytes, will be returned in a
+ * storage buffer allocated by the caller. The caller will
+ * pass the address of this buffer as a hidden first argument
+ * in r3, causing the first explicit argument to be passed in
+ * r4. */
+ context->ret_struct = ret_info->type == ARGTYPE_STRUCT;
+ if (context->ret_struct)
+ context->greg++;
+
+ return context;
+}
+
+struct fetch_context *
+arch_fetch_arg_clone(struct Process *proc,
+ struct fetch_context *context)
+{
+ struct fetch_context *clone = malloc(sizeof(*context));
+ if (clone == NULL)
+ return NULL;
+ *clone = *context;
+ return clone;
+}
+
+static int
+allocate_stack_slot(struct fetch_context *ctx, struct Process *proc,
+ struct arg_type_info *info, struct value *valuep)
+{
+ size_t sz = type_sizeof(proc, info);
+ if (sz == (size_t)-1)
+ return -1;
+
+ size_t a = type_alignof(proc, info);
+ size_t off = 0;
+ if (proc->e_machine == EM_PPC && a < 4)
+ a = 4;
+ else if (proc->e_machine == EM_PPC64 && a < 8)
+ a = 8;
+
+ /* XXX Remove the two double casts when target_address_t
+ * becomes integral type. */
+ uintptr_t tmp = align((uint64_t)(uintptr_t)ctx->stack_pointer, a);
+ ctx->stack_pointer = (target_address_t)tmp;
+
+ if (valuep != NULL) {
+ valuep->where = VAL_LOC_INFERIOR;
+ valuep->u.address = ctx->stack_pointer + off;
+ }
+
+ ctx->stack_pointer += sz;
+
+ return 0;
+}
+
+static uint64_t
+read_gpr(struct fetch_context *ctx, struct Process *proc, int reg_num)
+{
+ if (proc->e_machine == EM_PPC)
+ return ctx->regs.r32[reg_num];
+ else
+ return ctx->regs.r64[reg_num];
+}
+
+/* The support for little endian PowerPC is in upstream Linux and BFD,
+ * and Unix-like Solaris, which we might well support at some point,
+ * runs PowerPC in little endian as well. This code moves SZ-sized
+ * value to the beginning of W-sized BUF regardless of
+ * endian. */
+static void
+align_small_int(unsigned char *buf, size_t w, size_t sz)
+{
+ assert(w == 4 || w == 8);
+ union {
+ uint64_t i64;
+ uint32_t i32;
+ uint16_t i16;
+ uint8_t i8;
+ char buf[0];
+ } u;
+ memcpy(u.buf, buf, w);
+ if (w == 4)
+ u.i64 = u.i32;
+
+ switch (sz) {
+ case 1:
+ u.i8 = u.i64;
+ break;
+ case 2:
+ u.i16 = u.i64;
+ break;
+ case 4:
+ u.i32 = u.i64;
+ case 8:
+ break;
+ }
+
+ memcpy(buf, u.buf, sz);
+}
+
+static int
+allocate_gpr(struct fetch_context *ctx, struct Process *proc,
+ struct arg_type_info *info, struct value *valuep)
+{
+ if (ctx->greg > 10)
+ return allocate_stack_slot(ctx, proc, info, valuep);
+
+ int reg_num = ctx->greg++;
+ if (valuep == NULL)
+ return 0;
+
+ size_t sz = type_sizeof(proc, info);
+ if (sz == (size_t)-1)
+ return -1;
+ assert(sz == 1 || sz == 2 || sz == 4 || sz == 8);
+ if (value_reserve(valuep, sz) == NULL)
+ return -1;
+
+ union {
+ uint64_t i64;
+ unsigned char buf[0];
+ } u;
+
+ u.i64 = read_gpr(ctx, proc, reg_num);
+ if (proc->e_machine == EM_PPC)
+ align_small_int(u.buf, 8, sz);
+ memcpy(value_get_raw_data(valuep), u.buf, sz);
+ return 0;
+}
+
+static int
+allocate_float(struct fetch_context *ctx, struct Process *proc,
+ struct arg_type_info *info, struct value *valuep)
+{
+ int pool = proc->e_machine == EM_PPC64 ? 13 : 8;
+ if (ctx->freg <= pool) {
+ union {
+ double d;
+ float f;
+ char buf[0];
+ } u = { .d = ctx->fpregs.fpregs[ctx->freg] };
+
+ ctx->freg++;
+ if (proc->e_machine == EM_PPC64)
+ allocate_gpr(ctx, proc, info, NULL);
+
+ size_t sz = sizeof(double);
+ if (info->type == ARGTYPE_FLOAT) {
+ sz = sizeof(float);
+ u.f = (float)u.d;
+ }
+
+ if (value_reserve(valuep, sz) == NULL)
+ return -1;
+
+ memcpy(value_get_raw_data(valuep), u.buf, sz);
+ return 0;
+ }
+ return allocate_stack_slot(ctx, proc, info, valuep);
+}
+
+static int
+allocate_argument(struct fetch_context *ctx, struct Process *proc,
+ struct arg_type_info *info, struct value *valuep)
+{
+ /* Floating point types and void are handled specially. */
+ switch (info->type) {
+ case ARGTYPE_VOID:
+ value_set_word(valuep, 0);
+ return 0;
+
+ case ARGTYPE_FLOAT:
+ case ARGTYPE_DOUBLE:
+ return allocate_float(ctx, proc, info, valuep);
+
+ case ARGTYPE_STRUCT:
+ if (proc->e_machine == EM_PPC) {
+ if (value_pass_by_reference(valuep) < 0)
+ return -1;
+ } else {
+ /* PPC64: Fixed size aggregates and unions passed by
+ * value are mapped to as many doublewords of the
+ * parameter save area as the value uses in memory.
+ * [...] The first eight doublewords mapped to the
+ * parameter save area correspond to the registers r3
+ * through r10. */
+ }
+ /* fall through */
+ case ARGTYPE_CHAR:
+ case ARGTYPE_SHORT:
+ case ARGTYPE_USHORT:
+ case ARGTYPE_INT:
+ case ARGTYPE_UINT:
+ case ARGTYPE_LONG:
+ case ARGTYPE_ULONG:
+ case ARGTYPE_POINTER:
+ break;
+
+ case ARGTYPE_ARRAY:
+ /* Arrays decay into pointers. XXX Fortran? */
+ assert(info->type != ARGTYPE_ARRAY);
+ abort();
+ }
+
+ unsigned width = proc->e_machine == EM_PPC64 ? 8 : 4;
+
+ /* For other cases (integral types and aggregates), read the
+ * eightbytes comprising the data. */
+ size_t sz = type_sizeof(proc, valuep->type);
+ if (sz == (size_t)-1)
+ return -1;
+ size_t slots = (sz + width - 1) / width; /* Round up. */
+ unsigned char *buf = value_reserve(valuep, slots * width);
+ if (buf == NULL)
+ return -1;
+ struct arg_type_info *long_info = type_get_simple(ARGTYPE_LONG);
+
+ unsigned char *ptr = buf;
+ while (slots-- > 0) {
+ struct value val;
+ value_init(&val, proc, NULL, long_info, 0);
+ int rc = allocate_gpr(ctx, proc, long_info, &val);
+ if (rc >= 0) {
+ memcpy(ptr, value_get_data(&val, NULL), width);
+ ptr += width;
+ }
+ value_destroy(&val);
+ if (rc < 0)
+ return rc;
+ }
+
+ /* Small values need post-processing. */
+ if (sz < width) {
+ switch (info->type) {
+ case ARGTYPE_LONG:
+ case ARGTYPE_ULONG:
+ case ARGTYPE_POINTER:
+ case ARGTYPE_DOUBLE:
+ case ARGTYPE_VOID:
+ abort();
+
+ /* Simple integer types (char, short, int, long, enum)
+ * are mapped to a single doubleword. Values shorter
+ * than a doubleword are sign or zero extended as
+ * necessary. */
+ case ARGTYPE_CHAR:
+ case ARGTYPE_SHORT:
+ case ARGTYPE_INT:
+ case ARGTYPE_USHORT:
+ case ARGTYPE_UINT:
+ align_small_int(buf, width, sz);
+ break;
+
+ /* Single precision floating point values are mapped
+ * to the second word in a single doubleword.
+ *
+ * An aggregate or union smaller than one doubleword
+ * in size is padded so that it appears in the least
+ * significant bits of the doubleword. */
+ case ARGTYPE_FLOAT:
+ case ARGTYPE_ARRAY:
+ case ARGTYPE_STRUCT:
+ memmove(buf, buf + width - sz, sz);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int
+arch_fetch_arg_next(struct fetch_context *ctx, enum tof type,
+ struct Process *proc,
+ struct arg_type_info *info, struct value *valuep)
+{
+ return allocate_argument(ctx, proc, info, valuep);
+}
+
+int
+arch_fetch_retval(struct fetch_context *ctx, enum tof type,
+ struct Process *proc, struct arg_type_info *info,
+ struct value *valuep)
+{
+ if (ctx->ret_struct) {
+ assert(info->type == ARGTYPE_STRUCT);
+
+ uint64_t addr = read_gpr(ctx, proc, 3);
+ value_init(valuep, proc, NULL, info, 0);
+
+ valuep->where = VAL_LOC_INFERIOR;
+ /* XXX Remove the double cast when target_address_t
+ * becomes integral type. */
+ valuep->u.address = (target_address_t)(uintptr_t)addr;
+ return 0;
+ }
+
+ if (fetch_context_init(proc, ctx) < 0)
+ return -1;
+ return allocate_argument(ctx, proc, info, valuep);
+}
+
+void
+arch_fetch_arg_done(struct fetch_context *context)
+{
+ free(context);
+}
diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c
index 9717738..944bd6a 100644
--- a/sysdeps/linux-gnu/ppc/plt.c
+++ b/sysdeps/linux-gnu/ppc/plt.c
@@ -11,6 +11,7 @@
#include "library.h"
#include "breakpoint.h"
#include "linux-gnu/trace.h"
+#include "backend.h"
/* There are two PLT types on 32-bit PPC: old-style, BSS PLT, and
* new-style "secure" PLT. We can tell one from the other by the
diff --git a/sysdeps/linux-gnu/ppc/ptrace.h b/sysdeps/linux-gnu/ppc/ptrace.h
index c587f40..c3cbcb6 100644
--- a/sysdeps/linux-gnu/ppc/ptrace.h
+++ b/sysdeps/linux-gnu/ppc/ptrace.h
@@ -1,16 +1 @@
#include <sys/ptrace.h>
-#include <sys/ucontext.h>
-
-#ifdef __powerpc64__
-#define GET_FPREG(RS, R) ((RS)[R])
-#else
-#define GET_FPREG(RS, R) ((RS).fpregs[R])
-#endif
-
-typedef struct {
- int valid;
- gregset_t regs;
- fpregset_t fpregs;
- gregset_t regs_copy;
- fpregset_t fpregs_copy;
-} proc_archdep;
diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c
index 742785a..0b734e4 100644
--- a/sysdeps/linux-gnu/ppc/trace.c
+++ b/sysdeps/linux-gnu/ppc/trace.c
@@ -1,18 +1,41 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2010,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2011 Andreas Schwab
+ * Copyright (C) 2002,2004,2008,2009 Juan Cespedes
+ * Copyright (C) 2008 Luis Machado, IBM Corporation
+ * Copyright (C) 2006 Ian Wienand
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
#include "config.h"
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <sys/ptrace.h>
-#include <asm/ptrace.h>
+#include <assert.h>
#include <elf.h>
#include <errno.h>
+#include <signal.h>
#include <string.h>
-#include "proc.h"
+#include "backend.h"
+#include "breakpoint.h"
#include "common.h"
+#include "proc.h"
#include "ptrace.h"
-#include "breakpoint.h"
+#include "type.h"
#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
# define PTRACE_PEEKUSER PTRACE_PEEKUSR
@@ -24,23 +47,13 @@
void
get_arch_dep(Process *proc) {
- if (proc->arch_ptr == NULL) {
- proc->arch_ptr = malloc(sizeof(proc_archdep));
#ifdef __powerpc64__
- proc->mask_32bit = (proc->e_machine == EM_PPC);
+ proc->mask_32bit = (proc->e_machine == EM_PPC);
#endif
- }
-
- proc_archdep *a = (proc_archdep *) (proc->arch_ptr);
- a->valid = (ptrace(PTRACE_GETREGS, proc->pid, 0, &a->regs) >= 0)
- && (ptrace(PTRACE_GETFPREGS, proc->pid, 0, &a->fpregs) >= 0);
}
#define SYSCALL_INSN 0x44000002
-unsigned int greg = 3;
-unsigned int freg = 1;
-
/* Returns 1 if syscall, 2 if sysret, 0 otherwise. */
int
syscall_p(Process *proc, int status, int *sysnum) {
@@ -66,111 +79,6 @@ syscall_p(Process *proc, int status, int *sysnum) {
return 0;
}
-static long
-gimme_arg_regset(enum tof type, Process *proc, int arg_num, arg_type_info *info,
- gregset_t *regs, fpregset_t *fpregs)
-{
- union { long val; float fval; double dval; } cvt;
-
- if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) {
- if (freg <= 13 || (proc->mask_32bit && freg <= 8)) {
- double val = GET_FPREG(*fpregs, freg);
-
- if (info->type == ARGTYPE_FLOAT)
- cvt.fval = val;
- else
- cvt.dval = val;
-
- freg++;
- greg++;
-
- return cvt.val;
- }
- }
- else if (greg <= 10)
- return (*regs)[greg++];
- else {
-#ifdef __powerpc64__
- if (proc->mask_32bit)
- return ptrace (PTRACE_PEEKDATA, proc->pid,
- proc->stack_pointer + 8 +
- sizeof (int) * (arg_num - 8), 0) >> 32;
- else
- return ptrace (PTRACE_PEEKDATA, proc->pid,
- proc->stack_pointer + 112 +
- sizeof (long) * (arg_num - 8), 0);
-#else
- return ptrace (PTRACE_PEEKDATA, proc->pid,
- proc->stack_pointer + 8 +
- sizeof (long) * (arg_num - 8), 0);
-#endif
- }
-
- return 0;
-}
-
-static long
-gimme_retval(Process *proc, int arg_num, arg_type_info *info,
- gregset_t *regs, fpregset_t *fpregs)
-{
- union { long val; float fval; double dval; } cvt;
- if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) {
- double val = GET_FPREG(*fpregs, 1);
-
- if (info->type == ARGTYPE_FLOAT)
- cvt.fval = val;
- else
- cvt.dval = val;
-
- return cvt.val;
- }
- else
- return (*regs)[3];
-}
-
-/* Grab functions arguments based on the PPC64 ABI. */
-long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info)
-{
- proc_archdep *arch = (proc_archdep *)proc->arch_ptr;
- if (arch == NULL || !arch->valid)
- return -1;
-
- /* Check if we're entering a new function call to list parameters. If
- so, initialize the register control variables to keep track of where
- the parameters were stored. */
- if ((type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR)
- && arg_num == 0) {
- /* Initialize the set of registrers for parameter passing. */
- greg = 3;
- freg = 1;
- }
-
-
- if (type == LT_TOF_FUNCTIONR) {
- if (arg_num == -1)
- return gimme_retval(proc, arg_num, info,
- &arch->regs, &arch->fpregs);
- else
- return gimme_arg_regset(type, proc, arg_num, info,
- &arch->regs_copy,
- &arch->fpregs_copy);
- }
- else
- return gimme_arg_regset(type, proc, arg_num, info,
- &arch->regs, &arch->fpregs);
-}
-
-void
-save_register_args(enum tof type, Process *proc) {
- proc_archdep *arch = (proc_archdep *)proc->arch_ptr;
- if (arch == NULL || !arch->valid)
- return;
-
- memcpy(&arch->regs_copy, &arch->regs, sizeof(arch->regs));
- memcpy(&arch->fpregs_copy, &arch->fpregs, sizeof(arch->fpregs));
-}
-
/* The atomic skip code is mostly taken from GDB. */
/* Instruction masks used during single-stepping of atomic
@@ -309,3 +188,77 @@ arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp,
ptrace(PTRACE_CONT, proc->pid, 0, 0);
return 0;
}
+
+size_t
+arch_type_sizeof(struct Process *proc, struct arg_type_info *info)
+{
+ if (proc == NULL)
+ return (size_t)-2;
+
+ switch (info->type) {
+ case ARGTYPE_VOID:
+ return 0;
+
+ case ARGTYPE_CHAR:
+ return 1;
+
+ case ARGTYPE_SHORT:
+ case ARGTYPE_USHORT:
+ return 2;
+
+ case ARGTYPE_INT:
+ case ARGTYPE_UINT:
+ return 4;
+
+ case ARGTYPE_LONG:
+ case ARGTYPE_ULONG:
+ case ARGTYPE_POINTER:
+ return proc->e_machine == EM_PPC64 ? 8 : 4;
+
+ case ARGTYPE_FLOAT:
+ return 4;
+ case ARGTYPE_DOUBLE:
+ return 8;
+
+ case ARGTYPE_ARRAY:
+ case ARGTYPE_STRUCT:
+ /* Use default value. */
+ return (size_t)-2;
+ }
+ assert(info->type != info->type);
+ abort();
+}
+
+size_t
+arch_type_alignof(struct Process *proc, struct arg_type_info *info)
+{
+ if (proc == NULL)
+ return (size_t)-2;
+
+ switch (info->type) {
+ case ARGTYPE_VOID:
+ assert(info->type != ARGTYPE_VOID);
+ break;
+
+ case ARGTYPE_CHAR:
+ case ARGTYPE_SHORT:
+ case ARGTYPE_USHORT:
+ case ARGTYPE_INT:
+ case ARGTYPE_UINT:
+ case ARGTYPE_LONG:
+ case ARGTYPE_ULONG:
+ case ARGTYPE_POINTER:
+ case ARGTYPE_FLOAT:
+ case ARGTYPE_DOUBLE:
+ /* On both PPC and PPC64, fundamental types have the
+ * same alignment as size. */
+ return arch_type_sizeof(proc, info);
+
+ case ARGTYPE_ARRAY:
+ case ARGTYPE_STRUCT:
+ /* Use default value. */
+ return (size_t)-2;
+ }
+ assert(info->type != info->type);
+ abort();
+}
diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c
index c5282e4..e7556f5 100644
--- a/sysdeps/linux-gnu/proc.c
+++ b/sysdeps/linux-gnu/proc.c
@@ -1,3 +1,26 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2010 Zachary T Welch, CodeSourcery
+ * Copyright (C) 2010 Joe Damato
+ * Copyright (C) 1998,2008,2009 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
#define _GNU_SOURCE /* For getline. */
#include "config.h"
@@ -12,13 +35,18 @@
#include <link.h>
#include <signal.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include "common.h"
+#include "backend.h"
#include "breakpoint.h"
-#include "proc.h"
+#include "config.h"
+#include "debug.h"
+#include "events.h"
#include "library.h"
+#include "ltrace-elf.h"
+#include "proc.h"
/* /proc/pid doesn't exist just after the fork, and sometimes `ltrace'
* couldn't open it to find the executable. So it may be necessary to
@@ -88,7 +116,7 @@ each_line_starting(FILE *file, const char *prefix,
char * line;
while ((line = find_line_starting(file, prefix, len)) != NULL) {
enum callback_status st = (*cb)(line, prefix, data);
- free (line);
+ free(line);
if (st == CBS_STOP)
return;
}
@@ -588,3 +616,9 @@ task_kill (pid_t pid, int sig)
ret = syscall (__NR_tkill, pid, sig);
return ret;
}
+
+void
+process_removed(struct Process *proc)
+{
+ delete_events_for(proc);
+}
diff --git a/sysdeps/linux-gnu/s390/arch.h b/sysdeps/linux-gnu/s390/arch.h
index 5cf168c..6597355 100644
--- a/sysdeps/linux-gnu/s390/arch.h
+++ b/sysdeps/linux-gnu/s390/arch.h
@@ -1,8 +1,23 @@
/*
-** S/390 version
-** (C) Copyright 2001 IBM Poughkeepsie, IBM Corporation
-*/
+ * This file is part of ltrace.
+ * Copyright (C) 2001 IBM Poughkeepsie, IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
#define BREAKPOINT_VALUE { 0x00, 0x01 }
#define BREAKPOINT_LENGTH 2
#define DECR_PC_AFTER_BREAK 2
diff --git a/sysdeps/linux-gnu/s390/trace.c b/sysdeps/linux-gnu/s390/trace.c
index 8c08f1f..3381ccc 100644
--- a/sysdeps/linux-gnu/s390/trace.c
+++ b/sysdeps/linux-gnu/s390/trace.c
@@ -161,7 +161,8 @@ syscall_p(Process *proc, int status, int *sysnum) {
}
long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
long ret;
switch (arg_num) {
@@ -200,7 +201,3 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
#endif
return ret;
}
-
-void
-save_register_args(enum tof type, Process *proc) {
-}
diff --git a/sysdeps/linux-gnu/sparc/arch.h b/sysdeps/linux-gnu/sparc/arch.h
index 75251b8..9685d13 100644
--- a/sysdeps/linux-gnu/sparc/arch.h
+++ b/sysdeps/linux-gnu/sparc/arch.h
@@ -1,3 +1,23 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2004 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
#define BREAKPOINT_VALUE {0x91, 0xd0, 0x20, 0x01}
#define BREAKPOINT_LENGTH 4
#define DECR_PC_AFTER_BREAK 0
diff --git a/sysdeps/linux-gnu/sparc/trace.c b/sysdeps/linux-gnu/sparc/trace.c
index e05c4d3..83337fc 100644
--- a/sysdeps/linux-gnu/sparc/trace.c
+++ b/sysdeps/linux-gnu/sparc/trace.c
@@ -45,7 +45,8 @@ syscall_p(Process *proc, int status, int *sysnum) {
}
long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
+gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info)
+{
proc_archdep *a = (proc_archdep *) proc->arch_ptr;
if (!a->valid) {
fprintf(stderr, "Could not get child registers\n");
@@ -69,14 +70,3 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
}
return 0;
}
-
-void
-save_register_args(enum tof type, Process *proc) {
- proc_archdep *a = (proc_archdep *) proc->arch_ptr;
- if (a->valid) {
- if (type == LT_TOF_FUNCTION)
- memcpy(a->func_arg, &a->regs.u_regs[UREG_G7], sizeof(a->func_arg));
- else
- memcpy(a->sysc_arg, &a->regs.u_regs[UREG_G7], sizeof(a->sysc_arg));
- }
-}
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
index bd5d826..cef8e3d 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -1,3 +1,26 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2007,2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2010 Joe Damato
+ * Copyright (C) 1998,2002,2003,2004,2008,2009 Juan Cespedes
+ * Copyright (C) 2006 Ian Wienand
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -14,11 +37,15 @@
#include <asm/unistd.h>
#include <assert.h>
-#include "common.h"
+#include "linux-gnu/trace.h"
+#include "backend.h"
-#include "config.h"
#include "breakpoint.h"
+#include "debug.h"
+#include "events.h"
+#include "options.h"
#include "proc.h"
-#include "linux-gnu/trace.h"
+#include "ptrace.h"
+#include "type.h"
#include "config.h"
#ifdef HAVE_LIBSELINUX
@@ -47,44 +74,6 @@
#endif /* PTRACE_EVENT_FORK */
-#ifdef ARCH_HAVE_UMOVELONG
-extern int arch_umovelong (Process *, void *, long *, arg_type_info *);
-int
-umovelong (Process *proc, void *addr, long *result, arg_type_info *info) {
- return arch_umovelong (proc, addr, result, info);
-}
-#else
-/* Read a single long from the process's memory address 'addr' */
-int
-umovelong (Process *proc, void *addr, long *result, arg_type_info *info) {
- long pointed_to;
-
- errno = 0;
- pointed_to = ptrace (PTRACE_PEEKTEXT, proc->pid, addr, 0);
- if (pointed_to == -1 && errno)
- return -errno;
-
-#if SIZEOF_LONG == 8
- if (info != NULL
- && (info->type == ARGTYPE_INT
- || (proc->mask_32bit
- && (info->type == ARGTYPE_POINTER
- || info->type == ARGTYPE_STRING)))) {
-#if defined (ARCH_ENDIAN_LITTLE)
- pointed_to &= 0x00000000ffffffffUL;
-#elif defined (ARCH_ENDIAN_BIG)
- pointed_to = (long)(((unsigned long)pointed_to) >> 32);
-#else
-# error arch.h has to define endianness
-#endif
- }
-#endif
-
- *result = pointed_to;
- return 0;
-}
-#endif
-
void
trace_fail_warning(pid_t pid)
{
@@ -366,15 +363,15 @@ process_stopping_done(struct process_stopping_handler * self, Process * leader)
{
debug(DEBUG_PROCESS, "process stopping done %d",
self->task_enabling_breakpoint->pid);
- size_t i;
+
if (!self->exiting) {
+ size_t i;
for (i = 0; i < self->pids.count; ++i)
if (self->pids.tasks[i].pid != 0
&& (self->pids.tasks[i].delivered
|| self->pids.tasks[i].sysret))
continue_process(self->pids.tasks[i].pid);
continue_process(self->task_enabling_breakpoint->pid);
- destroy_event_handler(leader);
}
if (self->exiting) {
@@ -390,6 +387,7 @@ process_stopping_done(struct process_stopping_handler * self, Process * leader)
case CBS_CONT:
goto ugly_workaround;
}
+ destroy_event_handler(leader);
}
}
@@ -678,8 +676,8 @@ singlestep_error(struct process_stopping_handler *self)
struct Process *teb = self->task_enabling_breakpoint;
struct breakpoint *sbp = self->breakpoint_being_enabled;
fprintf(stderr, "%d couldn't continue when handling %s (%p) at %p\n",
- teb->pid, sbp->libsym != NULL ? sbp->libsym->name : NULL,
- sbp->addr, get_instruction_pointer(teb));
+ teb->pid, breakpoint_name(sbp), sbp->addr,
+ get_instruction_pointer(teb));
delete_breakpoint(teb->leader, sbp->addr);
}
@@ -1233,32 +1231,3 @@ umovebytes(Process *proc, void *addr, void *laddr, size_t len) {
return bytes_read;
}
-
-/* Read a series of bytes starting at the process's memory address
- 'addr' and continuing until a NUL ('\0') is seen or 'len' bytes
- have been read.
-*/
-int
-umovestr(Process *proc, void *addr, int len, void *laddr) {
- union {
- long a;
- char c[sizeof(long)];
- } a;
- unsigned i;
- int offset = 0;
-
- while (offset < len) {
- a.a = ptrace(PTRACE_PEEKTEXT, proc->pid, addr + offset, 0);
- for (i = 0; i < sizeof(long); i++) {
- if (a.c[i] && offset + (signed)i < len) {
- *(char *)(laddr + offset + i) = a.c[i];
- } else {
- *(char *)(laddr + offset + i) = '\0';
- return 0;
- }
- }
- offset += sizeof(long);
- }
- *(char *)(laddr + offset) = '\0';
- return 0;
-}
diff --git a/sysdeps/linux-gnu/trace.h b/sysdeps/linux-gnu/trace.h
index 0f40709..88ac33d 100644
--- a/sysdeps/linux-gnu/trace.h
+++ b/sysdeps/linux-gnu/trace.h
@@ -21,6 +21,8 @@
#ifndef _LTRACE_LINUX_TRACE_H_
#define _LTRACE_LINUX_TRACE_H_
+#include "proc.h"
+
/* This publishes some Linux-specific data structures used for process
* handling. */
diff --git a/sysdeps/linux-gnu/x86/Makefile.am b/sysdeps/linux-gnu/x86/Makefile.am
new file mode 100644
index 0000000..d5eb6e7
--- /dev/null
+++ b/sysdeps/linux-gnu/x86/Makefile.am
@@ -0,0 +1,19 @@
+noinst_LTLIBRARIES = \
+ ../libcpu.la
+
+___libcpu_la_SOURCES = \
+ plt.c \
+ regs.c \
+ trace.c \
+ fetch.c
+
+noinst_HEADERS = \
+ arch.h \
+ ptrace.h \
+ signalent.h \
+ signalent1.h \
+ syscallent.h \
+ syscallent1.h
+
+MAINTAINERCLEANFILES = \
+ Makefile.in
diff --git a/sysdeps/linux-gnu/x86/arch.h b/sysdeps/linux-gnu/x86/arch.h
new file mode 100644
index 0000000..77a09d7
--- /dev/null
+++ b/sysdeps/linux-gnu/x86/arch.h
@@ -0,0 +1,40 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011 Petr Machata
+ * Copyright (C) 2006 Ian Wienand
+ * Copyright (C) 2004 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#define BREAKPOINT_VALUE {0xcc}
+#define BREAKPOINT_LENGTH 1
+#define DECR_PC_AFTER_BREAK 1
+#define ARCH_HAVE_FETCH_ARG
+#define ARCH_HAVE_SIZEOF
+#define ARCH_HAVE_ALIGNOF
+#define ARCH_ENDIAN_LITTLE
+
+#ifdef __x86_64__
+#define LT_ELFCLASS ELFCLASS64
+#define LT_ELF_MACHINE EM_X86_64
+#endif
+#define LT_ELFCLASS2 ELFCLASS32
+#define LT_ELF_MACHINE2 EM_386
+
+/* __NR_fork, __NR_clone, __NR_clone2, __NR_vfork and __NR_execve
+ from asm-i386/unistd.h. */
+#define FORK_EXEC_SYSCALLS , { 2, 120, -1, 190, 11 }
diff --git a/sysdeps/linux-gnu/x86/fetch.c b/sysdeps/linux-gnu/x86/fetch.c
new file mode 100644
index 0000000..64f57f3
--- /dev/null
+++ b/sysdeps/linux-gnu/x86/fetch.c
@@ -0,0 +1,809 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <assert.h>
+#include <gelf.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "backend.h"
+#include "expr.h"
+#include "fetch.h"
+#include "proc.h"
+#include "ptrace.h"
+#include "type.h"
+#include "value.h"
+
+enum arg_class {
+ CLASS_INTEGER,
+ CLASS_SSE,
+ CLASS_NO,
+ CLASS_MEMORY,
+ CLASS_X87,
+};
+
+enum reg_pool {
+ POOL_FUNCALL,
+ POOL_SYSCALL,
+ /* A common pool for system call and function call return is
+ * enough, the ABI is similar enough. */
+ POOL_RETVAL,
+};
+
+struct fetch_context
+{
+ struct user_regs_struct iregs;
+ struct user_fpregs_struct fpregs;
+
+ void *stack_pointer;
+ size_t ireg; /* Used-up integer registers. */
+ size_t freg; /* Used-up floating registers. */
+
+ union {
+ struct {
+ /* Storage classes for return type. We need
+ * to compute them anyway, so let's keep them
+ * around. */
+ enum arg_class ret_classes[2];
+ ssize_t num_ret_classes;
+ } x86_64;
+ struct {
+ struct value retval;
+ } ix86;
+ } u;
+};
+
+#ifndef __x86_64__
+__attribute__((noreturn)) static void
+i386_unreachable(void)
+{
+ abort();
+}
+#endif
+
+static int
+contains_unaligned_fields(struct arg_type_info *info)
+{
+ /* XXX currently we don't support structure alignment. */
+ return 0;
+}
+
+static int
+has_nontrivial_ctor_dtor(struct arg_type_info *info)
+{
+ /* XXX another unsupported aspect of type info. We might call
+ * these types "class" instead of "struct" in the config
+ * file. */
+ return 0;
+}
+
+static void
+copy_int_register(struct fetch_context *context,
+ struct value *valuep, unsigned long val, size_t offset)
+{
+ if (valuep != NULL) {
+ unsigned char *buf = value_get_raw_data(valuep);
+ memcpy(buf + offset, &val, sizeof(val));
+ }
+ context->ireg++;
+}
+
+static void
+copy_sse_register(struct fetch_context *context, struct value *valuep,
+ int half, size_t sz, size_t offset)
+{
+#ifdef __x86_64__
+ union {
+ uint32_t sse[4];
+ long halves[2];
+ } u;
+ size_t off = 4 * context->freg++;
+ memcpy(u.sse, context->fpregs.xmm_space + off, sizeof(u.sse));
+
+ if (valuep != NULL) {
+ unsigned char *buf = value_get_raw_data(valuep);
+ memcpy(buf + offset, u.halves + half, sz);
+ }
+#else
+ i386_unreachable();
+#endif
+}
+
+static void
+allocate_stack_slot(struct fetch_context *context,
+ struct value *valuep, size_t sz, size_t offset,
+ size_t archw)
+{
+ size_t a = type_alignof(valuep->inferior, valuep->type);
+ if (a < archw)
+ a = archw;
+ context->stack_pointer
+ = (void *)align((unsigned long)context->stack_pointer, a);
+
+ if (valuep != NULL) {
+ valuep->where = VAL_LOC_INFERIOR;
+ valuep->u.address = context->stack_pointer;
+ }
+ context->stack_pointer += sz;
+}
+
+static enum arg_class
+allocate_x87(struct fetch_context *context, struct value *valuep,
+ size_t sz, size_t offset, enum reg_pool pool, size_t archw)
+{
+ /* Both i386 and x86_64 ABI only ever really use x87 registers
+ * to return values. Otherwise, the parameter is treated as
+ * if it were CLASS_MEMORY. On x86_64 x87 registers are only
+ * used for returning long double values, which we currently
+ * don't support. */
+
+ if (pool != POOL_RETVAL) {
+ allocate_stack_slot(context, valuep, sz, offset, archw);
+ return CLASS_MEMORY;
+
+ }
+
+ /* If the class is X87, the value is returned on the X87 stack
+ * in %st0 as 80-bit x87 number.
+ *
+ * If the class is X87UP, the value is returned together with
+ * the previous X87 value in %st0.
+ *
+ * If the class is COMPLEX_X87, the real part of the value is
+ * returned in %st0 and the imaginary part in %st1. */
+
+ if (valuep != NULL) {
+ union {
+ long double ld;
+ double d;
+ float f;
+ char buf[0];
+ } u;
+
+ /* The x87 floating point value is in long double
+ * format, so we need to convert in to the right type.
+ * Alternatively we might just leave it as is and
+ * smuggle the long double type into the value (via
+ * value_set_type), but for that we first need to
+ * support long double in the first place. */
+
+#ifdef __x86_64__
+ unsigned int *reg;
+#else
+ long int *reg;
+#endif
+ reg = &context->fpregs.st_space[0];
+ memcpy(&u.ld, reg, sizeof(u));
+ if (valuep->type->type == ARGTYPE_FLOAT)
+ u.f = (float)u.ld;
+ else if (valuep->type->type == ARGTYPE_DOUBLE)
+ u.d = (double)u.ld;
+ else
+ assert(!"Unexpected floating type!"), abort();
+
+ unsigned char *buf = value_get_raw_data(valuep);
+ memcpy(buf + offset, u.buf, sz);
+ }
+ return CLASS_X87;
+}
+
+static enum arg_class
+allocate_integer(struct fetch_context *context, struct value *valuep,
+ size_t sz, size_t offset, enum reg_pool pool)
+{
+#define HANDLE(NUM, WHICH) \
+ case NUM: \
+ copy_int_register(context, valuep, \
+ context->iregs.WHICH, offset); \
+ return CLASS_INTEGER
+
+ switch (pool) {
+ case POOL_FUNCALL:
+#ifdef __x86_64__
+ switch (context->ireg) {
+ HANDLE(0, rdi);
+ HANDLE(1, rsi);
+ HANDLE(2, rdx);
+ HANDLE(3, rcx);
+ HANDLE(4, r8);
+ HANDLE(5, r9);
+ default:
+ allocate_stack_slot(context, valuep, sz, offset, 8);
+ return CLASS_MEMORY;
+ }
+#else
+ i386_unreachable();
+#endif
+
+ case POOL_SYSCALL:
+#ifdef __x86_64__
+ switch (context->ireg) {
+ HANDLE(0, rdi);
+ HANDLE(1, rsi);
+ HANDLE(2, rdx);
+ HANDLE(3, r10);
+ HANDLE(4, r8);
+ HANDLE(5, r9);
+ default:
+ assert(!"More than six syscall arguments???");
+ abort();
+ }
+#else
+ i386_unreachable();
+#endif
+
+ case POOL_RETVAL:
+ switch (context->ireg) {
+#ifdef __x86_64__
+ HANDLE(0, rax);
+ HANDLE(1, rdx);
+#else
+ HANDLE(0, eax);
+#endif
+ default:
+ assert(!"Too many return value classes.");
+ abort();
+ }
+ }
+
+ abort();
+
+#undef HANDLE
+}
+
+static enum arg_class
+allocate_sse(struct fetch_context *context, struct value *valuep,
+ size_t sz, size_t offset, enum reg_pool pool)
+{
+ size_t num_regs = 0;
+ switch (pool) {
+ case POOL_FUNCALL:
+ num_regs = 8;
+ case POOL_SYSCALL:
+ break;
+ case POOL_RETVAL:
+ num_regs = 2;
+ }
+
+ if (context->freg >= num_regs) {
+ /* We shouldn't see overflow for RETVAL or SYSCALL
+ * pool. */
+ assert(pool == POOL_FUNCALL);
+ allocate_stack_slot(context, valuep, sz, offset, 8);
+ return CLASS_MEMORY;
+ } else {
+ copy_sse_register(context, valuep, 0, sz, offset);
+ return CLASS_SSE;
+ }
+}
+
+/* This allocates registers or stack space for another argument of the
+ * class CLS. */
+static enum arg_class
+allocate_class(enum arg_class cls, struct fetch_context *context,
+ struct value *valuep, size_t sz, size_t offset, enum reg_pool pool)
+{
+ switch (cls) {
+ case CLASS_MEMORY:
+ allocate_stack_slot(context, valuep, sz, offset, 8);
+ case CLASS_NO:
+ return cls;
+
+ case CLASS_INTEGER:
+ return allocate_integer(context, valuep, sz, offset, pool);
+
+ case CLASS_SSE:
+ return allocate_sse(context, valuep, sz, offset, pool);
+
+ case CLASS_X87:
+ return allocate_x87(context, valuep, sz, offset, pool, 8);
+ }
+ abort();
+}
+
+static ssize_t
+classify(struct Process *proc, struct fetch_context *context,
+ struct arg_type_info *info, struct value *valuep, enum arg_class classes[],
+ size_t sz, size_t eightbytes);
+
+/* This classifies one eightbyte part of an array or struct. */
+static ssize_t
+classify_eightbyte(struct Process *proc, struct fetch_context *context,
+ struct arg_type_info *info, struct value *valuep,
+ enum arg_class *classp, size_t start, size_t end,
+ struct arg_type_info *(*getter)(struct arg_type_info *,
+ size_t))
+{
+ size_t i;
+ enum arg_class cls = CLASS_NO;
+ for (i = start; i < end; ++i) {
+ enum arg_class cls2;
+ struct arg_type_info *info2 = getter(info, i);
+ size_t sz = type_sizeof(proc, info2);
+ if (sz == (size_t)-1)
+ return -1;
+ if (classify(proc, context, info2, valuep, &cls2, sz, 1) < 0)
+ return -1;
+
+ if (cls == CLASS_NO)
+ cls = cls2;
+ else if (cls2 == CLASS_NO || cls == cls2)
+ ;
+ else if (cls == CLASS_MEMORY || cls2 == CLASS_MEMORY)
+ cls = CLASS_MEMORY;
+ else if (cls == CLASS_INTEGER || cls2 == CLASS_INTEGER)
+ cls = CLASS_INTEGER;
+ else
+ cls = CLASS_SSE;
+ }
+
+ *classp = cls;
+ return 1;
+}
+
+/* This classifies small arrays and structs. */
+static ssize_t
+classify_eightbytes(struct Process *proc, struct fetch_context *context,
+ struct arg_type_info *info, struct value *valuep,
+ enum arg_class classes[], size_t elements,
+ size_t eightbytes,
+ struct arg_type_info *(*getter)(struct arg_type_info *,
+ size_t))
+{
+ if (eightbytes > 1) {
+ /* Where the second eightbyte starts. Number of the
+ * first element in the structure that belongs to the
+ * second eightbyte. */
+ size_t start_2nd = 0;
+ size_t i;
+ for (i = 0; i < elements; ++i)
+ if (type_offsetof(proc, info, i) >= 8) {
+ start_2nd = i;
+ break;
+ }
+
+ enum arg_class cls1, cls2;
+ if (classify_eightbyte(proc, context, info, valuep, &cls1,
+ 0, start_2nd, getter) < 0
+ || classify_eightbyte(proc, context, info, valuep, &cls2,
+ start_2nd, elements, getter) < 0)
+ return -1;
+
+ if (cls1 == CLASS_MEMORY || cls2 == CLASS_MEMORY) {
+ classes[0] = CLASS_MEMORY;
+ return 1;
+ }
+
+ classes[0] = cls1;
+ classes[1] = cls2;
+ return 2;
+ }
+
+ return classify_eightbyte(proc, context, info, valuep, classes,
+ 0, elements, getter);
+}
+
+static struct arg_type_info *
+get_array_field(struct arg_type_info *info, size_t emt)
+{
+ return info->u.array_info.elt_type;
+}
+
+static ssize_t
+classify(struct Process *proc, struct fetch_context *context,
+ struct arg_type_info *info, struct value *valuep, enum arg_class classes[],
+ size_t sz, size_t eightbytes)
+{
+ switch (info->type) {
+ case ARGTYPE_VOID:
+ return 0;
+
+ case ARGTYPE_CHAR:
+ case ARGTYPE_SHORT:
+ case ARGTYPE_USHORT:
+ case ARGTYPE_INT:
+ case ARGTYPE_UINT:
+ case ARGTYPE_LONG:
+ case ARGTYPE_ULONG:
+
+ case ARGTYPE_POINTER:
+ /* and LONGLONG */
+ /* CLASS_INTEGER */
+ classes[0] = CLASS_INTEGER;
+ return 1;
+
+ case ARGTYPE_FLOAT:
+ case ARGTYPE_DOUBLE:
+ /* and DECIMAL, and _m64 */
+ classes[0] = CLASS_SSE;
+ return 1;
+
+ case ARGTYPE_ARRAY:
+ /* N.B. this cannot be top-level array, those decay to
+ * pointers. Therefore, it must be inside structure
+ * that's at most 2 eightbytes long. */
+
+ /* Structures with flexible array members can't be
+ * passed by value. */
+ assert(expr_is_compile_constant(info->u.array_info.length));
+
+ long l;
+ if (expr_eval_constant(info->u.array_info.length, &l) < 0)
+ return -1;
+
+ return classify_eightbytes(proc, context, info, valuep, classes,
+ (size_t)l, eightbytes,
+ get_array_field);
+
+ case ARGTYPE_STRUCT:
+ /* N.B. "big" structs are dealt with in the
+ * caller. */
+ return classify_eightbytes(proc, context, info, valuep, classes,
+ type_struct_size(info),
+ eightbytes, type_struct_get);
+ }
+ abort();
+}
+
+static ssize_t
+pass_by_reference(struct value *valuep, enum arg_class classes[])
+{
+ if (valuep != NULL && value_pass_by_reference(valuep) < 0)
+ return -1;
+ classes[0] = CLASS_INTEGER;
+ return 1;
+}
+
+static ssize_t
+classify_argument(struct Process *proc, struct fetch_context *context,
+ struct arg_type_info *info, struct value *valuep,
+ enum arg_class classes[], size_t *sizep)
+{
+ size_t sz = type_sizeof(proc, info);
+ if (sz == (size_t)-1)
+ return -1;
+ *sizep = sz;
+
+ size_t eightbytes = (sz + 7) / 8; /* Round up. */
+
+ /* Arrays decay into pointers. */
+ assert(info->type != ARGTYPE_ARRAY);
+
+ if (info->type == ARGTYPE_STRUCT) {
+ if (eightbytes > 2 || contains_unaligned_fields(info)) {
+ classes[0] = CLASS_MEMORY;
+ return 1;
+ }
+
+ if (has_nontrivial_ctor_dtor(info))
+ return pass_by_reference(valuep, classes);
+ }
+
+ return classify(proc, context, info, valuep, classes, sz, eightbytes);
+}
+
+static int
+fetch_register_banks(struct Process *proc, struct fetch_context *context,
+ int floating)
+{
+ if (ptrace(PTRACE_GETREGS, proc->pid, 0, &context->iregs) < 0)
+ return -1;
+ context->ireg = 0;
+
+ if (floating) {
+ if (ptrace(PTRACE_GETFPREGS, proc->pid,
+ 0, &context->fpregs) < 0)
+ return -1;
+ context->freg = 0;
+ } else {
+ context->freg = -1;
+ }
+
+ return 0;
+}
+
+static int
+arch_fetch_arg_next_32(struct fetch_context *context, enum tof type,
+ struct Process *proc, struct arg_type_info *info,
+ struct value *valuep)
+{
+ size_t sz = type_sizeof(proc, info);
+ if (sz == (size_t)-1)
+ return -1;
+
+ allocate_stack_slot(context, valuep, sz, 0, 4);
+
+ return 0;
+}
+
+static int
+arch_fetch_retval_32(struct fetch_context *context, enum tof type,
+ struct Process *proc, struct arg_type_info *info,
+ struct value *valuep)
+{
+ if (fetch_register_banks(proc, context, type == LT_TOF_FUNCTIONR) < 0)
+ return -1;
+
+ struct value *retval = &context->u.ix86.retval;
+ if (retval->type != NULL) {
+ /* Struct return value was extracted when in fetch
+ * init. */
+ memcpy(valuep, &context->u.ix86.retval, sizeof(*valuep));
+ return 0;
+ }
+
+ size_t sz = type_sizeof(proc, info);
+ if (sz == (size_t)-1)
+ return -1;
+ if (value_reserve(valuep, sz) == NULL)
+ return -1;
+
+ switch (info->type) {
+ enum arg_class cls;
+ case ARGTYPE_VOID:
+ return 0;
+
+ case ARGTYPE_INT:
+ case ARGTYPE_UINT:
+ case ARGTYPE_LONG:
+ case ARGTYPE_ULONG:
+ case ARGTYPE_CHAR:
+ case ARGTYPE_SHORT:
+ case ARGTYPE_USHORT:
+ case ARGTYPE_POINTER:
+ cls = allocate_integer(context, valuep, sz, 0, POOL_RETVAL);
+ assert(cls == CLASS_INTEGER);
+ return 0;
+
+ case ARGTYPE_FLOAT:
+ case ARGTYPE_DOUBLE:
+ cls = allocate_x87(context, valuep, sz, 0, POOL_RETVAL, 4);
+ assert(cls == CLASS_X87);
+ return 0;
+
+ case ARGTYPE_ARRAY:
+ case ARGTYPE_STRUCT: /* Handled above. */
+ assert(!"Unexpected i386 retval type!");
+ abort();
+ }
+
+ abort();
+}
+
+static target_address_t
+fetch_stack_pointer(struct fetch_context *context)
+{
+ target_address_t sp;
+#ifdef __x86_64__
+ sp = (target_address_t)context->iregs.rsp;
+#else
+ sp = (target_address_t)context->iregs.esp;
+#endif
+ return sp;
+}
+
+struct fetch_context *
+arch_fetch_arg_init_32(struct fetch_context *context,
+ enum tof type, struct Process *proc,
+ struct arg_type_info *ret_info)
+{
+ context->stack_pointer = fetch_stack_pointer(context) + 4;
+
+ size_t sz = type_sizeof(proc, ret_info);
+ if (sz == (size_t)-1)
+ return NULL;
+
+ struct value *retval = &context->u.ix86.retval;
+ if (ret_info->type == ARGTYPE_STRUCT) {
+ value_init(retval, proc, NULL, ret_info, 0);
+
+ enum arg_class dummy[2];
+ if (pass_by_reference(retval, dummy) < 0)
+ return NULL;
+ allocate_stack_slot(context, retval, 4, 0, 4);
+
+ } else {
+ value_init_detached(retval, NULL, NULL, 0);
+ }
+
+ return context;
+}
+
+struct fetch_context *
+arch_fetch_arg_init_64(struct fetch_context *ctx, enum tof type,
+ struct Process *proc, struct arg_type_info *ret_info)
+{
+ /* The first stack slot holds a return address. */
+ ctx->stack_pointer = fetch_stack_pointer(ctx) + 8;
+
+ size_t size;
+ ctx->u.x86_64.num_ret_classes
+ = classify_argument(proc, ctx, ret_info, NULL,
+ ctx->u.x86_64.ret_classes, &size);
+ if (ctx->u.x86_64.num_ret_classes == -1)
+ return NULL;
+
+ /* If the class is MEMORY, then the first argument is a hidden
+ * pointer to the allocated storage. */
+ if (ctx->u.x86_64.num_ret_classes > 0
+ && ctx->u.x86_64.ret_classes[0] == CLASS_MEMORY) {
+ /* MEMORY should be the sole class. */
+ assert(ctx->u.x86_64.num_ret_classes == 1);
+ allocate_integer(ctx, NULL, size, 0, POOL_FUNCALL);
+ }
+
+ return ctx;
+}
+
+struct fetch_context *
+arch_fetch_arg_init(enum tof type, struct Process *proc,
+ struct arg_type_info *ret_info)
+{
+ struct fetch_context *ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ assert(type != LT_TOF_FUNCTIONR
+ && type != LT_TOF_SYSCALLR);
+ if (fetch_register_banks(proc, ctx, type == LT_TOF_FUNCTION) < 0) {
+ fail:
+ free(ctx);
+ return NULL;
+ }
+
+ struct fetch_context *ret;
+ if (proc->e_machine == EM_386)
+ ret = arch_fetch_arg_init_32(ctx, type, proc, ret_info);
+ else
+ ret = arch_fetch_arg_init_64(ctx, type, proc, ret_info);
+ if (ret == NULL)
+ goto fail;
+ return ret;
+}
+
+struct fetch_context *
+arch_fetch_arg_clone(struct Process *proc, struct fetch_context *context)
+{
+ struct fetch_context *ret = malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ return memcpy(ret, context, sizeof(*ret));
+}
+
+static int
+arch_fetch_pool_arg_next(struct fetch_context *context, enum tof type,
+ struct Process *proc, struct arg_type_info *info,
+ struct value *valuep, enum reg_pool pool)
+{
+ enum arg_class classes[2];
+ size_t sz, sz1;
+ ssize_t i;
+ ssize_t nclasses = classify_argument(proc, context, info, valuep,
+ classes, &sz);
+ if (nclasses == -1)
+ return -1;
+ if (value_reserve(valuep, sz) == NULL)
+ return -1;
+
+ /* If there are no registers available for any eightbyte of an
+ * argument, the whole argument is passed on the stack. If
+ * registers have already been assigned for some eightbytes of
+ * such an argument, the assignments get reverted. */
+ struct fetch_context tmp_context = *context;
+ int revert;
+ if (nclasses == 1) {
+ revert = allocate_class(classes[0], &tmp_context,
+ valuep, sz, 0, pool) != classes[0];
+ } else {
+ revert = 0;
+ for (i = 0; i < nclasses; ++i) {
+ sz1 = (size_t)(8 * (i + 1)) > sz ? sz - 8 * i : 8;
+ if (allocate_class(classes[i], &tmp_context, valuep,
+ sz1, 8 * i, pool) != classes[i])
+ revert = 1;
+ }
+ }
+
+ if (nclasses > 1 && revert)
+ allocate_class(CLASS_MEMORY, context, valuep, sz, 0, pool);
+ else
+ *context = tmp_context; /* Commit. */
+
+ return 0;
+}
+
+int
+arch_fetch_fun_retval(struct fetch_context *context, enum tof type,
+ struct Process *proc, struct arg_type_info *info,
+ struct value *valuep)
+{
+ assert(type != LT_TOF_FUNCTION
+ && type != LT_TOF_SYSCALL);
+ if (value_reserve(valuep, 8 * context->u.x86_64.num_ret_classes) == NULL
+ || fetch_register_banks(proc, context,
+ type == LT_TOF_FUNCTIONR) < 0)
+ return -1;
+
+ if (context->u.x86_64.num_ret_classes == 1
+ && context->u.x86_64.ret_classes[0] == CLASS_MEMORY)
+ pass_by_reference(valuep, context->u.x86_64.ret_classes);
+
+ size_t sz = type_sizeof(proc, valuep->type);
+ if (sz == (size_t)-1)
+ return -1;
+
+ ssize_t i;
+ size_t sz1 = context->u.x86_64.num_ret_classes == 1 ? sz : 8;
+ for (i = 0; i < context->u.x86_64.num_ret_classes; ++i) {
+ enum arg_class cls
+ = allocate_class(context->u.x86_64.ret_classes[i],
+ context, valuep, sz1,
+ 8 * i, POOL_RETVAL);
+ assert(cls == context->u.x86_64.ret_classes[i]);
+ }
+ return 0;
+}
+
+int
+arch_fetch_arg_next(struct fetch_context *context, enum tof type,
+ struct Process *proc, struct arg_type_info *info,
+ struct value *valuep)
+{
+ if (proc->e_machine == EM_386)
+ return arch_fetch_arg_next_32(context, type, proc,
+ info, valuep);
+
+ switch (type) {
+ case LT_TOF_FUNCTION:
+ case LT_TOF_FUNCTIONR:
+ return arch_fetch_pool_arg_next(context, type, proc,
+ info, valuep, POOL_FUNCALL);
+
+ case LT_TOF_SYSCALL:
+ case LT_TOF_SYSCALLR:
+ return arch_fetch_pool_arg_next(context, type, proc,
+ info, valuep, POOL_SYSCALL);
+ }
+
+ abort();
+}
+
+int
+arch_fetch_retval(struct fetch_context *context, enum tof type,
+ struct Process *proc, struct arg_type_info *info,
+ struct value *valuep)
+{
+ if (proc->e_machine == EM_386)
+ return arch_fetch_retval_32(context, type, proc, info, valuep);
+
+ return arch_fetch_fun_retval(context, type, proc, info, valuep);
+}
+
+void
+arch_fetch_arg_done(struct fetch_context *context)
+{
+ if (context != NULL)
+ free(context);
+}
diff --git a/sysdeps/linux-gnu/x86/plt.c b/sysdeps/linux-gnu/x86/plt.c
new file mode 100644
index 0000000..bb1b2b1
--- /dev/null
+++ b/sysdeps/linux-gnu/x86/plt.c
@@ -0,0 +1,14 @@
+#include <gelf.h>
+#include "proc.h"
+#include "common.h"
+#include "library.h"
+
+GElf_Addr
+arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) {
+ return lte->plt_addr + (ndx + 1) * 16;
+}
+
+void *
+sym2addr(Process *proc, struct library_symbol *sym) {
+ return sym->enter_addr;
+}
diff --git a/sysdeps/linux-gnu/x86/ptrace.h b/sysdeps/linux-gnu/x86/ptrace.h
new file mode 100644
index 0000000..e0f5261
--- /dev/null
+++ b/sysdeps/linux-gnu/x86/ptrace.h
@@ -0,0 +1,2 @@
+#include <sys/ptrace.h>
+#include <sys/user.h>
diff --git a/sysdeps/linux-gnu/x86/regs.c b/sysdeps/linux-gnu/x86/regs.c
new file mode 100644
index 0000000..477abca
--- /dev/null
+++ b/sysdeps/linux-gnu/x86/regs.c
@@ -0,0 +1,116 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 1998,2002,2004,2008,2009 Juan Cespedes
+ * Copyright (C) 2006 Ian Wienand
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/reg.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "backend.h"
+#include "proc.h"
+
+#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
+# define PTRACE_PEEKUSER PTRACE_PEEKUSR
+#endif
+
+#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR))
+# define PTRACE_POKEUSER PTRACE_POKEUSR
+#endif
+
+#ifdef __x86_64__
+# define XIP (8 * RIP)
+# define XSP (8 * RSP)
+#else
+# define XIP (4 * EIP)
+# define XSP (4 * UESP)
+#endif
+
+static target_address_t
+conv_32(target_address_t val)
+{
+ /* XXX Drop the multiple double casts when target_address_t
+ * becomes integral. */
+ return (target_address_t)(uintptr_t)(uint32_t)(uintptr_t)val;
+}
+
+void *
+get_instruction_pointer(struct Process *proc)
+{
+ long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, XIP, 0);
+ if (proc->e_machine == EM_386)
+ ret &= 0xffffffff;
+ return (void *)ret;
+}
+
+void
+set_instruction_pointer(struct Process *proc, target_address_t addr)
+{
+ if (proc->e_machine == EM_386)
+ addr = conv_32(addr);
+ ptrace(PTRACE_POKEUSER, proc->pid, XIP, addr);
+}
+
+void *
+get_stack_pointer(struct Process *proc)
+{
+ long sp = ptrace(PTRACE_PEEKUSER, proc->pid, XSP, 0);
+ if (sp == -1 && errno) {
+ fprintf(stderr, "Couldn't read SP register: %s\n",
+ strerror(errno));
+ return NULL;
+ }
+
+ /* XXX Drop the multiple double casts when target_address_t
+ * becomes integral. */
+ target_address_t ret = (target_address_t)(uintptr_t)sp;
+ if (proc->e_machine == EM_386)
+ ret = conv_32(ret);
+ return ret;
+}
+
+void *
+get_return_addr(struct Process *proc, void *sp)
+{
+ long a = ptrace(PTRACE_PEEKTEXT, proc->pid, sp, 0);
+ if (a == -1 && errno) {
+ fprintf(stderr, "Couldn't read return value: %s\n",
+ strerror(errno));
+ return NULL;
+ }
+
+ /* XXX Drop the multiple double casts when target_address_t
+ * becomes integral. */
+ target_address_t ret = (target_address_t)(uintptr_t)a;
+ if (proc->e_machine == EM_386)
+ ret = conv_32(ret);
+ return ret;
+}
+
+void
+set_return_addr(Process *proc, void *addr) {
+ if (proc->e_machine == EM_386)
+ addr = (void *)((long int)addr & 0xffffffff);
+ ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, addr);
+}
diff --git a/sysdeps/linux-gnu/x86/signalent.h b/sysdeps/linux-gnu/x86/signalent.h
new file mode 100644
index 0000000..d58a36c
--- /dev/null
+++ b/sysdeps/linux-gnu/x86/signalent.h
@@ -0,0 +1,32 @@
+"SIG_0", /* 0 */
+ "SIGHUP", /* 1 */
+ "SIGINT", /* 2 */
+ "SIGQUIT", /* 3 */
+ "SIGILL", /* 4 */
+ "SIGTRAP", /* 5 */
+ "SIGABRT", /* 6 */
+ "SIGBUS", /* 7 */
+ "SIGFPE", /* 8 */
+ "SIGKILL", /* 9 */
+ "SIGUSR1", /* 10 */
+ "SIGSEGV", /* 11 */
+ "SIGUSR2", /* 12 */
+ "SIGPIPE", /* 13 */
+ "SIGALRM", /* 14 */
+ "SIGTERM", /* 15 */
+ "SIGSTKFLT", /* 16 */
+ "SIGCHLD", /* 17 */
+ "SIGCONT", /* 18 */
+ "SIGSTOP", /* 19 */
+ "SIGTSTP", /* 20 */
+ "SIGTTIN", /* 21 */
+ "SIGTTOU", /* 22 */
+ "SIGURG", /* 23 */
+ "SIGXCPU", /* 24 */
+ "SIGXFSZ", /* 25 */
+ "SIGVTALRM", /* 26 */
+ "SIGPROF", /* 27 */
+ "SIGWINCH", /* 28 */
+ "SIGIO", /* 29 */
+ "SIGPWR", /* 30 */
+ "SIGSYS", /* 31 */
diff --git a/sysdeps/linux-gnu/x86/signalent1.h b/sysdeps/linux-gnu/x86/signalent1.h
new file mode 100644
index 0000000..5395f82
--- /dev/null
+++ b/sysdeps/linux-gnu/x86/signalent1.h
@@ -0,0 +1,32 @@
+ "SIG_0", /* 0 */
+ "SIGHUP", /* 1 */
+ "SIGINT", /* 2 */
+ "SIGQUIT", /* 3 */
+ "SIGILL", /* 4 */
+ "SIGTRAP", /* 5 */
+ "SIGABRT", /* 6 */
+ "SIGBUS", /* 7 */
+ "SIGFPE", /* 8 */
+ "SIGKILL", /* 9 */
+ "SIGUSR1", /* 10 */
+ "SIGSEGV", /* 11 */
+ "SIGUSR2", /* 12 */
+ "SIGPIPE", /* 13 */
+ "SIGALRM", /* 14 */
+ "SIGTERM", /* 15 */
+ "SIGSTKFLT", /* 16 */
+ "SIGCHLD", /* 17 */
+ "SIGCONT", /* 18 */
+ "SIGSTOP", /* 19 */
+ "SIGTSTP", /* 20 */
+ "SIGTTIN", /* 21 */
+ "SIGTTOU", /* 22 */
+ "SIGURG", /* 23 */
+ "SIGXCPU", /* 24 */
+ "SIGXFSZ", /* 25 */
+ "SIGVTALRM", /* 26 */
+ "SIGPROF", /* 27 */
+ "SIGWINCH", /* 28 */
+ "SIGIO", /* 29 */
+ "SIGPWR", /* 30 */
+ "SIGSYS", /* 31 */
diff --git a/sysdeps/linux-gnu/x86/syscallent.h b/sysdeps/linux-gnu/x86/syscallent.h
new file mode 100644
index 0000000..7d746b7
--- /dev/null
+++ b/sysdeps/linux-gnu/x86/syscallent.h
@@ -0,0 +1,350 @@
+/* This file is for i386 system call names. */
+ "restart_syscall", /* 0 */
+ "exit", /* 1 */
+ "fork", /* 2 */
+ "read", /* 3 */
+ "write", /* 4 */
+ "open", /* 5 */
+ "close", /* 6 */
+ "waitpid", /* 7 */
+ "creat", /* 8 */
+ "link", /* 9 */
+ "unlink", /* 10 */
+ "execve", /* 11 */
+ "chdir", /* 12 */
+ "time", /* 13 */
+ "mknod", /* 14 */
+ "chmod", /* 15 */
+ "lchown", /* 16 */
+ "break", /* 17 */
+ "oldstat", /* 18 */
+ "lseek", /* 19 */
+ "getpid", /* 20 */
+ "mount", /* 21 */
+ "umount", /* 22 */
+ "setuid", /* 23 */
+ "getuid", /* 24 */
+ "stime", /* 25 */
+ "ptrace", /* 26 */
+ "alarm", /* 27 */
+ "oldfstat", /* 28 */
+ "pause", /* 29 */
+ "utime", /* 30 */
+ "stty", /* 31 */
+ "gtty", /* 32 */
+ "access", /* 33 */
+ "nice", /* 34 */
+ "ftime", /* 35 */
+ "sync", /* 36 */
+ "kill", /* 37 */
+ "rename", /* 38 */
+ "mkdir", /* 39 */
+ "rmdir", /* 40 */
+ "dup", /* 41 */
+ "pipe", /* 42 */
+ "times", /* 43 */
+ "prof", /* 44 */
+ "brk", /* 45 */
+ "setgid", /* 46 */
+ "getgid", /* 47 */
+ "signal", /* 48 */
+ "geteuid", /* 49 */
+ "getegid", /* 50 */
+ "acct", /* 51 */
+ "umount2", /* 52 */
+ "lock", /* 53 */
+ "ioctl", /* 54 */
+ "fcntl", /* 55 */
+ "mpx", /* 56 */
+ "setpgid", /* 57 */
+ "ulimit", /* 58 */
+ "oldolduname", /* 59 */
+ "umask", /* 60 */
+ "chroot", /* 61 */
+ "ustat", /* 62 */
+ "dup2", /* 63 */
+ "getppid", /* 64 */
+ "getpgrp", /* 65 */
+ "setsid", /* 66 */
+ "sigaction", /* 67 */
+ "sgetmask", /* 68 */
+ "ssetmask", /* 69 */
+ "setreuid", /* 70 */
+ "setregid", /* 71 */
+ "sigsuspend", /* 72 */
+ "sigpending", /* 73 */
+ "sethostname", /* 74 */
+ "setrlimit", /* 75 */
+ "getrlimit", /* 76 */
+ "getrusage", /* 77 */
+ "gettimeofday", /* 78 */
+ "settimeofday", /* 79 */
+ "getgroups", /* 80 */
+ "setgroups", /* 81 */
+ "select", /* 82 */
+ "symlink", /* 83 */
+ "oldlstat", /* 84 */
+ "readlink", /* 85 */
+ "uselib", /* 86 */
+ "swapon", /* 87 */
+ "reboot", /* 88 */
+ "readdir", /* 89 */
+ "mmap", /* 90 */
+ "munmap", /* 91 */
+ "truncate", /* 92 */
+ "ftruncate", /* 93 */
+ "fchmod", /* 94 */
+ "fchown", /* 95 */
+ "getpriority", /* 96 */
+ "setpriority", /* 97 */
+ "profil", /* 98 */
+ "statfs", /* 99 */
+ "fstatfs", /* 100 */
+ "ioperm", /* 101 */
+ "socketcall", /* 102 */
+ "syslog", /* 103 */
+ "setitimer", /* 104 */
+ "getitimer", /* 105 */
+ "stat", /* 106 */
+ "lstat", /* 107 */
+ "fstat", /* 108 */
+ "olduname", /* 109 */
+ "iopl", /* 110 */
+ "vhangup", /* 111 */
+ "idle", /* 112 */
+ "vm86old", /* 113 */
+ "wait4", /* 114 */
+ "swapoff", /* 115 */
+ "sysinfo", /* 116 */
+ "ipc", /* 117 */
+ "fsync", /* 118 */
+ "sigreturn", /* 119 */
+ "clone", /* 120 */
+ "setdomainname", /* 121 */
+ "uname", /* 122 */
+ "modify_ldt", /* 123 */
+ "adjtimex", /* 124 */
+ "mprotect", /* 125 */
+ "sigprocmask", /* 126 */
+ "create_module", /* 127 */
+ "init_module", /* 128 */
+ "delete_module", /* 129 */
+ "get_kernel_syms", /* 130 */
+ "quotactl", /* 131 */
+ "getpgid", /* 132 */
+ "fchdir", /* 133 */
+ "bdflush", /* 134 */
+ "sysfs", /* 135 */
+ "personality", /* 136 */
+ "afs_syscall", /* 137 */
+ "setfsuid", /* 138 */
+ "setfsgid", /* 139 */
+ "_llseek", /* 140 */
+ "getdents", /* 141 */
+ "_newselect", /* 142 */
+ "flock", /* 143 */
+ "msync", /* 144 */
+ "readv", /* 145 */
+ "writev", /* 146 */
+ "getsid", /* 147 */
+ "fdatasync", /* 148 */
+ "_sysctl", /* 149 */
+ "mlock", /* 150 */
+ "munlock", /* 151 */
+ "mlockall", /* 152 */
+ "munlockall", /* 153 */
+ "sched_setparam", /* 154 */
+ "sched_getparam", /* 155 */
+ "sched_setscheduler", /* 156 */
+ "sched_getscheduler", /* 157 */
+ "sched_yield", /* 158 */
+ "sched_get_priority_max", /* 159 */
+ "sched_get_priority_min", /* 160 */
+ "sched_rr_get_interval", /* 161 */
+ "nanosleep", /* 162 */
+ "mremap", /* 163 */
+ "setresuid", /* 164 */
+ "getresuid", /* 165 */
+ "vm86", /* 166 */
+ "query_module", /* 167 */
+ "poll", /* 168 */
+ "nfsservctl", /* 169 */
+ "setresgid", /* 170 */
+ "getresgid", /* 171 */
+ "prctl", /* 172 */
+ "rt_sigreturn", /* 173 */
+ "rt_sigaction", /* 174 */
+ "rt_sigprocmask", /* 175 */
+ "rt_sigpending", /* 176 */
+ "rt_sigtimedwait", /* 177 */
+ "rt_sigqueueinfo", /* 178 */
+ "rt_sigsuspend", /* 179 */
+ "pread64", /* 180 */
+ "pwrite64", /* 181 */
+ "chown", /* 182 */
+ "getcwd", /* 183 */
+ "capget", /* 184 */
+ "capset", /* 185 */
+ "sigaltstack", /* 186 */
+ "sendfile", /* 187 */
+ "getpmsg", /* 188 */
+ "putpmsg", /* 189 */
+ "vfork", /* 190 */
+ "ugetrlimit", /* 191 */
+ "mmap2", /* 192 */
+ "truncate64", /* 193 */
+ "ftruncate64", /* 194 */
+ "stat64", /* 195 */
+ "lstat64", /* 196 */
+ "fstat64", /* 197 */
+ "lchown32", /* 198 */
+ "getuid32", /* 199 */
+ "getgid32", /* 200 */
+ "geteuid32", /* 201 */
+ "getegid32", /* 202 */
+ "setreuid32", /* 203 */
+ "setregid32", /* 204 */
+ "getgroups32", /* 205 */
+ "setgroups32", /* 206 */
+ "fchown32", /* 207 */
+ "setresuid32", /* 208 */
+ "getresuid32", /* 209 */
+ "setresgid32", /* 210 */
+ "getresgid32", /* 211 */
+ "chown32", /* 212 */
+ "setuid32", /* 213 */
+ "setgid32", /* 214 */
+ "setfsuid32", /* 215 */
+ "setfsgid32", /* 216 */
+ "pivot_root", /* 217 */
+ "mincore", /* 218 */
+ "madvise1", /* 219 */
+ "getdents64", /* 220 */
+ "fcntl64", /* 221 */
+ "222", /* 222 */
+ "223", /* 223 */
+ "gettid", /* 224 */
+ "readahead", /* 225 */
+ "setxattr", /* 226 */
+ "lsetxattr", /* 227 */
+ "fsetxattr", /* 228 */
+ "getxattr", /* 229 */
+ "lgetxattr", /* 230 */
+ "fgetxattr", /* 231 */
+ "listxattr", /* 232 */
+ "llistxattr", /* 233 */
+ "flistxattr", /* 234 */
+ "removexattr", /* 235 */
+ "lremovexattr", /* 236 */
+ "fremovexattr", /* 237 */
+ "tkill", /* 238 */
+ "sendfile64", /* 239 */
+ "futex", /* 240 */
+ "sched_setaffinity", /* 241 */
+ "sched_getaffinity", /* 242 */
+ "set_thread_area", /* 243 */
+ "get_thread_area", /* 244 */
+ "io_setup", /* 245 */
+ "io_destroy", /* 246 */
+ "io_getevents", /* 247 */
+ "io_submit", /* 248 */
+ "io_cancel", /* 249 */
+ "fadvise64", /* 250 */
+ "251", /* 251 */
+ "exit_group", /* 252 */
+ "lookup_dcookie", /* 253 */
+ "epoll_create", /* 254 */
+ "epoll_ctl", /* 255 */
+ "epoll_wait", /* 256 */
+ "remap_file_pages", /* 257 */
+ "set_tid_address", /* 258 */
+ "timer_create", /* 259 */
+ "260", /* 260 */
+ "261", /* 261 */
+ "262", /* 262 */
+ "263", /* 263 */
+ "264", /* 264 */
+ "265", /* 265 */
+ "266", /* 266 */
+ "267", /* 267 */
+ "statfs64", /* 268 */
+ "fstatfs64", /* 269 */
+ "tgkill", /* 270 */
+ "utimes", /* 271 */
+ "fadvise64_64", /* 272 */
+ "vserver", /* 273 */
+ "mbind", /* 274 */
+ "get_mempolicy", /* 275 */
+ "set_mempolicy", /* 276 */
+ "mq_open", /* 277 */
+ "278", /* 278 */
+ "279", /* 279 */
+ "280", /* 280 */
+ "281", /* 281 */
+ "282", /* 282 */
+ "kexec_load", /* 283 */
+ "waitid", /* 284 */
+ "285", /* 285 */
+ "add_key", /* 286 */
+ "request_key", /* 287 */
+ "keyctl", /* 288 */
+ "ioprio_set", /* 289 */
+ "ioprio_get", /* 290 */
+ "inotify_init", /* 291 */
+ "inotify_add_watch", /* 292 */
+ "inotify_rm_watch", /* 293 */
+ "migrate_pages", /* 294 */
+ "openat", /* 295 */
+ "mkdirat", /* 296 */
+ "mknodat", /* 297 */
+ "fchownat", /* 298 */
+ "futimesat", /* 299 */
+ "fstatat64", /* 300 */
+ "unlinkat", /* 301 */
+ "renameat", /* 302 */
+ "linkat", /* 303 */
+ "symlinkat", /* 304 */
+ "readlinkat", /* 305 */
+ "fchmodat", /* 306 */
+ "faccessat", /* 307 */
+ "pselect6", /* 308 */
+ "ppoll", /* 309 */
+ "unshare", /* 310 */
+ "set_robust_list", /* 311 */
+ "get_robust_list", /* 312 */
+ "splice", /* 313 */
+ "sync_file_range", /* 314 */
+ "tee", /* 315 */
+ "vmsplice", /* 316 */
+ "move_pages", /* 317 */
+ "getcpu", /* 318 */
+ "epoll_pwait", /* 319 */
+ "utimensat", /* 320 */
+ "signalfd", /* 321 */
+ "timerfd_create", /* 322 */
+ "eventfd", /* 323 */
+ "fallocate", /* 324 */
+ "timerfd_settime", /* 325 */
+ "timerfd_gettime", /* 326 */
+ "signalfd4", /* 327 */
+ "eventfd2", /* 328 */
+ "epoll_create1", /* 329 */
+ "dup3", /* 330 */
+ "pipe2", /* 331 */
+ "inotify_init1", /* 332 */
+ "preadv", /* 333 */
+ "pwritev", /* 334 */
+ "rt_tgsigqueueinfo", /* 335 */
+ "perf_event_open", /* 336 */
+ "recvmmsg", /* 337 */
+ "fanotify_init", /* 338 */
+ "fanotify_mark", /* 339 */
+ "prlimit64", /* 340 */
+ "name_to_handle_at", /* 341 */
+ "open_by_handle_at", /* 342 */
+ "clock_adjtime", /* 343 */
+ "syncfs", /* 344 */
+ "sendmmsg", /* 345 */
+ "setns", /* 346 */
+ "process_vm_readv", /* 347 */
+ "process_vm_writev", /* 348 */
diff --git a/sysdeps/linux-gnu/x86/syscallent1.h b/sysdeps/linux-gnu/x86/syscallent1.h
new file mode 100644
index 0000000..c2d9017
--- /dev/null
+++ b/sysdeps/linux-gnu/x86/syscallent1.h
@@ -0,0 +1,313 @@
+/* This file is for x86_64 system call names. */
+ "read", /* 0 */
+ "write", /* 1 */
+ "open", /* 2 */
+ "close", /* 3 */
+ "stat", /* 4 */
+ "fstat", /* 5 */
+ "lstat", /* 6 */
+ "poll", /* 7 */
+ "lseek", /* 8 */
+ "mmap", /* 9 */
+ "mprotect", /* 10 */
+ "munmap", /* 11 */
+ "brk", /* 12 */
+ "rt_sigaction", /* 13 */
+ "rt_sigprocmask", /* 14 */
+ "rt_sigreturn", /* 15 */
+ "ioctl", /* 16 */
+ "pread", /* 17 */
+ "pwrite", /* 18 */
+ "readv", /* 19 */
+ "writev", /* 20 */
+ "access", /* 21 */
+ "pipe", /* 22 */
+ "select", /* 23 */
+ "sched_yield", /* 24 */
+ "mremap", /* 25 */
+ "msync", /* 26 */
+ "mincore", /* 27 */
+ "madvise", /* 28 */
+ "shmget", /* 29 */
+ "shmat", /* 30 */
+ "shmctl", /* 31 */
+ "dup", /* 32 */
+ "dup2", /* 33 */
+ "pause", /* 34 */
+ "nanosleep", /* 35 */
+ "getitimer", /* 36 */
+ "alarm", /* 37 */
+ "setitimer", /* 38 */
+ "getpid", /* 39 */
+ "sendfile", /* 40 */
+ "socket", /* 41 */
+ "connect", /* 42 */
+ "accept", /* 43 */
+ "sendto", /* 44 */
+ "recvfrom", /* 45 */
+ "sendmsg", /* 46 */
+ "recvmsg", /* 47 */
+ "shutdown", /* 48 */
+ "bind", /* 49 */
+ "listen", /* 50 */
+ "getsockname", /* 51 */
+ "getpeername", /* 52 */
+ "socketpair", /* 53 */
+ "setsockopt", /* 54 */
+ "getsockopt", /* 55 */
+ "clone", /* 56 */
+ "fork", /* 57 */
+ "vfork", /* 58 */
+ "execve", /* 59 */
+ "exit", /* 60 */
+ "wait4", /* 61 */
+ "kill", /* 62 */
+ "uname", /* 63 */
+ "semget", /* 64 */
+ "semop", /* 65 */
+ "semctl", /* 66 */
+ "shmdt", /* 67 */
+ "msgget", /* 68 */
+ "msgsnd", /* 69 */
+ "msgrcv", /* 70 */
+ "msgctl", /* 71 */
+ "fcntl", /* 72 */
+ "flock", /* 73 */
+ "fsync", /* 74 */
+ "fdatasync", /* 75 */
+ "truncate", /* 76 */
+ "ftruncate", /* 77 */
+ "getdents", /* 78 */
+ "getcwd", /* 79 */
+ "chdir", /* 80 */
+ "fchdir", /* 81 */
+ "rename", /* 82 */
+ "mkdir", /* 83 */
+ "rmdir", /* 84 */
+ "creat", /* 85 */
+ "link", /* 86 */
+ "unlink", /* 87 */
+ "symlink", /* 88 */
+ "readlink", /* 89 */
+ "chmod", /* 90 */
+ "fchmod", /* 91 */
+ "chown", /* 92 */
+ "fchown", /* 93 */
+ "lchown", /* 94 */
+ "umask", /* 95 */
+ "gettimeofday", /* 96 */
+ "getrlimit", /* 97 */
+ "getrusage", /* 98 */
+ "sysinfo", /* 99 */
+ "times", /* 100 */
+ "ptrace", /* 101 */
+ "getuid", /* 102 */
+ "syslog", /* 103 */
+ "getgid", /* 104 */
+ "setuid", /* 105 */
+ "setgid", /* 106 */
+ "geteuid", /* 107 */
+ "getegid", /* 108 */
+ "setpgid", /* 109 */
+ "getppid", /* 110 */
+ "getpgrp", /* 111 */
+ "setsid", /* 112 */
+ "setreuid", /* 113 */
+ "setregid", /* 114 */
+ "getgroups", /* 115 */
+ "setgroups", /* 116 */
+ "setresuid", /* 117 */
+ "getresuid", /* 118 */
+ "setresgid", /* 119 */
+ "getresgid", /* 120 */
+ "getpgid", /* 121 */
+ "setfsuid", /* 122 */
+ "setfsgid", /* 123 */
+ "getsid", /* 124 */
+ "capget", /* 125 */
+ "capset", /* 126 */
+ "rt_sigpending", /* 127 */
+ "rt_sigtimedwait", /* 128 */
+ "rt_sigqueueinfo", /* 129 */
+ "rt_sigsuspend", /* 130 */
+ "sigaltstack", /* 131 */
+ "utime", /* 132 */
+ "mknod", /* 133 */
+ "uselib", /* 134 */
+ "personality", /* 135 */
+ "ustat", /* 136 */
+ "statfs", /* 137 */
+ "fstatfs", /* 138 */
+ "sysfs", /* 139 */
+ "getpriority", /* 140 */
+ "setpriority", /* 141 */
+ "sched_setparam", /* 142 */
+ "sched_getparam", /* 143 */
+ "sched_setscheduler", /* 144 */
+ "sched_getscheduler", /* 145 */
+ "sched_get_priority_max", /* 146 */
+ "sched_get_priority_min", /* 147 */
+ "sched_rr_get_interval", /* 148 */
+ "mlock", /* 149 */
+ "munlock", /* 150 */
+ "mlockall", /* 151 */
+ "munlockall", /* 152 */
+ "vhangup", /* 153 */
+ "modify_ldt", /* 154 */
+ "pivot_root", /* 155 */
+ "_sysctl", /* 156 */
+ "prctl", /* 157 */
+ "arch_prctl", /* 158 */
+ "adjtimex", /* 159 */
+ "setrlimit", /* 160 */
+ "chroot", /* 161 */
+ "sync", /* 162 */
+ "acct", /* 163 */
+ "settimeofday", /* 164 */
+ "mount", /* 165 */
+ "umount2", /* 166 */
+ "swapon", /* 167 */
+ "swapoff", /* 168 */
+ "reboot", /* 169 */
+ "sethostname", /* 170 */
+ "setdomainname", /* 171 */
+ "iopl", /* 172 */
+ "ioperm", /* 173 */
+ "create_module", /* 174 */
+ "init_module", /* 175 */
+ "delete_module", /* 176 */
+ "get_kernel_syms", /* 177 */
+ "query_module", /* 178 */
+ "quotactl", /* 179 */
+ "nfsservctl", /* 180 */
+ "getpmsg", /* 181 */
+ "putpmsg", /* 182 */
+ "afs_syscall", /* 183 */
+ "tuxcall", /* 184 */
+ "security", /* 185 */
+ "gettid", /* 186 */
+ "readahead", /* 187 */
+ "setxattr", /* 188 */
+ "lsetxattr", /* 189 */
+ "fsetxattr", /* 190 */
+ "getxattr", /* 191 */
+ "lgetxattr", /* 192 */
+ "fgetxattr", /* 193 */
+ "listxattr", /* 194 */
+ "llistxattr", /* 195 */
+ "flistxattr", /* 196 */
+ "removexattr", /* 197 */
+ "lremovexattr", /* 198 */
+ "fremovexattr", /* 199 */
+ "tkill", /* 200 */
+ "time", /* 201 */
+ "futex", /* 202 */
+ "sched_setaffinity", /* 203 */
+ "sched_getaffinity", /* 204 */
+ "set_thread_area", /* 205 */
+ "io_setup", /* 206 */
+ "io_destroy", /* 207 */
+ "io_getevents", /* 208 */
+ "io_submit", /* 209 */
+ "io_cancel", /* 210 */
+ "get_thread_area", /* 211 */
+ "lookup_dcookie", /* 212 */
+ "epoll_create", /* 213 */
+ "epoll_ctl", /* 214 */
+ "epoll_wait", /* 215 */
+ "remap_file_pages", /* 216 */
+ "getdents64", /* 217 */
+ "set_tid_address", /* 218 */
+ "restart_syscall", /* 219 */
+ "semtimedop", /* 220 */
+ "fadvise64", /* 221 */
+ "timer_create", /* 222 */
+ "timer_settime", /* 223 */
+ "timer_gettime", /* 224 */
+ "timer_getoverrun", /* 225 */
+ "timer_delete", /* 226 */
+ "clock_settime", /* 227 */
+ "clock_gettime", /* 228 */
+ "clock_getres", /* 229 */
+ "clock_nanosleep", /* 230 */
+ "exit_group", /* 231 */
+ "epoll_wait", /* 232 */
+ "epoll_ctl", /* 233 */
+ "tgkill", /* 234 */
+ "utimes", /* 235 */
+ "vserver", /* 236 */
+ "mbind", /* 237 */
+ "set_mempolicy", /* 238 */
+ "get_mempolicy", /* 239 */
+ "mq_open", /* 240 */
+ "mq_unlink", /* 241 */
+ "mq_timedsend", /* 242 */
+ "mq_timedreceive", /* 243 */
+ "mq_notify", /* 244 */
+ "mq_getsetattr", /* 245 */
+ "kexec_load", /* 246 */
+ "waitid", /* 247 */
+ "add_key", /* 248 */
+ "request_key", /* 249 */
+ "keyctl", /* 250 */
+ "ioprio_set", /* 251 */
+ "ioprio_get", /* 252 */
+ "inotify_init", /* 253 */
+ "inotify_add_watch", /* 254 */
+ "inotify_rm_watch", /* 255 */
+ "migrate_pages", /* 256 */
+ "openat", /* 257 */
+ "mkdirat", /* 258 */
+ "mknodat", /* 259 */
+ "fchownat", /* 260 */
+ "futimesat", /* 261 */
+ "newfstatat", /* 262 */
+ "unlinkat", /* 263 */
+ "renameat", /* 264 */
+ "linkat", /* 265 */
+ "symlinkat", /* 266 */
+ "readlinkat", /* 267 */
+ "fchmodat", /* 268 */
+ "faccessat", /* 269 */
+ "pselect6", /* 270 */
+ "ppoll", /* 271 */
+ "unshare", /* 272 */
+ "set_robust_list", /* 273 */
+ "get_robust_list", /* 274 */
+ "splice", /* 275 */
+ "tee", /* 276 */
+ "sync_file_range", /* 277 */
+ "vmsplice", /* 278 */
+ "move_pages", /* 279 */
+ "utimensat", /* 280 */
+ "epoll_pwait", /* 281 */
+ "signalfd", /* 282 */
+ "timerfd_create", /* 283 */
+ "eventfd", /* 284 */
+ "fallocate", /* 285 */
+ "timerfd_settime", /* 286 */
+ "timerfd_gettime", /* 287 */
+ "accept4", /* 288 */
+ "signalfd4", /* 289 */
+ "eventfd2", /* 290 */
+ "epoll_create1", /* 291 */
+ "dup3", /* 292 */
+ "pipe2", /* 293 */
+ "inotify_init1", /* 294 */
+ "preadv", /* 295 */
+ "pwritev", /* 296 */
+ "rt_tgsigqueueinfo", /* 297 */
+ "perf_event_open", /* 298 */
+ "recvmmsg", /* 299 */
+ "fanotify_init", /* 300 */
+ "fanotify_mark", /* 301 */
+ "prlimit64", /* 302 */
+ "name_to_handle_at", /* 303 */
+ "open_by_handle_at", /* 304 */
+ "clock_adjtime", /* 305 */
+ "syncfs", /* 306 */
+ "sendmmsg", /* 307 */
+ "setns", /* 308 */
+ "getcpu", /* 309 */
+ "process_vm_readv", /* 310 */
+ "process_vm_writev", /* 311 */
diff --git a/sysdeps/linux-gnu/x86/trace.c b/sysdeps/linux-gnu/x86/trace.c
new file mode 100644
index 0000000..cc1a6a1
--- /dev/null
+++ b/sysdeps/linux-gnu/x86/trace.c
@@ -0,0 +1,189 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2010,2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2004,2008,2009 Juan Cespedes
+ * Copyright (C) 2006 Ian Wienand
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include "config.h"
+
+#include <sys/reg.h>
+#include <sys/wait.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "backend.h"
+#include "debug.h"
+#include "proc.h"
+#include "ptrace.h"
+#include "type.h"
+
+#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
+# define PTRACE_PEEKUSER PTRACE_PEEKUSR
+#endif
+
+#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR))
+# define PTRACE_POKEUSER PTRACE_POKEUSR
+#endif
+
+#ifdef __x86_64__
+# define ORIG_XAX (8 * ORIG_RAX)
+#else
+# define ORIG_XAX (4 * ORIG_EAX)
+#endif
+
+#ifdef __x86_64__
+static const int x86_64 = 1;
+#else
+static const int x86_64 = 0;
+#endif
+
+void
+get_arch_dep(struct Process *proc)
+{
+ /* Unfortunately there are still remnants of mask_32bit uses
+ * around. */
+
+ if (proc->e_machine == EM_X86_64) {
+ proc->mask_32bit = 0;
+ proc->personality = 1;
+ } else if (x86_64) { /* x86_64/i386 */
+ proc->mask_32bit = 1;
+ proc->personality = 0;
+ } else {
+ proc->mask_32bit = 0;
+ proc->personality = 0;
+ }
+}
+
+/* Returns 1 if syscall, 2 if sysret, 0 otherwise.
+ */
+int
+syscall_p(struct Process *proc, int status, int *sysnum)
+{
+ if (WIFSTOPPED(status)
+ && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) {
+ struct callstack_element *elem = NULL;
+ if (proc->callstack_depth > 0)
+ elem = proc->callstack + proc->callstack_depth - 1;
+
+ long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, ORIG_XAX, 0);
+ if (ret == -1) {
+ if (errno)
+ return -1;
+ /* Otherwise, ORIG_RAX == -1 means that the
+ * system call should not be restarted. In
+ * that case rely on what we have on
+ * stack. */
+ if (elem != NULL && elem->is_syscall)
+ ret = elem->c_un.syscall;
+ }
+
+ *sysnum = ret;
+ debug(DEBUG_FUNCTION, "sysnum=%ld %p %d\n", ret,
+ get_instruction_pointer(proc), errno);
+ if (elem != NULL && elem->is_syscall
+ && elem->c_un.syscall == *sysnum)
+ return 2;
+
+ if (*sysnum >= 0)
+ return 1;
+ }
+ return 0;
+}
+
+size_t
+arch_type_sizeof(struct Process *proc, struct arg_type_info *info)
+{
+ if (proc == NULL)
+ return (size_t)-2;
+
+ switch (info->type) {
+ case ARGTYPE_VOID:
+ return 0;
+
+ case ARGTYPE_CHAR:
+ return 1;
+
+ case ARGTYPE_SHORT:
+ case ARGTYPE_USHORT:
+ return 2;
+
+ case ARGTYPE_INT:
+ case ARGTYPE_UINT:
+ return 4;
+
+ case ARGTYPE_LONG:
+ case ARGTYPE_ULONG:
+ case ARGTYPE_POINTER:
+ return proc->e_machine == EM_X86_64 ? 8 : 4;
+
+ case ARGTYPE_FLOAT:
+ return 4;
+ case ARGTYPE_DOUBLE:
+ return 8;
+
+ case ARGTYPE_ARRAY:
+ case ARGTYPE_STRUCT:
+ /* Use default value. */
+ return (size_t)-2;
+ }
+ assert(info->type != info->type);
+ abort();
+}
+
+size_t
+arch_type_alignof(struct Process *proc, struct arg_type_info *info)
+{
+ if (proc == NULL)
+ return (size_t)-2;
+
+ switch (info->type) {
+ case ARGTYPE_VOID:
+ assert(info->type != ARGTYPE_VOID);
+ break;
+
+ case ARGTYPE_CHAR:
+ return 1;
+
+ case ARGTYPE_SHORT:
+ case ARGTYPE_USHORT:
+ return 2;
+
+ case ARGTYPE_INT:
+ case ARGTYPE_UINT:
+ return 4;
+
+ case ARGTYPE_LONG:
+ case ARGTYPE_ULONG:
+ case ARGTYPE_POINTER:
+ return proc->e_machine == EM_X86_64 ? 8 : 4;
+
+ case ARGTYPE_FLOAT:
+ return 4;
+ case ARGTYPE_DOUBLE:
+ return proc->e_machine == EM_X86_64 ? 8 : 4;
+
+ case ARGTYPE_ARRAY:
+ case ARGTYPE_STRUCT:
+ /* Use default value. */
+ return (size_t)-2;
+ }
+ abort();
+}
diff --git a/sysdeps/linux-gnu/x86_64/Makefile.am b/sysdeps/linux-gnu/x86_64/Makefile.am
deleted file mode 100644
index 2e476d8..0000000
--- a/sysdeps/linux-gnu/x86_64/Makefile.am
+++ /dev/null
@@ -1,18 +0,0 @@
-noinst_LTLIBRARIES = \
- ../libcpu.la
-
-___libcpu_la_SOURCES = \
- plt.c \
- regs.c \
- trace.c
-
-noinst_HEADERS = \
- arch.h \
- ptrace.h \
- signalent.h \
- signalent1.h \
- syscallent.h \
- syscallent1.h
-
-MAINTAINERCLEANFILES = \
- Makefile.in
diff --git a/sysdeps/linux-gnu/x86_64/arch.h b/sysdeps/linux-gnu/x86_64/arch.h
deleted file mode 100644
index 255395c..0000000
--- a/sysdeps/linux-gnu/x86_64/arch.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#define BREAKPOINT_VALUE {0xcc}
-#define BREAKPOINT_LENGTH 1
-#define DECR_PC_AFTER_BREAK 1
-#define ARCH_ENDIAN_LITTLE
-
-#define LT_ELFCLASS ELFCLASS64
-#define LT_ELF_MACHINE EM_X86_64
-#define LT_ELFCLASS2 ELFCLASS32
-#define LT_ELF_MACHINE2 EM_386
-
-/* __NR_fork, __NR_clone, __NR_clone2, __NR_vfork and __NR_execve
- from asm-i386/unistd.h. */
-#define FORK_EXEC_SYSCALLS , { 2, 120, -1, 190, 11 }
diff --git a/sysdeps/linux-gnu/x86_64/plt.c b/sysdeps/linux-gnu/x86_64/plt.c
deleted file mode 100644
index bb1b2b1..0000000
--- a/sysdeps/linux-gnu/x86_64/plt.c
+++ /dev/null
@@ -1,14 +0,0 @@
-#include <gelf.h>
-#include "proc.h"
-#include "common.h"
-#include "library.h"
-
-GElf_Addr
-arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) {
- return lte->plt_addr + (ndx + 1) * 16;
-}
-
-void *
-sym2addr(Process *proc, struct library_symbol *sym) {
- return sym->enter_addr;
-}
diff --git a/sysdeps/linux-gnu/x86_64/ptrace.h b/sysdeps/linux-gnu/x86_64/ptrace.h
deleted file mode 100644
index d92771f..0000000
--- a/sysdeps/linux-gnu/x86_64/ptrace.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#include <stdint.h>
-#include <sys/ptrace.h>
-#include <sys/user.h>
-
-typedef struct {
- int valid;
- struct user_regs_struct regs;
- struct user_fpregs_struct fpregs;
-} proc_archdep;
-
-typedef struct {
- struct user_regs_struct regs_copy;
- struct user_fpregs_struct fpregs_copy;
-} callstack_achdep;
diff --git a/sysdeps/linux-gnu/x86_64/regs.c b/sysdeps/linux-gnu/x86_64/regs.c
deleted file mode 100644
index 0ff3281..0000000
--- a/sysdeps/linux-gnu/x86_64/regs.c
+++ /dev/null
@@ -1,54 +0,0 @@
-#include "config.h"
-
-#include <sys/types.h>
-#include <sys/ptrace.h>
-#include <sys/reg.h>
-
-#include "proc.h"
-
-#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
-# define PTRACE_PEEKUSER PTRACE_PEEKUSR
-#endif
-
-#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR))
-# define PTRACE_POKEUSER PTRACE_POKEUSR
-#endif
-
-void *
-get_instruction_pointer(Process *proc) {
- long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RIP, 0);
- if (proc->mask_32bit)
- ret &= 0xffffffff;
- return (void *)ret;
-}
-
-void
-set_instruction_pointer(Process *proc, void *addr) {
- if (proc->mask_32bit)
- addr = (void *)((long int)addr & 0xffffffff);
- ptrace(PTRACE_POKEUSER, proc->pid, 8 * RIP, addr);
-}
-
-void *
-get_stack_pointer(Process *proc) {
- long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RSP, 0);
- if (proc->mask_32bit)
- ret &= 0xffffffff;
- return (void *)ret;
-}
-
-void *
-get_return_addr(Process *proc, void *stack_pointer) {
- unsigned long int ret;
- ret = ptrace(PTRACE_PEEKTEXT, proc->pid, stack_pointer, 0);
- if (proc->mask_32bit)
- ret &= 0xffffffff;
- return (void *)ret;
-}
-
-void
-set_return_addr(Process *proc, void *addr) {
- if (proc->mask_32bit)
- addr = (void *)((long int)addr & 0xffffffff);
- ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, addr);
-}
diff --git a/sysdeps/linux-gnu/x86_64/signalent.h b/sysdeps/linux-gnu/x86_64/signalent.h
deleted file mode 100644
index d58a36c..0000000
--- a/sysdeps/linux-gnu/x86_64/signalent.h
+++ /dev/null
@@ -1,32 +0,0 @@
-"SIG_0", /* 0 */
- "SIGHUP", /* 1 */
- "SIGINT", /* 2 */
- "SIGQUIT", /* 3 */
- "SIGILL", /* 4 */
- "SIGTRAP", /* 5 */
- "SIGABRT", /* 6 */
- "SIGBUS", /* 7 */
- "SIGFPE", /* 8 */
- "SIGKILL", /* 9 */
- "SIGUSR1", /* 10 */
- "SIGSEGV", /* 11 */
- "SIGUSR2", /* 12 */
- "SIGPIPE", /* 13 */
- "SIGALRM", /* 14 */
- "SIGTERM", /* 15 */
- "SIGSTKFLT", /* 16 */
- "SIGCHLD", /* 17 */
- "SIGCONT", /* 18 */
- "SIGSTOP", /* 19 */
- "SIGTSTP", /* 20 */
- "SIGTTIN", /* 21 */
- "SIGTTOU", /* 22 */
- "SIGURG", /* 23 */
- "SIGXCPU", /* 24 */
- "SIGXFSZ", /* 25 */
- "SIGVTALRM", /* 26 */
- "SIGPROF", /* 27 */
- "SIGWINCH", /* 28 */
- "SIGIO", /* 29 */
- "SIGPWR", /* 30 */
- "SIGSYS", /* 31 */
diff --git a/sysdeps/linux-gnu/x86_64/signalent1.h b/sysdeps/linux-gnu/x86_64/signalent1.h
deleted file mode 100644
index 5ead946..0000000
--- a/sysdeps/linux-gnu/x86_64/signalent1.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "i386/signalent.h"
diff --git a/sysdeps/linux-gnu/x86_64/syscallent.h b/sysdeps/linux-gnu/x86_64/syscallent.h
deleted file mode 100644
index 5e5f88a..0000000
--- a/sysdeps/linux-gnu/x86_64/syscallent.h
+++ /dev/null
@@ -1,256 +0,0 @@
-"read", /* 0 */
- "write", /* 1 */
- "open", /* 2 */
- "close", /* 3 */
- "stat", /* 4 */
- "fstat", /* 5 */
- "lstat", /* 6 */
- "poll", /* 7 */
- "lseek", /* 8 */
- "mmap", /* 9 */
- "mprotect", /* 10 */
- "munmap", /* 11 */
- "brk", /* 12 */
- "rt_sigaction", /* 13 */
- "rt_sigprocmask", /* 14 */
- "rt_sigreturn", /* 15 */
- "ioctl", /* 16 */
- "pread", /* 17 */
- "pwrite", /* 18 */
- "readv", /* 19 */
- "writev", /* 20 */
- "access", /* 21 */
- "pipe", /* 22 */
- "select", /* 23 */
- "sched_yield", /* 24 */
- "mremap", /* 25 */
- "msync", /* 26 */
- "mincore", /* 27 */
- "madvise", /* 28 */
- "shmget", /* 29 */
- "shmat", /* 30 */
- "shmctl", /* 31 */
- "dup", /* 32 */
- "dup2", /* 33 */
- "pause", /* 34 */
- "nanosleep", /* 35 */
- "getitimer", /* 36 */
- "alarm", /* 37 */
- "setitimer", /* 38 */
- "getpid", /* 39 */
- "sendfile", /* 40 */
- "socket", /* 41 */
- "connect", /* 42 */
- "accept", /* 43 */
- "sendto", /* 44 */
- "recvfrom", /* 45 */
- "sendmsg", /* 46 */
- "recvmsg", /* 47 */
- "shutdown", /* 48 */
- "bind", /* 49 */
- "listen", /* 50 */
- "getsockname", /* 51 */
- "getpeername", /* 52 */
- "socketpair", /* 53 */
- "setsockopt", /* 54 */
- "getsockopt", /* 55 */
- "clone", /* 56 */
- "fork", /* 57 */
- "vfork", /* 58 */
- "execve", /* 59 */
- "exit", /* 60 */
- "wait4", /* 61 */
- "kill", /* 62 */
- "uname", /* 63 */
- "semget", /* 64 */
- "semop", /* 65 */
- "semctl", /* 66 */
- "shmdt", /* 67 */
- "msgget", /* 68 */
- "msgsnd", /* 69 */
- "msgrcv", /* 70 */
- "msgctl", /* 71 */
- "fcntl", /* 72 */
- "flock", /* 73 */
- "fsync", /* 74 */
- "fdatasync", /* 75 */
- "truncate", /* 76 */
- "ftruncate", /* 77 */
- "getdents", /* 78 */
- "getcwd", /* 79 */
- "chdir", /* 80 */
- "fchdir", /* 81 */
- "rename", /* 82 */
- "mkdir", /* 83 */
- "rmdir", /* 84 */
- "creat", /* 85 */
- "link", /* 86 */
- "unlink", /* 87 */
- "symlink", /* 88 */
- "readlink", /* 89 */
- "chmod", /* 90 */
- "fchmod", /* 91 */
- "chown", /* 92 */
- "fchown", /* 93 */
- "lchown", /* 94 */
- "umask", /* 95 */
- "gettimeofday", /* 96 */
- "getrlimit", /* 97 */
- "getrusage", /* 98 */
- "sysinfo", /* 99 */
- "times", /* 100 */
- "ptrace", /* 101 */
- "getuid", /* 102 */
- "syslog", /* 103 */
- "getgid", /* 104 */
- "setuid", /* 105 */
- "setgid", /* 106 */
- "geteuid", /* 107 */
- "getegid", /* 108 */
- "setpgid", /* 109 */
- "getppid", /* 110 */
- "getpgrp", /* 111 */
- "setsid", /* 112 */
- "setreuid", /* 113 */
- "setregid", /* 114 */
- "getgroups", /* 115 */
- "setgroups", /* 116 */
- "setresuid", /* 117 */
- "getresuid", /* 118 */
- "setresgid", /* 119 */
- "getresgid", /* 120 */
- "getpgid", /* 121 */
- "setfsuid", /* 122 */
- "setfsgid", /* 123 */
- "getsid", /* 124 */
- "capget", /* 125 */
- "capset", /* 126 */
- "rt_sigpending", /* 127 */
- "rt_sigtimedwait", /* 128 */
- "rt_sigqueueinfo", /* 129 */
- "rt_sigsuspend", /* 130 */
- "sigaltstack", /* 131 */
- "utime", /* 132 */
- "mknod", /* 133 */
- "uselib", /* 134 */
- "personality", /* 135 */
- "ustat", /* 136 */
- "statfs", /* 137 */
- "fstatfs", /* 138 */
- "sysfs", /* 139 */
- "getpriority", /* 140 */
- "setpriority", /* 141 */
- "sched_setparam", /* 142 */
- "sched_getparam", /* 143 */
- "sched_setscheduler", /* 144 */
- "sched_getscheduler", /* 145 */
- "sched_get_priority_max", /* 146 */
- "sched_get_priority_min", /* 147 */
- "sched_rr_get_interval", /* 148 */
- "mlock", /* 149 */
- "munlock", /* 150 */
- "mlockall", /* 151 */
- "munlockall", /* 152 */
- "vhangup", /* 153 */
- "modify_ldt", /* 154 */
- "pivot_root", /* 155 */
- "_sysctl", /* 156 */
- "prctl", /* 157 */
- "arch_prctl", /* 158 */
- "adjtimex", /* 159 */
- "setrlimit", /* 160 */
- "chroot", /* 161 */
- "sync", /* 162 */
- "acct", /* 163 */
- "settimeofday", /* 164 */
- "mount", /* 165 */
- "umount2", /* 166 */
- "swapon", /* 167 */
- "swapoff", /* 168 */
- "reboot", /* 169 */
- "sethostname", /* 170 */
- "setdomainname", /* 171 */
- "iopl", /* 172 */
- "ioperm", /* 173 */
- "create_module", /* 174 */
- "init_module", /* 175 */
- "delete_module", /* 176 */
- "get_kernel_syms", /* 177 */
- "query_module", /* 178 */
- "quotactl", /* 179 */
- "nfsservctl", /* 180 */
- "getpmsg", /* 181 */
- "putpmsg", /* 182 */
- "afs_syscall", /* 183 */
- "tuxcall", /* 184 */
- "security", /* 185 */
- "gettid", /* 186 */
- "readahead", /* 187 */
- "setxattr", /* 188 */
- "lsetxattr", /* 189 */
- "fsetxattr", /* 190 */
- "getxattr", /* 191 */
- "lgetxattr", /* 192 */
- "fgetxattr", /* 193 */
- "listxattr", /* 194 */
- "llistxattr", /* 195 */
- "flistxattr", /* 196 */
- "removexattr", /* 197 */
- "lremovexattr", /* 198 */
- "fremovexattr", /* 199 */
- "tkill", /* 200 */
- "time", /* 201 */
- "futex", /* 202 */
- "sched_setaffinity", /* 203 */
- "sched_getaffinity", /* 204 */
- "set_thread_area", /* 205 */
- "io_setup", /* 206 */
- "io_destroy", /* 207 */
- "io_getevents", /* 208 */
- "io_submit", /* 209 */
- "io_cancel", /* 210 */
- "get_thread_area", /* 211 */
- "lookup_dcookie", /* 212 */
- "epoll_create", /* 213 */
- "epoll_ctl", /* 214 */
- "epoll_wait", /* 215 */
- "remap_file_pages", /* 216 */
- "getdents64", /* 217 */
- "set_tid_address", /* 218 */
- "restart_syscall", /* 219 */
- "semtimedop", /* 220 */
- "fadvise64", /* 221 */
- "timer_create", /* 222 */
- "timer_settime", /* 223 */
- "timer_gettime", /* 224 */
- "timer_getoverrun", /* 225 */
- "timer_delete", /* 226 */
- "clock_settime", /* 227 */
- "clock_gettime", /* 228 */
- "clock_getres", /* 229 */
- "clock_nanosleep", /* 230 */
- "exit_group", /* 231 */
- "epoll_wait", /* 232 */
- "epoll_ctl", /* 233 */
- "tgkill", /* 234 */
- "utimes", /* 235 */
- "vserver", /* 236 */
- "mbind", /* 237 */
- "set_mempolicy", /* 238 */
- "get_mempolicy", /* 239 */
- "mq_open", /* 240 */
- "mq_unlink", /* 241 */
- "mq_timedsend", /* 242 */
- "mq_timedreceive", /* 243 */
- "mq_notify", /* 244 */
- "mq_getsetattr", /* 245 */
- "kexec_load", /* 246 */
- "waitid", /* 247 */
- "add_key", /* 248 */
- "request_key", /* 249 */
- "keyctl", /* 250 */
- "ioprio_set", /* 251 */
- "ioprio_get", /* 252 */
- "inotify_init", /* 253 */
- "inotify_add_watch", /* 254 */
- "inotify_rm_watch", /* 255 */
diff --git a/sysdeps/linux-gnu/x86_64/syscallent1.h b/sysdeps/linux-gnu/x86_64/syscallent1.h
deleted file mode 100644
index d8dd9f7..0000000
--- a/sysdeps/linux-gnu/x86_64/syscallent1.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "i386/syscallent.h"
diff --git a/sysdeps/linux-gnu/x86_64/trace.c b/sysdeps/linux-gnu/x86_64/trace.c
deleted file mode 100644
index 0d3f693..0000000
--- a/sysdeps/linux-gnu/x86_64/trace.c
+++ /dev/null
@@ -1,201 +0,0 @@
-#include "config.h"
-
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <sys/ptrace.h>
-#include <sys/reg.h>
-#include <string.h>
-#include <assert.h>
-#include <errno.h>
-
-#include "common.h"
-#include "ptrace.h"
-#include "proc.h"
-
-#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR))
-# define PTRACE_PEEKUSER PTRACE_PEEKUSR
-#endif
-
-#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR))
-# define PTRACE_POKEUSER PTRACE_POKEUSR
-#endif
-
-void
-get_arch_dep(Process *proc) {
- proc_archdep *a;
-
- if (!proc->arch_ptr)
- proc->arch_ptr = (void *)malloc(sizeof(proc_archdep));
-
- a = (proc_archdep *) (proc->arch_ptr);
- a->valid = (ptrace(PTRACE_GETREGS, proc->pid, 0, &a->regs) >= 0);
- if (a->valid) {
- a->valid = (ptrace(PTRACE_GETFPREGS, proc->pid, 0, &a->fpregs) >= 0);
- }
- if (a->regs.cs == 0x23) {
- proc->mask_32bit = 1;
- proc->personality = 1;
- }
-}
-
-/* Returns 1 if syscall, 2 if sysret, 0 otherwise.
- */
-int
-syscall_p(struct Process *proc, int status, int *sysnum)
-{
- if (WIFSTOPPED(status)
- && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) {
- struct callstack_element *elem = NULL;
- if (proc->callstack_depth > 0)
- elem = proc->callstack + proc->callstack_depth - 1;
-
- long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * ORIG_RAX, 0);
- if (ret == -1) {
- if (errno)
- return -1;
- /* Otherwise, ORIG_RAX == -1 means that the
- * system call should not be restarted. In
- * that case rely on what we have on
- * stack. */
- if (elem != NULL && elem->is_syscall)
- ret = elem->c_un.syscall;
- }
-
- *sysnum = ret;
- debug(DEBUG_FUNCTION, "sysnum=%ld %p %d\n", ret,
- get_instruction_pointer(proc), errno);
- if (elem != NULL && elem->is_syscall
- && elem->c_un.syscall == *sysnum)
- return 2;
-
- if (*sysnum >= 0)
- return 1;
- }
- return 0;
-}
-
-static unsigned int
-gimme_arg32(enum tof type, Process *proc, int arg_num) {
- proc_archdep *a = (proc_archdep *) proc->arch_ptr;
-
- if (arg_num == -1) { /* return value */
- return a->regs.rax;
- }
-
- if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) {
- return ptrace(PTRACE_PEEKTEXT, proc->pid,
- proc->stack_pointer + 4 * (arg_num + 1), 0);
- } else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) {
- switch (arg_num) {
- case 0:
- return a->regs.rbx;
- case 1:
- return a->regs.rcx;
- case 2:
- return a->regs.rdx;
- case 3:
- return a->regs.rsi;
- case 4:
- return a->regs.rdi;
- case 5:
- return a->regs.rbp;
- default:
- fprintf(stderr,
- "gimme_arg32 called with wrong arguments\n");
- exit(2);
- }
- }
- fprintf(stderr, "gimme_arg called with wrong arguments\n");
- exit(1);
-}
-
-static long
-gimme_arg_regset(Process *proc, int arg_num, arg_type_info *info,
- struct user_regs_struct *regs,
- struct user_fpregs_struct *fpregs)
-{
- union {
- uint32_t sse[4];
- long lval;
- float fval;
- double dval;
- } cvt;
-
- if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) {
- memcpy(cvt.sse, fpregs->xmm_space + 4*arg_num,
- sizeof(cvt.sse));
- return cvt.lval;
- }
-
- switch (arg_num) {
- case 0:
- return regs->rdi;
- case 1:
- return regs->rsi;
- case 2:
- return regs->rdx;
- case 3:
- return regs->rcx;
- case 4:
- return regs->r8;
- case 5:
- return regs->r9;
- default:
- return ptrace(PTRACE_PEEKTEXT, proc->pid,
- proc->stack_pointer + 8 * (arg_num - 6 + 1), 0);
- }
-}
-static long
-gimme_retval(Process *proc, int arg_num, arg_type_info *info,
- struct user_regs_struct *regs, struct user_fpregs_struct *fpregs)
-{
- if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE)
- return gimme_arg_regset(proc, 0, info, regs, fpregs);
- else
- return regs->rax;
-}
-
-long
-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) {
- if (proc->mask_32bit)
- return (unsigned int)gimme_arg32(type, proc, arg_num);
-
- proc_archdep *arch = (proc_archdep *)proc->arch_ptr;
-
- if (arch == NULL || !arch->valid)
- return -1;
-
- if (type == LT_TOF_FUNCTIONR) {
- if (arg_num == -1)
- return gimme_retval(proc, arg_num, info,
- &arch->regs, &arch->fpregs);
- else {
- struct callstack_element *elem
- = proc->callstack + proc->callstack_depth - 1;
- callstack_achdep *csad = elem->arch_ptr;
- assert(csad != NULL);
- return gimme_arg_regset(proc, arg_num, info,
- &csad->regs_copy,
- &csad->fpregs_copy);
- }
- }
- else
- return gimme_arg_regset(proc, arg_num, info,
- &arch->regs, &arch->fpregs);
-}
-
-void
-save_register_args(enum tof type, Process *proc) {
- proc_archdep *arch = (proc_archdep *)proc->arch_ptr;
- if (arch == NULL || !arch->valid)
- return;
-
- callstack_achdep *csad = malloc(sizeof(*csad));
- memset(csad, 0, sizeof(*csad));
- memcpy(&csad->regs_copy, &arch->regs, sizeof(arch->regs));
- memcpy(&csad->fpregs_copy, &arch->fpregs, sizeof(arch->fpregs));
-
- proc->callstack[proc->callstack_depth - 1].arch_ptr = csad;
-}
diff --git a/testsuite/ltrace.main/parameters-lib.c b/testsuite/ltrace.main/parameters-lib.c
index ef98143..f9a869d 100644
--- a/testsuite/ltrace.main/parameters-lib.c
+++ b/testsuite/ltrace.main/parameters-lib.c
@@ -27,6 +27,11 @@ void func_strfixed(char* p)
strcpy(p, "Hello world");
}
+void func_string(char* p)
+{
+ printf("%s\n", p);
+}
+
void func_ppp(int*** ppp)
{
printf("%d\n", ***ppp);
@@ -133,3 +138,106 @@ void func_call (char *x, char* y, void (*cb) (char *))
cb (y);
*x = (*y)++;
}
+
+struct S2 {
+ float f;
+ char a;
+ char b;
+};
+
+struct S3 {
+ char a[6];
+ float f;
+};
+
+struct S2
+func_struct_2(int i, struct S3 s3, double d)
+{
+ return (struct S2){ s3.f, s3.a[1], s3.a[2] };
+}
+
+struct S4 {
+ long a;
+ long b;
+ long c;
+ long d;
+};
+
+struct S4
+func_struct_large(struct S4 a, struct S4 b)
+{
+ return (struct S4){ a.a + b.a, a.b + b.b, a.c + b.c, a.d + b.d };
+}
+
+struct S5 {
+ char a;
+ char b;
+ long c;
+ long d;
+};
+
+struct S5
+func_struct_large2(struct S5 a, struct S5 b)
+{
+ return (struct S5){ a.a + b.a, a.b + b.b, a.c + b.c, a.d + b.d };
+}
+
+struct S6 {
+ long a;
+ long b;
+ char c;
+ char d;
+};
+
+struct S6
+func_struct_large3(struct S6 a, struct S6 b)
+{
+ return (struct S6){ a.a + b.a, a.b + b.b, a.c + b.c, a.d + b.d };
+}
+
+void
+func_many_args(int a, int b, long c, double d, char e, int f, float g, char h,
+ int i, double j, int k, double l, char m, int n, short o, int p,
+ char q, float r, float s, double t, long u, float v, float w,
+ float x, float y)
+{
+}
+
+void
+func_lens(int a, long b, short c, long d)
+{
+}
+
+int
+func_bool(int a, int b)
+{
+ return !b;
+}
+
+void
+func_hide(int a, int b, int c, int d, int e, int f)
+{
+}
+
+long *
+func_short_enums(short values[])
+{
+ static long retvals[4];
+ retvals[0] = values[0];
+ retvals[1] = values[1];
+ retvals[2] = values[2];
+ retvals[3] = values[3];
+ return retvals;
+}
+
+long
+func_negative_enum(short a, unsigned short b, int c, unsigned d,
+ long e, unsigned long f)
+{
+ return -1;
+}
+
+void
+func_charp_string(char *p)
+{
+}
diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c
index fb46dfe..e8207fe 100644
--- a/testsuite/ltrace.main/parameters.c
+++ b/testsuite/ltrace.main/parameters.c
@@ -59,6 +59,7 @@ main ()
func_intptr_ret(&x);
+ func_string("zero\0xxxxxxxxxxxxxx");
func_strlen(buf);
printf("%s\n", buf);
@@ -133,5 +134,89 @@ main ()
func_call(x, y, call_func_work);
}
+ struct S2 {
+ float f;
+ char a;
+ char b;
+ };
+ struct S3 {
+ char a[6];
+ float f;
+ };
+ struct S2 func_struct_2(int, struct S3 s3, double d);
+ func_struct_2(17, (struct S3){ "ABCDE", 0.25 }, 0.5);
+
+ struct S4 {
+ long a;
+ long b;
+ long c;
+ long d;
+ };
+ struct S4 func_struct_large(struct S4 a, struct S4 b);
+ func_struct_large((struct S4){ 1, 2, 3, 4 }, (struct S4){ 5, 6, 7, 8 });
+
+ struct S5 {
+ char a;
+ char b;
+ long c;
+ long d;
+ };
+ struct S5 func_struct_large2(struct S5 a, struct S5 b);
+ func_struct_large2((struct S5){ '0', '1', 3, 4 }, (struct S5){ '2', '3', 7, 8 });
+
+ struct S6 {
+ long a;
+ long b;
+ char c;
+ char d;
+ };
+ struct S6 func_struct_large3(struct S6 a, struct S6 b);
+ func_struct_large3((struct S6){ 3, 4, '0', '1' }, (struct S6){ 7, 8 ,'2', '3' });
+
+ void func_many_args(int a, int b, long c, double d, char e, int f, float g,
+ char h, int i, double j, int k, double l, char m, int n,
+ short o, int p, char q, float r, float s, double t,
+ long u, float v, float w, float x, float y);
+ func_many_args(1, 2, 3, 4.0, '5', 6, 7.0,
+ '8', 9, 10.0, 11, 12.0, 'A', 14,
+ 15, 16, 'B', 18.0, 19.0, 20.0,
+ 21, 22.0, 23.0, 24.0, 25.0);
+
+ printf("sotnuh %d %ld %g %c\n", 5, 6L, 1.5, 'X');
+ printf("sotnuh1 %d %ld %hd\n", 5, 6L, (short)7);
+ printf("sotnuh2 %s %10s %10s\n", "a string", "a trimmed string", "short");
+ printf("many_args"
+ "%d %d %ld %g %c %d %g "
+ "%c %d %g %d %g %c %d "
+ "%hd %d %c %g %g %g "
+ "%ld %g %g %g %g",
+ 1, 2, 3, 4.0, '5', 6, 7.0,
+ '8', 9, 10.0, 11, 12.0, 'A', 14,
+ (short)15, 16, 'B', 18.0, 19.0, 20.0,
+ 21L, 22.0, 23.0, 24.0, 25.0);
+
+ printf("sotnuh3 %*s\n", 4, "a trimmed string");
+
+ void func_lens(int, long, short, long);
+ func_lens(22, 23, 24, 25);
+
+ int func_bool(int a, int b);
+ func_bool(1, 10);
+ func_bool(2, 0);
+
+ void func_hide(int a, int b, int c, int d, int e, int f);
+ func_hide(1, 2, 3, 4, 5, 6);
+
+ enum ab { A, B };
+ long *func_short_enums(short abs[]);
+ func_short_enums((short[]){ A, B, A, A });
+
+ long func_negative_enum(short a, unsigned short b, int c, unsigned d,
+ long e, unsigned long f);
+ func_negative_enum(-1, -1, -1, -1, -1, -1);
+
+ void func_charp_string(char *p);
+ func_charp_string("null-terminated string");
+
return 0;
}
diff --git a/testsuite/ltrace.main/parameters.conf b/testsuite/ltrace.main/parameters.conf
index e94ce2c..9e0c967 100644
--- a/testsuite/ltrace.main/parameters.conf
+++ b/testsuite/ltrace.main/parameters.conf
@@ -1,8 +1,9 @@
void func_intptr(int*)
void func_intptr_ret(+int*)
+void func_string(string[10])
int func_strlen(+string[retval])
void func_strfixed(string[4])
-void func_ppp(int***)
+void func_ppp(int * **)
void func_stringp(string*)
void func_enum(enum (RED=0,GREEN=1,BLUE=2,CHARTREUSE=3,PETUNIA=4))
void func_short(short,short)
@@ -11,8 +12,20 @@ float func_float(float,float)
double func_double(double,double)
typedef color = enum (RED=0,GREEN=1,BLUE=2,CHARTREUSE=3,PETUNIA=4)
void func_typedef(color)
-void func_arrayi(array(int,arg2)*,void)
-void func_arrayf(array(float,arg2)*,void)
+void func_arrayi(array(int,arg2)*,int)
+void func_arrayf(array(float,arg2)*,int)
void func_struct(struct(int,int,int,array(struct(int,int),elt2)*,array(struct(int,int),3),string[elt3])*)
void func_work(+string);
void func_call(+string, string);
+struct(float,char,char) func_struct_2(int, struct(string(array(char, 6)),float), double);
+struct(long,long,long,long) func_struct_large(struct(long,long,long,long), struct(long,long,long,long));
+struct(char,char,long,long) func_struct_large2(struct(char,char,long,long), struct(char,char,long,long));
+struct(long,long,char,char) func_struct_large3(struct(long,long,char,char), struct(long,long,char,char));
+void func_many_args(int, int, long, double, char, int, float, char, int, double, int, double, char, int, short, int, char, float, float, double, long, float, float, float, float);
+int printf(format);
+void func_lens(octal, octal(long), hex(short), hex(long));
+bool(int) func_bool(int, bool(int));
+void func_hide(int, hide(int), hide(int), int, hide(int), int);
+array(enum[long](A,B), 4) *func_short_enums(array(enum[short](A,B), 4));
+enum[long](A=-1) func_negative_enum(enum[short](A=-1), enum[ushort](A=-1), enum[int](A=-1), enum[uint](A=-1), enum[long](A=-1), enum[ulong](A=-1));
+void func_charp_string(string(char *));
diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp
index 924afaf..8403721 100644
--- a/testsuite/ltrace.main/parameters.exp
+++ b/testsuite/ltrace.main/parameters.exp
@@ -35,7 +35,8 @@ if [regexp {ELF from incompatible architecture} $exec_output] {
return
}
-set xfail_spec {"arm*-*" "i*86-*"}
+set xfail_spec {"arm*-*" }
+set xfail_spec_arm {"arm*-*"}
# Verify the output
set pattern "func_intptr(17)"
@@ -50,6 +51,8 @@ set pattern "func_ppp(80)"
ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
set pattern "func_stringp(\\\"Dude\\\")"
ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+set pattern "func_string(\\\"zero\\\")"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
set pattern "func_enum(BLUE)"
ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
set pattern "func_short(-8, -9)"
@@ -63,13 +66,13 @@ set pattern "func_double(3.40*, -3.40*).*= -3.40*"
ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
set pattern "func_typedef(BLUE)"
ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
-set pattern "func_arrayi(. 10, 11, 12, 13\\.\\.\\. ., )"
+set pattern "func_arrayi(. 10, 11, 12, 13\\.\\.\\. ., 8)"
ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
-set pattern "func_arrayi(. 10, 11 ., )"
+set pattern "func_arrayi(. 10, 11 ., 2)"
ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
-set pattern "func_arrayf(. 10.10*, 11.10*, 12.10*, 13.10*\\.\\.\\. ., )"
+set pattern "func_arrayf(. 10.10*, 11.10*, 12.10*, 13.10*\\.\\.\\. ., 8)"
ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
-set pattern "func_arrayf(. 10.10*, 11.10* ., )"
+set pattern "func_arrayf(. 10.10*, 11.10* ., 2)"
ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
set pattern "exited (status 0)"
ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
@@ -79,6 +82,56 @@ set pattern "func_call( <unfinished ...>"
ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
set pattern "func_work(\\\"x\\\")"
ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
-eval "setup_xfail $xfail_spec"
+set pattern "func_struct_2(17, { \\\"ABCDE\\\\\\\\0\\\", 0.250* }, 0.50*).*= { 0.250*, 'B', 'C' }"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+eval "setup_xfail $xfail_spec_arm"
set pattern "<... func_call resumed> \\\"x\\\", \\\"y\\\")"
ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "func_struct_large({ 1, 2, 3, 4 }, { 5, 6, 7, 8 }).*= { 6, 8, 10, 12 }"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "func_struct_large2({ '0', '1', 3, 4 }, { '2', '3', 7, 8 }).*= { 'b', 'd', 10, 12 }"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "func_struct_large3({ 3, 4, '0', '1' }, { 7, 8, '2', '3' }).*= { 10, 12, 'b', 'd' }"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "func_many_args(1, 2, 3, 4.00*, '5', 6, 7.00*, '8', 9, 10.00*, 11, 12.00*, 'A', 14, 15, 16, 'B', 18.00*, 19.00*, 20.00*, 21, 22.00*, 23.00*, 24.00*, 25.00*)"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "printf(\\\"sotnuh %d %ld %g %c.n\\\", 5, 6, 1.500*, 'X')"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "printf(\\\"sotnuh1 %d %ld %hd.n\\\", 5, 6, 7)"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "printf(\\\"sotnuh2 %s %10s %10s.n\\\", \\\"a string\\\", \\\"a trimmed \\\", \\\"short\\\")"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "printf(\\\"sotnuh3 %.s.n\\\", 4, \\\"a tr\\\")"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "printf(\\\"many_args%d %d %ld %g %c %d %g .*, 1, 2, 3, 4.00*, '5', 6, 7.00*, '8', 9, 10.00*, 11, 12.00*, 'A', 14, 15, 16, 'B', 18.00*, 19.00*, 20.00*, 21, 22.00*, 23.00*, 24.00*, 25.00*)"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "func_lens(026, 027, 0x18, 0x19)"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "func_bool(1, true).*= false"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "func_bool(2, false).*= true"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "func_hide(1, 4, 6)"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "func_short_enums(. A, B, A, A .).*= . A, B, A, A ."
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "func_negative_enum(A, A, A, A, A, A).*= A"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
+
+set pattern "func_charp_string(\\\"null-terminated string\\\")"
+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1
diff --git a/type.c b/type.c
new file mode 100644
index 0000000..6341042
--- /dev/null
+++ b/type.c
@@ -0,0 +1,515 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2007,2008 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "type.h"
+#include "sysdep.h"
+#include "expr.h"
+#include "lens.h"
+
+struct arg_type_info *
+type_get_simple(enum arg_type type)
+{
+#define HANDLE(T) { \
+ static struct arg_type_info t = { T }; \
+ case T: \
+ return &t; \
+ }
+
+ switch (type) {
+ HANDLE(ARGTYPE_VOID)
+ HANDLE(ARGTYPE_INT)
+ HANDLE(ARGTYPE_UINT)
+ HANDLE(ARGTYPE_LONG)
+ HANDLE(ARGTYPE_ULONG)
+ HANDLE(ARGTYPE_CHAR)
+ HANDLE(ARGTYPE_SHORT)
+ HANDLE(ARGTYPE_USHORT)
+ HANDLE(ARGTYPE_FLOAT)
+ HANDLE(ARGTYPE_DOUBLE)
+
+#undef HANDLE
+
+ case ARGTYPE_ARRAY:
+ case ARGTYPE_STRUCT:
+ case ARGTYPE_POINTER:
+ assert(!"Not a simple type!");
+ };
+ abort();
+}
+
+static void
+type_init_common(struct arg_type_info *info, enum arg_type type)
+{
+ info->type = type;
+ info->lens = NULL;
+ info->own_lens = 0;
+}
+
+struct struct_field {
+ struct arg_type_info *info;
+ int own_info;
+};
+
+void
+type_init_struct(struct arg_type_info *info)
+{
+ type_init_common(info, ARGTYPE_STRUCT);
+ VECT_INIT(&info->u.entries, struct struct_field);
+}
+
+int
+type_struct_add(struct arg_type_info *info,
+ struct arg_type_info *field_info, int own)
+{
+ assert(info->type == ARGTYPE_STRUCT);
+ struct struct_field field = { field_info, own };
+ return VECT_PUSHBACK(&info->u.entries, &field);
+}
+
+struct arg_type_info *
+type_struct_get(struct arg_type_info *info, size_t idx)
+{
+ assert(info->type == ARGTYPE_STRUCT);
+ struct struct_field *field = VECT_ELEMENT(&info->u.entries,
+ struct struct_field, idx);
+ if (field == NULL)
+ return NULL;
+ return field->info;
+}
+
+size_t
+type_struct_size(struct arg_type_info *info)
+{
+ assert(info->type == ARGTYPE_STRUCT);
+ return vect_size(&info->u.entries);
+}
+
+static void
+struct_field_dtor(struct struct_field *field, void *data)
+{
+ if (field->own_info) {
+ type_destroy(field->info);
+ free(field->info);
+ }
+}
+
+static void
+type_struct_destroy(struct arg_type_info *info)
+{
+ VECT_DESTROY(&info->u.entries, struct struct_field,
+ struct_field_dtor, NULL);
+}
+
+static int
+layout_struct(struct Process *proc, struct arg_type_info *info,
+ size_t *sizep, size_t *alignmentp, size_t *offsetofp)
+{
+ size_t sz = 0;
+ size_t max_alignment = 0;
+ size_t i;
+ size_t offsetof_field = (size_t)-1;
+ if (offsetofp != NULL)
+ offsetof_field = *offsetofp;
+
+ assert(info->type == ARGTYPE_STRUCT);
+ for (i = 0; i < vect_size(&info->u.entries); ++i) {
+ struct struct_field *field
+ = VECT_ELEMENT(&info->u.entries,
+ struct struct_field, i);
+
+ size_t alignment = type_alignof(proc, field->info);
+ if (alignment == (size_t)-1)
+ return -1;
+
+ /* Add padding to SZ to align the next element. */
+ sz = align(sz, alignment);
+ if (i == offsetof_field) {
+ *offsetofp = sz;
+ if (sizep == NULL && alignmentp == NULL)
+ return 0;
+ }
+
+ size_t size = type_sizeof(proc, field->info);
+ if (size == (size_t)-1)
+ return -1;
+ sz += size;
+
+ if (alignment > max_alignment)
+ max_alignment = alignment;
+ }
+
+ if (max_alignment > 0)
+ sz = align(sz, max_alignment);
+
+ if (sizep != NULL)
+ *sizep = sz;
+
+ if (alignmentp != NULL)
+ *alignmentp = max_alignment;
+
+ return 0;
+}
+
+void
+type_init_array(struct arg_type_info *info,
+ struct arg_type_info *element_info, int own_info,
+ struct expr_node *length_expr, int own_length)
+{
+ type_init_common(info, ARGTYPE_ARRAY);
+ info->u.array_info.elt_type = element_info;
+ info->u.array_info.own_info = own_info;
+ info->u.array_info.length = length_expr;
+ info->u.array_info.own_length = own_length;
+}
+
+static void
+type_array_destroy(struct arg_type_info *info)
+{
+ if (info->u.array_info.own_info) {
+ type_destroy(info->u.array_info.elt_type);
+ free(info->u.array_info.elt_type);
+ }
+ if (info->u.array_info.own_length) {
+ expr_destroy(info->u.array_info.length);
+ free(info->u.array_info.length);
+ }
+}
+
+void
+type_init_pointer(struct arg_type_info *info,
+ struct arg_type_info *pointee_info, int own_info)
+{
+ type_init_common(info, ARGTYPE_POINTER);
+ info->u.ptr_info.info = pointee_info;
+ info->u.ptr_info.own_info = own_info;
+}
+
+static void
+type_pointer_destroy(struct arg_type_info *info)
+{
+ if (info->u.ptr_info.own_info) {
+ type_destroy(info->u.ptr_info.info);
+ free(info->u.ptr_info.info);
+ }
+}
+
+void
+type_destroy(struct arg_type_info *info)
+{
+ if (info == NULL)
+ return;
+
+ switch (info->type) {
+ case ARGTYPE_STRUCT:
+ type_struct_destroy(info);
+ break;
+
+ case ARGTYPE_ARRAY:
+ type_array_destroy(info);
+ break;
+
+ case ARGTYPE_POINTER:
+ type_pointer_destroy(info);
+ break;
+
+ case ARGTYPE_VOID:
+ case ARGTYPE_INT:
+ case ARGTYPE_UINT:
+ case ARGTYPE_LONG:
+ case ARGTYPE_ULONG:
+ case ARGTYPE_CHAR:
+ case ARGTYPE_SHORT:
+ case ARGTYPE_USHORT:
+ case ARGTYPE_FLOAT:
+ case ARGTYPE_DOUBLE:
+ break;
+ }
+
+ if (info->own_lens) {
+ lens_destroy(info->lens);
+ free(info->lens);
+ }
+}
+
+#ifdef ARCH_HAVE_SIZEOF
+size_t arch_type_sizeof(struct Process *proc, struct arg_type_info * arg);
+#else
+size_t
+arch_type_sizeof(struct Process *proc, struct arg_type_info * arg)
+{
+ /* Use default value. */
+ return (size_t)-2;
+}
+#endif
+
+#ifdef ARCH_HAVE_ALIGNOF
+size_t arch_type_alignof(struct Process *proc, struct arg_type_info * arg);
+#else
+size_t
+arch_type_alignof(struct Process *proc, struct arg_type_info * arg)
+{
+ /* Use default value. */
+ return (size_t)-2;
+}
+#endif
+
+/* We need to support alignments that are not power of two. E.g. long
+ * double on x86 has alignment of 12. */
+size_t
+align(size_t sz, size_t alignment)
+{
+ assert(alignment != 0);
+
+ if ((sz % alignment) != 0)
+ sz = ((sz / alignment) + 1) * alignment;
+
+ return sz;
+}
+
+size_t
+type_sizeof(struct Process *proc, struct arg_type_info *type)
+{
+ size_t arch_size = arch_type_sizeof(proc, type);
+ if (arch_size != (size_t)-2)
+ return arch_size;
+
+ switch (type->type) {
+ size_t size;
+ case ARGTYPE_CHAR:
+ return sizeof(char);
+
+ case ARGTYPE_SHORT:
+ case ARGTYPE_USHORT:
+ return sizeof(short);
+
+ case ARGTYPE_INT:
+ case ARGTYPE_UINT:
+ return sizeof(int);
+
+ case ARGTYPE_LONG:
+ case ARGTYPE_ULONG:
+ return sizeof(long);
+
+ case ARGTYPE_FLOAT:
+ return sizeof(float);
+
+ case ARGTYPE_DOUBLE:
+ return sizeof(double);
+
+ case ARGTYPE_STRUCT:
+ if (layout_struct(proc, type, &size, NULL, NULL) < 0)
+ return (size_t)-1;
+ return size;
+
+ case ARGTYPE_POINTER:
+ return sizeof(void *);
+
+ case ARGTYPE_ARRAY:
+ if (expr_is_compile_constant(type->u.array_info.length)) {
+ long l;
+ if (expr_eval_constant(type->u.array_info.length,
+ &l) < 0)
+ return -1;
+
+ struct arg_type_info *elt_ti
+ = type->u.array_info.elt_type;
+
+ size_t elt_size = type_sizeof(proc, elt_ti);
+ if (elt_size == (size_t)-1)
+ return (size_t)-1;
+
+ return ((size_t)l) * elt_size;
+
+ } else {
+ /* Flexible arrays don't count into the
+ * sizeof. */
+ return 0;
+ }
+
+ case ARGTYPE_VOID:
+ return 0;
+ }
+
+ abort();
+}
+
+#undef alignof
+#define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st))
+
+size_t
+type_alignof(struct Process *proc, struct arg_type_info *type)
+{
+ size_t arch_alignment = arch_type_alignof(proc, type);
+ if (arch_alignment != (size_t)-2)
+ return arch_alignment;
+
+ struct { char c; char C; } cC;
+ struct { char c; short s; } cs;
+ struct { char c; int i; } ci;
+ struct { char c; long l; } cl;
+ struct { char c; void* p; } cp;
+ struct { char c; float f; } cf;
+ struct { char c; double d; } cd;
+
+ static size_t char_alignment = alignof(C, cC);
+ static size_t short_alignment = alignof(s, cs);
+ static size_t int_alignment = alignof(i, ci);
+ static size_t long_alignment = alignof(l, cl);
+ static size_t ptr_alignment = alignof(p, cp);
+ static size_t float_alignment = alignof(f, cf);
+ static size_t double_alignment = alignof(d, cd);
+
+ switch (type->type) {
+ size_t alignment;
+ case ARGTYPE_LONG:
+ case ARGTYPE_ULONG:
+ return long_alignment;
+ case ARGTYPE_CHAR:
+ return char_alignment;
+ case ARGTYPE_SHORT:
+ case ARGTYPE_USHORT:
+ return short_alignment;
+ case ARGTYPE_FLOAT:
+ return float_alignment;
+ case ARGTYPE_DOUBLE:
+ return double_alignment;
+ case ARGTYPE_POINTER:
+ return ptr_alignment;
+
+ case ARGTYPE_ARRAY:
+ return type_alignof(proc, type->u.array_info.elt_type);
+
+ case ARGTYPE_STRUCT:
+ if (layout_struct(proc, type, NULL, &alignment, NULL) < 0)
+ return (size_t)-1;
+ return alignment;
+
+ default:
+ return int_alignment;
+ }
+}
+
+size_t
+type_offsetof(struct Process *proc, struct arg_type_info *type, size_t emt)
+{
+ assert(type->type == ARGTYPE_STRUCT
+ || type->type == ARGTYPE_ARRAY);
+
+ switch (type->type) {
+ size_t alignment;
+ size_t size;
+ case ARGTYPE_ARRAY:
+ alignment = type_alignof(proc, type->u.array_info.elt_type);
+ if (alignment == (size_t)-1)
+ return (size_t)-1;
+
+ size = type_sizeof(proc, type->u.array_info.elt_type);
+ if (size == (size_t)-1)
+ return (size_t)-1;
+
+ return emt * align(size, alignment);
+
+ case ARGTYPE_STRUCT:
+ if (layout_struct(proc, type, NULL, NULL, &emt) < 0)
+ return (size_t)-1;
+ return emt;
+
+ default:
+ abort();
+ }
+}
+
+struct arg_type_info *
+type_element(struct arg_type_info *info, size_t emt)
+{
+ assert(info->type == ARGTYPE_STRUCT
+ || info->type == ARGTYPE_ARRAY);
+
+ switch (info->type) {
+ case ARGTYPE_ARRAY:
+ return info->u.array_info.elt_type;
+
+ case ARGTYPE_STRUCT:
+ assert(emt < type_struct_size(info));
+ return type_struct_get(info, emt);
+
+ default:
+ abort();
+ }
+}
+
+int
+type_is_integral(enum arg_type type)
+{
+ switch (type) {
+ case ARGTYPE_INT:
+ case ARGTYPE_UINT:
+ case ARGTYPE_LONG:
+ case ARGTYPE_ULONG:
+ case ARGTYPE_CHAR:
+ case ARGTYPE_SHORT:
+ case ARGTYPE_USHORT:
+ return 1;
+
+ case ARGTYPE_VOID:
+ case ARGTYPE_FLOAT:
+ case ARGTYPE_DOUBLE:
+ case ARGTYPE_ARRAY:
+ case ARGTYPE_STRUCT:
+ case ARGTYPE_POINTER:
+ return 0;
+ }
+ abort();
+}
+
+int
+type_is_signed(enum arg_type type)
+{
+ assert(type_is_integral(type));
+
+ switch (type) {
+ case ARGTYPE_CHAR:
+ return CHAR_MIN != 0;
+
+ case ARGTYPE_SHORT:
+ case ARGTYPE_INT:
+ case ARGTYPE_LONG:
+ return 1;
+
+ case ARGTYPE_UINT:
+ case ARGTYPE_ULONG:
+ case ARGTYPE_USHORT:
+ return 0;
+
+ case ARGTYPE_VOID:
+ case ARGTYPE_FLOAT:
+ case ARGTYPE_DOUBLE:
+ case ARGTYPE_ARRAY:
+ case ARGTYPE_STRUCT:
+ case ARGTYPE_POINTER:
+ abort();
+ }
+ abort();
+}
diff --git a/type.h b/type.h
new file mode 100644
index 0000000..545173c
--- /dev/null
+++ b/type.h
@@ -0,0 +1,133 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 1997-2009 Juan Cespedes
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef TYPE_H
+#define TYPE_H
+
+#include <stddef.h>
+#include "forward.h"
+#include "vect.h"
+
+enum arg_type {
+ ARGTYPE_VOID,
+ ARGTYPE_INT,
+ ARGTYPE_UINT,
+ ARGTYPE_LONG,
+ ARGTYPE_ULONG,
+ ARGTYPE_CHAR,
+ ARGTYPE_SHORT,
+ ARGTYPE_USHORT,
+ ARGTYPE_FLOAT,
+ ARGTYPE_DOUBLE,
+ ARGTYPE_ARRAY, /* Series of values in memory */
+ ARGTYPE_STRUCT, /* Structure of values */
+ ARGTYPE_POINTER, /* Pointer to some other type */
+};
+
+struct arg_type_info {
+ enum arg_type type;
+ union {
+ struct vect entries;
+
+ /* ARGTYPE_ARRAY */
+ struct {
+ struct arg_type_info *elt_type;
+ struct expr_node *length;
+ int own_info:1;
+ int own_length:1;
+ } array_info;
+
+ /* ARGTYPE_POINTER */
+ struct {
+ struct arg_type_info *info;
+ int own_info:1;
+ } ptr_info;
+ } u;
+
+ struct lens *lens;
+ int own_lens;
+};
+
+/* Return a type info for simple type TYPE (which shall not be array,
+ * struct, or pointer. Each call with the same TYPE yields the same
+ * arg_type_info pointer. */
+struct arg_type_info *type_get_simple(enum arg_type type);
+
+/* Initialize INFO so it becomes ARGTYPE_STRUCT. The created
+ * structure contains no fields. Use type_struct_add to populate the
+ * structure. */
+void type_init_struct(struct arg_type_info *info);
+
+/* Add a new field of type FIELD_INFO to a structure INFO. If OWN,
+ * the field type is owned and destroyed together with INFO. */
+int type_struct_add(struct arg_type_info *info,
+ struct arg_type_info *field_info, int own);
+
+/* Get IDX-th field of structure type INFO. */
+struct arg_type_info *type_struct_get(struct arg_type_info *info, size_t idx);
+
+/* Return number of fields of structure type INFO. */
+size_t type_struct_size(struct arg_type_info *info);
+
+/* Initialize INFO so it becomes ARGTYPE_ARRAY. The element type is
+ * passed in ELEMENT_INFO, and array length in LENGTH_EXPR. If,
+ * respectively, OWN_INFO and OWN_LENGTH are true, the pointee and
+ * length are owned and destroyed together with INFO. */
+void type_init_array(struct arg_type_info *info,
+ struct arg_type_info *element_info, int own_info,
+ struct expr_node *length_expr, int own_length);
+
+/* Initialize INFO so it becomes ARGTYPE_POINTER. The pointee type is
+ * passed in POINTEE_INFO. If OWN_INFO, the pointee type is owned and
+ * destroyed together with INFO. */
+void type_init_pointer(struct arg_type_info *info,
+ struct arg_type_info *pointee_info, int own_info);
+
+/* Release any memory associated with INFO. Doesn't free INFO
+ * itself. */
+void type_destroy(struct arg_type_info *info);
+
+/* Compute a size of given type. Return (size_t)-1 for error. */
+size_t type_sizeof(struct Process *proc, struct arg_type_info *type);
+
+/* Compute an alignment necessary for elements of this type. Return
+ * (size_t)-1 for error. */
+size_t type_alignof(struct Process *proc, struct arg_type_info *type);
+
+/* Align value SZ to ALIGNMENT and return the result. */
+size_t align(size_t sz, size_t alignment);
+
+/* Return ELT-th element of compound type TYPE. This is useful for
+ * arrays and structures. */
+struct arg_type_info *type_element(struct arg_type_info *type, size_t elt);
+
+/* Compute an offset of EMT-th element of type TYPE. This works for
+ * arrays and structures. Return (size_t)-1 for error. */
+size_t type_offsetof(struct Process *proc,
+ struct arg_type_info *type, size_t elt);
+
+/* Whether TYPE is an integral type as defined by the C standard. */
+int type_is_integral(enum arg_type type);
+
+/* Whether TYPE, which shall be integral, is a signed type. */
+int type_is_signed(enum arg_type type);
+
+#endif /* TYPE_H */
diff --git a/value.c b/value.c
new file mode 100644
index 0000000..62466f2
--- /dev/null
+++ b/value.c
@@ -0,0 +1,455 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "value.h"
+#include "type.h"
+#include "common.h"
+#include "expr.h"
+#include "backend.h"
+
+static void
+value_common_init(struct value *valp, struct Process *inferior,
+ struct value *parent, struct arg_type_info *type,
+ int own_type)
+{
+ valp->type = type;
+ valp->own_type = own_type;
+ valp->inferior = inferior;
+ memset(&valp->u, 0, sizeof(valp->u));
+ valp->where = VAL_LOC_NODATA;
+ valp->parent = parent;
+ valp->size = (size_t)-1;
+}
+
+void
+value_init(struct value *valp, struct Process *inferior, struct value *parent,
+ struct arg_type_info *type, int own_type)
+{
+ assert(inferior != NULL);
+ value_common_init(valp, inferior, parent, type, own_type);
+}
+
+void
+value_init_detached(struct value *valp, struct value *parent,
+ struct arg_type_info *type, int own_type)
+{
+ value_common_init(valp, NULL, parent, type, own_type);
+}
+
+void
+value_set_type(struct value *value, struct arg_type_info *type, int own_type)
+{
+ if (value->own_type)
+ type_destroy(value->type);
+ value->type = type;
+ value->own_type = own_type;
+}
+
+void
+value_take_type(struct value *value, struct arg_type_info **type,
+ int *own_type)
+{
+ *type = value->type;
+ *own_type = value->own_type;
+ value->own_type = 0;
+}
+
+void
+value_release(struct value *val)
+{
+ if (val == NULL)
+ return;
+ if (val->where == VAL_LOC_COPY) {
+ free(val->u.address);
+ val->where = VAL_LOC_NODATA;
+ }
+}
+
+void
+value_destroy(struct value *val)
+{
+ if (val == NULL)
+ return;
+ value_release(val);
+ value_set_type(val, NULL, 0);
+}
+
+unsigned char *
+value_reserve(struct value *valp, size_t size)
+{
+ if (size <= sizeof(valp->u.value)) {
+ valp->where = VAL_LOC_WORD;
+ valp->u.value = 0;
+ } else {
+ valp->where = VAL_LOC_COPY;
+ valp->u.address = calloc(size, 1);
+ if (valp->u.address == 0)
+ return NULL;
+ }
+ return value_get_raw_data(valp);
+}
+
+int
+value_reify(struct value *val, struct value_dict *arguments)
+{
+ if (val->where != VAL_LOC_INFERIOR)
+ return 0;
+ assert(val->inferior != NULL);
+
+ size_t size = value_size(val, arguments);
+ if (size == (size_t)-1)
+ return -1;
+
+ void *data;
+ enum value_location_t nloc;
+ if (size <= sizeof(val->u.value)) {
+ data = &val->u.value;
+ nloc = VAL_LOC_WORD;
+ } else {
+ data = malloc(size);
+ if (data == NULL)
+ return -1;
+ nloc = VAL_LOC_COPY;
+ }
+
+ if (umovebytes(val->inferior, val->u.address, data, size) < size) {
+ if (nloc == VAL_LOC_COPY)
+ free(data);
+ return -1;
+ }
+
+ val->where = nloc;
+ if (nloc == VAL_LOC_COPY)
+ val->u.address = data;
+
+ return 0;
+}
+
+unsigned char *
+value_get_data(struct value *val, struct value_dict *arguments)
+{
+ if (value_reify(val, arguments) < 0)
+ return NULL;
+ return value_get_raw_data(val);
+}
+
+unsigned char *
+value_get_raw_data(struct value *val)
+{
+ switch (val->where) {
+ case VAL_LOC_INFERIOR:
+ abort();
+ case VAL_LOC_NODATA:
+ return NULL;
+ case VAL_LOC_COPY:
+ case VAL_LOC_SHARED:
+ return val->u.address;
+ case VAL_LOC_WORD:
+ return val->u.buf;
+ }
+
+ assert(!"Unexpected value of val->where");
+ abort();
+}
+
+int
+value_clone(struct value *retp, struct value *val)
+{
+ *retp = *val;
+ if (val->where == VAL_LOC_COPY) {
+ assert(val->inferior != NULL);
+ size_t size = type_sizeof(val->inferior, val->type);
+ if (size == (size_t)-1)
+ return -1;
+
+ retp->u.address = malloc(size);
+ if (retp->u.address == NULL)
+ return -1;
+
+ memcpy(retp->u.address, val->u.address, size);
+ }
+
+ return 0;
+}
+
+size_t
+value_size(struct value *val, struct value_dict *arguments)
+{
+ if (val->size != (size_t)-1)
+ return val->size;
+
+ if (val->type->type != ARGTYPE_ARRAY)
+ return val->size = type_sizeof(val->inferior, val->type);
+
+ struct value length;
+ if (expr_eval(val->type->u.array_info.length, val,
+ arguments, &length) < 0)
+ return (size_t)-1;
+
+ size_t l;
+ int o = value_extract_word(&length, (long *)&l, arguments);
+ value_destroy(&length);
+
+ if (o < 0)
+ return (size_t)-1;
+
+ size_t elt_size = type_sizeof(val->inferior,
+ val->type->u.array_info.elt_type);
+ if (elt_size == (size_t)-1)
+ return (size_t)-1;
+
+ return val->size = elt_size * l;
+}
+
+int
+value_init_element(struct value *ret_val, struct value *val, size_t element)
+{
+ size_t off = type_offsetof(val->inferior, val->type, element);
+ if (off == (size_t)-1)
+ return -1;
+
+ struct arg_type_info *e_info = type_element(val->type, element);
+ if (e_info == NULL)
+ return -1;
+
+ value_common_init(ret_val, val->inferior, val, e_info, 0);
+
+ switch (val->where) {
+ case VAL_LOC_COPY:
+ case VAL_LOC_SHARED:
+ ret_val->u.address = val->u.address + off;
+ ret_val->where = VAL_LOC_SHARED;
+ return 0;
+
+ case VAL_LOC_WORD:
+ ret_val->u.address = value_get_raw_data(val) + off;
+ ret_val->where = VAL_LOC_SHARED;
+ return 0;
+
+ case VAL_LOC_INFERIOR:
+ ret_val->u.address = val->u.address + off;
+ ret_val->where = VAL_LOC_INFERIOR;
+ return 0;
+
+ case VAL_LOC_NODATA:
+ assert(!"Can't offset NODATA.");
+ abort();
+ }
+ abort();
+}
+
+int
+value_init_deref(struct value *ret_val, struct value *valp)
+{
+ assert(valp->type->type == ARGTYPE_POINTER);
+
+ /* Note: extracting a pointer value should not need value_dict
+ * with function arguments. */
+ long l;
+ if (value_extract_word(valp, &l, NULL) < 0)
+ return -1;
+
+ /* We need "long" to be long enough to hold platform
+ * pointers. */
+ typedef char assert__long_enough_long[-(sizeof(l) < sizeof(void *))];
+
+ value_common_init(ret_val, valp->inferior, valp,
+ valp->type->u.ptr_info.info, 0);
+ ret_val->u.value = l; /* Set the address. */
+ ret_val->where = VAL_LOC_INFERIOR;
+ return 0;
+}
+
+/* The functions value_extract_buf and value_extract_word assume that
+ * data in VALUE is stored at the start of the internal buffer. For
+ * value_extract_buf in particular there's no other reasonable
+ * default. If we need to copy out four bytes, they need to be the
+ * bytes pointed to by the buffer pointer.
+ *
+ * But actually the situation is similar for value_extract_word as
+ * well. This function is used e.g. to extract characters from
+ * strings. Those weren't stored by value_set_word, they might still
+ * be in client for all we know. So value_extract_word has to assume
+ * that the whole of data is data is stored at the buffer pointer.
+ *
+ * This is a problem on big endian machines, where 2-byte quantity
+ * carried in 4- or 8-byte long is stored at the end of that long.
+ * (Though that quantity itself is still big endian.) So we need to
+ * make a little dance to shift the value to the right part of the
+ * buffer. */
+
+union word_data {
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ long l;
+ unsigned char buf[0];
+} u;
+
+void
+value_set_word(struct value *value, long word)
+{
+ size_t sz = type_sizeof(value->inferior, value->type);
+ assert(sz != (size_t)-1);
+ assert(sz <= sizeof(value->u.value));
+
+ value->where = VAL_LOC_WORD;
+
+ union word_data u = {};
+
+ switch (sz) {
+ case 0:
+ u.l = 0;
+ break;
+ case 1:
+ u.u8 = word;
+ break;
+ case 2:
+ u.u16 = word;
+ break;
+ case 4:
+ u.u32 = word;
+ break;
+ case 8:
+ u.u64 = word;
+ break;
+ default:
+ assert(sz != sz);
+ abort();
+ }
+
+ value->u.value = u.l;
+}
+
+static int
+value_extract_buf_sz(struct value *value, unsigned char *tgt, size_t sz,
+ struct value_dict *arguments)
+{
+ unsigned char *data = value_get_data(value, arguments);
+ if (data == NULL)
+ return -1;
+
+ memcpy(tgt, data, sz);
+ return 0;
+}
+
+int
+value_extract_word(struct value *value, long *retp,
+ struct value_dict *arguments)
+{
+ size_t sz = type_sizeof(value->inferior, value->type);
+ if (sz == (size_t)-1)
+ return -1;
+ assert(sz <= sizeof(value->u.value));
+
+ if (sz == 0) {
+ *retp = 0;
+ return 0;
+ }
+
+ union word_data u = {};
+ if (value_extract_buf_sz(value, u.buf, sz, arguments) < 0)
+ return -1;
+
+ switch (sz) {
+ case 1:
+ *retp = (long)u.u8;
+ return 0;
+ case 2:
+ *retp = (long)u.u16;
+ return 0;
+ case 4:
+ *retp = (long)u.u32;
+ return 0;
+ case 8:
+ *retp = (long)u.u64;
+ return 0;
+ default:
+ assert(sz != sz);
+ abort();
+ }
+}
+
+int
+value_extract_buf(struct value *value, unsigned char *tgt,
+ struct value_dict *arguments)
+{
+ size_t sz = type_sizeof(value->inferior, value->type);
+ if (sz == (size_t)-1)
+ return -1;
+
+ return value_extract_buf_sz(value, tgt, sz, arguments);
+}
+
+struct value *
+value_get_parental_struct(struct value *val)
+{
+ struct value *parent;
+ for (parent = val->parent; parent != NULL; parent = parent->parent)
+ if (parent->type->type == ARGTYPE_STRUCT)
+ return parent;
+ return NULL;
+}
+
+int
+value_is_zero(struct value *val, struct value_dict *arguments)
+{
+ unsigned char *data = value_get_data(val, arguments);
+ if (data == NULL)
+ return -1;
+ size_t sz = type_sizeof(val->inferior, val->type);
+ if (sz == (size_t)-1)
+ return -1;
+
+ int zero = 1;
+ size_t j;
+ for (j = 0; j < sz; ++j) {
+ if (data[j] != 0) {
+ zero = 0;
+ break;
+ }
+ }
+ return zero;
+}
+
+int
+value_pass_by_reference(struct value *value)
+{
+ assert(value != NULL);
+ assert(value->type->type == ARGTYPE_STRUCT);
+
+ struct arg_type_info *new_info = calloc(sizeof(*new_info), 1);
+ if (new_info == NULL)
+ return -1;
+
+ int own;
+ struct arg_type_info *orig;
+ value_take_type(value, &orig, &own);
+ type_init_pointer(new_info, orig, own);
+ new_info->lens = orig->lens;
+ value_set_type(value, new_info, 1);
+
+ return 0;
+}
diff --git a/value.h b/value.h
new file mode 100644
index 0000000..1ba8a6c
--- /dev/null
+++ b/value.h
@@ -0,0 +1,155 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef VALUE_H
+#define VALUE_H
+
+#include "forward.h"
+
+/* Values are objects that capture data fetched from an inferior.
+ * Typically a value is attached to a single inferior where it was
+ * extracted from, but it is possible to create a detached value.
+ * Each value is typed. Values support a number of routines, such as
+ * dereferencing if the value is of pointer type, array or structure
+ * access, etc.
+ *
+ * A value can be uninitialized, abstract or reified. Abstract values
+ * are just references into inferior, no transfer has taken place yet.
+ * Reified values have been copied out of the corresponding inferior,
+ * or otherwise set to some value. */
+
+enum value_location_t {
+ VAL_LOC_NODATA = 0, /* Uninitialized. */
+ VAL_LOC_INFERIOR, /* Value is in the inferior process. */
+ VAL_LOC_COPY, /* Value was copied out of the inferior. */
+ VAL_LOC_SHARED, /* Like VAL_LOC_COPY, but don't free. */
+ VAL_LOC_WORD, /* Like VAL_LOC_COPY, but small enough. */
+};
+
+struct value {
+ struct arg_type_info *type;
+ struct Process *inferior;
+ struct value *parent;
+ size_t size;
+ union {
+ void *address; /* VAL_LOC_CLIENT, VAL_LOC_COPY,
+ VAL_LOC_SHARED */
+ long value; /* VAL_LOC_WORD */
+ unsigned char buf[0];
+ } u;
+ enum value_location_t where;
+ int own_type;
+};
+
+/* Initialize VALUE. INFERIOR must not be NULL. PARENT is parental
+ * value, in case of compound types. It may be NULL. TYPE is a type
+ * of the value. It may be NULL if the type is not yet known. If
+ * OWN_TYPE, the passed-in type is owned and released by value. */
+void value_init(struct value *value, struct Process *inferior,
+ struct value *parent, struct arg_type_info *type,
+ int own_type);
+
+/* Initialize VALUE. This is like value_init, except that inferior is
+ * NULL. VALP is initialized as a detached value, without assigned
+ * process. You have to be careful not to use VAL_LOC_INFERIOR
+ * values if the value is detached. */
+void value_init_detached(struct value *value, struct value *parent,
+ struct arg_type_info *type, int own_type);
+
+/* Set TYPE. This releases old type if it was owned. TYPE is owned
+ * and released if OWN_TYPE. */
+void value_set_type(struct value *value,
+ struct arg_type_info *type, int own_type);
+
+/* Transfers the ownership of VALUE's type, if any, to the caller.
+ * This doesn't reset the VALUE's type, but gives up ownership if
+ * there was one. Previous ownership is passed in OWN_TYPE. */
+void value_take_type(struct value *value,
+ struct arg_type_info **type, int *own_type);
+
+/* Release the data held by VALP, if any, but not the type. */
+void value_release(struct value *valp);
+
+/* Destroy the value. This is like value_release, but it additionally
+ * frees the value type, if it's own_type. It doesn't free the VAL
+ * pointer itself. */
+void value_destroy(struct value *val);
+
+/* Set the data held by VALP to VALUE. This also sets the value's
+ * where to VAL_LOC_WORD. */
+void value_set_word(struct value *valp, long value);
+
+/* Set the data held by VALP to a buffer of size SIZE. This buffer
+ * may be allocated by malloc. Returns NULL on failure. */
+unsigned char *value_reserve(struct value *valp, size_t size);
+
+/* Access ELEMENT-th field of the compound value VALP, and store the
+ * result into the value RET_VAL. Returns 0 on success, or negative
+ * value on failure. */
+int value_init_element(struct value *ret_val, struct value *valp, size_t element);
+
+/* De-reference pointer value, and store the result into the value
+ * RET_VAL. Returns 0 on success, or negative value on failure. */
+int value_init_deref(struct value *ret_val, struct value *valp);
+
+/* If value is in inferior, copy it over to ltrace. Return 0 for
+ * success or negative value for failure. */
+int value_reify(struct value *val, struct value_dict *arguments);
+
+/* Return a pointer to the data of the value. This copies the data
+ * from the inferior to the tracer. Returns NULL on failure. */
+unsigned char *value_get_data(struct value *val, struct value_dict *arguments);
+
+/* Return a pointer to the raw data of the value. This shall not be
+ * called on a VAL_LOC_INFERIOR value. */
+unsigned char *value_get_raw_data(struct value *val);
+
+/* Copy value VAL into the area pointed-to by RETP. Return 0 on
+ * success or a negative value on failure. */
+int value_clone(struct value *retp, struct value *val);
+
+/* Give a size of given value. Return (size_t)-1 for error. This is
+ * a full size of the value. In particular for arrays, it returns
+ * actual length of the array, as computed by the length
+ * expression. */
+size_t value_size(struct value *val, struct value_dict *arguments);
+
+/* Extract at most word-sized datum from the value. Return 0 on
+ * success or negative value on failure. */
+int value_extract_word(struct value *val, long *retp,
+ struct value_dict *arguments);
+
+/* Copy contents of VAL to DATA. The buffer must be large enough to
+ * hold all the data inside. */
+int value_extract_buf(struct value *val, unsigned char *data,
+ struct value_dict *arguments);
+
+/* Find the most enclosing parental value that is a struct. Return
+ * NULL when there is no such parental value. */
+struct value *value_get_parental_struct(struct value *val);
+
+/* Determine whether this is all-zero value. Returns >0 if it is, ==0
+ * if it isn't, <0 on error. */
+int value_is_zero(struct value *val, struct value_dict *arguments);
+
+/* Convert a structure type to pointer to that structure type. */
+int value_pass_by_reference(struct value *value);
+
+#endif /* VALUE_H */
diff --git a/value_dict.c b/value_dict.c
new file mode 100644
index 0000000..3f3880a
--- /dev/null
+++ b/value_dict.c
@@ -0,0 +1,147 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "value_dict.h"
+#include "value.h"
+
+struct named_value
+{
+ const char *name;
+ struct value value;
+ int own_name;
+};
+
+void
+val_dict_init(struct value_dict *dict)
+{
+ VECT_INIT(&dict->numbered, struct value);
+ VECT_INIT(&dict->named, struct named_value);
+}
+
+static int
+value_clone_cb(struct value *tgt, struct value *src, void *data)
+{
+ return value_clone(tgt, src);
+}
+
+static void
+value_dtor(struct value *val, void *data)
+{
+ value_destroy(val);
+}
+
+static int
+named_value_clone(struct named_value *tgt, struct named_value *src, void *data)
+{
+ tgt->name = strdup(src->name);
+ if (tgt->name == NULL)
+ return -1;
+ tgt->own_name = 1;
+ if (value_clone(&tgt->value, &src->value) < 0) {
+ free((char *)tgt->name);
+ return -1;
+ }
+ return 0;
+}
+
+static void
+named_value_dtor(struct named_value *named, void *data)
+{
+ if (named->own_name)
+ free((char *)named->name);
+ value_destroy(&named->value);
+}
+
+int
+val_dict_clone(struct value_dict *target, struct value_dict *source)
+{
+ if (VECT_CLONE(&target->numbered, &source->numbered, struct value,
+ value_clone_cb, value_dtor, NULL) < 0)
+ return -1;
+
+ if (VECT_CLONE(&target->named, &source->named, struct named_value,
+ named_value_clone, named_value_dtor, NULL) < 0) {
+ VECT_DESTROY(&target->numbered, struct value, value_dtor, NULL);
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+val_dict_push_next(struct value_dict *dict, struct value *val)
+{
+ return VECT_PUSHBACK(&dict->numbered, val);
+}
+
+int
+val_dict_push_named(struct value_dict *dict, struct value *val,
+ const char *name, int own_name)
+{
+ if (own_name && (name = strdup(name)) == NULL)
+ return -1;
+ struct named_value element = { name, *val, own_name };
+ if (VECT_PUSHBACK(&dict->named, &element) < 0) {
+ if (own_name)
+ free((char *)name);
+ return -1;
+ }
+ return 0;
+}
+
+size_t
+val_dict_count(struct value_dict *dict)
+{
+ return vect_size(&dict->numbered);
+}
+
+struct value *
+val_dict_get_num(struct value_dict *dict, size_t num)
+{
+ assert(num < vect_size(&dict->numbered));
+ return VECT_ELEMENT(&dict->numbered, struct value, num);
+}
+
+struct value *
+val_dict_get_name(struct value_dict *dict, const char *name)
+{
+ size_t i;
+ for (i = 0; i < vect_size(&dict->named); ++i) {
+ struct named_value *element
+ = VECT_ELEMENT(&dict->named, struct named_value, i);
+ if (strcmp(element->name, name) == 0)
+ return &element->value;
+ }
+ return NULL;
+}
+
+void
+val_dict_destroy(struct value_dict *dict)
+{
+ if (dict == NULL)
+ return;
+
+ VECT_DESTROY(&dict->numbered, struct value, value_dtor, NULL);
+ VECT_DESTROY(&dict->named, struct named_value, named_value_dtor, NULL);
+}
diff --git a/value_dict.h b/value_dict.h
new file mode 100644
index 0000000..f75ee37
--- /dev/null
+++ b/value_dict.h
@@ -0,0 +1,67 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef ARGS_H
+#define ARGS_H
+
+#include "forward.h"
+#include "vect.h"
+
+/* Value dictionary is used to store actual function arguments. It
+ * supports both numbered and named arguments. */
+struct value_dict
+{
+ struct vect numbered;
+ struct vect named;
+};
+
+/* Initialize DICT. */
+void val_dict_init(struct value_dict *dict);
+
+/* Clone SOURCE into TARGET. Return 0 on success or a negative value
+ * on failure. */
+int val_dict_clone(struct value_dict *target, struct value_dict *source);
+
+/* Push next numbered value, VAL. The value is copied over and the
+ * dictionary becomes its owner, and is responsible for destroying it
+ * later. Returns 0 on success and a negative value on failure. */
+int val_dict_push_next(struct value_dict *dict, struct value *val);
+
+/* Return count of numbered arguments. */
+size_t val_dict_count(struct value_dict *dict);
+
+/* Push value VAL named NAME. See notes at val_dict_push_next about
+ * value ownership. The name is owned and freed if OWN_NAME is
+ * non-zero. */
+int val_dict_push_named(struct value_dict *dict, struct value *val,
+ const char *name, int own_name);
+
+/* Get NUM-th numbered argument, or NULL if there's not that much
+ * arguments. */
+struct value *val_dict_get_num(struct value_dict *dict, size_t num);
+
+/* Get argument named NAME, or NULL if there's no such argument. */
+struct value *val_dict_get_name(struct value_dict *dict, const char *name);
+
+/* Destroy the dictionary and all the values in it. Note that DICT
+ * itself (the pointer) is not freed. */
+void val_dict_destroy(struct value_dict *dict);
+
+#endif /* ARGS_H */
diff --git a/vect.c b/vect.c
new file mode 100644
index 0000000..f2e58b2
--- /dev/null
+++ b/vect.c
@@ -0,0 +1,136 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include "vect.h"
+
+static void *
+slot(struct vect *vec, size_t i)
+{
+ return ((char *)vec->data) + vec->elt_size * i;
+}
+
+void
+vect_init(struct vect *vec, size_t elt_size)
+{
+ *vec = (struct vect){ NULL, 0, 0, elt_size };
+}
+
+static int
+copy_elt(void *tgt, void *src, void *data)
+{
+ struct vect *target = data;
+ memcpy(tgt, src, target->elt_size);
+ return 0;
+}
+
+int
+vect_clone(struct vect *target, struct vect *source,
+ int (*clone)(void *tgt, void *src, void *data),
+ void (*dtor)(void *elt, void *data),
+ void *data)
+{
+ vect_init(target, source->elt_size);
+ if (vect_reserve(target, source->size) < 0)
+ return -1;
+
+ if (clone == NULL) {
+ assert(dtor == NULL);
+ clone = copy_elt;
+ data = target;
+ } else {
+ assert(dtor != NULL);
+ }
+
+ size_t i;
+ for (i = 0; i < source->size; ++i)
+ if (clone(slot(target, i), slot(source, i), data) < 0)
+ goto fail;
+
+ target->size = source->size;
+ return 0;
+
+fail:
+ /* N.B. destroy the elements in opposite order. */
+ if (dtor != NULL)
+ while (i-- != 0)
+ dtor(slot(target, i), data);
+ vect_destroy(target, NULL, NULL);
+ return -1;
+}
+
+int
+vect_reserve(struct vect *vec, size_t count)
+{
+ if (count > vec->allocated) {
+ size_t na = vec->allocated != 0 ? 2 * vec->allocated : 4;
+ void *n = realloc(vec->data, na * vec->elt_size);
+ if (n == NULL)
+ return -1;
+ vec->data = n;
+ vec->allocated = na;
+ }
+ assert(count <= vec->allocated);
+ return 0;
+}
+
+size_t
+vect_size(struct vect *vec)
+{
+ return vec->size;
+}
+
+int
+vect_empty(struct vect *vec)
+{
+ return vec->size == 0;
+}
+
+int
+vect_reserve_additional(struct vect *vec, size_t count)
+{
+ return vect_reserve(vec, vect_size(vec) + count);
+}
+
+int
+vect_pushback(struct vect *vec, void *eltp)
+{
+ if (vect_reserve_additional(vec, 1) < 0)
+ return -1;
+ memcpy(slot(vec, vec->size++), eltp, vec->elt_size);
+ return 0;
+}
+
+void
+vect_destroy(struct vect *vec, void (*dtor)(void *emt, void *data), void *data)
+{
+ if (vec == NULL)
+ return;
+
+ if (dtor != NULL) {
+ size_t i;
+ size_t sz = vect_size(vec);
+ for (i = 0; i < sz; ++i)
+ dtor(slot(vec, i), data);
+ }
+ free(vec->data);
+}
diff --git a/vect.h b/vect.h
new file mode 100644
index 0000000..50401bb
--- /dev/null
+++ b/vect.h
@@ -0,0 +1,125 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef VECT_H
+#define VECT_H
+
+#include <stddef.h>
+
+/* Vector is an array that can grow as needed to accommodate the data
+ * that it needs to hold. ELT_SIZE is also used as an elementary
+ * sanity check, because the array itself is not typed. */
+
+struct vect
+{
+ void *data;
+ size_t size; /* In elements. */
+ size_t allocated; /* In elements. */
+ size_t elt_size; /* In bytes. */
+};
+
+/* Initialize VEC, which will hold elements of size ELT_SIZE. */
+void vect_init(struct vect *vec, size_t elt_size);
+
+/* Initialize VECP, which will hold elements of type ELT_TYPE. */
+#define VECT_INIT(VECP, ELT_TYPE) \
+ (vect_init(VECP, sizeof(ELT_TYPE)))
+
+/* Initialize TARGET by copying over contents of vector SOURCE. If
+ * CLONE is non-NULL, it's evoked on each element, and should clone
+ * SRC into TGT. It should return 0 on success or negative value on
+ * failure. DATA is passed to CLONE verbatim. This function returns
+ * 0 on success or negative value on failure. In case of failure, if
+ * DTOR is non-NULL, it is invoked on all hitherto created elements
+ * with the same DATA. If one of CLONE, DTOR is non-NULL, then both
+ * have to be. */
+int vect_clone(struct vect *target, struct vect *source,
+ int (*clone)(void *tgt, void *src, void *data),
+ void (*dtor)(void *elt, void *data),
+ void *data);
+
+/* Destroy VEC, which holds elements of type ELT_TYPE, using DTOR. */
+#define VECT_CLONE(TGT_VEC, SRC_VEC, ELT_TYPE, CLONE, DTOR, DATA) \
+ /* xxx GCC-ism necessary to get in the safety latches. */ \
+ ({ \
+ struct vect *_source_vec = (SRC_VEC); \
+ assert(_source_vec->elt_size == sizeof(ELT_TYPE)); \
+ /* Check that callbacks are typed properly. */ \
+ void (*_dtor_callback)(ELT_TYPE *, void *) = DTOR; \
+ int (*_clone_callback)(ELT_TYPE *, \
+ ELT_TYPE *, void *) = CLONE; \
+ vect_clone((TGT_VEC), _source_vec, \
+ (int (*)(void *, void *, void *))_clone_callback, \
+ (void (*)(void *, void *))_dtor_callback, \
+ DATA); \
+ })
+
+/* Return number of elements in VEC. */
+size_t vect_size(struct vect *vec);
+
+/* Emptiness predicate. */
+int vect_empty(struct vect *vec);
+
+/* Accessor. Fetch ELT_NUM-th argument of type ELT_TYPE from the
+ * vector referenced by VECP. */
+#define VECT_ELEMENT(VECP, ELT_TYPE, ELT_NUM) \
+ (assert((VECP)->elt_size == sizeof(ELT_TYPE)), \
+ assert((ELT_NUM) < (VECP)->size), \
+ ((ELT_TYPE *)(VECP)->data) + (ELT_NUM))
+
+#define VECT_BACK(VECP, ELT_TYPE) \
+ VECT_ELEMENT(VECP, ELT_TYPE, (VECP)->size)
+
+/* Copy element referenced by ELTP to the end of VEC. The object
+ * referenced by ELTP is now owned by VECT. Returns 0 if the
+ * operation was successful, or negative value on error. */
+int vect_pushback(struct vect *vec, void *eltp);
+
+/* Copy element referenced by ELTP to the end of VEC. See
+ * vect_pushback for details. In addition, make a check whether VECP
+ * holds elements of the right size. */
+#define VECT_PUSHBACK(VECP, ELTP) \
+ (assert((VECP)->elt_size == sizeof(*(ELTP))), \
+ vect_pushback((VECP), (ELTP)))
+
+/* Make sure that VEC can hold at least COUNT elements. Return 0 on
+ * success, negative value on failure. */
+int vect_reserve(struct vect *vec, size_t count);
+
+/* Make sure that VEC can accommodate COUNT additional elements. */
+int vect_reserve_additional(struct vect *vec, size_t count);
+
+/* Destroy VEC. If DTOR is non-NULL, then it's called on each element
+ * of the vector. DATA is passed to DTOR verbatim. The memory
+ * pointed-to by VEC is not freed. */
+void vect_destroy(struct vect *vec,
+ void (*dtor)(void *emt, void *data), void *data);
+
+/* Destroy VEC, which holds elements of type ELT_TYPE, using DTOR. */
+#define VECT_DESTROY(VECP, ELT_TYPE, DTOR, DATA) \
+ do { \
+ assert((VECP)->elt_size == sizeof(ELT_TYPE)); \
+ /* Check that DTOR is typed properly. */ \
+ void (*_dtor_callback)(ELT_TYPE *, void *) = DTOR; \
+ vect_destroy((VECP), (void (*)(void *, void *))_dtor_callback, \
+ DATA); \
+ } while (0)
+
+#endif /* VECT_H */
diff --git a/zero.c b/zero.c
new file mode 100644
index 0000000..bc119ee
--- /dev/null
+++ b/zero.c
@@ -0,0 +1,105 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <error.h>
+#include <errno.h>
+
+#include "zero.h"
+#include "common.h"
+#include "type.h"
+#include "value.h"
+#include "expr.h"
+
+static int
+zero_callback_max(struct value *ret_value, struct value *lhs,
+ struct value_dict *arguments,
+ size_t max, void *data)
+{
+ size_t i;
+ for (i = 0; i < max; ++i) {
+ struct value element;
+ if (value_init_element(&element, lhs, i) < 0)
+ return -1;
+
+ int zero = value_is_zero(&element, arguments);
+
+ value_destroy(&element);
+
+ if (zero)
+ break;
+ }
+
+ struct arg_type_info *long_type = type_get_simple(ARGTYPE_LONG);
+ value_init_detached(ret_value, NULL, long_type, 0);
+ value_set_word(ret_value, i);
+ return 0;
+}
+
+/* LHS->zero(RHS). Looks for a length of zero-terminated array, but
+ * looks no further than first RHS bytes. */
+static int
+zero_callback(struct value *ret_value, struct value *lhs,
+ struct value *rhs, struct value_dict *arguments, void *data)
+{
+ long l;
+ if (value_extract_word(rhs, &l, arguments) < 0)
+ return -1;
+ if (l < 0)
+ /* It might just be a positive value >2GB, but that's
+ * not likely. */
+ report_global_error("maximum array length seems negative");
+ size_t max = (size_t)l;
+ return zero_callback_max(ret_value, lhs, arguments, max, data);
+}
+
+/* LHS->zero. Looks for a length of zero-terminated array, without
+ * limit. */
+static int
+zero1_callback(struct value *ret_value, struct value *lhs,
+ struct value_dict *arguments, void *data)
+{
+ return zero_callback_max(ret_value, lhs, arguments, (size_t)-1, data);
+}
+
+struct expr_node *
+build_zero_w_arg(struct expr_node *expr, int own)
+{
+ struct expr_node *e_z = malloc(sizeof(*e_z));
+ if (e_z == NULL)
+ return NULL;
+
+ expr_init_cb2(e_z, &zero_callback,
+ expr_self(), 0, expr, own, NULL);
+ return e_z;
+}
+
+struct expr_node *
+expr_node_zero(void)
+{
+ static struct expr_node *node = NULL;
+ if (node == NULL) {
+ node = malloc(sizeof(*node));
+ if (node == NULL)
+ error(1, errno, "malloc expr_node_zero");
+ expr_init_cb1(node, &zero1_callback,
+ expr_self(), 0, (void *)-1);
+ }
+ return node;
+}
diff --git a/zero.h b/zero.h
new file mode 100644
index 0000000..45d2771
--- /dev/null
+++ b/zero.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of ltrace.
+ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef ZERO_H
+#define ZERO_H
+
+#include "forward.h"
+
+/* This returns a pre-built "zero" node without argument. Share, but
+ don't free. */
+struct expr_node *expr_node_zero(void);
+
+/* This builds a new "zero" node with EXPR as argument. EXPR is owned
+ * by the built node if OWN. Returns NULL if something failed. */
+struct expr_node *build_zero_w_arg(struct expr_node *expr, int own);
+
+#endif /* ZERO_H */