Message ID | 20230630083344.984305-8-jolsa@kernel.org (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | BPF |
Headers | show |
Series | bpf: Add multi uprobe link | expand |
On Fri, Jun 30, 2023 at 1:35 AM Jiri Olsa <jolsa@kernel.org> wrote: > > Adding new elf object that will contain elf related functions. > There's no functional change. > > Suggested-by: Andrii Nakryiko <andrii@kernel.org> > Signed-off-by: Jiri Olsa <jolsa@kernel.org> > --- > tools/lib/bpf/Build | 2 +- > tools/lib/bpf/elf.c | 198 +++++++++++++++++++++++++++++++++++++ > tools/lib/bpf/libbpf.c | 186 +--------------------------------- > tools/lib/bpf/libbpf_elf.h | 11 +++ > 4 files changed, 211 insertions(+), 186 deletions(-) > create mode 100644 tools/lib/bpf/elf.c > create mode 100644 tools/lib/bpf/libbpf_elf.h > [...] > diff --git a/tools/lib/bpf/libbpf_elf.h b/tools/lib/bpf/libbpf_elf.h > new file mode 100644 > index 000000000000..1b652220fabf > --- /dev/null > +++ b/tools/lib/bpf/libbpf_elf.h > @@ -0,0 +1,11 @@ > +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ > + > +#ifndef __LIBBPF_LIBBPF_ELF_H > +#define __LIBBPF_LIBBPF_ELF_H > + > +#include <libelf.h> > + > +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); > + > +#endif /* *__LIBBPF_LIBBPF_ELF_H */ we have libbpf_internal.h, let's put all this there for now, it's already all the internal stuff together, I don't know if separate header with few functions gives us much > -- > 2.41.0 >
On Fri, Jun 30, 2023 at 1:35 AM Jiri Olsa <jolsa@kernel.org> wrote: > > Adding new elf object that will contain elf related functions. > There's no functional change. > > Suggested-by: Andrii Nakryiko <andrii@kernel.org> > Signed-off-by: Jiri Olsa <jolsa@kernel.org> > --- > tools/lib/bpf/Build | 2 +- > tools/lib/bpf/elf.c | 198 +++++++++++++++++++++++++++++++++++++ > tools/lib/bpf/libbpf.c | 186 +--------------------------------- > tools/lib/bpf/libbpf_elf.h | 11 +++ > 4 files changed, 211 insertions(+), 186 deletions(-) > create mode 100644 tools/lib/bpf/elf.c > create mode 100644 tools/lib/bpf/libbpf_elf.h > > diff --git a/tools/lib/bpf/Build b/tools/lib/bpf/Build > index b8b0a6369363..2d0c282c8588 100644 > --- a/tools/lib/bpf/Build > +++ b/tools/lib/bpf/Build > @@ -1,4 +1,4 @@ > libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o \ > netlink.o bpf_prog_linfo.o libbpf_probes.o hashmap.o \ > btf_dump.o ringbuf.o strset.o linker.o gen_loader.o relo_core.o \ > - usdt.o zip.o > + usdt.o zip.o elf.o > diff --git a/tools/lib/bpf/elf.c b/tools/lib/bpf/elf.c > new file mode 100644 > index 000000000000..2b62b4af28ce > --- /dev/null > +++ b/tools/lib/bpf/elf.c > @@ -0,0 +1,198 @@ > +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) > + > +#include <libelf.h> > +#include <gelf.h> > +#include <fcntl.h> > +#include <linux/kernel.h> do you know why we need linux/kernel.h include? is it to get __u32 and other typedefs? > + > +#include "libbpf_elf.h" > +#include "libbpf_internal.h" > +#include "str_error.h" > + > +#define STRERR_BUFSIZE 128 > + [...]
On Thu, Jul 06, 2023 at 04:02:22PM -0700, Andrii Nakryiko wrote: > On Fri, Jun 30, 2023 at 1:35 AM Jiri Olsa <jolsa@kernel.org> wrote: > > > > Adding new elf object that will contain elf related functions. > > There's no functional change. > > > > Suggested-by: Andrii Nakryiko <andrii@kernel.org> > > Signed-off-by: Jiri Olsa <jolsa@kernel.org> > > --- > > tools/lib/bpf/Build | 2 +- > > tools/lib/bpf/elf.c | 198 +++++++++++++++++++++++++++++++++++++ > > tools/lib/bpf/libbpf.c | 186 +--------------------------------- > > tools/lib/bpf/libbpf_elf.h | 11 +++ > > 4 files changed, 211 insertions(+), 186 deletions(-) > > create mode 100644 tools/lib/bpf/elf.c > > create mode 100644 tools/lib/bpf/libbpf_elf.h > > > > [...] > > > diff --git a/tools/lib/bpf/libbpf_elf.h b/tools/lib/bpf/libbpf_elf.h > > new file mode 100644 > > index 000000000000..1b652220fabf > > --- /dev/null > > +++ b/tools/lib/bpf/libbpf_elf.h > > @@ -0,0 +1,11 @@ > > +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ > > + > > +#ifndef __LIBBPF_LIBBPF_ELF_H > > +#define __LIBBPF_LIBBPF_ELF_H > > + > > +#include <libelf.h> > > + > > +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); > > + > > +#endif /* *__LIBBPF_LIBBPF_ELF_H */ > > we have libbpf_internal.h, let's put all this there for now, it's > already all the internal stuff together, I don't know if separate > header with few functions gives us much there's more functions coming later in the patchset struct elf_fd { Elf *elf; int fd; }; int elf_open(const char *binary_path, struct elf_fd *elf_fd); void elf_close(struct elf_fd *elf_fd); int elf_resolve_syms_offsets(const char *binary_path, int cnt, const char **syms, unsigned long **poffsets); int elf_resolve_pattern_offsets(const char *binary_path, const char *pattern, unsigned long **poffsets, size_t *pcnt); and there's probably more elf helpers to eventually move in: libbpf.c:static const char *elf_sym_str(const struct bpf_object *obj, size_t off); libbpf.c:static const char *elf_sec_str(const struct bpf_object *obj, size_t off); libbpf.c:static Elf_Scn *elf_sec_by_idx(const struct bpf_object *obj, size_t idx); libbpf.c:static Elf_Scn *elf_sec_by_name(const struct bpf_object *obj, const char *name); libbpf.c:static Elf64_Shdr *elf_sec_hdr(const struct bpf_object *obj, Elf_Scn *scn); libbpf.c:static const char *elf_sec_name(const struct bpf_object *obj, Elf_Scn *scn); libbpf.c:static Elf_Data *elf_sec_data(const struct bpf_object *obj, Elf_Scn *scn); libbpf.c:static Elf64_Sym *elf_sym_by_idx(const struct bpf_object *obj, size_t idx); libbpf.c:static Elf64_Rel *elf_rel_by_idx(Elf_Data *data, size_t idx); usdt.c:static int find_elf_sec_by_name(Elf *elf, const char *sec_name, GElf_Shdr *shdr, Elf_Scn **scn) 'struct elf_seg' stuff usdt.c:static int cmp_elf_segs(const void *_a, const void *_b) usdt.c:static int parse_elf_segs(Elf *elf, const char *path, struct elf_seg **segs, size_t *seg_cnt) usdt.c:static int parse_vma_segs(int pid, const char *lib_path, struct elf_seg **segs, size_t *seg_cnt) usdt.c:static struct elf_seg *find_elf_seg(struct elf_seg *segs, size_t seg_cnt, long virtaddr) usdt.c:static struct elf_seg *find_vma_seg(struct elf_seg *segs, size_t seg_cnt, long offset) but I can add the new header file later in follow up changes when we have more elf functions in jirka
On Thu, Jul 06, 2023 at 04:03:22PM -0700, Andrii Nakryiko wrote: > On Fri, Jun 30, 2023 at 1:35 AM Jiri Olsa <jolsa@kernel.org> wrote: > > > > Adding new elf object that will contain elf related functions. > > There's no functional change. > > > > Suggested-by: Andrii Nakryiko <andrii@kernel.org> > > Signed-off-by: Jiri Olsa <jolsa@kernel.org> > > --- > > tools/lib/bpf/Build | 2 +- > > tools/lib/bpf/elf.c | 198 +++++++++++++++++++++++++++++++++++++ > > tools/lib/bpf/libbpf.c | 186 +--------------------------------- > > tools/lib/bpf/libbpf_elf.h | 11 +++ > > 4 files changed, 211 insertions(+), 186 deletions(-) > > create mode 100644 tools/lib/bpf/elf.c > > create mode 100644 tools/lib/bpf/libbpf_elf.h > > > > diff --git a/tools/lib/bpf/Build b/tools/lib/bpf/Build > > index b8b0a6369363..2d0c282c8588 100644 > > --- a/tools/lib/bpf/Build > > +++ b/tools/lib/bpf/Build > > @@ -1,4 +1,4 @@ > > libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o \ > > netlink.o bpf_prog_linfo.o libbpf_probes.o hashmap.o \ > > btf_dump.o ringbuf.o strset.o linker.o gen_loader.o relo_core.o \ > > - usdt.o zip.o > > + usdt.o zip.o elf.o > > diff --git a/tools/lib/bpf/elf.c b/tools/lib/bpf/elf.c > > new file mode 100644 > > index 000000000000..2b62b4af28ce > > --- /dev/null > > +++ b/tools/lib/bpf/elf.c > > @@ -0,0 +1,198 @@ > > +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) > > + > > +#include <libelf.h> > > +#include <gelf.h> > > +#include <fcntl.h> > > +#include <linux/kernel.h> > > do you know why we need linux/kernel.h include? is it to get __u32 and > other typedefs? it's for the ARRAY_SIZE macro jirka > > > + > > +#include "libbpf_elf.h" > > +#include "libbpf_internal.h" > > +#include "str_error.h" > > + > > +#define STRERR_BUFSIZE 128 > > + > > [...]
On Tue, Jul 11, 2023 at 2:05 AM Jiri Olsa <olsajiri@gmail.com> wrote: > > On Thu, Jul 06, 2023 at 04:02:22PM -0700, Andrii Nakryiko wrote: > > On Fri, Jun 30, 2023 at 1:35 AM Jiri Olsa <jolsa@kernel.org> wrote: > > > > > > Adding new elf object that will contain elf related functions. > > > There's no functional change. > > > > > > Suggested-by: Andrii Nakryiko <andrii@kernel.org> > > > Signed-off-by: Jiri Olsa <jolsa@kernel.org> > > > --- > > > tools/lib/bpf/Build | 2 +- > > > tools/lib/bpf/elf.c | 198 +++++++++++++++++++++++++++++++++++++ > > > tools/lib/bpf/libbpf.c | 186 +--------------------------------- > > > tools/lib/bpf/libbpf_elf.h | 11 +++ > > > 4 files changed, 211 insertions(+), 186 deletions(-) > > > create mode 100644 tools/lib/bpf/elf.c > > > create mode 100644 tools/lib/bpf/libbpf_elf.h > > > > > > > [...] > > > > > diff --git a/tools/lib/bpf/libbpf_elf.h b/tools/lib/bpf/libbpf_elf.h > > > new file mode 100644 > > > index 000000000000..1b652220fabf > > > --- /dev/null > > > +++ b/tools/lib/bpf/libbpf_elf.h > > > @@ -0,0 +1,11 @@ > > > +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ > > > + > > > +#ifndef __LIBBPF_LIBBPF_ELF_H > > > +#define __LIBBPF_LIBBPF_ELF_H > > > + > > > +#include <libelf.h> > > > + > > > +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); > > > + > > > +#endif /* *__LIBBPF_LIBBPF_ELF_H */ > > > > we have libbpf_internal.h, let's put all this there for now, it's > > already all the internal stuff together, I don't know if separate > > header with few functions gives us much > > there's more functions coming later in the patchset > > struct elf_fd { > Elf *elf; > int fd; > }; > > int elf_open(const char *binary_path, struct elf_fd *elf_fd); > void elf_close(struct elf_fd *elf_fd); > > int elf_resolve_syms_offsets(const char *binary_path, int cnt, > const char **syms, unsigned long **poffsets); > > int elf_resolve_pattern_offsets(const char *binary_path, const char *pattern, > unsigned long **poffsets, size_t *pcnt); > > > and there's probably more elf helpers to eventually move in: > > libbpf.c:static const char *elf_sym_str(const struct bpf_object *obj, size_t off); > libbpf.c:static const char *elf_sec_str(const struct bpf_object *obj, size_t off); > libbpf.c:static Elf_Scn *elf_sec_by_idx(const struct bpf_object *obj, size_t idx); > libbpf.c:static Elf_Scn *elf_sec_by_name(const struct bpf_object *obj, const char *name); > libbpf.c:static Elf64_Shdr *elf_sec_hdr(const struct bpf_object *obj, Elf_Scn *scn); > libbpf.c:static const char *elf_sec_name(const struct bpf_object *obj, Elf_Scn *scn); > libbpf.c:static Elf_Data *elf_sec_data(const struct bpf_object *obj, Elf_Scn *scn); > libbpf.c:static Elf64_Sym *elf_sym_by_idx(const struct bpf_object *obj, size_t idx); > libbpf.c:static Elf64_Rel *elf_rel_by_idx(Elf_Data *data, size_t idx); > yep, I was anticipating that these will move as well. But I think it's fine if they all stay in libbpf_internal.h, IMO. I'd rather not have many small internal header for no good reason (like we have str_error.h right now, with a single func declaration) > usdt.c:static int find_elf_sec_by_name(Elf *elf, const char *sec_name, GElf_Shdr *shdr, Elf_Scn **scn) > > 'struct elf_seg' stuff > > usdt.c:static int cmp_elf_segs(const void *_a, const void *_b) > usdt.c:static int parse_elf_segs(Elf *elf, const char *path, struct elf_seg **segs, size_t *seg_cnt) > usdt.c:static int parse_vma_segs(int pid, const char *lib_path, struct elf_seg **segs, size_t *seg_cnt) > usdt.c:static struct elf_seg *find_elf_seg(struct elf_seg *segs, size_t seg_cnt, long virtaddr) > usdt.c:static struct elf_seg *find_vma_seg(struct elf_seg *segs, size_t seg_cnt, long offset) > > > but I can add the new header file later in follow up changes when > we have more elf functions in see above, I don't think we should, let's stick to libbpf_internal.h for now > > jirka
diff --git a/tools/lib/bpf/Build b/tools/lib/bpf/Build index b8b0a6369363..2d0c282c8588 100644 --- a/tools/lib/bpf/Build +++ b/tools/lib/bpf/Build @@ -1,4 +1,4 @@ libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o str_error.o \ netlink.o bpf_prog_linfo.o libbpf_probes.o hashmap.o \ btf_dump.o ringbuf.o strset.o linker.o gen_loader.o relo_core.o \ - usdt.o zip.o + usdt.o zip.o elf.o diff --git a/tools/lib/bpf/elf.c b/tools/lib/bpf/elf.c new file mode 100644 index 000000000000..2b62b4af28ce --- /dev/null +++ b/tools/lib/bpf/elf.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) + +#include <libelf.h> +#include <gelf.h> +#include <fcntl.h> +#include <linux/kernel.h> + +#include "libbpf_elf.h" +#include "libbpf_internal.h" +#include "str_error.h" + +#define STRERR_BUFSIZE 128 + +/* Return next ELF section of sh_type after scn, or first of that type if scn is NULL. */ +static Elf_Scn *elf_find_next_scn_by_type(Elf *elf, int sh_type, Elf_Scn *scn) +{ + while ((scn = elf_nextscn(elf, scn)) != NULL) { + GElf_Shdr sh; + + if (!gelf_getshdr(scn, &sh)) + continue; + if (sh.sh_type == sh_type) + return 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. + */ +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; + size_t name_len; + GElf_Ehdr ehdr; + + 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; + + name_len = strlen(name); + /* Does name specify "@@LIB"? */ + is_name_qualified = strstr(name, "@@") != NULL; + + /* 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 + * linked binary may not have SHT_DYMSYM, so absence of a section should not be + * reported as a warning/error. + */ + for (i = 0; i < ARRAY_SIZE(sh_types); i++) { + size_t nr_syms, strtabidx, idx; + Elf_Data *symbols = NULL; + Elf_Scn *scn = NULL; + int last_bind = -1; + const char *sname; + GElf_Shdr sh; + + scn = elf_find_next_scn_by_type(elf, sh_types[i], NULL); + if (!scn) { + pr_debug("elf: failed to find symbol table ELF sections in '%s'\n", + binary_path); + continue; + } + if (!gelf_getshdr(scn, &sh)) + continue; + strtabidx = sh.sh_link; + symbols = elf_getdata(scn, 0); + if (!symbols) { + pr_warn("elf: failed to get symbols for symtab section in '%s': %s\n", + binary_path, elf_errmsg(-1)); + ret = -LIBBPF_ERRNO__FORMAT; + goto out; + } + 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; + + if (GELF_ST_TYPE(sym.st_info) != STT_FUNC) + continue; + + sname = elf_strptr(elf, strtabidx, sym.st_name); + 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; + + 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; + } + } + + /* 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; + + ret = sym.st_value - sym_sh.sh_addr + sym_sh.sh_offset; + last_bind = curr_bind; + } + if (ret > 0) + break; + } + + if (ret > 0) { + pr_debug("elf: symbol address match for '%s' in '%s': 0x%lx\n", name, binary_path, + ret); + } else { + if (ret == 0) { + pr_warn("elf: '%s' is 0 in symtab for '%s': %s\n", name, binary_path, + is_shared_lib ? "should not be 0 in a shared library" : + "try using shared library path instead"); + ret = -ENOENT; + } else { + pr_warn("elf: failed to find symbol '%s' in '%s'\n", name, binary_path); + } + } +out: + return ret; +} + +/* Find offset of function name in ELF object specified by path. "name" matches + * symbol name or name@@LIB for library functions. + */ +long elf_find_func_offset_from_file(const char *binary_path, const char *name) +{ + char errmsg[STRERR_BUFSIZE]; + long ret = -ENOENT; + Elf *elf; + int fd; + + fd = open(binary_path, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + ret = -errno; + pr_warn("failed to open %s: %s\n", binary_path, + libbpf_strerror_r(ret, errmsg, sizeof(errmsg))); + return ret; + } + elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); + if (!elf) { + pr_warn("elf: could not read elf from %s: %s\n", binary_path, elf_errmsg(-1)); + close(fd); + return -LIBBPF_ERRNO__FORMAT; + } + + ret = elf_find_func_offset(elf, binary_path, name); + elf_end(elf); + close(fd); + return ret; +} + diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 53dcf25c4878..093add8124d8 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -54,6 +54,7 @@ #include "hashmap.h" #include "bpf_gen_internal.h" #include "zip.h" +#include "libbpf_elf.h" #ifndef BPF_FS_MAGIC #define BPF_FS_MAGIC 0xcafe4a11 @@ -10811,191 +10812,6 @@ static int perf_event_uprobe_open_legacy(const char *probe_name, bool retprobe, return err; } -/* Return next ELF section of sh_type after scn, or first of that type if scn is NULL. */ -static Elf_Scn *elf_find_next_scn_by_type(Elf *elf, int sh_type, Elf_Scn *scn) -{ - while ((scn = elf_nextscn(elf, scn)) != NULL) { - GElf_Shdr sh; - - if (!gelf_getshdr(scn, &sh)) - continue; - if (sh.sh_type == sh_type) - return 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; - size_t name_len; - GElf_Ehdr ehdr; - - 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; - - name_len = strlen(name); - /* Does name specify "@@LIB"? */ - is_name_qualified = strstr(name, "@@") != NULL; - - /* 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 - * linked binary may not have SHT_DYMSYM, so absence of a section should not be - * reported as a warning/error. - */ - for (i = 0; i < ARRAY_SIZE(sh_types); i++) { - size_t nr_syms, strtabidx, idx; - Elf_Data *symbols = NULL; - Elf_Scn *scn = NULL; - int last_bind = -1; - const char *sname; - GElf_Shdr sh; - - scn = elf_find_next_scn_by_type(elf, sh_types[i], NULL); - if (!scn) { - pr_debug("elf: failed to find symbol table ELF sections in '%s'\n", - binary_path); - continue; - } - if (!gelf_getshdr(scn, &sh)) - continue; - strtabidx = sh.sh_link; - symbols = elf_getdata(scn, 0); - if (!symbols) { - pr_warn("elf: failed to get symbols for symtab section in '%s': %s\n", - binary_path, elf_errmsg(-1)); - ret = -LIBBPF_ERRNO__FORMAT; - goto out; - } - 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; - - if (GELF_ST_TYPE(sym.st_info) != STT_FUNC) - continue; - - sname = elf_strptr(elf, strtabidx, sym.st_name); - 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; - - 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; - } - } - - /* 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; - - ret = sym.st_value - sym_sh.sh_addr + sym_sh.sh_offset; - last_bind = curr_bind; - } - if (ret > 0) - break; - } - - if (ret > 0) { - pr_debug("elf: symbol address match for '%s' in '%s': 0x%lx\n", name, binary_path, - ret); - } else { - if (ret == 0) { - pr_warn("elf: '%s' is 0 in symtab for '%s': %s\n", name, binary_path, - is_shared_lib ? "should not be 0 in a shared library" : - "try using shared library path instead"); - ret = -ENOENT; - } else { - pr_warn("elf: failed to find symbol '%s' in '%s'\n", name, binary_path); - } - } -out: - return ret; -} - -/* Find offset of function name in ELF object specified by path. "name" matches - * symbol name or name@@LIB for library functions. - */ -static long elf_find_func_offset_from_file(const char *binary_path, const char *name) -{ - char errmsg[STRERR_BUFSIZE]; - long ret = -ENOENT; - Elf *elf; - int fd; - - fd = open(binary_path, O_RDONLY | O_CLOEXEC); - if (fd < 0) { - ret = -errno; - pr_warn("failed to open %s: %s\n", binary_path, - libbpf_strerror_r(ret, errmsg, sizeof(errmsg))); - return ret; - } - elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); - if (!elf) { - pr_warn("elf: could not read elf from %s: %s\n", binary_path, elf_errmsg(-1)); - close(fd); - return -LIBBPF_ERRNO__FORMAT; - } - - ret = elf_find_func_offset(elf, binary_path, name); - elf_end(elf); - close(fd); - return ret; -} - /* Find offset of function name in archive specified by path. Currently * supported are .zip files that do not compress their contents, as used on * Android in the form of APKs, for example. "file_name" is the name of the ELF diff --git a/tools/lib/bpf/libbpf_elf.h b/tools/lib/bpf/libbpf_elf.h new file mode 100644 index 000000000000..1b652220fabf --- /dev/null +++ b/tools/lib/bpf/libbpf_elf.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ + +#ifndef __LIBBPF_LIBBPF_ELF_H +#define __LIBBPF_LIBBPF_ELF_H + +#include <libelf.h> + +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); + +#endif /* *__LIBBPF_LIBBPF_ELF_H */
Adding new elf object that will contain elf related functions. There's no functional change. Suggested-by: Andrii Nakryiko <andrii@kernel.org> Signed-off-by: Jiri Olsa <jolsa@kernel.org> --- tools/lib/bpf/Build | 2 +- tools/lib/bpf/elf.c | 198 +++++++++++++++++++++++++++++++++++++ tools/lib/bpf/libbpf.c | 186 +--------------------------------- tools/lib/bpf/libbpf_elf.h | 11 +++ 4 files changed, 211 insertions(+), 186 deletions(-) create mode 100644 tools/lib/bpf/elf.c create mode 100644 tools/lib/bpf/libbpf_elf.h