GCC 内联汇编 - 在调用之前将浮点移动到 XMM0
我目前正在尝试从 GCC 内联汇编调用通用 C 函数(我知道这是个坏主意,但我今天很无聊......)。
我的操作系统是 Mac OS X,64 位,因此调用约定是 System V,这意味着参数 0-6 通过 rdi
、rsi
、rdx< 传递/code>、
rcx
、r8
和 r9
寄存器。其他参数被推入堆栈。
我知道函数签名,因此我可以猜测返回类型和参数类型。 有了这些信息,我就可以将参数放入正确的寄存器中。
对于整数类型,一切都运行良好,但我在浮点值方面遇到了问题。
浮点值需要通过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 作为参数。它有效...
现在我尝试使用 float 参数进行相同的操作。我只需使用 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>
I'm currently trying to call a generic C function from GCC inline assembly (bad idea, I know, but I'm bored today...).
My operating system is Mac OS X, 64bits, so the calling convention is System V, meaning arguments 0-6 are passed through the rdi
, rsi
, rdx
, rcx
, r8
and r9
registers. Other arguments are pushed to the stack.
I know the function signature, so I can guess the return type, and the type of the arguments.
With that information, I can place the arguments in the correct registers.
Everything is working great with integer types, but I got a problem with floating point values.
Floating point values need to be passed through the xmm0
-xmm7
registers.
So the problem is basically the following. I've got a C variable of type float
. I need to move that variable in, let's say, the xmm0
register, using GCC's inline assembly.
Imagine the following code:
#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;
}
The function foo
is called, with 42 as parameter. It works...
Now I try the same with a float argument. I only have to use movss
instead of mov
, and it works.
The problem comes when I try to call both functions:
#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;
}
The function taking the float argument receive 0. I don't understand why.
I don't touch the stack, so there's no cleanup to do...
If I call the functions directly from C, GCC produces the following:
movl $42, -4(%rbp)
movl $0x42280000, %eax
movl %eax, -8(%rbp)
movl -4(%rbp), %edi
call _foo
movss -8(%rbp), %xmm0
call _bar
I don't get the difference... Any help will be greatly appreciated : )
Have a nice day, all
EDIT
As requested, here's the ASM output when using inline assembly:
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
As requested, here's the GDB output:
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>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我花了一段时间,但我明白了这一点。在使用内联汇编的输出中,gcc 使用 rbp 的负偏移量来存储值。但是,由于它不知道内联汇编中的函数调用,因此它认为它没有调用任何函数。因此,它将变量放在红色区域中,并且不会更改 rsp 来为变量腾出空间。当您调用 foo 时,返回地址会被推入堆栈,覆盖您存储的变量并给您一个不正确的变量。
如果在程序集之外的主函数中的任何一点调用了函数,则 gcc 将更改堆栈以保留变量。例如,如果将
foo(-1);
添加到 main 的顶部,它将起作用。It took me a while, but I figured this out. In the output using inline assembly, gcc uses negative offsets of
rbp
to store the values. However, since it doesn't know about the function calls in the inline assembly, it doesn't think it calls any functions. Therefore, it puts the variables in the red zone and doesn't changersp
to make room for the variables. When you call foo, the return address is pushed to the stack, overwriting your stored variables and giving you an incorrect variable.If, at any point in the main function outside of the assembly, you called a function, then gcc would change the stack to preserve the variables. For example, if you add
foo(-1);
to the top of main, it would work.