I'm showing my ignorance here, but couldn't find an example of something
similar on another RISCy target, so here goes...

My latest round of testing showed these failures for MIPS16:

FAIL: 21_strings/basic_string/numeric_conversions/char/stod.cc execution test
FAIL: 21_strings/basic_string/numeric_conversions/char/stof.cc execution test
FAIL: 21_strings/basic_string/numeric_conversions/char/stold.cc execution test
FAIL: 21_strings/basic_string/numeric_conversions/wchar_t/stod.cc execution test
FAIL: 21_strings/basic_string/numeric_conversions/wchar_t/stof.cc execution test
FAIL: 21_strings/basic_string/numeric_conversions/wchar_t/stold.cc execution 
test

I've no idea why they're suddenly showing up now, since the problem has
been around forever.  A depressingly trivial testcase is:

  #include <stdexcept>

  double
  foo (void)
  {
    throw std::out_of_range ("foo");
  }

  double (*volatile foo_ptr) (void) = foo;

  int
  main (void)
  {
    try { foo_ptr (); } catch (...) {}
    return 0;
  }

which triggers:

  terminate called after throwing an instance of 'std::out_of_range'
    what():  foo
  Aborted

The call to foo_ptr goes through a stub that moves the return value of
the real foo from floating-point to general registers.  Rather than
create a frame and save the return address on the stack, the stub
instead stores it in _call-saved_ register $18.  The MIPS16 caller
is aware of this special exception and makes sure to save $18 itself
where necessary.

(I don't know whether this was supposed to be a time or a space saving,
but there will obviously be cases where it makes code bigger.)

libgcc has stubs for indirect calls, while gcc generates wrappers for
direct calls.  The latter are only pulled in when needed.

The first (predictable) problem is that these stubs lack cfi info.
I first tried to fix this for libgcc by adding the cfi markup below:

#define CALL_STUB_RET(NAME, CODE, MODE) \
STARTFN (NAME);                         \
        .cfi_startproc;                 \
        move    $18,$31;                \
        .cfi_register 31,18;            \
        STUB_ARGS_##CODE;               \
        .set    noreorder;              \
        jalr    $2;                     \
        move    $25,$2;                 \
        .set    reorder;                \
        MOVE_##MODE##_RET (f, $18);     \
        .cfi_endproc;                   \
        ENDFN (NAME)

This fixes gdb backtraces from a stripped executable, but libgcc's
unwinder seems to get confused by having a stub in the middle of the
trace that (a) isn't a signal handler and (b) has no frame of its own.
The CFA at the point of the call to the stub (i.e. the point that is
trying to catch the exception) is the same as the CFA at the call
in the stub itself.  We then trip:

      /* Don't let us unwind past the handler context.  */
      gcc_assert (!match_handler);

in _Unwind_RaiseException_Phase2.  What's the right thing to do here?

Thanks,
Richard

Reply via email to