You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在x64 MSVC中获取RIP指令指针:方法正确性与其他方案问询

问题与解答

问题

我尝试获取下方代码中int here = 42;这条指令的运行时内存地址,请问当前采用的方法是否正确?同时想了解在x64版本的Microsoft Visual C++(MSVC)编译器中,还有哪些获取RIP(指令指针寄存器)的方法?

当前使用的C++代码示例:

#include <stdio.h>

void* RIP = NULL;

__declspec(noinline) void GetRIP(void)
{
    // RIP is the instruction pointer register on a 64-bit platform.
    RIP = _ReturnAddress();
}

int main(void)
{
    GetRIP();
    
    // Find the runtime memory address of this instruction.
    int here = 42;

    printf("RIP = %p\n", RIP);
    
    return 0;
}

注:推测Godbolt使用的是Debug构建,因为int here = 42;未被优化掉。

用户同时提供了MASM x64获取RIP的代码:

option casemap:none

extrn printf:proc

.data
     szRIP db 'RIP = %p',0ah,0

.code

main proc
     call    _here
_here:
     pop     rax
     ; rax now holds the RIP.
     mov     rdx,rax 
     mov     rcx,offset szRIP
     sub     rsp,20h
     call    printf
     add     rsp,20h

     ret
main endp

end

当前方法是否正确?

当前方法既不正确也不可靠,原因如下:

  1. _ReturnAddress()返回的是GetRIP()执行完毕后,回到main函数要执行的下一条指令地址。在Debug构建下,这个地址可能恰好是int here = 42;的起始地址,但这完全依赖于编译器的代码生成逻辑——如果编译器在GetRIP();和int here = 42;之间插入额外指令(比如栈对齐、调试信息指令),返回的地址就会偏离目标指令。
  2. 这种方式没有直接关联到int here = 42;这条指令本身,无法保证在不同编译模式(如Release)、不同优化等级下都能精准获取目标指令的地址。

x64 MSVC中获取RIP的其他方法

1. 内联汇编直接获取

利用x64架构的call指令特性,和你提供的MASM逻辑一致,通过内联汇编实现:

#include <stdio.h>

int main() {
    void* current_rip;
    __asm {
        call get_rip_label
get_rip_label:
        pop rax                ; 弹出call指令压栈的返回地址(即get_rip_label处的RIP)
        mov current_rip, rax
    }
    printf("当前RIP地址:%p\n", current_rip);
    return 0;
}

该方法能直接获取get_rip_label标签处的指令地址,若要定位特定指令,调整标签位置即可。

2. 使用__builtin_return_address

MSVC支持GCC兼容的__builtin_return_address(n),其中n=0表示当前函数的返回地址(即调用当前函数的下一条指令地址),效果和_ReturnAddress()类似,但可以指定栈回溯层级:

#include <stdio.h>

__declspec(noinline) void GetRIP() {
    void* rip = __builtin_return_address(0);
    printf("返回地址(对应调用者的下一条指令RIP):%p\n", rip);
}

int main() {
    GetRIP();
    int here = 42;
    return 0;
}

3. 利用RIP相对寻址特性

x64架构原生支持RIP相对寻址,通过内联汇编直接加载当前RIP:

#include <stdio.h>

int main() {
    void* current_rip;
    __asm {
        lea rax, [rip]        ; 直接将当前RIP加载到rax寄存器
        mov current_rip, rax
    }
    printf("当前RIP地址:%p\n", current_rip);
    return 0;
}

或者用纯C结合MSVC标签地址扩展:

#include <stdio.h>

int main() {
    void* current_rip = (void*)&&current_label;
current_label:
    printf("当前RIP地址:%p\n", current_rip);
    return 0;
}

这种方式直接获取标签处的指令地址,精准度高。

4. 栈回溯函数间接获取

使用Windows API的RtlCaptureStackBackTrace或CaptureStackBackTrace,通过栈回溯获取返回地址列表,其中第一个返回地址对应当前函数的下一条指令RIP:

#include <stdio.h>
#include <Windows.h>

int main() {
    void* backtrace[1];
    RtlCaptureStackBackTrace(0, 1, backtrace, NULL);
    printf("栈回溯得到的返回地址:%p\n", backtrace[0]);
    return 0;
}

该方法多用于调试场景,并非直接获取当前执行指令的RIP。


内容的提问来源于stack exchange,提问作者vengy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 14:30:16