614 lines · 15.0 KB · C
RawDownload
/* * kallsyms.c: in-kernel printing of symbolic oopses and stack traces. * * Rewritten and vastly simplified by Rusty Russell for in-kernel * module loader: *   Copyright 2002 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation * * ChangeLog: * * (25/Aug/2004) Paulo Marques <pmarques@grupopie.com> *      Changed the compression method from stem compression to "table lookup" *      compression (see scripts/kallsyms.c for a more complete description) */#include <linux/kallsyms.h>#include <linux/module.h>#include <linux/init.h>#include <linux/seq_file.h>#include <linux/fs.h>#include <linux/kdb.h>#include <linux/err.h>#include <linux/proc_fs.h>#include <linux/sched.h>	/* for cond_resched */#include <linux/mm.h>#include <linux/ctype.h>#include <linux/slab.h> #include <asm/sections.h> #ifdef CONFIG_KALLSYMS_ALL#define all_var 1#else#define all_var 0#endif /* * These will be re-linked against their real values * during the second link stage. */extern const unsigned long kallsyms_addresses[] __attribute__((weak));extern const u8 kallsyms_names[] __attribute__((weak)); /* * Tell the compiler that the count isn't in the small data section if the arch * has one (eg: FRV). */extern const unsigned long kallsyms_num_syms__attribute__((weak, section(".rodata"))); extern const u8 kallsyms_token_table[] __attribute__((weak));extern const u16 kallsyms_token_index[] __attribute__((weak)); extern const unsigned long kallsyms_markers[] __attribute__((weak)); static inline int is_kernel_inittext(unsigned long addr){	if (addr >= (unsigned long)_sinittext	    && addr <= (unsigned long)_einittext)		return 1;	return 0;} static inline int is_kernel_text(unsigned long addr){	if ((addr >= (unsigned long)_stext && addr <= (unsigned long)_etext) ||	    arch_is_kernel_text(addr))		return 1;	return in_gate_area_no_mm(addr);} static inline int is_kernel(unsigned long addr){	if (addr >= (unsigned long)_stext && addr <= (unsigned long)_end)		return 1;	return in_gate_area_no_mm(addr);} static int is_ksym_addr(unsigned long addr){	if (all_var)		return is_kernel(addr); 	return is_kernel_text(addr) || is_kernel_inittext(addr);} /* * Expand a compressed symbol data into the resulting uncompressed string, * if uncompressed string is too long (>= maxlen), it will be truncated, * given the offset to where the symbol is in the compressed stream. */static unsigned int kallsyms_expand_symbol(unsigned int off,					   char *result, size_t maxlen){	int len, skipped_first = 0;	const u8 *tptr, *data; 	/* Get the compressed symbol length from the first symbol byte. */	data = &kallsyms_names[off];	len = *data;	data++; 	/*	 * Update the offset to return the offset for the next symbol on	 * the compressed stream.	 */	off += len + 1; 	/*	 * For every byte on the compressed symbol data, copy the table	 * entry for that byte.	 */	while (len) {		tptr = &kallsyms_token_table[kallsyms_token_index[*data]];		data++;		len--; 		while (*tptr) {			if (skipped_first) {				if (maxlen <= 1)					goto tail;				*result = *tptr;				result++;				maxlen--;			} else				skipped_first = 1;			tptr++;		}	} tail:	if (maxlen)		*result = '\0'; 	/* Return to offset to the next symbol. */	return off;} /* * Get symbol type information. This is encoded as a single char at the * beginning of the symbol name. */static char kallsyms_get_symbol_type(unsigned int off){	/*	 * Get just the first code, look it up in the token table,	 * and return the first char from this token.	 */	return kallsyms_token_table[kallsyms_token_index[kallsyms_names[off + 1]]];}  /* * Find the offset on the compressed stream given and index in the * kallsyms array. */static unsigned int get_symbol_offset(unsigned long pos){	const u8 *name;	int i; 	/*	 * Use the closest marker we have. We have markers every 256 positions,	 * so that should be close enough.	 */	name = &kallsyms_names[kallsyms_markers[pos >> 8]]; 	/*	 * Sequentially scan all the symbols up to the point we're searching	 * for. Every symbol is stored in a [<len>][<len> bytes of data] format,	 * so we just need to add the len to the current pointer for every	 * symbol we wish to skip.	 */	for (i = 0; i < (pos & 0xFF); i++)		name = name + (*name) + 1; 	return name - kallsyms_names;} /* Lookup the address for this symbol. Returns 0 if not found. */unsigned long kallsyms_lookup_name(const char *name){	char namebuf[KSYM_NAME_LEN];	unsigned long i;	unsigned int off; 	for (i = 0, off = 0; i < kallsyms_num_syms; i++) {		off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf)); 		if (strcmp(namebuf, name) == 0)			return kallsyms_addresses[i];	}	return module_kallsyms_lookup_name(name);}EXPORT_SYMBOL_GPL(kallsyms_lookup_name); int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,				      unsigned long),			    void *data){	char namebuf[KSYM_NAME_LEN];	unsigned long i;	unsigned int off;	int ret; 	for (i = 0, off = 0; i < kallsyms_num_syms; i++) {		off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));		ret = fn(data, namebuf, NULL, kallsyms_addresses[i]);		if (ret != 0)			return ret;	}	return module_kallsyms_on_each_symbol(fn, data);}EXPORT_SYMBOL_GPL(kallsyms_on_each_symbol); static unsigned long get_symbol_pos(unsigned long addr,				    unsigned long *symbolsize,				    unsigned long *offset){	unsigned long symbol_start = 0, symbol_end = 0;	unsigned long i, low, high, mid; 	/* This kernel should never had been booted. */	BUG_ON(!kallsyms_addresses); 	/* Do a binary search on the sorted kallsyms_addresses array. */	low = 0;	high = kallsyms_num_syms; 	while (high - low > 1) {		mid = low + (high - low) / 2;		if (kallsyms_addresses[mid] <= addr)			low = mid;		else			high = mid;	} 	/*	 * Search for the first aliased symbol. Aliased	 * symbols are symbols with the same address.	 */	while (low && kallsyms_addresses[low-1] == kallsyms_addresses[low])		--low; 	symbol_start = kallsyms_addresses[low]; 	/* Search for next non-aliased symbol. */	for (i = low + 1; i < kallsyms_num_syms; i++) {		if (kallsyms_addresses[i] > symbol_start) {			symbol_end = kallsyms_addresses[i];			break;		}	} 	/* If we found no next symbol, we use the end of the section. */	if (!symbol_end) {		if (is_kernel_inittext(addr))			symbol_end = (unsigned long)_einittext;		else if (all_var)			symbol_end = (unsigned long)_end;		else			symbol_end = (unsigned long)_etext;	} 	if (symbolsize)		*symbolsize = symbol_end - symbol_start;	if (offset)		*offset = addr - symbol_start; 	return low;} /* * Lookup an address but don't bother to find any names. */int kallsyms_lookup_size_offset(unsigned long addr, unsigned long *symbolsize,				unsigned long *offset){	char namebuf[KSYM_NAME_LEN];	if (is_ksym_addr(addr))		return !!get_symbol_pos(addr, symbolsize, offset); 	return !!module_address_lookup(addr, symbolsize, offset, NULL, namebuf);} /* * Lookup an address * - modname is set to NULL if it's in the kernel. * - We guarantee that the returned name is valid until we reschedule even if. *   It resides in a module. * - We also guarantee that modname will be valid until rescheduled. */const char *kallsyms_lookup(unsigned long addr,			    unsigned long *symbolsize,			    unsigned long *offset,			    char **modname, char *namebuf){	namebuf[KSYM_NAME_LEN - 1] = 0;	namebuf[0] = 0; 	if (is_ksym_addr(addr)) {		unsigned long pos; 		pos = get_symbol_pos(addr, symbolsize, offset);		/* Grab name */		kallsyms_expand_symbol(get_symbol_offset(pos),				       namebuf, KSYM_NAME_LEN);		if (modname)			*modname = NULL;		return namebuf;	} 	/* See if it's in a module. */	return module_address_lookup(addr, symbolsize, offset, modname,				     namebuf);} int lookup_symbol_name(unsigned long addr, char *symname){	symname[0] = '\0';	symname[KSYM_NAME_LEN - 1] = '\0'; 	if (is_ksym_addr(addr)) {		unsigned long pos; 		pos = get_symbol_pos(addr, NULL, NULL);		/* Grab name */		kallsyms_expand_symbol(get_symbol_offset(pos),				       symname, KSYM_NAME_LEN);		return 0;	}	/* See if it's in a module. */	return lookup_module_symbol_name(addr, symname);} int lookup_symbol_attrs(unsigned long addr, unsigned long *size,			unsigned long *offset, char *modname, char *name){	name[0] = '\0';	name[KSYM_NAME_LEN - 1] = '\0'; 	if (is_ksym_addr(addr)) {		unsigned long pos; 		pos = get_symbol_pos(addr, size, offset);		/* Grab name */		kallsyms_expand_symbol(get_symbol_offset(pos),				       name, KSYM_NAME_LEN);		modname[0] = '\0';		return 0;	}	/* See if it's in a module. */	return lookup_module_symbol_attrs(addr, size, offset, modname, name);} /* Look up a kernel symbol and return it in a text buffer. */static int __sprint_symbol(char *buffer, unsigned long address,			   int symbol_offset, int add_offset){	char *modname;	const char *name;	unsigned long offset, size;	int len; 	address += symbol_offset;	name = kallsyms_lookup(address, &size, &offset, &modname, buffer);	if (!name)		return sprintf(buffer, "0x%lx", address); 	if (name != buffer)		strcpy(buffer, name);	len = strlen(buffer);	offset -= symbol_offset; 	if (add_offset)		len += sprintf(buffer + len, "+%#lx/%#lx", offset, size); 	if (modname)		len += sprintf(buffer + len, " [%s]", modname); 	return len;} /** * sprint_symbol - Look up a kernel symbol and return it in a text buffer * @buffer: buffer to be stored * @address: address to lookup * * This function looks up a kernel symbol with @address and stores its name, * offset, size and module name to @buffer if possible. If no symbol was found, * just saves its @address as is. * * This function returns the number of bytes stored in @buffer. */int sprint_symbol(char *buffer, unsigned long address){	return __sprint_symbol(buffer, address, 0, 1);}EXPORT_SYMBOL_GPL(sprint_symbol); /** * sprint_symbol_no_offset - Look up a kernel symbol and return it in a text buffer * @buffer: buffer to be stored * @address: address to lookup * * This function looks up a kernel symbol with @address and stores its name * and module name to @buffer if possible. If no symbol was found, just saves * its @address as is. * * This function returns the number of bytes stored in @buffer. */int sprint_symbol_no_offset(char *buffer, unsigned long address){	return __sprint_symbol(buffer, address, 0, 0);}EXPORT_SYMBOL_GPL(sprint_symbol_no_offset); /** * sprint_backtrace - Look up a backtrace symbol and return it in a text buffer * @buffer: buffer to be stored * @address: address to lookup * * This function is for stack backtrace and does the same thing as * sprint_symbol() but with modified/decreased @address. If there is a * tail-call to the function marked "noreturn", gcc optimized out code after * the call so that the stack-saved return address could point outside of the * caller. This function ensures that kallsyms will find the original caller * by decreasing @address. * * This function returns the number of bytes stored in @buffer. */int sprint_backtrace(char *buffer, unsigned long address){	return __sprint_symbol(buffer, address, -1, 1);} /* Look up a kernel symbol and print it to the kernel messages. */void __print_symbol(const char *fmt, unsigned long address){	char buffer[KSYM_SYMBOL_LEN]; 	sprint_symbol(buffer, address); 	printk(fmt, buffer);}EXPORT_SYMBOL(__print_symbol); /* To avoid using get_symbol_offset for every symbol, we carry prefix along. */struct kallsym_iter {	loff_t pos;	unsigned long value;	unsigned int nameoff; /* If iterating in core kernel symbols. */	char type;	char name[KSYM_NAME_LEN];	char module_name[MODULE_NAME_LEN];	int exported;}; static int get_ksymbol_mod(struct kallsym_iter *iter){	if (module_get_kallsym(iter->pos - kallsyms_num_syms, &iter->value,				&iter->type, iter->name, iter->module_name,				&iter->exported) < 0)		return 0;	return 1;} /* Returns space to next name. */static unsigned long get_ksymbol_core(struct kallsym_iter *iter){	unsigned off = iter->nameoff; 	iter->module_name[0] = '\0';	iter->value = kallsyms_addresses[iter->pos]; 	iter->type = kallsyms_get_symbol_type(off); 	off = kallsyms_expand_symbol(off, iter->name, ARRAY_SIZE(iter->name)); 	return off - iter->nameoff;} static void reset_iter(struct kallsym_iter *iter, loff_t new_pos){	iter->name[0] = '\0';	iter->nameoff = get_symbol_offset(new_pos);	iter->pos = new_pos;} /* Returns false if pos at or past end of file. */static int update_iter(struct kallsym_iter *iter, loff_t pos){	/* Module symbols can be accessed randomly. */	if (pos >= kallsyms_num_syms) {		iter->pos = pos;		return get_ksymbol_mod(iter);	} 	/* If we're not on the desired position, reset to new position. */	if (pos != iter->pos)		reset_iter(iter, pos); 

Lines 1–500 of 614.