🚲

lldのlinker scriptについて

2021/04/25に公開

はじめに

clangを使っていて
link時の動きについて少し(ほんとに少し)解ったことがあったので
記載をします。

使用したclangのバージョンは以下

$ clang --version
clang version 12.0.0 (https://github.com/llvm/llvm-project/ b978a93635b584db380274d7c8963c73989944a1)
Target: x86_64-unknown-linux-gnu
Thread model: posix

clang hello.cと打ったときに動くリンカは?

最初はリンカはlldが動くものと思っていました。
-vオプションを付けて、詳細を見てみると
/usr/bin/ldが呼ばれていました。

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

int main(void) {
    printf("Hello world\n");
    return 0;
}

$ clang -v hello.c 
clang version 12.0.0 (https://github.com/llvm/llvm-project/ b978a93635b584db380274d7c8963c73989944a1)
Target: x86_64-unknown-linux-gnu
Thread model: posix
<省略>
 "/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 /tmp/hello-ef37fe.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
 
$ /usr/bin/ld --version
GNU ld (GNU Binutils for Ubuntu) 2.34
Copyright (C) 2020 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.

linker scriptについて

試しに以下のようなコード、linker scriptを用意します。

sample.c
void __init_array_start(void){};
void __init_array_end(void){};
int main()
{
	return 0;
}
sample.ld
SECTIONS
{
    sample = 0;
    sample += 1;
    sample -= 1;
    . = 0x08048000 + SIZEOF_HEADERS;
    .text : {*(.text)}
    .rodata : {*(.rodata)}
    .data : {*(.data)}
    .bss : {*(.bss)}
}

これをx86用と、arm用でコンパイル、リンクをしてみます。
まずはx86。特にエラーは出ません。

$ clang -fPIE -target x86_64-unknown-none-elf -nodefaultlibs sample.c -T sample.ld
$

次にarm用でコンパイル、リンク。
以下の用にリンク時にエラーとなります。

$ clang  -target arm-none-eabi -march=armv7-m -mcpu=cortex-m4 -nodefaultlibs sample.c -T sample.ld
ld.lld: error: sample.ld:5: malformed number: =
>>>     sample -= 1;
>>>             ^
clang-12: error: ld.lld command failed with exit code 1 (use -v to see invocation)

これは、-vオプションをつけてみるとわかるのですが、
armをターゲットにリンクをしたときには
呼ばれるリンカがld.lldとなっています。

$ clang -v  -target arm-none-eabi -march=armv7-m -mcpu=cortex-m4 -nodefaultlibs sample.c -T sample.ld
clang version 12.0.0 (https://github.com/llvm/llvm-project/ b978a93635b584db380274d7c8963c73989944a1)
Target: arm-none-unknown-eabi
Thread model: posix
<省略>
 "<省略>/bin/ld.lld" /tmp/sample-cf86eb.o -Bstatic -L/home/saitoyutaka/clang/clang+llvm-12.0.0-x86_64-linux-gnu-ubuntu-20.04/lib/clang/12.0.0/lib/baremetal -L/home/saitoyutaka/clang/clang+llvm-12.0.0-x86_64-linux-gnu-ubuntu-20.04/bin/../lib/clang-runtimes/arm-none-eabi/lib -T sample.ld -o a.out
ld.lld: error: sample.ld:5: malformed number: =
>>>     sample -= 1;
>>>             ^
clang-12: error: ld.lld command failed with exit code 1 (use -v to see invocation)

どうやら、ld.lldではスクリプト内で-=が使えないようです。

聞いてみる

メールで問い合わせてみました。
ただ、このときはx86, armで呼ばれるリンカが異なることを知らなかったので
(-vオプションで確認していなかった。。。)
指定するターゲットによってスクリプトの動き変わる?みたいな
聞き方をしています。。。

https://lists.llvm.org/pipermail/llvm-dev/2021-April/150170.html

返ってきた返事はこちら。

https://lists.llvm.org/pipermail/llvm-dev/2021-April/150172.html

-= is not supported. ld.lld only supports +=.
-= is probably too rare in the wild.

Actually changing the value of a symbol is hardly a good idea.
The evaluation order of expressions is not well defined.
Using -= or += may cause difficult-to-explain portability issues across
GNU ld and ld.lld.

The evaluation order of expressions is not well defined.というところが
へーと思いました。

まとめ

clangコマンドで呼ばれるリンカはターゲットによって異なるみたい。
ただ、この書き方は正確じゃない気がしています。
確認した時のオプションの指定のしかたが適切ではないのも原因と思っています。
詳細は調べていません。

ldとlldでリンカスクリプトの動きが異なる。
今回は-=の違いがわかりましたが、他にもあるのですかね。

おまけ

で、なんでこのようなことを調べてみたかというと、
spresense用のnuttxをclangでビルドしてみたところ
ここでエラーとなったためです。

Discussion