私は本質的にCFI
(DWARF EH 情報の呼び出しフレーム情報) とrbp
フレームrsp
間のリンクを壊す、かなりニッチなことをしようとしています。その主な理由は、スレッド制御フローの特定のポイントを過ぎて、基本的に一方向のテールコールとスタックをクリーンアップしてからスタックの一番上に戻る利回りを組み合わせたコールの継続を実行したいからです。継続ポイントで再度実行する準備ができています。
原則として、スタックを混乱させる行をコメントアウトしておく限り機能します。
/*
* x86_64 SysV:
* rdi, rsi, rds, rcx, r8, r9, xmm0-xmm7
*/
__asm {
mov rax, TCB
mov rax, qword ptr [rax] OSThreadControlBlock.StartFn;
call rax;
mov rax, 0;
// end of stack
//push rax;
//push rax;
//push rbx;
// last "real" frame
//push rbp;
//mov rbp, rsp;
//push rbx;
// make the call
mov rdi, RL;
lea rax, qword ptr __OS_RUNLOOP_START__;
call rax;
// trap if it returns
//int 3;
}
SP/BP レジスタの背後にある一般原則を認識しており、具体的には を使用して-fno-omit-frame-pointer
います。私の質問は、それを機能させるために何時間も費やした後、何が欠けているのでしょうか? スタック レイアウトを変更すると、コールの前にスタックを整列させたままプッシュするだけで、次のようなもの (カスタム シグナル ハンドラー) で始まるスノーボール クラッシュが発生するようです。
Received fatal signal: Segmentation fault (11) [thread: 10298 ctl-thrd]
* Unknown error at address 0x0 Regs:
%rip=0x00000000003E2D91 %rbp=0x00007F820A547EA8 %rsp=0x00007F820A547DE8 %rax=0x00007F820A547DE8 %rbx=0x00007F820A547F38
%rdi=0x00000000002121E1 %rsi=0x000000000000007B %rcx=0x000000000000000A %r8=0x0000000000000900 %r9=0x00007F820A5490C0
問題の ABI はlibc++
/ Linuxlibc++abi
でx86_64
、LLVM/Clang 6.0.X ベースのツールチェーンを使用します。私は事実上すべてを試しました。上記が奇妙に見えることはわかっていますが、これはインライン アセンブリの MS 拡張です。完全に正常なコードを生成することを逆アセンブリで何度も確認しました。私が理解している限り、これは CFI とフレーム ポインター ベースのものとの間の奇妙な競合ですが、私はそれほど得意でx86_64
はないので、何が欠けているのかよくわかりません。アンワインド プロセスが番兵 (最後のフレームで SP/FP が null) によって終了されることを意図していることはわかっていますが、この時点で正直に迷っています。
本当にありがたい提案があれば、いろいろ試してみましたが、コアの問題は同じで、スタックに触れるとすぐに、元に戻してもすべてがうまくいきません。最後の呼び出しは慣習的に戻ることを意図していないため、 asm ブロックを超えた clobber は問題ではありません。私が気づいたことの 1 つは、これが何らかの形で TLV に関連しているように見えることですが、NPTL はそれを構成することを意図しているため、どのようにすればよいかわかりません。
助けや提案があれば、私は非常に感謝しています。
編集:
Valgrind からのこのコメントは、何が起こっているのかを説明しているようです:
/* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_
macros. In order not to trash the stack redzone, we need to drop
%rsp by 128 before the hidden call, and restore afterwards. The
nastyness is that it is only by luck that the stack still appears
to be unwindable during the hidden call - since then the behaviour
of any routine using this macro does not match what the CFI data
says. Sigh.
Why is this important? Imagine that a wrapper has a stack
allocated local, and passes to the hidden call, a pointer to it.
Because gcc does not know about the hidden call, it may allocate
that local in the redzone. Unfortunately the hidden call may then
trash it before it comes to use it. So we must step clear of the
redzone, for the duration of the hidden call, to make it safe.
Probably the same problem afflicts the other redzone-style ABIs too
(ppc64-linux, ppc32-aix5, ppc64-aix5); but for those, the stack is
self describing (none of this CFI nonsense) so at least messing
with the stack pointer doesn't give a danger of non-unwindable
stack. */