我在Clang兼容的“GNU extended 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_pointer
的值分配给%rax
(这对于naked
函数是致命的),然后进一步将其“溢出”到不具有extern "C" __attribute__((naked)) void get_address_of_x(void)
{
asm volatile("movq __ZN7foreign13magic_pointerE@GOTPCREL(%rip), %rax\n\t"
"ret");
}
功能的堆栈帧上甚至存在,所以它可以在内联asm块中再次将其拉出。
那么,我怎样才能让Clang完全生成我想要的代码,而不需要手动命名?我的意思是我可以写
"p"
但如果有任何方法可以帮助我,我真的不想这样做。
在点击"i"
之前,我尝试了"n"
和%flags
限制;但它们似乎无法正常使用64位指针操作数。 Clang不断给我一些关于无法将操作数分配给foo(void *p, ...)
寄存器的错误消息,这似乎是疯狂的事情。
对于那些有兴趣解决“XY问题”的人:我真的想写一个更长的汇编存根,调用另一个函数p
,其中参数naked
被设置为魔术指针值和其他参数是根据输入此装配存根的CPU寄存器的原始值设置的。 (因此,.S
功能。)任意公司政策阻止只在foreign::magic_pointer
文件中写下该死的东西;此外,我真的 喜欢写__ZN7foreign...etc...
而不是asm volatile(".long %[magic_pointer]" : : [magic_pointer] "???"(&foreign::magic_pointer));
。无论如何,这应该解释为什么在这种情况下严格禁止将临时结果溢出到堆栈或寄存器。
也许有一些方法可以写
{{1}}
让Clang准确插入我想要的重定位?
答案 0 :(得分:2)
我认为这就是你想要的:
namespace foreign {
extern char magic_pointer[];
}
extern "C" __attribute__((naked)) void get_address_of_x(void)
{
asm volatile ("ret" : : "a"(&foreign::magic_pointer));
}
在此上下文中,“a”是一个约束,指定必须使用%rax
。然后,Clang会将magic_pointer
的地址加载到%rax
,以准备执行您的内联asm,这就是您所需要的。
这有点狡猾,因为它定义了asm文本中未引用的约束,我不确定这是否在技术上允许/定义良好 - 但它确实适用于最新的clang。
在clang 3.0-6ubuntu3上(因为我懒惰并使用gcc.godbolt.org),-fPIC
,这是你得到的:
get_address_of_x: # @get_address_of_x
movq foreign::magic_pointer@GOTPCREL(%rip), %rax
ret
ret
没有-fPIC
:
get_address_of_x: # @get_address_of_x
movl foreign::magic_pointer, %eax
ret
ret
答案 1 :(得分:1)
我最后只是编写一个帮助器extern "C"
函数来返回魔术值,然后从汇编代码中调用该函数。我仍然认为Clang应该以某种方式支持我的原始方法,但在我的现实案例中,这种方法的主要问题是它没有扩展到x86-32。在x86-64上,可以在%rdx
- 相对%rip
的单个指令中将任意地址加载到mov
。但是在x86-32上,用-fPIC
加载一个任意地址变成了一个 ton 的代码,.indirect_symbol
指令,两个内存访问......我只是不想要尝试手工编写所有内容。所以我的最终汇编代码看起来像
asm volatile(
"...save original register values...;"
"call _get_magic_pointer;"
"movq %rax, %rdx;"
"...set up other parameters to foo...;"
"call _foo;"
"...cleanup..."
);
更简单,更清洁。 :)