Linux の一般的な x86 ユーザーランド アプリケーションで SIGBUS (バス エラー) が発生する原因は何ですか? 私がオンラインで見つけることができたすべての議論は、メモリ アライメント エラーに関するものであり、私が理解していることから、x86 には実際には当てはまりません。
(関連するプロセッサ固有の癖がある場合に備えて、私のコードはGeodeで実行されています。)
Linux の一般的な x86 ユーザーランド アプリケーションで SIGBUS (バス エラー) が発生する原因は何ですか? 私がオンラインで見つけることができたすべての議論は、メモリ アライメント エラーに関するものであり、私が理解していることから、x86 には実際には当てはまりません。
(関連するプロセッサ固有の癖がある場合に備えて、私のコードはGeodeで実行されています。)
SIGBUS
Linux では、メモリ アラインメント エラー以外の多くの理由で発生する可能性があります。たとえば、mmap
マップされたファイルの末尾を超えて領域にアクセスしようとした場合などです。
、共有メモリ領域などを使用していmmap
ますか?
アラインされていないアクセストラップをオンにすると、アラインされていないアクセスからSIGBUSを取得できますが、通常、x86ではオフになっています。なんらかのエラーが発生した場合は、メモリマップドデバイスにアクセスして取得することもできます。
最善の策は、デバッガーを使用して障害のある命令(SIGBUSは同期)を識別し、それが何をしようとしていたかを確認することです。
x86 (x86_64 を含む) での SIGBUS Linux は珍しい獣です。mmap
ed ファイルの末尾を超えてアクセスしようとした場合、または POSIX で説明されているその他の状況で発生する場合があります。
しかし、ハードウェアの障害により、SIGBUS を取得するのは容易ではありません。つまり、SIMD であろうとなかろうと、命令からのアラインされていないアクセスは通常、SIGSEGV になります。スタック オーバーフローが発生すると、SIGSEGV が発生します。正規の形式ではないアドレスへのアクセスでも、SIGSEGV が発生します。これはすべて、ほとんどの場合 SIGSEGV にマップされる #GP が発生したためです。
ここで、CPU 例外のために SIGBUS を取得する方法をいくつか示します。
で AC ビットを有効にEFLAGS
してから、メモリの読み取りまたは書き込み命令による非境界整列アクセスを実行します。詳細については、このディスカッションを参照してください。
#SS を生成するスタック ポインター レジスタ (rsp
または) を介して正規違反を行います。rbp
GCC の例を次に示します (でコンパイルgcc test.c -o test -masm=intel
):
int main() { __asm__("mov rbp,0x400000000000000\n" "mov rax,[rbp]\n" "ud2\n"); }
そうそう、SIGBUS を取得するもう 1 つの奇妙な方法があります。
メモリ不足 (OOM キラーを無効にする必要があります) または失敗した IO 要求のために、カーネルがコード ページのページインに失敗した場合、SIGBUS.
殴られた道から少し外れていますが、アライメントされていないSSE2(m128)ロードからSIGBUSを取得できます。
x86 Linux でのバス エラーの一般的な原因は、実際にはポインターではないもの、またはワイルド ポインターである何かを逆参照しようとすることです。たとえば、ポインターの初期化に失敗したり、任意の整数をポインターに割り当てて逆参照しようとすると、通常、セグメンテーション違反またはバス エラーが発生します。
アライメントは x86 に適用されます。x86 のメモリはバイトアドレス指定可能 (したがって、任意のアドレスへの char ポインターを持つことができます) ですが、たとえば 4 バイト整数へのポインターがある場合、そのポインターはアラインされている必要があります。
プログラムを gdb で実行し、バス エラーを生成しているポインタ アクセスを特定して、問題を診断する必要があります。