discuss-gnustep
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

eh_personality.c in libobjc2


From: Mathias Bauer
Subject: eh_personality.c in libobjc2
Date: Wed, 05 Mar 2014 14:52:53 +0100
User-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:24.0) Gecko/20100101 Thunderbird/24.3.0

Hi dear list members,

as already reported here, libobjc2 and the latest exception handling produced by clang 3.5pre builds an ARM don't work together well. In all but trivial cases it ends up in an endless loop.

I investigated the unwinder code in gcc for both ARM and DWARF EHABI. From what I saw there and from what I see in the personality routine of libobjc2, I see a possible reason why this doesn't go together well.

OTOH I assume that exception handling on ARM usually works on gcc builds with libobjc2 (does it?), so I'm unsure how to align that with my findings. Maybe myfindings are wrong, so if someone that better than me, please help me understanding that better.

Here's what I believe is going on:

The ARM EHABI spec states:
(http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf)

Finally the unwinder calls the PR, passing state
_US_VIRTUAL_UNWIND_FRAME, the UCB pointer, and an _Unwind_Context
pointer for VRS access.
The PR must discover whether this frame contains a propagation
barrier to the exception object, by examining the EHT entry, pointed
to from the UCB pr_cache. It must also adjust the VRS as necessary by
calling functions in the language-independent unwinding library. It
returns to _Unwind_RaiseException with one of:
- Barrier found (_URC_HANDLER_FOUND)
- No barrier (_URC_CONTINUE_UNWIND)
- Error (_URC_FAILURE)

Consequently, this is what the ARM unwinder does (unwind-arm.c in gcc):

  /* Unwind until we reach a propagation barrier.  */
  do
    {
      /* Find the entry for this routine.  */
      if (get_eit_entry (ucbp, saved_vrs.core.r[R_PC]) != _URC_OK)
        return _URC_FAILURE;

      /* Call the pr to decide what to do.  */
      pr_result = ((personality_routine) UCB_PR_ADDR (ucbp))
        (_US_VIRTUAL_UNWIND_FRAME, ucbp, (void *) &saved_vrs);
    }
  while (pr_result == _URC_CONTINUE_UNWIND);

So what happens in the personality routine? (eh_personality.c, function "internal_objc_personality"):

The first thing that the PR does wrong is that it does not examine the whole frames' data, only the current context. If that is a cleanup, the PR will stop there and return _URC_CONTINUE_UNWIND. But according to the spec it should check the whole frame for a "barrier" (a catch handler) and return _URC_HANDLER_FOUND if it found one.

Only if the frame does not contain such a handler, the return code _URC_CONTINUE_UNWIND would be correct, but in that case the personality routine had to do something more, as stated in the ARM EHABI spec:

_URC_CONTINUE_UNWIND indicates that no applicable propagation barrier
was found in the function. Before returning, the PR is required to
have done a virtual unwind by updating the VRS to reflect the machine
state at the call to the current function.

But the PR in eh_personality.c does not do that.

So in case the current context points to a cleanup, neither the unwinder nor the personality routine will change any data. The PR will return _URC_CONTINUE_UNWIND and will be called by the unwinder again and again with the same input data. It's obvious that this will create an endless loop.

The PR obviously does only what it needs to support the DWARF unwinder:
(http://mentorembedded.github.io/cxx-abi/abi-eh.html)

In the search phase, the framework repeatedly calls the personality
routine, with the _UA_SEARCH_PHASE flag as described below, first for
the current PC and register state, and then unwinding a frame to a
new PC at each step, until the personality routine reports either
success (a handler found in the queried frame) or failure (no
handler) in all frames.

So the DWARF unwinder does this (undwind.inc in gcc):

  /* Phase 1: Search.  Unwind the stack, calling the personality routine
     with the _UA_SEARCH_PHASE flag set.  Do not modify the stack yet.  */
  while (1)
    {
      _Unwind_FrameState fs;

      /* Set up fs to describe the FDE for the caller of cur_context.  The
         first time through the loop, that means __cxa_throw.  */
      code = uw_frame_state_for (&cur_context, &fs);

      if (code == _URC_END_OF_STACK)
        /* Hit end of stack with no handler found.  */
        return _URC_END_OF_STACK;

      if (code != _URC_NO_REASON)
        /* Some error encountered.  Usually the unwinder doesn't
           diagnose these and merely crashes.  */
        return _URC_FATAL_PHASE1_ERROR;

      /* Unwind successful.  Run the personality routine, if any.  */
      if (fs.personality)
        {
          code = (*fs.personality) (1, _UA_SEARCH_PHASE, exc->exception_class,
                                    exc, &cur_context);
          if (code == _URC_HANDLER_FOUND)
            break;
          else if (code != _URC_CONTINUE_UNWIND)
            return _URC_FATAL_PHASE1_ERROR;
        }

      /* Update cur_context to describe the same frame as fs.  */
      uw_update_context (&cur_context, &fs);
    }

Now the PR does not need to do anything with the context if it points to a cleanup, as the unwinder takes care of updating the context after each call to the personality routine.

It seems that we have to change the personality routine to support the ARM EHABI spec.

Comments welcome.

Regards,
Mathias



reply via email to

[Prev in Thread] Current Thread [Next in Thread]