diff options
Diffstat (limited to 'runtime/transport/symbols.c')
-rw-r--r-- | runtime/transport/symbols.c | 420 |
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_ */ |