这个方法好吗?

对于某些function,我需要切换堆栈,以便原始堆栈保持不变。 为此,我写了两个macros,如下所示。

#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是线程局部variables。 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寄存器访问局部variables和参数,相应地在function序言中减去rsp,直到在函数epilogue中被添加以使其恢复到原始值。 所以我在这里看不出什么问题。

我不确定,如果在上下文切换和信号存在的情况下这是正确的。 此外,我不确定如果这是正确的,如果函数内联或尾部呼叫优化(其中使用jmp而不是呼叫 )被应用。 你看到这种方法有什么问题或副作用吗?

使用上面显示的代码,我可以考虑以下破坏:

  1. (在ARM上, __attribute__((__naked__))没有序言/结尾的情况下强制执行代码的创建(在ARM / x64上),GCC将会使用prologue / epilogues“装饰”你的函数。 ,也没有堆栈设置)。
    切换堆栈之前,可能会最终分配堆栈/创建堆栈内存位置的引用。 更糟糕的是,如果再次由于编译器的选择,在切换堆栈之前将这样的地址放入非易失性寄存器中,它可能会混叠到两个位置(堆栈指针 – 相对于您更改的地址和另一个相对的地址那是一样的)。

  2. 同样,在x86 / x64上,ABI建议对叶函数(“红区”)进行优化,在该函数中没有分配栈帧,但在结尾“下面”有128字节的堆栈可用。 除非你的内存缓冲区考虑到了这一点,否则可能会发生超出你不期待的情况。

  3. 信号在备用堆栈上处理(请参阅sigaltstack() ),执行自己的堆栈切换可能会使您的代码在信号处理程序内不可用。 这肯定会使它不可重入,并且取决于你在哪里/如何检索“堆栈位置”,也肯定会使它不是线程安全的。

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

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

编辑:

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