Met*_*est 3 c gcc x86-64 inline-assembly stack-pointer
对于某些功能,我需要切换堆栈以使原始堆栈保持不变。为此,我编写了两个宏,如下所示。
#define SAVE_STACK() __asm__ __volatile__ ( "mov %%rsp, %0; mov %1, %%rsp" : \
"=m" (saved_sp) : "m" (temp_sp) );
#define RESTORE_STACK() __asm__ __volatile__ ( "mov %0, %%rsp" : \
"=m" (saved_sp) );
Run Code Online (Sandbox Code Playgroud)
这里temp_sp和saved_sp是线程局部变量。temp_sp指向我们使用的临时堆栈。对于一个我希望不修改其原始堆栈的函数,我将 SAVE_STACK 放在开头,将 RESTORE_STACK 放在底部。例如,像这样。
int some_func(int param1, int param2)
{
int a, b, r;
SAVE_STACK();
// Function Body here
.....................
RESTORE_STACK();
return r;
}
Run Code Online (Sandbox Code Playgroud)
现在我的问题是这种方法是否可行。在 x86(64 位)上,局部变量和参数是通过rbp寄存器访问的,并且rsp在函数序言中相应地被减去,直到在函数尾声中才被触及,在函数结尾中将其添加到原始值。因此,我认为这里没有问题。
我不确定在存在上下文切换和信号的情况下这是否正确。(在 Linux 上)。另外,我不确定如果函数是内联的,或者是否应用尾部调用优化(使用jmp而不是call ),这是否正确。您发现这种方法有任何问题或副作用吗?
通过上面显示的代码,我可以想到以下损坏:
在 x86/x64 上,如果 GCC 认为合适,它会用序言/尾声“装饰”你的函数,并且你无法阻止它这样做(就像在 ARM 上一样,__attribute__((__naked__))强制在没有序言/尾声的情况下创建代码,也就是没有堆栈帧设置) 。在切换堆栈之前,
这可能最终会分配堆栈/创建对堆栈内存位置的引用。更糟糕的是,如果由于编译器的选择,在切换堆栈之前将这样的地址放入非易失性寄存器中,则它可能会别名为两个位置(您更改的堆栈指针相对位置和其他寄存器相对位置)这是一样的)。
同样,在 x86/x64 上,ABI 建议对叶函数(“红色区域”)进行优化,其中未分配堆栈帧,但末尾“下方”的 128 字节堆栈可供函数使用。除非您的内存缓冲区考虑到这一点,否则可能会发生您意想不到的溢出。
信号在备用堆栈上进行处理(请参阅 参考资料sigaltstack()),并且执行您自己的堆栈切换可能会使您的代码无法从信号处理程序中调用。它肯定会使其不可重入,并且取决于您检索“堆栈位置”的位置/方式,也肯定会使其成为非线程安全的。
一般来说,如果您想在不同的堆栈上运行特定的代码片段,为什么不呢:
SIGUSR1并在信号处理程序中运行您的代码(您可以将其配置为使用不同的堆栈)?makecontext()通过/运行它swapcontext()(参见手册页中的示例)?编辑:
既然您说“您想比较两个进程的内存”,那么有不同的方法,特别是外部进程跟踪 - 附加一个“调试器”(可以是您自己编写的进程,用于ptrace()控制您想要的内容)进行监视,并让它代表您跟踪的对象处理断点/检查点,以执行您需要的验证)。这也会更加灵活,因为它不需要更改您检查的代码。