xref: /openbmc/linux/tools/lib/bpf/elf.c (revision f90eb70d4489ea6fa6f3a7638aa614e15ad7181f)
15c742725SJiri Olsa // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
25c742725SJiri Olsa 
35c742725SJiri Olsa #include <libelf.h>
45c742725SJiri Olsa #include <gelf.h>
55c742725SJiri Olsa #include <fcntl.h>
65c742725SJiri Olsa #include <linux/kernel.h>
75c742725SJiri Olsa 
85c742725SJiri Olsa #include "libbpf_internal.h"
95c742725SJiri Olsa #include "str_error.h"
105c742725SJiri Olsa 
115c742725SJiri Olsa #define STRERR_BUFSIZE  128
125c742725SJiri Olsa 
13*f90eb70dSJiri Olsa int elf_open(const char *binary_path, struct elf_fd *elf_fd)
14*f90eb70dSJiri Olsa {
15*f90eb70dSJiri Olsa 	char errmsg[STRERR_BUFSIZE];
16*f90eb70dSJiri Olsa 	int fd, ret;
17*f90eb70dSJiri Olsa 	Elf *elf;
18*f90eb70dSJiri Olsa 
19*f90eb70dSJiri Olsa 	if (elf_version(EV_CURRENT) == EV_NONE) {
20*f90eb70dSJiri Olsa 		pr_warn("elf: failed to init libelf for %s\n", binary_path);
21*f90eb70dSJiri Olsa 		return -LIBBPF_ERRNO__LIBELF;
22*f90eb70dSJiri Olsa 	}
23*f90eb70dSJiri Olsa 	fd = open(binary_path, O_RDONLY | O_CLOEXEC);
24*f90eb70dSJiri Olsa 	if (fd < 0) {
25*f90eb70dSJiri Olsa 		ret = -errno;
26*f90eb70dSJiri Olsa 		pr_warn("elf: failed to open %s: %s\n", binary_path,
27*f90eb70dSJiri Olsa 			libbpf_strerror_r(ret, errmsg, sizeof(errmsg)));
28*f90eb70dSJiri Olsa 		return ret;
29*f90eb70dSJiri Olsa 	}
30*f90eb70dSJiri Olsa 	elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
31*f90eb70dSJiri Olsa 	if (!elf) {
32*f90eb70dSJiri Olsa 		pr_warn("elf: could not read elf from %s: %s\n", binary_path, elf_errmsg(-1));
33*f90eb70dSJiri Olsa 		close(fd);
34*f90eb70dSJiri Olsa 		return -LIBBPF_ERRNO__FORMAT;
35*f90eb70dSJiri Olsa 	}
36*f90eb70dSJiri Olsa 	elf_fd->fd = fd;
37*f90eb70dSJiri Olsa 	elf_fd->elf = elf;
38*f90eb70dSJiri Olsa 	return 0;
39*f90eb70dSJiri Olsa }
40*f90eb70dSJiri Olsa 
41*f90eb70dSJiri Olsa void elf_close(struct elf_fd *elf_fd)
42*f90eb70dSJiri Olsa {
43*f90eb70dSJiri Olsa 	if (!elf_fd)
44*f90eb70dSJiri Olsa 		return;
45*f90eb70dSJiri Olsa 	elf_end(elf_fd->elf);
46*f90eb70dSJiri Olsa 	close(elf_fd->fd);
47*f90eb70dSJiri Olsa }
48*f90eb70dSJiri Olsa 
495c742725SJiri Olsa /* Return next ELF section of sh_type after scn, or first of that type if scn is NULL. */
505c742725SJiri Olsa static Elf_Scn *elf_find_next_scn_by_type(Elf *elf, int sh_type, Elf_Scn *scn)
515c742725SJiri Olsa {
525c742725SJiri Olsa 	while ((scn = elf_nextscn(elf, scn)) != NULL) {
535c742725SJiri Olsa 		GElf_Shdr sh;
545c742725SJiri Olsa 
555c742725SJiri Olsa 		if (!gelf_getshdr(scn, &sh))
565c742725SJiri Olsa 			continue;
575c742725SJiri Olsa 		if (sh.sh_type == sh_type)
585c742725SJiri Olsa 			return scn;
595c742725SJiri Olsa 	}
605c742725SJiri Olsa 	return NULL;
615c742725SJiri Olsa }
625c742725SJiri Olsa 
635c742725SJiri Olsa /* Find offset of function name in the provided ELF object. "binary_path" is
645c742725SJiri Olsa  * the path to the ELF binary represented by "elf", and only used for error
655c742725SJiri Olsa  * reporting matters. "name" matches symbol name or name@@LIB for library
665c742725SJiri Olsa  * functions.
675c742725SJiri Olsa  */
685c742725SJiri Olsa long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name)
695c742725SJiri Olsa {
705c742725SJiri Olsa 	int i, sh_types[2] = { SHT_DYNSYM, SHT_SYMTAB };
715c742725SJiri Olsa 	bool is_shared_lib, is_name_qualified;
725c742725SJiri Olsa 	long ret = -ENOENT;
735c742725SJiri Olsa 	size_t name_len;
745c742725SJiri Olsa 	GElf_Ehdr ehdr;
755c742725SJiri Olsa 
765c742725SJiri Olsa 	if (!gelf_getehdr(elf, &ehdr)) {
775c742725SJiri Olsa 		pr_warn("elf: failed to get ehdr from %s: %s\n", binary_path, elf_errmsg(-1));
785c742725SJiri Olsa 		ret = -LIBBPF_ERRNO__FORMAT;
795c742725SJiri Olsa 		goto out;
805c742725SJiri Olsa 	}
815c742725SJiri Olsa 	/* for shared lib case, we do not need to calculate relative offset */
825c742725SJiri Olsa 	is_shared_lib = ehdr.e_type == ET_DYN;
835c742725SJiri Olsa 
845c742725SJiri Olsa 	name_len = strlen(name);
855c742725SJiri Olsa 	/* Does name specify "@@LIB"? */
865c742725SJiri Olsa 	is_name_qualified = strstr(name, "@@") != NULL;
875c742725SJiri Olsa 
885c742725SJiri Olsa 	/* Search SHT_DYNSYM, SHT_SYMTAB for symbol. This search order is used because if
895c742725SJiri Olsa 	 * a binary is stripped, it may only have SHT_DYNSYM, and a fully-statically
905c742725SJiri Olsa 	 * linked binary may not have SHT_DYMSYM, so absence of a section should not be
915c742725SJiri Olsa 	 * reported as a warning/error.
925c742725SJiri Olsa 	 */
935c742725SJiri Olsa 	for (i = 0; i < ARRAY_SIZE(sh_types); i++) {
945c742725SJiri Olsa 		size_t nr_syms, strtabidx, idx;
955c742725SJiri Olsa 		Elf_Data *symbols = NULL;
965c742725SJiri Olsa 		Elf_Scn *scn = NULL;
975c742725SJiri Olsa 		int last_bind = -1;
985c742725SJiri Olsa 		const char *sname;
995c742725SJiri Olsa 		GElf_Shdr sh;
1005c742725SJiri Olsa 
1015c742725SJiri Olsa 		scn = elf_find_next_scn_by_type(elf, sh_types[i], NULL);
1025c742725SJiri Olsa 		if (!scn) {
1035c742725SJiri Olsa 			pr_debug("elf: failed to find symbol table ELF sections in '%s'\n",
1045c742725SJiri Olsa 				 binary_path);
1055c742725SJiri Olsa 			continue;
1065c742725SJiri Olsa 		}
1075c742725SJiri Olsa 		if (!gelf_getshdr(scn, &sh))
1085c742725SJiri Olsa 			continue;
1095c742725SJiri Olsa 		strtabidx = sh.sh_link;
1105c742725SJiri Olsa 		symbols = elf_getdata(scn, 0);
1115c742725SJiri Olsa 		if (!symbols) {
1125c742725SJiri Olsa 			pr_warn("elf: failed to get symbols for symtab section in '%s': %s\n",
1135c742725SJiri Olsa 				binary_path, elf_errmsg(-1));
1145c742725SJiri Olsa 			ret = -LIBBPF_ERRNO__FORMAT;
1155c742725SJiri Olsa 			goto out;
1165c742725SJiri Olsa 		}
1175c742725SJiri Olsa 		nr_syms = symbols->d_size / sh.sh_entsize;
1185c742725SJiri Olsa 
1195c742725SJiri Olsa 		for (idx = 0; idx < nr_syms; idx++) {
1205c742725SJiri Olsa 			int curr_bind;
1215c742725SJiri Olsa 			GElf_Sym sym;
1225c742725SJiri Olsa 			Elf_Scn *sym_scn;
1235c742725SJiri Olsa 			GElf_Shdr sym_sh;
1245c742725SJiri Olsa 
1255c742725SJiri Olsa 			if (!gelf_getsym(symbols, idx, &sym))
1265c742725SJiri Olsa 				continue;
1275c742725SJiri Olsa 
1285c742725SJiri Olsa 			if (GELF_ST_TYPE(sym.st_info) != STT_FUNC)
1295c742725SJiri Olsa 				continue;
1305c742725SJiri Olsa 
1315c742725SJiri Olsa 			sname = elf_strptr(elf, strtabidx, sym.st_name);
1325c742725SJiri Olsa 			if (!sname)
1335c742725SJiri Olsa 				continue;
1345c742725SJiri Olsa 
1355c742725SJiri Olsa 			curr_bind = GELF_ST_BIND(sym.st_info);
1365c742725SJiri Olsa 
1375c742725SJiri Olsa 			/* User can specify func, func@@LIB or func@@LIB_VERSION. */
1385c742725SJiri Olsa 			if (strncmp(sname, name, name_len) != 0)
1395c742725SJiri Olsa 				continue;
1405c742725SJiri Olsa 			/* ...but we don't want a search for "foo" to match 'foo2" also, so any
1415c742725SJiri Olsa 			 * additional characters in sname should be of the form "@@LIB".
1425c742725SJiri Olsa 			 */
1435c742725SJiri Olsa 			if (!is_name_qualified && sname[name_len] != '\0' && sname[name_len] != '@')
1445c742725SJiri Olsa 				continue;
1455c742725SJiri Olsa 
1465c742725SJiri Olsa 			if (ret >= 0) {
1475c742725SJiri Olsa 				/* handle multiple matches */
1485c742725SJiri Olsa 				if (last_bind != STB_WEAK && curr_bind != STB_WEAK) {
1495c742725SJiri Olsa 					/* Only accept one non-weak bind. */
1505c742725SJiri Olsa 					pr_warn("elf: ambiguous match for '%s', '%s' in '%s'\n",
1515c742725SJiri Olsa 						sname, name, binary_path);
1525c742725SJiri Olsa 					ret = -LIBBPF_ERRNO__FORMAT;
1535c742725SJiri Olsa 					goto out;
1545c742725SJiri Olsa 				} else if (curr_bind == STB_WEAK) {
1555c742725SJiri Olsa 					/* already have a non-weak bind, and
1565c742725SJiri Olsa 					 * this is a weak bind, so ignore.
1575c742725SJiri Olsa 					 */
1585c742725SJiri Olsa 					continue;
1595c742725SJiri Olsa 				}
1605c742725SJiri Olsa 			}
1615c742725SJiri Olsa 
1625c742725SJiri Olsa 			/* Transform symbol's virtual address (absolute for
1635c742725SJiri Olsa 			 * binaries and relative for shared libs) into file
1645c742725SJiri Olsa 			 * offset, which is what kernel is expecting for
1655c742725SJiri Olsa 			 * uprobe/uretprobe attachment.
1665c742725SJiri Olsa 			 * See Documentation/trace/uprobetracer.rst for more
1675c742725SJiri Olsa 			 * details.
1685c742725SJiri Olsa 			 * This is done by looking up symbol's containing
1695c742725SJiri Olsa 			 * section's header and using it's virtual address
1705c742725SJiri Olsa 			 * (sh_addr) and corresponding file offset (sh_offset)
1715c742725SJiri Olsa 			 * to transform sym.st_value (virtual address) into
1725c742725SJiri Olsa 			 * desired final file offset.
1735c742725SJiri Olsa 			 */
1745c742725SJiri Olsa 			sym_scn = elf_getscn(elf, sym.st_shndx);
1755c742725SJiri Olsa 			if (!sym_scn)
1765c742725SJiri Olsa 				continue;
1775c742725SJiri Olsa 			if (!gelf_getshdr(sym_scn, &sym_sh))
1785c742725SJiri Olsa 				continue;
1795c742725SJiri Olsa 
1805c742725SJiri Olsa 			ret = sym.st_value - sym_sh.sh_addr + sym_sh.sh_offset;
1815c742725SJiri Olsa 			last_bind = curr_bind;
1825c742725SJiri Olsa 		}
1835c742725SJiri Olsa 		if (ret > 0)
1845c742725SJiri Olsa 			break;
1855c742725SJiri Olsa 	}
1865c742725SJiri Olsa 
1875c742725SJiri Olsa 	if (ret > 0) {
1885c742725SJiri Olsa 		pr_debug("elf: symbol address match for '%s' in '%s': 0x%lx\n", name, binary_path,
1895c742725SJiri Olsa 			 ret);
1905c742725SJiri Olsa 	} else {
1915c742725SJiri Olsa 		if (ret == 0) {
1925c742725SJiri Olsa 			pr_warn("elf: '%s' is 0 in symtab for '%s': %s\n", name, binary_path,
1935c742725SJiri Olsa 				is_shared_lib ? "should not be 0 in a shared library" :
1945c742725SJiri Olsa 						"try using shared library path instead");
1955c742725SJiri Olsa 			ret = -ENOENT;
1965c742725SJiri Olsa 		} else {
1975c742725SJiri Olsa 			pr_warn("elf: failed to find symbol '%s' in '%s'\n", name, binary_path);
1985c742725SJiri Olsa 		}
1995c742725SJiri Olsa 	}
2005c742725SJiri Olsa out:
2015c742725SJiri Olsa 	return ret;
2025c742725SJiri Olsa }
2035c742725SJiri Olsa 
2045c742725SJiri Olsa /* Find offset of function name in ELF object specified by path. "name" matches
2055c742725SJiri Olsa  * symbol name or name@@LIB for library functions.
2065c742725SJiri Olsa  */
2075c742725SJiri Olsa long elf_find_func_offset_from_file(const char *binary_path, const char *name)
2085c742725SJiri Olsa {
209*f90eb70dSJiri Olsa 	struct elf_fd elf_fd;
2105c742725SJiri Olsa 	long ret = -ENOENT;
2115c742725SJiri Olsa 
212*f90eb70dSJiri Olsa 	ret = elf_open(binary_path, &elf_fd);
213*f90eb70dSJiri Olsa 	if (ret)
214*f90eb70dSJiri Olsa 		return ret;
215*f90eb70dSJiri Olsa 	ret = elf_find_func_offset(elf_fd.elf, binary_path, name);
216*f90eb70dSJiri Olsa 	elf_close(&elf_fd);
2175c742725SJiri Olsa 	return ret;
2185c742725SJiri Olsa }
219