This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Offset ip value in MD_FALLBACK_FRAME_STATE_FOR on x86-linux


Hello,

When an exception is raised from a signal handler, the unwinding process
typically resorts to MD_FALLBACK_FRAME_STATE_FOR (when defined) to unwind past
the signal handler, because the return address from there is associated to
some kernel/library function for which no unwind data is at hand.

The idea there is to setup the internal frame state data to let things appear
as if the handler had been "called" from the point at which the signal
occured, so that unwinding may proceed and possible exception handlers there
are entered.

OTOH, the "regular" unwinding process considers the exception context ip
values as call return addresses, because this is indeed the nominal case. In
this nominal case, we are actually interested in what region the corresponding
call pertains to, and the corresponding return address may be outside that
region.  The unwinder so inconditionally adjusts the ip values while searching
tables to ensure it looks up for things corresponding to the region of the
call.

Now, the address we have at hand in the FALLBACK macro is typically coming
from some pushed signal context and corresponds to the instruction which
triggered the signal. In that case, we are interested in what region this
instruction pertains to, and if it happens to be the first instruction in a
region, the unwinder's adjustment looses because it yields an address outside
that region.

This potential problem is currently visible on x86-linux for the following Ada
testcase:

   with System;

   procedure SEGV is
      X : Integer;
      for X'Address use System.Null_Address;
   begin
      X := 12;
   exception
      when others => null;
   end;

The assembly output out of the current mainline compiler reads:

   _ada_segv:
   [...]
   .LEHB1:
	   movl    $12, 0  #,
   .LEHE1:

The GNAT run-time library catches the signal and raises an exception from the
signal handler. That exception is expected to be caught by the exception
handler, but it is not because of the ip adjustment mentioned above and the
program reports an unhandled exception instead of remaining silent.

The patch suggested below addresses that problem (on x86-linux) by
anticipating the unwinder's adjustement beforehand in the FALLBACK macro,
pretending the context's "return address" is the address of the faulting
instruction plus one. It does so by hijacking an unused signal context slot
and not by directly adjusting eip to avoid troubles that could result from
mutliple invocations of the macro for the same signal occurrence. Multiple
invocations are indeed expected by virtue of the two phase unwinding process
involved in exception propagations.

Bootstrapped and regression tested on i686-pc-linux-gnu, all languages.

Thanks in advance for your feedback,

Olivier

--

2004-05-05  Olivier Hainque  <hainque@act-europe.fr>

        * i386/linux.h (MD_FALLBACK_FRAME_STATE_FOR): Compensate unwinder
        adjustments to return address.

*** gcc/config/i386/linux.h.ori	Wed May  5 12:39:21 2004
--- gcc/config/i386/linux.h	Wed May  5 12:39:48 2004
*************** Boston, MA 02111-1307, USA.  */
*** 280,287 ****
      (FS)->regs.reg[7].loc.offset = (long)&sc_->edi - new_cfa_;		\
      (FS)->regs.reg[5].how = REG_SAVED_OFFSET;				\
      (FS)->regs.reg[5].loc.offset = (long)&sc_->ebp - new_cfa_;		\
      (FS)->regs.reg[8].how = REG_SAVED_OFFSET;				\
!     (FS)->regs.reg[8].loc.offset = (long)&sc_->eip - new_cfa_;		\
      (FS)->retaddr_column = 8;						\
      goto SUCCESS;							\
    } while (0)
--- 280,301 ----
      (FS)->regs.reg[7].loc.offset = (long)&sc_->edi - new_cfa_;		\
      (FS)->regs.reg[5].how = REG_SAVED_OFFSET;				\
      (FS)->regs.reg[5].loc.offset = (long)&sc_->ebp - new_cfa_;		\
+                                                                         \
+     /* The instruction pointer we have at hand in the signal context	\
+        is the one which should be checked for EH region ownership	\
+        later on.  The generic dwarf2 unwinding circuitry considers the	\
+        retaddr_colum as a call return address, however, and adjusts	\
+        its value by one to see what region the corresponding call	\
+        instruction pertains to. We need to compensate that adjustment	\
+        to avoid troubles with signals triggered by the very first	\
+        instruction of a region. Directly adjusting the sigcontext's ip	\
+        does not work because we may be called more than once for the	\
+        same signal event, so we hijack a context entry for that		\
+        purpose.  */							\
+     sc_->err = sc_->eip + 1;						\
+                                                                         \
      (FS)->regs.reg[8].how = REG_SAVED_OFFSET;				\
!     (FS)->regs.reg[8].loc.offset = (long)&sc_->err - new_cfa_;		\
      (FS)->retaddr_column = 8;						\
      goto SUCCESS;							\
    } while (0)












Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]