ホストアクセラレータシステムのホスト側をプログラミングしています。ホストは、Ubuntu LinuxのPCで実行され、USB接続を介して組み込みハードウェアと通信します。通信は、組み込みハードウェアのメモリとの間でメモリチャンクをコピーすることにより実行されます。
ボードのメモリには、データを読み書きするメールボックスとして使用するメモリ領域があります。メールボックスは構造体として定義されており、同じ定義を使用して、ホストスペースにミラーメールボックスを割り当てています。
過去にこの手法を成功裏に使用していたので、ホストEclipseプロジェクトを現在のプロジェクトのワークスペースにコピーし、適切な名前を変更しました。奇妙なことに、ホストプロジェクトをビルドすると、次のメッセージが表示されます。
ビルドターゲット:fft2d_Host
呼び出し:GCC Cリンカー
gcc -L/opt/adapteva/esdk/tools/Host/x86_64/lib -o "fft2d_Host" ./src/fft2d_Host.o -le_Host -lrt./src/fft2d_Host.o:関数「main」内:
fft2d_Host.c :(。text + 0x280):./src/fft2d_Host.oのCOMMONセクションで定義されたシンボル「Mailbox」に対するR_X86_64_PC32
このエラーは何を意味し、現在のプロジェクトではビルドできないのに、古いプロジェクトでは問題ないのはなぜですか?
選択した相対アドレッシングモードの32ビットディスプレイスメントでサポートできるよりも相対アドレッシングスキームのターゲットが遠くなるように、プロジェクトをリンクしようとしています。これは、現在のプロジェクトが大きいため、オブジェクトファイルが異なる順序でリンクされているため、または不必要に拡張的なマッピングスキームが使用されているためです。
この質問は、エラーメッセージの一般的な部分でWeb検索を行うことがしばしば生産的である理由の完璧な例です。次のようなものが見つかります。
http://www.technovelty.org/code/c/relocation-truncated.html
いくつかの治療上の提案を提供しています。
エラーを生成する最小限の例
main.S
:addressを%eax
(32ビット)に移動します:
_start:
mov $_start, %eax
linker.ld
:
SECTIONS
{
/* This says where `.text` will go in the executable. */
. = 0x100000000;
.text :
{
*(*)
}
}
X86-64でコンパイルします。
as -o main.o main.S
ld -o main.out -T linker.ld main.o
ld
の結果:
(.text+0x1): relocation truncated to fit: R_X86_64_32 against `.text'
それを念頭に置いて:
as
は、他のセクションが指定されていない場合、すべてを.text
に配置しますld
は、ENTRY
の場合、.text
をデフォルトのエントリポイントとして使用します。したがって、_start
は.text
の最初のバイトです。修正方法:代わりにこのlinker.ld
を使用し、先頭から1を引きます:
SECTIONS
{
. = 0xFFFFFFFF;
.text :
{
*(*)
}
}
ノート:
この例では_start
を使用して.global _start
をグローバルにすることはできません。そうしないと失敗します。これは、グローバルシンボルに配置の制約があるために発生すると思います(0xFFFFFFF0
が機能します)。 TODO ELF規格のどこに文書化されていますか?
.text
セグメントには、p_align == 2M
のアライメント制約もあります。しかし、リンカは0xFFE00000
にセグメントを配置し、0xFFFFFFFF
までゼロで埋めてe_entry == 0xFFFFFFFF
を設定するのに十分賢いです。これは機能しますが、特大の実行可能ファイルを生成します。
Ubuntu 14.04 AMD64、Binutils 2.24でテスト済み。
説明
最初に、最小限の例で再配置とは何かを理解する必要があります。 https://stackoverflow.com/a/30507725/895245
次に、objdump -Sr main.o
を見てください。
0000000000000000 <_start>:
0: b8 00 00 00 00 mov $0x0,%eax
1: R_X86_64_32 .text
Intelのマニュアルで命令がどのようにエンコードされているかを調べると、次のことがわかります。
b8
は、これが%eax
へのmov
であると言います0
は、%eax
に移動する即値です。その後、再配置により、_start
のアドレスが含まれるように変更されます。32ビットレジスタに移動する場合、即値も32ビットでなければなりません。
しかし、ここでは、再配置はそれらの32ビットを変更して、リンクが発生した後に_start
のアドレスをそれらに入れる必要があります。
0x100000000
は32ビットに適合しませんが、0xFFFFFFFF
は適合します。したがって、エラー。
このエラーは、切り捨てを生成する再配置でのみ発生します。 R_X86_64_32
(8バイトから4バイト)、ただしR_X86_64_64
ではありません。
そして、ここに示されているように、ゼロ拡張の代わりにsign拡張を必要とする再配置のタイプがいくつかありますR_X86_64_32S
。参照: https://stackoverflow.com/a/33289761/895245
エラーメッセージに順番に取り組むことを忘れないでください。私の場合、このエラーの上のエラーは「未定義の参照」であり、それを視覚的にスキップして、より興味深い「切り捨てられた再配置」エラーを見つけました。実際、私の問題は「未定義の参照」メッセージを引き起こしていた古いライブラリでした。それを修正すると、「切り捨てられた再配置」もなくなりました。
膨大な量のスタックスペース(2 GiB以上)を必要とするプログラムをビルドしているときに、この問題に遭遇しました。解決策は、フラグ -mcmodel=medium
。GCCとIntelコンパイラの両方でサポートされています。
多くの場合、このエラーはプログラムが大きすぎることを意味し、1つ以上の非常に大きなデータオブジェクトが含まれているために大きすぎることがよくあります。例えば、
char large_array[1ul << 31];
int other_global;
int main(void) { return other_global; }
x86-64/Linuxでは、最適化せずにデフォルトモードでコンパイルした場合、「再配置は収まるように切り捨てられます」エラーが発生します。 (最適化を有効にすると、少なくとも理論的には、large_array
が未使用である、および/またはother_global
が決して書き込まれないことがわかり、問題を引き起こさないコードを生成できます。)
何が起こっているかというと、デフォルトでは、GCCはこのアーキテクチャで「小さなコードモデル」を使用します。このアーキテクチャでは、プログラムのコードと静的に割り当てられたデータはすべてアドレス空間の最低2GBに収まる必要があります。 (正確な上限は、2GB-2MBのようなものです。これは、プログラムのアドレス空間の最も低い2MBが永久に使用できないためです。共有ライブラリまたは位置に依存しない実行可能ファイルをコンパイルする場合、コードとデータはすべて2ギガバイトですが、アドレス空間の最下部には固定されていません。)large_array
はそれだけですべての空間を消費するため、other_global
には制限を超えるアドレスが割り当てられ、 main
に到達できません。より複雑な場合、コンパイラはlarge_array
が届かないことを知ることができないため、コンパイラからの有用な「other_global
が大きすぎます」エラーではなく、リンカから不可解なエラーを受け取ります。 、したがって、単純なケースでも試されません。
ほとんどの場合、このエラーが発生した場合の正しい対応は、巨大な静的配列やギガバイトのマシンコードを必要としないようにプログラムをリファクタリングすることです。ただし、何らかの理由で本当に必要な場合は、 "medium"または "large"コードモデル を使用して制限を解除できますが、コード生成の効率はやや低下します。これらのコードモデルはx86-64固有です。同様のことが他のほとんどのアーキテクチャにも存在しますが、「モデル」の正確なセットと関連する制限は異なります。 (たとえば、32ビットアーキテクチャでは、コードとデータの総量が2のようなものに制限された「小さな」モデルがあるかもしれません24 バイト。)
Cygwinでは、-mcmodel=medium
はすでにデフォルトであり、役に立ちません。 GCCリンカーに-Wl,--image-base -Wl,0x10000000
を追加すると、エラーが修正されました。