GCC built-in - move float to XMM0 before calling

I'm currently trying to call a generic C function from within the GCC built-in assembly (a bad idea, I know, but I'm bored today ...).

My operating system is Mac OS X, 64 bit, so the calling convention is System V, that is, arguments 0-6 are passed through rdi , rsi , rdx , rcx , r8 and r9 . Other arguments are pushed onto the stack.

I know the signature of the function, so I can guess the return type and argument type. With this information, I can put the arguments in the correct registers.

Everything works fine with integer types, but I am having a problem with floating point values.

Floating point values ​​must go through the registers xmm0 - xmm7 .

Thus, the problem is mainly as follows. I have a C variable of type float . I need to move this variable into, say, the xmm0 register using the built-in GCC assembly.

Provide 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 foo function is called with parameter 42. It works ...

Now I try the same with the float argument. I need to use movss instead of mov , and it works.

The problem occurs 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; } 

A function that takes a float argument takes 0. I don’t understand why. I do not touch the stack, so there is no cleaning ...

If I call functions directly from C, GCC creates 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 do not understand ... Any help would be greatly appreciated :)

Good day everyone

EDIT

As requested, ASM is issued using the built-in 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, the output of 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> 
+6
source share
1 answer

It took me a while, but I figured it out. In the output using the built-in assembly, gcc uses negative rbp to store the values. However, since he does not know about function calls in the built-in assembly, he does not consider that he calls any functions. Therefore, it places the variables in the red zone and does not change rsp to make room for the variables. When you call foo, the return address is pushed onto the stack, overwriting the stored variables and giving you the wrong variable.

If at any point in the main function outside the assembly you called the function, then gcc will change the stack to save the variables. For example, if you add foo(-1); to the beginning of main, this will work.

+7
source

Source: https://habr.com/ru/post/887131/


All Articles