summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2011-03-08 12:00:10 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2011-03-10 14:50:16 +0100
commitf4491d7b0404191a143aae3ee93fe04d396b3dea (patch)
treea175755560e318ef0b9d9649643cd66fd121c685 /common
parent084df155d3f5e4991f675a3e7a707e4eafbd1cd8 (diff)
downloadbarebox-f4491d7b0404191a143aae3ee93fe04d396b3dea.tar.gz
barebox-f4491d7b0404191a143aae3ee93fe04d396b3dea.tar.xz
kallsyms/printk: enable symbol printing support (%pS)
With this kallsyms finally start working at least on ARM. This enables us resolving addresses into symbols which is particularly useful in combination with stack unwinding support. As kallsyms now compile and work we can remove the depends on BROKEN. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'common')
-rw-r--r--common/Kconfig3
-rw-r--r--common/kallsyms.c148
2 files changed, 147 insertions, 4 deletions
diff --git a/common/Kconfig b/common/Kconfig
index 02bc67ed90..9e30579a79 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 490adb9223..0218991c46 100644
--- a/common/kallsyms.c
+++ b/common/kallsyms.c
@@ -1,6 +1,7 @@
#include <common.h>
#include <init.h>
#include <kallsyms.h>
+#include <asm-generic/sections.h>
#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 [<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)
{
@@ -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);
+