Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249// SPDX-License-Identifier: GPL-2.0/* * Obtain symbols from vmlinux for usage by kallsyms. Replaces mksysmap. * * To retain compatibility, it provides the same output as nm, only faster. */
#include <ctype.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <xalloc.h>
#include "elf-parse.h"#include "kallsyms.h"
/* * What mksysmap dropped from System.map, by name: symbols that are not needed * there, or not suitable for kallsyms. */static const char *const sysmap_omit_prefixes[] = { /* local symbols for ARM, MIPS, etc. */ "$", /* local labels, .LBB, .Ltmpxxx, .L__unnamed_xx, .LASANPC, etc. */ ".L", /* arm64 EFI stub namespace */ "__efistub_", /* arm64 local symbols in PIE namespace */ "__pi_$", "__pi_.L", /* arm64 local symbols in non-VHE KVM namespace */ "__kvm_nvhe_$", "__kvm_nvhe_.L", /* CFI type identifiers */ "__kcfi_typeid_", "__kvm_nvhe___kcfi_typeid_", "__pi___kcfi_typeid_", /* CRC from modversions */ "__crc_", /* EXPORT_SYMBOL (symbol name) */ "__kstrtab_", /* EXPORT_SYMBOL (namespace) */ "__kstrtabns_", /* MODULE_DEVICE_TABLE (symbol name) */ "__mod_device_table__",};
static const char *const sysmap_omit_suffixes[] = { /* arm */ "_from_arm", "_from_thumb", "_veneer",};
static const char *const sysmap_omit_names[] = { /* for LoongArch? */ "L0", /* ppc */ "_SDA_BASE_", "_SDA2_BASE_",};
/* Symbols that contain the pattern anywhere are dropped. */static const char *const sysmap_omit_patterns[] = { /* ppc stub */ ".long_branch.", ".plt_branch.",};
/* __<alnum>*Thunk_: the linker's range extension thunks on arm. */static bool is_range_thunk(const char *name){ const char *p;
if (!string_starts_with(name, "__")) return false; for (p = name + 2; isalnum((unsigned char)*p); p++) ; return p - name >= 7 && *p == '_' && strncmp(p - 5, "Thunk", 5) == 0;}
/* __UNIQUE_ID_modinfo_<n>: the MODULE_INFO() strings of built-in code. */static bool is_modinfo_id(const char *name){ static const char prefix[] = "__UNIQUE_ID_modinfo_"; const char *p;
if (!string_starts_with(name, prefix)) return false; for (p = name + strlen(prefix); isdigit((unsigned char)*p); p++) ; return !*p;}
static bool sysmap_omits(const char *name, char type){ size_t i;
/* Absolute, undefined and debugging symbols. */ if (type == 'a' || type == 'N' || type == 'U' || type == 'w') return true;
for (i = 0; i < ARRAY_SIZE(sysmap_omit_prefixes); i++) if (string_starts_with(name, sysmap_omit_prefixes[i])) return true; for (i = 0; i < ARRAY_SIZE(sysmap_omit_suffixes); i++) if (string_ends_with(name, sysmap_omit_suffixes[i])) return true; for (i = 0; i < ARRAY_SIZE(sysmap_omit_names); i++) if (strcmp(name, sysmap_omit_names[i]) == 0) return true; for (i = 0; i < ARRAY_SIZE(sysmap_omit_patterns); i++) if (strstr(name, sysmap_omit_patterns[i])) return true;
return is_range_thunk(name) || is_modinfo_id(name);}
/* nm's letter for a symbol defined in a section, as BFD classifies it. */static char section_symbol_type(Elf_Shdr *shdr, const char *secname){ static const char *const debug_prefixes[] = { ".debug", ".zdebug", ".gnu.debuglto_.debug_", ".gnu.linkonce.wi.", ".line", ".stab", }; const uint64_t flags = shdr_flags(shdr); size_t i;
if (flags & SHF_EXECINSTR) return 't'; if (flags & SHF_ALLOC) { if (shdr_type(shdr) == SHT_NOBITS) return 'b'; return flags & SHF_WRITE ? 'd' : 'r'; } for (i = 0; i < ARRAY_SIZE(debug_prefixes); i++) if (string_starts_with(secname, debug_prefixes[i])) return 'N'; if (shdr_type(shdr) != SHT_NOBITS && !(flags & SHF_WRITE)) return 'n'; return '?';}
/* The letter nm prints for a symbol, or 0 for one it leaves out. */static char elf_symbol_type(const struct elf_file *elf, Elf_Sym *sym){ unsigned int bind = sym_bind(sym), type = sym_type(sym); unsigned int shndx = sym_shndx(sym); Elf_Shdr *shdr; char c;
if (type == STT_SECTION || type == STT_FILE) return 0; if (shndx == SHN_COMMON) return 'C'; if (shndx == SHN_UNDEF) { if (bind == STB_WEAK) return type == STT_OBJECT ? 'v' : 'w'; return 'U'; } if (type == STT_GNU_IFUNC) return 'i'; if (bind == STB_WEAK) return type == STT_OBJECT ? 'V' : 'W'; if (bind == STB_GNU_UNIQUE) return 'u'; if (bind != STB_GLOBAL && bind != STB_LOCAL) return '?';
if (shndx == SHN_ABS) { c = 'a'; } else if (shndx < elf->shnum) { shdr = elf_section(elf, shndx); c = section_symbol_type(shdr, elf_section_name(elf, shdr)); } else { return '?'; }
return bind == STB_GLOBAL ? toupper(c) : c;}
/* nm -n order: by address, then by name. */static int compare_symbols(const void *a, const void *b){ const struct sysmap_symbol *sa = a, *sb = b;
if (sa->addr != sb->addr) return sa->addr < sb->addr ? -1 : 1; return strcmp(sa->name, sb->name);}
/* The symbols "nm -n | mksysmap" would list, in that order. */static struct sysmap_symbol *elf_read_symbols(const struct elf_file *elf, size_t *nr_kept){ struct sysmap_symbol *syms = xmalloc(elf->nr_syms * sizeof(*syms)); size_t i, n = 0;
for (i = 1; i < elf->nr_syms; i++) { Elf_Sym *sym = elf_symbol(elf, i); const char *name = elf->strtab + sym_name(sym); char type = elf_symbol_type(elf, sym);
if (!type || sysmap_omits(name, type)) continue;
syms[n].addr = sym_value(sym); syms[n].name = name; syms[n].type = type; n++; }
qsort(syms, n, sizeof(*syms), compare_symbols); *nr_kept = n; return syms;}
struct sysmap *sysmap_read(const char *path){ struct sysmap *map = xcalloc(1, sizeof(*map));
/* The names point into the mapping; keep it until the map is freed. */ if (elf_open_ro(path, (1 << ET_EXEC) | (1 << ET_DYN), &map->elf)) exit(EXIT_FAILURE); map->syms = elf_read_symbols(&map->elf, &map->nr_syms); map->addr_width = elf_map_long_size(map->elf.base) * 2;
return map;}
void sysmap_write(const struct sysmap *map, FILE *out){ size_t i;
for (i = 0; i < map->nr_syms; i++) { const struct sysmap_symbol *s = &map->syms[i];
fprintf(out, "%0*llx %c %s\n", map->addr_width, s->addr, s->type, s->name); }}
void sysmap_free(struct sysmap *map){ free(map->syms); elf_close(&map->elf); free(map);}