The CFI code for the -fsplit-stack support was incomplete. It did not correctly unwind throughout the function. This patch fixes that by adding the CFI information at the start. This is probably still not precisely correct at all times, but it is better.
In 32-bit mode, the exception cleanup routine has to change %ebx when running in a shared library. Therefore, it is necessary that the unwinder restore %ebx to the old value. This patch implements that in a simple way, but having the main routine save %ebx with appropriate CFI information. Bootstrapped and ran testsuite on x86_64-unknown-linux-gnu. Committed to mainline. Ian 2011-10-28 Ian Lance Taylor <i...@google.com> * config/i386/morestack.S: Correct CFI information to do proper returns throughout function. In 32-bit mode, save %ebx so that it is restored on unwind.
Index: config/i386/morestack.S =================================================================== --- config/i386/morestack.S (revision 180342) +++ config/i386/morestack.S (working copy) @@ -139,44 +139,68 @@ __morestack: .cfi_lsda 0x1b,.LLSDA1 #endif - # Set up a normal backtrace. - pushl %ebp - .cfi_def_cfa_offset 8 - .cfi_offset %ebp, -8 - movl %esp, %ebp - .cfi_def_cfa_register %ebp - # We return below with a ret $8. We will return to a single # return instruction, which will return to the caller of our # caller. We let the unwinder skip that single return # instruction, and just return to the real caller. - .cfi_offset 8, 8 + + # Here CFA points just past the return address on the stack, + # e.g., on function entry it is %esp + 4. Later we will + # change it to %ebp + 8, as set by .cfi_def_cfa_register and + # .cfi_def_cfa_offset above. The stack looks like this: + # CFA + 12: stack pointer after two returns + # CFA + 8: return address of morestack caller's caller + # CFA + 4: size of parameters + # CFA: new stack frame size + # CFA - 4: return address of this function + # CFA - 8: previous value of %ebp; %ebp points here + # We want to set %esp to the stack pointer after the double + # return, which is CFA + 12. + .cfi_offset 8, 8 # New PC stored at CFA + 8 .cfi_escape 0x15, 4, 0x7d # DW_CFA_val_offset_sf, %esp, 12/-4 + # i.e., next %esp is CFA + 12 + + # Set up a normal backtrace. + pushl %ebp + .cfi_def_cfa_offset 8 + .cfi_offset %ebp, -8 + movl %esp,%ebp + .cfi_def_cfa_register %ebp # In 32-bit mode the parameters are pushed on the stack. The # argument size is pushed then the new stack frame size is # pushed. + # Align stack to 16-byte boundary with enough space for saving + # registers and passing parameters to functions we call. + subl $40,%esp + + # Because our cleanup code may need to clobber %ebx, we need + # to save it here so the unwinder can restore the value used + # by the caller. Note that we don't have to restore the + # register, since we don't change it, we just have to save it + # for the unwinder. + movl %ebx,-4(%ebp) + .cfi_offset %ebx, -12 + # In 32-bit mode the registers %eax, %edx, and %ecx may be # used for parameters, depending on the regparm and fastcall # attributes. - pushl %eax - pushl %edx - pushl %ecx + movl %eax,-8(%ebp) + movl %edx,-12(%ebp) + movl %ecx,-16(%ebp) call __morestack_block_signals - pushl 12(%ebp) # The size of the parameters. + movl 12(%ebp),%eax # The size of the parameters. + movl %eax,8(%esp) leal 20(%ebp),%eax # Address of caller's parameters. - pushl %eax + movl %eax,4(%esp) addl $BACKOFF,8(%ebp) # Ask for backoff bytes. leal 8(%ebp),%eax # The address of the new frame size. - pushl %eax + movl %eax,(%esp) - # Note that %esp is exactly 32 bytes below the CFA -- perfect for - # a 16-byte aligned stack. That said, we still ought to compile - # generic-morestack.c with -mpreferred-stack-boundary=2. FIXME. call __generic_morestack movl %eax,%esp # Switch to the new stack. @@ -191,8 +215,8 @@ __morestack: call __morestack_unblock_signals - movl -8(%ebp),%edx # Restore registers. - movl -12(%ebp),%ecx + movl -12(%ebp),%edx # Restore registers. + movl -16(%ebp),%ecx movl 4(%ebp),%eax # Increment the return address cmpb $0xc3,(%eax) # to skip the ret instruction; @@ -200,12 +224,12 @@ __morestack: addl $2,%eax 1: inc %eax - movl %eax,-8(%ebp) # Store return address in an + movl %eax,-12(%ebp) # Store return address in an # unused slot. - movl -4(%ebp),%eax # Restore the last register. + movl -8(%ebp),%eax # Restore the last register. - call *-8(%ebp) # Call our caller! + call *-12(%ebp) # Call our caller! # The caller will return here, as predicted. @@ -255,9 +279,13 @@ __morestack: popl %eax .cfi_remember_state + + # We never changed %ebx, so we don't have to actually restore it. + .cfi_restore %ebx + popl %ebp .cfi_restore %ebp - .cfi_def_cfa %esp, 12 + .cfi_def_cfa %esp, 4 ret $8 # Return to caller, which will # immediately return. Pop # arguments as we go. @@ -300,13 +328,6 @@ __morestack: .cfi_lsda 0x1b,.LLSDA1 #endif - # Set up a normal backtrace. - pushq %rbp - .cfi_def_cfa_offset 16 - .cfi_offset %rbp, -16 - movq %rsp, %rbp - .cfi_def_cfa_register %rbp - # We will return a single return instruction, which will # return to the caller of our caller. Let the unwinder skip # that single return instruction, and just return to the real @@ -314,6 +335,13 @@ __morestack: .cfi_offset 16, 0 .cfi_escape 0x15, 7, 0x7f # DW_CFA_val_offset_sf, %esp, 8/-8 + # Set up a normal backtrace. + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + # In 64-bit mode the new stack frame size is passed in r10 # and the argument size is passed in r11.