考虑像这样的内联汇编:
uint64_t flags;
asm ("pushf\n\tpop %0" : "=rm"(flags) : : /* ??? */);
尽管可能存在某种内在函数来获取 RFLAGS 的内容,但我如何向编译器表明我的内联汇编破坏了堆栈顶部的一个四字内存?
就我而言,目前这是不可能的。
除了 Peter Corde 跳过红区的方法:
long getflags0(void){
long f; __asm(
"add $-128, %%rsp;\n"
"pushf; pop %0;\n"
"sub $-128, %%rsp\n" : "=r"(f) :: );
return f;
}
呈现:
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
您也可以将
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;
}
呈现:
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
根据经验(玩过很多次),gcc 实际上可以很好地处理
rsp
破坏 - 通过强制帧指针(它不会让你与 rsp 一起破坏 - 这是一个硬汇编错误),避免redzone,相对于帧指针寻址局部变量,并通过在函数末尾强制执行 %rsp
恢复代码。
无论如何,VLA 和分配都需要使编译器
let go
位于堆栈末尾的机制,所以我认为它不会有任何进展。
我认为这样的
rsp
破坏者对于自定义堆栈分配、释放和堆栈切换非常有用,只要你不弄乱编译器在它给你的堆栈指针下面溢出的内容(或者将其打开以使其混乱)与)。
我只在 clang 上遇到了这种方法的一些问题,但对编译器的修复似乎微不足道:https://github.com/llvm/llvm-project/issues/61898.
至于在不影响整个编译单元的情况下抑制警告,
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated"
//...
#pragma GCC diagnostic pop
可以在(可能是内联的——内联函数内的 rsp 破坏者也没有问题)函数中很好地工作,或者您可以使用
_Pragma
生成编译指示以使其在宏内部可用。
Clang 不会抱怨
rsp
clobbers(尽管如果您在其上使用 rsp clobbers 进行内存分配,除非您将我的修复应用到自定义构建,否则您会遇到问题),除非您使用 -fstack-clash-protection
进行编译。那么警告就是-Wstack-protector
,并且它同样可以静音。