2 * kallsyms.c: in-kernel printing of symbolic oopses and stack traces.
4 * Rewritten and vastly simplified by Rusty Russell for in-kernel
6 * Copyright 2002 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation
10 * (25/Aug/2004) Paulo Marques <pmarques@grupopie.com>
11 * Changed the compression method from stem compression to "table lookup"
12 * compression (see scripts/kallsyms.c for a more complete description)
14 #include <lwk/kernel.h>
15 #include <lwk/kallsyms.h>
17 #include <lwk/string.h>
18 #include <arch/sections.h>
20 #ifdef CONFIG_KALLSYMS_ALL
27 * These will be re-linked against their
28 * real values during the second link stage
30 extern unsigned long kallsyms_addresses[] __attribute__((weak));
31 extern unsigned long kallsyms_num_syms __attribute__((weak,section("data")));
32 extern u8 kallsyms_names[] __attribute__((weak));
34 extern u8 kallsyms_token_table[] __attribute__((weak));
35 extern u16 kallsyms_token_index[] __attribute__((weak));
37 extern unsigned long kallsyms_markers[] __attribute__((weak));
39 static inline int is_kernel_inittext(unsigned long addr)
41 if (addr >= (unsigned long)_sinittext
42 && addr <= (unsigned long)_einittext)
47 static inline int is_kernel_extratext(unsigned long addr)
49 if (addr >= (unsigned long)_sextratext
50 && addr <= (unsigned long)_eextratext)
55 static inline int is_kernel_text(unsigned long addr)
57 if (addr >= (unsigned long)_stext && addr <= (unsigned long)_etext)
62 static inline int is_kernel(unsigned long addr)
64 if (addr >= (unsigned long)_stext && addr <= (unsigned long)_end)
70 * Expand a compressed symbol data into the resulting uncompressed string,
71 * given the offset to where the symbol is in the compressed stream.
73 static unsigned int kallsyms_expand_symbol(unsigned int off, char *result)
75 int len, skipped_first = 0;
78 /* get the compressed symbol length from the first symbol byte */
79 data = &kallsyms_names[off];
83 /* update the offset to return the offset for the next symbol on
84 * the compressed stream */
87 /* for every byte on the compressed symbol data, copy the table
88 entry for that byte */
90 tptr = &kallsyms_token_table[ kallsyms_token_index[*data] ];
106 /* return to offset to the next symbol */
111 * Find the offset on the compressed stream given and index in the
114 static unsigned int get_symbol_offset(unsigned long pos)
119 /* use the closest marker we have. We have markers every 256 positions,
120 * so that should be close enough */
121 name = &kallsyms_names[ kallsyms_markers[pos>>8] ];
123 /* sequentially scan all the symbols up to the point we're searching
124 * for. Every symbol is stored in a [<len>][<len> bytes of data]
125 * format, so we just need to add the len to the current pointer for
126 * every symbol we wish to skip */
127 for(i = 0; i < (pos&0xFF); i++)
128 name = name + (*name) + 1;
130 return name - kallsyms_names;
134 * Lookup the address for this symbol. Returns 0 if not found.
136 unsigned long kallsyms_lookup_name(const char *name)
138 char namebuf[KSYM_NAME_LEN+1];
142 for (i = 0, off = 0; i < kallsyms_num_syms; i++) {
143 off = kallsyms_expand_symbol(off, namebuf);
145 if (strcmp(namebuf, name) == 0)
146 return kallsyms_addresses[i];
152 * Lookup the symbol name corresponding to a kernel address
154 const char *kallsyms_lookup(unsigned long addr,
155 unsigned long *symbolsize,
156 unsigned long *offset,
159 unsigned long i, low, high, mid;
161 /* This kernel should never had been booted. */
162 BUG_ON(!kallsyms_addresses);
164 namebuf[KSYM_NAME_LEN] = 0;
167 if ((all_var && is_kernel(addr)) ||
168 (!all_var && (is_kernel_text(addr) || is_kernel_inittext(addr) ||
169 is_kernel_extratext(addr)))) {
170 unsigned long symbol_end = 0;
172 /* do a binary search on the sorted kallsyms_addresses array */
174 high = kallsyms_num_syms;
176 while (high-low > 1) {
177 mid = (low + high) / 2;
178 if (kallsyms_addresses[mid] <= addr) low = mid;
182 /* search for the first aliased symbol. Aliased symbols are
183 symbols with the same address */
184 while (low && kallsyms_addresses[low - 1] ==
185 kallsyms_addresses[low])
189 kallsyms_expand_symbol(get_symbol_offset(low), namebuf);
191 /* Search for next non-aliased symbol */
192 for (i = low + 1; i < kallsyms_num_syms; i++) {
193 if (kallsyms_addresses[i] > kallsyms_addresses[low]) {
194 symbol_end = kallsyms_addresses[i];
199 /* if we found no next symbol, we use the end of the section */
201 if (is_kernel_inittext(addr))
202 symbol_end = (unsigned long)_einittext;
204 symbol_end = (all_var) ? (unsigned long)_end
205 : (unsigned long)_etext;
209 *symbolsize = symbol_end - kallsyms_addresses[low];
211 *offset = addr - kallsyms_addresses[low];