diff mbox series

[PATCHv3,bpf-next,10/26] libbpf: Add elf_resolve_syms_offsets function

Message ID 20230630083344.984305-11-jolsa@kernel.org (mailing list archive)
State Changes Requested
Delegated to: BPF
Headers show
Series bpf: Add multi uprobe link | expand

Checks

Context Check Description
netdev/series_format fail Series longer than 15 patches (and no cover letter)
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 8 this patch: 8
netdev/cc_maintainers warning 3 maintainers not CCed: kpsingh@kernel.org martin.lau@linux.dev song@kernel.org
netdev/build_clang fail Errors and warnings before: 18 this patch: 18
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 8 this patch: 8
netdev/checkpatch warning WARNING: line length of 82 exceeds 80 columns WARNING: line length of 85 exceeds 80 columns WARNING: line length of 90 exceeds 80 columns WARNING: line length of 94 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-PR success PR summary
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ${{ matrix.test }} on ${{ matrix.arch }} with ${{ matrix.toolchain_full }}
bpf/vmtest-bpf-next-VM_Test-2 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-3 success Logs for build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-4 success Logs for build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-5 success Logs for build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-6 fail Logs for build for x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-7 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-8 success Logs for veristat

Commit Message

Jiri Olsa June 30, 2023, 8:33 a.m. UTC
Adding elf_resolve_syms_offsets function that looks up
offsets for symbols specified in syms array argument.

Offsets are returned in allocated array with the 'cnt' size,
that needs to be released by the caller.

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 tools/lib/bpf/elf.c        | 105 +++++++++++++++++++++++++++++++++++++
 tools/lib/bpf/libbpf_elf.h |   2 +
 2 files changed, 107 insertions(+)

Comments

Andrii Nakryiko July 7, 2023, 3:48 a.m. UTC | #1
On Fri, Jun 30, 2023 at 1:35 AM Jiri Olsa <jolsa@kernel.org> wrote:
>
> Adding elf_resolve_syms_offsets function that looks up
> offsets for symbols specified in syms array argument.
>
> Offsets are returned in allocated array with the 'cnt' size,
> that needs to be released by the caller.
>
> Signed-off-by: Jiri Olsa <jolsa@kernel.org>
> ---
>  tools/lib/bpf/elf.c        | 105 +++++++++++++++++++++++++++++++++++++
>  tools/lib/bpf/libbpf_elf.h |   2 +
>  2 files changed, 107 insertions(+)
>
> diff --git a/tools/lib/bpf/elf.c b/tools/lib/bpf/elf.c
> index fcce4bd2478f..7e2f3b2e1fb6 100644
> --- a/tools/lib/bpf/elf.c
> +++ b/tools/lib/bpf/elf.c
> @@ -271,3 +271,108 @@ long elf_find_func_offset_from_file(const char *binary_path, const char *name)
>         elf_close(&elf_fd);
>         return ret;
>  }
> +
> +struct symbol {
> +       const char *name;
> +       int bind;
> +       int idx;
> +};
> +
> +static int symbol_cmp(const void *_a, const void *_b)
> +{
> +       const struct symbol *a = _a;
> +       const struct symbol *b = _b;

please, let's not (over)use leading underscores, x/y, s1/s2, whatever

> +
> +       return strcmp(a->name, b->name);
> +}
> +

probably worth leaving a comment that the caller should free offsets on success?

> +int elf_resolve_syms_offsets(const char *binary_path, int cnt,
> +                            const char **syms, unsigned long **poffsets)
> +{
> +       int sh_types[2] = { SHT_DYNSYM, SHT_SYMTAB };
> +       int err = 0, i, cnt_done = 0;
> +       unsigned long *offsets;
> +       struct symbol *symbols;
> +       struct elf_fd elf_fd;
> +
> +       err = elf_open(binary_path, &elf_fd);
> +       if (err)
> +               return err;
> +
> +       offsets = calloc(cnt, sizeof(*offsets));
> +       symbols = calloc(cnt, sizeof(*symbols));
> +
> +       if (!offsets || !symbols) {
> +               err = -ENOMEM;
> +               goto out;
> +       }
> +
> +       for (i = 0; i < cnt; i++) {
> +               symbols[i].name = syms[i];
> +               symbols[i].idx = i;
> +       }
> +
> +       qsort(symbols, cnt, sizeof(*symbols), symbol_cmp);
> +
> +       for (i = 0; i < ARRAY_SIZE(sh_types); i++) {
> +               struct elf_sym_iter iter;
> +               struct elf_sym *sym;
> +
> +               err = elf_sym_iter_new(&iter, elf_fd.elf, binary_path, sh_types[i], STT_FUNC);
> +               if (err) {
> +                       if (err == -ENOENT)
> +                               continue;
> +                       goto out;
> +               }

same nit, no need for nested ifs
> +
> +               while ((sym = elf_sym_iter_next(&iter))) {
> +                       int bind = GELF_ST_BIND(sym->sym.st_info);
> +                       struct symbol *found, tmp = {
> +                               .name = sym->name,
> +                       };
> +                       unsigned long *offset;
> +
> +                       found = bsearch(&tmp, symbols, cnt, sizeof(*symbols), symbol_cmp);
> +                       if (!found)
> +                               continue;
> +
> +                       offset = &offsets[found->idx];
> +                       if (*offset > 0) {
> +                               /* same offset, no problem */
> +                               if (*offset == elf_sym_offset(sym))
> +                                       continue;
> +                               /* handle multiple matches */
> +                               if (found->bind != STB_WEAK && bind != STB_WEAK) {
> +                                       /* Only accept one non-weak bind. */
> +                                       pr_warn("elf: ambiguous match foundr '%s', '%s' in '%s'\n",

typo: found

but also wouldn't sym->name and found->name be always the same? Maybe
log sym->name, previous *offset and newly calculated
elf_sym_offset(sym) instead?

> +                                               sym->name, found->name, binary_path);
> +                                       err = -LIBBPF_ERRNO__FORMAT;

I'd minimize using those custom libbpf-only errors, why not -ESRCH here?

> +                                       goto out;
> +                               } else if (bind == STB_WEAK) {
> +                                       /* already have a non-weak bind, and
> +                                        * this is a weak bind, so ignore.
> +                                        */
> +                                       continue;
> +                               }
> +                       } else {
> +                               cnt_done++;
> +                       }
> +                       *offset = elf_sym_offset(sym);

maybe remember elf_sym_offset() result in a variable? you are using it
in two (and with my suggestion above it will be three) places already

> +                       found->bind = bind;
> +               }
> +       }
> +
> +       if (cnt != cnt_done) {
> +               err = -ENOENT;
> +               goto out;
> +       }
> +
> +       *poffsets = offsets;
> +
> +out:
> +       free(symbols);
> +       if (err)
> +               free(offsets);
> +       elf_close(&elf_fd);
> +       return err;
> +}
> diff --git a/tools/lib/bpf/libbpf_elf.h b/tools/lib/bpf/libbpf_elf.h
> index c763ac35a85e..026c7b378727 100644
> --- a/tools/lib/bpf/libbpf_elf.h
> +++ b/tools/lib/bpf/libbpf_elf.h
> @@ -16,4 +16,6 @@ void elf_close(struct elf_fd *elf_fd);
>  long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name);
>  long elf_find_func_offset_from_file(const char *binary_path, const char *name);
>
> +int elf_resolve_syms_offsets(const char *binary_path, int cnt,
> +                            const char **syms, unsigned long **poffsets);
>  #endif /* *__LIBBPF_LIBBPF_ELF_H */
> --
> 2.41.0
>
Jiri Olsa July 11, 2023, 9:04 a.m. UTC | #2
On Thu, Jul 06, 2023 at 08:48:13PM -0700, Andrii Nakryiko wrote:
> On Fri, Jun 30, 2023 at 1:35 AM Jiri Olsa <jolsa@kernel.org> wrote:
> >
> > Adding elf_resolve_syms_offsets function that looks up
> > offsets for symbols specified in syms array argument.
> >
> > Offsets are returned in allocated array with the 'cnt' size,
> > that needs to be released by the caller.
> >
> > Signed-off-by: Jiri Olsa <jolsa@kernel.org>
> > ---
> >  tools/lib/bpf/elf.c        | 105 +++++++++++++++++++++++++++++++++++++
> >  tools/lib/bpf/libbpf_elf.h |   2 +
> >  2 files changed, 107 insertions(+)
> >
> > diff --git a/tools/lib/bpf/elf.c b/tools/lib/bpf/elf.c
> > index fcce4bd2478f..7e2f3b2e1fb6 100644
> > --- a/tools/lib/bpf/elf.c
> > +++ b/tools/lib/bpf/elf.c
> > @@ -271,3 +271,108 @@ long elf_find_func_offset_from_file(const char *binary_path, const char *name)
> >         elf_close(&elf_fd);
> >         return ret;
> >  }
> > +
> > +struct symbol {
> > +       const char *name;
> > +       int bind;
> > +       int idx;
> > +};
> > +
> > +static int symbol_cmp(const void *_a, const void *_b)
> > +{
> > +       const struct symbol *a = _a;
> > +       const struct symbol *b = _b;
> 
> please, let's not (over)use leading underscores, x/y, s1/s2, whatever

ok

> 
> > +
> > +       return strcmp(a->name, b->name);
> > +}
> > +
> 
> probably worth leaving a comment that the caller should free offsets on success?

ook

> 
> > +int elf_resolve_syms_offsets(const char *binary_path, int cnt,
> > +                            const char **syms, unsigned long **poffsets)
> > +{
> > +       int sh_types[2] = { SHT_DYNSYM, SHT_SYMTAB };
> > +       int err = 0, i, cnt_done = 0;
> > +       unsigned long *offsets;
> > +       struct symbol *symbols;
> > +       struct elf_fd elf_fd;
> > +
> > +       err = elf_open(binary_path, &elf_fd);
> > +       if (err)
> > +               return err;
> > +
> > +       offsets = calloc(cnt, sizeof(*offsets));
> > +       symbols = calloc(cnt, sizeof(*symbols));
> > +
> > +       if (!offsets || !symbols) {
> > +               err = -ENOMEM;
> > +               goto out;
> > +       }
> > +
> > +       for (i = 0; i < cnt; i++) {
> > +               symbols[i].name = syms[i];
> > +               symbols[i].idx = i;
> > +       }
> > +
> > +       qsort(symbols, cnt, sizeof(*symbols), symbol_cmp);
> > +
> > +       for (i = 0; i < ARRAY_SIZE(sh_types); i++) {
> > +               struct elf_sym_iter iter;
> > +               struct elf_sym *sym;
> > +
> > +               err = elf_sym_iter_new(&iter, elf_fd.elf, binary_path, sh_types[i], STT_FUNC);
> > +               if (err) {
> > +                       if (err == -ENOENT)
> > +                               continue;
> > +                       goto out;
> > +               }
> 
> same nit, no need for nested ifs

ok

> > +
> > +               while ((sym = elf_sym_iter_next(&iter))) {
> > +                       int bind = GELF_ST_BIND(sym->sym.st_info);
> > +                       struct symbol *found, tmp = {
> > +                               .name = sym->name,
> > +                       };
> > +                       unsigned long *offset;
> > +
> > +                       found = bsearch(&tmp, symbols, cnt, sizeof(*symbols), symbol_cmp);
> > +                       if (!found)
> > +                               continue;
> > +
> > +                       offset = &offsets[found->idx];
> > +                       if (*offset > 0) {
> > +                               /* same offset, no problem */
> > +                               if (*offset == elf_sym_offset(sym))
> > +                                       continue;
> > +                               /* handle multiple matches */
> > +                               if (found->bind != STB_WEAK && bind != STB_WEAK) {
> > +                                       /* Only accept one non-weak bind. */
> > +                                       pr_warn("elf: ambiguous match foundr '%s', '%s' in '%s'\n",
> 
> typo: found
> 
> but also wouldn't sym->name and found->name be always the same? Maybe
> log sym->name, previous *offset and newly calculated
> elf_sym_offset(sym) instead?

ok

> 
> > +                                               sym->name, found->name, binary_path);
> > +                                       err = -LIBBPF_ERRNO__FORMAT;
> 
> I'd minimize using those custom libbpf-only errors, why not -ESRCH here?

ok

> 
> > +                                       goto out;
> > +                               } else if (bind == STB_WEAK) {
> > +                                       /* already have a non-weak bind, and
> > +                                        * this is a weak bind, so ignore.
> > +                                        */
> > +                                       continue;
> > +                               }
> > +                       } else {
> > +                               cnt_done++;
> > +                       }
> > +                       *offset = elf_sym_offset(sym);
> 
> maybe remember elf_sym_offset() result in a variable? you are using it
> in two (and with my suggestion above it will be three) places already

ok


jirka
diff mbox series

Patch

diff --git a/tools/lib/bpf/elf.c b/tools/lib/bpf/elf.c
index fcce4bd2478f..7e2f3b2e1fb6 100644
--- a/tools/lib/bpf/elf.c
+++ b/tools/lib/bpf/elf.c
@@ -271,3 +271,108 @@  long elf_find_func_offset_from_file(const char *binary_path, const char *name)
 	elf_close(&elf_fd);
 	return ret;
 }
+
+struct symbol {
+	const char *name;
+	int bind;
+	int idx;
+};
+
+static int symbol_cmp(const void *_a, const void *_b)
+{
+	const struct symbol *a = _a;
+	const struct symbol *b = _b;
+
+	return strcmp(a->name, b->name);
+}
+
+int elf_resolve_syms_offsets(const char *binary_path, int cnt,
+			     const char **syms, unsigned long **poffsets)
+{
+	int sh_types[2] = { SHT_DYNSYM, SHT_SYMTAB };
+	int err = 0, i, cnt_done = 0;
+	unsigned long *offsets;
+	struct symbol *symbols;
+	struct elf_fd elf_fd;
+
+	err = elf_open(binary_path, &elf_fd);
+	if (err)
+		return err;
+
+	offsets = calloc(cnt, sizeof(*offsets));
+	symbols = calloc(cnt, sizeof(*symbols));
+
+	if (!offsets || !symbols) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < cnt; i++) {
+		symbols[i].name = syms[i];
+		symbols[i].idx = i;
+	}
+
+	qsort(symbols, cnt, sizeof(*symbols), symbol_cmp);
+
+	for (i = 0; i < ARRAY_SIZE(sh_types); i++) {
+		struct elf_sym_iter iter;
+		struct elf_sym *sym;
+
+		err = elf_sym_iter_new(&iter, elf_fd.elf, binary_path, sh_types[i], STT_FUNC);
+		if (err) {
+			if (err == -ENOENT)
+				continue;
+			goto out;
+		}
+
+		while ((sym = elf_sym_iter_next(&iter))) {
+			int bind = GELF_ST_BIND(sym->sym.st_info);
+			struct symbol *found, tmp = {
+				.name = sym->name,
+			};
+			unsigned long *offset;
+
+			found = bsearch(&tmp, symbols, cnt, sizeof(*symbols), symbol_cmp);
+			if (!found)
+				continue;
+
+			offset = &offsets[found->idx];
+			if (*offset > 0) {
+				/* same offset, no problem */
+				if (*offset == elf_sym_offset(sym))
+					continue;
+				/* handle multiple matches */
+				if (found->bind != STB_WEAK && bind != STB_WEAK) {
+					/* Only accept one non-weak bind. */
+					pr_warn("elf: ambiguous match foundr '%s', '%s' in '%s'\n",
+						sym->name, found->name, binary_path);
+					err = -LIBBPF_ERRNO__FORMAT;
+					goto out;
+				} else if (bind == STB_WEAK) {
+					/* already have a non-weak bind, and
+					 * this is a weak bind, so ignore.
+					 */
+					continue;
+				}
+			} else {
+				cnt_done++;
+			}
+			*offset = elf_sym_offset(sym);
+			found->bind = bind;
+		}
+	}
+
+	if (cnt != cnt_done) {
+		err = -ENOENT;
+		goto out;
+	}
+
+	*poffsets = offsets;
+
+out:
+	free(symbols);
+	if (err)
+		free(offsets);
+	elf_close(&elf_fd);
+	return err;
+}
diff --git a/tools/lib/bpf/libbpf_elf.h b/tools/lib/bpf/libbpf_elf.h
index c763ac35a85e..026c7b378727 100644
--- a/tools/lib/bpf/libbpf_elf.h
+++ b/tools/lib/bpf/libbpf_elf.h
@@ -16,4 +16,6 @@  void elf_close(struct elf_fd *elf_fd);
 long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name);
 long elf_find_func_offset_from_file(const char *binary_path, const char *name);
 
+int elf_resolve_syms_offsets(const char *binary_path, int cnt,
+			     const char **syms, unsigned long **poffsets);
 #endif /* *__LIBBPF_LIBBPF_ELF_H */