| #include <common.h> |
| #include <init.h> |
| #include <kallsyms.h> |
| #include <asm-generic/sections.h> |
| |
| #ifndef DOXYGEN_SHOULD_SKIP_THIS |
| |
| /* These will be re-linked against their real values during the second link stage */ |
| 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)); |
| |
| extern const unsigned long kallsyms_markers[] __attribute__((weak)); |
| |
| #endif /* DOXYGEN_SHOULD_SKIP_THIS */ |
| |
| static inline int is_kernel_text(unsigned long addr) |
| { |
| if ((addr >= (unsigned long)_stext && addr <= (unsigned long)_etext)) |
| return 1; |
| return 0; |
| } |
| |
| /* 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; |
| } |
| |
| /* |
| * 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 [<len>][<len> 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[KSYM_NAME_LEN]; |
| unsigned long i; |
| unsigned int off; |
| |
| for (i = 0, off = 0; i < kallsyms_num_syms; i++) { |
| off = kallsyms_expand_symbol(off, namebuf); |
| |
| if (strcmp(namebuf, name) == 0) |
| return kallsyms_addresses[i]; |
| } |
| |
| /* module kallsyms not yet supported */ |
| return 0; |
| } |
| |
| static unsigned long get_symbol_pos(unsigned long addr, |
| unsigned long *symbolsize, |
| unsigned long *offset) |
| { |
| 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); |
| |
| /* Do a binary search on the sorted kallsyms_addresses array. */ |
| low = 0; |
| high = kallsyms_num_syms; |
| |
| while (high - low > 1) { |
| mid = low + (high - low) / 2; |
| if (kallsyms_addresses[mid] <= addr) |
| low = mid; |
| else |
| high = mid; |
| } |
| |
| /* |
| * Search for the first aliased symbol. Aliased |
| * symbols are symbols with the same address. |
| */ |
| while (low && kallsyms_addresses[low-1] == kallsyms_addresses[low]) |
| --low; |
| |
| 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; |
| } |
| } |
| |
| /* If we found no next symbol, we use the end of the section. */ |
| if (!symbol_end) { |
| symbol_end = (unsigned long)_etext; |
| } |
| |
| if (symbolsize) |
| *symbolsize = symbol_end - symbol_start; |
| if (offset) |
| *offset = addr - symbol_start; |
| |
| return low; |
| } |
| |
| /* |
| * 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) |
| { |
| namebuf[KSYM_NAME_LEN - 1] = 0; |
| namebuf[0] = 0; |
| |
| if (is_kernel_text(addr)) { |
| unsigned long pos; |
| |
| pos = get_symbol_pos(addr, symbolsize, offset); |
| /* Grab name */ |
| kallsyms_expand_symbol(get_symbol_offset(pos), namebuf); |
| if (modname) |
| *modname = NULL; |
| return namebuf; |
| } |
| |
| /* moduled not yet supported in kallsyms */ |
| return NULL; |
| } |
| |
| /* Look up a kernel symbol and return it in a text buffer. */ |
| int sprint_symbol(char *buffer, unsigned long address) |
| { |
| char *modname; |
| const char *name; |
| unsigned long offset, size; |
| int len; |
| |
| name = kallsyms_lookup(address, &size, &offset, &modname, buffer); |
| if (!name) |
| return sprintf(buffer, "0x%lx", address); |
| |
| if (name != buffer) |
| strcpy(buffer, name); |
| len = strlen(buffer); |
| buffer += len; |
| |
| if (modname) |
| len += sprintf(buffer, "+%#lx/%#lx [%s]", |
| offset, size, modname); |
| else |
| len += sprintf(buffer, "+%#lx/%#lx", offset, size); |
| |
| return len; |
| } |
| EXPORT_SYMBOL_GPL(sprint_symbol); |
| |