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 <linux/kallsyms.h> 15 #include <linux/module.h> 16 #include <linux/init.h> 17 #include <linux/seq_file.h> 18 #include <linux/fs.h> 19 #include <linux/err.h> 20 #include <linux/proc_fs.h> 21 #include <linux/mm.h> 22 23 #include <asm/sections.h> 24 25 #ifdef CONFIG_KALLSYMS_ALL 26 #define all_var 1 27 #else 28 #define all_var 0 29 #endif 30 31 /* These will be re-linked against their real values during the second link stage */ 32 extern unsigned long kallsyms_addresses[] __attribute__((weak)); 33 extern unsigned long kallsyms_num_syms __attribute__((weak,section("data"))); 34 extern u8 kallsyms_names[] __attribute__((weak)); 35 36 extern u8 kallsyms_token_table[] __attribute__((weak)); 37 extern u16 kallsyms_token_index[] __attribute__((weak)); 38 39 extern unsigned long kallsyms_markers[] __attribute__((weak)); 40 41 static inline int is_kernel_inittext(unsigned long addr) 42 { 43 if (addr >= (unsigned long)_sinittext 44 && addr <= (unsigned long)_einittext) 45 return 1; 46 return 0; 47 } 48 49 static inline int is_kernel_text(unsigned long addr) 50 { 51 if (addr >= (unsigned long)_stext && addr <= (unsigned long)_etext) 52 return 1; 53 return in_gate_area_no_task(addr); 54 } 55 56 static inline int is_kernel(unsigned long addr) 57 { 58 if (addr >= (unsigned long)_stext && addr <= (unsigned long)_end) 59 return 1; 60 return in_gate_area_no_task(addr); 61 } 62 63 /* expand a compressed symbol data into the resulting uncompressed string, 64 given the offset to where the symbol is in the compressed stream */ 65 static unsigned int kallsyms_expand_symbol(unsigned int off, char *result) 66 { 67 int len, skipped_first = 0; 68 u8 *tptr, *data; 69 70 /* get the compressed symbol length from the first symbol byte */ 71 data = &kallsyms_names[off]; 72 len = *data; 73 data++; 74 75 /* update the offset to return the offset for the next symbol on 76 * the compressed stream */ 77 off += len + 1; 78 79 /* for every byte on the compressed symbol data, copy the table 80 entry for that byte */ 81 while(len) { 82 tptr = &kallsyms_token_table[ kallsyms_token_index[*data] ]; 83 data++; 84 len--; 85 86 while (*tptr) { 87 if(skipped_first) { 88 *result = *tptr; 89 result++; 90 } else 91 skipped_first = 1; 92 tptr++; 93 } 94 } 95 96 *result = '\0'; 97 98 /* return to offset to the next symbol */ 99 return off; 100 } 101 102 /* get symbol type information. This is encoded as a single char at the 103 * begining of the symbol name */ 104 static char kallsyms_get_symbol_type(unsigned int off) 105 { 106 /* get just the first code, look it up in the token table, and return the 107 * first char from this token */ 108 return kallsyms_token_table[ kallsyms_token_index[ kallsyms_names[off+1] ] ]; 109 } 110 111 112 /* find the offset on the compressed stream given and index in the 113 * kallsyms array */ 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 for. 124 * Every symbol is stored in a [<len>][<len> bytes of data] format, so we 125 * just need to add the len to the current pointer for every symbol we 126 * 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 /* Lookup the address for this symbol. Returns 0 if not found. */ 134 unsigned long kallsyms_lookup_name(const char *name) 135 { 136 char namebuf[KSYM_NAME_LEN+1]; 137 unsigned long i; 138 unsigned int off; 139 140 for (i = 0, off = 0; i < kallsyms_num_syms; i++) { 141 off = kallsyms_expand_symbol(off, namebuf); 142 143 if (strcmp(namebuf, name) == 0) 144 return kallsyms_addresses[i]; 145 } 146 return module_kallsyms_lookup_name(name); 147 } 148 EXPORT_SYMBOL_GPL(kallsyms_lookup_name); 149 150 /* 151 * Lookup an address 152 * - modname is set to NULL if it's in the kernel 153 * - we guarantee that the returned name is valid until we reschedule even if 154 * it resides in a module 155 * - we also guarantee that modname will be valid until rescheduled 156 */ 157 const char *kallsyms_lookup(unsigned long addr, 158 unsigned long *symbolsize, 159 unsigned long *offset, 160 char **modname, char *namebuf) 161 { 162 unsigned long i, low, high, mid; 163 const char *msym; 164 165 /* This kernel should never had been booted. */ 166 BUG_ON(!kallsyms_addresses); 167 168 namebuf[KSYM_NAME_LEN] = 0; 169 namebuf[0] = 0; 170 171 if ((all_var && is_kernel(addr)) || 172 (!all_var && (is_kernel_text(addr) || is_kernel_inittext(addr)))) { 173 unsigned long symbol_end=0; 174 175 /* do a binary search on the sorted kallsyms_addresses array */ 176 low = 0; 177 high = kallsyms_num_syms; 178 179 while (high-low > 1) { 180 mid = (low + high) / 2; 181 if (kallsyms_addresses[mid] <= addr) low = mid; 182 else high = mid; 183 } 184 185 /* search for the first aliased symbol. Aliased symbols are 186 symbols with the same address */ 187 while (low && kallsyms_addresses[low - 1] == kallsyms_addresses[low]) 188 --low; 189 190 /* Grab name */ 191 kallsyms_expand_symbol(get_symbol_offset(low), namebuf); 192 193 /* Search for next non-aliased symbol */ 194 for (i = low + 1; i < kallsyms_num_syms; i++) { 195 if (kallsyms_addresses[i] > kallsyms_addresses[low]) { 196 symbol_end = kallsyms_addresses[i]; 197 break; 198 } 199 } 200 201 /* if we found no next symbol, we use the end of the section */ 202 if (!symbol_end) { 203 if (is_kernel_inittext(addr)) 204 symbol_end = (unsigned long)_einittext; 205 else 206 symbol_end = all_var ? (unsigned long)_end : (unsigned long)_etext; 207 } 208 209 *symbolsize = symbol_end - kallsyms_addresses[low]; 210 *modname = NULL; 211 *offset = addr - kallsyms_addresses[low]; 212 return namebuf; 213 } 214 215 /* see if it's in a module */ 216 msym = module_address_lookup(addr, symbolsize, offset, modname); 217 if (msym) 218 return strncpy(namebuf, msym, KSYM_NAME_LEN); 219 220 return NULL; 221 } 222 223 /* Replace "%s" in format with address, or returns -errno. */ 224 void __print_symbol(const char *fmt, unsigned long address) 225 { 226 char *modname; 227 const char *name; 228 unsigned long offset, size; 229 char namebuf[KSYM_NAME_LEN+1]; 230 char buffer[sizeof("%s+%#lx/%#lx [%s]") + KSYM_NAME_LEN + 231 2*(BITS_PER_LONG*3/10) + MODULE_NAME_LEN + 1]; 232 233 name = kallsyms_lookup(address, &size, &offset, &modname, namebuf); 234 235 if (!name) 236 sprintf(buffer, "0x%lx", address); 237 else { 238 if (modname) 239 sprintf(buffer, "%s+%#lx/%#lx [%s]", name, offset, 240 size, modname); 241 else 242 sprintf(buffer, "%s+%#lx/%#lx", name, offset, size); 243 } 244 printk(fmt, buffer); 245 } 246 247 /* To avoid using get_symbol_offset for every symbol, we carry prefix along. */ 248 struct kallsym_iter 249 { 250 loff_t pos; 251 struct module *owner; 252 unsigned long value; 253 unsigned int nameoff; /* If iterating in core kernel symbols */ 254 char type; 255 char name[KSYM_NAME_LEN+1]; 256 }; 257 258 /* Only label it "global" if it is exported. */ 259 static void upcase_if_global(struct kallsym_iter *iter) 260 { 261 if (is_exported(iter->name, iter->owner)) 262 iter->type += 'A' - 'a'; 263 } 264 265 static int get_ksymbol_mod(struct kallsym_iter *iter) 266 { 267 iter->owner = module_get_kallsym(iter->pos - kallsyms_num_syms, 268 &iter->value, 269 &iter->type, iter->name); 270 if (iter->owner == NULL) 271 return 0; 272 273 upcase_if_global(iter); 274 return 1; 275 } 276 277 /* Returns space to next name. */ 278 static unsigned long get_ksymbol_core(struct kallsym_iter *iter) 279 { 280 unsigned off = iter->nameoff; 281 282 iter->owner = NULL; 283 iter->value = kallsyms_addresses[iter->pos]; 284 285 iter->type = kallsyms_get_symbol_type(off); 286 287 off = kallsyms_expand_symbol(off, iter->name); 288 289 return off - iter->nameoff; 290 } 291 292 static void reset_iter(struct kallsym_iter *iter, loff_t new_pos) 293 { 294 iter->name[0] = '\0'; 295 iter->nameoff = get_symbol_offset(new_pos); 296 iter->pos = new_pos; 297 } 298 299 /* Returns false if pos at or past end of file. */ 300 static int update_iter(struct kallsym_iter *iter, loff_t pos) 301 { 302 /* Module symbols can be accessed randomly. */ 303 if (pos >= kallsyms_num_syms) { 304 iter->pos = pos; 305 return get_ksymbol_mod(iter); 306 } 307 308 /* If we're not on the desired position, reset to new position. */ 309 if (pos != iter->pos) 310 reset_iter(iter, pos); 311 312 iter->nameoff += get_ksymbol_core(iter); 313 iter->pos++; 314 315 return 1; 316 } 317 318 static void *s_next(struct seq_file *m, void *p, loff_t *pos) 319 { 320 (*pos)++; 321 322 if (!update_iter(m->private, *pos)) 323 return NULL; 324 return p; 325 } 326 327 static void *s_start(struct seq_file *m, loff_t *pos) 328 { 329 if (!update_iter(m->private, *pos)) 330 return NULL; 331 return m->private; 332 } 333 334 static void s_stop(struct seq_file *m, void *p) 335 { 336 } 337 338 static int s_show(struct seq_file *m, void *p) 339 { 340 struct kallsym_iter *iter = m->private; 341 342 /* Some debugging symbols have no name. Ignore them. */ 343 if (!iter->name[0]) 344 return 0; 345 346 if (iter->owner) 347 seq_printf(m, "%0*lx %c %s\t[%s]\n", 348 (int)(2*sizeof(void*)), 349 iter->value, iter->type, iter->name, 350 module_name(iter->owner)); 351 else 352 seq_printf(m, "%0*lx %c %s\n", 353 (int)(2*sizeof(void*)), 354 iter->value, iter->type, iter->name); 355 return 0; 356 } 357 358 static struct seq_operations kallsyms_op = { 359 .start = s_start, 360 .next = s_next, 361 .stop = s_stop, 362 .show = s_show 363 }; 364 365 static int kallsyms_open(struct inode *inode, struct file *file) 366 { 367 /* We keep iterator in m->private, since normal case is to 368 * s_start from where we left off, so we avoid doing 369 * using get_symbol_offset for every symbol */ 370 struct kallsym_iter *iter; 371 int ret; 372 373 iter = kmalloc(sizeof(*iter), GFP_KERNEL); 374 if (!iter) 375 return -ENOMEM; 376 reset_iter(iter, 0); 377 378 ret = seq_open(file, &kallsyms_op); 379 if (ret == 0) 380 ((struct seq_file *)file->private_data)->private = iter; 381 else 382 kfree(iter); 383 return ret; 384 } 385 386 static int kallsyms_release(struct inode *inode, struct file *file) 387 { 388 struct seq_file *m = (struct seq_file *)file->private_data; 389 kfree(m->private); 390 return seq_release(inode, file); 391 } 392 393 static struct file_operations kallsyms_operations = { 394 .open = kallsyms_open, 395 .read = seq_read, 396 .llseek = seq_lseek, 397 .release = kallsyms_release, 398 }; 399 400 static int __init kallsyms_init(void) 401 { 402 struct proc_dir_entry *entry; 403 404 entry = create_proc_entry("kallsyms", 0444, NULL); 405 if (entry) 406 entry->proc_fops = &kallsyms_operations; 407 return 0; 408 } 409 __initcall(kallsyms_init); 410 411 EXPORT_SYMBOL(__print_symbol); 412