mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			165 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			165 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
	
| /* DWARF2 EH unwinding support for PA Linux.
 | |
|    Copyright (C) 2004-2016 Free Software Foundation, Inc.
 | |
| 
 | |
| This file is part of GCC.
 | |
| 
 | |
| GCC is free software; you can redistribute it and/or modify
 | |
| it under the terms of the GNU General Public License as published by
 | |
| the Free Software Foundation; either version 3, or (at your option)
 | |
| any later version.
 | |
| 
 | |
| GCC is distributed in the hope that it will be useful,
 | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| GNU General Public License for more details.
 | |
| 
 | |
| Under Section 7 of GPL version 3, you are granted additional
 | |
| permissions described in the GCC Runtime Library Exception, version
 | |
| 3.1, as published by the Free Software Foundation.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License and
 | |
| a copy of the GCC Runtime Library Exception along with this program;
 | |
| see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 | |
| <http://www.gnu.org/licenses/>.  */
 | |
| 
 | |
| 
 | |
| /* Do code reading to identify a signal frame, and set the frame
 | |
|    state data appropriately.  See unwind-dw2.c for the structs.  */
 | |
| 
 | |
| /* Don't use this if inhibit_libc is set.
 | |
|    The build for this target will fail trying to include missing headers. */
 | |
| #ifndef inhibit_libc
 | |
| #include <signal.h>
 | |
| #include <sys/ucontext.h>
 | |
| 
 | |
| /* Return TRUE if read access to *P is allowed.  */
 | |
| 
 | |
| static inline long
 | |
| pa32_read_access_ok (void *p)
 | |
| {
 | |
|   long ret;
 | |
| 
 | |
|   __asm__ ("proberi (%1),3,%0" : "=r" (ret) : "r" (p) :);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| /* Unfortunately, because of various bugs and changes to the kernel,
 | |
|    we have several cases to deal with.
 | |
| 
 | |
|    In 2.4, the signal trampoline is 4 words, and (CONTEXT)->ra should
 | |
|    point directly at the beginning of the trampoline and struct rt_sigframe.
 | |
| 
 | |
|    In <= 2.6.5-rc2-pa3, the signal trampoline is 9 words, and 
 | |
|    (CONTEXT)->ra points at the 4th word in the trampoline structure.  This 
 | |
|    is wrong, it should point at the 5th word.  This is fixed in 2.6.5-rc2-pa4.
 | |
| 
 | |
|    To detect these cases, we first take (CONTEXT)->ra, align it to 64-bytes
 | |
|    to get the beginning of the signal frame, and then check offsets 0, 4
 | |
|    and 5 to see if we found the beginning of the trampoline.  This will
 | |
|    tell us how to locate the sigcontext structure.
 | |
| 
 | |
|    Note that with a 2.4 64-bit kernel, the signal context is not properly
 | |
|    passed back to userspace so the unwind will not work correctly.
 | |
| 
 | |
|    There is also a bug in various glibc versions.  The (CONTEXT)->ra
 | |
|    for the outermost frame is not marked as undefined, so we need to
 | |
|    check whether read access is allowed for all the accesses used in
 | |
|    searching for the signal trampoline.  */
 | |
| 
 | |
| #define MD_FALLBACK_FRAME_STATE_FOR pa32_fallback_frame_state
 | |
| 
 | |
| static _Unwind_Reason_Code
 | |
| pa32_fallback_frame_state (struct _Unwind_Context *context,
 | |
| 			   _Unwind_FrameState *fs)
 | |
| {
 | |
|   unsigned long sp = (unsigned long)context->ra & ~63;
 | |
|   unsigned int *pc = (unsigned int *)sp;
 | |
|   unsigned long off;
 | |
|   _Unwind_Ptr new_cfa;
 | |
|   int i;
 | |
|   struct sigcontext *sc;
 | |
|   struct rt_sigframe {
 | |
|     siginfo_t info;
 | |
|     struct ucontext uc;
 | |
|   } *frame;
 | |
| 
 | |
|   /* rt_sigreturn trampoline:
 | |
|      3419000x ldi 0, %r25 or ldi 1, %r25   (x = 0 or 2)
 | |
|      3414015a ldi __NR_rt_sigreturn, %r20
 | |
|      e4008200 be,l 0x100(%sr2, %r0), %sr0, %r31
 | |
|      08000240 nop  */
 | |
| 
 | |
|   if (pa32_read_access_ok (pc)
 | |
|       && (pc[0] == 0x34190000 || pc[0] == 0x34190002))
 | |
|     off = 4*4;
 | |
|   else if (pa32_read_access_ok (&pc[4])
 | |
| 	   && (pc[4] == 0x34190000 || pc[4] == 0x34190002))
 | |
|     {
 | |
|       pc += 4;
 | |
|       off = 10 * 4;
 | |
|     }
 | |
|   else if (pa32_read_access_ok (&pc[5])
 | |
| 	   && (pc[5] == 0x34190000 || pc[5] == 0x34190002))
 | |
|     {
 | |
|       pc += 5;
 | |
|       off = 10 * 4;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       /* We may have to unwind through an alternate signal stack.
 | |
| 	 We assume that the alignment of the alternate signal stack
 | |
| 	 is BIGGEST_ALIGNMENT (i.e., that it has been allocated using
 | |
| 	 malloc).  As a result, we can't distinguish trampolines
 | |
| 	 used prior to 2.6.5-rc2-pa4.  However after 2.6.5-rc2-pa4,
 | |
| 	 the return address of a signal trampoline will be on an odd
 | |
| 	 word boundary and we can then determine the frame offset.  */
 | |
|       sp = (unsigned long)context->ra;
 | |
|       pc = (unsigned int *)sp;
 | |
|       if ((sp & 4)
 | |
| 	  && pa32_read_access_ok (pc)
 | |
| 	  && (pc[0] == 0x34190000 || pc[0] == 0x34190002))
 | |
| 	off = 5 * 4;
 | |
|       else
 | |
| 	return _URC_END_OF_STACK;
 | |
|     }
 | |
| 
 | |
|   if (!pa32_read_access_ok (&pc[3])
 | |
|       || pc[1] != 0x3414015a
 | |
|       || pc[2] != 0xe4008200
 | |
|       || pc[3] != 0x08000240)
 | |
|     return _URC_END_OF_STACK;
 | |
| 
 | |
|   frame = (struct rt_sigframe *)(sp + off);
 | |
|   sc = &frame->uc.uc_mcontext;
 | |
| 
 | |
|   new_cfa = sc->sc_gr[30];
 | |
|   fs->regs.cfa_how = CFA_REG_OFFSET;
 | |
|   fs->regs.cfa_reg = 30;
 | |
|   fs->regs.cfa_offset = new_cfa - (long) context->cfa;
 | |
|   for (i = 1; i <= 31; i++)
 | |
|     {
 | |
|       fs->regs.reg[i].how = REG_SAVED_OFFSET;
 | |
|       fs->regs.reg[i].loc.offset = (long)&sc->sc_gr[i] - new_cfa;
 | |
|     }
 | |
|   for (i = 4; i <= 31; i++)
 | |
|     {
 | |
|       /* FP regs have left and right halves */
 | |
|       fs->regs.reg[2*i+24].how = REG_SAVED_OFFSET;
 | |
|       fs->regs.reg[2*i+24].loc.offset
 | |
| 	= (long)&sc->sc_fr[i] - new_cfa;
 | |
|       fs->regs.reg[2*i+24+1].how = REG_SAVED_OFFSET;
 | |
|       fs->regs.reg[2*i+24+1].loc.offset
 | |
| 	= (long)&sc->sc_fr[i] + 4 - new_cfa;
 | |
|     }
 | |
|   fs->regs.reg[88].how = REG_SAVED_OFFSET;
 | |
|   fs->regs.reg[88].loc.offset = (long) &sc->sc_sar - new_cfa;
 | |
|   fs->regs.reg[__LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__].how
 | |
|     = REG_SAVED_OFFSET;
 | |
|   fs->regs.reg[__LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__].loc.offset
 | |
|     = (long) &sc->sc_iaoq[0] - new_cfa;
 | |
|   fs->retaddr_column = __LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__;
 | |
|   fs->signal_frame = 1;
 | |
|   return _URC_NO_REASON;
 | |
| }
 | |
| #endif /* inhibit_libc */
 |