xref: /linux/tools/objtool/klp-sympos.c (revision a0acd94e3819fcd8346ae3c16df987e0fabb3128)
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