The symptom is that if you segfault and then throw an exception in the
segfault handler call-saved fields in the Condition Register are
corrupted.

The reason is that the unwinder data for CR in the vDSO is wrong.  The
line that affects the CR is here in
arch/powerpc/kernel/vdso64/sigtramp.S:

  rsave (70, 38*RSIZE)          /* cr */

This restores a 64-bit register from offset 38 in the sigcontext
register save area to DWARF Column 70.  This much is correct...

Unfortunately, gcc saves and restores only the *least significant*
32-bit half of CR on the stack.  As this is a big-endian machine, the
result is that when unwinding __kernel_sigtramp_rt64 the correctly
saved CR is written to the upper half of the word on the stack, not
the lower half, and the saved CR is overwritten with zeroes.

It is not immediately clear to me how to fix this: I think you would
need to find a DWARF expression that copies a halfword value.

Test case attached.  Tested on Kernel 2.6.18-8.1.10.el5.

Andrew.



#include <signal.h>
#include <cstddef>
#include <cstdio>

class SegfaultException
{
};

void catch_segv (int)
{
  throw new SegfaultException;
}

void
segfault (int *p)
{
  fprintf (stderr, "%n", *p);
}


int main(int argc, char **argv)
{
  unsigned long cr, cr2;
  __asm__ __volatile__
    ("mtcrf   8, %0" : : "r" (0x2000): "cr4");

  struct sigaction sa;
  sa.sa_handler = catch_segv;
  sigemptyset (&sa.sa_mask);
  sa.sa_flags = SA_NODEFER;
  sigaction (SIGSEGV, &sa, NULL);

  __asm__ __volatile__
    ("mfcr %0" : "=r" (cr));
  fprintf (stderr, "cr = 0x%x\n", cr & 0xfff000);

  try
    {
      segfault(NULL);
    }
  catch (SegfaultException *a)
    {
    }

  __asm__ __volatile__
    ("mfcr %0" : "=r" (cr));
  fprintf (stderr, "cr = 0x%x\n", cr & 0xfff000);

  return 0;
}

Reply via email to