libbpf: Add generic bpf_program__attach()
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
This commit is contained in:
parent
0d13bfce02
commit
d7a18ea7e8
@ -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)
|
#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;
|
const char *sec;
|
||||||
size_t len;
|
size_t len;
|
||||||
enum bpf_prog_type prog_type;
|
enum bpf_prog_type prog_type;
|
||||||
@ -4980,25 +5001,40 @@ static const struct {
|
|||||||
bool is_attachable;
|
bool is_attachable;
|
||||||
bool is_attach_btf;
|
bool is_attach_btf;
|
||||||
enum bpf_attach_type attach_type;
|
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("socket", BPF_PROG_TYPE_SOCKET_FILTER),
|
||||||
BPF_PROG_SEC("sk_reuseport", BPF_PROG_TYPE_SK_REUSEPORT),
|
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("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("uretprobe/", BPF_PROG_TYPE_KPROBE),
|
||||||
BPF_PROG_SEC("classifier", BPF_PROG_TYPE_SCHED_CLS),
|
BPF_PROG_SEC("classifier", BPF_PROG_TYPE_SCHED_CLS),
|
||||||
BPF_PROG_SEC("action", BPF_PROG_TYPE_SCHED_ACT),
|
BPF_PROG_SEC("action", BPF_PROG_TYPE_SCHED_ACT),
|
||||||
BPF_PROG_SEC("tracepoint/", BPF_PROG_TYPE_TRACEPOINT),
|
SEC_DEF("tracepoint/", TRACEPOINT,
|
||||||
BPF_PROG_SEC("tp/", BPF_PROG_TYPE_TRACEPOINT),
|
.attach_fn = attach_tp),
|
||||||
BPF_PROG_SEC("raw_tracepoint/", BPF_PROG_TYPE_RAW_TRACEPOINT),
|
SEC_DEF("tp/", TRACEPOINT,
|
||||||
BPF_PROG_SEC("raw_tp/", BPF_PROG_TYPE_RAW_TRACEPOINT),
|
.attach_fn = attach_tp),
|
||||||
BPF_PROG_BTF("tp_btf/", BPF_PROG_TYPE_TRACING,
|
SEC_DEF("raw_tracepoint/", RAW_TRACEPOINT,
|
||||||
BPF_TRACE_RAW_TP),
|
.attach_fn = attach_raw_tp),
|
||||||
BPF_PROG_BTF("fentry/", BPF_PROG_TYPE_TRACING,
|
SEC_DEF("raw_tp/", RAW_TRACEPOINT,
|
||||||
BPF_TRACE_FENTRY),
|
.attach_fn = attach_raw_tp),
|
||||||
BPF_PROG_BTF("fexit/", BPF_PROG_TYPE_TRACING,
|
SEC_DEF("tp_btf/", TRACING,
|
||||||
BPF_TRACE_FEXIT),
|
.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("xdp", BPF_PROG_TYPE_XDP),
|
||||||
BPF_PROG_SEC("perf_event", BPF_PROG_TYPE_PERF_EVENT),
|
BPF_PROG_SEC("perf_event", BPF_PROG_TYPE_PERF_EVENT),
|
||||||
BPF_PROG_SEC("lwt_in", BPF_PROG_TYPE_LWT_IN),
|
BPF_PROG_SEC("lwt_in", BPF_PROG_TYPE_LWT_IN),
|
||||||
@ -5060,12 +5096,26 @@ static const struct {
|
|||||||
#undef BPF_APROG_SEC
|
#undef BPF_APROG_SEC
|
||||||
#undef BPF_EAPROG_SEC
|
#undef BPF_EAPROG_SEC
|
||||||
#undef BPF_APROG_COMPAT
|
#undef BPF_APROG_COMPAT
|
||||||
|
#undef SEC_DEF
|
||||||
|
|
||||||
#define MAX_TYPE_NAME_SIZE 32
|
#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 §ion_defs[i];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static char *libbpf_get_type_names(bool attach_type)
|
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;
|
char *buf;
|
||||||
|
|
||||||
buf = malloc(len);
|
buf = malloc(len);
|
||||||
@ -5074,16 +5124,16 @@ static char *libbpf_get_type_names(bool attach_type)
|
|||||||
|
|
||||||
buf[0] = '\0';
|
buf[0] = '\0';
|
||||||
/* Forge string buf with all available names */
|
/* Forge string buf with all available names */
|
||||||
for (i = 0; i < ARRAY_SIZE(section_names); i++) {
|
for (i = 0; i < ARRAY_SIZE(section_defs); i++) {
|
||||||
if (attach_type && !section_names[i].is_attachable)
|
if (attach_type && !section_defs[i].is_attachable)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (strlen(buf) + strlen(section_names[i].sec) + 2 > len) {
|
if (strlen(buf) + strlen(section_defs[i].sec) + 2 > len) {
|
||||||
free(buf);
|
free(buf);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
strcat(buf, " ");
|
strcat(buf, " ");
|
||||||
strcat(buf, section_names[i].sec);
|
strcat(buf, section_defs[i].sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf;
|
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,
|
int libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,
|
||||||
enum bpf_attach_type *expected_attach_type)
|
enum bpf_attach_type *expected_attach_type)
|
||||||
{
|
{
|
||||||
|
const struct bpf_sec_def *sec_def;
|
||||||
char *type_names;
|
char *type_names;
|
||||||
int i;
|
|
||||||
|
|
||||||
if (!name)
|
if (!name)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(section_names); i++) {
|
sec_def = find_sec_def(name);
|
||||||
if (strncmp(name, section_names[i].sec, section_names[i].len))
|
if (sec_def) {
|
||||||
continue;
|
*prog_type = sec_def->prog_type;
|
||||||
*prog_type = section_names[i].prog_type;
|
*expected_attach_type = sec_def->expected_attach_type;
|
||||||
*expected_attach_type = section_names[i].expected_attach_type;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_warn("failed to guess program type from ELF section '%s'\n", name);
|
pr_warn("failed to guess program type from ELF section '%s'\n", name);
|
||||||
type_names = libbpf_get_type_names(false);
|
type_names = libbpf_get_type_names(false);
|
||||||
if (type_names != NULL) {
|
if (type_names != NULL) {
|
||||||
@ -5187,16 +5237,16 @@ static int libbpf_find_attach_btf_id(const char *name,
|
|||||||
if (!name)
|
if (!name)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(section_names); i++) {
|
for (i = 0; i < ARRAY_SIZE(section_defs); i++) {
|
||||||
if (!section_names[i].is_attach_btf)
|
if (!section_defs[i].is_attach_btf)
|
||||||
continue;
|
continue;
|
||||||
if (strncmp(name, section_names[i].sec, section_names[i].len))
|
if (strncmp(name, section_defs[i].sec, section_defs[i].len))
|
||||||
continue;
|
continue;
|
||||||
if (attach_prog_fd)
|
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);
|
attach_prog_fd);
|
||||||
else
|
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);
|
attach_type);
|
||||||
if (err <= 0)
|
if (err <= 0)
|
||||||
pr_warn("%s is not found in vmlinux BTF\n", name);
|
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)
|
if (!name)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(section_names); i++) {
|
for (i = 0; i < ARRAY_SIZE(section_defs); i++) {
|
||||||
if (strncmp(name, section_names[i].sec, section_names[i].len))
|
if (strncmp(name, section_defs[i].sec, section_defs[i].len))
|
||||||
continue;
|
continue;
|
||||||
if (!section_names[i].is_attachable)
|
if (!section_defs[i].is_attachable)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
*attach_type = section_names[i].attach_type;
|
*attach_type = section_defs[i].attach_type;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
pr_warn("failed to guess attach type based on ELF section name '%s'\n", name);
|
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;
|
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,
|
struct bpf_link *bpf_program__attach_uprobe(struct bpf_program *prog,
|
||||||
bool retprobe, pid_t pid,
|
bool retprobe, pid_t pid,
|
||||||
const char *binary_path,
|
const char *binary_path,
|
||||||
@ -5792,6 +5854,32 @@ struct bpf_link *bpf_program__attach_tracepoint(struct bpf_program *prog,
|
|||||||
return link;
|
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)
|
static int bpf_link__destroy_fd(struct bpf_link *link)
|
||||||
{
|
{
|
||||||
struct bpf_link_fd *l = (void *)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;
|
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)
|
struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
|
||||||
{
|
{
|
||||||
char errmsg[STRERR_BUFSIZE];
|
char errmsg[STRERR_BUFSIZE];
|
||||||
@ -5862,6 +5958,23 @@ struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
|
|||||||
return (struct bpf_link *)link;
|
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
|
enum bpf_perf_event_ret
|
||||||
bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
|
bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
|
||||||
void **copy_mem, size_t *copy_size,
|
void **copy_mem, size_t *copy_size,
|
||||||
|
@ -237,6 +237,8 @@ struct bpf_link;
|
|||||||
|
|
||||||
LIBBPF_API int bpf_link__destroy(struct bpf_link *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 *
|
LIBBPF_API struct bpf_link *
|
||||||
bpf_program__attach_perf_event(struct bpf_program *prog, int pfd);
|
bpf_program__attach_perf_event(struct bpf_program *prog, int pfd);
|
||||||
LIBBPF_API struct bpf_link *
|
LIBBPF_API struct bpf_link *
|
||||||
|
@ -210,4 +210,6 @@ LIBBPF_0.0.6 {
|
|||||||
} LIBBPF_0.0.5;
|
} LIBBPF_0.0.5;
|
||||||
|
|
||||||
LIBBPF_0.0.7 {
|
LIBBPF_0.0.7 {
|
||||||
|
global:
|
||||||
|
bpf_program__attach;
|
||||||
} LIBBPF_0.0.6;
|
} LIBBPF_0.0.6;
|
||||||
|
@ -3,8 +3,7 @@
|
|||||||
|
|
||||||
void test_probe_user(void)
|
void test_probe_user(void)
|
||||||
{
|
{
|
||||||
#define kprobe_name "__sys_connect"
|
const char *prog_name = "kprobe/__sys_connect";
|
||||||
const char *prog_name = "kprobe/" kprobe_name;
|
|
||||||
const char *obj_file = "./test_probe_user.o";
|
const char *obj_file = "./test_probe_user.o";
|
||||||
DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts, );
|
DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts, );
|
||||||
int err, results_map_fd, sock_fd, duration = 0;
|
int err, results_map_fd, sock_fd, duration = 0;
|
||||||
@ -33,8 +32,7 @@ void test_probe_user(void)
|
|||||||
"err %d\n", results_map_fd))
|
"err %d\n", results_map_fd))
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
kprobe_link = bpf_program__attach_kprobe(kprobe_prog, false,
|
kprobe_link = bpf_program__attach(kprobe_prog);
|
||||||
kprobe_name);
|
|
||||||
if (CHECK(IS_ERR(kprobe_link), "attach_kprobe",
|
if (CHECK(IS_ERR(kprobe_link), "attach_kprobe",
|
||||||
"err %ld\n", PTR_ERR(kprobe_link))) {
|
"err %ld\n", PTR_ERR(kprobe_link))) {
|
||||||
kprobe_link = NULL;
|
kprobe_link = NULL;
|
||||||
|
Loading…
Reference in New Issue
Block a user