summaryrefslogtreecommitdiffstats
path: root/runtime/transport/symbols.c
diff options
context:
space:
mode:
authorhunt <hunt>2006-11-02 18:37:00 +0000
committerhunt <hunt>2006-11-02 18:37:00 +0000
commitf1bad60c76d79f01a87e4128df266bf4252a71e0 (patch)
tree5114f087d844e702253a439efbcc36e310268a4b /runtime/transport/symbols.c
parent202e1828643725d7bebef76c48cbaa28c463cee3 (diff)
downloadsystemtap-steved-f1bad60c76d79f01a87e4128df266bf4252a71e0.tar.gz
systemtap-steved-f1bad60c76d79f01a87e4128df266bf4252a71e0.tar.xz
systemtap-steved-f1bad60c76d79f01a87e4128df266bf4252a71e0.zip
New dynamic module and symbol handling code.
Diffstat (limited to 'runtime/transport/symbols.c')
-rw-r--r--runtime/transport/symbols.c420
1 files changed, 420 insertions, 0 deletions
diff --git a/runtime/transport/symbols.c b/runtime/transport/symbols.c
new file mode 100644
index 00000000..e583103f
--- /dev/null
+++ b/runtime/transport/symbols.c
@@ -0,0 +1,420 @@
+/* -*- linux-c -*-
+ * symbols.c - stp symbol and module functions
+ *
+ * Copyright (C) Red Hat Inc, 2006
+ *
+ * This file is part of systemtap, and is free software. You can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
+ */
+
+#ifndef _SYMBOLS_C_
+#define _SYMBOLS_C_
+#include "../sym.h"
+#include <linux/sort.h>
+
+spinlock_t _stp_module_lock = SPIN_LOCK_UNLOCKED;
+#define STP_TRYLOCK_MODULES ({ \
+ int numtrylock = 0; \
+ while (!spin_trylock_irqsave (&_stp_module_lock, flags) && (++numtrylock < MAXTRYLOCK)) \
+ ndelay (TRYLOCKDELAY); \
+ (numtrylock >= MAXTRYLOCK); \
+ })
+#define STP_LOCK_MODULES spin_lock_irqsave(&_stp_module_lock, flags)
+#define STP_UNLOCK_MODULES spin_unlock_irqrestore(&_stp_module_lock, flags)
+
+static char *_stp_symbol_data = NULL;
+static int _stp_symbol_state = 0;
+static char *_stp_module_data = NULL;
+static int _stp_module_state = 0;
+
+
+/* these are all the symbol types we are interested in */
+static int _stp_sym_type_ok(int type)
+{
+ switch (type) {
+ case 'T':
+ case 't':
+ return 1;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+/* From a module struct, scan the symtab and figure out how much space */
+/* we need to store all the parts we are interested in */
+static unsigned _stp_get_sym_sizes(struct module *m, unsigned *dsize)
+{
+ int i;
+ unsigned num = 0, datasize = 0;
+ for (i=0; i < m->num_symtab; i++) {
+ char *str = (char *)(m->strtab + m->symtab[i].st_name);
+ if (*str != '\0' && _stp_sym_type_ok(m->symtab[i].st_info)) {
+ datasize += strlen(str)+1;
+ num++;
+ }
+ }
+ *dsize = datasize;
+ return num;
+}
+
+/* allocate space for a module and symbols */
+static struct _stp_module * _stp_alloc_module(unsigned num, unsigned datasize)
+{
+ struct _stp_module *mod = (struct _stp_module *)kmalloc(sizeof(struct _stp_module), GFP_KERNEL);
+ if (mod == NULL)
+ goto bad;
+
+ memset(mod, 0, sizeof(struct _stp_module));
+ mod->symbols = (struct _stp_symbol *)kmalloc(num * sizeof(struct _stp_symbol), GFP_KERNEL);
+ if (mod->symbols == NULL) {
+ mod->symbols = (struct _stp_symbol *)vmalloc(num * sizeof(struct _stp_symbol));
+ if (mod->symbols == NULL)
+ goto bad;
+ mod->allocated = 1;
+ }
+
+ mod->symbol_data = kmalloc(datasize, GFP_KERNEL);
+ if (mod->symbol_data == NULL) {
+ mod->symbol_data = vmalloc(datasize);
+ if (mod->symbol_data == NULL)
+ goto bad;
+ mod->allocated |= 2;
+ }
+ mod->num_symbols = num;
+ return mod;
+
+bad:
+ if (mod)
+ kfree(mod);
+ if (mod->allocated && mod->symbols)
+ vfree(mod->symbols);
+ else
+ kfree(mod->symbols);
+ return NULL;
+}
+
+static struct _stp_module * _stp_alloc_module_from_module (struct module *m)
+{
+ unsigned datasize, num = _stp_get_sym_sizes(m, &datasize);
+ return _stp_alloc_module(num, datasize);
+}
+
+/* Delete a module and free its memory. */
+/* The lock should already be held before calling this. */
+static void _stp_del_module(struct _stp_module *mod)
+{
+ int i, num;
+
+ kbug("deleting %s\n", mod->name);
+
+ /* remove module from the arrays */
+ for (num = 1; num < _stp_num_modules; num++) {
+ if (_stp_modules[num] == mod)
+ break;
+ }
+ if (num >= _stp_num_modules)
+ return;
+
+ for (i = num; i < _stp_num_modules-1; i++)
+ _stp_modules[i] = _stp_modules[i+1];
+
+ for (num = 1; num < _stp_num_modules; num++) {
+ if (_stp_modules_by_addr[num] == mod)
+ break;
+ }
+ for (i = num; i < _stp_num_modules-1; i++)
+ _stp_modules_by_addr[i] = _stp_modules_by_addr[i+1];
+
+ _stp_num_modules--;
+
+ /* free symbol memory */
+ if (mod->num_symbols) {
+ if (mod->allocated & 1)
+ vfree(mod->symbols);
+ else
+ kfree(mod->symbols);
+ if (mod->allocated & 2)
+ vfree(mod->symbol_data);
+ else
+ kfree(mod->symbol_data);
+ }
+ if (mod->sections)
+ kfree(mod->sections);
+
+ /* free module memory */
+ kfree(mod);
+}
+
+static void _stp_free_modules(void)
+{
+ int i;
+ unsigned long flags;
+
+ STP_LOCK_MODULES;
+ for (i = _stp_num_modules - 1; i >= 0; i--)
+ _stp_del_module(_stp_modules[i]);
+ STP_UNLOCK_MODULES;
+}
+
+static unsigned long _stp_kallsyms_lookup_name(const char *name);
+
+/* process the KERNEL symbols */
+static int _stp_do_symbols(const char __user *buf, int count)
+{
+ unsigned datasize, num;
+ int i;
+ struct _stp_symbol *s;
+
+ switch (_stp_symbol_state) {
+ case 0:
+ if (count != 8) {
+ printk("unexpected systemtap error: _stp_do_symbols: count=%d\n", count);
+ return -EFAULT;
+ }
+ if (get_user(num, (unsigned __user *)buf))
+ return -EFAULT;
+ if (get_user(datasize, (unsigned __user *)(buf+4)))
+ return -EFAULT;
+ kbug("num=%d datasize=%d\n", num, datasize);
+
+ _stp_modules[0] = _stp_alloc_module(num, datasize);
+ if (_stp_modules[0] == NULL) {
+ printk("unexpected systemtap error: cannot allocate memory\n");
+ return -EFAULT;
+ }
+ _stp_symbol_state = 1;
+ break;
+ case 1:
+ if (copy_from_user ((char *)_stp_modules[0]->symbols, buf, count))
+ return -EFAULT;
+ kbug("got stap_symbols, count=%d\n", count);
+ _stp_symbol_state = 2;
+ break;
+ case 2:
+ if (copy_from_user (_stp_modules[0]->symbol_data, buf, count))
+ return -EFAULT;
+ kbug("got symbol data, count=%d\n", count);
+ _stp_num_modules = 1;
+
+
+ s = _stp_modules[0]->symbols;
+ for (i = 0; i < _stp_modules[0]->num_symbols; i++)
+ s[i].symbol += (long)_stp_modules[0]->symbol_data;
+ _stp_symbol_state = 3;
+ _stp_modules[0]->text = _stp_kallsyms_lookup_name("_stext");
+ _stp_modules_by_addr[0] = _stp_modules[0];
+ break;
+ default:
+ printk("systemtap error: unexpected symbol data of size %d.\n", count);
+ }
+ return count;
+}
+
+static int _stp_compare_addr(const void *p1, const void *p2)
+{
+ struct _stp_symbol *s1 = (struct _stp_symbol *)p1;
+ struct _stp_symbol *s2 = (struct _stp_symbol *)p2;
+ if (s1->addr == s2->addr) return 0;
+ if (s1->addr < s2->addr) return -1;
+ return 1;
+}
+
+static void _stp_swap_symbol(void *x, void *y, int size)
+{
+ struct _stp_symbol *a = (struct _stp_symbol *)x;
+ struct _stp_symbol *b = (struct _stp_symbol *)y;
+ unsigned long addr = a->addr;
+ const char *symbol = a->symbol;
+ a->addr = b->addr;
+ a->symbol = b->symbol;
+ b->addr = addr;
+ b->symbol = symbol;
+}
+
+
+/* Create a new _stp_module and load the symbols */
+static struct _stp_module *_stp_load_module_symbols (struct _stp_module *imod)
+{
+ int i, num=0;
+ struct module *m = (struct module *)imod->module;
+ struct _stp_module *mod = NULL;
+ char *dataptr;
+
+ if (m == NULL) {
+ kbug("imod->module is NULL\n");
+ return NULL;
+ }
+ if (try_module_get(m)) {
+
+ mod = _stp_alloc_module_from_module(m);
+ if (mod == NULL) {
+ module_put(m);
+ printk("Systemtap failed to allocate memory for module.\n");
+ return NULL;
+ }
+
+ strlcpy(mod->name, imod->name, STP_MODULE_NAME_LEN);
+ mod->module = imod->module;
+ mod->text = imod->text;
+ mod->data = imod->data;
+ mod->num_sections = imod->num_sections;
+ mod->sections = imod->sections;
+
+ /* now copy all the symbols we are interested in */
+ dataptr = mod->symbol_data;
+ for (i=0; i < m->num_symtab; i++) {
+ char *str = (char *)(m->strtab + m->symtab[i].st_name);
+ if (*str != '\0' && _stp_sym_type_ok(m->symtab[i].st_info)) {
+ mod->symbols[num].symbol = dataptr;
+ mod->symbols[num].addr = m->symtab[i].st_value;
+ while (*str) *dataptr++ = *str++;
+ *dataptr++ = 0;
+ num++;
+ }
+ }
+ module_put(m);
+
+ /* sort symbols by address */
+ sort (mod->symbols, num, sizeof(struct _stp_symbol), _stp_compare_addr, _stp_swap_symbol);
+ }
+ return mod;
+}
+
+/* Do we already have this module? */
+static int _stp_module_exists(struct _stp_module *mod)
+{
+ int i, res;
+ unsigned long flags;
+ kbug("exists? %s\n", mod->name);
+ STP_LOCK_MODULES;
+ for (i = 1; i < _stp_num_modules; i++) {
+ res = strcmp(_stp_modules[i]->name, mod->name);
+ if (res > 0)
+ break;
+ if (res == 0 && _stp_modules[i]->module == mod->module) {
+ STP_UNLOCK_MODULES;
+ return 1;
+ }
+ }
+ STP_UNLOCK_MODULES;
+ return 0;
+}
+
+static void _stp_ins_module(struct _stp_module *mod)
+{
+ int i, num, res;
+ unsigned long flags;
+
+ kbug("insert %s\n", mod->name);
+
+ STP_LOCK_MODULES;
+
+ /* insert alphabetically in _stp_modules[] */
+ for (num = 1; num < _stp_num_modules; num++) {
+ res = strcmp(_stp_modules[num]->name, mod->name);
+ if (res < 0)
+ continue;
+ if (res > 0)
+ break;
+ _stp_del_module(_stp_modules[num]);
+ break;
+ }
+ for (i = _stp_num_modules; i > num; i--)
+ _stp_modules[i] = _stp_modules[i-1];
+ _stp_modules[num] = mod;
+
+ /* insert by text address in _stp_modules_by_addr[] */
+ for (num = 1; num < _stp_num_modules; num++) {
+ if (_stp_modules_by_addr[num]->text > mod->text)
+ break;
+ }
+ for (i = _stp_num_modules; i > num; i--)
+ _stp_modules_by_addr[i] = _stp_modules_by_addr[i-1];
+ _stp_modules_by_addr[num] = mod;
+
+ _stp_num_modules++;
+
+ STP_UNLOCK_MODULES;
+}
+
+
+/* Called from procfs.c when a STP_MODULE msg is received */
+static int _stp_do_module(const char __user *buf, int count)
+{
+ struct _stp_module tmpmod, *mod;
+ int i;
+
+ if (count < sizeof(tmpmod)) {
+ printk("_stp_do_modules: expected %ld and got %d\n", sizeof(tmpmod), count);
+ return -EFAULT;
+ }
+ if (copy_from_user ((char *)&tmpmod, buf, sizeof(tmpmod)))
+ return -EFAULT;
+
+ kbug("Got module %s, count=%d(0x%x)\n", tmpmod.name, count,count);
+
+ if (_stp_module_exists(&tmpmod))
+ return count;
+
+ /* copy in section data */
+ tmpmod.sections = kmalloc(count - sizeof(tmpmod), GFP_KERNEL);
+ if (tmpmod.sections == NULL) {
+ printk("_stp_do_module: unable to allocate memory.\n");
+ return -EFAULT;
+ }
+ if (copy_from_user ((char *)tmpmod.sections, buf+sizeof(tmpmod), count-sizeof(tmpmod))) {
+ kfree(tmpmod.sections);
+ return -EFAULT;
+ }
+ for (i = 0; i < tmpmod.num_sections; i++) {
+ tmpmod.sections[i].symbol =
+ (char *)((long)tmpmod.sections[i].symbol
+ + (long)((long)tmpmod.sections + tmpmod.num_sections * sizeof(struct _stp_symbol)));
+ }
+
+ #ifdef DEBUG
+ for (i = 0; i < tmpmod.num_sections; i++)
+ printk("section %d (stored at %p): %s %lx\n", i, &tmpmod.sections[i], tmpmod.sections[i].symbol, tmpmod.sections[i].addr);
+ #endif
+
+ /* load symbols from tmpmod.module to mod */
+ mod = _stp_load_module_symbols(&tmpmod);
+ if (mod == NULL) {
+ kfree(tmpmod.sections);
+ return -EFAULT;
+ }
+
+ _stp_ins_module(mod);
+
+ return count;
+}
+
+static int _stp_transport_send (int type, void *data, int len);
+
+static int _stp_module_load_notify(struct notifier_block * self, unsigned long val, void * data)
+{
+#ifdef CONFIG_MODULES
+ struct module *mod = (struct module *)data;
+ struct _stp_module rmod;
+
+ switch (val) {
+ case MODULE_STATE_COMING:
+ dbug("module %s loaded\n", mod->name);
+ strlcpy(rmod.name, mod->name, STP_MODULE_NAME_LEN);
+ _stp_transport_send(STP_MODULE, &rmod, sizeof(struct _stp_module));
+ break;
+ default:
+ printk("module loaded? val=%ld\n", val);
+ }
+#endif
+ return 0;
+}
+
+static struct notifier_block _stp_module_load_nb = {
+ .notifier_call = _stp_module_load_notify,
+};
+
+#endif /* _SYMBOLS_C_ */