xref: /linux-6.15/kernel/module/kallsyms.c (revision 6593a2c9)
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;
180039de468SSebastian Andrzej Siewior 	struct mod_kallsyms *kallsyms;
18191fb02f3SAaron Tomlin 
182039de468SSebastian Andrzej Siewior 	kallsyms = init_data_base + info->mod_kallsyms_init_off;
18391fb02f3SAaron Tomlin 
184039de468SSebastian Andrzej Siewior 	kallsyms->symtab = (void *)symsec->sh_addr;
185039de468SSebastian Andrzej Siewior 	kallsyms->num_symtab = symsec->sh_size / sizeof(Elf_Sym);
18691fb02f3SAaron Tomlin 	/* Make sure we get permanent strtab: don't use info->strtab. */
187039de468SSebastian Andrzej Siewior 	kallsyms->strtab = (void *)info->sechdrs[info->index.str].sh_addr;
188039de468SSebastian Andrzej Siewior 	kallsyms->typetab = init_data_base + info->init_typeoffs;
18991fb02f3SAaron Tomlin 
19091fb02f3SAaron Tomlin 	/*
19191fb02f3SAaron Tomlin 	 * Now populate the cut down core kallsyms for after init
19291fb02f3SAaron Tomlin 	 * and set types up while we still have access to sections.
19391fb02f3SAaron Tomlin 	 */
194ac3b4328SSong Liu 	mod->core_kallsyms.symtab = dst = data_base + info->symoffs;
195ac3b4328SSong Liu 	mod->core_kallsyms.strtab = s = data_base + info->stroffs;
196ac3b4328SSong Liu 	mod->core_kallsyms.typetab = data_base + info->core_typeoffs;
19735adf9a4SAdrian Hunter 	strtab_size = info->core_typeoffs - info->stroffs;
198039de468SSebastian Andrzej Siewior 	src = kallsyms->symtab;
199039de468SSebastian Andrzej Siewior 	for (ndst = i = 0; i < kallsyms->num_symtab; i++) {
200039de468SSebastian Andrzej Siewior 		kallsyms->typetab[i] = elf_type(src + i, info);
20191fb02f3SAaron Tomlin 		if (i == 0 || is_livepatch_module(mod) ||
20291fb02f3SAaron Tomlin 		    is_core_symbol(src + i, info->sechdrs, info->hdr->e_shnum,
20391fb02f3SAaron Tomlin 				   info->index.pcpu)) {
20435adf9a4SAdrian Hunter 			ssize_t ret;
20535adf9a4SAdrian Hunter 
20691fb02f3SAaron Tomlin 			mod->core_kallsyms.typetab[ndst] =
207039de468SSebastian Andrzej Siewior 				kallsyms->typetab[i];
20891fb02f3SAaron Tomlin 			dst[ndst] = src[i];
20991fb02f3SAaron Tomlin 			dst[ndst++].st_name = s - mod->core_kallsyms.strtab;
210039de468SSebastian Andrzej Siewior 			ret = strscpy(s, &kallsyms->strtab[src[i].st_name],
21135adf9a4SAdrian Hunter 				      strtab_size);
21235adf9a4SAdrian Hunter 			if (ret < 0)
21335adf9a4SAdrian Hunter 				break;
21435adf9a4SAdrian Hunter 			s += ret + 1;
21535adf9a4SAdrian Hunter 			strtab_size -= ret + 1;
21691fb02f3SAaron Tomlin 		}
21791fb02f3SAaron Tomlin 	}
218039de468SSebastian Andrzej Siewior 
219039de468SSebastian Andrzej Siewior 	/* Set up to point into init section. */
220039de468SSebastian Andrzej Siewior 	rcu_assign_pointer(mod->kallsyms, kallsyms);
22191fb02f3SAaron Tomlin 	mod->core_kallsyms.num_symtab = ndst;
22291fb02f3SAaron Tomlin }
22391fb02f3SAaron Tomlin 
22491fb02f3SAaron Tomlin #if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID)
init_build_id(struct module * mod,const struct load_info * info)22591fb02f3SAaron Tomlin void init_build_id(struct module *mod, const struct load_info *info)
22691fb02f3SAaron Tomlin {
22791fb02f3SAaron Tomlin 	const Elf_Shdr *sechdr;
22891fb02f3SAaron Tomlin 	unsigned int i;
22991fb02f3SAaron Tomlin 
23091fb02f3SAaron Tomlin 	for (i = 0; i < info->hdr->e_shnum; i++) {
23191fb02f3SAaron Tomlin 		sechdr = &info->sechdrs[i];
23291fb02f3SAaron Tomlin 		if (!sect_empty(sechdr) && sechdr->sh_type == SHT_NOTE &&
23391fb02f3SAaron Tomlin 		    !build_id_parse_buf((void *)sechdr->sh_addr, mod->build_id,
23491fb02f3SAaron Tomlin 					sechdr->sh_size))
23591fb02f3SAaron Tomlin 			break;
23691fb02f3SAaron Tomlin 	}
23791fb02f3SAaron Tomlin }
23891fb02f3SAaron Tomlin #else
init_build_id(struct module * mod,const struct load_info * info)23991fb02f3SAaron Tomlin void init_build_id(struct module *mod, const struct load_info *info)
24091fb02f3SAaron Tomlin {
24191fb02f3SAaron Tomlin }
24291fb02f3SAaron Tomlin #endif
24391fb02f3SAaron Tomlin 
kallsyms_symbol_name(struct mod_kallsyms * kallsyms,unsigned int symnum)24491fb02f3SAaron Tomlin static const char *kallsyms_symbol_name(struct mod_kallsyms *kallsyms, unsigned int symnum)
24591fb02f3SAaron Tomlin {
24691fb02f3SAaron Tomlin 	return kallsyms->strtab + kallsyms->symtab[symnum].st_name;
24791fb02f3SAaron Tomlin }
24891fb02f3SAaron Tomlin 
24991fb02f3SAaron Tomlin /*
25091fb02f3SAaron Tomlin  * Given a module and address, find the corresponding symbol and return its name
25191fb02f3SAaron Tomlin  * while providing its size and offset if needed.
25291fb02f3SAaron Tomlin  */
find_kallsyms_symbol(struct module * mod,unsigned long addr,unsigned long * size,unsigned long * offset)25391fb02f3SAaron Tomlin static const char *find_kallsyms_symbol(struct module *mod,
25491fb02f3SAaron Tomlin 					unsigned long addr,
25591fb02f3SAaron Tomlin 					unsigned long *size,
25691fb02f3SAaron Tomlin 					unsigned long *offset)
25791fb02f3SAaron Tomlin {
25891fb02f3SAaron Tomlin 	unsigned int i, best = 0;
25991fb02f3SAaron Tomlin 	unsigned long nextval, bestval;
260f0136923SSebastian Andrzej Siewior 	struct mod_kallsyms *kallsyms = rcu_dereference(mod->kallsyms);
261ac3b4328SSong Liu 	struct module_memory *mod_mem;
26291fb02f3SAaron Tomlin 
26391fb02f3SAaron Tomlin 	/* At worse, next value is at end of module */
26491fb02f3SAaron Tomlin 	if (within_module_init(addr, mod))
265ac3b4328SSong Liu 		mod_mem = &mod->mem[MOD_INIT_TEXT];
26691fb02f3SAaron Tomlin 	else
267ac3b4328SSong Liu 		mod_mem = &mod->mem[MOD_TEXT];
268ac3b4328SSong Liu 
269ac3b4328SSong Liu 	nextval = (unsigned long)mod_mem->base + mod_mem->size;
27091fb02f3SAaron Tomlin 
27191fb02f3SAaron Tomlin 	bestval = kallsyms_symbol_value(&kallsyms->symtab[best]);
27291fb02f3SAaron Tomlin 
27391fb02f3SAaron Tomlin 	/*
27491fb02f3SAaron Tomlin 	 * Scan for closest preceding symbol, and next symbol. (ELF
27591fb02f3SAaron Tomlin 	 * starts real symbols at 1).
27691fb02f3SAaron Tomlin 	 */
27791fb02f3SAaron Tomlin 	for (i = 1; i < kallsyms->num_symtab; i++) {
27891fb02f3SAaron Tomlin 		const Elf_Sym *sym = &kallsyms->symtab[i];
27991fb02f3SAaron Tomlin 		unsigned long thisval = kallsyms_symbol_value(sym);
28091fb02f3SAaron Tomlin 
28191fb02f3SAaron Tomlin 		if (sym->st_shndx == SHN_UNDEF)
28291fb02f3SAaron Tomlin 			continue;
28391fb02f3SAaron Tomlin 
28491fb02f3SAaron Tomlin 		/*
28591fb02f3SAaron Tomlin 		 * We ignore unnamed symbols: they're uninformative
28691fb02f3SAaron Tomlin 		 * and inserted at a whim.
28791fb02f3SAaron Tomlin 		 */
28891fb02f3SAaron Tomlin 		if (*kallsyms_symbol_name(kallsyms, i) == '\0' ||
2890a3bf860STiezhu Yang 		    is_mapping_symbol(kallsyms_symbol_name(kallsyms, i)))
29091fb02f3SAaron Tomlin 			continue;
29191fb02f3SAaron Tomlin 
29291fb02f3SAaron Tomlin 		if (thisval <= addr && thisval > bestval) {
29391fb02f3SAaron Tomlin 			best = i;
29491fb02f3SAaron Tomlin 			bestval = thisval;
29591fb02f3SAaron Tomlin 		}
29691fb02f3SAaron Tomlin 		if (thisval > addr && thisval < nextval)
29791fb02f3SAaron Tomlin 			nextval = thisval;
29891fb02f3SAaron Tomlin 	}
29991fb02f3SAaron Tomlin 
30091fb02f3SAaron Tomlin 	if (!best)
30191fb02f3SAaron Tomlin 		return NULL;
30291fb02f3SAaron Tomlin 
30391fb02f3SAaron Tomlin 	if (size)
30491fb02f3SAaron Tomlin 		*size = nextval - bestval;
30591fb02f3SAaron Tomlin 	if (offset)
30691fb02f3SAaron Tomlin 		*offset = addr - bestval;
30791fb02f3SAaron Tomlin 
30891fb02f3SAaron Tomlin 	return kallsyms_symbol_name(kallsyms, best);
30991fb02f3SAaron Tomlin }
31091fb02f3SAaron Tomlin 
dereference_module_function_descriptor(struct module * mod,void * ptr)31191fb02f3SAaron Tomlin void * __weak dereference_module_function_descriptor(struct module *mod,
31291fb02f3SAaron Tomlin 						     void *ptr)
31391fb02f3SAaron Tomlin {
31491fb02f3SAaron Tomlin 	return ptr;
31591fb02f3SAaron Tomlin }
31691fb02f3SAaron Tomlin 
31791fb02f3SAaron Tomlin /*
31891fb02f3SAaron Tomlin  * For kallsyms to ask for address resolution.  NULL means not found.  Careful
319*6593a2c9SSebastian Andrzej Siewior  * not to lock to avoid deadlock on oopses, RCU is enough.
32091fb02f3SAaron Tomlin  */
module_address_lookup(unsigned long addr,unsigned long * size,unsigned long * offset,char ** modname,const unsigned char ** modbuildid,char * namebuf)3217e1f4eb9SArnd Bergmann int module_address_lookup(unsigned long addr,
32291fb02f3SAaron Tomlin 			  unsigned long *size,
32391fb02f3SAaron Tomlin 			  unsigned long *offset,
32491fb02f3SAaron Tomlin 			  char **modname,
32591fb02f3SAaron Tomlin 			  const unsigned char **modbuildid,
32691fb02f3SAaron Tomlin 			  char *namebuf)
32791fb02f3SAaron Tomlin {
3287e1f4eb9SArnd Bergmann 	const char *sym;
3297e1f4eb9SArnd Bergmann 	int ret = 0;
33091fb02f3SAaron Tomlin 	struct module *mod;
33191fb02f3SAaron Tomlin 
332f0136923SSebastian Andrzej Siewior 	guard(rcu)();
33391fb02f3SAaron Tomlin 	mod = __module_address(addr);
33491fb02f3SAaron Tomlin 	if (mod) {
33591fb02f3SAaron Tomlin 		if (modname)
33691fb02f3SAaron Tomlin 			*modname = mod->name;
33791fb02f3SAaron Tomlin 		if (modbuildid) {
33891fb02f3SAaron Tomlin #if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID)
33991fb02f3SAaron Tomlin 			*modbuildid = mod->build_id;
34091fb02f3SAaron Tomlin #else
34191fb02f3SAaron Tomlin 			*modbuildid = NULL;
34291fb02f3SAaron Tomlin #endif
34391fb02f3SAaron Tomlin 		}
34491fb02f3SAaron Tomlin 
3457e1f4eb9SArnd Bergmann 		sym = find_kallsyms_symbol(mod, addr, size, offset);
3467e1f4eb9SArnd Bergmann 
3477e1f4eb9SArnd Bergmann 		if (sym)
3487e1f4eb9SArnd Bergmann 			ret = strscpy(namebuf, sym, KSYM_NAME_LEN);
34991fb02f3SAaron Tomlin 	}
35091fb02f3SAaron Tomlin 	return ret;
35191fb02f3SAaron Tomlin }
35291fb02f3SAaron Tomlin 
lookup_module_symbol_name(unsigned long addr,char * symname)35391fb02f3SAaron Tomlin int lookup_module_symbol_name(unsigned long addr, char *symname)
35491fb02f3SAaron Tomlin {
35591fb02f3SAaron Tomlin 	struct module *mod;
35691fb02f3SAaron Tomlin 
357f0136923SSebastian Andrzej Siewior 	guard(rcu)();
35891fb02f3SAaron Tomlin 	list_for_each_entry_rcu(mod, &modules, list) {
35991fb02f3SAaron Tomlin 		if (mod->state == MODULE_STATE_UNFORMED)
36091fb02f3SAaron Tomlin 			continue;
36191fb02f3SAaron Tomlin 		if (within_module(addr, mod)) {
36291fb02f3SAaron Tomlin 			const char *sym;
36391fb02f3SAaron Tomlin 
36491fb02f3SAaron Tomlin 			sym = find_kallsyms_symbol(mod, addr, NULL, NULL);
36591fb02f3SAaron Tomlin 			if (!sym)
36691fb02f3SAaron Tomlin 				goto out;
36791fb02f3SAaron Tomlin 
36891fb02f3SAaron Tomlin 			strscpy(symname, sym, KSYM_NAME_LEN);
36991fb02f3SAaron Tomlin 			return 0;
37091fb02f3SAaron Tomlin 		}
37191fb02f3SAaron Tomlin 	}
37291fb02f3SAaron Tomlin out:
37391fb02f3SAaron Tomlin 	return -ERANGE;
37491fb02f3SAaron Tomlin }
37591fb02f3SAaron Tomlin 
module_get_kallsym(unsigned int symnum,unsigned long * value,char * type,char * name,char * module_name,int * exported)37691fb02f3SAaron Tomlin int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
37791fb02f3SAaron Tomlin 		       char *name, char *module_name, int *exported)
37891fb02f3SAaron Tomlin {
37991fb02f3SAaron Tomlin 	struct module *mod;
38091fb02f3SAaron Tomlin 
381c4fadf38SSebastian Andrzej Siewior 	guard(rcu)();
38291fb02f3SAaron Tomlin 	list_for_each_entry_rcu(mod, &modules, list) {
38391fb02f3SAaron Tomlin 		struct mod_kallsyms *kallsyms;
38491fb02f3SAaron Tomlin 
38591fb02f3SAaron Tomlin 		if (mod->state == MODULE_STATE_UNFORMED)
38691fb02f3SAaron Tomlin 			continue;
387c4fadf38SSebastian Andrzej Siewior 		kallsyms = rcu_dereference(mod->kallsyms);
38891fb02f3SAaron Tomlin 		if (symnum < kallsyms->num_symtab) {
38991fb02f3SAaron Tomlin 			const Elf_Sym *sym = &kallsyms->symtab[symnum];
39091fb02f3SAaron Tomlin 
39191fb02f3SAaron Tomlin 			*value = kallsyms_symbol_value(sym);
39291fb02f3SAaron Tomlin 			*type = kallsyms->typetab[symnum];
39391fb02f3SAaron Tomlin 			strscpy(name, kallsyms_symbol_name(kallsyms, symnum), KSYM_NAME_LEN);
39491fb02f3SAaron Tomlin 			strscpy(module_name, mod->name, MODULE_NAME_LEN);
39591fb02f3SAaron Tomlin 			*exported = is_exported(name, *value, mod);
39691fb02f3SAaron Tomlin 			return 0;
39791fb02f3SAaron Tomlin 		}
39891fb02f3SAaron Tomlin 		symnum -= kallsyms->num_symtab;
39991fb02f3SAaron Tomlin 	}
40091fb02f3SAaron Tomlin 	return -ERANGE;
40191fb02f3SAaron Tomlin }
40291fb02f3SAaron Tomlin 
40391fb02f3SAaron 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)404d099f594SJiri Olsa static unsigned long __find_kallsyms_symbol_value(struct module *mod, const char *name)
40591fb02f3SAaron Tomlin {
40691fb02f3SAaron Tomlin 	unsigned int i;
407f27d8ed6SSebastian Andrzej Siewior 	struct mod_kallsyms *kallsyms = rcu_dereference(mod->kallsyms);
40891fb02f3SAaron Tomlin 
40991fb02f3SAaron Tomlin 	for (i = 0; i < kallsyms->num_symtab; i++) {
41091fb02f3SAaron Tomlin 		const Elf_Sym *sym = &kallsyms->symtab[i];
41191fb02f3SAaron Tomlin 
41291fb02f3SAaron Tomlin 		if (strcmp(name, kallsyms_symbol_name(kallsyms, i)) == 0 &&
41391fb02f3SAaron Tomlin 		    sym->st_shndx != SHN_UNDEF)
41491fb02f3SAaron Tomlin 			return kallsyms_symbol_value(sym);
41591fb02f3SAaron Tomlin 	}
41691fb02f3SAaron Tomlin 	return 0;
41791fb02f3SAaron Tomlin }
41891fb02f3SAaron Tomlin 
__module_kallsyms_lookup_name(const char * name)41907ade45aSChristophe Leroy static unsigned long __module_kallsyms_lookup_name(const char *name)
42091fb02f3SAaron Tomlin {
42191fb02f3SAaron Tomlin 	struct module *mod;
42291fb02f3SAaron Tomlin 	char *colon;
42391fb02f3SAaron Tomlin 
424ecc726f1SChristophe Leroy 	colon = strnchr(name, MODULE_NAME_LEN, ':');
425ecc726f1SChristophe Leroy 	if (colon) {
426ecc726f1SChristophe Leroy 		mod = find_module_all(name, colon - name, false);
427ecc726f1SChristophe Leroy 		if (mod)
428d099f594SJiri Olsa 			return __find_kallsyms_symbol_value(mod, colon + 1);
42907ade45aSChristophe Leroy 		return 0;
43007ade45aSChristophe Leroy 	}
43107ade45aSChristophe Leroy 
43291fb02f3SAaron Tomlin 	list_for_each_entry_rcu(mod, &modules, list) {
43307ade45aSChristophe Leroy 		unsigned long ret;
43407ade45aSChristophe Leroy 
43591fb02f3SAaron Tomlin 		if (mod->state == MODULE_STATE_UNFORMED)
43691fb02f3SAaron Tomlin 			continue;
437d099f594SJiri Olsa 		ret = __find_kallsyms_symbol_value(mod, name);
438ecc726f1SChristophe Leroy 		if (ret)
43907ade45aSChristophe Leroy 			return ret;
44091fb02f3SAaron Tomlin 	}
44107ade45aSChristophe Leroy 	return 0;
44291fb02f3SAaron Tomlin }
44307ade45aSChristophe Leroy 
44407ade45aSChristophe Leroy /* Look for this name: can be of form module:name. */
module_kallsyms_lookup_name(const char * name)44507ade45aSChristophe Leroy unsigned long module_kallsyms_lookup_name(const char *name)
44607ade45aSChristophe Leroy {
44707ade45aSChristophe Leroy 	/* Don't lock: we're in enough trouble already. */
448febaa65cSSebastian Andrzej Siewior 	guard(rcu)();
449f27d8ed6SSebastian Andrzej Siewior 	return __module_kallsyms_lookup_name(name);
45091fb02f3SAaron Tomlin }
45191fb02f3SAaron Tomlin 
find_kallsyms_symbol_value(struct module * mod,const char * name)452d099f594SJiri Olsa unsigned long find_kallsyms_symbol_value(struct module *mod, const char *name)
453d099f594SJiri Olsa {
454f27d8ed6SSebastian Andrzej Siewior 	guard(rcu)();
455f27d8ed6SSebastian Andrzej Siewior 	return __find_kallsyms_symbol_value(mod, name);
456d099f594SJiri Olsa }
457d099f594SJiri Olsa 
module_kallsyms_on_each_symbol(const char * modname,int (* fn)(void *,const char *,unsigned long),void * data)45807cc2c93SZhen Lei int module_kallsyms_on_each_symbol(const char *modname,
4593703bd54SZhen Lei 				   int (*fn)(void *, const char *, unsigned long),
46091fb02f3SAaron Tomlin 				   void *data)
46191fb02f3SAaron Tomlin {
46291fb02f3SAaron Tomlin 	struct module *mod;
46391fb02f3SAaron Tomlin 	unsigned int i;
46491fb02f3SAaron Tomlin 	int ret = 0;
46591fb02f3SAaron Tomlin 
46691fb02f3SAaron Tomlin 	mutex_lock(&module_mutex);
46791fb02f3SAaron Tomlin 	list_for_each_entry(mod, &modules, list) {
46808126db5SAaron Tomlin 		struct mod_kallsyms *kallsyms;
46991fb02f3SAaron Tomlin 
47091fb02f3SAaron Tomlin 		if (mod->state == MODULE_STATE_UNFORMED)
47191fb02f3SAaron Tomlin 			continue;
47208126db5SAaron Tomlin 
47307cc2c93SZhen Lei 		if (modname && strcmp(modname, mod->name))
47407cc2c93SZhen Lei 			continue;
47507cc2c93SZhen Lei 
476cdd9335cSSebastian Andrzej Siewior 		kallsyms = rcu_dereference_check(mod->kallsyms,
477cdd9335cSSebastian Andrzej Siewior 						 lockdep_is_held(&module_mutex));
47808126db5SAaron Tomlin 
47991fb02f3SAaron Tomlin 		for (i = 0; i < kallsyms->num_symtab; i++) {
48091fb02f3SAaron Tomlin 			const Elf_Sym *sym = &kallsyms->symtab[i];
48191fb02f3SAaron Tomlin 
48291fb02f3SAaron Tomlin 			if (sym->st_shndx == SHN_UNDEF)
48391fb02f3SAaron Tomlin 				continue;
48491fb02f3SAaron Tomlin 
48591fb02f3SAaron Tomlin 			ret = fn(data, kallsyms_symbol_name(kallsyms, i),
4863703bd54SZhen Lei 				 kallsyms_symbol_value(sym));
48791fb02f3SAaron Tomlin 			if (ret != 0)
48891fb02f3SAaron Tomlin 				goto out;
48991fb02f3SAaron Tomlin 		}
49007cc2c93SZhen Lei 
49107cc2c93SZhen Lei 		/*
49207cc2c93SZhen Lei 		 * The given module is found, the subsequent modules do not
49307cc2c93SZhen Lei 		 * need to be compared.
49407cc2c93SZhen Lei 		 */
49507cc2c93SZhen Lei 		if (modname)
49607cc2c93SZhen Lei 			break;
49791fb02f3SAaron Tomlin 	}
49891fb02f3SAaron Tomlin out:
49991fb02f3SAaron Tomlin 	mutex_unlock(&module_mutex);
50091fb02f3SAaron Tomlin 	return ret;
50191fb02f3SAaron Tomlin }
502