Я пытаюсь сделать довольно нишевую вещь, которая по сути нарушает ссылки 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;
}
IЯ знаю об общих принципах работы регистров 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++
/ libc++abi
в x86_64
Linux с набором инструментов на основе LLVM / Clang 6.0.X.Я перепробовал практически все, я знаю, что вышеприведенное выглядит странно, но это расширение MS для встроенной сборки, я неоднократно проверял в разборках, что он генерирует совершенно нормальный код.Насколько я понимаю, это какой-то странный конфликт между CFI и основанными на указателе фреймов вещами, но я не настолько хорош в x86_64
, поэтому я не совсем уверен, что мне не хватает.Я знаю, что процесс раскрутки должен быть остановлен дозорным (нулевой SP / FP в последнем кадре), но в этот момент я честно заблудился, потому что даже отладчик был полностью сброшен этим.
Еслиу кого-нибудь есть предложения, которые были бы по достоинству оценены, я пробовал разные вещи, но основная проблема та же, как только я касаюсь стека, даже если я возвращаю его в нормальное состояние, все идет напрасно.Clobber за пределами блока asm не имеет значения, поскольку последний вызов не предназначен для обычного возврата.Одна вещь, которую я заметил, заключается в том, что, похоже, это как-то связано с 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. */