summaryrefslogtreecommitdiffstats
path: root/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'runtime')
-rw-r--r--runtime/staprun/common.c7
-rw-r--r--runtime/staprun/staprun.c77
-rw-r--r--runtime/staprun/staprun.h4
-rw-r--r--runtime/staprun/staprun_funcs.c27
-rw-r--r--runtime/uprobes/Makefile10
-rw-r--r--runtime/uprobes/uprobes.c112
-rw-r--r--runtime/uprobes/uprobes.h8
-rw-r--r--runtime/uprobes/uprobes_i386.c6
8 files changed, 136 insertions, 115 deletions
diff --git a/runtime/staprun/common.c b/runtime/staprun/common.c
index 9056c710..d3f8835a 100644
--- a/runtime/staprun/common.c
+++ b/runtime/staprun/common.c
@@ -23,6 +23,7 @@ char *target_cmd;
char *outfile_name;
int attach_mod;
int load_only;
+int need_uprobes;
/* module variables */
char *modname = NULL;
@@ -44,9 +45,13 @@ void parse_args(int argc, char **argv)
outfile_name = NULL;
attach_mod = 0;
load_only = 0;
+ need_uprobes = 0;
- while ((c = getopt(argc, argv, "ALvb:t:d:c:o:x:")) != EOF) {
+ while ((c = getopt(argc, argv, "ALuvb:t:d:c:o:x:")) != EOF) {
switch (c) {
+ case 'u':
+ need_uprobes = 1;
+ break;
case 'v':
verbose++;
break;
diff --git a/runtime/staprun/staprun.c b/runtime/staprun/staprun.c
index 71039e0b..67b01abb 100644
--- a/runtime/staprun/staprun.c
+++ b/runtime/staprun/staprun.c
@@ -35,6 +35,16 @@ run_as(uid_t uid, gid_t gid, const char *path, char *const argv[])
pid_t pid;
int rstatus;
+ if (verbose >= 2) {
+ int i = 0;
+ err("execing: ");
+ while (argv[i]) {
+ err("%s ", argv[i]);
+ i++;
+ }
+ err("\n");
+ }
+
if ((pid = fork()) < 0) {
_perr("fork");
return -1;
@@ -77,18 +87,63 @@ static int run_stapio(char **argv)
gid_t gid = getgid();
argv[0] = PKGLIBDIR "/stapio";
- if (verbose >= 2) {
- int i = 0;
- err("execing: ");
- while (argv[i]) {
- err("%s ", argv[i]);
- i++;
- }
- err("\n");
- }
return run_as(uid, gid, argv[0], argv);
}
+/*
+ * Module to be inserted has one or more user-space probes. Make sure
+ * uprobes is enabled.
+ * If /proc/kallsyms lists a symbol in uprobes (e.g. unregister_uprobe),
+ * we're done.
+ * Else try "modprobe uprobes" to load the uprobes module (if any)
+ * built with the kernel.
+ * If that fails, load the uprobes module built in runtime/uprobes.
+ */
+static int enable_uprobes(void)
+{
+ int i;
+ char *argv[10];
+ uid_t uid = getuid();
+ gid_t gid = getgid();
+
+ i = 0;
+ argv[i++] = "/bin/grep";
+ argv[i++] = "-q";
+ argv[i++] = "unregister_uprobe";
+ argv[i++] = "/proc/kallsyms";
+ argv[i] = NULL;
+ if (run_as(uid, gid, argv[0], argv) == 0)
+ return 0;
+
+ /*
+ * TODO: If user can't setresuid to root here, staprun will exit.
+ * Is there a situation where that would fail but the subsequent
+ * attempt to use CAP_SYS_MODULE privileges (in insert_module())
+ * would succeed?
+ */
+ dbug(2, "Inserting uprobes module from /lib/modules, if any.\n");
+ i = 0;
+ argv[i++] = "/sbin/modprobe";
+ argv[i++] = "-q";
+ argv[i++] = "uprobes";
+ argv[i] = NULL;
+ if (run_as(0, 0, argv[0], argv) == 0)
+ return 0;
+
+ dbug(2, "Inserting uprobes module from SystemTap runtime.\n");
+ argv[0] = NULL;
+ return insert_module(PKGDATADIR "/runtime/uprobes/uprobes.ko",
+ NULL, argv);
+}
+
+static int insert_stap_module(void)
+{
+ char bufsize_option[128];
+ if (snprintf_chk(bufsize_option, 128, "_stp_bufsize=%d", buffer_size))
+ return -1;
+ return insert_module(modpath, bufsize_option, modoptions);
+}
+
int init_staprun(void)
{
@@ -101,7 +156,9 @@ int init_staprun(void)
drop_cap(CAP_SYS_ADMIN);
if (!attach_mod) {
- if (insert_module() < 0)
+ if (need_uprobes && enable_uprobes() != 0)
+ return -1;
+ if (insert_stap_module() < 0)
return -1;
else
inserted_module = 1;
diff --git a/runtime/staprun/staprun.h b/runtime/staprun/staprun.h
index cde44922..1b0f3221 100644
--- a/runtime/staprun/staprun.h
+++ b/runtime/staprun/staprun.h
@@ -141,7 +141,8 @@ void drop_cap(cap_value_t cap);
/* staprun_funcs.c */
void setup_staprun_signals(void);
const char *moderror(int err);
-int insert_module(void);
+int insert_module(const char *path, const char *special_options,
+ char **options);
int mountfs(void);
int check_permissions(void);
void handle_symbols(void);
@@ -171,6 +172,7 @@ extern char *target_cmd;
extern char *outfile_name;
extern int attach_mod;
extern int load_only;
+extern int need_uprobes;
/* getopt variables */
extern char *optarg;
diff --git a/runtime/staprun/staprun_funcs.c b/runtime/staprun/staprun_funcs.c
index 0747b530..eec4ae63 100644
--- a/runtime/staprun/staprun_funcs.c
+++ b/runtime/staprun/staprun_funcs.c
@@ -47,7 +47,7 @@ const char *moderror(int err)
}
}
-int insert_module(void)
+int insert_module(const char *path, const char *special_options, char **options)
{
int i;
long ret;
@@ -58,34 +58,35 @@ int insert_module(void)
dbug(2, "inserting module\n");
- opts = malloc(128);
+ if (special_options)
+ opts = strdup(special_options);
+ else
+ opts = strdup("");
if (opts == NULL) {
_perr("allocating memory failed");
return -1;
}
- if (snprintf_chk(opts, 128, "_stp_bufsize=%d", buffer_size))
- return -1;
- for (i = 0; modoptions[i] != NULL; i++) {
- opts = realloc(opts, strlen(opts) + strlen(modoptions[i]) + 2);
+ for (i = 0; options[i] != NULL; i++) {
+ opts = realloc(opts, strlen(opts) + strlen(options[i]) + 2);
if (opts == NULL) {
- _perr("reallocating memory failed");
+ _perr("[re]allocating memory failed");
return -1;
}
strcat(opts, " ");
- strcat(opts, modoptions[i]);
+ strcat(opts, options[i]);
}
dbug(2, "module options: %s\n", opts);
/* Open the module file. */
- fd = open(modpath, O_RDONLY);
+ fd = open(path, O_RDONLY);
if (fd < 0) {
- perr("Error opening '%s'", modpath);
+ perr("Error opening '%s'", path);
return -1;
}
/* Now that the file is open, figure out how big it is. */
if (fstat(fd, &sbuf) < 0) {
- _perr("Error stat'ing '%s'", modpath);
+ _perr("Error stat'ing '%s'", path);
close(fd);
return -1;
}
@@ -93,7 +94,7 @@ int insert_module(void)
/* mmap in the entire module. */
file = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (file == MAP_FAILED) {
- _perr("Error mapping '%s'", modpath);
+ _perr("Error mapping '%s'", path);
close(fd);
free(opts);
return -1;
@@ -109,7 +110,7 @@ int insert_module(void)
close(fd);
if (ret != 0) {
- err("Error inserting module '%s': %s\n", modpath, moderror(saved_errno));
+ err("Error inserting module '%s': %s\n", path, moderror(saved_errno));
return -1;
}
return 0;
diff --git a/runtime/uprobes/Makefile b/runtime/uprobes/Makefile
new file mode 100644
index 00000000..806f7c48
--- /dev/null
+++ b/runtime/uprobes/Makefile
@@ -0,0 +1,10 @@
+obj-m := uprobes.o
+KDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+
+default:
+ $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
+
+clean:
+ rm -f *.mod.c *.ko *.o .*.cmd *~
+ rm -rf .tmp_versions
diff --git a/runtime/uprobes/uprobes.c b/runtime/uprobes/uprobes.c
index 41d0ef11..16c5e046 100644
--- a/runtime/uprobes/uprobes.c
+++ b/runtime/uprobes/uprobes.c
@@ -59,36 +59,22 @@ typedef void (*uprobe_handler_t)(struct uprobe*, struct pt_regs*);
/* Point utask->active_probe at this while running uretprobe handler. */
static struct uprobe_probept uretprobe_trampoline_dummy_probe;
-/*
- * These data structures are shared by all SystemTap-generated modules
- * that use uprobes.
- */
-struct uprobe_globals {
- struct hlist_head uproc_table[UPROBE_TABLE_SIZE];
- struct mutex uproc_mutex;
- struct hlist_head utask_table[UPROBE_TABLE_SIZE];
- spinlock_t utask_table_lock;
-};
-static struct uprobe_globals *globals;
-static struct hlist_head *uproc_table; /* = globals->uproc_table */
-static struct hlist_head *utask_table; /* = globals->utask_table */
+/* Table of currently probed processes, hashed by tgid. */
+static struct hlist_head uproc_table[UPROBE_TABLE_SIZE];
-#define lock_uproc_table() mutex_lock(&globals->uproc_mutex)
-#define unlock_uproc_table() mutex_unlock(&globals->uproc_mutex)
+/* Protects uproc_table during uprobe (un)registration */
+static DEFINE_MUTEX(uproc_mutex);
-#define lock_utask_table(flags) \
- spin_lock_irqsave(&globals->utask_table_lock, (flags))
-#define unlock_utask_table(flags) \
- spin_unlock_irqrestore(&globals->utask_table_lock, (flags))
+/* Table of uprobe_tasks, hashed by task_struct pointer. */
+static struct hlist_head utask_table[UPROBE_TABLE_SIZE];
+static DEFINE_SPINLOCK(utask_table_lock);
-/*
- * uprobes_data and uprobes_mutex are the only uprobes hooks in the kernel.
- * A pointer to the uprobes_global area is stored in uprobe_data.
- */
-extern void *uprobes_data;
-extern struct mutex uprobes_mutex;
+#define lock_uproc_table() mutex_lock(&uproc_mutex)
+#define unlock_uproc_table() mutex_unlock(&uproc_mutex)
-static int verify_uprobes(void);
+#define lock_utask_table(flags) spin_lock_irqsave(&utask_table_lock, (flags))
+#define unlock_utask_table(flags) \
+ spin_unlock_irqrestore(&utask_table_lock, (flags))
/* p_uprobe_utrace_ops = &uprobe_utrace_ops. Fwd refs are a pain w/o this. */
static const struct utrace_engine_ops *p_uprobe_utrace_ops;
@@ -824,7 +810,6 @@ static int defer_registration(struct uprobe *u, int regflag,
}
/* See Documentation/uprobes.txt. */
-static
int register_uprobe(struct uprobe *u)
{
struct task_struct *p;
@@ -833,9 +818,6 @@ int register_uprobe(struct uprobe *u)
struct uprobe_probept *ppt;
struct uprobe_task *cur_utask, *cur_utask_quiescing = NULL;
int survivors, ret = 0, uproc_is_new = 0;
- if ((ret = verify_uprobes()) < 0)
- return ret;
-
if (!u || !u->handler)
return -EINVAL;
@@ -982,9 +964,9 @@ fail_tsk:
put_task_struct(p);
return ret;
}
+EXPORT_SYMBOL_GPL(register_uprobe);
/* See Documentation/uprobes.txt. */
-static
void unregister_uprobe(struct uprobe *u)
{
struct task_struct *p;
@@ -993,8 +975,6 @@ void unregister_uprobe(struct uprobe *u)
struct uprobe_probept *ppt;
struct uprobe_task *cur_utask, *cur_utask_quiescing = NULL;
- if (verify_uprobes() < 0)
- return;
if (!u)
return;
p = uprobe_get_task(u->pid);
@@ -1069,6 +1049,7 @@ done:
up_write(&uproc->rwsem);
uprobe_put_process(uproc);
}
+EXPORT_SYMBOL_GPL(unregister_uprobe);
/* Find a surviving thread in uproc. Runs with uproc->rwsem locked. */
static struct task_struct *find_surviving_thread(struct uprobe_process *uproc)
@@ -2071,64 +2052,26 @@ static const struct utrace_engine_ops uprobe_utrace_ops =
.report_exec = uprobe_report_exec
};
-/*
- * Initialize the uprobes global data area, and return a pointer
- * to it. Caller will initialize uprobes_data pointer afterward, to
- * ensure that no other module sees a non-null uprobes_data until it's
- * completely initialized.
- */
-static struct uprobe_globals *init_uprobes(void)
+static int __init init_uprobes(void)
{
int i;
- struct uprobe_globals *g = kmalloc(sizeof(*g), GFP_USER);
- if (!g)
- return ERR_PTR(-ENOMEM);
+
for (i = 0; i < UPROBE_TABLE_SIZE; i++) {
- INIT_HLIST_HEAD(&g->uproc_table[i]);
- INIT_HLIST_HEAD(&g->utask_table[i]);
+ INIT_HLIST_HEAD(&uproc_table[i]);
+ INIT_HLIST_HEAD(&utask_table[i]);
}
- mutex_init(&g->uproc_mutex);
- spin_lock_init(&g->utask_table_lock);
- return g;
+
+ p_uprobe_utrace_ops = &uprobe_utrace_ops;
+ return 0;
}
-/*
- * Verify that the uprobes globals area has been set up, and that the
- * current module's globals variable points at it. Returns 0 if the
- * area is successfully set up, or a negative erro otherwise.
- */
-static int verify_uprobes(void)
+static void __exit exit_uprobes(void)
{
- if (unlikely(!globals)) {
- /*
- * First time through for this instrumentation module.
- * uprobes_mutex protects both the global uprobes
- * initialization and this module's local initialization.
- */
- struct uprobe_globals *g;
-
- mutex_lock(&uprobes_mutex);
- if (!uprobes_data) {
- /* First time through since boot time */
- g = init_uprobes();
- uprobes_data = g;
- } else
- g = uprobes_data;
- if (!IS_ERR(g)) {
- p_uprobe_utrace_ops = &uprobe_utrace_ops;
- uproc_table = g->uproc_table;
- utask_table = g->utask_table;
- }
-
- /* Set globals pointer to signify all is initialized. */
- globals = g;
- mutex_unlock(&uprobes_mutex);
- }
- if (unlikely(IS_ERR(globals)))
- return (int) PTR_ERR(globals);
- return 0;
}
+module_init(init_uprobes);
+module_exit(exit_uprobes);
+
#ifdef CONFIG_URETPROBES
/* Called when the entry-point probe u is hit. */
@@ -2209,7 +2152,6 @@ static void uretprobe_handle_return(struct pt_regs *regs,
arch_restore_uret_addr(orig_ret_addr, regs);
}
-static
int register_uretprobe(struct uretprobe *rp)
{
if (!rp || !rp->handler)
@@ -2217,6 +2159,7 @@ int register_uretprobe(struct uretprobe *rp)
rp->u.handler = URETPROBE_HANDLE_ENTRY;
return register_uprobe(&rp->u);
}
+EXPORT_SYMBOL_GPL(register_uretprobe);
/*
* The uretprobe containing u is being unregistered. Its uretprobe_instances
@@ -2244,13 +2187,13 @@ static void zap_uretprobe_instances(struct uprobe *u,
}
}
-static
void unregister_uretprobe(struct uretprobe *rp)
{
if (!rp)
return;
unregister_uprobe(&rp->u);
}
+EXPORT_SYMBOL_GPL(unregister_uretprobe);
/*
* uproc->ssol_area has been successfully set up. Establish the
@@ -2295,3 +2238,4 @@ static void zap_uretprobe_instances(struct uprobe *u,
#endif /* CONFIG_URETPROBES */
#include "uprobes_arch.c"
+MODULE_LICENSE("GPL");
diff --git a/runtime/uprobes/uprobes.h b/runtime/uprobes/uprobes.h
index 574bee62..84dd0b2a 100644
--- a/runtime/uprobes/uprobes.h
+++ b/runtime/uprobes/uprobes.h
@@ -72,11 +72,11 @@ struct uretprobe_instance {
unsigned long reserved2;
};
-static int register_uprobe(struct uprobe *u);
-static void unregister_uprobe(struct uprobe *u);
+extern int register_uprobe(struct uprobe *u);
+extern void unregister_uprobe(struct uprobe *u);
/* For runtime, assume uprobes support includes uretprobes. */
-static int register_uretprobe(struct uretprobe *rp);
-static void unregister_uretprobe(struct uretprobe *rp);
+extern int register_uretprobe(struct uretprobe *rp);
+extern void unregister_uretprobe(struct uretprobe *rp);
#ifdef UPROBES_IMPLEMENTATION
diff --git a/runtime/uprobes/uprobes_i386.c b/runtime/uprobes/uprobes_i386.c
index 90d50ba0..21420681 100644
--- a/runtime/uprobes/uprobes_i386.c
+++ b/runtime/uprobes/uprobes_i386.c
@@ -18,8 +18,10 @@
*
* Copyright (C) IBM Corporation, 2006
*/
-#define UPROBES_IMPLEMENTATION 1
-#include "uprobes.h"
+/*
+ * In versions of uprobes built in the SystemTap runtime, this file
+ * is #included at the end of uprobes.c.
+ */
#include <linux/uaccess.h>
/* Adapted from arch/x86_64/kprobes.c */