Skip to content

Commit 6245947

Browse files
anakryikoAlexei Starovoitov
authored andcommitted
libbpf: Allow gaps in BPF program sections to support overriden weak functions
Currently libbpf is very strict about parsing BPF program instruction sections. No gaps are allowed between sequential BPF programs within a given ELF section. Libbpf enforced that by keeping track of the next section offset that should start a new BPF (sub)program and cross-checks that by searching for a corresponding STT_FUNC ELF symbol. But this is too restrictive once we allow to have weak BPF programs and link together two or more BPF object files. In such case, some weak BPF programs might be "overridden" by either non-weak BPF program with the same name and signature, or even by another weak BPF program that just happened to be linked first. That, in turn, leaves BPF instructions of the "lost" BPF (sub)program intact, but there is no corresponding ELF symbol, because no one is going to be referencing it. Libbpf already correctly handles such cases in the sense that it won't append such dead code to actual BPF programs loaded into kernel. So the only change that needs to be done is to relax the logic of parsing BPF instruction sections. Instead of assuming next BPF (sub)program section offset, iterate available STT_FUNC ELF symbols to discover all available BPF subprograms and programs. Signed-off-by: Andrii Nakryiko <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Acked-by: Yonghong Song <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent aea28a6 commit 6245947

File tree

1 file changed

+22
-36
lines changed

1 file changed

+22
-36
lines changed

tools/lib/bpf/libbpf.c

Lines changed: 22 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -502,8 +502,6 @@ static Elf_Scn *elf_sec_by_name(const struct bpf_object *obj, const char *name);
502502
static int elf_sec_hdr(const struct bpf_object *obj, Elf_Scn *scn, GElf_Shdr *hdr);
503503
static const char *elf_sec_name(const struct bpf_object *obj, Elf_Scn *scn);
504504
static Elf_Data *elf_sec_data(const struct bpf_object *obj, Elf_Scn *scn);
505-
static int elf_sym_by_sec_off(const struct bpf_object *obj, size_t sec_idx,
506-
size_t off, __u32 sym_type, GElf_Sym *sym);
507505

508506
void bpf_program__unload(struct bpf_program *prog)
509507
{
@@ -644,25 +642,29 @@ static int
644642
bpf_object__add_programs(struct bpf_object *obj, Elf_Data *sec_data,
645643
const char *sec_name, int sec_idx)
646644
{
645+
Elf_Data *symbols = obj->efile.symbols;
647646
struct bpf_program *prog, *progs;
648647
void *data = sec_data->d_buf;
649-
size_t sec_sz = sec_data->d_size, sec_off, prog_sz;
650-
int nr_progs, err;
648+
size_t sec_sz = sec_data->d_size, sec_off, prog_sz, nr_syms;
649+
int nr_progs, err, i;
651650
const char *name;
652651
GElf_Sym sym;
653652

654653
progs = obj->programs;
655654
nr_progs = obj->nr_programs;
655+
nr_syms = symbols->d_size / sizeof(GElf_Sym);
656656
sec_off = 0;
657657

658-
while (sec_off < sec_sz) {
659-
if (elf_sym_by_sec_off(obj, sec_idx, sec_off, STT_FUNC, &sym)) {
660-
pr_warn("sec '%s': failed to find program symbol at offset %zu\n",
661-
sec_name, sec_off);
662-
return -LIBBPF_ERRNO__FORMAT;
663-
}
658+
for (i = 0; i < nr_syms; i++) {
659+
if (!gelf_getsym(symbols, i, &sym))
660+
continue;
661+
if (sym.st_shndx != sec_idx)
662+
continue;
663+
if (GELF_ST_TYPE(sym.st_info) != STT_FUNC)
664+
continue;
664665

665666
prog_sz = sym.st_size;
667+
sec_off = sym.st_value;
666668

667669
name = elf_sym_str(obj, sym.st_name);
668670
if (!name) {
@@ -711,8 +713,6 @@ bpf_object__add_programs(struct bpf_object *obj, Elf_Data *sec_data,
711713

712714
nr_progs++;
713715
obj->nr_programs = nr_progs;
714-
715-
sec_off += prog_sz;
716716
}
717717

718718
return 0;
@@ -2825,26 +2825,6 @@ static Elf_Data *elf_sec_data(const struct bpf_object *obj, Elf_Scn *scn)
28252825
return data;
28262826
}
28272827

2828-
static int elf_sym_by_sec_off(const struct bpf_object *obj, size_t sec_idx,
2829-
size_t off, __u32 sym_type, GElf_Sym *sym)
2830-
{
2831-
Elf_Data *symbols = obj->efile.symbols;
2832-
size_t n = symbols->d_size / sizeof(GElf_Sym);
2833-
int i;
2834-
2835-
for (i = 0; i < n; i++) {
2836-
if (!gelf_getsym(symbols, i, sym))
2837-
continue;
2838-
if (sym->st_shndx != sec_idx || sym->st_value != off)
2839-
continue;
2840-
if (GELF_ST_TYPE(sym->st_info) != sym_type)
2841-
continue;
2842-
return 0;
2843-
}
2844-
2845-
return -ENOENT;
2846-
}
2847-
28482828
static bool is_sec_name_dwarf(const char *name)
28492829
{
28502830
/* approximation, but the actual list is too long */
@@ -3723,11 +3703,16 @@ bpf_object__collect_prog_relos(struct bpf_object *obj, GElf_Shdr *shdr, Elf_Data
37233703
int err, i, nrels;
37243704
const char *sym_name;
37253705
__u32 insn_idx;
3706+
Elf_Scn *scn;
3707+
Elf_Data *scn_data;
37263708
GElf_Sym sym;
37273709
GElf_Rel rel;
37283710

3711+
scn = elf_sec_by_idx(obj, sec_idx);
3712+
scn_data = elf_sec_data(obj, scn);
3713+
37293714
relo_sec_name = elf_sec_str(obj, shdr->sh_name);
3730-
sec_name = elf_sec_name(obj, elf_sec_by_idx(obj, sec_idx));
3715+
sec_name = elf_sec_name(obj, scn);
37313716
if (!relo_sec_name || !sec_name)
37323717
return -EINVAL;
37333718

@@ -3745,7 +3730,8 @@ bpf_object__collect_prog_relos(struct bpf_object *obj, GElf_Shdr *shdr, Elf_Data
37453730
relo_sec_name, (size_t)GELF_R_SYM(rel.r_info), i);
37463731
return -LIBBPF_ERRNO__FORMAT;
37473732
}
3748-
if (rel.r_offset % BPF_INSN_SZ) {
3733+
3734+
if (rel.r_offset % BPF_INSN_SZ || rel.r_offset >= scn_data->d_size) {
37493735
pr_warn("sec '%s': invalid offset 0x%zx for relo #%d\n",
37503736
relo_sec_name, (size_t)GELF_R_SYM(rel.r_info), i);
37513737
return -LIBBPF_ERRNO__FORMAT;
@@ -3769,9 +3755,9 @@ bpf_object__collect_prog_relos(struct bpf_object *obj, GElf_Shdr *shdr, Elf_Data
37693755

37703756
prog = find_prog_by_sec_insn(obj, sec_idx, insn_idx);
37713757
if (!prog) {
3772-
pr_warn("sec '%s': relo #%d: program not found in section '%s' for insn #%u\n",
3758+
pr_debug("sec '%s': relo #%d: couldn't find program in section '%s' for insn #%u, probably overridden weak function, skipping...\n",
37733759
relo_sec_name, i, sec_name, insn_idx);
3774-
return -LIBBPF_ERRNO__RELOC;
3760+
continue;
37753761
}
37763762

37773763
relos = libbpf_reallocarray(prog->reloc_desc,

0 commit comments

Comments
 (0)