diff mbox series

[v2,bpf-next,4/9] libbpf: refactor internal sec_def handling to enable pluggability

Message ID 20210920234320.3312820-5-andrii@kernel.org (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series libbpf: stricter BPF program section name handling | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR success PR summary
netdev/cover_letter success Link
netdev/fixes_present success Link
netdev/patch_count success Link
netdev/tree_selection success Clearly marked for bpf-next
netdev/subject_prefix success Link
netdev/cc_maintainers warning 6 maintainers not CCed: kpsingh@kernel.org john.fastabend@gmail.com yhs@fb.com songliubraving@fb.com netdev@vger.kernel.org kafai@fb.com
netdev/source_inline success Was 0 now: 0
netdev/verify_signedoff success Link
netdev/module_param success Was 0 now: 0
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/verify_fixes success Link
netdev/checkpatch warning WARNING: line length of 102 exceeds 80 columns WARNING: line length of 81 exceeds 80 columns WARNING: line length of 82 exceeds 80 columns WARNING: line length of 83 exceeds 80 columns WARNING: line length of 85 exceeds 80 columns WARNING: line length of 89 exceeds 80 columns WARNING: line length of 98 exceeds 80 columns
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/header_inline success Link
bpf/vmtest-bpf-next success VM_Test

Commit Message

Andrii Nakryiko Sept. 20, 2021, 11:43 p.m. UTC
Refactor internals of libbpf to allow adding custom SEC() handling logic
easily from outside of libbpf. To that effect, each SEC()-handling
registration sets mandatory program type/expected attach type for
a given prefix and can provide three callbacks called at different
points of BPF program lifetime:

  - init callback for right after bpf_program is initialized and
  prog_type/expected_attach_type is set. This happens during
  bpf_object__open() step, close to the very end of constructing
  bpf_object, so all the libbpf APIs for querying and updating
  bpf_program properties should be available;

  - pre-load callback is called right before BPF_PROG_LOAD command is
  called in the kernel. This callbacks has ability to set both
  bpf_program properties, as well as program load attributes, overriding
  and augmenting the standard libbpf handling of them;

  - optional auto-attach callback, which makes a given SEC() handler
  support auto-attachment of a BPF program through bpf_program__attach()
  API and/or BPF skeletons <skel>__attach() method.

Each callbacks gets a `long cookie` parameter passed in, which is
specified during SEC() handling. This can be used by callbacks to lookup
whatever additional information is necessary.

This is not yet completely ready to be exposed to the outside world,
mainly due to non-public nature of struct bpf_prog_load_params. Instead
of making it part of public API, we'll wait until the planned low-level
libbpf API improvements for BPF_PROG_LOAD and other typical bpf()
syscall APIs, at which point we'll have a public, probably OPTS-based,
way to fully specify BPF program load parameters, which will be used as
an interface for custom pre-load callbacks.

But this change itself is already a good first step to unify the BPF
program hanling logic even within the libbpf itself. As one example, all
the extra per-program type handling (sleepable bit, attach_btf_id
resolution, unsetting optional expected attach type) is now more obvious
and is gathered in one place.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
 tools/lib/bpf/libbpf.c | 129 +++++++++++++++++++++++++++--------------
 1 file changed, 87 insertions(+), 42 deletions(-)

Comments

Dave Marchevsky Sept. 22, 2021, 12:42 a.m. UTC | #1
On 9/20/21 7:43 PM, Andrii Nakryiko wrote:   
> Refactor internals of libbpf to allow adding custom SEC() handling logic
> easily from outside of libbpf. To that effect, each SEC()-handling
> registration sets mandatory program type/expected attach type for
> a given prefix and can provide three callbacks called at different
> points of BPF program lifetime:
> 
>   - init callback for right after bpf_program is initialized and
>   prog_type/expected_attach_type is set. This happens during
>   bpf_object__open() step, close to the very end of constructing
>   bpf_object, so all the libbpf APIs for querying and updating
>   bpf_program properties should be available;

Do you have a usecase in mind that would set this? USDT? 

>   - pre-load callback is called right before BPF_PROG_LOAD command is
>   called in the kernel. This callbacks has ability to set both
>   bpf_program properties, as well as program load attributes, overriding
>   and augmenting the standard libbpf handling of them;

[...]

> @@ -6094,6 +6100,44 @@ static int bpf_object__sanitize_prog(struct bpf_object *obj, struct bpf_program
>  	return 0;
>  }
>  
> +static int libbpf_find_attach_btf_id(struct bpf_program *prog, int *btf_obj_fd, int *btf_type_id);
> +
> +/* this is called as prog->sec_def->preload_fn for libbpf-supported sec_defs */
> +static int libbpf_preload_prog(struct bpf_program *prog,
> +			       struct bpf_prog_load_params *attr, long cookie)
> +{
> +	/* old kernels might not support specifying expected_attach_type */
> +	if (prog->sec_def->is_exp_attach_type_optional &&
> +	    !kernel_supports(prog->obj, FEAT_EXP_ATTACH_TYPE))
> +		attr->expected_attach_type = 0;
> +
> +	if (prog->sec_def->is_sleepable)
> +		attr->prog_flags |= BPF_F_SLEEPABLE;
> +
> +	if ((prog->type == BPF_PROG_TYPE_TRACING ||
> +	     prog->type == BPF_PROG_TYPE_LSM ||
> +	     prog->type == BPF_PROG_TYPE_EXT) && !prog->attach_btf_id) {
> +		int btf_obj_fd = 0, btf_type_id = 0, err;
> +
> +		err = libbpf_find_attach_btf_id(prog, &btf_obj_fd, &btf_type_id);
> +		if (err)
> +			return err;
> +
> +		/* cache resolved BTF FD and BTF type ID in the prog */
> +		prog->attach_btf_obj_fd = btf_obj_fd;
> +		prog->attach_btf_id = btf_type_id;
> +
> +		/* but by now libbpf common logic is not utilizing
> +		 * prog->atach_btf_obj_fd/prog->attach_btf_id anymore because
> +		 * this callback is called after attrs were populated by
> +		 * libbpf, so this callback has to update attr explicitly here
> +		 */
> +		attr->attach_btf_obj_fd = btf_obj_fd;
> +		attr->attach_btf_id = btf_type_id;
> +	}
> +	return 0;
> +}
> +
We talked on VC about some general approach questions I had here, will
summarize. Discussion touched on changes in patches 5 and 6 as well. I thought 
the pulling of these chunks into libbpf_preload_prog made sense, but wondered
whether some of this prog-type specific functionality would also be useful to
"average" custom sec_def writer even if it's not considered 'standard libbpf
handling', e.g. custom sec_def writer whose SEC produces a PROG_TYPE_TRACING
is likely to want the find_attach_btf_id niceness as well. So perhaps something
like the ability to chain the callbacks so that sec_def writer can use libbpf's
would be useful.

Your response was that you explicitly wanted to avoid doing this because this
would result in libbpf's callbacks becoming part of the API and stability 
requirements following from that. Furthermore, you don't anticipate libbpf's
preload callback becoming very complicated and expect that the average 
custom sec_def writer will be familiar enough with libbpf to be able to pull
out whatever they need.

Response made sense to me, LGTM

Acked-by: Dave Marchevsky <davemarchevsky@fb.com>
Andrii Nakryiko Sept. 22, 2021, 10:06 p.m. UTC | #2
On Tue, Sep 21, 2021 at 5:42 PM Dave Marchevsky <davemarchevsky@fb.com> wrote:
>
> On 9/20/21 7:43 PM, Andrii Nakryiko wrote:
> > Refactor internals of libbpf to allow adding custom SEC() handling logic
> > easily from outside of libbpf. To that effect, each SEC()-handling
> > registration sets mandatory program type/expected attach type for
> > a given prefix and can provide three callbacks called at different
> > points of BPF program lifetime:
> >
> >   - init callback for right after bpf_program is initialized and
> >   prog_type/expected_attach_type is set. This happens during
> >   bpf_object__open() step, close to the very end of constructing
> >   bpf_object, so all the libbpf APIs for querying and updating
> >   bpf_program properties should be available;
>
> Do you have a usecase in mind that would set this? USDT?

Don't have specific use case in mind, but this callback gives the
fully constructed `struct bpf_program` access to those custom
callbacks, so if there is any additional book keeping/feature
checking/whatever that needs to be done, this will be the earliest
point where some library/framework will discover the program. Felt
like an important addition, even if libbpf internally has no need for
it (because libbpf can always access struct bpf_program through its
own means).

>
> >   - pre-load callback is called right before BPF_PROG_LOAD command is
> >   called in the kernel. This callbacks has ability to set both
> >   bpf_program properties, as well as program load attributes, overriding
> >   and augmenting the standard libbpf handling of them;
>
> [...]
>
> > @@ -6094,6 +6100,44 @@ static int bpf_object__sanitize_prog(struct bpf_object *obj, struct bpf_program
> >       return 0;
> >  }
> >
> > +static int libbpf_find_attach_btf_id(struct bpf_program *prog, int *btf_obj_fd, int *btf_type_id);
> > +
> > +/* this is called as prog->sec_def->preload_fn for libbpf-supported sec_defs */
> > +static int libbpf_preload_prog(struct bpf_program *prog,
> > +                            struct bpf_prog_load_params *attr, long cookie)
> > +{
> > +     /* old kernels might not support specifying expected_attach_type */
> > +     if (prog->sec_def->is_exp_attach_type_optional &&
> > +         !kernel_supports(prog->obj, FEAT_EXP_ATTACH_TYPE))
> > +             attr->expected_attach_type = 0;
> > +
> > +     if (prog->sec_def->is_sleepable)
> > +             attr->prog_flags |= BPF_F_SLEEPABLE;
> > +
> > +     if ((prog->type == BPF_PROG_TYPE_TRACING ||
> > +          prog->type == BPF_PROG_TYPE_LSM ||
> > +          prog->type == BPF_PROG_TYPE_EXT) && !prog->attach_btf_id) {
> > +             int btf_obj_fd = 0, btf_type_id = 0, err;
> > +
> > +             err = libbpf_find_attach_btf_id(prog, &btf_obj_fd, &btf_type_id);
> > +             if (err)
> > +                     return err;
> > +
> > +             /* cache resolved BTF FD and BTF type ID in the prog */
> > +             prog->attach_btf_obj_fd = btf_obj_fd;
> > +             prog->attach_btf_id = btf_type_id;
> > +
> > +             /* but by now libbpf common logic is not utilizing
> > +              * prog->atach_btf_obj_fd/prog->attach_btf_id anymore because
> > +              * this callback is called after attrs were populated by
> > +              * libbpf, so this callback has to update attr explicitly here
> > +              */
> > +             attr->attach_btf_obj_fd = btf_obj_fd;
> > +             attr->attach_btf_id = btf_type_id;
> > +     }
> > +     return 0;
> > +}
> > +
> We talked on VC about some general approach questions I had here, will
> summarize. Discussion touched on changes in patches 5 and 6 as well. I thought
> the pulling of these chunks into libbpf_preload_prog made sense, but wondered
> whether some of this prog-type specific functionality would also be useful to
> "average" custom sec_def writer even if it's not considered 'standard libbpf
> handling', e.g. custom sec_def writer whose SEC produces a PROG_TYPE_TRACING
> is likely to want the find_attach_btf_id niceness as well. So perhaps something
> like the ability to chain the callbacks so that sec_def writer can use libbpf's
> would be useful.
>
> Your response was that you explicitly wanted to avoid doing this because this
> would result in libbpf's callbacks becoming part of the API and stability
> requirements following from that. Furthermore, you don't anticipate libbpf's
> preload callback becoming very complicated and expect that the average
> custom sec_def writer will be familiar enough with libbpf to be able to pull
> out whatever they need.
>
> Response made sense to me, LGTM
>
> Acked-by: Dave Marchevsky <davemarchevsky@fb.com>
diff mbox series

Patch

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index da65a1666a5e..8ab2edbf7a3b 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -220,7 +220,9 @@  struct reloc_desc {
 
 struct bpf_sec_def;
 
-typedef struct bpf_link *(*attach_fn_t)(const struct bpf_program *prog);
+typedef int (*init_fn_t)(struct bpf_program *prog, long cookie);
+typedef int (*preload_fn_t)(struct bpf_program *prog, struct bpf_prog_load_params *attr, long cookie);
+typedef struct bpf_link *(*attach_fn_t)(const struct bpf_program *prog, long cookie);
 
 struct bpf_sec_def {
 	const char *sec;
@@ -231,7 +233,11 @@  struct bpf_sec_def {
 	bool is_attachable;
 	bool is_attach_btf;
 	bool is_sleepable;
+
+	init_fn_t init_fn;
+	preload_fn_t preload_fn;
 	attach_fn_t attach_fn;
+	long cookie;
 };
 
 /*
@@ -6094,6 +6100,44 @@  static int bpf_object__sanitize_prog(struct bpf_object *obj, struct bpf_program
 	return 0;
 }
 
+static int libbpf_find_attach_btf_id(struct bpf_program *prog, int *btf_obj_fd, int *btf_type_id);
+
+/* this is called as prog->sec_def->preload_fn for libbpf-supported sec_defs */
+static int libbpf_preload_prog(struct bpf_program *prog,
+			       struct bpf_prog_load_params *attr, long cookie)
+{
+	/* old kernels might not support specifying expected_attach_type */
+	if (prog->sec_def->is_exp_attach_type_optional &&
+	    !kernel_supports(prog->obj, FEAT_EXP_ATTACH_TYPE))
+		attr->expected_attach_type = 0;
+
+	if (prog->sec_def->is_sleepable)
+		attr->prog_flags |= BPF_F_SLEEPABLE;
+
+	if ((prog->type == BPF_PROG_TYPE_TRACING ||
+	     prog->type == BPF_PROG_TYPE_LSM ||
+	     prog->type == BPF_PROG_TYPE_EXT) && !prog->attach_btf_id) {
+		int btf_obj_fd = 0, btf_type_id = 0, err;
+
+		err = libbpf_find_attach_btf_id(prog, &btf_obj_fd, &btf_type_id);
+		if (err)
+			return err;
+
+		/* cache resolved BTF FD and BTF type ID in the prog */
+		prog->attach_btf_obj_fd = btf_obj_fd;
+		prog->attach_btf_id = btf_type_id;
+
+		/* but by now libbpf common logic is not utilizing
+		 * prog->atach_btf_obj_fd/prog->attach_btf_id anymore because
+		 * this callback is called after attrs were populated by
+		 * libbpf, so this callback has to update attr explicitly here
+		 */
+		attr->attach_btf_obj_fd = btf_obj_fd;
+		attr->attach_btf_id = btf_type_id;
+	}
+	return 0;
+}
+
 static int
 load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
 	     char *license, __u32 kern_version, int *pfd)
@@ -6102,7 +6146,7 @@  load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
 	char *cp, errmsg[STRERR_BUFSIZE];
 	size_t log_buf_size = 0;
 	char *log_buf = NULL;
-	int btf_fd, ret;
+	int btf_fd, ret, err;
 
 	if (prog->type == BPF_PROG_TYPE_UNSPEC) {
 		/*
@@ -6118,22 +6162,15 @@  load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
 		return -EINVAL;
 
 	load_attr.prog_type = prog->type;
-	/* old kernels might not support specifying expected_attach_type */
-	if (!kernel_supports(prog->obj, FEAT_EXP_ATTACH_TYPE) && prog->sec_def &&
-	    prog->sec_def->is_exp_attach_type_optional)
-		load_attr.expected_attach_type = 0;
-	else
-		load_attr.expected_attach_type = prog->expected_attach_type;
+	load_attr.expected_attach_type = prog->expected_attach_type;
 	if (kernel_supports(prog->obj, FEAT_PROG_NAME))
 		load_attr.name = prog->name;
 	load_attr.insns = insns;
 	load_attr.insn_cnt = insns_cnt;
 	load_attr.license = license;
 	load_attr.attach_btf_id = prog->attach_btf_id;
-	if (prog->attach_prog_fd)
-		load_attr.attach_prog_fd = prog->attach_prog_fd;
-	else
-		load_attr.attach_btf_obj_fd = prog->attach_btf_obj_fd;
+	load_attr.attach_prog_fd = prog->attach_prog_fd;
+	load_attr.attach_btf_obj_fd = prog->attach_btf_obj_fd;
 	load_attr.attach_btf_id = prog->attach_btf_id;
 	load_attr.kern_version = kern_version;
 	load_attr.prog_ifindex = prog->prog_ifindex;
@@ -6152,6 +6189,16 @@  load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
 	load_attr.log_level = prog->log_level;
 	load_attr.prog_flags = prog->prog_flags;
 
+	/* adjust load_attr if sec_def provides custom preload callback */
+	if (prog->sec_def && prog->sec_def->preload_fn) {
+		err = prog->sec_def->preload_fn(prog, &load_attr, prog->sec_def->cookie);
+		if (err < 0) {
+			pr_warn("prog '%s': failed to prepare load attributes: %d\n",
+				prog->name, err);
+			return err;
+		}
+	}
+
 	if (prog->obj->gen_loader) {
 		bpf_gen__prog_load(prog->obj->gen_loader, &load_attr,
 				   prog - prog->obj->programs);
@@ -6267,8 +6314,6 @@  static int bpf_program__record_externs(struct bpf_program *prog)
 	return 0;
 }
 
-static int libbpf_find_attach_btf_id(struct bpf_program *prog, int *btf_obj_fd, int *btf_type_id);
-
 int bpf_program__load(struct bpf_program *prog, char *license, __u32 kern_ver)
 {
 	int err = 0, fd, i;
@@ -6278,19 +6323,6 @@  int bpf_program__load(struct bpf_program *prog, char *license, __u32 kern_ver)
 		return libbpf_err(-EINVAL);
 	}
 
-	if ((prog->type == BPF_PROG_TYPE_TRACING ||
-	     prog->type == BPF_PROG_TYPE_LSM ||
-	     prog->type == BPF_PROG_TYPE_EXT) && !prog->attach_btf_id) {
-		int btf_obj_fd = 0, btf_type_id = 0;
-
-		err = libbpf_find_attach_btf_id(prog, &btf_obj_fd, &btf_type_id);
-		if (err)
-			return libbpf_err(err);
-
-		prog->attach_btf_obj_fd = btf_obj_fd;
-		prog->attach_btf_id = btf_type_id;
-	}
-
 	if (prog->instances.nr < 0 || !prog->instances.fds) {
 		if (prog->preprocessor) {
 			pr_warn("Internal error: can't load program '%s'\n",
@@ -6400,6 +6432,7 @@  static const struct bpf_sec_def *find_sec_def(const char *sec_name);
 static int bpf_object_init_progs(struct bpf_object *obj, const struct bpf_object_open_opts *opts)
 {
 	struct bpf_program *prog;
+	int err;
 
 	bpf_object__for_each_program(prog, obj) {
 		prog->sec_def = find_sec_def(prog->sec_name);
@@ -6410,8 +6443,6 @@  static int bpf_object_init_progs(struct bpf_object *obj, const struct bpf_object
 			continue;
 		}
 
-		if (prog->sec_def->is_sleepable)
-			prog->prog_flags |= BPF_F_SLEEPABLE;
 		bpf_program__set_type(prog, prog->sec_def->prog_type);
 		bpf_program__set_expected_attach_type(prog, prog->sec_def->expected_attach_type);
 
@@ -6421,6 +6452,18 @@  static int bpf_object_init_progs(struct bpf_object *obj, const struct bpf_object
 		    prog->sec_def->prog_type == BPF_PROG_TYPE_EXT)
 			prog->attach_prog_fd = OPTS_GET(opts, attach_prog_fd, 0);
 #pragma GCC diagnostic pop
+
+		/* sec_def can have custom callback which should be called
+		 * after bpf_program is initialized to adjust its properties
+		 */
+		if (prog->sec_def->init_fn) {
+			err = prog->sec_def->init_fn(prog, prog->sec_def->cookie);
+			if (err < 0) {
+				pr_warn("prog '%s': failed to initialize: %d\n",
+					prog->name, err);
+				return err;
+			}
+		}
 	}
 
 	return 0;
@@ -7918,6 +7961,7 @@  void bpf_program__set_expected_attach_type(struct bpf_program *prog,
 		.is_exp_attach_type_optional = eatype_optional,		    \
 		.is_attachable = attachable,				    \
 		.is_attach_btf = attach_btf,				    \
+		.preload_fn = libbpf_preload_prog,			    \
 	}
 
 /* Programs that can NOT be attached. */
@@ -7944,15 +7988,16 @@  void bpf_program__set_expected_attach_type(struct bpf_program *prog,
 	.sec = sec_pfx,							    \
 	.len = sizeof(sec_pfx) - 1,					    \
 	.prog_type = BPF_PROG_TYPE_##ptype,				    \
+	.preload_fn = libbpf_preload_prog,				    \
 	__VA_ARGS__							    \
 }
 
-static struct bpf_link *attach_kprobe(const struct bpf_program *prog);
-static struct bpf_link *attach_tp(const struct bpf_program *prog);
-static struct bpf_link *attach_raw_tp(const struct bpf_program *prog);
-static struct bpf_link *attach_trace(const struct bpf_program *prog);
-static struct bpf_link *attach_lsm(const struct bpf_program *prog);
-static struct bpf_link *attach_iter(const struct bpf_program *prog);
+static struct bpf_link *attach_kprobe(const struct bpf_program *prog, long cookie);
+static struct bpf_link *attach_tp(const struct bpf_program *prog, long cookie);
+static struct bpf_link *attach_raw_tp(const struct bpf_program *prog, long cookie);
+static struct bpf_link *attach_trace(const struct bpf_program *prog, long cookie);
+static struct bpf_link *attach_lsm(const struct bpf_program *prog, long cookie);
+static struct bpf_link *attach_iter(const struct bpf_program *prog, long cookie);
 
 static const struct bpf_sec_def section_defs[] = {
 	BPF_PROG_SEC("socket",			BPF_PROG_TYPE_SOCKET_FILTER),
@@ -9400,7 +9445,7 @@  struct bpf_link *bpf_program__attach_kprobe(const struct bpf_program *prog,
 	return bpf_program__attach_kprobe_opts(prog, func_name, &opts);
 }
 
-static struct bpf_link *attach_kprobe(const struct bpf_program *prog)
+static struct bpf_link *attach_kprobe(const struct bpf_program *prog, long cookie)
 {
 	DECLARE_LIBBPF_OPTS(bpf_kprobe_opts, opts);
 	unsigned long offset = 0;
@@ -9573,7 +9618,7 @@  struct bpf_link *bpf_program__attach_tracepoint(const struct bpf_program *prog,
 	return bpf_program__attach_tracepoint_opts(prog, tp_category, tp_name, NULL);
 }
 
-static struct bpf_link *attach_tp(const struct bpf_program *prog)
+static struct bpf_link *attach_tp(const struct bpf_program *prog, long cookie)
 {
 	char *sec_name, *tp_cat, *tp_name;
 	struct bpf_link *link;
@@ -9627,7 +9672,7 @@  struct bpf_link *bpf_program__attach_raw_tracepoint(const struct bpf_program *pr
 	return link;
 }
 
-static struct bpf_link *attach_raw_tp(const struct bpf_program *prog)
+static struct bpf_link *attach_raw_tp(const struct bpf_program *prog, long cookie)
 {
 	const char *tp_name = prog->sec_name + prog->sec_def->len;
 
@@ -9674,12 +9719,12 @@  struct bpf_link *bpf_program__attach_lsm(const struct bpf_program *prog)
 	return bpf_program__attach_btf_id(prog);
 }
 
-static struct bpf_link *attach_trace(const struct bpf_program *prog)
+static struct bpf_link *attach_trace(const struct bpf_program *prog, long cookie)
 {
 	return bpf_program__attach_trace(prog);
 }
 
-static struct bpf_link *attach_lsm(const struct bpf_program *prog)
+static struct bpf_link *attach_lsm(const struct bpf_program *prog, long cookie)
 {
 	return bpf_program__attach_lsm(prog);
 }
@@ -9810,7 +9855,7 @@  bpf_program__attach_iter(const struct bpf_program *prog,
 	return link;
 }
 
-static struct bpf_link *attach_iter(const struct bpf_program *prog)
+static struct bpf_link *attach_iter(const struct bpf_program *prog, long cookie)
 {
 	return bpf_program__attach_iter(prog, NULL);
 }
@@ -9820,7 +9865,7 @@  struct bpf_link *bpf_program__attach(const struct bpf_program *prog)
 	if (!prog->sec_def || !prog->sec_def->attach_fn)
 		return libbpf_err_ptr(-ESRCH);
 
-	return prog->sec_def->attach_fn(prog);
+	return prog->sec_def->attach_fn(prog, prog->sec_def->cookie);
 }
 
 static int bpf_link__detach_struct_ops(struct bpf_link *link)