libbpf: Add generic bpf_program__attach()
authorAndrii Nakryiko <andriin@fb.com>
Sat, 14 Dec 2019 01:43:26 +0000 (17:43 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Sun, 15 Dec 2019 23:58:04 +0000 (15:58 -0800)
Generalize BPF program attaching and allow libbpf to auto-detect type (and
extra parameters, where applicable) and attach supported BPF program types
based on program sections. Currently this is supported for:
- kprobe/kretprobe;
- tracepoint;
- raw tracepoint;
- tracing programs (typed raw TP/fentry/fexit).

More types support can be trivially added within this framework.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Link: https://lore.kernel.org/bpf/20191214014341.3442258-3-andriin@fb.com
tools/lib/bpf/libbpf.c
tools/lib/bpf/libbpf.h
tools/lib/bpf/libbpf.map
tools/testing/selftests/bpf/prog_tests/probe_user.c

index dc993112b40ba22a36bf5c305982f57e4a1c237a..61b8cdf78332506dde5e77391c2cb0e0ba224e14 100644 (file)
@@ -4972,7 +4972,28 @@ void bpf_program__set_expected_attach_type(struct bpf_program *prog,
  */
 #define BPF_APROG_COMPAT(string, ptype) BPF_PROG_SEC(string, ptype)
 
-static const struct {
+#define SEC_DEF(sec_pfx, ptype, ...) {                                     \
+       .sec = sec_pfx,                                                     \
+       .len = sizeof(sec_pfx) - 1,                                         \
+       .prog_type = BPF_PROG_TYPE_##ptype,                                 \
+       __VA_ARGS__                                                         \
+}
+
+struct bpf_sec_def;
+
+typedef struct bpf_link *(*attach_fn_t)(const struct bpf_sec_def *sec,
+                                       struct bpf_program *prog);
+
+static struct bpf_link *attach_kprobe(const struct bpf_sec_def *sec,
+                                     struct bpf_program *prog);
+static struct bpf_link *attach_tp(const struct bpf_sec_def *sec,
+                                 struct bpf_program *prog);
+static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec,
+                                     struct bpf_program *prog);
+static struct bpf_link *attach_trace(const struct bpf_sec_def *sec,
+                                    struct bpf_program *prog);
+
+struct bpf_sec_def {
        const char *sec;
        size_t len;
        enum bpf_prog_type prog_type;
@@ -4980,25 +5001,40 @@ static const struct {
        bool is_attachable;
        bool is_attach_btf;
        enum bpf_attach_type attach_type;
-} section_names[] = {
+       attach_fn_t attach_fn;
+};
+
+static const struct bpf_sec_def section_defs[] = {
        BPF_PROG_SEC("socket",                  BPF_PROG_TYPE_SOCKET_FILTER),
        BPF_PROG_SEC("sk_reuseport",            BPF_PROG_TYPE_SK_REUSEPORT),
-       BPF_PROG_SEC("kprobe/",                 BPF_PROG_TYPE_KPROBE),
+       SEC_DEF("kprobe/", KPROBE,
+               .attach_fn = attach_kprobe),
        BPF_PROG_SEC("uprobe/",                 BPF_PROG_TYPE_KPROBE),
-       BPF_PROG_SEC("kretprobe/",              BPF_PROG_TYPE_KPROBE),
+       SEC_DEF("kretprobe/", KPROBE,
+               .attach_fn = attach_kprobe),
        BPF_PROG_SEC("uretprobe/",              BPF_PROG_TYPE_KPROBE),
        BPF_PROG_SEC("classifier",              BPF_PROG_TYPE_SCHED_CLS),
        BPF_PROG_SEC("action",                  BPF_PROG_TYPE_SCHED_ACT),
-       BPF_PROG_SEC("tracepoint/",             BPF_PROG_TYPE_TRACEPOINT),
-       BPF_PROG_SEC("tp/",                     BPF_PROG_TYPE_TRACEPOINT),
-       BPF_PROG_SEC("raw_tracepoint/",         BPF_PROG_TYPE_RAW_TRACEPOINT),
-       BPF_PROG_SEC("raw_tp/",                 BPF_PROG_TYPE_RAW_TRACEPOINT),
-       BPF_PROG_BTF("tp_btf/",                 BPF_PROG_TYPE_TRACING,
-                                               BPF_TRACE_RAW_TP),
-       BPF_PROG_BTF("fentry/",                 BPF_PROG_TYPE_TRACING,
-                                               BPF_TRACE_FENTRY),
-       BPF_PROG_BTF("fexit/",                  BPF_PROG_TYPE_TRACING,
-                                               BPF_TRACE_FEXIT),
+       SEC_DEF("tracepoint/", TRACEPOINT,
+               .attach_fn = attach_tp),
+       SEC_DEF("tp/", TRACEPOINT,
+               .attach_fn = attach_tp),
+       SEC_DEF("raw_tracepoint/", RAW_TRACEPOINT,
+               .attach_fn = attach_raw_tp),
+       SEC_DEF("raw_tp/", RAW_TRACEPOINT,
+               .attach_fn = attach_raw_tp),
+       SEC_DEF("tp_btf/", TRACING,
+               .expected_attach_type = BPF_TRACE_RAW_TP,
+               .is_attach_btf = true,
+               .attach_fn = attach_trace),
+       SEC_DEF("fentry/", TRACING,
+               .expected_attach_type = BPF_TRACE_FENTRY,
+               .is_attach_btf = true,
+               .attach_fn = attach_trace),
+       SEC_DEF("fexit/", TRACING,
+               .expected_attach_type = BPF_TRACE_FEXIT,
+               .is_attach_btf = true,
+               .attach_fn = attach_trace),
        BPF_PROG_SEC("xdp",                     BPF_PROG_TYPE_XDP),
        BPF_PROG_SEC("perf_event",              BPF_PROG_TYPE_PERF_EVENT),
        BPF_PROG_SEC("lwt_in",                  BPF_PROG_TYPE_LWT_IN),
@@ -5060,12 +5096,26 @@ static const struct {
 #undef BPF_APROG_SEC
 #undef BPF_EAPROG_SEC
 #undef BPF_APROG_COMPAT
+#undef SEC_DEF
 
 #define MAX_TYPE_NAME_SIZE 32
 
+static const struct bpf_sec_def *find_sec_def(const char *sec_name)
+{
+       int i, n = ARRAY_SIZE(section_defs);
+
+       for (i = 0; i < n; i++) {
+               if (strncmp(sec_name,
+                           section_defs[i].sec, section_defs[i].len))
+                       continue;
+               return &section_defs[i];
+       }
+       return NULL;
+}
+
 static char *libbpf_get_type_names(bool attach_type)
 {
-       int i, len = ARRAY_SIZE(section_names) * MAX_TYPE_NAME_SIZE;
+       int i, len = ARRAY_SIZE(section_defs) * MAX_TYPE_NAME_SIZE;
        char *buf;
 
        buf = malloc(len);
@@ -5074,16 +5124,16 @@ static char *libbpf_get_type_names(bool attach_type)
 
        buf[0] = '\0';
        /* Forge string buf with all available names */
-       for (i = 0; i < ARRAY_SIZE(section_names); i++) {
-               if (attach_type && !section_names[i].is_attachable)
+       for (i = 0; i < ARRAY_SIZE(section_defs); i++) {
+               if (attach_type && !section_defs[i].is_attachable)
                        continue;
 
-               if (strlen(buf) + strlen(section_names[i].sec) + 2 > len) {
+               if (strlen(buf) + strlen(section_defs[i].sec) + 2 > len) {
                        free(buf);
                        return NULL;
                }
                strcat(buf, " ");
-               strcat(buf, section_names[i].sec);
+               strcat(buf, section_defs[i].sec);
        }
 
        return buf;
@@ -5092,19 +5142,19 @@ static char *libbpf_get_type_names(bool attach_type)
 int libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,
                             enum bpf_attach_type *expected_attach_type)
 {
+       const struct bpf_sec_def *sec_def;
        char *type_names;
-       int i;
 
        if (!name)
                return -EINVAL;
 
-       for (i = 0; i < ARRAY_SIZE(section_names); i++) {
-               if (strncmp(name, section_names[i].sec, section_names[i].len))
-                       continue;
-               *prog_type = section_names[i].prog_type;
-               *expected_attach_type = section_names[i].expected_attach_type;
+       sec_def = find_sec_def(name);
+       if (sec_def) {
+               *prog_type = sec_def->prog_type;
+               *expected_attach_type = sec_def->expected_attach_type;
                return 0;
        }
+
        pr_warn("failed to guess program type from ELF section '%s'\n", name);
        type_names = libbpf_get_type_names(false);
        if (type_names != NULL) {
@@ -5187,16 +5237,16 @@ static int libbpf_find_attach_btf_id(const char *name,
        if (!name)
                return -EINVAL;
 
-       for (i = 0; i < ARRAY_SIZE(section_names); i++) {
-               if (!section_names[i].is_attach_btf)
+       for (i = 0; i < ARRAY_SIZE(section_defs); i++) {
+               if (!section_defs[i].is_attach_btf)
                        continue;
-               if (strncmp(name, section_names[i].sec, section_names[i].len))
+               if (strncmp(name, section_defs[i].sec, section_defs[i].len))
                        continue;
                if (attach_prog_fd)
-                       err = libbpf_find_prog_btf_id(name + section_names[i].len,
+                       err = libbpf_find_prog_btf_id(name + section_defs[i].len,
                                                      attach_prog_fd);
                else
-                       err = libbpf_find_vmlinux_btf_id(name + section_names[i].len,
+                       err = libbpf_find_vmlinux_btf_id(name + section_defs[i].len,
                                                         attach_type);
                if (err <= 0)
                        pr_warn("%s is not found in vmlinux BTF\n", name);
@@ -5215,12 +5265,12 @@ int libbpf_attach_type_by_name(const char *name,
        if (!name)
                return -EINVAL;
 
-       for (i = 0; i < ARRAY_SIZE(section_names); i++) {
-               if (strncmp(name, section_names[i].sec, section_names[i].len))
+       for (i = 0; i < ARRAY_SIZE(section_defs); i++) {
+               if (strncmp(name, section_defs[i].sec, section_defs[i].len))
                        continue;
-               if (!section_names[i].is_attachable)
+               if (!section_defs[i].is_attachable)
                        return -EINVAL;
-               *attach_type = section_names[i].attach_type;
+               *attach_type = section_defs[i].attach_type;
                return 0;
        }
        pr_warn("failed to guess attach type based on ELF section name '%s'\n", name);
@@ -5680,6 +5730,18 @@ struct bpf_link *bpf_program__attach_kprobe(struct bpf_program *prog,
        return link;
 }
 
+static struct bpf_link *attach_kprobe(const struct bpf_sec_def *sec,
+                                     struct bpf_program *prog)
+{
+       const char *func_name;
+       bool retprobe;
+
+       func_name = bpf_program__title(prog, false) + sec->len;
+       retprobe = strcmp(sec->sec, "kretprobe/") == 0;
+
+       return bpf_program__attach_kprobe(prog, retprobe, func_name);
+}
+
 struct bpf_link *bpf_program__attach_uprobe(struct bpf_program *prog,
                                            bool retprobe, pid_t pid,
                                            const char *binary_path,
@@ -5792,6 +5854,32 @@ struct bpf_link *bpf_program__attach_tracepoint(struct bpf_program *prog,
        return link;
 }
 
+static struct bpf_link *attach_tp(const struct bpf_sec_def *sec,
+                                 struct bpf_program *prog)
+{
+       char *sec_name, *tp_cat, *tp_name;
+       struct bpf_link *link;
+
+       sec_name = strdup(bpf_program__title(prog, false));
+       if (!sec_name)
+               return ERR_PTR(-ENOMEM);
+
+       /* extract "tp/<category>/<name>" */
+       tp_cat = sec_name + sec->len;
+       tp_name = strchr(tp_cat, '/');
+       if (!tp_name) {
+               link = ERR_PTR(-EINVAL);
+               goto out;
+       }
+       *tp_name = '\0';
+       tp_name++;
+
+       link = bpf_program__attach_tracepoint(prog, tp_cat, tp_name);
+out:
+       free(sec_name);
+       return link;
+}
+
 static int bpf_link__destroy_fd(struct bpf_link *link)
 {
        struct bpf_link_fd *l = (void *)link;
@@ -5831,6 +5919,14 @@ struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
        return (struct bpf_link *)link;
 }
 
+static struct bpf_link *attach_raw_tp(const struct bpf_sec_def *sec,
+                                     struct bpf_program *prog)
+{
+       const char *tp_name = bpf_program__title(prog, false) + sec->len;
+
+       return bpf_program__attach_raw_tracepoint(prog, tp_name);
+}
+
 struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
 {
        char errmsg[STRERR_BUFSIZE];
@@ -5862,6 +5958,23 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
        return (struct bpf_link *)link;
 }
 
+static struct bpf_link *attach_trace(const struct bpf_sec_def *sec,
+                                    struct bpf_program *prog)
+{
+       return bpf_program__attach_trace(prog);
+}
+
+struct bpf_link *bpf_program__attach(struct bpf_program *prog)
+{
+       const struct bpf_sec_def *sec_def;
+
+       sec_def = find_sec_def(bpf_program__title(prog, false));
+       if (!sec_def || !sec_def->attach_fn)
+               return ERR_PTR(-ESRCH);
+
+       return sec_def->attach_fn(sec_def, prog);
+}
+
 enum bpf_perf_event_ret
 bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
                           void **copy_mem, size_t *copy_size,
index 0dbf4bfba0c4db663812d8c7871ce33993974d8d..804f445c99574ce5ff964abbf415e1b9bd50906c 100644 (file)
@@ -237,6 +237,8 @@ struct bpf_link;
 
 LIBBPF_API int bpf_link__destroy(struct bpf_link *link);
 
+LIBBPF_API struct bpf_link *
+bpf_program__attach(struct bpf_program *prog);
 LIBBPF_API struct bpf_link *
 bpf_program__attach_perf_event(struct bpf_program *prog, int pfd);
 LIBBPF_API struct bpf_link *
index 495df575f87f8f03e924e9968a08263dc8d841dd..757a88f64b5a48ffee0813330b312b9d1bef00b1 100644 (file)
@@ -210,4 +210,6 @@ LIBBPF_0.0.6 {
 } LIBBPF_0.0.5;
 
 LIBBPF_0.0.7 {
+       global:
+               bpf_program__attach;
 } LIBBPF_0.0.6;
index 8a3187dec04859579c42bb299e59127aa1dca918..7aecfd9e87d159d1c3fc1abe3117fd3f8aa3a63b 100644 (file)
@@ -3,8 +3,7 @@
 
 void test_probe_user(void)
 {
-#define kprobe_name "__sys_connect"
-       const char *prog_name = "kprobe/" kprobe_name;
+       const char *prog_name = "kprobe/__sys_connect";
        const char *obj_file = "./test_probe_user.o";
        DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts, );
        int err, results_map_fd, sock_fd, duration = 0;
@@ -33,8 +32,7 @@ void test_probe_user(void)
                  "err %d\n", results_map_fd))
                goto cleanup;
 
-       kprobe_link = bpf_program__attach_kprobe(kprobe_prog, false,
-                                                kprobe_name);
+       kprobe_link = bpf_program__attach(kprobe_prog);
        if (CHECK(IS_ERR(kprobe_link), "attach_kprobe",
                  "err %ld\n", PTR_ERR(kprobe_link))) {
                kprobe_link = NULL;