2017-04-12 7 views
4

64ビットLinuxはデフォルトで小さなメモリモデルを使用しています。これは、すべてのコードと静的データを2GBのアドレス制限以下にします。これにより、32ビットの絶対アドレスを使用できることが保証されます。古いバージョンのgccは、相対アドレス計算のための余分な命令を節約するために、静的配列に対して32ビットの絶対アドレスを使用します。しかし、これはもはや機能しません。アセンブリで32ビット絶対アドレスを作成しようとすると、リンカーエラーが発生します。 "共有オブジェクトを作成するときに` .data 'に対するR_X86_64_32Sの再配置は使用できません。-fPICで再コンパイルしてください。 私は共有オブジェクトを作成していないので、このエラーメッセージは誤解を招きます。-fPICは役に立ちません。 これまで私が知っているのはこれです:gccバージョン4.8.5では静的配列に32ビットの絶対アドレスを使用していますが、gccバージョン6.3.0ではそうではありません。バージョン5はおそらくどちらもしません。 binutils 2.24のリンカは32ビットの絶対アドレスを許可しますが、2.28のリンカはそうしません。x86-64 Linuxでは32ビットの絶対アドレスは許可されていませんか?

この変更の結果、古いライブラリを再コンパイルする必要があり、従来のアセンブリコードが壊れてしまいます。

今質問したい:この変更はいつ行われましたか?それはどこかに文書化されていますか? 32ビット絶対アドレスを受け入れるリンカオプションはありますか?

答えて

7

あなたのディストリビューションには--enable-default-pieというgccが設定されているため、デフォルトでは位置に依存しない実行可能ファイルが作成されます(実行可能ファイルとライブラリのASLRが可能です)。その移転はPIC/PIEでは認められていません。

gcc -fno-pie -no-pieを使用すると、以前の動作に戻すことができます。-no-pieはリンカーオプション、-fno-pie is the code-gen optionです。 -fno-pieだけでは、gccはmov eax, offset .LC0のようなコードを、まだ有効になっている-pieとリンクしません。


July 2017 patchは、gccでcompatのために、-nopieため-no-pieエイリアスを作っclang -fno-pie -nopieを、しかしclang4.0.1はそれを持っていません。打ち鳴らすはPIEは、あまりにも、デフォルトで有効になっていることができます。)のみ-no-pie

、(まだ-fpie)コンパイラによって生成されたコードは必要以上に遅くなり、まだASLRから利益を得ないであろう位置に依存する実行可能ファイルにリンクされます。このペナルティは、主に(静的なストレージクラスを持つファイルスコープの変数staticの代わりに)グローバルにアクセスするときに発生します。 -fPIEは、64ビットコードでは-fPICよりもはるかに悪くはないが、32ビットでは依然として悪いことに注意してください。 some examples on the Godbolt compiler explorerを参照し、The sorry state of dynamic libraries on Linuxを参照してください。

この方法でgccを設定した場合、gcc -v |& grep -o -e '[^ ]*pie'--enable-default-pieを出力します。この設定オプションのサポートがgccのearly 2015に追加されました。 Ubuntuは16.10でそれを有効にし、Debianはgcc 6.2.0-7(カーネルビルドエラー:https://lkml.org/lkml/2016/10/21/904)で同時刻に有効にしました。関連:Build compressed x86 kernels as PIE


ld自体はデフォルトを変更していないことに注意してください。それでも(少なくともbinutils 2.28のArch Linuxでは)正常に動作します。変更すると、または-no-pieを明示的に使用しない限り、gccはデフォルトで-pieをリンカーオプションとして渡します。

NASMソースファイルでは、絶対アドレスを取得するためにa32 mov eax, [abs buf]を使用しました。関連

nasm -felf64 -Worphan-labels -g -Fdwarf testloop.asm && 
ld -o testloop testloop.o    # works 

gcc -v -nostdlib testloop.o   # doesn't work 
... 
..../collect2 ... -pie ... 
/usr/bin/ld: testloop.o: relocation R_X86_64_32 against `.bss' can not be used when making a shared object; recompile with -fPIC 
/usr/bin/ld: final link failed: Nonrepresentable section on output 
collect2: error: ld returned 1 exit status 

gcc -v -no-pie -nostdlib testloop.o # works 
gcc -v -static -nostdlib testloop.o # also works: -static implies -no-pie 

:(67 a1 40 f1 60 00インテルCPU上LCPストール)がいるIt doesを6バイトの方法は、小さな絶対アドレス(アドレスサイズ+のMOV EAX、moffsをコードする場合、私はテストしていた。):building static/dynamic executables with/without libc, defining _start or mainを。


filereadelfパイは、 "共有オブジェクト" ではなく、実行可能であることを言います。

$ gcc -fno-pie -no-pie -O3 hello.c 
$ file a.out 
a.out: ELF 64-bit LSB executable, ... 

$ gcc -O3 hello.c 
$ file a.out 
a.out: ELF 64-bit LSB shared object, ... 

半関連:別の最近のgccの特徴は、gcc -fno-pltです。最後に共有ライブラリへの呼び出しは、call [rip + [email protected]](AT & T call *[email protected](%rip))となり、PLTトランポリンはありません。

ディストリビューションでは、書き込み可能な+実行可能メモリページが不要になるため、すぐに有効にすることを願っています。これは、多くの共有ライブラリ呼び出しを行うプログラムにとっては、大幅な高速化です。 x86-64 clang -O2 -g tramp3dをコンパイルすると、どのハードウェアでも41.6秒から36.8秒になります。the patch author tested on。 (共有ライブラリ呼び出しの最悪のシナリオかもしれません)

遅延バインディングの代わりに早期バインディングが必要なので、すぐに終了する大きなプログラムの方が処理が遅くなります。 (例えば、clang --versionまたはhello.cをコンパイルする)。この減速は、明らかにプリリンクで減少する可能性があります。

これはPICコードの外部変数のGOTオーバーヘッドを削除しません。 (上記のゴッドボルトのリンクを参照してください)。

+1

'-m32'では、PCを保持する貴重なレジスタを無駄にするので、これは本当に悪いことです。私の64ビットディストリビューションではフラグも有効になっているので、 '-m32 'もそれを使います。 –

関連する問題