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:
Andrii Nakryiko authored and Andrii Nakryiko committed 2026-10-02 05:41:50 -07:00
1 parent f0b977cde9
commit 28826c4922
1 file changed
+11 -2
+11 -2
View File
@@ -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.