gpt4 book ai didi

c - 这种用于堆栈切换的内联汇编方法可以吗?

转载 作者:太空狗 更新时间:2023-10-29 11:05:38 25 4
gpt4 key购买 nike

对于某些函数,我需要切换堆栈,使原始堆栈保持不变。为此,我编写了两个宏,如下所示。

#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) );

这里的temp_spsaved_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;
}

现在我的问题是这种方法是否合适。在 x86(64 位)上,局部变量和参数通过 rbp 寄存器访问,并且 rsp 在函数序言中被相应地减去,直到在函数尾声中添加它时才被触及使其恢复到原来的值。因此,我认为这里没有问题。

我不确定这在存在上下文切换和信号的情况下是否正确。 (在 Linux 上)。此外,如果函数是内联的,或者是否应用了尾调用优化(使用 jmp 而不是 call ),我也不确定这是否正确。您认为这种方法有任何问题或副作用吗?

最佳答案

根据您上面显示的代码,我可以想到以下破损:

  1. 在 x86/x64 上,如果 GCC 认为合适,它会用序言/结语“装饰”您的函数,您无法阻止它这样做(就像在 ARM 上一样,其中 __attribute__((__naked__ )) 在没有序言/结语的情况下强制创建代码,也就是没有堆栈框架设置)。
    这可能会在您切换堆栈之前分配堆栈/创建对堆栈内存位置的引用。更糟糕的是,如果再次由于编译器的选择,在切换堆栈之前将这样的地址放入非 volatile 寄存器中,它可能会别名到两个位置(您更改的与堆栈指针相关的位置和与其他寄存器相关的位置)那是一样的)。

  2. 同样,在 x86/x64 上,ABI 建议对叶函数(“红色区域”)进行优化,其中未分配任何堆栈帧,但末尾“下方”的 128 字节堆栈可供函数使用。除非您的内存缓冲区考虑到这一点,否则可能会发生您未预料到的溢出。

  3. 信号在备用堆栈上处理(请参阅 sigaltstack()),您自己进行堆栈切换可能会使您的代码无法从信号处理程序中调用。它肯定会使其不可重入,并且根据您检索“堆栈位置”的位置/方式也肯定会使其成为非线程安全的。

一般来说,如果您想在不同的堆栈上运行一段特定的代码,为什么不这样做:

  • 在不同的线程中运行它(每个线程获得不同的堆栈)?
  • 触发,例如SIGUSR1 并在信号处理程序中运行您的代码(您可以将其配置为使用不同的堆栈)?
  • 通过 makecontext()/swapcontext() 运行它(参见联机帮助页中的示例)?

编辑:

既然你说“你想比较两个进程的内存”,那么同样有不同的方法,特别是外部进程跟踪 - 附加一个“调试器”(可以是一个进程你自己编写使用 ptrace() 来控制你想要监控的内容,并让它代表你跟踪的人处理例如断点/检查点,以执行你需要的验证)。这也会更加灵活,因为它不需要更改您检查的代码。

关于c - 这种用于堆栈切换的内联汇编方法可以吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8818808/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com