Repository: davidlattimore/wild (rust)

Issue:
Shared objects should trigger archive entry activation
It looks like currently we're not activating archive entries in response to undefined symbols in shared objects that are provided by the archive entry. This was observed in https://github.com/davidlattimore/wild/issues/826#issuecomment-2999071548

Pull request description:
Allow undefined symbols in shared objects to trigger archive entries
Fixes #930

Note, the change in shared.c fixes a test failure that would otherwise occur due to GNU ld only triggering if the undefined symbol preceeds the definition.

Patch:
diff --git a/libwild/src/grouping.rs b/libwild/src/grouping.rs
index cc21ec1..c693b69 100644
--- a/libwild/src/grouping.rs
+++ b/libwild/src/grouping.rs
@@ -235,6 +235,14 @@ impl SequencedInput<'_> {
             ),
         }
     }
+
+    pub(crate) fn is_dynamic(&self) -> bool {
+        if let SequencedInput::Object(o) = self {
+            o.is_dynamic()
+        } else {
+            false
+        }
+    }
 }
 
 impl Display for GroupsDisplay<'_, '_> {
diff --git a/libwild/src/resolution.rs b/libwild/src/resolution.rs
index 6cad839..f9a612b 100644
--- a/libwild/src/resolution.rs
+++ b/libwild/src/resolution.rs
@@ -956,8 +956,14 @@ fn resolve_symbol<'data>(
             let symbol_file_id = resources.symbol_db.file_id_for_symbol(symbol_id);
 
             if symbol_file_id != obj.file_id && !local_symbol.is_weak() {
-                // Undefined symbols in shared objects don't trigger loading of other objects.
-                if !is_from_shared_object {
+                // Undefined symbols in shared objects should actually activate as-needed shared
+                // objects, however the rules for whether this should result in a DT_NEEDED entry
+                // are kind of subtle, so for now, we don't activate shared objects from shared
+                // objects. See
+                // https://github.com/davidlattimore/wild/issues/930#issuecomment-3007027924 for
+                // more details. TODO: Fix this.
+                if !is_from_shared_object || !resources.symbol_db.file(symbol_file_id).is_dynamic()
+                {
                     resources.request_file_id(symbol_file_id);
                 }
             } else if symbol_file_id != PRELUDE_FILE_ID {


Test patch:
diff --git a/wild/tests/integration_tests.rs b/wild/tests/integration_tests.rs
index 1e35d7f..659a889 100644
--- a/wild/tests/integration_tests.rs
+++ b/wild/tests/integration_tests.rs
@@ -2518,6 +2518,7 @@ fn integration_test(
         "mixed-verdef-verneed.c",
         "copy-relocations.c",
         "force-undefined.c",
+        "shlib-archive-activation.c",
         "linker-script.c",
         "linker-script-executable.c",
         "libc-ifunc.c",
diff --git a/wild/tests/sources/shared.c b/wild/tests/sources/shared.c
index ffcee16..148e1a2 100644
--- a/wild/tests/sources/shared.c
+++ b/wild/tests/sources/shared.c
@@ -10,8 +10,8 @@
 //#Mode:dynamic
 // TODO: https://rust-lang.zulipchat.com/#narrow/channel/421156-gsoc/topic/Project.3A.20Improve.20Wild.20linker.20test.20suites/near/521482968
 //#Cross:false
-//#Archive:shared-a1.c,shared-a2.c
 //#Shared:shared-s1.c
+//#Archive:shared-a1.c,shared-a2.c
 //#DiffIgnore:.dynamic.DT_RELA
 //#DiffIgnore:.dynamic.DT_RELAENT
 //#DiffIgnore:.dynamic.DT_NEEDED
diff --git a/wild/tests/sources/shlib-archive-activation-1.c b/wild/tests/sources/shlib-archive-activation-1.c
new file mode 100644
index 0000000..9068c99
--- /dev/null
+++ b/wild/tests/sources/shlib-archive-activation-1.c
@@ -0,0 +1,14 @@
+int f1a(void) {
+    return 50;
+}
+
+int f1(void) {
+    return f1a();
+}
+
+int f2b(void);
+
+int f2(void) {
+    // This reference causes the archive entry shlib-archive-activation-2 to be loaded.
+    return f2b();
+}
diff --git a/wild/tests/sources/shlib-archive-activation-2.c b/wild/tests/sources/shlib-archive-activation-2.c
new file mode 100644
index 0000000..c77eb22
--- /dev/null
+++ b/wild/tests/sources/shlib-archive-activation-2.c
@@ -0,0 +1,7 @@
+int f2b(void) {
+    return 0;
+}
+
+int f1a(void) {
+    return 10;
+}
diff --git a/wild/tests/sources/shlib-archive-activation.c b/wild/tests/sources/shlib-archive-activation.c
new file mode 100644
index 0000000..cd89422
--- /dev/null
+++ b/wild/tests/sources/shlib-archive-activation.c
@@ -0,0 +1,29 @@
+// Tests how undefined symbols in shared objects activate archive entries.
+
+//#AbstractConfig:default
+//#CompArgs:-fPIC
+//#Object:runtime.c
+//#Mode:dynamic
+//#DiffIgnore:.dynamic.DT_RELA
+//#DiffIgnore:.dynamic.DT_RELAENT
+//#DiffIgnore:.dynamic.DT_NEEDED
+
+//#Config:archive:default
+//#Shared:shlib-archive-activation-1.c
+//#Archive:shlib-archive-activation-2.c
+
+#include "runtime.h"
+
+int f1(void);
+
+void _start(void) {
+    runtime_init();
+
+    // The second file is an archive. It will take priority over the shared object even though the
+    // shared object is earlier.
+    if (f1() != 10) {
+        exit_syscall(101);
+    }
+
+    exit_syscall(42);
+}