diff --git a/ltrace-0.6.0-abi.patch b/ltrace-0.6.0-abi.patch new file mode 100644 index 0000000..ee9e813 --- /dev/null +++ b/ltrace-0.6.0-abi.patch @@ -0,0 +1,13571 @@ +diff --git a/Makefile.am b/Makefile.am +index a00c8bf..dc9091d 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -15,7 +15,6 @@ libltrace_la_SOURCES = \ + debug.c \ + demangle.c \ + dict.c \ +- display_args.c \ + ltrace-elf.c \ + execute_program.c \ + handle_event.c \ +@@ -27,7 +26,19 @@ libltrace_la_SOURCES = \ + summary.c \ + library.c \ + filter.c \ +- glob.c ++ glob.c \ ++ type.c \ ++ value.c \ ++ value_dict.c \ ++ expr.c \ ++ fetch.c \ ++ vect.c \ ++ param.c \ ++ printf.c \ ++ zero.c \ ++ lens.c \ ++ lens_default.c \ ++ lens_enum.c + + libltrace_la_LIBADD = \ + $(libelf_LIBS) \ +@@ -50,6 +61,7 @@ ltrace_LDADD = \ + + + noinst_HEADERS = \ ++ backend.h \ + common.h \ + debug.h \ + defs.h \ +@@ -62,7 +74,20 @@ noinst_HEADERS = \ + read_config_file.h \ + library.h \ + filter.h \ +- glob.h ++ glob.h \ ++ vect.h \ ++ type.h \ ++ value.h \ ++ value_dict.h \ ++ expr.h \ ++ fetch.h \ ++ vect.h \ ++ param.h \ ++ printf.h \ ++ zero.h \ ++ lens.h \ ++ lens_default.h \ ++ lens_enum.h + + dist_man1_MANS = \ + ltrace.1 +diff --git a/backend.h b/backend.h +new file mode 100644 +index 0000000..08306e1 +--- /dev/null ++++ b/backend.h +@@ -0,0 +1,250 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef BACKEND_H ++#define BACKEND_H ++ ++#include "forward.h" ++#include ++ ++enum process_status { ++ ps_invalid, /* Failure. */ ++ ps_stop, /* Job-control stop. */ ++ ps_tracing_stop, ++ ps_sleeping, ++ ps_zombie, ++ ps_other, /* Necessary other states can be added as needed. */ ++}; ++ ++typedef void *target_address_t; ++ ++/* ++ * This file contains documentation of back end interface. Some of ++ * these may be implemented on an OS level (i.e. they are the same ++ * e.g. on all Linux architectures), some may differ per architecture ++ * on the same OS (e.g. a way to insert a breakpoint into the process ++ * image is a likely candidate). ++ */ ++ ++/* Convert a PID to a path to the corresponding binary. */ ++char *pid2name(pid_t pid); ++ ++/* Given a PID, find a leader of thread group. */ ++pid_t process_leader(pid_t pid); ++ ++/* Given a PID of leader thread, fill in PIDs of all the tasks. The ++ * function will initialize the pointer *RET_TASKS to a ++ * newly-allocated array, and will store number of elements in that ++ * array to *RET_N. You have to free that buffer when you don't need ++ * it anymore. */ ++int process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n); ++ ++/* Answer whether the process PID is stopped. Returns 0 when not ++ * stopped, 1 when stopped, or -1 when there was an error. */ ++int process_stopped(pid_t pid); ++ ++/* Answer a status of the task PID. See enum process_status. */ ++enum process_status process_status(pid_t pid); ++ ++/* Wait for PID to be ready for tracing. */ ++int wait_for_proc(pid_t pid); ++ ++/* Send a signal SIG to the task PID. */ ++int task_kill(pid_t pid, int sig); ++ ++/* Called after PID is attached, but before it is continued. */ ++void trace_set_options(struct Process *proc); ++ ++/* Called after ltrace forks. Should attach the newly created child, ++ * in whose context this function is called. */ ++void trace_me(void); ++ ++/* Called when ltrace needs to attach to PID, such as when it attaches ++ * to a running process, whose PID is given on the command line. */ ++int trace_pid(pid_t pid); ++ ++/* Stop tracing PID. */ ++void untrace_pid(pid_t pid); ++ ++/* The back end may need to store arbitrary data to a process. This ++ * is a place where it can initialize PROC->arch_dep. XXX this should ++ * be dropped in favor of arhc_process_init on pmachata/libs. */ ++void get_arch_dep(struct Process *proc); ++ ++/* Return current instruction pointer of PROC. ++ * ++ * XXX note that the IP must fit into an arch pointer. This prevents ++ * us to use 32-bit ltrace to trace 64-bit process, even on arches ++ * that would otherwise support this. Above we have a definition of ++ * target_address_t. This should be converted to an integral type and ++ * used for target addresses throughout. */ ++void *get_instruction_pointer(struct Process *proc); ++ ++/* Set instruction pointer of PROC to ADDR. XXX see above. */ ++void set_instruction_pointer(struct Process *proc, void *addr); ++ ++/* Return current stack pointer of PROC. XXX see above. */ ++void *get_stack_pointer(struct Process *proc); ++ ++/* Find and return caller address, i.e. the address where the current ++ * function returns. */ ++void *get_return_addr(struct Process *proc, void *stack_pointer); ++ ++/* Adjust PROC so that when the current function returns, it returns ++ * to ADDR. */ ++void set_return_addr(struct Process *proc, void *addr); ++ ++/* Enable breakpoint SBP in process PROC. */ ++void enable_breakpoint(struct Process *proc, struct breakpoint *sbp); ++ ++/* Disable breakpoint SBP in process PROC. */ ++void disable_breakpoint(struct Process *proc, struct breakpoint *sbp); ++ ++/* Determine whether the event that we have just seen (and that is ++ * recorded in STATUS) was a syscall. If it was, return 1. If it was ++ * a return from syscall, return 2. In both cases, set *SYSNUM to the ++ * number of said syscall. If it wasn't a syscall, return 0. If ++ * there was an error, return -1. */ ++int syscall_p(struct Process *proc, int status, int *sysnum); ++ ++/* Continue execution of the process with given PID. */ ++void continue_process(pid_t pid); ++ ++/* Called after we received a signal SIGNUM. Should do whatever ++ * book-keeping is necessary and continue the process if ++ * necessary. */ ++void continue_after_signal(pid_t pid, int signum); ++ ++/* Called after we received a system call SYSNUM. RET_P is 0 if this ++ * is system call, otherwise it's return from a system call. The ++ * callback should do whatever book-keeping is necessary and continue ++ * the process if necessary. */ ++void continue_after_syscall(struct Process *proc, int sysnum, int ret_p); ++ ++/* Called after we hit a breakpoint SBP. Should do whatever ++ * book-keeping is necessary and then continue the process. */ ++void continue_after_breakpoint(struct Process *proc, struct breakpoint *sbp); ++ ++/* Called after we received a vfork. Should do whatever book-keeping ++ * is necessary and continue the process if necessary. N.B. right ++ * now, with Linux/GNU the only back end, this is not necessary. I ++ * imagine other systems may be different. */ ++void continue_after_vfork(struct Process *proc); ++ ++/* Called when trace_me or primary trace_pid fail. This may plug in ++ * any platform-specific knowledge of why it could be so. */ ++void trace_fail_warning(pid_t pid); ++ ++/* A pair of functions called to initiate a detachment request when ++ * ltrace is about to exit. Their job is to undo any effects that ++ * tracing had and eventually detach process, perhaps by way of ++ * installing a process handler. ++ * ++ * OS_LTRACE_EXITING_SIGHANDLER is called from a signal handler ++ * context right after the signal was captured. It returns 1 if the ++ * request was handled or 0 if it wasn't. ++ * ++ * If the call to OS_LTRACE_EXITING_SIGHANDLER didn't handle the ++ * request, OS_LTRACE_EXITING is called when the next event is ++ * generated. Therefore it's called in "safe" context, without ++ * re-entrancy concerns, but it's only called after an even is ++ * generated. */ ++int os_ltrace_exiting_sighandler(void); ++void os_ltrace_exiting(void); ++ ++/* Should copy COUNT bytes from address ADDR of process PROC to local ++ * buffer BUF. */ ++size_t umovebytes (struct Process *proc, void *addr, void *buf, size_t count); ++ ++/* Find out an address of symbol SYM in process PROC, and return. ++ * Returning NULL delays breakpoint insertion and enables heaps of ++ * arch-specific black magic that we should clean up some day. ++ * ++ * XXX the same points as for get_instruction_pointer apply. */ ++void *sym2addr(struct Process *proc, struct library_symbol *sym); ++ ++/* Called at some point after we have attached to PROC. This callback ++ * should insert an introspection breakpoint for handling dynamic ++ * linker library loads. */ ++int linkmap_init(struct Process *proc, target_address_t dyn_addr); ++ ++/* Called for breakpoints defined over an artificial symbol "". This ++ * can be used (like it is on Linux/GNU) to add more breakpoints ++ * because a dlopen'ed library was mapped in. ++ * ++ * XXX we should somehow clean up this interface. For starters, ++ * breakpoints should have their own handler callbacks, so that we can ++ * generalize this to e.g. systemtap SDT probes. linkmap_init could ++ * perhaps be rolled into some other process init callback. */ ++void arch_check_dbg(struct Process *proc); ++ ++/* This should produce and return the next event of one of the traced ++ * processes. The returned pointer will not be freed by the core and ++ * should be either statically allocated, or the management should be ++ * done some other way. */ ++struct Event *next_event(void); ++ ++/* Called when process PROC was removed. */ ++void process_removed(struct Process *proc); ++ ++int arch_elf_init(struct ltelf *lte, struct library *lib); ++void arch_elf_destroy(struct ltelf *lte); ++ ++enum plt_status { ++ plt_fail, ++ plt_ok, ++ plt_default, ++}; ++ ++enum plt_status arch_elf_add_plt_entry(struct Process *p, struct ltelf *l, ++ const char *n, GElf_Rela *r, size_t i, ++ struct library_symbol **ret); ++ ++int arch_breakpoint_init(struct Process *proc, struct breakpoint *sbp); ++void arch_breakpoint_destroy(struct breakpoint *sbp); ++int arch_breakpoint_clone(struct breakpoint *retp, struct breakpoint *sbp); ++ ++void arch_library_init(struct library *lib); ++void arch_library_destroy(struct library *lib); ++void arch_library_clone(struct library *retp, struct library *lib); ++ ++int arch_library_symbol_init(struct library_symbol *libsym); ++void arch_library_symbol_destroy(struct library_symbol *libsym); ++int arch_library_symbol_clone(struct library_symbol *retp, ++ struct library_symbol *libsym); ++ ++int arch_process_init(struct Process *proc); ++void arch_process_destroy(struct Process *proc); ++int arch_process_clone(struct Process *retp, struct Process *proc); ++int arch_process_exec(struct Process *proc); ++ ++/* This should extract entry point address and interpreter (dynamic ++ * linker) bias if possible. Returns 0 if there were no errors, -1 ++ * otherwise. Sets *ENTRYP and *INTERP_BIASP to non-zero values if ++ * the corresponding value is known. Unknown values are set to 0. */ ++int process_get_entry(struct Process *proc, ++ target_address_t *entryp, ++ target_address_t *interp_biasp); ++ ++/* This is called after the dynamic linker is done with the ++ * process startup. */ ++void arch_dynlink_done(struct Process *proc); ++ ++#endif /* BACKEND_H */ +diff --git a/breakpoints.c b/breakpoints.c +index 93ae244..8dc09df 100644 +--- a/breakpoints.c ++++ b/breakpoints.c +@@ -1,18 +1,21 @@ + #include "config.h" + +-#include +-#include + #include + #include ++#include ++#include ++#include + + #ifdef __powerpc__ + #include + #endif + ++#include "backend.h" + #include "breakpoint.h" +-#include "common.h" +-#include "proc.h" ++#include "debug.h" + #include "library.h" ++#include "ltrace-elf.h" ++#include "proc.h" + + #ifndef ARCH_HAVE_TRANSLATE_ADDRESS + int +diff --git a/common.h b/common.h +index 7fffa76..959715a 100644 +--- a/common.h ++++ b/common.h +@@ -1,3 +1,27 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2010 Joe Damato ++ * Copyright (C) 2009 Juan Cespedes ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef COMMON_H ++#define COMMON_H + #include + + #include +@@ -15,6 +37,7 @@ + #include "ltrace-elf.h" + #include "read_config_file.h" + #include "proc.h" ++#include "forward.h" + + #if defined HAVE_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 +-#include +-#include +-#include +-#include +- +-#include "common.h" +-#include "proc.h" +- +-static int display_char(int what); +-static int display_string(enum tof type, Process *proc, +- void* addr, size_t maxlen); +-static int display_value(enum tof type, Process *proc, +- long value, arg_type_info *info, +- void *st, arg_type_info* st_info); +-static int display_unknown(enum tof type, Process *proc, long value); +-static int display_format(enum tof type, Process *proc, int arg_num); +- +-static size_t string_maxlength = INT_MAX; +-static size_t array_maxlength = INT_MAX; +- +-static long +-get_length(enum tof type, Process *proc, int len_spec, +- void *st, arg_type_info* st_info) { +- long len; +- arg_type_info info; +- +- if (len_spec > 0) +- return len_spec; +- if (type == LT_TOF_STRUCT) { +- umovelong (proc, st + st_info->u.struct_info.offset[-len_spec-1], +- &len, st_info->u.struct_info.fields[-len_spec-1]); +- return len; +- } +- +- info.type = ARGTYPE_INT; +- return gimme_arg(type, proc, -len_spec-1, &info); +-} +- +-static int +-display_ptrto(enum tof type, Process *proc, long item, +- arg_type_info * info, +- void *st, arg_type_info* st_info) { +- arg_type_info temp; +- temp.type = ARGTYPE_POINTER; +- temp.u.ptr_info.info = info; +- return display_value(type, proc, item, &temp, st, st_info); +-} +- +-/* +- * addr - A pointer to the first element of the array +- * +- * The function name is used to indicate that we're not actually +- * looking at an 'array', which is a contiguous region of memory +- * containing a sequence of elements of some type; instead, we have a +- * pointer to that region of memory. +- */ +-static int +-display_arrayptr(enum tof type, Process *proc, +- void *addr, arg_type_info * info, +- void *st, arg_type_info* st_info) { +- int len = 0; +- size_t i; +- size_t array_len; +- +- if (addr == NULL) +- return fprintf(options.output, "NULL"); +- +- array_len = get_length(type, proc, info->u.array_info.len_spec, +- st, st_info); +- len += fprintf(options.output, "[ "); +- for (i = 0; i < options.arraylen && i < array_maxlength && i < array_len; i++) { +- arg_type_info *elt_type = info->u.array_info.elt_type; +- size_t elt_size = info->u.array_info.elt_size; +- if (i != 0) +- len += fprintf(options.output, ", "); +- if (options.debug) +- len += fprintf(options.output, "%p=", addr); +- len += +- display_ptrto(type, proc, (long) addr, elt_type, st, st_info); +- addr += elt_size; +- } +- if (i < array_len) +- len += fprintf(options.output, "..."); +- len += fprintf(options.output, " ]"); +- return len; +-} +- +-/* addr - A pointer to the beginning of the memory region occupied by +- * the struct (aka a pointer to the struct) +- */ +-static int +-display_structptr(enum tof type, Process *proc, +- void *addr, arg_type_info * info) { +- int i; +- arg_type_info *field; +- int len = 0; +- +- if (addr == NULL) +- return fprintf(options.output, "NULL"); +- +- len += fprintf(options.output, "{ "); +- for (i = 0; (field = info->u.struct_info.fields[i]) != NULL; i++) { +- if (i != 0) +- len += fprintf(options.output, ", "); +- if (options.debug) +- len += +- fprintf(options.output, "%p=", +- addr + info->u.struct_info.offset[i]); +- len += +- display_ptrto(LT_TOF_STRUCT, proc, +- (long) addr + info->u.struct_info.offset[i], +- field, addr, info); +- } +- len += fprintf(options.output, " }"); +- +- return len; +-} +- +-static int +-display_pointer(enum tof type, Process *proc, long value, +- arg_type_info * info, +- void *st, arg_type_info* st_info) { +- long pointed_to; +- arg_type_info *inner = info->u.ptr_info.info; +- +- if (inner->type == ARGTYPE_ARRAY) { +- return display_arrayptr(type, proc, (void*) value, inner, +- st, st_info); +- } else if (inner->type == ARGTYPE_STRUCT) { +- return display_structptr(type, proc, (void *) value, inner); +- } else { +- if (value == 0) +- return fprintf(options.output, "NULL"); +- else if (umovelong (proc, (void *) value, &pointed_to, +- info->u.ptr_info.info) < 0) +- return fprintf(options.output, "?"); +- else +- return display_value(type, proc, pointed_to, inner, +- st, st_info); +- } +-} +- +-static int +-display_enum(enum tof type, Process *proc, +- arg_type_info* info, long value) { +- size_t ii; +- for (ii = 0; ii < info->u.enum_info.entries; ++ii) { +- if (info->u.enum_info.values[ii] == value) +- return fprintf(options.output, "%s", info->u.enum_info.keys[ii]); +- } +- +- return display_unknown(type, proc, value); +-} +- +-/* Args: +- type - syscall or shared library function or memory +- proc - information about the traced process +- value - the value to display +- info - the description of the type to display +- st - if the current value is a struct member, the address of the struct +- st_info - type of the above struct +- +- Those last two parameters are used for structs containing arrays or +- strings whose length is given by another structure element. +-*/ +-int +-display_value(enum tof type, Process *proc, +- long value, arg_type_info *info, +- void *st, arg_type_info* st_info) { +- int tmp; +- +- switch (info->type) { +- case ARGTYPE_VOID: +- return 0; +- case ARGTYPE_INT: +- return fprintf(options.output, "%d", (int) value); +- case ARGTYPE_UINT: +- return fprintf(options.output, "%u", (unsigned) value); +- case ARGTYPE_LONG: +- if (proc->mask_32bit) +- return fprintf(options.output, "%d", (int) value); +- else +- return fprintf(options.output, "%ld", value); +- case ARGTYPE_ULONG: +- if (proc->mask_32bit) +- return fprintf(options.output, "%u", (unsigned) value); +- else +- return fprintf(options.output, "%lu", (unsigned long) value); +- case ARGTYPE_OCTAL: +- return fprintf(options.output, "0%o", (unsigned) value); +- case ARGTYPE_CHAR: +- tmp = fprintf(options.output, "'"); +- tmp += display_char(value == -1 ? value : (char) value); +- tmp += fprintf(options.output, "'"); +- return tmp; +- case ARGTYPE_SHORT: +- return fprintf(options.output, "%hd", (short) value); +- case ARGTYPE_USHORT: +- return fprintf(options.output, "%hu", (unsigned short) value); +- case ARGTYPE_FLOAT: { +- union { long l; float f; double d; } cvt; +- cvt.l = value; +- return fprintf(options.output, "%f", cvt.f); +- } +- case ARGTYPE_DOUBLE: { +- union { long l; float f; double d; } cvt; +- cvt.l = value; +- return fprintf(options.output, "%lf", cvt.d); +- } +- case ARGTYPE_ADDR: +- if (!value) +- return fprintf(options.output, "NULL"); +- else +- return fprintf(options.output, "0x%08lx", value); +- case ARGTYPE_FORMAT: +- fprintf(stderr, "Should never encounter a format anywhere but at the top level (for now?)\n"); +- exit(1); +- case ARGTYPE_STRING: +- return display_string(type, proc, (void*) value, +- string_maxlength); +- case ARGTYPE_STRING_N: +- return display_string(type, proc, (void*) value, +- get_length(type, proc, +- info->u.string_n_info.size_spec, st, st_info)); +- case ARGTYPE_ARRAY: +- return fprintf(options.output, ""); +- case ARGTYPE_ENUM: +- return display_enum(type, proc, info, value); +- case ARGTYPE_STRUCT: +- return fprintf(options.output, ""); +- case ARGTYPE_POINTER: +- return display_pointer(type, proc, value, info, +- st, st_info); +- case ARGTYPE_UNKNOWN: +- default: +- return display_unknown(type, proc, value); +- } +-} +- +-int +-display_arg(enum tof type, Process *proc, int arg_num, arg_type_info * info) { +- long arg; +- +- if (info->type == ARGTYPE_VOID) { +- return 0; +- } else if (info->type == ARGTYPE_FORMAT) { +- return display_format(type, proc, arg_num); +- } else { +- arg = gimme_arg(type, proc, arg_num, info); +- return display_value(type, proc, arg, info, NULL, NULL); +- } +-} +- +-static int +-display_char(int what) { +- switch (what) { +- case -1: +- return fprintf(options.output, "EOF"); +- case '\r': +- return fprintf(options.output, "\\r"); +- case '\n': +- return fprintf(options.output, "\\n"); +- case '\t': +- return fprintf(options.output, "\\t"); +- case '\b': +- return fprintf(options.output, "\\b"); +- case '\\': +- return fprintf(options.output, "\\\\"); +- default: +- if (isprint(what)) { +- return fprintf(options.output, "%c", what); +- } else { +- return fprintf(options.output, "\\%03o", (unsigned char)what); +- } +- } +-} +- +-#define MIN(a,b) (((a)<(b)) ? (a) : (b)) +- +-static int +-display_string(enum tof type, Process *proc, void *addr, +- size_t maxlength) { +- unsigned char *str1; +- size_t i; +- int len = 0; +- +- if (!addr) { +- return fprintf(options.output, "NULL"); +- } +- +- str1 = malloc(MIN(options.strlen, maxlength) + 3); +- if (!str1) { +- return fprintf(options.output, "???"); +- } +- umovestr(proc, addr, MIN(options.strlen, maxlength) + 1, str1); +- len = fprintf(options.output, "\""); +- for (i = 0; i < MIN(options.strlen, maxlength); i++) { +- if (str1[i]) { +- len += display_char(str1[i]); +- } else { +- break; +- } +- } +- len += fprintf(options.output, "\""); +- if (str1[i] && (options.strlen <= maxlength)) { +- len += fprintf(options.output, "..."); +- } +- free(str1); +- return len; +-} +- +-static int +-display_unknown(enum tof type, Process *proc, long value) { +- if (proc->mask_32bit) { +- if ((int)value < 1000000 && (int)value > -1000000) +- return fprintf(options.output, "%d", (int)value); +- else +- return fprintf(options.output, "%p", (void *)value); +- } else if (value < 1000000 && value > -1000000) { +- return fprintf(options.output, "%ld", value); +- } else { +- return fprintf(options.output, "%p", (void *)value); +- } +-} +- +-static int +-display_format(enum tof type, Process *proc, int arg_num) { +- void *addr; +- unsigned char *str1; +- int i; +- size_t len = 0; +- arg_type_info info; +- +- info.type = ARGTYPE_POINTER; +- addr = (void *)gimme_arg(type, proc, arg_num, &info); +- if (!addr) { +- return fprintf(options.output, "NULL"); +- } +- +- str1 = malloc(MIN(options.strlen, string_maxlength) + 3); +- if (!str1) { +- return fprintf(options.output, "???"); +- } +- umovestr(proc, addr, MIN(options.strlen, string_maxlength) + 1, str1); +- len = fprintf(options.output, "\""); +- for (i = 0; len < MIN(options.strlen, string_maxlength) + 1; i++) { +- if (str1[i]) { +- len += display_char(str1[i]); +- } else { +- break; +- } +- } +- len += fprintf(options.output, "\""); +- if (str1[i] && (options.strlen <= string_maxlength)) { +- len += fprintf(options.output, "..."); +- } +- for (i = 0; str1[i]; i++) { +- if (str1[i] == '%') { +- int is_long = 0; +- while (1) { +- unsigned char c = str1[++i]; +- if (c == '%') { +- break; +- } else if (!c) { +- break; +- } else if (strchr("lzZtj", c)) { +- is_long++; +- if (c == 'j') +- is_long++; +- if (is_long > 1 +- && (sizeof(long) < sizeof(long long) +- || proc->mask_32bit)) { +- len += fprintf(options.output, ", ..."); +- str1[i + 1] = '\0'; +- break; +- } +- } else if (c == 'd' || c == 'i') { +- info.type = ARGTYPE_LONG; +- if (!is_long || proc->mask_32bit) +- len += +- fprintf(options.output, ", %d", +- (int)gimme_arg(type, proc, ++arg_num, &info)); +- else +- len += +- fprintf(options.output, ", %ld", +- gimme_arg(type, proc, ++arg_num, &info)); +- break; +- } else if (c == 'u') { +- info.type = ARGTYPE_LONG; +- if (!is_long || proc->mask_32bit) +- len += +- fprintf(options.output, ", %u", +- (int)gimme_arg(type, proc, ++arg_num, &info)); +- else +- len += +- fprintf(options.output, ", %lu", +- gimme_arg(type, proc, ++arg_num, &info)); +- break; +- } else if (c == 'o') { +- info.type = ARGTYPE_LONG; +- if (!is_long || proc->mask_32bit) +- len += +- fprintf(options.output, ", 0%o", +- (int)gimme_arg(type, proc, ++arg_num, &info)); +- else +- len += +- fprintf(options.output, ", 0%lo", +- gimme_arg(type, proc, ++arg_num, &info)); +- break; +- } else if (c == 'x' || c == 'X') { +- info.type = ARGTYPE_LONG; +- if (!is_long || proc->mask_32bit) +- len += +- fprintf(options.output, ", %#x", +- (int)gimme_arg(type, proc, ++arg_num, &info)); +- else +- len += +- fprintf(options.output, ", %#lx", +- gimme_arg(type, proc, ++arg_num, &info)); +- break; +- } else if (strchr("eEfFgGaACS", c) +- || (is_long +- && (c == 'c' || c == 's'))) { +- len += fprintf(options.output, ", ..."); +- str1[i + 1] = '\0'; +- break; +- } else if (c == 'c') { +- info.type = ARGTYPE_LONG; +- len += fprintf(options.output, ", '"); +- len += +- display_char((int) +- gimme_arg(type, proc, ++arg_num, &info)); +- len += fprintf(options.output, "'"); +- break; +- } else if (c == 's') { +- info.type = ARGTYPE_POINTER; +- len += fprintf(options.output, ", "); +- len += +- display_string(type, proc, +- (void *)gimme_arg(type, proc, ++arg_num, &info), +- string_maxlength); +- break; +- } else if (c == 'p' || c == 'n') { +- info.type = ARGTYPE_POINTER; +- len += +- fprintf(options.output, ", %p", +- (void *)gimme_arg(type, proc, ++arg_num, &info)); +- break; +- } else if (c == '*') { +- info.type = ARGTYPE_LONG; +- len += +- fprintf(options.output, ", %d", +- (int)gimme_arg(type, proc, ++arg_num, &info)); +- } +- } +- } +- } +- free(str1); +- return len; +-} +diff --git a/etc/ltrace.conf b/etc/ltrace.conf +index 3caf6b7..6513752 100644 +--- a/etc/ltrace.conf ++++ b/etc/ltrace.conf +@@ -101,7 +101,7 @@ string inet_ntoa(addr); ; It isn't an ADDR but an hexa number... + addr inet_addr(string); + + ; bfd.h +-void bfd_init(void); ++void bfd_init(); + int bfd_set_default_target(string); + addr bfd_scan_vma(string, addr, int); + addr bfd_openr(string,string); +@@ -110,9 +110,9 @@ int bfd_check_format(addr,int); + ; ctype.h + char tolower(char); + char toupper(char); +-addr __ctype_b_loc(void); +-addr __ctype_tolower_loc(void); +-addr __ctype_toupper_loc(void); ++addr __ctype_b_loc(); ++addr __ctype_tolower_loc(); ++addr __ctype_toupper_loc(); + + ; curses.h + int waddch(addr, char); +@@ -129,12 +129,12 @@ addr readdir64(addr); + + ; dlfcn.h + addr dlopen(string, int); +-string dlerror(void); ++string dlerror(); + addr dlsym(addr, string); + int dlclose(addr); + + ; errno.h +-addr __errno_location(void); ++addr __errno_location(); + + ; fcntl.h + int open(string,int,octal); ; WARNING: 3rd argument may not be there +@@ -148,10 +148,10 @@ int getopt_long(int,addr,string,addr,int*); + int getopt_long_only(int,addr,string,addr,addr); + + ; grp.h +-void endgrent(void); ++void endgrent(); + addr getgrnam(string); +-void setgrent(void); +-addr getgrent(void); ++void setgrent(); ++addr getgrent(); + + ; libintl.h + string __dcgettext(string,string,int); +@@ -166,8 +166,8 @@ int _IO_putc(char,file); + string setlocale(int, string); + + ; mcheck.h +-void mtrace(void); +-void muntrace(void); ++void mtrace(); ++void muntrace(); + + ; mntent.h + int endmntent(file); +@@ -187,28 +187,28 @@ long mq_receive(int, +string0, ulong, addr); + long mq_timedreceive(int, +string0, ulong, addr, addr); + + ; netdb.h +-void endhostent(void); +-void endnetent(void); +-void endnetgrent(void); +-void endprotoent(void); +-void endservent(void); ++void endhostent(); ++void endnetent(); ++void endnetgrent(); ++void endprotoent(); ++void endservent(); + void freeaddrinfo(addr); + string gai_strerror(int); + int getaddrinfo(string, string, addr, addr); + addr gethostbyaddr(string, uint, int); + addr gethostbyname(string); +-addr gethostent(void); ++addr gethostent(); + int getnameinfo(addr, uint, string, uint, string, uint, uint); + addr getnetbyaddr(uint, int); + addr getnetbyname(string); +-addr getnetent(void); ++addr getnetent(); + int getnetgrent(addr, addr, addr); + addr getprotobyname(string); + addr getprotobynumber(int); +-addr getprotoent(void); ++addr getprotoent(); + addr getservbyname(string, string); + addr getservbyport(int, string); +-addr getservent(void); ++addr getservent(); + void herror(string); + string hstrerror(int); + int rcmd(addr, ushort, string, string, string, addr); +@@ -237,9 +237,9 @@ int pcap_compile(addr, addr, string, int, addr); + + ; pwd.h + string getpass(string); +-void endpwent(void); ++void endpwent(); + addr getpwnam(string); +-void setpwent(void); ++void setpwent(); + + ; readline/readline.h + string readline(string); +@@ -305,7 +305,7 @@ int setenv(string,string,int); + void unsetenv(string); + addr malloc(ulong); + void qsort(addr,ulong,ulong,addr); +-int random(void); ++int random(); + addr realloc(addr,ulong); + void srandom(uint); + int system(string); +@@ -364,7 +364,7 @@ int uname(addr); + int statfs(string,addr); + + ; syslog.h +-void closelog(void); ++void closelog(); + void openlog(string,int,int); + void syslog(int,format); + +@@ -395,21 +395,21 @@ int dup2(int,int); + int execlp(string,string,addr,addr,addr); + int execv(string,addr); + int fchdir(int); +-int fork(void); ++int fork(); + int ftruncate(int,ulong); + string2 getcwd(addr,ulong); + int getdomainname(+string2,ulong); +-int geteuid(void); +-int getegid(void); +-int getgid(void); ++int geteuid(); ++int getegid(); ++int getgid(); + int gethostname(+string2,ulong); +-string getlogin(void); ++string getlogin(); + int getopt(int,addr,string); +-int getpid(void); +-int getppid(void); +-int getuid(void); +-int getpgrp(void); +-int setpgrp(void); ++int getpid(); ++int getppid(); ++int getuid(); ++int getpgrp(); ++int setpgrp(); + int getpgid(int); + int isatty(int); + int link(string,string); +@@ -424,21 +424,21 @@ int setreuid(uint, uint); + int setuid(int); + uint sleep(uint); + int symlink(string,string); +-int sync(void); ++int sync(); + int truncate(string,ulong); + string ttyname(int); + int unlink(string); + void usleep(uint); + long write(int, string3, ulong); + addr sbrk(long); +-int getpagesize(void); ++int getpagesize(); + long lseek(int,long,int); + int pipe(addr); + + ; utmp.h +-void endutent(void); +-addr getutent(void); +-void setutent(void); ++void endutent(); ++addr getutent(); ++void setutent(); + + ; wchar.h + int fwide(addr, int); +@@ -480,7 +480,7 @@ int acl_set_qualifier(addr,addr); + int acl_set_tag_type(addr,int); + int acl_size(addr); + string acl_to_text(addr,addr); +-itn acl_valid(addr); ++int acl_valid(addr); + + ; acl/libacl.h + int acl_check(addr,addr); +@@ -500,9 +500,9 @@ int SYS_close(int); + int SYS_execve(string,addr,addr); + void SYS_exit(int); + void SYS_exit_group(int); +-int SYS_fork(void); ++int SYS_fork(); + int SYS_getcwd(+string2,ulong); +-int SYS_getpid(void); ++int SYS_getpid(); + ;addr SYS_mmap(addr,ulong,int,int,int,long); + int SYS_munmap(addr,ulong); + int SYS_open(string,int,octal); +@@ -512,7 +512,7 @@ int SYS_stat(string,addr); + octal SYS_umask(octal); + int SYS_uname(addr); + long SYS_write(int,string3,ulong); +-int SYS_sync(void); ++int SYS_sync(); + int SYS_setxattr(string,string,addr,uint,int); + int SYS_lsetxattr(string,string,addr,uint,int); + int SYS_fsetxattr(int,string,addr,uint,int); +@@ -545,16 +545,16 @@ int SYS_gettimeofday(addr,addr); + int SYS_settimeofday(addr,addr); + int SYS_setfsgid(int); + int SYS_setfsuid(int); +-int SYS_getuid(void); ++int SYS_getuid(); + int SYS_setuid(int); +-int SYS_getgid(void); ++int SYS_getgid(); + int SYS_setgid(int); + int SYS_getsid(int); + int SYS_setsid(int); + int SYS_setreuid(int,int); + int SYS_setregid(int,int); +-int SYS_geteuid(void); +-int SYS_getegid(void); ++int SYS_geteuid(); ++int SYS_getegid(); + int SYS_setpgid(int,int); + int SYS_getresuid(addr,addr,addr); + int SYS_setresuid(int,int,int); +@@ -592,7 +592,7 @@ int SYS_utime(string,addr); + long SYS_lseek(int,long,int); + addr SYS_signal(int,addr); + int SYS_sigaction(int,addr,addr); +-int SYS_pause(void); ++int SYS_pause(); + int SYS_sigpending(addr); + int SYS_sigprocmask(int,addr,addr); + int SYS_sigqueue(int,int,addr); +diff --git a/execute_program.c b/execute_program.c +index 55df205..f469b2f 100644 +--- a/execute_program.c ++++ b/execute_program.c +@@ -14,7 +14,9 @@ + #include + #include + +-#include "common.h" ++#include "backend.h" ++#include "options.h" ++#include "debug.h" + + static void + change_uid(const char * command) +diff --git a/expr.c b/expr.c +new file mode 100644 +index 0000000..c0ebcef +--- /dev/null ++++ b/expr.c +@@ -0,0 +1,337 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "expr.h" ++ ++static void ++expr_init_common(struct expr_node *node, enum expr_node_kind kind) ++{ ++ node->kind = kind; ++ node->lhs = NULL; ++ node->own_lhs = 0; ++ memset(&node->u, 0, sizeof(node->u)); ++} ++ ++void ++expr_init_self(struct expr_node *node) ++{ ++ expr_init_common(node, EXPR_OP_SELF); ++} ++ ++void ++expr_init_named(struct expr_node *node, ++ const char *name, int own_name) ++{ ++ expr_init_common(node, EXPR_OP_NAMED); ++ node->u.name.s = name; ++ node->u.name.own = own_name; ++} ++ ++void ++expr_init_argno(struct expr_node *node, size_t num) ++{ ++ expr_init_common(node, EXPR_OP_ARGNO); ++ node->u.num = num; ++} ++ ++void ++expr_init_const(struct expr_node *node, struct value *val) ++{ ++ expr_init_common(node, EXPR_OP_CONST); ++ node->u.value = *val; ++} ++ ++void ++expr_init_const_word(struct expr_node *node, long l, ++ struct arg_type_info *type, int own_type) ++{ ++ struct value val; ++ value_init_detached(&val, NULL, type, own_type); ++ value_set_word(&val, l); ++ expr_init_const(node, &val); ++} ++ ++void ++expr_init_index(struct expr_node *node, ++ struct expr_node *lhs, int own_lhs, ++ struct expr_node *rhs, int own_rhs) ++{ ++ expr_init_common(node, EXPR_OP_INDEX); ++ node->lhs = lhs; ++ node->own_lhs = own_lhs; ++ node->u.node.n = rhs; ++ node->u.node.own = own_rhs; ++} ++ ++void ++expr_init_up(struct expr_node *node, struct expr_node *lhs, int own_lhs) ++{ ++ assert(lhs != NULL); ++ expr_init_common(node, EXPR_OP_UP); ++ node->lhs = lhs; ++ node->own_lhs = own_lhs; ++} ++ ++void ++expr_init_cb1(struct expr_node *node, ++ int (*cb)(struct value *ret_value, struct value *value, ++ struct value_dict *arguments, void *data), ++ struct expr_node *lhs, int own_lhs, void *data) ++{ ++ expr_init_common(node, EXPR_OP_CALL1); ++ node->lhs = lhs; ++ node->own_lhs = own_lhs; ++ node->u.call.u.cb1 = cb; ++ node->u.call.data = data; ++} ++ ++void ++expr_init_cb2(struct expr_node *node, ++ int (*cb)(struct value *ret_value, ++ struct value *lhs, struct value *rhs, ++ struct value_dict *arguments, void *data), ++ struct expr_node *lhs, int own_lhs, ++ struct expr_node *rhs, int own_rhs, void *data) ++{ ++ expr_init_common(node, EXPR_OP_CALL2); ++ node->lhs = lhs; ++ node->own_lhs = own_lhs; ++ node->u.call.rhs = rhs; ++ node->u.call.own_rhs = own_rhs; ++ node->u.call.u.cb2 = cb; ++ node->u.call.data = data; ++} ++ ++static void ++release_lhs(struct expr_node *node) ++{ ++ if (node->own_lhs) ++ expr_destroy(node->lhs); ++} ++ ++void ++expr_destroy(struct expr_node *node) ++{ ++ if (node == NULL) ++ return; ++ ++ switch (node->kind) { ++ case EXPR_OP_ARGNO: ++ case EXPR_OP_SELF: ++ return; ++ ++ case EXPR_OP_CONST: ++ value_destroy(&node->u.value); ++ return; ++ ++ case EXPR_OP_NAMED: ++ if (node->u.name.own) ++ free((char *)node->u.name.s); ++ return; ++ ++ case EXPR_OP_INDEX: ++ release_lhs(node); ++ if (node->u.node.own) ++ expr_destroy(node->u.node.n); ++ return; ++ ++ case EXPR_OP_CALL2: ++ if (node->u.call.own_rhs) ++ expr_destroy(node->u.call.rhs); ++ case EXPR_OP_UP: ++ case EXPR_OP_CALL1: ++ release_lhs(node); ++ return; ++ } ++ ++ assert(!"Invalid value of node kind"); ++ abort(); ++} ++ ++int ++expr_is_compile_constant(struct expr_node *node) ++{ ++ return node->kind == EXPR_OP_CONST; ++} ++ ++static int ++eval_up(struct expr_node *node, struct value *context, ++ struct value_dict *arguments, struct value *ret_value) ++{ ++ if (expr_eval(node->lhs, context, arguments, ret_value) < 0) ++ return -1; ++ struct value *parent = value_get_parental_struct(ret_value); ++ if (parent == NULL) { ++ value_destroy(ret_value); ++ return -1; ++ } ++ *ret_value = *parent; ++ return 0; ++} ++ ++static int ++eval_cb1(struct expr_node *node, struct value *context, ++ struct value_dict *arguments, struct value *ret_value) ++{ ++ struct value val; ++ if (expr_eval(node->lhs, context, arguments, &val) < 0) ++ return -1; ++ ++ int ret = 0; ++ if (node->u.call.u.cb1(ret_value, &val, arguments, ++ node->u.call.data) < 0) ++ ret = -1; ++ ++ /* N.B. the callback must return its own value, or somehow ++ * clone the incoming argument. */ ++ value_destroy(&val); ++ return ret; ++} ++ ++static int ++eval_cb2(struct expr_node *node, struct value *context, ++ struct value_dict *arguments, struct value *ret_value) ++{ ++ struct value lhs; ++ if (expr_eval(node->lhs, context, arguments, &lhs) < 0) ++ return -1; ++ ++ struct value rhs; ++ if (expr_eval(node->u.call.rhs, context, arguments, &rhs) < 0) { ++ value_destroy(&lhs); ++ return -1; ++ } ++ ++ int ret = 0; ++ if (node->u.call.u.cb2(ret_value, &lhs, &rhs, arguments, ++ node->u.call.data) < 0) ++ ret = -1; ++ ++ /* N.B. the callback must return its own value, or somehow ++ * clone the incoming argument. */ ++ value_destroy(&lhs); ++ value_destroy(&rhs); ++ return ret; ++} ++ ++int ++eval_index(struct expr_node *node, struct value *context, ++ struct value_dict *arguments, struct value *ret_value) ++{ ++ struct value lhs; ++ if (expr_eval(node->lhs, context, arguments, &lhs) < 0) ++ return -1; ++ ++ long l; ++ if (expr_eval_word(node->u.node.n, context, arguments, &l) < 0) { ++ fail: ++ value_destroy(&lhs); ++ return -1; ++ } ++ ++ if (value_init_element(ret_value, &lhs, (size_t)l) < 0) ++ goto fail; ++ return 0; ++} ++ ++int ++expr_eval(struct expr_node *node, struct value *context, ++ struct value_dict *arguments, struct value *ret_value) ++{ ++ switch (node->kind) { ++ struct value *valp; ++ case EXPR_OP_ARGNO: ++ valp = val_dict_get_num(arguments, node->u.num); ++ if (valp == NULL) ++ return -1; ++ *ret_value = *valp; ++ return 0; ++ ++ case EXPR_OP_NAMED: ++ valp = val_dict_get_name(arguments, node->u.name.s); ++ if (valp == NULL) ++ return -1; ++ *ret_value = *valp; ++ return 0; ++ ++ case EXPR_OP_SELF: ++ *ret_value = *context; ++ return 0; ++ ++ case EXPR_OP_CONST: ++ *ret_value = node->u.value; ++ return 0; ++ ++ case EXPR_OP_INDEX: ++ return eval_index(node, context, arguments, ret_value); ++ ++ case EXPR_OP_UP: ++ return eval_up(node, context, arguments, ret_value); ++ ++ case EXPR_OP_CALL1: ++ return eval_cb1(node, context, arguments, ret_value); ++ ++ case EXPR_OP_CALL2: ++ return eval_cb2(node, context, arguments, ret_value); ++ } ++ ++ assert(!"Unknown node kind."); ++ abort(); ++} ++ ++int ++expr_eval_word(struct expr_node *node, struct value *context, ++ struct value_dict *arguments, long *ret_value) ++{ ++ struct value val; ++ if (expr_eval(node, context, arguments, &val) < 0) ++ return -1; ++ int ret = 0; ++ if (value_extract_word(&val, ret_value, arguments) < 0) ++ ret = -1; ++ value_destroy(&val); ++ return ret; ++} ++ ++int ++expr_eval_constant(struct expr_node *node, long *valuep) ++{ ++ assert(expr_is_compile_constant(node)); ++ return expr_eval_word(node, NULL, NULL, valuep); ++} ++ ++struct expr_node * ++expr_self(void) ++{ ++ static struct expr_node *node = NULL; ++ if (node == NULL) { ++ node = malloc(sizeof(*node)); ++ if (node == NULL) ++ error(1, errno, "malloc expr_self"); ++ expr_init_self(node); ++ } ++ return node; ++} +diff --git a/expr.h b/expr.h +new file mode 100644 +index 0000000..53b75b7 +--- /dev/null ++++ b/expr.h +@@ -0,0 +1,156 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef EXPR_H ++#define EXPR_H ++ ++#include "value.h" ++#include "value_dict.h" ++ ++/* Expressions serve as a way of encoding array lengths. */ ++ ++enum expr_node_kind { ++ EXPR_OP_SELF, /* reference to the variable in question */ ++ EXPR_OP_NAMED, /* value of named argument */ ++ EXPR_OP_ARGNO, /* value of numbered argument */ ++ EXPR_OP_CONST, /* constant value */ ++ EXPR_OP_INDEX, /* A[B] */ ++ EXPR_OP_UP, /* reference to containing structure */ ++ EXPR_OP_CALL1, /* internal callback with one operand */ ++ EXPR_OP_CALL2, /* internal callback with two operands */ ++}; ++ ++struct expr_node { ++ enum expr_node_kind kind; ++ ++ struct expr_node *lhs; ++ int own_lhs; ++ ++ union { ++ struct { ++ const char *s; ++ int own; ++ } name; ++ struct { ++ struct expr_node *n; ++ int own; ++ } node; ++ struct value value; ++ size_t num; ++ struct { ++ union { ++ int (*cb1)(struct value *ret_value, ++ struct value *lhs, ++ struct value_dict *arguments, ++ void *data); ++ int (*cb2)(struct value *ret_value, ++ struct value *lhs, ++ struct value *rhs, ++ struct value_dict *arguments, ++ void *data); ++ } u; ++ void *data; ++ struct expr_node *rhs; ++ int own_rhs; ++ } call; ++ } u; ++}; ++ ++/* Expression of type self just returns the value in consideration. ++ * For example, if what we seek is length of an array, then the value ++ * representing that array is returned by the expression. */ ++void expr_init_self(struct expr_node *node); ++ ++/* Expression that yields the value of an argument named NAME. NAME ++ * is owned if OWN_NAME. */ ++void expr_init_named(struct expr_node *node, ++ const char *name, int own_name); ++ ++/* Expression that yields the value of an argument number NUM. */ ++void expr_init_argno(struct expr_node *node, size_t num); ++ ++/* Constant expression always returns the same value VAL. VAL is ++ * copied into NODE and owned by it. */ ++void expr_init_const(struct expr_node *node, struct value *val); ++void expr_init_const_word(struct expr_node *node, long l, ++ struct arg_type_info *type, int own_type); ++ ++/* Expression LHS[RHS]. LHS and RHS are owned if, respectively, ++ * OWN_LHS and OWN_RHS. */ ++void expr_init_index(struct expr_node *node, ++ struct expr_node *lhs, int own_lhs, ++ struct expr_node *rhs, int own_rhs); ++ ++/* This expression returns the containing value of LHS (^LHS). LHS is ++ * owned if OWN_LHS. */ ++void expr_init_up(struct expr_node *node, struct expr_node *lhs, int own_lhs); ++ ++/* Callback expression calls CB(eval(LHS), DATA). LHS is owned if ++ * OWN_LHS. DATA is passed to callback verbatim. */ ++void expr_init_cb1(struct expr_node *node, ++ int (*cb)(struct value *ret_value, ++ struct value *value, ++ struct value_dict *arguments, ++ void *data), ++ struct expr_node *lhs, int own_lhs, void *data); ++ ++/* Callback expression calls CB(eval(LHS), eval(RHS), DATA). LHS and ++ * RHS are owned if, respectively, OWN_LHS and OWN_RHS. DATA is ++ * passed to callback verbatim. */ ++void expr_init_cb2(struct expr_node *node, ++ int (*cb)(struct value *ret_value, ++ struct value *lhs, struct value *rhs, ++ struct value_dict *arguments, ++ void *data), ++ struct expr_node *lhs, int own_lhs, ++ struct expr_node *rhs, int own_rhs, void *data); ++ ++/* Release the data inside NODE. Doesn't free NODE itself. */ ++void expr_destroy(struct expr_node *node); ++ ++/* Evaluate the expression NODE in context of VALUE. ARGUMENTS is a ++ * dictionary of named and numbered values that NODE may use. Returns ++ * 0 in case of success or a negative value on error. CONTEXT and ++ * ARGUMENTS may be NULL, but then the expression mustn't need them ++ * for evaluation. */ ++int expr_eval(struct expr_node *node, struct value *context, ++ struct value_dict *arguments, struct value *ret_value); ++ ++/* Evaluate compile-time expression. Returns 0 on success or negative ++ * value on failure. Computed value is passed back in *VALUEP. */ ++int expr_eval_constant(struct expr_node *node, long *valuep); ++ ++/* Evaluate expression, whose result should fit into a word. In order ++ * to easily support all the structure and array accesses, we simply ++ * operate on values represented by struct value. But eventually we need ++ * to be able to get out a word-size datum to use it as an index, a ++ * length, etc. */ ++int expr_eval_word(struct expr_node *node, struct value *context, ++ struct value_dict *arguments, long *ret_value); ++ ++/* Returns non-zero value if the expression is a compile-time ++ * constant. Currently this is only EXPR_OP_CONST, but eventually ++ * things like sizeof or simple expressions might be allowed. */ ++int expr_is_compile_constant(struct expr_node *node); ++ ++/* Returns a pre-computed expression "self". */ ++struct expr_node *expr_self(void); ++ ++#endif /* EXPR_H */ +diff --git a/fetch.c b/fetch.c +new file mode 100644 +index 0000000..bce949f +--- /dev/null ++++ b/fetch.c +@@ -0,0 +1,132 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++ ++#include "fetch.h" ++#include "value.h" ++#include "arch.h" ++#include "type.h" ++ ++#ifdef ARCH_HAVE_FETCH_ARG ++struct fetch_context *arch_fetch_arg_init(enum tof type, struct Process *proc, ++ struct arg_type_info *ret_info); ++ ++struct fetch_context *arch_fetch_arg_clone(struct Process *proc, ++ struct fetch_context *context); ++ ++int arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, ++ struct Process *proc, struct arg_type_info *info, ++ struct value *valuep); ++ ++int arch_fetch_retval(struct fetch_context *ctx, enum tof type, ++ struct Process *proc, struct arg_type_info *info, ++ struct value *valuep); ++ ++void arch_fetch_arg_done(struct fetch_context *context); ++ ++#else ++/* Fall back to gimme_arg. */ ++ ++long gimme_arg(enum tof type, struct Process *proc, int arg_num, ++ struct arg_type_info *info); ++ ++struct fetch_context { ++ int argnum; ++}; ++ ++struct fetch_context * ++arch_fetch_arg_init(enum tof type, struct Process *proc, ++ struct arg_type_info *ret_info) ++{ ++ return calloc(sizeof(struct fetch_context), 1); ++} ++ ++struct fetch_context * ++arch_fetch_arg_clone(struct Process *proc, struct fetch_context *context) ++{ ++ struct fetch_context *ret = malloc(sizeof(*ret)); ++ if (ret == NULL) ++ return NULL; ++ return memcpy(ret, context, sizeof(*ret)); ++} ++ ++int ++arch_fetch_arg_next(struct fetch_context *context, enum tof type, ++ struct Process *proc, ++ struct arg_type_info *info, struct value *valuep) ++{ ++ long l = gimme_arg(type, proc, context->argnum++, info); ++ value_set_word(valuep, l); ++ return 0; ++} ++ ++int ++arch_fetch_retval(struct fetch_context *context, enum tof type, ++ struct Process *proc, ++ struct arg_type_info *info, struct value *valuep) ++{ ++ long l = gimme_arg(type, proc, -1, info); ++ value_set_word(valuep, l); ++ return 0; ++} ++ ++void ++arch_fetch_arg_done(struct fetch_context *context) ++{ ++ free(context); ++} ++#endif ++ ++struct fetch_context * ++fetch_arg_init(enum tof type, struct Process *proc, ++ struct arg_type_info *ret_info) ++{ ++ return arch_fetch_arg_init(type, proc, ret_info); ++} ++ ++struct fetch_context * ++fetch_arg_clone(struct Process *proc, struct fetch_context *context) ++{ ++ return arch_fetch_arg_clone(proc, context); ++} ++ ++int ++fetch_arg_next(struct fetch_context *context, enum tof type, ++ struct Process *proc, ++ struct arg_type_info *info, struct value *valuep) ++{ ++ return arch_fetch_arg_next(context, type, proc, info, valuep); ++} ++ ++int ++fetch_retval(struct fetch_context *context, enum tof type, ++ struct Process *proc, ++ struct arg_type_info *info, struct value *valuep) ++{ ++ return arch_fetch_retval(context, type, proc, info, valuep); ++} ++ ++void ++fetch_arg_done(struct fetch_context *context) ++{ ++ return arch_fetch_arg_done(context); ++} +diff --git a/fetch.h b/fetch.h +new file mode 100644 +index 0000000..6a5385c +--- /dev/null ++++ b/fetch.h +@@ -0,0 +1,64 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef FETCH_H ++#define FETCH_H ++ ++#include "forward.h" ++ ++/* XXX isn't SYSCALL TOF just a different ABI? Maybe we needed to ++ * support variant ABIs all along. */ ++enum tof { ++ LT_TOF_FUNCTION, /* A real library function */ ++ LT_TOF_FUNCTIONR, /* Return from a real library function */ ++ LT_TOF_SYSCALL, /* A syscall */ ++ LT_TOF_SYSCALLR, /* Return from a syscall */ ++}; ++ ++/* The contents of the structure is defined by the back end. */ ++struct fetch_context; ++ ++/* Initialize argument fetching. Returns NULL on failure. RET_INFO ++ * is the return type of the function. */ ++struct fetch_context *fetch_arg_init(enum tof type, struct Process *proc, ++ struct arg_type_info *ret_info); ++ ++/* Make a clone of context. */ ++struct fetch_context *fetch_arg_clone(struct Process *proc, ++ struct fetch_context *context); ++ ++/* Load next argument. The function returns 0 on success or a ++ * negative value on failure. The extracted value is stored in ++ * *VALUEP. */ ++int fetch_arg_next(struct fetch_context *context, enum tof type, ++ struct Process *proc, ++ struct arg_type_info *info, struct value *valuep); ++ ++/* Load return value. The function returns 0 on success or a negative ++ * value on failure. The extracted value is stored in *VALUEP. */ ++int fetch_retval(struct fetch_context *context, enum tof type, ++ struct Process *proc, ++ struct arg_type_info *info, struct value *valuep); ++ ++/* Destroy fetch context. CONTEXT shall be the same memory location ++ * that was passed to fetch_arg_next. */ ++void fetch_arg_done(struct fetch_context *context); ++ ++#endif /* FETCH_H */ +diff --git a/forward.h b/forward.h +new file mode 100644 +index 0000000..e4233e5 +--- /dev/null ++++ b/forward.h +@@ -0,0 +1,14 @@ ++/* Important types defined in other header files are declared ++ here. */ ++struct Event; ++struct Process; ++struct arg_type_info; ++struct breakpoint; ++struct expr_node; ++struct library; ++struct library_symbol; ++struct ltelf; ++struct param; ++struct param_enum; ++struct value; ++struct value_dict; +diff --git a/handle_event.c b/handle_event.c +index 73c118a..5b6cc40 100644 +--- a/handle_event.c ++++ b/handle_event.c +@@ -1,6 +1,31 @@ +-#define _GNU_SOURCE ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2010 Arnaud Patard, Mandriva SA ++ * Copyright (C) 1998,2001,2002,2003,2004,2007,2008,2009 Juan Cespedes ++ * Copyright (C) 2008 Luis Machado, IBM Corporation ++ * Copyright (C) 2006 Ian Wienand ++ * Copyright (C) 2006 Paul Gilliam, IBM Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ + #include "config.h" + ++#define _GNU_SOURCE + #include + #include + #include +@@ -9,10 +34,13 @@ + #include + #include + ++#include "backend.h" + #include "breakpoint.h" + #include "common.h" ++#include "fetch.h" + #include "library.h" + #include "proc.h" ++#include "value_dict.h" + + static void handle_signal(Event *event); + static void handle_exit(Event *event); +@@ -228,9 +256,11 @@ handle_clone(Event *event) + if (proc == NULL) { + fail: + free(proc); +- /* XXX proper error handling here, please. */ +- perror("malloc()"); +- exit(1); ++ fprintf(stderr, ++ "Error during init of tracing process %d\n" ++ "This process won't be traced.\n", ++ event->proc->pid); ++ return; + } + + if (process_clone(proc, event->proc, event->e_un.newpid) < 0) +@@ -386,13 +416,13 @@ handle_exit_signal(Event *event) { + } + + static void +-output_syscall(struct Process *proc, const char *name, ++output_syscall(struct Process *proc, const char *name, enum tof tof, + void (*output)(enum tof, struct Process *, + struct library_symbol *)) + { + struct library_symbol syscall; + if (library_symbol_init(&syscall, 0, name, 0, LS_TOPLT_NONE) >= 0) { +- (*output)(LT_TOF_SYSCALL, proc, &syscall); ++ (*output)(tof, proc, &syscall); + library_symbol_destroy(&syscall); + } + } +@@ -400,13 +430,13 @@ output_syscall(struct Process *proc, const char *name, + static void + output_syscall_left(struct Process *proc, const char *name) + { +- output_syscall(proc, name, &output_left); ++ output_syscall(proc, name, LT_TOF_SYSCALL, &output_left); + } + + static void + output_syscall_right(struct Process *proc, const char *name) + { +- output_syscall(proc, name, &output_right); ++ output_syscall(proc, name, LT_TOF_SYSCALLR, &output_right); + } + + static void +@@ -670,6 +700,7 @@ callstack_push_syscall(Process *proc, int sysnum) { + } + + elem = &proc->callstack[proc->callstack_depth]; ++ *elem = (struct callstack_element){}; + elem->is_syscall = 1; + elem->c_un.syscall = sysnum; + elem->return_addr = NULL; +@@ -694,6 +725,7 @@ callstack_push_symfunc(Process *proc, struct library_symbol *sym) { + } + + elem = &proc->callstack[proc->callstack_depth++]; ++ *elem = (struct callstack_element){}; + elem->is_syscall = 0; + elem->c_un.libfunc = sym; + +@@ -701,7 +733,6 @@ callstack_push_symfunc(Process *proc, struct library_symbol *sym) { + if (elem->return_addr) + insert_breakpoint(proc, elem->return_addr, NULL); + +- /* handle functions like atexit() on mips which have no return */ + if (opt_T || options.summary) { + struct timezone tz; + gettimeofday(&elem->time_spent, &tz); +@@ -719,9 +750,5 @@ callstack_pop(Process *proc) { + assert(proc->leader != NULL); + delete_breakpoint(proc, elem->return_addr); + } +- if (elem->arch_ptr != NULL) { +- free(elem->arch_ptr); +- elem->arch_ptr = NULL; +- } + proc->callstack_depth--; + } +diff --git a/lens.c b/lens.c +new file mode 100644 +index 0000000..07cdcca +--- /dev/null ++++ b/lens.c +@@ -0,0 +1,62 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++ ++#include "lens.h" ++#include "lens_default.h" ++#include "type.h" ++#include "value.h" ++ ++int ++format_argument(FILE *stream, struct value *value, ++ struct value_dict *arguments) ++{ ++ /* Find the closest enclosing parental value whose type has a ++ * lens assigned. */ ++ struct value *parent; ++ for (parent = value; (parent != NULL && parent->type != NULL ++ && parent->type->lens == NULL); ++ parent = parent->parent) ++ ; ++ ++ struct lens *lens = &default_lens; ++ if (parent != NULL && parent->type != NULL ++ && parent->type->lens != NULL) ++ lens = parent->type->lens; ++ ++ return lens_format(lens, stream, value, arguments); ++} ++ ++int ++lens_format(struct lens *lens, FILE *stream, struct value *value, ++ struct value_dict *arguments) ++{ ++ assert(lens->format_cb != NULL); ++ return lens->format_cb(lens, stream, value, arguments); ++} ++ ++void ++lens_destroy(struct lens *lens) ++{ ++ if (lens != NULL ++ && lens->destroy_cb != NULL) ++ lens->destroy_cb(lens); ++} +diff --git a/lens.h b/lens.h +new file mode 100644 +index 0000000..f7de3b5 +--- /dev/null ++++ b/lens.h +@@ -0,0 +1,51 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef LENS_H ++#define LENS_H ++ ++#include ++#include "forward.h" ++ ++struct lens { ++ /* Format VALUE into STREAM. The dictionary of all arguments ++ * is given for purposes of evaluating array lengths and other ++ * dynamic expressions. Returns number of characters ++ * outputted, -1 in case of failure. */ ++ int (*format_cb)(struct lens *lens, FILE *stream, struct value *value, ++ struct value_dict *arguments); ++ ++ /* Erase any memory allocated for LENS. Keep the LENS pointer ++ * itself intact. */ ++ void (*destroy_cb)(struct lens *lens); ++}; ++ ++/* Extracts a lens from VALUE and calls lens_format on that. */ ++int format_argument(FILE *stream, struct value *value, ++ struct value_dict *arguments); ++ ++/* Calls format callback associated with LENS. */ ++int lens_format(struct lens *lens, FILE *stream, struct value *value, ++ struct value_dict *arguments); ++ ++/* Calls destroy callback associated with LENS. */ ++void lens_destroy(struct lens *lens); ++ ++#endif /* LENS_H */ +diff --git a/lens_default.c b/lens_default.c +new file mode 100644 +index 0000000..f59d328 +--- /dev/null ++++ b/lens_default.c +@@ -0,0 +1,499 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 1998,2004,2007,2008,2009 Juan Cespedes ++ * Copyright (C) 2006 Ian Wienand ++ * Copyright (C) 2006 Steve Fink ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "proc.h" ++#include "lens_default.h" ++#include "value.h" ++#include "expr.h" ++#include "type.h" ++#include "common.h" ++#include "zero.h" ++ ++#define READER(NAME, TYPE) \ ++ static int \ ++ NAME(struct value *value, TYPE *ret, struct value_dict *arguments) \ ++ { \ ++ union { \ ++ TYPE val; \ ++ unsigned char buf[0]; \ ++ } u; \ ++ if (value_extract_buf(value, u.buf, arguments) < 0) \ ++ return -1; \ ++ *ret = u.val; \ ++ return 0; \ ++ } ++ ++READER(read_float, float) ++READER(read_double, double) ++ ++#undef READER ++ ++#define HANDLE_WIDTH(BITS) \ ++ do { \ ++ long l; \ ++ if (value_extract_word(value, &l, arguments) < 0) \ ++ return -1; \ ++ int##BITS##_t i = l; \ ++ switch (format) { \ ++ case INT_FMT_unknown: \ ++ if (i < -10000 || i > 10000) \ ++ case INT_FMT_x: \ ++ return fprintf(stream, "%#"PRIx##BITS, i); \ ++ case INT_FMT_i: \ ++ return fprintf(stream, "%"PRIi##BITS, i); \ ++ case INT_FMT_u: \ ++ return fprintf(stream, "%"PRIu##BITS, i); \ ++ case INT_FMT_o: \ ++ return fprintf(stream, "0%"PRIo##BITS, i); \ ++ } \ ++ } while (0) ++ ++enum int_fmt_t ++{ ++ INT_FMT_i, ++ INT_FMT_u, ++ INT_FMT_o, ++ INT_FMT_x, ++ INT_FMT_unknown, ++}; ++ ++static int ++format_integer(FILE *stream, struct value *value, enum int_fmt_t format, ++ struct value_dict *arguments) ++{ ++ switch (type_sizeof(value->inferior, value->type)) { ++ ++ case 1: HANDLE_WIDTH(8); ++ case 2: HANDLE_WIDTH(16); ++ case 4: HANDLE_WIDTH(32); ++ case 8: HANDLE_WIDTH(64); ++ ++ default: ++ assert(!"unsupported integer width"); ++ abort(); ++ ++ case -1: ++ return -1; ++ } ++} ++ ++#undef HANDLE_WIDTH ++ ++static int ++account(int *countp, int c) ++{ ++ if (c >= 0) ++ *countp += c; ++ return c; ++} ++ ++static int ++acc_fprintf(int *countp, FILE *stream, const char *format, ...) ++{ ++ va_list pa; ++ va_start(pa, format); ++ int i = account(countp, vfprintf(stream, format, pa)); ++ va_end(pa); ++ ++ return i; ++} ++ ++static int ++format_char(FILE *stream, struct value *value, struct value_dict *arguments) ++{ ++ long lc; ++ if (value_extract_word(value, &lc, arguments) < 0) ++ return -1; ++ int c = (int)lc; ++ ++ const char *fmt; ++ switch (c) { ++ case -1: ++ fmt = "EOF"; ++ break; ++ case 0: ++ fmt = "\\0"; ++ break; ++ case '\a': ++ fmt = "\\a"; ++ break; ++ case '\b': ++ fmt = "\\b"; ++ break; ++ case '\t': ++ fmt = "\\t"; ++ break; ++ case '\n': ++ fmt = "\\n"; ++ break; ++ case '\v': ++ fmt = "\\v"; ++ break; ++ case '\f': ++ fmt = "\\f"; ++ break; ++ case '\r': ++ fmt = "\\r"; ++ break; ++ case '\\': ++ fmt = "\\\\"; ++ break; ++ default: ++ if (isprint(c) || c == ' ') ++ fmt = "%c"; ++ else ++ fmt = "\\%03o"; ++ } ++ ++ return fprintf(stream, fmt, c); ++} ++ ++static int ++format_naked_char(FILE *stream, struct value *value, ++ struct value_dict *arguments) ++{ ++ int written = 0; ++ if (acc_fprintf(&written, stream, "'") < 0 ++ || account(&written, format_char(stream, value, arguments)) < 0 ++ || acc_fprintf(&written, stream, "'") < 0) ++ return -1; ++ ++ return written; ++} ++ ++static int ++format_floating(FILE *stream, struct value *value, struct value_dict *arguments) ++{ ++ switch (value->type->type) { ++ float f; ++ double d; ++ case ARGTYPE_FLOAT: ++ if (read_float(value, &f, arguments) < 0) ++ return -1; ++ return fprintf(stream, "%f", (double)f); ++ case ARGTYPE_DOUBLE: ++ if (read_double(value, &d, arguments) < 0) ++ return -1; ++ return fprintf(stream, "%f", d); ++ default: ++ abort(); ++ } ++} ++ ++static int ++format_struct(FILE *stream, struct value *value, struct value_dict *arguments) ++{ ++ int written = 0; ++ if (acc_fprintf(&written, stream, "{ ") < 0) ++ return -1; ++ size_t i; ++ for (i = 0; i < type_struct_size(value->type); ++i) { ++ if (i > 0 && acc_fprintf(&written, stream, ", ") < 0) ++ return -1; ++ ++ struct value element; ++ if (value_init_element(&element, value, i) < 0) ++ return -1; ++ int o = format_argument(stream, &element, arguments); ++ if (o < 0) ++ return -1; ++ written += o; ++ } ++ if (acc_fprintf(&written, stream, " }") < 0) ++ return -1; ++ return written; ++} ++ ++int ++format_pointer(FILE *stream, struct value *value, struct value_dict *arguments) ++{ ++ struct value element; ++ if (value_init_deref(&element, value) < 0) ++ return -1; ++ return format_argument(stream, &element, arguments); ++} ++ ++/* ++ * LENGTH is an expression whose evaluation will yield the actual ++ * length of the array. ++ * ++ * MAXLEN is the actual maximum length that we care about ++ * ++ * BEFORE if LENGTH>MAXLEN, we display ellipsis. We display it before ++ * the closing parenthesis if BEFORE, otherwise after it. ++ * ++ * OPEN, CLOSE, DELIM are opening and closing parenthesis and element ++ * delimiter. ++ */ ++int ++format_array(FILE *stream, struct value *value, struct value_dict *arguments, ++ struct expr_node *length, size_t maxlen, int before, ++ const char *open, const char *close, const char *delim) ++{ ++ /* We need "long" to be long enough to cover the whole address ++ * space. */ ++ typedef char assert__long_enough_long[-(sizeof(long) < sizeof(void *))]; ++ long l; ++ if (expr_eval_word(length, value, arguments, &l) < 0) ++ return -1; ++ size_t len = (size_t)l; ++ ++ int written = 0; ++ if (acc_fprintf(&written, stream, "%s", open) < 0) ++ return -1; ++ ++ size_t i; ++ for (i = 0; i < len && i <= maxlen; ++i) { ++ if (i == maxlen) { ++ if (before && acc_fprintf(&written, stream, "...") < 0) ++ return -1; ++ break; ++ } ++ ++ if (i > 0 && acc_fprintf(&written, stream, "%s", delim) < 0) ++ return -1; ++ ++ struct value element; ++ if (value_init_element(&element, value, i) < 0) ++ return -1; ++ int o = format_argument(stream, &element, arguments); ++ if (o < 0) ++ return -1; ++ written += o; ++ } ++ if (acc_fprintf(&written, stream, "%s", close) < 0) ++ return -1; ++ if (i == maxlen && !before && acc_fprintf(&written, stream, "...") < 0) ++ return -1; ++ ++ return written; ++} ++ ++static int ++toplevel_format_lens(struct lens *lens, FILE *stream, ++ struct value *value, struct value_dict *arguments, ++ enum int_fmt_t int_fmt) ++{ ++ switch (value->type->type) { ++ case ARGTYPE_VOID: ++ return fprintf(stream, ""); ++ ++ case ARGTYPE_SHORT: ++ case ARGTYPE_INT: ++ case ARGTYPE_LONG: ++ return format_integer(stream, value, int_fmt, arguments); ++ ++ case ARGTYPE_USHORT: ++ case ARGTYPE_UINT: ++ case ARGTYPE_ULONG: ++ if (int_fmt == INT_FMT_i) ++ int_fmt = INT_FMT_u; ++ return format_integer(stream, value, int_fmt, arguments); ++ ++ case ARGTYPE_CHAR: ++ return format_naked_char(stream, value, arguments); ++ ++ case ARGTYPE_FLOAT: ++ case ARGTYPE_DOUBLE: ++ return format_floating(stream, value, arguments); ++ ++ case ARGTYPE_STRUCT: ++ return format_struct(stream, value, arguments); ++ ++ case ARGTYPE_POINTER: ++ if (value->type->u.array_info.elt_type->type != ARGTYPE_VOID) ++ return format_pointer(stream, value, arguments); ++ return format_integer(stream, value, INT_FMT_x, arguments); ++ ++ case ARGTYPE_ARRAY: ++ return format_array(stream, value, arguments, ++ value->type->u.array_info.length, ++ options.arraylen, 1, "[ ", " ]", ", "); ++ } ++ abort(); ++} ++ ++static int ++default_lens_format_cb(struct lens *lens, FILE *stream, ++ struct value *value, struct value_dict *arguments) ++{ ++ return toplevel_format_lens(lens, stream, value, arguments, INT_FMT_i); ++} ++ ++struct lens default_lens = { ++ .format_cb = default_lens_format_cb, ++}; ++ ++ ++static int ++blind_lens_format_cb(struct lens *lens, FILE *stream, ++ struct value *value, struct value_dict *arguments) ++{ ++ return 0; ++} ++ ++struct lens blind_lens = { ++ .format_cb = blind_lens_format_cb, ++}; ++ ++ ++static int ++octal_lens_format_cb(struct lens *lens, FILE *stream, ++ struct value *value, struct value_dict *arguments) ++{ ++ return toplevel_format_lens(lens, stream, value, arguments, INT_FMT_o); ++} ++ ++struct lens octal_lens = { ++ .format_cb = octal_lens_format_cb, ++}; ++ ++ ++static int ++hex_lens_format_cb(struct lens *lens, FILE *stream, ++ struct value *value, struct value_dict *arguments) ++{ ++ return toplevel_format_lens(lens, stream, value, arguments, INT_FMT_x); ++} ++ ++struct lens hex_lens = { ++ .format_cb = hex_lens_format_cb, ++}; ++ ++ ++static int ++guess_lens_format_cb(struct lens *lens, FILE *stream, ++ struct value *value, struct value_dict *arguments) ++{ ++ return toplevel_format_lens(lens, stream, value, arguments, ++ INT_FMT_unknown); ++} ++ ++struct lens guess_lens = { ++ .format_cb = guess_lens_format_cb, ++}; ++ ++ ++static int ++bool_lens_format_cb(struct lens *lens, FILE *stream, ++ struct value *value, struct value_dict *arguments) ++{ ++ switch (value->type->type) { ++ case ARGTYPE_VOID: ++ case ARGTYPE_FLOAT: ++ case ARGTYPE_DOUBLE: ++ case ARGTYPE_STRUCT: ++ case ARGTYPE_POINTER: ++ case ARGTYPE_ARRAY: ++ return toplevel_format_lens(lens, stream, value, ++ arguments, INT_FMT_i); ++ ++ int zero; ++ case ARGTYPE_SHORT: ++ case ARGTYPE_INT: ++ case ARGTYPE_LONG: ++ case ARGTYPE_USHORT: ++ case ARGTYPE_UINT: ++ case ARGTYPE_ULONG: ++ case ARGTYPE_CHAR: ++ if ((zero = value_is_zero(value, arguments)) < 0) ++ return -1; ++ if (zero) ++ return fprintf(stream, "false"); ++ else ++ return fprintf(stream, "true"); ++ } ++ abort(); ++} ++ ++struct lens bool_lens = { ++ .format_cb = bool_lens_format_cb, ++}; ++ ++ ++static int ++string_lens_format_cb(struct lens *lens, FILE *stream, ++ struct value *value, struct value_dict *arguments) ++{ ++ switch (value->type->type) { ++ case ARGTYPE_POINTER: ++ /* This should really be written as either "string", ++ * or, if lens, then string(array(char, zero)*). But ++ * I suspect people are so used to the char * C idiom, ++ * that string(char *) might actually turn up. So ++ * let's just support it. */ ++ if (value->type->u.ptr_info.info->type == ARGTYPE_CHAR) { ++ struct arg_type_info info[2]; ++ type_init_array(&info[1], ++ value->type->u.ptr_info.info, 0, ++ expr_node_zero(), 0); ++ type_init_pointer(&info[0], &info[1], 0); ++ info->lens = lens; ++ info->own_lens = 0; ++ struct value tmp; ++ if (value_clone(&tmp, value) < 0) ++ return -1; ++ value_set_type(&tmp, info, 0); ++ int ret = string_lens_format_cb(lens, stream, &tmp, ++ arguments); ++ type_destroy(&info[0]); ++ type_destroy(&info[1]); ++ value_destroy(&tmp); ++ return ret; ++ } ++ ++ /* fall-through */ ++ case ARGTYPE_VOID: ++ case ARGTYPE_FLOAT: ++ case ARGTYPE_DOUBLE: ++ case ARGTYPE_STRUCT: ++ case ARGTYPE_SHORT: ++ case ARGTYPE_INT: ++ case ARGTYPE_LONG: ++ case ARGTYPE_USHORT: ++ case ARGTYPE_UINT: ++ case ARGTYPE_ULONG: ++ return toplevel_format_lens(lens, stream, value, ++ arguments, INT_FMT_i); ++ ++ case ARGTYPE_CHAR: ++ return format_char(stream, value, arguments); ++ ++ case ARGTYPE_ARRAY: ++ return format_array(stream, value, arguments, ++ value->type->u.array_info.length, ++ options.strlen, 0, "\"", "\"", ""); ++ } ++ abort(); ++} ++ ++struct lens string_lens = { ++ .format_cb = string_lens_format_cb, ++}; +diff --git a/lens_default.h b/lens_default.h +new file mode 100644 +index 0000000..9a9d0c6 +--- /dev/null ++++ b/lens_default.h +@@ -0,0 +1,49 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef LENS_DEFAULT_H ++#define LENS_DEFAULT_H ++ ++#include "lens.h" ++ ++/* Default lens that does reasonable job for most cases. */ ++extern struct lens default_lens; ++ ++/* A lens that doesn't output anything. */ ++extern struct lens blind_lens; ++ ++/* A lens that formats integers in octal. */ ++extern struct lens octal_lens; ++ ++/* A lens that formats integers in hexadecimal. */ ++extern struct lens hex_lens; ++ ++/* A lens that formats integers as either "true" or "false". */ ++extern struct lens bool_lens; ++ ++/* A lens that tries to guess whether the value is "large" (i.e. a ++ * pointer, and should be formatted in hex), or "small" (and should be ++ * formatted in decimal). */ ++extern struct lens guess_lens; ++ ++/* A lens for strings. */ ++extern struct lens string_lens; ++ ++#endif /* LENS_DEFAULT_H */ +diff --git a/lens_enum.c b/lens_enum.c +new file mode 100644 +index 0000000..1af94d2 +--- /dev/null ++++ b/lens_enum.c +@@ -0,0 +1,163 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++ ++#include "arch.h" ++#include "lens_enum.h" ++#include "lens_default.h" ++#include "value.h" ++#include "type.h" ++ ++struct enum_entry { ++ char *key; ++ int own_key; ++ struct value *value; ++ int own_value; ++}; ++ ++static void ++enum_entry_dtor(struct enum_entry *entry, void *data) ++{ ++ if (entry->own_key) ++ free(entry->key); ++ if (entry->own_value) { ++ value_destroy(entry->value); ++ free(entry->value); ++ } ++} ++ ++static void ++enum_lens_destroy_cb(struct lens *lens) ++{ ++ struct enum_lens *self = (void *)lens; ++ ++ VECT_DESTROY(&self->entries, struct enum_entry, ++ enum_entry_dtor, NULL); ++} ++ ++enum { ++#ifdef ARCH_ENDIAN_BIG ++ big_endian = 1, ++#elif defined (ARCH_ENDIAN_LITTLE) ++ big_endian = 0, ++#else ++# error Undefined endianness. ++#endif ++}; ++ ++/* Returns 0 if they are not equal, >0 if they are, and <0 if there ++ * was an error. */ ++static int ++enum_values_equal(struct value *inf_value, struct value *enum_value, ++ struct value_dict *arguments) ++{ ++ /* Width may not match between what's defined in config file ++ * and what arrives from the back end. Typically, if there is ++ * a mismatch, the config file size will be larger, as we are ++ * in a situation where 64-bit tracer traces 32-bit process. ++ * But opposite situation can occur e.g. on PPC, where it's ++ * apparently possible for 32-bit tracer to trace 64-bit ++ * inferior, or hypothetically in a x32/x86_64 situation. */ ++ ++ unsigned char *inf_data = value_get_data(inf_value, arguments); ++ size_t inf_sz = value_size(inf_value, arguments); ++ if (inf_data == NULL || inf_sz == (size_t)-1) ++ return -1; ++ ++ assert(inf_value->type->type == enum_value->type->type); ++ ++ unsigned char *enum_data = value_get_data(enum_value, arguments); ++ size_t enum_sz = value_size(enum_value, arguments); ++ if (enum_data == NULL || enum_sz == (size_t)-1) ++ return -1; ++ ++ size_t sz = enum_sz > inf_sz ? inf_sz : enum_sz; ++ ++ if (big_endian) ++ return memcmp(enum_data + enum_sz - sz, ++ inf_data + inf_sz - sz, sz) == 0; ++ else ++ return memcmp(enum_data, inf_data, sz) == 0; ++} ++ ++static const char * ++enum_get(struct enum_lens *lens, struct value *value, ++ struct value_dict *arguments) ++{ ++ size_t i; ++ for (i = 0; i < vect_size(&lens->entries); ++i) { ++ struct enum_entry *entry = VECT_ELEMENT(&lens->entries, ++ struct enum_entry, i); ++ int st = enum_values_equal(value, entry->value, arguments); ++ if (st < 0) ++ return NULL; ++ else if (st != 0) ++ return entry->key; ++ } ++ return NULL; ++} ++ ++static int ++enum_lens_format_cb(struct lens *lens, FILE *stream, ++ struct value *value, struct value_dict *arguments) ++{ ++ struct enum_lens *self = (void *)lens; ++ ++ long l; ++ if (value_extract_word(value, &l, arguments) < 0) ++ return -1; ++ ++ const char *name = enum_get(self, value, arguments); ++ if (name != NULL) ++ return fprintf(stream, "%s", name); ++ ++ return lens_format(&default_lens, stream, value, arguments); ++} ++ ++ ++void ++lens_init_enum(struct enum_lens *lens) ++{ ++ *lens = (struct enum_lens){ ++ { ++ .format_cb = enum_lens_format_cb, ++ .destroy_cb = enum_lens_destroy_cb, ++ }, ++ }; ++ VECT_INIT(&lens->entries, struct enum_entry); ++} ++ ++int ++lens_enum_add(struct enum_lens *lens, ++ const char *key, int own_key, ++ struct value *value, int own_value) ++{ ++ struct enum_entry entry = { (char *)key, own_key, value, own_value }; ++ return VECT_PUSHBACK(&lens->entries, &entry); ++} ++ ++size_t ++lens_enum_size(struct enum_lens *lens) ++{ ++ return vect_size(&lens->entries); ++} +diff --git a/lens_enum.h b/lens_enum.h +new file mode 100644 +index 0000000..de7b386 +--- /dev/null ++++ b/lens_enum.h +@@ -0,0 +1,48 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef LENS_ENUM_H ++#define LENS_ENUM_H ++ ++#include "lens.h" ++#include "vect.h" ++ ++struct enum_lens { ++ struct lens super; ++ struct vect entries; ++ int own; ++}; ++ ++/* Init enumeration LENS. */ ++void lens_init_enum(struct enum_lens *lens); ++ ++/* Push another member of the enumeration, named KEY, with given ++ * VALUE. If OWN_KEY, KEY is owned and released after the type is ++ * destroyed. KEY is typed as const char *, but note that if OWN_KEY, ++ * this value will be freed. If OWN_VALUE, then VALUE is owned and ++ * destroyed by LENS. */ ++int lens_enum_add(struct enum_lens *lens, ++ const char *key, int own_key, ++ struct value *value, int own_value); ++ ++/* Return number of enum elements of type INFO. */ ++size_t lens_enum_size(struct enum_lens *lens); ++ ++#endif /* LENS_ENUM_H */ +diff --git a/libltrace.c b/libltrace.c +index 92f2701..f4bb2c8 100644 +--- a/libltrace.c ++++ b/libltrace.c +@@ -1,3 +1,24 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2009 Juan Cespedes ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ + #include "config.h" + + #include +@@ -11,6 +32,8 @@ + + #include "common.h" + #include "proc.h" ++#include "read_config_file.h" ++#include "backend.h" + + char *command = NULL; + +@@ -84,6 +107,7 @@ ltrace_init(int argc, char **argv) { + signal(SIGTERM, signal_exit); /* ... or killed */ + + argv = process_options(argc, argv); ++ init_global_config(); + while (opt_F) { + /* If filename begins with ~, expand it to the user's home */ + /* directory. This does not correctly handle ~yoda, but that */ +diff --git a/library.c b/library.c +index 92fccea..2bd7dbb 100644 +--- a/library.c ++++ b/library.c +@@ -27,7 +27,7 @@ + #include "library.h" + #include "proc.h" // for enum callback_status + #include "debug.h" +-#include "common.h" // for arch_library_symbol_init, arch_library_init ++#include "backend.h" // for arch_library_symbol_init, arch_library_init + + #ifndef ARCH_HAVE_LIBRARY_DATA + void +@@ -207,10 +207,18 @@ static void + private_library_init(struct library *lib, enum library_type type) + { + lib->next = NULL; ++ ++ lib->key = 0; ++ lib->base = 0; ++ lib->entry = 0; ++ lib->dyn_addr = 0; ++ + lib->soname = NULL; + lib->own_soname = 0; ++ + lib->pathname = NULL; + lib->own_pathname = 0; ++ + lib->symbols = NULL; + lib->type = type; + } +diff --git a/ltrace-elf.c b/ltrace-elf.c +index b1af070..c40a188 100644 +--- a/ltrace-elf.c ++++ b/ltrace-elf.c +@@ -1,3 +1,30 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2006,2010,2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2010 Zachary T Welch, CodeSourcery ++ * Copyright (C) 2010 Joe Damato ++ * Copyright (C) 1997,1998,2001,2004,2007,2008,2009 Juan Cespedes ++ * Copyright (C) 2006 Olaf Hering, SUSE Linux GmbH ++ * Copyright (C) 2006 Eric Vaitl, Cisco Systems, Inc. ++ * Copyright (C) 2006 Paul Gilliam, IBM Corporation ++ * Copyright (C) 2006 Ian Wienand ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ + #include "config.h" + + #include +@@ -13,10 +40,13 @@ + #include + #include + +-#include "common.h" +-#include "proc.h" +-#include "library.h" ++#include "backend.h" + #include "filter.h" ++#include "library.h" ++#include "ltrace-elf.h" ++#include "proc.h" ++#include "debug.h" ++#include "options.h" + + #ifdef PLT_REINITALISATION_BP + extern char *PLTs_initialized_by_here; +@@ -241,8 +271,11 @@ open_elf(struct ltelf *lte, const char *filename) + exit(EXIT_FAILURE); + } + +- if ((lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS +- || lte->ehdr.e_machine != LT_ELF_MACHINE) ++ if (1 ++#ifdef LT_ELF_MACHINE ++ && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS ++ || lte->ehdr.e_machine != LT_ELF_MACHINE) ++#endif + #ifdef LT_ELF_MACHINE2 + && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS2 + || lte->ehdr.e_machine != LT_ELF_MACHINE2) +diff --git a/output.c b/output.c +index db6e93e..8bfe3f0 100644 +--- a/output.c ++++ b/output.c +@@ -1,3 +1,27 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2010 Joe Damato ++ * Copyright (C) 1997,1998,1999,2001,2002,2003,2004,2007,2008,2009 Juan Cespedes ++ * Copyright (C) 2006 Paul Gilliam ++ * Copyright (C) 2006 Ian Wienand ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ + #include "config.h" + + #include +@@ -7,10 +31,18 @@ + #include + #include + #include ++#include ++#include + + #include "common.h" + #include "proc.h" + #include "library.h" ++#include "type.h" ++#include "value.h" ++#include "value_dict.h" ++#include "param.h" ++#include "fetch.h" ++#include "lens_default.h" + + /* TODO FIXME XXX: include in common.h: */ + extern struct timeval current_time_spent; +@@ -18,7 +50,7 @@ extern struct timeval current_time_spent; + Dict *dict_opt_c = NULL; + + static Process *current_proc = 0; +-static int current_depth = 0; ++static size_t current_depth = 0; + static int current_column = 0; + + static void +@@ -29,7 +61,8 @@ output_indent(struct Process *proc) + } + + static void +-begin_of_line(enum tof type, Process *proc) { ++begin_of_line(Process *proc, int is_func, int indent) ++{ + current_column = 0; + if (!proc) { + return; +@@ -85,40 +118,89 @@ begin_of_line(enum tof type, Process *proc) { + } + } + if (opt_i) { +- if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { ++ if (is_func) + current_column += fprintf(options.output, "[%p] ", + proc->return_addr); +- } else { ++ else + current_column += fprintf(options.output, "[%p] ", + proc->instruction_pointer); +- } + } +- if (options.indent > 0 && type != LT_TOF_NONE) { ++ if (options.indent > 0 && indent) { + output_indent(proc); + } + } + ++static struct arg_type_info * ++get_unknown_type(void) ++{ ++ static struct arg_type_info *info = NULL; ++ if (info == NULL) { ++ info = malloc(sizeof(*info)); ++ if (info == NULL) { ++ report_global_error("malloc: %s", strerror(errno)); ++ abort(); ++ } ++ *info = *type_get_simple(ARGTYPE_LONG); ++ info->lens = &guess_lens; ++ } ++ return info; ++} ++ ++/* The default prototype is: long X(long, long, long, long). */ ++static Function * ++build_default_prototype(void) ++{ ++ Function *ret = malloc(sizeof(*ret)); ++ size_t i = 0; ++ if (ret == NULL) ++ goto err; ++ memset(ret, 0, sizeof(*ret)); ++ ++ struct arg_type_info *unknown_type = get_unknown_type(); ++ ++ ret->return_info = unknown_type; ++ ret->own_return_info = 0; ++ ++ ret->num_params = 4; ++ ret->params = malloc(sizeof(*ret->params) * ret->num_params); ++ if (ret->params == NULL) ++ goto err; ++ ++ for (i = 0; i < ret->num_params; ++i) ++ param_init_type(&ret->params[i], unknown_type, 0); ++ ++ return ret; ++ ++err: ++ report_global_error("malloc: %s", strerror(errno)); ++ if (ret->params != NULL) { ++ while (i-- > 0) ++ param_destroy(&ret->params[i]); ++ free(ret->params); ++ } ++ ++ free(ret); ++ ++ return NULL; ++} ++ + static Function * + name2func(char const *name) { + Function *tmp; + const char *str1, *str2; + +- tmp = list_of_functions; +- while (tmp) { +-#ifdef USE_DEMANGLE +- str1 = options.demangle ? my_demangle(tmp->name) : tmp->name; +- str2 = options.demangle ? my_demangle(name) : name; +-#else ++ for (tmp = list_of_functions; tmp != NULL; tmp = tmp->next) { + str1 = tmp->name; + str2 = name; +-#endif +- if (!strcmp(str1, str2)) { +- ++ if (!strcmp(str1, str2)) + return tmp; +- } +- tmp = tmp->next; + } +- return NULL; ++ ++ static Function *def = NULL; ++ if (def == NULL) ++ def = build_default_prototype(); ++ ++ return def; + } + + void +@@ -139,7 +221,7 @@ output_line(Process *proc, char *fmt, ...) { + if (!fmt) { + return; + } +- begin_of_line(LT_TOF_NONE, proc); ++ begin_of_line(proc, 0, 0); + + va_start(args, fmt); + vfprintf(options.output, fmt, args); +@@ -155,15 +237,191 @@ tabto(int col) { + } + } + ++static int ++account_output(int o) ++{ ++ if (o < 0) ++ return -1; ++ current_column += o; ++ return 0; ++} ++ ++static int ++output_error(void) ++{ ++ return account_output(fprintf(options.output, "?")); ++} ++ ++static int ++fetch_simple_param(enum tof type, Process *proc, struct fetch_context *context, ++ struct value_dict *arguments, struct arg_type_info *info, ++ struct value *valuep) ++{ ++ /* Arrays decay into pointers per C standard. We check for ++ * this here, because here we also capture arrays that come ++ * from parameter packs. */ ++ int own = 0; ++ if (info->type == ARGTYPE_ARRAY) { ++ struct arg_type_info *tmp = malloc(sizeof(*tmp)); ++ if (tmp != NULL) { ++ type_init_pointer(tmp, info, 0); ++ tmp->lens = info->lens; ++ info = tmp; ++ own = 1; ++ } ++ } ++ ++ struct value value; ++ value_init(&value, proc, NULL, info, own); ++ if (fetch_arg_next(context, type, proc, info, &value) < 0) ++ return -1; ++ ++ if (val_dict_push_next(arguments, &value) < 0) { ++ value_destroy(&value); ++ return -1; ++ } ++ ++ if (valuep != NULL) ++ *valuep = value; ++ ++ return 0; ++} ++ ++static void ++fetch_param_stop(struct value_dict *arguments, ssize_t *params_leftp) ++{ ++ if (*params_leftp == -1) ++ *params_leftp = val_dict_count(arguments); ++} ++ ++static int ++fetch_param_pack(enum tof type, Process *proc, struct fetch_context *context, ++ struct value_dict *arguments, struct param *param, ++ ssize_t *params_leftp) ++{ ++ struct param_enum *e = param_pack_init(param, arguments); ++ if (e == NULL) ++ return -1; ++ ++ int ret = 0; ++ while (1) { ++ int insert_stop = 0; ++ struct arg_type_info *info = malloc(sizeof(*info)); ++ if (info == NULL ++ || param_pack_next(param, e, info, &insert_stop) < 0) { ++ fail: ++ free(info); ++ ret = -1; ++ break; ++ } ++ ++ if (insert_stop) ++ fetch_param_stop(arguments, params_leftp); ++ ++ if (info->type == ARGTYPE_VOID) ++ break; ++ ++ struct value val; ++ if (fetch_simple_param(type, proc, context, arguments, ++ info, &val) < 0) ++ goto fail; ++ ++ int stop = 0; ++ switch (param_pack_stop(param, e, &val)) { ++ case PPCB_ERR: ++ goto fail; ++ case PPCB_STOP: ++ stop = 1; ++ case PPCB_CONT: ++ break; ++ } ++ ++ if (stop) ++ break; ++ } ++ ++ param_pack_done(param, e); ++ return ret; ++} ++ ++static int ++fetch_one_param(enum tof type, Process *proc, struct fetch_context *context, ++ struct value_dict *arguments, struct param *param, ++ ssize_t *params_leftp) ++{ ++ switch (param->flavor) { ++ case PARAM_FLAVOR_TYPE: ++ return fetch_simple_param(type, proc, context, arguments, ++ param->u.type.type, NULL); ++ ++ case PARAM_FLAVOR_PACK: ++ return fetch_param_pack(type, proc, context, arguments, ++ param, params_leftp); ++ ++ case PARAM_FLAVOR_STOP: ++ fetch_param_stop(arguments, params_leftp); ++ return 0; ++ } ++ ++ assert(!"Invalid param flavor!"); ++ abort(); ++} ++ ++static int ++fetch_params(enum tof type, Process *proc, struct fetch_context *context, ++ struct value_dict *arguments, Function *func, ssize_t *params_leftp) ++{ ++ size_t i; ++ for (i = 0; i < func->num_params; ++i) ++ if (fetch_one_param(type, proc, context, arguments, ++ &func->params[i], params_leftp) < 0) ++ return -1; ++ ++ /* Implicit stop at the end of parameter list. */ ++ fetch_param_stop(arguments, params_leftp); ++ ++ return 0; ++} ++ ++static int ++output_one(struct value *val, struct value_dict *arguments) ++{ ++ int o = format_argument(options.output, val, arguments); ++ if (account_output(o) < 0) { ++ if (output_error() < 0) ++ return -1; ++ o = 1; ++ } ++ return o; ++} ++ ++static int ++output_params(struct value_dict *arguments, size_t start, size_t end, ++ int *need_delimp) ++{ ++ size_t i; ++ int need_delim = *need_delimp; ++ for (i = start; i < end; ++i) { ++ if (need_delim ++ && account_output(fprintf(options.output, ", ")) < 0) ++ return -1; ++ struct value *value = val_dict_get_num(arguments, i); ++ if (value == NULL) ++ return -1; ++ need_delim = output_one(value, arguments); ++ if (need_delim < 0) ++ return -1; ++ } ++ *need_delimp = need_delim; ++ return 0; ++} ++ + void + output_left(enum tof type, struct Process *proc, + struct library_symbol *libsym) + { + const char *function_name = libsym->name; + Function *func; +- static arg_type_info *arg_unknown = NULL; +- if (arg_unknown == NULL) +- arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN); + + if (options.summary) { + return; +@@ -174,50 +432,47 @@ output_left(enum tof type, struct Process *proc, + } + current_proc = proc; + current_depth = proc->callstack_depth; +- begin_of_line(type, proc); ++ begin_of_line(proc, type == LT_TOF_FUNCTION, 1); + if (!options.hide_caller && libsym->lib != NULL + && libsym->plt_type != LS_TOPLT_NONE) + current_column += fprintf(options.output, "%s->", + libsym->lib->soname); ++ ++ const char *name = function_name; + #ifdef USE_DEMANGLE +- current_column += +- fprintf(options.output, "%s(", +- (options.demangle +- ? my_demangle(function_name) : function_name)); +-#else +- current_column += fprintf(options.output, "%s(", function_name); ++ if (options.demangle) ++ name = my_demangle(function_name); + #endif ++ if (account_output(fprintf(options.output, "%s(", name)) < 0) ++ return; + + func = name2func(function_name); +- if (!func) { +- int i; +- for (i = 0; i < 4; i++) { +- current_column += +- display_arg(type, proc, i, arg_unknown); +- current_column += fprintf(options.output, ", "); +- } +- current_column += display_arg(type, proc, 4, arg_unknown); ++ if (func == NULL) + return; +- } else { +- int i; +- for (i = 0; i < func->num_params - func->params_right - 1; i++) { +- current_column += +- display_arg(type, proc, i, func->arg_info[i]); +- current_column += fprintf(options.output, ", "); +- } +- if (func->num_params > func->params_right) { +- current_column += +- display_arg(type, proc, i, func->arg_info[i]); +- if (func->params_right) { +- current_column += fprintf(options.output, ", "); +- } +- } +- if (func->params_right +- || func->return_info->type == ARGTYPE_STRING_N +- || func->return_info->type == ARGTYPE_ARRAY) { +- save_register_args(type, proc); +- } ++ ++ struct fetch_context *context = fetch_arg_init(type, proc, ++ func->return_info); ++ struct value_dict *arguments = malloc(sizeof(*arguments)); ++ if (arguments == NULL) ++ return; ++ val_dict_init(arguments); ++ ++ ssize_t params_left = -1; ++ int need_delim = 0; ++ if (fetch_params(type, proc, context, arguments, func, ¶ms_left) < 0 ++ || output_params(arguments, 0, params_left, &need_delim) < 0) { ++ val_dict_destroy(arguments); ++ fetch_arg_done(context); ++ arguments = NULL; ++ context = NULL; + } ++ ++ struct callstack_element *stel ++ = &proc->callstack[proc->callstack_depth - 1]; ++ stel->fetch_context = context; ++ stel->arguments = arguments; ++ stel->out.params_left = params_left; ++ stel->out.need_delim = need_delim; + } + + void +@@ -225,9 +480,8 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) + { + const char *function_name = libsym->name; + Function *func = name2func(function_name); +- static arg_type_info *arg_unknown = NULL; +- if (arg_unknown == NULL) +- arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN); ++ if (func == NULL) ++ return; + + if (options.summary) { + struct opt_c_struct *st; +@@ -268,7 +522,7 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) + current_proc = 0; + } + if (current_proc != proc) { +- begin_of_line(type, proc); ++ begin_of_line(proc, type == LT_TOF_FUNCTIONR, 1); + #ifdef USE_DEMANGLE + current_column += + fprintf(options.output, "<... %s resumed> ", +@@ -279,32 +533,48 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) + #endif + } + +- if (!func) { +- current_column += fprintf(options.output, ") "); +- tabto(options.align - 1); +- fprintf(options.output, "= "); +- display_arg(type, proc, -1, arg_unknown); +- } else { +- int i; +- for (i = func->num_params - func->params_right; +- i < func->num_params - 1; i++) { +- current_column += +- display_arg(type, proc, i, func->arg_info[i]); +- current_column += fprintf(options.output, ", "); +- } +- if (func->params_right) { +- current_column += +- display_arg(type, proc, i, func->arg_info[i]); +- } +- current_column += fprintf(options.output, ") "); +- tabto(options.align - 1); +- fprintf(options.output, "= "); +- if (func->return_info->type == ARGTYPE_VOID) { +- fprintf(options.output, ""); +- } else { +- display_arg(type, proc, -1, func->return_info); ++ struct callstack_element *stel ++ = &proc->callstack[proc->callstack_depth - 1]; ++ ++ struct fetch_context *context = stel->fetch_context; ++ ++ /* Fetch & enter into dictionary the retval first, so that ++ * other values can use it in expressions. */ ++ struct value retval; ++ int own_retval = 0; ++ if (context != NULL) { ++ value_init(&retval, proc, NULL, func->return_info, 0); ++ own_retval = 1; ++ if (fetch_retval(context, type, proc, func->return_info, ++ &retval) == 0) { ++ if (stel->arguments != NULL ++ && val_dict_push_named(stel->arguments, &retval, ++ "retval", 0) == 0) ++ own_retval = 0; + } + } ++ ++ if (stel->arguments != NULL) ++ output_params(stel->arguments, stel->out.params_left, ++ val_dict_count(stel->arguments), ++ &stel->out.need_delim); ++ ++ current_column += fprintf(options.output, ") "); ++ tabto(options.align - 1); ++ fprintf(options.output, "= "); ++ ++ output_one(&retval, stel->arguments); ++ ++ if (own_retval) ++ value_destroy(&retval); ++ ++ if (stel->arguments != NULL) { ++ val_dict_destroy(stel->arguments); ++ free(stel->arguments); ++ } ++ if (context != NULL) ++ fetch_arg_done(context); ++ + if (opt_T) { + fprintf(options.output, " <%lu.%06d>", + current_time_spent.tv_sec, +@@ -336,3 +606,44 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) + current_proc = 0; + current_column = 0; + } ++ ++static void ++do_report(const char *filename, unsigned line_no, const char *severity, ++ const char *fmt, va_list args) ++{ ++ char buf[128]; ++ vsnprintf(buf, sizeof(buf), fmt, args); ++ buf[sizeof(buf) - 1] = 0; ++ if (filename != NULL) ++ output_line(0, "%s:%d: %s: %s", ++ filename, line_no, severity, buf); ++ else ++ output_line(0, "%s: %s", severity, buf); ++} ++ ++void ++report_error(const char *filename, unsigned line_no, char *fmt, ...) ++{ ++ va_list args; ++ va_start(args, fmt); ++ do_report(filename, line_no, "error", fmt, args); ++ va_end(args); ++} ++ ++void ++report_warning(const char *filename, unsigned line_no, char *fmt, ...) ++{ ++ va_list args; ++ va_start(args, fmt); ++ do_report(filename, line_no, "warning", fmt, args); ++ va_end(args); ++} ++ ++void ++report_global_error(char *fmt, ...) ++{ ++ va_list args; ++ va_start(args, fmt); ++ do_report(NULL, 0, "error", fmt, args); ++ va_end(args); ++} +diff --git a/output.h b/output.h +index 714078f..481a385 100644 +--- a/output.h ++++ b/output.h +@@ -1,7 +1,33 @@ +-struct Process; +-struct library_symbol; ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2009 Juan Cespedes ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++#include "fetch.h" ++ ++#include "forward.h" ++ + void output_line(struct Process *proc, char *fmt, ...); + void output_left(enum tof type, struct Process *proc, + struct library_symbol *libsym); + void output_right(enum tof type, struct Process *proc, + struct library_symbol *libsym); ++ ++void report_error(char const *file, unsigned line_no, char *fmt, ...); ++void report_warning(char const *file, unsigned line_no, char *fmt, ...); ++void report_global_error(char *fmt, ...); +diff --git a/param.c b/param.c +new file mode 100644 +index 0000000..7715571 +--- /dev/null ++++ b/param.c +@@ -0,0 +1,140 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++ ++#include "param.h" ++#include "type.h" ++#include "value.h" ++#include "expr.h" ++ ++void ++param_init_type(struct param *param, struct arg_type_info *type, int own) ++{ ++ param->flavor = PARAM_FLAVOR_TYPE; ++ param->u.type.type = type; ++ param->u.type.own_type = own; ++} ++ ++void ++param_init_stop(struct param *param) ++{ ++ param->flavor = PARAM_FLAVOR_STOP; ++} ++ ++void ++param_init_pack(struct param *param, ++ struct expr_node *args, size_t nargs, int own_args, ++ struct param_enum *(*init)(struct value *cb_args, ++ size_t nargs, ++ struct value_dict *arguments), ++ int (*next)(struct param_enum *context, ++ struct arg_type_info *infop, ++ int *insert_stop), ++ enum param_status (*stop)(struct param_enum *ctx, ++ struct value *value), ++ void (*done)(struct param_enum *)) ++{ ++ param->flavor = PARAM_FLAVOR_PACK; ++ param->u.pack.args = args; ++ param->u.pack.nargs = nargs; ++ param->u.pack.own_args = own_args; ++ param->u.pack.init = init; ++ param->u.pack.next = next; ++ param->u.pack.stop = stop; ++ param->u.pack.done = done; ++} ++ ++struct param_enum * ++param_pack_init(struct param *param, struct value_dict *fargs) ++{ ++ struct value cb_args[param->u.pack.nargs]; ++ size_t i; ++ ++ /* For evaluation of argument expressions, we pass in this as ++ * a "current" value. */ ++ struct arg_type_info *void_type = type_get_simple(ARGTYPE_VOID); ++ struct value void_val; ++ value_init_detached(&void_val, NULL, void_type, 0); ++ ++ struct param_enum *ret = NULL; ++ for (i = 0; i < param->u.pack.nargs; ++i) { ++ if (expr_eval(¶m->u.pack.args[i], &void_val, ++ fargs, &cb_args[i]) < 0) ++ goto release; ++ } ++ ++ ret = param->u.pack.init(cb_args, param->u.pack.nargs, fargs); ++ ++release: ++ while (i-- > 0) ++ value_destroy(&cb_args[i]); ++ return ret; ++} ++ ++int ++param_pack_next(struct param *param, struct param_enum *context, ++ struct arg_type_info *infop, int *insert_stop) ++{ ++ return param->u.pack.next(context, infop, insert_stop); ++} ++ ++enum param_status ++param_pack_stop(struct param *param, ++ struct param_enum *context, struct value *value) ++{ ++ return param->u.pack.stop(context, value); ++} ++ ++void ++param_pack_done(struct param *param, struct param_enum *context) ++{ ++ return param->u.pack.done(context); ++} ++ ++void ++param_destroy(struct param *param) ++{ ++ if (param == NULL) ++ return; ++ ++ switch (param->flavor) { ++ case PARAM_FLAVOR_TYPE: ++ if (param->u.type.own_type) { ++ type_destroy(param->u.type.type); ++ free(param->u.type.type); ++ } ++ return; ++ ++ case PARAM_FLAVOR_PACK: ++ if (param->u.pack.own_args) { ++ size_t i; ++ for (i = 0; i < param->u.pack.nargs; ++i) ++ expr_destroy(¶m->u.pack.args[i]); ++ free(param->u.pack.args); ++ } ++ case PARAM_FLAVOR_STOP: ++ return; ++ } ++ ++ assert(!"Unknown value of param flavor!"); ++ abort(); ++} +diff --git a/param.h b/param.h +new file mode 100644 +index 0000000..5882689 +--- /dev/null ++++ b/param.h +@@ -0,0 +1,150 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef PARAM_H ++#define PARAM_H ++ ++#include "forward.h" ++ ++/* The structure param holds information about a parameter of a ++ * function. It's used to configure a function prototype. There are ++ * two flavors of parameters: ++ * ++ * - simple types ++ * - parameter packs ++ * ++ * Parameter packs are used to describe various vararg constructs. ++ * They themselves are parametrized by ltrace expressions. Those will ++ * typically be references to other arguments, but constants might ++ * also make sense, and it principle, anything can be used. */ ++ ++enum param_flavor { ++ PARAM_FLAVOR_TYPE, ++ PARAM_FLAVOR_PACK, ++ ++ /* This is for emitting arguments in two bunches. This is ++ * where we should stop emitting "left" bunch. All that's ++ * after this parameter should be emitted in the "right" ++ * bunch. */ ++ PARAM_FLAVOR_STOP, ++}; ++ ++enum param_status { ++ PPCB_ERR = -1, /* An error occurred. */ ++ PPCB_STOP, /* Stop fetching the arguments. */ ++ PPCB_CONT, /* Display this argument and keep going. */ ++}; ++ ++/* Each parameter enumerator defines its own context object. ++ * Definitions of these are in respective .c files of each ++ * enumerator. */ ++struct param_enum; ++ ++/* int printf(string, pack(format, arg1)); */ ++struct param { ++ enum param_flavor flavor; ++ union { ++ struct { ++ struct arg_type_info *type; ++ int own_type; ++ } type; ++ struct { ++ struct expr_node *args; ++ size_t nargs; ++ int own_args; ++ ++ struct param_enum *(*init)(struct value *cb_args, ++ size_t nargs, ++ struct value_dict *arguments); ++ int (*next)(struct param_enum *self, ++ struct arg_type_info *info, ++ int *insert_stop); ++ enum param_status (*stop)(struct param_enum *self, ++ struct value *value); ++ void (*done)(struct param_enum *self); ++ } pack; ++ } u; ++}; ++ ++/* Initialize simple type parameter. TYPE is owned and released by ++ * PARAM if OWN_TYPE. */ ++void param_init_type(struct param *param, ++ struct arg_type_info *type, int own_type); ++ ++/* Initialize a stop. */ ++void param_init_stop(struct param *param); ++ ++/* Initialize parameter pack PARAM. ARGS is an array of expressions ++ * with parameters. ARGS is owned and released by the pack if ++ * OWN_ARGS. NARGS is number of ARGS. ++ * ++ * When the parameter pack should be expanded, those expressions are ++ * evaluated and passed to the INIT callback. This has to return a ++ * non-NULL context object. ++ * ++ * The NEXT callback is then called repeatedly, and should initialize ++ * its INFOP argument to a type of the next parameter in the pack. ++ * When there are no more parameters in the pack, the NEXT callback ++ * will set INFOP to a VOID parameter. If the callback sets ++ * INSERT_STOP to a non-zero value, a stop parameter shall be inserted ++ * before this actual parameter. ++ * ++ * Core then uses the passed-in type to fetch the next argument, which ++ * is in turn passed to STOP callback. This callback then tells ++ * ltrace core what to do next: whether there are more arguments, and ++ * if not, whether this argument should be displayed. ++ * ++ * After the enumeration is ended, DONE callback is called. */ ++void param_init_pack(struct param *param, ++ struct expr_node *args, size_t nargs, int own_args, ++ struct param_enum *(*init)(struct value *cb_args, ++ size_t nargs, ++ struct value_dict *arguments), ++ int (*next)(struct param_enum *self, ++ struct arg_type_info *infop, ++ int *insert_stop), ++ enum param_status (*stop)(struct param_enum *self, ++ struct value *value), ++ void (*done)(struct param_enum *self)); ++ ++/* Start enumerating types in parameter pack. This evaluates the ++ * parameter the pack arguments and calls the init callback. See the ++ * documentation of param_init_pack for details. */ ++struct param_enum *param_pack_init(struct param *param, ++ struct value_dict *fargs); ++ ++/* Ask for next type in enumeration. See the documentation of ++ * param_init_pack for details. */ ++int param_pack_next(struct param *param, struct param_enum *self, ++ struct arg_type_info *infop, int *insert_stop); ++ ++/* Ask whether we should stop enumerating. See the documentation of ++ * param_init_pack for details. */ ++enum param_status param_pack_stop(struct param *param, ++ struct param_enum *self, struct value *value); ++ ++/* Finish enumerating types in parameter pack. See the documentation ++ * of param_init_pack for details. */ ++void param_pack_done(struct param *param, struct param_enum *self); ++ ++/* Destroy data held by PARAM, but not the PARAM pointer itself. */ ++void param_destroy(struct param *param); ++ ++#endif /* PARAM_H */ +diff --git a/printf.c b/printf.c +new file mode 100644 +index 0000000..1fe3025 +--- /dev/null ++++ b/printf.c +@@ -0,0 +1,351 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 1998,2004,2007,2008,2009 Juan Cespedes ++ * Copyright (C) 2006 Steve Fink ++ * Copyright (C) 2006 Ian Wienand ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++ ++#include "printf.h" ++#include "type.h" ++#include "value.h" ++#include "expr.h" ++#include "zero.h" ++#include "param.h" ++#include "lens_default.h" ++ ++struct param_enum { ++ struct value array; ++ int percent; ++ size_t *future_length; ++ char *format; ++ char const *ptr; ++ char const *end; ++}; ++ ++static struct param_enum * ++param_printf_init(struct value *cb_args, size_t nargs, ++ struct value_dict *arguments) ++{ ++ assert(nargs == 1); ++ ++ /* We expect a char array pointer. */ ++ if (cb_args->type->type != ARGTYPE_POINTER ++ || cb_args->type->u.ptr_info.info->type != ARGTYPE_ARRAY ++ || (cb_args->type->u.ptr_info.info->u.array_info.elt_type->type ++ != ARGTYPE_CHAR)) ++ return NULL; ++ ++ struct param_enum *self = malloc(sizeof(*self)); ++ if (self == NULL) { ++ fail: ++ free(self); ++ return NULL; ++ } ++ ++ if (value_init_deref(&self->array, cb_args) < 0) ++ goto fail; ++ ++ assert(self->array.type->type == ARGTYPE_ARRAY); ++ ++ self->format = (char *)value_get_data(&self->array, arguments); ++ if (self->format == NULL) ++ goto fail; ++ ++ size_t size = value_size(&self->array, arguments); ++ if (size == (size_t)-1) ++ goto fail; ++ ++ self->percent = 0; ++ self->ptr = self->format; ++ self->end = self->format + size; ++ self->future_length = NULL; ++ return self; ++} ++ ++static void ++drop_future_length(struct param_enum *self) ++{ ++ if (self->future_length != NULL) { ++ free(self->future_length); ++ self->future_length = NULL; ++ } ++} ++ ++static int ++form_next_param(struct param_enum *self, ++ enum arg_type format_type, enum arg_type elt_type, ++ unsigned hlf, unsigned lng, char *len_buf, size_t len_buf_len, ++ struct arg_type_info *infop) ++{ ++ /* XXX note: Some types are wrong because we lack ++ ARGTYPE_LONGLONG, ARGTYPE_UCHAR and ARGTYPE_SCHAR. */ ++ assert(lng <= 2); ++ assert(hlf <= 2); ++ static enum arg_type ints[] = ++ { ARGTYPE_CHAR, ARGTYPE_SHORT, ARGTYPE_INT, ++ ARGTYPE_LONG, ARGTYPE_ULONG }; ++ static enum arg_type uints[] = ++ { ARGTYPE_CHAR, ARGTYPE_USHORT, ARGTYPE_UINT, ++ ARGTYPE_ULONG, ARGTYPE_ULONG }; ++ ++ struct arg_type_info *elt_info = NULL; ++ if (format_type == ARGTYPE_ARRAY || format_type == ARGTYPE_POINTER) ++ elt_info = type_get_simple(elt_type); ++ else if (format_type == ARGTYPE_INT) ++ format_type = ints[2 + lng - hlf]; ++ else if (format_type == ARGTYPE_UINT) ++ format_type = uints[2 + lng - hlf]; ++ ++ ++ if (format_type == ARGTYPE_ARRAY) { ++ struct expr_node *node = NULL; ++ if (len_buf_len != 0 ++ || self->future_length != NULL) { ++ struct tmp { ++ struct expr_node node; ++ struct arg_type_info type; ++ }; ++ struct tmp *len = malloc(sizeof(*len)); ++ if (len == NULL) { ++ fail: ++ free(len); ++ return -1; ++ } ++ ++ len->type.type = ARGTYPE_LONG; ++ ++ long l; ++ if (self->future_length != NULL) { ++ l = *self->future_length; ++ drop_future_length(self); ++ } else { ++ l = atol(len_buf); ++ } ++ ++ expr_init_const_word(&len->node, l, &len->type, 0); ++ ++ node = build_zero_w_arg(&len->node, 1); ++ if (node == NULL) ++ goto fail; ++ ++ } else { ++ node = expr_node_zero(); ++ } ++ ++ assert(node != NULL); ++ type_init_array(infop, elt_info, 0, node, 1); ++ ++ } else if (format_type == ARGTYPE_POINTER) { ++ type_init_pointer(infop, elt_info, 1); ++ ++ } else { ++ *infop = *type_get_simple(format_type); ++ } ++ ++ return 0; ++} ++ ++static int ++param_printf_next(struct param_enum *self, struct arg_type_info *infop, ++ int *insert_stop) ++{ ++ unsigned hlf = 0; ++ unsigned lng = 0; ++ enum arg_type format_type = ARGTYPE_VOID; ++ enum arg_type elt_type = ARGTYPE_VOID; ++ char len_buf[25] = {}; ++ size_t len_buf_len = 0; ++ struct lens *lens = NULL; ++ ++ for (; self->ptr < self->end; ++self->ptr) { ++ if (!self->percent) { ++ if (*self->ptr == '%') ++ self->percent = 1; ++ continue; ++ } ++ ++ switch (*self->ptr) { ++ case '#': case ' ': case '-': ++ case '+': case 'I': case '\'': ++ /* These are only important for formatting, ++ * not for interpreting the type. */ ++ continue; ++ ++ case '*': ++ /* Length parameter given in the next ++ * argument. */ ++ if (self->future_length == NULL) ++ /* This should really be an assert, ++ * but we can't just fail on invalid ++ * format string. */ ++ self->future_length ++ = malloc(sizeof(*self->future_length)); ++ ++ if (self->future_length != NULL) { ++ ++self->ptr; ++ format_type = ARGTYPE_INT; ++ break; ++ } ++ ++ case '0': ++ case '1': case '2': case '3': ++ case '4': case '5': case '6': ++ case '7': case '8': case '9': ++ /* Field length likewise, but we need to parse ++ * this to attach the appropriate string ++ * length expression. */ ++ if (len_buf_len < sizeof(len_buf) - 1) ++ len_buf[len_buf_len++] = *self->ptr; ++ continue; ++ ++ case 'h': ++ if (hlf < 2) ++ hlf++; ++ continue; ++ ++ case 'l': ++ if (lng < 2) ++ lng++; ++ continue; ++ ++ case 'q': ++ lng = 2; ++ continue; ++ ++ case 'L': /* long double */ ++ lng = 1; ++ continue; ++ ++ case 'j': /* intmax_t */ ++ /* XXX ABI should know */ ++ lng = 2; ++ continue; ++ ++ case 't': /* ptrdiff_t */ ++ case 'Z': case 'z': /* size_t */ ++ lng = 1; /* XXX ABI should tell */ ++ continue; ++ ++ case 'd': ++ case 'i': ++ format_type = ARGTYPE_INT; ++ self->percent = 0; ++ break; ++ ++ case 'o': ++ lens = &octal_lens; ++ goto uint; ++ ++ case 'x': case 'X': ++ lens = &hex_lens; ++ case 'u': ++ uint: ++ format_type = ARGTYPE_UINT; ++ self->percent = 0; ++ break; ++ ++ case 'e': case 'E': ++ case 'f': case 'F': ++ case 'g': case 'G': ++ case 'a': case 'A': ++ format_type = ARGTYPE_DOUBLE; ++ self->percent = 0; ++ break; ++ ++ case 'C': /* like "lc" */ ++ if (lng == 0) ++ lng++; ++ case 'c': ++ /* XXX "lc" means wchar_t string. */ ++ format_type = ARGTYPE_CHAR; ++ self->percent = 0; ++ break; ++ ++ case 'S': /* like "ls" */ ++ if (lng == 0) ++ lng++; ++ case 's': ++ format_type = ARGTYPE_ARRAY; ++ /* XXX "ls" means wchar_t string. */ ++ elt_type = ARGTYPE_CHAR; ++ self->percent = 0; ++ lens = &string_lens; ++ break; ++ ++ case 'p': ++ case 'n': /* int* where to store no. of printed chars. */ ++ format_type = ARGTYPE_POINTER; ++ elt_type = ARGTYPE_VOID; ++ self->percent = 0; ++ break; ++ ++ case 'm': /* (glibc) print argument of errno */ ++ case '%': ++ lng = 0; ++ hlf = 0; ++ self->percent = 0; ++ continue; ++ ++ default: ++ continue; ++ } ++ ++ /* If we got here, the type must have been set. */ ++ assert(format_type != ARGTYPE_VOID); ++ ++ if (form_next_param(self, format_type, elt_type, hlf, lng, ++ len_buf, len_buf_len, infop) < 0) ++ return -1; ++ ++ infop->lens = lens; ++ infop->own_lens = 0; ++ ++ return 0; ++ } ++ ++ infop->type = ARGTYPE_VOID; ++ return 0; ++} ++ ++static enum param_status ++param_printf_stop(struct param_enum *self, struct value *value) ++{ ++ if (self->future_length != NULL ++ && value_extract_word(value, (long *)self->future_length, NULL) < 0) ++ drop_future_length(self); ++ ++ return PPCB_CONT; ++} ++ ++static void ++param_printf_done(struct param_enum *context) ++{ ++ free(context); ++} ++ ++void ++param_pack_init_printf(struct param *param, struct expr_node *arg, int own_arg) ++{ ++ param_init_pack(param, arg, 1, own_arg, ++ ¶m_printf_init, ¶m_printf_next, ++ ¶m_printf_stop, ¶m_printf_done); ++} +diff --git a/printf.h b/printf.h +new file mode 100644 +index 0000000..983c951 +--- /dev/null ++++ b/printf.h +@@ -0,0 +1,34 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef PRINTF_H ++#define PRINTF_H ++ ++#include ++#include "forward.h" ++ ++/* Wrapper around param_init_pack with callbacks to handle ++ * printf-style format strings. ARG should be an expression that ++ * evaluates to a pointer to a character array with the format string ++ * contents. */ ++void param_pack_init_printf(struct param *param, ++ struct expr_node *arg, int own_arg); ++ ++#endif /* PRINTF_H */ +diff --git a/proc.c b/proc.c +index 3e6d5cb..b280df8 100644 +--- a/proc.c ++++ b/proc.c +@@ -1,20 +1,45 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2010 Joe Damato ++ * Copyright (C) 1998,2009 Juan Cespedes ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ + #include "config.h" + ++#include ++#include ++#include ++#include ++#include ++#include ++ + #if defined(HAVE_LIBUNWIND) + #include + #include + #endif /* defined(HAVE_LIBUNWIND) */ + +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "common.h" ++#include "backend.h" + #include "breakpoint.h" ++#include "debug.h" ++#include "fetch.h" + #include "proc.h" ++#include "value_dict.h" + + #ifndef ARCH_HAVE_PROCESS_DATA + int +@@ -237,7 +262,7 @@ int + process_clone(struct Process *retp, struct Process *proc, pid_t pid) + { + if (process_bare_init(retp, proc->filename, pid, 0) < 0) { +- fail: ++ fail1: + fprintf(stderr, "failed to clone process %d->%d : %s\n", + proc->pid, pid, strerror(errno)); + return -1; +@@ -268,7 +293,7 @@ process_clone(struct Process *retp, struct Process *proc, pid_t pid) + free(lib); + lib = next; + } +- goto fail; ++ goto fail1; + } + + nlibp = &(*nlibp)->next; +@@ -282,16 +307,57 @@ process_clone(struct Process *retp, struct Process *proc, pid_t pid) + .error = 0, + }; + dict_apply_to_all(proc->breakpoints, &clone_single_bp, &data); ++ if (data.error < 0) ++ goto fail2; + + /* And finally the call stack. */ + memcpy(retp->callstack, proc->callstack, sizeof(retp->callstack)); + retp->callstack_depth = proc->callstack_depth; + +- if (data.error < 0) +- goto fail2; ++ size_t i; ++ for (i = 0; i < retp->callstack_depth; ++i) { ++ struct fetch_context *ctx = retp->callstack[i].fetch_context; ++ if (ctx != NULL) { ++ struct fetch_context *nctx = fetch_arg_clone(retp, ctx); ++ if (nctx == NULL) { ++ size_t j; ++ fail3: ++ for (j = 0; j < i; ++j) { ++ nctx = retp->callstack[i].fetch_context; ++ fetch_arg_done(nctx); ++ retp->callstack[i].fetch_context = NULL; ++ } ++ goto fail2; ++ } ++ retp->callstack[i].fetch_context = nctx; ++ } ++ ++ struct value_dict *args = retp->callstack[i].arguments; ++ if (args != NULL) { ++ struct value_dict *nargs = malloc(sizeof(*nargs)); ++ if (nargs == NULL ++ || val_dict_clone(nargs, args) < 0) { ++ size_t j; ++ fail4: ++ for (j = 0; j < i; ++j) { ++ nargs = retp->callstack[i].arguments; ++ val_dict_destroy(nargs); ++ free(nargs); ++ retp->callstack[i].arguments = NULL; ++ } ++ ++ /* Pretend that this round went well, ++ * so that fail3 frees I-th ++ * fetch_context. */ ++ ++i; ++ goto fail3; ++ } ++ retp->callstack[i].arguments = nargs; ++ } ++ } + + if (arch_process_clone(retp, proc) < 0) +- goto fail2; ++ goto fail4; + + return 0; + } +@@ -528,23 +594,6 @@ clear_leader(struct Process *proc, void *data) + return CBS_CONT; + } + +-static enum ecb_status +-event_for_proc(Event * event, void * data) +-{ +- if (event->proc == data) +- return ecb_deque; +- else +- return ecb_cont; +-} +- +-static void +-delete_events_for(Process * proc) +-{ +- Event * event; +- while ((event = each_qd_event(&event_for_proc, proc)) != NULL) +- free(event); +-} +- + void + remove_process(Process *proc) + { +@@ -554,7 +603,7 @@ remove_process(Process *proc) + each_task(proc, NULL, &clear_leader, NULL); + + unlist_process(proc); +- delete_events_for(proc); ++ process_removed(proc); + process_destroy(proc); + free(proc); + } +diff --git a/proc.h b/proc.h +index 443bd8e..86bf7da 100644 +--- a/proc.h ++++ b/proc.h +@@ -27,6 +27,8 @@ + #ifndef _PROC_H_ + #define _PROC_H_ + ++#include "config.h" ++ + #if defined(HAVE_LIBUNWIND) + # include + #endif /* defined(HAVE_LIBUNWIND) */ +@@ -60,6 +60,11 @@ enum process_state { + STATE_IGNORED /* ignore this process (it's a fork and no -f was used) */ + }; + ++struct output_state { ++ size_t params_left; ++ int need_delim; ++}; ++ + struct callstack_element { + union { + int syscall; +@@ -68,7 +73,9 @@ struct callstack_element { + int is_syscall; + void * return_addr; + struct timeval time_spent; +- void * arch_ptr; ++ struct fetch_context *fetch_context; ++ struct value_dict *arguments; ++ struct output_state out; + }; + + /* XXX We should get rid of this. */ +@@ -96,7 +103,7 @@ struct Process { + unsigned int personality; + int tracesysgood; /* signal indicating a PTRACE_SYSCALL trap */ + +- int callstack_depth; ++ size_t callstack_depth; + struct callstack_element callstack[MAX_CALLDEPTH]; + + /* Linked list of libraries in backwards order of mapping. +diff --git a/read_config_file.c b/read_config_file.c +index b4b1b56..87e87e7 100644 +--- a/read_config_file.c ++++ b/read_config_file.c +@@ -1,95 +1,100 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 1998,1999,2003,2007,2008,2009 Juan Cespedes ++ * Copyright (C) 2006 Ian Wienand ++ * Copyright (C) 2006 Steve Fink ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ + #include "config.h" + + #include + #include + #include ++#include ++#include ++#include + + #include "common.h" ++#include "output.h" ++#include "expr.h" ++#include "param.h" ++#include "printf.h" ++#include "zero.h" ++#include "type.h" ++#include "lens.h" ++#include "lens_default.h" ++#include "lens_enum.h" + + static int line_no; + static char *filename; +-static int error_count = 0; + +-static arg_type_info *parse_type(char **str); ++static struct arg_type_info *parse_nonpointer_type(char **str, ++ struct param **extra_param, ++ size_t param_num, int *ownp); ++static struct arg_type_info *parse_type(char **str, struct param **extra_param, ++ size_t param_num, int *ownp); ++static struct arg_type_info *parse_lens(char **str, struct param **extra_param, ++ size_t param_num, int *ownp); ++static int parse_enum(char **str, struct arg_type_info **retp, int *ownp); + + Function *list_of_functions = NULL; + +-/* Map of strings to type names. These do not need to be in any +- * particular order */ +-static struct list_of_pt_t { +- char *name; +- enum arg_type pt; +-} list_of_pt[] = { +- { +- "void", ARGTYPE_VOID}, { +- "int", ARGTYPE_INT}, { +- "uint", ARGTYPE_UINT}, { +- "long", ARGTYPE_LONG}, { +- "ulong", ARGTYPE_ULONG}, { +- "octal", ARGTYPE_OCTAL}, { +- "char", ARGTYPE_CHAR}, { +- "short", ARGTYPE_SHORT}, { +- "ushort", ARGTYPE_USHORT}, { +- "float", ARGTYPE_FLOAT}, { +- "double", ARGTYPE_DOUBLE}, { +- "addr", ARGTYPE_ADDR}, { +- "file", ARGTYPE_FILE}, { +- "format", ARGTYPE_FORMAT}, { +- "string", ARGTYPE_STRING}, { +- "array", ARGTYPE_ARRAY}, { +- "struct", ARGTYPE_STRUCT}, { +- "enum", ARGTYPE_ENUM}, { +- NULL, ARGTYPE_UNKNOWN} /* Must finish with NULL */ +-}; +- +-/* Array of prototype objects for each of the types. The order in this +- * array must exactly match the list of enumerated values in +- * common.h */ +-static arg_type_info arg_type_prototypes[] = { +- { ARGTYPE_VOID }, +- { ARGTYPE_INT }, +- { ARGTYPE_UINT }, +- { ARGTYPE_LONG }, +- { ARGTYPE_ULONG }, +- { ARGTYPE_OCTAL }, +- { ARGTYPE_CHAR }, +- { ARGTYPE_SHORT }, +- { ARGTYPE_USHORT }, +- { ARGTYPE_FLOAT }, +- { ARGTYPE_DOUBLE }, +- { ARGTYPE_ADDR }, +- { ARGTYPE_FILE }, +- { ARGTYPE_FORMAT }, +- { ARGTYPE_STRING }, +- { ARGTYPE_STRING_N }, +- { ARGTYPE_ARRAY }, +- { ARGTYPE_ENUM }, +- { ARGTYPE_STRUCT }, +- { ARGTYPE_POINTER }, +- { ARGTYPE_UNKNOWN } +-}; +- +-arg_type_info * +-lookup_prototype(enum arg_type at) { +- if (at >= 0 && at <= ARGTYPE_COUNT) +- return &arg_type_prototypes[at]; +- else +- return &arg_type_prototypes[ARGTYPE_COUNT]; /* UNKNOWN */ +-} +- +-static arg_type_info * +-str2type(char **str) { +- struct list_of_pt_t *tmp = &list_of_pt[0]; +- +- while (tmp->name) { +- if (!strncmp(*str, tmp->name, strlen(tmp->name)) +- && index(" ,()#*;012345[", *(*str + strlen(tmp->name)))) { +- *str += strlen(tmp->name); +- return lookup_prototype(tmp->pt); +- } +- tmp++; +- } +- return lookup_prototype(ARGTYPE_UNKNOWN); ++static int ++parse_arg_type(char **name, enum arg_type *ret) ++{ ++ char *rest = NULL; ++ enum arg_type candidate; ++ ++#define KEYWORD(KWD, TYPE) \ ++ do { \ ++ if (strncmp(*name, KWD, sizeof(KWD) - 1) == 0) { \ ++ rest = *name + sizeof(KWD) - 1; \ ++ candidate = TYPE; \ ++ goto ok; \ ++ } \ ++ } while (0) ++ ++ KEYWORD("void", ARGTYPE_VOID); ++ KEYWORD("int", ARGTYPE_INT); ++ KEYWORD("uint", ARGTYPE_UINT); ++ KEYWORD("long", ARGTYPE_LONG); ++ KEYWORD("ulong", ARGTYPE_ULONG); ++ KEYWORD("char", ARGTYPE_CHAR); ++ KEYWORD("short", ARGTYPE_SHORT); ++ KEYWORD("ushort", ARGTYPE_USHORT); ++ KEYWORD("float", ARGTYPE_FLOAT); ++ KEYWORD("double", ARGTYPE_DOUBLE); ++ KEYWORD("array", ARGTYPE_ARRAY); ++ KEYWORD("struct", ARGTYPE_STRUCT); ++ ++ assert(rest == NULL); ++ return -1; ++ ++#undef KEYWORD ++ ++ok: ++ if (isalnum(*rest)) ++ return -1; ++ ++ *name = rest; ++ *ret = candidate; ++ return 0; + } + + static void +@@ -102,6 +107,10 @@ eat_spaces(char **str) { + static char * + xstrndup(char *str, size_t len) { + char *ret = (char *) malloc(len + 1); ++ if (ret == NULL) { ++ report_global_error("malloc: %s", strerror(errno)); ++ return NULL; ++ } + strncpy(ret, str, len); + ret[len] = 0; + return ret; +@@ -111,10 +120,8 @@ static char * + parse_ident(char **str) { + char *ident = *str; + +- if (!isalnum(**str) && **str != '_') { +- output_line(0, "Syntax error in `%s', line %d: Bad identifier", +- filename, line_no); +- error_count++; ++ if (!isalpha(**str) && **str != '_') { ++ report_error(filename, line_no, "bad identifier"); + return NULL; + } + +@@ -158,61 +165,189 @@ start_of_arg_sig(char *str) { + } + + static int +-parse_int(char **str) { ++parse_int(char **str, long *ret) ++{ + char *end; + long n = strtol(*str, &end, 0); + if (end == *str) { +- output_line(0, "Syntax error in `%s', line %d: Bad number (%s)", +- filename, line_no, *str); +- error_count++; +- return 0; ++ report_error(filename, line_no, "bad number"); ++ return -1; + } + + *str = end; +- return n; ++ if (ret != NULL) ++ *ret = n; ++ return 0; ++} ++ ++static int ++check_nonnegative(long l) ++{ ++ if (l < 0) { ++ report_error(filename, line_no, ++ "expected non-negative value, got %ld", l); ++ return -1; ++ } ++ return 0; ++} ++ ++static int ++check_int(long l) ++{ ++ int i = l; ++ if ((long)i != l) { ++ report_error(filename, line_no, ++ "Number too large: %ld", l); ++ return -1; ++ } ++ return 0; ++} ++ ++static int ++parse_char(char **str, char expected) ++{ ++ if (**str != expected) { ++ report_error(filename, line_no, ++ "expected '%c', got '%c'", expected, **str); ++ return -1; ++ } ++ ++ ++*str; ++ return 0; ++} ++ ++static struct expr_node *parse_argnum(char **str, int zero); ++ ++static struct expr_node * ++parse_zero(char **str, struct expr_node *ret) ++{ ++ eat_spaces(str); ++ if (**str == '(') { ++ ++*str; ++ struct expr_node *arg = parse_argnum(str, 0); ++ if (arg == NULL) ++ return NULL; ++ if (parse_char(str, ')') < 0) { ++ fail: ++ expr_destroy(arg); ++ free(arg); ++ return NULL; ++ } ++ ++ struct expr_node *ret = build_zero_w_arg(arg, 1); ++ if (ret == NULL) ++ goto fail; ++ return ret; ++ ++ } else { ++ return expr_node_zero(); ++ } ++} ++ ++static int ++wrap_in_zero(struct expr_node **nodep) ++{ ++ struct expr_node *n = build_zero_w_arg(*nodep, 1); ++ if (n == NULL) ++ return -1; ++ *nodep = n; ++ return 0; + } + + /* + * Input: +- * argN : The value of argument #N, counting from 1 (arg0 = retval) ++ * argN : The value of argument #N, counting from 1 + * eltN : The value of element #N of the containing structure + * retval : The return value +- * 0 : Error +- * N : The numeric value N, if N > 0 +- * +- * Output: +- * > 0 actual numeric value +- * = 0 return value +- * < 0 (arg -n), counting from one ++ * N : The numeric value N + */ +-static int +-parse_argnum(char **str) { +- int multiplier = 1; +- int n = 0; +- +- if (strncmp(*str, "arg", 3) == 0) { +- (*str) += 3; +- multiplier = -1; +- } else if (strncmp(*str, "elt", 3) == 0) { +- (*str) += 3; +- multiplier = -1; +- } else if (strncmp(*str, "retval", 6) == 0) { +- (*str) += 6; +- return 0; +- } ++static struct expr_node * ++parse_argnum(char **str, int zero) ++{ ++ struct expr_node *expr = malloc(sizeof(*expr)); ++ if (expr == NULL) ++ return NULL; ++ ++ if (isdigit(**str)) { ++ long l; ++ if (parse_int(str, &l) < 0 ++ || check_nonnegative(l) < 0 ++ || check_int(l) < 0) ++ goto fail; + +- n = parse_int(str); ++ expr_init_const_word(expr, l, type_get_simple(ARGTYPE_LONG), 0); + +- return n * multiplier; ++ if (zero && wrap_in_zero(&expr) < 0) ++ goto fail; ++ ++ return expr; ++ ++ } else { ++ char *name = parse_ident(str); ++ if (name == NULL) ++ goto fail; ++ ++ int is_arg = strncmp(name, "arg", 3) == 0; ++ int is_elt = !is_arg && strncmp(name, "elt", 3) == 0; ++ if (is_arg || is_elt) { ++ long l; ++ name += 3; ++ if (parse_int(&name, &l) < 0 ++ || check_int(l) < 0) ++ goto fail; ++ ++ if (is_arg) { ++ expr_init_argno(expr, l - 1); ++ } else { ++ struct expr_node *e_up = malloc(sizeof(*e_up)); ++ struct expr_node *e_ix = malloc(sizeof(*e_ix)); ++ if (e_up == NULL || e_ix == NULL) { ++ free(e_up); ++ free(e_ix); ++ goto fail; ++ } ++ ++ expr_init_up(e_up, expr_self(), 0); ++ struct arg_type_info *ti ++ = type_get_simple(ARGTYPE_LONG); ++ expr_init_const_word(e_ix, l - 1, ti, 0); ++ expr_init_index(expr, e_up, 1, e_ix, 1); ++ } ++ ++ } else if (strcmp(name, "retval") == 0) { ++ expr_init_named(expr, "retval", 0); ++ ++ } else if (strcmp(name, "zero") == 0) { ++ struct expr_node *ret = parse_zero(str, expr); ++ if (ret == NULL) ++ goto fail; ++ return ret; ++ ++ } else { ++ report_error(filename, line_no, ++ "Unknown length specifier: '%s'", name); ++ goto fail; ++ } ++ ++ if (zero && wrap_in_zero(&expr) < 0) ++ goto fail; ++ ++ return expr; ++ } ++ ++fail: ++ free(expr); ++ return NULL; + } + + struct typedef_node_t { + char *name; +- arg_type_info *info; ++ struct arg_type_info *info; ++ int own_type; + struct typedef_node_t *next; + } *typedefs = NULL; + +-static arg_type_info * ++static struct arg_type_info * + lookup_typedef(char **str) { + struct typedef_node_t *node; + char *end = *str; +@@ -231,11 +366,22 @@ lookup_typedef(char **str) { + return NULL; + } + ++static struct typedef_node_t * ++insert_typedef(char *name, struct arg_type_info *info, int own_type) ++{ ++ struct typedef_node_t *binding = malloc(sizeof(*binding)); ++ binding->name = name; ++ binding->info = info; ++ binding->own_type = own_type; ++ binding->next = typedefs; ++ typedefs = binding; ++ return binding; ++} ++ + static void + parse_typedef(char **str) { + char *name; +- arg_type_info *info; +- struct typedef_node_t *binding; ++ struct arg_type_info *info; + + (*str) += strlen("typedef"); + eat_spaces(str); +@@ -245,386 +391,689 @@ parse_typedef(char **str) { + + // Skip = sign + eat_spaces(str); +- if (**str != '=') { +- output_line(0, +- "Syntax error in `%s', line %d: expected '=', got '%c'", +- filename, line_no, **str); +- error_count++; ++ if (parse_char(str, '=') < 0) + return; +- } +- (*str)++; + eat_spaces(str); + + // Parse the type +- info = parse_type(str); ++ int own; ++ info = parse_type(str, NULL, 0, &own); + +- // Insert onto beginning of linked list +- binding = malloc(sizeof(*binding)); +- binding->name = name; +- binding->info = info; +- binding->next = typedefs; +- typedefs = binding; ++ insert_typedef(name, info, own); ++} ++ ++static void ++destroy_fun(Function *fun) ++{ ++ size_t i; ++ if (fun == NULL) ++ return; ++ if (fun->own_return_info) { ++ type_destroy(fun->return_info); ++ free(fun->return_info); ++ } ++ for (i = 0; i < fun->num_params; ++i) ++ param_destroy(&fun->params[i]); ++ free(fun->params); + } + +-static size_t +-arg_sizeof(arg_type_info * arg) { +- if (arg->type == ARGTYPE_CHAR) { +- return sizeof(char); +- } else if (arg->type == ARGTYPE_SHORT || arg->type == ARGTYPE_USHORT) { +- return sizeof(short); +- } else if (arg->type == ARGTYPE_FLOAT) { +- return sizeof(float); +- } else if (arg->type == ARGTYPE_DOUBLE) { +- return sizeof(double); +- } else if (arg->type == ARGTYPE_ENUM) { +- return sizeof(int); +- } else if (arg->type == ARGTYPE_STRUCT) { +- return arg->u.struct_info.size; +- } else if (arg->type == ARGTYPE_POINTER) { +- return sizeof(void*); +- } else if (arg->type == ARGTYPE_ARRAY) { +- if (arg->u.array_info.len_spec > 0) +- return arg->u.array_info.len_spec * arg->u.array_info.elt_size; ++/* Syntax: struct ( type,type,type,... ) */ ++static int ++parse_struct(char **str, struct arg_type_info *info) ++{ ++ eat_spaces(str); ++ if (parse_char(str, '(') < 0) ++ return -1; ++ ++ eat_spaces(str); // Empty arg list with whitespace inside ++ ++ type_init_struct(info); ++ ++ while (1) { ++ eat_spaces(str); ++ if (**str == 0 || **str == ')') { ++ parse_char(str, ')'); ++ return 0; ++ } ++ ++ /* Field delimiter. */ ++ if (type_struct_size(info) > 0) ++ parse_char(str, ','); ++ ++ eat_spaces(str); ++ int own; ++ struct arg_type_info *field = parse_lens(str, NULL, 0, &own); ++ if (field == NULL || type_struct_add(info, field, own)) { ++ type_destroy(info); ++ return -1; ++ } ++ } ++} ++ ++static int ++parse_string(char **str, struct arg_type_info **retp, int *ownp) ++{ ++ struct arg_type_info *info = malloc(sizeof(*info) * 2); ++ if (info == NULL) { ++ fail: ++ free(info); ++ return -1; ++ } ++ ++ struct expr_node *length; ++ int own_length; ++ int with_arg = 0; ++ ++ if (isdigit(**str)) { ++ /* string0 is string[retval], length is zero(retval) ++ * stringN is string[argN], length is zero(argN) */ ++ long l; ++ if (parse_int(str, &l) < 0 ++ || check_int(l) < 0) ++ goto fail; ++ ++ struct expr_node *length_arg = malloc(sizeof(*length_arg)); ++ if (length_arg == NULL) ++ goto fail; ++ ++ if (l == 0) ++ expr_init_named(length_arg, "retval", 0); + else +- return sizeof(void *); ++ expr_init_argno(length_arg, l - 1); ++ ++ length = build_zero_w_arg(length_arg, 1); ++ if (length == NULL) { ++ expr_destroy(length_arg); ++ free(length_arg); ++ goto fail; ++ } ++ own_length = 1; ++ + } else { +- return sizeof(int); +- } +-} +- +-#undef alignof +-#define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st)) +-static size_t +-arg_align(arg_type_info * arg) { +- struct { char c; char C; } cC; +- struct { char c; short s; } cs; +- struct { char c; int i; } ci; +- struct { char c; long l; } cl; +- struct { char c; void* p; } cp; +- struct { char c; float f; } cf; +- struct { char c; double d; } cd; +- +- static size_t char_alignment = alignof(C, cC); +- static size_t short_alignment = alignof(s, cs); +- static size_t int_alignment = alignof(i, ci); +- static size_t long_alignment = alignof(l, cl); +- static size_t ptr_alignment = alignof(p, cp); +- static size_t float_alignment = alignof(f, cf); +- static size_t double_alignment = alignof(d, cd); +- +- switch (arg->type) { +- case ARGTYPE_LONG: +- case ARGTYPE_ULONG: +- return long_alignment; +- case ARGTYPE_CHAR: +- return char_alignment; +- case ARGTYPE_SHORT: +- case ARGTYPE_USHORT: +- return short_alignment; +- case ARGTYPE_FLOAT: +- return float_alignment; +- case ARGTYPE_DOUBLE: +- return double_alignment; +- case ARGTYPE_ADDR: +- case ARGTYPE_FILE: +- case ARGTYPE_FORMAT: +- case ARGTYPE_STRING: +- case ARGTYPE_STRING_N: +- case ARGTYPE_POINTER: +- return ptr_alignment; +- +- case ARGTYPE_ARRAY: +- return arg_align(&arg->u.array_info.elt_type[0]); +- +- case ARGTYPE_STRUCT: +- return arg_align(arg->u.struct_info.fields[0]); +- +- default: +- return int_alignment; +- } +-} +- +-static size_t +-align_skip(size_t alignment, size_t offset) { +- if (offset % alignment) +- return alignment - (offset % alignment); +- else +- return 0; ++ eat_spaces(str); ++ if (**str == '[') { ++ (*str)++; ++ eat_spaces(str); ++ ++ length = parse_argnum(str, 1); ++ if (length == NULL) ++ goto fail; ++ own_length = 1; ++ ++ eat_spaces(str); ++ parse_char(str, ']'); ++ ++ } else if (**str == '(') { ++ /* Usage of "string" as lens. */ ++ ++*str; ++ ++ free(info); ++ ++ eat_spaces(str); ++ info = parse_type(str, NULL, 0, ownp); ++ if (info == NULL) ++ goto fail; ++ ++ eat_spaces(str); ++ parse_char(str, ')'); ++ ++ with_arg = 1; ++ ++ } else { ++ /* It was just a simple string after all. */ ++ length = expr_node_zero(); ++ own_length = 0; ++ } ++ } ++ ++ /* String is a pointer to array of chars. */ ++ if (!with_arg) { ++ type_init_array(&info[1], type_get_simple(ARGTYPE_CHAR), 0, ++ length, own_length); ++ ++ type_init_pointer(&info[0], &info[1], 0); ++ *ownp = 1; ++ } ++ ++ info->lens = &string_lens; ++ info->own_lens = 0; ++ ++ *retp = info; ++ return 0; + } + +-/* I'm sure this isn't completely correct, but just try to get most of +- * them right for now. */ +-static void +-align_struct(arg_type_info* info) { +- size_t offset; +- int i; ++static int ++build_printf_pack(struct param **packp, size_t param_num) ++{ ++ if (packp == NULL) { ++ report_error(filename, line_no, ++ "'format' type in unexpected context"); ++ return -1; ++ } ++ if (*packp != NULL) { ++ report_error(filename, line_no, ++ "only one 'format' type per function supported"); ++ return -1; ++ } + +- if (info->u.struct_info.size != 0) +- return; // Already done ++ *packp = malloc(sizeof(**packp)); ++ if (*packp == NULL) ++ return -1; + +- // Compute internal padding due to alignment constraints for +- // various types. +- offset = 0; +- for (i = 0; info->u.struct_info.fields[i] != NULL; i++) { +- arg_type_info *field = info->u.struct_info.fields[i]; +- offset += align_skip(arg_align(field), offset); +- info->u.struct_info.offset[i] = offset; +- offset += arg_sizeof(field); ++ struct expr_node *node = malloc(sizeof(*node)); ++ if (node == NULL) { ++ free(*packp); ++ return -1; + } + +- info->u.struct_info.size = offset; ++ expr_init_argno(node, param_num); ++ ++ param_pack_init_printf(*packp, node, 1); ++ ++ return 0; + } + +-static arg_type_info * +-parse_nonpointer_type(char **str) { +- arg_type_info *simple; +- arg_type_info *info; ++/* Match and consume KWD if it's next in stream, and return 0. ++ * Otherwise return negative number. */ ++static int ++try_parse_kwd(char **str, const char *kwd) ++{ ++ size_t len = strlen(kwd); ++ if (strncmp(*str, kwd, len) == 0 ++ && !isalnum((*str)[len])) { ++ (*str) += len; ++ return 0; ++ } ++ return -1; ++} + +- if (strncmp(*str, "typedef", 7) == 0) { +- parse_typedef(str); +- return lookup_prototype(ARGTYPE_UNKNOWN); ++/* Make a copy of INFO and set the *OWN bit if it's not already ++ * owned. */ ++static int ++unshare_type_info(struct arg_type_info **infop, int *ownp) ++{ ++ if (*ownp) ++ return 0; ++ ++ struct arg_type_info *ninfo = malloc(sizeof(*ninfo)); ++ if (ninfo == NULL) { ++ report_error(filename, line_no, ++ "malloc: %s", strerror(errno)); ++ return -1; + } ++ *ninfo = **infop; ++ *infop = ninfo; ++ *ownp = 1; ++ return 0; ++} + +- simple = str2type(str); +- if (simple->type == ARGTYPE_UNKNOWN) { +- info = lookup_typedef(str); +- if (info) +- return info; +- else +- return simple; // UNKNOWN ++/* XXX extra_param and param_num are a kludge to get in ++ * backward-compatible support for "format" parameter type. The ++ * latter is only valid if the former is non-NULL, which is only in ++ * top-level context. */ ++static int ++parse_alias(char **str, struct arg_type_info **retp, int *ownp, ++ struct param **extra_param, size_t param_num) ++{ ++ /* For backward compatibility, we need to support things like ++ * stringN (which is like string[argN], string[N], and also ++ * bare string. We might, in theory, replace this by ++ * preprocessing configure file sources with M4, but for now, ++ * "string" is syntax. */ ++ if (strncmp(*str, "string", 6) == 0) { ++ (*str) += 6; ++ return parse_string(str, retp, ownp); ++ ++ } else if (try_parse_kwd(str, "format") >= 0 ++ && extra_param != NULL) { ++ /* For backward compatibility, format is parsed as ++ * "string", but it smuggles to the parameter list of ++ * a function a "printf" argument pack with this ++ * parameter as argument. */ ++ if (parse_string(str, retp, ownp) < 0) ++ return -1; ++ ++ return build_printf_pack(extra_param, param_num); ++ ++ } else if (try_parse_kwd(str, "enum") >=0) { ++ ++ return parse_enum(str, retp, ownp); ++ ++ } else { ++ *retp = NULL; ++ return 0; + } ++} + +- info = malloc(sizeof(*info)); +- info->type = simple->type; ++/* Syntax: array ( type, N|argN ) */ ++static int ++parse_array(char **str, struct arg_type_info *info) ++{ ++ eat_spaces(str); ++ if (parse_char(str, '(') < 0) ++ return -1; + +- /* Code to parse parameterized types will go into the following +- switch statement. */ ++ eat_spaces(str); ++ int own; ++ struct arg_type_info *elt_info = parse_lens(str, NULL, 0, &own); ++ if (elt_info == NULL) ++ return -1; + +- switch (info->type) { ++ eat_spaces(str); ++ parse_char(str, ','); + +- /* Syntax: array ( type, N|argN ) */ +- case ARGTYPE_ARRAY: +- (*str)++; // Get past open paren +- eat_spaces(str); +- if ((info->u.array_info.elt_type = parse_type(str)) == NULL) +- return NULL; +- info->u.array_info.elt_size = +- arg_sizeof(info->u.array_info.elt_type); +- (*str)++; // Get past comma ++ eat_spaces(str); ++ struct expr_node *length = parse_argnum(str, 0); ++ if (length == NULL) { ++ if (own) { ++ type_destroy(elt_info); ++ free(elt_info); ++ } ++ return -1; ++ } ++ ++ type_init_array(info, elt_info, own, length, 1); ++ ++ eat_spaces(str); ++ parse_char(str, ')'); ++ return 0; ++} ++ ++/* Syntax: ++ * enum (keyname[=value],keyname[=value],... ) ++ * enum (keyname[=value],keyname[=value],... ) ++ */ ++static int ++parse_enum(char **str, struct arg_type_info **retp, int *ownp) ++{ ++ /* Optional type argument. */ ++ eat_spaces(str); ++ if (**str == '[') { ++ parse_char(str, '['); + eat_spaces(str); +- info->u.array_info.len_spec = parse_argnum(str); +- (*str)++; // Get past close paren +- return info; +- +- /* Syntax: enum ( keyname=value,keyname=value,... ) */ +- case ARGTYPE_ENUM:{ +- struct enum_opt { +- char *key; +- int value; +- struct enum_opt *next; +- }; +- struct enum_opt *list = NULL; +- struct enum_opt *p; +- int entries = 0; +- int ii; ++ *retp = parse_nonpointer_type(str, NULL, 0, ownp); ++ if (*retp == NULL) ++ return -1; ++ ++ if (!type_is_integral((*retp)->type)) { ++ report_error(filename, line_no, ++ "integral type required as enum argument"); ++ fail: ++ if (*ownp) { ++ /* This also releases associated lens ++ * if any was set so far. */ ++ type_destroy(*retp); ++ free(*retp); ++ } ++ return -1; ++ } + + eat_spaces(str); +- (*str)++; // Get past open paren ++ if (parse_char(str, ']') < 0) ++ goto fail; ++ ++ } else { ++ *retp = type_get_simple(ARGTYPE_INT); ++ *ownp = 0; ++ } ++ ++ /* We'll need to set the lens, so unshare. */ ++ if (unshare_type_info(retp, ownp) < 0) ++ goto fail; ++ ++ eat_spaces(str); ++ if (parse_char(str, '(') < 0) ++ goto fail; ++ ++ struct enum_lens *lens = malloc(sizeof(*lens)); ++ if (lens == NULL) { ++ report_error(filename, line_no, ++ "malloc enum lens: %s", strerror(errno)); ++ return -1; ++ } ++ ++ lens_init_enum(lens); ++ (*retp)->lens = &lens->super; ++ (*retp)->own_lens = 1; ++ ++ long last_val = 0; ++ while (1) { + eat_spaces(str); ++ if (**str == 0 || **str == ')') { ++ parse_char(str, ')'); ++ return 0; ++ } + +- while (**str && **str != ')') { +- p = (struct enum_opt *) malloc(sizeof(*p)); +- eat_spaces(str); +- p->key = parse_ident(str); +- if (error_count) { +- free(p); +- return NULL; +- } +- eat_spaces(str); +- if (**str != '=') { +- free(p->key); +- free(p); +- output_line(0, +- "Syntax error in `%s', line %d: expected '=', got '%c'", +- filename, line_no, **str); +- error_count++; +- return NULL; +- } +- ++(*str); +- eat_spaces(str); +- p->value = parse_int(str); +- p->next = list; +- list = p; +- ++entries; ++ /* Field delimiter. XXX should we support the C ++ * syntax, where the enumeration can end in pending ++ * comma? */ ++ if (lens_enum_size(lens) > 0) ++ parse_char(str, ','); + +- // Skip comma +- eat_spaces(str); +- if (**str == ',') { +- (*str)++; +- eat_spaces(str); +- } ++ eat_spaces(str); ++ char *key = parse_ident(str); ++ if (key == NULL) { ++ err: ++ free(key); ++ goto fail; + } + +- info->u.enum_info.entries = entries; +- info->u.enum_info.keys = +- (char **) malloc(entries * sizeof(char *)); +- info->u.enum_info.values = +- (int *) malloc(entries * sizeof(int)); +- for (ii = 0, p = NULL; list; ++ii, list = list->next) { +- if (p) +- free(p); +- info->u.enum_info.keys[ii] = list->key; +- info->u.enum_info.values[ii] = list->value; +- p = list; ++ if (**str == '=') { ++ ++*str; ++ eat_spaces(str); ++ if (parse_int(str, &last_val) < 0) ++ goto err; + } +- if (p) +- free(p); + +- return info; ++ struct value *value = malloc(sizeof(*value)); ++ if (value == NULL) ++ goto err; ++ value_init_detached(value, NULL, *retp, 0); ++ value_set_word(value, last_val); ++ ++ if (lens_enum_add(lens, key, 1, value, 1) < 0) ++ goto err; ++ ++ last_val++; + } + +- case ARGTYPE_STRING: +- if (!isdigit(**str) && **str != '[') { +- /* Oops, was just a simple string after all */ +- free(info); ++ return 0; ++} ++ ++static struct arg_type_info * ++parse_nonpointer_type(char **str, struct param **extra_param, size_t param_num, ++ int *ownp) ++{ ++ enum arg_type type; ++ if (parse_arg_type(str, &type) < 0) { ++ struct arg_type_info *simple; ++ if (parse_alias(str, &simple, ownp, extra_param, param_num) < 0) ++ return NULL; ++ if (simple == NULL) ++ simple = lookup_typedef(str); ++ if (simple != NULL) { ++ *ownp = 0; + return simple; + } + +- info->type = ARGTYPE_STRING_N; ++ report_error(filename, line_no, ++ "unknown type around '%s'", *str); ++ return NULL; ++ } ++ ++ int (*parser) (char **, struct arg_type_info *) = NULL; ++ ++ /* For some types that's all we need. */ ++ switch (type) { ++ case ARGTYPE_VOID: ++ case ARGTYPE_INT: ++ case ARGTYPE_UINT: ++ case ARGTYPE_LONG: ++ case ARGTYPE_ULONG: ++ case ARGTYPE_CHAR: ++ case ARGTYPE_SHORT: ++ case ARGTYPE_USHORT: ++ case ARGTYPE_FLOAT: ++ case ARGTYPE_DOUBLE: ++ *ownp = 0; ++ return type_get_simple(type); ++ ++ case ARGTYPE_ARRAY: ++ parser = parse_array; ++ break; ++ ++ case ARGTYPE_STRUCT: ++ parser = parse_struct; ++ break; ++ ++ case ARGTYPE_POINTER: ++ /* Pointer syntax is not based on keyword, so we ++ * should never get this type. */ ++ assert(type != ARGTYPE_POINTER); ++ abort(); ++ } ++ ++ struct arg_type_info *info = malloc(sizeof(*info)); ++ if (info == NULL) { ++ report_error(filename, line_no, ++ "malloc: %s", strerror(errno)); ++ return NULL; ++ } ++ *ownp = 1; ++ ++ if (parser(str, info) < 0) { ++ free(info); ++ return NULL; ++ } + +- /* Backwards compatibility for string0, string1, ... */ +- if (isdigit(**str)) { +- info->u.string_n_info.size_spec = -parse_int(str); +- return info; ++ return info; ++} ++ ++static struct named_lens { ++ const char *name; ++ struct lens *lens; ++} lenses[] = { ++ { "hide", &blind_lens }, ++ { "octal", &octal_lens }, ++ { "hex", &hex_lens }, ++ { "bool", &bool_lens }, ++ { "guess", &guess_lens }, ++}; ++ ++static struct lens * ++name2lens(char **str, int *own_lensp) ++{ ++ size_t i; ++ for (i = 0; i < sizeof(lenses)/sizeof(*lenses); ++i) ++ if (try_parse_kwd(str, lenses[i].name) == 0) { ++ *own_lensp = 0; ++ return lenses[i].lens; + } + +- (*str)++; // Skip past opening [ +- eat_spaces(str); +- info->u.string_n_info.size_spec = parse_argnum(str); ++ return NULL; ++} ++ ++static struct arg_type_info * ++parse_type(char **str, struct param **extra_param, size_t param_num, int *ownp) ++{ ++ struct arg_type_info *info ++ = parse_nonpointer_type(str, extra_param, param_num, ownp); ++ if (info == NULL) ++ return NULL; ++ ++ while (1) { + eat_spaces(str); +- (*str)++; // Skip past closing ] +- return info; +- +- // Syntax: struct ( type,type,type,... ) +- case ARGTYPE_STRUCT:{ +- int field_num = 0; +- (*str)++; // Get past open paren +- info->u.struct_info.fields = +- malloc((MAX_ARGS + 1) * sizeof(void *)); +- info->u.struct_info.offset = +- malloc((MAX_ARGS + 1) * sizeof(size_t)); +- info->u.struct_info.size = 0; +- eat_spaces(str); // Empty arg list with whitespace inside +- while (**str && **str != ')') { +- if (field_num == MAX_ARGS) { +- output_line(0, +- "Error in `%s', line %d: Too many structure elements", +- filename, line_no); +- error_count++; ++ if (**str == '*') { ++ struct arg_type_info *outer = malloc(sizeof(*outer)); ++ if (outer == NULL) { ++ if (*ownp) { ++ type_destroy(info); ++ free(info); ++ } ++ report_error(filename, line_no, ++ "malloc: %s", strerror(errno)); + return NULL; + } +- eat_spaces(str); +- if (field_num != 0) { +- (*str)++; // Get past comma +- eat_spaces(str); +- } +- if ((info->u.struct_info.fields[field_num++] = +- parse_type(str)) == NULL) +- return NULL; ++ type_init_pointer(outer, info, *ownp); ++ *ownp = 1; ++ (*str)++; ++ info = outer; ++ } else ++ break; ++ } ++ return info; ++} + +- // Must trim trailing spaces so the check for +- // the closing paren is simple +- eat_spaces(str); ++static struct arg_type_info * ++parse_lens(char **str, struct param **extra_param, size_t param_num, int *ownp) ++{ ++ int own_lens; ++ struct lens *lens = name2lens(str, &own_lens); ++ int has_args = 1; ++ struct arg_type_info *info; ++ if (lens != NULL) { ++ eat_spaces(str); ++ ++ /* Octal lens gets special treatment, because of ++ * backward compatibility. */ ++ if (lens == &octal_lens && **str != '(') { ++ has_args = 0; ++ info = type_get_simple(ARGTYPE_INT); ++ *ownp = 0; ++ } else if (parse_char(str, '(') < 0) { ++ report_error(filename, line_no, ++ "expected type argument after the lens"); ++ return NULL; + } +- (*str)++; // Get past closing paren +- info->u.struct_info.fields[field_num] = NULL; +- align_struct(info); +- return info; + } + +- default: +- if (info->type == ARGTYPE_UNKNOWN) { +- output_line(0, "Syntax error in `%s', line %d: " +- "Unknown type encountered", +- filename, line_no); +- free(info); +- error_count++; ++ if (has_args) { ++ eat_spaces(str); ++ info = parse_type(str, extra_param, param_num, ownp); ++ if (info == NULL) { ++ fail: ++ if (own_lens && lens != NULL) ++ lens_destroy(lens); + return NULL; +- } else { +- return info; + } + } +-} + +-static arg_type_info * +-parse_type(char **str) { +- arg_type_info *info = parse_nonpointer_type(str); +- while (**str == '*') { +- arg_type_info *outer = malloc(sizeof(*info)); +- outer->type = ARGTYPE_POINTER; +- outer->u.ptr_info.info = info; +- (*str)++; +- info = outer; ++ if (lens != NULL && has_args) { ++ eat_spaces(str); ++ parse_char(str, ')'); + } ++ ++ /* We can't modify shared types. Make a copy if we have a ++ * lens. */ ++ if (lens != NULL && unshare_type_info(&info, ownp) < 0) ++ goto fail; ++ ++ if (lens != NULL) { ++ info->lens = lens; ++ info->own_lens = own_lens; ++ } ++ + return info; + } + ++static int ++add_param(Function *fun, size_t *allocdp) ++{ ++ size_t allocd = *allocdp; ++ /* XXX +1 is for the extra_param handling hack. */ ++ if ((fun->num_params + 1) >= allocd) { ++ allocd = allocd > 0 ? 2 * allocd : 8; ++ void *na = realloc(fun->params, sizeof(*fun->params) * allocd); ++ if (na == NULL) ++ return -1; ++ ++ fun->params = na; ++ *allocdp = allocd; ++ } ++ return 0; ++} ++ + static Function * + process_line(char *buf) { +- Function fun; +- Function *fun_p; + char *str = buf; + char *tmp; +- int i; +- int float_num = 0; + + line_no++; + debug(3, "Reading line %d of `%s'", line_no, filename); + eat_spaces(&str); +- fun.return_info = parse_type(&str); +- if (fun.return_info == NULL) ++ ++ /* A comment or empty line. */ ++ if (*str == ';' || *str == 0 || *str == '\n') ++ return NULL; ++ ++ if (strncmp(str, "typedef", 7) == 0) { ++ parse_typedef(&str); ++ return NULL; ++ } ++ ++ Function *fun = calloc(1, sizeof(*fun)); ++ if (fun == NULL) { ++ report_error(filename, line_no, ++ "alloc function: %s", strerror(errno)); + return NULL; +- if (fun.return_info->type == ARGTYPE_UNKNOWN) { ++ } ++ ++ fun->return_info = parse_lens(&str, NULL, 0, &fun->own_return_info); ++ if (fun->return_info == NULL) { ++ err: + debug(3, " Skipping line %d", line_no); ++ destroy_fun(fun); + return NULL; + } +- debug(4, " return_type = %d", fun.return_info->type); ++ debug(4, " return_type = %d", fun->return_info->type); ++ + eat_spaces(&str); + tmp = start_of_arg_sig(str); +- if (!tmp) { +- output_line(0, "Syntax error in `%s', line %d", filename, +- line_no); +- error_count++; +- return NULL; ++ if (tmp == NULL) { ++ report_error(filename, line_no, "syntax error"); ++ goto err; + } + *tmp = '\0'; +- fun.name = strdup(str); ++ fun->name = strdup(str); + str = tmp + 1; +- debug(3, " name = %s", fun.name); +- fun.params_right = 0; +- for (i = 0; i < MAX_ARGS; i++) { ++ debug(3, " name = %s", fun->name); ++ ++ size_t allocd = 0; ++ struct param *extra_param = NULL; ++ ++ int have_stop = 0; ++ ++ while (1) { + eat_spaces(&str); +- if (*str == ')') { ++ if (*str == ')') + break; +- } ++ + if (str[0] == '+') { +- fun.params_right++; ++ if (have_stop == 0) { ++ if (add_param(fun, &allocd) < 0) ++ goto add_err; ++ param_init_stop ++ (&fun->params[fun->num_params++]); ++ have_stop = 1; ++ } + str++; +- } else if (fun.params_right) { +- fun.params_right++; + } +- fun.arg_info[i] = parse_type(&str); +- if (fun.arg_info[i] == NULL) { +- output_line(0, "Syntax error in `%s', line %d" +- ": unknown argument type", +- filename, line_no); +- error_count++; +- return NULL; ++ ++ if (add_param(fun, &allocd) < 0) { ++ add_err: ++ report_error(filename, line_no, "(re)alloc params: %s", ++ strerror(errno)); ++ goto err; ++ } ++ ++ int own; ++ struct arg_type_info *type = parse_lens(&str, &extra_param, ++ fun->num_params, &own); ++ if (type == NULL) { ++ report_error(filename, line_no, ++ "unknown argument type"); ++ goto err; ++ } ++ ++ /* XXX We used to allow void parameter as a synonym to ++ * an argument that shouldn't be displayed. We may ++ * wish to re-introduce this when lenses are ++ * implemented, as a synonym, but backends generally ++ * need to know the type, so disallow bare void for ++ * now. */ ++ if (type->type == ARGTYPE_VOID) { ++ report_warning(filename, line_no, ++ "void parameter assumed to be 'int'"); ++ if (own) { ++ type_destroy(type); ++ free(type); ++ } ++ type = type_get_simple(ARGTYPE_INT); ++ own = 0; + } +- if (fun.arg_info[i]->type == ARGTYPE_FLOAT) +- fun.arg_info[i]->u.float_info.float_index = float_num++; +- else if (fun.arg_info[i]->type == ARGTYPE_DOUBLE) +- fun.arg_info[i]->u.double_info.float_index = float_num++; ++ ++ param_init_type(&fun->params[fun->num_params++], type, own); ++ + eat_spaces(&str); + if (*str == ',') { + str++; +@@ -634,20 +1083,35 @@ process_line(char *buf) { + } else { + if (str[strlen(str) - 1] == '\n') + str[strlen(str) - 1] = '\0'; +- output_line(0, "Syntax error in `%s', line %d at ...\"%s\"", +- filename, line_no, str); +- error_count++; +- return NULL; ++ report_error(filename, line_no, ++ "syntax error around \"%s\"", str); ++ goto err; + } + } +- fun.num_params = i; +- fun_p = malloc(sizeof(Function)); +- if (!fun_p) { +- perror("ltrace: malloc"); +- exit(1); ++ ++ if (extra_param != NULL) { ++ assert(fun->num_params < allocd); ++ memcpy(&fun->params[fun->num_params++], extra_param, ++ sizeof(*extra_param)); + } +- memcpy(fun_p, &fun, sizeof(Function)); +- return fun_p; ++ ++ return fun; ++} ++ ++void ++init_global_config(void) ++{ ++ struct arg_type_info *info = malloc(2 * sizeof(*info)); ++ if (info == NULL) ++ error(1, errno, "malloc in init_global_config"); ++ ++ memset(info, 0, 2 * sizeof(*info)); ++ info[0].type = ARGTYPE_POINTER; ++ info[0].u.ptr_info.info = &info[1]; ++ info[1].type = ARGTYPE_VOID; ++ ++ insert_typedef(strdup("addr"), info, 0); ++ insert_typedef(strdup("file"), info, 1); + } + + void +@@ -667,7 +1131,6 @@ read_config_file(char *file) { + while (fgets(buf, 1024, stream)) { + Function *tmp; + +- error_count = 0; + tmp = process_line(buf); + + if (tmp) { +diff --git a/read_config_file.h b/read_config_file.h +index 8000b1c..06215ab 100644 +--- a/read_config_file.h ++++ b/read_config_file.h +@@ -1 +1,2 @@ + extern void read_config_file(char *); ++extern void init_global_config(void); +diff --git a/sysdeps/README b/sysdeps/README +index 0a37909..db51c9e 100644 +--- a/sysdeps/README ++++ b/sysdeps/README +@@ -7,27 +7,5 @@ first target, and must remove "sysdep.o" in this dir. + Files "sysdep.h", "signalent.h" and "syscallent.h" must be present + inside the directory after invoking the first target of the Makefile. + +------------ +-"sysdep.o" must export the following functions: +- +-Event * next_event(void); +-void continue_after_breakpoint(Process * proc, Breakpoint * sbp, int delete_it); +-void continue_after_signal(pid_t pid, int signum); +-void continue_enabling_breakpoint(pid_t pid, Breakpoint * sbp); +-void continue_process(pid_t pid); +-void enable_breakpoint(pid_t pid, Breakpoint * sbp); +-void disable_breakpoint(pid_t pid, Breakpoint * sbp); +-int fork_p(int sysnum); +-int exec_p(int sysnum); +-int syscall_p(Process * proc, int status, int * sysnum); +-void * get_instruction_pointer(pid_t pid); +-void * get_stack_pointer(pid_t pid); +-void * get_return_addr(pid_t pid, void * stack_pointer); +-long gimme_arg(enum tof type, Process * proc, arg_type_info*); +-int umovestr(Process * proc, void * addr, int len, void * laddr); +-int umovelong(Process * proc, void * addr, long * result); +-char * pid2name(pid_t pid); +-void trace_me(void); +-int trace_pid(pid_t pid); +-void untrace_pid(pid_t pid); +-void linkmap_init(Process *, struct ltelf *); ++See the file "backend.h" for description of backend interfaces, which ++have to be provided by "sysdep.o". +diff --git a/sysdeps/linux-gnu/Makefile.am b/sysdeps/linux-gnu/Makefile.am +index e6fd7ef..80e6594 100644 +--- a/sysdeps/linux-gnu/Makefile.am ++++ b/sysdeps/linux-gnu/Makefile.am +@@ -1,14 +1,13 @@ + DIST_SUBDIRS = \ + alpha \ + arm \ +- i386 \ + ia64 \ + m68k \ + mipsel \ + ppc \ + s390 \ + sparc \ +- x86_64 ++ x86 + + SUBDIRS = \ + $(HOST_CPU) +@@ -29,7 +28,8 @@ noinst_HEADERS = \ + arch_syscallent.h \ + signalent1.h \ + syscallent1.h \ +- trace.h ++ trace.h \ ++ events.h + + EXTRA_DIST = \ + arch_mksyscallent \ +diff --git a/sysdeps/linux-gnu/arm/arch.h b/sysdeps/linux-gnu/arm/arch.h +index d50e439..291443a 100644 +--- a/sysdeps/linux-gnu/arm/arch.h ++++ b/sysdeps/linux-gnu/arm/arch.h +@@ -1,3 +1,23 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 1998,2004,2008 Juan Cespedes ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ + #define ARCH_HAVE_ENABLE_BREAKPOINT 1 + #define ARCH_HAVE_DISABLE_BREAKPOINT 1 + +diff --git a/sysdeps/linux-gnu/arm/trace.c b/sysdeps/linux-gnu/arm/trace.c +index 768e1a8..4e4dd25 100644 +--- a/sysdeps/linux-gnu/arm/trace.c ++++ b/sysdeps/linux-gnu/arm/trace.c +@@ -82,7 +82,8 @@ syscall_p(Process *proc, int status, int *sysnum) { + } + + long +-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { ++gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info) ++{ + proc_archdep *a = (proc_archdep *) proc->arch_ptr; + + if (arg_num == -1) { /* return value */ +@@ -123,14 +124,3 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { + + return 0; + } +- +-void +-save_register_args(enum tof type, Process *proc) { +- proc_archdep *a = (proc_archdep *) proc->arch_ptr; +- if (a->valid) { +- if (type == LT_TOF_FUNCTION) +- memcpy(a->func_arg, a->regs.uregs, sizeof(a->func_arg)); +- else +- memcpy(a->sysc_arg, a->regs.uregs, sizeof(a->sysc_arg)); +- } +-} +diff --git a/sysdeps/linux-gnu/breakpoint.c b/sysdeps/linux-gnu/breakpoint.c +index e05e730..40a8436 100644 +--- a/sysdeps/linux-gnu/breakpoint.c ++++ b/sysdeps/linux-gnu/breakpoint.c +@@ -1,3 +1,25 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2006 Ian Wienand ++ * Copyright (C) 2002,2008,2009 Juan Cespedes ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ + #include "config.h" + + #include +@@ -6,6 +28,8 @@ + #include + + #include "common.h" ++#include "backend.h" ++#include "arch.h" + #include "sysdep.h" + #include "breakpoint.h" + #include "proc.h" +diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c +index 91d873e..d0c1e5c 100644 +--- a/sysdeps/linux-gnu/events.c ++++ b/sysdeps/linux-gnu/events.c +@@ -1,18 +1,42 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2007,2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 1998,2001,2004,2007,2008,2009 Juan Cespedes ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ + #include "config.h" + + #define _GNU_SOURCE 1 +-#include ++#include + #include + #include ++#include + #include + #include ++#include ++#include + #include +-#include +-#include + #include + +-#include "common.h" ++#include "backend.h" + #include "breakpoint.h" ++#include "debug.h" ++#include "events.h" + #include "proc.h" + + static Event event; +@@ -35,7 +59,7 @@ enque_event(Event * event) + event->proc->pid, event->type); + Event * ne = malloc(sizeof(*ne)); + if (ne == NULL) { +- perror("event will be missed: malloc"); ++ fprintf(stderr, "event will be missed: %s\n", strerror(errno)); + return; + } + +@@ -311,3 +335,20 @@ next_event(void) + + return &event; + } ++ ++static enum ecb_status ++event_for_proc(struct Event *event, void *data) ++{ ++ if (event->proc == data) ++ return ecb_deque; ++ else ++ return ecb_cont; ++} ++ ++void ++delete_events_for(struct Process *proc) ++{ ++ struct Event *event; ++ while ((event = each_qd_event(&event_for_proc, proc)) != NULL) ++ free(event); ++} +diff --git a/sysdeps/linux-gnu/events.h b/sysdeps/linux-gnu/events.h +new file mode 100644 +index 0000000..3802aff +--- /dev/null ++++ b/sysdeps/linux-gnu/events.h +@@ -0,0 +1,41 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef SYSDEPS_LINUX_GNU_EVENTS_H ++#define SYSDEPS_LINUX_GNU_EVENTS_H ++ ++#include "forward.h" ++ ++/* Declarations for event que functions. */ ++ ++enum ecb_status { ++ ecb_cont, /* The iteration should continue. */ ++ ecb_yield, /* The iteration should stop, yielding this ++ * event. */ ++ ecb_deque, /* Like ecb_stop, but the event should be removed ++ * from the queue. */ ++}; ++ ++struct Event *each_qd_event(enum ecb_status (*cb)(struct Event *event, ++ void *data), void *data); ++void delete_events_for(struct Process * proc); ++void enque_event(struct Event *event); ++ ++#endif /* SYSDEPS_LINUX_GNU_EVENTS_H */ +diff --git a/sysdeps/linux-gnu/i386/Makefile.am b/sysdeps/linux-gnu/i386/Makefile.am +deleted file mode 100644 +index a79d2f7..0000000 +--- a/sysdeps/linux-gnu/i386/Makefile.am ++++ /dev/null +@@ -1,16 +0,0 @@ +-noinst_LTLIBRARIES = \ +- ../libcpu.la +- +-___libcpu_la_SOURCES = \ +- plt.c \ +- regs.c \ +- trace.c +- +-noinst_HEADERS = \ +- arch.h \ +- ptrace.h \ +- signalent.h \ +- syscallent.h +- +-MAINTAINERCLEANFILES = \ +- Makefile.in +diff --git a/sysdeps/linux-gnu/i386/arch.h b/sysdeps/linux-gnu/i386/arch.h +deleted file mode 100644 +index dc7383f..0000000 +--- a/sysdeps/linux-gnu/i386/arch.h ++++ /dev/null +@@ -1,7 +0,0 @@ +-#define BREAKPOINT_VALUE {0xcc} +-#define BREAKPOINT_LENGTH 1 +-#define DECR_PC_AFTER_BREAK 1 +-#define ARCH_ENDIAN_LITTLE +- +-#define LT_ELFCLASS ELFCLASS32 +-#define LT_ELF_MACHINE EM_386 +diff --git a/sysdeps/linux-gnu/i386/plt.c b/sysdeps/linux-gnu/i386/plt.c +deleted file mode 100644 +index daaf15a..0000000 +--- a/sysdeps/linux-gnu/i386/plt.c ++++ /dev/null +@@ -1,16 +0,0 @@ +-#include +-#include "proc.h" +-#include "library.h" +-#include "ltrace-elf.h" +- +-GElf_Addr +-arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) +-{ +- return lte->plt_addr + (ndx + 1) * 16; +-} +- +-void * +-sym2addr(struct Process *proc, struct library_symbol *sym) +-{ +- return sym->enter_addr; +-} +diff --git a/sysdeps/linux-gnu/i386/ptrace.h b/sysdeps/linux-gnu/i386/ptrace.h +deleted file mode 100644 +index c3cbcb6..0000000 +--- a/sysdeps/linux-gnu/i386/ptrace.h ++++ /dev/null +@@ -1 +0,0 @@ +-#include +diff --git a/sysdeps/linux-gnu/i386/regs.c b/sysdeps/linux-gnu/i386/regs.c +deleted file mode 100644 +index a1584ac..0000000 +--- a/sysdeps/linux-gnu/i386/regs.c ++++ /dev/null +@@ -1,40 +0,0 @@ +-#include "config.h" +- +-#include +-#include +-#include +- +-#include "proc.h" +- +-#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +-# define PTRACE_PEEKUSER PTRACE_PEEKUSR +-#endif +- +-#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +-# define PTRACE_POKEUSER PTRACE_POKEUSR +-#endif +- +-void * +-get_instruction_pointer(Process *proc) { +- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EIP, 0); +-} +- +-void +-set_instruction_pointer(Process *proc, void *addr) { +- ptrace(PTRACE_POKEUSER, proc->pid, 4 * EIP, (long)addr); +-} +- +-void * +-get_stack_pointer(Process *proc) { +- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 4 * UESP, 0); +-} +- +-void * +-get_return_addr(Process *proc, void *stack_pointer) { +- return (void *)ptrace(PTRACE_PEEKTEXT, proc->pid, stack_pointer, 0); +-} +- +-void +-set_return_addr(Process *proc, void *addr) { +- ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, (long)addr); +-} +diff --git a/sysdeps/linux-gnu/i386/signalent.h b/sysdeps/linux-gnu/i386/signalent.h +deleted file mode 100644 +index 5395f82..0000000 +--- a/sysdeps/linux-gnu/i386/signalent.h ++++ /dev/null +@@ -1,32 +0,0 @@ +- "SIG_0", /* 0 */ +- "SIGHUP", /* 1 */ +- "SIGINT", /* 2 */ +- "SIGQUIT", /* 3 */ +- "SIGILL", /* 4 */ +- "SIGTRAP", /* 5 */ +- "SIGABRT", /* 6 */ +- "SIGBUS", /* 7 */ +- "SIGFPE", /* 8 */ +- "SIGKILL", /* 9 */ +- "SIGUSR1", /* 10 */ +- "SIGSEGV", /* 11 */ +- "SIGUSR2", /* 12 */ +- "SIGPIPE", /* 13 */ +- "SIGALRM", /* 14 */ +- "SIGTERM", /* 15 */ +- "SIGSTKFLT", /* 16 */ +- "SIGCHLD", /* 17 */ +- "SIGCONT", /* 18 */ +- "SIGSTOP", /* 19 */ +- "SIGTSTP", /* 20 */ +- "SIGTTIN", /* 21 */ +- "SIGTTOU", /* 22 */ +- "SIGURG", /* 23 */ +- "SIGXCPU", /* 24 */ +- "SIGXFSZ", /* 25 */ +- "SIGVTALRM", /* 26 */ +- "SIGPROF", /* 27 */ +- "SIGWINCH", /* 28 */ +- "SIGIO", /* 29 */ +- "SIGPWR", /* 30 */ +- "SIGSYS", /* 31 */ +diff --git a/sysdeps/linux-gnu/i386/syscallent.h b/sysdeps/linux-gnu/i386/syscallent.h +deleted file mode 100644 +index 8f4c887..0000000 +--- a/sysdeps/linux-gnu/i386/syscallent.h ++++ /dev/null +@@ -1,317 +0,0 @@ +- "restart_syscall", /* 0 */ +- "exit", /* 1 */ +- "fork", /* 2 */ +- "read", /* 3 */ +- "write", /* 4 */ +- "open", /* 5 */ +- "close", /* 6 */ +- "waitpid", /* 7 */ +- "creat", /* 8 */ +- "link", /* 9 */ +- "unlink", /* 10 */ +- "execve", /* 11 */ +- "chdir", /* 12 */ +- "time", /* 13 */ +- "mknod", /* 14 */ +- "chmod", /* 15 */ +- "lchown", /* 16 */ +- "break", /* 17 */ +- "oldstat", /* 18 */ +- "lseek", /* 19 */ +- "getpid", /* 20 */ +- "mount", /* 21 */ +- "umount", /* 22 */ +- "setuid", /* 23 */ +- "getuid", /* 24 */ +- "stime", /* 25 */ +- "ptrace", /* 26 */ +- "alarm", /* 27 */ +- "oldfstat", /* 28 */ +- "pause", /* 29 */ +- "utime", /* 30 */ +- "stty", /* 31 */ +- "gtty", /* 32 */ +- "access", /* 33 */ +- "nice", /* 34 */ +- "ftime", /* 35 */ +- "sync", /* 36 */ +- "kill", /* 37 */ +- "rename", /* 38 */ +- "mkdir", /* 39 */ +- "rmdir", /* 40 */ +- "dup", /* 41 */ +- "pipe", /* 42 */ +- "times", /* 43 */ +- "prof", /* 44 */ +- "brk", /* 45 */ +- "setgid", /* 46 */ +- "getgid", /* 47 */ +- "signal", /* 48 */ +- "geteuid", /* 49 */ +- "getegid", /* 50 */ +- "acct", /* 51 */ +- "umount2", /* 52 */ +- "lock", /* 53 */ +- "ioctl", /* 54 */ +- "fcntl", /* 55 */ +- "mpx", /* 56 */ +- "setpgid", /* 57 */ +- "ulimit", /* 58 */ +- "oldolduname", /* 59 */ +- "umask", /* 60 */ +- "chroot", /* 61 */ +- "ustat", /* 62 */ +- "dup2", /* 63 */ +- "getppid", /* 64 */ +- "getpgrp", /* 65 */ +- "setsid", /* 66 */ +- "sigaction", /* 67 */ +- "sgetmask", /* 68 */ +- "ssetmask", /* 69 */ +- "setreuid", /* 70 */ +- "setregid", /* 71 */ +- "sigsuspend", /* 72 */ +- "sigpending", /* 73 */ +- "sethostname", /* 74 */ +- "setrlimit", /* 75 */ +- "getrlimit", /* 76 */ +- "getrusage", /* 77 */ +- "gettimeofday", /* 78 */ +- "settimeofday", /* 79 */ +- "getgroups", /* 80 */ +- "setgroups", /* 81 */ +- "select", /* 82 */ +- "symlink", /* 83 */ +- "oldlstat", /* 84 */ +- "readlink", /* 85 */ +- "uselib", /* 86 */ +- "swapon", /* 87 */ +- "reboot", /* 88 */ +- "readdir", /* 89 */ +- "mmap", /* 90 */ +- "munmap", /* 91 */ +- "truncate", /* 92 */ +- "ftruncate", /* 93 */ +- "fchmod", /* 94 */ +- "fchown", /* 95 */ +- "getpriority", /* 96 */ +- "setpriority", /* 97 */ +- "profil", /* 98 */ +- "statfs", /* 99 */ +- "fstatfs", /* 100 */ +- "ioperm", /* 101 */ +- "socketcall", /* 102 */ +- "syslog", /* 103 */ +- "setitimer", /* 104 */ +- "getitimer", /* 105 */ +- "stat", /* 106 */ +- "lstat", /* 107 */ +- "fstat", /* 108 */ +- "olduname", /* 109 */ +- "iopl", /* 110 */ +- "vhangup", /* 111 */ +- "idle", /* 112 */ +- "vm86old", /* 113 */ +- "wait4", /* 114 */ +- "swapoff", /* 115 */ +- "sysinfo", /* 116 */ +- "ipc", /* 117 */ +- "fsync", /* 118 */ +- "sigreturn", /* 119 */ +- "clone", /* 120 */ +- "setdomainname", /* 121 */ +- "uname", /* 122 */ +- "modify_ldt", /* 123 */ +- "adjtimex", /* 124 */ +- "mprotect", /* 125 */ +- "sigprocmask", /* 126 */ +- "create_module", /* 127 */ +- "init_module", /* 128 */ +- "delete_module", /* 129 */ +- "get_kernel_syms", /* 130 */ +- "quotactl", /* 131 */ +- "getpgid", /* 132 */ +- "fchdir", /* 133 */ +- "bdflush", /* 134 */ +- "sysfs", /* 135 */ +- "personality", /* 136 */ +- "afs_syscall", /* 137 */ +- "setfsuid", /* 138 */ +- "setfsgid", /* 139 */ +- "_llseek", /* 140 */ +- "getdents", /* 141 */ +- "_newselect", /* 142 */ +- "flock", /* 143 */ +- "msync", /* 144 */ +- "readv", /* 145 */ +- "writev", /* 146 */ +- "getsid", /* 147 */ +- "fdatasync", /* 148 */ +- "_sysctl", /* 149 */ +- "mlock", /* 150 */ +- "munlock", /* 151 */ +- "mlockall", /* 152 */ +- "munlockall", /* 153 */ +- "sched_setparam", /* 154 */ +- "sched_getparam", /* 155 */ +- "sched_setscheduler", /* 156 */ +- "sched_getscheduler", /* 157 */ +- "sched_yield", /* 158 */ +- "sched_get_priority_max", /* 159 */ +- "sched_get_priority_min", /* 160 */ +- "sched_rr_get_interval", /* 161 */ +- "nanosleep", /* 162 */ +- "mremap", /* 163 */ +- "setresuid", /* 164 */ +- "getresuid", /* 165 */ +- "vm86", /* 166 */ +- "query_module", /* 167 */ +- "poll", /* 168 */ +- "nfsservctl", /* 169 */ +- "setresgid", /* 170 */ +- "getresgid", /* 171 */ +- "prctl", /* 172 */ +- "rt_sigreturn", /* 173 */ +- "rt_sigaction", /* 174 */ +- "rt_sigprocmask", /* 175 */ +- "rt_sigpending", /* 176 */ +- "rt_sigtimedwait", /* 177 */ +- "rt_sigqueueinfo", /* 178 */ +- "rt_sigsuspend", /* 179 */ +- "pread64", /* 180 */ +- "pwrite64", /* 181 */ +- "chown", /* 182 */ +- "getcwd", /* 183 */ +- "capget", /* 184 */ +- "capset", /* 185 */ +- "sigaltstack", /* 186 */ +- "sendfile", /* 187 */ +- "getpmsg", /* 188 */ +- "putpmsg", /* 189 */ +- "vfork", /* 190 */ +- "ugetrlimit", /* 191 */ +- "mmap2", /* 192 */ +- "truncate64", /* 193 */ +- "ftruncate64", /* 194 */ +- "stat64", /* 195 */ +- "lstat64", /* 196 */ +- "fstat64", /* 197 */ +- "lchown32", /* 198 */ +- "getuid32", /* 199 */ +- "getgid32", /* 200 */ +- "geteuid32", /* 201 */ +- "getegid32", /* 202 */ +- "setreuid32", /* 203 */ +- "setregid32", /* 204 */ +- "getgroups32", /* 205 */ +- "setgroups32", /* 206 */ +- "fchown32", /* 207 */ +- "setresuid32", /* 208 */ +- "getresuid32", /* 209 */ +- "setresgid32", /* 210 */ +- "getresgid32", /* 211 */ +- "chown32", /* 212 */ +- "setuid32", /* 213 */ +- "setgid32", /* 214 */ +- "setfsuid32", /* 215 */ +- "setfsgid32", /* 216 */ +- "pivot_root", /* 217 */ +- "mincore", /* 218 */ +- "madvise1", /* 219 */ +- "getdents64", /* 220 */ +- "fcntl64", /* 221 */ +- "222", /* 222 */ +- "223", /* 223 */ +- "gettid", /* 224 */ +- "readahead", /* 225 */ +- "setxattr", /* 226 */ +- "lsetxattr", /* 227 */ +- "fsetxattr", /* 228 */ +- "getxattr", /* 229 */ +- "lgetxattr", /* 230 */ +- "fgetxattr", /* 231 */ +- "listxattr", /* 232 */ +- "llistxattr", /* 233 */ +- "flistxattr", /* 234 */ +- "removexattr", /* 235 */ +- "lremovexattr", /* 236 */ +- "fremovexattr", /* 237 */ +- "tkill", /* 238 */ +- "sendfile64", /* 239 */ +- "futex", /* 240 */ +- "sched_setaffinity", /* 241 */ +- "sched_getaffinity", /* 242 */ +- "set_thread_area", /* 243 */ +- "get_thread_area", /* 244 */ +- "io_setup", /* 245 */ +- "io_destroy", /* 246 */ +- "io_getevents", /* 247 */ +- "io_submit", /* 248 */ +- "io_cancel", /* 249 */ +- "fadvise64", /* 250 */ +- "251", /* 251 */ +- "exit_group", /* 252 */ +- "lookup_dcookie", /* 253 */ +- "epoll_create", /* 254 */ +- "epoll_ctl", /* 255 */ +- "epoll_wait", /* 256 */ +- "remap_file_pages", /* 257 */ +- "set_tid_address", /* 258 */ +- "timer_create", /* 259 */ +- "260", /* 260 */ +- "261", /* 261 */ +- "262", /* 262 */ +- "263", /* 263 */ +- "264", /* 264 */ +- "265", /* 265 */ +- "266", /* 266 */ +- "267", /* 267 */ +- "statfs64", /* 268 */ +- "fstatfs64", /* 269 */ +- "tgkill", /* 270 */ +- "utimes", /* 271 */ +- "fadvise64_64", /* 272 */ +- "vserver", /* 273 */ +- "mbind", /* 274 */ +- "get_mempolicy", /* 275 */ +- "set_mempolicy", /* 276 */ +- "mq_open", /* 277 */ +- "278", /* 278 */ +- "279", /* 279 */ +- "280", /* 280 */ +- "281", /* 281 */ +- "282", /* 282 */ +- "kexec_load", /* 283 */ +- "waitid", /* 284 */ +- "285", /* 285 */ +- "add_key", /* 286 */ +- "request_key", /* 287 */ +- "keyctl", /* 288 */ +- "ioprio_set", /* 289 */ +- "ioprio_get", /* 290 */ +- "inotify_init", /* 291 */ +- "inotify_add_watch", /* 292 */ +- "inotify_rm_watch", /* 293 */ +- "migrate_pages", /* 294 */ +- "openat", /* 295 */ +- "mkdirat", /* 296 */ +- "mknodat", /* 297 */ +- "fchownat", /* 298 */ +- "futimesat", /* 299 */ +- "fstatat64", /* 300 */ +- "unlinkat", /* 301 */ +- "renameat", /* 302 */ +- "linkat", /* 303 */ +- "symlinkat", /* 304 */ +- "readlinkat", /* 305 */ +- "fchmodat", /* 306 */ +- "faccessat", /* 307 */ +- "pselect6", /* 308 */ +- "ppoll", /* 309 */ +- "unshare", /* 310 */ +- "set_robust_list", /* 311 */ +- "get_robust_list", /* 312 */ +- "splice", /* 313 */ +- "sync_file_range", /* 314 */ +- "tee", /* 315 */ +- "vmsplice", /* 316 */ +diff --git a/sysdeps/linux-gnu/i386/trace.c b/sysdeps/linux-gnu/i386/trace.c +deleted file mode 100644 +index f0c1e50..0000000 +--- a/sysdeps/linux-gnu/i386/trace.c ++++ /dev/null +@@ -1,99 +0,0 @@ +-#include "config.h" +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "proc.h" +-#include "common.h" +- +-#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +-# define PTRACE_PEEKUSER PTRACE_PEEKUSR +-#endif +- +-#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +-# define PTRACE_POKEUSER PTRACE_POKEUSR +-#endif +- +-void +-get_arch_dep(Process *proc) { +-} +- +-/* Returns 1 if syscall, 2 if sysret, 0 otherwise. +- */ +-int +-syscall_p(struct Process *proc, int status, int *sysnum) +-{ +- if (WIFSTOPPED(status) +- && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { +- struct callstack_element *elem = NULL; +- if (proc->callstack_depth > 0) +- elem = proc->callstack + proc->callstack_depth - 1; +- +- *sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, 4 * ORIG_EAX, 0); +- if (*sysnum == -1) { +- if (errno) +- return -1; +- /* Otherwise, ORIG_EAX == -1 means that the +- * system call should not be restarted. In +- * that case rely on what we have on +- * stack. */ +- if (elem != NULL && elem->is_syscall) +- *sysnum = elem->c_un.syscall; +- } +- +- if (elem != NULL && elem->is_syscall +- && elem->c_un.syscall == *sysnum) +- return 2; +- +- if (*sysnum >= 0) +- return 1; +- } +- return 0; +-} +- +-long +-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { +- if (arg_num == -1) { /* return value */ +- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EAX, 0); +- } +- +- if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { +- return ptrace(PTRACE_PEEKTEXT, proc->pid, +- proc->stack_pointer + 4 * (arg_num + 1), 0); +- } else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) { +-#if 0 +- switch (arg_num) { +- case 0: +- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EBX, 0); +- case 1: +- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * ECX, 0); +- case 2: +- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EDX, 0); +- case 3: +- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * ESI, 0); +- case 4: +- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EDI, 0); +- default: +- fprintf(stderr, +- "gimme_arg called with wrong arguments\n"); +- exit(2); +- } +-#else +- return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * arg_num, 0); +-#endif +- } else { +- fprintf(stderr, "gimme_arg called with wrong arguments\n"); +- exit(1); +- } +- +- return 0; +-} +- +-void +-save_register_args(enum tof type, Process *proc) { +-} +diff --git a/sysdeps/linux-gnu/ia64/arch.h b/sysdeps/linux-gnu/ia64/arch.h +index 673047c..00bb077 100644 +--- a/sysdeps/linux-gnu/ia64/arch.h ++++ b/sysdeps/linux-gnu/ia64/arch.h +@@ -1,3 +1,24 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2006,2011 Petr Machata ++ * Copyright (C) 2006 Ian Wienand ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ + #define ARCH_HAVE_DISABLE_BREAKPOINT 1 + #define ARCH_HAVE_ENABLE_BREAKPOINT 1 + +diff --git a/sysdeps/linux-gnu/ia64/trace.c b/sysdeps/linux-gnu/ia64/trace.c +index 385fac1..d4813e6 100644 +--- a/sysdeps/linux-gnu/ia64/trace.c ++++ b/sysdeps/linux-gnu/ia64/trace.c +@@ -1,3 +1,25 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2008,2009 Juan Cespedes ++ * Copyright (C) 2006 Steve Fink ++ * Copyright (C) 2006 Ian Wienand ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ + #include "config.h" + + #include +@@ -13,6 +35,7 @@ + + #include "proc.h" + #include "common.h" ++#include "type.h" + + /* What we think of as a bundle, ptrace thinks of it as two unsigned + * longs */ +@@ -244,18 +267,26 @@ gimme_float_arg(enum tof type, Process *proc, int arg_num) { + exit(1); + } + ++static unsigned f_index; ++ + long +-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { ++gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info) ++{ + union { + long l; + float f; + double d; + } cvt; + +- if (info->type == ARGTYPE_FLOAT) +- cvt.f = gimme_float_arg(type, proc, info->u.float_info.float_index); +- else if (info->type == ARGTYPE_DOUBLE) +- cvt.d = gimme_float_arg(type, proc, info->u.double_info.float_index); ++ if ((type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) ++ && arg_num == 0) ++ /* See above for the parameter passing convention. */ ++ f_index = 0; ++ ++ if (info != NULL && info->type == ARGTYPE_FLOAT) ++ cvt.f = gimme_float_arg(type, proc, f_index++); ++ else if (info != NULL && info->type == ARGTYPE_DOUBLE) ++ cvt.d = gimme_float_arg(type, proc, f_index++); + else + cvt.l = gimme_long_arg(type, proc, arg_num); + +@@ -263,9 +294,5 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { + } + + void +-save_register_args(enum tof type, Process *proc) { +-} +- +-void + get_arch_dep(Process *proc) { + } +diff --git a/sysdeps/linux-gnu/m68k/arch.h b/sysdeps/linux-gnu/m68k/arch.h +index 1790d09..9ca08ca 100644 +--- a/sysdeps/linux-gnu/m68k/arch.h ++++ b/sysdeps/linux-gnu/m68k/arch.h +@@ -1,3 +1,23 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 1998,2002,2004 Juan Cespedes ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ + #define BREAKPOINT_VALUE { 0x4e, 0x4f } + #define BREAKPOINT_LENGTH 2 + #define DECR_PC_AFTER_BREAK 2 +diff --git a/sysdeps/linux-gnu/m68k/trace.c b/sysdeps/linux-gnu/m68k/trace.c +index c63702d..4bec016 100644 +--- a/sysdeps/linux-gnu/m68k/trace.c ++++ b/sysdeps/linux-gnu/m68k/trace.c +@@ -47,7 +47,8 @@ syscall_p(Process *proc, int status, int *sysnum) { + } + + long +-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { ++gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info) ++{ + if (arg_num == -1) { /* return value */ + return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_D0, 0); + } +@@ -84,7 +85,3 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { + + return 0; + } +- +-void +-save_register_args(enum tof type, Process *proc) { +-} +diff --git a/sysdeps/linux-gnu/mipsel/arch.h b/sysdeps/linux-gnu/mipsel/arch.h +index f7e2316..9ee29fd 100644 +--- a/sysdeps/linux-gnu/mipsel/arch.h ++++ b/sysdeps/linux-gnu/mipsel/arch.h +@@ -1,3 +1,23 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2006 Eric Vaitl ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ + #ifndef LTRACE_MIPS_ARCH_H + #define LTRACE_MIPS_ARCH_H + +diff --git a/sysdeps/linux-gnu/mipsel/trace.c b/sysdeps/linux-gnu/mipsel/trace.c +index 4b999e4..fffaf75 100644 +--- a/sysdeps/linux-gnu/mipsel/trace.c ++++ b/sysdeps/linux-gnu/mipsel/trace.c +@@ -9,6 +9,7 @@ + #include "proc.h" + #include "common.h" + #include "mipsel.h" ++#include "type.h" + #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) + # define PTRACE_PEEKUSER PTRACE_PEEKUSR + #endif +@@ -118,7 +119,8 @@ I'm not doing any floating point support here. + + */ + long +-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { ++gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info) ++{ + long ret; + long addr; + debug(2,"type %d arg %d",type,arg_num); +@@ -174,19 +176,4 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { + return 0; + } + +-/** +- \param type Type of call/return +- \param proc Process to work with. +- +- Called by \c output_left(), which is called on a syscall or +- function. +- +- The other architectures stub this out, but seems to be the place to +- stash off the arguments on a call so we have them on the return. +- +-*/ +-void +-save_register_args(enum tof type, Process *proc) { +-} +- + /**@}*/ +diff --git a/sysdeps/linux-gnu/ppc/Makefile.am b/sysdeps/linux-gnu/ppc/Makefile.am +index 7392452..e302016 100644 +--- a/sysdeps/linux-gnu/ppc/Makefile.am ++++ b/sysdeps/linux-gnu/ppc/Makefile.am +@@ -4,7 +4,8 @@ noinst_LTLIBRARIES = \ + ___libcpu_la_SOURCES = \ + plt.c \ + regs.c \ +- trace.c ++ trace.c \ ++ fetch.c + + noinst_HEADERS = \ + arch.h \ +diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h +index 6e258e7..846961e 100644 +--- a/sysdeps/linux-gnu/ppc/arch.h ++++ b/sysdeps/linux-gnu/ppc/arch.h +@@ -1,3 +1,24 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2012 Petr Machata ++ * Copyright (C) 2006 Paul Gilliam ++ * Copyright (C) 2002,2004 Juan Cespedes ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ + #ifndef LTRACE_PPC_ARCH_H + #define LTRACE_PPC_ARCH_H + +@@ -18,6 +39,9 @@ + #define ARCH_HAVE_ADD_PLT_ENTRY + #define ARCH_HAVE_TRANSLATE_ADDRESS + #define ARCH_HAVE_DYNLINK_DONE ++#define ARCH_HAVE_FETCH_ARG ++#define ARCH_HAVE_SIZEOF ++#define ARCH_HAVE_ALIGNOF + + struct library_symbol; + +diff --git a/sysdeps/linux-gnu/ppc/fetch.c b/sysdeps/linux-gnu/ppc/fetch.c +new file mode 100644 +index 0000000..370f43e +--- /dev/null ++++ b/sysdeps/linux-gnu/ppc/fetch.c +@@ -0,0 +1,427 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "backend.h" ++#include "fetch.h" ++#include "type.h" ++#include "proc.h" ++#include "value.h" ++ ++static int allocate_gpr(struct fetch_context *ctx, struct Process *proc, ++ struct arg_type_info *info, struct value *valuep); ++ ++/* Floating point registers have the same width on 32-bit as well as ++ * 64-bit PPC, but presents a different API depending on ++ * whether ltrace is PPC32 or PPC64. ++ * ++ * This is PPC64 definition. The PPC32 is simply an array of 33 ++ * doubles, and doesn't contain the terminating pad. Both seem ++ * compatible enough. */ ++struct fpregs_t ++{ ++ double fpregs[32]; ++ double fpscr; ++ unsigned int _pad[2]; ++}; ++ ++typedef uint32_t gregs32_t[48]; ++typedef uint64_t gregs64_t[48]; ++ ++struct fetch_context { ++ target_address_t stack_pointer; ++ int greg; ++ int freg; ++ int ret_struct; ++ ++ union { ++ gregs32_t r32; ++ gregs64_t r64; ++ } regs; ++ struct fpregs_t fpregs; ++ ++}; ++ ++static int ++fetch_context_init(struct Process *proc, struct fetch_context *context) ++{ ++ context->greg = 3; ++ context->freg = 1; ++ ++ if (proc->e_machine == EM_PPC) ++ context->stack_pointer = proc->stack_pointer + 8; ++ else ++ context->stack_pointer = proc->stack_pointer + 112; ++ ++ /* When ltrace is 64-bit, we might use PTRACE_GETREGS to ++ * obtain 64-bit as well as 32-bit registers. But if we do it ++ * this way, 32-bit ltrace can obtain 64-bit registers. ++ * ++ * XXX this direction is not supported as of this writing, but ++ * should be eventually. */ ++ if (proc->e_machine == EM_PPC64) { ++ if (ptrace(PTRACE_GETREGS64, proc->pid, 0, ++ &context->regs.r64) < 0) ++ return -1; ++ } else { ++#ifdef __powerpc64__ ++ if (ptrace(PTRACE_GETREGS, proc->pid, 0, ++ &context->regs.r64) < 0) ++ return -1; ++ unsigned i; ++ for (i = 0; i < sizeof(context->regs.r64)/8; ++i) ++ context->regs.r32[i] = context->regs.r64[i]; ++#else ++ if (ptrace(PTRACE_GETREGS, proc->pid, 0, ++ &context->regs.r32) < 0) ++ return -1; ++#endif ++ } ++ ++ if (ptrace(PTRACE_GETFPREGS, proc->pid, 0, &context->fpregs) < 0) ++ return -1; ++ ++ return 0; ++} ++ ++struct fetch_context * ++arch_fetch_arg_init(enum tof type, struct Process *proc, ++ struct arg_type_info *ret_info) ++{ ++ struct fetch_context *context = malloc(sizeof(*context)); ++ if (context == NULL ++ || fetch_context_init(proc, context) < 0) { ++ free(context); ++ return NULL; ++ } ++ ++ /* Aggregates or unions of any length, and character strings ++ * of length longer than 8 bytes, will be returned in a ++ * storage buffer allocated by the caller. The caller will ++ * pass the address of this buffer as a hidden first argument ++ * in r3, causing the first explicit argument to be passed in ++ * r4. */ ++ context->ret_struct = ret_info->type == ARGTYPE_STRUCT; ++ if (context->ret_struct) ++ context->greg++; ++ ++ return context; ++} ++ ++struct fetch_context * ++arch_fetch_arg_clone(struct Process *proc, ++ struct fetch_context *context) ++{ ++ struct fetch_context *clone = malloc(sizeof(*context)); ++ if (clone == NULL) ++ return NULL; ++ *clone = *context; ++ return clone; ++} ++ ++static int ++allocate_stack_slot(struct fetch_context *ctx, struct Process *proc, ++ struct arg_type_info *info, struct value *valuep) ++{ ++ size_t sz = type_sizeof(proc, info); ++ if (sz == (size_t)-1) ++ return -1; ++ ++ size_t a = type_alignof(proc, info); ++ size_t off = 0; ++ if (proc->e_machine == EM_PPC && a < 4) ++ a = 4; ++ else if (proc->e_machine == EM_PPC64 && a < 8) ++ a = 8; ++ ++ /* XXX Remove the two double casts when target_address_t ++ * becomes integral type. */ ++ uintptr_t tmp = align((uint64_t)(uintptr_t)ctx->stack_pointer, a); ++ ctx->stack_pointer = (target_address_t)tmp; ++ ++ if (valuep != NULL) { ++ valuep->where = VAL_LOC_INFERIOR; ++ valuep->u.address = ctx->stack_pointer + off; ++ } ++ ++ ctx->stack_pointer += sz; ++ ++ return 0; ++} ++ ++static uint64_t ++read_gpr(struct fetch_context *ctx, struct Process *proc, int reg_num) ++{ ++ if (proc->e_machine == EM_PPC) ++ return ctx->regs.r32[reg_num]; ++ else ++ return ctx->regs.r64[reg_num]; ++} ++ ++/* The support for little endian PowerPC is in upstream Linux and BFD, ++ * and Unix-like Solaris, which we might well support at some point, ++ * runs PowerPC in little endian as well. This code moves SZ-sized ++ * value to the beginning of W-sized BUF regardless of ++ * endian. */ ++static void ++align_small_int(unsigned char *buf, size_t w, size_t sz) ++{ ++ assert(w == 4 || w == 8); ++ union { ++ uint64_t i64; ++ uint32_t i32; ++ uint16_t i16; ++ uint8_t i8; ++ char buf[0]; ++ } u; ++ memcpy(u.buf, buf, w); ++ if (w == 4) ++ u.i64 = u.i32; ++ ++ switch (sz) { ++ case 1: ++ u.i8 = u.i64; ++ break; ++ case 2: ++ u.i16 = u.i64; ++ break; ++ case 4: ++ u.i32 = u.i64; ++ case 8: ++ break; ++ } ++ ++ memcpy(buf, u.buf, sz); ++} ++ ++static int ++allocate_gpr(struct fetch_context *ctx, struct Process *proc, ++ struct arg_type_info *info, struct value *valuep) ++{ ++ if (ctx->greg > 10) ++ return allocate_stack_slot(ctx, proc, info, valuep); ++ ++ int reg_num = ctx->greg++; ++ if (valuep == NULL) ++ return 0; ++ ++ size_t sz = type_sizeof(proc, info); ++ if (sz == (size_t)-1) ++ return -1; ++ assert(sz == 1 || sz == 2 || sz == 4 || sz == 8); ++ if (value_reserve(valuep, sz) == NULL) ++ return -1; ++ ++ union { ++ uint64_t i64; ++ unsigned char buf[0]; ++ } u; ++ ++ u.i64 = read_gpr(ctx, proc, reg_num); ++ if (proc->e_machine == EM_PPC) ++ align_small_int(u.buf, 8, sz); ++ memcpy(value_get_raw_data(valuep), u.buf, sz); ++ return 0; ++} ++ ++static int ++allocate_float(struct fetch_context *ctx, struct Process *proc, ++ struct arg_type_info *info, struct value *valuep) ++{ ++ int pool = proc->e_machine == EM_PPC64 ? 13 : 8; ++ if (ctx->freg <= pool) { ++ union { ++ double d; ++ float f; ++ char buf[0]; ++ } u = { .d = ctx->fpregs.fpregs[ctx->freg] }; ++ ++ ctx->freg++; ++ if (proc->e_machine == EM_PPC64) ++ allocate_gpr(ctx, proc, info, NULL); ++ ++ size_t sz = sizeof(double); ++ if (info->type == ARGTYPE_FLOAT) { ++ sz = sizeof(float); ++ u.f = (float)u.d; ++ } ++ ++ if (value_reserve(valuep, sz) == NULL) ++ return -1; ++ ++ memcpy(value_get_raw_data(valuep), u.buf, sz); ++ return 0; ++ } ++ return allocate_stack_slot(ctx, proc, info, valuep); ++} ++ ++static int ++allocate_argument(struct fetch_context *ctx, struct Process *proc, ++ struct arg_type_info *info, struct value *valuep) ++{ ++ /* Floating point types and void are handled specially. */ ++ switch (info->type) { ++ case ARGTYPE_VOID: ++ value_set_word(valuep, 0); ++ return 0; ++ ++ case ARGTYPE_FLOAT: ++ case ARGTYPE_DOUBLE: ++ return allocate_float(ctx, proc, info, valuep); ++ ++ case ARGTYPE_STRUCT: ++ if (proc->e_machine == EM_PPC) { ++ if (value_pass_by_reference(valuep) < 0) ++ return -1; ++ } else { ++ /* PPC64: Fixed size aggregates and unions passed by ++ * value are mapped to as many doublewords of the ++ * parameter save area as the value uses in memory. ++ * [...] The first eight doublewords mapped to the ++ * parameter save area correspond to the registers r3 ++ * through r10. */ ++ } ++ /* fall through */ ++ case ARGTYPE_CHAR: ++ case ARGTYPE_SHORT: ++ case ARGTYPE_USHORT: ++ case ARGTYPE_INT: ++ case ARGTYPE_UINT: ++ case ARGTYPE_LONG: ++ case ARGTYPE_ULONG: ++ case ARGTYPE_POINTER: ++ break; ++ ++ case ARGTYPE_ARRAY: ++ /* Arrays decay into pointers. XXX Fortran? */ ++ assert(info->type != ARGTYPE_ARRAY); ++ abort(); ++ } ++ ++ unsigned width = proc->e_machine == EM_PPC64 ? 8 : 4; ++ ++ /* For other cases (integral types and aggregates), read the ++ * eightbytes comprising the data. */ ++ size_t sz = type_sizeof(proc, valuep->type); ++ if (sz == (size_t)-1) ++ return -1; ++ size_t slots = (sz + width - 1) / width; /* Round up. */ ++ unsigned char *buf = value_reserve(valuep, slots * width); ++ if (buf == NULL) ++ return -1; ++ struct arg_type_info *long_info = type_get_simple(ARGTYPE_LONG); ++ ++ unsigned char *ptr = buf; ++ while (slots-- > 0) { ++ struct value val; ++ value_init(&val, proc, NULL, long_info, 0); ++ int rc = allocate_gpr(ctx, proc, long_info, &val); ++ if (rc >= 0) { ++ memcpy(ptr, value_get_data(&val, NULL), width); ++ ptr += width; ++ } ++ value_destroy(&val); ++ if (rc < 0) ++ return rc; ++ } ++ ++ /* Small values need post-processing. */ ++ if (sz < width) { ++ switch (info->type) { ++ case ARGTYPE_LONG: ++ case ARGTYPE_ULONG: ++ case ARGTYPE_POINTER: ++ case ARGTYPE_DOUBLE: ++ case ARGTYPE_VOID: ++ abort(); ++ ++ /* Simple integer types (char, short, int, long, enum) ++ * are mapped to a single doubleword. Values shorter ++ * than a doubleword are sign or zero extended as ++ * necessary. */ ++ case ARGTYPE_CHAR: ++ case ARGTYPE_SHORT: ++ case ARGTYPE_INT: ++ case ARGTYPE_USHORT: ++ case ARGTYPE_UINT: ++ align_small_int(buf, width, sz); ++ break; ++ ++ /* Single precision floating point values are mapped ++ * to the second word in a single doubleword. ++ * ++ * An aggregate or union smaller than one doubleword ++ * in size is padded so that it appears in the least ++ * significant bits of the doubleword. */ ++ case ARGTYPE_FLOAT: ++ case ARGTYPE_ARRAY: ++ case ARGTYPE_STRUCT: ++ memmove(buf, buf + width - sz, sz); ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++int ++arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, ++ struct Process *proc, ++ struct arg_type_info *info, struct value *valuep) ++{ ++ return allocate_argument(ctx, proc, info, valuep); ++} ++ ++int ++arch_fetch_retval(struct fetch_context *ctx, enum tof type, ++ struct Process *proc, struct arg_type_info *info, ++ struct value *valuep) ++{ ++ if (ctx->ret_struct) { ++ assert(info->type == ARGTYPE_STRUCT); ++ ++ uint64_t addr = read_gpr(ctx, proc, 3); ++ value_init(valuep, proc, NULL, info, 0); ++ ++ valuep->where = VAL_LOC_INFERIOR; ++ /* XXX Remove the double cast when target_address_t ++ * becomes integral type. */ ++ valuep->u.address = (target_address_t)(uintptr_t)addr; ++ return 0; ++ } ++ ++ if (fetch_context_init(proc, ctx) < 0) ++ return -1; ++ return allocate_argument(ctx, proc, info, valuep); ++} ++ ++void ++arch_fetch_arg_done(struct fetch_context *context) ++{ ++ free(context); ++} +diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c +index 9717738..944bd6a 100644 +--- a/sysdeps/linux-gnu/ppc/plt.c ++++ b/sysdeps/linux-gnu/ppc/plt.c +@@ -11,6 +11,7 @@ + #include "library.h" + #include "breakpoint.h" + #include "linux-gnu/trace.h" ++#include "backend.h" + + /* There are two PLT types on 32-bit PPC: old-style, BSS PLT, and + * new-style "secure" PLT. We can tell one from the other by the +diff --git a/sysdeps/linux-gnu/ppc/ptrace.h b/sysdeps/linux-gnu/ppc/ptrace.h +index c587f40..c3cbcb6 100644 +--- a/sysdeps/linux-gnu/ppc/ptrace.h ++++ b/sysdeps/linux-gnu/ppc/ptrace.h +@@ -1,16 +1 @@ + #include +-#include +- +-#ifdef __powerpc64__ +-#define GET_FPREG(RS, R) ((RS)[R]) +-#else +-#define GET_FPREG(RS, R) ((RS).fpregs[R]) +-#endif +- +-typedef struct { +- int valid; +- gregset_t regs; +- fpregset_t fpregs; +- gregset_t regs_copy; +- fpregset_t fpregs_copy; +-} proc_archdep; +diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c +index 742785a..0b734e4 100644 +--- a/sysdeps/linux-gnu/ppc/trace.c ++++ b/sysdeps/linux-gnu/ppc/trace.c +@@ -1,18 +1,41 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2010,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2011 Andreas Schwab ++ * Copyright (C) 2002,2004,2008,2009 Juan Cespedes ++ * Copyright (C) 2008 Luis Machado, IBM Corporation ++ * Copyright (C) 2006 Ian Wienand ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ + #include "config.h" + +-#include +-#include +-#include +-#include +-#include ++#include + #include + #include ++#include + #include + +-#include "proc.h" ++#include "backend.h" ++#include "breakpoint.h" + #include "common.h" ++#include "proc.h" + #include "ptrace.h" +-#include "breakpoint.h" ++#include "type.h" + + #if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) + # define PTRACE_PEEKUSER PTRACE_PEEKUSR +@@ -24,23 +47,13 @@ + + void + get_arch_dep(Process *proc) { +- if (proc->arch_ptr == NULL) { +- proc->arch_ptr = malloc(sizeof(proc_archdep)); + #ifdef __powerpc64__ +- proc->mask_32bit = (proc->e_machine == EM_PPC); ++ proc->mask_32bit = (proc->e_machine == EM_PPC); + #endif +- } +- +- proc_archdep *a = (proc_archdep *) (proc->arch_ptr); +- a->valid = (ptrace(PTRACE_GETREGS, proc->pid, 0, &a->regs) >= 0) +- && (ptrace(PTRACE_GETFPREGS, proc->pid, 0, &a->fpregs) >= 0); + } + + #define SYSCALL_INSN 0x44000002 + +-unsigned int greg = 3; +-unsigned int freg = 1; +- + /* Returns 1 if syscall, 2 if sysret, 0 otherwise. */ + int + syscall_p(Process *proc, int status, int *sysnum) { +@@ -66,111 +79,6 @@ syscall_p(Process *proc, int status, int *sysnum) { + return 0; + } + +-static long +-gimme_arg_regset(enum tof type, Process *proc, int arg_num, arg_type_info *info, +- gregset_t *regs, fpregset_t *fpregs) +-{ +- union { long val; float fval; double dval; } cvt; +- +- if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) { +- if (freg <= 13 || (proc->mask_32bit && freg <= 8)) { +- double val = GET_FPREG(*fpregs, freg); +- +- if (info->type == ARGTYPE_FLOAT) +- cvt.fval = val; +- else +- cvt.dval = val; +- +- freg++; +- greg++; +- +- return cvt.val; +- } +- } +- else if (greg <= 10) +- return (*regs)[greg++]; +- else { +-#ifdef __powerpc64__ +- if (proc->mask_32bit) +- return ptrace (PTRACE_PEEKDATA, proc->pid, +- proc->stack_pointer + 8 + +- sizeof (int) * (arg_num - 8), 0) >> 32; +- else +- return ptrace (PTRACE_PEEKDATA, proc->pid, +- proc->stack_pointer + 112 + +- sizeof (long) * (arg_num - 8), 0); +-#else +- return ptrace (PTRACE_PEEKDATA, proc->pid, +- proc->stack_pointer + 8 + +- sizeof (long) * (arg_num - 8), 0); +-#endif +- } +- +- return 0; +-} +- +-static long +-gimme_retval(Process *proc, int arg_num, arg_type_info *info, +- gregset_t *regs, fpregset_t *fpregs) +-{ +- union { long val; float fval; double dval; } cvt; +- if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) { +- double val = GET_FPREG(*fpregs, 1); +- +- if (info->type == ARGTYPE_FLOAT) +- cvt.fval = val; +- else +- cvt.dval = val; +- +- return cvt.val; +- } +- else +- return (*regs)[3]; +-} +- +-/* Grab functions arguments based on the PPC64 ABI. */ +-long +-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) +-{ +- proc_archdep *arch = (proc_archdep *)proc->arch_ptr; +- if (arch == NULL || !arch->valid) +- return -1; +- +- /* Check if we're entering a new function call to list parameters. If +- so, initialize the register control variables to keep track of where +- the parameters were stored. */ +- if ((type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) +- && arg_num == 0) { +- /* Initialize the set of registrers for parameter passing. */ +- greg = 3; +- freg = 1; +- } +- +- +- if (type == LT_TOF_FUNCTIONR) { +- if (arg_num == -1) +- return gimme_retval(proc, arg_num, info, +- &arch->regs, &arch->fpregs); +- else +- return gimme_arg_regset(type, proc, arg_num, info, +- &arch->regs_copy, +- &arch->fpregs_copy); +- } +- else +- return gimme_arg_regset(type, proc, arg_num, info, +- &arch->regs, &arch->fpregs); +-} +- +-void +-save_register_args(enum tof type, Process *proc) { +- proc_archdep *arch = (proc_archdep *)proc->arch_ptr; +- if (arch == NULL || !arch->valid) +- return; +- +- memcpy(&arch->regs_copy, &arch->regs, sizeof(arch->regs)); +- memcpy(&arch->fpregs_copy, &arch->fpregs, sizeof(arch->fpregs)); +-} +- + /* The atomic skip code is mostly taken from GDB. */ + + /* Instruction masks used during single-stepping of atomic +@@ -309,3 +188,77 @@ arch_atomic_singlestep(struct Process *proc, struct breakpoint *sbp, + ptrace(PTRACE_CONT, proc->pid, 0, 0); + return 0; + } ++ ++size_t ++arch_type_sizeof(struct Process *proc, struct arg_type_info *info) ++{ ++ if (proc == NULL) ++ return (size_t)-2; ++ ++ switch (info->type) { ++ case ARGTYPE_VOID: ++ return 0; ++ ++ case ARGTYPE_CHAR: ++ return 1; ++ ++ case ARGTYPE_SHORT: ++ case ARGTYPE_USHORT: ++ return 2; ++ ++ case ARGTYPE_INT: ++ case ARGTYPE_UINT: ++ return 4; ++ ++ case ARGTYPE_LONG: ++ case ARGTYPE_ULONG: ++ case ARGTYPE_POINTER: ++ return proc->e_machine == EM_PPC64 ? 8 : 4; ++ ++ case ARGTYPE_FLOAT: ++ return 4; ++ case ARGTYPE_DOUBLE: ++ return 8; ++ ++ case ARGTYPE_ARRAY: ++ case ARGTYPE_STRUCT: ++ /* Use default value. */ ++ return (size_t)-2; ++ } ++ assert(info->type != info->type); ++ abort(); ++} ++ ++size_t ++arch_type_alignof(struct Process *proc, struct arg_type_info *info) ++{ ++ if (proc == NULL) ++ return (size_t)-2; ++ ++ switch (info->type) { ++ case ARGTYPE_VOID: ++ assert(info->type != ARGTYPE_VOID); ++ break; ++ ++ case ARGTYPE_CHAR: ++ case ARGTYPE_SHORT: ++ case ARGTYPE_USHORT: ++ case ARGTYPE_INT: ++ case ARGTYPE_UINT: ++ case ARGTYPE_LONG: ++ case ARGTYPE_ULONG: ++ case ARGTYPE_POINTER: ++ case ARGTYPE_FLOAT: ++ case ARGTYPE_DOUBLE: ++ /* On both PPC and PPC64, fundamental types have the ++ * same alignment as size. */ ++ return arch_type_sizeof(proc, info); ++ ++ case ARGTYPE_ARRAY: ++ case ARGTYPE_STRUCT: ++ /* Use default value. */ ++ return (size_t)-2; ++ } ++ assert(info->type != info->type); ++ abort(); ++} +diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c +index c5282e4..e7556f5 100644 +--- a/sysdeps/linux-gnu/proc.c ++++ b/sysdeps/linux-gnu/proc.c +@@ -1,3 +1,26 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2010 Zachary T Welch, CodeSourcery ++ * Copyright (C) 2010 Joe Damato ++ * Copyright (C) 1998,2008,2009 Juan Cespedes ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ + #define _GNU_SOURCE /* For getline. */ + #include "config.h" + +@@ -12,13 +35,18 @@ + #include + #include + #include ++#include + #include + #include + +-#include "common.h" ++#include "backend.h" + #include "breakpoint.h" +-#include "proc.h" ++#include "config.h" ++#include "debug.h" ++#include "events.h" + #include "library.h" ++#include "ltrace-elf.h" ++#include "proc.h" + + /* /proc/pid doesn't exist just after the fork, and sometimes `ltrace' + * couldn't open it to find the executable. So it may be necessary to +@@ -88,7 +116,7 @@ each_line_starting(FILE *file, const char *prefix, + char * line; + while ((line = find_line_starting(file, prefix, len)) != NULL) { + enum callback_status st = (*cb)(line, prefix, data); +- free (line); ++ free(line); + if (st == CBS_STOP) + return; + } +@@ -588,3 +616,9 @@ task_kill (pid_t pid, int sig) + ret = syscall (__NR_tkill, pid, sig); + return ret; + } ++ ++void ++process_removed(struct Process *proc) ++{ ++ delete_events_for(proc); ++} +diff --git a/sysdeps/linux-gnu/s390/arch.h b/sysdeps/linux-gnu/s390/arch.h +index 5cf168c..6597355 100644 +--- a/sysdeps/linux-gnu/s390/arch.h ++++ b/sysdeps/linux-gnu/s390/arch.h +@@ -1,8 +1,23 @@ + /* +-** S/390 version +-** (C) Copyright 2001 IBM Poughkeepsie, IBM Corporation +-*/ ++ * This file is part of ltrace. ++ * Copyright (C) 2001 IBM Poughkeepsie, IBM Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ + + #define BREAKPOINT_VALUE { 0x00, 0x01 } + #define BREAKPOINT_LENGTH 2 + #define DECR_PC_AFTER_BREAK 2 +diff --git a/sysdeps/linux-gnu/s390/trace.c b/sysdeps/linux-gnu/s390/trace.c +index 8c08f1f..3381ccc 100644 +--- a/sysdeps/linux-gnu/s390/trace.c ++++ b/sysdeps/linux-gnu/s390/trace.c +@@ -161,7 +161,8 @@ syscall_p(Process *proc, int status, int *sysnum) { + } + + long +-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { ++gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info) ++{ + long ret; + + switch (arg_num) { +@@ -200,7 +201,3 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { + #endif + return ret; + } +- +-void +-save_register_args(enum tof type, Process *proc) { +-} +diff --git a/sysdeps/linux-gnu/sparc/arch.h b/sysdeps/linux-gnu/sparc/arch.h +index 75251b8..9685d13 100644 +--- a/sysdeps/linux-gnu/sparc/arch.h ++++ b/sysdeps/linux-gnu/sparc/arch.h +@@ -1,3 +1,23 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2004 Juan Cespedes ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ + #define BREAKPOINT_VALUE {0x91, 0xd0, 0x20, 0x01} + #define BREAKPOINT_LENGTH 4 + #define DECR_PC_AFTER_BREAK 0 +diff --git a/sysdeps/linux-gnu/sparc/trace.c b/sysdeps/linux-gnu/sparc/trace.c +index e05c4d3..83337fc 100644 +--- a/sysdeps/linux-gnu/sparc/trace.c ++++ b/sysdeps/linux-gnu/sparc/trace.c +@@ -45,7 +45,8 @@ syscall_p(Process *proc, int status, int *sysnum) { + } + + long +-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { ++gimme_arg(enum tof type, Process *proc, int arg_num, struct arg_type_info *info) ++{ + proc_archdep *a = (proc_archdep *) proc->arch_ptr; + if (!a->valid) { + fprintf(stderr, "Could not get child registers\n"); +@@ -69,14 +70,3 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { + } + return 0; + } +- +-void +-save_register_args(enum tof type, Process *proc) { +- proc_archdep *a = (proc_archdep *) proc->arch_ptr; +- if (a->valid) { +- if (type == LT_TOF_FUNCTION) +- memcpy(a->func_arg, &a->regs.u_regs[UREG_G7], sizeof(a->func_arg)); +- else +- memcpy(a->sysc_arg, &a->regs.u_regs[UREG_G7], sizeof(a->sysc_arg)); +- } +-} +diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c +index bd5d826..cef8e3d 100644 +--- a/sysdeps/linux-gnu/trace.c ++++ b/sysdeps/linux-gnu/trace.c +@@ -1,3 +1,26 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2007,2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2010 Joe Damato ++ * Copyright (C) 1998,2002,2003,2004,2008,2009 Juan Cespedes ++ * Copyright (C) 2006 Ian Wienand ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ + #include + #include + #include +@@ -14,11 +37,15 @@ + #include + #include + +-#include "common.h" ++#include "linux-gnu/trace.h" ++#include "backend.h" +-#include "config.h" + #include "breakpoint.h" ++#include "debug.h" ++#include "events.h" ++#include "options.h" + #include "proc.h" +-#include "linux-gnu/trace.h" ++#include "ptrace.h" ++#include "type.h" + + #include "config.h" + #ifdef HAVE_LIBSELINUX +@@ -47,44 +74,6 @@ + + #endif /* PTRACE_EVENT_FORK */ + +-#ifdef ARCH_HAVE_UMOVELONG +-extern int arch_umovelong (Process *, void *, long *, arg_type_info *); +-int +-umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { +- return arch_umovelong (proc, addr, result, info); +-} +-#else +-/* Read a single long from the process's memory address 'addr' */ +-int +-umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { +- long pointed_to; +- +- errno = 0; +- pointed_to = ptrace (PTRACE_PEEKTEXT, proc->pid, addr, 0); +- if (pointed_to == -1 && errno) +- return -errno; +- +-#if SIZEOF_LONG == 8 +- if (info != NULL +- && (info->type == ARGTYPE_INT +- || (proc->mask_32bit +- && (info->type == ARGTYPE_POINTER +- || info->type == ARGTYPE_STRING)))) { +-#if defined (ARCH_ENDIAN_LITTLE) +- pointed_to &= 0x00000000ffffffffUL; +-#elif defined (ARCH_ENDIAN_BIG) +- pointed_to = (long)(((unsigned long)pointed_to) >> 32); +-#else +-# error arch.h has to define endianness +-#endif +- } +-#endif +- +- *result = pointed_to; +- return 0; +-} +-#endif +- + void + trace_fail_warning(pid_t pid) + { +@@ -366,15 +363,15 @@ process_stopping_done(struct process_stopping_handler * self, Process * leader) + { + debug(DEBUG_PROCESS, "process stopping done %d", + self->task_enabling_breakpoint->pid); +- size_t i; ++ + if (!self->exiting) { ++ size_t i; + for (i = 0; i < self->pids.count; ++i) + if (self->pids.tasks[i].pid != 0 + && (self->pids.tasks[i].delivered + || self->pids.tasks[i].sysret)) + continue_process(self->pids.tasks[i].pid); + continue_process(self->task_enabling_breakpoint->pid); +- destroy_event_handler(leader); + } + + if (self->exiting) { +@@ -390,6 +387,7 @@ process_stopping_done(struct process_stopping_handler * self, Process * leader) + case CBS_CONT: + goto ugly_workaround; + } ++ destroy_event_handler(leader); + } + } + +@@ -678,8 +676,8 @@ singlestep_error(struct process_stopping_handler *self) + struct Process *teb = self->task_enabling_breakpoint; + struct breakpoint *sbp = self->breakpoint_being_enabled; + fprintf(stderr, "%d couldn't continue when handling %s (%p) at %p\n", +- teb->pid, sbp->libsym != NULL ? sbp->libsym->name : NULL, +- sbp->addr, get_instruction_pointer(teb)); ++ teb->pid, breakpoint_name(sbp), sbp->addr, ++ get_instruction_pointer(teb)); + delete_breakpoint(teb->leader, sbp->addr); + } + +@@ -1233,32 +1231,3 @@ umovebytes(Process *proc, void *addr, void *laddr, size_t len) { + + return bytes_read; + } +- +-/* Read a series of bytes starting at the process's memory address +- 'addr' and continuing until a NUL ('\0') is seen or 'len' bytes +- have been read. +-*/ +-int +-umovestr(Process *proc, void *addr, int len, void *laddr) { +- union { +- long a; +- char c[sizeof(long)]; +- } a; +- unsigned i; +- int offset = 0; +- +- while (offset < len) { +- a.a = ptrace(PTRACE_PEEKTEXT, proc->pid, addr + offset, 0); +- for (i = 0; i < sizeof(long); i++) { +- if (a.c[i] && offset + (signed)i < len) { +- *(char *)(laddr + offset + i) = a.c[i]; +- } else { +- *(char *)(laddr + offset + i) = '\0'; +- return 0; +- } +- } +- offset += sizeof(long); +- } +- *(char *)(laddr + offset) = '\0'; +- return 0; +-} +diff --git a/sysdeps/linux-gnu/trace.h b/sysdeps/linux-gnu/trace.h +index 0f40709..88ac33d 100644 +--- a/sysdeps/linux-gnu/trace.h ++++ b/sysdeps/linux-gnu/trace.h +@@ -21,6 +21,8 @@ + #ifndef _LTRACE_LINUX_TRACE_H_ + #define _LTRACE_LINUX_TRACE_H_ + ++#include "proc.h" ++ + /* This publishes some Linux-specific data structures used for process + * handling. */ + +diff --git a/sysdeps/linux-gnu/x86/Makefile.am b/sysdeps/linux-gnu/x86/Makefile.am +new file mode 100644 +index 0000000..d5eb6e7 +--- /dev/null ++++ b/sysdeps/linux-gnu/x86/Makefile.am +@@ -0,0 +1,19 @@ ++noinst_LTLIBRARIES = \ ++ ../libcpu.la ++ ++___libcpu_la_SOURCES = \ ++ plt.c \ ++ regs.c \ ++ trace.c \ ++ fetch.c ++ ++noinst_HEADERS = \ ++ arch.h \ ++ ptrace.h \ ++ signalent.h \ ++ signalent1.h \ ++ syscallent.h \ ++ syscallent1.h ++ ++MAINTAINERCLEANFILES = \ ++ Makefile.in +diff --git a/sysdeps/linux-gnu/x86/arch.h b/sysdeps/linux-gnu/x86/arch.h +new file mode 100644 +index 0000000..77a09d7 +--- /dev/null ++++ b/sysdeps/linux-gnu/x86/arch.h +@@ -0,0 +1,40 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011 Petr Machata ++ * Copyright (C) 2006 Ian Wienand ++ * Copyright (C) 2004 Juan Cespedes ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#define BREAKPOINT_VALUE {0xcc} ++#define BREAKPOINT_LENGTH 1 ++#define DECR_PC_AFTER_BREAK 1 ++#define ARCH_HAVE_FETCH_ARG ++#define ARCH_HAVE_SIZEOF ++#define ARCH_HAVE_ALIGNOF ++#define ARCH_ENDIAN_LITTLE ++ ++#ifdef __x86_64__ ++#define LT_ELFCLASS ELFCLASS64 ++#define LT_ELF_MACHINE EM_X86_64 ++#endif ++#define LT_ELFCLASS2 ELFCLASS32 ++#define LT_ELF_MACHINE2 EM_386 ++ ++/* __NR_fork, __NR_clone, __NR_clone2, __NR_vfork and __NR_execve ++ from asm-i386/unistd.h. */ ++#define FORK_EXEC_SYSCALLS , { 2, 120, -1, 190, 11 } +diff --git a/sysdeps/linux-gnu/x86/fetch.c b/sysdeps/linux-gnu/x86/fetch.c +new file mode 100644 +index 0000000..64f57f3 +--- /dev/null ++++ b/sysdeps/linux-gnu/x86/fetch.c +@@ -0,0 +1,809 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "backend.h" ++#include "expr.h" ++#include "fetch.h" ++#include "proc.h" ++#include "ptrace.h" ++#include "type.h" ++#include "value.h" ++ ++enum arg_class { ++ CLASS_INTEGER, ++ CLASS_SSE, ++ CLASS_NO, ++ CLASS_MEMORY, ++ CLASS_X87, ++}; ++ ++enum reg_pool { ++ POOL_FUNCALL, ++ POOL_SYSCALL, ++ /* A common pool for system call and function call return is ++ * enough, the ABI is similar enough. */ ++ POOL_RETVAL, ++}; ++ ++struct fetch_context ++{ ++ struct user_regs_struct iregs; ++ struct user_fpregs_struct fpregs; ++ ++ void *stack_pointer; ++ size_t ireg; /* Used-up integer registers. */ ++ size_t freg; /* Used-up floating registers. */ ++ ++ union { ++ struct { ++ /* Storage classes for return type. We need ++ * to compute them anyway, so let's keep them ++ * around. */ ++ enum arg_class ret_classes[2]; ++ ssize_t num_ret_classes; ++ } x86_64; ++ struct { ++ struct value retval; ++ } ix86; ++ } u; ++}; ++ ++#ifndef __x86_64__ ++__attribute__((noreturn)) static void ++i386_unreachable(void) ++{ ++ abort(); ++} ++#endif ++ ++static int ++contains_unaligned_fields(struct arg_type_info *info) ++{ ++ /* XXX currently we don't support structure alignment. */ ++ return 0; ++} ++ ++static int ++has_nontrivial_ctor_dtor(struct arg_type_info *info) ++{ ++ /* XXX another unsupported aspect of type info. We might call ++ * these types "class" instead of "struct" in the config ++ * file. */ ++ return 0; ++} ++ ++static void ++copy_int_register(struct fetch_context *context, ++ struct value *valuep, unsigned long val, size_t offset) ++{ ++ if (valuep != NULL) { ++ unsigned char *buf = value_get_raw_data(valuep); ++ memcpy(buf + offset, &val, sizeof(val)); ++ } ++ context->ireg++; ++} ++ ++static void ++copy_sse_register(struct fetch_context *context, struct value *valuep, ++ int half, size_t sz, size_t offset) ++{ ++#ifdef __x86_64__ ++ union { ++ uint32_t sse[4]; ++ long halves[2]; ++ } u; ++ size_t off = 4 * context->freg++; ++ memcpy(u.sse, context->fpregs.xmm_space + off, sizeof(u.sse)); ++ ++ if (valuep != NULL) { ++ unsigned char *buf = value_get_raw_data(valuep); ++ memcpy(buf + offset, u.halves + half, sz); ++ } ++#else ++ i386_unreachable(); ++#endif ++} ++ ++static void ++allocate_stack_slot(struct fetch_context *context, ++ struct value *valuep, size_t sz, size_t offset, ++ size_t archw) ++{ ++ size_t a = type_alignof(valuep->inferior, valuep->type); ++ if (a < archw) ++ a = archw; ++ context->stack_pointer ++ = (void *)align((unsigned long)context->stack_pointer, a); ++ ++ if (valuep != NULL) { ++ valuep->where = VAL_LOC_INFERIOR; ++ valuep->u.address = context->stack_pointer; ++ } ++ context->stack_pointer += sz; ++} ++ ++static enum arg_class ++allocate_x87(struct fetch_context *context, struct value *valuep, ++ size_t sz, size_t offset, enum reg_pool pool, size_t archw) ++{ ++ /* Both i386 and x86_64 ABI only ever really use x87 registers ++ * to return values. Otherwise, the parameter is treated as ++ * if it were CLASS_MEMORY. On x86_64 x87 registers are only ++ * used for returning long double values, which we currently ++ * don't support. */ ++ ++ if (pool != POOL_RETVAL) { ++ allocate_stack_slot(context, valuep, sz, offset, archw); ++ return CLASS_MEMORY; ++ ++ } ++ ++ /* If the class is X87, the value is returned on the X87 stack ++ * in %st0 as 80-bit x87 number. ++ * ++ * If the class is X87UP, the value is returned together with ++ * the previous X87 value in %st0. ++ * ++ * If the class is COMPLEX_X87, the real part of the value is ++ * returned in %st0 and the imaginary part in %st1. */ ++ ++ if (valuep != NULL) { ++ union { ++ long double ld; ++ double d; ++ float f; ++ char buf[0]; ++ } u; ++ ++ /* The x87 floating point value is in long double ++ * format, so we need to convert in to the right type. ++ * Alternatively we might just leave it as is and ++ * smuggle the long double type into the value (via ++ * value_set_type), but for that we first need to ++ * support long double in the first place. */ ++ ++#ifdef __x86_64__ ++ unsigned int *reg; ++#else ++ long int *reg; ++#endif ++ reg = &context->fpregs.st_space[0]; ++ memcpy(&u.ld, reg, sizeof(u)); ++ if (valuep->type->type == ARGTYPE_FLOAT) ++ u.f = (float)u.ld; ++ else if (valuep->type->type == ARGTYPE_DOUBLE) ++ u.d = (double)u.ld; ++ else ++ assert(!"Unexpected floating type!"), abort(); ++ ++ unsigned char *buf = value_get_raw_data(valuep); ++ memcpy(buf + offset, u.buf, sz); ++ } ++ return CLASS_X87; ++} ++ ++static enum arg_class ++allocate_integer(struct fetch_context *context, struct value *valuep, ++ size_t sz, size_t offset, enum reg_pool pool) ++{ ++#define HANDLE(NUM, WHICH) \ ++ case NUM: \ ++ copy_int_register(context, valuep, \ ++ context->iregs.WHICH, offset); \ ++ return CLASS_INTEGER ++ ++ switch (pool) { ++ case POOL_FUNCALL: ++#ifdef __x86_64__ ++ switch (context->ireg) { ++ HANDLE(0, rdi); ++ HANDLE(1, rsi); ++ HANDLE(2, rdx); ++ HANDLE(3, rcx); ++ HANDLE(4, r8); ++ HANDLE(5, r9); ++ default: ++ allocate_stack_slot(context, valuep, sz, offset, 8); ++ return CLASS_MEMORY; ++ } ++#else ++ i386_unreachable(); ++#endif ++ ++ case POOL_SYSCALL: ++#ifdef __x86_64__ ++ switch (context->ireg) { ++ HANDLE(0, rdi); ++ HANDLE(1, rsi); ++ HANDLE(2, rdx); ++ HANDLE(3, r10); ++ HANDLE(4, r8); ++ HANDLE(5, r9); ++ default: ++ assert(!"More than six syscall arguments???"); ++ abort(); ++ } ++#else ++ i386_unreachable(); ++#endif ++ ++ case POOL_RETVAL: ++ switch (context->ireg) { ++#ifdef __x86_64__ ++ HANDLE(0, rax); ++ HANDLE(1, rdx); ++#else ++ HANDLE(0, eax); ++#endif ++ default: ++ assert(!"Too many return value classes."); ++ abort(); ++ } ++ } ++ ++ abort(); ++ ++#undef HANDLE ++} ++ ++static enum arg_class ++allocate_sse(struct fetch_context *context, struct value *valuep, ++ size_t sz, size_t offset, enum reg_pool pool) ++{ ++ size_t num_regs = 0; ++ switch (pool) { ++ case POOL_FUNCALL: ++ num_regs = 8; ++ case POOL_SYSCALL: ++ break; ++ case POOL_RETVAL: ++ num_regs = 2; ++ } ++ ++ if (context->freg >= num_regs) { ++ /* We shouldn't see overflow for RETVAL or SYSCALL ++ * pool. */ ++ assert(pool == POOL_FUNCALL); ++ allocate_stack_slot(context, valuep, sz, offset, 8); ++ return CLASS_MEMORY; ++ } else { ++ copy_sse_register(context, valuep, 0, sz, offset); ++ return CLASS_SSE; ++ } ++} ++ ++/* This allocates registers or stack space for another argument of the ++ * class CLS. */ ++static enum arg_class ++allocate_class(enum arg_class cls, struct fetch_context *context, ++ struct value *valuep, size_t sz, size_t offset, enum reg_pool pool) ++{ ++ switch (cls) { ++ case CLASS_MEMORY: ++ allocate_stack_slot(context, valuep, sz, offset, 8); ++ case CLASS_NO: ++ return cls; ++ ++ case CLASS_INTEGER: ++ return allocate_integer(context, valuep, sz, offset, pool); ++ ++ case CLASS_SSE: ++ return allocate_sse(context, valuep, sz, offset, pool); ++ ++ case CLASS_X87: ++ return allocate_x87(context, valuep, sz, offset, pool, 8); ++ } ++ abort(); ++} ++ ++static ssize_t ++classify(struct Process *proc, struct fetch_context *context, ++ struct arg_type_info *info, struct value *valuep, enum arg_class classes[], ++ size_t sz, size_t eightbytes); ++ ++/* This classifies one eightbyte part of an array or struct. */ ++static ssize_t ++classify_eightbyte(struct Process *proc, struct fetch_context *context, ++ struct arg_type_info *info, struct value *valuep, ++ enum arg_class *classp, size_t start, size_t end, ++ struct arg_type_info *(*getter)(struct arg_type_info *, ++ size_t)) ++{ ++ size_t i; ++ enum arg_class cls = CLASS_NO; ++ for (i = start; i < end; ++i) { ++ enum arg_class cls2; ++ struct arg_type_info *info2 = getter(info, i); ++ size_t sz = type_sizeof(proc, info2); ++ if (sz == (size_t)-1) ++ return -1; ++ if (classify(proc, context, info2, valuep, &cls2, sz, 1) < 0) ++ return -1; ++ ++ if (cls == CLASS_NO) ++ cls = cls2; ++ else if (cls2 == CLASS_NO || cls == cls2) ++ ; ++ else if (cls == CLASS_MEMORY || cls2 == CLASS_MEMORY) ++ cls = CLASS_MEMORY; ++ else if (cls == CLASS_INTEGER || cls2 == CLASS_INTEGER) ++ cls = CLASS_INTEGER; ++ else ++ cls = CLASS_SSE; ++ } ++ ++ *classp = cls; ++ return 1; ++} ++ ++/* This classifies small arrays and structs. */ ++static ssize_t ++classify_eightbytes(struct Process *proc, struct fetch_context *context, ++ struct arg_type_info *info, struct value *valuep, ++ enum arg_class classes[], size_t elements, ++ size_t eightbytes, ++ struct arg_type_info *(*getter)(struct arg_type_info *, ++ size_t)) ++{ ++ if (eightbytes > 1) { ++ /* Where the second eightbyte starts. Number of the ++ * first element in the structure that belongs to the ++ * second eightbyte. */ ++ size_t start_2nd = 0; ++ size_t i; ++ for (i = 0; i < elements; ++i) ++ if (type_offsetof(proc, info, i) >= 8) { ++ start_2nd = i; ++ break; ++ } ++ ++ enum arg_class cls1, cls2; ++ if (classify_eightbyte(proc, context, info, valuep, &cls1, ++ 0, start_2nd, getter) < 0 ++ || classify_eightbyte(proc, context, info, valuep, &cls2, ++ start_2nd, elements, getter) < 0) ++ return -1; ++ ++ if (cls1 == CLASS_MEMORY || cls2 == CLASS_MEMORY) { ++ classes[0] = CLASS_MEMORY; ++ return 1; ++ } ++ ++ classes[0] = cls1; ++ classes[1] = cls2; ++ return 2; ++ } ++ ++ return classify_eightbyte(proc, context, info, valuep, classes, ++ 0, elements, getter); ++} ++ ++static struct arg_type_info * ++get_array_field(struct arg_type_info *info, size_t emt) ++{ ++ return info->u.array_info.elt_type; ++} ++ ++static ssize_t ++classify(struct Process *proc, struct fetch_context *context, ++ struct arg_type_info *info, struct value *valuep, enum arg_class classes[], ++ size_t sz, size_t eightbytes) ++{ ++ switch (info->type) { ++ case ARGTYPE_VOID: ++ return 0; ++ ++ case ARGTYPE_CHAR: ++ case ARGTYPE_SHORT: ++ case ARGTYPE_USHORT: ++ case ARGTYPE_INT: ++ case ARGTYPE_UINT: ++ case ARGTYPE_LONG: ++ case ARGTYPE_ULONG: ++ ++ case ARGTYPE_POINTER: ++ /* and LONGLONG */ ++ /* CLASS_INTEGER */ ++ classes[0] = CLASS_INTEGER; ++ return 1; ++ ++ case ARGTYPE_FLOAT: ++ case ARGTYPE_DOUBLE: ++ /* and DECIMAL, and _m64 */ ++ classes[0] = CLASS_SSE; ++ return 1; ++ ++ case ARGTYPE_ARRAY: ++ /* N.B. this cannot be top-level array, those decay to ++ * pointers. Therefore, it must be inside structure ++ * that's at most 2 eightbytes long. */ ++ ++ /* Structures with flexible array members can't be ++ * passed by value. */ ++ assert(expr_is_compile_constant(info->u.array_info.length)); ++ ++ long l; ++ if (expr_eval_constant(info->u.array_info.length, &l) < 0) ++ return -1; ++ ++ return classify_eightbytes(proc, context, info, valuep, classes, ++ (size_t)l, eightbytes, ++ get_array_field); ++ ++ case ARGTYPE_STRUCT: ++ /* N.B. "big" structs are dealt with in the ++ * caller. */ ++ return classify_eightbytes(proc, context, info, valuep, classes, ++ type_struct_size(info), ++ eightbytes, type_struct_get); ++ } ++ abort(); ++} ++ ++static ssize_t ++pass_by_reference(struct value *valuep, enum arg_class classes[]) ++{ ++ if (valuep != NULL && value_pass_by_reference(valuep) < 0) ++ return -1; ++ classes[0] = CLASS_INTEGER; ++ return 1; ++} ++ ++static ssize_t ++classify_argument(struct Process *proc, struct fetch_context *context, ++ struct arg_type_info *info, struct value *valuep, ++ enum arg_class classes[], size_t *sizep) ++{ ++ size_t sz = type_sizeof(proc, info); ++ if (sz == (size_t)-1) ++ return -1; ++ *sizep = sz; ++ ++ size_t eightbytes = (sz + 7) / 8; /* Round up. */ ++ ++ /* Arrays decay into pointers. */ ++ assert(info->type != ARGTYPE_ARRAY); ++ ++ if (info->type == ARGTYPE_STRUCT) { ++ if (eightbytes > 2 || contains_unaligned_fields(info)) { ++ classes[0] = CLASS_MEMORY; ++ return 1; ++ } ++ ++ if (has_nontrivial_ctor_dtor(info)) ++ return pass_by_reference(valuep, classes); ++ } ++ ++ return classify(proc, context, info, valuep, classes, sz, eightbytes); ++} ++ ++static int ++fetch_register_banks(struct Process *proc, struct fetch_context *context, ++ int floating) ++{ ++ if (ptrace(PTRACE_GETREGS, proc->pid, 0, &context->iregs) < 0) ++ return -1; ++ context->ireg = 0; ++ ++ if (floating) { ++ if (ptrace(PTRACE_GETFPREGS, proc->pid, ++ 0, &context->fpregs) < 0) ++ return -1; ++ context->freg = 0; ++ } else { ++ context->freg = -1; ++ } ++ ++ return 0; ++} ++ ++static int ++arch_fetch_arg_next_32(struct fetch_context *context, enum tof type, ++ struct Process *proc, struct arg_type_info *info, ++ struct value *valuep) ++{ ++ size_t sz = type_sizeof(proc, info); ++ if (sz == (size_t)-1) ++ return -1; ++ ++ allocate_stack_slot(context, valuep, sz, 0, 4); ++ ++ return 0; ++} ++ ++static int ++arch_fetch_retval_32(struct fetch_context *context, enum tof type, ++ struct Process *proc, struct arg_type_info *info, ++ struct value *valuep) ++{ ++ if (fetch_register_banks(proc, context, type == LT_TOF_FUNCTIONR) < 0) ++ return -1; ++ ++ struct value *retval = &context->u.ix86.retval; ++ if (retval->type != NULL) { ++ /* Struct return value was extracted when in fetch ++ * init. */ ++ memcpy(valuep, &context->u.ix86.retval, sizeof(*valuep)); ++ return 0; ++ } ++ ++ size_t sz = type_sizeof(proc, info); ++ if (sz == (size_t)-1) ++ return -1; ++ if (value_reserve(valuep, sz) == NULL) ++ return -1; ++ ++ switch (info->type) { ++ enum arg_class cls; ++ case ARGTYPE_VOID: ++ return 0; ++ ++ case ARGTYPE_INT: ++ case ARGTYPE_UINT: ++ case ARGTYPE_LONG: ++ case ARGTYPE_ULONG: ++ case ARGTYPE_CHAR: ++ case ARGTYPE_SHORT: ++ case ARGTYPE_USHORT: ++ case ARGTYPE_POINTER: ++ cls = allocate_integer(context, valuep, sz, 0, POOL_RETVAL); ++ assert(cls == CLASS_INTEGER); ++ return 0; ++ ++ case ARGTYPE_FLOAT: ++ case ARGTYPE_DOUBLE: ++ cls = allocate_x87(context, valuep, sz, 0, POOL_RETVAL, 4); ++ assert(cls == CLASS_X87); ++ return 0; ++ ++ case ARGTYPE_ARRAY: ++ case ARGTYPE_STRUCT: /* Handled above. */ ++ assert(!"Unexpected i386 retval type!"); ++ abort(); ++ } ++ ++ abort(); ++} ++ ++static target_address_t ++fetch_stack_pointer(struct fetch_context *context) ++{ ++ target_address_t sp; ++#ifdef __x86_64__ ++ sp = (target_address_t)context->iregs.rsp; ++#else ++ sp = (target_address_t)context->iregs.esp; ++#endif ++ return sp; ++} ++ ++struct fetch_context * ++arch_fetch_arg_init_32(struct fetch_context *context, ++ enum tof type, struct Process *proc, ++ struct arg_type_info *ret_info) ++{ ++ context->stack_pointer = fetch_stack_pointer(context) + 4; ++ ++ size_t sz = type_sizeof(proc, ret_info); ++ if (sz == (size_t)-1) ++ return NULL; ++ ++ struct value *retval = &context->u.ix86.retval; ++ if (ret_info->type == ARGTYPE_STRUCT) { ++ value_init(retval, proc, NULL, ret_info, 0); ++ ++ enum arg_class dummy[2]; ++ if (pass_by_reference(retval, dummy) < 0) ++ return NULL; ++ allocate_stack_slot(context, retval, 4, 0, 4); ++ ++ } else { ++ value_init_detached(retval, NULL, NULL, 0); ++ } ++ ++ return context; ++} ++ ++struct fetch_context * ++arch_fetch_arg_init_64(struct fetch_context *ctx, enum tof type, ++ struct Process *proc, struct arg_type_info *ret_info) ++{ ++ /* The first stack slot holds a return address. */ ++ ctx->stack_pointer = fetch_stack_pointer(ctx) + 8; ++ ++ size_t size; ++ ctx->u.x86_64.num_ret_classes ++ = classify_argument(proc, ctx, ret_info, NULL, ++ ctx->u.x86_64.ret_classes, &size); ++ if (ctx->u.x86_64.num_ret_classes == -1) ++ return NULL; ++ ++ /* If the class is MEMORY, then the first argument is a hidden ++ * pointer to the allocated storage. */ ++ if (ctx->u.x86_64.num_ret_classes > 0 ++ && ctx->u.x86_64.ret_classes[0] == CLASS_MEMORY) { ++ /* MEMORY should be the sole class. */ ++ assert(ctx->u.x86_64.num_ret_classes == 1); ++ allocate_integer(ctx, NULL, size, 0, POOL_FUNCALL); ++ } ++ ++ return ctx; ++} ++ ++struct fetch_context * ++arch_fetch_arg_init(enum tof type, struct Process *proc, ++ struct arg_type_info *ret_info) ++{ ++ struct fetch_context *ctx = malloc(sizeof(*ctx)); ++ if (ctx == NULL) ++ return NULL; ++ ++ assert(type != LT_TOF_FUNCTIONR ++ && type != LT_TOF_SYSCALLR); ++ if (fetch_register_banks(proc, ctx, type == LT_TOF_FUNCTION) < 0) { ++ fail: ++ free(ctx); ++ return NULL; ++ } ++ ++ struct fetch_context *ret; ++ if (proc->e_machine == EM_386) ++ ret = arch_fetch_arg_init_32(ctx, type, proc, ret_info); ++ else ++ ret = arch_fetch_arg_init_64(ctx, type, proc, ret_info); ++ if (ret == NULL) ++ goto fail; ++ return ret; ++} ++ ++struct fetch_context * ++arch_fetch_arg_clone(struct Process *proc, struct fetch_context *context) ++{ ++ struct fetch_context *ret = malloc(sizeof(*ret)); ++ if (ret == NULL) ++ return NULL; ++ return memcpy(ret, context, sizeof(*ret)); ++} ++ ++static int ++arch_fetch_pool_arg_next(struct fetch_context *context, enum tof type, ++ struct Process *proc, struct arg_type_info *info, ++ struct value *valuep, enum reg_pool pool) ++{ ++ enum arg_class classes[2]; ++ size_t sz, sz1; ++ ssize_t i; ++ ssize_t nclasses = classify_argument(proc, context, info, valuep, ++ classes, &sz); ++ if (nclasses == -1) ++ return -1; ++ if (value_reserve(valuep, sz) == NULL) ++ return -1; ++ ++ /* If there are no registers available for any eightbyte of an ++ * argument, the whole argument is passed on the stack. If ++ * registers have already been assigned for some eightbytes of ++ * such an argument, the assignments get reverted. */ ++ struct fetch_context tmp_context = *context; ++ int revert; ++ if (nclasses == 1) { ++ revert = allocate_class(classes[0], &tmp_context, ++ valuep, sz, 0, pool) != classes[0]; ++ } else { ++ revert = 0; ++ for (i = 0; i < nclasses; ++i) { ++ sz1 = (size_t)(8 * (i + 1)) > sz ? sz - 8 * i : 8; ++ if (allocate_class(classes[i], &tmp_context, valuep, ++ sz1, 8 * i, pool) != classes[i]) ++ revert = 1; ++ } ++ } ++ ++ if (nclasses > 1 && revert) ++ allocate_class(CLASS_MEMORY, context, valuep, sz, 0, pool); ++ else ++ *context = tmp_context; /* Commit. */ ++ ++ return 0; ++} ++ ++int ++arch_fetch_fun_retval(struct fetch_context *context, enum tof type, ++ struct Process *proc, struct arg_type_info *info, ++ struct value *valuep) ++{ ++ assert(type != LT_TOF_FUNCTION ++ && type != LT_TOF_SYSCALL); ++ if (value_reserve(valuep, 8 * context->u.x86_64.num_ret_classes) == NULL ++ || fetch_register_banks(proc, context, ++ type == LT_TOF_FUNCTIONR) < 0) ++ return -1; ++ ++ if (context->u.x86_64.num_ret_classes == 1 ++ && context->u.x86_64.ret_classes[0] == CLASS_MEMORY) ++ pass_by_reference(valuep, context->u.x86_64.ret_classes); ++ ++ size_t sz = type_sizeof(proc, valuep->type); ++ if (sz == (size_t)-1) ++ return -1; ++ ++ ssize_t i; ++ size_t sz1 = context->u.x86_64.num_ret_classes == 1 ? sz : 8; ++ for (i = 0; i < context->u.x86_64.num_ret_classes; ++i) { ++ enum arg_class cls ++ = allocate_class(context->u.x86_64.ret_classes[i], ++ context, valuep, sz1, ++ 8 * i, POOL_RETVAL); ++ assert(cls == context->u.x86_64.ret_classes[i]); ++ } ++ return 0; ++} ++ ++int ++arch_fetch_arg_next(struct fetch_context *context, enum tof type, ++ struct Process *proc, struct arg_type_info *info, ++ struct value *valuep) ++{ ++ if (proc->e_machine == EM_386) ++ return arch_fetch_arg_next_32(context, type, proc, ++ info, valuep); ++ ++ switch (type) { ++ case LT_TOF_FUNCTION: ++ case LT_TOF_FUNCTIONR: ++ return arch_fetch_pool_arg_next(context, type, proc, ++ info, valuep, POOL_FUNCALL); ++ ++ case LT_TOF_SYSCALL: ++ case LT_TOF_SYSCALLR: ++ return arch_fetch_pool_arg_next(context, type, proc, ++ info, valuep, POOL_SYSCALL); ++ } ++ ++ abort(); ++} ++ ++int ++arch_fetch_retval(struct fetch_context *context, enum tof type, ++ struct Process *proc, struct arg_type_info *info, ++ struct value *valuep) ++{ ++ if (proc->e_machine == EM_386) ++ return arch_fetch_retval_32(context, type, proc, info, valuep); ++ ++ return arch_fetch_fun_retval(context, type, proc, info, valuep); ++} ++ ++void ++arch_fetch_arg_done(struct fetch_context *context) ++{ ++ if (context != NULL) ++ free(context); ++} +diff --git a/sysdeps/linux-gnu/x86/plt.c b/sysdeps/linux-gnu/x86/plt.c +new file mode 100644 +index 0000000..bb1b2b1 +--- /dev/null ++++ b/sysdeps/linux-gnu/x86/plt.c +@@ -0,0 +1,14 @@ ++#include ++#include "proc.h" ++#include "common.h" ++#include "library.h" ++ ++GElf_Addr ++arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { ++ return lte->plt_addr + (ndx + 1) * 16; ++} ++ ++void * ++sym2addr(Process *proc, struct library_symbol *sym) { ++ return sym->enter_addr; ++} +diff --git a/sysdeps/linux-gnu/x86/ptrace.h b/sysdeps/linux-gnu/x86/ptrace.h +new file mode 100644 +index 0000000..e0f5261 +--- /dev/null ++++ b/sysdeps/linux-gnu/x86/ptrace.h +@@ -0,0 +1,2 @@ ++#include ++#include +diff --git a/sysdeps/linux-gnu/x86/regs.c b/sysdeps/linux-gnu/x86/regs.c +new file mode 100644 +index 0000000..477abca +--- /dev/null ++++ b/sysdeps/linux-gnu/x86/regs.c +@@ -0,0 +1,116 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 1998,2002,2004,2008,2009 Juan Cespedes ++ * Copyright (C) 2006 Ian Wienand ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "backend.h" ++#include "proc.h" ++ ++#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) ++# define PTRACE_PEEKUSER PTRACE_PEEKUSR ++#endif ++ ++#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) ++# define PTRACE_POKEUSER PTRACE_POKEUSR ++#endif ++ ++#ifdef __x86_64__ ++# define XIP (8 * RIP) ++# define XSP (8 * RSP) ++#else ++# define XIP (4 * EIP) ++# define XSP (4 * UESP) ++#endif ++ ++static target_address_t ++conv_32(target_address_t val) ++{ ++ /* XXX Drop the multiple double casts when target_address_t ++ * becomes integral. */ ++ return (target_address_t)(uintptr_t)(uint32_t)(uintptr_t)val; ++} ++ ++void * ++get_instruction_pointer(struct Process *proc) ++{ ++ long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, XIP, 0); ++ if (proc->e_machine == EM_386) ++ ret &= 0xffffffff; ++ return (void *)ret; ++} ++ ++void ++set_instruction_pointer(struct Process *proc, target_address_t addr) ++{ ++ if (proc->e_machine == EM_386) ++ addr = conv_32(addr); ++ ptrace(PTRACE_POKEUSER, proc->pid, XIP, addr); ++} ++ ++void * ++get_stack_pointer(struct Process *proc) ++{ ++ long sp = ptrace(PTRACE_PEEKUSER, proc->pid, XSP, 0); ++ if (sp == -1 && errno) { ++ fprintf(stderr, "Couldn't read SP register: %s\n", ++ strerror(errno)); ++ return NULL; ++ } ++ ++ /* XXX Drop the multiple double casts when target_address_t ++ * becomes integral. */ ++ target_address_t ret = (target_address_t)(uintptr_t)sp; ++ if (proc->e_machine == EM_386) ++ ret = conv_32(ret); ++ return ret; ++} ++ ++void * ++get_return_addr(struct Process *proc, void *sp) ++{ ++ long a = ptrace(PTRACE_PEEKTEXT, proc->pid, sp, 0); ++ if (a == -1 && errno) { ++ fprintf(stderr, "Couldn't read return value: %s\n", ++ strerror(errno)); ++ return NULL; ++ } ++ ++ /* XXX Drop the multiple double casts when target_address_t ++ * becomes integral. */ ++ target_address_t ret = (target_address_t)(uintptr_t)a; ++ if (proc->e_machine == EM_386) ++ ret = conv_32(ret); ++ return ret; ++} ++ ++void ++set_return_addr(Process *proc, void *addr) { ++ if (proc->e_machine == EM_386) ++ addr = (void *)((long int)addr & 0xffffffff); ++ ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, addr); ++} +diff --git a/sysdeps/linux-gnu/x86/signalent.h b/sysdeps/linux-gnu/x86/signalent.h +new file mode 100644 +index 0000000..d58a36c +--- /dev/null ++++ b/sysdeps/linux-gnu/x86/signalent.h +@@ -0,0 +1,32 @@ ++"SIG_0", /* 0 */ ++ "SIGHUP", /* 1 */ ++ "SIGINT", /* 2 */ ++ "SIGQUIT", /* 3 */ ++ "SIGILL", /* 4 */ ++ "SIGTRAP", /* 5 */ ++ "SIGABRT", /* 6 */ ++ "SIGBUS", /* 7 */ ++ "SIGFPE", /* 8 */ ++ "SIGKILL", /* 9 */ ++ "SIGUSR1", /* 10 */ ++ "SIGSEGV", /* 11 */ ++ "SIGUSR2", /* 12 */ ++ "SIGPIPE", /* 13 */ ++ "SIGALRM", /* 14 */ ++ "SIGTERM", /* 15 */ ++ "SIGSTKFLT", /* 16 */ ++ "SIGCHLD", /* 17 */ ++ "SIGCONT", /* 18 */ ++ "SIGSTOP", /* 19 */ ++ "SIGTSTP", /* 20 */ ++ "SIGTTIN", /* 21 */ ++ "SIGTTOU", /* 22 */ ++ "SIGURG", /* 23 */ ++ "SIGXCPU", /* 24 */ ++ "SIGXFSZ", /* 25 */ ++ "SIGVTALRM", /* 26 */ ++ "SIGPROF", /* 27 */ ++ "SIGWINCH", /* 28 */ ++ "SIGIO", /* 29 */ ++ "SIGPWR", /* 30 */ ++ "SIGSYS", /* 31 */ +diff --git a/sysdeps/linux-gnu/x86/signalent1.h b/sysdeps/linux-gnu/x86/signalent1.h +new file mode 100644 +index 0000000..5395f82 +--- /dev/null ++++ b/sysdeps/linux-gnu/x86/signalent1.h +@@ -0,0 +1,32 @@ ++ "SIG_0", /* 0 */ ++ "SIGHUP", /* 1 */ ++ "SIGINT", /* 2 */ ++ "SIGQUIT", /* 3 */ ++ "SIGILL", /* 4 */ ++ "SIGTRAP", /* 5 */ ++ "SIGABRT", /* 6 */ ++ "SIGBUS", /* 7 */ ++ "SIGFPE", /* 8 */ ++ "SIGKILL", /* 9 */ ++ "SIGUSR1", /* 10 */ ++ "SIGSEGV", /* 11 */ ++ "SIGUSR2", /* 12 */ ++ "SIGPIPE", /* 13 */ ++ "SIGALRM", /* 14 */ ++ "SIGTERM", /* 15 */ ++ "SIGSTKFLT", /* 16 */ ++ "SIGCHLD", /* 17 */ ++ "SIGCONT", /* 18 */ ++ "SIGSTOP", /* 19 */ ++ "SIGTSTP", /* 20 */ ++ "SIGTTIN", /* 21 */ ++ "SIGTTOU", /* 22 */ ++ "SIGURG", /* 23 */ ++ "SIGXCPU", /* 24 */ ++ "SIGXFSZ", /* 25 */ ++ "SIGVTALRM", /* 26 */ ++ "SIGPROF", /* 27 */ ++ "SIGWINCH", /* 28 */ ++ "SIGIO", /* 29 */ ++ "SIGPWR", /* 30 */ ++ "SIGSYS", /* 31 */ +diff --git a/sysdeps/linux-gnu/x86/syscallent.h b/sysdeps/linux-gnu/x86/syscallent.h +new file mode 100644 +index 0000000..7d746b7 +--- /dev/null ++++ b/sysdeps/linux-gnu/x86/syscallent.h +@@ -0,0 +1,350 @@ ++/* This file is for i386 system call names. */ ++ "restart_syscall", /* 0 */ ++ "exit", /* 1 */ ++ "fork", /* 2 */ ++ "read", /* 3 */ ++ "write", /* 4 */ ++ "open", /* 5 */ ++ "close", /* 6 */ ++ "waitpid", /* 7 */ ++ "creat", /* 8 */ ++ "link", /* 9 */ ++ "unlink", /* 10 */ ++ "execve", /* 11 */ ++ "chdir", /* 12 */ ++ "time", /* 13 */ ++ "mknod", /* 14 */ ++ "chmod", /* 15 */ ++ "lchown", /* 16 */ ++ "break", /* 17 */ ++ "oldstat", /* 18 */ ++ "lseek", /* 19 */ ++ "getpid", /* 20 */ ++ "mount", /* 21 */ ++ "umount", /* 22 */ ++ "setuid", /* 23 */ ++ "getuid", /* 24 */ ++ "stime", /* 25 */ ++ "ptrace", /* 26 */ ++ "alarm", /* 27 */ ++ "oldfstat", /* 28 */ ++ "pause", /* 29 */ ++ "utime", /* 30 */ ++ "stty", /* 31 */ ++ "gtty", /* 32 */ ++ "access", /* 33 */ ++ "nice", /* 34 */ ++ "ftime", /* 35 */ ++ "sync", /* 36 */ ++ "kill", /* 37 */ ++ "rename", /* 38 */ ++ "mkdir", /* 39 */ ++ "rmdir", /* 40 */ ++ "dup", /* 41 */ ++ "pipe", /* 42 */ ++ "times", /* 43 */ ++ "prof", /* 44 */ ++ "brk", /* 45 */ ++ "setgid", /* 46 */ ++ "getgid", /* 47 */ ++ "signal", /* 48 */ ++ "geteuid", /* 49 */ ++ "getegid", /* 50 */ ++ "acct", /* 51 */ ++ "umount2", /* 52 */ ++ "lock", /* 53 */ ++ "ioctl", /* 54 */ ++ "fcntl", /* 55 */ ++ "mpx", /* 56 */ ++ "setpgid", /* 57 */ ++ "ulimit", /* 58 */ ++ "oldolduname", /* 59 */ ++ "umask", /* 60 */ ++ "chroot", /* 61 */ ++ "ustat", /* 62 */ ++ "dup2", /* 63 */ ++ "getppid", /* 64 */ ++ "getpgrp", /* 65 */ ++ "setsid", /* 66 */ ++ "sigaction", /* 67 */ ++ "sgetmask", /* 68 */ ++ "ssetmask", /* 69 */ ++ "setreuid", /* 70 */ ++ "setregid", /* 71 */ ++ "sigsuspend", /* 72 */ ++ "sigpending", /* 73 */ ++ "sethostname", /* 74 */ ++ "setrlimit", /* 75 */ ++ "getrlimit", /* 76 */ ++ "getrusage", /* 77 */ ++ "gettimeofday", /* 78 */ ++ "settimeofday", /* 79 */ ++ "getgroups", /* 80 */ ++ "setgroups", /* 81 */ ++ "select", /* 82 */ ++ "symlink", /* 83 */ ++ "oldlstat", /* 84 */ ++ "readlink", /* 85 */ ++ "uselib", /* 86 */ ++ "swapon", /* 87 */ ++ "reboot", /* 88 */ ++ "readdir", /* 89 */ ++ "mmap", /* 90 */ ++ "munmap", /* 91 */ ++ "truncate", /* 92 */ ++ "ftruncate", /* 93 */ ++ "fchmod", /* 94 */ ++ "fchown", /* 95 */ ++ "getpriority", /* 96 */ ++ "setpriority", /* 97 */ ++ "profil", /* 98 */ ++ "statfs", /* 99 */ ++ "fstatfs", /* 100 */ ++ "ioperm", /* 101 */ ++ "socketcall", /* 102 */ ++ "syslog", /* 103 */ ++ "setitimer", /* 104 */ ++ "getitimer", /* 105 */ ++ "stat", /* 106 */ ++ "lstat", /* 107 */ ++ "fstat", /* 108 */ ++ "olduname", /* 109 */ ++ "iopl", /* 110 */ ++ "vhangup", /* 111 */ ++ "idle", /* 112 */ ++ "vm86old", /* 113 */ ++ "wait4", /* 114 */ ++ "swapoff", /* 115 */ ++ "sysinfo", /* 116 */ ++ "ipc", /* 117 */ ++ "fsync", /* 118 */ ++ "sigreturn", /* 119 */ ++ "clone", /* 120 */ ++ "setdomainname", /* 121 */ ++ "uname", /* 122 */ ++ "modify_ldt", /* 123 */ ++ "adjtimex", /* 124 */ ++ "mprotect", /* 125 */ ++ "sigprocmask", /* 126 */ ++ "create_module", /* 127 */ ++ "init_module", /* 128 */ ++ "delete_module", /* 129 */ ++ "get_kernel_syms", /* 130 */ ++ "quotactl", /* 131 */ ++ "getpgid", /* 132 */ ++ "fchdir", /* 133 */ ++ "bdflush", /* 134 */ ++ "sysfs", /* 135 */ ++ "personality", /* 136 */ ++ "afs_syscall", /* 137 */ ++ "setfsuid", /* 138 */ ++ "setfsgid", /* 139 */ ++ "_llseek", /* 140 */ ++ "getdents", /* 141 */ ++ "_newselect", /* 142 */ ++ "flock", /* 143 */ ++ "msync", /* 144 */ ++ "readv", /* 145 */ ++ "writev", /* 146 */ ++ "getsid", /* 147 */ ++ "fdatasync", /* 148 */ ++ "_sysctl", /* 149 */ ++ "mlock", /* 150 */ ++ "munlock", /* 151 */ ++ "mlockall", /* 152 */ ++ "munlockall", /* 153 */ ++ "sched_setparam", /* 154 */ ++ "sched_getparam", /* 155 */ ++ "sched_setscheduler", /* 156 */ ++ "sched_getscheduler", /* 157 */ ++ "sched_yield", /* 158 */ ++ "sched_get_priority_max", /* 159 */ ++ "sched_get_priority_min", /* 160 */ ++ "sched_rr_get_interval", /* 161 */ ++ "nanosleep", /* 162 */ ++ "mremap", /* 163 */ ++ "setresuid", /* 164 */ ++ "getresuid", /* 165 */ ++ "vm86", /* 166 */ ++ "query_module", /* 167 */ ++ "poll", /* 168 */ ++ "nfsservctl", /* 169 */ ++ "setresgid", /* 170 */ ++ "getresgid", /* 171 */ ++ "prctl", /* 172 */ ++ "rt_sigreturn", /* 173 */ ++ "rt_sigaction", /* 174 */ ++ "rt_sigprocmask", /* 175 */ ++ "rt_sigpending", /* 176 */ ++ "rt_sigtimedwait", /* 177 */ ++ "rt_sigqueueinfo", /* 178 */ ++ "rt_sigsuspend", /* 179 */ ++ "pread64", /* 180 */ ++ "pwrite64", /* 181 */ ++ "chown", /* 182 */ ++ "getcwd", /* 183 */ ++ "capget", /* 184 */ ++ "capset", /* 185 */ ++ "sigaltstack", /* 186 */ ++ "sendfile", /* 187 */ ++ "getpmsg", /* 188 */ ++ "putpmsg", /* 189 */ ++ "vfork", /* 190 */ ++ "ugetrlimit", /* 191 */ ++ "mmap2", /* 192 */ ++ "truncate64", /* 193 */ ++ "ftruncate64", /* 194 */ ++ "stat64", /* 195 */ ++ "lstat64", /* 196 */ ++ "fstat64", /* 197 */ ++ "lchown32", /* 198 */ ++ "getuid32", /* 199 */ ++ "getgid32", /* 200 */ ++ "geteuid32", /* 201 */ ++ "getegid32", /* 202 */ ++ "setreuid32", /* 203 */ ++ "setregid32", /* 204 */ ++ "getgroups32", /* 205 */ ++ "setgroups32", /* 206 */ ++ "fchown32", /* 207 */ ++ "setresuid32", /* 208 */ ++ "getresuid32", /* 209 */ ++ "setresgid32", /* 210 */ ++ "getresgid32", /* 211 */ ++ "chown32", /* 212 */ ++ "setuid32", /* 213 */ ++ "setgid32", /* 214 */ ++ "setfsuid32", /* 215 */ ++ "setfsgid32", /* 216 */ ++ "pivot_root", /* 217 */ ++ "mincore", /* 218 */ ++ "madvise1", /* 219 */ ++ "getdents64", /* 220 */ ++ "fcntl64", /* 221 */ ++ "222", /* 222 */ ++ "223", /* 223 */ ++ "gettid", /* 224 */ ++ "readahead", /* 225 */ ++ "setxattr", /* 226 */ ++ "lsetxattr", /* 227 */ ++ "fsetxattr", /* 228 */ ++ "getxattr", /* 229 */ ++ "lgetxattr", /* 230 */ ++ "fgetxattr", /* 231 */ ++ "listxattr", /* 232 */ ++ "llistxattr", /* 233 */ ++ "flistxattr", /* 234 */ ++ "removexattr", /* 235 */ ++ "lremovexattr", /* 236 */ ++ "fremovexattr", /* 237 */ ++ "tkill", /* 238 */ ++ "sendfile64", /* 239 */ ++ "futex", /* 240 */ ++ "sched_setaffinity", /* 241 */ ++ "sched_getaffinity", /* 242 */ ++ "set_thread_area", /* 243 */ ++ "get_thread_area", /* 244 */ ++ "io_setup", /* 245 */ ++ "io_destroy", /* 246 */ ++ "io_getevents", /* 247 */ ++ "io_submit", /* 248 */ ++ "io_cancel", /* 249 */ ++ "fadvise64", /* 250 */ ++ "251", /* 251 */ ++ "exit_group", /* 252 */ ++ "lookup_dcookie", /* 253 */ ++ "epoll_create", /* 254 */ ++ "epoll_ctl", /* 255 */ ++ "epoll_wait", /* 256 */ ++ "remap_file_pages", /* 257 */ ++ "set_tid_address", /* 258 */ ++ "timer_create", /* 259 */ ++ "260", /* 260 */ ++ "261", /* 261 */ ++ "262", /* 262 */ ++ "263", /* 263 */ ++ "264", /* 264 */ ++ "265", /* 265 */ ++ "266", /* 266 */ ++ "267", /* 267 */ ++ "statfs64", /* 268 */ ++ "fstatfs64", /* 269 */ ++ "tgkill", /* 270 */ ++ "utimes", /* 271 */ ++ "fadvise64_64", /* 272 */ ++ "vserver", /* 273 */ ++ "mbind", /* 274 */ ++ "get_mempolicy", /* 275 */ ++ "set_mempolicy", /* 276 */ ++ "mq_open", /* 277 */ ++ "278", /* 278 */ ++ "279", /* 279 */ ++ "280", /* 280 */ ++ "281", /* 281 */ ++ "282", /* 282 */ ++ "kexec_load", /* 283 */ ++ "waitid", /* 284 */ ++ "285", /* 285 */ ++ "add_key", /* 286 */ ++ "request_key", /* 287 */ ++ "keyctl", /* 288 */ ++ "ioprio_set", /* 289 */ ++ "ioprio_get", /* 290 */ ++ "inotify_init", /* 291 */ ++ "inotify_add_watch", /* 292 */ ++ "inotify_rm_watch", /* 293 */ ++ "migrate_pages", /* 294 */ ++ "openat", /* 295 */ ++ "mkdirat", /* 296 */ ++ "mknodat", /* 297 */ ++ "fchownat", /* 298 */ ++ "futimesat", /* 299 */ ++ "fstatat64", /* 300 */ ++ "unlinkat", /* 301 */ ++ "renameat", /* 302 */ ++ "linkat", /* 303 */ ++ "symlinkat", /* 304 */ ++ "readlinkat", /* 305 */ ++ "fchmodat", /* 306 */ ++ "faccessat", /* 307 */ ++ "pselect6", /* 308 */ ++ "ppoll", /* 309 */ ++ "unshare", /* 310 */ ++ "set_robust_list", /* 311 */ ++ "get_robust_list", /* 312 */ ++ "splice", /* 313 */ ++ "sync_file_range", /* 314 */ ++ "tee", /* 315 */ ++ "vmsplice", /* 316 */ ++ "move_pages", /* 317 */ ++ "getcpu", /* 318 */ ++ "epoll_pwait", /* 319 */ ++ "utimensat", /* 320 */ ++ "signalfd", /* 321 */ ++ "timerfd_create", /* 322 */ ++ "eventfd", /* 323 */ ++ "fallocate", /* 324 */ ++ "timerfd_settime", /* 325 */ ++ "timerfd_gettime", /* 326 */ ++ "signalfd4", /* 327 */ ++ "eventfd2", /* 328 */ ++ "epoll_create1", /* 329 */ ++ "dup3", /* 330 */ ++ "pipe2", /* 331 */ ++ "inotify_init1", /* 332 */ ++ "preadv", /* 333 */ ++ "pwritev", /* 334 */ ++ "rt_tgsigqueueinfo", /* 335 */ ++ "perf_event_open", /* 336 */ ++ "recvmmsg", /* 337 */ ++ "fanotify_init", /* 338 */ ++ "fanotify_mark", /* 339 */ ++ "prlimit64", /* 340 */ ++ "name_to_handle_at", /* 341 */ ++ "open_by_handle_at", /* 342 */ ++ "clock_adjtime", /* 343 */ ++ "syncfs", /* 344 */ ++ "sendmmsg", /* 345 */ ++ "setns", /* 346 */ ++ "process_vm_readv", /* 347 */ ++ "process_vm_writev", /* 348 */ +diff --git a/sysdeps/linux-gnu/x86/syscallent1.h b/sysdeps/linux-gnu/x86/syscallent1.h +new file mode 100644 +index 0000000..c2d9017 +--- /dev/null ++++ b/sysdeps/linux-gnu/x86/syscallent1.h +@@ -0,0 +1,313 @@ ++/* This file is for x86_64 system call names. */ ++ "read", /* 0 */ ++ "write", /* 1 */ ++ "open", /* 2 */ ++ "close", /* 3 */ ++ "stat", /* 4 */ ++ "fstat", /* 5 */ ++ "lstat", /* 6 */ ++ "poll", /* 7 */ ++ "lseek", /* 8 */ ++ "mmap", /* 9 */ ++ "mprotect", /* 10 */ ++ "munmap", /* 11 */ ++ "brk", /* 12 */ ++ "rt_sigaction", /* 13 */ ++ "rt_sigprocmask", /* 14 */ ++ "rt_sigreturn", /* 15 */ ++ "ioctl", /* 16 */ ++ "pread", /* 17 */ ++ "pwrite", /* 18 */ ++ "readv", /* 19 */ ++ "writev", /* 20 */ ++ "access", /* 21 */ ++ "pipe", /* 22 */ ++ "select", /* 23 */ ++ "sched_yield", /* 24 */ ++ "mremap", /* 25 */ ++ "msync", /* 26 */ ++ "mincore", /* 27 */ ++ "madvise", /* 28 */ ++ "shmget", /* 29 */ ++ "shmat", /* 30 */ ++ "shmctl", /* 31 */ ++ "dup", /* 32 */ ++ "dup2", /* 33 */ ++ "pause", /* 34 */ ++ "nanosleep", /* 35 */ ++ "getitimer", /* 36 */ ++ "alarm", /* 37 */ ++ "setitimer", /* 38 */ ++ "getpid", /* 39 */ ++ "sendfile", /* 40 */ ++ "socket", /* 41 */ ++ "connect", /* 42 */ ++ "accept", /* 43 */ ++ "sendto", /* 44 */ ++ "recvfrom", /* 45 */ ++ "sendmsg", /* 46 */ ++ "recvmsg", /* 47 */ ++ "shutdown", /* 48 */ ++ "bind", /* 49 */ ++ "listen", /* 50 */ ++ "getsockname", /* 51 */ ++ "getpeername", /* 52 */ ++ "socketpair", /* 53 */ ++ "setsockopt", /* 54 */ ++ "getsockopt", /* 55 */ ++ "clone", /* 56 */ ++ "fork", /* 57 */ ++ "vfork", /* 58 */ ++ "execve", /* 59 */ ++ "exit", /* 60 */ ++ "wait4", /* 61 */ ++ "kill", /* 62 */ ++ "uname", /* 63 */ ++ "semget", /* 64 */ ++ "semop", /* 65 */ ++ "semctl", /* 66 */ ++ "shmdt", /* 67 */ ++ "msgget", /* 68 */ ++ "msgsnd", /* 69 */ ++ "msgrcv", /* 70 */ ++ "msgctl", /* 71 */ ++ "fcntl", /* 72 */ ++ "flock", /* 73 */ ++ "fsync", /* 74 */ ++ "fdatasync", /* 75 */ ++ "truncate", /* 76 */ ++ "ftruncate", /* 77 */ ++ "getdents", /* 78 */ ++ "getcwd", /* 79 */ ++ "chdir", /* 80 */ ++ "fchdir", /* 81 */ ++ "rename", /* 82 */ ++ "mkdir", /* 83 */ ++ "rmdir", /* 84 */ ++ "creat", /* 85 */ ++ "link", /* 86 */ ++ "unlink", /* 87 */ ++ "symlink", /* 88 */ ++ "readlink", /* 89 */ ++ "chmod", /* 90 */ ++ "fchmod", /* 91 */ ++ "chown", /* 92 */ ++ "fchown", /* 93 */ ++ "lchown", /* 94 */ ++ "umask", /* 95 */ ++ "gettimeofday", /* 96 */ ++ "getrlimit", /* 97 */ ++ "getrusage", /* 98 */ ++ "sysinfo", /* 99 */ ++ "times", /* 100 */ ++ "ptrace", /* 101 */ ++ "getuid", /* 102 */ ++ "syslog", /* 103 */ ++ "getgid", /* 104 */ ++ "setuid", /* 105 */ ++ "setgid", /* 106 */ ++ "geteuid", /* 107 */ ++ "getegid", /* 108 */ ++ "setpgid", /* 109 */ ++ "getppid", /* 110 */ ++ "getpgrp", /* 111 */ ++ "setsid", /* 112 */ ++ "setreuid", /* 113 */ ++ "setregid", /* 114 */ ++ "getgroups", /* 115 */ ++ "setgroups", /* 116 */ ++ "setresuid", /* 117 */ ++ "getresuid", /* 118 */ ++ "setresgid", /* 119 */ ++ "getresgid", /* 120 */ ++ "getpgid", /* 121 */ ++ "setfsuid", /* 122 */ ++ "setfsgid", /* 123 */ ++ "getsid", /* 124 */ ++ "capget", /* 125 */ ++ "capset", /* 126 */ ++ "rt_sigpending", /* 127 */ ++ "rt_sigtimedwait", /* 128 */ ++ "rt_sigqueueinfo", /* 129 */ ++ "rt_sigsuspend", /* 130 */ ++ "sigaltstack", /* 131 */ ++ "utime", /* 132 */ ++ "mknod", /* 133 */ ++ "uselib", /* 134 */ ++ "personality", /* 135 */ ++ "ustat", /* 136 */ ++ "statfs", /* 137 */ ++ "fstatfs", /* 138 */ ++ "sysfs", /* 139 */ ++ "getpriority", /* 140 */ ++ "setpriority", /* 141 */ ++ "sched_setparam", /* 142 */ ++ "sched_getparam", /* 143 */ ++ "sched_setscheduler", /* 144 */ ++ "sched_getscheduler", /* 145 */ ++ "sched_get_priority_max", /* 146 */ ++ "sched_get_priority_min", /* 147 */ ++ "sched_rr_get_interval", /* 148 */ ++ "mlock", /* 149 */ ++ "munlock", /* 150 */ ++ "mlockall", /* 151 */ ++ "munlockall", /* 152 */ ++ "vhangup", /* 153 */ ++ "modify_ldt", /* 154 */ ++ "pivot_root", /* 155 */ ++ "_sysctl", /* 156 */ ++ "prctl", /* 157 */ ++ "arch_prctl", /* 158 */ ++ "adjtimex", /* 159 */ ++ "setrlimit", /* 160 */ ++ "chroot", /* 161 */ ++ "sync", /* 162 */ ++ "acct", /* 163 */ ++ "settimeofday", /* 164 */ ++ "mount", /* 165 */ ++ "umount2", /* 166 */ ++ "swapon", /* 167 */ ++ "swapoff", /* 168 */ ++ "reboot", /* 169 */ ++ "sethostname", /* 170 */ ++ "setdomainname", /* 171 */ ++ "iopl", /* 172 */ ++ "ioperm", /* 173 */ ++ "create_module", /* 174 */ ++ "init_module", /* 175 */ ++ "delete_module", /* 176 */ ++ "get_kernel_syms", /* 177 */ ++ "query_module", /* 178 */ ++ "quotactl", /* 179 */ ++ "nfsservctl", /* 180 */ ++ "getpmsg", /* 181 */ ++ "putpmsg", /* 182 */ ++ "afs_syscall", /* 183 */ ++ "tuxcall", /* 184 */ ++ "security", /* 185 */ ++ "gettid", /* 186 */ ++ "readahead", /* 187 */ ++ "setxattr", /* 188 */ ++ "lsetxattr", /* 189 */ ++ "fsetxattr", /* 190 */ ++ "getxattr", /* 191 */ ++ "lgetxattr", /* 192 */ ++ "fgetxattr", /* 193 */ ++ "listxattr", /* 194 */ ++ "llistxattr", /* 195 */ ++ "flistxattr", /* 196 */ ++ "removexattr", /* 197 */ ++ "lremovexattr", /* 198 */ ++ "fremovexattr", /* 199 */ ++ "tkill", /* 200 */ ++ "time", /* 201 */ ++ "futex", /* 202 */ ++ "sched_setaffinity", /* 203 */ ++ "sched_getaffinity", /* 204 */ ++ "set_thread_area", /* 205 */ ++ "io_setup", /* 206 */ ++ "io_destroy", /* 207 */ ++ "io_getevents", /* 208 */ ++ "io_submit", /* 209 */ ++ "io_cancel", /* 210 */ ++ "get_thread_area", /* 211 */ ++ "lookup_dcookie", /* 212 */ ++ "epoll_create", /* 213 */ ++ "epoll_ctl", /* 214 */ ++ "epoll_wait", /* 215 */ ++ "remap_file_pages", /* 216 */ ++ "getdents64", /* 217 */ ++ "set_tid_address", /* 218 */ ++ "restart_syscall", /* 219 */ ++ "semtimedop", /* 220 */ ++ "fadvise64", /* 221 */ ++ "timer_create", /* 222 */ ++ "timer_settime", /* 223 */ ++ "timer_gettime", /* 224 */ ++ "timer_getoverrun", /* 225 */ ++ "timer_delete", /* 226 */ ++ "clock_settime", /* 227 */ ++ "clock_gettime", /* 228 */ ++ "clock_getres", /* 229 */ ++ "clock_nanosleep", /* 230 */ ++ "exit_group", /* 231 */ ++ "epoll_wait", /* 232 */ ++ "epoll_ctl", /* 233 */ ++ "tgkill", /* 234 */ ++ "utimes", /* 235 */ ++ "vserver", /* 236 */ ++ "mbind", /* 237 */ ++ "set_mempolicy", /* 238 */ ++ "get_mempolicy", /* 239 */ ++ "mq_open", /* 240 */ ++ "mq_unlink", /* 241 */ ++ "mq_timedsend", /* 242 */ ++ "mq_timedreceive", /* 243 */ ++ "mq_notify", /* 244 */ ++ "mq_getsetattr", /* 245 */ ++ "kexec_load", /* 246 */ ++ "waitid", /* 247 */ ++ "add_key", /* 248 */ ++ "request_key", /* 249 */ ++ "keyctl", /* 250 */ ++ "ioprio_set", /* 251 */ ++ "ioprio_get", /* 252 */ ++ "inotify_init", /* 253 */ ++ "inotify_add_watch", /* 254 */ ++ "inotify_rm_watch", /* 255 */ ++ "migrate_pages", /* 256 */ ++ "openat", /* 257 */ ++ "mkdirat", /* 258 */ ++ "mknodat", /* 259 */ ++ "fchownat", /* 260 */ ++ "futimesat", /* 261 */ ++ "newfstatat", /* 262 */ ++ "unlinkat", /* 263 */ ++ "renameat", /* 264 */ ++ "linkat", /* 265 */ ++ "symlinkat", /* 266 */ ++ "readlinkat", /* 267 */ ++ "fchmodat", /* 268 */ ++ "faccessat", /* 269 */ ++ "pselect6", /* 270 */ ++ "ppoll", /* 271 */ ++ "unshare", /* 272 */ ++ "set_robust_list", /* 273 */ ++ "get_robust_list", /* 274 */ ++ "splice", /* 275 */ ++ "tee", /* 276 */ ++ "sync_file_range", /* 277 */ ++ "vmsplice", /* 278 */ ++ "move_pages", /* 279 */ ++ "utimensat", /* 280 */ ++ "epoll_pwait", /* 281 */ ++ "signalfd", /* 282 */ ++ "timerfd_create", /* 283 */ ++ "eventfd", /* 284 */ ++ "fallocate", /* 285 */ ++ "timerfd_settime", /* 286 */ ++ "timerfd_gettime", /* 287 */ ++ "accept4", /* 288 */ ++ "signalfd4", /* 289 */ ++ "eventfd2", /* 290 */ ++ "epoll_create1", /* 291 */ ++ "dup3", /* 292 */ ++ "pipe2", /* 293 */ ++ "inotify_init1", /* 294 */ ++ "preadv", /* 295 */ ++ "pwritev", /* 296 */ ++ "rt_tgsigqueueinfo", /* 297 */ ++ "perf_event_open", /* 298 */ ++ "recvmmsg", /* 299 */ ++ "fanotify_init", /* 300 */ ++ "fanotify_mark", /* 301 */ ++ "prlimit64", /* 302 */ ++ "name_to_handle_at", /* 303 */ ++ "open_by_handle_at", /* 304 */ ++ "clock_adjtime", /* 305 */ ++ "syncfs", /* 306 */ ++ "sendmmsg", /* 307 */ ++ "setns", /* 308 */ ++ "getcpu", /* 309 */ ++ "process_vm_readv", /* 310 */ ++ "process_vm_writev", /* 311 */ +diff --git a/sysdeps/linux-gnu/x86/trace.c b/sysdeps/linux-gnu/x86/trace.c +new file mode 100644 +index 0000000..cc1a6a1 +--- /dev/null ++++ b/sysdeps/linux-gnu/x86/trace.c +@@ -0,0 +1,189 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2010,2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2004,2008,2009 Juan Cespedes ++ * Copyright (C) 2006 Ian Wienand ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "backend.h" ++#include "debug.h" ++#include "proc.h" ++#include "ptrace.h" ++#include "type.h" ++ ++#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) ++# define PTRACE_PEEKUSER PTRACE_PEEKUSR ++#endif ++ ++#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) ++# define PTRACE_POKEUSER PTRACE_POKEUSR ++#endif ++ ++#ifdef __x86_64__ ++# define ORIG_XAX (8 * ORIG_RAX) ++#else ++# define ORIG_XAX (4 * ORIG_EAX) ++#endif ++ ++#ifdef __x86_64__ ++static const int x86_64 = 1; ++#else ++static const int x86_64 = 0; ++#endif ++ ++void ++get_arch_dep(struct Process *proc) ++{ ++ /* Unfortunately there are still remnants of mask_32bit uses ++ * around. */ ++ ++ if (proc->e_machine == EM_X86_64) { ++ proc->mask_32bit = 0; ++ proc->personality = 1; ++ } else if (x86_64) { /* x86_64/i386 */ ++ proc->mask_32bit = 1; ++ proc->personality = 0; ++ } else { ++ proc->mask_32bit = 0; ++ proc->personality = 0; ++ } ++} ++ ++/* Returns 1 if syscall, 2 if sysret, 0 otherwise. ++ */ ++int ++syscall_p(struct Process *proc, int status, int *sysnum) ++{ ++ if (WIFSTOPPED(status) ++ && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { ++ struct callstack_element *elem = NULL; ++ if (proc->callstack_depth > 0) ++ elem = proc->callstack + proc->callstack_depth - 1; ++ ++ long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, ORIG_XAX, 0); ++ if (ret == -1) { ++ if (errno) ++ return -1; ++ /* Otherwise, ORIG_RAX == -1 means that the ++ * system call should not be restarted. In ++ * that case rely on what we have on ++ * stack. */ ++ if (elem != NULL && elem->is_syscall) ++ ret = elem->c_un.syscall; ++ } ++ ++ *sysnum = ret; ++ debug(DEBUG_FUNCTION, "sysnum=%ld %p %d\n", ret, ++ get_instruction_pointer(proc), errno); ++ if (elem != NULL && elem->is_syscall ++ && elem->c_un.syscall == *sysnum) ++ return 2; ++ ++ if (*sysnum >= 0) ++ return 1; ++ } ++ return 0; ++} ++ ++size_t ++arch_type_sizeof(struct Process *proc, struct arg_type_info *info) ++{ ++ if (proc == NULL) ++ return (size_t)-2; ++ ++ switch (info->type) { ++ case ARGTYPE_VOID: ++ return 0; ++ ++ case ARGTYPE_CHAR: ++ return 1; ++ ++ case ARGTYPE_SHORT: ++ case ARGTYPE_USHORT: ++ return 2; ++ ++ case ARGTYPE_INT: ++ case ARGTYPE_UINT: ++ return 4; ++ ++ case ARGTYPE_LONG: ++ case ARGTYPE_ULONG: ++ case ARGTYPE_POINTER: ++ return proc->e_machine == EM_X86_64 ? 8 : 4; ++ ++ case ARGTYPE_FLOAT: ++ return 4; ++ case ARGTYPE_DOUBLE: ++ return 8; ++ ++ case ARGTYPE_ARRAY: ++ case ARGTYPE_STRUCT: ++ /* Use default value. */ ++ return (size_t)-2; ++ } ++ assert(info->type != info->type); ++ abort(); ++} ++ ++size_t ++arch_type_alignof(struct Process *proc, struct arg_type_info *info) ++{ ++ if (proc == NULL) ++ return (size_t)-2; ++ ++ switch (info->type) { ++ case ARGTYPE_VOID: ++ assert(info->type != ARGTYPE_VOID); ++ break; ++ ++ case ARGTYPE_CHAR: ++ return 1; ++ ++ case ARGTYPE_SHORT: ++ case ARGTYPE_USHORT: ++ return 2; ++ ++ case ARGTYPE_INT: ++ case ARGTYPE_UINT: ++ return 4; ++ ++ case ARGTYPE_LONG: ++ case ARGTYPE_ULONG: ++ case ARGTYPE_POINTER: ++ return proc->e_machine == EM_X86_64 ? 8 : 4; ++ ++ case ARGTYPE_FLOAT: ++ return 4; ++ case ARGTYPE_DOUBLE: ++ return proc->e_machine == EM_X86_64 ? 8 : 4; ++ ++ case ARGTYPE_ARRAY: ++ case ARGTYPE_STRUCT: ++ /* Use default value. */ ++ return (size_t)-2; ++ } ++ abort(); ++} +diff --git a/sysdeps/linux-gnu/x86_64/Makefile.am b/sysdeps/linux-gnu/x86_64/Makefile.am +deleted file mode 100644 +index 2e476d8..0000000 +--- a/sysdeps/linux-gnu/x86_64/Makefile.am ++++ /dev/null +@@ -1,18 +0,0 @@ +-noinst_LTLIBRARIES = \ +- ../libcpu.la +- +-___libcpu_la_SOURCES = \ +- plt.c \ +- regs.c \ +- trace.c +- +-noinst_HEADERS = \ +- arch.h \ +- ptrace.h \ +- signalent.h \ +- signalent1.h \ +- syscallent.h \ +- syscallent1.h +- +-MAINTAINERCLEANFILES = \ +- Makefile.in +diff --git a/sysdeps/linux-gnu/x86_64/arch.h b/sysdeps/linux-gnu/x86_64/arch.h +deleted file mode 100644 +index 255395c..0000000 +--- a/sysdeps/linux-gnu/x86_64/arch.h ++++ /dev/null +@@ -1,13 +0,0 @@ +-#define BREAKPOINT_VALUE {0xcc} +-#define BREAKPOINT_LENGTH 1 +-#define DECR_PC_AFTER_BREAK 1 +-#define ARCH_ENDIAN_LITTLE +- +-#define LT_ELFCLASS ELFCLASS64 +-#define LT_ELF_MACHINE EM_X86_64 +-#define LT_ELFCLASS2 ELFCLASS32 +-#define LT_ELF_MACHINE2 EM_386 +- +-/* __NR_fork, __NR_clone, __NR_clone2, __NR_vfork and __NR_execve +- from asm-i386/unistd.h. */ +-#define FORK_EXEC_SYSCALLS , { 2, 120, -1, 190, 11 } +diff --git a/sysdeps/linux-gnu/x86_64/plt.c b/sysdeps/linux-gnu/x86_64/plt.c +deleted file mode 100644 +index bb1b2b1..0000000 +--- a/sysdeps/linux-gnu/x86_64/plt.c ++++ /dev/null +@@ -1,14 +0,0 @@ +-#include +-#include "proc.h" +-#include "common.h" +-#include "library.h" +- +-GElf_Addr +-arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { +- return lte->plt_addr + (ndx + 1) * 16; +-} +- +-void * +-sym2addr(Process *proc, struct library_symbol *sym) { +- return sym->enter_addr; +-} +diff --git a/sysdeps/linux-gnu/x86_64/ptrace.h b/sysdeps/linux-gnu/x86_64/ptrace.h +deleted file mode 100644 +index d92771f..0000000 +--- a/sysdeps/linux-gnu/x86_64/ptrace.h ++++ /dev/null +@@ -1,14 +0,0 @@ +-#include +-#include +-#include +- +-typedef struct { +- int valid; +- struct user_regs_struct regs; +- struct user_fpregs_struct fpregs; +-} proc_archdep; +- +-typedef struct { +- struct user_regs_struct regs_copy; +- struct user_fpregs_struct fpregs_copy; +-} callstack_achdep; +diff --git a/sysdeps/linux-gnu/x86_64/regs.c b/sysdeps/linux-gnu/x86_64/regs.c +deleted file mode 100644 +index 0ff3281..0000000 +--- a/sysdeps/linux-gnu/x86_64/regs.c ++++ /dev/null +@@ -1,54 +0,0 @@ +-#include "config.h" +- +-#include +-#include +-#include +- +-#include "proc.h" +- +-#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +-# define PTRACE_PEEKUSER PTRACE_PEEKUSR +-#endif +- +-#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +-# define PTRACE_POKEUSER PTRACE_POKEUSR +-#endif +- +-void * +-get_instruction_pointer(Process *proc) { +- long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RIP, 0); +- if (proc->mask_32bit) +- ret &= 0xffffffff; +- return (void *)ret; +-} +- +-void +-set_instruction_pointer(Process *proc, void *addr) { +- if (proc->mask_32bit) +- addr = (void *)((long int)addr & 0xffffffff); +- ptrace(PTRACE_POKEUSER, proc->pid, 8 * RIP, addr); +-} +- +-void * +-get_stack_pointer(Process *proc) { +- long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RSP, 0); +- if (proc->mask_32bit) +- ret &= 0xffffffff; +- return (void *)ret; +-} +- +-void * +-get_return_addr(Process *proc, void *stack_pointer) { +- unsigned long int ret; +- ret = ptrace(PTRACE_PEEKTEXT, proc->pid, stack_pointer, 0); +- if (proc->mask_32bit) +- ret &= 0xffffffff; +- return (void *)ret; +-} +- +-void +-set_return_addr(Process *proc, void *addr) { +- if (proc->mask_32bit) +- addr = (void *)((long int)addr & 0xffffffff); +- ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, addr); +-} +diff --git a/sysdeps/linux-gnu/x86_64/signalent.h b/sysdeps/linux-gnu/x86_64/signalent.h +deleted file mode 100644 +index d58a36c..0000000 +--- a/sysdeps/linux-gnu/x86_64/signalent.h ++++ /dev/null +@@ -1,32 +0,0 @@ +-"SIG_0", /* 0 */ +- "SIGHUP", /* 1 */ +- "SIGINT", /* 2 */ +- "SIGQUIT", /* 3 */ +- "SIGILL", /* 4 */ +- "SIGTRAP", /* 5 */ +- "SIGABRT", /* 6 */ +- "SIGBUS", /* 7 */ +- "SIGFPE", /* 8 */ +- "SIGKILL", /* 9 */ +- "SIGUSR1", /* 10 */ +- "SIGSEGV", /* 11 */ +- "SIGUSR2", /* 12 */ +- "SIGPIPE", /* 13 */ +- "SIGALRM", /* 14 */ +- "SIGTERM", /* 15 */ +- "SIGSTKFLT", /* 16 */ +- "SIGCHLD", /* 17 */ +- "SIGCONT", /* 18 */ +- "SIGSTOP", /* 19 */ +- "SIGTSTP", /* 20 */ +- "SIGTTIN", /* 21 */ +- "SIGTTOU", /* 22 */ +- "SIGURG", /* 23 */ +- "SIGXCPU", /* 24 */ +- "SIGXFSZ", /* 25 */ +- "SIGVTALRM", /* 26 */ +- "SIGPROF", /* 27 */ +- "SIGWINCH", /* 28 */ +- "SIGIO", /* 29 */ +- "SIGPWR", /* 30 */ +- "SIGSYS", /* 31 */ +diff --git a/sysdeps/linux-gnu/x86_64/signalent1.h b/sysdeps/linux-gnu/x86_64/signalent1.h +deleted file mode 100644 +index 5ead946..0000000 +--- a/sysdeps/linux-gnu/x86_64/signalent1.h ++++ /dev/null +@@ -1 +0,0 @@ +-#include "i386/signalent.h" +diff --git a/sysdeps/linux-gnu/x86_64/syscallent.h b/sysdeps/linux-gnu/x86_64/syscallent.h +deleted file mode 100644 +index 5e5f88a..0000000 +--- a/sysdeps/linux-gnu/x86_64/syscallent.h ++++ /dev/null +@@ -1,256 +0,0 @@ +-"read", /* 0 */ +- "write", /* 1 */ +- "open", /* 2 */ +- "close", /* 3 */ +- "stat", /* 4 */ +- "fstat", /* 5 */ +- "lstat", /* 6 */ +- "poll", /* 7 */ +- "lseek", /* 8 */ +- "mmap", /* 9 */ +- "mprotect", /* 10 */ +- "munmap", /* 11 */ +- "brk", /* 12 */ +- "rt_sigaction", /* 13 */ +- "rt_sigprocmask", /* 14 */ +- "rt_sigreturn", /* 15 */ +- "ioctl", /* 16 */ +- "pread", /* 17 */ +- "pwrite", /* 18 */ +- "readv", /* 19 */ +- "writev", /* 20 */ +- "access", /* 21 */ +- "pipe", /* 22 */ +- "select", /* 23 */ +- "sched_yield", /* 24 */ +- "mremap", /* 25 */ +- "msync", /* 26 */ +- "mincore", /* 27 */ +- "madvise", /* 28 */ +- "shmget", /* 29 */ +- "shmat", /* 30 */ +- "shmctl", /* 31 */ +- "dup", /* 32 */ +- "dup2", /* 33 */ +- "pause", /* 34 */ +- "nanosleep", /* 35 */ +- "getitimer", /* 36 */ +- "alarm", /* 37 */ +- "setitimer", /* 38 */ +- "getpid", /* 39 */ +- "sendfile", /* 40 */ +- "socket", /* 41 */ +- "connect", /* 42 */ +- "accept", /* 43 */ +- "sendto", /* 44 */ +- "recvfrom", /* 45 */ +- "sendmsg", /* 46 */ +- "recvmsg", /* 47 */ +- "shutdown", /* 48 */ +- "bind", /* 49 */ +- "listen", /* 50 */ +- "getsockname", /* 51 */ +- "getpeername", /* 52 */ +- "socketpair", /* 53 */ +- "setsockopt", /* 54 */ +- "getsockopt", /* 55 */ +- "clone", /* 56 */ +- "fork", /* 57 */ +- "vfork", /* 58 */ +- "execve", /* 59 */ +- "exit", /* 60 */ +- "wait4", /* 61 */ +- "kill", /* 62 */ +- "uname", /* 63 */ +- "semget", /* 64 */ +- "semop", /* 65 */ +- "semctl", /* 66 */ +- "shmdt", /* 67 */ +- "msgget", /* 68 */ +- "msgsnd", /* 69 */ +- "msgrcv", /* 70 */ +- "msgctl", /* 71 */ +- "fcntl", /* 72 */ +- "flock", /* 73 */ +- "fsync", /* 74 */ +- "fdatasync", /* 75 */ +- "truncate", /* 76 */ +- "ftruncate", /* 77 */ +- "getdents", /* 78 */ +- "getcwd", /* 79 */ +- "chdir", /* 80 */ +- "fchdir", /* 81 */ +- "rename", /* 82 */ +- "mkdir", /* 83 */ +- "rmdir", /* 84 */ +- "creat", /* 85 */ +- "link", /* 86 */ +- "unlink", /* 87 */ +- "symlink", /* 88 */ +- "readlink", /* 89 */ +- "chmod", /* 90 */ +- "fchmod", /* 91 */ +- "chown", /* 92 */ +- "fchown", /* 93 */ +- "lchown", /* 94 */ +- "umask", /* 95 */ +- "gettimeofday", /* 96 */ +- "getrlimit", /* 97 */ +- "getrusage", /* 98 */ +- "sysinfo", /* 99 */ +- "times", /* 100 */ +- "ptrace", /* 101 */ +- "getuid", /* 102 */ +- "syslog", /* 103 */ +- "getgid", /* 104 */ +- "setuid", /* 105 */ +- "setgid", /* 106 */ +- "geteuid", /* 107 */ +- "getegid", /* 108 */ +- "setpgid", /* 109 */ +- "getppid", /* 110 */ +- "getpgrp", /* 111 */ +- "setsid", /* 112 */ +- "setreuid", /* 113 */ +- "setregid", /* 114 */ +- "getgroups", /* 115 */ +- "setgroups", /* 116 */ +- "setresuid", /* 117 */ +- "getresuid", /* 118 */ +- "setresgid", /* 119 */ +- "getresgid", /* 120 */ +- "getpgid", /* 121 */ +- "setfsuid", /* 122 */ +- "setfsgid", /* 123 */ +- "getsid", /* 124 */ +- "capget", /* 125 */ +- "capset", /* 126 */ +- "rt_sigpending", /* 127 */ +- "rt_sigtimedwait", /* 128 */ +- "rt_sigqueueinfo", /* 129 */ +- "rt_sigsuspend", /* 130 */ +- "sigaltstack", /* 131 */ +- "utime", /* 132 */ +- "mknod", /* 133 */ +- "uselib", /* 134 */ +- "personality", /* 135 */ +- "ustat", /* 136 */ +- "statfs", /* 137 */ +- "fstatfs", /* 138 */ +- "sysfs", /* 139 */ +- "getpriority", /* 140 */ +- "setpriority", /* 141 */ +- "sched_setparam", /* 142 */ +- "sched_getparam", /* 143 */ +- "sched_setscheduler", /* 144 */ +- "sched_getscheduler", /* 145 */ +- "sched_get_priority_max", /* 146 */ +- "sched_get_priority_min", /* 147 */ +- "sched_rr_get_interval", /* 148 */ +- "mlock", /* 149 */ +- "munlock", /* 150 */ +- "mlockall", /* 151 */ +- "munlockall", /* 152 */ +- "vhangup", /* 153 */ +- "modify_ldt", /* 154 */ +- "pivot_root", /* 155 */ +- "_sysctl", /* 156 */ +- "prctl", /* 157 */ +- "arch_prctl", /* 158 */ +- "adjtimex", /* 159 */ +- "setrlimit", /* 160 */ +- "chroot", /* 161 */ +- "sync", /* 162 */ +- "acct", /* 163 */ +- "settimeofday", /* 164 */ +- "mount", /* 165 */ +- "umount2", /* 166 */ +- "swapon", /* 167 */ +- "swapoff", /* 168 */ +- "reboot", /* 169 */ +- "sethostname", /* 170 */ +- "setdomainname", /* 171 */ +- "iopl", /* 172 */ +- "ioperm", /* 173 */ +- "create_module", /* 174 */ +- "init_module", /* 175 */ +- "delete_module", /* 176 */ +- "get_kernel_syms", /* 177 */ +- "query_module", /* 178 */ +- "quotactl", /* 179 */ +- "nfsservctl", /* 180 */ +- "getpmsg", /* 181 */ +- "putpmsg", /* 182 */ +- "afs_syscall", /* 183 */ +- "tuxcall", /* 184 */ +- "security", /* 185 */ +- "gettid", /* 186 */ +- "readahead", /* 187 */ +- "setxattr", /* 188 */ +- "lsetxattr", /* 189 */ +- "fsetxattr", /* 190 */ +- "getxattr", /* 191 */ +- "lgetxattr", /* 192 */ +- "fgetxattr", /* 193 */ +- "listxattr", /* 194 */ +- "llistxattr", /* 195 */ +- "flistxattr", /* 196 */ +- "removexattr", /* 197 */ +- "lremovexattr", /* 198 */ +- "fremovexattr", /* 199 */ +- "tkill", /* 200 */ +- "time", /* 201 */ +- "futex", /* 202 */ +- "sched_setaffinity", /* 203 */ +- "sched_getaffinity", /* 204 */ +- "set_thread_area", /* 205 */ +- "io_setup", /* 206 */ +- "io_destroy", /* 207 */ +- "io_getevents", /* 208 */ +- "io_submit", /* 209 */ +- "io_cancel", /* 210 */ +- "get_thread_area", /* 211 */ +- "lookup_dcookie", /* 212 */ +- "epoll_create", /* 213 */ +- "epoll_ctl", /* 214 */ +- "epoll_wait", /* 215 */ +- "remap_file_pages", /* 216 */ +- "getdents64", /* 217 */ +- "set_tid_address", /* 218 */ +- "restart_syscall", /* 219 */ +- "semtimedop", /* 220 */ +- "fadvise64", /* 221 */ +- "timer_create", /* 222 */ +- "timer_settime", /* 223 */ +- "timer_gettime", /* 224 */ +- "timer_getoverrun", /* 225 */ +- "timer_delete", /* 226 */ +- "clock_settime", /* 227 */ +- "clock_gettime", /* 228 */ +- "clock_getres", /* 229 */ +- "clock_nanosleep", /* 230 */ +- "exit_group", /* 231 */ +- "epoll_wait", /* 232 */ +- "epoll_ctl", /* 233 */ +- "tgkill", /* 234 */ +- "utimes", /* 235 */ +- "vserver", /* 236 */ +- "mbind", /* 237 */ +- "set_mempolicy", /* 238 */ +- "get_mempolicy", /* 239 */ +- "mq_open", /* 240 */ +- "mq_unlink", /* 241 */ +- "mq_timedsend", /* 242 */ +- "mq_timedreceive", /* 243 */ +- "mq_notify", /* 244 */ +- "mq_getsetattr", /* 245 */ +- "kexec_load", /* 246 */ +- "waitid", /* 247 */ +- "add_key", /* 248 */ +- "request_key", /* 249 */ +- "keyctl", /* 250 */ +- "ioprio_set", /* 251 */ +- "ioprio_get", /* 252 */ +- "inotify_init", /* 253 */ +- "inotify_add_watch", /* 254 */ +- "inotify_rm_watch", /* 255 */ +diff --git a/sysdeps/linux-gnu/x86_64/syscallent1.h b/sysdeps/linux-gnu/x86_64/syscallent1.h +deleted file mode 100644 +index d8dd9f7..0000000 +--- a/sysdeps/linux-gnu/x86_64/syscallent1.h ++++ /dev/null +@@ -1 +0,0 @@ +-#include "i386/syscallent.h" +diff --git a/sysdeps/linux-gnu/x86_64/trace.c b/sysdeps/linux-gnu/x86_64/trace.c +deleted file mode 100644 +index 0d3f693..0000000 +--- a/sysdeps/linux-gnu/x86_64/trace.c ++++ /dev/null +@@ -1,201 +0,0 @@ +-#include "config.h" +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "common.h" +-#include "ptrace.h" +-#include "proc.h" +- +-#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +-# define PTRACE_PEEKUSER PTRACE_PEEKUSR +-#endif +- +-#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +-# define PTRACE_POKEUSER PTRACE_POKEUSR +-#endif +- +-void +-get_arch_dep(Process *proc) { +- proc_archdep *a; +- +- if (!proc->arch_ptr) +- proc->arch_ptr = (void *)malloc(sizeof(proc_archdep)); +- +- a = (proc_archdep *) (proc->arch_ptr); +- a->valid = (ptrace(PTRACE_GETREGS, proc->pid, 0, &a->regs) >= 0); +- if (a->valid) { +- a->valid = (ptrace(PTRACE_GETFPREGS, proc->pid, 0, &a->fpregs) >= 0); +- } +- if (a->regs.cs == 0x23) { +- proc->mask_32bit = 1; +- proc->personality = 1; +- } +-} +- +-/* Returns 1 if syscall, 2 if sysret, 0 otherwise. +- */ +-int +-syscall_p(struct Process *proc, int status, int *sysnum) +-{ +- if (WIFSTOPPED(status) +- && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { +- struct callstack_element *elem = NULL; +- if (proc->callstack_depth > 0) +- elem = proc->callstack + proc->callstack_depth - 1; +- +- long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * ORIG_RAX, 0); +- if (ret == -1) { +- if (errno) +- return -1; +- /* Otherwise, ORIG_RAX == -1 means that the +- * system call should not be restarted. In +- * that case rely on what we have on +- * stack. */ +- if (elem != NULL && elem->is_syscall) +- ret = elem->c_un.syscall; +- } +- +- *sysnum = ret; +- debug(DEBUG_FUNCTION, "sysnum=%ld %p %d\n", ret, +- get_instruction_pointer(proc), errno); +- if (elem != NULL && elem->is_syscall +- && elem->c_un.syscall == *sysnum) +- return 2; +- +- if (*sysnum >= 0) +- return 1; +- } +- return 0; +-} +- +-static unsigned int +-gimme_arg32(enum tof type, Process *proc, int arg_num) { +- proc_archdep *a = (proc_archdep *) proc->arch_ptr; +- +- if (arg_num == -1) { /* return value */ +- return a->regs.rax; +- } +- +- if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { +- return ptrace(PTRACE_PEEKTEXT, proc->pid, +- proc->stack_pointer + 4 * (arg_num + 1), 0); +- } else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) { +- switch (arg_num) { +- case 0: +- return a->regs.rbx; +- case 1: +- return a->regs.rcx; +- case 2: +- return a->regs.rdx; +- case 3: +- return a->regs.rsi; +- case 4: +- return a->regs.rdi; +- case 5: +- return a->regs.rbp; +- default: +- fprintf(stderr, +- "gimme_arg32 called with wrong arguments\n"); +- exit(2); +- } +- } +- fprintf(stderr, "gimme_arg called with wrong arguments\n"); +- exit(1); +-} +- +-static long +-gimme_arg_regset(Process *proc, int arg_num, arg_type_info *info, +- struct user_regs_struct *regs, +- struct user_fpregs_struct *fpregs) +-{ +- union { +- uint32_t sse[4]; +- long lval; +- float fval; +- double dval; +- } cvt; +- +- if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) { +- memcpy(cvt.sse, fpregs->xmm_space + 4*arg_num, +- sizeof(cvt.sse)); +- return cvt.lval; +- } +- +- switch (arg_num) { +- case 0: +- return regs->rdi; +- case 1: +- return regs->rsi; +- case 2: +- return regs->rdx; +- case 3: +- return regs->rcx; +- case 4: +- return regs->r8; +- case 5: +- return regs->r9; +- default: +- return ptrace(PTRACE_PEEKTEXT, proc->pid, +- proc->stack_pointer + 8 * (arg_num - 6 + 1), 0); +- } +-} +-static long +-gimme_retval(Process *proc, int arg_num, arg_type_info *info, +- struct user_regs_struct *regs, struct user_fpregs_struct *fpregs) +-{ +- if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) +- return gimme_arg_regset(proc, 0, info, regs, fpregs); +- else +- return regs->rax; +-} +- +-long +-gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { +- if (proc->mask_32bit) +- return (unsigned int)gimme_arg32(type, proc, arg_num); +- +- proc_archdep *arch = (proc_archdep *)proc->arch_ptr; +- +- if (arch == NULL || !arch->valid) +- return -1; +- +- if (type == LT_TOF_FUNCTIONR) { +- if (arg_num == -1) +- return gimme_retval(proc, arg_num, info, +- &arch->regs, &arch->fpregs); +- else { +- struct callstack_element *elem +- = proc->callstack + proc->callstack_depth - 1; +- callstack_achdep *csad = elem->arch_ptr; +- assert(csad != NULL); +- return gimme_arg_regset(proc, arg_num, info, +- &csad->regs_copy, +- &csad->fpregs_copy); +- } +- } +- else +- return gimme_arg_regset(proc, arg_num, info, +- &arch->regs, &arch->fpregs); +-} +- +-void +-save_register_args(enum tof type, Process *proc) { +- proc_archdep *arch = (proc_archdep *)proc->arch_ptr; +- if (arch == NULL || !arch->valid) +- return; +- +- callstack_achdep *csad = malloc(sizeof(*csad)); +- memset(csad, 0, sizeof(*csad)); +- memcpy(&csad->regs_copy, &arch->regs, sizeof(arch->regs)); +- memcpy(&csad->fpregs_copy, &arch->fpregs, sizeof(arch->fpregs)); +- +- proc->callstack[proc->callstack_depth - 1].arch_ptr = csad; +-} +diff --git a/testsuite/ltrace.main/parameters-lib.c b/testsuite/ltrace.main/parameters-lib.c +index ef98143..f9a869d 100644 +--- a/testsuite/ltrace.main/parameters-lib.c ++++ b/testsuite/ltrace.main/parameters-lib.c +@@ -27,6 +27,11 @@ void func_strfixed(char* p) + strcpy(p, "Hello world"); + } + ++void func_string(char* p) ++{ ++ printf("%s\n", p); ++} ++ + void func_ppp(int*** ppp) + { + printf("%d\n", ***ppp); +@@ -133,3 +138,106 @@ void func_call (char *x, char* y, void (*cb) (char *)) + cb (y); + *x = (*y)++; + } ++ ++struct S2 { ++ float f; ++ char a; ++ char b; ++}; ++ ++struct S3 { ++ char a[6]; ++ float f; ++}; ++ ++struct S2 ++func_struct_2(int i, struct S3 s3, double d) ++{ ++ return (struct S2){ s3.f, s3.a[1], s3.a[2] }; ++} ++ ++struct S4 { ++ long a; ++ long b; ++ long c; ++ long d; ++}; ++ ++struct S4 ++func_struct_large(struct S4 a, struct S4 b) ++{ ++ return (struct S4){ a.a + b.a, a.b + b.b, a.c + b.c, a.d + b.d }; ++} ++ ++struct S5 { ++ char a; ++ char b; ++ long c; ++ long d; ++}; ++ ++struct S5 ++func_struct_large2(struct S5 a, struct S5 b) ++{ ++ return (struct S5){ a.a + b.a, a.b + b.b, a.c + b.c, a.d + b.d }; ++} ++ ++struct S6 { ++ long a; ++ long b; ++ char c; ++ char d; ++}; ++ ++struct S6 ++func_struct_large3(struct S6 a, struct S6 b) ++{ ++ return (struct S6){ a.a + b.a, a.b + b.b, a.c + b.c, a.d + b.d }; ++} ++ ++void ++func_many_args(int a, int b, long c, double d, char e, int f, float g, char h, ++ int i, double j, int k, double l, char m, int n, short o, int p, ++ char q, float r, float s, double t, long u, float v, float w, ++ float x, float y) ++{ ++} ++ ++void ++func_lens(int a, long b, short c, long d) ++{ ++} ++ ++int ++func_bool(int a, int b) ++{ ++ return !b; ++} ++ ++void ++func_hide(int a, int b, int c, int d, int e, int f) ++{ ++} ++ ++long * ++func_short_enums(short values[]) ++{ ++ static long retvals[4]; ++ retvals[0] = values[0]; ++ retvals[1] = values[1]; ++ retvals[2] = values[2]; ++ retvals[3] = values[3]; ++ return retvals; ++} ++ ++long ++func_negative_enum(short a, unsigned short b, int c, unsigned d, ++ long e, unsigned long f) ++{ ++ return -1; ++} ++ ++void ++func_charp_string(char *p) ++{ ++} +diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c +index fb46dfe..e8207fe 100644 +--- a/testsuite/ltrace.main/parameters.c ++++ b/testsuite/ltrace.main/parameters.c +@@ -59,6 +59,7 @@ main () + + func_intptr_ret(&x); + ++ func_string("zero\0xxxxxxxxxxxxxx"); + func_strlen(buf); + printf("%s\n", buf); + +@@ -133,5 +134,89 @@ main () + func_call(x, y, call_func_work); + } + ++ struct S2 { ++ float f; ++ char a; ++ char b; ++ }; ++ struct S3 { ++ char a[6]; ++ float f; ++ }; ++ struct S2 func_struct_2(int, struct S3 s3, double d); ++ func_struct_2(17, (struct S3){ "ABCDE", 0.25 }, 0.5); ++ ++ struct S4 { ++ long a; ++ long b; ++ long c; ++ long d; ++ }; ++ struct S4 func_struct_large(struct S4 a, struct S4 b); ++ func_struct_large((struct S4){ 1, 2, 3, 4 }, (struct S4){ 5, 6, 7, 8 }); ++ ++ struct S5 { ++ char a; ++ char b; ++ long c; ++ long d; ++ }; ++ struct S5 func_struct_large2(struct S5 a, struct S5 b); ++ func_struct_large2((struct S5){ '0', '1', 3, 4 }, (struct S5){ '2', '3', 7, 8 }); ++ ++ struct S6 { ++ long a; ++ long b; ++ char c; ++ char d; ++ }; ++ struct S6 func_struct_large3(struct S6 a, struct S6 b); ++ func_struct_large3((struct S6){ 3, 4, '0', '1' }, (struct S6){ 7, 8 ,'2', '3' }); ++ ++ void func_many_args(int a, int b, long c, double d, char e, int f, float g, ++ char h, int i, double j, int k, double l, char m, int n, ++ short o, int p, char q, float r, float s, double t, ++ long u, float v, float w, float x, float y); ++ func_many_args(1, 2, 3, 4.0, '5', 6, 7.0, ++ '8', 9, 10.0, 11, 12.0, 'A', 14, ++ 15, 16, 'B', 18.0, 19.0, 20.0, ++ 21, 22.0, 23.0, 24.0, 25.0); ++ ++ printf("sotnuh %d %ld %g %c\n", 5, 6L, 1.5, 'X'); ++ printf("sotnuh1 %d %ld %hd\n", 5, 6L, (short)7); ++ printf("sotnuh2 %s %10s %10s\n", "a string", "a trimmed string", "short"); ++ printf("many_args" ++ "%d %d %ld %g %c %d %g " ++ "%c %d %g %d %g %c %d " ++ "%hd %d %c %g %g %g " ++ "%ld %g %g %g %g", ++ 1, 2, 3, 4.0, '5', 6, 7.0, ++ '8', 9, 10.0, 11, 12.0, 'A', 14, ++ (short)15, 16, 'B', 18.0, 19.0, 20.0, ++ 21L, 22.0, 23.0, 24.0, 25.0); ++ ++ printf("sotnuh3 %*s\n", 4, "a trimmed string"); ++ ++ void func_lens(int, long, short, long); ++ func_lens(22, 23, 24, 25); ++ ++ int func_bool(int a, int b); ++ func_bool(1, 10); ++ func_bool(2, 0); ++ ++ void func_hide(int a, int b, int c, int d, int e, int f); ++ func_hide(1, 2, 3, 4, 5, 6); ++ ++ enum ab { A, B }; ++ long *func_short_enums(short abs[]); ++ func_short_enums((short[]){ A, B, A, A }); ++ ++ long func_negative_enum(short a, unsigned short b, int c, unsigned d, ++ long e, unsigned long f); ++ func_negative_enum(-1, -1, -1, -1, -1, -1); ++ ++ void func_charp_string(char *p); ++ func_charp_string("null-terminated string"); ++ + return 0; + } +diff --git a/testsuite/ltrace.main/parameters.conf b/testsuite/ltrace.main/parameters.conf +index e94ce2c..9e0c967 100644 +--- a/testsuite/ltrace.main/parameters.conf ++++ b/testsuite/ltrace.main/parameters.conf +@@ -1,8 +1,9 @@ + void func_intptr(int*) + void func_intptr_ret(+int*) ++void func_string(string[10]) + int func_strlen(+string[retval]) + void func_strfixed(string[4]) +-void func_ppp(int***) ++void func_ppp(int * **) + void func_stringp(string*) + void func_enum(enum (RED=0,GREEN=1,BLUE=2,CHARTREUSE=3,PETUNIA=4)) + void func_short(short,short) +@@ -11,8 +12,20 @@ float func_float(float,float) + double func_double(double,double) + typedef color = enum (RED=0,GREEN=1,BLUE=2,CHARTREUSE=3,PETUNIA=4) + void func_typedef(color) +-void func_arrayi(array(int,arg2)*,void) +-void func_arrayf(array(float,arg2)*,void) ++void func_arrayi(array(int,arg2)*,int) ++void func_arrayf(array(float,arg2)*,int) + void func_struct(struct(int,int,int,array(struct(int,int),elt2)*,array(struct(int,int),3),string[elt3])*) + void func_work(+string); + void func_call(+string, string); ++struct(float,char,char) func_struct_2(int, struct(string(array(char, 6)),float), double); ++struct(long,long,long,long) func_struct_large(struct(long,long,long,long), struct(long,long,long,long)); ++struct(char,char,long,long) func_struct_large2(struct(char,char,long,long), struct(char,char,long,long)); ++struct(long,long,char,char) func_struct_large3(struct(long,long,char,char), struct(long,long,char,char)); ++void func_many_args(int, int, long, double, char, int, float, char, int, double, int, double, char, int, short, int, char, float, float, double, long, float, float, float, float); ++int printf(format); ++void func_lens(octal, octal(long), hex(short), hex(long)); ++bool(int) func_bool(int, bool(int)); ++void func_hide(int, hide(int), hide(int), int, hide(int), int); ++array(enum[long](A,B), 4) *func_short_enums(array(enum[short](A,B), 4)); ++enum[long](A=-1) func_negative_enum(enum[short](A=-1), enum[ushort](A=-1), enum[int](A=-1), enum[uint](A=-1), enum[long](A=-1), enum[ulong](A=-1)); ++void func_charp_string(string(char *)); +diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp +index 924afaf..8403721 100644 +--- a/testsuite/ltrace.main/parameters.exp ++++ b/testsuite/ltrace.main/parameters.exp +@@ -35,7 +35,8 @@ if [regexp {ELF from incompatible architecture} $exec_output] { + return + } + +-set xfail_spec {"arm*-*" "i*86-*"} ++set xfail_spec {"arm*-*" } ++set xfail_spec_arm {"arm*-*"} + + # Verify the output + set pattern "func_intptr(17)" +@@ -50,6 +51,8 @@ set pattern "func_ppp(80)" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 + set pattern "func_stringp(\\\"Dude\\\")" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++set pattern "func_string(\\\"zero\\\")" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 + set pattern "func_enum(BLUE)" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 + set pattern "func_short(-8, -9)" +@@ -63,13 +66,13 @@ set pattern "func_double(3.40*, -3.40*).*= -3.40*" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 + set pattern "func_typedef(BLUE)" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-set pattern "func_arrayi(. 10, 11, 12, 13\\.\\.\\. ., )" ++set pattern "func_arrayi(. 10, 11, 12, 13\\.\\.\\. ., 8)" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-set pattern "func_arrayi(. 10, 11 ., )" ++set pattern "func_arrayi(. 10, 11 ., 2)" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-set pattern "func_arrayf(. 10.10*, 11.10*, 12.10*, 13.10*\\.\\.\\. ., )" ++set pattern "func_arrayf(. 10.10*, 11.10*, 12.10*, 13.10*\\.\\.\\. ., 8)" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-set pattern "func_arrayf(. 10.10*, 11.10* ., )" ++set pattern "func_arrayf(. 10.10*, 11.10* ., 2)" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 + set pattern "exited (status 0)" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +@@ -79,6 +82,56 @@ set pattern "func_call( " + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 + set pattern "func_work(\\\"x\\\")" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +-eval "setup_xfail $xfail_spec" ++set pattern "func_struct_2(17, { \\\"ABCDE\\\\\\\\0\\\", 0.250* }, 0.50*).*= { 0.250*, 'B', 'C' }" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++eval "setup_xfail $xfail_spec_arm" + set pattern "<... func_call resumed> \\\"x\\\", \\\"y\\\")" + ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++ ++set pattern "func_struct_large({ 1, 2, 3, 4 }, { 5, 6, 7, 8 }).*= { 6, 8, 10, 12 }" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++ ++set pattern "func_struct_large2({ '0', '1', 3, 4 }, { '2', '3', 7, 8 }).*= { 'b', 'd', 10, 12 }" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++ ++set pattern "func_struct_large3({ 3, 4, '0', '1' }, { 7, 8, '2', '3' }).*= { 10, 12, 'b', 'd' }" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++ ++set pattern "func_many_args(1, 2, 3, 4.00*, '5', 6, 7.00*, '8', 9, 10.00*, 11, 12.00*, 'A', 14, 15, 16, 'B', 18.00*, 19.00*, 20.00*, 21, 22.00*, 23.00*, 24.00*, 25.00*)" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++ ++set pattern "printf(\\\"sotnuh %d %ld %g %c.n\\\", 5, 6, 1.500*, 'X')" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++ ++set pattern "printf(\\\"sotnuh1 %d %ld %hd.n\\\", 5, 6, 7)" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++ ++set pattern "printf(\\\"sotnuh2 %s %10s %10s.n\\\", \\\"a string\\\", \\\"a trimmed \\\", \\\"short\\\")" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++ ++set pattern "printf(\\\"sotnuh3 %.s.n\\\", 4, \\\"a tr\\\")" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++ ++set pattern "printf(\\\"many_args%d %d %ld %g %c %d %g .*, 1, 2, 3, 4.00*, '5', 6, 7.00*, '8', 9, 10.00*, 11, 12.00*, 'A', 14, 15, 16, 'B', 18.00*, 19.00*, 20.00*, 21, 22.00*, 23.00*, 24.00*, 25.00*)" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++ ++set pattern "func_lens(026, 027, 0x18, 0x19)" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++ ++set pattern "func_bool(1, true).*= false" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++ ++set pattern "func_bool(2, false).*= true" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++ ++set pattern "func_hide(1, 4, 6)" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++ ++set pattern "func_short_enums(. A, B, A, A .).*= . A, B, A, A ." ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++ ++set pattern "func_negative_enum(A, A, A, A, A, A).*= A" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 ++ ++set pattern "func_charp_string(\\\"null-terminated string\\\")" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 1 +diff --git a/type.c b/type.c +new file mode 100644 +index 0000000..6341042 +--- /dev/null ++++ b/type.c +@@ -0,0 +1,515 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2007,2008 Juan Cespedes ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++ ++#include "type.h" ++#include "sysdep.h" ++#include "expr.h" ++#include "lens.h" ++ ++struct arg_type_info * ++type_get_simple(enum arg_type type) ++{ ++#define HANDLE(T) { \ ++ static struct arg_type_info t = { T }; \ ++ case T: \ ++ return &t; \ ++ } ++ ++ switch (type) { ++ HANDLE(ARGTYPE_VOID) ++ HANDLE(ARGTYPE_INT) ++ HANDLE(ARGTYPE_UINT) ++ HANDLE(ARGTYPE_LONG) ++ HANDLE(ARGTYPE_ULONG) ++ HANDLE(ARGTYPE_CHAR) ++ HANDLE(ARGTYPE_SHORT) ++ HANDLE(ARGTYPE_USHORT) ++ HANDLE(ARGTYPE_FLOAT) ++ HANDLE(ARGTYPE_DOUBLE) ++ ++#undef HANDLE ++ ++ case ARGTYPE_ARRAY: ++ case ARGTYPE_STRUCT: ++ case ARGTYPE_POINTER: ++ assert(!"Not a simple type!"); ++ }; ++ abort(); ++} ++ ++static void ++type_init_common(struct arg_type_info *info, enum arg_type type) ++{ ++ info->type = type; ++ info->lens = NULL; ++ info->own_lens = 0; ++} ++ ++struct struct_field { ++ struct arg_type_info *info; ++ int own_info; ++}; ++ ++void ++type_init_struct(struct arg_type_info *info) ++{ ++ type_init_common(info, ARGTYPE_STRUCT); ++ VECT_INIT(&info->u.entries, struct struct_field); ++} ++ ++int ++type_struct_add(struct arg_type_info *info, ++ struct arg_type_info *field_info, int own) ++{ ++ assert(info->type == ARGTYPE_STRUCT); ++ struct struct_field field = { field_info, own }; ++ return VECT_PUSHBACK(&info->u.entries, &field); ++} ++ ++struct arg_type_info * ++type_struct_get(struct arg_type_info *info, size_t idx) ++{ ++ assert(info->type == ARGTYPE_STRUCT); ++ struct struct_field *field = VECT_ELEMENT(&info->u.entries, ++ struct struct_field, idx); ++ if (field == NULL) ++ return NULL; ++ return field->info; ++} ++ ++size_t ++type_struct_size(struct arg_type_info *info) ++{ ++ assert(info->type == ARGTYPE_STRUCT); ++ return vect_size(&info->u.entries); ++} ++ ++static void ++struct_field_dtor(struct struct_field *field, void *data) ++{ ++ if (field->own_info) { ++ type_destroy(field->info); ++ free(field->info); ++ } ++} ++ ++static void ++type_struct_destroy(struct arg_type_info *info) ++{ ++ VECT_DESTROY(&info->u.entries, struct struct_field, ++ struct_field_dtor, NULL); ++} ++ ++static int ++layout_struct(struct Process *proc, struct arg_type_info *info, ++ size_t *sizep, size_t *alignmentp, size_t *offsetofp) ++{ ++ size_t sz = 0; ++ size_t max_alignment = 0; ++ size_t i; ++ size_t offsetof_field = (size_t)-1; ++ if (offsetofp != NULL) ++ offsetof_field = *offsetofp; ++ ++ assert(info->type == ARGTYPE_STRUCT); ++ for (i = 0; i < vect_size(&info->u.entries); ++i) { ++ struct struct_field *field ++ = VECT_ELEMENT(&info->u.entries, ++ struct struct_field, i); ++ ++ size_t alignment = type_alignof(proc, field->info); ++ if (alignment == (size_t)-1) ++ return -1; ++ ++ /* Add padding to SZ to align the next element. */ ++ sz = align(sz, alignment); ++ if (i == offsetof_field) { ++ *offsetofp = sz; ++ if (sizep == NULL && alignmentp == NULL) ++ return 0; ++ } ++ ++ size_t size = type_sizeof(proc, field->info); ++ if (size == (size_t)-1) ++ return -1; ++ sz += size; ++ ++ if (alignment > max_alignment) ++ max_alignment = alignment; ++ } ++ ++ if (max_alignment > 0) ++ sz = align(sz, max_alignment); ++ ++ if (sizep != NULL) ++ *sizep = sz; ++ ++ if (alignmentp != NULL) ++ *alignmentp = max_alignment; ++ ++ return 0; ++} ++ ++void ++type_init_array(struct arg_type_info *info, ++ struct arg_type_info *element_info, int own_info, ++ struct expr_node *length_expr, int own_length) ++{ ++ type_init_common(info, ARGTYPE_ARRAY); ++ info->u.array_info.elt_type = element_info; ++ info->u.array_info.own_info = own_info; ++ info->u.array_info.length = length_expr; ++ info->u.array_info.own_length = own_length; ++} ++ ++static void ++type_array_destroy(struct arg_type_info *info) ++{ ++ if (info->u.array_info.own_info) { ++ type_destroy(info->u.array_info.elt_type); ++ free(info->u.array_info.elt_type); ++ } ++ if (info->u.array_info.own_length) { ++ expr_destroy(info->u.array_info.length); ++ free(info->u.array_info.length); ++ } ++} ++ ++void ++type_init_pointer(struct arg_type_info *info, ++ struct arg_type_info *pointee_info, int own_info) ++{ ++ type_init_common(info, ARGTYPE_POINTER); ++ info->u.ptr_info.info = pointee_info; ++ info->u.ptr_info.own_info = own_info; ++} ++ ++static void ++type_pointer_destroy(struct arg_type_info *info) ++{ ++ if (info->u.ptr_info.own_info) { ++ type_destroy(info->u.ptr_info.info); ++ free(info->u.ptr_info.info); ++ } ++} ++ ++void ++type_destroy(struct arg_type_info *info) ++{ ++ if (info == NULL) ++ return; ++ ++ switch (info->type) { ++ case ARGTYPE_STRUCT: ++ type_struct_destroy(info); ++ break; ++ ++ case ARGTYPE_ARRAY: ++ type_array_destroy(info); ++ break; ++ ++ case ARGTYPE_POINTER: ++ type_pointer_destroy(info); ++ break; ++ ++ case ARGTYPE_VOID: ++ case ARGTYPE_INT: ++ case ARGTYPE_UINT: ++ case ARGTYPE_LONG: ++ case ARGTYPE_ULONG: ++ case ARGTYPE_CHAR: ++ case ARGTYPE_SHORT: ++ case ARGTYPE_USHORT: ++ case ARGTYPE_FLOAT: ++ case ARGTYPE_DOUBLE: ++ break; ++ } ++ ++ if (info->own_lens) { ++ lens_destroy(info->lens); ++ free(info->lens); ++ } ++} ++ ++#ifdef ARCH_HAVE_SIZEOF ++size_t arch_type_sizeof(struct Process *proc, struct arg_type_info * arg); ++#else ++size_t ++arch_type_sizeof(struct Process *proc, struct arg_type_info * arg) ++{ ++ /* Use default value. */ ++ return (size_t)-2; ++} ++#endif ++ ++#ifdef ARCH_HAVE_ALIGNOF ++size_t arch_type_alignof(struct Process *proc, struct arg_type_info * arg); ++#else ++size_t ++arch_type_alignof(struct Process *proc, struct arg_type_info * arg) ++{ ++ /* Use default value. */ ++ return (size_t)-2; ++} ++#endif ++ ++/* We need to support alignments that are not power of two. E.g. long ++ * double on x86 has alignment of 12. */ ++size_t ++align(size_t sz, size_t alignment) ++{ ++ assert(alignment != 0); ++ ++ if ((sz % alignment) != 0) ++ sz = ((sz / alignment) + 1) * alignment; ++ ++ return sz; ++} ++ ++size_t ++type_sizeof(struct Process *proc, struct arg_type_info *type) ++{ ++ size_t arch_size = arch_type_sizeof(proc, type); ++ if (arch_size != (size_t)-2) ++ return arch_size; ++ ++ switch (type->type) { ++ size_t size; ++ case ARGTYPE_CHAR: ++ return sizeof(char); ++ ++ case ARGTYPE_SHORT: ++ case ARGTYPE_USHORT: ++ return sizeof(short); ++ ++ case ARGTYPE_INT: ++ case ARGTYPE_UINT: ++ return sizeof(int); ++ ++ case ARGTYPE_LONG: ++ case ARGTYPE_ULONG: ++ return sizeof(long); ++ ++ case ARGTYPE_FLOAT: ++ return sizeof(float); ++ ++ case ARGTYPE_DOUBLE: ++ return sizeof(double); ++ ++ case ARGTYPE_STRUCT: ++ if (layout_struct(proc, type, &size, NULL, NULL) < 0) ++ return (size_t)-1; ++ return size; ++ ++ case ARGTYPE_POINTER: ++ return sizeof(void *); ++ ++ case ARGTYPE_ARRAY: ++ if (expr_is_compile_constant(type->u.array_info.length)) { ++ long l; ++ if (expr_eval_constant(type->u.array_info.length, ++ &l) < 0) ++ return -1; ++ ++ struct arg_type_info *elt_ti ++ = type->u.array_info.elt_type; ++ ++ size_t elt_size = type_sizeof(proc, elt_ti); ++ if (elt_size == (size_t)-1) ++ return (size_t)-1; ++ ++ return ((size_t)l) * elt_size; ++ ++ } else { ++ /* Flexible arrays don't count into the ++ * sizeof. */ ++ return 0; ++ } ++ ++ case ARGTYPE_VOID: ++ return 0; ++ } ++ ++ abort(); ++} ++ ++#undef alignof ++#define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st)) ++ ++size_t ++type_alignof(struct Process *proc, struct arg_type_info *type) ++{ ++ size_t arch_alignment = arch_type_alignof(proc, type); ++ if (arch_alignment != (size_t)-2) ++ return arch_alignment; ++ ++ struct { char c; char C; } cC; ++ struct { char c; short s; } cs; ++ struct { char c; int i; } ci; ++ struct { char c; long l; } cl; ++ struct { char c; void* p; } cp; ++ struct { char c; float f; } cf; ++ struct { char c; double d; } cd; ++ ++ static size_t char_alignment = alignof(C, cC); ++ static size_t short_alignment = alignof(s, cs); ++ static size_t int_alignment = alignof(i, ci); ++ static size_t long_alignment = alignof(l, cl); ++ static size_t ptr_alignment = alignof(p, cp); ++ static size_t float_alignment = alignof(f, cf); ++ static size_t double_alignment = alignof(d, cd); ++ ++ switch (type->type) { ++ size_t alignment; ++ case ARGTYPE_LONG: ++ case ARGTYPE_ULONG: ++ return long_alignment; ++ case ARGTYPE_CHAR: ++ return char_alignment; ++ case ARGTYPE_SHORT: ++ case ARGTYPE_USHORT: ++ return short_alignment; ++ case ARGTYPE_FLOAT: ++ return float_alignment; ++ case ARGTYPE_DOUBLE: ++ return double_alignment; ++ case ARGTYPE_POINTER: ++ return ptr_alignment; ++ ++ case ARGTYPE_ARRAY: ++ return type_alignof(proc, type->u.array_info.elt_type); ++ ++ case ARGTYPE_STRUCT: ++ if (layout_struct(proc, type, NULL, &alignment, NULL) < 0) ++ return (size_t)-1; ++ return alignment; ++ ++ default: ++ return int_alignment; ++ } ++} ++ ++size_t ++type_offsetof(struct Process *proc, struct arg_type_info *type, size_t emt) ++{ ++ assert(type->type == ARGTYPE_STRUCT ++ || type->type == ARGTYPE_ARRAY); ++ ++ switch (type->type) { ++ size_t alignment; ++ size_t size; ++ case ARGTYPE_ARRAY: ++ alignment = type_alignof(proc, type->u.array_info.elt_type); ++ if (alignment == (size_t)-1) ++ return (size_t)-1; ++ ++ size = type_sizeof(proc, type->u.array_info.elt_type); ++ if (size == (size_t)-1) ++ return (size_t)-1; ++ ++ return emt * align(size, alignment); ++ ++ case ARGTYPE_STRUCT: ++ if (layout_struct(proc, type, NULL, NULL, &emt) < 0) ++ return (size_t)-1; ++ return emt; ++ ++ default: ++ abort(); ++ } ++} ++ ++struct arg_type_info * ++type_element(struct arg_type_info *info, size_t emt) ++{ ++ assert(info->type == ARGTYPE_STRUCT ++ || info->type == ARGTYPE_ARRAY); ++ ++ switch (info->type) { ++ case ARGTYPE_ARRAY: ++ return info->u.array_info.elt_type; ++ ++ case ARGTYPE_STRUCT: ++ assert(emt < type_struct_size(info)); ++ return type_struct_get(info, emt); ++ ++ default: ++ abort(); ++ } ++} ++ ++int ++type_is_integral(enum arg_type type) ++{ ++ switch (type) { ++ case ARGTYPE_INT: ++ case ARGTYPE_UINT: ++ case ARGTYPE_LONG: ++ case ARGTYPE_ULONG: ++ case ARGTYPE_CHAR: ++ case ARGTYPE_SHORT: ++ case ARGTYPE_USHORT: ++ return 1; ++ ++ case ARGTYPE_VOID: ++ case ARGTYPE_FLOAT: ++ case ARGTYPE_DOUBLE: ++ case ARGTYPE_ARRAY: ++ case ARGTYPE_STRUCT: ++ case ARGTYPE_POINTER: ++ return 0; ++ } ++ abort(); ++} ++ ++int ++type_is_signed(enum arg_type type) ++{ ++ assert(type_is_integral(type)); ++ ++ switch (type) { ++ case ARGTYPE_CHAR: ++ return CHAR_MIN != 0; ++ ++ case ARGTYPE_SHORT: ++ case ARGTYPE_INT: ++ case ARGTYPE_LONG: ++ return 1; ++ ++ case ARGTYPE_UINT: ++ case ARGTYPE_ULONG: ++ case ARGTYPE_USHORT: ++ return 0; ++ ++ case ARGTYPE_VOID: ++ case ARGTYPE_FLOAT: ++ case ARGTYPE_DOUBLE: ++ case ARGTYPE_ARRAY: ++ case ARGTYPE_STRUCT: ++ case ARGTYPE_POINTER: ++ abort(); ++ } ++ abort(); ++} +diff --git a/type.h b/type.h +new file mode 100644 +index 0000000..545173c +--- /dev/null ++++ b/type.h +@@ -0,0 +1,133 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 1997-2009 Juan Cespedes ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef TYPE_H ++#define TYPE_H ++ ++#include ++#include "forward.h" ++#include "vect.h" ++ ++enum arg_type { ++ ARGTYPE_VOID, ++ ARGTYPE_INT, ++ ARGTYPE_UINT, ++ ARGTYPE_LONG, ++ ARGTYPE_ULONG, ++ ARGTYPE_CHAR, ++ ARGTYPE_SHORT, ++ ARGTYPE_USHORT, ++ ARGTYPE_FLOAT, ++ ARGTYPE_DOUBLE, ++ ARGTYPE_ARRAY, /* Series of values in memory */ ++ ARGTYPE_STRUCT, /* Structure of values */ ++ ARGTYPE_POINTER, /* Pointer to some other type */ ++}; ++ ++struct arg_type_info { ++ enum arg_type type; ++ union { ++ struct vect entries; ++ ++ /* ARGTYPE_ARRAY */ ++ struct { ++ struct arg_type_info *elt_type; ++ struct expr_node *length; ++ int own_info:1; ++ int own_length:1; ++ } array_info; ++ ++ /* ARGTYPE_POINTER */ ++ struct { ++ struct arg_type_info *info; ++ int own_info:1; ++ } ptr_info; ++ } u; ++ ++ struct lens *lens; ++ int own_lens; ++}; ++ ++/* Return a type info for simple type TYPE (which shall not be array, ++ * struct, or pointer. Each call with the same TYPE yields the same ++ * arg_type_info pointer. */ ++struct arg_type_info *type_get_simple(enum arg_type type); ++ ++/* Initialize INFO so it becomes ARGTYPE_STRUCT. The created ++ * structure contains no fields. Use type_struct_add to populate the ++ * structure. */ ++void type_init_struct(struct arg_type_info *info); ++ ++/* Add a new field of type FIELD_INFO to a structure INFO. If OWN, ++ * the field type is owned and destroyed together with INFO. */ ++int type_struct_add(struct arg_type_info *info, ++ struct arg_type_info *field_info, int own); ++ ++/* Get IDX-th field of structure type INFO. */ ++struct arg_type_info *type_struct_get(struct arg_type_info *info, size_t idx); ++ ++/* Return number of fields of structure type INFO. */ ++size_t type_struct_size(struct arg_type_info *info); ++ ++/* Initialize INFO so it becomes ARGTYPE_ARRAY. The element type is ++ * passed in ELEMENT_INFO, and array length in LENGTH_EXPR. If, ++ * respectively, OWN_INFO and OWN_LENGTH are true, the pointee and ++ * length are owned and destroyed together with INFO. */ ++void type_init_array(struct arg_type_info *info, ++ struct arg_type_info *element_info, int own_info, ++ struct expr_node *length_expr, int own_length); ++ ++/* Initialize INFO so it becomes ARGTYPE_POINTER. The pointee type is ++ * passed in POINTEE_INFO. If OWN_INFO, the pointee type is owned and ++ * destroyed together with INFO. */ ++void type_init_pointer(struct arg_type_info *info, ++ struct arg_type_info *pointee_info, int own_info); ++ ++/* Release any memory associated with INFO. Doesn't free INFO ++ * itself. */ ++void type_destroy(struct arg_type_info *info); ++ ++/* Compute a size of given type. Return (size_t)-1 for error. */ ++size_t type_sizeof(struct Process *proc, struct arg_type_info *type); ++ ++/* Compute an alignment necessary for elements of this type. Return ++ * (size_t)-1 for error. */ ++size_t type_alignof(struct Process *proc, struct arg_type_info *type); ++ ++/* Align value SZ to ALIGNMENT and return the result. */ ++size_t align(size_t sz, size_t alignment); ++ ++/* Return ELT-th element of compound type TYPE. This is useful for ++ * arrays and structures. */ ++struct arg_type_info *type_element(struct arg_type_info *type, size_t elt); ++ ++/* Compute an offset of EMT-th element of type TYPE. This works for ++ * arrays and structures. Return (size_t)-1 for error. */ ++size_t type_offsetof(struct Process *proc, ++ struct arg_type_info *type, size_t elt); ++ ++/* Whether TYPE is an integral type as defined by the C standard. */ ++int type_is_integral(enum arg_type type); ++ ++/* Whether TYPE, which shall be integral, is a signed type. */ ++int type_is_signed(enum arg_type type); ++ ++#endif /* TYPE_H */ +diff --git a/value.c b/value.c +new file mode 100644 +index 0000000..62466f2 +--- /dev/null ++++ b/value.c +@@ -0,0 +1,455 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++ ++#include "value.h" ++#include "type.h" ++#include "common.h" ++#include "expr.h" ++#include "backend.h" ++ ++static void ++value_common_init(struct value *valp, struct Process *inferior, ++ struct value *parent, struct arg_type_info *type, ++ int own_type) ++{ ++ valp->type = type; ++ valp->own_type = own_type; ++ valp->inferior = inferior; ++ memset(&valp->u, 0, sizeof(valp->u)); ++ valp->where = VAL_LOC_NODATA; ++ valp->parent = parent; ++ valp->size = (size_t)-1; ++} ++ ++void ++value_init(struct value *valp, struct Process *inferior, struct value *parent, ++ struct arg_type_info *type, int own_type) ++{ ++ assert(inferior != NULL); ++ value_common_init(valp, inferior, parent, type, own_type); ++} ++ ++void ++value_init_detached(struct value *valp, struct value *parent, ++ struct arg_type_info *type, int own_type) ++{ ++ value_common_init(valp, NULL, parent, type, own_type); ++} ++ ++void ++value_set_type(struct value *value, struct arg_type_info *type, int own_type) ++{ ++ if (value->own_type) ++ type_destroy(value->type); ++ value->type = type; ++ value->own_type = own_type; ++} ++ ++void ++value_take_type(struct value *value, struct arg_type_info **type, ++ int *own_type) ++{ ++ *type = value->type; ++ *own_type = value->own_type; ++ value->own_type = 0; ++} ++ ++void ++value_release(struct value *val) ++{ ++ if (val == NULL) ++ return; ++ if (val->where == VAL_LOC_COPY) { ++ free(val->u.address); ++ val->where = VAL_LOC_NODATA; ++ } ++} ++ ++void ++value_destroy(struct value *val) ++{ ++ if (val == NULL) ++ return; ++ value_release(val); ++ value_set_type(val, NULL, 0); ++} ++ ++unsigned char * ++value_reserve(struct value *valp, size_t size) ++{ ++ if (size <= sizeof(valp->u.value)) { ++ valp->where = VAL_LOC_WORD; ++ valp->u.value = 0; ++ } else { ++ valp->where = VAL_LOC_COPY; ++ valp->u.address = calloc(size, 1); ++ if (valp->u.address == 0) ++ return NULL; ++ } ++ return value_get_raw_data(valp); ++} ++ ++int ++value_reify(struct value *val, struct value_dict *arguments) ++{ ++ if (val->where != VAL_LOC_INFERIOR) ++ return 0; ++ assert(val->inferior != NULL); ++ ++ size_t size = value_size(val, arguments); ++ if (size == (size_t)-1) ++ return -1; ++ ++ void *data; ++ enum value_location_t nloc; ++ if (size <= sizeof(val->u.value)) { ++ data = &val->u.value; ++ nloc = VAL_LOC_WORD; ++ } else { ++ data = malloc(size); ++ if (data == NULL) ++ return -1; ++ nloc = VAL_LOC_COPY; ++ } ++ ++ if (umovebytes(val->inferior, val->u.address, data, size) < size) { ++ if (nloc == VAL_LOC_COPY) ++ free(data); ++ return -1; ++ } ++ ++ val->where = nloc; ++ if (nloc == VAL_LOC_COPY) ++ val->u.address = data; ++ ++ return 0; ++} ++ ++unsigned char * ++value_get_data(struct value *val, struct value_dict *arguments) ++{ ++ if (value_reify(val, arguments) < 0) ++ return NULL; ++ return value_get_raw_data(val); ++} ++ ++unsigned char * ++value_get_raw_data(struct value *val) ++{ ++ switch (val->where) { ++ case VAL_LOC_INFERIOR: ++ abort(); ++ case VAL_LOC_NODATA: ++ return NULL; ++ case VAL_LOC_COPY: ++ case VAL_LOC_SHARED: ++ return val->u.address; ++ case VAL_LOC_WORD: ++ return val->u.buf; ++ } ++ ++ assert(!"Unexpected value of val->where"); ++ abort(); ++} ++ ++int ++value_clone(struct value *retp, struct value *val) ++{ ++ *retp = *val; ++ if (val->where == VAL_LOC_COPY) { ++ assert(val->inferior != NULL); ++ size_t size = type_sizeof(val->inferior, val->type); ++ if (size == (size_t)-1) ++ return -1; ++ ++ retp->u.address = malloc(size); ++ if (retp->u.address == NULL) ++ return -1; ++ ++ memcpy(retp->u.address, val->u.address, size); ++ } ++ ++ return 0; ++} ++ ++size_t ++value_size(struct value *val, struct value_dict *arguments) ++{ ++ if (val->size != (size_t)-1) ++ return val->size; ++ ++ if (val->type->type != ARGTYPE_ARRAY) ++ return val->size = type_sizeof(val->inferior, val->type); ++ ++ struct value length; ++ if (expr_eval(val->type->u.array_info.length, val, ++ arguments, &length) < 0) ++ return (size_t)-1; ++ ++ size_t l; ++ int o = value_extract_word(&length, (long *)&l, arguments); ++ value_destroy(&length); ++ ++ if (o < 0) ++ return (size_t)-1; ++ ++ size_t elt_size = type_sizeof(val->inferior, ++ val->type->u.array_info.elt_type); ++ if (elt_size == (size_t)-1) ++ return (size_t)-1; ++ ++ return val->size = elt_size * l; ++} ++ ++int ++value_init_element(struct value *ret_val, struct value *val, size_t element) ++{ ++ size_t off = type_offsetof(val->inferior, val->type, element); ++ if (off == (size_t)-1) ++ return -1; ++ ++ struct arg_type_info *e_info = type_element(val->type, element); ++ if (e_info == NULL) ++ return -1; ++ ++ value_common_init(ret_val, val->inferior, val, e_info, 0); ++ ++ switch (val->where) { ++ case VAL_LOC_COPY: ++ case VAL_LOC_SHARED: ++ ret_val->u.address = val->u.address + off; ++ ret_val->where = VAL_LOC_SHARED; ++ return 0; ++ ++ case VAL_LOC_WORD: ++ ret_val->u.address = value_get_raw_data(val) + off; ++ ret_val->where = VAL_LOC_SHARED; ++ return 0; ++ ++ case VAL_LOC_INFERIOR: ++ ret_val->u.address = val->u.address + off; ++ ret_val->where = VAL_LOC_INFERIOR; ++ return 0; ++ ++ case VAL_LOC_NODATA: ++ assert(!"Can't offset NODATA."); ++ abort(); ++ } ++ abort(); ++} ++ ++int ++value_init_deref(struct value *ret_val, struct value *valp) ++{ ++ assert(valp->type->type == ARGTYPE_POINTER); ++ ++ /* Note: extracting a pointer value should not need value_dict ++ * with function arguments. */ ++ long l; ++ if (value_extract_word(valp, &l, NULL) < 0) ++ return -1; ++ ++ /* We need "long" to be long enough to hold platform ++ * pointers. */ ++ typedef char assert__long_enough_long[-(sizeof(l) < sizeof(void *))]; ++ ++ value_common_init(ret_val, valp->inferior, valp, ++ valp->type->u.ptr_info.info, 0); ++ ret_val->u.value = l; /* Set the address. */ ++ ret_val->where = VAL_LOC_INFERIOR; ++ return 0; ++} ++ ++/* The functions value_extract_buf and value_extract_word assume that ++ * data in VALUE is stored at the start of the internal buffer. For ++ * value_extract_buf in particular there's no other reasonable ++ * default. If we need to copy out four bytes, they need to be the ++ * bytes pointed to by the buffer pointer. ++ * ++ * But actually the situation is similar for value_extract_word as ++ * well. This function is used e.g. to extract characters from ++ * strings. Those weren't stored by value_set_word, they might still ++ * be in client for all we know. So value_extract_word has to assume ++ * that the whole of data is data is stored at the buffer pointer. ++ * ++ * This is a problem on big endian machines, where 2-byte quantity ++ * carried in 4- or 8-byte long is stored at the end of that long. ++ * (Though that quantity itself is still big endian.) So we need to ++ * make a little dance to shift the value to the right part of the ++ * buffer. */ ++ ++union word_data { ++ uint8_t u8; ++ uint16_t u16; ++ uint32_t u32; ++ uint64_t u64; ++ long l; ++ unsigned char buf[0]; ++} u; ++ ++void ++value_set_word(struct value *value, long word) ++{ ++ size_t sz = type_sizeof(value->inferior, value->type); ++ assert(sz != (size_t)-1); ++ assert(sz <= sizeof(value->u.value)); ++ ++ value->where = VAL_LOC_WORD; ++ ++ union word_data u = {}; ++ ++ switch (sz) { ++ case 0: ++ u.l = 0; ++ break; ++ case 1: ++ u.u8 = word; ++ break; ++ case 2: ++ u.u16 = word; ++ break; ++ case 4: ++ u.u32 = word; ++ break; ++ case 8: ++ u.u64 = word; ++ break; ++ default: ++ assert(sz != sz); ++ abort(); ++ } ++ ++ value->u.value = u.l; ++} ++ ++static int ++value_extract_buf_sz(struct value *value, unsigned char *tgt, size_t sz, ++ struct value_dict *arguments) ++{ ++ unsigned char *data = value_get_data(value, arguments); ++ if (data == NULL) ++ return -1; ++ ++ memcpy(tgt, data, sz); ++ return 0; ++} ++ ++int ++value_extract_word(struct value *value, long *retp, ++ struct value_dict *arguments) ++{ ++ size_t sz = type_sizeof(value->inferior, value->type); ++ if (sz == (size_t)-1) ++ return -1; ++ assert(sz <= sizeof(value->u.value)); ++ ++ if (sz == 0) { ++ *retp = 0; ++ return 0; ++ } ++ ++ union word_data u = {}; ++ if (value_extract_buf_sz(value, u.buf, sz, arguments) < 0) ++ return -1; ++ ++ switch (sz) { ++ case 1: ++ *retp = (long)u.u8; ++ return 0; ++ case 2: ++ *retp = (long)u.u16; ++ return 0; ++ case 4: ++ *retp = (long)u.u32; ++ return 0; ++ case 8: ++ *retp = (long)u.u64; ++ return 0; ++ default: ++ assert(sz != sz); ++ abort(); ++ } ++} ++ ++int ++value_extract_buf(struct value *value, unsigned char *tgt, ++ struct value_dict *arguments) ++{ ++ size_t sz = type_sizeof(value->inferior, value->type); ++ if (sz == (size_t)-1) ++ return -1; ++ ++ return value_extract_buf_sz(value, tgt, sz, arguments); ++} ++ ++struct value * ++value_get_parental_struct(struct value *val) ++{ ++ struct value *parent; ++ for (parent = val->parent; parent != NULL; parent = parent->parent) ++ if (parent->type->type == ARGTYPE_STRUCT) ++ return parent; ++ return NULL; ++} ++ ++int ++value_is_zero(struct value *val, struct value_dict *arguments) ++{ ++ unsigned char *data = value_get_data(val, arguments); ++ if (data == NULL) ++ return -1; ++ size_t sz = type_sizeof(val->inferior, val->type); ++ if (sz == (size_t)-1) ++ return -1; ++ ++ int zero = 1; ++ size_t j; ++ for (j = 0; j < sz; ++j) { ++ if (data[j] != 0) { ++ zero = 0; ++ break; ++ } ++ } ++ return zero; ++} ++ ++int ++value_pass_by_reference(struct value *value) ++{ ++ assert(value != NULL); ++ assert(value->type->type == ARGTYPE_STRUCT); ++ ++ struct arg_type_info *new_info = calloc(sizeof(*new_info), 1); ++ if (new_info == NULL) ++ return -1; ++ ++ int own; ++ struct arg_type_info *orig; ++ value_take_type(value, &orig, &own); ++ type_init_pointer(new_info, orig, own); ++ new_info->lens = orig->lens; ++ value_set_type(value, new_info, 1); ++ ++ return 0; ++} +diff --git a/value.h b/value.h +new file mode 100644 +index 0000000..1ba8a6c +--- /dev/null ++++ b/value.h +@@ -0,0 +1,155 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef VALUE_H ++#define VALUE_H ++ ++#include "forward.h" ++ ++/* Values are objects that capture data fetched from an inferior. ++ * Typically a value is attached to a single inferior where it was ++ * extracted from, but it is possible to create a detached value. ++ * Each value is typed. Values support a number of routines, such as ++ * dereferencing if the value is of pointer type, array or structure ++ * access, etc. ++ * ++ * A value can be uninitialized, abstract or reified. Abstract values ++ * are just references into inferior, no transfer has taken place yet. ++ * Reified values have been copied out of the corresponding inferior, ++ * or otherwise set to some value. */ ++ ++enum value_location_t { ++ VAL_LOC_NODATA = 0, /* Uninitialized. */ ++ VAL_LOC_INFERIOR, /* Value is in the inferior process. */ ++ VAL_LOC_COPY, /* Value was copied out of the inferior. */ ++ VAL_LOC_SHARED, /* Like VAL_LOC_COPY, but don't free. */ ++ VAL_LOC_WORD, /* Like VAL_LOC_COPY, but small enough. */ ++}; ++ ++struct value { ++ struct arg_type_info *type; ++ struct Process *inferior; ++ struct value *parent; ++ size_t size; ++ union { ++ void *address; /* VAL_LOC_CLIENT, VAL_LOC_COPY, ++ VAL_LOC_SHARED */ ++ long value; /* VAL_LOC_WORD */ ++ unsigned char buf[0]; ++ } u; ++ enum value_location_t where; ++ int own_type; ++}; ++ ++/* Initialize VALUE. INFERIOR must not be NULL. PARENT is parental ++ * value, in case of compound types. It may be NULL. TYPE is a type ++ * of the value. It may be NULL if the type is not yet known. If ++ * OWN_TYPE, the passed-in type is owned and released by value. */ ++void value_init(struct value *value, struct Process *inferior, ++ struct value *parent, struct arg_type_info *type, ++ int own_type); ++ ++/* Initialize VALUE. This is like value_init, except that inferior is ++ * NULL. VALP is initialized as a detached value, without assigned ++ * process. You have to be careful not to use VAL_LOC_INFERIOR ++ * values if the value is detached. */ ++void value_init_detached(struct value *value, struct value *parent, ++ struct arg_type_info *type, int own_type); ++ ++/* Set TYPE. This releases old type if it was owned. TYPE is owned ++ * and released if OWN_TYPE. */ ++void value_set_type(struct value *value, ++ struct arg_type_info *type, int own_type); ++ ++/* Transfers the ownership of VALUE's type, if any, to the caller. ++ * This doesn't reset the VALUE's type, but gives up ownership if ++ * there was one. Previous ownership is passed in OWN_TYPE. */ ++void value_take_type(struct value *value, ++ struct arg_type_info **type, int *own_type); ++ ++/* Release the data held by VALP, if any, but not the type. */ ++void value_release(struct value *valp); ++ ++/* Destroy the value. This is like value_release, but it additionally ++ * frees the value type, if it's own_type. It doesn't free the VAL ++ * pointer itself. */ ++void value_destroy(struct value *val); ++ ++/* Set the data held by VALP to VALUE. This also sets the value's ++ * where to VAL_LOC_WORD. */ ++void value_set_word(struct value *valp, long value); ++ ++/* Set the data held by VALP to a buffer of size SIZE. This buffer ++ * may be allocated by malloc. Returns NULL on failure. */ ++unsigned char *value_reserve(struct value *valp, size_t size); ++ ++/* Access ELEMENT-th field of the compound value VALP, and store the ++ * result into the value RET_VAL. Returns 0 on success, or negative ++ * value on failure. */ ++int value_init_element(struct value *ret_val, struct value *valp, size_t element); ++ ++/* De-reference pointer value, and store the result into the value ++ * RET_VAL. Returns 0 on success, or negative value on failure. */ ++int value_init_deref(struct value *ret_val, struct value *valp); ++ ++/* If value is in inferior, copy it over to ltrace. Return 0 for ++ * success or negative value for failure. */ ++int value_reify(struct value *val, struct value_dict *arguments); ++ ++/* Return a pointer to the data of the value. This copies the data ++ * from the inferior to the tracer. Returns NULL on failure. */ ++unsigned char *value_get_data(struct value *val, struct value_dict *arguments); ++ ++/* Return a pointer to the raw data of the value. This shall not be ++ * called on a VAL_LOC_INFERIOR value. */ ++unsigned char *value_get_raw_data(struct value *val); ++ ++/* Copy value VAL into the area pointed-to by RETP. Return 0 on ++ * success or a negative value on failure. */ ++int value_clone(struct value *retp, struct value *val); ++ ++/* Give a size of given value. Return (size_t)-1 for error. This is ++ * a full size of the value. In particular for arrays, it returns ++ * actual length of the array, as computed by the length ++ * expression. */ ++size_t value_size(struct value *val, struct value_dict *arguments); ++ ++/* Extract at most word-sized datum from the value. Return 0 on ++ * success or negative value on failure. */ ++int value_extract_word(struct value *val, long *retp, ++ struct value_dict *arguments); ++ ++/* Copy contents of VAL to DATA. The buffer must be large enough to ++ * hold all the data inside. */ ++int value_extract_buf(struct value *val, unsigned char *data, ++ struct value_dict *arguments); ++ ++/* Find the most enclosing parental value that is a struct. Return ++ * NULL when there is no such parental value. */ ++struct value *value_get_parental_struct(struct value *val); ++ ++/* Determine whether this is all-zero value. Returns >0 if it is, ==0 ++ * if it isn't, <0 on error. */ ++int value_is_zero(struct value *val, struct value_dict *arguments); ++ ++/* Convert a structure type to pointer to that structure type. */ ++int value_pass_by_reference(struct value *value); ++ ++#endif /* VALUE_H */ +diff --git a/value_dict.c b/value_dict.c +new file mode 100644 +index 0000000..3f3880a +--- /dev/null ++++ b/value_dict.c +@@ -0,0 +1,147 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++ ++#include "value_dict.h" ++#include "value.h" ++ ++struct named_value ++{ ++ const char *name; ++ struct value value; ++ int own_name; ++}; ++ ++void ++val_dict_init(struct value_dict *dict) ++{ ++ VECT_INIT(&dict->numbered, struct value); ++ VECT_INIT(&dict->named, struct named_value); ++} ++ ++static int ++value_clone_cb(struct value *tgt, struct value *src, void *data) ++{ ++ return value_clone(tgt, src); ++} ++ ++static void ++value_dtor(struct value *val, void *data) ++{ ++ value_destroy(val); ++} ++ ++static int ++named_value_clone(struct named_value *tgt, struct named_value *src, void *data) ++{ ++ tgt->name = strdup(src->name); ++ if (tgt->name == NULL) ++ return -1; ++ tgt->own_name = 1; ++ if (value_clone(&tgt->value, &src->value) < 0) { ++ free((char *)tgt->name); ++ return -1; ++ } ++ return 0; ++} ++ ++static void ++named_value_dtor(struct named_value *named, void *data) ++{ ++ if (named->own_name) ++ free((char *)named->name); ++ value_destroy(&named->value); ++} ++ ++int ++val_dict_clone(struct value_dict *target, struct value_dict *source) ++{ ++ if (VECT_CLONE(&target->numbered, &source->numbered, struct value, ++ value_clone_cb, value_dtor, NULL) < 0) ++ return -1; ++ ++ if (VECT_CLONE(&target->named, &source->named, struct named_value, ++ named_value_clone, named_value_dtor, NULL) < 0) { ++ VECT_DESTROY(&target->numbered, struct value, value_dtor, NULL); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++int ++val_dict_push_next(struct value_dict *dict, struct value *val) ++{ ++ return VECT_PUSHBACK(&dict->numbered, val); ++} ++ ++int ++val_dict_push_named(struct value_dict *dict, struct value *val, ++ const char *name, int own_name) ++{ ++ if (own_name && (name = strdup(name)) == NULL) ++ return -1; ++ struct named_value element = { name, *val, own_name }; ++ if (VECT_PUSHBACK(&dict->named, &element) < 0) { ++ if (own_name) ++ free((char *)name); ++ return -1; ++ } ++ return 0; ++} ++ ++size_t ++val_dict_count(struct value_dict *dict) ++{ ++ return vect_size(&dict->numbered); ++} ++ ++struct value * ++val_dict_get_num(struct value_dict *dict, size_t num) ++{ ++ assert(num < vect_size(&dict->numbered)); ++ return VECT_ELEMENT(&dict->numbered, struct value, num); ++} ++ ++struct value * ++val_dict_get_name(struct value_dict *dict, const char *name) ++{ ++ size_t i; ++ for (i = 0; i < vect_size(&dict->named); ++i) { ++ struct named_value *element ++ = VECT_ELEMENT(&dict->named, struct named_value, i); ++ if (strcmp(element->name, name) == 0) ++ return &element->value; ++ } ++ return NULL; ++} ++ ++void ++val_dict_destroy(struct value_dict *dict) ++{ ++ if (dict == NULL) ++ return; ++ ++ VECT_DESTROY(&dict->numbered, struct value, value_dtor, NULL); ++ VECT_DESTROY(&dict->named, struct named_value, named_value_dtor, NULL); ++} +diff --git a/value_dict.h b/value_dict.h +new file mode 100644 +index 0000000..f75ee37 +--- /dev/null ++++ b/value_dict.h +@@ -0,0 +1,67 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef ARGS_H ++#define ARGS_H ++ ++#include "forward.h" ++#include "vect.h" ++ ++/* Value dictionary is used to store actual function arguments. It ++ * supports both numbered and named arguments. */ ++struct value_dict ++{ ++ struct vect numbered; ++ struct vect named; ++}; ++ ++/* Initialize DICT. */ ++void val_dict_init(struct value_dict *dict); ++ ++/* Clone SOURCE into TARGET. Return 0 on success or a negative value ++ * on failure. */ ++int val_dict_clone(struct value_dict *target, struct value_dict *source); ++ ++/* Push next numbered value, VAL. The value is copied over and the ++ * dictionary becomes its owner, and is responsible for destroying it ++ * later. Returns 0 on success and a negative value on failure. */ ++int val_dict_push_next(struct value_dict *dict, struct value *val); ++ ++/* Return count of numbered arguments. */ ++size_t val_dict_count(struct value_dict *dict); ++ ++/* Push value VAL named NAME. See notes at val_dict_push_next about ++ * value ownership. The name is owned and freed if OWN_NAME is ++ * non-zero. */ ++int val_dict_push_named(struct value_dict *dict, struct value *val, ++ const char *name, int own_name); ++ ++/* Get NUM-th numbered argument, or NULL if there's not that much ++ * arguments. */ ++struct value *val_dict_get_num(struct value_dict *dict, size_t num); ++ ++/* Get argument named NAME, or NULL if there's no such argument. */ ++struct value *val_dict_get_name(struct value_dict *dict, const char *name); ++ ++/* Destroy the dictionary and all the values in it. Note that DICT ++ * itself (the pointer) is not freed. */ ++void val_dict_destroy(struct value_dict *dict); ++ ++#endif /* ARGS_H */ +diff --git a/vect.c b/vect.c +new file mode 100644 +index 0000000..f2e58b2 +--- /dev/null ++++ b/vect.c +@@ -0,0 +1,136 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include "vect.h" ++ ++static void * ++slot(struct vect *vec, size_t i) ++{ ++ return ((char *)vec->data) + vec->elt_size * i; ++} ++ ++void ++vect_init(struct vect *vec, size_t elt_size) ++{ ++ *vec = (struct vect){ NULL, 0, 0, elt_size }; ++} ++ ++static int ++copy_elt(void *tgt, void *src, void *data) ++{ ++ struct vect *target = data; ++ memcpy(tgt, src, target->elt_size); ++ return 0; ++} ++ ++int ++vect_clone(struct vect *target, struct vect *source, ++ int (*clone)(void *tgt, void *src, void *data), ++ void (*dtor)(void *elt, void *data), ++ void *data) ++{ ++ vect_init(target, source->elt_size); ++ if (vect_reserve(target, source->size) < 0) ++ return -1; ++ ++ if (clone == NULL) { ++ assert(dtor == NULL); ++ clone = copy_elt; ++ data = target; ++ } else { ++ assert(dtor != NULL); ++ } ++ ++ size_t i; ++ for (i = 0; i < source->size; ++i) ++ if (clone(slot(target, i), slot(source, i), data) < 0) ++ goto fail; ++ ++ target->size = source->size; ++ return 0; ++ ++fail: ++ /* N.B. destroy the elements in opposite order. */ ++ if (dtor != NULL) ++ while (i-- != 0) ++ dtor(slot(target, i), data); ++ vect_destroy(target, NULL, NULL); ++ return -1; ++} ++ ++int ++vect_reserve(struct vect *vec, size_t count) ++{ ++ if (count > vec->allocated) { ++ size_t na = vec->allocated != 0 ? 2 * vec->allocated : 4; ++ void *n = realloc(vec->data, na * vec->elt_size); ++ if (n == NULL) ++ return -1; ++ vec->data = n; ++ vec->allocated = na; ++ } ++ assert(count <= vec->allocated); ++ return 0; ++} ++ ++size_t ++vect_size(struct vect *vec) ++{ ++ return vec->size; ++} ++ ++int ++vect_empty(struct vect *vec) ++{ ++ return vec->size == 0; ++} ++ ++int ++vect_reserve_additional(struct vect *vec, size_t count) ++{ ++ return vect_reserve(vec, vect_size(vec) + count); ++} ++ ++int ++vect_pushback(struct vect *vec, void *eltp) ++{ ++ if (vect_reserve_additional(vec, 1) < 0) ++ return -1; ++ memcpy(slot(vec, vec->size++), eltp, vec->elt_size); ++ return 0; ++} ++ ++void ++vect_destroy(struct vect *vec, void (*dtor)(void *emt, void *data), void *data) ++{ ++ if (vec == NULL) ++ return; ++ ++ if (dtor != NULL) { ++ size_t i; ++ size_t sz = vect_size(vec); ++ for (i = 0; i < sz; ++i) ++ dtor(slot(vec, i), data); ++ } ++ free(vec->data); ++} +diff --git a/vect.h b/vect.h +new file mode 100644 +index 0000000..50401bb +--- /dev/null ++++ b/vect.h +@@ -0,0 +1,125 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef VECT_H ++#define VECT_H ++ ++#include ++ ++/* Vector is an array that can grow as needed to accommodate the data ++ * that it needs to hold. ELT_SIZE is also used as an elementary ++ * sanity check, because the array itself is not typed. */ ++ ++struct vect ++{ ++ void *data; ++ size_t size; /* In elements. */ ++ size_t allocated; /* In elements. */ ++ size_t elt_size; /* In bytes. */ ++}; ++ ++/* Initialize VEC, which will hold elements of size ELT_SIZE. */ ++void vect_init(struct vect *vec, size_t elt_size); ++ ++/* Initialize VECP, which will hold elements of type ELT_TYPE. */ ++#define VECT_INIT(VECP, ELT_TYPE) \ ++ (vect_init(VECP, sizeof(ELT_TYPE))) ++ ++/* Initialize TARGET by copying over contents of vector SOURCE. If ++ * CLONE is non-NULL, it's evoked on each element, and should clone ++ * SRC into TGT. It should return 0 on success or negative value on ++ * failure. DATA is passed to CLONE verbatim. This function returns ++ * 0 on success or negative value on failure. In case of failure, if ++ * DTOR is non-NULL, it is invoked on all hitherto created elements ++ * with the same DATA. If one of CLONE, DTOR is non-NULL, then both ++ * have to be. */ ++int vect_clone(struct vect *target, struct vect *source, ++ int (*clone)(void *tgt, void *src, void *data), ++ void (*dtor)(void *elt, void *data), ++ void *data); ++ ++/* Destroy VEC, which holds elements of type ELT_TYPE, using DTOR. */ ++#define VECT_CLONE(TGT_VEC, SRC_VEC, ELT_TYPE, CLONE, DTOR, DATA) \ ++ /* xxx GCC-ism necessary to get in the safety latches. */ \ ++ ({ \ ++ struct vect *_source_vec = (SRC_VEC); \ ++ assert(_source_vec->elt_size == sizeof(ELT_TYPE)); \ ++ /* Check that callbacks are typed properly. */ \ ++ void (*_dtor_callback)(ELT_TYPE *, void *) = DTOR; \ ++ int (*_clone_callback)(ELT_TYPE *, \ ++ ELT_TYPE *, void *) = CLONE; \ ++ vect_clone((TGT_VEC), _source_vec, \ ++ (int (*)(void *, void *, void *))_clone_callback, \ ++ (void (*)(void *, void *))_dtor_callback, \ ++ DATA); \ ++ }) ++ ++/* Return number of elements in VEC. */ ++size_t vect_size(struct vect *vec); ++ ++/* Emptiness predicate. */ ++int vect_empty(struct vect *vec); ++ ++/* Accessor. Fetch ELT_NUM-th argument of type ELT_TYPE from the ++ * vector referenced by VECP. */ ++#define VECT_ELEMENT(VECP, ELT_TYPE, ELT_NUM) \ ++ (assert((VECP)->elt_size == sizeof(ELT_TYPE)), \ ++ assert((ELT_NUM) < (VECP)->size), \ ++ ((ELT_TYPE *)(VECP)->data) + (ELT_NUM)) ++ ++#define VECT_BACK(VECP, ELT_TYPE) \ ++ VECT_ELEMENT(VECP, ELT_TYPE, (VECP)->size) ++ ++/* Copy element referenced by ELTP to the end of VEC. The object ++ * referenced by ELTP is now owned by VECT. Returns 0 if the ++ * operation was successful, or negative value on error. */ ++int vect_pushback(struct vect *vec, void *eltp); ++ ++/* Copy element referenced by ELTP to the end of VEC. See ++ * vect_pushback for details. In addition, make a check whether VECP ++ * holds elements of the right size. */ ++#define VECT_PUSHBACK(VECP, ELTP) \ ++ (assert((VECP)->elt_size == sizeof(*(ELTP))), \ ++ vect_pushback((VECP), (ELTP))) ++ ++/* Make sure that VEC can hold at least COUNT elements. Return 0 on ++ * success, negative value on failure. */ ++int vect_reserve(struct vect *vec, size_t count); ++ ++/* Make sure that VEC can accommodate COUNT additional elements. */ ++int vect_reserve_additional(struct vect *vec, size_t count); ++ ++/* Destroy VEC. If DTOR is non-NULL, then it's called on each element ++ * of the vector. DATA is passed to DTOR verbatim. The memory ++ * pointed-to by VEC is not freed. */ ++void vect_destroy(struct vect *vec, ++ void (*dtor)(void *emt, void *data), void *data); ++ ++/* Destroy VEC, which holds elements of type ELT_TYPE, using DTOR. */ ++#define VECT_DESTROY(VECP, ELT_TYPE, DTOR, DATA) \ ++ do { \ ++ assert((VECP)->elt_size == sizeof(ELT_TYPE)); \ ++ /* Check that DTOR is typed properly. */ \ ++ void (*_dtor_callback)(ELT_TYPE *, void *) = DTOR; \ ++ vect_destroy((VECP), (void (*)(void *, void *))_dtor_callback, \ ++ DATA); \ ++ } while (0) ++ ++#endif /* VECT_H */ +diff --git a/zero.c b/zero.c +new file mode 100644 +index 0000000..bc119ee +--- /dev/null ++++ b/zero.c +@@ -0,0 +1,105 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++ ++#include "zero.h" ++#include "common.h" ++#include "type.h" ++#include "value.h" ++#include "expr.h" ++ ++static int ++zero_callback_max(struct value *ret_value, struct value *lhs, ++ struct value_dict *arguments, ++ size_t max, void *data) ++{ ++ size_t i; ++ for (i = 0; i < max; ++i) { ++ struct value element; ++ if (value_init_element(&element, lhs, i) < 0) ++ return -1; ++ ++ int zero = value_is_zero(&element, arguments); ++ ++ value_destroy(&element); ++ ++ if (zero) ++ break; ++ } ++ ++ struct arg_type_info *long_type = type_get_simple(ARGTYPE_LONG); ++ value_init_detached(ret_value, NULL, long_type, 0); ++ value_set_word(ret_value, i); ++ return 0; ++} ++ ++/* LHS->zero(RHS). Looks for a length of zero-terminated array, but ++ * looks no further than first RHS bytes. */ ++static int ++zero_callback(struct value *ret_value, struct value *lhs, ++ struct value *rhs, struct value_dict *arguments, void *data) ++{ ++ long l; ++ if (value_extract_word(rhs, &l, arguments) < 0) ++ return -1; ++ if (l < 0) ++ /* It might just be a positive value >2GB, but that's ++ * not likely. */ ++ report_global_error("maximum array length seems negative"); ++ size_t max = (size_t)l; ++ return zero_callback_max(ret_value, lhs, arguments, max, data); ++} ++ ++/* LHS->zero. Looks for a length of zero-terminated array, without ++ * limit. */ ++static int ++zero1_callback(struct value *ret_value, struct value *lhs, ++ struct value_dict *arguments, void *data) ++{ ++ return zero_callback_max(ret_value, lhs, arguments, (size_t)-1, data); ++} ++ ++struct expr_node * ++build_zero_w_arg(struct expr_node *expr, int own) ++{ ++ struct expr_node *e_z = malloc(sizeof(*e_z)); ++ if (e_z == NULL) ++ return NULL; ++ ++ expr_init_cb2(e_z, &zero_callback, ++ expr_self(), 0, expr, own, NULL); ++ return e_z; ++} ++ ++struct expr_node * ++expr_node_zero(void) ++{ ++ static struct expr_node *node = NULL; ++ if (node == NULL) { ++ node = malloc(sizeof(*node)); ++ if (node == NULL) ++ error(1, errno, "malloc expr_node_zero"); ++ expr_init_cb1(node, &zero1_callback, ++ expr_self(), 0, (void *)-1); ++ } ++ return node; ++} +diff --git a/zero.h b/zero.h +new file mode 100644 +index 0000000..45d2771 +--- /dev/null ++++ b/zero.h +@@ -0,0 +1,34 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef ZERO_H ++#define ZERO_H ++ ++#include "forward.h" ++ ++/* This returns a pre-built "zero" node without argument. Share, but ++ don't free. */ ++struct expr_node *expr_node_zero(void); ++ ++/* This builds a new "zero" node with EXPR as argument. EXPR is owned ++ * by the built node if OWN. Returns NULL if something failed. */ ++struct expr_node *build_zero_w_arg(struct expr_node *expr, int own); ++ ++#endif /* ZERO_H */ diff --git a/ltrace.spec b/ltrace.spec index 67fd381..ac08012 100644 --- a/ltrace.spec +++ b/ltrace.spec @@ -1,7 +1,7 @@ Summary: Tracks runtime library calls from dynamically linked executables Name: ltrace Version: 0.6.0 -Release: 13%{?dist} +Release: 14%{?dist} URL: http://ltrace.alioth.debian.org/ License: GPLv2+ Group: Development/Debuggers @@ -33,6 +33,7 @@ Patch17: ltrace-0.6.0-ppc-lwarx.patch Patch18: ltrace-0.6.0-libs.patch Patch19: ltrace-0.6.0-libs-fixes-1.patch Patch20: ltrace-0.6.0-dash-n.patch +Patch21: ltrace-0.6.0-abi.patch %description Ltrace is a debugging program which runs a specified command until the @@ -64,13 +65,13 @@ execution of processes. %patch18 -p1 %patch19 -p1 %patch20 -p1 -sed -i -e 's/-o root -g root//' Makefile.in +%patch21 -p1 %build # This ugly hack is necessary to build and link files for correct # architecture. It makes a difference on ppc. export CC="gcc`echo $RPM_OPT_FLAGS | sed -n 's/^.*\(-m[36][124]\).*$/ \1/p'` -D_LARGEFILE64_SOURCE" -# We touch configure.ac up there +autoreconf -i %configure CC="$CC" make %{?_smp_mflags} @@ -92,6 +93,10 @@ echo ====================TESTING END===================== %config(noreplace) %{_sysconfdir}/ltrace.conf %changelog +* Fri May 18 2012 Petr Machata - 0.6.0-14 +- Add upstream patch that improves parameter passing support (the + upstream "revamp" branch) (ltrace-0.6.0-abi.patch) + * Thu May 3 2012 Petr Machata - 0.6.0-13 - Check -n argument for validity (ltrace-0.6.0-dash-n.patch) - Resolves: #818529