linux/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 *
   8 * ChangeLog:
   9 *
  10 * (25/Aug/2004) Paulo Marques <pmarques@grupopie.com>
  11 *      Changed the compression method from stem compression to "table lookup"
  12 *      compression (see scripts/kallsyms.c for a more complete description)
  13 */
  14#include <linux/kallsyms.h>
  15#include <linux/module.h>
  16#include <linux/init.h>
  17#include <linux/seq_file.h>
  18#include <linux/fs.h>
  19#include <linux/err.h>
  20#include <linux/proc_fs.h>
  21#include <linux/sched.h>        /* for cond_resched */
  22#include <linux/mm.h>
  23#include <linux/ctype.h>
  24
  25#include <asm/sections.h>
  26
  27#ifdef CONFIG_KALLSYMS_ALL
  28#define all_var 1
  29#else
  30#define all_var 0
  31#endif
  32
  33/* These will be re-linked against their real values during the second link stage */
  34extern const unsigned long kallsyms_addresses[] __attribute__((weak));
  35extern const u8 kallsyms_names[] __attribute__((weak));
  36
  37/* tell the compiler that the count isn't in the small data section if the arch
  38 * has one (eg: FRV)
  39 */
  40extern const unsigned long kallsyms_num_syms
  41__attribute__((weak, section(".rodata")));
  42
  43extern const u8 kallsyms_token_table[] __attribute__((weak));
  44extern const u16 kallsyms_token_index[] __attribute__((weak));
  45
  46extern const unsigned long kallsyms_markers[] __attribute__((weak));
  47
  48static inline int is_kernel_inittext(unsigned long addr)
  49{
  50        if (addr >= (unsigned long)_sinittext
  51            && addr <= (unsigned long)_einittext)
  52                return 1;
  53        return 0;
  54}
  55
  56static inline int is_kernel_extratext(unsigned long addr)
  57{
  58        if (addr >= (unsigned long)_sextratext
  59            && addr <= (unsigned long)_eextratext)
  60                return 1;
  61        return 0;
  62}
  63
  64static inline int is_kernel_text(unsigned long addr)
  65{
  66        if (addr >= (unsigned long)_stext && addr <= (unsigned long)_etext)
  67                return 1;
  68        return in_gate_area_no_task(addr);
  69}
  70
  71static inline int is_kernel(unsigned long addr)
  72{
  73        if (addr >= (unsigned long)_stext && addr <= (unsigned long)_end)
  74                return 1;
  75        return in_gate_area_no_task(addr);
  76}
  77
  78static int is_ksym_addr(unsigned long addr)
  79{
  80        if (all_var)
  81                return is_kernel(addr);
  82
  83        return is_kernel_text(addr) || is_kernel_inittext(addr) ||
  84                is_kernel_extratext(addr);
  85}
  86
  87/* expand a compressed symbol data into the resulting uncompressed string,
  88   given the offset to where the symbol is in the compressed stream */
  89static unsigned int kallsyms_expand_symbol(unsigned int off, char *result)
  90{
  91        int len, skipped_first = 0;
  92        const u8 *tptr, *data;
  93
  94        /* get the compressed symbol length from the first symbol byte */
  95        data = &kallsyms_names[off];
  96        len = *data;
  97        data++;
  98
  99        /* update the offset to return the offset for the next symbol on
 100         * the compressed stream */
 101        off += len + 1;
 102
 103        /* for every byte on the compressed symbol data, copy the table
 104           entry for that byte */
 105        while(len) {
 106                tptr = &kallsyms_token_table[ kallsyms_token_index[*data] ];
 107                data++;
 108                len--;
 109
 110                while (*tptr) {
 111                        if(skipped_first) {
 112                                *result = *tptr;
 113                                result++;
 114                        } else
 115                                skipped_first = 1;
 116                        tptr++;
 117                }
 118        }
 119
 120        *result = '\0';
 121
 122        /* return to offset to the next symbol */
 123        return off;
 124}
 125
 126/* get symbol type information. This is encoded as a single char at the
 127 * begining of the symbol name */
 128static char kallsyms_get_symbol_type(unsigned int off)
 129{
 130        /* get just the first code, look it up in the token table, and return the
 131         * first char from this token */
 132        return kallsyms_token_table[ kallsyms_token_index[ kallsyms_names[off+1] ] ];
 133}
 134
 135
 136/* find the offset on the compressed stream given and index in the
 137 * kallsyms array */
 138static unsigned int get_symbol_offset(unsigned long pos)
 139{
 140        const u8 *name;
 141        int i;
 142
 143        /* use the closest marker we have. We have markers every 256 positions,
 144         * so that should be close enough */
 145        name = &kallsyms_names[ kallsyms_markers[pos>>8] ];
 146
 147        /* sequentially scan all the symbols up to the point we're searching for.
 148         * Every symbol is stored in a [<len>][<len> bytes of data] format, so we
 149         * just need to add the len to the current pointer for every symbol we
 150         * wish to skip */
 151        for(i = 0; i < (pos&0xFF); i++)
 152                name = name + (*name) + 1;
 153
 154        return name - kallsyms_names;
 155}
 156
 157/* Lookup the address for this symbol. Returns 0 if not found. */
 158unsigned long kallsyms_lookup_name(const char *name)
 159{
 160        char namebuf[KSYM_NAME_LEN];
 161        unsigned long i;
 162        unsigned int off;
 163
 164        for (i = 0, off = 0; i < kallsyms_num_syms; i++) {
 165                off = kallsyms_expand_symbol(off, namebuf);
 166
 167                if (strcmp(namebuf, name) == 0)
 168                        return kallsyms_addresses[i];
 169        }
 170        return module_kallsyms_lookup_name(name);
 171}
 172
 173static unsigned long get_symbol_pos(unsigned long addr,
 174                                    unsigned long *symbolsize,
 175                                    unsigned long *offset)
 176{
 177        unsigned long symbol_start = 0, symbol_end = 0;
 178        unsigned long i, low, high, mid;
 179
 180        /* This kernel should never had been booted. */
 181        BUG_ON(!kallsyms_addresses);
 182
 183        /* do a binary search on the sorted kallsyms_addresses array */
 184        low = 0;
 185        high = kallsyms_num_syms;
 186
 187        while (high - low > 1) {
 188                mid = (low + high) / 2;
 189                if (kallsyms_addresses[mid] <= addr)
 190                        low = mid;
 191                else
 192                        high = mid;
 193        }
 194
 195        /*
 196         * search for the first aliased symbol. Aliased
 197         * symbols are symbols with the same address
 198         */
 199        while (low && kallsyms_addresses[low-1] == kallsyms_addresses[low])
 200                --low;
 201
 202        symbol_start = kallsyms_addresses[low];
 203
 204        /* Search for next non-aliased symbol */
 205        for (i = low + 1; i < kallsyms_num_syms; i++) {
 206                if (kallsyms_addresses[i] > symbol_start) {
 207                        symbol_end = kallsyms_addresses[i];
 208                        break;
 209                }
 210        }
 211
 212        /* if we found no next symbol, we use the end of the section */
 213        if (!symbol_end) {
 214                if (is_kernel_inittext(addr))
 215                        symbol_end = (unsigned long)_einittext;
 216                else if (all_var)
 217                        symbol_end = (unsigned long)_end;
 218                else
 219                        symbol_end = (unsigned long)_etext;
 220        }
 221
 222        if (symbolsize)
 223                *symbolsize = symbol_end - symbol_start;
 224        if (offset)
 225                *offset = addr - symbol_start;
 226
 227        return low;
 228}
 229
 230/*
 231 * Lookup an address but don't bother to find any names.
 232 */
 233int kallsyms_lookup_size_offset(unsigned long addr, unsigned long *symbolsize,
 234                                unsigned long *offset)
 235{
 236        if (is_ksym_addr(addr))
 237                return !!get_symbol_pos(addr, symbolsize, offset);
 238
 239        return !!module_address_lookup(addr, symbolsize, offset, NULL);
 240}
 241
 242/*
 243 * Lookup an address
 244 * - modname is set to NULL if it's in the kernel
 245 * - we guarantee that the returned name is valid until we reschedule even if
 246 *   it resides in a module
 247 * - we also guarantee that modname will be valid until rescheduled
 248 */
 249const char *kallsyms_lookup(unsigned long addr,
 250                            unsigned long *symbolsize,
 251                            unsigned long *offset,
 252                            char **modname, char *namebuf)
 253{
 254        const char *msym;
 255
 256        namebuf[KSYM_NAME_LEN - 1] = 0;
 257        namebuf[0] = 0;
 258
 259        if (is_ksym_addr(addr)) {
 260                unsigned long pos;
 261
 262                pos = get_symbol_pos(addr, symbolsize, offset);
 263                /* Grab name */
 264                kallsyms_expand_symbol(get_symbol_offset(pos), namebuf);
 265                if (modname)
 266                        *modname = NULL;
 267                return namebuf;
 268        }
 269
 270        /* see if it's in a module */
 271        msym = module_address_lookup(addr, symbolsize, offset, modname);
 272        if (msym)
 273                return strncpy(namebuf, msym, KSYM_NAME_LEN - 1);
 274
 275        return NULL;
 276}
 277
 278int lookup_symbol_name(unsigned long addr, char *symname)
 279{
 280        symname[0] = '\0';
 281        symname[KSYM_NAME_LEN - 1] = '\0';
 282
 283        if (is_ksym_addr(addr)) {
 284                unsigned long pos;
 285
 286                pos = get_symbol_pos(addr, NULL, NULL);
 287                /* Grab name */
 288                kallsyms_expand_symbol(get_symbol_offset(pos), symname);
 289                return 0;
 290        }
 291        /* see if it's in a module */
 292        return lookup_module_symbol_name(addr, symname);
 293}
 294
 295int lookup_symbol_attrs(unsigned long addr, unsigned long *size,
 296                        unsigned long *offset, char *modname, char *name)
 297{
 298        name[0] = '\0';
 299        name[KSYM_NAME_LEN - 1] = '\0';
 300
 301        if (is_ksym_addr(addr)) {
 302                unsigned long pos;
 303
 304                pos = get_symbol_pos(addr, size, offset);
 305                /* Grab name */
 306                kallsyms_expand_symbol(get_symbol_offset(pos), name);
 307                modname[0] = '\0';
 308                return 0;
 309        }
 310        /* see if it's in a module */
 311        return lookup_module_symbol_attrs(addr, size, offset, modname, name);
 312}
 313
 314/* Look up a kernel symbol and return it in a text buffer. */
 315int sprint_symbol(char *buffer, unsigned long address)
 316{
 317        char *modname;
 318        const char *name;
 319        unsigned long offset, size;
 320        char namebuf[KSYM_NAME_LEN];
 321
 322        name = kallsyms_lookup(address, &size, &offset, &modname, namebuf);
 323        if (!name)
 324                return sprintf(buffer, "0x%lx", address);
 325
 326        if (modname)
 327                return sprintf(buffer, "%s+%#lx/%#lx [%s]", name, offset,
 328                                size, modname);
 329        else
 330                return sprintf(buffer, "%s+%#lx/%#lx", name, offset, size);
 331}
 332
 333/* Look up a kernel symbol and print it to the kernel messages. */
 334void __print_symbol(const char *fmt, unsigned long address)
 335{
 336        char buffer[KSYM_SYMBOL_LEN];
 337
 338        sprint_symbol(buffer, address);
 339
 340        printk(fmt, buffer);
 341}
 342
 343/* To avoid using get_symbol_offset for every symbol, we carry prefix along. */
 344struct kallsym_iter
 345{
 346        loff_t pos;
 347        unsigned long value;
 348        unsigned int nameoff; /* If iterating in core kernel symbols */
 349        char type;
 350        char name[KSYM_NAME_LEN];
 351        char module_name[MODULE_NAME_LEN];
 352        int exported;
 353};
 354
 355static int get_ksymbol_mod(struct kallsym_iter *iter)
 356{
 357        if (module_get_kallsym(iter->pos - kallsyms_num_syms, &iter->value,
 358                                &iter->type, iter->name, iter->module_name,
 359                                &iter->exported) < 0)
 360                return 0;
 361        return 1;
 362}
 363
 364/* Returns space to next name. */
 365static unsigned long get_ksymbol_core(struct kallsym_iter *iter)
 366{
 367        unsigned off = iter->nameoff;
 368
 369        iter->module_name[0] = '\0';
 370        iter->value = kallsyms_addresses[iter->pos];
 371
 372        iter->type = kallsyms_get_symbol_type(off);
 373
 374        off = kallsyms_expand_symbol(off, iter->name);
 375
 376        return off - iter->nameoff;
 377}
 378
 379static void reset_iter(struct kallsym_iter *iter, loff_t new_pos)
 380{
 381        iter->name[0] = '\0';
 382        iter->nameoff = get_symbol_offset(new_pos);
 383        iter->pos = new_pos;
 384}
 385
 386/* Returns false if pos at or past end of file. */
 387static int update_iter(struct kallsym_iter *iter, loff_t pos)
 388{
 389        /* Module symbols can be accessed randomly. */
 390        if (pos >= kallsyms_num_syms) {
 391                iter->pos = pos;
 392                return get_ksymbol_mod(iter);
 393        }
 394        
 395        /* If we're not on the desired position, reset to new position. */
 396        if (pos != iter->pos)
 397                reset_iter(iter, pos);
 398
 399        iter->nameoff += get_ksymbol_core(iter);
 400        iter->pos++;
 401
 402        return 1;
 403}
 404
 405static void *s_next(struct seq_file *m, void *p, loff_t *pos)
 406{
 407        (*pos)++;
 408
 409        if (!update_iter(m->private, *pos))
 410                return NULL;
 411        return p;
 412}
 413
 414static void *s_start(struct seq_file *m, loff_t *pos)
 415{
 416        if (!update_iter(m->private, *pos))
 417                return NULL;
 418        return m->private;
 419}
 420
 421static void s_stop(struct seq_file *m, void *p)
 422{
 423}
 424
 425static int s_show(struct seq_file *m, void *p)
 426{
 427        struct kallsym_iter *iter = m->private;
 428
 429        /* Some debugging symbols have no name.  Ignore them. */ 
 430        if (!iter->name[0])
 431                return 0;
 432
 433        if (iter->module_name[0]) {
 434                char type;
 435
 436                /* Label it "global" if it is exported,
 437                 * "local" if not exported. */
 438                type = iter->exported ? toupper(iter->type) :
 439                                        tolower(iter->type);
 440                seq_printf(m, "%0*lx %c %s\t[%s]\n",
 441                           (int)(2*sizeof(void*)),
 442                           iter->value, type, iter->name, iter->module_name);
 443        } else
 444                seq_printf(m, "%0*lx %c %s\n",
 445                           (int)(2*sizeof(void*)),
 446                           iter->value, iter->type, iter->name);
 447        return 0;
 448}
 449
 450static const struct seq_operations kallsyms_op = {
 451        .start = s_start,
 452        .next = s_next,
 453        .stop = s_stop,
 454        .show = s_show
 455};
 456
 457static int kallsyms_open(struct inode *inode, struct file *file)
 458{
 459        /* We keep iterator in m->private, since normal case is to
 460         * s_start from where we left off, so we avoid doing
 461         * using get_symbol_offset for every symbol */
 462        struct kallsym_iter *iter;
 463        int ret;
 464
 465        iter = kmalloc(sizeof(*iter), GFP_KERNEL);
 466        if (!iter)
 467                return -ENOMEM;
 468        reset_iter(iter, 0);
 469
 470        ret = seq_open(file, &kallsyms_op);
 471        if (ret == 0)
 472                ((struct seq_file *)file->private_data)->private = iter;
 473        else
 474                kfree(iter);
 475        return ret;
 476}
 477
 478static const struct file_operations kallsyms_operations = {
 479        .open = kallsyms_open,
 480        .read = seq_read,
 481        .llseek = seq_lseek,
 482        .release = seq_release_private,
 483};
 484
 485static int __init kallsyms_init(void)
 486{
 487        struct proc_dir_entry *entry;
 488
 489        entry = create_proc_entry("kallsyms", 0444, NULL);
 490        if (entry)
 491                entry->proc_fops = &kallsyms_operations;
 492        return 0;
 493}
 494__initcall(kallsyms_init);
 495
 496EXPORT_SYMBOL(__print_symbol);
 497EXPORT_SYMBOL_GPL(sprint_symbol);
 498