root/kernel/module/kallsyms.c
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Module kallsyms support
 *
 * Copyright (C) 2010 Rusty Russell
 */

#include <linux/module.h>
#include <linux/module_symbol.h>
#include <linux/kallsyms.h>
#include <linux/buildid.h>
#include <linux/bsearch.h>
#include "internal.h"

/* Lookup exported symbol in given range of kernel_symbols */
static const struct kernel_symbol *lookup_exported_symbol(const char *name,
                                                          const struct kernel_symbol *start,
                                                          const struct kernel_symbol *stop)
{
        return bsearch(name, start, stop - start,
                        sizeof(struct kernel_symbol), cmp_name);
}

static int is_exported(const char *name, unsigned long value,
                       const struct module *mod)
{
        const struct kernel_symbol *ks;

        if (!mod)
                ks = lookup_exported_symbol(name, __start___ksymtab, __stop___ksymtab);
        else
                ks = lookup_exported_symbol(name, mod->syms, mod->syms + mod->num_syms);

        return ks && kernel_symbol_value(ks) == value;
}

/* As per nm */
static char elf_type(const Elf_Sym *sym, const struct load_info *info)
{
        const Elf_Shdr *sechdrs = info->sechdrs;

        if (ELF_ST_BIND(sym->st_info) == STB_WEAK) {
                if (ELF_ST_TYPE(sym->st_info) == STT_OBJECT)
                        return 'v';
                else
                        return 'w';
        }
        if (sym->st_shndx == SHN_UNDEF)
                return 'U';
        if (sym->st_shndx == SHN_ABS || sym->st_shndx == info->index.pcpu)
                return 'a';
        if (sym->st_shndx >= SHN_LORESERVE)
                return '?';
        if (sechdrs[sym->st_shndx].sh_flags & SHF_EXECINSTR)
                return 't';
        if (sechdrs[sym->st_shndx].sh_flags & SHF_ALLOC &&
            sechdrs[sym->st_shndx].sh_type != SHT_NOBITS) {
                if (!(sechdrs[sym->st_shndx].sh_flags & SHF_WRITE))
                        return 'r';
                else if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL)
                        return 'g';
                else
                        return 'd';
        }
        if (sechdrs[sym->st_shndx].sh_type == SHT_NOBITS) {
                if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL)
                        return 's';
                else
                        return 'b';
        }
        if (strstarts(info->secstrings + sechdrs[sym->st_shndx].sh_name,
                      ".debug")) {
                return 'n';
        }
        return '?';
}

static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs,
                           unsigned int shnum, unsigned int pcpundx)
{
        const Elf_Shdr *sec;
        enum mod_mem_type type;

        if (src->st_shndx == SHN_UNDEF ||
            src->st_shndx >= shnum ||
            !src->st_name)
                return false;

#ifdef CONFIG_KALLSYMS_ALL
        if (src->st_shndx == pcpundx)
                return true;
#endif

        sec = sechdrs + src->st_shndx;
        type = sec->sh_entsize >> SH_ENTSIZE_TYPE_SHIFT;
        if (!(sec->sh_flags & SHF_ALLOC)
#ifndef CONFIG_KALLSYMS_ALL
            || !(sec->sh_flags & SHF_EXECINSTR)
#endif
            || mod_mem_type_is_init(type))
                return false;

        return true;
}

/*
 * We only allocate and copy the strings needed by the parts of symtab
 * we keep.  This is simple, but has the effect of making multiple
 * copies of duplicates.  We could be more sophisticated, see
 * linux-kernel thread starting with
 * <73defb5e4bca04a6431392cc341112b1@localhost>.
 */
void layout_symtab(struct module *mod, struct load_info *info)
{
        Elf_Shdr *symsect = info->sechdrs + info->index.sym;
        Elf_Shdr *strsect = info->sechdrs + info->index.str;
        const Elf_Sym *src;
        unsigned int i, nsrc, ndst, strtab_size = 0;
        struct module_memory *mod_mem_data = &mod->mem[MOD_DATA];
        struct module_memory *mod_mem_init_data = &mod->mem[MOD_INIT_DATA];

        /* Put symbol section at end of init part of module. */
        symsect->sh_flags |= SHF_ALLOC;
        symsect->sh_entsize = module_get_offset_and_type(mod, MOD_INIT_DATA,
                                                         symsect, info->index.sym);
        pr_debug("\t%s\n", info->secstrings + symsect->sh_name);

        src = (void *)info->hdr + symsect->sh_offset;
        nsrc = symsect->sh_size / sizeof(*src);

        /* Compute total space required for the core symbols' strtab. */
        for (ndst = i = 0; i < nsrc; i++) {
                if (i == 0 || is_livepatch_module(mod) ||
                    is_core_symbol(src + i, info->sechdrs, info->hdr->e_shnum,
                                   info->index.pcpu)) {
                        strtab_size += strlen(&info->strtab[src[i].st_name]) + 1;
                        ndst++;
                }
        }

        /* Append room for core symbols at end of core part. */
        info->symoffs = ALIGN(mod_mem_data->size, symsect->sh_addralign ?: 1);
        info->stroffs = mod_mem_data->size = info->symoffs + ndst * sizeof(Elf_Sym);
        mod_mem_data->size += strtab_size;
        /* Note add_kallsyms() computes strtab_size as core_typeoffs - stroffs */
        info->core_typeoffs = mod_mem_data->size;
        mod_mem_data->size += ndst * sizeof(char);

        /* Put string table section at end of init part of module. */
        strsect->sh_flags |= SHF_ALLOC;
        strsect->sh_entsize = module_get_offset_and_type(mod, MOD_INIT_DATA,
                                                         strsect, info->index.str);
        pr_debug("\t%s\n", info->secstrings + strsect->sh_name);

        /* We'll tack temporary mod_kallsyms on the end. */
        mod_mem_init_data->size = ALIGN(mod_mem_init_data->size,
                                        __alignof__(struct mod_kallsyms));
        info->mod_kallsyms_init_off = mod_mem_init_data->size;

        mod_mem_init_data->size += sizeof(struct mod_kallsyms);
        info->init_typeoffs = mod_mem_init_data->size;
        mod_mem_init_data->size += nsrc * sizeof(char);
}

/*
 * We use the full symtab and strtab which layout_symtab arranged to
 * be appended to the init section.  Later we switch to the cut-down
 * core-only ones.
 */
void add_kallsyms(struct module *mod, const struct load_info *info)
{
        unsigned int i, ndst;
        const Elf_Sym *src;
        Elf_Sym *dst;
        char *s;
        Elf_Shdr *symsec = &info->sechdrs[info->index.sym];
        unsigned long strtab_size;
        void *data_base = mod->mem[MOD_DATA].base;
        void *init_data_base = mod->mem[MOD_INIT_DATA].base;
        struct mod_kallsyms *kallsyms;

        kallsyms = init_data_base + info->mod_kallsyms_init_off;

        kallsyms->symtab = (void *)symsec->sh_addr;
        kallsyms->num_symtab = symsec->sh_size / sizeof(Elf_Sym);
        /* Make sure we get permanent strtab: don't use info->strtab. */
        kallsyms->strtab = (void *)info->sechdrs[info->index.str].sh_addr;
        kallsyms->typetab = init_data_base + info->init_typeoffs;

        /*
         * Now populate the cut down core kallsyms for after init
         * and set types up while we still have access to sections.
         */
        mod->core_kallsyms.symtab = dst = data_base + info->symoffs;
        mod->core_kallsyms.strtab = s = data_base + info->stroffs;
        mod->core_kallsyms.typetab = data_base + info->core_typeoffs;
        strtab_size = info->core_typeoffs - info->stroffs;
        src = kallsyms->symtab;
        for (ndst = i = 0; i < kallsyms->num_symtab; i++) {
                kallsyms->typetab[i] = elf_type(src + i, info);
                if (i == 0 || is_livepatch_module(mod) ||
                    is_core_symbol(src + i, info->sechdrs, info->hdr->e_shnum,
                                   info->index.pcpu)) {
                        ssize_t ret;

                        mod->core_kallsyms.typetab[ndst] =
                                kallsyms->typetab[i];
                        dst[ndst] = src[i];
                        dst[ndst++].st_name = s - mod->core_kallsyms.strtab;
                        ret = strscpy(s, &kallsyms->strtab[src[i].st_name],
                                      strtab_size);
                        if (ret < 0)
                                break;
                        s += ret + 1;
                        strtab_size -= ret + 1;
                }
        }

        /* Set up to point into init section. */
        rcu_assign_pointer(mod->kallsyms, kallsyms);
        mod->core_kallsyms.num_symtab = ndst;
}

#if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID)
void init_build_id(struct module *mod, const struct load_info *info)
{
        const Elf_Shdr *sechdr;
        unsigned int i;

        for (i = 0; i < info->hdr->e_shnum; i++) {
                sechdr = &info->sechdrs[i];
                if (!sect_empty(sechdr) && sechdr->sh_type == SHT_NOTE &&
                    !build_id_parse_buf((void *)sechdr->sh_addr, mod->build_id,
                                        sechdr->sh_size))
                        break;
        }
}
#else
void init_build_id(struct module *mod, const struct load_info *info)
{
}
#endif

static const char *kallsyms_symbol_name(struct mod_kallsyms *kallsyms, unsigned int symnum)
{
        return kallsyms->strtab + kallsyms->symtab[symnum].st_name;
}

/*
 * Given a module and address, find the corresponding symbol and return its name
 * while providing its size and offset if needed.
 */
static const char *find_kallsyms_symbol(struct module *mod,
                                        unsigned long addr,
                                        unsigned long *size,
                                        unsigned long *offset)
{
        unsigned int i, best = 0;
        unsigned long nextval, bestval;
        struct mod_kallsyms *kallsyms = rcu_dereference(mod->kallsyms);
        struct module_memory *mod_mem;

        /* At worse, next value is at end of module */
        if (within_module_init(addr, mod))
                mod_mem = &mod->mem[MOD_INIT_TEXT];
        else
                mod_mem = &mod->mem[MOD_TEXT];

        nextval = (unsigned long)mod_mem->base + mod_mem->size;

        bestval = kallsyms_symbol_value(&kallsyms->symtab[best]);

        /*
         * Scan for closest preceding symbol, and next symbol. (ELF
         * starts real symbols at 1).
         */
        for (i = 1; i < kallsyms->num_symtab; i++) {
                const Elf_Sym *sym = &kallsyms->symtab[i];
                unsigned long thisval = kallsyms_symbol_value(sym);

                if (sym->st_shndx == SHN_UNDEF)
                        continue;

                /*
                 * We ignore unnamed symbols: they're uninformative
                 * and inserted at a whim.
                 */
                if (*kallsyms_symbol_name(kallsyms, i) == '\0' ||
                    is_mapping_symbol(kallsyms_symbol_name(kallsyms, i)))
                        continue;

                if (thisval <= addr && thisval > bestval) {
                        best = i;
                        bestval = thisval;
                }
                if (thisval > addr && thisval < nextval)
                        nextval = thisval;
        }

        if (!best)
                return NULL;

        if (size)
                *size = nextval - bestval;
        if (offset)
                *offset = addr - bestval;

        return kallsyms_symbol_name(kallsyms, best);
}

void * __weak dereference_module_function_descriptor(struct module *mod,
                                                     void *ptr)
{
        return ptr;
}

/*
 * For kallsyms to ask for address resolution.  NULL means not found.  Careful
 * not to lock to avoid deadlock on oopses, RCU is enough.
 */
int module_address_lookup(unsigned long addr,
                          unsigned long *size,
                          unsigned long *offset,
                          char **modname,
                          const unsigned char **modbuildid,
                          char *namebuf)
{
        const char *sym;
        int ret = 0;
        struct module *mod;

        guard(rcu)();
        mod = __module_address(addr);
        if (mod) {
                if (modname)
                        *modname = mod->name;
                if (modbuildid)
                        *modbuildid = module_buildid(mod);

                sym = find_kallsyms_symbol(mod, addr, size, offset);

                if (sym)
                        ret = strscpy(namebuf, sym, KSYM_NAME_LEN);
        }
        return ret;
}

int lookup_module_symbol_name(unsigned long addr, char *symname)
{
        struct module *mod;

        guard(rcu)();
        list_for_each_entry_rcu(mod, &modules, list) {
                if (mod->state == MODULE_STATE_UNFORMED)
                        continue;
                if (within_module(addr, mod)) {
                        const char *sym;

                        sym = find_kallsyms_symbol(mod, addr, NULL, NULL);
                        if (!sym)
                                goto out;

                        strscpy(symname, sym, KSYM_NAME_LEN);
                        return 0;
                }
        }
out:
        return -ERANGE;
}

int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
                       char *name, char *module_name, int *exported)
{
        struct module *mod;

        guard(rcu)();
        list_for_each_entry_rcu(mod, &modules, list) {
                struct mod_kallsyms *kallsyms;

                if (mod->state == MODULE_STATE_UNFORMED)
                        continue;
                kallsyms = rcu_dereference(mod->kallsyms);
                if (symnum < kallsyms->num_symtab) {
                        const Elf_Sym *sym = &kallsyms->symtab[symnum];

                        *value = kallsyms_symbol_value(sym);
                        *type = kallsyms->typetab[symnum];
                        strscpy(name, kallsyms_symbol_name(kallsyms, symnum), KSYM_NAME_LEN);
                        strscpy(module_name, mod->name, MODULE_NAME_LEN);
                        *exported = is_exported(name, *value, mod);
                        return 0;
                }
                symnum -= kallsyms->num_symtab;
        }
        return -ERANGE;
}

/* Given a module and name of symbol, find and return the symbol's value */
static unsigned long __find_kallsyms_symbol_value(struct module *mod, const char *name)
{
        unsigned int i;
        struct mod_kallsyms *kallsyms = rcu_dereference(mod->kallsyms);

        for (i = 0; i < kallsyms->num_symtab; i++) {
                const Elf_Sym *sym = &kallsyms->symtab[i];

                if (strcmp(name, kallsyms_symbol_name(kallsyms, i)) == 0 &&
                    sym->st_shndx != SHN_UNDEF)
                        return kallsyms_symbol_value(sym);
        }
        return 0;
}

static unsigned long __module_kallsyms_lookup_name(const char *name)
{
        struct module *mod;
        char *colon;

        colon = strnchr(name, MODULE_NAME_LEN, ':');
        if (colon) {
                mod = find_module_all(name, colon - name, false);
                if (mod)
                        return __find_kallsyms_symbol_value(mod, colon + 1);
                return 0;
        }

        list_for_each_entry_rcu(mod, &modules, list) {
                unsigned long ret;

                if (mod->state == MODULE_STATE_UNFORMED)
                        continue;
                ret = __find_kallsyms_symbol_value(mod, name);
                if (ret)
                        return ret;
        }
        return 0;
}

/* Look for this name: can be of form module:name. */
unsigned long module_kallsyms_lookup_name(const char *name)
{
        /* Don't lock: we're in enough trouble already. */
        guard(rcu)();
        return __module_kallsyms_lookup_name(name);
}

unsigned long find_kallsyms_symbol_value(struct module *mod, const char *name)
{
        guard(rcu)();
        return __find_kallsyms_symbol_value(mod, name);
}

int module_kallsyms_on_each_symbol(const char *modname,
                                   int (*fn)(void *, const char *, unsigned long),
                                   void *data)
{
        struct module *mod;
        unsigned int i;
        int ret = 0;

        mutex_lock(&module_mutex);
        list_for_each_entry(mod, &modules, list) {
                struct mod_kallsyms *kallsyms;

                if (mod->state == MODULE_STATE_UNFORMED)
                        continue;

                if (modname && strcmp(modname, mod->name))
                        continue;

                kallsyms = rcu_dereference_check(mod->kallsyms,
                                                 lockdep_is_held(&module_mutex));

                for (i = 0; i < kallsyms->num_symtab; i++) {
                        const Elf_Sym *sym = &kallsyms->symtab[i];

                        if (sym->st_shndx == SHN_UNDEF)
                                continue;

                        ret = fn(data, kallsyms_symbol_name(kallsyms, i),
                                 kallsyms_symbol_value(sym));
                        if (ret != 0)
                                goto out;
                }

                /*
                 * The given module is found, the subsequent modules do not
                 * need to be compared.
                 */
                if (modname)
                        break;
        }
out:
        mutex_unlock(&module_mutex);
        return ret;
}