summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/kallsyms.c291
-rw-r--r--kernel/module.c89
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;