RHEL4/kernel/kallsyms.c
<<
>>
Prefs
   1/*
   2 * kallsyms.c: in-kernel printing of symbolic oopses and stack traces.
   3 *
   4 * Rewritten and vastly simplified by Rusty Russell for in-kernel
   5 * module loader:
   6 *   Copyright 2002 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation
   7 * Stem compression by Andi Kleen.
   8 */
   9#include <linux/kallsyms.h>
  10#include <linux/module.h>
  11#include <linux/init.h>
  12#include <linux/seq_file.h>
  13#include <linux/fs.h>
  14#include <linux/err.h>
  15#include <linux/proc_fs.h>
  16
  17/* These will be re-linked against their real values during the second link stage */
  18extern unsigned long kallsyms_addresses[] __attribute__((weak));
  19extern unsigned long kallsyms_num_syms __attribute__((weak));
  20extern char kallsyms_names[] __attribute__((weak));
  21
  22/* Defined by the linker script. */
  23extern char _stext[], _etext[], _sinittext[], _einittext[];
  24
  25static inline int is_kernel_inittext(unsigned long addr)
  26{
  27        if (addr >= (unsigned long)_sinittext
  28            && addr <= (unsigned long)_einittext)
  29                return 1;
  30        return 0;
  31}
  32
  33static inline int is_kernel_text(unsigned long addr)
  34{
  35        if (addr >= (unsigned long)_stext && addr <= (unsigned long)_etext)
  36                return 1;
  37        return 0;
  38}
  39
  40/* Lookup the address for this symbol. Returns 0 if not found. */
  41unsigned long kallsyms_lookup_name(const char *name)
  42{
  43        char namebuf[KSYM_NAME_LEN+1];
  44        unsigned long i;
  45        char *knames;
  46
  47        for (i = 0, knames = kallsyms_names; i < kallsyms_num_syms; i++) {
  48                unsigned prefix = *knames++;
  49
  50                strlcpy(namebuf + prefix, knames, KSYM_NAME_LEN - prefix);
  51                if (strcmp(namebuf, name) == 0)
  52                        return kallsyms_addresses[i];
  53
  54                knames += strlen(knames) + 1;
  55        }
  56        return module_kallsyms_lookup_name(name);
  57}
  58EXPORT_SYMBOL_GPL(kallsyms_lookup_name);
  59
  60/*
  61 * Lookup an address
  62 * - modname is set to NULL if it's in the kernel
  63 * - we guarantee that the returned name is valid until we reschedule even if
  64 *   it resides in a module
  65 * - we also guarantee that modname will be valid until rescheduled
  66 */
  67const char *kallsyms_lookup(unsigned long addr,
  68                            unsigned long *symbolsize,
  69                            unsigned long *offset,
  70                            char **modname, char *namebuf)
  71{
  72        unsigned long i, best = 0;
  73        const char *msym;
  74
  75        /* This kernel should never had been booted. */
  76        BUG_ON(!kallsyms_addresses);
  77
  78        namebuf[KSYM_NAME_LEN] = 0;
  79        namebuf[0] = 0;
  80
  81        if (is_kernel_text(addr) || is_kernel_inittext(addr)) {
  82                unsigned long symbol_end;
  83                const char *name = kallsyms_names;
  84
  85                /* They're sorted, we could be clever here, but who cares? */
  86                for (i = 0; i < kallsyms_num_syms; i++) {
  87                        if (kallsyms_addresses[i] > kallsyms_addresses[best] &&
  88                            kallsyms_addresses[i] <= addr)
  89                                best = i;
  90                }
  91
  92                /* Grab name */
  93                for (i = 0; i <= best; i++) { 
  94                        unsigned prefix = *name++;
  95                        strncpy(namebuf + prefix, name, KSYM_NAME_LEN - prefix);
  96                        name += strlen(name) + 1;
  97                }
  98
  99                /* At worst, symbol ends at end of section. */
 100                if (is_kernel_inittext(addr))
 101                        symbol_end = (unsigned long)_einittext;
 102                else
 103                        symbol_end = (unsigned long)_etext;
 104
 105                /* Search for next non-aliased symbol */
 106                for (i = best+1; i < kallsyms_num_syms; i++) {
 107                        if (kallsyms_addresses[i] > kallsyms_addresses[best]) {
 108                                symbol_end = kallsyms_addresses[i];
 109                                break;
 110                        }
 111                }
 112
 113                *symbolsize = symbol_end - kallsyms_addresses[best];
 114                *modname = NULL;
 115                *offset = addr - kallsyms_addresses[best];
 116                return namebuf;
 117        }
 118
 119        /* see if it's in a module */
 120        msym = module_address_lookup(addr, symbolsize, offset, modname);
 121        if (msym)
 122                return strncpy(namebuf, msym, KSYM_NAME_LEN);
 123
 124        return NULL;
 125}
 126
 127/* Replace "%s" in format with address, or returns -errno. */
 128void __print_symbol(const char *fmt, unsigned long address)
 129{
 130        char *modname;
 131        const char *name;
 132        unsigned long offset, size;
 133        char namebuf[KSYM_NAME_LEN+1];
 134        char buffer[sizeof("%s+%#lx/%#lx [%s]") + KSYM_NAME_LEN +
 135                    2*(BITS_PER_LONG*3/10) + MODULE_NAME_LEN + 1];
 136
 137        name = kallsyms_lookup(address, &size, &offset, &modname, namebuf);
 138
 139        if (!name)
 140                sprintf(buffer, "0x%lx", address);
 141        else {
 142                if (modname)
 143                        sprintf(buffer, "%s+%#lx/%#lx [%s]", name, offset,
 144                                size, modname);
 145                else
 146                        sprintf(buffer, "%s+%#lx/%#lx", name, offset, size);
 147        }
 148        printk(fmt, buffer);
 149}
 150
 151/* To avoid O(n^2) iteration, we carry prefix along. */
 152struct kallsym_iter
 153{
 154        loff_t pos;
 155        struct module *owner;
 156        unsigned long value;
 157        unsigned int nameoff; /* If iterating in core kernel symbols */
 158        char type;
 159        char name[KSYM_NAME_LEN+1];
 160};
 161
 162/* Only label it "global" if it is exported. */
 163static void upcase_if_global(struct kallsym_iter *iter)
 164{
 165        if (is_exported(iter->name, iter->owner))
 166                iter->type += 'A' - 'a';
 167}
 168
 169static int get_ksymbol_mod(struct kallsym_iter *iter)
 170{
 171        iter->owner = module_get_kallsym(iter->pos - kallsyms_num_syms,
 172                                         &iter->value,
 173                                         &iter->type, iter->name);
 174        if (iter->owner == NULL)
 175                return 0;
 176
 177        upcase_if_global(iter);
 178        return 1;
 179}
 180
 181/* Returns space to next name. */
 182static unsigned long get_ksymbol_core(struct kallsym_iter *iter)
 183{
 184        unsigned stemlen, off = iter->nameoff;
 185
 186        /* First char of each symbol name indicates prefix length
 187           shared with previous name (stem compression). */
 188        stemlen = kallsyms_names[off++];
 189
 190        strlcpy(iter->name+stemlen, kallsyms_names + off,
 191                KSYM_NAME_LEN+1-stemlen);
 192        off += strlen(kallsyms_names + off) + 1;
 193        iter->owner = NULL;
 194        iter->value = kallsyms_addresses[iter->pos];
 195        if (is_kernel_text(iter->value) || is_kernel_inittext(iter->value))
 196                iter->type = 't';
 197        else
 198                iter->type = 'd';
 199
 200        upcase_if_global(iter);
 201        return off - iter->nameoff;
 202}
 203
 204static void reset_iter(struct kallsym_iter *iter)
 205{
 206        iter->name[0] = '\0';
 207        iter->nameoff = 0;
 208        iter->pos = 0;
 209}
 210
 211/* Returns false if pos at or past end of file. */
 212static int update_iter(struct kallsym_iter *iter, loff_t pos)
 213{
 214        /* Module symbols can be accessed randomly. */
 215        if (pos >= kallsyms_num_syms) {
 216                iter->pos = pos;
 217                return get_ksymbol_mod(iter);
 218        }
 219        
 220        /* If we're past the desired position, reset to start. */
 221        if (pos < iter->pos)
 222                reset_iter(iter);
 223
 224        /* We need to iterate through the previous symbols: can be slow */
 225        for (; iter->pos != pos; iter->pos++) {
 226                iter->nameoff += get_ksymbol_core(iter);
 227                cond_resched();
 228        }
 229        get_ksymbol_core(iter);
 230        return 1;
 231}
 232
 233static void *s_next(struct seq_file *m, void *p, loff_t *pos)
 234{
 235        (*pos)++;
 236
 237        if (!update_iter(m->private, *pos))
 238                return NULL;
 239        return p;
 240}
 241
 242static void *s_start(struct seq_file *m, loff_t *pos)
 243{
 244        if (!update_iter(m->private, *pos))
 245                return NULL;
 246        return m->private;
 247}
 248
 249static void s_stop(struct seq_file *m, void *p)
 250{
 251}
 252
 253static int s_show(struct seq_file *m, void *p)
 254{
 255        struct kallsym_iter *iter = m->private;
 256
 257        /* Some debugging symbols have no name.  Ignore them. */ 
 258        if (!iter->name[0])
 259                return 0;
 260
 261        if (iter->owner)
 262                seq_printf(m, "%0*lx %c %s\t[%s]\n",
 263                           (int)(2*sizeof(void*)),
 264                           iter->value, iter->type, iter->name,
 265                           module_name(iter->owner));
 266        else
 267                seq_printf(m, "%0*lx %c %s\n",
 268                           (int)(2*sizeof(void*)),
 269                           iter->value, iter->type, iter->name);
 270        return 0;
 271}
 272
 273struct seq_operations kallsyms_op = {
 274        .start = s_start,
 275        .next = s_next,
 276        .stop = s_stop,
 277        .show = s_show
 278};
 279
 280static int kallsyms_open(struct inode *inode, struct file *file)
 281{
 282        /* We keep iterator in m->private, since normal case is to
 283         * s_start from where we left off, so we avoid O(N^2). */
 284        struct kallsym_iter *iter;
 285        int ret;
 286
 287        iter = kmalloc(sizeof(*iter), GFP_KERNEL);
 288        if (!iter)
 289                return -ENOMEM;
 290        reset_iter(iter);
 291
 292        ret = seq_open(file, &kallsyms_op);
 293        if (ret == 0)
 294                ((struct seq_file *)file->private_data)->private = iter;
 295        else
 296                kfree(iter);
 297        return ret;
 298}
 299
 300static int kallsyms_release(struct inode *inode, struct file *file)
 301{
 302        struct seq_file *m = (struct seq_file *)file->private_data;
 303        kfree(m->private);
 304        return seq_release(inode, file);
 305}
 306
 307static struct file_operations kallsyms_operations = {
 308        .open = kallsyms_open,
 309        .read = seq_read,
 310        .llseek = seq_lseek,
 311        .release = kallsyms_release,
 312};
 313
 314int __init kallsyms_init(void)
 315{
 316        struct proc_dir_entry *entry;
 317
 318        entry = create_proc_entry("kallsyms", 0444, NULL);
 319        if (entry)
 320                entry->proc_fops = &kallsyms_operations;
 321        return 0;
 322}
 323__initcall(kallsyms_init);
 324
 325EXPORT_SYMBOL(__print_symbol);
 326