xref: /openbmc/linux/kernel/module/kallsyms.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
191fb02f3SAaron Tomlin // SPDX-License-Identifier: GPL-2.0-or-later
291fb02f3SAaron Tomlin /*
391fb02f3SAaron Tomlin  * Module kallsyms support
491fb02f3SAaron Tomlin  *
591fb02f3SAaron Tomlin  * Copyright (C) 2010 Rusty Russell
691fb02f3SAaron Tomlin  */
791fb02f3SAaron Tomlin 
891fb02f3SAaron Tomlin #include <linux/module.h>
9987d2e0aSTiezhu Yang #include <linux/module_symbol.h>
1091fb02f3SAaron Tomlin #include <linux/kallsyms.h>
1191fb02f3SAaron Tomlin #include <linux/buildid.h>
1291fb02f3SAaron Tomlin #include <linux/bsearch.h>
1391fb02f3SAaron Tomlin #include "internal.h"
1491fb02f3SAaron Tomlin 
1591fb02f3SAaron Tomlin /* Lookup exported symbol in given range of kernel_symbols */
lookup_exported_symbol(const char * name,const struct kernel_symbol * start,const struct kernel_symbol * stop)1691fb02f3SAaron Tomlin static const struct kernel_symbol *lookup_exported_symbol(const char *name,
1791fb02f3SAaron Tomlin 							  const struct kernel_symbol *start,
1891fb02f3SAaron Tomlin 							  const struct kernel_symbol *stop)
1991fb02f3SAaron Tomlin {
2091fb02f3SAaron Tomlin 	return bsearch(name, start, stop - start,
2191fb02f3SAaron Tomlin 			sizeof(struct kernel_symbol), cmp_name);
2291fb02f3SAaron Tomlin }
2391fb02f3SAaron Tomlin 
is_exported(const char * name,unsigned long value,const struct module * mod)2491fb02f3SAaron Tomlin static int is_exported(const char *name, unsigned long value,
2591fb02f3SAaron Tomlin 		       const struct module *mod)
2691fb02f3SAaron Tomlin {
2791fb02f3SAaron Tomlin 	const struct kernel_symbol *ks;
2891fb02f3SAaron Tomlin 
2991fb02f3SAaron Tomlin 	if (!mod)
3091fb02f3SAaron Tomlin 		ks = lookup_exported_symbol(name, __start___ksymtab, __stop___ksymtab);
3191fb02f3SAaron Tomlin 	else
3291fb02f3SAaron Tomlin 		ks = lookup_exported_symbol(name, mod->syms, mod->syms + mod->num_syms);
3391fb02f3SAaron Tomlin 
3491fb02f3SAaron Tomlin 	return ks && kernel_symbol_value(ks) == value;
3591fb02f3SAaron Tomlin }
3691fb02f3SAaron Tomlin 
3791fb02f3SAaron Tomlin /* As per nm */
elf_type(const Elf_Sym * sym,const struct load_info * info)3891fb02f3SAaron Tomlin static char elf_type(const Elf_Sym *sym, const struct load_info *info)
3991fb02f3SAaron Tomlin {
4091fb02f3SAaron Tomlin 	const Elf_Shdr *sechdrs = info->sechdrs;
4191fb02f3SAaron Tomlin 
4291fb02f3SAaron Tomlin 	if (ELF_ST_BIND(sym->st_info) == STB_WEAK) {
4391fb02f3SAaron Tomlin 		if (ELF_ST_TYPE(sym->st_info) == STT_OBJECT)
4491fb02f3SAaron Tomlin 			return 'v';
4591fb02f3SAaron Tomlin 		else
4691fb02f3SAaron Tomlin 			return 'w';
4791fb02f3SAaron Tomlin 	}
4891fb02f3SAaron Tomlin 	if (sym->st_shndx == SHN_UNDEF)
4991fb02f3SAaron Tomlin 		return 'U';
5091fb02f3SAaron Tomlin 	if (sym->st_shndx == SHN_ABS || sym->st_shndx == info->index.pcpu)
5191fb02f3SAaron Tomlin 		return 'a';
5291fb02f3SAaron Tomlin 	if (sym->st_shndx >= SHN_LORESERVE)
5391fb02f3SAaron Tomlin 		return '?';
5491fb02f3SAaron Tomlin 	if (sechdrs[sym->st_shndx].sh_flags & SHF_EXECINSTR)
5591fb02f3SAaron Tomlin 		return 't';
5691fb02f3SAaron Tomlin 	if (sechdrs[sym->st_shndx].sh_flags & SHF_ALLOC &&
5791fb02f3SAaron Tomlin 	    sechdrs[sym->st_shndx].sh_type != SHT_NOBITS) {
5891fb02f3SAaron Tomlin 		if (!(sechdrs[sym->st_shndx].sh_flags & SHF_WRITE))
5991fb02f3SAaron Tomlin 			return 'r';
6091fb02f3SAaron Tomlin 		else if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL)
6191fb02f3SAaron Tomlin 			return 'g';
6291fb02f3SAaron Tomlin 		else
6391fb02f3SAaron Tomlin 			return 'd';
6491fb02f3SAaron Tomlin 	}
6591fb02f3SAaron Tomlin 	if (sechdrs[sym->st_shndx].sh_type == SHT_NOBITS) {
6691fb02f3SAaron Tomlin 		if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL)
6791fb02f3SAaron Tomlin 			return 's';
6891fb02f3SAaron Tomlin 		else
6991fb02f3SAaron Tomlin 			return 'b';
7091fb02f3SAaron Tomlin 	}
7191fb02f3SAaron Tomlin 	if (strstarts(info->secstrings + sechdrs[sym->st_shndx].sh_name,
7291fb02f3SAaron Tomlin 		      ".debug")) {
7391fb02f3SAaron Tomlin 		return 'n';
7491fb02f3SAaron Tomlin 	}
7591fb02f3SAaron Tomlin 	return '?';
7691fb02f3SAaron Tomlin }
7791fb02f3SAaron Tomlin 
is_core_symbol(const Elf_Sym * src,const Elf_Shdr * sechdrs,unsigned int shnum,unsigned int pcpundx)7891fb02f3SAaron Tomlin static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs,
7991fb02f3SAaron Tomlin 			   unsigned int shnum, unsigned int pcpundx)
8091fb02f3SAaron Tomlin {
8191fb02f3SAaron Tomlin 	const Elf_Shdr *sec;
82ac3b4328SSong Liu 	enum mod_mem_type type;
8391fb02f3SAaron Tomlin 
8491fb02f3SAaron Tomlin 	if (src->st_shndx == SHN_UNDEF ||
8591fb02f3SAaron Tomlin 	    src->st_shndx >= shnum ||
8691fb02f3SAaron Tomlin 	    !src->st_name)
8791fb02f3SAaron Tomlin 		return false;
8891fb02f3SAaron Tomlin 
8991fb02f3SAaron Tomlin #ifdef CONFIG_KALLSYMS_ALL
9091fb02f3SAaron Tomlin 	if (src->st_shndx == pcpundx)
9191fb02f3SAaron Tomlin 		return true;
9291fb02f3SAaron Tomlin #endif
9391fb02f3SAaron Tomlin 
9491fb02f3SAaron Tomlin 	sec = sechdrs + src->st_shndx;
95ac3b4328SSong Liu 	type = sec->sh_entsize >> SH_ENTSIZE_TYPE_SHIFT;
9691fb02f3SAaron Tomlin 	if (!(sec->sh_flags & SHF_ALLOC)
9791fb02f3SAaron Tomlin #ifndef CONFIG_KALLSYMS_ALL
9891fb02f3SAaron Tomlin 	    || !(sec->sh_flags & SHF_EXECINSTR)
9991fb02f3SAaron Tomlin #endif
100ac3b4328SSong Liu 	    || mod_mem_type_is_init(type))
10191fb02f3SAaron Tomlin 		return false;
10291fb02f3SAaron Tomlin 
10391fb02f3SAaron Tomlin 	return true;
10491fb02f3SAaron Tomlin }
10591fb02f3SAaron Tomlin 
10691fb02f3SAaron Tomlin /*
10791fb02f3SAaron Tomlin  * We only allocate and copy the strings needed by the parts of symtab
10891fb02f3SAaron Tomlin  * we keep.  This is simple, but has the effect of making multiple
10991fb02f3SAaron Tomlin  * copies of duplicates.  We could be more sophisticated, see
11091fb02f3SAaron Tomlin  * linux-kernel thread starting with
11191fb02f3SAaron Tomlin  * <73defb5e4bca04a6431392cc341112b1@localhost>.
11291fb02f3SAaron Tomlin  */
layout_symtab(struct module * mod,struct load_info * info)11391fb02f3SAaron Tomlin void layout_symtab(struct module *mod, struct load_info *info)
11491fb02f3SAaron Tomlin {
11591fb02f3SAaron Tomlin 	Elf_Shdr *symsect = info->sechdrs + info->index.sym;
11691fb02f3SAaron Tomlin 	Elf_Shdr *strsect = info->sechdrs + info->index.str;
11791fb02f3SAaron Tomlin 	const Elf_Sym *src;
11891fb02f3SAaron Tomlin 	unsigned int i, nsrc, ndst, strtab_size = 0;
119ac3b4328SSong Liu 	struct module_memory *mod_mem_data = &mod->mem[MOD_DATA];
120ac3b4328SSong Liu 	struct module_memory *mod_mem_init_data = &mod->mem[MOD_INIT_DATA];
12191fb02f3SAaron Tomlin 
12291fb02f3SAaron Tomlin 	/* Put symbol section at end of init part of module. */
12391fb02f3SAaron Tomlin 	symsect->sh_flags |= SHF_ALLOC;
124ac3b4328SSong Liu 	symsect->sh_entsize = module_get_offset_and_type(mod, MOD_INIT_DATA,
125ac3b4328SSong Liu 							 symsect, info->index.sym);
12691fb02f3SAaron Tomlin 	pr_debug("\t%s\n", info->secstrings + symsect->sh_name);
12791fb02f3SAaron Tomlin 
12891fb02f3SAaron Tomlin 	src = (void *)info->hdr + symsect->sh_offset;
12991fb02f3SAaron Tomlin 	nsrc = symsect->sh_size / sizeof(*src);
13091fb02f3SAaron Tomlin 
13191fb02f3SAaron Tomlin 	/* Compute total space required for the core symbols' strtab. */
13291fb02f3SAaron Tomlin 	for (ndst = i = 0; i < nsrc; i++) {
13391fb02f3SAaron Tomlin 		if (i == 0 || is_livepatch_module(mod) ||
13491fb02f3SAaron Tomlin 		    is_core_symbol(src + i, info->sechdrs, info->hdr->e_shnum,
13591fb02f3SAaron Tomlin 				   info->index.pcpu)) {
13691fb02f3SAaron Tomlin 			strtab_size += strlen(&info->strtab[src[i].st_name]) + 1;
13791fb02f3SAaron Tomlin 			ndst++;
13891fb02f3SAaron Tomlin 		}
13991fb02f3SAaron Tomlin 	}
14091fb02f3SAaron Tomlin 
14191fb02f3SAaron Tomlin 	/* Append room for core symbols at end of core part. */
142ac3b4328SSong Liu 	info->symoffs = ALIGN(mod_mem_data->size, symsect->sh_addralign ?: 1);
143ac3b4328SSong Liu 	info->stroffs = mod_mem_data->size = info->symoffs + ndst * sizeof(Elf_Sym);
144ac3b4328SSong Liu 	mod_mem_data->size += strtab_size;
14535adf9a4SAdrian Hunter 	/* Note add_kallsyms() computes strtab_size as core_typeoffs - stroffs */
146ac3b4328SSong Liu 	info->core_typeoffs = mod_mem_data->size;
147ac3b4328SSong Liu 	mod_mem_data->size += ndst * sizeof(char);
14891fb02f3SAaron Tomlin 
14991fb02f3SAaron Tomlin 	/* Put string table section at end of init part of module. */
15091fb02f3SAaron Tomlin 	strsect->sh_flags |= SHF_ALLOC;
151ac3b4328SSong Liu 	strsect->sh_entsize = module_get_offset_and_type(mod, MOD_INIT_DATA,
152ac3b4328SSong Liu 							 strsect, info->index.str);
15391fb02f3SAaron Tomlin 	pr_debug("\t%s\n", info->secstrings + strsect->sh_name);
15491fb02f3SAaron Tomlin 
15591fb02f3SAaron Tomlin 	/* We'll tack temporary mod_kallsyms on the end. */
156ac3b4328SSong Liu 	mod_mem_init_data->size = ALIGN(mod_mem_init_data->size,
15791fb02f3SAaron Tomlin 					__alignof__(struct mod_kallsyms));
158ac3b4328SSong Liu 	info->mod_kallsyms_init_off = mod_mem_init_data->size;
159ac3b4328SSong Liu 
160ac3b4328SSong Liu 	mod_mem_init_data->size += sizeof(struct mod_kallsyms);
161ac3b4328SSong Liu 	info->init_typeoffs = mod_mem_init_data->size;
162ac3b4328SSong Liu 	mod_mem_init_data->size += nsrc * sizeof(char);
16391fb02f3SAaron Tomlin }
16491fb02f3SAaron Tomlin 
16591fb02f3SAaron Tomlin /*
16691fb02f3SAaron Tomlin  * We use the full symtab and strtab which layout_symtab arranged to
16791fb02f3SAaron Tomlin  * be appended to the init section.  Later we switch to the cut-down
16891fb02f3SAaron Tomlin  * core-only ones.
16991fb02f3SAaron Tomlin  */
add_kallsyms(struct module * mod,const struct load_info * info)17091fb02f3SAaron Tomlin void add_kallsyms(struct module *mod, const struct load_info *info)
17191fb02f3SAaron Tomlin {
17291fb02f3SAaron Tomlin 	unsigned int i, ndst;
17391fb02f3SAaron Tomlin 	const Elf_Sym *src;
17491fb02f3SAaron Tomlin 	Elf_Sym *dst;
17591fb02f3SAaron Tomlin 	char *s;
17691fb02f3SAaron Tomlin 	Elf_Shdr *symsec = &info->sechdrs[info->index.sym];
17735adf9a4SAdrian Hunter 	unsigned long strtab_size;
178ac3b4328SSong Liu 	void *data_base = mod->mem[MOD_DATA].base;
179ac3b4328SSong Liu 	void *init_data_base = mod->mem[MOD_INIT_DATA].base;
18091fb02f3SAaron Tomlin 
18191fb02f3SAaron Tomlin 	/* Set up to point into init section. */
182ac3b4328SSong Liu 	mod->kallsyms = (void __rcu *)init_data_base +
18308126db5SAaron Tomlin 		info->mod_kallsyms_init_off;
18491fb02f3SAaron Tomlin 
185e69a6614SAaron Tomlin 	rcu_read_lock();
18691fb02f3SAaron Tomlin 	/* The following is safe since this pointer cannot change */
187e69a6614SAaron Tomlin 	rcu_dereference(mod->kallsyms)->symtab = (void *)symsec->sh_addr;
188e69a6614SAaron Tomlin 	rcu_dereference(mod->kallsyms)->num_symtab = symsec->sh_size / sizeof(Elf_Sym);
18991fb02f3SAaron Tomlin 	/* Make sure we get permanent strtab: don't use info->strtab. */
190e69a6614SAaron Tomlin 	rcu_dereference(mod->kallsyms)->strtab =
19108126db5SAaron Tomlin 		(void *)info->sechdrs[info->index.str].sh_addr;
192ac3b4328SSong Liu 	rcu_dereference(mod->kallsyms)->typetab = init_data_base + info->init_typeoffs;
19391fb02f3SAaron Tomlin 
19491fb02f3SAaron Tomlin 	/*
19591fb02f3SAaron Tomlin 	 * Now populate the cut down core kallsyms for after init
19691fb02f3SAaron Tomlin 	 * and set types up while we still have access to sections.
19791fb02f3SAaron Tomlin 	 */
198ac3b4328SSong Liu 	mod->core_kallsyms.symtab = dst = data_base + info->symoffs;
199ac3b4328SSong Liu 	mod->core_kallsyms.strtab = s = data_base + info->stroffs;
200ac3b4328SSong Liu 	mod->core_kallsyms.typetab = data_base + info->core_typeoffs;
20135adf9a4SAdrian Hunter 	strtab_size = info->core_typeoffs - info->stroffs;
202e69a6614SAaron Tomlin 	src = rcu_dereference(mod->kallsyms)->symtab;
203e69a6614SAaron Tomlin 	for (ndst = i = 0; i < rcu_dereference(mod->kallsyms)->num_symtab; i++) {
204e69a6614SAaron Tomlin 		rcu_dereference(mod->kallsyms)->typetab[i] = elf_type(src + i, info);
20591fb02f3SAaron Tomlin 		if (i == 0 || is_livepatch_module(mod) ||
20691fb02f3SAaron Tomlin 		    is_core_symbol(src + i, info->sechdrs, info->hdr->e_shnum,
20791fb02f3SAaron Tomlin 				   info->index.pcpu)) {
20835adf9a4SAdrian Hunter 			ssize_t ret;
20935adf9a4SAdrian Hunter 
21091fb02f3SAaron Tomlin 			mod->core_kallsyms.typetab[ndst] =
211e69a6614SAaron Tomlin 			    rcu_dereference(mod->kallsyms)->typetab[i];
21291fb02f3SAaron Tomlin 			dst[ndst] = src[i];
21391fb02f3SAaron Tomlin 			dst[ndst++].st_name = s - mod->core_kallsyms.strtab;
21435adf9a4SAdrian Hunter 			ret = strscpy(s,
215e69a6614SAaron Tomlin 				      &rcu_dereference(mod->kallsyms)->strtab[src[i].st_name],
21635adf9a4SAdrian Hunter 				      strtab_size);
21735adf9a4SAdrian Hunter 			if (ret < 0)
21835adf9a4SAdrian Hunter 				break;
21935adf9a4SAdrian Hunter 			s += ret + 1;
22035adf9a4SAdrian Hunter 			strtab_size -= ret + 1;
22191fb02f3SAaron Tomlin 		}
22291fb02f3SAaron Tomlin 	}
223e69a6614SAaron Tomlin 	rcu_read_unlock();
22491fb02f3SAaron Tomlin 	mod->core_kallsyms.num_symtab = ndst;
22591fb02f3SAaron Tomlin }
22691fb02f3SAaron Tomlin 
22791fb02f3SAaron Tomlin #if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID)
init_build_id(struct module * mod,const struct load_info * info)22891fb02f3SAaron Tomlin void init_build_id(struct module *mod, const struct load_info *info)
22991fb02f3SAaron Tomlin {
23091fb02f3SAaron Tomlin 	const Elf_Shdr *sechdr;
23191fb02f3SAaron Tomlin 	unsigned int i;
23291fb02f3SAaron Tomlin 
23391fb02f3SAaron Tomlin 	for (i = 0; i < info->hdr->e_shnum; i++) {
23491fb02f3SAaron Tomlin 		sechdr = &info->sechdrs[i];
23591fb02f3SAaron Tomlin 		if (!sect_empty(sechdr) && sechdr->sh_type == SHT_NOTE &&
23691fb02f3SAaron Tomlin 		    !build_id_parse_buf((void *)sechdr->sh_addr, mod->build_id,
23791fb02f3SAaron Tomlin 					sechdr->sh_size))
23891fb02f3SAaron Tomlin 			break;
23991fb02f3SAaron Tomlin 	}
24091fb02f3SAaron Tomlin }
24191fb02f3SAaron Tomlin #else
init_build_id(struct module * mod,const struct load_info * info)24291fb02f3SAaron Tomlin void init_build_id(struct module *mod, const struct load_info *info)
24391fb02f3SAaron Tomlin {
24491fb02f3SAaron Tomlin }
24591fb02f3SAaron Tomlin #endif
24691fb02f3SAaron Tomlin 
kallsyms_symbol_name(struct mod_kallsyms * kallsyms,unsigned int symnum)24791fb02f3SAaron Tomlin static const char *kallsyms_symbol_name(struct mod_kallsyms *kallsyms, unsigned int symnum)
24891fb02f3SAaron Tomlin {
24991fb02f3SAaron Tomlin 	return kallsyms->strtab + kallsyms->symtab[symnum].st_name;
25091fb02f3SAaron Tomlin }
25191fb02f3SAaron Tomlin 
25291fb02f3SAaron Tomlin /*
25391fb02f3SAaron Tomlin  * Given a module and address, find the corresponding symbol and return its name
25491fb02f3SAaron Tomlin  * while providing its size and offset if needed.
25591fb02f3SAaron Tomlin  */
find_kallsyms_symbol(struct module * mod,unsigned long addr,unsigned long * size,unsigned long * offset)25691fb02f3SAaron Tomlin static const char *find_kallsyms_symbol(struct module *mod,
25791fb02f3SAaron Tomlin 					unsigned long addr,
25891fb02f3SAaron Tomlin 					unsigned long *size,
25991fb02f3SAaron Tomlin 					unsigned long *offset)
26091fb02f3SAaron Tomlin {
26191fb02f3SAaron Tomlin 	unsigned int i, best = 0;
26291fb02f3SAaron Tomlin 	unsigned long nextval, bestval;
26391fb02f3SAaron Tomlin 	struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms);
264ac3b4328SSong Liu 	struct module_memory *mod_mem;
26591fb02f3SAaron Tomlin 
26691fb02f3SAaron Tomlin 	/* At worse, next value is at end of module */
26791fb02f3SAaron Tomlin 	if (within_module_init(addr, mod))
268ac3b4328SSong Liu 		mod_mem = &mod->mem[MOD_INIT_TEXT];
26991fb02f3SAaron Tomlin 	else
270ac3b4328SSong Liu 		mod_mem = &mod->mem[MOD_TEXT];
271ac3b4328SSong Liu 
272ac3b4328SSong Liu 	nextval = (unsigned long)mod_mem->base + mod_mem->size;
27391fb02f3SAaron Tomlin 
27491fb02f3SAaron Tomlin 	bestval = kallsyms_symbol_value(&kallsyms->symtab[best]);
27591fb02f3SAaron Tomlin 
27691fb02f3SAaron Tomlin 	/*
27791fb02f3SAaron Tomlin 	 * Scan for closest preceding symbol, and next symbol. (ELF
27891fb02f3SAaron Tomlin 	 * starts real symbols at 1).
27991fb02f3SAaron Tomlin 	 */
28091fb02f3SAaron Tomlin 	for (i = 1; i < kallsyms->num_symtab; i++) {
28191fb02f3SAaron Tomlin 		const Elf_Sym *sym = &kallsyms->symtab[i];
28291fb02f3SAaron Tomlin 		unsigned long thisval = kallsyms_symbol_value(sym);
28391fb02f3SAaron Tomlin 
28491fb02f3SAaron Tomlin 		if (sym->st_shndx == SHN_UNDEF)
28591fb02f3SAaron Tomlin 			continue;
28691fb02f3SAaron Tomlin 
28791fb02f3SAaron Tomlin 		/*
28891fb02f3SAaron Tomlin 		 * We ignore unnamed symbols: they're uninformative
28991fb02f3SAaron Tomlin 		 * and inserted at a whim.
29091fb02f3SAaron Tomlin 		 */
29191fb02f3SAaron Tomlin 		if (*kallsyms_symbol_name(kallsyms, i) == '\0' ||
292*0a3bf860STiezhu Yang 		    is_mapping_symbol(kallsyms_symbol_name(kallsyms, i)))
29391fb02f3SAaron Tomlin 			continue;
29491fb02f3SAaron Tomlin 
29591fb02f3SAaron Tomlin 		if (thisval <= addr && thisval > bestval) {
29691fb02f3SAaron Tomlin 			best = i;
29791fb02f3SAaron Tomlin 			bestval = thisval;
29891fb02f3SAaron Tomlin 		}
29991fb02f3SAaron Tomlin 		if (thisval > addr && thisval < nextval)
30091fb02f3SAaron Tomlin 			nextval = thisval;
30191fb02f3SAaron Tomlin 	}
30291fb02f3SAaron Tomlin 
30391fb02f3SAaron Tomlin 	if (!best)
30491fb02f3SAaron Tomlin 		return NULL;
30591fb02f3SAaron Tomlin 
30691fb02f3SAaron Tomlin 	if (size)
30791fb02f3SAaron Tomlin 		*size = nextval - bestval;
30891fb02f3SAaron Tomlin 	if (offset)
30991fb02f3SAaron Tomlin 		*offset = addr - bestval;
31091fb02f3SAaron Tomlin 
31191fb02f3SAaron Tomlin 	return kallsyms_symbol_name(kallsyms, best);
31291fb02f3SAaron Tomlin }
31391fb02f3SAaron Tomlin 
dereference_module_function_descriptor(struct module * mod,void * ptr)31491fb02f3SAaron Tomlin void * __weak dereference_module_function_descriptor(struct module *mod,
31591fb02f3SAaron Tomlin 						     void *ptr)
31691fb02f3SAaron Tomlin {
31791fb02f3SAaron Tomlin 	return ptr;
31891fb02f3SAaron Tomlin }
31991fb02f3SAaron Tomlin 
32091fb02f3SAaron Tomlin /*
32191fb02f3SAaron Tomlin  * For kallsyms to ask for address resolution.  NULL means not found.  Careful
32291fb02f3SAaron Tomlin  * not to lock to avoid deadlock on oopses, simply disable preemption.
32391fb02f3SAaron Tomlin  */
module_address_lookup(unsigned long addr,unsigned long * size,unsigned long * offset,char ** modname,const unsigned char ** modbuildid,char * namebuf)32491fb02f3SAaron Tomlin const char *module_address_lookup(unsigned long addr,
32591fb02f3SAaron Tomlin 				  unsigned long *size,
32691fb02f3SAaron Tomlin 			    unsigned long *offset,
32791fb02f3SAaron Tomlin 			    char **modname,
32891fb02f3SAaron Tomlin 			    const unsigned char **modbuildid,
32991fb02f3SAaron Tomlin 			    char *namebuf)
33091fb02f3SAaron Tomlin {
33191fb02f3SAaron Tomlin 	const char *ret = NULL;
33291fb02f3SAaron Tomlin 	struct module *mod;
33391fb02f3SAaron Tomlin 
33491fb02f3SAaron Tomlin 	preempt_disable();
33591fb02f3SAaron Tomlin 	mod = __module_address(addr);
33691fb02f3SAaron Tomlin 	if (mod) {
33791fb02f3SAaron Tomlin 		if (modname)
33891fb02f3SAaron Tomlin 			*modname = mod->name;
33991fb02f3SAaron Tomlin 		if (modbuildid) {
34091fb02f3SAaron Tomlin #if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID)
34191fb02f3SAaron Tomlin 			*modbuildid = mod->build_id;
34291fb02f3SAaron Tomlin #else
34391fb02f3SAaron Tomlin 			*modbuildid = NULL;
34491fb02f3SAaron Tomlin #endif
34591fb02f3SAaron Tomlin 		}
34691fb02f3SAaron Tomlin 
34791fb02f3SAaron Tomlin 		ret = find_kallsyms_symbol(mod, addr, size, offset);
34891fb02f3SAaron Tomlin 	}
34991fb02f3SAaron Tomlin 	/* Make a copy in here where it's safe */
35091fb02f3SAaron Tomlin 	if (ret) {
35191fb02f3SAaron Tomlin 		strncpy(namebuf, ret, KSYM_NAME_LEN - 1);
35291fb02f3SAaron Tomlin 		ret = namebuf;
35391fb02f3SAaron Tomlin 	}
35491fb02f3SAaron Tomlin 	preempt_enable();
35591fb02f3SAaron Tomlin 
35691fb02f3SAaron Tomlin 	return ret;
35791fb02f3SAaron Tomlin }
35891fb02f3SAaron Tomlin 
lookup_module_symbol_name(unsigned long addr,char * symname)35991fb02f3SAaron Tomlin int lookup_module_symbol_name(unsigned long addr, char *symname)
36091fb02f3SAaron Tomlin {
36191fb02f3SAaron Tomlin 	struct module *mod;
36291fb02f3SAaron Tomlin 
36391fb02f3SAaron Tomlin 	preempt_disable();
36491fb02f3SAaron Tomlin 	list_for_each_entry_rcu(mod, &modules, list) {
36591fb02f3SAaron Tomlin 		if (mod->state == MODULE_STATE_UNFORMED)
36691fb02f3SAaron Tomlin 			continue;
36791fb02f3SAaron Tomlin 		if (within_module(addr, mod)) {
36891fb02f3SAaron Tomlin 			const char *sym;
36991fb02f3SAaron Tomlin 
37091fb02f3SAaron Tomlin 			sym = find_kallsyms_symbol(mod, addr, NULL, NULL);
37191fb02f3SAaron Tomlin 			if (!sym)
37291fb02f3SAaron Tomlin 				goto out;
37391fb02f3SAaron Tomlin 
37491fb02f3SAaron Tomlin 			strscpy(symname, sym, KSYM_NAME_LEN);
37591fb02f3SAaron Tomlin 			preempt_enable();
37691fb02f3SAaron Tomlin 			return 0;
37791fb02f3SAaron Tomlin 		}
37891fb02f3SAaron Tomlin 	}
37991fb02f3SAaron Tomlin out:
38091fb02f3SAaron Tomlin 	preempt_enable();
38191fb02f3SAaron Tomlin 	return -ERANGE;
38291fb02f3SAaron Tomlin }
38391fb02f3SAaron Tomlin 
module_get_kallsym(unsigned int symnum,unsigned long * value,char * type,char * name,char * module_name,int * exported)38491fb02f3SAaron Tomlin int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
38591fb02f3SAaron Tomlin 		       char *name, char *module_name, int *exported)
38691fb02f3SAaron Tomlin {
38791fb02f3SAaron Tomlin 	struct module *mod;
38891fb02f3SAaron Tomlin 
38991fb02f3SAaron Tomlin 	preempt_disable();
39091fb02f3SAaron Tomlin 	list_for_each_entry_rcu(mod, &modules, list) {
39191fb02f3SAaron Tomlin 		struct mod_kallsyms *kallsyms;
39291fb02f3SAaron Tomlin 
39391fb02f3SAaron Tomlin 		if (mod->state == MODULE_STATE_UNFORMED)
39491fb02f3SAaron Tomlin 			continue;
39591fb02f3SAaron Tomlin 		kallsyms = rcu_dereference_sched(mod->kallsyms);
39691fb02f3SAaron Tomlin 		if (symnum < kallsyms->num_symtab) {
39791fb02f3SAaron Tomlin 			const Elf_Sym *sym = &kallsyms->symtab[symnum];
39891fb02f3SAaron Tomlin 
39991fb02f3SAaron Tomlin 			*value = kallsyms_symbol_value(sym);
40091fb02f3SAaron Tomlin 			*type = kallsyms->typetab[symnum];
40191fb02f3SAaron Tomlin 			strscpy(name, kallsyms_symbol_name(kallsyms, symnum), KSYM_NAME_LEN);
40291fb02f3SAaron Tomlin 			strscpy(module_name, mod->name, MODULE_NAME_LEN);
40391fb02f3SAaron Tomlin 			*exported = is_exported(name, *value, mod);
40491fb02f3SAaron Tomlin 			preempt_enable();
40591fb02f3SAaron Tomlin 			return 0;
40691fb02f3SAaron Tomlin 		}
40791fb02f3SAaron Tomlin 		symnum -= kallsyms->num_symtab;
40891fb02f3SAaron Tomlin 	}
40991fb02f3SAaron Tomlin 	preempt_enable();
41091fb02f3SAaron Tomlin 	return -ERANGE;
41191fb02f3SAaron Tomlin }
41291fb02f3SAaron Tomlin 
41391fb02f3SAaron Tomlin /* Given a module and name of symbol, find and return the symbol's value */
__find_kallsyms_symbol_value(struct module * mod,const char * name)41491fb02f3SAaron Tomlin static unsigned long __find_kallsyms_symbol_value(struct module *mod, const char *name)
41591fb02f3SAaron Tomlin {
41691fb02f3SAaron Tomlin 	unsigned int i;
41791fb02f3SAaron Tomlin 	struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms);
41891fb02f3SAaron Tomlin 
41991fb02f3SAaron Tomlin 	for (i = 0; i < kallsyms->num_symtab; i++) {
42091fb02f3SAaron Tomlin 		const Elf_Sym *sym = &kallsyms->symtab[i];
42191fb02f3SAaron Tomlin 
42291fb02f3SAaron Tomlin 		if (strcmp(name, kallsyms_symbol_name(kallsyms, i)) == 0 &&
42391fb02f3SAaron Tomlin 		    sym->st_shndx != SHN_UNDEF)
42491fb02f3SAaron Tomlin 			return kallsyms_symbol_value(sym);
42591fb02f3SAaron Tomlin 	}
42691fb02f3SAaron Tomlin 	return 0;
42791fb02f3SAaron Tomlin }
42891fb02f3SAaron Tomlin 
__module_kallsyms_lookup_name(const char * name)42991fb02f3SAaron Tomlin static unsigned long __module_kallsyms_lookup_name(const char *name)
43091fb02f3SAaron Tomlin {
43191fb02f3SAaron Tomlin 	struct module *mod;
43291fb02f3SAaron Tomlin 	char *colon;
43391fb02f3SAaron Tomlin 
43491fb02f3SAaron Tomlin 	colon = strnchr(name, MODULE_NAME_LEN, ':');
43591fb02f3SAaron Tomlin 	if (colon) {
43691fb02f3SAaron Tomlin 		mod = find_module_all(name, colon - name, false);
43791fb02f3SAaron Tomlin 		if (mod)
43891fb02f3SAaron Tomlin 			return __find_kallsyms_symbol_value(mod, colon + 1);
43991fb02f3SAaron Tomlin 		return 0;
44091fb02f3SAaron Tomlin 	}
44191fb02f3SAaron Tomlin 
442d099f594SJiri Olsa 	list_for_each_entry_rcu(mod, &modules, list) {
44391fb02f3SAaron Tomlin 		unsigned long ret;
44491fb02f3SAaron Tomlin 
44591fb02f3SAaron Tomlin 		if (mod->state == MODULE_STATE_UNFORMED)
44691fb02f3SAaron Tomlin 			continue;
44791fb02f3SAaron Tomlin 		ret = __find_kallsyms_symbol_value(mod, name);
44891fb02f3SAaron Tomlin 		if (ret)
44991fb02f3SAaron Tomlin 			return ret;
45091fb02f3SAaron Tomlin 	}
45191fb02f3SAaron Tomlin 	return 0;
45291fb02f3SAaron Tomlin }
45391fb02f3SAaron Tomlin 
45491fb02f3SAaron Tomlin /* Look for this name: can be of form module:name. */
module_kallsyms_lookup_name(const char * name)45591fb02f3SAaron Tomlin unsigned long module_kallsyms_lookup_name(const char *name)
45691fb02f3SAaron Tomlin {
45707ade45aSChristophe Leroy 	unsigned long ret;
45891fb02f3SAaron Tomlin 
45991fb02f3SAaron Tomlin 	/* Don't lock: we're in enough trouble already. */
46091fb02f3SAaron Tomlin 	preempt_disable();
46191fb02f3SAaron Tomlin 	ret = __module_kallsyms_lookup_name(name);
462ecc726f1SChristophe Leroy 	preempt_enable();
463ecc726f1SChristophe Leroy 	return ret;
464ecc726f1SChristophe Leroy }
465ecc726f1SChristophe Leroy 
find_kallsyms_symbol_value(struct module * mod,const char * name)466d099f594SJiri Olsa unsigned long find_kallsyms_symbol_value(struct module *mod, const char *name)
46707ade45aSChristophe Leroy {
46807ade45aSChristophe Leroy 	unsigned long ret;
46907ade45aSChristophe Leroy 
47091fb02f3SAaron Tomlin 	preempt_disable();
47107ade45aSChristophe Leroy 	ret = __find_kallsyms_symbol_value(mod, name);
47207ade45aSChristophe Leroy 	preempt_enable();
47391fb02f3SAaron Tomlin 	return ret;
47491fb02f3SAaron Tomlin }
475d099f594SJiri Olsa 
module_kallsyms_on_each_symbol(const char * modname,int (* fn)(void *,const char *,unsigned long),void * data)476ecc726f1SChristophe Leroy int module_kallsyms_on_each_symbol(const char *modname,
47707ade45aSChristophe Leroy 				   int (*fn)(void *, const char *, unsigned long),
47891fb02f3SAaron Tomlin 				   void *data)
47907ade45aSChristophe Leroy {
48091fb02f3SAaron Tomlin 	struct module *mod;
48107ade45aSChristophe Leroy 	unsigned int i;
48207ade45aSChristophe Leroy 	int ret = 0;
48307ade45aSChristophe Leroy 
48407ade45aSChristophe Leroy 	mutex_lock(&module_mutex);
48507ade45aSChristophe Leroy 	list_for_each_entry(mod, &modules, list) {
48607ade45aSChristophe Leroy 		struct mod_kallsyms *kallsyms;
48707ade45aSChristophe Leroy 
48807ade45aSChristophe Leroy 		if (mod->state == MODULE_STATE_UNFORMED)
48907ade45aSChristophe Leroy 			continue;
49091fb02f3SAaron Tomlin 
49191fb02f3SAaron Tomlin 		if (modname && strcmp(modname, mod->name))
49291fb02f3SAaron Tomlin 			continue;
49391fb02f3SAaron Tomlin 
494d099f594SJiri Olsa 		/* Use rcu_dereference_sched() to remain compliant with the sparse tool */
495d099f594SJiri Olsa 		preempt_disable();
496d099f594SJiri Olsa 		kallsyms = rcu_dereference_sched(mod->kallsyms);
497d099f594SJiri Olsa 		preempt_enable();
498d099f594SJiri Olsa 
499d099f594SJiri Olsa 		for (i = 0; i < kallsyms->num_symtab; i++) {
500d099f594SJiri Olsa 			const Elf_Sym *sym = &kallsyms->symtab[i];
501d099f594SJiri Olsa 
502d099f594SJiri Olsa 			if (sym->st_shndx == SHN_UNDEF)
503d099f594SJiri Olsa 				continue;
50407cc2c93SZhen Lei 
5053703bd54SZhen Lei 			ret = fn(data, kallsyms_symbol_name(kallsyms, i),
50691fb02f3SAaron Tomlin 				 kallsyms_symbol_value(sym));
50791fb02f3SAaron Tomlin 			if (ret != 0)
50891fb02f3SAaron Tomlin 				goto out;
50991fb02f3SAaron Tomlin 		}
51091fb02f3SAaron Tomlin 
51191fb02f3SAaron Tomlin 		/*
51291fb02f3SAaron Tomlin 		 * The given module is found, the subsequent modules do not
51391fb02f3SAaron Tomlin 		 * need to be compared.
51408126db5SAaron Tomlin 		 */
51591fb02f3SAaron Tomlin 		if (modname)
51691fb02f3SAaron Tomlin 			break;
51791fb02f3SAaron Tomlin 	}
51808126db5SAaron Tomlin out:
51907cc2c93SZhen Lei 	mutex_unlock(&module_mutex);
52007cc2c93SZhen Lei 	return ret;
52107cc2c93SZhen Lei }
52208126db5SAaron Tomlin