fuz*_*fuz 7 c gcc x86-64 inline-assembly red-zone
考虑这样的内联汇编:
uint64_t flags;
asm ("pushf\n\tpop %0" : "=rm"(flags) : : /* ??? */);
Run Code Online (Sandbox Code Playgroud)
尽管可能存在某种内在函数来获取 RFLAGS 的内容,但我如何向编译器表明我的内联汇编破坏了堆栈顶部的一个四字内存?
除了 Peter Cordes 跳过红区的方法之外:
long getflags0(void){
long f; __asm(
"add $-128, %%rsp;\n"
"pushf; pop %0;\n"
"sub $-128, %%rsp\n" : "=r"(f) :: );
return f;
}
Run Code Online (Sandbox Code Playgroud)
呈现:
0000000000000000 <getflags0>:
0: 48 83 c4 80 add $0xffffffffffffff80,%rsp
4: 9c pushfq
5: 58 pop %rax
6: 48 83 ec 80 sub $0xffffffffffffff80,%rsp
a: c3 retq
$sz(getflags0)=11
Run Code Online (Sandbox Code Playgroud)
您还可以仅将其rsp列为破坏者并消除弃用警告:
long getflags(void){
long f;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated"
__asm("pushf; pop %0" : "=r"(f) :: "rsp");
#pragma GCC diagnostic pop
return f;
}
Run Code Online (Sandbox Code Playgroud)
呈现:
000000000000000b <getflags>:
b: 55 push %rbp
c: 48 89 e5 mov %rsp,%rbp
f: 9c pushfq
10: 58 pop %rax
11: c9 leaveq
12: c3 retq
$sz(getflags)=8
Run Code Online (Sandbox Code Playgroud)
根据经验(玩过很多次),gcc 实际上可以rsp很好地处理破坏 - 通过强制帧指针(它不会让你与 rsp 一起破坏 - 这是一个硬汇编器错误),避免红区,解决本地问题相对于帧指针,并通过%rsp在函数末尾强制执行恢复代码。
无论如何,VLA 和分配都需要让编译器释放堆栈末尾的机制,所以我认为它不会发生任何事情。
我认为这样的rsp破坏器对于自定义堆栈分配、释放和堆栈切换非常有用,只要您不弄乱编译器在它给您的堆栈指针下方溢出的内容(或将其打开以使其混乱)。
我在 clang 上只遇到了这种方法的一些问题,但对编译器的修复似乎微不足道:https ://github.com/llvm/llvm-project/issues/61898 。
至于抑制警告而不影响整个编译单元,
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated"
//...
#pragma GCC diagnostic pop
Run Code Online (Sandbox Code Playgroud)
可以在(可能是内联的——内联函数内的 rsp 破坏者也没有问题)函数中很好地工作,或者您可以生成编译指示以_Pragma使其在宏内部可用。
Clang 不会抱怨rspclobbers(尽管如果您在其上使用 rsp clobbers 进行内存分配,除非您将我的修复应用到自定义构建,否则您会遇到问题),除非您使用-fstack-clash-protection. 那么警告是-Wstack-protector,并且它是等效的可静音的。
请记住,虽然这确实有效,但并未得到官方支持。来自https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Clobbers-and-Scratch-Registers-1:
编译器要求 asm 语句之后的堆栈指针值与语句入口处的堆栈指针值相同。然而,之前版本的GCC并没有强制执行这个规则,而是允许堆栈指针出现在列表中,语义不清晰。此行为已被弃用,并且在 GCC 的未来版本中列出堆栈指针可能会成为错误。