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