diff options
author | kaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk> | 2005-07-22 16:44:33 +0000 |
---|---|---|
committer | kaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk> | 2005-07-22 16:44:33 +0000 |
commit | c1f454ab0cce212413c558afc2c00f1ed8090f81 (patch) | |
tree | 674fe04ae76526b05123302d7d08bf60acce8dcb /xen/common/symbols.c | |
parent | b6d8dea8bfde631c3c1aa723627717c5b8712306 (diff) | |
download | xen-c1f454ab0cce212413c558afc2c00f1ed8090f81.tar.gz xen-c1f454ab0cce212413c558afc2c00f1ed8090f81.tar.bz2 xen-c1f454ab0cce212413c558afc2c00f1ed8090f81.zip |
Port kallsyms to Xen, as 'symbols'.
Signed-off-by: Keir Fraser <keir@xensource.com>
Diffstat (limited to 'xen/common/symbols.c')
-rw-r--r-- | xen/common/symbols.c | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/xen/common/symbols.c b/xen/common/symbols.c new file mode 100644 index 0000000000..84e04d973c --- /dev/null +++ b/xen/common/symbols.c @@ -0,0 +1,158 @@ +/* + * symbols.c: in-kernel printing of symbolic oopses and stack traces. + * + * Copyright 2002 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation + * + * ChangeLog: + * + * (25/Aug/2004) Paulo Marques <pmarques@grupopie.com> + * Changed the compression method from stem compression to "table lookup" + * compression (see tools/symbols.c for a more complete description) + */ + +#include <xen/config.h> +#include <xen/symbols.h> +#include <xen/init.h> +#include <xen/lib.h> +#include <xen/string.h> + +/* These will be re-linked against their real values during the second link stage */ +extern unsigned long symbols_addresses[] __attribute__((weak)); +extern unsigned long symbols_num_syms __attribute__((weak,section("data"))); +extern u8 symbols_names[] __attribute__((weak)); + +extern u8 symbols_token_table[] __attribute__((weak)); +extern u16 symbols_token_index[] __attribute__((weak)); + +extern unsigned long symbols_markers[] __attribute__((weak)); + +/* 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 symbols_expand_symbol(unsigned int off, char *result) +{ + int len, skipped_first = 0; + u8 *tptr, *data; + + /* get the compressed symbol length from the first symbol byte */ + data = &symbols_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 = &symbols_token_table[ symbols_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 + * symbols array */ +static unsigned int get_symbol_offset(unsigned long pos) +{ + u8 *name; + int i; + + /* use the closest marker we have. We have markers every 256 positions, + * so that should be close enough */ + name = &symbols_names[ symbols_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 - symbols_names; +} + +const char *symbols_lookup(unsigned long addr, + unsigned long *symbolsize, + unsigned long *offset, + char *namebuf) +{ + unsigned long i, low, high, mid; + unsigned long symbol_end = 0; + + /* This kernel should never had been booted. */ + BUG_ON(!symbols_addresses); + + namebuf[KSYM_NAME_LEN] = 0; + namebuf[0] = 0; + + if (!is_kernel_text(addr)) + return NULL; + + /* do a binary search on the sorted symbols_addresses array */ + low = 0; + high = symbols_num_syms; + + while (high-low > 1) { + mid = (low + high) / 2; + if (symbols_addresses[mid] <= addr) low = mid; + else high = mid; + } + + /* search for the first aliased symbol. Aliased symbols are + symbols with the same address */ + while (low && symbols_addresses[low - 1] == symbols_addresses[low]) + --low; + + /* Grab name */ + symbols_expand_symbol(get_symbol_offset(low), namebuf); + + /* Search for next non-aliased symbol */ + for (i = low + 1; i < symbols_num_syms; i++) { + if (symbols_addresses[i] > symbols_addresses[low]) { + symbol_end = symbols_addresses[i]; + break; + } + } + + /* if we found no next symbol, we use the end of the section */ + if (!symbol_end) + symbol_end = kernel_text_end(); + + *symbolsize = symbol_end - symbols_addresses[low]; + *offset = addr - symbols_addresses[low]; + return namebuf; +} + +/* Replace "%s" in format with address, or returns -errno. */ +void __print_symbol(const char *fmt, unsigned long address) +{ + const char *name; + unsigned long offset, size; + char namebuf[KSYM_NAME_LEN+1]; + char buffer[sizeof("%s+%#lx/%#lx [%s]") + KSYM_NAME_LEN + + 2*(BITS_PER_LONG*3/10) + 1]; + + name = symbols_lookup(address, &size, &offset, namebuf); + + if (!name) + sprintf(buffer, "???"); + else + sprintf(buffer, "%s+%#lx/%#lx", name, offset, size); + + printk(fmt, buffer); +} |