| 1 | |
| 2 | |
| 3 | * Module kallsyms support
|
| 4 | *
|
| 5 | * Copyright (C) 2010 Rusty Russell
|
| 6 |
|
| 7 |
|
| 8 | #include <linux/module.h>
|
| 9 | #include <linux/module_symbol.h>
|
| 10 | #include <linux/kallsyms.h>
|
| 11 | #include <linux/buildid.h>
|
| 12 | #include <linux/bsearch.h>
|
| 13 | #include "internal.h"
|
| 14 |
|
| 15 |
|
| 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 |
|
| 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;
|
| 82 | enum mod_mem_type type;
|
| 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;
|
| 95 | type = sec->sh_entsize >> SH_ENTSIZE_TYPE_SHIFT;
|
| 96 | if (!(sec->sh_flags & SHF_ALLOC)
|
| 97 | #ifndef CONFIG_KALLSYMS_ALL
|
| 98 | || !(sec->sh_flags & SHF_EXECINSTR)
|
| 99 | #endif
|
| 100 | || mod_mem_type_is_init(type))
|
| 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;
|
| 119 | struct module_memory *mod_mem_data = &mod->mem[MOD_DATA];
|
| 120 | struct module_memory *mod_mem_init_data = &mod->mem[MOD_INIT_DATA];
|
| 121 |
|
| 122 |
|
| 123 | symsect->sh_flags |= SHF_ALLOC;
|
| 124 | symsect->sh_entsize = module_get_offset_and_type(mod, MOD_INIT_DATA,
|
| 125 | symsect, info->index.sym);
|
| 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 |
|
| 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 |
|
| 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;
|
| 145 |
|
| 146 | info->core_typeoffs = mod_mem_data->size;
|
| 147 | mod_mem_data->size += ndst * sizeof(char);
|
| 148 |
|
| 149 |
|
| 150 | strsect->sh_flags |= SHF_ALLOC;
|
| 151 | strsect->sh_entsize = module_get_offset_and_type(mod, MOD_INIT_DATA,
|
| 152 | strsect, info->index.str);
|
| 153 | pr_debug("\t%s\n", info->secstrings + strsect->sh_name);
|
| 154 |
|
| 155 |
|
| 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);
|
| 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];
|
| 177 | unsigned long strtab_size;
|
| 178 | void *data_base = mod->mem[MOD_DATA].base;
|
| 179 | void *init_data_base = mod->mem[MOD_INIT_DATA].base;
|
| 180 | struct mod_kallsyms *kallsyms;
|
| 181 |
|
| 182 | kallsyms = init_data_base + info->mod_kallsyms_init_off;
|
| 183 |
|
| 184 | kallsyms->symtab = (void *)symsec->sh_addr;
|
| 185 | kallsyms->num_symtab = symsec->sh_size / sizeof(Elf_Sym);
|
| 186 |
|
| 187 | kallsyms->strtab = (void *)info->sechdrs[info->index.str].sh_addr;
|
| 188 | kallsyms->typetab = init_data_base + info->init_typeoffs;
|
| 189 |
|
| 190 | |
| 191 | * Now populate the cut down core kallsyms for after init
|
| 192 | * and set types up while we still have access to sections.
|
| 193 |
|
| 194 | mod->core_kallsyms.symtab = dst = data_base + info->symoffs;
|
| 195 | mod->core_kallsyms.strtab = s = data_base + info->stroffs;
|
| 196 | mod->core_kallsyms.typetab = data_base + info->core_typeoffs;
|
| 197 | strtab_size = info->core_typeoffs - info->stroffs;
|
| 198 | src = kallsyms->symtab;
|
| 199 | for (ndst = i = 0; i < kallsyms->num_symtab; i++) {
|
| 200 | kallsyms->typetab[i] = elf_type(src + i, info);
|
| 201 | if (i == 0 || is_livepatch_module(mod) ||
|
| 202 | is_core_symbol(src + i, info->sechdrs, info->hdr->e_shnum,
|
| 203 | info->index.pcpu)) {
|
| 204 | ssize_t ret;
|
| 205 |
|
| 206 | mod->core_kallsyms.typetab[ndst] =
|
| 207 | kallsyms->typetab[i];
|
| 208 | dst[ndst] = src[i];
|
| 209 | dst[ndst++].st_name = s - mod->core_kallsyms.strtab;
|
| 210 | ret = strscpy(s, &kallsyms->strtab[src[i].st_name],
|
| 211 | strtab_size);
|
| 212 | if (ret < 0)
|
| 213 | break;
|
| 214 | s += ret + 1;
|
| 215 | strtab_size -= ret + 1;
|
| 216 | }
|
| 217 | }
|
| 218 |
|
| 219 |
|
| 220 | rcu_assign_pointer(mod->kallsyms, kallsyms);
|
| 221 | mod->core_kallsyms.num_symtab = ndst;
|
| 222 | }
|
| 223 |
|
| 224 | #if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID)
|
| 225 | void init_build_id(struct module *mod, const struct load_info *info)
|
| 226 | {
|
| 227 | const Elf_Shdr *sechdr;
|
| 228 | unsigned int i;
|
| 229 |
|
| 230 | for (i = 0; i < info->hdr->e_shnum; i++) {
|
| 231 | sechdr = &info->sechdrs[i];
|
| 232 | if (!sect_empty(sechdr) && sechdr->sh_type == SHT_NOTE &&
|
| 233 | !build_id_parse_buf((void *)sechdr->sh_addr, mod->build_id,
|
| 234 | sechdr->sh_size))
|
| 235 | break;
|
| 236 | }
|
| 237 | }
|
| 238 | #else
|
| 239 | void init_build_id(struct module *mod, const struct load_info *info)
|
| 240 | {
|
| 241 | }
|
| 242 | #endif
|
| 243 |
|
| 244 | static const char *kallsyms_symbol_name(struct mod_kallsyms *kallsyms, unsigned int symnum)
|
| 245 | {
|
| 246 | return kallsyms->strtab + kallsyms->symtab[symnum].st_name;
|
| 247 | }
|
| 248 |
|
| 249 | |
| 250 | * Given a module and address, find the corresponding symbol and return its name
|
| 251 | * while providing its size and offset if needed.
|
| 252 |
|
| 253 | static const char *find_kallsyms_symbol(struct module *mod,
|
| 254 | unsigned long addr,
|
| 255 | unsigned long *size,
|
| 256 | unsigned long *offset)
|
| 257 | {
|
| 258 | unsigned int i, best = 0;
|
| 259 | unsigned long nextval, bestval;
|
| 260 | struct mod_kallsyms *kallsyms = rcu_dereference(mod->kallsyms);
|
| 261 | struct module_memory *mod_mem = NULL;
|
| 262 |
|
| 263 | for_each_mod_mem_type(type) {
|
| 264 | #ifndef CONFIG_KALLSYMS_ALL
|
| 265 | if (!mod_mem_type_is_text(type))
|
| 266 | continue;
|
| 267 | #endif
|
| 268 | if (within_module_mem_type(addr, mod, type)) {
|
| 269 | mod_mem = &mod->mem[type];
|
| 270 | break;
|
| 271 | }
|
| 272 | }
|
| 273 |
|
| 274 | if (!mod_mem)
|
| 275 | return NULL;
|
| 276 |
|
| 277 |
|
| 278 | nextval = (unsigned long)mod_mem->base + mod_mem->size;
|
| 279 | bestval = (unsigned long)mod_mem->base - 1;
|
| 280 |
|
| 281 | |
| 282 | * Scan for closest preceding symbol, and next symbol. (ELF
|
| 283 | * starts real symbols at 1).
|
| 284 |
|
| 285 | for (i = 1; i < kallsyms->num_symtab; i++) {
|
| 286 | const Elf_Sym *sym = &kallsyms->symtab[i];
|
| 287 | unsigned long thisval = kallsyms_symbol_value(sym);
|
| 288 |
|
| 289 | if (sym->st_shndx == SHN_UNDEF)
|
| 290 | continue;
|
| 291 |
|
| 292 | |
| 293 | * We ignore unnamed symbols: they're uninformative
|
| 294 | * and inserted at a whim.
|
| 295 |
|
| 296 | if (*kallsyms_symbol_name(kallsyms, i) == '\0' ||
|
| 297 | is_mapping_symbol(kallsyms_symbol_name(kallsyms, i)))
|
| 298 | continue;
|
| 299 |
|
| 300 | if (thisval <= addr && thisval > bestval) {
|
| 301 | best = i;
|
| 302 | bestval = thisval;
|
| 303 | }
|
| 304 | if (thisval > addr && thisval < nextval)
|
| 305 | nextval = thisval;
|
| 306 | }
|
| 307 |
|
| 308 | if (!best)
|
| 309 | return NULL;
|
| 310 |
|
| 311 | if (size)
|
| 312 | *size = nextval - bestval;
|
| 313 | if (offset)
|
| 314 | *offset = addr - bestval;
|
| 315 |
|
| 316 | return kallsyms_symbol_name(kallsyms, best);
|
| 317 | }
|
| 318 |
|
| 319 | void * __weak dereference_module_function_descriptor(struct module *mod,
|
| 320 | void *ptr)
|
| 321 | {
|
| 322 | return ptr;
|
| 323 | }
|
| 324 |
|
| 325 | |
| 326 | * For kallsyms to ask for address resolution. NULL means not found. Careful
|
| 327 | * not to lock to avoid deadlock on oopses, RCU is enough.
|
| 328 |
|
| 329 | int module_address_lookup(unsigned long addr,
|
| 330 | unsigned long *size,
|
| 331 | unsigned long *offset,
|
| 332 | char **modname,
|
| 333 | const unsigned char **modbuildid,
|
| 334 | char *namebuf)
|
| 335 | {
|
| 336 | const char *sym;
|
| 337 | int ret = 0;
|
| 338 | struct module *mod;
|
| 339 |
|
| 340 | guard(rcu)();
|
| 341 | mod = __module_address(addr);
|
| 342 | if (mod) {
|
| 343 | if (modname)
|
| 344 | *modname = mod->name;
|
| 345 | if (modbuildid)
|
| 346 | *modbuildid = module_buildid(mod);
|
| 347 |
|
| 348 | sym = find_kallsyms_symbol(mod, addr, size, offset);
|
| 349 |
|
| 350 | if (sym)
|
| 351 | ret = strscpy(namebuf, sym, KSYM_NAME_LEN);
|
| 352 | }
|
| 353 | return ret;
|
| 354 | }
|
| 355 |
|
| 356 | int lookup_module_symbol_name(unsigned long addr, char *symname)
|
| 357 | {
|
| 358 | struct module *mod;
|
| 359 |
|
| 360 | guard(rcu)();
|
| 361 | list_for_each_entry_rcu(mod, &modules, list) {
|
| 362 | if (mod->state == MODULE_STATE_UNFORMED)
|
| 363 | continue;
|
| 364 | if (within_module(addr, mod)) {
|
| 365 | const char *sym;
|
| 366 |
|
| 367 | sym = find_kallsyms_symbol(mod, addr, NULL, NULL);
|
| 368 | if (!sym)
|
| 369 | goto out;
|
| 370 |
|
| 371 | strscpy(symname, sym, KSYM_NAME_LEN);
|
| 372 | return 0;
|
| 373 | }
|
| 374 | }
|
| 375 | out:
|
| 376 | return -ERANGE;
|
| 377 | }
|
| 378 |
|
| 379 | int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
|
| 380 | char *name, char *module_name, int *exported)
|
| 381 | {
|
| 382 | struct module *mod;
|
| 383 |
|
| 384 | guard(rcu)();
|
| 385 | list_for_each_entry_rcu(mod, &modules, list) {
|
| 386 | struct mod_kallsyms *kallsyms;
|
| 387 |
|
| 388 | if (mod->state == MODULE_STATE_UNFORMED)
|
| 389 | continue;
|
| 390 | kallsyms = rcu_dereference(mod->kallsyms);
|
| 391 | if (symnum < kallsyms->num_symtab) {
|
| 392 | const Elf_Sym *sym = &kallsyms->symtab[symnum];
|
| 393 |
|
| 394 | *value = kallsyms_symbol_value(sym);
|
| 395 | *type = kallsyms->typetab[symnum];
|
| 396 | strscpy(name, kallsyms_symbol_name(kallsyms, symnum), KSYM_NAME_LEN);
|
| 397 | strscpy(module_name, mod->name, MODULE_NAME_LEN);
|
| 398 | *exported = is_exported(name, *value, mod);
|
| 399 | return 0;
|
| 400 | }
|
| 401 | symnum -= kallsyms->num_symtab;
|
| 402 | }
|
| 403 | return -ERANGE;
|
| 404 | }
|
| 405 |
|
| 406 |
|
| 407 | static unsigned long __find_kallsyms_symbol_value(struct module *mod, const char *name)
|
| 408 | {
|
| 409 | unsigned int i;
|
| 410 | struct mod_kallsyms *kallsyms = rcu_dereference(mod->kallsyms);
|
| 411 |
|
| 412 | for (i = 0; i < kallsyms->num_symtab; i++) {
|
| 413 | const Elf_Sym *sym = &kallsyms->symtab[i];
|
| 414 |
|
| 415 | if (strcmp(name, kallsyms_symbol_name(kallsyms, i)) == 0 &&
|
| 416 | sym->st_shndx != SHN_UNDEF)
|
| 417 | return kallsyms_symbol_value(sym);
|
| 418 | }
|
| 419 | return 0;
|
| 420 | }
|
| 421 |
|
| 422 | static unsigned long __module_kallsyms_lookup_name(const char *name)
|
| 423 | {
|
| 424 | struct module *mod;
|
| 425 | char *colon;
|
| 426 |
|
| 427 | colon = strnchr(name, MODULE_NAME_LEN, ':');
|
| 428 | if (colon) {
|
| 429 | mod = find_module_all(name, colon - name, false);
|
| 430 | if (mod)
|
| 431 | return __find_kallsyms_symbol_value(mod, colon + 1);
|
| 432 | return 0;
|
| 433 | }
|
| 434 |
|
| 435 | list_for_each_entry_rcu(mod, &modules, list) {
|
| 436 | unsigned long ret;
|
| 437 |
|
| 438 | if (mod->state == MODULE_STATE_UNFORMED)
|
| 439 | continue;
|
| 440 | ret = __find_kallsyms_symbol_value(mod, name);
|
| 441 | if (ret)
|
| 442 | return ret;
|
| 443 | }
|
| 444 | return 0;
|
| 445 | }
|
| 446 |
|
| 447 |
|
| 448 | unsigned long module_kallsyms_lookup_name(const char *name)
|
| 449 | {
|
| 450 |
|
| 451 | guard(rcu)();
|
| 452 | return __module_kallsyms_lookup_name(name);
|
| 453 | }
|
| 454 |
|
| 455 | unsigned long find_kallsyms_symbol_value(struct module *mod, const char *name)
|
| 456 | {
|
| 457 | guard(rcu)();
|
| 458 | return __find_kallsyms_symbol_value(mod, name);
|
| 459 | }
|
| 460 |
|
| 461 | int module_kallsyms_on_each_symbol(const char *modname,
|
| 462 | int (*fn)(void *, const char *, unsigned long),
|
| 463 | void *data)
|
| 464 | {
|
| 465 | struct module *mod;
|
| 466 | unsigned int i;
|
| 467 | int ret = 0;
|
| 468 |
|
| 469 | mutex_lock(&module_mutex);
|
| 470 | list_for_each_entry(mod, &modules, list) {
|
| 471 | struct mod_kallsyms *kallsyms;
|
| 472 |
|
| 473 | if (mod->state == MODULE_STATE_UNFORMED)
|
| 474 | continue;
|
| 475 |
|
| 476 | if (modname && strcmp(modname, mod->name))
|
| 477 | continue;
|
| 478 |
|
| 479 | kallsyms = rcu_dereference_check(mod->kallsyms,
|
| 480 | lockdep_is_held(&module_mutex));
|
| 481 |
|
| 482 | for (i = 0; i < kallsyms->num_symtab; i++) {
|
| 483 | const Elf_Sym *sym = &kallsyms->symtab[i];
|
| 484 |
|
| 485 | if (sym->st_shndx == SHN_UNDEF)
|
| 486 | continue;
|
| 487 |
|
| 488 | ret = fn(data, kallsyms_symbol_name(kallsyms, i),
|
| 489 | kallsyms_symbol_value(sym));
|
| 490 | if (ret != 0)
|
| 491 | goto out;
|
| 492 | }
|
| 493 |
|
| 494 | |
| 495 | * The given module is found, the subsequent modules do not
|
| 496 | * need to be compared.
|
| 497 |
|
| 498 | if (modname)
|
| 499 | break;
|
| 500 | }
|
| 501 | out:
|
| 502 | mutex_unlock(&module_mutex);
|
| 503 | return ret;
|
| 504 | }
|