在Mac OS X Lion上获取C中的堆栈指针

Mic*_*ker 2 c macos assembly x86-64

尝试在C中获取当前堆栈指针时(使用内联ASM),我遇到了一些奇怪的行为.代码如下:

#include <stdio.h>
class os {
  public:
    static void* current_stack_pointer();
};

void* os::current_stack_pointer() {
  register void *esp __asm__ ("rsp");
  return esp;
}

int main() {
  printf("%p\n", os::current_stack_pointer());
}
Run Code Online (Sandbox Code Playgroud)

如果我使用标准gcc选项编译代码:

$ g++ test.cc -o test
Run Code Online (Sandbox Code Playgroud)

它生成以下程序集:

__ZN2os21current_stack_pointerEv:
0000000000000000        pushq   %rbp
0000000000000001        movq    %rsp,%rbp
0000000000000004        movq    %rdi,0xf8(%rbp)
0000000000000008        movq    0xe0(%rbp),%rax
000000000000000c        movq    %rax,%rsp
000000000000000f        movq    %rsp,%rax
0000000000000012        movq    %rax,0xe8(%rbp)
0000000000000016        movq    0xe8(%rbp),%rax
000000000000001a        movq    %rax,0xf0(%rbp)
000000000000001e        movq    0xf0(%rbp),%rax
0000000000000022        popq    %rbp
Run Code Online (Sandbox Code Playgroud)

如果我运行生成的二进制文件,它将使用SIGILL(非法指令)崩溃.但是,如果我在编译中添加一些优化:

$ g++ -O1 test.cc -o test
Run Code Online (Sandbox Code Playgroud)

生成的程序集更简单:

0000000000000000        pushq   %rbp
0000000000000001        movq    %rsp,%rbp
0000000000000004        movq    %rsp,%rax
0000000000000007        popq    %rbp
0000000000000008        ret
Run Code Online (Sandbox Code Playgroud)

代码运行正常.所以对于这个问题; 从Mac OS X上的C代码获取堆栈指针是否更稳定?相同的代码在Linux上没有问题.

Jas*_*son 5

尝试通过函数调用获取堆栈指针的问题是,被调用函数内的堆栈指针指向一个在函数返回后将完全不同的值,因此您将捕获将在该函数返回的位置的地址通话结束后无效.您还假设编译器在该平台上没有添加函数序言(即,您的函数当前都有一个序言,其中编译器在函数的堆栈上设置当前激活记录,这将改变您尝试捕获的RSP值).至少,如果编译器没有添加函数序言,你需要减去你正在使用的平台上的指针大小,以便实际获得堆栈所在的"真实"地址.从函数调用返回后指向.这是因为汇编命令call将指令指针的返回地址压入堆栈,而ret被调用者将该值从堆栈中弹出.因此,在被调用者内部,至少会有一个返回地址指令,堆栈指针将指向该指令,并且该函数调用后该位置将无效.最后,在某些平台上(不幸的是不是x86),你可以使用__attributes__((naked))标签创建一个没有序言的函数gcc.使用inline关键字来避免序言并不完全可靠,因为它不会强制编译器内联函数...在某些低优化级别下,内联不会发生,并且你最终会再次进行序言,并且如果您决定在这些情况下使用它的地址,则堆栈指针将不会指向正确的位置.

如果你必须拥有堆栈指针的值,那么唯一可靠的方法是使用程序集,遵循平台的ABI规则,使用汇编程序编译到目标文件,然后将该目标文件与其余的可执行文件中的目标文件.然后,您可以通过在头文件中包含函数声明,将汇编程序函数公开给其余代码.所以你的代码看起来像(假设你gcc用来编译程序集):

//get_stack_pointer.h
extern "C" void* get_stack_ptr();

//get_stack_pointer.S
.section .text
.global get_stack_ptr

get_stack_ptr:
    movq %rsp, %rax
    addq $8, %rax
    ret
Run Code Online (Sandbox Code Playgroud)