resolve_btfids: Index BTF ID symbols by address

Keep an address-sorted index of parsed .BTF_ids symbols so that the
original BTF_ID symbol name can be recovered from an entry address.

Use the index in find_kfunc_flags() to scan BTF_SET8_KFUNCS entries
directly and match each entry back to the requested kfunc.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
Reviewed-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/bpf/20260722233518.778854-3-ihor.solodrai@linux.dev
This commit is contained in:
Ihor Solodrai
2026-07-22 16:35:12 -07:00
committed by Andrii Nakryiko
parent 47d62db550
commit c5ab68a5fa

View File

@@ -119,6 +119,11 @@ struct btf_id {
Elf64_Addr addr[ADDR_CNT];
};
struct addr_sym {
Elf64_Addr addr;
const char *name;
};
struct object {
const char *path;
const char *btf_path;
@@ -150,6 +155,10 @@ struct object {
int nr_structs;
int nr_unions;
int nr_typedefs;
struct addr_sym *addr_syms;
u32 addr_syms_cnt;
u32 addr_syms_cap;
};
#define KF_IMPLICIT_ARGS (1 << 16)
@@ -509,6 +518,40 @@ static int elf_collect(struct object *obj)
return 0;
}
static int push_addr_sym(struct object *obj, Elf64_Addr addr, const char *name)
{
if (ensure_mem(&obj->addr_syms, &obj->addr_syms_cap, obj->addr_syms_cnt + 1))
return -ENOMEM;
obj->addr_syms[obj->addr_syms_cnt++] = (struct addr_sym){
.addr = addr,
.name = name,
};
return 0;
}
static int cmp_addr_sym(const void *a, const void *b)
{
Elf64_Addr aa = ((const struct addr_sym *)a)->addr;
Elf64_Addr ab = ((const struct addr_sym *)b)->addr;
return (aa > ab) - (aa < ab);
}
static const char *find_name_by_addr(struct object *obj, Elf64_Addr addr)
{
struct addr_sym key = { .addr = addr };
struct addr_sym *res;
if (!obj->addr_syms_cnt)
return NULL;
res = bsearch(&key, obj->addr_syms, obj->addr_syms_cnt,
sizeof(*obj->addr_syms), cmp_addr_sym);
return res ? res->name : NULL;
}
static int symbols_collect(struct object *obj)
{
Elf_Scn *scn = NULL;
@@ -602,8 +645,15 @@ static int symbols_collect(struct object *obj)
return -1;
}
id->addr[id->addr_cnt++] = sym.st_value;
if (push_addr_sym(obj, sym.st_value, id->name))
return -1;
}
if (obj->addr_syms_cnt)
qsort(obj->addr_syms, obj->addr_syms_cnt,
sizeof(*obj->addr_syms), cmp_addr_sym);
return 0;
}
@@ -957,43 +1007,40 @@ static int collect_decl_tags(struct btf2btf_context *ctx)
}
/*
* To find the kfunc flags having its struct btf_id (with ELF addresses)
* we need to find the address that is in range of a set8.
* If a set8 is found, then the flags are located at addr + 4 bytes.
* To find kfunc flags, scan BTF_SET8_KFUNCS entries and use the entry
* address to recover the corresponding BTF_ID symbol name.
* Return 0 (no flags!) if not found.
*/
static u32 find_kfunc_flags(struct object *obj, struct btf_id *kfunc_id)
{
const u32 *elf_data_ptr = obj->efile.idlist->d_buf;
u64 set_lower_addr, set_upper_addr, addr;
Elf_Data *idlist = obj->efile.idlist;
struct btf_id *set_id;
struct rb_node *next;
u32 flags;
u64 idx;
for (next = rb_first(&obj->sets); next; next = rb_next(next)) {
struct btf_id_set8 *set8;
u64 set_addr;
set_id = rb_entry(next, struct btf_id, rb_node);
if (set_id->kind != BTF_ID_KIND_SET8 || set_id->addr_cnt != 1)
continue;
set_lower_addr = set_id->addr[0];
set_upper_addr = set_lower_addr + set_id->cnt * sizeof(u64);
set_addr = set_id->addr[0];
set8 = idlist->d_buf + (set_addr - obj->efile.idlist_addr);
if (!(set8->flags & BTF_SET8_KFUNCS))
continue;
for (u32 i = 0; i < kfunc_id->addr_cnt; i++) {
addr = kfunc_id->addr[i];
/*
* Lower bound is exclusive to skip the 8-byte header of the set.
* Upper bound is inclusive to capture the last entry at offset 8*cnt.
*/
if (set_lower_addr < addr && addr <= set_upper_addr) {
pr_debug("found kfunc %s in BTF_ID_FLAGS %s\n",
kfunc_id->name, set_id->name);
idx = addr - obj->efile.idlist_addr;
idx = idx / sizeof(u32) + 1;
flags = elf_data_ptr[idx];
for (u32 i = 0; i < set_id->cnt; i++) {
size_t off = (char *)&set8->pairs[i] - (char *)set8;
const char *name = find_name_by_addr(obj, set_addr + off);
return flags;
}
if (!name || strcmp(name, kfunc_id->name) != 0)
continue;
pr_debug("found kfunc %s in BTF_ID_FLAGS %s\n",
kfunc_id->name, set_id->name);
return set8->pairs[i].flags;
}
}
@@ -1586,6 +1633,7 @@ int main(int argc, const char **argv)
btf_id__free_all(&obj.typedefs);
btf_id__free_all(&obj.funcs);
btf_id__free_all(&obj.sets);
free(obj.addr_syms);
if (obj.efile.elf) {
elf_end(obj.efile.elf);
close(obj.efile.fd);