Bug 32783 - [RISC-V] Spurious dynamic weakdef symbols in binary
Summary: [RISC-V] Spurious dynamic weakdef symbols in binary
Status: UNCONFIRMED
Alias: None
Product: binutils
Classification: Unclassified
Component: binutils (show other bugs)
Version: 2.45
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2025-03-12 09:09 UTC by W. van der Laan
Modified: 2025-03-12 09:09 UTC (History)
0 users

See Also:
Host:
Target:
Build:
Last reconfirmed:
Project(s) to access:
ssh public key:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description W. van der Laan 2025-03-12 09:09:53 UTC
While performing deterministic guix builds for different architectures, we noticed one specific behavior only in RISC-V builds: apparently randomly (but deterministically), dynamic symbols were exported from the binary. 

To reproduce, create a small C++ test program:

#include <iostream>

int main()
{
    std::string test{"12345"};
    std::cout << test << std::endl;

    return 0;
}

The resulting binary will contain a spurious symbol "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag":

$ riscv64-linux-gnu-g++ test.cpp -o test.rv -Wl,--exclude-libs,ALL -fvisibility=hidden  -static-libstdc++ 
$ riscv64-linux-gnu-objdump -T test.rv|grep "\.text"
00000000000176b0 l    d  .text  0000000000000000              .text
0000000000018810  w   DF .text  00000000000000fe  Base        _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag

X86, for comparison; all other architectures i've tried behave like this:

$ x86_64-linux-gnu-g++ test.cpp -o test.x64 -Wl,--exclude-libs,ALL -fvisibility=hidden  -static-libstdc++ 
$ x86_64-linux-gnu-objdump -T test.x64|grep "\.text"
(no output)

Even though libc++ is linked statically, default visibility is hidden, and all libs are excluded from export, the weakdef symbol leaks into the final binary.

Eventually we narrowed it down to the following code in allocate_dynrelocs in bfd/elf64-riscv.c:

      /* Make sure this symbol is output as a dynamic symbol.
         Undefined weak syms won't yet be marked as dynamic.  */
      if (h->dynindx == -1
          && !h->forced_local)
        {
          if (! bfd_elf_link_record_dynamic_symbol (info, h))
            return false;
        }

Commenting out this specific call to bfd_elf_link_record_dynamic_symbol makes the spurious symbols disappear. i don't, however, understand the motivation for this logic–what do relocations have to do with whether to export symbols?–so this is where we get stuck.

More details of the investigation can be found in https://github.com/bitcoin/bitcoin/issues/28095 . Let me know if you need more information.