diff --git a/libwild/src/elf.rs b/libwild/src/elf.rs index 2bdf75541..3c1ebb8c3 100644 --- a/libwild/src/elf.rs +++ b/libwild/src/elf.rs @@ -628,12 +628,24 @@ impl platform::Platform for Elf { .section_group_end(output_section_id::FINI_ARRAY, "__fini_array_end") .hide(); - symbols.section_end(output_section_id::TEXT, "etext"); - symbols.section_end(output_section_id::TEXT, "_etext"); - symbols.section_end(output_section_id::TEXT, "__etext"); + // GNU ld doesn't emit these symbols in shared libraries, so we hide them + let hidden = output_kind.is_shared_object(); + symbols + .section_end(output_section_id::TEXT, "etext") + .set_hidden(hidden); + symbols + .section_end(output_section_id::TEXT, "_etext") + .set_hidden(hidden); + symbols + .section_end(output_section_id::TEXT, "__etext") + .set_hidden(hidden); - symbols.section_end(output_section_id::BSS, "end"); - symbols.section_end(output_section_id::BSS, "_end"); + symbols + .section_end(output_section_id::BSS, "end") + .set_hidden(hidden); + symbols + .section_end(output_section_id::BSS, "_end") + .set_hidden(hidden); symbols.section_end(output_section_id::BSS, "__end").hide(); // TODO: define the symbol only on RISC-V target @@ -642,8 +654,12 @@ impl platform::Platform for Elf { crate::elf::GLOBAL_POINTER_SYMBOL_NAME, ); - symbols.section_end(output_section_id::DATA, "edata"); - symbols.section_end(output_section_id::DATA, "_edata"); + symbols + .section_end(output_section_id::DATA, "edata") + .set_hidden(hidden); + symbols + .section_end(output_section_id::DATA, "_edata") + .set_hidden(hidden); symbols .section_start(output_section_id::TDATA, "__tdata_start") diff --git a/libwild/src/parsing.rs b/libwild/src/parsing.rs index 02a87c5f4..2cebab8df 100644 --- a/libwild/src/parsing.rs +++ b/libwild/src/parsing.rs @@ -189,6 +189,11 @@ impl<'data> InternalSymDefInfo<'data> { self.is_hidden = true; self } + + pub(crate) fn set_hidden(&mut self, hidden: bool) -> &mut Self { + self.is_hidden = hidden; + self + } } impl<'data, P: Platform> ParsedInputObject<'data, P> { diff --git a/wild/tests/integration_tests.rs b/wild/tests/integration_tests.rs index d90ec0e8d..7d6d69df3 100644 --- a/wild/tests/integration_tests.rs +++ b/wild/tests/integration_tests.rs @@ -161,6 +161,9 @@ //! //! size=N: Type: Integer. Asserts the st_size of the symbol. Useful for verifying that //! size-changing relaxations (e.g. RISC-V call relaxation) were applied. +//! +//! binding=local|global|weak: Type: string. Asserts the binding of the symbol (STB_LOCAL, +//! STB_GLOBAL or STB_WEAK). mod external_tests; @@ -897,6 +900,8 @@ struct SymtabAssertions { absolute_address: Option, size: Option, + + binding: Option, } impl ExpectedSymtabEntry { @@ -2987,6 +2992,28 @@ fn verify_symbol_assertions( ); } } + + if let Some(expected_binding) = exp.assertions.binding.as_deref() { + if !matches!(expected_binding, "local" | "global" | "weak") { + bail!( + "Invalid binding value `{expected_binding}` for symbol `{name}`. \ + Must be one of: local, global, weak" + ); + } + let actual_binding = if sym.is_weak() { + "weak" + } else if sym.is_global() { + "global" + } else { + "local" + }; + if expected_binding != actual_binding { + bail!( + "Expected symbol `{name}` to have binding `{expected_binding}`, \ + but it actually had binding `{actual_binding}`" + ); + } + } } let missing: Vec<&str> = missing.into_keys().collect(); @@ -3559,6 +3586,7 @@ fn integration_test( "linker-script-executable.c", "linker-script-provide.c", "linker-defined-provide.c", + "linker-defined-syms-shared.c", "libc-ifunc.c", "libc-integration.c", "rust-integration.rs", diff --git a/wild/tests/sources/linker-defined-syms-shared.c b/wild/tests/sources/linker-defined-syms-shared.c new file mode 100644 index 000000000..2203e1f3c --- /dev/null +++ b/wild/tests/sources/linker-defined-syms-shared.c @@ -0,0 +1,35 @@ +// Test that linker-defined section boundary symbols are not exported to .dynsym +// in shared objects, matching GNU ld behavior. +// TODO: Once linker-defined symbol GC is implemented, add a variant that +// references these symbols and asserts they appear as GLOBAL in .dynsym. +//#LinkArgs:-shared -z now +//#RunEnabled:false +//#CompArgs:-fPIC +//#DiffEnabled:false + +// These symbols should NOT appear in .dynsym for shared objects +//#NoDynSym:etext +//#NoDynSym:_etext +//#NoDynSym:__etext +//#NoDynSym:end +//#NoDynSym:_end +//#NoDynSym:edata +//#NoDynSym:_edata + +// Wild keeps them in .symtab as LOCAL. GNU ld removes them entirely with +// --gc-sections, so we only assert this for Wild. +//#SkipLinker:ld +//#ExpectSym:etext binding=local +//#ExpectSym:_etext binding=local +//#ExpectSym:__etext binding=local +//#ExpectSym:end binding=local +//#ExpectSym:_end binding=local +//#ExpectSym:edata binding=local +//#ExpectSym:_edata binding=local + +// data_var and bss_var ensure .data and .bss sections exist so that +// edata, _edata, end and _end symbols are emitted by Wild. +int data_var = 1; +int bss_var; + +void foo(void) {}