115fa203eSJosh Poimboeuf // SPDX-License-Identifier: GPL-2.0-or-later 215fa203eSJosh Poimboeuf /* 315fa203eSJosh Poimboeuf * Compute "sympos", the position used by livepatch to disambiguate 415fa203eSJosh Poimboeuf * duplicate symbol names in the patched object. 515fa203eSJosh Poimboeuf */ 615fa203eSJosh Poimboeuf #include <stdlib.h> 715fa203eSJosh Poimboeuf #include <string.h> 815fa203eSJosh Poimboeuf #include <fcntl.h> 915fa203eSJosh Poimboeuf 1015fa203eSJosh Poimboeuf #include <objtool/objtool.h> 1115fa203eSJosh Poimboeuf #include <objtool/warn.h> 1215fa203eSJosh Poimboeuf #include <objtool/endianness.h> 1315fa203eSJosh Poimboeuf #include <objtool/klp.h> 1415fa203eSJosh Poimboeuf 1515fa203eSJosh Poimboeuf #include <linux/string.h> 1615fa203eSJosh Poimboeuf 1715fa203eSJosh Poimboeuf struct vmlinux_sym { 1815fa203eSJosh Poimboeuf struct hlist_node hash; 1915fa203eSJosh Poimboeuf const char *name; 2015fa203eSJosh Poimboeuf u64 addr; 2115fa203eSJosh Poimboeuf }; 2215fa203eSJosh Poimboeuf 2315fa203eSJosh Poimboeuf struct vmlinux_symid { 2415fa203eSJosh Poimboeuf struct hlist_node hash; 2515fa203eSJosh Poimboeuf u64 id; 2615fa203eSJosh Poimboeuf u64 addr; 2715fa203eSJosh Poimboeuf }; 2815fa203eSJosh Poimboeuf 2915fa203eSJosh Poimboeuf struct vmlinux_o_symid { 3015fa203eSJosh Poimboeuf struct hlist_node hash; 3115fa203eSJosh Poimboeuf u64 id; 3215fa203eSJosh Poimboeuf unsigned int sym_idx; 3315fa203eSJosh Poimboeuf }; 3415fa203eSJosh Poimboeuf 3515fa203eSJosh Poimboeuf static DEFINE_HASHTABLE(vmlinux_o_symids, 16); 3615fa203eSJosh Poimboeuf 3715fa203eSJosh Poimboeuf /* 3815fa203eSJosh Poimboeuf * The original linked kernel, found next to the orig vmlinux.o. Read with raw 3915fa203eSJosh Poimboeuf * libelf rather than elf_open_read(): only the symbol table and the resolved 4015fa203eSJosh Poimboeuf * .klp.symid table are needed, not the (huge) instruction/reloc machinery. 4115fa203eSJosh Poimboeuf * 4215fa203eSJosh Poimboeuf * Both tables are built once by read_orig_vmlinux(). The Elf handle stays 4315fa203eSJosh Poimboeuf * open because the hashed names point into its mmapped string table. 4415fa203eSJosh Poimboeuf */ 4515fa203eSJosh Poimboeuf static struct { 4615fa203eSJosh Poimboeuf Elf *elf; 4715fa203eSJosh Poimboeuf DECLARE_HASHTABLE(syms, 16); /* name -> address */ 4815fa203eSJosh Poimboeuf DECLARE_HASHTABLE(symids, 16); /* .klp.symid id -> address */ 4915fa203eSJosh Poimboeuf } vmlinux; 5015fa203eSJosh Poimboeuf 5115fa203eSJosh Poimboeuf /* 5215fa203eSJosh Poimboeuf * Would the symbol be visible to the runtime's kallsyms-based symbol lookup? 5315fa203eSJosh Poimboeuf */ 5415fa203eSJosh Poimboeuf static bool vmlinux_sym_in_kallsyms(Elf *elf, GElf_Sym *sym) 5515fa203eSJosh Poimboeuf { 5615fa203eSJosh Poimboeuf unsigned int type = GELF_ST_TYPE(sym->st_info); 5715fa203eSJosh Poimboeuf GElf_Shdr shdr; 5815fa203eSJosh Poimboeuf Elf_Scn *scn; 5915fa203eSJosh Poimboeuf 6015fa203eSJosh Poimboeuf if (sym->st_shndx == SHN_UNDEF || sym->st_shndx >= SHN_LORESERVE) 6115fa203eSJosh Poimboeuf return false; 6215fa203eSJosh Poimboeuf 6315fa203eSJosh Poimboeuf if (type == STT_SECTION || type == STT_FILE) 6415fa203eSJosh Poimboeuf return false; 6515fa203eSJosh Poimboeuf 6615fa203eSJosh Poimboeuf scn = elf_getscn(elf, sym->st_shndx); 6715fa203eSJosh Poimboeuf if (!scn || !gelf_getshdr(scn, &shdr)) 6815fa203eSJosh Poimboeuf return false; 6915fa203eSJosh Poimboeuf 7015fa203eSJosh Poimboeuf return shdr.sh_flags & SHF_ALLOC; 7115fa203eSJosh Poimboeuf } 7215fa203eSJosh Poimboeuf 7315fa203eSJosh Poimboeuf static int read_orig_vmlinux(const char *filename) 7415fa203eSJosh Poimboeuf { 7515fa203eSJosh Poimboeuf size_t shstrndx, nr_syms = 0, nr_symids = 0, strtab_idx = 0; 7615fa203eSJosh Poimboeuf Elf_Data *symtab_data = NULL, *symid_data = NULL; 7715fa203eSJosh Poimboeuf struct klp_symid *symids; 7815fa203eSJosh Poimboeuf Elf_Scn *scn = NULL; 7915fa203eSJosh Poimboeuf GElf_Ehdr ehdr; 8015fa203eSJosh Poimboeuf int fd; 8115fa203eSJosh Poimboeuf 8215fa203eSJosh Poimboeuf fd = open(filename, O_RDONLY); 8315fa203eSJosh Poimboeuf if (fd == -1) { 8415fa203eSJosh Poimboeuf ERROR_GLIBC("can't open '%s'", filename); 8515fa203eSJosh Poimboeuf return -1; 8615fa203eSJosh Poimboeuf } 8715fa203eSJosh Poimboeuf 8815fa203eSJosh Poimboeuf if (elf_version(EV_CURRENT) == EV_NONE) { 8915fa203eSJosh Poimboeuf ERROR_ELF("elf_version"); 9015fa203eSJosh Poimboeuf return -1; 9115fa203eSJosh Poimboeuf } 9215fa203eSJosh Poimboeuf 9315fa203eSJosh Poimboeuf vmlinux.elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); 9415fa203eSJosh Poimboeuf if (!vmlinux.elf) { 9515fa203eSJosh Poimboeuf ERROR_ELF("elf_begin"); 9615fa203eSJosh Poimboeuf return -1; 9715fa203eSJosh Poimboeuf } 9815fa203eSJosh Poimboeuf 9915fa203eSJosh Poimboeuf if (!gelf_getehdr(vmlinux.elf, &ehdr)) { 10015fa203eSJosh Poimboeuf ERROR_ELF("gelf_getehdr"); 10115fa203eSJosh Poimboeuf return -1; 10215fa203eSJosh Poimboeuf } 10315fa203eSJosh Poimboeuf 10415fa203eSJosh Poimboeuf if (elf_getshdrstrndx(vmlinux.elf, &shstrndx)) { 10515fa203eSJosh Poimboeuf ERROR_ELF("elf_getshdrstrndx"); 10615fa203eSJosh Poimboeuf return -1; 10715fa203eSJosh Poimboeuf } 10815fa203eSJosh Poimboeuf 10915fa203eSJosh Poimboeuf while ((scn = elf_nextscn(vmlinux.elf, scn))) { 11015fa203eSJosh Poimboeuf const char *name; 11115fa203eSJosh Poimboeuf GElf_Shdr shdr; 11215fa203eSJosh Poimboeuf 11315fa203eSJosh Poimboeuf if (!gelf_getshdr(scn, &shdr)) { 11415fa203eSJosh Poimboeuf ERROR_ELF("gelf_getshdr"); 11515fa203eSJosh Poimboeuf return -1; 11615fa203eSJosh Poimboeuf } 11715fa203eSJosh Poimboeuf 11815fa203eSJosh Poimboeuf if (shdr.sh_type == SHT_SYMTAB) { 11915fa203eSJosh Poimboeuf symtab_data = elf_getdata(scn, NULL); 12015fa203eSJosh Poimboeuf if (!symtab_data) { 12115fa203eSJosh Poimboeuf ERROR_ELF("elf_getdata"); 12215fa203eSJosh Poimboeuf return -1; 12315fa203eSJosh Poimboeuf } 12415fa203eSJosh Poimboeuf nr_syms = shdr.sh_size / shdr.sh_entsize; 12515fa203eSJosh Poimboeuf strtab_idx = shdr.sh_link; 12615fa203eSJosh Poimboeuf continue; 12715fa203eSJosh Poimboeuf } 12815fa203eSJosh Poimboeuf 12915fa203eSJosh Poimboeuf name = elf_strptr(vmlinux.elf, shstrndx, shdr.sh_name); 13015fa203eSJosh Poimboeuf if (name && !strcmp(name, KLP_SYMID_SEC)) { 13115fa203eSJosh Poimboeuf if (shdr.sh_size % sizeof(struct klp_symid)) { 13215fa203eSJosh Poimboeuf ERROR("%s: %s: struct klp_symid size mismatch", 13315fa203eSJosh Poimboeuf filename, KLP_SYMID_SEC); 13415fa203eSJosh Poimboeuf return -1; 13515fa203eSJosh Poimboeuf } 13615fa203eSJosh Poimboeuf symid_data = elf_getdata(scn, NULL); 13715fa203eSJosh Poimboeuf if (!symid_data) { 13815fa203eSJosh Poimboeuf ERROR_ELF("elf_getdata"); 13915fa203eSJosh Poimboeuf return -1; 14015fa203eSJosh Poimboeuf } 14115fa203eSJosh Poimboeuf nr_symids = shdr.sh_size / sizeof(struct klp_symid); 14215fa203eSJosh Poimboeuf } 14315fa203eSJosh Poimboeuf } 14415fa203eSJosh Poimboeuf 14515fa203eSJosh Poimboeuf if (!symtab_data) { 14615fa203eSJosh Poimboeuf ERROR("%s: missing symbol table", filename); 14715fa203eSJosh Poimboeuf return -1; 14815fa203eSJosh Poimboeuf } 14915fa203eSJosh Poimboeuf 15015fa203eSJosh Poimboeuf if (!symid_data) { 15115fa203eSJosh Poimboeuf ERROR("%s: missing %s section, kernel not built with CONFIG_KLP_BUILD?", 15215fa203eSJosh Poimboeuf filename, KLP_SYMID_SEC); 15315fa203eSJosh Poimboeuf return -1; 15415fa203eSJosh Poimboeuf } 15515fa203eSJosh Poimboeuf 15615fa203eSJosh Poimboeuf for (size_t i = 0; i < nr_syms; i++) { 15715fa203eSJosh Poimboeuf struct vmlinux_sym *vsym; 15815fa203eSJosh Poimboeuf const char *name; 15915fa203eSJosh Poimboeuf GElf_Sym s; 16015fa203eSJosh Poimboeuf 16115fa203eSJosh Poimboeuf if (!gelf_getsym(symtab_data, i, &s)) { 16215fa203eSJosh Poimboeuf ERROR_ELF("gelf_getsym"); 16315fa203eSJosh Poimboeuf return -1; 16415fa203eSJosh Poimboeuf } 16515fa203eSJosh Poimboeuf 16615fa203eSJosh Poimboeuf if (!vmlinux_sym_in_kallsyms(vmlinux.elf, &s)) 16715fa203eSJosh Poimboeuf continue; 16815fa203eSJosh Poimboeuf 16915fa203eSJosh Poimboeuf name = elf_strptr(vmlinux.elf, strtab_idx, s.st_name); 17015fa203eSJosh Poimboeuf if (!name) 17115fa203eSJosh Poimboeuf continue; 17215fa203eSJosh Poimboeuf 17315fa203eSJosh Poimboeuf vsym = calloc(1, sizeof(*vsym)); 17415fa203eSJosh Poimboeuf if (!vsym) { 17515fa203eSJosh Poimboeuf ERROR_GLIBC("calloc"); 17615fa203eSJosh Poimboeuf return -1; 17715fa203eSJosh Poimboeuf } 17815fa203eSJosh Poimboeuf 17915fa203eSJosh Poimboeuf vsym->name = name; 18015fa203eSJosh Poimboeuf vsym->addr = s.st_value; 18115fa203eSJosh Poimboeuf hash_add(vmlinux.syms, &vsym->hash, str_hash(name)); 18215fa203eSJosh Poimboeuf } 18315fa203eSJosh Poimboeuf 18415fa203eSJosh Poimboeuf symids = symid_data->d_buf; 18515fa203eSJosh Poimboeuf 18615fa203eSJosh Poimboeuf for (size_t i = 0; i < nr_symids; i++) { 18715fa203eSJosh Poimboeuf struct vmlinux_symid *vsymid; 18815fa203eSJosh Poimboeuf 18915fa203eSJosh Poimboeuf vsymid = calloc(1, sizeof(*vsymid)); 19015fa203eSJosh Poimboeuf if (!vsymid) { 19115fa203eSJosh Poimboeuf ERROR_GLIBC("calloc"); 19215fa203eSJosh Poimboeuf return -1; 19315fa203eSJosh Poimboeuf } 19415fa203eSJosh Poimboeuf 19515fa203eSJosh Poimboeuf vsymid->id = __bswap_if_needed(&ehdr, symids[i].id); 19615fa203eSJosh Poimboeuf vsymid->addr = __bswap_if_needed(&ehdr, symids[i].addr); 19715fa203eSJosh Poimboeuf hash_add(vmlinux.symids, &vsymid->hash, vsymid->id); 19815fa203eSJosh Poimboeuf } 19915fa203eSJosh Poimboeuf 20015fa203eSJosh Poimboeuf /* the fd and Elf handle stay open, the hashed names live in the mmap */ 20115fa203eSJosh Poimboeuf return 0; 20215fa203eSJosh Poimboeuf } 20315fa203eSJosh Poimboeuf 20415fa203eSJosh Poimboeuf /* 20515fa203eSJosh Poimboeuf * Read the orig vmlinux.o's .klp.symid table, an array of entries whose 'addr' 20615fa203eSJosh Poimboeuf * fields have relocs to the symbols they describe. 20715fa203eSJosh Poimboeuf */ 20815fa203eSJosh Poimboeuf static int read_vmlinux_o_symids(struct elf *vmlinux_o) 20915fa203eSJosh Poimboeuf { 21015fa203eSJosh Poimboeuf struct section *sec; 21115fa203eSJosh Poimboeuf 21215fa203eSJosh Poimboeuf for_each_sec(vmlinux_o, sec) { 21315fa203eSJosh Poimboeuf unsigned long nr; 21415fa203eSJosh Poimboeuf 21515fa203eSJosh Poimboeuf if (strcmp(sec->name, KLP_SYMID_SEC)) 21615fa203eSJosh Poimboeuf continue; 21715fa203eSJosh Poimboeuf 21815fa203eSJosh Poimboeuf if (sec_size(sec) % sizeof(struct klp_symid)) { 21915fa203eSJosh Poimboeuf ERROR("%s: %s: struct klp_symid size mismatch", 22015fa203eSJosh Poimboeuf vmlinux_o->name, KLP_SYMID_SEC); 22115fa203eSJosh Poimboeuf return -1; 22215fa203eSJosh Poimboeuf } 22315fa203eSJosh Poimboeuf 22415fa203eSJosh Poimboeuf nr = sec_size(sec) / sizeof(struct klp_symid); 22515fa203eSJosh Poimboeuf 22615fa203eSJosh Poimboeuf for (unsigned long i = 0; i < nr; i++) { 22715fa203eSJosh Poimboeuf unsigned long offset = i * sizeof(struct klp_symid); 22815fa203eSJosh Poimboeuf struct vmlinux_o_symid *entry; 22915fa203eSJosh Poimboeuf struct klp_symid *symid; 23015fa203eSJosh Poimboeuf struct reloc *reloc; 23115fa203eSJosh Poimboeuf 23215fa203eSJosh Poimboeuf entry = calloc(1, sizeof(*entry)); 23315fa203eSJosh Poimboeuf if (!entry) { 23415fa203eSJosh Poimboeuf ERROR_GLIBC("calloc"); 23515fa203eSJosh Poimboeuf return -1; 23615fa203eSJosh Poimboeuf } 23715fa203eSJosh Poimboeuf 23815fa203eSJosh Poimboeuf symid = sec->data->d_buf + offset; 23915fa203eSJosh Poimboeuf entry->id = bswap_if_needed(vmlinux_o, symid->id); 24015fa203eSJosh Poimboeuf 24115fa203eSJosh Poimboeuf reloc = find_reloc_by_dest(vmlinux_o, sec, 24215fa203eSJosh Poimboeuf offset + offsetof(struct klp_symid, addr)); 24315fa203eSJosh Poimboeuf if (!reloc) { 24415fa203eSJosh Poimboeuf ERROR("%s: missing reloc for %s entry", 24515fa203eSJosh Poimboeuf vmlinux_o->name, KLP_SYMID_SEC); 24615fa203eSJosh Poimboeuf return -1; 24715fa203eSJosh Poimboeuf } 24815fa203eSJosh Poimboeuf entry->sym_idx = reloc->sym->idx; 24915fa203eSJosh Poimboeuf 25015fa203eSJosh Poimboeuf hash_add(vmlinux_o_symids, &entry->hash, entry->sym_idx); 25115fa203eSJosh Poimboeuf } 25215fa203eSJosh Poimboeuf } 25315fa203eSJosh Poimboeuf 25415fa203eSJosh Poimboeuf return 0; 25515fa203eSJosh Poimboeuf } 25615fa203eSJosh Poimboeuf 25715fa203eSJosh Poimboeuf int klp_sympos_init(struct elf *orig) 25815fa203eSJosh Poimboeuf { 25915fa203eSJosh Poimboeuf char *filename; 26015fa203eSJosh Poimboeuf int ret; 26115fa203eSJosh Poimboeuf 26215fa203eSJosh Poimboeuf if (!str_ends_with(objname, "vmlinux.o")) 26315fa203eSJosh Poimboeuf return 0; 26415fa203eSJosh Poimboeuf 26515fa203eSJosh Poimboeuf if (read_vmlinux_o_symids(orig)) 26615fa203eSJosh Poimboeuf return -1; 26715fa203eSJosh Poimboeuf 26815fa203eSJosh Poimboeuf filename = strndup(objname, strlen(objname) - 2); 26915fa203eSJosh Poimboeuf if (!filename) { 27015fa203eSJosh Poimboeuf ERROR_GLIBC("strndup"); 27115fa203eSJosh Poimboeuf return -1; 27215fa203eSJosh Poimboeuf } 27315fa203eSJosh Poimboeuf 27415fa203eSJosh Poimboeuf ret = read_orig_vmlinux(filename); 27515fa203eSJosh Poimboeuf free(filename); 27615fa203eSJosh Poimboeuf 27715fa203eSJosh Poimboeuf return ret; 27815fa203eSJosh Poimboeuf } 27915fa203eSJosh Poimboeuf 28015fa203eSJosh Poimboeuf /* Find the symbol's id in the orig vmlinux.o's .klp.symid table */ 28115fa203eSJosh Poimboeuf static int find_vmlinux_o_symid(struct symbol *sym, u64 *id) 28215fa203eSJosh Poimboeuf { 28315fa203eSJosh Poimboeuf struct vmlinux_o_symid *entry; 28415fa203eSJosh Poimboeuf 28515fa203eSJosh Poimboeuf hash_for_each_possible(vmlinux_o_symids, entry, hash, sym->idx) { 28615fa203eSJosh Poimboeuf if (entry->sym_idx == sym->idx) { 28715fa203eSJosh Poimboeuf *id = entry->id; 28815fa203eSJosh Poimboeuf return 0; 28915fa203eSJosh Poimboeuf } 29015fa203eSJosh Poimboeuf } 29115fa203eSJosh Poimboeuf 29215fa203eSJosh Poimboeuf ERROR("no %s entry for symbol %s in orig vmlinux.o", KLP_SYMID_SEC, 29315fa203eSJosh Poimboeuf sym->name); 29415fa203eSJosh Poimboeuf return -1; 29515fa203eSJosh Poimboeuf } 29615fa203eSJosh Poimboeuf 29715fa203eSJosh Poimboeuf /* Find the symbol's final address in the orig vmlinux's .klp.symid table */ 29815fa203eSJosh Poimboeuf static int find_vmlinux_symid_addr(u64 id, u64 *addr) 29915fa203eSJosh Poimboeuf { 30015fa203eSJosh Poimboeuf struct vmlinux_symid *symid; 30115fa203eSJosh Poimboeuf 30215fa203eSJosh Poimboeuf hash_for_each_possible(vmlinux.symids, symid, hash, id) { 30315fa203eSJosh Poimboeuf if (symid->id == id) { 30415fa203eSJosh Poimboeuf *addr = symid->addr; 30515fa203eSJosh Poimboeuf return 0; 30615fa203eSJosh Poimboeuf } 30715fa203eSJosh Poimboeuf } 30815fa203eSJosh Poimboeuf 30915fa203eSJosh Poimboeuf return -1; 31015fa203eSJosh Poimboeuf } 31115fa203eSJosh Poimboeuf 31215fa203eSJosh Poimboeuf /* 31315fa203eSJosh Poimboeuf * Find the sympos of a vmlinux-local symbol by ranking its final address 31415fa203eSJosh Poimboeuf * among the duplicately named symbols in the linked orig vmlinux, replicating 31515fa203eSJosh Poimboeuf * the order in which kallsyms_on_each_match_symbol() counts them. 31615fa203eSJosh Poimboeuf */ 31715fa203eSJosh Poimboeuf static unsigned long find_vmlinux_sympos(struct symbol *sym) 31815fa203eSJosh Poimboeuf { 31915fa203eSJosh Poimboeuf unsigned long nr_matches = 0, sympos = 1; 32015fa203eSJosh Poimboeuf u32 key = str_hash(sym->name); 32115fa203eSJosh Poimboeuf struct vmlinux_sym *vsym; 32215fa203eSJosh Poimboeuf bool found = false; 32315fa203eSJosh Poimboeuf u64 id, addr; 32415fa203eSJosh Poimboeuf 32515fa203eSJosh Poimboeuf hash_for_each_possible(vmlinux.syms, vsym, hash, key) 32615fa203eSJosh Poimboeuf if (!strcmp(vsym->name, sym->name)) 32715fa203eSJosh Poimboeuf nr_matches++; 32815fa203eSJosh Poimboeuf 32915fa203eSJosh Poimboeuf if (!nr_matches) { 33015fa203eSJosh Poimboeuf ERROR("can't find symbol %s in orig vmlinux", sym->name); 33115fa203eSJosh Poimboeuf return ULONG_MAX; 33215fa203eSJosh Poimboeuf } 33315fa203eSJosh Poimboeuf 33415fa203eSJosh Poimboeuf /* 33515fa203eSJosh Poimboeuf * Unique symbols don't need disambiguating. They also have no 33615fa203eSJosh Poimboeuf * .klp.symid entry, which is only emitted for names duplicated in 33715fa203eSJosh Poimboeuf * vmlinux.o, so the lookups below would fail. 33815fa203eSJosh Poimboeuf */ 33915fa203eSJosh Poimboeuf if (nr_matches == 1) 34015fa203eSJosh Poimboeuf return 0; 34115fa203eSJosh Poimboeuf 34215fa203eSJosh Poimboeuf if (find_vmlinux_o_symid(sym, &id)) 34315fa203eSJosh Poimboeuf return ULONG_MAX; 34415fa203eSJosh Poimboeuf 34515fa203eSJosh Poimboeuf if (find_vmlinux_symid_addr(id, &addr)) { 34615fa203eSJosh Poimboeuf ERROR("no %s entry for symbol %s in orig vmlinux", KLP_SYMID_SEC, 34715fa203eSJosh Poimboeuf sym->name); 34815fa203eSJosh Poimboeuf return ULONG_MAX; 34915fa203eSJosh Poimboeuf } 35015fa203eSJosh Poimboeuf 35115fa203eSJosh Poimboeuf hash_for_each_possible(vmlinux.syms, vsym, hash, key) { 35215fa203eSJosh Poimboeuf if (strcmp(vsym->name, sym->name)) 35315fa203eSJosh Poimboeuf continue; 35415fa203eSJosh Poimboeuf 35515fa203eSJosh Poimboeuf if (vsym->addr < addr) 35615fa203eSJosh Poimboeuf sympos++; 35715fa203eSJosh Poimboeuf else if (vsym->addr == addr) 35815fa203eSJosh Poimboeuf found = true; 35915fa203eSJosh Poimboeuf } 36015fa203eSJosh Poimboeuf 36115fa203eSJosh Poimboeuf if (!found) { 36215fa203eSJosh Poimboeuf ERROR("%s address mismatch for symbol %s, stale orig vmlinux?", 36315fa203eSJosh Poimboeuf KLP_SYMID_SEC, sym->name); 36415fa203eSJosh Poimboeuf return ULONG_MAX; 36515fa203eSJosh Poimboeuf } 36615fa203eSJosh Poimboeuf 36715fa203eSJosh Poimboeuf return sympos; 36815fa203eSJosh Poimboeuf } 36915fa203eSJosh Poimboeuf 370*69f361b8SJosh Poimboeuf static bool is_init_sym(struct symbol *sym) 371*69f361b8SJosh Poimboeuf { 372*69f361b8SJosh Poimboeuf return strstarts(sym->sec->name, ".init"); 373*69f361b8SJosh Poimboeuf } 374*69f361b8SJosh Poimboeuf 37515fa203eSJosh Poimboeuf /* 37615fa203eSJosh Poimboeuf * "sympos" is used by livepatch to disambiguate duplicate symbol names. 37715fa203eSJosh Poimboeuf */ 37815fa203eSJosh Poimboeuf unsigned long klp_find_sympos(struct elf *elf, struct symbol *sym) 37915fa203eSJosh Poimboeuf { 38015fa203eSJosh Poimboeuf unsigned long sympos = 0, nr_matches = 0; 38115fa203eSJosh Poimboeuf bool has_dup = false; 38215fa203eSJosh Poimboeuf struct symbol *s; 38315fa203eSJosh Poimboeuf 384*69f361b8SJosh Poimboeuf if (is_init_sym(sym)) { 385*69f361b8SJosh Poimboeuf ERROR("%s: can't patch or reference init code/data", sym->name); 386*69f361b8SJosh Poimboeuf return ULONG_MAX; 387*69f361b8SJosh Poimboeuf } 388*69f361b8SJosh Poimboeuf 38915fa203eSJosh Poimboeuf if (sym->bind != STB_LOCAL) 39015fa203eSJosh Poimboeuf return 0; 39115fa203eSJosh Poimboeuf 39215fa203eSJosh Poimboeuf /* 39315fa203eSJosh Poimboeuf * vmlinux: the final link reorders symbols relative to vmlinux.o, 39415fa203eSJosh Poimboeuf * so the position needs to be derived from the linked orig vmlinux via 39515fa203eSJosh Poimboeuf * the .klp.symid table. 39615fa203eSJosh Poimboeuf */ 39715fa203eSJosh Poimboeuf if (vmlinux.elf) 39815fa203eSJosh Poimboeuf return find_vmlinux_sympos(sym); 39915fa203eSJosh Poimboeuf 40015fa203eSJosh Poimboeuf /* 40115fa203eSJosh Poimboeuf * modules: the final .ko preserves symbol table order, so a 40215fa203eSJosh Poimboeuf * symtab-order count here matches the runtime count done by 40315fa203eSJosh Poimboeuf * module_kallsyms_on_each_symbol(). 40415fa203eSJosh Poimboeuf */ 40515fa203eSJosh Poimboeuf for_each_sym(elf, s) { 40615fa203eSJosh Poimboeuf if (!strcmp(s->name, sym->name)) { 40715fa203eSJosh Poimboeuf nr_matches++; 40815fa203eSJosh Poimboeuf if (s == sym) 40915fa203eSJosh Poimboeuf sympos = nr_matches; 41015fa203eSJosh Poimboeuf else 41115fa203eSJosh Poimboeuf has_dup = true; 41215fa203eSJosh Poimboeuf } 41315fa203eSJosh Poimboeuf } 41415fa203eSJosh Poimboeuf 41515fa203eSJosh Poimboeuf if (!sympos) { 41615fa203eSJosh Poimboeuf ERROR("can't find sympos for %s", sym->name); 41715fa203eSJosh Poimboeuf return ULONG_MAX; 41815fa203eSJosh Poimboeuf } 41915fa203eSJosh Poimboeuf 42015fa203eSJosh Poimboeuf return has_dup ? sympos : 0; 42115fa203eSJosh Poimboeuf } 422