On 8/15/2026 3:57 AM, Andrii Nakryiko wrote:
> On Wed, Aug 12, 2026 at 8:26 PM Fuyu Zhao <[email protected]> wrote:
>>
>> Add support for a new ELF section ".kmod_btfs" that allows BPF programs
>> to declare which kernel modules need BTF loading. This avoids loading
>> all module BTFs and speeds up program load when only a subset of modules
>> is needed.
>>
>> Specifically, this patch introduces two internal functions:
>>   - bpf_object__collect_kmod_btf_names(): parses the ".kmod_btfs"
>>     section and collects the declared module names.
>>   - is_kmod_btf_needed(): determines whether a given module's BTF
>>     should be loaded, allowing libbpf to skip unneeded modules.
>>
>> Signed-off-by: Fuyu Zhao <[email protected]>
>> ---
>>  tools/lib/bpf/bpf_helpers.h |  14 +++++
>>  tools/lib/bpf/libbpf.c      | 106 ++++++++++++++++++++++++++++++++++++
>>  2 files changed, 120 insertions(+)
>>
>> diff --git a/tools/lib/bpf/bpf_helpers.h b/tools/lib/bpf/bpf_helpers.h
>> index 9d160b5b9c0e..171ea055cd32 100644
>> --- a/tools/lib/bpf/bpf_helpers.h
>> +++ b/tools/lib/bpf/bpf_helpers.h
>> @@ -188,6 +188,20 @@ enum libbpf_tristate {
>>         TRI_MODULE = 2,
>>  };
>>
>> +/* Helper typedef for declaring kernel module names that need BTF loading.
>> + *
>> + * Usage: define an array in the ".kmod_btfs" ELF section to specify
>> + * which modules need BTF loading:
>> + *
>> + *   DEFINE_KMOD_BTFS(_needed_kmods)= { "module1", "module2", ... };
>> + *
>> + * This avoids unnecessary BTF loading and speeds up the BPF program
>> + * load process.
>> + */
>> +#define KMOD_NAME_LEN 64
>> +#define DEFINE_KMOD_BTFS(name) \
>> +       SEC(".kmod_btfs") char name[][KMOD_NAME_LEN]
>> +
> 
> I don't really like another special ELF section just to provide a
> custom list of modules.
> 
> If we have to do a customization of BTF searching behavior of libbpf,
> let's do this through bpf_object_open_opts struct and pass NULL or a
> empty/non-empty list of kernel modules, or whatever other appropriate
> way to specify this.
> 
> But in any case, extending ELF format for BPF object file for this
> seems like an overkill and overcomplication.
> 
> pw-bot: cr
> 

Hi Andrii,

Thanks for the feedback. I also considered passing the module list through
bpf_object_open_opts.

The main reason for using an ELF section is that it keeps the module
dependency information together with the BPF object and requires minimal
changes for existing users.

However, I understand the concern that extending the ELF format introduces
additional complexity. I'll explore bpf_object_open_opts or other more
suitable approaches.

Best regards,
Fuyu

>>  #define __kconfig __attribute__((section(".kconfig")))
>>  #define __ksym __attribute__((section(".ksyms")))
>>  #define __kptr_untrusted __attribute__((btf_type_tag("kptr_untrusted")))
>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
>> index 514e4e9daa82..9fe8a653e8b6 100644
>> --- a/tools/lib/bpf/libbpf.c
>> +++ b/tools/lib/bpf/libbpf.c
>> @@ -548,6 +548,7 @@ struct bpf_struct_ops {
>>  #define STRUCT_OPS_SEC ".struct_ops"
>>  #define STRUCT_OPS_LINK_SEC ".struct_ops.link"
>>  #define ARENA_SEC ".addr_space.1"
>> +#define KMODS_BTFS_SEC ".kmod_btfs"
>>
>>  enum libbpf_map_type {
>>         LIBBPF_MAP_UNSPEC,
>> @@ -703,6 +704,9 @@ enum bpf_object_state {
>>         OBJ_LOADED,
>>  };
>>
>> +/* Should match the definition in bpf_helpers.h */
>> +#define KMOD_NAME_LEN 64
>> +
>>  struct bpf_object {
>>         char name[BPF_OBJ_NAME_LEN];
>>         char license[64];
>> @@ -779,6 +783,14 @@ struct bpf_object {
>>         char *token_path;
>>         int token_fd;
>>
>> +       /* kernel module BTFs to load, declared in ".kmod_btfs" ELF section 
>> */
>> +       struct {
>> +               char (*data)[KMOD_NAME_LEN];
>> +               size_t nr_names;
>> +               size_t nr_loaded;
>> +               struct hashmap *hashmap;
>> +       } *kmod_btfs;
>> +
>>         char path[];
>>  };
>>
>> @@ -901,6 +913,75 @@ bpf_object__init_prog(struct bpf_object *obj, struct 
>> bpf_program *prog,
>>         return -ENOMEM;
>>  }
>>
>> +static size_t mod_name_hash_fn(long key, void *ctx)
>> +{
>> +       return str_hash((char *)key);
>> +}
>> +
>> +static bool mod_name_equal_fn(long key1, long key2, void *ctx)
>> +{
>> +       return strcmp((char *)key1, (char *)key2) == 0;
>> +}
>> +
>> +static int
>> +bpf_object__collect_kmod_btf_names(struct bpf_object *obj, Elf_Data 
>> *sec_data,
>> +                                  const char *sec_name)
>> +{
>> +       int module_cnt, i, err = 0;
>> +
>> +       if (obj->kmod_btfs) {
>> +               pr_warn("sec '%s': duplicate sections detected\n", sec_name);
>> +               return -EEXIST;
>> +       }
>> +
>> +       if (sec_data->d_size % KMOD_NAME_LEN != 0) {
>> +               pr_warn("sec '%s': size %zu should be multiple of %d\n",
>> +                       sec_name, sec_data->d_size, KMOD_NAME_LEN);
>> +               return -EINVAL;
>> +       }
>> +
>> +       module_cnt = sec_data->d_size / KMOD_NAME_LEN;
>> +       obj->kmod_btfs = calloc(1, sizeof(*obj->kmod_btfs));
>> +       if (!obj->kmod_btfs)
>> +               return -ENOMEM;
>> +
>> +       obj->kmod_btfs->data = calloc(module_cnt, KMOD_NAME_LEN);
>> +       if (!obj->kmod_btfs->data) {
>> +               err = -ENOMEM;
>> +               goto err_out;
>> +       }
>> +       memcpy(obj->kmod_btfs->data, sec_data->d_buf, sec_data->d_size);
>> +
>> +       obj->kmod_btfs->hashmap = hashmap__new(mod_name_hash_fn,
>> +                                              mod_name_equal_fn, NULL);
>> +       if (IS_ERR(obj->kmod_btfs->hashmap)) {
>> +               err = PTR_ERR(obj->kmod_btfs->hashmap);
>> +               goto err_out;
>> +       }
>> +
>> +       for (i = 0; i < module_cnt; i++) {
>> +               obj->kmod_btfs->data[i][KMOD_NAME_LEN - 1] = '\0';
>> +               if (hashmap__find(obj->kmod_btfs->hashmap,
>> +                                 obj->kmod_btfs->data[i], NULL)) {
>> +                       pr_warn("sec '%s': ignored duplicate module '%s'\n",
>> +                               sec_name, obj->kmod_btfs->data[i]);
>> +                       continue;
>> +               }
>> +               err = hashmap__set(obj->kmod_btfs->hashmap, 
>> obj->kmod_btfs->data[i],
>> +                                  0, NULL, NULL);
>> +               if (err)
>> +                       goto err_out;
>> +               obj->kmod_btfs->nr_names++;
>> +       }
>> +       return 0;
>> +
>> +err_out:
>> +       hashmap__free(obj->kmod_btfs->hashmap);
>> +       zfree(&obj->kmod_btfs->data);
>> +       zfree(&obj->kmod_btfs);
>> +       return err;
>> +}
>> +
>>  static int
>>  bpf_object__add_programs(struct bpf_object *obj, Elf_Data *sec_data,
>>                          const char *sec_name, int sec_idx)
>> @@ -4034,6 +4115,10 @@ static int bpf_object__elf_collect(struct bpf_object 
>> *obj)
>>                                 memcpy(obj->jumptables_data, data->d_buf, 
>> data->d_size);
>>                                 obj->jumptables_data_sz = data->d_size;
>>                                 obj->efile.jumptables_data_shndx = idx;
>> +                       } else if (strcmp(name, KMODS_BTFS_SEC) == 0) {
>> +                               err = 
>> bpf_object__collect_kmod_btf_names(obj, data, name);
>> +                               if (err)
>> +                                       return err;
>>                         } else {
>>                                 pr_info("elf: skipping unrecognized data 
>> section(%d) %s\n",
>>                                         idx, name);
>> @@ -5803,6 +5888,11 @@ int bpf_core_add_cands(struct bpf_core_cand 
>> *local_cand,
>>         return 0;
>>  }
>>
>> +static bool is_kmod_btf_needed(struct bpf_object *obj, const char *name)
>> +{
>> +       return hashmap_find(obj->kmod_btfs->hashmap, (long)name, NULL);
>> +}
>> +
>>  static int load_module_btfs(struct bpf_object *obj)
>>  {
>>         struct bpf_btf_info info;
>> @@ -5867,6 +5957,12 @@ static int load_module_btfs(struct bpf_object *obj)
>>                         continue;
>>                 }
>>
>> +               if (obj->kmod_btfs && obj->kmod_btfs->hashmap &&
>> +                   !is_kmod_btf_needed(obj, name)) {
>> +                       close(fd);
>> +                       continue;
>> +               }
>> +
>>                 btf = btf_get_from_fd(fd, obj->btf_vmlinux);
>>                 err = libbpf_get_error(btf);
>>                 if (err) {
>> @@ -5891,6 +5987,10 @@ static int load_module_btfs(struct bpf_object *obj)
>>                         break;
>>                 }
>>                 obj->btf_module_cnt++;
>> +
>> +               if (obj->kmod_btfs &&
>> +                   obj->kmod_btfs->nr_names == obj->kmod_btfs->nr_loaded)
>> +                       break;
>>         }
>>
>>         if (err) {
>> @@ -9030,6 +9130,12 @@ static void bpf_object_cleanup_btf(struct bpf_object 
>> *obj)
>>         /* clean up vmlinux BTF */
>>         btf__free(obj->btf_vmlinux);
>>         obj->btf_vmlinux = NULL;
>> +
>> +       if (obj->kmod_btfs) {
>> +               hashmap__free(obj->kmod_btfs->hashmap);
>> +               zfree(&obj->kmod_btfs->data);
>> +               zfree(&obj->kmod_btfs);
>> +       }
>>  }
>>
>>  static void bpf_object_post_load_cleanup(struct bpf_object *obj)
>> --
>> 2.34.1
>>


Reply via email to