【问题标题】:inline assembly function pointer calling内联汇编函数指针调用
【发布时间】:2021-07-13 14:47:42
【问题描述】:

极简主义示例:

void someFunction(){
  std::cout << "Hello World" << std::endl;
}

void (*functionPointer)();
functionPointer = someFunction;

int main(){
  __asm__("call *%P0"::"m"(functionPointer):);
  return 0;
}

在 gcc 10.3.0 中,这会导致以下错误:

重定位被截断以适应:R_X86_64_32S 针对 `.bss'

collect2.exe:错误:ld 返回 1 个退出状态

有什么想法吗?

【问题讨论】:

  • 您为我编译的示例(如此处 godbolt.org/z/1zPKqKq38 )。您是否尝试过以下问题的答案:stackoverflow.com/questions/10486116/…
  • 在什么操作系统上,以及什么编译/链接选项?
  • 请注意,从内联 asm 调用函数需要安全完成更多工作。该函数可能会破坏您尚未声明的寄存器。在使用 SysV ABI 的系统上,由于红色区域,这要困难得多。
  • @Renat:你将 functionPointer 设为本地内部 main,所以你得到 call *-8(%rsp)。 (所以"m" 约束真的很傻,强制编译器进行存储而不是使用寄存器值。)

标签: pointers x86-64 inline-assembly


【解决方案1】:

这是不安全的 - 首先,必须假设函数调用会破坏所有调用破坏的寄存器,以及 RSP 下面的红色区域,所以这样做非常痛苦安全地从 GNU C 内联汇编。 Calling printf in extended inline ASM 显示了一个安全示例,它在所有相关的整数、mmx、x87 和 xmm 寄存器上声明了clobbers,并避免了红色区域。


*%P0 使 GCC 打印 *functionPointer 而不是 *functionPointer(%rip),后者无法链接到 PIE 可执行文件,因为它是 32 位符号扩展的绝对寻址模式。 32-bit absolute addresses no longer allowed in x86-64 Linux?

请记住,您正在执行内存-间接跳转,因此您需要正常的数据寻址模式,而不是裸符号名称。所以你只需要*%0,就像它可能是一个寄存器一样,如果它愿意,你可以让GCC发出call *%rax,作为"rm"约束。

https://godbolt.org/z/hWeexz8cf

%P 仅在您想要 直接 调用时才有意义,例如 asm("call %P0" : : "i"(callee));,例如call callee 不是 call *callee

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-20
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多