🐮

clangでオプションでリンカを指定する

2021/04/29に公開

はじめに

この前以下の記事を書きました。
https://zenn.dev/saitoyutaka/articles/abfa04ba83ab9e
この中で、clangを実行すると
デフォルト(?)でGNU ldが呼ばれると書きました。

引数でリンカを指定する方法がないかを調べてみました。

しらべる

とりあえず、clang --help | grep -E "ld|linkerで見てみる。

$ clang --help | grep -E "ld|linker"
  --emit-static-lib       Enable linker job to emit a static library.
  -faapcs-bitfield-load   Follows the AAPCS standard that all volatile bit-field write generates at least one load. (ARM only).
  -faapcs-bitfield-width  Follow the AAPCS standard requirement stating that volatile bit-field width is dictated by the field container type. (ARM only).
  -fallow-editor-placeholders
                          Treat editor placeholders as valid source code
  -fbuild-session-file=<file>
                          Use the last modification time of <file> as the build session timestamp
  -fbuild-session-timestamp=<time since Epoch in seconds>
                          Time when the current build session started
                          Require member pointer base types to be complete if they would be significant under the Microsoft ABI
  -fdiagnostics-hotness-threshold=<value>
                          Prevent optimization remarks from being output if they do not have at least this profile count. Use 'auto' to apply the threshold from profile summary
  -fembed-bitcode-marker  Embed placeholder LLVM IR data as a marker
  -ffine-grained-bitfield-accesses
                          Use separate accesses for consecutive bitfield runs with legal widths and alignments.
  -fmodule-name=<name>    Specify the name of the module to build
                          Ignore the definition of the given macro when building and loading modules
  -fmodules-user-build-path <directory>
                          Specify the module user build path
  -fmodules-validate-once-per-build-session
                          Don't verify input files for the modules if the module has been successfully validated or loaded during this build session
  -fno-aapcs-bitfield-width
                          Do not follow the AAPCS standard requirement stating that volatile bit-field width is dictated by the field container type. (ARM only).
  -fno-allow-editor-placeholders
  -fno-autolink           Disable generation of linker directives for automatic library linking
                          Do not require member pointer base types to be complete if they would be significant under the Microsoft ABI
  -fno-fine-grained-bitfield-accesses
                          Use large-integer access for consecutive bitfield runs.
  -fno-rtlib-add-rpath    Do not add -rpath with architecture-specific resource directory to the linker flags
  -fno-temp-file          Directly create compilation output files. This may lead to incorrect incremental builds if the compiler crashes
                          Instantiate templates already while building a PCH
  -frtlib-add-rpath       Add -rpath with architecture-specific resource directory to the linker flags
  -fsanitize-address-field-padding=<value>
                          Level of field padding for AddressSanitizer
                          Enable linker dead stripping of globals in AddressSanitizer
  -fsystem-module         Build this module as a system module. Only used with -emit-module
                          Compute and store the hash of input files used to build an AST. Files with mismatching mtime's are considered valid if both contents is identical
  -fxray-instruction-threshold= <value>
  -membedded-data         Place constants in the .rodata section instead of the .sdata section even if they meet the -G <size> threshold (MIPS)
  -mextern-sdata          Assume that externally defined data is in the small data if it meets the -G <size> threshold (MIPS)
  -mincremental-linker-compatible
                          (integrated-as) Emit an object file which can be used with an incremental linker
  -mms-bitfields          Set the default structure layout to be compatible with the Microsoft compiler standard
  -mno-embedded-data      Do not place constants in the .rodata section instead of the .sdata if they meet the -G <size> threshold (MIPS)
  -mno-extern-sdata       Do not assume that externally defined data is in the small data if it meets the -G <size> threshold (MIPS)
  -mno-incremental-linker-compatible
                          (integrated-as) Emit an object file which cannot be used with an incremental linker
  -mno-ms-bitfields       Do not set the default structure layout to be compatible with the Microsoft compiler standard
  -mno-relax              Disable linker relaxation
  -mrelax                 Enable linker relaxation
  -relocatable-pch        Whether to build a relocatable precompiled header
  -T <script>             Specify <script> as linker script
  -Wl,<arg>               Pass the comma separated arguments in <arg> to the linker
  -Xlinker <arg>          Pass <arg> to the linker
  -z <arg>                Pass -z <arg> to the linker

それっぽいものは見つからず。
念の為(?) -Xlinkerオプションを付けて実行してみる。
最近知ったのですが、-###をつけると、dry runとなります。
(gccでも同じオプションがあるみたい)

$ clang -### -Xlinker lld hello.c
clang version 12.0.0 (https://github.com/llvm/llvm-project/ b978a93635b584db380274d7c8963c73989944a1)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir:
<省略>
 "/usr/bin/ld" "-z" "relro" "--hash-style=gnu" "--eh-frame-hdr" "-m" "elf_x86_64" "-dynamic-linker" "/lib64/ld-linux-x86-64.so.2" "-o" "a.out" "/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crt1.o" "/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crti.o" "/usr/lib/gcc/x86_64-linux-gnu/9/crtbegin.o" "-L/usr/lib/gcc/x86_64-linux-gnu/9" "-L/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu" "-L/usr/lib/gcc/x86_64-linux-gnu/9/../../../../lib64" "-L/lib/x86_64-linux-gnu" "-L/lib/../lib64" "-L/usr/lib/x86_64-linux-gnu" "-L/usr/lib/../lib64" "-L/usr/lib/x86_64-linux-gnu/../../lib64" "-L/usr/lib/gcc/x86_64-linux-gnu/9/../../.." "-L/home/saitoyutaka/clang/clang+llvm-12.0.0-x86_64-linux-gnu-ubuntu-20.04/bin/../lib" "-L/lib" "-L/usr/lib" "lld" "/tmp/hello-ec9dab.o" "-lgcc" "--as-needed" "-lgcc_s" "--no-as-needed" "-lc" "-lgcc" "--as-needed" "-lgcc_s" "--no-as-needed" "/usr/lib/gcc/x86_64-linux-gnu/9/crtend.o" "/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crtn.o"

GNU ldが呼ばれていました。

しらべてみる(コードを)

カンでldとかでgrepしたら何かわかるかと思いやってみたら
以下の処理が引っかかりました。

https://github.com/llvm/llvm-project/blob/main/clang/lib/Driver/ToolChain.cpp#L540

コメントを見ると、

ToolChain.cpp
  // Get -fuse-ld= first to prevent -Wunused-command-line-argument. -fuse-ld= is
  // considered as the linker flavor, e.g. "bfd", "gold", or "lld".
  const Arg* A = Args.getLastArg(options::OPT_fuse_ld_EQ);
  StringRef UseLinker = A ? A->getValue() : CLANG_DEFAULT_LINKER;

-fuse-ldというオプションがあるっぽい。
(clang --helpでは出なかったけど)
オンラインドキュメントには記載があった。
https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fuse-ld

"-fuse-ld=lld"をつけてみる

-fuse-ld=lldをつけてみた実行結果は以下。

$ cat hello.c 
#include <stdio.h>

int main(void) {
    printf("Hello world\n");
    return 0;
}
$ clang -### -fuse-ld=lld hello.c
clang version 12.0.0 (https://github.com/llvm/llvm-project/ b978a93635b584db380274d7c8963c73989944a1)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: 
<省略>
 "<省略>bin/ld.lld" "-z" "relro" "--hash-style=gnu" "--eh-frame-hdr" "-m" "elf_x86_64" "-dynamic-linker" "/lib64/ld-linux-x86-64.so.2" "-o" "a.out" "/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crt1.o" "/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crti.o" "/usr/lib/gcc/x86_64-linux-gnu/9/crtbegin.o" "-L/usr/lib/gcc/x86_64-linux-gnu/9" "-L/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu" "-L/usr/lib/gcc/x86_64-linux-gnu/9/../../../../lib64" "-L/lib/x86_64-linux-gnu" "-L/lib/../lib64" "-L/usr/lib/x86_64-linux-gnu" "-L/usr/lib/../lib64" "-L/usr/lib/x86_64-linux-gnu/../../lib64" "-L/usr/lib/gcc/x86_64-linux-gnu/9/../../.." "-L/home/saitoyutaka/clang/clang+llvm-12.0.0-x86_64-linux-gnu-ubuntu-20.04/bin/../lib" "-L/lib" "-L/usr/lib" "/tmp/hello-4520a6.o" "-lgcc" "--as-needed" "-lgcc_s" "--no-as-needed" "-lc" "-lgcc" "--as-needed" "-lgcc_s" "--no-as-needed" "/usr/lib/gcc/x86_64-linux-gnu/9/crtend.o" "/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crtn.o"

ld.lldが呼ばれるようになった。

-###を外してみる。ちゃんと実行もできた。

$ clang -fuse-ld=lld hello.c
$ ./a.out 
Hello world
$

補足gccのオンラインドキュメント

gccのオンラインドキュメントには以下のように記載がされている。

https://gcc.gnu.org/onlinedocs/gcc-9.3.0/gcc/Link-Options.html#index-fuse-ld_003dlld

-fuse-ld=bfd
Use the bfd linker instead of the default linker.

-fuse-ld=gold
Use the gold linker instead of the default linker.

-fuse-ld=lld
Use the LLVM lld linker instead of the default linker.

まとめ

clangコマンドの引数でリンカの指定をするには
-fuse-ldオプションを使う。

(たぶん)gccのドキュメントもヒントになる。

Discussion