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 | } |
---|