[34] | 1 | /* |
---|
| 2 | * symbols.c: in-kernel printing of symbolic oopses and stack traces. |
---|
| 3 | * |
---|
| 4 | * Copyright 2002 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation |
---|
| 5 | * |
---|
| 6 | * ChangeLog: |
---|
| 7 | * |
---|
| 8 | * (25/Aug/2004) Paulo Marques <pmarques@grupopie.com> |
---|
| 9 | * Changed the compression method from stem compression to "table lookup" |
---|
| 10 | * compression (see tools/symbols.c for a more complete description) |
---|
| 11 | */ |
---|
| 12 | |
---|
| 13 | #include <xen/config.h> |
---|
| 14 | #include <xen/symbols.h> |
---|
| 15 | #include <xen/kernel.h> |
---|
| 16 | #include <xen/init.h> |
---|
| 17 | #include <xen/lib.h> |
---|
| 18 | #include <xen/string.h> |
---|
| 19 | #include <xen/spinlock.h> |
---|
| 20 | |
---|
| 21 | extern unsigned long symbols_addresses[]; |
---|
| 22 | extern unsigned long symbols_num_syms; |
---|
| 23 | extern u8 symbols_names[]; |
---|
| 24 | |
---|
| 25 | extern u8 symbols_token_table[]; |
---|
| 26 | extern u16 symbols_token_index[]; |
---|
| 27 | |
---|
| 28 | extern unsigned long symbols_markers[]; |
---|
| 29 | |
---|
| 30 | /* expand a compressed symbol data into the resulting uncompressed string, |
---|
| 31 | given the offset to where the symbol is in the compressed stream */ |
---|
| 32 | static unsigned int symbols_expand_symbol(unsigned int off, char *result) |
---|
| 33 | { |
---|
| 34 | int len, skipped_first = 0; |
---|
| 35 | u8 *tptr, *data; |
---|
| 36 | |
---|
| 37 | /* get the compressed symbol length from the first symbol byte */ |
---|
| 38 | data = &symbols_names[off]; |
---|
| 39 | len = *data; |
---|
| 40 | data++; |
---|
| 41 | |
---|
| 42 | /* update the offset to return the offset for the next symbol on |
---|
| 43 | * the compressed stream */ |
---|
| 44 | off += len + 1; |
---|
| 45 | |
---|
| 46 | /* for every byte on the compressed symbol data, copy the table |
---|
| 47 | entry for that byte */ |
---|
| 48 | while(len) { |
---|
| 49 | tptr = &symbols_token_table[ symbols_token_index[*data] ]; |
---|
| 50 | data++; |
---|
| 51 | len--; |
---|
| 52 | |
---|
| 53 | while (*tptr) { |
---|
| 54 | if(skipped_first) { |
---|
| 55 | *result = *tptr; |
---|
| 56 | result++; |
---|
| 57 | } else |
---|
| 58 | skipped_first = 1; |
---|
| 59 | tptr++; |
---|
| 60 | } |
---|
| 61 | } |
---|
| 62 | |
---|
| 63 | *result = '\0'; |
---|
| 64 | |
---|
| 65 | /* return to offset to the next symbol */ |
---|
| 66 | return off; |
---|
| 67 | } |
---|
| 68 | |
---|
| 69 | /* find the offset on the compressed stream given and index in the |
---|
| 70 | * symbols array */ |
---|
| 71 | static unsigned int get_symbol_offset(unsigned long pos) |
---|
| 72 | { |
---|
| 73 | u8 *name; |
---|
| 74 | int i; |
---|
| 75 | |
---|
| 76 | /* use the closest marker we have. We have markers every 256 positions, |
---|
| 77 | * so that should be close enough */ |
---|
| 78 | name = &symbols_names[ symbols_markers[pos>>8] ]; |
---|
| 79 | |
---|
| 80 | /* sequentially scan all the symbols up to the point we're searching for. |
---|
| 81 | * Every symbol is stored in a [<len>][<len> bytes of data] format, so we |
---|
| 82 | * just need to add the len to the current pointer for every symbol we |
---|
| 83 | * wish to skip */ |
---|
| 84 | for(i = 0; i < (pos&0xFF); i++) |
---|
| 85 | name = name + (*name) + 1; |
---|
| 86 | |
---|
| 87 | return name - symbols_names; |
---|
| 88 | } |
---|
| 89 | |
---|
| 90 | const char *symbols_lookup(unsigned long addr, |
---|
| 91 | unsigned long *symbolsize, |
---|
| 92 | unsigned long *offset, |
---|
| 93 | char *namebuf) |
---|
| 94 | { |
---|
| 95 | unsigned long i, low, high, mid; |
---|
| 96 | unsigned long symbol_end = 0; |
---|
| 97 | |
---|
| 98 | namebuf[KSYM_NAME_LEN] = 0; |
---|
| 99 | namebuf[0] = 0; |
---|
| 100 | |
---|
| 101 | if (!is_kernel_text(addr) && !is_kernel_inittext(addr)) |
---|
| 102 | return NULL; |
---|
| 103 | |
---|
| 104 | /* do a binary search on the sorted symbols_addresses array */ |
---|
| 105 | low = 0; |
---|
| 106 | high = symbols_num_syms; |
---|
| 107 | |
---|
| 108 | while (high-low > 1) { |
---|
| 109 | mid = (low + high) / 2; |
---|
| 110 | if (symbols_addresses[mid] <= addr) low = mid; |
---|
| 111 | else high = mid; |
---|
| 112 | } |
---|
| 113 | |
---|
| 114 | /* search for the first aliased symbol. Aliased symbols are |
---|
| 115 | symbols with the same address */ |
---|
| 116 | while (low && symbols_addresses[low - 1] == symbols_addresses[low]) |
---|
| 117 | --low; |
---|
| 118 | |
---|
| 119 | /* Grab name */ |
---|
| 120 | symbols_expand_symbol(get_symbol_offset(low), namebuf); |
---|
| 121 | |
---|
| 122 | /* Search for next non-aliased symbol */ |
---|
| 123 | for (i = low + 1; i < symbols_num_syms; i++) { |
---|
| 124 | if (symbols_addresses[i] > symbols_addresses[low]) { |
---|
| 125 | symbol_end = symbols_addresses[i]; |
---|
| 126 | break; |
---|
| 127 | } |
---|
| 128 | } |
---|
| 129 | |
---|
| 130 | /* if we found no next symbol, we use the end of the section */ |
---|
| 131 | if (!symbol_end) |
---|
| 132 | symbol_end = is_kernel_inittext(addr) ? |
---|
| 133 | (unsigned long)_einittext : (unsigned long)_etext; |
---|
| 134 | |
---|
| 135 | *symbolsize = symbol_end - symbols_addresses[low]; |
---|
| 136 | *offset = addr - symbols_addresses[low]; |
---|
| 137 | return namebuf; |
---|
| 138 | } |
---|
| 139 | |
---|
| 140 | /* Replace "%s" in format with address, or returns -errno. */ |
---|
| 141 | void __print_symbol(const char *fmt, unsigned long address) |
---|
| 142 | { |
---|
| 143 | const char *name; |
---|
| 144 | unsigned long offset, size, flags; |
---|
| 145 | |
---|
| 146 | static DEFINE_SPINLOCK(lock); |
---|
| 147 | static char namebuf[KSYM_NAME_LEN+1]; |
---|
| 148 | #define BUFFER_SIZE sizeof("%s+%#lx/%#lx [%s]") + KSYM_NAME_LEN + \ |
---|
| 149 | 2*(BITS_PER_LONG*3/10) + 1 |
---|
| 150 | static char buffer[BUFFER_SIZE]; |
---|
| 151 | |
---|
| 152 | spin_lock_irqsave(&lock, flags); |
---|
| 153 | |
---|
| 154 | name = symbols_lookup(address, &size, &offset, namebuf); |
---|
| 155 | |
---|
| 156 | if (!name) |
---|
| 157 | snprintf(buffer, BUFFER_SIZE, "???"); |
---|
| 158 | else |
---|
| 159 | snprintf(buffer, BUFFER_SIZE, "%s+%#lx/%#lx", name, offset, size); |
---|
| 160 | |
---|
| 161 | printk(fmt, buffer); |
---|
| 162 | |
---|
| 163 | spin_unlock_irqrestore(&lock, flags); |
---|
| 164 | } |
---|