The output of ld.lld -v
includes a message "compatible
with GNU linkers" to address detection
mechanism used by GNU Libtool. This problem is described by Software
compatibility and our own "User-Agent" problem.
The latest m4/libtool.m4
continues to rely on a
GNU
check.
1 | [AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld, |
Check-based configuration can be a valuable tool, ensuring software remains functional in the future. However, this example highlights how overly specific checks can lead to unintended consequences.
If Libtool needs to check whether certain options are available, it
can utilize -v
.
1 | % ld.bfd -v --whole-archive |
This blog post explores more forms of the "User-Agent" problem exposed by an LLD patch changing the version message format.
LLD supports many object file formats. It largely emulates the
behavior of GNU ld for ELF, while emulating the behavior of MSVC
link.exe for PE/COFF. Previously, LLD's ELF port displays the version
information like this: 1
2% /tmp/out/custom2/bin/ld.lld --version
LLD 19.0.0 (compatible with GNU linkers)
A recent patch (llvm-project#97323)
changed it to one of the following formats, depending on the build-time
variable LLVM_APPEND_VC_REV
:
With LLVM_APPEND_VC_REV=on
: 1
2% /tmp/out/custom2/bin/ld.lld --version
LLD 19.0.0 (git@github.com:llvm/llvm-project.git 0f9fbbb63cfcd2069441aa2ebef622c9716f8dbb), compatible with GNU linkers
With LLVM_APPEND_VC_REV=off
: 1
2% /tmp/out/custom2/bin/ld.lld --version
LLD 19.0.0, compatible with GNU linkers
Meson
In Meson, mesonbuild/linkers/detect.py:guess_win_linker
checks the --version
output to determine whether the LLD
invocation is for ELF or PE/COFF. It performed an overly strict check
"(compatible with GNU linkers)", which failed when the parentheses were
stripped by #97323.
1 | # mesonbuild/linkers/detect.py |
The latest Meson has loosened the check (meson#13383).
It seems that the linker detection has a larger problem that
--target=
is not taken into account with Clang (#6662).
Linux kernel
The Linux kernel's scripts/ld-version.sh
script detects
linker versions. Introduced in 2014, it initially checked for GNU ld
compatibility with GCC LTO (though LTO support remains unmerged). It was
later revamped to handle LLD versions as well. While it can handle
suffixes like 2.34-4.fc32
, it struggles with versions
containing with comma suffix (19.0.0,
).
1 | % scripts/ld-version.sh /tmp/out/custom2/bin/ld.lld |
The script extracts the version string from the
--version
output and parses it as major.minor.patch.
1 | # Get the first line of the --version output. |
To support suffixes starting with either -
or
,
, the script will
employ a POSIX shell trick utilizing the "Remove Largest Suffix
Pattern" feature:
1 | version=${version%%[!0-9.]*} |
More fun with versions
llvm-nm and llvm-objcopy also claim GNU compatibility.
1 | % /tmp/Rel/bin/llvm-nm --version |
Ever wondered what the subtle differences are between
-v
, -V
, and --version
when using
GNU ld? Let's break it down:
--version
skips linker input processing and displays brief copyright information.-v
and-V
keep processing command line arguments and perfoming a linking step. This behavior gives an easy way to check whether an option is supported.-V
goes a step further than-v
by including a list of supported BFD emulations alongside the version information.
Prior to September 2022, -V
in ld.lld used to an alias
for --version
. This caused issues when using
gcc -v -fuse-ld=lld
on certain targets like
*-freebsd
and powerpc-*
: gcc passes -V to the
linker, expecting it to process the input files and complete the linking
step. However, ld.lld's behavior with -V
skipped this
process.
I made an adjustment by making
-V
an alias for -v
instead. This ensures
that gcc -v -fuse-ld=lld
performs the linking step.
GCC has a similar -v
and --version
behavior, but -V
does not exist.
Clang's GNU driver emulates GCC 4.2.1, but you can change the version
with -fgnuc-version=
.
1 | % clang -E -dM -xc /dev/null | grep GNU |