diff --git a/Makefile.am b/Makefile.am index 6c299d8..ae58f42 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,6 +22,7 @@ libltrace_la_SOURCES = \ libltrace.c \ options.c \ output.c \ + mock.c \ proc.c \ read_config_file.c \ summary.c @@ -61,6 +62,8 @@ noinst_HEADERS = \ dist_man1_MANS = \ ltrace.1 +docdir = ${datadir}/doc/${PACKAGE} + dist_doc_DATA = \ COPYING \ README \ diff --git a/breakpoints.c b/breakpoints.c index 1ea406a..5e2cf03 100644 --- a/breakpoints.c +++ b/breakpoints.c @@ -194,6 +194,11 @@ breakpoints_init(Process *proc) { } tmp2 = tmp2->next; } + + if (opt_q && !keep && (!strcmp((*tmp1)->name, "time") || !strcmp((*tmp1)->name, "gettimeofday") || !strcmp((*tmp1)->name, "clock_gettime"))) { + keep = 1; + } + if (!keep) { *tmp1 = (*tmp1)->next; } else { diff --git a/common.h b/common.h index 9f1d40e..b3da806 100644 --- a/common.h +++ b/common.h @@ -25,6 +25,8 @@ extern char * command; extern int exiting; /* =1 if we have to exit ASAP */ +extern int opt_fake_return; + typedef struct Breakpoint Breakpoint; struct Breakpoint { void * addr; @@ -54,6 +56,8 @@ enum arg_type { ARGTYPE_FORMAT, /* printf-like format */ ARGTYPE_STRING, /* NUL-terminated string */ ARGTYPE_STRING_N, /* String of known maxlen */ + ARGTYPE_BYTES, /* Array of bytes */ + ARGTYPE_BYTES_N, /* Binary data of known maxlen */ ARGTYPE_ARRAY, /* Series of values in memory */ ARGTYPE_ENUM, /* Enumeration */ ARGTYPE_STRUCT, /* Structure of values */ @@ -213,6 +217,7 @@ struct opt_c_struct { #include "options.h" #include "output.h" +#include "mock.h" #ifdef USE_DEMANGLE #include "demangle.h" #endif @@ -226,6 +231,7 @@ extern Process * pid2proc(pid_t pid); extern void handle_event(Event * event); extern void execute_program(Process *, char **); extern int display_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); +extern long get_length(enum tof type, Process *proc, int len_spec, void *st, arg_type_info* st_info); extern Breakpoint * address2bpstruct(Process * proc, void * addr); extern void breakpoints_init(Process * proc); extern void insert_breakpoint(Process * proc, void * addr, struct library_symbol * libsym); @@ -254,6 +260,7 @@ 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 set_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); @@ -267,10 +274,12 @@ extern void continue_after_signal(pid_t pid, int signum); extern void continue_after_breakpoint(Process * proc, Breakpoint * sbp); extern void continue_enabling_breakpoint(pid_t pid, Breakpoint * sbp); extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); +extern void set_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info, long value); 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 size_t uunmovebytes (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(Process *, struct ltelf *); diff --git a/configure.ac b/configure.ac index 4bf7a85..c65f6ba 100644 --- a/configure.ac +++ b/configure.ac @@ -1,3 +1,5 @@ +m4_include([m4/as-python.m4]) + # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) @@ -65,6 +67,14 @@ AC_CHECK_LIB([elf], [elf_begin],, CPPFLAGS="${saved_CPPFLAGS}" LDFLAGS="${saved_LDFLAGS}" +#Python +AS_PATH_PYTHON([2.6.0]) +AM_CHECK_PYTHON_HEADERS([AM_CPPFLAGS="$AM_CPPFLAGS $PYTHON_INCLUDES"],[AC_MSG_NOTICE([could not find Python headers])]) +AM_CHECK_PYTHON_LIBS( + [ + AC_DEFINE([HAVE_PYTHON], [1], [we have python]) + AM_LDFLAGS="${AM_LDFLAGS} $PYTHON_LIBS" + ],[AC_MSG_NOTICE([could not find Python libs])]) # HAVE_LIBIBERTY AC_CHECK_LIB([iberty], [cplus_demangle], [ @@ -109,7 +119,7 @@ case "${enable_libunwind}" in enable_libunwind=no else AC_MSG_RESULT([$enable_libunwind]) - AC_MSG_ERROR([libunwind.h or libunwind-ptrace.h cannot be found]) + AC_MSG_ERROR([libunwind.h or libunwind-ptrace.h cannot be found]) fi ;; (*) ;; @@ -244,6 +254,8 @@ if test x$enable_werror = xyes; then AM_CFLAGS="${AM_CFLAGS} -Werror" fi +AM_LDFLAGS="${AM_LDFLAGS} -lrt" + AC_SUBST(AM_CPPFLAGS) AC_SUBST(AM_CFLAGS) AC_SUBST(AM_LDFLAGS) diff --git a/display_args.c b/display_args.c index c639c88..a5ece06 100644 --- a/display_args.c +++ b/display_args.c @@ -18,7 +18,7 @@ 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 +long get_length(enum tof type, Process *proc, int len_spec, void *st, arg_type_info* st_info) { long len; @@ -216,9 +216,11 @@ display_value(enum tof type, Process *proc, fprintf(stderr, "Should never encounter a format anywhere but at the top level (for now?)\n"); exit(1); case ARGTYPE_STRING: + case ARGTYPE_BYTES: return display_string(type, proc, (void*) value, string_maxlength); case ARGTYPE_STRING_N: + case ARGTYPE_BYTES_N: return display_string(type, proc, (void*) value, get_length(type, proc, info->u.string_n_info.size_spec, st, st_info)); diff --git a/etc/ltrace.conf b/etc/ltrace.conf index 3caf6b7..a2b7c9c 100644 --- a/etc/ltrace.conf +++ b/etc/ltrace.conf @@ -25,6 +25,12 @@ ; string[retval] == (char *) [show only up to (return val) bytes] ; string[arg0] == (char *) [same as string[retval]] ; string[N] == (char *) [N>0] [show only up to N bytes] +; binary == (char *) +; binary[argN] == (char *) [N>0] [show only up to (arg N) bytes] +; binary[eltN] == (char *) [N>0] [show only up to (elt N) bytes] +; binary[retval] == (char *) [show only up to (return val) bytes] +; binary[arg0] == (char *) [same as string[retval]] +; binary[N] == (char *) [N>0] [show only up to N bytes] ; type* == (type *) [pointer to any other type] ; enum (key=value,key=value,...) [enumeration, see below] ; array(type,argN) @@ -38,6 +44,8 @@ ; Backwards-compatibility: ; string0 == (char *) [same as string[retval]] ; stringN == (char *) [N>0] [same as string[argN]] +; binary0 == (char *) [same as binary[retval]] +; binaryN == (char *) [N>0] [same as binary[argN]] @@ -378,6 +386,7 @@ int tcsetattr(int,int,addr); ; time.h string ctime(addr); int gettimeofday(addr, addr); +int clock_gettime(enum(CLOCK_REALTIME=0,CLOCK_MONOTONIC=1,CLOCK_PROCESS_CPUTIME_ID=2,CLOCK_THREAD_CPUTIME_ID=3,CLOCK_REALTIME_HR=4,CLOCK_MONOTONIC_HR=5),struct(int, long)*); addr gmtime(addr); addr localtime(addr); ulong strftime(+string2,ulong,string,addr); @@ -429,7 +438,7 @@ int truncate(string,ulong); string ttyname(int); int unlink(string); void usleep(uint); -long write(int, string3, ulong); +long write(int, bytes3, ulong); addr sbrk(long); int getpagesize(void); long lseek(int,long,int); diff --git a/handle_event.c b/handle_event.c index 01309ff..aed98c4 100644 --- a/handle_event.c +++ b/handle_event.c @@ -605,6 +605,8 @@ handle_breakpoint(Event *event) { } event->proc->return_addr = event->e_un.brk_addr; if (event->proc->state != STATE_IGNORED) { + mock_return(LT_TOF_FUNCTIONR, event->proc, + event->proc->callstack[i].c_un.libfunc->name); output_right(LT_TOF_FUNCTIONR, event->proc, event->proc->callstack[i].c_un.libfunc->name); } diff --git a/libltrace.c b/libltrace.c index 0f48d11..5cef577 100644 --- a/libltrace.c +++ b/libltrace.c @@ -9,6 +9,10 @@ #include #include +#ifdef HAVE_PYTHON +#include "Python.h" +#endif + #include "common.h" char *command = NULL; @@ -68,12 +72,19 @@ normal_exit(void) { fclose(options.output); options.output = NULL; } +#ifdef HAVE_PYTHON + Py_Finalize(); +#endif } void ltrace_init(int argc, char **argv) { struct opt_p_t *opt_p_tmp; +#ifdef HAVE_PYTHON + Py_Initialize(); +#endif + atexit(normal_exit); signal(SIGINT, signal_exit); /* Detach processes when interrupted */ signal(SIGTERM, signal_exit); /* ... or killed */ @@ -130,7 +141,7 @@ static void dispatch_callbacks(Event * ev) { int i; /* Ignoring case 1: signal into a dying tracer */ - if (ev->type==EVENT_SIGNAL && + if (ev->type==EVENT_SIGNAL && exiting && ev->e_un.signum == SIGSTOP) { return; } diff --git a/m4/as-python.m4 b/m4/as-python.m4 new file mode 100644 index 0000000..6c4e32b --- /dev/null +++ b/m4/as-python.m4 @@ -0,0 +1,238 @@ +dnl as-python.m4 0.1.1 +dnl autostars m4 macro for python checks + +dnl From Andrew Dalke +dnl Updated by James Henstridge +dnl Updated by Andy Wingo to loop through possible pythons +dnl Updated by Thomas Vander Stichele to check for presence of packages/modules + +dnl $Id: as-python.m4,v 1.3 2006/02/28 16:49:31 thomasvs Exp $ + +# AS_PATH_PYTHON([MINIMUM-VERSION]) + +# Adds support for distributing Python modules and packages. To +# install modules, copy them to $(pythondir), using the python_PYTHON +# automake variable. To install a package with the same name as the +# automake package, install to $(pkgpythondir), or use the +# pkgpython_PYTHON automake variable. + +# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as +# locations to install python extension modules (shared libraries). +# Another macro is required to find the appropriate flags to compile +# extension modules. + +# If your package is configured with a different prefix to python, +# users will have to add the install directory to the PYTHONPATH +# environment variable, or create a .pth file (see the python +# documentation for details). + +# If the MINIMUM-VERSION argument is passed, AS_PATH_PYTHON will +# cause an error if the version of python installed on the system +# doesn't meet the requirement. MINIMUM-VERSION should consist of +# numbers and dots only. + +# Updated to loop over all possible python binaries by Andy Wingo +# + +AC_DEFUN([AS_PATH_PYTHON], + [ + dnl Find a version of Python. Only checks for python 2.2 or newer. + + dnl should we do the version check? + ifelse([$1],[], + [AC_PATH_PROG(PYTHON, python python2.5 python2.4 python2.3 python2.2 python2.6 python2.7 python2.8 python2.9 python3.0 python3.1 python3.2)], + [ + AC_MSG_NOTICE(Looking for Python version >= $1) + changequote(<<, >>)dnl + prog=" +import sys +minver = tuple(map(int,'$1'.split('.'))) + ('final', 0) +pyver = sys.version_info +if pyver >= minver: + sys.exit(0) +else: + sys.exit(1)" + changequote([, ])dnl + + python_good=false + for python_candidate in python python3.2 python3.1 python3.0 python2.9 python2.8 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2; do + unset PYTHON + AC_PATH_PROG(PYTHON, $python_candidate) 1> /dev/null 2> /dev/null + + if test "x$PYTHON" = "x"; then continue; fi + + if $PYTHON -c "$prog" 1>&AC_FD_CC 2>&AC_FD_CC; then + AC_MSG_CHECKING(["$PYTHON":]) + AC_MSG_RESULT([okay]) + python_good=true + break; + else + dnl clear the cache val + unset ac_cv_path_PYTHON + fi + done + ]) + + if test "$python_good" != "true"; then + AC_MSG_RESULT([No suitable version of python found]) + else + + AC_MSG_CHECKING([local Python configuration]) + + dnl Need to change quote character because of [:2] + AC_SUBST(PYTHON_VERSION) + changequote(<<, >>)dnl + PYTHON_VERSION=`$PYTHON -c "import sys; print '.'.join(map(str,sys.version_info[:2]))"` + changequote([, ])dnl + + + dnl Use the values of $prefix and $exec_prefix for the corresponding + dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made + dnl distinct variables so they can be overridden if need be. However, + dnl general consensus is that you shouldn't need this ability. + + AC_SUBST(PYTHON_PREFIX) + PYTHON_PREFIX='${prefix}' + + AC_SUBST(PYTHON_EXEC_PREFIX) + PYTHON_EXEC_PREFIX='${exec_prefix}' + + dnl At times (like when building shared libraries) you may want + dnl to know which OS platform Python thinks this is. + + AC_SUBST(PYTHON_PLATFORM) + PYTHON_PLATFORM=`$PYTHON -c "import sys; print sys.platform"` + + + dnl Set up 4 directories: + + dnl pythondir -- where to install python scripts. This is the + dnl site-packages directory, not the python standard library + dnl directory like in previous automake betas. This behaviour + dnl is more consistent with lispdir.m4 for example. + dnl + dnl Also, if the package prefix isn't the same as python's prefix, + dnl then the old $(pythondir) was pretty useless. + + AC_SUBST(pythondir) + pythondir=$PYTHON_PREFIX"/lib/python"$PYTHON_VERSION/site-packages + + dnl pkgpythondir -- $PACKAGE directory under pythondir. Was + dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is + dnl more consistent with the rest of automake. + dnl Maybe this should be put in python.am? + + AC_SUBST(pkgpythondir) + pkgpythondir=\${pythondir}/$PACKAGE + + dnl pyexecdir -- directory for installing python extension modules + dnl (shared libraries) Was PYTHON_SITE_EXEC in previous betas. + + AC_SUBST(pyexecdir) + pyexecdir=$PYTHON_EXEC_PREFIX"/lib/python"$PYTHON_VERSION/site-packages + + dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE) + dnl Maybe this should be put in python.am? + + AC_SUBST(pkgpyexecdir) + pkgpyexecdir=\${pyexecdir}/$PACKAGE + + AC_MSG_RESULT([looks good]) + fi +]) + +dnl AS_PYTHON_IMPORT(PACKAGE/MODULE, [ACTION-IF-FOUND, [ACTION-IF-NOT-FOUND, [PREAMBLE, [POSTAMBLE]]]]) +dnl Try to import the given PACKAGE/MODULE + +AC_DEFUN([AS_PYTHON_IMPORT], +[ + dnl Check if we can import a given module. + dnl Requires AS_PATH_PYTHON to be called before. + dnl PREAMBLE and POSTAMBLE, if given, is code executed right before and after + dnl the import; stdout is rerouted to config.log, and stderr to the console. + + AC_MSG_CHECKING([for python module $1]) + + changequote(<<, >>)dnl + prog=" +import sys + +try: + $4 + import $1 + $5 + sys.exit(0) +except ImportError, e: + raise SystemExit(*e.args) +except SystemExit, e: + if not e.args or (e.args[0] != 0 and not isinstance(e.args[0], str)): + raise SystemExit('Unknown error') + raise +except Exception, e: + print ' Error while trying to import $1:' + print ' %r: %s' % (e, e) + sys.exit(1)" + changequote([, ])dnl + +if $PYTHON -c "$prog" 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_FD +then + AC_MSG_RESULT(found) + ifelse([$2], , :, [$2]) +else + dnl python has already printed a message on the message fd + ifelse([$3], , :, [$3]) +fi +]) + +dnl a macro to check for ability to create python extensions +dnl AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE]) +dnl function also defines PYTHON_INCLUDES +AC_DEFUN([AM_CHECK_PYTHON_HEADERS], + [ + AM_PATH_PYTHON(,,[AC_MSG_NOTICE([could not find python])]) + AC_MSG_CHECKING(for headers required to compile python extensions) + + dnl deduce PYTHON_INCLUDES + py_prefix=`$PYTHON -c "import sys; print sys.prefix"` + py_exec_prefix=`$PYTHON -c "import sys; print sys.exec_prefix"` + PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}" + + if test "$py_prefix" != "$py_exec_prefix"; then + PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}" + fi + AC_SUBST(PYTHON_INCLUDES) + + dnl check if the headers exist: + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES" +AC_TRY_CPP([#include ],dnl +[AC_MSG_RESULT(found) +$1],dnl +[AC_MSG_RESULT(not found) +$2]) +CPPFLAGS="$save_CPPFLAGS" +]) + +dnl a macro to check for ability to link against libpython +dnl AM_CHECK_PYTHON_LIBS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE]) +dnl function also defines PYTHON_LIBS +AC_DEFUN([AM_CHECK_PYTHON_LIBS], +[AC_REQUIRE([AM_CHECK_PYTHON_HEADERS]) +AC_MSG_CHECKING(for libraries required to link against libpython) +dnl deduce PYTHON_LIBS +if test "x$PYTHON_LIBS" == x; then + PYTHON_LIBS="-L${py_prefix}/lib -lpython${PYTHON_VERSION}" +fi +if test "x$PYTHON_LIB_LOC" == x; then + PYTHON_LIB_LOC="${py_prefix}/lib" +fi +AC_SUBST(PYTHON_LIBS) +AC_SUBST(PYTHON_LIB_LOC) +dnl check if libpython exist: +save_LIBS="$LIBS" +LIBS="$LIBS $PYTHON_LIBS" +AC_TRY_LINK_FUNC(Py_Initialize, dnl + [LIBS="$save_LIBS"; AC_MSG_RESULT(found); $1], dnl + [LIBS="$save_LIBS"; AC_MSG_RESULT(not found); $2]) + +]) diff --git a/mock.c b/mock.c new file mode 100644 index 0000000..b78b357 --- /dev/null +++ b/mock.c @@ -0,0 +1,439 @@ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +#include "sys/ptrace.h" +#include "sys/time.h" +#include + +#ifdef HAVE_PYTHON +#include + +static int +set_arg_into_python_tuple(enum tof type, Process *proc, int arg_num, arg_type_info * info, PyObject *pArgs); +static int +set_format_into_python_tuple(enum tof type, Process *proc, int arg_num, PyObject *pArgs); +static PyObject * +convert_value_to_python(enum tof type, Process *proc, + long value, arg_type_info *info, + void *st, arg_type_info* st_info); +static PyObject* +convert_string_to_python(enum tof type, Process *proc, + void* addr, size_t maxlen); +static PyObject* +convert_bytes_to_python(enum tof type, Process *proc, + void* addr, size_t maxlen); + +#endif //HAVE_PYTHON + +void +mock_return(enum tof type, Process *proc, char *function_name) { + int replaced = 0; + static arg_type_info infop = {.type=ARGTYPE_POINTER}, infoi = {.type=ARGTYPE_INT}; + struct opt_m_t *tmp = opt_m; + while (tmp) { + if (strcmp(tmp->name, function_name) == 0) { + if (tmp->value) { + int result = atoi(tmp->value); + set_arg(type, proc, -1, &infoi, result); + set_arch_dep(proc); + ++replaced; + } + break; + } + tmp = tmp->next; + } + +#ifdef HAVE_PYTHON + Function *func = name2func(function_name); + + if (func && !replaced && opt_fake_return) { + PyObject *pModule, *pFunc = NULL; + PyObject *pArgs, *pValue; + + PyObject* sysmodules = PyImport_GetModuleDict(); + pModule = PyMapping_GetItemString(sysmodules, "__main__"); + + if (pModule != NULL) { + char *wrapper_name = malloc(strlen(function_name) + 9); + if (!wrapper_name) { + perror("ltrace: malloc"); + exit(1); + } + sprintf(wrapper_name, "%s_wrapper", function_name); + if (PyObject_HasAttrString(pModule, wrapper_name)) + pFunc = PyObject_GetAttrString(pModule, wrapper_name); + free(wrapper_name); + /* pFunc is a new reference */ + if (pFunc && PyCallable_Check(pFunc)) { + pArgs = PyTuple_New(func->num_params); + int i; + for (i = 0; i < func->num_params; i++) { + i = set_arg_into_python_tuple(type, proc, i, func->arg_info[i], pArgs); + } + + pValue = PyObject_CallObject(pFunc, pArgs); + Py_DECREF(pArgs); + if (pValue != NULL) { + if (pValue != Py_None) { + set_arg(type, proc, -1, func->return_info, PyInt_AsLong(pValue)); + set_arch_dep(proc); + } + Py_DECREF(pValue); + } + else { + Py_DECREF(pFunc); + Py_DECREF(pModule); + PyErr_Print(); + fprintf(stderr,"Call failed\n"); + return; + } + } + Py_XDECREF(pFunc); + Py_DECREF(pModule); + } + else { + PyErr_Print(); + fprintf(stderr, "Failed to find module __main__\n"); + return; + } + } +#endif // HAVE_PYTHON + + if (opt_q) { + static struct timespec first_real_time; + static int first_real_time_set = 0; + + if (strcmp(function_name, "time") == 0 || strcmp(function_name, "gettimeofday") == 0 + || strcmp(function_name, "clock_gettime") == 0) { + if (!first_real_time_set) { + clock_gettime(CLOCK_REALTIME, &first_real_time); + first_real_time_set = 1; + } + + struct timespec real_time; + clock_gettime(CLOCK_REALTIME, &real_time); + if (real_time.tv_nsec < first_real_time.tv_nsec) { + real_time.tv_nsec = first_real_time.tv_nsec - real_time.tv_nsec; + --(real_time.tv_sec); + } + else + real_time.tv_nsec -= first_real_time.tv_nsec; + + real_time.tv_sec += opt_q_time - first_real_time.tv_sec; + + if (strcmp(function_name, "time") == 0) { + void *addr = (void*) gimme_arg(LT_TOF_FUNCTION, proc, 0, &infop); + time_t real_time_fake = real_time.tv_sec; + if (addr) + uunmovebytes(proc, addr, (void *) &real_time_fake, sizeof(time_t)); + set_arg(type, proc, -1, &infoi, real_time_fake); + set_arch_dep(proc); + } else if (strcmp(function_name, "gettimeofday") == 0) { + void *addr = (void*)gimme_arg(LT_TOF_FUNCTION, proc, 0, &infop); + if (addr) { + struct timeval real_time_fake; + real_time_fake.tv_sec = real_time.tv_sec; + real_time_fake.tv_usec = real_time.tv_nsec / 1000; + uunmovebytes(proc, addr, (void *) &real_time_fake, sizeof(struct timeval)); + } + } else if (strcmp(function_name, "clock_gettime") == 0) { + arg_type_info infot; + infot.type = ARGTYPE_INT; + clockid_t type = (clockid_t) gimme_arg(LT_TOF_FUNCTION, proc, 0, &infoi); + void *addr = (void *)gimme_arg(LT_TOF_FUNCTION, proc, 1, &infop); + if (addr && type == CLOCK_REALTIME) + uunmovebytes(proc, addr, (void *) &real_time, sizeof(struct timespec)); + } + } + } +} + +#ifdef HAVE_PYTHON +static int +set_arg_into_python_tuple(enum tof type, Process *proc, int arg_num, arg_type_info * info, PyObject *pArgs) { + long arg; + + if (info->type == ARGTYPE_VOID) { + PyTuple_SetItem(pArgs, arg_num, Py_None); + } else if (info->type == ARGTYPE_FORMAT) { + arg_num = set_format_into_python_tuple(type, proc, arg_num, pArgs); + } else { + arg = gimme_arg(type, proc, arg_num, info); + PyObject *pVal = convert_value_to_python(type, proc, arg, info, NULL, NULL); + if (!pVal) { + PyTuple_SetItem(pArgs, arg_num, Py_None); + } else { + PyTuple_SetItem(pArgs, arg_num, pVal); + } + } + return arg_num; +} + +static int +set_format_into_python_tuple(enum tof type, Process *proc, int arg_num, PyObject *pArgs) { + void *addr; + unsigned char *str1; + int i; + arg_type_info info; + + info.type = ARGTYPE_POINTER; + addr = (void *)gimme_arg(type, proc, arg_num, &info); + if (!addr) { + PyTuple_SetItem(pArgs, arg_num, Py_None); + return arg_num; + } + + str1 = malloc(INT_MAX); + if (!str1) { + PyTuple_SetItem(pArgs, arg_num, Py_None); + return arg_num; + } + + int len = umovebytes(proc, addr, str1, INT_MAX - 2); + + PyObject *pystr; + pystr = PyByteArray_FromStringAndSize(addr, len); + if (!pystr) { + free(str1); + PyTuple_SetItem(pArgs, arg_num, Py_None); + return arg_num; + } + PyTuple_SetItem(pArgs, arg_num, pystr); + + 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)) { + str1[i + 1] = '\0'; + break; + } + } else if (c == 'd' || c == 'i' || c == 'o' || c == 'x') { + info.type = ARGTYPE_LONG; + if (!is_long || proc->mask_32bit) { + PyObject *pl = PyInt_FromLong((int)gimme_arg(type, proc, ++arg_num, &info)); + if (!pl) { + PyTuple_SetItem(pArgs, arg_num, Py_None); + } else { + PyTuple_SetItem(pArgs, arg_num, pl); + } + } else { + PyObject *pl = PyLong_FromLong(gimme_arg(type, proc, ++arg_num, &info)); + if (!pl) { + PyTuple_SetItem(pArgs, arg_num, Py_None); + } else { + PyTuple_SetItem(pArgs, arg_num, pl); + } + } + break; + } else if (c == 'u') { + info.type = ARGTYPE_LONG; + if (!is_long || proc->mask_32bit) { + PyObject *pl = PyInt_FromLong((int)gimme_arg(type, proc, ++arg_num, &info)); + if (!pl) { + PyTuple_SetItem(pArgs, arg_num, Py_None); + } else { + PyTuple_SetItem(pArgs, arg_num, pl); + } + } else { + PyObject *pl = PyLong_FromUnsignedLong(gimme_arg(type, proc, ++arg_num, &info)); + if (!pl) { + PyTuple_SetItem(pArgs, arg_num, Py_None); + } else { + PyTuple_SetItem(pArgs, arg_num, pl); + } + } + break; + } else if (strchr("eEfFgGaACS", c) + || (is_long + && (c == 'c' || c == 's'))) { + str1[i + 1] = '\0'; + break; + } else if (c == 'c') { + info.type = ARGTYPE_LONG; + PyObject *pl = PyInt_FromLong((int)gimme_arg(type, proc, ++arg_num, &info)); + if (!pl) { + PyTuple_SetItem(pArgs, arg_num, Py_None); + } else { + PyTuple_SetItem(pArgs, arg_num, pl); + } + break; + } else if (c == 's') { + info.type = ARGTYPE_POINTER; + pystr = PyByteArray_FromStringAndSize((void *)gimme_arg(type, proc, ++arg_num, &info), INT_MAX - 2); + if (!pystr) { + PyTuple_SetItem(pArgs, arg_num, Py_None); + } else { + PyTuple_SetItem(pArgs, arg_num, pystr); + } + break; + } else if (c == 'p' || c == 'n') { + PyObject *pl = PyLong_FromUnsignedLong(gimme_arg(type, proc, ++arg_num, &info)); + if (!pl) { + PyTuple_SetItem(pArgs, arg_num, Py_None); + } else { + PyTuple_SetItem(pArgs, arg_num, pl); + } + break; + } else if (c == '*') { + info.type = ARGTYPE_LONG; + PyObject *pl = PyInt_FromLong((int)gimme_arg(type, proc, ++arg_num, &info)); + if (!pl) { + PyTuple_SetItem(pArgs, arg_num, Py_None); + } else { + PyTuple_SetItem(pArgs, arg_num, pl); + } + } + } + } + } + free(str1); + return arg_num; +} + +/* 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. + */ +PyObject * +convert_value_to_python(enum tof type, Process *proc, + long value, arg_type_info *info, + void *st, arg_type_info* st_info) { + + switch (info->type) { + case ARGTYPE_VOID: + return Py_None; + case ARGTYPE_INT: + return PyInt_FromLong((int) value); + case ARGTYPE_UINT: + case ARGTYPE_OCTAL: + return PyInt_FromLong((unsigned) value); + case ARGTYPE_LONG: + if (proc->mask_32bit) + return PyInt_FromLong((int) value); + else + return PyLong_FromLong(value); + case ARGTYPE_ULONG: + if (proc->mask_32bit) + return PyInt_FromLong((unsigned) value); + else + return PyLong_FromUnsignedLong((unsigned long) value); + case ARGTYPE_CHAR: + return PyInt_FromLong((char) value); + case ARGTYPE_SHORT: + return PyInt_FromLong((short) value); + case ARGTYPE_USHORT: + return PyInt_FromLong((unsigned short) value); + case ARGTYPE_DOUBLE: + case ARGTYPE_FLOAT: { + union { long l; float f; double d; } cvt; + cvt.l = value; + return PyFloat_FromDouble(cvt.d); + } + case ARGTYPE_POINTER: + case ARGTYPE_ADDR: + if (!value) + return Py_None; + else + return PyLong_FromUnsignedLong((unsigned long) 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 convert_string_to_python(type, proc, (void*) value, + INT_MAX); + case ARGTYPE_STRING_N: + return convert_string_to_python(type, proc, (void*) value, + get_length(type, proc, + info->u.string_n_info.size_spec, st, st_info)); + case ARGTYPE_BYTES: + return convert_bytes_to_python(type, proc, (void*) value, + INT_MAX); + case ARGTYPE_BYTES_N: + return convert_bytes_to_python(type, proc, (void*) value, + get_length(type, proc, + info->u.string_n_info.size_spec, st, st_info)); + case ARGTYPE_ARRAY: + return Py_None; + case ARGTYPE_ENUM: + return PyInt_FromLong((int) value); + case ARGTYPE_STRUCT: + return Py_None; + case ARGTYPE_UNKNOWN: + default: + if (proc->mask_32bit) + return PyInt_FromLong((int) value); + else + return PyLong_FromLong(value); + } +} + +static PyObject* +convert_string_to_python(enum tof type, Process *proc, void *addr, + size_t maxlength) { + char *str1; + + if (!addr) + return Py_None; + + str1 = malloc(maxlength + 3); + if (!str1) + return Py_None; + + int len = umovestr(proc, addr, maxlength, str1); + PyObject *result = PyString_FromStringAndSize(str1, (Py_ssize_t) len); + free(str1); + if (!result) + return Py_None; + + return result; +} + +static PyObject* +convert_bytes_to_python(enum tof type, Process *proc, void *addr, + size_t maxlength) { + char *str1; + + if (!addr) + return Py_None; + + str1 = malloc(maxlength + 3); + if (!str1) + return Py_None; + + int len = umovebytes(proc, addr, str1, maxlength); + PyObject *result = PyByteArray_FromStringAndSize(str1, (Py_ssize_t) len); + free(str1); + if (!result) + return Py_None; + + return result; +} +#endif // HAVE_PYTHON diff --git a/mock.h b/mock.h new file mode 100644 index 0000000..278bae3 --- /dev/null +++ b/mock.h @@ -0,0 +1 @@ +void mock_return(enum tof type, Process *proc, char *function_name); diff --git a/options.c b/options.c index 74c28bd..19be8ea 100644 --- a/options.c +++ b/options.c @@ -10,7 +10,12 @@ #include +#ifdef HAVE_PYTHON +#include +#endif + #include "common.h" +#include #ifndef SYSCONFDIR #define SYSCONFDIR "/etc" @@ -40,6 +45,9 @@ char *library[MAX_LIBRARIES]; size_t library_num = 0; static char *progname; /* Program name (`ltrace') */ int opt_i = 0; /* instruction pointer */ +int opt_q = 0; /* quick time travel */ +int opt_fake_return = 0; /* fake return values */ +time_t opt_q_time; /* travel from ts*/ int opt_r = 0; /* print relative timestamp */ int opt_t = 0; /* print absolute timestamp */ int opt_T = 0; /* show the time spent inside each call */ @@ -51,6 +59,9 @@ struct opt_p_t *opt_p = NULL; /* attach to process with a given pid */ struct opt_e_t *opt_e = NULL; int opt_e_enable = 1; +/* List of function names and values given to option -m: */ +struct opt_m_t *opt_m = NULL; + /* List of global function names given to -x: */ struct opt_x_t *opt_x = NULL; unsigned int opt_x_cnt = 0; @@ -91,6 +102,10 @@ usage(void) { " -i print instruction pointer at time of library call.\n" " -l, --library=FILE print library calls from this library only.\n" " -L do NOT display library calls.\n" + " -m expr mock result values of functions (func1=val1,...).\n" +#ifdef HAVE_PYTHON + " -M, --wrappers=FILE python file with result-wrapper functions (named like 'func1_wrapper').\n" +#endif " -n, --indent=NR indent output by NR spaces for each call level nesting.\n" " -o, --output=FILE write the trace output to that file.\n" " -p PID attach to the process with the process ID pid.\n" @@ -99,6 +114,7 @@ usage(void) { " -S display system calls.\n" " -t, -tt, -ttt print absolute timestamps.\n" " -T show the time spent inside each call.\n" + " -q, --travel=ts quick time travel.\n" " -u USERNAME run command with the userid, groupid of username.\n" " -V, --version output version information and exit.\n" #if defined(HAVE_LIBUNWIND) @@ -115,7 +131,7 @@ usage(void) { static void usage_debug(void) { fprintf(stdout, "%s debugging option, --debug= or -D:\n", progname); - fprintf(stdout, + fprintf(stdout, "\n" " number ref. in source description\n" " 1 general Generally helpful progress information\n" @@ -207,7 +223,11 @@ process_options(int argc, char **argv) { {"indent", 1, 0, 'n'}, {"help", 0, 0, 'h'}, {"library", 1, 0, 'l'}, +#ifdef HAVE_PYTHON + {"wrappers", 1, 0, 'M'}, +#endif {"output", 1, 0, 'o'}, + {"travel", 1, 0, 'q'}, {"version", 0, 0, 'V'}, {"no-plt", 0, 0, 'g'}, {"no-signals", 0, 0, 'b'}, @@ -220,11 +240,10 @@ process_options(int argc, char **argv) { # ifdef USE_DEMANGLE "C" # endif -#if defined(HAVE_LIBUNWIND) - "a:A:D:e:F:l:n:o:p:s:u:x:X:w:", long_options, -#else /* !defined(HAVE_LIBUNWIND) */ - "a:A:D:e:F:l:n:o:p:s:u:x:X:", long_options, +#ifdef HAVE_LIBUNWIND + "w:" #endif + "a:A:D:e:F:l:m:M:n:o:p:q:s:u:x:X:", long_options, &option_index); if (c == -1) { break; @@ -327,6 +346,61 @@ process_options(int argc, char **argv) { case 'L': options.libcalls = 0; break; + case 'm': + { + char *str_m = strdup(optarg); + if (!str_m) { + perror("ltrace: strdup"); + exit(1); + } + while (*str_m) { + struct opt_m_t *tmp; + char *str2 = strchr(str_m, ','); + if (str2) { + *str2 = '\0'; + } + char *streq = strchr(str_m, '='); + if (streq) { + *streq = '\0'; + } + tmp = malloc(sizeof(struct opt_m_t)); + if (!tmp) { + perror("ltrace: malloc"); + exit(1); + } + tmp->name = str_m; + tmp->next = opt_m; + tmp->value = streq ? streq+1 : NULL; + opt_m = tmp; + if (str2) { + str_m = str2 + 1; + } else { + break; + } + } + break; + } +#ifdef HAVE_PYTHON + case 'M': + { + FILE *wrfile = fopen(optarg, "r"); + if (!wrfile) { + fprintf(stderr, "Can't open file with python wrappers for reading: %s\n", optarg); + exit(1); + } + + int result = PyRun_SimpleFile(wrfile, optarg); + fclose(wrfile); + + if (result != 0) { + fprintf(stderr, "Can't load mock module from '%s'\n", optarg); + exit(1); + } + + opt_fake_return = 1; + break; + } +#endif case 'n': options.indent = atoi(optarg); break; @@ -353,6 +427,10 @@ process_options(int argc, char **argv) { opt_p = tmp; break; } + case 'q': + opt_q++; + opt_q_time = atoi(optarg); + break; case 'r': opt_r++; break; diff --git a/options.h b/options.h index 9a00629..76661fd 100644 --- a/options.h +++ b/options.h @@ -1,5 +1,6 @@ #include #include +#include struct options_t { int align; /* -a: default alignment column for results */ @@ -23,6 +24,8 @@ struct options_t { extern struct options_t options; extern int opt_i; /* instruction pointer */ +extern int opt_q; /* quick time travel */ +extern time_t opt_q_time; /* quick time travel */ extern int opt_r; /* print relative timestamp */ extern int opt_t; /* print absolute timestamp */ extern int opt_T; /* show the time spent inside each call */ @@ -37,6 +40,12 @@ struct opt_e_t { struct opt_e_t *next; }; +struct opt_m_t { + char *name; + char *value; + struct opt_m_t *next; +}; + struct opt_F_t { char *filename; struct opt_F_t *next; @@ -54,6 +63,8 @@ extern struct opt_p_t *opt_p; /* attach to process with a given pid */ extern struct opt_e_t *opt_e; /* list of function names to display */ extern int opt_e_enable; /* 0 if '!' is used, 1 otherwise */ +extern struct opt_m_t *opt_m; /* list of function to mock result */ + extern struct opt_F_t *opt_F; /* alternate configuration file(s) */ extern struct opt_x_t *opt_x; /* list of functions to break at */ diff --git a/output.c b/output.c index dd9cdf8..3c1d189 100644 --- a/output.c +++ b/output.c @@ -95,7 +95,7 @@ begin_of_line(enum tof type, Process *proc) { } } -static Function * +Function * name2func(char *name) { Function *tmp; const char *str1, *str2; @@ -202,8 +202,9 @@ output_left(enum tof type, Process *proc, char *function_name) { current_column += fprintf(options.output, ", "); } } - if (func->params_right + if (opt_fake_return || func->params_right || func->return_info->type == ARGTYPE_STRING_N + || func->return_info->type == ARGTYPE_BYTES_N || func->return_info->type == ARGTYPE_ARRAY) { save_register_args(type, proc); } @@ -266,7 +267,6 @@ output_right(enum tof type, Process *proc, char *function_name) { fprintf(options.output, "<... %s resumed> ", function_name); #endif } - if (!func) { current_column += fprintf(options.output, ") "); tabto(options.align - 1); diff --git a/output.h b/output.h index c58577a..4764460 100644 --- a/output.h +++ b/output.h @@ -1,3 +1,6 @@ +Function * +name2func(char *name); + void output_line(Process *proc, char *fmt, ...); void output_left(enum tof type, Process *proc, char *function_name); void output_right(enum tof type, Process *proc, char *function_name); diff --git a/read_config_file.c b/read_config_file.c index b4b1b56..7f891f4 100644 --- a/read_config_file.c +++ b/read_config_file.c @@ -36,6 +36,7 @@ static struct list_of_pt_t { "file", ARGTYPE_FILE}, { "format", ARGTYPE_FORMAT}, { "string", ARGTYPE_STRING}, { + "bytes", ARGTYPE_BYTES}, { "array", ARGTYPE_ARRAY}, { "struct", ARGTYPE_STRUCT}, { "enum", ARGTYPE_ENUM}, { @@ -62,6 +63,8 @@ static arg_type_info arg_type_prototypes[] = { { ARGTYPE_FORMAT }, { ARGTYPE_STRING }, { ARGTYPE_STRING_N }, + { ARGTYPE_BYTES }, + { ARGTYPE_BYTES_N }, { ARGTYPE_ARRAY }, { ARGTYPE_ENUM }, { ARGTYPE_STRUCT }, @@ -330,6 +333,8 @@ arg_align(arg_type_info * arg) { case ARGTYPE_FORMAT: case ARGTYPE_STRING: case ARGTYPE_STRING_N: + case ARGTYPE_BYTES: + case ARGTYPE_BYTES_N: case ARGTYPE_POINTER: return ptr_alignment; @@ -483,6 +488,7 @@ parse_nonpointer_type(char **str) { return info; } + case ARGTYPE_BYTES: case ARGTYPE_STRING: if (!isdigit(**str) && **str != '[') { /* Oops, was just a simple string after all */ @@ -490,7 +496,7 @@ parse_nonpointer_type(char **str) { return simple; } - info->type = ARGTYPE_STRING_N; + info->type = (info->type == ARGTYPE_STRING) ? ARGTYPE_STRING_N : ARGTYPE_BYTES_N; /* Backwards compatibility for string0, string1, ... */ if (isdigit(**str)) { diff --git a/sysdeps/linux-gnu/alpha/trace.c b/sysdeps/linux-gnu/alpha/trace.c index e4d4063..82bd20a 100644 --- a/sysdeps/linux-gnu/alpha/trace.c +++ b/sysdeps/linux-gnu/alpha/trace.c @@ -73,3 +73,15 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { void save_register_args(enum tof type, Process *proc) { } + +void +set_arch_dep(Process *proc) { + fprintf(stderr, "set_arch_dep is not supported on your platform\n"); + exit(1); +} + +void +set_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info, long value) { + fprintf(stderr, "set_arg is not supported on your platform\n"); + exit(1); +} diff --git a/sysdeps/linux-gnu/arm/trace.c b/sysdeps/linux-gnu/arm/trace.c index 39b8264..e421bc8 100644 --- a/sysdeps/linux-gnu/arm/trace.c +++ b/sysdeps/linux-gnu/arm/trace.c @@ -130,3 +130,15 @@ save_register_args(enum tof type, Process *proc) { memcpy(a->sysc_arg, a->regs.uregs, sizeof(a->sysc_arg)); } } + +void +set_arch_dep(Process *proc) { + fprintf(stderr, "set_arch_dep is not supported on your platform\n"); + exit(1); +} + +void +set_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info, long value) { + fprintf(stderr, "set_arg is not supported on your platform\n"); + exit(1); +} diff --git a/sysdeps/linux-gnu/cris/trace.c b/sysdeps/linux-gnu/cris/trace.c index b9439fc..a181525 100644 --- a/sysdeps/linux-gnu/cris/trace.c +++ b/sysdeps/linux-gnu/cris/trace.c @@ -73,3 +73,17 @@ long gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) void save_register_args(enum tof type, Process *proc) { } + +void +set_arch_dep(Process *proc) +{ + fprintf(stderr, "set_arch_dep is not supported on your platform\n"); + exit(1); +} + +void +set_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info, long value) +{ + fprintf(stderr, "set_arg is not supported on your platform\n"); + exit(1); +} diff --git a/sysdeps/linux-gnu/i386/trace.c b/sysdeps/linux-gnu/i386/trace.c index 76f1105..546c381 100644 --- a/sysdeps/linux-gnu/i386/trace.c +++ b/sysdeps/linux-gnu/i386/trace.c @@ -21,6 +21,10 @@ void get_arch_dep(Process *proc) { } +void +set_arch_dep(Process *proc) { +} + /* Returns 1 if syscall, 2 if sysret, 0 otherwise. */ int @@ -80,6 +84,26 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { return 0; } +void +set_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info, long value) { + if (arg_num == -1) { /* return value */ + ptrace(PTRACE_POKEUSER, proc->pid, 4 * EAX, value); + return; + } + + if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { + ptrace(PTRACE_POKETEXT, proc->pid, + proc->stack_pointer + 4 * (arg_num + 1), value); + return; + } else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) { + ptrace(PTRACE_POKEUSER, proc->pid, 4 * arg_num, value); + return; + } else { + fprintf(stderr, "set_arg called with wrong arguments\n"); + exit(1); + } +} + void save_register_args(enum tof type, Process *proc) { } diff --git a/sysdeps/linux-gnu/ia64/trace.c b/sysdeps/linux-gnu/ia64/trace.c index 799e0ff..8f49ec2 100644 --- a/sysdeps/linux-gnu/ia64/trace.c +++ b/sysdeps/linux-gnu/ia64/trace.c @@ -266,3 +266,15 @@ save_register_args(enum tof type, Process *proc) { void get_arch_dep(Process *proc) { } + +void +set_arch_dep(Process *proc) { + fprintf(stderr, "set_arch_dep is not supported on your platform\n"); + exit(1); +} + +void +set_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info, long value) { + fprintf(stderr, "set_arg is not supported on your platform\n"); + exit(1); +} diff --git a/sysdeps/linux-gnu/m68k/trace.c b/sysdeps/linux-gnu/m68k/trace.c index 2f89fdf..fff5386 100644 --- a/sysdeps/linux-gnu/m68k/trace.c +++ b/sysdeps/linux-gnu/m68k/trace.c @@ -87,3 +87,15 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { void save_register_args(enum tof type, Process *proc) { } + +void +set_arch_dep(Process *proc) { + fprintf(stderr, "set_arch_dep is not supported on your platform\n"); + exit(1); +} + +void +set_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info, long value) { + fprintf(stderr, "set_arg is not supported on your platform\n"); + exit(1); +} diff --git a/sysdeps/linux-gnu/mipsel/trace.c b/sysdeps/linux-gnu/mipsel/trace.c index 6553967..fcef983 100644 --- a/sysdeps/linux-gnu/mipsel/trace.c +++ b/sysdeps/linux-gnu/mipsel/trace.c @@ -188,4 +188,15 @@ void save_register_args(enum tof type, Process *proc) { } +void +set_arch_dep(Process *proc) { + fprintf(stderr, "set_arch_dep is not supported on your platform\n"); + exit(1); +} + +void +set_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info, long value) { + fprintf(stderr, "set_arg is not supported on your platform\n"); + exit(1); +} /**@}*/ diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c index 8642157..dd568fb 100644 --- a/sysdeps/linux-gnu/ppc/trace.c +++ b/sysdeps/linux-gnu/ppc/trace.c @@ -122,7 +122,7 @@ gimme_retval(Process *proc, int arg_num, arg_type_info *info, return cvt.val; } - else + else return (*regs)[3]; } @@ -188,7 +188,7 @@ arch_umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { if (info) { if (info->type == ARGTYPE_INT || (proc->mask_32bit && (info->type == ARGTYPE_POINTER - || info->type == ARGTYPE_STRING))) { + || info->type == ARGTYPE_STRING || info->type == ARGTYPE_BYTES))) { pointed_to = (long) (((unsigned long) pointed_to) >> 32); } } @@ -197,3 +197,15 @@ arch_umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { *result = pointed_to; return 0; } + +void +set_arch_dep(Process *proc) { + fprintf(stderr, "set_arch_dep is not supported on your platform\n"); + exit(1); +} + +void +set_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info, long value) { + fprintf(stderr, "set_arg is not supported on your platform\n"); + exit(1); +} diff --git a/sysdeps/linux-gnu/s390/trace.c b/sysdeps/linux-gnu/s390/trace.c index 63935de..adb8b26 100644 --- a/sysdeps/linux-gnu/s390/trace.c +++ b/sysdeps/linux-gnu/s390/trace.c @@ -203,3 +203,15 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { void save_register_args(enum tof type, Process *proc) { } + +void +set_arch_dep(Process *proc) { + fprintf(stderr, "set_arch_dep is not supported on your platform\n"); + exit(1); +} + +void +set_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info, long value) { + fprintf(stderr, "set_arg is not supported on your platform\n"); + exit(1); +} diff --git a/sysdeps/linux-gnu/sparc/trace.c b/sysdeps/linux-gnu/sparc/trace.c index 7f05b55..5a94825 100644 --- a/sysdeps/linux-gnu/sparc/trace.c +++ b/sysdeps/linux-gnu/sparc/trace.c @@ -79,3 +79,15 @@ save_register_args(enum tof type, Process *proc) { memcpy(a->sysc_arg, &a->regs.u_regs[UREG_G7], sizeof(a->sysc_arg)); } } + +void +set_arch_dep(Process *proc) { + fprintf(stderr, "set_arch_dep is not supported on your platform\n"); + exit(1); +} + +void +set_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info, long value) { + fprintf(stderr, "set_arg is not supported on your platform\n"); + exit(1); +} diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c index b810f2d..8dceb69 100644 --- a/sysdeps/linux-gnu/trace.c +++ b/sysdeps/linux-gnu/trace.c @@ -224,11 +224,56 @@ umovestr(Process *proc, void *addr, int len, void *laddr) { *(char *)(laddr + offset + i) = a.c[i]; } else { *(char *)(laddr + offset + i) = '\0'; - return 0; + return offset + (signed)i; } } offset += sizeof(long); } *(char *)(laddr + offset) = '\0'; - return 0; + return offset; +} + +size_t +uunmovebytes(Process *proc, void *addr, void *laddr, size_t len) { + union { + long a; + char c[sizeof(long)]; + } a; + int started = 0; + size_t offset = 0, bytes_written = 0; + + while (offset < len) { + if (len - offset >= sizeof(long)) { + memcpy(&a.c[0], laddr + offset, sizeof(long)); + } + else { + a.a = ptrace(PTRACE_PEEKTEXT, proc->pid, addr + offset, 0); + if (a.a == -1 && errno) { + if (started && errno == EIO) + return bytes_written; + else + return -1; + } + memcpy(&a.c[0], laddr + offset, len - offset); + } + + a.a = ptrace(PTRACE_POKETEXT, proc->pid, addr + offset, a.a); + if (a.a == -1 && errno) { + if (started && errno == EIO) + return bytes_written; + else + return -1; + } + + if (len - offset >= sizeof(long)) + bytes_written += sizeof(long); + else + bytes_written += len - offset; + + started = 1; + + offset += sizeof(long); + } + + return bytes_written; } diff --git a/sysdeps/linux-gnu/x86_64/trace.c b/sysdeps/linux-gnu/x86_64/trace.c index e8581af..4222846 100644 --- a/sysdeps/linux-gnu/x86_64/trace.c +++ b/sysdeps/linux-gnu/x86_64/trace.c @@ -38,6 +38,19 @@ get_arch_dep(Process *proc) { } } +void +set_arch_dep(Process *proc) +{ + proc_archdep *a; + + a = (proc_archdep *) (proc->arch_ptr); + if (!a || !a->valid) + return; + + ptrace(PTRACE_SETREGS, proc->pid, 0, &a->regs); + ptrace(PTRACE_GETFPREGS, proc->pid, 0, &a->fpregs); +} + /* Returns 1 if syscall, 2 if sysret, 0 otherwise. */ int @@ -94,6 +107,48 @@ gimme_arg32(enum tof type, Process *proc, int arg_num) { exit(1); } +static void +set_arg32(enum tof type, Process *proc, int arg_num, unsigned int value) { + proc_archdep *a = (proc_archdep *) proc->arch_ptr; + + if (arg_num == -1) { /* return value */ + a->regs.rax = value; + return; + } + + if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { + ptrace(PTRACE_POKETEXT, proc->pid, + proc->stack_pointer + 4 * (arg_num + 1), &value); + return; + } else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) { + switch (arg_num) { + case 0: + a->regs.rbx = value; + return; + case 1: + a->regs.rcx = value; + return; + case 2: + a->regs.rdx = value; + return; + case 3: + a->regs.rsi = value; + return; + case 4: + a->regs.rdi = value; + return; + case 5: + a->regs.rbp = value; + return; + default: + fprintf(stderr, + "set_arg32 called with wrong arguments\n"); + exit(2); + } + } + fprintf(stderr, "set_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, @@ -130,16 +185,72 @@ gimme_arg_regset(Process *proc, int arg_num, arg_type_info *info, proc->stack_pointer + 8 * (arg_num - 6 + 1), 0); } } + +static void +set_arg_regset(Process *proc, int arg_num, arg_type_info *info, + struct user_regs_struct *regs, + struct user_fpregs_struct *fpregs, long value) +{ + union { + uint32_t sse[4]; + long lval; + float fval; + double dval; + } cvt; + + cvt.lval = value; + + if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) { + memcpy(fpregs->xmm_space + 4*arg_num, cvt.sse, + sizeof(cvt.sse)); + return; + } + + switch (arg_num) { + case 0: + regs->rdi = value; + break; + case 1: + regs->rsi = value; + break; + case 2: + regs->rdx = value; + break; + case 3: + regs->rcx = value; + break; + case 4: + regs->r8 = value; + break; + case 5: + regs->r9 = value; + break; + default: + ptrace(PTRACE_POKETEXT, proc->pid, + proc->stack_pointer + 8 * (arg_num - 6 + 1), &value); + } +} + 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) + if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) return gimme_arg_regset(proc, 0, info, regs, fpregs); else return regs->rax; } +static void +set_retval(Process *proc, int arg_num, arg_type_info *info, + struct user_regs_struct *regs, struct user_fpregs_struct *fpregs, long value) +{ + if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) + set_arg_regset(proc, 0, info, regs, fpregs, value); + else + regs->rax = value; +} + long gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { if (proc->mask_32bit) @@ -170,10 +281,43 @@ gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { } void -save_register_args(enum tof type, Process *proc) { - proc_archdep *arch = (proc_archdep *)proc->arch_ptr; - if (arch == NULL || !arch->valid) - return; +set_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info, long value) +{ + if (proc->mask_32bit) { + set_arg32(type, proc, arg_num, value); + return; + } + + proc_archdep *arch = (proc_archdep *)proc->arch_ptr; + + if (arch == NULL || !arch->valid) + return; + + if (type == LT_TOF_FUNCTIONR) { + if (arg_num == -1) + return set_retval(proc, arg_num, info, + &arch->regs, &arch->fpregs, value); + else { + struct callstack_element *elem + = proc->callstack + proc->callstack_depth - 1; + callstack_achdep *csad = elem->arch_ptr; + assert(csad != NULL); + return set_arg_regset(proc, arg_num, info, + &csad->regs_copy, + &csad->fpregs_copy, value); + } + } + else + return set_arg_regset(proc, arg_num, info, + &arch->regs, &arch->fpregs, value); +} + +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));