64ビットFreeBSDでCアプリケーションをコンパイルしようとすると、次のエラーが発生しました。
共有オブジェクトを作成する場合、再配置R_X86_64_32Sは使用できません。 -fPICで再コンパイルする
とは R_X86_64_32S
再配置とR_X86_64_64
?
私はエラーについてグーグルで調べましたが、考えられる原因です-R_X86_64_32Sが本当に何を意味するのかを誰かが伝えることができれば素晴らしいと思います。
R_X86_64_32S
およびR_X86_64_64
は、AMD64アーキテクチャ用にコンパイルされたコードの再配置タイプの名前です。 AMD64 ABI でそれらすべてを調べることができます。それによると、R_X86_64_64
は次のように分類されます。
およびR_X86_64_32S
to:
どちらの場合も、基本的には「この再配置が指すシンボルの値と加数」を意味します。にとって R_X86_64_32S
その後、リンカは、生成された値が元の64ビット値に符号拡張されることを検証します。
現在、実行可能ファイルでは、コードとデータセグメントに指定された仮想ベースアドレスが与えられています。実行可能コードは共有されず、各実行可能ファイルは独自の新しいアドレス空間を取得します。これは、コンパイラーがデータセクションの場所を正確に認識し、直接参照できることを意味します。一方、ライブラリは、そのデータセクションがベースアドレスから指定されたオフセットにあることのみを知ることができます。そのベースアドレスの値は、実行時にのみ知ることができます。したがって、すべてのライブラリは、位置独立コード(略してPIC)と呼ばれる、メモリ内のどこに配置されても実行できるコードを使用して作成する必要があります。
問題を解決することになると、エラーメッセージ自体が語っています。
これのいずれかが理にかなっているためには、最初に:
規格
R_X86_64_64
、R_X86_64_32
、およびR_X86_64_32S
はすべて、 System V AMD ABI で定義され、ELFファイル形式のAMD64仕様が含まれています。
これらはすべて、ELF形式のアーキテクチャに中立な部分を指定する System V ABI 4.1(1997) で指定された再配置エントリのELF32_R_TYPE
フィールドで可能な値です。その標準はフィールドのみを指定しますが、Arch依存値ではありません。
4.4.1「再配置タイプ」の下に、要約表が表示されます。
Name Field Calculation
------------ ------ -----------
R_X86_64_64 Word64 A + S
R_X86_64_32 Word32 A + S
R_X86_64_32S Word32 A + S
このテーブルについては後で説明します。
そして、メモ:
R_X86_64_32
およびR_X86_64_32S
再配置は、計算された値を32ビットに切り捨てます。リンカは、R_X86_64_32(R_X86_64_32S)の生成値が元の64ビット値にゼロ拡張(符号拡張)することを確認する必要があります。
R_X86_64_64およびR_X86_64_32の例
最初にR_X86_64_64
とR_X86_64_32
を見てみましょう:
.section .text
/* Both a and b contain the address of s. */
a: .long s
b: .quad s
s:
次に:
as --64 -o main.o main.S
objdump -dzr main.o
含まれるもの:
0000000000000000 <a>:
0: 00 00 add %al,(%rax)
0: R_X86_64_32 .text+0xc
2: 00 00 add %al,(%rax)
0000000000000004 <b>:
4: 00 00 add %al,(%rax)
4: R_X86_64_64 .text+0xc
6: 00 00 add %al,(%rax)
8: 00 00 add %al,(%rax)
a: 00 00 add %al,(%rax)
Ubuntu 14.04、Binutils 2.24でテスト済み。
今のところは逆アセンブリを無視し(これはデータであるため意味がありません)、ラベル、バイト、および再配置のみに注目してください。
最初の再配置:
0: R_X86_64_32 .text+0xc
つまり:
0
:バイト0で動作(ラベルa
)R_X86_64_
:AMD64システムV ABIのすべての再配置タイプで使用されるプレフィックス32
:.long
(4バイト)のみを指定したため、ラベルの64ビットアドレスs
は32ビットアドレスに切り捨てられます。.text
:.text
セクションにいます0xc
:これはaddendで、再配置エントリのフィールドです再配置のアドレスは次のように計算されます。
A + S
どこ:
A
:加数、ここ0xC
S
:再配置前のシンボルの値、ここ00 00 00 00 == 0
したがって、再配置後、新しいアドレスは.text
セクションの0xC == 12バイトになります。
s
は.long
(4バイト)と.quad
(8バイト)の後に来るため、これはまさに期待どおりです。
R_X86_64_64
は類似していますが、ここではs
のアドレスを切り捨てる必要がないため、より単純です。これは、Field
列のWord64
の代わりにWord32
を介して標準によって示されます。
R_X86_64_32S vs R_X86_64_32
R_X86_64_32S
とR_X86_64_32
の違いは、リンカが「再配置が収まるように切り捨てられた」と文句を言うときです。
32
:再配置後に切り捨てられた値が古い値をゼロ拡張しない場合、つまり切り捨てられたバイトがゼロでなければならない場合に文句を言います:
例:FF FF FF FF 80 00 00 00
が0ではないため、80 00 00 00
からFF FF FF FF
は苦情を生成します。
32S
:再配置後に切り捨てられた値が signextend 古い値でない場合に文句を言います。
例:FF FF FF FF 80 00 00 00
の最後のビットと切り捨てられたビットはすべて1であるため、80 00 00 00
から80 00 00 00
は問題ありません。
参照: このGCCエラー "...再配置は、収まるように切り詰められました..."の意味は?
R_X86_64_32S
は以下で生成できます:
.section .text
.global _start
_start:
mov s, %eax
s:
次に:
as --64 -o main.o main.S
objdump -dzr main.o
与える:
0000000000000000 <_start>:
0: 8b 04 25 00 00 00 00 mov 0x0,%eax
3: R_X86_64_32S .text+0x7
リンカースクリプトを使用して、32S
に収まるように切り詰められた「再配置」を確認できます。
SECTIONS
{
. = 0xFFFFFFFF80000000;
.text :
{
*(*)
}
}
今:
ld -Tlink.ld a.o
0xFFFFFFFF80000000
は符号拡張である80000000
に切り捨てられるため、問題ありません。
しかし、リンカスクリプトを次のように変更すると、
. = 0xFFFF0FFF80000000;
0
がもはや符号拡張ではなくなったため、エラーが生成されます。
メモリアクセスには32S
を使用し、イミディエイトには32
を使用する理由: R_X86_64_32Sのようなゼロ拡張の代わりにR_X86_64_32Sのような符号拡張再配置を使用する方がよいのはいつですか?
R_X86_64_32SおよびPIE(位置に依存しない実行可能ファイル
R_X86_64_32Sは、位置に依存しない実行可能ファイル、たとえばgcc -pie
で行われます。そうでない場合、リンクは次のように失敗します:
relocation R_X86_64_32S against `.text' can not be used when making a PIE object; recompile with -fPIC
l
私はそれを説明する最小限の例を提供しました: gccとldの位置に依存しない実行可能ファイルの-fPIEオプションとは
つまり、必要に応じて-fPIC
フラグを使用せずに共有オブジェクトをコンパイルしました。
gcc -shared foo.c -o libfoo.so # Wrong
電話する必要があります
gcc -shared -fPIC foo.c -o libfoo.so # Right
ELFプラットフォーム(Linux)では、共有オブジェクトは位置に依存しないコードでコンパイルされます-メモリ内の任意の場所から実行できるコード。このフラグが指定されていない場合、生成されるコードは位置に依存するため、この共有を使用することはできませんオブジェクト。
私はこの問題に出くわしましたが、この答えは役に立たないことがわかりました。静的ライブラリを共有ライブラリとリンクしようとしていました。また、コマンドラインで-fPICスイッチを先に配置することも調査しました(他の場所の回答でアドバイスされています)。私にとって問題を解決したのは、静的ライブラリを共有に変更することだけでした。 -fPICに関するエラーメッセージはさまざまな原因で発生する可能性がありますが、根本的に見たいのは、ライブラリの構築方法であり、さまざまな方法で構築されているライブラリを疑います。
私の場合、コンパイルするプログラムがリモートディレクトリで共有ライブラリを見つけることを期待していましたが、対応する静的ライブラリだけが間違っていたため、問題が発生しました。
実際、この再配置エラーは偽装されたファイルではないエラーでした。
この他のスレッドでどのように対処したかを詳しく説明しました https://stackoverflow.com/a/42388145/5459638