引数付きの簡単なコンソールアセンブリプログラムを作成しようとしています。コードは次のとおりです。
.section __TEXT,__text
.globl _main
_main:
movl $0, %edi
callq _exit
コンパイルとリンクのスクリプトは次のとおりです。
as test.s -o test.o
ld test.o -e _main -o test -lc
これで、引数の数に応じて、プログラムがセグメンテーション違反で失敗するか、エラーなしで実行されます。
$ ./test
Segmentation fault: 11
$ ./test 1
$ ./test 1 2
$ ./test 1 2 3
Segmentation fault: 11
$ ./test 1 2 3 4
$ ./test 1 2 3 4 5
Segmentation fault: 11
等々。
LLDBの下に、より有益なエラーが表示されます。
Process 16318 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #0: 0x00007fffad14b2fa libdyld.dylib`stack_not_16_byte_aligned_error
libdyld.dylib`stack_not_16_byte_aligned_error:
-> 0x7fffad14b2fa <+0>: movdqa %xmm0, (%rsp)
0x7fffad14b2ff <+5>: int3
libdyld.dylib`_dyld_func_lookup:
0x7fffad14b300 <+0>: pushq %rbp
0x7fffad14b301 <+1>: movq %rsp, %rbp
実際、プログラムの最初の行で実行を停止すると、スタックはある引数カウントに対して16バイトに整列されていないのに対し、別の引数カウントに対しては整列されていることがわかります。が、AMD64用のSystem V ABIは、と述べています:
スタックポインタは、スタックの一部である最下位アドレスを持つバイトのアドレスを保持します。プロセスエントリで16バイトに整列されることが保証されています。
何が足りないのですか?
OS Xでは、カーネルはへのエントリ時にスタックの配置を保証しないと思いますmain
。スタックを手動で整列させる必要があります。幸い、これはかなり簡単で、スタックポインタの少なくとも4ビットをゼロにするだけです。引数ベクトルまたはその他のデータをフェッチする必要がある場合は、必ず元のスタックポインタを次の場所に格納してください。
_main:
mov %rsp,%rax # copy original stack pointer
and $-16,%rsp # align stack to 16 bytes
push %rax # save original stack pointer
push %rbp # establish...
mov %rsp,%rbp # ...stack frame
... # business logic here
leave # tear down stack frame
pop %rsp # restore original stack pointer
... # exit process
また、スタックの配置を精神的に追跡する必要があります。main
通常のスタックフレームを使用できるように、スタックの配置を行ってから実際のメイン関数を呼び出すだけの方が簡単な場合があります。
_main:
mov %rsp,%rbx # save original stack pointer
and $-16,%rsp # align stack to 16 bytes
call _my_main # call actual main function
mov %rbx,%rsp # restore original stack pointer
... # exit process
特定のサンプルプログラムでは、次の最小限のコードを使用できます。
_main:
and $-16,%rsp # align stack to 16 bytes
xor %edi,%edi # set exit status to zero
call _exit # exit program
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加