Linux – -O1 and GCC segment faults for inline assemblers

-O1 and GCC segment faults for inline assemblers… here is a solution to the problem.

-O1 and GCC segment faults for inline assemblers

I

detected a weird segfault in my code, if this could be a GCC bug or just my fault, I’d like to hear from you!

The function looks like this:

void testMMX( ... ) {
unsigned long a         = ...;
unsigned char const* b = ...;
unsigned long c    = ...;
__asm__ volatile ( 
    "pusha;" 
);
__asm__ volatile ( "mov %0, %%eax;" : : "m"( a ) : "%eax" );  with "r"( a ) it just works fine!
__asm__ volatile ( "add %0, %%eax;" : : "m"( b ) : "%eax" );
__asm__ volatile ( "mov %0, %%esi;" : : "m"( c ) : "%eax", "%esi" );
__asm__ volatile (
    "sub %eax, %esi;"
    "dec %esi;"
    "movd (%esi), %mm0;"
    "popa;" 
);
}

If I compile it with -O0, it works fine. But it will have a segfault with -O1 and -O2. It took me a long time to figure out that this segfault was caused by a missing frame pointer. The pusha instruction increases the stack size by 4*8=32 bytes (x86_32), so ESP should also increase. But the GCC does not recognize this. If I add ESP fix manually

__asm__("add $32, %esp")

Or use the “-fno-omit-frame-pointer” flag in gcc, I can compile and run it with -O1 and -O2 without any errors!

So my question now is: if frame-pointer-omission is enabled, why doesn’t gcc use any push/pop inline assembler operations to tweak ESP? Is this a gcc error? Will the GCC even be able to detect this? Am I missing something?

It would be interesting to solve this problem.

Thanks in advance!

Solution

No – GCC cannot detect this. It does not perform any analysis on the instructions that appear in the ASM block. It is your responsibility to notify the compiler of any side effects. Can you explain what test you are doing?

Also, you should consider using a single asm block for this code; Volatile may prevent the reordering of ASM blocks, but you cannot assume this yields consecutive instructions .

Related Problems and Solutions