Fix issue #15778: GDB Aarch64 signal frame unwinder issue
authorHui Zhu <teawater@gmail.com>
Tue, 20 May 2014 05:19:06 +0000 (13:19 +0800)
committerHui Zhu <teawater@gmail.com>
Tue, 20 May 2014 05:19:06 +0000 (13:19 +0800)
commitf2205de0080d999c9b67872c9db471c31b53e378
treeaf786f02ac9cb46069e2367e270130f5637ab8ab
parentcdf2a8b762199f2fe7b2bd0230bb755296867c69
Fix issue #15778: GDB Aarch64 signal frame unwinder issue

The root cause of this issue is unwinder of "#3  <signal handler called>"
doesn't supply right values of registers.
When GDB want to get the previous frame of "#3  <signal handler called>",
it will call cache init function of unwinder "aarch64_linux_sigframe_init".
The address or the value of the registers is get from this function.
So the bug is inside thie function.

I check the asm code of "#3  <signal handler called>":
(gdb) frame 3
(gdb) p $pc
$1 = (void (*)()) 0x7f931fa4d0
(gdb) disassemble $pc, +10
Dump of assembler code from 0x7f931fa4d0 to 0x7f931fa4da:
=> 0x0000007f931fa4d0: mov x8, #0x8b                   // #139
   0x0000007f931fa4d4: svc #0x0
   0x0000007f931fa4d8: nop

This is the syscall sys_rt_sigreturn, Linux kernel function "restore_sigframe"
will set the frame:
for (i = 0; i < 31; i++)
__get_user_error(regs->regs[i], &sf->uc.uc_mcontext.regs[i],
 err);
__get_user_error(regs->sp, &sf->uc.uc_mcontext.sp, err);
__get_user_error(regs->pc, &sf->uc.uc_mcontext.pc, err);
The struct of uc_mcontext is:
struct sigcontext {
__u64 fault_address;
/* AArch64 registers */
__u64 regs[31];
__u64 sp;
__u64 pc;
__u64 pstate;
/* 4K reserved for FP/SIMD state and future expansion */
__u8 __reserved[4096] __attribute__((__aligned__(16)));
};

But in GDB function "aarch64_linux_sigframe_init", the code the get address
of registers is:
  for (i = 0; i < 31; i++)
    {
      trad_frame_set_reg_addr (this_cache,
       AARCH64_X0_REGNUM + i,
       sigcontext_addr + AARCH64_SIGCONTEXT_XO_OFFSET
       + i * AARCH64_SIGCONTEXT_REG_SIZE);
    }

  trad_frame_set_reg_addr (this_cache, AARCH64_FP_REGNUM, fp);
  trad_frame_set_reg_addr (this_cache, AARCH64_LR_REGNUM, fp + 8);
  trad_frame_set_reg_addr (this_cache, AARCH64_PC_REGNUM, fp + 8);

The code that get pc and sp is not right, so I change the code according
to Linux kernel code:
  trad_frame_set_reg_addr (this_cache, AARCH64_SP_REGNUM,
   sigcontext_addr + AARCH64_SIGCONTEXT_XO_OFFSET
     + 31 * AARCH64_SIGCONTEXT_REG_SIZE);
  trad_frame_set_reg_addr (this_cache, AARCH64_PC_REGNUM,
   sigcontext_addr + AARCH64_SIGCONTEXT_XO_OFFSET
     + 32 * AARCH64_SIGCONTEXT_REG_SIZE);

The issue was fixed by this change, and I did the regression test.  It
also fixed a lot of other XFAIL and FAIL.

2014-05-20  Hui Zhu  <hui@codesourcery.com>
    Yao Qi  <yao@codesourcery.com>

PR backtrace/16558
* aarch64-linux-tdep.c (aarch64_linux_sigframe_init): Update comments
and change address of sp and pc.
gdb/ChangeLog
gdb/aarch64-linux-tdep.c