diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index 5cc1b0785d18..b23f11c53109 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -238,10 +238,15 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
 	if (attr.prog_type == BPF_PROG_TYPE_STRUCT_OPS ||
 	    attr.prog_type == BPF_PROG_TYPE_LSM) {
 		attr.attach_btf_id = load_attr->attach_btf_id;
-	} else if (attr.prog_type == BPF_PROG_TYPE_TRACING ||
-		   attr.prog_type == BPF_PROG_TYPE_EXT) {
+	} else if (attr.prog_type == BPF_PROG_TYPE_EXT) {
 		attr.attach_btf_id = load_attr->attach_btf_id;
 		attr.attach_prog_fd = load_attr->attach_prog_fd;
+	} else if (attr.prog_type == BPF_PROG_TYPE_TRACING) {
+		attr.attach_btf_id = load_attr->attach_btf_id;
+		if (attr.expected_attach_type == BPF_TRACE_DUMP)
+			attr.attach_target_fd = load_attr->attach_target_fd;
+		else
+			attr.attach_prog_fd = load_attr->attach_prog_fd;
 	} else {
 		attr.prog_ifindex = load_attr->prog_ifindex;
 		attr.kern_version = load_attr->kern_version;
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index 46d47afdd887..7f8d740afde9 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -81,6 +81,7 @@ struct bpf_load_program_attr {
 	union {
 		__u32 kern_version;
 		__u32 attach_prog_fd;
+		__u32 attach_target_fd;
 	};
 	union {
 		__u32 prog_ifindex;
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index ff9174282a8c..ad7726c0c1dc 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -79,6 +79,7 @@ static struct bpf_program *bpf_object__find_prog_by_idx(struct bpf_object *obj,
 							int idx);
 static const struct btf_type *
 skip_mods_and_typedefs(const struct btf *btf, __u32 id, __u32 *res_id);
+static int fill_dumper_info(struct bpf_program *prog);
 
 static int __base_pr(enum libbpf_print_level level, const char *format,
 		     va_list args)
@@ -229,6 +230,7 @@ struct bpf_program {
 	enum bpf_attach_type expected_attach_type;
 	__u32 attach_btf_id;
 	__u32 attach_prog_fd;
+	__u32 attach_target_fd;
 	void *func_info;
 	__u32 func_info_rec_size;
 	__u32 func_info_cnt;
@@ -2365,8 +2367,12 @@ static inline bool libbpf_prog_needs_vmlinux_btf(struct bpf_program *prog)
 	/* BPF_PROG_TYPE_TRACING programs which do not attach to other programs
 	 * also need vmlinux BTF
 	 */
-	if (prog->type == BPF_PROG_TYPE_TRACING && !prog->attach_prog_fd)
-		return true;
+	if (prog->type == BPF_PROG_TYPE_TRACING) {
+		if (prog->expected_attach_type == BPF_TRACE_DUMP)
+			return false;
+		if (!prog->attach_prog_fd)
+			return true;
+	}
 
 	return false;
 }
@@ -4870,10 +4876,15 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
 	if (prog->type == BPF_PROG_TYPE_STRUCT_OPS ||
 	    prog->type == BPF_PROG_TYPE_LSM) {
 		load_attr.attach_btf_id = prog->attach_btf_id;
-	} else if (prog->type == BPF_PROG_TYPE_TRACING ||
-		   prog->type == BPF_PROG_TYPE_EXT) {
+	} else if (prog->type == BPF_PROG_TYPE_EXT) {
 		load_attr.attach_prog_fd = prog->attach_prog_fd;
 		load_attr.attach_btf_id = prog->attach_btf_id;
+	} else if (prog->type == BPF_PROG_TYPE_TRACING) {
+		load_attr.attach_btf_id = prog->attach_btf_id;
+		if (load_attr.expected_attach_type == BPF_TRACE_DUMP)
+			load_attr.attach_target_fd = prog->attach_target_fd;
+		else
+			load_attr.attach_prog_fd = prog->attach_prog_fd;
 	} else {
 		load_attr.kern_version = kern_version;
 		load_attr.prog_ifindex = prog->prog_ifindex;
@@ -4958,7 +4969,7 @@ int bpf_program__load(struct bpf_program *prog, char *license, __u32 kern_ver)
 {
 	int err = 0, fd, i, btf_id;
 
-	if ((prog->type == BPF_PROG_TYPE_TRACING ||
+	if (((prog->type == BPF_PROG_TYPE_TRACING && prog->expected_attach_type != BPF_TRACE_DUMP) ||
 	     prog->type == BPF_PROG_TYPE_LSM ||
 	     prog->type == BPF_PROG_TYPE_EXT) && !prog->attach_btf_id) {
 		btf_id = libbpf_find_attach_btf_id(prog);
@@ -5319,6 +5330,7 @@ static int bpf_object__resolve_externs(struct bpf_object *obj,
 
 int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
 {
+	struct bpf_program *prog;
 	struct bpf_object *obj;
 	int err, i;
 
@@ -5335,7 +5347,17 @@ int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
 
 	obj->loaded = true;
 
-	err = bpf_object__probe_caps(obj);
+	err = 0;
+	bpf_object__for_each_program(prog, obj) {
+		if (prog->type == BPF_PROG_TYPE_TRACING &&
+		    prog->expected_attach_type == BPF_TRACE_DUMP) {
+			err = fill_dumper_info(prog);
+			if (err)
+				break;
+		}
+	}
+
+	err = err ? : bpf_object__probe_caps(obj);
 	err = err ? : bpf_object__resolve_externs(obj, obj->kconfig);
 	err = err ? : bpf_object__sanitize_and_load_btf(obj);
 	err = err ? : bpf_object__sanitize_maps(obj);
@@ -6322,6 +6344,8 @@ static const struct bpf_sec_def section_defs[] = {
 		.is_attach_btf = true,
 		.expected_attach_type = BPF_LSM_MAC,
 		.attach_fn = attach_lsm),
+	SEC_DEF("dump/", TRACING,
+		.expected_attach_type = BPF_TRACE_DUMP),
 	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),
@@ -6401,6 +6425,58 @@ static const struct bpf_sec_def *find_sec_def(const char *sec_name)
 	return NULL;
 }
 
+static int fill_dumper_info(struct bpf_program *prog)
+{
+	const struct bpf_sec_def *sec;
+	const char *dump_target;
+	int fd;
+
+	sec = find_sec_def(bpf_program__title(prog, false));
+	if (sec) {
+		dump_target = bpf_program__title(prog, false) + sec->len;
+		fd = open(dump_target, O_RDONLY);
+		if (fd < 0)
+			return fd;
+		prog->attach_target_fd = fd;
+	}
+	return 0;
+}
+
+int bpf_dump__pin(struct bpf_program *prog, const char *dname)
+{
+	int len, prog_fd = bpf_program__fd(prog);
+	const struct bpf_sec_def *sec;
+	const char *dump_target;
+	char *name_buf;
+	int err;
+
+	if (dname[0] == '/')
+		return bpf_obj_pin(prog_fd, dname);
+
+	sec = find_sec_def(bpf_program__title(prog, false));
+	if (!sec)
+		return bpf_obj_pin(prog_fd, dname);
+
+	dump_target = bpf_program__title(prog, false) + sec->len;
+	len = strlen(dump_target) + strlen(dname) + 2;
+	name_buf = malloc(len);
+	if (!name_buf)
+		return -ENOMEM;
+
+	strcpy(name_buf, dump_target);
+	strcat(name_buf, "/");
+	strcat(name_buf, dname);
+
+	err = bpf_obj_pin(prog_fd, name_buf);
+	free(name_buf);
+	return err;
+}
+
+int bpf_dump__unpin(struct bpf_program *prog, const char *dname)
+{
+	return -EINVAL;
+}
+
 static char *libbpf_get_type_names(bool attach_type)
 {
 	int i, len = ARRAY_SIZE(section_defs) * MAX_TYPE_NAME_SIZE;
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 44df1d3e7287..e0d31e93d21c 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -217,6 +217,9 @@ LIBBPF_API int bpf_program__pin(struct bpf_program *prog, const char *path);
 LIBBPF_API int bpf_program__unpin(struct bpf_program *prog, const char *path);
 LIBBPF_API void bpf_program__unload(struct bpf_program *prog);
 
+LIBBPF_API int bpf_dump__pin(struct bpf_program *prog, const char *dname);
+LIBBPF_API int bpf_dump__unpin(struct bpf_program *prog, const char *dname);
+
 struct bpf_link;
 
 LIBBPF_API struct bpf_link *bpf_link__open(const char *path);
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index bb8831605b25..0beb70bfe65a 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -238,6 +238,8 @@ LIBBPF_0.0.7 {
 
 LIBBPF_0.0.8 {
 	global:
+		bpf_dump__pin;
+		bpf_dump__unpin;
 		bpf_link__fd;
 		bpf_link__open;
 		bpf_link__pin;
