「車輪の再実装」って言葉が好き(実践はできてない)

gccで本当に使われているデフォルトリンカスクリプトを手に入れる。

結論

ld --verboseではなく、gcc -Wl,--verboseを使いましょう。

環境

$ gcc --version
gcc (Ubuntu 8.2.0-7ubuntu1) 8.2.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ ld --version                 
GNU ld (GNU Binutils for Ubuntu) 2.31.1
Copyright (C) 2018 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.

$ ld --verbose

というようなコマンドを実行するとデフォルトリンカスクリプトが手に入る。というような記述をしばしば見かけます。(リンカ・ローダ実践開発テクニックとか)

このスクリプトを指定して生成した実行バイナリと、リンカスクリプトを指定しないで生成した実行バイナリは等しいものになると思いますよね?

という訳で、得られたリンカスクリプト*1 (default.ldsとします。)を用いて、以下を実行します。

$ gcc -o hoge hoge.c
$ gcc -o hoge_with_lds hoge.c -T default.lds
$ diff hoge hoge_with_lds 
Binary files hoge and hoge_with_lds differ 

実行結果が期待通りにならない...

恐らくld --verboseで得たリンカスクリプトが違うんだろう... *2

ではどうすれば実際に使われているスクリプトを得られるかというと、コンパイル時に-Wlオプションを使って、直接リンカに--verboseオプションを渡すとうまくいきます。

$ gcc  -Wl,--verbose

これで得られたスクリプトを用いて再度コンパイル&差分を確認すると、無事差分がなくなりました。

ちなみに実際のスクリプトの差を確認したら以下のようになりました。

$ diff default.lds default_gcc.lds
1c1
< /* Script for -z combreloc -z separate-code: combine and sort reloc sections with separate code segment */
---
> /* Script for -pie -z combreloc -z now -z relro -z separate-code: position independent executable, combine & sort relocs with separate code segment */
14c14
<   PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
---
>   PROVIDE (__executable_start = SEGMENT_START("text-segment", 0)); . = SEGMENT_START("text-segment", 0) + SIZEOF_HEADERS;
155,157c155,156
<   .got            : { *(.got) *(.igot) }
<   . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
<   .got.plt        : { *(.got.plt)  *(.igot.plt) }
---
>   .got            : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) }
>   . = DATA_SEGMENT_RELRO_END (0, .);
240a240
> 

どうやら オプションごとにデフォルトリンカスクリプトは異なるらしい。

gccコマンドを使用して入手した方は -pie -z combreloc ... 向けみたいですね。

そういえば、ある時から(確かUbuntuでは)位置独立実行形式(PIE)がデフォルトになったと聞いた気がするな...

*1:実際の ld --verbose の出力はメタ情報等を含むのでいい感じに整形してください

*2:gccは内部?で色々なコマンドを実行しているので、この情報だけでは ld --verbose で得たリンカスクリプトが間違っていることが原因とは断定できない気はします