使用 GCC 内联汇编直接调用 C 函数
如果您想从内联汇编中调用 C/C++ 函数,您可以执行以下操作:
void callee() {}
void caller()
{
asm("call *%0" : : "r"(callee));
}
然后,GCC 将发出如下所示的代码:
movl $callee, %eax
call *%eax
这可能会出现问题,因为间接调用会破坏旧 CPU 上的管道。
由于callee
的地址最终是一个常量,因此可以想象可以使用i
约束。引用 GCC 在线 文档:
`我'
允许使用立即整数操作数(具有常量值)。这 包括符号常量,其 值只有在装配时才知道 时间或更晚。
如果我尝试像这样使用它:
asm("call %0" : : "i"(callee));
我从汇编器中收到以下错误:
错误:后缀或操作数对于“call”无效
这是因为GCC发出代码
call $callee
而不是
call callee
所以我的问题是是否可以使GCC输出正确的call
。
If you want to call a C/C++ function from inline assembly, you can do something like this:
void callee() {}
void caller()
{
asm("call *%0" : : "r"(callee));
}
GCC will then emit code which looks like this:
movl $callee, %eax
call *%eax
This can be problematic since the indirect call will destroy the pipeline on older CPUs.
Since the address of callee
is eventually a constant, one can imagine that it would be possible to use the i
constraint. Quoting from the GCC online docs:
`i'
An immediate integer operand (one with constant value) is allowed. This
includes symbolic constants whose
values will be known only at assembly
time or later.
If I try to use it like this:
asm("call %0" : : "i"(callee));
I get the following error from the assembler:
Error: suffix or operands invalid for `call'
This is because GCC emits the code
call $callee
Instead of
call callee
So my question is whether it is possible to make GCC output the correct call
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我从 GCC 的邮件列表中得到了答案:
现在我只需要找出
%P0
的实际含义,因为它似乎是一个未记录的功能...编辑:查看 GCC 源代码后,并不清楚什么约束前面的代码
P
表示约束。但是,除此之外,它还阻止 GCC 将$
放在常量值前面。这正是我在这种情况下所需要的。为了安全起见,您需要告诉编译器该函数调用可能修改的所有寄存器,例如
: "eax", "ecx", "edx", "xmm0", "xmm1", ... 、“st(0)”、“st(1)”、...
。请参阅在扩展内联 ASM 中调用 printf 以获取完整的 x86- 64 正确且安全地从内联汇编进行函数调用的示例。
I got the answer from GCC's mailing list:
Now I just need to find out what
%P0
actually means because it seems to be an undocumented feature...Edit: After looking at the GCC source code, it's not exactly clear what the code
P
in front of a constraint means. But, among other things, it prevents GCC from putting a$
in front of constant values. Which is exactly what I need in this case.For this to be safe, you need to tell the compiler about all registers that the function call might modify, e.g.
: "eax", "ecx", "edx", "xmm0", "xmm1", ..., "st(0)", "st(1)", ...
.See Calling printf in extended inline ASM for a full x86-64 example of correctly and safely making a function call from inline asm.
也许我在这里遗漏了一些东西,但
应该可以正常工作。您需要 extern "C" 以便名称不会根据 C++ 命名修饰规则进行修饰。
Maybe I am missing something here, but
should work fine. You need extern "C" so that the name won't be decorated based on C++ naming mangling rules.
如果您生成 32 位代码(例如 -m32 gcc 选项),则以下 asm 内联将发出直接调用:
If you're generating 32-bit code (e.g. -m32 gcc option), the following asm inline emits a direct call:
诀窍是字符串文字连接。在 GCC 开始尝试从代码中获取任何实际含义之前,它将连接相邻的字符串文字,因此即使汇编字符串与您在程序中使用的其他字符串不同,如果您这样做,它们也应该连接起来:
因为您正在使用 GCC 您也可以这样做
如果您还不知道, ,您应该知道从内联汇编中调用事物是非常棘手的。当编译器生成自己对其他函数的调用时,它包含在调用之前和之后设置和恢复内容的代码。但它不知道自己应该为您的呼叫执行这些操作。您必须自己包含它(很难正确完成,并且可能会因编译器升级或编译标志而中断),或者确保您的函数以这样的方式编写:它似乎没有更改任何寄存器或条件堆栈(或其中的变量)。
编辑这仅适用于 C 函数名称 - 不适用于 C++,因为它们被破坏了。
The trick is string literal concatenation. Before GCC starts trying to get any real meaning from your code it will concatenate adjacent string literals, so even though assembly strings aren't the same as other strings you use in your program they should be concatenated if you do:
Since you are using GCC you could also do
If you don't already know you should be aware that calling things from inline assembly is very tricky. When the compiler generates its own calls to other functions it includes code to set up and restore things before and after the call. It doesn't know that it should be doing any of this for your call, though. You will have to either include that yourself (very tricky to get right and may break with a compiler upgrade or compilation flags) or ensure that your function is written in such a way that it does not appear to have changed any registers or condition of the stack (or variable on it).
edit this will only work for C function names -- not C++ as they are mangled.