diff options
| author | Rusty Russell <rusty@rustcorp.com.au> | 2002-11-18 02:48:17 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@penguin.transmeta.com> | 2002-11-18 02:48:17 -0800 |
| commit | a5508ddcd000a435baa37f61ef1bdfb490fcf3c0 (patch) | |
| tree | 8c231825fd939453a7dbbf1c4bd707b3adb3ae24 /kernel | |
| parent | 60c30d29536a741757c3f693ecea14fc0144b6da (diff) | |
[PATCH] kallsyms for new modules
Since I believe kallsyms is important, this reimplements it sanely,
using the current module infrastructure, and not using an external
kallsyms script.
FYI, the previous interface was:
int kallsyms_symbol_to_address(
const char *name, /* Name to lookup */
unsigned long *token, /* Which module to start with */
const char **mod_name, /* Set to module name or "kernel" */
unsigned long *mod_start, /* Set to start address of module */
unsigned long *mod_end, /* Set to end address of module */
const char **sec_name, /* Set to section name */
unsigned long *sec_start, /* Set to start address of section */
unsigned long *sec_end, /* Set to end address of section */
const char **sym_name, /* Set to full symbol name */
unsigned long *sym_start, /* Set to start address of symbol */
unsigned long *sym_end /* Set to end address of symbol */
);
The new one is:
/* Lookup an address. modname is set to NULL if it's in the kernel. */
const char *kallsyms_lookup(unsigned long addr,
unsigned long *symbolsize,
unsigned long *offset,
char **modname);
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/kallsyms.c | 291 | ||||
| -rw-r--r-- | kernel/module.c | 89 |
2 files changed, 166 insertions, 214 deletions
diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index 36d2da107b75..0335cc7fe4a1 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -1,233 +1,102 @@ /* - * kksymoops.c: in-kernel printing of symbolic oopses and stack traces. + * kallsyms.c: in-kernel printing of symbolic oopses and stack traces. * - * Copyright 2000 Keith Owens <kaos@ocs.com.au> April 2000 - * Copyright 2002 Arjan van de Ven <arjanv@redhat.com> - * - This code uses the list of all kernel and module symbols to :- + * Rewritten and vastly simplified by Rusty Russell for in-kernel + * module loader: + * Copyright 2002 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation + */ +#include <linux/kallsyms.h> +#include <linux/module.h> - * Find any non-stack symbol in a kernel or module. Symbols do - not have to be exported for debugging. +static char kallsyms_dummy; - * Convert an address to the module (or kernel) that owns it, the - section it is in and the nearest symbol. This finds all non-stack - symbols, not just exported ones. +/* These will be re-linked against their real values during the second link stage */ +extern unsigned long kallsyms_addresses[1] __attribute__((weak, alias("kallsyms_dummy"))); +extern unsigned long kallsyms_num_syms __attribute__((weak, alias("kallsyms_dummy"))); +extern char kallsyms_names[1] __attribute__((weak, alias("kallsyms_dummy"))); - */ +/* Defined by the linker script. */ +extern char _stext[], _etext[]; -#include <linux/mm.h> -#include <linux/module.h> -#include <linux/kallsyms.h> +/* Lookup an address. modname is set to NULL if it's in the kernel. */ +const char *kallsyms_lookup(unsigned long addr, + unsigned long *symbolsize, + unsigned long *offset, + char **modname) +{ + unsigned long i, best = 0; -/* A symbol can appear in more than one module. A token is used to - * restart the scan at the next module, set the token to 0 for the - * first scan of each symbol. - */ + /* This kernel should never had been booted. */ + if ((void *)kallsyms_addresses == &kallsyms_dummy) + BUG(); -int kallsyms_symbol_to_address( - const char *name, /* Name to lookup */ - unsigned long *token, /* Which module to start at */ - const char **mod_name, /* Set to module name */ - unsigned long *mod_start, /* Set to start address of module */ - unsigned long *mod_end, /* Set to end address of module */ - const char **sec_name, /* Set to section name */ - unsigned long *sec_start, /* Set to start address of section */ - unsigned long *sec_end, /* Set to end address of section */ - const char **sym_name, /* Set to full symbol name */ - unsigned long *sym_start, /* Set to start address of symbol */ - unsigned long *sym_end /* Set to end address of symbol */ - ) -{ - const struct kallsyms_header *ka_hdr = NULL; /* stupid gcc */ - const struct kallsyms_section *ka_sec; - const struct kallsyms_symbol *ka_sym = NULL; - const char *ka_str = NULL; - const struct module *m; - int i = 0, l; - const char *p, *pt_R; - char *p2; - - /* Restart? */ - m = module_list; - if (token && *token) { - for (; m; m = m->next) - if ((unsigned long)m == *token) - break; - if (m) - m = m->next; - } + if (addr >= (unsigned long)_stext && addr <= (unsigned long)_etext) { + unsigned long symbol_end; + char *name = kallsyms_names; - for (; m; m = m->next) { - if (!mod_member_present(m, kallsyms_start) || - !mod_member_present(m, kallsyms_end) || - m->kallsyms_start >= m->kallsyms_end) - continue; - ka_hdr = (struct kallsyms_header *)m->kallsyms_start; - ka_sym = (struct kallsyms_symbol *) - ((char *)(ka_hdr) + ka_hdr->symbol_off); - ka_str = - ((char *)(ka_hdr) + ka_hdr->string_off); - for (i = 0; i < ka_hdr->symbols; ++i, kallsyms_next_sym(ka_hdr, ka_sym)) { - p = ka_str + ka_sym->name_off; - if (strcmp(p, name) == 0) - break; - /* Unversioned requests match versioned names */ - if (!(pt_R = strstr(p, "_R"))) - continue; - l = strlen(pt_R); - if (l < 10) - continue; /* Not _R.*xxxxxxxx */ - (void)simple_strtoul(pt_R+l-8, &p2, 16); - if (*p2) - continue; /* Not _R.*xxxxxxxx */ - if (strncmp(p, name, pt_R-p) == 0) - break; /* Match with version */ + /* They're sorted, we could be clever here, but who cares? */ + for (i = 0; i < kallsyms_num_syms; i++) { + if (kallsyms_addresses[i] > kallsyms_addresses[best] && + kallsyms_addresses[i] <= addr) + best = i; } - if (i < ka_hdr->symbols) - break; - } - if (token) - *token = (unsigned long)m; - if (!m) - return(0); /* not found */ - - ka_sec = (const struct kallsyms_section *) - ((char *)ka_hdr + ka_hdr->section_off + ka_sym->section_off); - *mod_name = m->name; - *mod_start = ka_hdr->start; - *mod_end = ka_hdr->end; - *sec_name = ka_sec->name_off + ka_str; - *sec_start = ka_sec->start; - *sec_end = ka_sec->start + ka_sec->size; - *sym_name = ka_sym->name_off + ka_str; - *sym_start = ka_sym->symbol_addr; - if (i < ka_hdr->symbols-1) { - const struct kallsyms_symbol *ka_symn = ka_sym; - kallsyms_next_sym(ka_hdr, ka_symn); - *sym_end = ka_symn->symbol_addr; - } - else - *sym_end = *sec_end; - return(1); -} + /* Grab name */ + for (i = 0; i < best; i++) + name += strlen(name)+1; -int kallsyms_address_to_symbol( - unsigned long address, /* Address to lookup */ - const char **mod_name, /* Set to module name */ - unsigned long *mod_start, /* Set to start address of module */ - unsigned long *mod_end, /* Set to end address of module */ - const char **sec_name, /* Set to section name */ - unsigned long *sec_start, /* Set to start address of section */ - unsigned long *sec_end, /* Set to end address of section */ - const char **sym_name, /* Set to full symbol name */ - unsigned long *sym_start, /* Set to start address of symbol */ - unsigned long *sym_end /* Set to end address of symbol */ - ) -{ - const struct kallsyms_header *ka_hdr = NULL; /* stupid gcc */ - const struct kallsyms_section *ka_sec = NULL; - const struct kallsyms_symbol *ka_sym; - const char *ka_str; - const struct module *m; - int i; - unsigned long end; - - for (m = module_list; m; m = m->next) { - - if (!mod_member_present(m, kallsyms_start) || - !mod_member_present(m, kallsyms_end) || - m->kallsyms_start >= m->kallsyms_end) - continue; - ka_hdr = (struct kallsyms_header *)m->kallsyms_start; - ka_sec = (const struct kallsyms_section *) - ((char *)ka_hdr + ka_hdr->section_off); - /* Is the address in any section in this module? */ - for (i = 0; i < ka_hdr->sections; ++i, kallsyms_next_sec(ka_hdr, ka_sec)) { - if (ka_sec->start <= address && - (ka_sec->start + ka_sec->size) > address) + /* Base symbol size on next symbol, but beware aliases. */ + symbol_end = (unsigned long)_etext; + for (i = best+1; i < kallsyms_num_syms; i++) { + if (kallsyms_addresses[i] != kallsyms_addresses[best]){ + symbol_end = kallsyms_addresses[i]; break; + } } - if (i < ka_hdr->sections) - break; /* Found a matching section */ - } - if (!m) - return(0); /* not found */ - - ka_sym = (struct kallsyms_symbol *) - ((char *)(ka_hdr) + ka_hdr->symbol_off); - ka_str = - ((char *)(ka_hdr) + ka_hdr->string_off); - *mod_name = m->name; - *mod_start = ka_hdr->start; - *mod_end = ka_hdr->end; - *sec_name = ka_sec->name_off + ka_str; - *sec_start = ka_sec->start; - *sec_end = ka_sec->start + ka_sec->size; - *sym_name = *sec_name; /* In case we find no matching symbol */ - *sym_start = *sec_start; - *sym_end = *sec_end; - - for (i = 0; i < ka_hdr->symbols; ++i, kallsyms_next_sym(ka_hdr, ka_sym)) { - if (ka_sym->symbol_addr > address) - continue; - if (i < ka_hdr->symbols-1) { - const struct kallsyms_symbol *ka_symn = ka_sym; - kallsyms_next_sym(ka_hdr, ka_symn); - end = ka_symn->symbol_addr; - } - else - end = *sec_end; - if (end <= address) - continue; - if ((char *)ka_hdr + ka_hdr->section_off + ka_sym->section_off - != (char *)ka_sec) - continue; /* wrong section */ - *sym_name = ka_str + ka_sym->name_off; - *sym_start = ka_sym->symbol_addr; - *sym_end = end; - break; + *symbolsize = symbol_end - kallsyms_addresses[best]; + *modname = NULL; + *offset = addr - kallsyms_addresses[best]; + return name; } - return(1); + + return module_address_lookup(addr, symbolsize, offset, modname); } -/* List all sections in all modules. The callback routine is invoked with - * token, module name, section name, section start, section end, section flags. - */ -int kallsyms_sections(void *token, - int (*callback)(void *, const char *, const char *, ElfW(Addr), ElfW(Addr), ElfW(Word))) +/* Replace "%s" in format with address, or returns -errno. */ +void __print_symbol(const char *fmt, unsigned long address) { - const struct kallsyms_header *ka_hdr = NULL; /* stupid gcc */ - const struct kallsyms_section *ka_sec = NULL; - const char *ka_str; - const struct module *m; - int i; - - for (m = module_list; m; m = m->next) { - if (!mod_member_present(m, kallsyms_start) || - !mod_member_present(m, kallsyms_end) || - m->kallsyms_start >= m->kallsyms_end) - continue; - ka_hdr = (struct kallsyms_header *)m->kallsyms_start; - ka_sec = (const struct kallsyms_section *) ((char *)ka_hdr + ka_hdr->section_off); - ka_str = ((char *)(ka_hdr) + ka_hdr->string_off); - for (i = 0; i < ka_hdr->sections; ++i, kallsyms_next_sec(ka_hdr, ka_sec)) { - if (callback( - token, - *(m->name) ? m->name : "kernel", - ka_sec->name_off + ka_str, - ka_sec->start, - ka_sec->start + ka_sec->size, - ka_sec->flags)) - return(0); - } + char *modname; + const char *name; + unsigned long offset, size; + + name = kallsyms_lookup(address, &size, &offset, &modname); + + if (!name) { + char addrstr[sizeof("0x%lx") + (BITS_PER_LONG*3/10)]; + + sprintf(addrstr, "0x%lx", address); + printk(fmt, addrstr); + return; } - return(1); -} -/* Allocate the __kallsyms section, so it's already present in - * the temporary vmlinux that kallsyms is run on, so the first - * run will pick up the section info already. */ + if (modname) { + /* This is pretty small. */ + char buffer[sizeof("%s+%#lx/%#lx [%s]") + + strlen(name) + 2*(BITS_PER_LONG*3/10) + + strlen(modname)]; + + sprintf(buffer, "%s+%#lx/%#lx [%s]", + name, offset, size, modname); + printk(fmt, buffer); + } else { + char buffer[sizeof("%s+%#lx/%#lx") + + strlen(name) + 2*(BITS_PER_LONG*3/10)]; + + sprintf(buffer, "%s+%#lx/%#lx", name, offset, size); + printk(fmt, buffer); + } +} -__asm__(".section __kallsyms,\"a\"\n.previous"); +EXPORT_SYMBOL(kallsyms_lookup); +EXPORT_SYMBOL(__print_symbol); diff --git a/kernel/module.c b/kernel/module.c index 82aa7ebe6694..464c3d9320b4 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -898,6 +898,11 @@ static struct module *load_module(void *umod, DEBUGP("Exception table found in section %u\n", i); exindex = i; } +#ifdef CONFIG_KALLSYMS + /* symbol and string tables for decoding later. */ + if (sechdrs[i].sh_type == SHT_SYMTAB || i == hdr->e_shstrndx) + sechdrs[i].sh_flags |= SHF_ALLOC; +#endif #ifndef CONFIG_MODULE_UNLOAD /* Don't load .exit sections */ if (strstr(secstrings+sechdrs[i].sh_name, ".exit")) @@ -1026,6 +1031,11 @@ static struct module *load_module(void *umod, goto cleanup; } +#ifdef CONFIG_KALLSYMS + mod->symtab = (void *)sechdrs[symindex].sh_offset; + mod->num_syms = sechdrs[symindex].sh_size / sizeof(Elf_Sym); + mod->strtab = (void *)sechdrs[strindex].sh_offset; +#endif err = module_finalize(hdr, sechdrs, mod); if (err < 0) goto cleanup; @@ -1141,9 +1151,82 @@ sys_init_module(void *umod, return 0; } -/* Called by the /proc file system to return a current list of - modules. Al Viro came up with this interface as an "improvement". - God save us from any more such interface improvements. */ +#ifdef CONFIG_KALLSYMS +static inline int inside_init(struct module *mod, unsigned long addr) +{ + if (mod->module_init + && (unsigned long)mod->module_init <= addr + && (unsigned long)mod->module_init + mod->init_size > addr) + return 1; + return 0; +} + +static inline int inside_core(struct module *mod, unsigned long addr) +{ + if ((unsigned long)mod->module_core <= addr + && (unsigned long)mod->module_core + mod->core_size > addr) + return 1; + return 0; +} + +static const char *get_ksymbol(struct module *mod, + unsigned long addr, + unsigned long *size, + unsigned long *offset) +{ + unsigned int i, next = 0, best = 0; + + /* Scan for closest preceeding symbol, and next symbol. (ELF + starts real symbols at 1). */ + for (i = 1; i < mod->num_syms; i++) { + if (mod->symtab[i].st_shndx == SHN_UNDEF) + continue; + + if (mod->symtab[i].st_value <= addr + && mod->symtab[i].st_value > mod->symtab[best].st_value) + best = i; + if (mod->symtab[i].st_value > addr + && mod->symtab[i].st_value < mod->symtab[next].st_value) + next = i; + } + + if (!best) + return NULL; + + if (!next) { + /* Last symbol? It ends at the end of the module then. */ + if (inside_core(mod, addr)) + *size = mod->module_core+mod->core_size - (void*)addr; + else + *size = mod->module_init+mod->init_size - (void*)addr; + } else + *size = mod->symtab[next].st_value - addr; + + *offset = addr - mod->symtab[best].st_value; + return mod->strtab + mod->symtab[best].st_name; +} + +/* For kallsyms to ask for address resolution. NULL means not found. + We don't lock, as this is used for oops resolution and races are a + lesser concern. */ +const char *module_address_lookup(unsigned long addr, + unsigned long *size, + unsigned long *offset, + char **modname) +{ + struct module *mod; + + list_for_each_entry(mod, &modules, list) { + if (inside_core(mod, addr) || inside_init(mod, addr)) { + *modname = mod->name; + return get_ksymbol(mod, addr, size, offset); + } + } + return NULL; +} +#endif /* CONFIG_KALLSYMS */ + +/* Called by the /proc file system to return a list of modules. */ static void *m_start(struct seq_file *m, loff_t *pos) { struct list_head *i; |
