[RFC/PATCH bpf-next 06/20] libbpf: Factor elf_for_each_symbol function

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Currently we have elf_find_func_offset function that looks up
symbol in the binary and returns its offset to be used for uprobe
attachment.

For attaching multiple uprobes we will need interface that allows
us to get offsets for multiple symbols specified either by name or
regular expression.

Factoring out elf_for_each_symbol helper function that iterates
all symbols in binary and calls following callbacks:

  fn_match - on each symbol
             if it returns error < 0, we bail out with that error
  fn_done  - when we finish iterating symbol section,
             if it returns true, we don't iterate next section

It will be used in following changes to lookup multiple symbols
and their offsets.

Changing elf_find_func_offset to use elf_for_each_symbol with
single_match callback that's looking to match single function.

Signed-off-by: Jiri Olsa <jolsa@xxxxxxxxxx>
---
 tools/lib/bpf/libbpf.c | 185 +++++++++++++++++++++++++----------------
 1 file changed, 114 insertions(+), 71 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index b5bde1f19831..92c92ed2101f 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -10707,30 +10707,87 @@ static Elf_Scn *elf_find_next_scn_by_type(Elf *elf, int sh_type, Elf_Scn *scn)
 	return NULL;
 }
 
-/* Find offset of function name in the provided ELF object. "binary_path" is
- * the path to the ELF binary represented by "elf", and only used for error
- * reporting matters. "name" matches symbol name or name@@LIB for library
- * functions.
- */
-static long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name)
-{
-	int i, sh_types[2] = { SHT_DYNSYM, SHT_SYMTAB };
-	bool is_shared_lib, is_name_qualified;
-	long ret = -ENOENT;
+struct elf_func_offset {
+	const char *name;
+	unsigned long offset;
+	int last_bind;
 	size_t name_len;
-	GElf_Ehdr ehdr;
+	bool is_name_qualified;
+};
 
-	if (!gelf_getehdr(elf, &ehdr)) {
-		pr_warn("elf: failed to get ehdr from %s: %s\n", binary_path, elf_errmsg(-1));
-		ret = -LIBBPF_ERRNO__FORMAT;
-		goto out;
+static int single_done(void *_data)
+{
+	struct elf_func_offset *data = _data;
+
+	return data->offset > 0;
+}
+
+static int single_match(Elf *elf, const char *binary_path, const char *sname,
+			GElf_Sym *sym, void *_data)
+{
+	struct elf_func_offset *data = _data;
+	size_t name_len = data->name_len;
+	const char *name = data->name;
+	Elf_Scn *sym_scn;
+	GElf_Shdr sym_sh;
+	int curr_bind;
+
+	curr_bind = GELF_ST_BIND(sym->st_info);
+
+	/* User can specify func, func@@LIB or func@@LIB_VERSION. */
+	if (strncmp(sname, name, name_len) != 0)
+		return 0;
+	/* ...but we don't want a search for "foo" to match 'foo2" also, so any
+	 * additional characters in sname should be of the form "@@LIB".
+	 */
+	if (!data->is_name_qualified && sname[name_len] != '\0' && sname[name_len] != '@')
+		return 0;
+
+	if (data->offset > 0) {
+		/* handle multiple matches */
+		if (data->last_bind != STB_WEAK && curr_bind != STB_WEAK) {
+			/* Only accept one non-weak bind. */
+			pr_warn("elf: ambiguous match for '%s', '%s' in '%s'\n",
+				sname, name, binary_path);
+			return -LIBBPF_ERRNO__FORMAT;
+		} else if (curr_bind == STB_WEAK) {
+			/* already have a non-weak bind, and
+			 * this is a weak bind, so ignore.
+			 */
+			return 0;
+		}
 	}
-	/* for shared lib case, we do not need to calculate relative offset */
-	is_shared_lib = ehdr.e_type == ET_DYN;
 
-	name_len = strlen(name);
-	/* Does name specify "@@LIB"? */
-	is_name_qualified = strstr(name, "@@") != NULL;
+	/* Transform symbol's virtual address (absolute for
+	 * binaries and relative for shared libs) into file
+	 * offset, which is what kernel is expecting for
+	 * uprobe/uretprobe attachment.
+	 * See Documentation/trace/uprobetracer.rst for more
+	 * details.
+	 * This is done by looking up symbol's containing
+	 * section's header and using it's virtual address
+	 * (sh_addr) and corresponding file offset (sh_offset)
+	 * to transform sym.st_value (virtual address) into
+	 * desired final file offset.
+	 */
+	sym_scn = elf_getscn(elf, sym->st_shndx);
+	if (!sym_scn)
+		return 0;
+	if (!gelf_getshdr(sym_scn, &sym_sh))
+		return 0;
+
+	data->offset = sym->st_value - sym_sh.sh_addr + sym_sh.sh_offset;
+	data->last_bind = curr_bind;
+	return 0;
+}
+
+static int elf_for_each_symbol(Elf *elf, const char *binary_path,
+			       int (*fn_match)(Elf *, const char *, const char *, GElf_Sym *, void *),
+			       int (*fn_done)(void *),
+			       void *data)
+{
+	int i, sh_types[2] = { SHT_DYNSYM, SHT_SYMTAB };
+	int ret = -ENOENT;
 
 	/* Search SHT_DYNSYM, SHT_SYMTAB for symbol. This search order is used because if
 	 * a binary is stripped, it may only have SHT_DYNSYM, and a fully-statically
@@ -10741,7 +10798,6 @@ static long elf_find_func_offset(Elf *elf, const char *binary_path, const char *
 		size_t nr_syms, strtabidx, idx;
 		Elf_Data *symbols = NULL;
 		Elf_Scn *scn = NULL;
-		int last_bind = -1;
 		const char *sname;
 		GElf_Shdr sh;
 
@@ -10764,10 +10820,7 @@ static long elf_find_func_offset(Elf *elf, const char *binary_path, const char *
 		nr_syms = symbols->d_size / sh.sh_entsize;
 
 		for (idx = 0; idx < nr_syms; idx++) {
-			int curr_bind;
 			GElf_Sym sym;
-			Elf_Scn *sym_scn;
-			GElf_Shdr sym_sh;
 
 			if (!gelf_getsym(symbols, idx, &sym))
 				continue;
@@ -10779,58 +10832,48 @@ static long elf_find_func_offset(Elf *elf, const char *binary_path, const char *
 			if (!sname)
 				continue;
 
-			curr_bind = GELF_ST_BIND(sym.st_info);
-
-			/* User can specify func, func@@LIB or func@@LIB_VERSION. */
-			if (strncmp(sname, name, name_len) != 0)
-				continue;
-			/* ...but we don't want a search for "foo" to match 'foo2" also, so any
-			 * additional characters in sname should be of the form "@@LIB".
-			 */
-			if (!is_name_qualified && sname[name_len] != '\0' && sname[name_len] != '@')
-				continue;
+			ret = fn_match(elf, binary_path, sname, &sym, data);
+			if (ret < 0)
+				goto out;
+		}
+		if (fn_done(data))
+			break;
+	}
 
-			if (ret >= 0) {
-				/* handle multiple matches */
-				if (last_bind != STB_WEAK && curr_bind != STB_WEAK) {
-					/* Only accept one non-weak bind. */
-					pr_warn("elf: ambiguous match for '%s', '%s' in '%s'\n",
-						sname, name, binary_path);
-					ret = -LIBBPF_ERRNO__FORMAT;
-					goto out;
-				} else if (curr_bind == STB_WEAK) {
-					/* already have a non-weak bind, and
-					 * this is a weak bind, so ignore.
-					 */
-					continue;
-				}
-			}
+out:
+	return ret;
+}
 
-			/* Transform symbol's virtual address (absolute for
-			 * binaries and relative for shared libs) into file
-			 * offset, which is what kernel is expecting for
-			 * uprobe/uretprobe attachment.
-			 * See Documentation/trace/uprobetracer.rst for more
-			 * details.
-			 * This is done by looking up symbol's containing
-			 * section's header and using it's virtual address
-			 * (sh_addr) and corresponding file offset (sh_offset)
-			 * to transform sym.st_value (virtual address) into
-			 * desired final file offset.
-			 */
-			sym_scn = elf_getscn(elf, sym.st_shndx);
-			if (!sym_scn)
-				continue;
-			if (!gelf_getshdr(sym_scn, &sym_sh))
-				continue;
+/* Find offset of function name in the provided ELF object. "binary_path" is
+ * the path to the ELF binary represented by "elf", and only used for error
+ * reporting matters. "name" matches symbol name or name@@LIB for library
+ * functions.
+ */
+static long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name)
+{
+	struct elf_func_offset data = {
+		.name = name,
+		.last_bind = -1,
+		.name_len = strlen(name),
+		/* Does name specify "@@LIB"? */
+		.is_name_qualified = strstr(name, "@@") != NULL,
+	};
+	bool is_shared_lib;
+	GElf_Ehdr ehdr;
+	long ret;
+	int err;
 
-			ret = sym.st_value - sym_sh.sh_addr + sym_sh.sh_offset;
-			last_bind = curr_bind;
-		}
-		if (ret > 0)
-			break;
+	if (!gelf_getehdr(elf, &ehdr)) {
+		pr_warn("elf: failed to get ehdr from %s: %s\n", binary_path, elf_errmsg(-1));
+		ret = -LIBBPF_ERRNO__FORMAT;
+		goto out;
 	}
 
+	/* for shared lib case, we do not need to calculate relative offset */
+	is_shared_lib = ehdr.e_type == ET_DYN;
+
+	err = elf_for_each_symbol(elf, binary_path, single_match, single_done, &data);
+	ret = err < 0 ? (long) err : data.offset;
 	if (ret > 0) {
 		pr_debug("elf: symbol address match for '%s' in '%s': 0x%lx\n", name, binary_path,
 			 ret);
-- 
2.40.0




[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux