Repository: rui314/mold (2026-01-21T11:43:02Z)

Issue:
[gcc-16 regression] Linker scripts starting with `INPUT(AS_NEEDED(...))` do not work
Hi,

gcc-trunk (`g++-16 (GCC) 16.0.1 20260115 (experimental)`) currently does not work with mold:

```bash
# test.cpp = `int main() {}`
g++-16 -fuse-ld=mold test.cpp     
mold: fatal: /usr/local/stow/gcc-16/lib64/libatomic_asneeded.so:3: INPUT ( AS_NEEDED( -latomic ) )
                                                                           ^ library not found: AS_NEEDED
collect2: error: ld returned 1 exit status
```

### Background

GCC added some linker scripts (e.g., `INPUT ( AS_NEEDED ( -latomic ) )`):
* [Commit](https://github.com/gcc-mirror/gcc/commit/8a99fdb70493df1294b53406913e5ea1fc971c13#diff-92f95eff36a05cd10dd65568b4898d0edbe3963cc5f9d3367b00bcb338a66dcd)
* [Ticket](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=123396)

(Edit) The bug was also independently filed in [GCC's bug tracker](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=123654) and now links to this issue. 

### Stacktrace

```txt
mold::Script<mold::X86_64>::resolve_path(mold::Script<mold::X86_64> * this, mold::Script<mold::X86_64> * this@entry, std::__1::string_view tok, bool check_target) (\mold\src\linker-script.cc:185)
mold::Script<mold::X86_64>::get_script_output_type(mold::Script<mold::X86_64> * this) (\mold\src\linker-script.cc:253)
mold::detect_machine_type<mold::X86_64>(mold::Context<mold::X86_64> & ctx) (\mold\src\main.cc:153)
mold::mold_main<mold::X86_64>(int argc, char ** argv) (\mold\src\main.cc:295)
libc.so.6![Unknown/Just-In-Time compiled code] (Unknown Source:0)
libc.so.6!__libc_start_main (Unknown Source:0)
_start (Unknown Source:0)
```

### Misc

The error doesn't happen if:
* `-m` is set, or
* first entry in `INPUT` is a a path to a lib or uses `-l`


https://github.com/rui314/mold/blob/17956fdfa1ea18171587d02a7439e7dab6732ea1/src/main.cc#L294-L295
https://github.com/rui314/mold/blob/17956fdfa1ea18171587d02a7439e7dab6732ea1/src/main.cc#L152-L153
https://github.com/rui314/mold/blob/17956fdfa1ea18171587d02a7439e7dab6732ea1/src/linker-script.cc#L251-L253

Trying to treat `AS_NEEDED` as a path then results in the error.
The code probably needs to handle groups, similar to what [read_group](https://github.com/rui314/mold/blob/17956fdfa1ea18171587d02a7439e7dab6732ea1/src/linker-script.cc#L190) does.




Discussion:
seems that linker scripts found via `-l` and specified directly are treated differently:

```
/tmp$ echo 'INPUT ( AS_NEEDED ( -latomic ) )' > libfoo.so
/tmp$ ld.mold -o a -L. -lfoo a.o
mold: fatal: ./libfoo.so:1: INPUT ( AS_NEEDED ( -latomic ) )
                                    ^ library not found: AS_NEEDED
/tmp 1 $ ld.mold -o a ./libfoo.so a.o
mold: fatal: library not found: atomic
```

... this would cause the error we're seeing with GCC
> The code probably needs to handle groups, similar to what [read_group](https://github.com/rui314/mold/blob/17956fdfa1ea18171587d02a7439e7dab6732ea1/src/linker-script.cc#L190) does.

N.B. `GROUP( AS_NEEDED( -lx ))` also doesn't work:

```
mold: fatal: /tmp/libscript.a:2: GROUP ( AS_NEEDED ( -latomic ) )
                                         ^ library not found: AS_NEEDED
```

As Arsen said, the problem seems to be that a linker script found via `-l` is handled differently. Maybe it isn't using the `read_group` code at all.
Confirmed, `mold::read_input_files<mold::X86_64>` is never called for `-lfoo` arguments, so if it's a linker script, it won't use `read_group`.

Maybe the problem is here:
https://github.com/rui314/mold/blob/17956fdfa1ea18171587d02a7439e7dab6732ea1/src/linker-script.cc#L251-L254

Should this call `read_group`?

This kluge solves the immediate problem for GCC:

```diff
--- a/src/linker-script.cc
+++ b/src/linker-script.cc
@@ -249,9 +249,12 @@ std::string_view Script<E>::get_script_output_type() {
   }
 
   if (tok.size() >= 3 && (tok[0] == "INPUT" || tok[0] == "GROUP") &&
-      tok[1] == "(")
+      tok[1] == "(") {
+    if (tok[2] == "AS_NEEDED" && tok[3] == "(")
+      tok = tok.subspan(2);
     if (MappedFile *mf = resolve_path(tok[2], false))
       return get_machine_type(ctx, rctx, mf);
+  }
   return "";
 }
 
```

This code will still fail for anything more complex than `AS_NEEDED( -lx )` though.



Patch:
diff --git a/src/linker-script.cc b/src/linker-script.cc
index 51144b3029..6848b0e8b3 100644
--- a/src/linker-script.cc
+++ b/src/linker-script.cc
@@ -249,9 +249,12 @@ std::string_view Script<E>::get_script_output_type() {
   }
 
   if (tok.size() >= 3 && (tok[0] == "INPUT" || tok[0] == "GROUP") &&
-      tok[1] == "(")
+      tok[1] == "(") {
+    if (tok.size() >= 5 && tok[2] == "AS_NEEDED" && tok[3] == "(")
+      tok = tok.subspan(2);
     if (MappedFile *mf = resolve_path(tok[2], false))
       return get_machine_type(ctx, rctx, mf);
+  }
   return "";
 }
 


Test patch:
diff --git a/test/linker-script-group-as-needed.sh b/test/linker-script-group-as-needed.sh
new file mode 100755
index 0000000000..61dcba76be
--- /dev/null
+++ b/test/linker-script-group-as-needed.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+. $(dirname $0)/common.inc
+
+cat <<EOF | $CC -o $t/a.o -c -xc -
+#include <stdio.h>
+int main() {
+  printf("Hello world\n");
+}
+EOF
+
+cat <<EOF | $CC -B. -shared -o $t/libB.so -c -xc -
+void not_needed() { }
+EOF
+$AR
+
+cat <<EOF > $t/libscript.a
+GROUP(AS_NEEDED("$t/libB.so"))
+EOF
+
+$CC -B. -o $t/exe -L$t -lscript $t/a.o
+$QEMU $t/exe | grep 'Hello world'
+
+$CC -B. -o $t/exe -L$t -l:libscript.a $t/a.o
+$QEMU $t/exe | grep 'Hello world'