> resolve_btfids: Populate BTF link section

TL;DR: Nits only, no bugs found: patch_btf_link() duplicates much of
patch_btfids() and reopens vmlinux repeatedly, runs a full elf_collect() just
for class/encoding, and could use ensure_mem().

> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> index 15e7be13061f..f737fac60fa2 100644
> --- a/tools/bpf/resolve_btfids/main.c
> +++ b/tools/bpf/resolve_btfids/main.c

[ ... ]

> @@ -267,6 +282,49 @@ static int __ensure_mem(void **data, u32 *cap, u32 cnt, 
> size_t elem_sz)
>  #define ensure_mem(arr_ptr, cap_ptr, cnt) \
>       __ensure_mem((void **)(arr_ptr), (cap_ptr), (cnt), sizeof(**(arr_ptr)))
>  
> +static int parse_btf_link(const struct option *opt, const char *arg, int 
> unset)
> +{
> +     struct btf_links *links = opt->value;
> +     struct btf_link *link;
> +     char *separator;
> +
> +     if (unset)
> +             return -EINVAL;
> +     if (__ensure_mem((void **)&links->links, &links->cap, links->cnt + 1,
> +                      sizeof(*links->links)))
> +             return -ENOMEM;

this isn't a bug, but could parse_btf_link() use the ensure_mem() macro
defined right above it, the way the other callers in main.c do?

ensure_mem(&links->links, &links->cap, links->cnt + 1) would expand to the
same __ensure_mem() call.

[ ... ]

> @@ -1030,6 +1089,153 @@ static int dump_raw_btf(struct btf *btf, const char 
> *out_path)
>       return 0;
>  }
>  
> +static int patch_btf_link(struct object *obj, const struct btf_link *link)
> +{
> +     char section[128];

[ ... ]

> +     fd = open(obj->path, O_RDWR);
> +     if (fd < 0) {
> +             err = -errno;
> +             pr_err("FAILED to open %s: %s\n", obj->path, strerror(errno));
> +             goto out;
> +     }
> +     elf = elf_begin(fd, ELF_C_RDWR_MMAP, NULL);
> +     if (!elf) {
> +             err = -EINVAL;
> +             pr_err("FAILED to create ELF descriptor for %s: %s\n",
> +                    obj->path, elf_errmsg(-1));
> +             goto out;
> +     }
> +     elf_flagelf(elf, ELF_C_SET, ELF_F_LAYOUT);
> +     if (elf_getshdrstrndx(elf, &shdrstrndx)) {
> +             pr_err("FAILED to get section name index from %s: %s\n",
> +                    obj->path, elf_errmsg(-1));
> +             goto out;
> +     }
> +     while ((scn = elf_nextscn(elf, scn))) {
> +             if (gelf_getshdr(scn, &sh) != &sh) {
> +                     pr_err("FAILED to get section header from %s: %s\n",
> +                            obj->path, elf_errmsg(-1));
> +                     goto out;
> +             }
> +             name = elf_strptr(elf, shdrstrndx, sh.sh_name);
> +             if (name && !strcmp(name, section))
> +                     break;
> +     }
> +     if (!scn) {
> +             pr_err("FAILED: section %s not found in %s\n", section, 
> obj->path);
> +             goto out;
> +     }
> +     data = elf_getdata(scn, NULL);
> +     if (sh.sh_type != SHT_PROGBITS || !data || !data->d_buf ||
> +         data->d_size != module_name_len +
> +             BTF_SHA256_DIGEST_LENGTH + sizeof(u32)) {
> +             pr_err("FAILED: section %s is not a writable BTF link\n", 
> section);
> +             goto out;
> +     }

this isn't a bug, but most of patch_btf_link() repeats what patch_btfids()
already does: the open(O_RDWR) / elf_begin(ELF_C_RDWR_MMAP) /
elf_getshdrstrndx() setup, the loop that looks up a section by name,
elf_getdata(), and the elf_flagdata(ELF_F_DIRTY) / elf_update(ELF_C_WRITE) /
elf_end() / close() teardown.

This also means the final pass opens and rewrites vmlinux once for
.BTF_ids and then again for each --btf_link.

Could patch_btfids() and patch_btf_link() share a helper that opens the ELF
and finds a section by name, or even use a single RDWR descriptor?

[ ... ]

> @@ -1916,12 +2125,30 @@ int main(int argc, const char **argv)

[ ... ]

>       obj.path = argv[0];
>  
> -     if (btfids_path)
> -             return patch_btfids(btfids_path, obj.path);
> +     if (btfids_path) {
> +             err = patch_btfids(btfids_path, obj.path);
> +             if (err || !btf_links.cnt)
> +                     goto out;
> +             if (elf_collect(&obj)) {
> +                     err = -EINVAL;
> +                     goto out;
> +             }

this isn't a bug, but the --btf_link path runs the whole elf_collect() pass
only to get EI_CLASS and EI_DATA for patch_btf_link().

elf_collect() opens a second descriptor and mmap of the target, walks every
section, and byte-swaps .BTF_ids in its private map.  patch_btf_link() then
opens the same file again with its own Elf handle.

Since patch_btf_link() already calls elf_begin(), could it read the class
and data encoding directly with gelf_getehdr() / gelf_getclass(), so that
this elf_collect() call and the new efile.elf_class field aren't needed?


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/37924786344

Reply via email to