X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=kernel%2Fkallsyms.c;h=6f294ff4f9eec46f448dd9eb30fcb0b2ed47e6dc;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=ab39819f0e342c9df9c8c0ba264487513d0e0af6;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index ab39819f0..6f294ff4f 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -4,7 +4,12 @@ * Rewritten and vastly simplified by Rusty Russell for in-kernel * module loader: * Copyright 2002 Rusty Russell IBM Corporation - * Stem compression by Andi Kleen. + * + * ChangeLog: + * + * (25/Aug/2004) Paulo Marques + * Changed the compression method from stem compression to "table lookup" + * compression (see scripts/kallsyms.c for a more complete description) */ #include #include @@ -13,14 +18,27 @@ #include #include #include +#include /* for cond_resched */ +#include +#include + +#include + +#ifdef CONFIG_KALLSYMS_ALL +#define all_var 1 +#else +#define all_var 0 +#endif /* These will be re-linked against their real values during the second link stage */ -extern unsigned long kallsyms_addresses[] __attribute__((weak)); -extern unsigned long kallsyms_num_syms __attribute__((weak)); -extern char kallsyms_names[] __attribute__((weak)); +extern const unsigned long kallsyms_addresses[] __attribute__((weak)); +extern const unsigned long kallsyms_num_syms __attribute__((weak)); +extern const u8 kallsyms_names[] __attribute__((weak)); + +extern const u8 kallsyms_token_table[] __attribute__((weak)); +extern const u16 kallsyms_token_index[] __attribute__((weak)); -/* Defined by the linker script. */ -extern char _stext[], _etext[], _sinittext[], _einittext[]; +extern const unsigned long kallsyms_markers[] __attribute__((weak)); static inline int is_kernel_inittext(unsigned long addr) { @@ -30,79 +48,223 @@ static inline int is_kernel_inittext(unsigned long addr) return 0; } +static inline int is_kernel_extratext(unsigned long addr) +{ + if (addr >= (unsigned long)_sextratext + && addr <= (unsigned long)_eextratext) + return 1; + return 0; +} + static inline int is_kernel_text(unsigned long addr) { if (addr >= (unsigned long)_stext && addr <= (unsigned long)_etext) return 1; - return 0; + return in_gate_area_no_task(addr); +} + +static inline int is_kernel(unsigned long addr) +{ + if (addr >= (unsigned long)_stext && addr <= (unsigned long)_end) + return 1; + return in_gate_area_no_task(addr); +} + +static int is_ksym_addr(unsigned long addr) +{ + if (all_var) + return is_kernel(addr); + + return is_kernel_text(addr) || is_kernel_inittext(addr) || + is_kernel_extratext(addr); +} + +/* expand a compressed symbol data into the resulting uncompressed string, + given the offset to where the symbol is in the compressed stream */ +static unsigned int kallsyms_expand_symbol(unsigned int off, char *result) +{ + int len, skipped_first = 0; + const u8 *tptr, *data; + + /* get the compressed symbol length from the first symbol byte */ + data = &kallsyms_names[off]; + len = *data; + data++; + + /* update the offset to return the offset for the next symbol on + * the compressed stream */ + off += len + 1; + + /* for every byte on the compressed symbol data, copy the table + entry for that byte */ + while(len) { + tptr = &kallsyms_token_table[ kallsyms_token_index[*data] ]; + data++; + len--; + + while (*tptr) { + if(skipped_first) { + *result = *tptr; + result++; + } else + skipped_first = 1; + tptr++; + } + } + + *result = '\0'; + + /* return to offset to the next symbol */ + return off; +} + +/* get symbol type information. This is encoded as a single char at the + * begining of the symbol name */ +static char kallsyms_get_symbol_type(unsigned int off) +{ + /* get just the first code, look it up in the token table, and return the + * first char from this token */ + return kallsyms_token_table[ kallsyms_token_index[ kallsyms_names[off+1] ] ]; +} + + +/* find the offset on the compressed stream given and index in the + * kallsyms array */ +static unsigned int get_symbol_offset(unsigned long pos) +{ + const u8 *name; + int i; + + /* use the closest marker we have. We have markers every 256 positions, + * so that should be close enough */ + name = &kallsyms_names[ kallsyms_markers[pos>>8] ]; + + /* sequentially scan all the symbols up to the point we're searching for. + * Every symbol is stored in a [][ bytes of data] format, so we + * just need to add the len to the current pointer for every symbol we + * wish to skip */ + for(i = 0; i < (pos&0xFF); i++) + name = name + (*name) + 1; + + return name - kallsyms_names; } /* Lookup the address for this symbol. Returns 0 if not found. */ unsigned long kallsyms_lookup_name(const char *name) { - char namebuf[128]; + char namebuf[KSYM_NAME_LEN+1]; unsigned long i; - char *knames; + unsigned int off; - for (i = 0, knames = kallsyms_names; i < kallsyms_num_syms; i++) { - unsigned prefix = *knames++; + for (i = 0, off = 0; i < kallsyms_num_syms; i++) { + off = kallsyms_expand_symbol(off, namebuf); - strlcpy(namebuf + prefix, knames, 127 - prefix); if (strcmp(namebuf, name) == 0) return kallsyms_addresses[i]; - - knames += strlen(knames) + 1; } return module_kallsyms_lookup_name(name); } -/* 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, char *namebuf) +static unsigned long get_symbol_pos(unsigned long addr, + unsigned long *symbolsize, + unsigned long *offset) { - unsigned long i, best = 0; + unsigned long symbol_start = 0, symbol_end = 0; + unsigned long i, low, high, mid; /* This kernel should never had been booted. */ BUG_ON(!kallsyms_addresses); - namebuf[127] = 0; - namebuf[0] = 0; + /* do a binary search on the sorted kallsyms_addresses array */ + low = 0; + high = kallsyms_num_syms; - if (is_kernel_text(addr) || is_kernel_inittext(addr)) { - unsigned long symbol_end; - char *name = kallsyms_names; + while (high - low > 1) { + mid = (low + high) / 2; + if (kallsyms_addresses[mid] <= addr) + low = mid; + else + high = mid; + } - /* 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; - } + /* + * search for the first aliased symbol. Aliased + * symbols are symbols with the same address + */ + while (low && kallsyms_addresses[low-1] == kallsyms_addresses[low]) + --low; - /* Grab name */ - for (i = 0; i <= best; i++) { - unsigned prefix = *name++; - strncpy(namebuf + prefix, name, 127 - prefix); - name += strlen(name) + 1; + symbol_start = kallsyms_addresses[low]; + + /* Search for next non-aliased symbol */ + for (i = low + 1; i < kallsyms_num_syms; i++) { + if (kallsyms_addresses[i] > symbol_start) { + symbol_end = kallsyms_addresses[i]; + break; } + } - /* Base symbol size on next symbol. */ - if (best + 1 < kallsyms_num_syms) - symbol_end = kallsyms_addresses[best + 1]; - else if (is_kernel_inittext(addr)) + /* if we found no next symbol, we use the end of the section */ + if (!symbol_end) { + if (is_kernel_inittext(addr)) symbol_end = (unsigned long)_einittext; + else if (all_var) + symbol_end = (unsigned long)_end; else symbol_end = (unsigned long)_etext; + } + + *symbolsize = symbol_end - symbol_start; + *offset = addr - symbol_start; + + return low; +} + +/* + * Lookup an address but don't bother to find any names. + */ +int kallsyms_lookup_size_offset(unsigned long addr, unsigned long *symbolsize, + unsigned long *offset) +{ + if (is_ksym_addr(addr)) + return !!get_symbol_pos(addr, symbolsize, offset); + + return !!module_address_lookup(addr, symbolsize, offset, NULL); +} + +/* + * Lookup an address + * - modname is set to NULL if it's in the kernel + * - we guarantee that the returned name is valid until we reschedule even if + * it resides in a module + * - we also guarantee that modname will be valid until rescheduled + */ +const char *kallsyms_lookup(unsigned long addr, + unsigned long *symbolsize, + unsigned long *offset, + char **modname, char *namebuf) +{ + const char *msym; + + namebuf[KSYM_NAME_LEN] = 0; + namebuf[0] = 0; + + if (is_ksym_addr(addr)) { + unsigned long pos; - *symbolsize = symbol_end - kallsyms_addresses[best]; + pos = get_symbol_pos(addr, symbolsize, offset); + /* Grab name */ + kallsyms_expand_symbol(get_symbol_offset(pos), namebuf); *modname = NULL; - *offset = addr - kallsyms_addresses[best]; return namebuf; } - return module_address_lookup(addr, symbolsize, offset, modname); + /* see if it's in a module */ + msym = module_address_lookup(addr, symbolsize, offset, modname); + if (msym) + return strncpy(namebuf, msym, KSYM_NAME_LEN); + + return NULL; } /* Replace "%s" in format with address, or returns -errno. */ @@ -111,37 +273,25 @@ void __print_symbol(const char *fmt, unsigned long address) char *modname; const char *name; unsigned long offset, size; - char namebuf[128]; + char namebuf[KSYM_NAME_LEN+1]; + char buffer[sizeof("%s+%#lx/%#lx [%s]") + KSYM_NAME_LEN + + 2*(BITS_PER_LONG*3/10) + MODULE_NAME_LEN + 1]; name = kallsyms_lookup(address, &size, &offset, &modname, namebuf); - if (!name) { - char addrstr[sizeof("0x%lx") + (BITS_PER_LONG*3/10)]; - - sprintf(addrstr, "0x%lx", address); - printk(fmt, addrstr); - return; - } - - 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); + if (!name) + sprintf(buffer, "0x%lx", address); + else { + if (modname) + sprintf(buffer, "%s+%#lx/%#lx [%s]", name, offset, + size, modname); + else + sprintf(buffer, "%s+%#lx/%#lx", name, offset, size); } + printk(fmt, buffer); } -/* To avoid O(n^2) iteration, we carry prefix along. */ +/* To avoid using get_symbol_offset for every symbol, we carry prefix along. */ struct kallsym_iter { loff_t pos; @@ -149,50 +299,44 @@ struct kallsym_iter unsigned long value; unsigned int nameoff; /* If iterating in core kernel symbols */ char type; - char name[128]; + char name[KSYM_NAME_LEN+1]; }; -/* Only label it "global" if it is exported. */ -static void upcase_if_global(struct kallsym_iter *iter) -{ - if (is_exported(iter->name, iter->owner)) - iter->type += 'A' - 'a'; -} - static int get_ksymbol_mod(struct kallsym_iter *iter) { iter->owner = module_get_kallsym(iter->pos - kallsyms_num_syms, - &iter->value, - &iter->type, iter->name); + &iter->value, &iter->type, + iter->name, sizeof(iter->name)); if (iter->owner == NULL) return 0; - upcase_if_global(iter); + /* Label it "global" if it is exported, "local" if not exported. */ + iter->type = is_exported(iter->name, iter->owner) + ? toupper(iter->type) : tolower(iter->type); + return 1; } -static void get_ksymbol_core(struct kallsym_iter *iter) +/* Returns space to next name. */ +static unsigned long get_ksymbol_core(struct kallsym_iter *iter) { - unsigned stemlen; - - /* First char of each symbol name indicates prefix length - shared with previous name (stem compression). */ - stemlen = kallsyms_names[iter->nameoff++]; + unsigned off = iter->nameoff; - strlcpy(iter->name+stemlen, kallsyms_names+iter->nameoff, 128-stemlen); - iter->nameoff += strlen(kallsyms_names + iter->nameoff) + 1; iter->owner = NULL; iter->value = kallsyms_addresses[iter->pos]; - iter->type = 't'; - upcase_if_global(iter); + iter->type = kallsyms_get_symbol_type(off); + + off = kallsyms_expand_symbol(off, iter->name); + + return off - iter->nameoff; } -static void reset_iter(struct kallsym_iter *iter) +static void reset_iter(struct kallsym_iter *iter, loff_t new_pos) { iter->name[0] = '\0'; - iter->nameoff = 0; - iter->pos = 0; + iter->nameoff = get_symbol_offset(new_pos); + iter->pos = new_pos; } /* Returns false if pos at or past end of file. */ @@ -204,15 +348,13 @@ static int update_iter(struct kallsym_iter *iter, loff_t pos) return get_ksymbol_mod(iter); } - /* If we're past the desired position, reset to start. */ - if (pos < iter->pos) - reset_iter(iter); - - /* We need to iterate through the previous symbols: can be slow */ - for (; iter->pos != pos; iter->pos++) { - get_ksymbol_core(iter); - cond_resched(); - } + /* If we're not on the desired position, reset to new position. */ + if (pos != iter->pos) + reset_iter(iter, pos); + + iter->nameoff += get_ksymbol_core(iter); + iter->pos++; + return 1; } @@ -256,7 +398,7 @@ static int s_show(struct seq_file *m, void *p) return 0; } -struct seq_operations kallsyms_op = { +static const struct seq_operations kallsyms_op = { .start = s_start, .next = s_next, .stop = s_stop, @@ -266,14 +408,15 @@ struct seq_operations kallsyms_op = { static int kallsyms_open(struct inode *inode, struct file *file) { /* We keep iterator in m->private, since normal case is to - * s_start from where we left off, so we avoid O(N^2). */ + * s_start from where we left off, so we avoid doing + * using get_symbol_offset for every symbol */ struct kallsym_iter *iter; int ret; iter = kmalloc(sizeof(*iter), GFP_KERNEL); if (!iter) return -ENOMEM; - reset_iter(iter); + reset_iter(iter, 0); ret = seq_open(file, &kallsyms_op); if (ret == 0) @@ -290,14 +433,14 @@ static int kallsyms_release(struct inode *inode, struct file *file) return seq_release(inode, file); } -static struct file_operations kallsyms_operations = { +static const struct file_operations kallsyms_operations = { .open = kallsyms_open, .read = seq_read, .llseek = seq_lseek, .release = kallsyms_release, }; -int __init kallsyms_init(void) +static int __init kallsyms_init(void) { struct proc_dir_entry *entry; @@ -308,5 +451,4 @@ int __init kallsyms_init(void) } __initcall(kallsyms_init); -EXPORT_SYMBOL(kallsyms_lookup); EXPORT_SYMBOL(__print_symbol);