mirror of
https://github.com/netdata/libbpf.git
synced 2026-11-05 17:30:15 +08:00
libbpf: Fix static linking of externs placed in allocated sections
Clang puts an extern variable that ends up with a section, an extern map in .maps or an arena global in .addr_space.<N>, into the BTF DATASEC of that section, next to the object's own definitions and with offset 0. Unlike .kconfig and .ksyms, that section is a real allocated ELF section whenever the same object also defines a variable in it. The static linker handles such externs incorrectly in two ways. The output symbol of an unresolved extern gets the destination section's index and offset instead of staying SHN_UNDEF with a zero value. For ephemeral sections both are 0, so this only shows up for allocated sections, where the extern turns into a size-zero NOTYPE definition. Linking the output again then fails with "conflicting non-weak symbol" against a strong definition of that variable, or, against a weak one, keeps the bogus symbol and resolves every reference to offset 0 of the section. And if the section of the object with the extern doesn't start at offset 0 of the output section, the output symbol fails linker_sanity_check_elf_symtab() on the next link with "invalid extern symbol" before any of that. When the extern is resolved by a later object in the same link, the DATASEC entry appended for the extern keeps its placeholder offset, as linker_append_btf() only updates the size of an existing entry. The ELF symbol and the BTF secinfo then disagree and skeleton generation fails with: Error: Something is wrong for .addr_space.1's variable #1: need offset 0, already at 8. Keep unresolved externs SHN_UNDEF with a zero value in the output symbol table, and once an extern is resolved take the offset of its DATASEC entry from the ELF symbol, which already accounts for weak vs. strong resolution. Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20260926000243.2830819-1-andrii@kernel.org
This commit is contained in:
1 parent
f0b977cde9
commit
28826c4922
1 file changed
+11
-2
+11
-2
@@ -2159,8 +2159,13 @@ add_sym:
|
||||
dst_sym->st_name = name_off;
|
||||
dst_sym->st_info = sym->st_info;
|
||||
dst_sym->st_other = sym->st_other;
|
||||
dst_sym->st_shndx = dst_sec ? dst_sec->sec_idx : sym->st_shndx;
|
||||
dst_sym->st_value = (src_sec ? src_sec->dst_off : 0) + sym->st_value;
|
||||
if (sym_is_extern) {
|
||||
dst_sym->st_shndx = SHN_UNDEF;
|
||||
dst_sym->st_value = 0;
|
||||
} else {
|
||||
dst_sym->st_shndx = dst_sec ? dst_sec->sec_idx : sym->st_shndx;
|
||||
dst_sym->st_value = (src_sec ? src_sec->dst_off : 0) + sym->st_value;
|
||||
}
|
||||
dst_sym->st_size = sym->st_size;
|
||||
|
||||
obj->sym_map[src_sym_idx] = dst_sym_idx;
|
||||
@@ -2592,6 +2597,10 @@ static int linker_append_btf(struct bpf_linker *linker, struct src_obj *obj)
|
||||
continue;
|
||||
|
||||
dst_var = &dst_sec->sec_vars[glob_sym->var_idx];
|
||||
if (!glob_sym->is_extern) {
|
||||
Elf64_Sym *sym = get_sym_by_idx(linker, glob_sym->sym_idx);
|
||||
dst_var->offset = sym->st_value;
|
||||
}
|
||||
/* Because underlying BTF type might have
|
||||
* changed, so might its size have changed, so
|
||||
* re-calculate and update it in sec_var.
|
||||
|
||||
Reference in new issue
Block a user