diff mbox series

[bpf-next,3/4] libbpf: add subskeleton scaffolding

Message ID 13cba9e1c39e999e7bfb14f1f986b76d13e150b3.1646188795.git.delyank@fb.com (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series Subskeleton support for BPF libraries | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR fail PR summary
netdev/tree_selection success Clearly marked for bpf-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers warning 6 maintainers not CCed: kpsingh@kernel.org john.fastabend@gmail.com kafai@fb.com songliubraving@fb.com yhs@fb.com netdev@vger.kernel.org
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch warning CHECK: No space is necessary after a cast WARNING: line length of 81 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 95 exceeds 80 columns WARNING: line length of 98 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 34 this patch: 34
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next fail VM_Test

Commit Message

Delyan Kratunov March 2, 2022, 2:48 a.m. UTC
In symmetry with bpf_object__open_skeleton(),
bpf_object__open_subskeleton() performs the actual walking and linking
of symbols described by bpf_sym_skeleton objects.

Signed-off-by: Delyan Kratunov <delyank@fb.com>
---
 tools/lib/bpf/libbpf.c   | 76 ++++++++++++++++++++++++++++++++++++++++
 tools/lib/bpf/libbpf.h   | 21 +++++++++++
 tools/lib/bpf/libbpf.map |  2 ++
 3 files changed, 99 insertions(+)

Comments

Daniel Borkmann March 2, 2022, 9:43 p.m. UTC | #1
Hi Delyan,

On 3/2/22 3:48 AM, Delyan Kratunov wrote:
> In symmetry with bpf_object__open_skeleton(),
> bpf_object__open_subskeleton() performs the actual walking and linking
> of symbols described by bpf_sym_skeleton objects.
> 
> Signed-off-by: Delyan Kratunov <delyank@fb.com>
> ---
>   tools/lib/bpf/libbpf.c   | 76 ++++++++++++++++++++++++++++++++++++++++
>   tools/lib/bpf/libbpf.h   | 21 +++++++++++
>   tools/lib/bpf/libbpf.map |  2 ++
>   3 files changed, 99 insertions(+)
> 

Triggers CI failure with:

 > build_kernel - Building kernel

   libbpf.c: In function ‘bpf_object__open_subskeleton’:
   libbpf.c:11779:27: error: ‘i’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
   11779 |      sym->section, s->syms[i].name);
         |                           ^
   cc1: all warnings being treated as errors
   make[5]: *** [/tmp/runner/work/bpf/bpf/tools/build/Makefile.build:96: /tmp/runner/work/bpf/bpf/tools/bpf/resolve_btfids/libbpf/staticobjs/libbpf.o] Error 1
   make[4]: *** [Makefile:157: /tmp/runner/work/bpf/bpf/tools/bpf/resolve_btfids/libbpf/staticobjs/libbpf-in.o] Error 2
   make[3]: *** [Makefile:55: /tmp/runner/work/bpf/bpf/tools/bpf/resolve_btfids//libbpf/libbpf.a] Error 2
   make[3]: *** Waiting for unfinished jobs....
   make[2]: *** [Makefile:72: bpf/resolve_btfids] Error 2
   make[1]: *** [Makefile:1334: tools/bpf/resolve_btfids] Error 2
   make[1]: *** Waiting for unfinished jobs....
   make: *** [Makefile:350: __build_one_by_one] Error 2
   Error: Process completed with exit code 2.

Thanks,
Daniel
Delyan Kratunov March 3, 2022, 12:20 a.m. UTC | #2
On Wed, 2022-03-02 at 22:43 +0100, Daniel Borkmann wrote:
> 
> Triggers CI failure with:
> 
>  > build_kernel - Building kernel
> 
>    libbpf.c: In function ‘bpf_object__open_subskeleton’:
>    libbpf.c:11779:27: error: ‘i’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
>    11779 |      sym->section, s->syms[i].name);
>          |                           ^
>    cc1: all warnings being treated as errors
>    make[5]: *** [/tmp/runner/work/bpf/bpf/tools/build/Makefile.build:96: /tmp/runner/work/bpf/bpf/tools/bpf/resolve_btfids/libbpf/staticobjs/libbpf.o] Error 1
>    make[4]: *** [Makefile:157: /tmp/runner/work/bpf/bpf/tools/bpf/resolve_btfids/libbpf/staticobjs/libbpf-in.o] Error 2
>    make[3]: *** [Makefile:55: /tmp/runner/work/bpf/bpf/tools/bpf/resolve_btfids//libbpf/libbpf.a] Error 2
>    make[3]: *** Waiting for unfinished jobs....
>    make[2]: *** [Makefile:72: bpf/resolve_btfids] Error 2
>    make[1]: *** [Makefile:1334: tools/bpf/resolve_btfids] Error 2
>    make[1]: *** Waiting for unfinished jobs....
>    make: *** [Makefile:350: __build_one_by_one] Error 2
>    Error: Process completed with exit code 2.
> 
> Thanks,
> Daniel

Argh, sorry about that, sending reroll in a few.
Andrii Nakryiko March 3, 2022, 12:28 a.m. UTC | #3
On Wed, Mar 2, 2022 at 4:20 PM Delyan Kratunov <delyank@fb.com> wrote:
>
> On Wed, 2022-03-02 at 22:43 +0100, Daniel Borkmann wrote:
> >
> > Triggers CI failure with:
> >
> >  > build_kernel - Building kernel
> >
> >    libbpf.c: In function ‘bpf_object__open_subskeleton’:
> >    libbpf.c:11779:27: error: ‘i’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
> >    11779 |      sym->section, s->syms[i].name);
> >          |                           ^
> >    cc1: all warnings being treated as errors
> >    make[5]: *** [/tmp/runner/work/bpf/bpf/tools/build/Makefile.build:96: /tmp/runner/work/bpf/bpf/tools/bpf/resolve_btfids/libbpf/staticobjs/libbpf.o] Error 1
> >    make[4]: *** [Makefile:157: /tmp/runner/work/bpf/bpf/tools/bpf/resolve_btfids/libbpf/staticobjs/libbpf-in.o] Error 2
> >    make[3]: *** [Makefile:55: /tmp/runner/work/bpf/bpf/tools/bpf/resolve_btfids//libbpf/libbpf.a] Error 2
> >    make[3]: *** Waiting for unfinished jobs....
> >    make[2]: *** [Makefile:72: bpf/resolve_btfids] Error 2
> >    make[1]: *** [Makefile:1334: tools/bpf/resolve_btfids] Error 2
> >    make[1]: *** Waiting for unfinished jobs....
> >    make: *** [Makefile:350: __build_one_by_one] Error 2
> >    Error: Process completed with exit code 2.
> >
> > Thanks,
> > Daniel
>
> Argh, sorry about that, sending reroll in a few.

Please hold off until you get review feedback
Delyan Kratunov March 3, 2022, 12:44 a.m. UTC | #4
On Wed, 2022-03-02 at 16:28 -0800, Andrii Nakryiko wrote:
> Please hold off until you get review feedback

Happy to wait, I discovered some functional issues in the test once rodata and
bpf helpers are involved in the lib. I have some debugging to do anyway.
Andrii Nakryiko March 3, 2022, 4:33 a.m. UTC | #5
On Tue, Mar 1, 2022 at 6:49 PM Delyan Kratunov <delyank@fb.com> wrote:
>
> In symmetry with bpf_object__open_skeleton(),
> bpf_object__open_subskeleton() performs the actual walking and linking
> of symbols described by bpf_sym_skeleton objects.
>
> Signed-off-by: Delyan Kratunov <delyank@fb.com>
> ---
>  tools/lib/bpf/libbpf.c   | 76 ++++++++++++++++++++++++++++++++++++++++
>  tools/lib/bpf/libbpf.h   | 21 +++++++++++
>  tools/lib/bpf/libbpf.map |  2 ++
>  3 files changed, 99 insertions(+)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index d20ae8f225ee..e6c27f4b9dea 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -11748,6 +11748,82 @@ int bpf_object__open_skeleton(struct bpf_object_skeleton *s,
>         return 0;
>  }
>
> +int bpf_object__open_subskeleton(struct bpf_object_subskeleton *s)
> +{
> +       int i, len, map_type_id, sym_idx;
> +       const char *var_name;
> +       struct bpf_map *map;
> +       struct btf *btf;
> +       const struct btf_type *map_type, *var_type;
> +       const struct bpf_sym_skeleton *sym;
> +       struct btf_var_secinfo *var;
> +       struct bpf_map *last_map = NULL;
> +       const struct btf_type *last_map_type = NULL;
> +
> +       if (!s->obj)
> +               return libbpf_err(-EINVAL);
> +
> +       btf = bpf_object__btf(s->obj);
> +       if (!btf)
> +               return libbpf_err(errno);

-errno

> +
> +       for (sym_idx = 0; sym_idx < s->sym_cnt; sym_idx++) {
> +               sym = &s->syms[sym_idx];
> +               if (last_map && (strcmp(sym->section, bpf_map__section_name(last_map)) == 0)) {

see above about having struct bpf_map ** instead of sym->section

> +                       map = last_map;
> +                       map_type = last_map_type;
> +               } else {
> +                       map = bpf_object__find_map_by_name(s->obj, sym->section);
> +                       if (!map) {
> +                               pr_warn("Could not find map for section %1$s, symbol %2$s",
> +                                       sym->section, s->syms[i].name);
> +                               return libbpf_err(-EINVAL);
> +                       }
> +                       map_type_id = btf__find_by_name_kind(btf, sym->section, BTF_KIND_DATASEC);

bpf_map__btf_value_type_id() ?

> +                       if (map_type_id < 0) {
> +                               pr_warn("Could not find map type in btf for section %1$s (due to symbol %2$s)",
> +                                       sym->section, sym->name);
> +                               return libbpf_err(-EINVAL);
> +                       }
> +                       map_type = btf__type_by_id(btf, map_type_id);
> +               }
> +

[...]

> +
> +void bpf_object__destroy_subskeleton(struct bpf_object_subskeleton *s)
> +{
> +       if (!s)
> +               return;
> +       if (s->syms)
> +               free(s->syms);

no need to check s->syms, free handles NULL just fine

> +       free(s);
> +}
> +
>  int bpf_object__load_skeleton(struct bpf_object_skeleton *s)
>  {
>         int i, err;
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index 7b66794f1c0a..915d59c31ad5 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -1291,6 +1291,27 @@ LIBBPF_API int bpf_object__attach_skeleton(struct bpf_object_skeleton *s);
>  LIBBPF_API void bpf_object__detach_skeleton(struct bpf_object_skeleton *s);
>  LIBBPF_API void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s);
>
> +struct bpf_sym_skeleton {

I tried to get used to this "sym" terminology for a bit, but it still
feels off. From user's perspective all this are variables. Any
objections to use "var" terminology?

> +       const char *name;
> +       const char *section;

what if we store a pointer to struct bpf_map * instead, that way we
won't need to search, we'll just have a pointer ready

> +       void **addr;
> +};
> +
> +struct bpf_object_subskeleton {
> +       size_t sz; /* size of this struct, for forward/backward compatibility */
> +
> +       const struct bpf_object *obj;
> +
> +       int sym_cnt;
> +       int sym_skel_sz;
> +       struct bpf_sym_skeleton *syms;

as mentioned in previous patch, let's also record maps and prog, it is
important and needed from the very beginning

> +};
> +
> +LIBBPF_API int
> +bpf_object__open_subskeleton(struct bpf_object_subskeleton *s);
> +LIBBPF_API void
> +bpf_object__destroy_subskeleton(struct bpf_object_subskeleton *s);
> +
>  struct gen_loader_opts {
>         size_t sz; /* size of this struct, for forward/backward compatiblity */
>         const char *data;
> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> index 5c85d297d955..81a1d0259866 100644
> --- a/tools/lib/bpf/libbpf.map
> +++ b/tools/lib/bpf/libbpf.map
> @@ -443,4 +443,6 @@ LIBBPF_0.7.0 {
>  LIBBPF_0.8.0 {
>         global:
>      bpf_map__section_name;
> +    bpf_object__open_subskeleton;
> +    bpf_object__destroy_subskeleton;

indentation looks off here... global should be indented with one tab,
and then APIs with two tabs

>  } LIBBPF_0.7.0;
> --
> 2.34.1
Andrii Nakryiko March 3, 2022, 4:34 a.m. UTC | #6
On Wed, Mar 2, 2022 at 8:33 PM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Tue, Mar 1, 2022 at 6:49 PM Delyan Kratunov <delyank@fb.com> wrote:
> >
> > In symmetry with bpf_object__open_skeleton(),
> > bpf_object__open_subskeleton() performs the actual walking and linking
> > of symbols described by bpf_sym_skeleton objects.
> >
> > Signed-off-by: Delyan Kratunov <delyank@fb.com>
> > ---
> >  tools/lib/bpf/libbpf.c   | 76 ++++++++++++++++++++++++++++++++++++++++
> >  tools/lib/bpf/libbpf.h   | 21 +++++++++++
> >  tools/lib/bpf/libbpf.map |  2 ++
> >  3 files changed, 99 insertions(+)
> >

forgot to mention, this patch logically probably should go before
bpftool changes: 1) define types and APIs in libbpf, and only then 2)
"use" those in bpftool

> > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > index d20ae8f225ee..e6c27f4b9dea 100644
> > --- a/tools/lib/bpf/libbpf.c
> > +++ b/tools/lib/bpf/libbpf.c
> > @@ -11748,6 +11748,82 @@ int bpf_object__open_skeleton(struct bpf_object_skeleton *s,
> >         return 0;
> >  }
> >
> > +int bpf_object__open_subskeleton(struct bpf_object_subskeleton *s)
> > +{
> > +       int i, len, map_type_id, sym_idx;
> > +       const char *var_name;
> > +       struct bpf_map *map;
> > +       struct btf *btf;
> > +       const struct btf_type *map_type, *var_type;
> > +       const struct bpf_sym_skeleton *sym;
> > +       struct btf_var_secinfo *var;
> > +       struct bpf_map *last_map = NULL;
> > +       const struct btf_type *last_map_type = NULL;
> > +
> > +       if (!s->obj)
> > +               return libbpf_err(-EINVAL);
> > +
> > +       btf = bpf_object__btf(s->obj);
> > +       if (!btf)
> > +               return libbpf_err(errno);
>
> -errno
>
> > +
> > +       for (sym_idx = 0; sym_idx < s->sym_cnt; sym_idx++) {
> > +               sym = &s->syms[sym_idx];
> > +               if (last_map && (strcmp(sym->section, bpf_map__section_name(last_map)) == 0)) {
>
> see above about having struct bpf_map ** instead of sym->section
>
> > +                       map = last_map;
> > +                       map_type = last_map_type;
> > +               } else {
> > +                       map = bpf_object__find_map_by_name(s->obj, sym->section);
> > +                       if (!map) {
> > +                               pr_warn("Could not find map for section %1$s, symbol %2$s",
> > +                                       sym->section, s->syms[i].name);
> > +                               return libbpf_err(-EINVAL);
> > +                       }
> > +                       map_type_id = btf__find_by_name_kind(btf, sym->section, BTF_KIND_DATASEC);
>
> bpf_map__btf_value_type_id() ?
>
> > +                       if (map_type_id < 0) {
> > +                               pr_warn("Could not find map type in btf for section %1$s (due to symbol %2$s)",
> > +                                       sym->section, sym->name);
> > +                               return libbpf_err(-EINVAL);
> > +                       }
> > +                       map_type = btf__type_by_id(btf, map_type_id);
> > +               }
> > +
>
> [...]
>
> > +
> > +void bpf_object__destroy_subskeleton(struct bpf_object_subskeleton *s)
> > +{
> > +       if (!s)
> > +               return;
> > +       if (s->syms)
> > +               free(s->syms);
>
> no need to check s->syms, free handles NULL just fine
>
> > +       free(s);
> > +}
> > +
> >  int bpf_object__load_skeleton(struct bpf_object_skeleton *s)
> >  {
> >         int i, err;
> > diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> > index 7b66794f1c0a..915d59c31ad5 100644
> > --- a/tools/lib/bpf/libbpf.h
> > +++ b/tools/lib/bpf/libbpf.h
> > @@ -1291,6 +1291,27 @@ LIBBPF_API int bpf_object__attach_skeleton(struct bpf_object_skeleton *s);
> >  LIBBPF_API void bpf_object__detach_skeleton(struct bpf_object_skeleton *s);
> >  LIBBPF_API void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s);
> >
> > +struct bpf_sym_skeleton {
>
> I tried to get used to this "sym" terminology for a bit, but it still
> feels off. From user's perspective all this are variables. Any
> objections to use "var" terminology?
>
> > +       const char *name;
> > +       const char *section;
>
> what if we store a pointer to struct bpf_map * instead, that way we
> won't need to search, we'll just have a pointer ready
>
> > +       void **addr;
> > +};
> > +
> > +struct bpf_object_subskeleton {
> > +       size_t sz; /* size of this struct, for forward/backward compatibility */
> > +
> > +       const struct bpf_object *obj;
> > +
> > +       int sym_cnt;
> > +       int sym_skel_sz;
> > +       struct bpf_sym_skeleton *syms;
>
> as mentioned in previous patch, let's also record maps and prog, it is
> important and needed from the very beginning
>
> > +};
> > +
> > +LIBBPF_API int
> > +bpf_object__open_subskeleton(struct bpf_object_subskeleton *s);
> > +LIBBPF_API void
> > +bpf_object__destroy_subskeleton(struct bpf_object_subskeleton *s);
> > +
> >  struct gen_loader_opts {
> >         size_t sz; /* size of this struct, for forward/backward compatiblity */
> >         const char *data;
> > diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> > index 5c85d297d955..81a1d0259866 100644
> > --- a/tools/lib/bpf/libbpf.map
> > +++ b/tools/lib/bpf/libbpf.map
> > @@ -443,4 +443,6 @@ LIBBPF_0.7.0 {
> >  LIBBPF_0.8.0 {
> >         global:
> >      bpf_map__section_name;
> > +    bpf_object__open_subskeleton;
> > +    bpf_object__destroy_subskeleton;
>
> indentation looks off here... global should be indented with one tab,
> and then APIs with two tabs
>
> >  } LIBBPF_0.7.0;
> > --
> > 2.34.1
Delyan Kratunov March 3, 2022, 7:09 p.m. UTC | #7
On Wed, 2022-03-02 at 20:34 -0800, Andrii Nakryiko wrote:
> > 
> 
> forgot to mention, this patch logically probably should go before
> bpftool changes: 1) define types and APIs in libbpf, and only then 2)
> "use" those in bpftool

Sure.

> > > 
> > > +struct bpf_sym_skeleton {
> > 
> > I tried to get used to this "sym" terminology for a bit, but it still
> > feels off. From user's perspective all this are variables. Any
> > objections to use "var" terminology?

"var" has a specific meaning in btf and I didn't want to make bpf_var_skeleton
look related to btf_var for example. Given the extern usage that libs require, I
figured "sym" would make sense to the user. 

If you don't think the confusion with btf_var is significant, I can rename it -
this is all used by generated code anyway.

> > 
> > > +       const char *name;
> > > +       const char *section;
> > 
> > what if we store a pointer to struct bpf_map * instead, that way we
> > won't need to search, we'll just have a pointer ready

We'd have to search *somewhere*. I'd rather have the search inside libbpf than
inside the generated code. Besides, finding the right bpf_map from within the
subskeleton is pretty annoying - you'll have to do suffix searches on the
bpf_map names in the passed-in bpf_object and codegening all that is unnecessary
when libbpf can look at real_name.

> 

Thanks,
Delyan
Andrii Nakryiko March 4, 2022, 7:40 p.m. UTC | #8
On Thu, Mar 3, 2022 at 11:09 AM Delyan Kratunov <delyank@fb.com> wrote:
>
> On Wed, 2022-03-02 at 20:34 -0800, Andrii Nakryiko wrote:
> > >
> >
> > forgot to mention, this patch logically probably should go before
> > bpftool changes: 1) define types and APIs in libbpf, and only then 2)
> > "use" those in bpftool
>
> Sure.
>
> > > >
> > > > +struct bpf_sym_skeleton {
> > >
> > > I tried to get used to this "sym" terminology for a bit, but it still
> > > feels off. From user's perspective all this are variables. Any
> > > objections to use "var" terminology?
>
> "var" has a specific meaning in btf and I didn't want to make bpf_var_skeleton
> look related to btf_var for example. Given the extern usage that libs require, I
> figured "sym" would make sense to the user.
>

Even for extern cases, we only generate stuff that really is a
variable. That is, it has allocated memory and there is specific
value. Only .kconfig is like that. .ksyms, for example, doesn't get
any exposure in skeleton as it can't be used from user-space code.

For me, symbol is just way too generic (could be basically anything,
including ELF section symbol, function, etc, etc). But our use case is
always variables available to both user-space and BPF code. I don't
think btf_var vs btf_var_skeleton confusion is significant (and even
then, each extern in .kconfig has corresponding BTF_KIND_VAR, so it
all is in sync).

> If you don't think the confusion with btf_var is significant, I can rename it -
> this is all used by generated code anyway.
>
> > >
> > > > +       const char *name;
> > > > +       const char *section;
> > >
> > > what if we store a pointer to struct bpf_map * instead, that way we
> > > won't need to search, we'll just have a pointer ready
>
> We'd have to search *somewhere*. I'd rather have the search inside libbpf than
> inside the generated code. Besides, finding the right bpf_map from within the
> subskeleton is pretty annoying - you'll have to do suffix searches on the
> bpf_map names in the passed-in bpf_object and codegening all that is unnecessary
> when libbpf can look at real_name.

I think you misunderstood what I proposed. There is no explicit
searching. Here is a simple example of sub-skeleton struct and how
code-generated code will fill it out


struct my_subskel {
    struct {
        struct bpf_map *my_map;
    } maps;
    struct my_subskel__data {
        int *my_var;
    } data;
};


/* in codegen'ed code */

struct my_subskel *s;

subskel->syms[0].name = "my_var";
subskel->syms[0].map = &s->maps.data_syn;


It's similar in principle to how we define maps (that are found and
filled out by libbpf):

        s->maps[4].name = ".data.dyn";
        s->maps[4].map = &obj->maps.data_dyn;
        s->maps[4].mmaped = (void **)&obj->data_dyn;


Except in this case we use &s->maps.data_syn for reading, not for
writing into it.

Hope this is clearer now.


>
> >
>
> Thanks,
> Delyan
diff mbox series

Patch

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index d20ae8f225ee..e6c27f4b9dea 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -11748,6 +11748,82 @@  int bpf_object__open_skeleton(struct bpf_object_skeleton *s,
 	return 0;
 }
 
+int bpf_object__open_subskeleton(struct bpf_object_subskeleton *s)
+{
+	int i, len, map_type_id, sym_idx;
+	const char *var_name;
+	struct bpf_map *map;
+	struct btf *btf;
+	const struct btf_type *map_type, *var_type;
+	const struct bpf_sym_skeleton *sym;
+	struct btf_var_secinfo *var;
+	struct bpf_map *last_map = NULL;
+	const struct btf_type *last_map_type = NULL;
+
+	if (!s->obj)
+		return libbpf_err(-EINVAL);
+
+	btf = bpf_object__btf(s->obj);
+	if (!btf)
+		return libbpf_err(errno);
+
+	for (sym_idx = 0; sym_idx < s->sym_cnt; sym_idx++) {
+		sym = &s->syms[sym_idx];
+		if (last_map && (strcmp(sym->section, bpf_map__section_name(last_map)) == 0)) {
+			map = last_map;
+			map_type = last_map_type;
+		} else {
+			map = bpf_object__find_map_by_name(s->obj, sym->section);
+			if (!map) {
+				pr_warn("Could not find map for section %1$s, symbol %2$s",
+					sym->section, s->syms[i].name);
+				return libbpf_err(-EINVAL);
+			}
+			map_type_id = btf__find_by_name_kind(btf, sym->section, BTF_KIND_DATASEC);
+			if (map_type_id < 0) {
+				pr_warn("Could not find map type in btf for section %1$s (due to symbol %2$s)",
+					sym->section, sym->name);
+				return libbpf_err(-EINVAL);
+			}
+			map_type = btf__type_by_id(btf, map_type_id);
+		}
+
+		/* We have a section and a corresponding type, now find the
+		 * symbol in the loaded map. This is clearly quadratic in the
+		 * number of symbols in the section, but that's easy to optimize
+		 * once the need arises.
+		 */
+
+		len = btf_vlen(map_type);
+		for (i = 0, var = btf_var_secinfos(map_type); i < len; i++, var++) {
+			var_type = btf__type_by_id(btf, var->type);
+			if (!var_type) {
+				pr_warn("Could not find var type for item %1$d in section %2$s",
+					i, sym->section);
+				return libbpf_err(-EINVAL);
+			}
+			var_name = btf__name_by_offset(btf, var_type->name_off);
+			if (strcmp(var_name, sym->name) == 0) {
+				*sym->addr = (char *) map->mmaped + var->offset;
+				break;
+			}
+		}
+
+		last_map = map;
+		last_map_type = map_type;
+	}
+	return 0;
+}
+
+void bpf_object__destroy_subskeleton(struct bpf_object_subskeleton *s)
+{
+	if (!s)
+		return;
+	if (s->syms)
+		free(s->syms);
+	free(s);
+}
+
 int bpf_object__load_skeleton(struct bpf_object_skeleton *s)
 {
 	int i, err;
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 7b66794f1c0a..915d59c31ad5 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -1291,6 +1291,27 @@  LIBBPF_API int bpf_object__attach_skeleton(struct bpf_object_skeleton *s);
 LIBBPF_API void bpf_object__detach_skeleton(struct bpf_object_skeleton *s);
 LIBBPF_API void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s);
 
+struct bpf_sym_skeleton {
+	const char *name;
+	const char *section;
+	void **addr;
+};
+
+struct bpf_object_subskeleton {
+	size_t sz; /* size of this struct, for forward/backward compatibility */
+
+	const struct bpf_object *obj;
+
+	int sym_cnt;
+	int sym_skel_sz;
+	struct bpf_sym_skeleton *syms;
+};
+
+LIBBPF_API int
+bpf_object__open_subskeleton(struct bpf_object_subskeleton *s);
+LIBBPF_API void
+bpf_object__destroy_subskeleton(struct bpf_object_subskeleton *s);
+
 struct gen_loader_opts {
 	size_t sz; /* size of this struct, for forward/backward compatiblity */
 	const char *data;
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 5c85d297d955..81a1d0259866 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -443,4 +443,6 @@  LIBBPF_0.7.0 {
 LIBBPF_0.8.0 {
 	global:
     bpf_map__section_name;
+    bpf_object__open_subskeleton;
+    bpf_object__destroy_subskeleton;
 } LIBBPF_0.7.0;