web-dev-qa-db-ja.com

R_X86_64_32SとR_X86_64_64の再配置はどういう意味ですか?

64ビットFreeBSDでCアプリケーションをコンパイルしようとすると、次のエラーが発生しました。

共有オブジェクトを作成する場合、再配置R_X86_64_32Sは使用できません。 -fPICで再コンパイルする

とは R_X86_64_32S再配置とR_X86_64_64

私はエラーについてグーグルで調べましたが、考えられる原因です-R_X86_64_32Sが本当に何を意味するのかを誰かが伝えることができれば素晴らしいと思います。

46
Raj

R_X86_64_32SおよびR_X86_64_64は、AMD64アーキテクチャ用にコンパイルされたコードの再配置タイプの名前です。 AMD64 ABI でそれらすべてを調べることができます。それによると、R_X86_64_64は次のように分類されます。

  • R_X86_64-すべての名前の先頭にはこれが付きます
  • 64-直接64ビット再配置

およびR_X86_64_32S to:

  • R_X86_64-プレフィックス
  • 32S-値を32ビットに切り捨て、符号拡張します

どちらの場合も、基本的には「この再配置が指すシンボルの値と加数」を意味します。にとって R_X86_64_32Sその後、リンカは、生成された値が元の64ビット値に符号拡張されることを検証します。

現在、実行可能ファイルでは、コードとデータセグメントに指定された仮想ベースアドレスが与えられています。実行可能コードは共有されず、各実行可能ファイルは独自の新しいアドレス空間を取得します。これは、コンパイラーがデータセクションの場所を正確に認識し、直接参照できることを意味します。一方、ライブラリは、そのデータセクションがベースアドレスから指定されたオフセットにあることのみを知ることができます。そのベースアドレスの値は、実行時にのみ知ることができます。したがって、すべてのライブラリは、位置独立コード(略してPIC)と呼ばれる、メモリ内のどこに配置されても実行できるコードを使用して作成する必要があります。

問題を解決することになると、エラーメッセージ自体が語っています。

34

これのいずれかが理にかなっているためには、最初に:

規格

R_X86_64_64R_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_64R_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_32SR_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)では、共有オブジェクトは位置に依存しないコードでコンパイルされます-メモリ内の任意の場所から実行できるコード。このフラグが指定されていない場合、生成されるコードは位置に依存するため、この共有を使用することはできませんオブジェクト。

2
Artyom

私はこの問題に出くわしましたが、この答えは役に立たないことがわかりました。静的ライブラリを共有ライブラリとリンクしようとしていました。また、コマンドラインで-fPICスイッチを先に配置することも調査しました(他の場所の回答でアドバイスされています)。私にとって問題を解決したのは、静的ライブラリを共有に変更することだけでした。 -fPICに関するエラーメッセージはさまざまな原因で発生する可能性がありますが、根本的に見たいのは、ライブラリの構築方法であり、さまざまな方法で構築されているライブラリを疑います。

2
jonawebb

私の場合、コンパイルするプログラムがリモートディレクトリで共有ライブラリを見つけることを期待していましたが、対応する静的ライブラリだけが間違っていたため、問題が発生しました。

実際、この再配置エラーは偽装されたファイルではないエラーでした。

この他のスレッドでどのように対処したかを詳しく説明しました https://stackoverflow.com/a/42388145/5459638

1
XavierStuvw