diff options
Diffstat (limited to 'runtime/staprun/staprun.c')
-rw-r--r-- | runtime/staprun/staprun.c | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/runtime/staprun/staprun.c b/runtime/staprun/staprun.c index 0291d01f..664b75ee 100644 --- a/runtime/staprun/staprun.c +++ b/runtime/staprun/staprun.c @@ -21,12 +21,19 @@ */ #include "staprun.h" +#include <sys/uio.h> +#include <glob.h> + + /* used in dbug, _err and _perr */ char *__name__ = "staprun"; extern long delete_module(const char *, unsigned int); +int send_relocations (); + + static int run_as(uid_t uid, gid_t gid, const char *path, char *const argv[]) { pid_t pid; @@ -206,6 +213,8 @@ int init_staprun(void) return -1; if (insert_stap_module() < 0) return -1; + if (send_relocations() < 0) + return -1; } return 0; } @@ -288,3 +297,178 @@ err: remove_module(modname, 1); return 1; } + + + +/* Send a variety of relocation-related data to the kernel: for the + kernel proper, just the "_stext" symbol address; for all loaded + modules, a variety of symbol base addresses. + + We do this under protest. The kernel ought expose this data to + modules such as ourselves, but instead the upstream community + continually shrinks its module-facing interfaces, including this + stuff, even when users exist. +*/ + + +void send_a_relocation (const char* module, const char* reloc, unsigned long long address) +{ + struct _stp_msg_relocation msg; + + if (strlen(module) >= STP_MODULE_NAME_LEN) + { _perr ("module name too long: %s", module); return; } + strcpy (msg.module, module); + + if (strlen(reloc) >= STP_SYMBOL_NAME_LEN) + { _perr ("reloc name too long: %s", reloc); return; } + strcpy (msg.reloc, reloc); + + msg.address = address; + + send_request (STP_RELOCATION, & msg, sizeof (msg)); + /* XXX: verify send_request RC */ +} + + +int send_relocation_kernel () +{ + int srkrc = 0; + + FILE* kallsyms = fopen ("/proc/kallsyms", "r"); + if (kallsyms == NULL) + { + perror("cannot open /proc/kallsyms"); + // ... and the kernel module will almost certainly fail to initialize. + } + else + { + int done_with_kallsyms = 0; + while (! feof(kallsyms) && !done_with_kallsyms) + { + char *line = NULL; + size_t linesz = 0; + ssize_t linesize = getline (& line, & linesz, kallsyms); + if (linesize < 0) + break; + else + { + unsigned long long address; + char type; + char* symbol = NULL; + int rc = sscanf (line, "%llx %c %as", &address, &type, &symbol); + free (line); line=NULL; + if (symbol == NULL) continue; /* OOM? */ + +#ifdef __powerpc__ +#define KERNEL_RELOC_SYMBOL ".__start" +#else +#define KERNEL_RELOC_SYMBOL "_stext" +#endif + if ((rc == 3) && (0 == strcmp(symbol,KERNEL_RELOC_SYMBOL))) + { + /* NB: even on ppc, we use the _stext relocation name. */ + send_a_relocation ("kernel", "_stext", address); + + /* We need nothing more from the kernel. */ + done_with_kallsyms=1; + } + + free (symbol); + } + } + fclose (kallsyms); + if (!done_with_kallsyms) srkrc = -1; + } + + return srkrc; +} + + +void send_relocation_modules () +{ + unsigned i; + glob_t globbuf; + int r = glob("/sys/module/*/sections/*", GLOB_PERIOD, NULL, &globbuf); + + if (r == GLOB_NOSPACE || r == GLOB_ABORTED) + return; + + for (i=0; i<globbuf.gl_pathc; i++) + { + char *module_section_file; + char *section_name; + char *module_name; + char *module_name_end; + FILE* secfile; + unsigned long long section_address; + + module_section_file = globbuf.gl_pathv[i]; + + /* Tokenize the file name. + Sample gl_pathv[]: /sys/modules/zlib_deflate/sections/.text + Pieces: ^^^^^^^^^^^^ ^^^^^ + */ + section_name = rindex (module_section_file, '/'); + if (! section_name) continue; + section_name ++; + + if (!strcmp (section_name, ".")) continue; + if (!strcmp (section_name, "..")) continue; + + module_name = index (module_section_file, '/'); + if (! module_name) continue; + module_name ++; + module_name = index (module_name, '/'); + if (! module_name) continue; + module_name ++; + module_name = index (module_name, '/'); + if (! module_name) continue; + module_name ++; + + module_name_end = index (module_name, '/'); + if (! module_name_end) continue; + + secfile = fopen (module_section_file, "r"); + if (! secfile) continue; + + if (1 == fscanf (secfile, "0x%llx", §ion_address)) + { + /* Now we destructively modify the string, but by now the file + is open so we won't need the full name again. */ + *module_name_end = '\0'; + + send_a_relocation (module_name, section_name, section_address); + } + + if (strcmp (section_name, ".gnu.linkonce.this_module")) + fclose (secfile); + else + { + set_clexec (fileno (secfile)); + /* NB: don't fclose this arbitrarily-chosen section file. + This forces the kernel to keep a nonzero reference count + on the subject module, until staprun exits, by which time + the kernel module will have inserted its separate claws + into the probeworthy modules. This prevents a race + condition where a probe may be just starting up at the + same time that a probeworthy module is being unloaded. */ + } + } + + globfree (& globbuf); +} + + + +int send_relocations () +{ + int rc; + rc = init_ctl_channel (modname, 0); + if (rc < 0) goto out; + rc = send_relocation_kernel (); + send_relocation_modules (); + close_ctl_channel (); + out: + return rc; +} + |