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