Add support for name-based attach to Userland Static-Defined Tracing (USDT) probes via lookup of ELF notes associated with probe definition. ELF notes are consulted for probe offset, and - if "is-enabled" style of probing is in use - semaphore offset. Signed-off-by: Alan Maguire <alan.maguire@xxxxxxxxxx> --- tools/lib/bpf/libbpf.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++---- tools/lib/bpf/libbpf.h | 9 +++++- 2 files changed, 86 insertions(+), 7 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index bccc26a..cdcd799 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -10275,6 +10275,52 @@ static ssize_t find_elf_func_offset(Elf *elf, const char *name) return ret; } +struct sdt_note { + uint64_t pc; + uint64_t base_addr; + uint64_t semaphore; + const char provider_probe[256]; +}; + +/* Find offset of USDT probe in object specified in ELF notes. + * Note may also specify semaphore offset, record value in *semaphore_offp. + */ +static ssize_t find_elf_usdt_offset(Elf *elf, const char *provider, + const char *name, ssize_t *semaphore_offp) +{ + Elf_Data *data = NULL; + Elf_Scn *scn = NULL; + + while ((scn = find_elfscn(elf, SHT_NOTE, scn)) > 0) { + while ((data = elf_getdata(scn, data)) != 0) { + size_t name_off, desc_off, off; + GElf_Nhdr nhdr; + + while ((off = gelf_getnote(data, off, &nhdr, + &name_off, &desc_off)) != 0) { + struct sdt_note *sdt_note; + const char *probe; + + if (nhdr.n_namesz != 8 || + memcmp((char *)data->d_buf + name_off, "stapsdt", 8) != 0) + continue; + sdt_note = (struct sdt_note *)(data->d_buf + desc_off); + if (strcmp(provider, sdt_note->provider_probe) != 0) + continue; + /* probe is after null-terminated provider */ + probe = sdt_note->provider_probe + + strlen(sdt_note->provider_probe) + 1; + if (strcmp(probe, name) != 0) + continue; + + *semaphore_offp = (ssize_t)sdt_note->semaphore; + return (ssize_t)sdt_note->pc; + } + } + } + return -ENOENT; +} + LIBBPF_API struct bpf_link * bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid, const char *binary_path, size_t func_offset, @@ -10286,7 +10332,7 @@ static ssize_t find_elf_func_offset(Elf *elf, const char *name) size_t ref_ctr_off; int pfd, err; bool retprobe, legacy; - const char *func_name; + const char *func_name, *usdt_name, *usdt_provider; if (!OPTS_VALID(opts, bpf_uprobe_opts)) return libbpf_err_ptr(-EINVAL); @@ -10296,11 +10342,25 @@ static ssize_t find_elf_func_offset(Elf *elf, const char *name) pe_opts.bpf_cookie = OPTS_GET(opts, bpf_cookie, 0); func_name = OPTS_GET(opts, func_name, NULL); - if (func_name) { - ssize_t sym_off, rel_off; + usdt_provider = OPTS_GET(opts, usdt_provider, NULL); + usdt_name = OPTS_GET(opts, usdt_name, NULL); + if (func_name || usdt_name) { + ssize_t sym_off, rel_off, semaphore_off = 0; Elf *elf; int fd; + if (func_name && usdt_name) { + pr_warn("both func_name and usdt_name cannot be specified\n"); + return libbpf_err_ptr(-EINVAL); + } + if (usdt_name && (func_offset || ref_ctr_off)) { + pr_warn("func_offset argument, ref_ctr_off option should be 0 when usdt_name is used\n"); + return libbpf_err_ptr(-EINVAL); + } + if (usdt_name && !usdt_provider) { + pr_warn("usdt_provider and usdt_name must be supplied\n"); + return libbpf_err_ptr(-EINVAL); + } if (pid == -1) { /* system-wide probing is not supported; we need * a running process to determine offsets. @@ -10330,20 +10390,32 @@ static ssize_t find_elf_func_offset(Elf *elf, const char *name) close(fd); return libbpf_err_ptr(-LIBBPF_ERRNO__FORMAT); } - sym_off = find_elf_func_offset(elf, func_name); + if (func_name) + sym_off = find_elf_func_offset(elf, func_name); + else + sym_off = find_elf_usdt_offset(elf, usdt_provider, usdt_name, + &semaphore_off); close(fd); elf_end(elf); if (sym_off < 0) { - pr_debug("could not find sym offset for %s\n", func_name); + pr_debug("could not find sym offset for %s\n", func_name ?: usdt_name); return libbpf_err_ptr(sym_off); } rel_off = get_rel_offset(pid, sym_off); if (rel_off < 0) { pr_debug("could not find relative offset for %s at 0x%lx\n", - func_name, sym_off); + func_name ?: usdt_name, sym_off); return libbpf_err_ptr(rel_off); } func_offset += (size_t)rel_off; + if (semaphore_off) { + ref_ctr_off = get_rel_offset(pid, semaphore_off); + if (ref_ctr_off < 0) { + pr_debug("could not find relative offset for semaphore for %s at 0x%lx\n", + usdt_name, semaphore_off); + return libbpf_err_ptr(ref_ctr_off); + } + } } legacy = determine_uprobe_perf_type() < 0; diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 40cb5ae..fcad6b1 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -439,9 +439,16 @@ struct bpf_uprobe_opts { * argument to specify argument _within_ the function. */ const char *func_name; + /* name of USDT (Userland Static-Defined Tracing) provider/probe. + * Offsets of USDT probe and associated semaphore (if any) are found + * in ELF notes. Note that if usdt_name is specified, func_offset + * argument and ref_ctr_offset values should be zero. + */ + const char *usdt_provider; + const char *usdt_name; size_t :0; }; -#define bpf_uprobe_opts__last_field func_name +#define bpf_uprobe_opts__last_field usdt_name /** * @brief **bpf_program__attach_uprobe()** attaches a BPF program -- 1.8.3.1