Skip to content

PoC weak symbol handling#1825

Draft
mati865 wants to merge 3 commits intowild-linker:mainfrom
mati865:push-noksvtlkzzxk
Draft

PoC weak symbol handling#1825
mati865 wants to merge 3 commits intowild-linker:mainfrom
mati865:push-noksvtlkzzxk

Conversation

@mati865
Copy link
Copy Markdown
Member

@mati865 mati865 commented Apr 8, 2026

Proof of concept for #1817 (comment)

@davidlattimore
Copy link
Copy Markdown
Member

I tried the following test with this change:

diff --git a/wild/tests/sources/elf/weak-ref-strong-def/weak-ref-strong-def-1.c b/wild/tests/sources/elf/weak-ref-strong-def/weak-ref-strong-def-1.c
new file mode 100644
index 0000000000..6cf1236844
--- /dev/null
+++ b/wild/tests/sources/elf/weak-ref-strong-def/weak-ref-strong-def-1.c
@@ -0,0 +1,7 @@
+int foo(void) {
+    return 42;
+}
+
+int bar(void) {
+    return 42;
+}
diff --git a/wild/tests/sources/elf/weak-ref-strong-def/weak-ref-strong-def-2.c b/wild/tests/sources/elf/weak-ref-strong-def/weak-ref-strong-def-2.c
new file mode 100644
index 0000000000..8737aa7886
--- /dev/null
+++ b/wild/tests/sources/elf/weak-ref-strong-def/weak-ref-strong-def-2.c
@@ -0,0 +1,5 @@
+int bar(void);
+
+int call_bar(void) {
+    return bar();
+}
diff --git a/wild/tests/sources/elf/weak-ref-strong-def/weak-ref-strong-def.c b/wild/tests/sources/elf/weak-ref-strong-def/weak-ref-strong-def.c
new file mode 100644
index 0000000000..3a1d90ba6e
--- /dev/null
+++ b/wild/tests/sources/elf/weak-ref-strong-def/weak-ref-strong-def.c
@@ -0,0 +1,31 @@
+//#Config:shared
+//#Mode:dynamic
+//#Object:runtime.c
+//#Shared:weak-ref-strong-def-1.c
+//#Object:weak-ref-strong-def-2.c
+//#ExpectDynSym:foo binding=weak
+//#ExpectDynSym:bar binding=global
+//#DiffIgnore:section.got
+//#DiffIgnore:.dynamic.DT_NEEDED
+
+#include "../common/runtime.h"
+
+// This weak reference is the only reference to foo.
+int __attribute__((weak)) foo(void);
+
+// For bar, there's also a non-weak reference in a different object file.
+int __attribute__((weak)) bar(void);
+
+void _start(void) {
+  runtime_init();
+
+  if (!foo || foo() != 42) {
+    exit_syscall(10);
+  }
+
+  if (!bar || bar() != 42) {
+    exit_syscall(11);
+  }
+
+  exit_syscall(42);
+}

Linker-diff showed diffs for bar being weak in wild's output but non-weak in GNU ld's output.

@mati865
Copy link
Copy Markdown
Member Author

mati865 commented Apr 9, 2026

There is also a race condition there. Once in a few runs this passes:

❯ nt elf/x86_64/weak-ref-strong-def/shared
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.05s
────────────
 Nextest run ID 364bcfff-45a1-4d5d-b132-36470e95a3ac with nextest profile: default
    Starting 1 test across 9 binaries (889 tests skipped)
        PASS [   0.010s] wild-linker::integration_tests elf/x86_64/weak-ref-strong-def/shared
────────────
     Summary [   0.011s] 1 test run: 1 passed, 889 skipped

❯ readelf -WDs /home/mateusz/Projects/wild/wild/tests/build/elf/x86_64/weak-ref-strong-def/shared/weak-ref-strong-def.c.wild

Symbol table for image contains 3 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND bar
     2: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND foo

While failing case produces:

❯ readelf -WDs /home/mateusz/Projects/wild/wild/tests/build/elf/x86_64/weak-ref-strong-def/shared/weak-ref-strong-def.c.wild

Symbol table for image contains 3 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND bar
     2: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND foo

Also, sym-info:

Details

Weak bar (failing test)

lobal name `bar` refers to: Some(sym-38)
Definitions / references with name `bar`:
  31 -> 38: Weak Undefined: ABSOLUTE | WEAK
    bar
    #8 in File #256 (1/0) /home/mateusz/Projects/wild/wild/tests/build/elf/x86_64/weak-ref-strong-def/shared/weak-ref-strong-def.c.o (LOADED)
  38: Global Func: DYNAMIC | FUNCTION | GOT | PLT | WEAK
    bar
    #1 in File #768 (3/0) /home/mateusz/Projects/wild/wild/tests/build/elf/x86_64/weak-ref-strong-def/shared/weak-ref-strong-def-1.c.wild.so (LOADED)
  44 -> 38: Global Undefined: ABSOLUTE
    bar
    #4 in File #1024 (4/0) /home/mateusz/Projects/wild/wild/tests/build/elf/x86_64/weak-ref-strong-def/shared/weak-ref-strong-def-2.c.o (LOADED)

Strong bar (passing test):

Global name `bar` refers to: Some(sym-38)
Definitions / references with name `bar`:
  31 -> 38: Weak Undefined: ABSOLUTE | WEAK
    bar
    #8 in File #256 (1/0) /home/mateusz/Projects/wild/wild/tests/build/elf/x86_64/weak-ref-strong-def/shared/weak-ref-strong-def.c.o (LOADED)
  38: Global Func: DYNAMIC | FUNCTION | GOT | PLT
    bar
    #1 in File #768 (3/0) /home/mateusz/Projects/wild/wild/tests/build/elf/x86_64/weak-ref-strong-def/shared/weak-ref-strong-def-1.c.wild.so (LOADED)
  44 -> 38: Global Undefined: ABSOLUTE
    bar
    #4 in File #1024 (4/0) /home/mateusz/Projects/wild/wild/tests/build/elf/x86_64/weak-ref-strong-def/shared/weak-ref-strong-def-2.c.o (LOADED)

I guess once in a while we process 31 before 44, so 31 adds WEAK and 44 removes it. I had posted this PoC because I was wondering if this is somewhat close to being a solution. Thank you for showing me how well it falls apart 😆.

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.

2 participants