diff --git a/Makefile b/Makefile index 1a4a54e65..55c83cc1c 100644 --- a/Makefile +++ b/Makefile @@ -599,7 +599,7 @@ endef # Generate .S file with all kernel symbols quiet_cmd_kallsyms = KSYM $@ - cmd_kallsyms = $(NM) -g -n $< | $(KALLSYMS) > $@ + cmd_kallsyms = $(NM) -n $< | $(KALLSYMS) --all-symbols > $@ .tmp_kallsyms1.o .tmp_kallsyms2.o .tmp_kallsyms3.o: %.o: %.S scripts FORCE $(call if_changed_dep,as_o_S) diff --git a/common/Kconfig b/common/Kconfig index 02bc67ed9..9e30579a7 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -148,13 +148,10 @@ config MODULES config KALLSYMS depends on HAS_KALLSYMS - depends on BROKEN bool "kallsyms" help With Kallsyms enabled all symbols are compiled into the barebox image. This is useful to print a nice backtrace when an exception occurs. - No architecture supports backtraces at the moment, so this option - is quite useless at the moment config RELOCATABLE depends on PPC diff --git a/common/kallsyms.c b/common/kallsyms.c index 490adb922..0218991c4 100644 --- a/common/kallsyms.c +++ b/common/kallsyms.c @@ -1,6 +1,7 @@ #include #include #include +#include #ifndef DOXYGEN_SHOULD_SKIP_THIS @@ -16,6 +17,13 @@ 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) @@ -55,6 +63,33 @@ static unsigned int kallsyms_expand_symbol(unsigned int off, char *result) 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 [][ 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) { @@ -68,6 +103,117 @@ unsigned long kallsyms_lookup_name(const char *name) if (strcmp(namebuf, name) == 0) return kallsyms_addresses[i]; } -// return module_kallsyms_lookup_name(name); + + /* 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); + diff --git a/include/kallsyms.h b/include/kallsyms.h index 5117be227..69b84d2c6 100644 --- a/include/kallsyms.h +++ b/include/kallsyms.h @@ -2,6 +2,11 @@ #define __KALLSYMS_H #define KSYM_NAME_LEN 128 +#define KSYM_SYMBOL_LEN (sizeof("%s+%#lx/%#lx [%s]") + (KSYM_NAME_LEN - 1) + \ + 2*(BITS_PER_LONG*3/10) + (MODULE_NAME_LEN - 1) + 1) unsigned long kallsyms_lookup_name(const char *name); +/* Look up a kernel symbol and return it in a text buffer. */ +int sprint_symbol(char *buffer, unsigned long address); + #endif /* __KALLSYMS_H */ diff --git a/lib/vsprintf.c b/lib/vsprintf.c index fec87bac0..ccccc5df0 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include