https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82926
Bug ID: 82926 Summary: x86_64 inline assembly with push/pop produces buggy code Product: gcc Version: unknown URL: https://bugs.freedesktop.org/show_bug.cgi?id=99066 Status: UNCONFIRMED Severity: normal Priority: P3 Component: inline-asm Assignee: unassigned at gcc dot gnu.org Reporter: EoD at xmw dot de Target Milestone: --- Created attachment 42569 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=42569&action=edit C++ program showing the issue Currently, pulseaudio fails compiling with "-O0" due to a crash in its get_cpuid() function (see URL above for further details). I attached a simple C++ program to show the issue. When compiling the program with "-O0" a segfault is caused g++ -mx32 -O0 get_cpuid_crash.cpp When compiling the program with either "-O1" or "-m64", the program does not segfault: g++ -mx32 -O1 get_cpuid_crash.cpp g++ -m64 -O0 get_cpuid_crash.cpp Also using clang does not cause a segfault clang++ -mx32 -O0 get_cpuid_crash.cpp I would also like to quote tanuk from https://bugs.freedesktop.org/show_bug.cgi?id=99066#c14 > I was asked for more information about the cpuid crash, so here we go: > > This is the code that GCC generates for get_cpuid() on x32: > > 0xf7b6a270 <get_cpuid> push %rbp > 0xf7b6a271 <get_cpuid+1> mov %esp,%ebp > 0xf7b6a273 <get_cpuid+3> mov %edi,-0x4(%ebp) > 0xf7b6a277 <get_cpuid+7> mov %rcx,%rax > 0xf7b6a27a <get_cpuid+10> mov %r8,%rcx > 0xf7b6a27d <get_cpuid+13> mov %esi,-0x8(%ebp) > 0xf7b6a281 <get_cpuid+17> mov %edx,-0xc(%ebp) > 0xf7b6a285 <get_cpuid+21> mov %eax,-0x10(%ebp) > 0xf7b6a289 <get_cpuid+25> mov %ecx,-0x14(%ebp) > 0xf7b6a28d <get_cpuid+29> mov -0x4(%ebp),%eax [breakpoint] > 0xf7b6a291 <get_cpuid+33> push %rbx > 0xf7b6a292 <get_cpuid+34> cpuid > 0xf7b6a294 <get_cpuid+36> mov %ebx,%esi > 0xf7b6a296 <get_cpuid+38> pop %rbx > 0xf7b6a297 <get_cpuid+39> mov -0x8(%ebp),%edi > 0xf7b6a29b <get_cpuid+43> mov %eax,(%edi) [segfault] > 0xf7b6a29e <get_cpuid+46> mov -0xc(%ebp),%eax > 0xf7b6a2a2 <get_cpuid+50> mov %esi,(%eax) > 0xf7b6a2a5 <get_cpuid+53> mov -0x10(%ebp),%eax > 0xf7b6a2a9 <get_cpuid+57> mov %ecx,(%eax) > 0xf7b6a2ac <get_cpuid+60> mov -0x14(%ebp),%eax > > "[breakpoint]" marks the place where the execution stops if you set a > breakpoint with "break get_cpuid". "[segfault]" marks the place where the > crash happens. > > Before the breakpoint there's the code that copies the function parameters > to the stack as follows: > > %edi is the "op" parameter. It's saved to -0x4(%ebp). > %rcx is the "c" parameter. It's moved to %rax and from %rax to -0x10(%ebp). > %r8 is the "d" parameter. It's moved to %rcx and from %rcx to -0x14(%ebp). > %esi is the "a" parameter. It's saved to -0x8(%ebp). > %edx is the "b" parameter. It's saved to -0xc(%ebp). > > The stack pointer is not updated when the parameters are saved to the stack. > Since the stack pointer points to the beginning of the frame, the push > instruction overwrites 8 bytes from the beginning of the frame, overwriting > the "op" and "a" parameters. > > I think the push is done, because the %rbx register is special in that it > must always have the same value when returning from a function as it had > when the function started. The cpuid instruction modifies the %rbx register, > so that's why we need to save and restore the %rbx register. > > After the pop, this happens: > > 0xf7b6a297 <get_cpuid+39> mov -0x8(%ebp),%edi > > This reads the stack from the position where the "a" parameter was saved. > The compiler seems to assume that it has the same value that was written > there in the beginning of the function, but the push instruction has written > some random garbage there. > > 0xf7b6a29b <get_cpuid+43> mov %eax,(%edi) [segfault] > > This is supposed to save the return value (well, one part of the return > value) of the cpuid instruction to the address stored in %edi, but we just > wrote garbage to %edi, so we end up dereferencing using garbage as the > pointer (in my tests the value in %edi was 1).