Skip to content

fix: Handle symbols with empty versions#1480

Merged
mati865 merged 7 commits into
wild-linker:mainfrom
mati865:push-skxmwsnxwkts
Feb 6, 2026
Merged

fix: Handle symbols with empty versions#1480
mati865 merged 7 commits into
wild-linker:mainfrom
mati865:push-skxmwsnxwkts

Conversation

@mati865

@mati865 mati865 commented Jan 22, 2026

Copy link
Copy Markdown
Member

Not to confuse with symbols without versions, empty version is foo@. It has special properties, making the linker ignore version script lines regarding this symbol.

Before this PR, fuse2 would fail to build with "Symbol fuse_new has undefined version".

@mati865 mati865 left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EDIT: fixed by ignoring symbols missing from one of the linkers.

This sort of duplicates symbol_diff.rs because missing symbols will be present in both cases, like in this case:

❯ WILD_REFERENCE_LINKER=ld.bfd ./run-with ~/Projects/wild/target/debug/wild
wild: ./bin
ref: ./bin.ref-linker
.gnu.hash
  wild Dynamic symbol `fuse_new` hash lookup found 128, expected 129
  ref Dynamic symbol `fuse_reply_statfs` hash lookup found 138, expected 139

.dynamic.DT_FLAGS_1.NOW
  wild 1
  ref

version.__fuse_exited
  wild
  ref global (hidden)

version.__fuse_loop_mt
  wild
  ref global (hidden)

version.__fuse_process_cmd
  wild
  ref global (hidden)

version.__fuse_read_cmd
  wild
  ref global (hidden)

version.__fuse_set_getcontext_func
  wild
  ref global (hidden)

version.__fuse_setup
  wild
  ref global (hidden)

version.__fuse_teardown
  wild
  ref global (hidden)

dynsym.__fuse_exited.section
  wild
  ref .text

dynsym.__fuse_loop_mt.section
  wild
  ref .text

dynsym.__fuse_process_cmd.section
  wild
  ref .text

dynsym.__fuse_read_cmd.section
  wild
  ref .text

dynsym.__fuse_set_getcontext_func.section
  wild
  ref .text

dynsym.__fuse_setup.section
  wild
  ref .text

dynsym.__fuse_teardown.section
  wild
  ref .text

Comment thread libwild/src/symbol_db.rs Outdated
Comment thread libwild/src/version_script.rs Outdated
);
}

values.sort_values();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, removing it doesn't fail Wild's tests, ATM.
This is necessary for cases when the same symbol has multiple versions like here:

version.fuse_new
  wild FUSE_2.5 (hidden),FUSE_2.6,FUSE_2.2 (hidden),global (hidden)
  ref FUSE_2.5 (hidden),global (hidden),FUSE_2.6,FUSE_2.2 (hidden)

Comment thread linker-diff/src/lib.rs Outdated
// If we don't optimise a TLS access, then we'll have references to __tls_get_addr,
// when GNU ld doesn't.
"dynsym.__tls_get_addr.*",
"version.__tls_get_addr",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

symbol_diff.rs uses a bit different formatting: dynsym.<section_name>.section, so there is an inconsistency here.

@mati865

mati865 commented Jan 23, 2026

Copy link
Copy Markdown
Member Author

EDIT: This also happens on loongarch64 but I don't test it locally.

@marxin any clue why dynsym is so much different between Wild and GNU ld only on RISC-V?
https://github.com/davidlattimore/wild/actions/runs/21263299851/job/61196004436

❯ readelf -WhsD /home/mateusz/Projects/wild/wild/tests/build/rust-integration-dynamic.rs/rdyn1.default-*.*.so | rg "File|entries" | sed "s#\s.*/rust-integration-dynamic.rs/# #g"
File: rdyn1.default-aarch64-3feecd11b8b8f5ad.ld.so
Symbol table for image contains 59 entries:
File: rdyn1.default-aarch64-3feecd11b8b8f5ad.wild.so
Symbol table for image contains 57 entries:
File: rdyn1.default-host-2c9e2ad080d8eb43.ld.so
Symbol table for image contains 57 entries:
File: rdyn1.default-host-2c9e2ad080d8eb43.wild.so
Symbol table for image contains 57 entries:
File: rdyn1.default-riscv64-8583f2504094e8de.ld.so
Symbol table for image contains 57 entries:
File: rdyn1.default-riscv64-8583f2504094e8de.wild.so
Symbol table for image contains 187 entries:
Details
❯ readelf -WsD /home/mateusz/Projects/wild/wild/tests/build/rust-integration-dynamic.rs/rdyn1.default-riscv64-8583f2504094e8de.wild.so

Symbol table for image contains 187 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTable
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND posix_spawn_file_actions_addchdir
     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_Resume@GCC_3.0 (2)
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_Backtrace@GCC_3.3 (3)
     6: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_GetLanguageSpecificData@GCC_3.0 (2)
     7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_FindEnclosingFunction@GCC_3.3 (3)
     8: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_GetIPInfo@GCC_4.2.0 (4)
     9: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_GetDataRelBase@GCC_3.0 (2)
    10: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_GetRegionStart@GCC_3.0 (2)
    11: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_SetIP@GCC_3.0 (2)
    12: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_GetIP@GCC_3.0 (2)
    13: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_DeleteException@GCC_3.0 (2)
    14: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_GetCFA@GCC_3.3 (3)
    15: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_RaiseException@GCC_3.0 (2)
    16: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_SetGR@GCC_3.0 (2)
    17: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_GetTextRelBase@GCC_3.0 (2)
    18: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND nanosleep@GLIBC_2.27 (5)
    19: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_detach@GLIBC_2.34 (11)
    20: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND gethostname@GLIBC_2.27 (5)
    21: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pwritev@GLIBC_2.27 (5)
    22: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND fchmod@GLIBC_2.27 (5)
    23: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND writev@GLIBC_2.27 (5)
    24: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_attr_setstacksize@GLIBC_2.34 (11)
    25: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND gnu_get_libc_version@GLIBC_2.27 (5)
    26: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND setsockopt@GLIBC_2.27 (5)
    27: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND chmod@GLIBC_2.27 (5)
    28: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND readv@GLIBC_2.27 (5)
    29: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND getpeername@GLIBC_2.27 (5)
    30: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_key_delete@GLIBC_2.34 (11)
    31: 0000000000000000     0 OBJECT  WEAK   DEFAULT  UND environ@GLIBC_2.27 (5)
    32: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND sysconf@GLIBC_2.27 (5)
    33: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND symlink@GLIBC_2.27 (5)
    34: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND dirfd@GLIBC_2.27 (5)
    35: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND setsid@GLIBC_2.27 (5)
    36: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND clock_gettime@GLIBC_2.27 (5)
    37: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND chroot@GLIBC_2.27 (5)
    38: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pidfd_spawnp@GLIBC_2.39 (12)
    39: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND openat64@GLIBC_2.27 (5)
    40: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND free@GLIBC_2.27 (5)
    41: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND chdir@GLIBC_2.27 (5)
    42: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND exit@GLIBC_2.27 (5)
    43: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND lseek64@GLIBC_2.27 (5)
    44: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND posix_spawn_file_actions_init@GLIBC_2.27 (5)
    45: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND statx@GLIBC_2.28 (6)
    46: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sendfile64@GLIBC_2.27 (5)
    47: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND setgid@GLIBC_2.27 (5)
    48: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND pread64@GLIBC_2.27 (5)
    49: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sigaddset@GLIBC_2.27 (5)
    50: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND readdir64@GLIBC_2.27 (5)
    51: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND setenv@GLIBC_2.27 (5)
    52: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND freeaddrinfo@GLIBC_2.27 (5)
    53: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND accept4@GLIBC_2.27 (5)
    54: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND futimens@GLIBC_2.27 (5)
    55: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getaddrinfo@GLIBC_2.27 (5)
    56: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND pthread_self@GLIBC_2.27 (5)
    57: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND fstatat64@GLIBC_2.33 (10)
    58: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND sigaction@GLIBC_2.27 (5)
    59: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND posix_spawn_file_actions_addchdir_np@GLIBC_2.29 (7)
    60: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND fcntl@GLIBC_2.27 (5)
    61: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND posix_spawnattr_setsigdefault@GLIBC_2.27 (5)
    62: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND closedir@GLIBC_2.27 (5)
    63: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND realpath@GLIBC_2.27 (5)
    64: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND bind@GLIBC_2.27 (5)
    65: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND open64@GLIBC_2.27 (5)
    66: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND setuid@GLIBC_2.27 (5)
    67: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND socketpair@GLIBC_2.27 (5)
    68: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND unlink@GLIBC_2.27 (5)
    69: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND munmap@GLIBC_2.27 (5)
    70: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND memmove@GLIBC_2.27 (5)
    71: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND mmap64@GLIBC_2.27 (5)
    72: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fdatasync@GLIBC_2.27 (5)
    73: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND setgroups@GLIBC_2.27 (5)
    74: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_thread_atexit_impl@GLIBC_2.27 (5)
    75: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND waitid@GLIBC_2.27 (5)
    76: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND posix_spawnattr_setpgroup@GLIBC_2.27 (5)
    77: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND poll@GLIBC_2.27 (5)
    78: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND flock@GLIBC_2.27 (5)
    79: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND socket@GLIBC_2.27 (5)
    80: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND setpgid@GLIBC_2.27 (5)
    81: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND fork@GLIBC_2.27 (5)
    82: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND recvfrom@GLIBC_2.27 (5)
    83: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __xpg_strerror_r@GLIBC_2.27 (5)
    84: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strlen@GLIBC_2.27 (5)
    85: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND getppid@GLIBC_2.27 (5)
    86: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_join@GLIBC_2.34 (11)
    87: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND rmdir@GLIBC_2.27 (5)
    88: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND read@GLIBC_2.27 (5)
    89: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND unsetenv@GLIBC_2.27 (5)
    90: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strnlen@GLIBC_2.27 (5)
    91: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND send@GLIBC_2.27 (5)
    92: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND mkdir@GLIBC_2.27 (5)
    93: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_attr_getguardsize@GLIBC_2.34 (11)
    94: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_attr_init@GLIBC_2.27 (5)
    95: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND fchown@GLIBC_2.27 (5)
    96: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_create@GLIBC_2.34 (11)
    97: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND dup2@GLIBC_2.27 (5)
    98: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND getpid@GLIBC_2.27 (5)
    99: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND recv@GLIBC_2.27 (5)
   100: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND chown@GLIBC_2.27 (5)
   101: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND readlink@GLIBC_2.27 (5)
   102: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND realloc@GLIBC_2.27 (5)
   103: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND execvp@GLIBC_2.27 (5)
   104: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND pthread_attr_destroy@GLIBC_2.27 (5)
   105: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _exit@GLIBC_2.27 (5)
   106: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND dl_iterate_phdr@GLIBC_2.27 (5)
   107: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pidfd_getpid@GLIBC_2.39 (12)
   108: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND lstat64@GLIBC_2.33 (10)
   109: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND getauxval@GLIBC_2.27 (5)
   110: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND shutdown@GLIBC_2.27 (5)
   111: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND malloc@GLIBC_2.27 (5)
   112: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND sched_yield@GLIBC_2.27 (5)
   113: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND recvmsg@GLIBC_2.27 (5)
   114: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND copy_file_range@GLIBC_2.27 (5)
   115: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_key_create@GLIBC_2.34 (11)
   116: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND posix_spawn_file_actions_adddup2@GLIBC_2.27 (5)
   117: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND sendto@GLIBC_2.27 (5)
   118: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_attr_getstack@GLIBC_2.34 (11)
   119: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND listen@GLIBC_2.27 (5)
   120: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND posix_spawn_file_actions_destroy@GLIBC_2.27 (5)
   121: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND posix_spawnattr_setflags@GLIBC_2.27 (5)
   122: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND mprotect@GLIBC_2.27 (5)
   123: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND dlsym@GLIBC_2.34 (11)
   124: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND sendmsg@GLIBC_2.27 (5)
   125: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND fdopendir@GLIBC_2.27 (5)
   126: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND linkat@GLIBC_2.27 (5)
   127: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __res_init@GLIBC_2.27 (5)
   128: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND fstat64@GLIBC_2.33 (10)
   129: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND getsockname@GLIBC_2.27 (5)
   130: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND clock_nanosleep@GLIBC_2.27 (5)
   131: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_finalize@GLIBC_2.27 (5)
   132: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND utimensat@GLIBC_2.27 (5)
   133: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND kill@GLIBC_2.27 (5)
   134: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND getsockopt@GLIBC_2.27 (5)
   135: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND bcmp@GLIBC_2.27 (5)
   136: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND mkfifo@GLIBC_2.27 (5)
   137: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getenv@GLIBC_2.27 (5)
   138: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND calloc@GLIBC_2.27 (5)
   139: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fsync@GLIBC_2.27 (5)
   140: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND memcmp@GLIBC_2.27 (5)
   141: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND gettid@GLIBC_2.30 (8)
   142: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND ftruncate64@GLIBC_2.27 (5)
   143: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND sigaltstack@GLIBC_2.27 (5)
   144: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND memset@GLIBC_2.27 (5)
   145: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_setspecific@GLIBC_2.34 (11)
   146: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND posix_spawnattr_init@GLIBC_2.27 (5)
   147: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND ioctl@GLIBC_2.27 (5)
   148: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND close@GLIBC_2.27 (5)
   149: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND preadv@GLIBC_2.27 (5)
   150: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND rename@GLIBC_2.27 (5)
   151: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND waitpid@GLIBC_2.27 (5)
   152: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND signal@GLIBC_2.27 (5)
   153: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND connect@GLIBC_2.27 (5)
   154: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND posix_spawnattr_destroy@GLIBC_2.27 (5)
   155: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND pwrite64@GLIBC_2.27 (5)
   156: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND opendir@GLIBC_2.27 (5)
   157: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gai_strerror@GLIBC_2.27 (5)
   158: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_setname_np@GLIBC_2.34 (11)
   159: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND getuid@GLIBC_2.27 (5)
   160: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sigemptyset@GLIBC_2.27 (5)
   161: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getpwuid_r@GLIBC_2.27 (5)
   162: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_getattr_np@GLIBC_2.32 (9)
   163: 0000000000000000     0 <OS specific>: 10 GLOBAL DEFAULT  UND memcpy@GLIBC_2.27 (5)
   164: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND getcwd@GLIBC_2.27 (5)
   165: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND posix_spawnp@GLIBC_2.27 (5)
   166: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND unlinkat@GLIBC_2.27 (5)
   167: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND stat64@GLIBC_2.33 (10)
   168: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __errno_location@GLIBC_2.27 (5)
   169: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND write@GLIBC_2.27 (5)
   170: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND syscall@GLIBC_2.27 (5)
   171: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND posix_memalign@GLIBC_2.27 (5)
   172: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND abort@GLIBC_2.27 (5)
   173: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND getrandom@GLIBC_2.27 (5)
   174: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sched_getaffinity@GLIBC_2.27 (5)
   175: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND pause@GLIBC_2.27 (5)
   176: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND splice@GLIBC_2.27 (5)
   177: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND lchown@GLIBC_2.27 (5)
   178: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND dup@GLIBC_2.27 (5)
   179: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND pipe2@GLIBC_2.27 (5)
   180: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __tls_get_addr@GLIBC_2.27 (13)
   181: 0000000000041820    42 FUNC    GLOBAL DEFAULT   14 get_tls1
   182: 0000000000041874    40 FUNC    GLOBAL DEFAULT   14 set_tls1
   183: 000000000004181c     4 FUNC    GLOBAL DEFAULT   14 foo
   184: 000000000004184a    42 FUNC    GLOBAL DEFAULT   14 get_tls2
   185: 000000000004189c    40 FUNC    GLOBAL DEFAULT   14 set_tls2
   186: 0000000000041818     4 FUNC    GLOBAL DEFAULT   14 bar

❯ readelf -WsD /home/mateusz/Projects/wild/wild/tests/build/rust-integration-dynamic.rs/rdyn1.default-riscv64-8583f2504094e8de.ld.so

Symbol table for image contains 57 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000003210     0 SECTION LOCAL  DEFAULT   10 .text
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND syscall@GLIBC_2.27 (2)
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_Backtrace@GCC_3.3 (3)
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTable
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getenv@GLIBC_2.27 (2)
     6: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND munmap@GLIBC_2.27 (2)
     7: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND gettid@GLIBC_2.30 (4)
     8: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_GetRegionStart@GCC_3.0 (5)
     9: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND malloc@GLIBC_2.27 (2)
    10: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_GetTextRelBase@GCC_3.0 (5)
    11: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_RaiseException@GCC_3.0 (5)
    12: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND realpath@GLIBC_2.27 (2)
    13: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND lseek64@GLIBC_2.27 (2)
    14: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND writev@GLIBC_2.27 (2)
    15: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND dl_iterate_phdr@GLIBC_2.27 (2)
    16: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND stat64@GLIBC_2.33 (6)
    17: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND mmap64@GLIBC_2.27 (2)
    18: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __errno_location@GLIBC_2.27 (2)
    19: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_key_create@GLIBC_2.34 (7)
    20: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND readlink@GLIBC_2.27 (2)
    21: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_GetIPInfo@GCC_4.2.0 (8)
    22: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND abort@GLIBC_2.27 (2)
    23: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND memmove@GLIBC_2.27 (2)
    24: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND memcpy@GLIBC_2.27 (2)
    25: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_GetLanguageSpecificData@GCC_3.0 (5)
    26: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_key_delete@GLIBC_2.34 (7)
    27: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND strlen@GLIBC_2.27 (2)
    28: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND read@GLIBC_2.27 (2)
    29: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND posix_memalign@GLIBC_2.27 (2)
    30: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_GetIP@GCC_3.0 (5)
    31: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND close@GLIBC_2.27 (2)
    32: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __tls_get_addr@GLIBC_2.27 (9)
    33: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND statx@GLIBC_2.28 (10)
    34: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_GetDataRelBase@GCC_3.0 (5)
    35: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_SetGR@GCC_3.0 (5)
    36: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getcwd@GLIBC_2.27 (2)
    37: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_thread_atexit_impl@GLIBC_2.27 (2)
    38: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND calloc@GLIBC_2.27 (2)
    39: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND bcmp@GLIBC_2.27 (2)
    40: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.27 (2)
    41: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND write@GLIBC_2.27 (2)
    42: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fstat64@GLIBC_2.33 (6)
    43: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
    44: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND realloc@GLIBC_2.27 (2)
    45: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND open64@GLIBC_2.27 (2)
    46: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_Resume@GCC_3.0 (5)
    47: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND memset@GLIBC_2.27 (2)
    48: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_setspecific@GLIBC_2.34 (7)
    49: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unwind_SetIP@GCC_3.0 (5)
    50: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND free@GLIBC_2.27 (2)
    51: 000000000000720e     4 FUNC    GLOBAL DEFAULT   10 bar
    52: 0000000000007212     4 FUNC    GLOBAL DEFAULT   10 foo
    53: 0000000000007216    42 FUNC    GLOBAL DEFAULT   10 get_tls1
    54: 000000000000726a    40 FUNC    GLOBAL DEFAULT   10 set_tls1
    55: 0000000000007292    40 FUNC    GLOBAL DEFAULT   10 set_tls2
    56: 0000000000007240    42 FUNC    GLOBAL DEFAULT   10 get_tls2

Comment thread linker-diff/src/version_diff.rs Outdated
Comment on lines +123 to +131
// TODO: this duplicates `symbol_diff.rs`
// On aarch64, GNU ld emits a dynamic symbol called "_stack", which it puts in some section
// or other that doesn't make sense. e.g. ".got.plt". It probably puts it in that section
// because it's closest to the value that it assigns to the symbol. It's not clear where
// this symbol comes from. It's neither in any input files, nor in GNU ld's built-in linker
// script.
if sym_name == b"_stack" {
continue;
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be decided what to do here before merging, leaving a comment.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to get GNU ld to emit "_start" in its linker script by running ld -m armelf --verbose on aarch64.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied this as is from symbol_diff.rs. Both symbol_diff.rs and this pass hit the same issues because they both iterate through .dynsym (pass from this PR also iterates through .gnu.version).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, gotcha. The comment does seem written in the way I would write it :) I guess it could be good to have the shared logic in a single method - probably wouldn't be less code, but would make it clear that they're have the same logic.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added DiffMode::IgnoreMissingValues, so we can get rid of the duplicated conditions.
Also, this allows removing the ignores for cases where only one of the linkers outputs a specific symbol. So this pass no longer duplicates missing symbols found by symbol_diff.rs.

Comment thread linker-diff/src/version_diff.rs Outdated
@mati865 mati865 marked this pull request as ready for review January 23, 2026 11:40
Not to confuse with symbols without versions, empty version is `foo@`.
It has special properties, making the linker ignore version script lines
 regarding this symbol.
 
 Before this PR, fuse2 would fail to build with "Symbol fuse_new has 
 undefined version".
Comment thread linker-diff/src/version_diff.rs Outdated
Comment on lines +123 to +131
// TODO: this duplicates `symbol_diff.rs`
// On aarch64, GNU ld emits a dynamic symbol called "_stack", which it puts in some section
// or other that doesn't make sense. e.g. ".got.plt". It probably puts it in that section
// because it's closest to the value that it assigns to the symbol. It's not clear where
// this symbol comes from. It's neither in any input files, nor in GNU ld's built-in linker
// script.
if sym_name == b"_stack" {
continue;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to get GNU ld to emit "_start" in its linker script by running ld -m armelf --verbose on aarch64.

// return Ok(None);
// }

// There is a quirk that I couldn't find docs for. When a symbol has an empty version

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you checked the behaviour of LLD? Does it do the same thing?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't mark unversioned symbol as hidden version, so we get this diff with LLD:

version.fuse_new
  wild FUSE_2.2 (hidden),FUSE_2.5 (hidden),FUSE_2.6,local or global (hidden)
  ref FUSE_2.2 (hidden),FUSE_2.5 (hidden),FUSE_2.6,local or global

FUSE_2.6 is non-hidden with all three linkers, local or global is hidden with GNU ld and Wild.

Other than that, it seems to be the same.

Comment thread libwild/src/symbol_db.rs Outdated
@marxin

marxin commented Jan 28, 2026

Copy link
Copy Markdown
Collaborator

@marxin any clue why dynsym is so much different between Wild and GNU ld only on RISC-V?

That might be related to the fact that there are many (compared to other architectures) debug info relocations pointing to symbols: https://github.com/davidlattimore/wild/blob/f757450b8780f70a3a27334432ac93256191b9f4/libwild/src/layout.rs#L4925
but it's strange as the extra symbols are undefined and from glibc library. So my answer is - I don't know :)

@mati865

mati865 commented Jan 28, 2026

Copy link
Copy Markdown
Member Author

Other linkers don't have them, that's what puzzling me. Thanks anyway, I'll split out as a new issue.

@davidlattimore davidlattimore left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this and sorry about the delay reviewing, I didn't realise you were waiting for me

@mati865

mati865 commented Feb 6, 2026

Copy link
Copy Markdown
Member Author

It's fine, if it was something important I'd have pinged you sooner.

@mati865 mati865 merged commit ba0806c into wild-linker:main Feb 6, 2026
54 of 58 checks passed
@mati865 mati865 deleted the push-skxmwsnxwkts branch February 6, 2026 12:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants