1169689Skan/* DWARF2 EH unwinding support for AMD x86-64 and x86.
2169689Skan   Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
3169689Skan
4169689SkanThis file is part of GCC.
5169689Skan
6169689SkanGCC is free software; you can redistribute it and/or modify
7169689Skanit under the terms of the GNU General Public License as published by
8169689Skanthe Free Software Foundation; either version 2, or (at your option)
9169689Skanany later version.
10169689Skan
11169689SkanIn addition to the permissions in the GNU General Public License, the
12169689SkanFree Software Foundation gives you unlimited permission to link the
13169689Skancompiled version of this file with other programs, and to distribute
14169689Skanthose programs without any restriction coming from the use of this
15169689Skanfile.  (The General Public License restrictions do apply in other
16169689Skanrespects; for example, they cover modification of the file, and
17169689Skandistribution when not linked into another program.)
18169689Skan
19169689SkanGCC is distributed in the hope that it will be useful,
20169689Skanbut WITHOUT ANY WARRANTY; without even the implied warranty of
21169689SkanMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22169689SkanGNU General Public License for more details.
23169689Skan
24169689SkanYou should have received a copy of the GNU General Public License
25169689Skanalong with GCC; see the file COPYING.  If not, write to
26169689Skanthe Free Software Foundation, 51 Franklin Street, Fifth Floor,
27169689SkanBoston, MA 02110-1301, USA.  */
28169689Skan
29169689Skan/* Do code reading to identify a signal frame, and set the frame
30169689Skan   state data appropriately.  See unwind-dw2.c for the structs.
31169689Skan   Don't use this at all if inhibit_libc is used.  */
32169689Skan
33169689Skan#ifndef inhibit_libc
34169689Skan
35169689Skan#ifdef __x86_64__
36169689Skan
37169689Skan#include <signal.h>
38169689Skan#include <sys/ucontext.h>
39169689Skan
40169689Skan#define MD_FALLBACK_FRAME_STATE_FOR x86_64_fallback_frame_state
41169689Skan
42169689Skanstatic _Unwind_Reason_Code
43169689Skanx86_64_fallback_frame_state (struct _Unwind_Context *context,
44169689Skan			     _Unwind_FrameState *fs)
45169689Skan{
46169689Skan  unsigned char *pc = context->ra;
47169689Skan  struct sigcontext *sc;
48169689Skan  long new_cfa;
49169689Skan
50169689Skan  /* movq __NR_rt_sigreturn, %rax ; syscall  */
51169689Skan  if (*(unsigned char *)(pc+0) == 0x48
52169689Skan      && *(unsigned long *)(pc+1) == 0x050f0000000fc0c7)
53169689Skan    {
54169689Skan      struct ucontext *uc_ = context->cfa;
55169689Skan      /* The void * cast is necessary to avoid an aliasing warning.
56169689Skan         The aliasing warning is correct, but should not be a problem
57169689Skan         because it does not alias anything.  */
58169689Skan      sc = (struct sigcontext *) (void *) &uc_->uc_mcontext;
59169689Skan    }
60169689Skan  else
61169689Skan    return _URC_END_OF_STACK;
62169689Skan
63169689Skan  new_cfa = sc->rsp;
64169689Skan  fs->cfa_how = CFA_REG_OFFSET;
65169689Skan  /* Register 7 is rsp  */
66169689Skan  fs->cfa_reg = 7;
67169689Skan  fs->cfa_offset = new_cfa - (long) context->cfa;
68169689Skan
69169689Skan  /* The SVR4 register numbering macros aren't usable in libgcc.  */
70169689Skan  fs->regs.reg[0].how = REG_SAVED_OFFSET;
71169689Skan  fs->regs.reg[0].loc.offset = (long)&sc->rax - new_cfa;
72169689Skan  fs->regs.reg[1].how = REG_SAVED_OFFSET;
73169689Skan  fs->regs.reg[1].loc.offset = (long)&sc->rdx - new_cfa;
74169689Skan  fs->regs.reg[2].how = REG_SAVED_OFFSET;
75169689Skan  fs->regs.reg[2].loc.offset = (long)&sc->rcx - new_cfa;
76169689Skan  fs->regs.reg[3].how = REG_SAVED_OFFSET;
77169689Skan  fs->regs.reg[3].loc.offset = (long)&sc->rbx - new_cfa;
78169689Skan  fs->regs.reg[4].how = REG_SAVED_OFFSET;
79169689Skan  fs->regs.reg[4].loc.offset = (long)&sc->rsi - new_cfa;
80169689Skan  fs->regs.reg[5].how = REG_SAVED_OFFSET;
81169689Skan  fs->regs.reg[5].loc.offset = (long)&sc->rdi - new_cfa;
82169689Skan  fs->regs.reg[6].how = REG_SAVED_OFFSET;
83169689Skan  fs->regs.reg[6].loc.offset = (long)&sc->rbp - new_cfa;
84169689Skan  fs->regs.reg[8].how = REG_SAVED_OFFSET;
85169689Skan  fs->regs.reg[8].loc.offset = (long)&sc->r8 - new_cfa;
86169689Skan  fs->regs.reg[9].how = REG_SAVED_OFFSET;
87169689Skan  fs->regs.reg[9].loc.offset = (long)&sc->r9 - new_cfa;
88169689Skan  fs->regs.reg[10].how = REG_SAVED_OFFSET;
89169689Skan  fs->regs.reg[10].loc.offset = (long)&sc->r10 - new_cfa;
90169689Skan  fs->regs.reg[11].how = REG_SAVED_OFFSET;
91169689Skan  fs->regs.reg[11].loc.offset = (long)&sc->r11 - new_cfa;
92169689Skan  fs->regs.reg[12].how = REG_SAVED_OFFSET;
93169689Skan  fs->regs.reg[12].loc.offset = (long)&sc->r12 - new_cfa;
94169689Skan  fs->regs.reg[13].how = REG_SAVED_OFFSET;
95169689Skan  fs->regs.reg[13].loc.offset = (long)&sc->r13 - new_cfa;
96169689Skan  fs->regs.reg[14].how = REG_SAVED_OFFSET;
97169689Skan  fs->regs.reg[14].loc.offset = (long)&sc->r14 - new_cfa;
98169689Skan  fs->regs.reg[15].how = REG_SAVED_OFFSET;
99169689Skan  fs->regs.reg[15].loc.offset = (long)&sc->r15 - new_cfa;
100169689Skan  fs->regs.reg[16].how = REG_SAVED_OFFSET;
101169689Skan  fs->regs.reg[16].loc.offset = (long)&sc->rip - new_cfa;
102169689Skan  fs->retaddr_column = 16;
103169689Skan  fs->signal_frame = 1;
104169689Skan  return _URC_NO_REASON;
105169689Skan}
106169689Skan
107169689Skan#else /* ifdef __x86_64__  */
108169689Skan
109169689Skan/* There's no sys/ucontext.h for glibc 2.0, so no
110169689Skan   signal-turned-exceptions for them.  There's also no configure-run for
111169689Skan   the target, so we can't check on (e.g.) HAVE_SYS_UCONTEXT_H.  Using the
112169689Skan   target libc version macro should be enough.  */
113169689Skan#if !(__GLIBC__ == 2 && __GLIBC_MINOR__ == 0)
114169689Skan
115169689Skan#include <signal.h>
116169689Skan#include <sys/ucontext.h>
117169689Skan
118169689Skan#define MD_FALLBACK_FRAME_STATE_FOR x86_fallback_frame_state
119169689Skan
120169689Skanstatic _Unwind_Reason_Code
121169689Skanx86_fallback_frame_state (struct _Unwind_Context *context,
122169689Skan			  _Unwind_FrameState *fs)
123169689Skan{
124169689Skan  unsigned char *pc = context->ra;
125169689Skan  struct sigcontext *sc;
126169689Skan  long new_cfa;
127169689Skan
128169689Skan  /* popl %eax ; movl $__NR_sigreturn,%eax ; int $0x80  */
129169689Skan  if (*(unsigned short *)(pc+0) == 0xb858
130169689Skan      && *(unsigned int *)(pc+2) == 119
131169689Skan      && *(unsigned short *)(pc+6) == 0x80cd)
132169689Skan    sc = context->cfa + 4;
133169689Skan  /* movl $__NR_rt_sigreturn,%eax ; int $0x80  */
134169689Skan  else if (*(unsigned char *)(pc+0) == 0xb8
135169689Skan	   && *(unsigned int *)(pc+1) == 173
136169689Skan	   && *(unsigned short *)(pc+5) == 0x80cd)
137169689Skan    {
138169689Skan      struct rt_sigframe {
139169689Skan	int sig;
140169689Skan	struct siginfo *pinfo;
141169689Skan	void *puc;
142169689Skan	struct siginfo info;
143169689Skan	struct ucontext uc;
144169689Skan      } *rt_ = context->cfa;
145169689Skan      /* The void * cast is necessary to avoid an aliasing warning.
146169689Skan         The aliasing warning is correct, but should not be a problem
147169689Skan         because it does not alias anything.  */
148169689Skan      sc = (struct sigcontext *) (void *) &rt_->uc.uc_mcontext;
149169689Skan    }
150169689Skan  else
151169689Skan    return _URC_END_OF_STACK;
152169689Skan
153169689Skan  new_cfa = sc->REG_NAME(esp);
154169689Skan  fs->cfa_how = CFA_REG_OFFSET;
155169689Skan  fs->cfa_reg = 4;
156169689Skan  fs->cfa_offset = new_cfa - (long) context->cfa;
157169689Skan
158169689Skan  /* The SVR4 register numbering macros aren't usable in libgcc.  */
159169689Skan  fs->regs.reg[0].how = REG_SAVED_OFFSET;
160169689Skan  fs->regs.reg[0].loc.offset = (long)&sc->REG_NAME(eax) - new_cfa;
161169689Skan  fs->regs.reg[3].how = REG_SAVED_OFFSET;
162169689Skan  fs->regs.reg[3].loc.offset = (long)&sc->REG_NAME(ebx) - new_cfa;
163169689Skan  fs->regs.reg[1].how = REG_SAVED_OFFSET;
164169689Skan  fs->regs.reg[1].loc.offset = (long)&sc->REG_NAME(ecx) - new_cfa;
165169689Skan  fs->regs.reg[2].how = REG_SAVED_OFFSET;
166169689Skan  fs->regs.reg[2].loc.offset = (long)&sc->REG_NAME(edx) - new_cfa;
167169689Skan  fs->regs.reg[6].how = REG_SAVED_OFFSET;
168169689Skan  fs->regs.reg[6].loc.offset = (long)&sc->REG_NAME(esi) - new_cfa;
169169689Skan  fs->regs.reg[7].how = REG_SAVED_OFFSET;
170169689Skan  fs->regs.reg[7].loc.offset = (long)&sc->REG_NAME(edi) - new_cfa;
171169689Skan  fs->regs.reg[5].how = REG_SAVED_OFFSET;
172169689Skan  fs->regs.reg[5].loc.offset = (long)&sc->REG_NAME(ebp) - new_cfa;
173169689Skan  fs->regs.reg[8].how = REG_SAVED_OFFSET;
174169689Skan  fs->regs.reg[8].loc.offset = (long)&sc->REG_NAME(eip) - new_cfa;
175169689Skan  fs->retaddr_column = 8;
176169689Skan  fs->signal_frame = 1;
177169689Skan  return _URC_NO_REASON;
178169689Skan}
179169689Skan#endif /* not glibc 2.0 */
180169689Skan#endif /* ifdef __x86_64__  */
181169689Skan#endif /* ifdef inhibit_libc  */
182