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) | ||||
| 
 | ||||
| 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 §ion_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, | ||||
|  | ||||
| @ -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 * | ||||
|  | ||||
| @ -210,4 +210,6 @@ LIBBPF_0.0.6 { | ||||
| } LIBBPF_0.0.5; | ||||
| 
 | ||||
| LIBBPF_0.0.7 { | ||||
| 	global: | ||||
| 		bpf_program__attach; | ||||
| } LIBBPF_0.0.6; | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user