我目前正在尝试从 GCC 内联汇编调用通用 C 函数(我知道这是个坏主意,但我今天很无聊...)。
我的操作系统是 Mac OS X,64 位,所以调用约定是 System V,意思是参数 0-6 通过 rdi
, rsi
, rdx
、rcx
、r8
和 r9
寄存器。其他参数被压入堆栈。
我知道函数签名,所以我可以猜出返回类型和参数类型。 有了这些信息,我可以将参数放入正确的寄存器中。
整数类型一切正常,但浮点值有问题。
float 需要通过xmm0
-xmm7
寄存器传递。
所以问题基本上是下面的。我有一个 float
类型的 C 变量。我需要使用 GCC 的内联汇编将该变量移入比方说 xmm0
寄存器。
想象一下下面的代码:
#include <stdio.h>
void foo( int x )
{
printf( "X: %i\n", x );
}
int main( void )
{
int x = 42;
__asm__
(
"mov %[x], %%rdi;"
"call _foo;"
:
: [ x ] "m" ( x )
);
return 0;
}
函数 foo
被调用,参数为 42。它有效...
现在我尝试使用浮点参数进行同样的操作。我只需要使用 movss
而不是 mov
,就可以了。
当我尝试调用这两个函数时出现问题:
#include <stdio.h>
void foo( int a )
{
printf( "A: %i\n", a );
}
void bar( float b )
{
printf( "B: %f\n", b );
}
int main( void )
{
int a = 42;
float b = 42;
__asm__
(
"mov %[a], %%rdi;"
"call _foo;"
"movss %[b], %%xmm0;"
"call _bar;"
:
: [ a ] "m" ( a ),
[ b ] "m" ( b )
);
return 0;
}
接受 float 参数的函数接收到 0。我不明白为什么。 我不接触堆栈,所以没有清理工作要做...
如果我直接从 C 调用函数,GCC 会产生以下结果:
movl $42, -4(%rbp)
movl $0x42280000, %eax
movl %eax, -8(%rbp)
movl -4(%rbp), %edi
call _foo
movss -8(%rbp), %xmm0
call _bar
我不明白其中的区别...任何帮助将不胜感激:)
祝大家有个愉快的一天
编辑
根据要求,这是使用内联汇编时的 ASM 输出:
movl $42, -4(%rbp)
movl $0x42280000, %eax
movl %eax, -8(%rbp)
mov -4(%rbp), %rdi;
call _foo;
movl -8(%rbp), %eax;
movl %eax, -4(%rbp);
movss -4(%rbp), %xmm0;
call _bar;
EDIT2
根据要求,这是 GDB 输出:
0x100000e9e <main+4>: movl $0x2a,-0x4(%rbp)
0x100000ea5 <main+11>: mov $0x42280000,%eax
0x100000eaa <main+16>: mov %eax,-0x8(%rbp)
0x100000ead <main+19>: mov -0x4(%rbp),%rdi
0x100000eb1 <main+23>: callq 0x100000e54 <foo>
0x100000eb6 <main+28>: movss -0x8(%rbp),%xmm0
0x100000ebb <main+33>: callq 0x100000e75 <bar>
最佳答案
我花了一段时间,但我弄明白了。在使用内联汇编的输出中,gcc 使用 rbp
的负偏移量来存储值。但是,由于它不知道内联汇编中的函数调用,因此它认为它没有调用任何函数。因此,它将变量放在红色区域,并且不更改 rsp
为变量腾出空间。当您调用 foo 时,返回地址被压入堆栈,覆盖您存储的变量并为您提供一个不正确的变量。
如果在程序集之外的主函数中的任何一点调用函数,则 gcc 将更改堆栈以保留变量。例如,如果您将 foo(-1);
添加到 main 的顶部,它将起作用。
关于c - GCC 内联汇编 - 在调用之前将 float 移动到 XMM0,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5860219/