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