* Makefile.am: Build with LuaJIT if configured so.
(strace_SOURCES): Add defs_shared.h, ffi.h, syscall_class.h.
* configure.ac: Add new --with-luajit configure option.
* defs.h (TCB_AD_HOC_INJECT): new TCB flag.
(QUAL_HOOK_ENTRY, QUAL_HOOK_EXIT): new qual flags.
(struct tcb): If built with LuaJIT support, include currpers field even
if SUPPORTED_PERSONALITIES is 0.
If built with LuaJIT support, include new ad_hoc_inject_vec field.
Move definitions that need to be fed to LuaJIT's FFI to...
* defs_shared.h: ...new file.
* ffi.h: New file.
* luajit.h: Likewise.
* luajit_funcs.h: Likewise.
* luajit_lib.h: Likewise.
* mpers_type.h (STRINGIFY): Rename to MPERS_STRINGIFY so that it does
not pollute the global namespace.
* qualify.c (syscall_classes): move syscall classes list to the global
scope, terminate it with a null entry.
(hook_entry_set, hook_exit_set): New sets (if built with LuaJIT
support).
(lookup_class): Use global, null entry-terminated list of syscall
classes.
(qual_flags): If built with LuaJIT support, return QUAL_HOOK_ENTRY and
QUAL_HOOK_EXIT flags.
(set_hook_qual): New function.
* strace.c (alloctcb): update the condition of presence of currpers
field.
(init): New -l option (if built with LuaJIT support).
(main): run Lua script, if built with LuaJIT support and a script
was provided.
* syscall.c (errnoent_vec, nerrnoent_vec, signalent_vec,
nsignalent_vec, ioctlent_vec, nioctlent_vec): New global variables.
(tcb_inject_opts): introduce a second argument indicating whether tcp's
inject_vec should be copied from the global inject_vec if needed.
If built with LuaJIT support and TCB_AD_HOC_INJECT flag is set, return
tcp's ad_hoc_inject_vec.
(tamper_with_syscall_entering): Don't copy inject_vec here; instead,
pass true as a second argument to tcb_inject_opts.
(tamper_with_syscall_exiting): Pass false as a second argument to
tcb_inject_opts.
(syscall_ad_hoc_inject): New function.
(syscall_entering_trace): perform ad hoc injection even if the syscall
is not traced.
(syscall_exiting_decode): don't return 0 ("bail out") if exiting hook is
set up for this syscall, or if an ad hoc injection was performed.
Call tamper_with_syscall_exiting on success.
(syscall_exiting_trace): Don't call tamper_with_syscall_exiting, check
if the syscall is not traced again.
(syscall_exiting_finish): Clear TCB_AD_HOC_INJECT bit.
* syscall_class.h: New file.
* sysent.h: Modify to support inclusion with FFI_CDEF.
---
 Makefile.am     |   9 ++
 configure.ac    |  36 ++++++++
 defs.h          |  52 +++--------
 defs_shared.h   |  58 ++++++++++++
 ffi.h           |  18 ++++
 luajit.h        | 269 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 luajit_funcs.h  |  28 ++++++
 luajit_lib.h    | 224 ++++++++++++++++++++++++++++++++++++++++++++++
 mpers_type.h    |   4 +-
 qualify.c       |  85 +++++++++++-------
 strace.c        |  35 +++++++-
 syscall.c       | 117 ++++++++++++++++++++----
 syscall_class.h |  19 ++++
 sysent.h        |  23 ++++-
 14 files changed, 883 insertions(+), 94 deletions(-)
 create mode 100644 defs_shared.h
 create mode 100644 ffi.h
 create mode 100644 luajit.h
 create mode 100644 luajit_funcs.h
 create mode 100644 luajit_lib.h
 create mode 100644 syscall_class.h

diff --git a/Makefile.am b/Makefile.am
index f9e78ed1..a77b77e2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -100,6 +100,7 @@ strace_SOURCES =    \
        copy_file_range.c \
        count.c         \
        defs.h          \
+       defs_shared.h   \
        desc.c          \
        dirent.c        \
        dirent64.c      \
@@ -122,6 +123,7 @@ strace_SOURCES =    \
        fetch_struct_stat.c \
        fetch_struct_stat64.c \
        fetch_struct_statfs.c \
+       ffi.h           \
        file_handle.c   \
        file_ioctl.c    \
        fs_x_ioctl.c    \
@@ -244,6 +246,7 @@ strace_SOURCES =    \
        strace.c        \
        swapon.c        \
        syscall.c       \
+       syscall_class.h \
        sysctl.c        \
        sysent.h        \
        sysinfo.c       \
@@ -279,6 +282,12 @@ strace_LDFLAGS += $(libunwind_LDFLAGS)
 strace_LDADD += $(libunwind_LIBS)
 endif
 
+if USE_LUAJIT
+strace_SOURCES += luajit.h luajit_lib.h luajit_funcs.h
+strace_CPPFLAGS += $(LUAJIT_CFLAGS)
+strace_LDADD += $(LUAJIT_LIBS)
+endif
+
 @CODE_COVERAGE_RULES@
 CODE_COVERAGE_BRANCH_COVERAGE = 1
 CODE_COVERAGE_GENHTML_OPTIONS = $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) \
diff --git a/configure.ac b/configure.ac
index bc7b637d..0e27936f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -717,6 +717,42 @@ AC_SUBST(dl_LIBS)
 
 AC_PATH_PROG([PERL], [perl])
 
+dnl LuaJIT scripting support
+use_luajit=no
+force_luajit=no
+luajit_lib=luajit
+LUAJIT_LIBS=
+LUAJIT_CFLAGS=
+AC_ARG_WITH([luajit],
+            [AS_HELP_STRING([--with-luajit],
+                            [build with LuaJIT scripting support])],
+            [case "${withval}" in
+             yes)   force_luajit=yes ;;
+             check) ;;
+             *)     force_luajit=yes; luajit_lib="${withval}" ;;
+             esac],
+            [:]
+)
+AS_IF([test "x$luajit_lib" != xno],
+      [PKG_CHECK_MODULES([LUAJIT],
+                         [$luajit_lib],
+                         [use_luajit=yes],
+                         [AS_IF([test "x$force_luajit" = xyes],
+                                [AC_MSG_ERROR([cannot find luajit library: 
$luajit_lib])]
+                               )]
+                        )]
+)
+
+dnl enable LuaJIT
+AC_MSG_CHECKING([whether to enable Lua scripting])
+if test "x$use_luajit" = xyes; then
+       AC_DEFINE([USE_LUAJIT], 1, [Enable Lua scripting support])
+       AC_SUBST(LUAJIT_LIBS)
+       AC_SUBST(LUAJIT_CFLAGS)
+fi
+AM_CONDITIONAL([USE_LUAJIT], [test "x$use_luajit" = xyes])
+AC_MSG_RESULT([$use_luajit])
+
 dnl stack trace with libunwind
 libunwind_CPPFLAGS=
 libunwind_LDFLAGS=
diff --git a/defs.h b/defs.h
index 29a681b9..c95e5ebe 100644
--- a/defs.h
+++ b/defs.h
@@ -193,11 +193,6 @@ extern char *stpcpy(char *dst, const char *src);
 # define PERSONALITY2_INCLUDE_FUNCS "empty.h"
 #endif
 
-typedef struct ioctlent {
-       const char *symbol;
-       unsigned int code;
-} struct_ioctlent;
-
 struct inject_opts {
        uint16_t first;
        uint16_t step;
@@ -208,39 +203,7 @@ struct inject_opts {
 #define MAX_ERRNO_VALUE                        4095
 #define INJECT_OPTS_RVAL_DEFAULT       (-(MAX_ERRNO_VALUE + 1))
 
-/* Trace Control Block */
-struct tcb {
-       int flags;              /* See below for TCB_ values */
-       int pid;                /* If 0, this tcb is free */
-       int qual_flg;           /* qual_flags[scno] or DEFAULT_QUAL_FLAGS + RAW 
*/
-       unsigned long u_error;  /* Error code */
-       kernel_ulong_t scno;    /* System call number */
-       kernel_ulong_t u_arg[MAX_ARGS]; /* System call arguments */
-       kernel_long_t u_rval;   /* Return value */
-#if SUPPORTED_PERSONALITIES > 1
-       unsigned int currpers;  /* Personality at the time of scno update */
-#endif
-       int sys_func_rval;      /* Syscall entry parser's return value */
-       int curcol;             /* Output column for this process */
-       FILE *outf;             /* Output file for this process */
-       const char *auxstr;     /* Auxiliary info from syscall (see RVAL_STR) */
-       void *_priv_data;       /* Private data for syscall decoding functions 
*/
-       void (*_free_priv_data)(void *); /* Callback for freeing priv_data */
-       const struct_sysent *s_ent; /* sysent[scno] or dummy struct for bad 
scno */
-       const struct_sysent *s_prev_ent; /* for "resuming interrupted SYSCALL" 
msg */
-       struct inject_opts *inject_vec[SUPPORTED_PERSONALITIES];
-       struct timeval stime;   /* System time usage as of last process wait */
-       struct timeval dtime;   /* Delta for system time usage */
-       struct timeval etime;   /* Syscall entry time */
-
-#ifdef USE_LIBUNWIND
-       struct UPT_info *libunwind_ui;
-       struct mmap_cache_t *mmap_cache;
-       unsigned int mmap_cache_size;
-       unsigned int mmap_cache_generation;
-       struct queue_t *queue;
-#endif
-};
+#include "defs_shared.h"
 
 /* TCB flags */
 /* We have attached to this process, but did not see it stopping yet */
@@ -264,6 +227,7 @@ struct tcb {
 #define TCB_TAMPERED   0x40    /* A syscall has been tampered with */
 #define TCB_HIDE_LOG   0x80    /* We should hide everything (until execve) */
 #define TCB_SKIP_DETACH_ON_FIRST_EXEC  0x100   /* -b execve should skip detach 
on first execve */
+#define TCB_AD_HOC_INJECT      0x200   /* an ad hoc injection was performed by 
Lua script */
 
 /* qualifier flags */
 #define QUAL_TRACE     0x001   /* this system call should be traced */
@@ -274,6 +238,8 @@ struct tcb {
 #define QUAL_SIGNAL    0x100   /* report events with this signal */
 #define QUAL_READ      0x200   /* dump data read from this file descriptor */
 #define QUAL_WRITE     0x400   /* dump data written to this file descriptor */
+#define QUAL_HOOK_ENTRY        0x800   /* return this syscall on entry from 
next_sc() */
+#define QUAL_HOOK_EXIT 0x1000  /* return this syscall on exit from next_sc() */
 
 #define DEFAULT_QUAL_FLAGS (QUAL_TRACE | QUAL_ABBREV | QUAL_VERBOSE)
 
@@ -672,6 +638,9 @@ extern struct number_set signal_set;
 extern bool is_number_in_set(unsigned int number, const struct number_set *);
 extern void qualify(const char *);
 extern unsigned int qual_flags(const unsigned int);
+#ifdef USE_LUAJIT
+extern void set_hook_qual(unsigned int scno, unsigned int pers, bool 
entry_hook, bool exit_hook);
+#endif
 
 #define DECL_IOCTL(name)                                               \
 extern int                                                             \
@@ -952,6 +921,13 @@ extern const char *const errnoent0[];
 extern const char *const signalent0[];
 extern const struct_ioctlent ioctlent0[];
 
+extern const char *const *errnoent_vec[SUPPORTED_PERSONALITIES];
+extern const char *const *signalent_vec[SUPPORTED_PERSONALITIES];
+extern const struct_ioctlent *const ioctlent_vec[SUPPORTED_PERSONALITIES];
+extern const unsigned int nerrnoent_vec[SUPPORTED_PERSONALITIES];
+extern const unsigned int nsignalent_vec[SUPPORTED_PERSONALITIES];
+extern const unsigned int nioctlent_vec[SUPPORTED_PERSONALITIES];
+
 #if SUPPORTED_PERSONALITIES > 1
 extern const struct_sysent *sysent;
 extern const char *const *errnoent;
diff --git a/defs_shared.h b/defs_shared.h
new file mode 100644
index 00000000..9129eb7e
--- /dev/null
+++ b/defs_shared.h
@@ -0,0 +1,58 @@
+/*
+ * Should only be included without FFI_CDEF from defs.h, so no include guards.
+ */
+
+#include "ffi.h"
+
+FFI_CONTENT(
+typedef struct ioctlent {
+       const char *symbol;
+       unsigned int code;
+} struct_ioctlent;
+)
+
+FFI_CONTENT(
+struct tcb {
+       int flags;              /* See below for TCB_ values */
+       int pid;                /* If 0, this tcb is free */
+       int qual_flg;           /* qual_flags[scno] or DEFAULT_QUAL_FLAGS + RAW 
*/
+       unsigned long u_error;  /* Error code */
+       kernel_ulong_t scno;    /* System call number */
+       kernel_ulong_t u_arg[MAX_ARGS]; /* System call arguments */
+       kernel_long_t u_rval;   /* Return value */
+)
+
+#if defined(USE_LUAJIT) || SUPPORTED_PERSONALITIES > 1
+FFI_CONTENT(
+       unsigned int currpers;  /* Personality at the time of scno update */
+)
+#endif
+
+#ifndef FFI_CDEF
+       int sys_func_rval;      /* Syscall entry parser's return value */
+       int curcol;             /* Output column for this process */
+       FILE *outf;             /* Output file for this process */
+       const char *auxstr;     /* Auxiliary info from syscall (see RVAL_STR) */
+       void *_priv_data;       /* Private data for syscall decoding functions 
*/
+       void (*_free_priv_data)(void *); /* Callback for freeing priv_data */
+       const struct_sysent *s_ent; /* sysent[scno] or dummy struct for bad 
scno */
+       const struct_sysent *s_prev_ent; /* for "resuming interrupted SYSCALL" 
msg */
+# ifdef USE_LUAJIT
+       struct inject_opts *ad_hoc_inject_opts;
+# endif
+       struct inject_opts *inject_vec[SUPPORTED_PERSONALITIES];
+       struct timeval stime;   /* System time usage as of last process wait */
+       struct timeval dtime;   /* Delta for system time usage */
+       struct timeval etime;   /* Syscall entry time */
+# ifdef USE_LIBUNWIND
+       struct UPT_info *libunwind_ui;
+       struct mmap_cache_t *mmap_cache;
+       unsigned int mmap_cache_size;
+       unsigned int mmap_cache_generation;
+       struct queue_t *queue;
+# endif
+#endif /* !FFI_CDEF */
+
+FFI_CONTENT(
+};
+)
diff --git a/ffi.h b/ffi.h
new file mode 100644
index 00000000..96dc977f
--- /dev/null
+++ b/ffi.h
@@ -0,0 +1,18 @@
+#ifndef STRACE_FFI_H
+#define STRACE_FFI_H
+
+#define FFI_STRINGIFY(...) #__VA_ARGS__
+#define FFI_CONCAT(a, b) a ## b
+#define FFI_CONCAT2(a, b) FFI_CONCAT(a, b)
+
+/*
+ * FFI_CONTENT expands to FFI_CONTENT_ (which strigifies its arguments) when
+ * FFI_CDEF is defined, and to FFI_CONTENT_FFI_CDEF (which simply expands to 
its
+ * arguments) when it is not.
+ */
+#define FFI_CONTENT FFI_CONCAT2(FFI_CONTENT_, FFI_CDEF)
+
+#define FFI_CONTENT_(...)         FFI_STRINGIFY(__VA_ARGS__)
+#define FFI_CONTENT_FFI_CDEF(...) __VA_ARGS__
+
+#endif /* !STRACE_FFI_H */
diff --git a/luajit.h b/luajit.h
new file mode 100644
index 00000000..d9885ebe
--- /dev/null
+++ b/luajit.h
@@ -0,0 +1,269 @@
+/*
+ * Should only be included from strace.c, so no include guards.
+ */
+
+#include "luajit_funcs.h"
+#include <signal.h>
+
+#define L script_L
+
+#define LUAJIT_FUNC(name) luajit_func_ ## name
+#define luajit_func_umoven umoven
+#define luajit_func_umovestr umovestr
+
+static struct tcb *
+LUAJIT_FUNC(next_sc)(void)
+{
+       static struct timeval tv = {};
+       static bool first = true;
+
+#define MBRESTART(res, sig)                                                    
        \
+       if ((res) >= 0 && ptrace_restart(PTRACE_SYSCALL, current_tcp, sig) < 0) 
{       \
+               /* Note: ptrace_restart emitted error message */                
        \
+               exit_code = 1;                                                  
        \
+               return NULL;                                                    
        \
+       }
+
+       if (!first) {
+               unsigned int sig = 0;
+               int res;
+               if (entering(current_tcp)) {
+                       res = syscall_entering_trace(current_tcp, &sig);
+                       syscall_entering_finish(current_tcp, res);
+               } else {
+                       res = syscall_exiting_trace(current_tcp, tv, 1);
+                       syscall_exiting_finish(current_tcp);
+               }
+               MBRESTART(res, sig);
+       }
+       first = false;
+
+       while (1) {
+               int status;
+               siginfo_t si;
+               enum trace_event ret = next_event(&status, &si);
+               if (ret == TE_SYSCALL_STOP) {
+                       unsigned int sig = 0;
+                       int res;
+                       if (entering(current_tcp)) {
+                               res = syscall_entering_decode(current_tcp);
+                               switch (res) {
+                               case 0:
+                                       break;
+                               case 1:
+                                       if (current_tcp->qual_flg & 
QUAL_HOOK_ENTRY)
+                                               return current_tcp;
+                                       res = 
syscall_entering_trace(current_tcp, &sig);
+                                       /* fall through */
+                               default:
+                                       syscall_entering_finish(current_tcp, 
res);
+                               }
+                       } else {
+                               res = syscall_exiting_decode(current_tcp, &tv);
+                               switch (res) {
+                               case 0:
+                                       break;
+                               case 1:
+                                       if (current_tcp->qual_flg & 
QUAL_HOOK_EXIT)
+                                               return current_tcp;
+                                       /* fall through */
+                               default:
+                                       res = 
syscall_exiting_trace(current_tcp, tv, res);
+                               }
+                               syscall_exiting_finish(current_tcp);
+                       }
+                       MBRESTART(res, sig);
+               } else {
+                       if (!dispatch_event(ret, &status, &si)) {
+                               return NULL;
+                       }
+               }
+       }
+#undef MBRESTART
+}
+
+static bool
+LUAJIT_FUNC(monitor)(unsigned int scno, unsigned int pers, bool entry_hook, 
bool exit_hook)
+{
+       if (pers >= SUPPORTED_PERSONALITIES || scno >= nsyscall_vec[pers])
+               return false;
+       set_hook_qual(scno, pers, entry_hook, exit_hook);
+       return true;
+}
+
+static void
+prepare_ad_hoc_inject(struct tcb *tcp)
+{
+       if (!tcp->ad_hoc_inject_opts) {
+               tcp->ad_hoc_inject_opts = 
xmalloc(sizeof(*tcp->ad_hoc_inject_opts));
+               tcp->ad_hoc_inject_opts->first = 1;
+               tcp->ad_hoc_inject_opts->step = 1;
+       }
+       if (!(tcp->flags & TCB_AD_HOC_INJECT)) {
+               tcp->ad_hoc_inject_opts->signo = 0;
+               tcp->ad_hoc_inject_opts->rval = INJECT_OPTS_RVAL_DEFAULT;
+               tcp->qual_flg |= QUAL_INJECT;
+               tcp->flags |= TCB_AD_HOC_INJECT;
+       }
+}
+
+static bool
+LUAJIT_FUNC(inject_signo)(struct tcb *tcp, int signo)
+{
+       if (exiting(tcp))
+               /* Too late! */
+               return false;
+       if (signo <= 0 || signo > SIGRTMAX)
+               return false;
+       prepare_ad_hoc_inject(tcp);
+       tcp->ad_hoc_inject_opts->signo = signo;
+       return true;
+}
+
+static bool
+LUAJIT_FUNC(inject_retval)(struct tcb *tcp, int retval)
+{
+       if (exiting(tcp))
+               /* Too late! */
+               return false;
+       if (retval < -MAX_ERRNO_VALUE)
+               return false;
+       prepare_ad_hoc_inject(tcp);
+       tcp->ad_hoc_inject_opts->rval = retval;
+       return true;
+}
+
+static const char *
+get_lua_msg(void)
+{
+       const char *msg = lua_tostring(L, -1);
+       return msg ? msg : "(error object can't be converted to string)";
+}
+
+static void
+assert_lua_impl(int ret, const char *expr, const char *file, int line)
+{
+       if (ret == 0)
+               return;
+       error_msg_and_die("assert_lua(%s) failed at %s:%d: %s", expr, file,
+               line, get_lua_msg());
+}
+
+#define assert_lua(expr) assert_lua_impl(expr, #expr, __FILE__, __LINE__)
+
+static void
+check_lua(int ret)
+{
+       if (ret == 0)
+               return;
+       error_msg_and_die("lua: %s", get_lua_msg());
+}
+
+static void
+init_luajit(const char *scriptfile)
+{
+       static struct strace_luajit_funcs funcs = {
+#define X(rettype, name, ...) LUAJIT_FUNC(name),
+LUAJIT_FUNCS_XPAND()
+#undef X
+       };
+
+       if (L)
+               /* already initialized? */
+               error_msg_and_help("multiple -l arguments");
+
+       if (!(L = luaL_newstate()))
+               die_out_of_memory();
+
+       luaL_openlibs(L);
+       /* L: - */
+       assert_lua(luaL_loadfile(L, scriptfile)); /* L: chunk */
+       lua_getglobal(L, "require"); /* L: chunk require */
+       lua_pushstring(L, "ffi"); /* L: chunk require "ffi" */
+       assert_lua(lua_pcall(L, 1, 1, 0)); /* L: chunk ffi */
+       lua_getfield(L, -1, "cdef"); /* L: chunk ffi cdef */
+       luaL_Buffer b;
+       luaL_buffinit(L, &b); /* L: chunk ffi cdef ? */
+       {
+               char buf[128];
+               snprintf(buf, sizeof(buf),
+                       "typedef int%d_t kernel_long_t;"
+                       "typedef uint%d_t kernel_ulong_t;",
+                       (int) sizeof(kernel_long_t) * 8,
+                       (int) sizeof(kernel_ulong_t) * 8);
+               luaL_addstring(&b, buf); /* L: chunk ffi cdef ? */
+       }
+       luaL_addstring(&b,
+#define FFI_CDEF
+#include "sysent.h"
+#include "defs_shared.h"
+#include "luajit_funcs.h"
+#include "syscall_class.h"
+#undef FFI_CDEF
+       ); /* L: chunk ffi cdef ? */
+       luaL_pushresult(&b); /* L: chunk ffi cdef str */
+       assert_lua(lua_pcall(L, 1, 0, 0)); /* L: chunk ffi */
+       lua_newtable(L); /* L: chunk ffi table */
+       lua_getfield(L, -2, "cast"); /* L: chunk ffi table cast */
+       lua_pushstring(L, "struct strace_luajit_funcs *"); /* L: chunk ffi 
table cast str */
+       lua_pushlightuserdata(L, &funcs); /* L: chunk ffi table cast str ptr */
+
+       assert_lua(lua_pcall(L, 2, 1, 0)); /* L: chunk ffi table funcs */
+
+#define X(rettype, name, ...)                                          \
+       lua_getfield(L, -1, #name); /* L: chunk ffi table funcs func */ \
+       lua_setfield(L, -3, #name); /* L: chunk ffi table funcs */
+LUAJIT_FUNCS_XPAND()
+#undef X
+
+       lua_pop(L, 1); /* L: chunk ffi table */
+
+       lua_pushinteger(L, SUPPORTED_PERSONALITIES); /* L: chunk ffi table 
number */
+       lua_setfield(L, -2, "npersonalities"); /* L: chunk ffi table */
+
+#define EXPOSE(type, ptr, name)                                                
                \
+       lua_getfield(L, -2, "cast"); /* L: chunk ffi table cast */              
        \
+       lua_pushstring(L, type); /* L: chunk ffi table cast str */              
        \
+       lua_pushlightuserdata(L, (void *) ptr); /* L: chunk ffi table cast str 
ptr */   \
+       assert_lua(lua_pcall(L, 2, 1, 0)); /* L: chunk ffi table value */       
        \
+       lua_setfield(L, -2, name); /* L: chunk ffi table */
+
+       EXPOSE("const struct_sysent **", sysent_vec, "sysent_vec")
+       EXPOSE("const char ***", errnoent_vec, "errnoent_vec")
+       EXPOSE("const char ***", signalent_vec, "signalent_vec")
+       EXPOSE("const struct_ioctlent **", ioctlent_vec, "ioctlent_vec");
+
+       EXPOSE("const unsigned int *", nsyscall_vec, /*(!)*/ "nsysent_vec")
+       EXPOSE("const unsigned int *", nerrnoent_vec, "nerrnoent_vec")
+       EXPOSE("const unsigned int *", nsignalent_vec, "nsignalent_vec")
+       EXPOSE("const unsigned int *", nioctlent_vec, "nioctlent_vec")
+
+       EXPOSE("const struct syscall_class *", syscall_classes, 
"syscall_classes")
+
+#undef EXPOSE
+
+       lua_setglobal(L, "strace"); /* L: chunk ffi */
+       lua_pop(L, 1); /* L: chunk */
+
+       const char *code =
+#include "luajit_lib.h"
+       ;
+       assert_lua(luaL_dostring(L, code));
+}
+
+static void ATTRIBUTE_NORETURN
+run_luajit(void)
+{
+       /* L: chunk */
+       check_lua(lua_pcall(L, 0, 0, 0)); /* L: - */
+
+       lua_getglobal(L, "strace"); /* L: strace */
+       lua_getfield(L, -1, "_run"); /* L: strace _run */
+       check_lua(lua_pcall(L, 0, 0, 0)); /* L: strace */
+
+       terminate();
+}
+
+#undef assert_lua
+#undef LUAJIT_FUNC
+#undef L
diff --git a/luajit_funcs.h b/luajit_funcs.h
new file mode 100644
index 00000000..599a5c9a
--- /dev/null
+++ b/luajit_funcs.h
@@ -0,0 +1,28 @@
+#if !defined(STRACE_LUAJIT_FUNCS_H) || defined(FFI_CDEF)
+#ifndef FFI_CDEF
+# define STRACE_LUAJIT_FUNCS_H
+#endif
+
+#include "ffi.h"
+
+#define LUAJIT_FUNCS_XPAND()                                                   
\
+       X(struct tcb *, next_sc,        void)                                   
\
+       X(bool,         monitor,        unsigned int scno, unsigned int pers,   
\
+                                       bool entry_hook, bool exit_hook)        
\
+       X(bool,         inject_signo,   struct tcb *, int signo)                
\
+       X(bool,         inject_retval,  struct tcb *, int val)                  
\
+       X(int,          umoven,         struct tcb *, kernel_ulong_t addr,      
\
+                                       unsigned int len, void *laddr)          
\
+       X(int,          umovestr,       struct tcb *, kernel_ulong_t addr,      
\
+                                       unsigned int len, char *laddr)          
\
+       /* end */
+
+#define X(rettype, name, ...) rettype (*name)(__VA_ARGS__);
+FFI_CONTENT(
+struct strace_luajit_funcs {
+       LUAJIT_FUNCS_XPAND()
+};
+)
+#undef X
+
+#endif /* !defined(STRACE_LUAJIT_FUNCS_H) || defined(FFI_CDEF) */
diff --git a/luajit_lib.h b/luajit_lib.h
new file mode 100644
index 00000000..0b60f57d
--- /dev/null
+++ b/luajit_lib.h
@@ -0,0 +1,224 @@
+#define STRINGIFY(...) #__VA_ARGS__
+#define STRVAL(...)    STRINGIFY(__VA_ARGS__)
+
+"do\n\
+       local ffi = require 'ffi'\n\
+       local bit = require 'bit'\n\
+       strace._en, strace._ex = {}, {}\n\
+       for p = 0, strace.npersonalities - 1 do\n\
+               strace._en[p] = {}\n\
+               strace._ex[p] = {}\n\
+       end\n\
+       local function chain(f, g)\n\
+               if not f then return g end\n\
+               return function(...)\n\
+                       f(...)\n\
+                       g(...)\n\
+               end\n\
+       end\n\
+       function strace.entering(tcp)\n\
+               return bit.band(tcp.flags, " STRVAL(TCB_INSYSCALL) ") == 0\n\
+       end\n\
+       function strace.exiting(tcp)\n\
+               return bit.band(tcp.flags, " STRVAL(TCB_INSYSCALL) ") ~= 0\n\
+       end\n\
+\n\
+       -- `arg' defaults to true.\n\
+       local function alter_trace_opt(flagbit, tcp, arg)\n\
+               if strace.exiting(tcp) then\n\
+                       error('altering tracing options must be done on syscall 
entering')\n\
+               end\n\
+               -- we want to support custom boolean-like objects, so check for 
nil explicitly\n\
+               if type(arg) == 'nil' or arg then\n\
+                       tcp.qual_flg = bit.bor(tcp.qual_flg, flagbit)\n\
+               else\n\
+                       tcp.qual_flg = bit.band(tcp.qual_flg, 
bit.bnot(flagbit))\n\
+               end\n\
+       end\n\
+       function strace.trace  (tcp, arg) alter_trace_opt(" STRVAL(QUAL_TRACE)  
 ", tcp, arg) end\n\
+       function strace.abbrev (tcp, arg) alter_trace_opt(" STRVAL(QUAL_ABBREV) 
 ", tcp, arg) end\n\
+       function strace.verbose(tcp, arg) alter_trace_opt(" 
STRVAL(QUAL_VERBOSE) ", tcp, arg) end\n\
+       function strace.raw    (tcp, arg) alter_trace_opt(" STRVAL(QUAL_RAW)    
 ", tcp, arg) end\n\
+\n\
+       function strace.ptr_to_kulong(v)\n\
+               return ffi.cast('kernel_ulong_t', ffi.cast('unsigned long', 
v))\n\
+       end\n\
+       function strace.at_exit(f)\n\
+               strace._at_exit = chain(strace._at_exit, f)\n\
+       end\n\
+       function strace.get_err_name(err, pers)\n\
+               pers = pers or 0\n\
+               if err < 0 or err > strace.nerrnoent_vec[pers] then\n\
+                       return nil\n\
+               end\n\
+               local s = strace.errnoent_vec[pers][err]\n\
+               if s ~= nil then\n\
+                       return ffi.string(s)\n\
+               end\n\
+               return nil\n\
+       end\n\
+       function strace.get_sc_name(scno, pers)\n\
+               pers = pers or 0\n\
+               if scno < 0 or scno >= strace.nsysent_vec[pers] then\n\
+                       return nil\n\
+               end\n\
+               local s = strace.sysent_vec[pers][scno].sys_name\n\
+               if s ~= nil then\n\
+                       return ffi.string(s)\n\
+               end\n\
+               return nil\n\
+       end\n\
+       function strace.get_scno(scname, pers)\n\
+               pers = pers or 0\n\
+               for i = 0, tonumber(strace.nsysent_vec[pers]) - 1 do\n\
+                       local s = strace.sysent_vec[pers][i].sys_name\n\
+                       if s ~= nil and ffi.string(s) == scname then\n\
+                               return i\n\
+                       end\n\
+               end\n\
+               return nil\n\
+       end\n\
+       function strace.get_ioctl_name(code, pers)\n\
+               pers = pers or 0\n\
+               -- we could have provided a definition for stdlib's bsearch() 
and used it, but LuaJIT's FFI manual\n\
+               -- says generated callbacks are a limited resource and also 
slow. So implement binary search ourselves.\n\
+               local lb, rb = ffi.cast('unsigned int', 0), 
strace.nioctlent_vec[pers]\n\
+               if rb == 0 then\n\
+                       return nil\n\
+               end\n\
+               local arr = strace.ioctlent_vec[pers]\n\
+               while rb - lb > 1 do\n\
+                       local mid = lb + (rb - lb) / 2\n\
+                       if arr[mid].code <= code then\n\
+                               lb = mid\n\
+                       else\n\
+                               rb = mid\n\
+                       end\n\
+               end\n\
+               if arr[lb].code == code then\n\
+                       return ffi.string(arr[lb].symbol)\n\
+               end\n\
+               return nil\n\
+       end\n\
+       function strace.get_signo(signame, pers)\n\
+               pers = pers or 0\n\
+               for i = 0, tonumber(strace.nsignalent_vec[pers]) - 1 do\n\
+                       local s = strace.signalent_vec[pers][i]\n\
+                       if s ~= nil and ffi.string(s) == signame then\n\
+                               return i\n\
+                       end\n\
+               end\n\
+               return nil\n\
+       end\n\
+       function strace.inject_signal(tcp, sig)\n\
+               if type(sig) == 'string' then\n\
+                       sig = strace.get_signo(sig, tcp.currpers)\n\
+                       if not sig then\n\
+                               error('signal not found')\n\
+                       end\n\
+               end\n\
+               if not strace.inject_signo(tcp, sig) then\n\
+                       error('cannot inject signal')\n\
+               end\n\
+       end\n\
+       function strace.get_errno(errname, pers)\n\
+               pers = pers or 0\n\
+               for i = 0, tonumber(strace.nerrnoent_vec[pers]) - 1 do\n\
+                       local s = strace.errnoent_vec[pers][i]\n\
+                       if s ~= nil and ffi.string(s) == errname then\n\
+                               return i\n\
+                       end\n\
+               end\n\
+               return nil\n\
+       end\n\
+       function strace.inject_error(tcp, err)\n\
+               if type(err) == 'string' then\n\
+                       err = strace.get_errno(err, tcp.currpers)\n\
+                       if not err then\n\
+                               error('error not found')\n\
+                       end\n\
+               end\n\
+               if err <= 0 then\n\
+                       error('err must be positive')\n\
+               end\n\
+               if not strace.inject_retval(tcp, -err) then\n\
+                       error('cannot inject error')\n\
+               end\n\
+       end\n\
+       local function register_hook(scno, pers, en, ex, cb)\n\
+               assert(not not strace.monitor(scno, pers, en, ex))\n\
+               if en then\n\
+                       strace._en[pers][scno] = chain(strace._en[pers][scno], 
cb)\n\
+               end\n\
+               if ex then\n\
+                       strace._ex[pers][scno] = chain(strace._ex[pers][scno], 
cb)\n\
+               end\n\
+       end\n\
+       local function parse_when(when)\n\
+               if when == 'entering' then\n\
+                       return true, false\n\
+               elseif when == 'exiting' then\n\
+                       return false, true\n\
+               elseif when == 'both' then\n\
+                       return true, true\n\
+               else\n\
+                       error('unknown \"when\" value')\n\
+               end\n\
+       end\n\
+       function strace.hook(scname, when, cb)\n\
+               local en, ex = parse_when(when)\n\
+               local found = false\n\
+               for p = 0, strace.npersonalities - 1 do\n\
+                       local scno = strace.get_scno(scname, p)\n\
+                       if scno then\n\
+                               register_hook(scno, p, en, ex, cb)\n\
+                               found = true\n\
+                       end\n\
+               end\n\
+               if not found then\n\
+                       error('syscall not found')\n\
+               end\n\
+       end\n\
+       function strace.hook_class(clsname, when, cb)\n\
+               local en, ex = parse_when(when)\n\
+               local flag = nil\n\
+               local ptr = strace.syscall_classes\n\
+               while ptr.name ~= nil do\n\
+                       if ffi.string(ptr.name) == clsname then\n\
+                               flag = ptr.value\n\
+                               break\n\
+                       end\n\
+                       ptr = ptr + 1\n\
+               end\n\
+               if not flag then\n\
+                       error('syscall class not found')\n\
+               end\n\
+               for p = 0, strace.npersonalities - 1 do\n\
+                       for i = 0, tonumber(strace.nsysent_vec[p]) - 1 do\n\
+                               if bit.band(strace.sysent_vec[p][i].sys_flags, 
flag) ~= 0 then\n\
+                                       register_hook(i, p, en, ex, cb)\n\
+                               end\n\
+                       end\n\
+               end\n\
+       end\n\
+       function strace._run()\n\
+               while true do\n\
+                       local tcp = strace.next_sc()\n\
+                       if tcp == nil then break end\n\
+                       local cb = (strace.entering(tcp) and strace._en or 
strace._ex)[tonumber(tcp.currpers)][tonumber(tcp.scno)]\n\
+                       cb(tcp)\n\
+               end\n\
+               if strace._at_exit then strace._at_exit() end\n\
+       end\n\
+       function print(...)\n\
+               local sep = ''\n\
+               for i = 1, select('#', ...) do\n\
+                       io.stderr:write(sep .. tostring(select(i, ...)))\n\
+                       sep = '\\t'\n\
+               end\n\
+               io.stderr:write('\\n')\n\
+       end\n\
+end"
+
+#undef STRINGIFY
+#undef STRVAL
diff --git a/mpers_type.h b/mpers_type.h
index ecb1efa8..26c00767 100644
--- a/mpers_type.h
+++ b/mpers_type.h
@@ -31,8 +31,8 @@
 #define STRACE_MPERS_TYPE_H
 
 #ifdef IN_MPERS
-# define STRINGIFY(a) #a
-# define DEF_MPERS_TYPE(args) STRINGIFY(args.h)
+# define MPERS_STRINGIFY(a) #a
+# define DEF_MPERS_TYPE(args) MPERS_STRINGIFY(args.h)
 # ifdef MPERS_IS_m32
 #  define MPERS_PREFIX m32_
 #  define MPERS_DEFS "m32_type_defs.h"
diff --git a/qualify.c b/qualify.c
index 3df4805a..894d002b 100644
--- a/qualify.c
+++ b/qualify.c
@@ -28,8 +28,34 @@
 
 #include "defs.h"
 #include "nsig.h"
+#include "syscall_class.h"
 #include <regex.h>
 
+const struct syscall_class syscall_classes[] = {
+       { "desc",       TRACE_DESC      },
+       { "file",       TRACE_FILE      },
+       { "memory",     TRACE_MEMORY    },
+       { "process",    TRACE_PROCESS   },
+       { "signal",     TRACE_SIGNAL    },
+       { "ipc",        TRACE_IPC       },
+       { "network",    TRACE_NETWORK   },
+       { "%desc",      TRACE_DESC      },
+       { "%file",      TRACE_FILE      },
+       { "%memory",    TRACE_MEMORY    },
+       { "%process",   TRACE_PROCESS   },
+       { "%signal",    TRACE_SIGNAL    },
+       { "%ipc",       TRACE_IPC       },
+       { "%network",   TRACE_NETWORK   },
+       { "%stat",      TRACE_STAT      },
+       { "%lstat",     TRACE_LSTAT     },
+       { "%fstat",     TRACE_FSTAT     },
+       { "%%stat",     TRACE_STAT_LIKE },
+       { "%statfs",    TRACE_STATFS    },
+       { "%fstatfs",   TRACE_FSTATFS   },
+       { "%%statfs",   TRACE_STATFS_LIKE       },
+       {}
+};
+
 typedef unsigned int number_slot_t;
 #define BITS_PER_SLOT (sizeof(number_slot_t) * 8)
 
@@ -48,6 +74,10 @@ static struct number_set inject_set[SUPPORTED_PERSONALITIES];
 static struct number_set raw_set[SUPPORTED_PERSONALITIES];
 static struct number_set trace_set[SUPPORTED_PERSONALITIES];
 static struct number_set verbose_set[SUPPORTED_PERSONALITIES];
+#ifdef USE_LUAJIT
+static struct number_set hook_entry_set[SUPPORTED_PERSONALITIES];
+static struct number_set hook_exit_set[SUPPORTED_PERSONALITIES];
+#endif
 
 static void
 number_setbit(const unsigned int i, number_slot_t *const vec)
@@ -245,37 +275,10 @@ qualify_syscall_regex(const char *s, struct number_set 
*set)
 static unsigned int
 lookup_class(const char *s)
 {
-       static const struct {
-               const char *name;
-               unsigned int value;
-       } syscall_class[] = {
-               { "desc",       TRACE_DESC      },
-               { "file",       TRACE_FILE      },
-               { "memory",     TRACE_MEMORY    },
-               { "process",    TRACE_PROCESS   },
-               { "signal",     TRACE_SIGNAL    },
-               { "ipc",        TRACE_IPC       },
-               { "network",    TRACE_NETWORK   },
-               { "%desc",      TRACE_DESC      },
-               { "%file",      TRACE_FILE      },
-               { "%memory",    TRACE_MEMORY    },
-               { "%process",   TRACE_PROCESS   },
-               { "%signal",    TRACE_SIGNAL    },
-               { "%ipc",       TRACE_IPC       },
-               { "%network",   TRACE_NETWORK   },
-               { "%stat",      TRACE_STAT      },
-               { "%lstat",     TRACE_LSTAT     },
-               { "%fstat",     TRACE_FSTAT     },
-               { "%%stat",     TRACE_STAT_LIKE },
-               { "%statfs",    TRACE_STATFS    },
-               { "%fstatfs",   TRACE_FSTATFS   },
-               { "%%statfs",   TRACE_STATFS_LIKE       },
-       };
-
-       unsigned int i;
-       for (i = 0; i < ARRAY_SIZE(syscall_class); ++i) {
-               if (strcmp(s, syscall_class[i].name) == 0) {
-                       return syscall_class[i].value;
+       const struct syscall_class *c;
+       for (c = syscall_classes; c->name; ++c) {
+               if (strcmp(s, c->name) == 0) {
+                       return c->value;
                }
        }
 
@@ -693,5 +696,23 @@ qual_flags(const unsigned int scno)
                | (is_number_in_set(scno, &raw_set[current_personality])
                   ? QUAL_RAW : 0)
                | (is_number_in_set(scno, &inject_set[current_personality])
-                  ? QUAL_INJECT : 0);
+                  ? QUAL_INJECT : 0)
+#ifdef USE_LUAJIT
+               | (is_number_in_set(scno, &hook_entry_set[current_personality])
+                  ? QUAL_HOOK_ENTRY : 0)
+               | (is_number_in_set(scno, &hook_exit_set[current_personality])
+                  ? QUAL_HOOK_EXIT : 0)
+#endif
+               ;
+}
+
+#ifdef USE_LUAJIT
+void
+set_hook_qual(unsigned int scno, unsigned int pers, bool entry_hook, bool 
exit_hook)
+{
+       if (entry_hook)
+               add_number_to_set(scno, &hook_entry_set[pers]);
+       if (exit_hook)
+               add_number_to_set(scno, &hook_exit_set[pers]);
 }
+#endif
diff --git a/strace.c b/strace.c
index 614ab9ec..2972cc20 100644
--- a/strace.c
+++ b/strace.c
@@ -45,10 +45,16 @@
 # include <sys/prctl.h>
 #endif
 #include <asm/unistd.h>
+#ifdef USE_LUAJIT
+# include <lua.h>
+# include <lualib.h>
+# include <lauxlib.h>
+#endif
 
 #include "scno.h"
 #include "ptrace.h"
 #include "printsiginfo.h"
+#include "syscall_class.h"
 
 /* In some libc, these aren't declared. Do it ourself: */
 extern char **environ;
@@ -166,6 +172,11 @@ static volatile sig_atomic_t interrupted;
 static volatile int interrupted;
 #endif
 
+#ifdef USE_LUAJIT
+static lua_State *script_L = NULL;
+static void init_luajit(const char *scriptfile);
+#endif
+
 #ifndef HAVE_STRERROR
 
 #if !HAVE_DECL_SYS_ERRLIST
@@ -216,6 +227,11 @@ Output format:\n\
   -k             obtain stack trace between each syscall (experimental)\n\
 "
 #endif
+#ifdef USE_LUAJIT
+"\
+  -l file        run a Lua script from FILE\n\
+"
+#endif
 "\
   -o file        send trace output to FILE instead of stderr\n\
   -q             suppress messages about attaching, detaching, etc.\n\
@@ -766,7 +782,7 @@ alloctcb(int pid)
                if (!tcp->pid) {
                        memset(tcp, 0, sizeof(*tcp));
                        tcp->pid = pid;
-#if SUPPORTED_PERSONALITIES > 1
+#if defined(USE_LUAJIT) || SUPPORTED_PERSONALITIES > 1
                        tcp->currpers = current_personality;
 #endif
 
@@ -1638,6 +1654,9 @@ init(int argc, char *argv[])
 #ifdef USE_LIBUNWIND
                "k"
 #endif
+#ifdef USE_LUAJIT
+               "l:"
+#endif
                "D"
                "a:e:o:O:p:s:S:u:E:P:I:")) != EOF) {
                switch (c) {
@@ -1748,6 +1767,11 @@ init(int argc, char *argv[])
                        stack_trace_enabled = true;
                        break;
 #endif
+#ifdef USE_LUAJIT
+               case 'l':
+                       init_luajit(optarg);
+                       break;
+#endif
                case 'E':
                        if (putenv(optarg) < 0)
                                die_out_of_memory();
@@ -2628,6 +2652,10 @@ terminate(void)
        exit(exit_code);
 }
 
+#ifdef USE_LUAJIT
+# include "luajit.h"
+#endif
+
 int
 main(int argc, char *argv[])
 {
@@ -2635,6 +2663,11 @@ main(int argc, char *argv[])
 
        exit_code = !nprocs;
 
+#ifdef USE_LUAJIT
+       if (script_L)
+               run_luajit();
+#endif
+
        int status;
        siginfo_t si;
        while (dispatch_event(next_event(&status, &si), &status, &si))
diff --git a/syscall.c b/syscall.c
index 02626c73..955ed2f4 100644
--- a/syscall.c
+++ b/syscall.c
@@ -196,6 +196,16 @@ enum {
 #endif
 };
 
+const char *const *errnoent_vec[SUPPORTED_PERSONALITIES] = {
+       errnoent0,
+#if SUPPORTED_PERSONALITIES > 1
+       errnoent1,
+# if SUPPORTED_PERSONALITIES > 2
+       errnoent2,
+# endif
+#endif
+};
+
 enum {
        nerrnos0 = ARRAY_SIZE(errnoent0)
 #if SUPPORTED_PERSONALITIES > 1
@@ -206,6 +216,16 @@ enum {
 #endif
 };
 
+const unsigned int nerrnoent_vec[] = {
+       nerrnos0,
+#if SUPPORTED_PERSONALITIES > 1
+       nerrnos1,
+# if SUPPORTED_PERSONALITIES > 2
+       nerrnos2,
+# endif
+#endif
+};
+
 enum {
        nsignals0 = ARRAY_SIZE(signalent0)
 #if SUPPORTED_PERSONALITIES > 1
@@ -216,6 +236,26 @@ enum {
 #endif
 };
 
+const char *const *signalent_vec[SUPPORTED_PERSONALITIES] = {
+       signalent0,
+#if SUPPORTED_PERSONALITIES > 1
+       signalent1,
+# if SUPPORTED_PERSONALITIES > 2
+       signalent2,
+# endif
+#endif
+};
+
+const unsigned int nsignalent_vec[] = {
+       nsignals0,
+#if SUPPORTED_PERSONALITIES > 1
+       nsignals1,
+# if SUPPORTED_PERSONALITIES > 2
+       nsignals2,
+# endif
+#endif
+};
+
 enum {
        nioctlents0 = ARRAY_SIZE(ioctlent0)
 #if SUPPORTED_PERSONALITIES > 1
@@ -226,6 +266,26 @@ enum {
 #endif
 };
 
+const unsigned int nioctlent_vec[] = {
+       nioctlents0,
+#if SUPPORTED_PERSONALITIES > 1
+       nioctlents1,
+# if SUPPORTED_PERSONALITIES > 2
+       nioctlents2,
+# endif
+#endif
+};
+
+const struct_ioctlent *const ioctlent_vec[SUPPORTED_PERSONALITIES] = {
+       ioctlent0,
+#if SUPPORTED_PERSONALITIES > 1
+       ioctlent1,
+# if SUPPORTED_PERSONALITIES > 2
+       ioctlent2,
+# endif
+#endif
+};
+
 #if SUPPORTED_PERSONALITIES > 1
 const struct_sysent *sysent = sysent0;
 const char *const *errnoent = errnoent0;
@@ -580,8 +640,19 @@ static int arch_set_success(struct tcb *);
 struct inject_opts *inject_vec[SUPPORTED_PERSONALITIES];
 
 static struct inject_opts *
-tcb_inject_opts(struct tcb *tcp)
+tcb_inject_opts(struct tcb *tcp, bool copy_if_needed)
 {
+#ifdef USE_LUAJIT
+       if (tcp->flags & TCB_AD_HOC_INJECT)
+               return tcp->ad_hoc_inject_opts;
+#endif
+       if (copy_if_needed && !tcp->inject_vec[current_personality]) {
+               tcp->inject_vec[current_personality] =
+                       xcalloc(nsyscalls, sizeof(**inject_vec));
+               memcpy(tcp->inject_vec[current_personality],
+                      inject_vec[current_personality],
+                      nsyscalls * sizeof(**inject_vec));
+       }
        return (scno_in_range(tcp->scno) && 
tcp->inject_vec[current_personality])
               ? &tcp->inject_vec[current_personality][tcp->scno] : NULL;
 }
@@ -590,15 +661,7 @@ tcb_inject_opts(struct tcb *tcp)
 static long
 tamper_with_syscall_entering(struct tcb *tcp, unsigned int *signo)
 {
-       if (!tcp->inject_vec[current_personality]) {
-               tcp->inject_vec[current_personality] =
-                       xcalloc(nsyscalls, sizeof(**inject_vec));
-               memcpy(tcp->inject_vec[current_personality],
-                      inject_vec[current_personality],
-                      nsyscalls * sizeof(**inject_vec));
-       }
-
-       struct inject_opts *opts = tcb_inject_opts(tcp);
+       struct inject_opts *opts = tcb_inject_opts(tcp, true);
 
        if (!opts || opts->first == 0)
                return 0;
@@ -621,7 +684,7 @@ tamper_with_syscall_entering(struct tcb *tcp, unsigned int 
*signo)
 static long
 tamper_with_syscall_exiting(struct tcb *tcp)
 {
-       struct inject_opts *opts = tcb_inject_opts(tcp);
+       struct inject_opts *opts = tcb_inject_opts(tcp, false);
 
        if (!opts)
                return 0;
@@ -699,6 +762,12 @@ syscall_entering_decode(struct tcb *tcp)
        return 1;
 }
 
+static bool
+syscall_ad_hoc_injected(struct tcb *tcp)
+{
+       return (tcp->qual_flg & QUAL_INJECT) && (tcp->flags & 
TCB_AD_HOC_INJECT);
+}
+
 int
 syscall_entering_trace(struct tcb *tcp, unsigned int *sig)
 {
@@ -721,13 +790,13 @@ syscall_entering_trace(struct tcb *tcp, unsigned int *sig)
         || (tracing_paths && !pathtrace_match(tcp))
        ) {
                tcp->flags |= TCB_FILTERED;
-               return 0;
+               goto maybe_ad_hoc_tamper;
        }
 
        tcp->flags &= ~TCB_FILTERED;
 
        if (hide_log(tcp)) {
-               return 0;
+               goto maybe_ad_hoc_tamper;
        }
 
        if (tcp->qual_flg & QUAL_INJECT)
@@ -750,6 +819,11 @@ syscall_entering_trace(struct tcb *tcp, unsigned int *sig)
                ? printargs(tcp) : tcp->s_ent->sys_func(tcp);
        fflush(tcp->outf);
        return res;
+
+maybe_ad_hoc_tamper:
+       if (syscall_ad_hoc_injected(tcp))
+               tamper_with_syscall_entering(tcp, sig);
+       return 0;
 }
 
 void
@@ -790,21 +864,28 @@ syscall_exiting_decode(struct tcb *tcp, struct timeval 
*ptv)
        }
 #endif
 
-       if (filtered(tcp) || hide_log(tcp))
+       if ((filtered(tcp) || hide_log(tcp))
+        && !(tcp->qual_flg & QUAL_HOOK_EXIT) && !syscall_ad_hoc_injected(tcp))
                return 0;
 
        get_regs(tcp->pid);
 #if SUPPORTED_PERSONALITIES > 1
        update_personality(tcp, tcp->currpers);
 #endif
-       return get_regs_error ? -1 : get_syscall_result(tcp);
+       if (get_regs_error || get_syscall_result(tcp) == -1)
+               return -1;
+
+       if (syserror(tcp) && syscall_tampered(tcp))
+               tamper_with_syscall_exiting(tcp);
+
+       return 1;
 }
 
 int
 syscall_exiting_trace(struct tcb *tcp, struct timeval tv, int res)
 {
-       if (syserror(tcp) && syscall_tampered(tcp))
-               tamper_with_syscall_exiting(tcp);
+       if (filtered(tcp) || hide_log(tcp))
+               return 0;
 
        if (cflag) {
                count_syscall(tcp, &tv);
@@ -1013,7 +1094,7 @@ syscall_exiting_trace(struct tcb *tcp, struct timeval tv, 
int res)
 void
 syscall_exiting_finish(struct tcb *tcp)
 {
-       tcp->flags &= ~(TCB_INSYSCALL | TCB_TAMPERED);
+       tcp->flags &= ~(TCB_INSYSCALL | TCB_TAMPERED | TCB_AD_HOC_INJECT);
        tcp->sys_func_rval = 0;
        free_tcb_priv_data(tcp);
 }
diff --git a/syscall_class.h b/syscall_class.h
new file mode 100644
index 00000000..7bdd0cb2
--- /dev/null
+++ b/syscall_class.h
@@ -0,0 +1,19 @@
+#if !defined(STRACE_SYSCALL_CLASS_H) || defined(FFI_CDEF)
+#ifndef FFI_CDEF
+# define STRACE_SYSCALL_CLASS_H
+#endif
+
+#include "ffi.h"
+
+FFI_CONTENT(
+struct syscall_class {
+       const char *name;
+       unsigned int value;
+};
+)
+
+#ifndef FFI_CDEF
+extern const struct syscall_class syscall_classes[];
+#endif
+
+#endif /* !defined(STRACE_SYSCALL_CLASS_H) || defined(FFI_CDEF) */
diff --git a/sysent.h b/sysent.h
index 92de7468..3409d296 100644
--- a/sysent.h
+++ b/sysent.h
@@ -1,13 +1,30 @@
-#ifndef STRACE_SYSENT_H
-#define STRACE_SYSENT_H
+#if !defined(STRACE_SYSENT_H) || defined(FFI_CDEF)
+#ifndef FFI_CDEF
+# define STRACE_SYSENT_H
+#endif
 
+#include "ffi.h"
+
+FFI_CONTENT(
 typedef struct sysent {
        unsigned nargs;
        int     sys_flags;
        int     sen;
+)
+/* We don't want to expose sys_func to LuaJIT */
+#ifdef FFI_CDEF
+FFI_CONTENT(
+       void *priv;
+)
+#else
+FFI_CONTENT(
        int     (*sys_func)();
+)
+#endif
+FFI_CONTENT(
        const char *sys_name;
 } struct_sysent;
+)
 
 #define TRACE_FILE                     00000001        /* Trace file-related 
syscalls. */
 #define TRACE_IPC                      00000002        /* Trace IPC-related 
syscalls. */
@@ -29,4 +46,4 @@ typedef struct sysent {
 #define TRACE_FSTAT                    00400000        /* Trace 
*fstat{,at}{,64} syscalls. */
 #define TRACE_STAT_LIKE                        01000000        /* Trace 
*{,l,f}stat{,x,at}{,64} syscalls. */
 
-#endif /* !STRACE_SYSENT_H */
+#endif /* !defined(STRACE_SYSENT_H) || defined(FFI_CDEF) */
-- 
2.11.0


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Strace-devel mailing list
Strace-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/strace-devel

Reply via email to