diff options
Diffstat (limited to 'kernel/kallsyms.c')
| -rw-r--r-- | kernel/kallsyms.c | 116 | 
1 files changed, 104 insertions, 12 deletions
| diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index 60c20f301a6b..83f499182c9a 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -146,7 +146,7 @@ static unsigned int get_symbol_offset(unsigned long pos)  	return name - kallsyms_names;  } -static unsigned long kallsyms_sym_address(int idx) +unsigned long kallsyms_sym_address(int idx)  {  	if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))  		return kallsyms_addresses[idx]; @@ -187,26 +187,100 @@ static bool cleanup_symbol_name(char *s)  	return false;  } +static int compare_symbol_name(const char *name, char *namebuf) +{ +	int ret; + +	ret = strcmp(name, namebuf); +	if (!ret) +		return ret; + +	if (cleanup_symbol_name(namebuf) && !strcmp(name, namebuf)) +		return 0; + +	return ret; +} + +static unsigned int get_symbol_seq(int index) +{ +	unsigned int i, seq = 0; + +	for (i = 0; i < 3; i++) +		seq = (seq << 8) | kallsyms_seqs_of_names[3 * index + i]; + +	return seq; +} + +static int kallsyms_lookup_names(const char *name, +				 unsigned int *start, +				 unsigned int *end) +{ +	int ret; +	int low, mid, high; +	unsigned int seq, off; +	char namebuf[KSYM_NAME_LEN]; + +	low = 0; +	high = kallsyms_num_syms - 1; + +	while (low <= high) { +		mid = low + (high - low) / 2; +		seq = get_symbol_seq(mid); +		off = get_symbol_offset(seq); +		kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf)); +		ret = compare_symbol_name(name, namebuf); +		if (ret > 0) +			low = mid + 1; +		else if (ret < 0) +			high = mid - 1; +		else +			break; +	} + +	if (low > high) +		return -ESRCH; + +	low = mid; +	while (low) { +		seq = get_symbol_seq(low - 1); +		off = get_symbol_offset(seq); +		kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf)); +		if (compare_symbol_name(name, namebuf)) +			break; +		low--; +	} +	*start = low; + +	if (end) { +		high = mid; +		while (high < kallsyms_num_syms - 1) { +			seq = get_symbol_seq(high + 1); +			off = get_symbol_offset(seq); +			kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf)); +			if (compare_symbol_name(name, namebuf)) +				break; +			high++; +		} +		*end = high; +	} + +	return 0; +} +  /* 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; +	int ret; +	unsigned int i;  	/* Skip the search for empty string. */  	if (!*name)  		return 0; -	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_sym_address(i); +	ret = kallsyms_lookup_names(name, &i, NULL); +	if (!ret) +		return kallsyms_sym_address(get_symbol_seq(i)); -		if (cleanup_symbol_name(namebuf) && strcmp(namebuf, name) == 0) -			return kallsyms_sym_address(i); -	}  	return module_kallsyms_lookup_name(name);  } @@ -233,6 +307,24 @@ int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,  	return 0;  } +int kallsyms_on_each_match_symbol(int (*fn)(void *, unsigned long), +				  const char *name, void *data) +{ +	int ret; +	unsigned int i, start, end; + +	ret = kallsyms_lookup_names(name, &start, &end); +	if (ret) +		return 0; + +	for (i = start; !ret && i <= end; i++) { +		ret = fn(data, kallsyms_sym_address(get_symbol_seq(i))); +		cond_resched(); +	} + +	return ret; +} +  static unsigned long get_symbol_pos(unsigned long addr,  				    unsigned long *symbolsize,  				    unsigned long *offset) |