Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

libbpf: Add auto-attach for uprobes based on section name

Now that u[ret]probes can use name-based specification, it makes
sense to add support for auto-attach based on SEC() definition.
The format proposed is

SEC("u[ret]probe/binary:[raw_offset|[function_name[+offset]]")

For example, to trace malloc() in libc:

SEC("uprobe/libc.so.6:malloc")

...or to trace function foo2 in /usr/bin/foo:

SEC("uprobe//usr/bin/foo:foo2")

Auto-attach is done for all tasks (pid -1). prog can be an absolute
path or simply a program/library name; in the latter case, we use
PATH/LD_LIBRARY_PATH to resolve the full path, falling back to
standard locations (/usr/bin:/usr/sbin or /usr/lib64:/usr/lib) if
the file is not found via environment-variable specified locations.

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/1648654000-21758-4-git-send-email-alan.maguire@oracle.com

authored by

Alan Maguire and committed by
Andrii Nakryiko
39f8dc43 433966e3

+76 -6
+72 -2
tools/lib/bpf/libbpf.c
··· 8630 8630 } 8631 8631 8632 8632 static int attach_kprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link); 8633 + static int attach_uprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link); 8633 8634 static int attach_tp(const struct bpf_program *prog, long cookie, struct bpf_link **link); 8634 8635 static int attach_raw_tp(const struct bpf_program *prog, long cookie, struct bpf_link **link); 8635 8636 static int attach_trace(const struct bpf_program *prog, long cookie, struct bpf_link **link); ··· 8643 8642 SEC_DEF("sk_reuseport/migrate", SK_REUSEPORT, BPF_SK_REUSEPORT_SELECT_OR_MIGRATE, SEC_ATTACHABLE | SEC_SLOPPY_PFX), 8644 8643 SEC_DEF("sk_reuseport", SK_REUSEPORT, BPF_SK_REUSEPORT_SELECT, SEC_ATTACHABLE | SEC_SLOPPY_PFX), 8645 8644 SEC_DEF("kprobe/", KPROBE, 0, SEC_NONE, attach_kprobe), 8646 - SEC_DEF("uprobe/", KPROBE, 0, SEC_NONE), 8645 + SEC_DEF("uprobe+", KPROBE, 0, SEC_NONE, attach_uprobe), 8647 8646 SEC_DEF("kretprobe/", KPROBE, 0, SEC_NONE, attach_kprobe), 8648 - SEC_DEF("uretprobe/", KPROBE, 0, SEC_NONE), 8647 + SEC_DEF("uretprobe+", KPROBE, 0, SEC_NONE, attach_uprobe), 8649 8648 SEC_DEF("kprobe.multi/", KPROBE, BPF_TRACE_KPROBE_MULTI, SEC_NONE, attach_kprobe_multi), 8650 8649 SEC_DEF("kretprobe.multi/", KPROBE, BPF_TRACE_KPROBE_MULTI, SEC_NONE, attach_kprobe_multi), 8651 8650 SEC_DEF("tc", SCHED_CLS, 0, SEC_NONE), ··· 10844 10843 free(legacy_probe); 10845 10844 return libbpf_err_ptr(err); 10846 10845 10846 + } 10847 + 10848 + /* Format of u[ret]probe section definition supporting auto-attach: 10849 + * u[ret]probe/binary:function[+offset] 10850 + * 10851 + * binary can be an absolute/relative path or a filename; the latter is resolved to a 10852 + * full binary path via bpf_program__attach_uprobe_opts. 10853 + * 10854 + * Specifying uprobe+ ensures we carry out strict matching; either "uprobe" must be 10855 + * specified (and auto-attach is not possible) or the above format is specified for 10856 + * auto-attach. 10857 + */ 10858 + static int attach_uprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link) 10859 + { 10860 + DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, opts); 10861 + char *func, *probe_name, *func_end; 10862 + char *func_name, binary_path[512]; 10863 + unsigned long long raw_offset; 10864 + size_t offset = 0; 10865 + int n; 10866 + 10867 + *link = NULL; 10868 + 10869 + opts.retprobe = str_has_pfx(prog->sec_name, "uretprobe"); 10870 + if (opts.retprobe) 10871 + probe_name = prog->sec_name + sizeof("uretprobe") - 1; 10872 + else 10873 + probe_name = prog->sec_name + sizeof("uprobe") - 1; 10874 + if (probe_name[0] == '/') 10875 + probe_name++; 10876 + 10877 + /* handle SEC("u[ret]probe") - format is valid, but auto-attach is impossible. */ 10878 + if (strlen(probe_name) == 0) 10879 + return 0; 10880 + 10881 + snprintf(binary_path, sizeof(binary_path), "%s", probe_name); 10882 + /* ':' should be prior to function+offset */ 10883 + func_name = strrchr(binary_path, ':'); 10884 + if (!func_name) { 10885 + pr_warn("section '%s' missing ':function[+offset]' specification\n", 10886 + prog->sec_name); 10887 + return -EINVAL; 10888 + } 10889 + func_name[0] = '\0'; 10890 + func_name++; 10891 + n = sscanf(func_name, "%m[a-zA-Z0-9_.]+%li", &func, &offset); 10892 + if (n < 1) { 10893 + pr_warn("uprobe name '%s' is invalid\n", func_name); 10894 + return -EINVAL; 10895 + } 10896 + if (opts.retprobe && offset != 0) { 10897 + free(func); 10898 + pr_warn("uretprobes do not support offset specification\n"); 10899 + return -EINVAL; 10900 + } 10901 + 10902 + /* Is func a raw address? */ 10903 + errno = 0; 10904 + raw_offset = strtoull(func, &func_end, 0); 10905 + if (!errno && !*func_end) { 10906 + free(func); 10907 + func = NULL; 10908 + offset = (size_t)raw_offset; 10909 + } 10910 + opts.func_name = func; 10911 + 10912 + *link = bpf_program__attach_uprobe_opts(prog, -1, binary_path, offset, &opts); 10913 + free(func); 10914 + return libbpf_get_error(*link); 10847 10915 } 10848 10916 10849 10917 struct bpf_link *bpf_program__attach_uprobe(const struct bpf_program *prog,
+2 -2
tools/testing/selftests/bpf/progs/test_bpf_cookie.c
··· 37 37 return 0; 38 38 } 39 39 40 - SEC("uprobe/trigger_func") 40 + SEC("uprobe") 41 41 int handle_uprobe(struct pt_regs *ctx) 42 42 { 43 43 update(ctx, &uprobe_res); 44 44 return 0; 45 45 } 46 46 47 - SEC("uretprobe/trigger_func") 47 + SEC("uretprobe") 48 48 int handle_uretprobe(struct pt_regs *ctx) 49 49 { 50 50 update(ctx, &uretprobe_res);
+1 -1
tools/testing/selftests/bpf/progs/test_task_pt_regs.c
··· 14 14 char ctx_regs[PT_REGS_SIZE] = {}; 15 15 int uprobe_res = 0; 16 16 17 - SEC("uprobe/trigger_func") 17 + SEC("uprobe") 18 18 int handle_uprobe(struct pt_regs *ctx) 19 19 { 20 20 struct task_struct *current;
+1 -1
tools/testing/selftests/bpf/progs/trigger_bench.c
··· 54 54 return -22; 55 55 } 56 56 57 - SEC("uprobe/self/uprobe_target") 57 + SEC("uprobe") 58 58 int bench_trigger_uprobe(void *ctx) 59 59 { 60 60 __sync_add_and_fetch(&hits, 1);