From 076f8b6d1e06f9569e1881c18c4340291aa86d05 Mon Sep 17 00:00:00 2001 From: Arth Srivastava Date: Fri, 13 Mar 2026 01:06:51 +0530 Subject: [PATCH 1/2] fix: Hide linker-defined symbols in shared libraries to match GNU ld behavior --- libwild/src/elf.rs | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/libwild/src/elf.rs b/libwild/src/elf.rs index 2bdf75541..ea4d6fb17 100644 --- a/libwild/src/elf.rs +++ b/libwild/src/elf.rs @@ -628,12 +628,28 @@ 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 + if output_kind.is_shared_object() { + symbols.section_end(output_section_id::TEXT, "etext").hide(); + symbols + .section_end(output_section_id::TEXT, "_etext") + .hide(); + symbols + .section_end(output_section_id::TEXT, "__etext") + .hide(); + } else { + symbols.section_end(output_section_id::TEXT, "etext"); + symbols.section_end(output_section_id::TEXT, "_etext"); + symbols.section_end(output_section_id::TEXT, "__etext"); + } - symbols.section_end(output_section_id::BSS, "end"); - symbols.section_end(output_section_id::BSS, "_end"); + if output_kind.is_shared_object() { + symbols.section_end(output_section_id::BSS, "end").hide(); + symbols.section_end(output_section_id::BSS, "_end").hide(); + } else { + symbols.section_end(output_section_id::BSS, "end"); + symbols.section_end(output_section_id::BSS, "_end"); + } symbols.section_end(output_section_id::BSS, "__end").hide(); // TODO: define the symbol only on RISC-V target @@ -642,8 +658,15 @@ 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"); + if output_kind.is_shared_object() { + symbols.section_end(output_section_id::DATA, "edata").hide(); + symbols + .section_end(output_section_id::DATA, "_edata") + .hide(); + } else { + symbols.section_end(output_section_id::DATA, "edata"); + symbols.section_end(output_section_id::DATA, "_edata"); + } symbols .section_start(output_section_id::TDATA, "__tdata_start") From e8dcd8bfcdf29c87116e9f652287a1e7d22886d8 Mon Sep 17 00:00:00 2001 From: Arth Srivastava Date: Sun, 15 Mar 2026 01:34:45 +0530 Subject: [PATCH 2/2] test: Add integration test and binding assertion for linker-defined symbols in shared objects --- libwild/src/elf.rs | 51 ++++++++----------- libwild/src/parsing.rs | 5 ++ wild/tests/integration_tests.rs | 28 ++++++++++ .../sources/linker-defined-syms-shared.c | 35 +++++++++++++ 4 files changed, 90 insertions(+), 29 deletions(-) create mode 100644 wild/tests/sources/linker-defined-syms-shared.c diff --git a/libwild/src/elf.rs b/libwild/src/elf.rs index ea4d6fb17..3c1ebb8c3 100644 --- a/libwild/src/elf.rs +++ b/libwild/src/elf.rs @@ -629,27 +629,23 @@ impl platform::Platform for Elf { .hide(); // GNU ld doesn't emit these symbols in shared libraries, so we hide them - if output_kind.is_shared_object() { - symbols.section_end(output_section_id::TEXT, "etext").hide(); - symbols - .section_end(output_section_id::TEXT, "_etext") - .hide(); - symbols - .section_end(output_section_id::TEXT, "__etext") - .hide(); - } else { - symbols.section_end(output_section_id::TEXT, "etext"); - symbols.section_end(output_section_id::TEXT, "_etext"); - symbols.section_end(output_section_id::TEXT, "__etext"); - } + 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); - if output_kind.is_shared_object() { - symbols.section_end(output_section_id::BSS, "end").hide(); - symbols.section_end(output_section_id::BSS, "_end").hide(); - } else { - 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 @@ -658,15 +654,12 @@ impl platform::Platform for Elf { crate::elf::GLOBAL_POINTER_SYMBOL_NAME, ); - if output_kind.is_shared_object() { - symbols.section_end(output_section_id::DATA, "edata").hide(); - symbols - .section_end(output_section_id::DATA, "_edata") - .hide(); - } else { - 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) {}