从MSVC符号到GCC的汇编端口,用于读取RIP



我有MSVC .asm代码

_get_addr:
call    _lb
_lb:
pop     rax
ret

及其头

PVOID _get_addr();

我想把它移植到GCC。
它在GCC中的替换应该是这样的,对吗?

static __inline__ __attribute__((always_inline)) PVOID _get_addr() {
PVOID pointer;
__asm__(
"call _lb"
"_lb: pop rax; ret %[pointer]"
: [pointer] "=r"(pointer)
);
return pointer;
}

编辑:
仅供参考,这不是我的代码,所以我不确定它是什么意思。我只知道asm实现和它的头。所以可能我的内联是不正确的。

我只是假设_get_addr()应该返回它的调用者的地址。在这种情况下,内联是有意义的,对吗?但你可能是对的,我不应该改变这一点。

EDTI2
用于EDK2自定义组件

PVOID GetImageAddress(void)
{
PVOID Addr = _get_addr();
PVOID Base = (PVOID)((UINT64)Addr & ~(DEFAULT_EDK_ALIGN - 1));
// get current module base by address inside of it
while (*(PUSHORT)Base != EFI_IMAGE_DOS_SIGNATURE)
{
Base = (PVOID)((PUCHAR)Base - DEFAULT_EDK_ALIGN);
}
return Base;
}

那个MASM代码是愚蠢的,64位模式不需要call读取它自己的地址。
您应该将其替换为与rip相关的LEA。
对于x86-64,在内联asm中push/pop也不安全(除非先将RSP偏移128以跳过红色区域);这将踩在x86-64 System V ABI中的RSP以下的红色区域,并且没有办法在此声明一个clober。避开call可以避免按压任何东西

也因为你不想让它成为一个函数,你可以让编译器选择寄存器。您已经使用了"=r",因此在模板字符串中使用%0来扩展到编译器选择的寄存器。硬编码RAX只有在编译器为"=r"输出选择RAX时才有效,否则会造成灾难。

void *current_RIP;
// assuming the default -masm=att, not -masm=intel
asm( "lea 0(%%rip), %0" : "=r"(current_RIP) );  // no inputs, no clobbers

(你的GNU C内联asm尝试在其他方面也被打破了,例如ret %rax不是一个有效的指令。在ret上放置操作数是没有意义的。由于这是内联asm,并且您已经弹出了返回地址,因此根本不应该使用ret指令。MASM代码需要它,因为它是一个返回给调用者的函数,而不是从内联asm语句的末尾掉出来。


避免内联asm

或者完全避免内联asm,只取一个代码地址。函数本身,如(void*)_get_addr,或goto标签。(https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html)

static inline get_addr() {
PVOID pointer = &&label;
label:
return pointer;
}

注意,_names在全局范围内被保留以供实现自己使用。除非这段代码是GCC或clang头文件的一部分,否则这是一个糟糕的名称选择。

Godbolt展示了它们如何使用gcc -O2和/不使用-fPIE进行编译。标签值将在非pie Linux可执行文件中使用mov eax, imm32绝对寻址,因为地址是没有ASLR的链接时间常数。但是如果ASLR是可能的(PIE),它将像asm一样使用rip相对LEA。

一个奇怪的事情是,由于标签指针从来没有被解引用,编译器只是把它放在它内联到的函数的某个地方。在本例中,在顶部,即使函数中有其他代码在获取当前地址之前。

跳转到那个地址显然是不安全的,所以IDK你打算用指针做什么,以及它需要有多精确。


正如David Wohlferd在注释中指出的,原始的独立asm函数总是返回相同的地址(内部)给每一个来电者。一个可以内联(甚至是强制内联)的函数将在该函数内部返回一个地址。

或者如果优化编译器选择进一步内联,甚至在更高的父函数中。这需要编译时内联,不像使用__builtin_return_address(0),所以至少它总是与C源代码中的实际调用地址相同的可执行文件或库中的地址。

最新更新