GCC 内联汇编 - 在调用之前将浮点移动到 XMM0

发布于 2024-11-04 14:21:51 字数 2431 浏览 0 评论 0原文

我目前正在尝试从 GCC 内联汇编调用通用 C 函数(我知道这是个坏主意,但我今天很无聊......)。

我的操作系统是 Mac OS X,64 位,因此调用约定是 System V,这意味着参数 0-6 通过 rdirsirdx< 传递/code>、rcxr8r9 寄存器。其他参数被推入堆栈。

我知道函数签名,因此我可以猜测返回类型和参数类型。 有了这些信息,我就可以将参数放入正确的寄存器中。

对于整数类型,一切都运行良好,但我在浮点值方面遇到了问题。

浮点值需要通过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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

我做我的改变 2024-11-11 14:21:51

我花了一段时间,但我明白了这一点。在使用内联汇编的输出中,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 change rsp 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文