| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | /* |
| 3 | * Module kallsyms support |
| 4 | * |
| 5 | * Copyright (C) 2010 Rusty Russell |
| 6 | */ |
| 7 | |
| 8 | #include <linux/module.h> |
| Tiezhu Yang | 987d2e0 | 2023-03-31 17:15:52 +0800 | [diff] [blame] | 9 | #include <linux/module_symbol.h> |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 10 | #include <linux/kallsyms.h> |
| 11 | #include <linux/buildid.h> |
| 12 | #include <linux/bsearch.h> |
| 13 | #include "internal.h" |
| 14 | |
| 15 | /* Lookup exported symbol in given range of kernel_symbols */ |
| 16 | static const struct kernel_symbol *lookup_exported_symbol(const char *name, |
| 17 | const struct kernel_symbol *start, |
| 18 | const struct kernel_symbol *stop) |
| 19 | { |
| 20 | return bsearch(name, start, stop - start, |
| 21 | sizeof(struct kernel_symbol), cmp_name); |
| 22 | } |
| 23 | |
| 24 | static int is_exported(const char *name, unsigned long value, |
| 25 | const struct module *mod) |
| 26 | { |
| 27 | const struct kernel_symbol *ks; |
| 28 | |
| 29 | if (!mod) |
| 30 | ks = lookup_exported_symbol(name, __start___ksymtab, __stop___ksymtab); |
| 31 | else |
| 32 | ks = lookup_exported_symbol(name, mod->syms, mod->syms + mod->num_syms); |
| 33 | |
| 34 | return ks && kernel_symbol_value(ks) == value; |
| 35 | } |
| 36 | |
| 37 | /* As per nm */ |
| 38 | static char elf_type(const Elf_Sym *sym, const struct load_info *info) |
| 39 | { |
| 40 | const Elf_Shdr *sechdrs = info->sechdrs; |
| 41 | |
| 42 | if (ELF_ST_BIND(sym->st_info) == STB_WEAK) { |
| 43 | if (ELF_ST_TYPE(sym->st_info) == STT_OBJECT) |
| 44 | return 'v'; |
| 45 | else |
| 46 | return 'w'; |
| 47 | } |
| 48 | if (sym->st_shndx == SHN_UNDEF) |
| 49 | return 'U'; |
| 50 | if (sym->st_shndx == SHN_ABS || sym->st_shndx == info->index.pcpu) |
| 51 | return 'a'; |
| 52 | if (sym->st_shndx >= SHN_LORESERVE) |
| 53 | return '?'; |
| 54 | if (sechdrs[sym->st_shndx].sh_flags & SHF_EXECINSTR) |
| 55 | return 't'; |
| 56 | if (sechdrs[sym->st_shndx].sh_flags & SHF_ALLOC && |
| 57 | sechdrs[sym->st_shndx].sh_type != SHT_NOBITS) { |
| 58 | if (!(sechdrs[sym->st_shndx].sh_flags & SHF_WRITE)) |
| 59 | return 'r'; |
| 60 | else if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL) |
| 61 | return 'g'; |
| 62 | else |
| 63 | return 'd'; |
| 64 | } |
| 65 | if (sechdrs[sym->st_shndx].sh_type == SHT_NOBITS) { |
| 66 | if (sechdrs[sym->st_shndx].sh_flags & ARCH_SHF_SMALL) |
| 67 | return 's'; |
| 68 | else |
| 69 | return 'b'; |
| 70 | } |
| 71 | if (strstarts(info->secstrings + sechdrs[sym->st_shndx].sh_name, |
| 72 | ".debug")) { |
| 73 | return 'n'; |
| 74 | } |
| 75 | return '?'; |
| 76 | } |
| 77 | |
| 78 | static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs, |
| 79 | unsigned int shnum, unsigned int pcpundx) |
| 80 | { |
| 81 | const Elf_Shdr *sec; |
| Song Liu | ac3b432 | 2023-02-06 16:28:02 -0800 | [diff] [blame] | 82 | enum mod_mem_type type; |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 83 | |
| 84 | if (src->st_shndx == SHN_UNDEF || |
| 85 | src->st_shndx >= shnum || |
| 86 | !src->st_name) |
| 87 | return false; |
| 88 | |
| 89 | #ifdef CONFIG_KALLSYMS_ALL |
| 90 | if (src->st_shndx == pcpundx) |
| 91 | return true; |
| 92 | #endif |
| 93 | |
| 94 | sec = sechdrs + src->st_shndx; |
| Song Liu | ac3b432 | 2023-02-06 16:28:02 -0800 | [diff] [blame] | 95 | type = sec->sh_entsize >> SH_ENTSIZE_TYPE_SHIFT; |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 96 | if (!(sec->sh_flags & SHF_ALLOC) |
| 97 | #ifndef CONFIG_KALLSYMS_ALL |
| 98 | || !(sec->sh_flags & SHF_EXECINSTR) |
| 99 | #endif |
| Song Liu | ac3b432 | 2023-02-06 16:28:02 -0800 | [diff] [blame] | 100 | || mod_mem_type_is_init(type)) |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 101 | return false; |
| 102 | |
| 103 | return true; |
| 104 | } |
| 105 | |
| 106 | /* |
| 107 | * We only allocate and copy the strings needed by the parts of symtab |
| 108 | * we keep. This is simple, but has the effect of making multiple |
| 109 | * copies of duplicates. We could be more sophisticated, see |
| 110 | * linux-kernel thread starting with |
| 111 | * <73defb5e4bca04a6431392cc341112b1@localhost>. |
| 112 | */ |
| 113 | void layout_symtab(struct module *mod, struct load_info *info) |
| 114 | { |
| 115 | Elf_Shdr *symsect = info->sechdrs + info->index.sym; |
| 116 | Elf_Shdr *strsect = info->sechdrs + info->index.str; |
| 117 | const Elf_Sym *src; |
| 118 | unsigned int i, nsrc, ndst, strtab_size = 0; |
| Song Liu | ac3b432 | 2023-02-06 16:28:02 -0800 | [diff] [blame] | 119 | struct module_memory *mod_mem_data = &mod->mem[MOD_DATA]; |
| 120 | struct module_memory *mod_mem_init_data = &mod->mem[MOD_INIT_DATA]; |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 121 | |
| 122 | /* Put symbol section at end of init part of module. */ |
| 123 | symsect->sh_flags |= SHF_ALLOC; |
| Song Liu | ac3b432 | 2023-02-06 16:28:02 -0800 | [diff] [blame] | 124 | symsect->sh_entsize = module_get_offset_and_type(mod, MOD_INIT_DATA, |
| 125 | symsect, info->index.sym); |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 126 | pr_debug("\t%s\n", info->secstrings + symsect->sh_name); |
| 127 | |
| 128 | src = (void *)info->hdr + symsect->sh_offset; |
| 129 | nsrc = symsect->sh_size / sizeof(*src); |
| 130 | |
| 131 | /* Compute total space required for the core symbols' strtab. */ |
| 132 | for (ndst = i = 0; i < nsrc; i++) { |
| 133 | if (i == 0 || is_livepatch_module(mod) || |
| 134 | is_core_symbol(src + i, info->sechdrs, info->hdr->e_shnum, |
| 135 | info->index.pcpu)) { |
| 136 | strtab_size += strlen(&info->strtab[src[i].st_name]) + 1; |
| 137 | ndst++; |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | /* Append room for core symbols at end of core part. */ |
| Song Liu | ac3b432 | 2023-02-06 16:28:02 -0800 | [diff] [blame] | 142 | info->symoffs = ALIGN(mod_mem_data->size, symsect->sh_addralign ?: 1); |
| 143 | info->stroffs = mod_mem_data->size = info->symoffs + ndst * sizeof(Elf_Sym); |
| 144 | mod_mem_data->size += strtab_size; |
| Adrian Hunter | 35adf9a | 2022-07-01 12:44:03 +0300 | [diff] [blame] | 145 | /* Note add_kallsyms() computes strtab_size as core_typeoffs - stroffs */ |
| Song Liu | ac3b432 | 2023-02-06 16:28:02 -0800 | [diff] [blame] | 146 | info->core_typeoffs = mod_mem_data->size; |
| 147 | mod_mem_data->size += ndst * sizeof(char); |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 148 | |
| 149 | /* Put string table section at end of init part of module. */ |
| 150 | strsect->sh_flags |= SHF_ALLOC; |
| Song Liu | ac3b432 | 2023-02-06 16:28:02 -0800 | [diff] [blame] | 151 | strsect->sh_entsize = module_get_offset_and_type(mod, MOD_INIT_DATA, |
| 152 | strsect, info->index.str); |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 153 | pr_debug("\t%s\n", info->secstrings + strsect->sh_name); |
| 154 | |
| 155 | /* We'll tack temporary mod_kallsyms on the end. */ |
| Song Liu | ac3b432 | 2023-02-06 16:28:02 -0800 | [diff] [blame] | 156 | mod_mem_init_data->size = ALIGN(mod_mem_init_data->size, |
| 157 | __alignof__(struct mod_kallsyms)); |
| 158 | info->mod_kallsyms_init_off = mod_mem_init_data->size; |
| 159 | |
| 160 | mod_mem_init_data->size += sizeof(struct mod_kallsyms); |
| 161 | info->init_typeoffs = mod_mem_init_data->size; |
| 162 | mod_mem_init_data->size += nsrc * sizeof(char); |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 163 | } |
| 164 | |
| 165 | /* |
| 166 | * We use the full symtab and strtab which layout_symtab arranged to |
| 167 | * be appended to the init section. Later we switch to the cut-down |
| 168 | * core-only ones. |
| 169 | */ |
| 170 | void add_kallsyms(struct module *mod, const struct load_info *info) |
| 171 | { |
| 172 | unsigned int i, ndst; |
| 173 | const Elf_Sym *src; |
| 174 | Elf_Sym *dst; |
| 175 | char *s; |
| 176 | Elf_Shdr *symsec = &info->sechdrs[info->index.sym]; |
| Adrian Hunter | 35adf9a | 2022-07-01 12:44:03 +0300 | [diff] [blame] | 177 | unsigned long strtab_size; |
| Song Liu | ac3b432 | 2023-02-06 16:28:02 -0800 | [diff] [blame] | 178 | void *data_base = mod->mem[MOD_DATA].base; |
| 179 | void *init_data_base = mod->mem[MOD_INIT_DATA].base; |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 180 | |
| 181 | /* Set up to point into init section. */ |
| Song Liu | ac3b432 | 2023-02-06 16:28:02 -0800 | [diff] [blame] | 182 | mod->kallsyms = (void __rcu *)init_data_base + |
| Aaron Tomlin | 08126db | 2022-03-22 14:03:40 +0000 | [diff] [blame] | 183 | info->mod_kallsyms_init_off; |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 184 | |
| Aaron Tomlin | e69a661 | 2022-07-11 18:17:19 +0100 | [diff] [blame] | 185 | rcu_read_lock(); |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 186 | /* The following is safe since this pointer cannot change */ |
| Aaron Tomlin | e69a661 | 2022-07-11 18:17:19 +0100 | [diff] [blame] | 187 | rcu_dereference(mod->kallsyms)->symtab = (void *)symsec->sh_addr; |
| 188 | rcu_dereference(mod->kallsyms)->num_symtab = symsec->sh_size / sizeof(Elf_Sym); |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 189 | /* Make sure we get permanent strtab: don't use info->strtab. */ |
| Aaron Tomlin | e69a661 | 2022-07-11 18:17:19 +0100 | [diff] [blame] | 190 | rcu_dereference(mod->kallsyms)->strtab = |
| Aaron Tomlin | 08126db | 2022-03-22 14:03:40 +0000 | [diff] [blame] | 191 | (void *)info->sechdrs[info->index.str].sh_addr; |
| Song Liu | ac3b432 | 2023-02-06 16:28:02 -0800 | [diff] [blame] | 192 | rcu_dereference(mod->kallsyms)->typetab = init_data_base + info->init_typeoffs; |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 193 | |
| 194 | /* |
| 195 | * Now populate the cut down core kallsyms for after init |
| 196 | * and set types up while we still have access to sections. |
| 197 | */ |
| Song Liu | ac3b432 | 2023-02-06 16:28:02 -0800 | [diff] [blame] | 198 | mod->core_kallsyms.symtab = dst = data_base + info->symoffs; |
| 199 | mod->core_kallsyms.strtab = s = data_base + info->stroffs; |
| 200 | mod->core_kallsyms.typetab = data_base + info->core_typeoffs; |
| Adrian Hunter | 35adf9a | 2022-07-01 12:44:03 +0300 | [diff] [blame] | 201 | strtab_size = info->core_typeoffs - info->stroffs; |
| Aaron Tomlin | e69a661 | 2022-07-11 18:17:19 +0100 | [diff] [blame] | 202 | src = rcu_dereference(mod->kallsyms)->symtab; |
| 203 | for (ndst = i = 0; i < rcu_dereference(mod->kallsyms)->num_symtab; i++) { |
| 204 | rcu_dereference(mod->kallsyms)->typetab[i] = elf_type(src + i, info); |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 205 | if (i == 0 || is_livepatch_module(mod) || |
| 206 | is_core_symbol(src + i, info->sechdrs, info->hdr->e_shnum, |
| 207 | info->index.pcpu)) { |
| Adrian Hunter | 35adf9a | 2022-07-01 12:44:03 +0300 | [diff] [blame] | 208 | ssize_t ret; |
| 209 | |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 210 | mod->core_kallsyms.typetab[ndst] = |
| Aaron Tomlin | e69a661 | 2022-07-11 18:17:19 +0100 | [diff] [blame] | 211 | rcu_dereference(mod->kallsyms)->typetab[i]; |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 212 | dst[ndst] = src[i]; |
| 213 | dst[ndst++].st_name = s - mod->core_kallsyms.strtab; |
| Adrian Hunter | 35adf9a | 2022-07-01 12:44:03 +0300 | [diff] [blame] | 214 | ret = strscpy(s, |
| Aaron Tomlin | e69a661 | 2022-07-11 18:17:19 +0100 | [diff] [blame] | 215 | &rcu_dereference(mod->kallsyms)->strtab[src[i].st_name], |
| Adrian Hunter | 35adf9a | 2022-07-01 12:44:03 +0300 | [diff] [blame] | 216 | strtab_size); |
| 217 | if (ret < 0) |
| 218 | break; |
| 219 | s += ret + 1; |
| 220 | strtab_size -= ret + 1; |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 221 | } |
| 222 | } |
| Aaron Tomlin | e69a661 | 2022-07-11 18:17:19 +0100 | [diff] [blame] | 223 | rcu_read_unlock(); |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 224 | mod->core_kallsyms.num_symtab = ndst; |
| 225 | } |
| 226 | |
| 227 | #if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID) |
| 228 | void init_build_id(struct module *mod, const struct load_info *info) |
| 229 | { |
| 230 | const Elf_Shdr *sechdr; |
| 231 | unsigned int i; |
| 232 | |
| 233 | for (i = 0; i < info->hdr->e_shnum; i++) { |
| 234 | sechdr = &info->sechdrs[i]; |
| 235 | if (!sect_empty(sechdr) && sechdr->sh_type == SHT_NOTE && |
| 236 | !build_id_parse_buf((void *)sechdr->sh_addr, mod->build_id, |
| 237 | sechdr->sh_size)) |
| 238 | break; |
| 239 | } |
| 240 | } |
| 241 | #else |
| 242 | void init_build_id(struct module *mod, const struct load_info *info) |
| 243 | { |
| 244 | } |
| 245 | #endif |
| 246 | |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 247 | static const char *kallsyms_symbol_name(struct mod_kallsyms *kallsyms, unsigned int symnum) |
| 248 | { |
| 249 | return kallsyms->strtab + kallsyms->symtab[symnum].st_name; |
| 250 | } |
| 251 | |
| 252 | /* |
| 253 | * Given a module and address, find the corresponding symbol and return its name |
| 254 | * while providing its size and offset if needed. |
| 255 | */ |
| 256 | static const char *find_kallsyms_symbol(struct module *mod, |
| 257 | unsigned long addr, |
| 258 | unsigned long *size, |
| 259 | unsigned long *offset) |
| 260 | { |
| 261 | unsigned int i, best = 0; |
| 262 | unsigned long nextval, bestval; |
| 263 | struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms); |
| Song Liu | ac3b432 | 2023-02-06 16:28:02 -0800 | [diff] [blame] | 264 | struct module_memory *mod_mem; |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 265 | |
| 266 | /* At worse, next value is at end of module */ |
| 267 | if (within_module_init(addr, mod)) |
| Song Liu | ac3b432 | 2023-02-06 16:28:02 -0800 | [diff] [blame] | 268 | mod_mem = &mod->mem[MOD_INIT_TEXT]; |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 269 | else |
| Song Liu | ac3b432 | 2023-02-06 16:28:02 -0800 | [diff] [blame] | 270 | mod_mem = &mod->mem[MOD_TEXT]; |
| 271 | |
| 272 | nextval = (unsigned long)mod_mem->base + mod_mem->size; |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 273 | |
| 274 | bestval = kallsyms_symbol_value(&kallsyms->symtab[best]); |
| 275 | |
| 276 | /* |
| 277 | * Scan for closest preceding symbol, and next symbol. (ELF |
| 278 | * starts real symbols at 1). |
| 279 | */ |
| 280 | for (i = 1; i < kallsyms->num_symtab; i++) { |
| 281 | const Elf_Sym *sym = &kallsyms->symtab[i]; |
| 282 | unsigned long thisval = kallsyms_symbol_value(sym); |
| 283 | |
| 284 | if (sym->st_shndx == SHN_UNDEF) |
| 285 | continue; |
| 286 | |
| 287 | /* |
| 288 | * We ignore unnamed symbols: they're uninformative |
| 289 | * and inserted at a whim. |
| 290 | */ |
| 291 | if (*kallsyms_symbol_name(kallsyms, i) == '\0' || |
| Tiezhu Yang | 0a3bf86 | 2023-03-31 17:15:53 +0800 | [diff] [blame] | 292 | is_mapping_symbol(kallsyms_symbol_name(kallsyms, i))) |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 293 | continue; |
| 294 | |
| 295 | if (thisval <= addr && thisval > bestval) { |
| 296 | best = i; |
| 297 | bestval = thisval; |
| 298 | } |
| 299 | if (thisval > addr && thisval < nextval) |
| 300 | nextval = thisval; |
| 301 | } |
| 302 | |
| 303 | if (!best) |
| 304 | return NULL; |
| 305 | |
| 306 | if (size) |
| 307 | *size = nextval - bestval; |
| 308 | if (offset) |
| 309 | *offset = addr - bestval; |
| 310 | |
| 311 | return kallsyms_symbol_name(kallsyms, best); |
| 312 | } |
| 313 | |
| 314 | void * __weak dereference_module_function_descriptor(struct module *mod, |
| 315 | void *ptr) |
| 316 | { |
| 317 | return ptr; |
| 318 | } |
| 319 | |
| 320 | /* |
| 321 | * For kallsyms to ask for address resolution. NULL means not found. Careful |
| 322 | * not to lock to avoid deadlock on oopses, simply disable preemption. |
| 323 | */ |
| Arnd Bergmann | 7e1f4eb | 2024-04-04 12:04:54 +0200 | [diff] [blame] | 324 | int module_address_lookup(unsigned long addr, |
| 325 | unsigned long *size, |
| 326 | unsigned long *offset, |
| 327 | char **modname, |
| 328 | const unsigned char **modbuildid, |
| 329 | char *namebuf) |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 330 | { |
| Arnd Bergmann | 7e1f4eb | 2024-04-04 12:04:54 +0200 | [diff] [blame] | 331 | const char *sym; |
| 332 | int ret = 0; |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 333 | struct module *mod; |
| 334 | |
| 335 | preempt_disable(); |
| 336 | mod = __module_address(addr); |
| 337 | if (mod) { |
| 338 | if (modname) |
| 339 | *modname = mod->name; |
| 340 | if (modbuildid) { |
| 341 | #if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID) |
| 342 | *modbuildid = mod->build_id; |
| 343 | #else |
| 344 | *modbuildid = NULL; |
| 345 | #endif |
| 346 | } |
| 347 | |
| Arnd Bergmann | 7e1f4eb | 2024-04-04 12:04:54 +0200 | [diff] [blame] | 348 | sym = find_kallsyms_symbol(mod, addr, size, offset); |
| 349 | |
| 350 | if (sym) |
| 351 | ret = strscpy(namebuf, sym, KSYM_NAME_LEN); |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 352 | } |
| 353 | preempt_enable(); |
| 354 | |
| 355 | return ret; |
| 356 | } |
| 357 | |
| 358 | int lookup_module_symbol_name(unsigned long addr, char *symname) |
| 359 | { |
| 360 | struct module *mod; |
| 361 | |
| 362 | preempt_disable(); |
| 363 | list_for_each_entry_rcu(mod, &modules, list) { |
| 364 | if (mod->state == MODULE_STATE_UNFORMED) |
| 365 | continue; |
| 366 | if (within_module(addr, mod)) { |
| 367 | const char *sym; |
| 368 | |
| 369 | sym = find_kallsyms_symbol(mod, addr, NULL, NULL); |
| 370 | if (!sym) |
| 371 | goto out; |
| 372 | |
| 373 | strscpy(symname, sym, KSYM_NAME_LEN); |
| 374 | preempt_enable(); |
| 375 | return 0; |
| 376 | } |
| 377 | } |
| 378 | out: |
| 379 | preempt_enable(); |
| 380 | return -ERANGE; |
| 381 | } |
| 382 | |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 383 | int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, |
| 384 | char *name, char *module_name, int *exported) |
| 385 | { |
| 386 | struct module *mod; |
| 387 | |
| 388 | preempt_disable(); |
| 389 | list_for_each_entry_rcu(mod, &modules, list) { |
| 390 | struct mod_kallsyms *kallsyms; |
| 391 | |
| 392 | if (mod->state == MODULE_STATE_UNFORMED) |
| 393 | continue; |
| 394 | kallsyms = rcu_dereference_sched(mod->kallsyms); |
| 395 | if (symnum < kallsyms->num_symtab) { |
| 396 | const Elf_Sym *sym = &kallsyms->symtab[symnum]; |
| 397 | |
| 398 | *value = kallsyms_symbol_value(sym); |
| 399 | *type = kallsyms->typetab[symnum]; |
| 400 | strscpy(name, kallsyms_symbol_name(kallsyms, symnum), KSYM_NAME_LEN); |
| 401 | strscpy(module_name, mod->name, MODULE_NAME_LEN); |
| 402 | *exported = is_exported(name, *value, mod); |
| 403 | preempt_enable(); |
| 404 | return 0; |
| 405 | } |
| 406 | symnum -= kallsyms->num_symtab; |
| 407 | } |
| 408 | preempt_enable(); |
| 409 | return -ERANGE; |
| 410 | } |
| 411 | |
| 412 | /* Given a module and name of symbol, find and return the symbol's value */ |
| Jiri Olsa | d099f59 | 2023-04-04 00:02:54 +0200 | [diff] [blame] | 413 | static unsigned long __find_kallsyms_symbol_value(struct module *mod, const char *name) |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 414 | { |
| 415 | unsigned int i; |
| 416 | struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms); |
| 417 | |
| 418 | for (i = 0; i < kallsyms->num_symtab; i++) { |
| 419 | const Elf_Sym *sym = &kallsyms->symtab[i]; |
| 420 | |
| 421 | if (strcmp(name, kallsyms_symbol_name(kallsyms, i)) == 0 && |
| 422 | sym->st_shndx != SHN_UNDEF) |
| 423 | return kallsyms_symbol_value(sym); |
| 424 | } |
| 425 | return 0; |
| 426 | } |
| 427 | |
| Christophe Leroy | 07ade45 | 2022-06-13 08:02:02 +0200 | [diff] [blame] | 428 | static unsigned long __module_kallsyms_lookup_name(const char *name) |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 429 | { |
| 430 | struct module *mod; |
| 431 | char *colon; |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 432 | |
| Christophe Leroy | ecc726f | 2022-06-13 08:02:01 +0200 | [diff] [blame] | 433 | colon = strnchr(name, MODULE_NAME_LEN, ':'); |
| 434 | if (colon) { |
| 435 | mod = find_module_all(name, colon - name, false); |
| 436 | if (mod) |
| Jiri Olsa | d099f59 | 2023-04-04 00:02:54 +0200 | [diff] [blame] | 437 | return __find_kallsyms_symbol_value(mod, colon + 1); |
| Christophe Leroy | 07ade45 | 2022-06-13 08:02:02 +0200 | [diff] [blame] | 438 | return 0; |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 439 | } |
| Christophe Leroy | 07ade45 | 2022-06-13 08:02:02 +0200 | [diff] [blame] | 440 | |
| 441 | list_for_each_entry_rcu(mod, &modules, list) { |
| 442 | unsigned long ret; |
| 443 | |
| 444 | if (mod->state == MODULE_STATE_UNFORMED) |
| 445 | continue; |
| Jiri Olsa | d099f59 | 2023-04-04 00:02:54 +0200 | [diff] [blame] | 446 | ret = __find_kallsyms_symbol_value(mod, name); |
| Christophe Leroy | 07ade45 | 2022-06-13 08:02:02 +0200 | [diff] [blame] | 447 | if (ret) |
| 448 | return ret; |
| 449 | } |
| 450 | return 0; |
| 451 | } |
| 452 | |
| 453 | /* Look for this name: can be of form module:name. */ |
| 454 | unsigned long module_kallsyms_lookup_name(const char *name) |
| 455 | { |
| 456 | unsigned long ret; |
| 457 | |
| 458 | /* Don't lock: we're in enough trouble already. */ |
| 459 | preempt_disable(); |
| 460 | ret = __module_kallsyms_lookup_name(name); |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 461 | preempt_enable(); |
| 462 | return ret; |
| 463 | } |
| 464 | |
| Jiri Olsa | d099f59 | 2023-04-04 00:02:54 +0200 | [diff] [blame] | 465 | unsigned long find_kallsyms_symbol_value(struct module *mod, const char *name) |
| 466 | { |
| 467 | unsigned long ret; |
| 468 | |
| 469 | preempt_disable(); |
| 470 | ret = __find_kallsyms_symbol_value(mod, name); |
| 471 | preempt_enable(); |
| 472 | return ret; |
| 473 | } |
| 474 | |
| Zhen Lei | 07cc2c9 | 2023-01-16 11:10:07 +0100 | [diff] [blame] | 475 | int module_kallsyms_on_each_symbol(const char *modname, |
| Zhen Lei | 3703bd5 | 2023-03-08 15:38:46 +0800 | [diff] [blame] | 476 | int (*fn)(void *, const char *, unsigned long), |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 477 | void *data) |
| 478 | { |
| 479 | struct module *mod; |
| 480 | unsigned int i; |
| 481 | int ret = 0; |
| 482 | |
| 483 | mutex_lock(&module_mutex); |
| 484 | list_for_each_entry(mod, &modules, list) { |
| Aaron Tomlin | 08126db | 2022-03-22 14:03:40 +0000 | [diff] [blame] | 485 | struct mod_kallsyms *kallsyms; |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 486 | |
| 487 | if (mod->state == MODULE_STATE_UNFORMED) |
| 488 | continue; |
| Aaron Tomlin | 08126db | 2022-03-22 14:03:40 +0000 | [diff] [blame] | 489 | |
| Zhen Lei | 07cc2c9 | 2023-01-16 11:10:07 +0100 | [diff] [blame] | 490 | if (modname && strcmp(modname, mod->name)) |
| 491 | continue; |
| 492 | |
| Aaron Tomlin | 08126db | 2022-03-22 14:03:40 +0000 | [diff] [blame] | 493 | /* Use rcu_dereference_sched() to remain compliant with the sparse tool */ |
| 494 | preempt_disable(); |
| 495 | kallsyms = rcu_dereference_sched(mod->kallsyms); |
| 496 | preempt_enable(); |
| 497 | |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 498 | for (i = 0; i < kallsyms->num_symtab; i++) { |
| 499 | const Elf_Sym *sym = &kallsyms->symtab[i]; |
| 500 | |
| 501 | if (sym->st_shndx == SHN_UNDEF) |
| 502 | continue; |
| 503 | |
| 504 | ret = fn(data, kallsyms_symbol_name(kallsyms, i), |
| Zhen Lei | 3703bd5 | 2023-03-08 15:38:46 +0800 | [diff] [blame] | 505 | kallsyms_symbol_value(sym)); |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 506 | if (ret != 0) |
| 507 | goto out; |
| 508 | } |
| Zhen Lei | 07cc2c9 | 2023-01-16 11:10:07 +0100 | [diff] [blame] | 509 | |
| 510 | /* |
| 511 | * The given module is found, the subsequent modules do not |
| 512 | * need to be compared. |
| 513 | */ |
| 514 | if (modname) |
| 515 | break; |
| Aaron Tomlin | 91fb02f | 2022-03-22 14:03:39 +0000 | [diff] [blame] | 516 | } |
| 517 | out: |
| 518 | mutex_unlock(&module_mutex); |
| 519 | return ret; |
| 520 | } |