Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Merge branch 'devel'
[palacios.git] / kitten / kernel / kallsyms.c
1 /*
2  * kallsyms.c: in-kernel printing of symbolic oopses and stack traces.
3  *
4  * Rewritten and vastly simplified by Rusty Russell for in-kernel
5  * module loader:
6  *   Copyright 2002 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation
7  *
8  * ChangeLog:
9  *
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)
13  */
14 #include <lwk/kernel.h>
15 #include <lwk/kallsyms.h>
16 #include <lwk/init.h>
17 #include <lwk/string.h>
18 #include <arch/sections.h>
19
20 #ifdef CONFIG_KALLSYMS_ALL
21 #define all_var 1
22 #else
23 #define all_var 0
24 #endif
25
26 /**
27  * These will be re-linked against their
28  * real values during the second link stage
29  */
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));
33
34 extern u8 kallsyms_token_table[] __attribute__((weak));
35 extern u16 kallsyms_token_index[] __attribute__((weak));
36
37 extern unsigned long kallsyms_markers[] __attribute__((weak));
38
39 static inline int is_kernel_inittext(unsigned long addr)
40 {
41         if (addr >= (unsigned long)_sinittext
42             && addr <= (unsigned long)_einittext)
43                 return 1;
44         return 0;
45 }
46
47 static inline int is_kernel_extratext(unsigned long addr)
48 {
49         if (addr >= (unsigned long)_sextratext
50             && addr <= (unsigned long)_eextratext)
51                 return 1;
52         return 0;
53 }
54
55 static inline int is_kernel_text(unsigned long addr)
56 {
57         if (addr >= (unsigned long)_stext && addr <= (unsigned long)_etext)
58                 return 1;
59         return 0;
60 }
61
62 static inline int is_kernel(unsigned long addr)
63 {
64         if (addr >= (unsigned long)_stext && addr <= (unsigned long)_end)
65                 return 1;
66         return 0;
67 }
68
69 /**
70  * Expand a compressed symbol data into the resulting uncompressed string,
71  * given the offset to where the symbol is in the compressed stream.
72  */
73 static unsigned int kallsyms_expand_symbol(unsigned int off, char *result)
74 {
75         int len, skipped_first = 0;
76         u8 *tptr, *data;
77
78         /* get the compressed symbol length from the first symbol byte */
79         data = &kallsyms_names[off];
80         len = *data;
81         data++;
82
83         /* update the offset to return the offset for the next symbol on
84          * the compressed stream */
85         off += len + 1;
86
87         /* for every byte on the compressed symbol data, copy the table
88            entry for that byte */
89         while(len) {
90                 tptr = &kallsyms_token_table[ kallsyms_token_index[*data] ];
91                 data++;
92                 len--;
93
94                 while (*tptr) {
95                         if(skipped_first) {
96                                 *result = *tptr;
97                                 result++;
98                         } else
99                                 skipped_first = 1;
100                         tptr++;
101                 }
102         }
103
104         *result = '\0';
105
106         /* return to offset to the next symbol */
107         return off;
108 }
109
110 /**
111  * Find the offset on the compressed stream given and index in the
112  * kallsyms array.
113  */
114 static unsigned int get_symbol_offset(unsigned long pos)
115 {
116         u8 *name;
117         int i;
118
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] ];
122
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;
129
130         return name - kallsyms_names;
131 }
132
133 /**
134  *  Lookup the address for this symbol. Returns 0 if not found.
135  */
136 unsigned long kallsyms_lookup_name(const char *name)
137 {
138         char namebuf[KSYM_NAME_LEN+1];
139         unsigned long i;
140         unsigned int off;
141
142         for (i = 0, off = 0; i < kallsyms_num_syms; i++) {
143                 off = kallsyms_expand_symbol(off, namebuf);
144
145                 if (strcmp(namebuf, name) == 0)
146                         return kallsyms_addresses[i];
147         }
148         return 0;
149 }
150
151 /**
152  * Lookup the symbol name corresponding to a kernel address
153  */
154 const char *kallsyms_lookup(unsigned long addr,
155                             unsigned long *symbolsize,
156                             unsigned long *offset,
157                             char *namebuf)
158 {
159         unsigned long i, low, high, mid;
160
161         /* This kernel should never had been booted. */
162         BUG_ON(!kallsyms_addresses);
163
164         namebuf[KSYM_NAME_LEN] = 0;
165         namebuf[0] = 0;
166
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;
171
172                 /* do a binary search on the sorted kallsyms_addresses array */
173                 low = 0;
174                 high = kallsyms_num_syms;
175
176                 while (high-low > 1) {
177                         mid = (low + high) / 2;
178                         if (kallsyms_addresses[mid] <= addr) low = mid;
179                         else high = mid;
180                 }
181
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])
186                         --low;
187
188                 /* Grab name */
189                 kallsyms_expand_symbol(get_symbol_offset(low), namebuf);
190
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];
195                                 break;
196                         }
197                 }
198
199                 /* if we found no next symbol, we use the end of the section */
200                 if (!symbol_end) {
201                         if (is_kernel_inittext(addr))
202                                 symbol_end = (unsigned long)_einittext;
203                         else
204                                 symbol_end = (all_var) ? (unsigned long)_end
205                                                        : (unsigned long)_etext;
206                 }
207
208                 if (symbolsize)
209                         *symbolsize = symbol_end - kallsyms_addresses[low];
210                 if (offset)
211                         *offset = addr - kallsyms_addresses[low];
212                 return namebuf;
213         }
214
215         return NULL;
216 }
217