我用 Clang 兼容的“GNU 扩展 asm”编写了这段代码:
namespace foreign {
extern char magic_pointer[];
}
extern "C" __attribute__((naked)) void get_address_of_x(void)
{
asm volatile("movq %[magic_pointer], %%rax\n\t"
"ret"
: : [magic_pointer] "p"(&foreign::magic_pointer));
}
我希望它编译成以下程序集:
_get_address_of_x:
## InlineAsm Start
movq $__ZN7foreign13magic_pointerE, %rax
ret
## InlineAsm End
ret /* useless but I don't think there's any way to get rid of it */
但相反,我得到了这个“废话”:
_get_address_of_x:
movq __ZN7foreign13magic_pointerE@GOTPCREL(%rip), %rax
movq %rax, -8(%rbp)
## InlineAsm Start
movq -8(%rbp), %rax
ret
## InlineAsm End
ret
显然 Clang 正在分配&foreign::magic_pointerinto的值%rax(这对函数来说是致命的naked),然后进一步将其“溢出”到一个甚至不存在的堆栈帧上,这样它就可以在 inline asm 块中再次将其拉出。
那么,我怎样才能让 Clang 生成我想要的代码,而不需要手动修改名称呢?我的意思是我可以写
extern "C" __attribute__((naked)) void get_address_of_x(void)
{
asm volatile("movq __ZN7foreign13magic_pointerE@GOTPCREL(%rip), %rax\n\t"
"ret");
}
但如果有任何方法可以帮助它,我真的不想这样做。
在开始之前"p",我尝试了"i"and"n"约束;但它们似乎不适用于 64 位指针操作数。Clang 不断给我错误消息,告诉我无法将操作数分配给%flags寄存器,这似乎是一些疯狂的事情出错了。
对于那些有兴趣在这里解决“XY问题”的人:我真的在尝试编写一个更长的程序集存根,它调用另一个函数foo(void *p, ...),其中参数p设置为此魔术指针值,其他参数基于原始设置输入此程序集存根时 CPU 寄存器的值。(因此,naked功能。)任意公司政策阻止只是在.S文件中写入该死的东西; 此外,我真的很想写foreign::magic_pointer而不是__ZN7foreign...etc.... 无论如何,这应该解释为什么在这种情况下将临时结果溢出到堆栈或寄存器是严格禁止的。
也许有一些方法可以写
asm volatile(".long %[magic_pointer]" : : [magic_pointer] "???"(&foreign::magic_pointer));
让 Clang 准确插入我想要的重定位?