1227825Stheraven/* DWARF2 EH unwinding support for AMD x86-64 and x86.
2227825Stheraven   Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
3227825Stheraven
4227825StheravenThis file is part of GCC.
5227825Stheraven
6227825StheravenGCC is free software; you can redistribute it and/or modify
7227825Stheravenit under the terms of the GNU General Public License as published by
8227825Stheraventhe Free Software Foundation; either version 2, or (at your option)
9227825Stheravenany later version.
10227825Stheraven
11227825StheravenIn addition to the permissions in the GNU General Public License, the
12227825StheravenFree Software Foundation gives you unlimited permission to link the
13227825Stheravencompiled version of this file with other programs, and to distribute
14227825Stheraventhose programs without any restriction coming from the use of this
15227825Stheravenfile.  (The General Public License restrictions do apply in other
16227825Stheravenrespects; for example, they cover modification of the file, and
17227825Stheravendistribution when not linked into another program.)
18227825Stheraven
19227825StheravenGCC is distributed in the hope that it will be useful,
20227825Stheravenbut WITHOUT ANY WARRANTY; without even the implied warranty of
21227825StheravenMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22227825StheravenGNU General Public License for more details.
23227825Stheraven
24227825StheravenYou should have received a copy of the GNU General Public License
25227825Stheravenalong with GCC; see the file COPYING.  If not, write to
26227825Stheraventhe Free Software Foundation, 51 Franklin Street, Fifth Floor,
27227825StheravenBoston, MA 02110-1301, USA.  */
28227825Stheraven
29227825Stheraven/* Do code reading to identify a signal frame, and set the frame
30227825Stheraven   state data appropriately.  See unwind-dw2.c for the structs.
31227825Stheraven   Don't use this at all if inhibit_libc is used.  */
32227825Stheraven
33227825Stheraven#ifndef inhibit_libc
34227825Stheraven
35227825Stheraven#ifdef __x86_64__
36227825Stheraven
37227825Stheraven#include <signal.h>
38227825Stheraven#include <sys/ucontext.h>
39227825Stheraven
40227825Stheraven#define MD_FALLBACK_FRAME_STATE_FOR x86_64_fallback_frame_state
41227825Stheraven
42227825Stheravenstatic _Unwind_Reason_Code
43227825Stheravenx86_64_fallback_frame_state (struct _Unwind_Context *context,
44227825Stheraven			     _Unwind_FrameState *fs)
45227825Stheraven{
46227825Stheraven  unsigned char *pc = context->ra;
47227825Stheraven  struct sigcontext *sc;
48227825Stheraven  long new_cfa;
49227825Stheraven
50227825Stheraven  /* movq __NR_rt_sigreturn, %rax ; syscall  */
51227825Stheraven  if (*(unsigned char *)(pc+0) == 0x48
52227825Stheraven      && *(unsigned long *)(pc+1) == 0x050f0000000fc0c7)
53227825Stheraven    {
54227825Stheraven      struct ucontext *uc_ = context->cfa;
55227825Stheraven      /* The void * cast is necessary to avoid an aliasing warning.
56227825Stheraven         The aliasing warning is correct, but should not be a problem
57227825Stheraven         because it does not alias anything.  */
58227825Stheraven      sc = (struct sigcontext *) (void *) &uc_->uc_mcontext;
59227825Stheraven    }
60227825Stheraven  else
61227825Stheraven    return _URC_END_OF_STACK;
62227825Stheraven
63227825Stheraven  new_cfa = sc->rsp;
64227825Stheraven  fs->cfa_how = CFA_REG_OFFSET;
65227825Stheraven  /* Register 7 is rsp  */
66227825Stheraven  fs->cfa_reg = 7;
67227825Stheraven  fs->cfa_offset = new_cfa - (long) context->cfa;
68227825Stheraven
69227825Stheraven  /* The SVR4 register numbering macros aren't usable in libgcc.  */
70227825Stheraven  fs->regs.reg[0].how = REG_SAVED_OFFSET;
71227825Stheraven  fs->regs.reg[0].loc.offset = (long)&sc->rax - new_cfa;
72227825Stheraven  fs->regs.reg[1].how = REG_SAVED_OFFSET;
73227825Stheraven  fs->regs.reg[1].loc.offset = (long)&sc->rdx - new_cfa;
74227825Stheraven  fs->regs.reg[2].how = REG_SAVED_OFFSET;
75227825Stheraven  fs->regs.reg[2].loc.offset = (long)&sc->rcx - new_cfa;
76227825Stheraven  fs->regs.reg[3].how = REG_SAVED_OFFSET;
77227825Stheraven  fs->regs.reg[3].loc.offset = (long)&sc->rbx - new_cfa;
78227825Stheraven  fs->regs.reg[4].how = REG_SAVED_OFFSET;
79227825Stheraven  fs->regs.reg[4].loc.offset = (long)&sc->rsi - new_cfa;
80227825Stheraven  fs->regs.reg[5].how = REG_SAVED_OFFSET;
81227825Stheraven  fs->regs.reg[5].loc.offset = (long)&sc->rdi - new_cfa;
82227825Stheraven  fs->regs.reg[6].how = REG_SAVED_OFFSET;
83227825Stheraven  fs->regs.reg[6].loc.offset = (long)&sc->rbp - new_cfa;
84227825Stheraven  fs->regs.reg[8].how = REG_SAVED_OFFSET;
85227825Stheraven  fs->regs.reg[8].loc.offset = (long)&sc->r8 - new_cfa;
86227825Stheraven  fs->regs.reg[9].how = REG_SAVED_OFFSET;
87227825Stheraven  fs->regs.reg[9].loc.offset = (long)&sc->r9 - new_cfa;
88227825Stheraven  fs->regs.reg[10].how = REG_SAVED_OFFSET;
89227825Stheraven  fs->regs.reg[10].loc.offset = (long)&sc->r10 - new_cfa;
90227825Stheraven  fs->regs.reg[11].how = REG_SAVED_OFFSET;
91227825Stheraven  fs->regs.reg[11].loc.offset = (long)&sc->r11 - new_cfa;
92227825Stheraven  fs->regs.reg[12].how = REG_SAVED_OFFSET;
93227825Stheraven  fs->regs.reg[12].loc.offset = (long)&sc->r12 - new_cfa;
94227825Stheraven  fs->regs.reg[13].how = REG_SAVED_OFFSET;
95227825Stheraven  fs->regs.reg[13].loc.offset = (long)&sc->r13 - new_cfa;
96227825Stheraven  fs->regs.reg[14].how = REG_SAVED_OFFSET;
97227825Stheraven  fs->regs.reg[14].loc.offset = (long)&sc->r14 - new_cfa;
98227825Stheraven  fs->regs.reg[15].how = REG_SAVED_OFFSET;
99227825Stheraven  fs->regs.reg[15].loc.offset = (long)&sc->r15 - new_cfa;
100227825Stheraven  fs->regs.reg[16].how = REG_SAVED_OFFSET;
101227825Stheraven  fs->regs.reg[16].loc.offset = (long)&sc->rip - new_cfa;
102227825Stheraven  fs->retaddr_column = 16;
103227825Stheraven  fs->signal_frame = 1;
104227825Stheraven  return _URC_NO_REASON;
105227825Stheraven}
106227825Stheraven
107227825Stheraven#else /* ifdef __x86_64__  */
108227825Stheraven
109227825Stheraven/* There's no sys/ucontext.h for glibc 2.0, so no
110227825Stheraven   signal-turned-exceptions for them.  There's also no configure-run for
111227825Stheraven   the target, so we can't check on (e.g.) HAVE_SYS_UCONTEXT_H.  Using the
112227825Stheraven   target libc version macro should be enough.  */
113227825Stheraven#if !(__GLIBC__ == 2 && __GLIBC_MINOR__ == 0)
114227825Stheraven
115227825Stheraven#include <signal.h>
116227825Stheraven#include <sys/ucontext.h>
117227825Stheraven
118227825Stheraven#define MD_FALLBACK_FRAME_STATE_FOR x86_fallback_frame_state
119227825Stheraven
120227825Stheravenstatic _Unwind_Reason_Code
121227825Stheravenx86_fallback_frame_state (struct _Unwind_Context *context,
122227825Stheraven			  _Unwind_FrameState *fs)
123227825Stheraven{
124227825Stheraven  unsigned char *pc = context->ra;
125227825Stheraven  struct sigcontext *sc;
126227825Stheraven  long new_cfa;
127227825Stheraven
128227825Stheraven  /* popl %eax ; movl $__NR_sigreturn,%eax ; int $0x80  */
129227825Stheraven  if (*(unsigned short *)(pc+0) == 0xb858
130227825Stheraven      && *(unsigned int *)(pc+2) == 119
131227825Stheraven      && *(unsigned short *)(pc+6) == 0x80cd)
132227825Stheraven    sc = context->cfa + 4;
133227825Stheraven  /* movl $__NR_rt_sigreturn,%eax ; int $0x80  */
134227825Stheraven  else if (*(unsigned char *)(pc+0) == 0xb8
135227825Stheraven	   && *(unsigned int *)(pc+1) == 173
136227825Stheraven	   && *(unsigned short *)(pc+5) == 0x80cd)
137227825Stheraven    {
138227825Stheraven      struct rt_sigframe {
139227825Stheraven	int sig;
140227825Stheraven	struct siginfo *pinfo;
141227825Stheraven	void *puc;
142227825Stheraven	struct siginfo info;
143227825Stheraven	struct ucontext uc;
144227825Stheraven      } *rt_ = context->cfa;
145227825Stheraven      /* The void * cast is necessary to avoid an aliasing warning.
146227825Stheraven         The aliasing warning is correct, but should not be a problem
147227825Stheraven         because it does not alias anything.  */
148227825Stheraven      sc = (struct sigcontext *) (void *) &rt_->uc.uc_mcontext;
149227825Stheraven    }
150227825Stheraven  else
151227825Stheraven    return _URC_END_OF_STACK;
152227825Stheraven
153227825Stheraven  new_cfa = sc->REG_NAME(esp);
154227825Stheraven  fs->cfa_how = CFA_REG_OFFSET;
155227825Stheraven  fs->cfa_reg = 4;
156227825Stheraven  fs->cfa_offset = new_cfa - (long) context->cfa;
157227825Stheraven
158227825Stheraven  /* The SVR4 register numbering macros aren't usable in libgcc.  */
159227825Stheraven  fs->regs.reg[0].how = REG_SAVED_OFFSET;
160227825Stheraven  fs->regs.reg[0].loc.offset = (long)&sc->REG_NAME(eax) - new_cfa;
161227825Stheraven  fs->regs.reg[3].how = REG_SAVED_OFFSET;
162227825Stheraven  fs->regs.reg[3].loc.offset = (long)&sc->REG_NAME(ebx) - new_cfa;
163227825Stheraven  fs->regs.reg[1].how = REG_SAVED_OFFSET;
164227825Stheraven  fs->regs.reg[1].loc.offset = (long)&sc->REG_NAME(ecx) - new_cfa;
165227825Stheraven  fs->regs.reg[2].how = REG_SAVED_OFFSET;
166227825Stheraven  fs->regs.reg[2].loc.offset = (long)&sc->REG_NAME(edx) - new_cfa;
167227825Stheraven  fs->regs.reg[6].how = REG_SAVED_OFFSET;
168227825Stheraven  fs->regs.reg[6].loc.offset = (long)&sc->REG_NAME(esi) - new_cfa;
169227825Stheraven  fs->regs.reg[7].how = REG_SAVED_OFFSET;
170227825Stheraven  fs->regs.reg[7].loc.offset = (long)&sc->REG_NAME(edi) - new_cfa;
171227825Stheraven  fs->regs.reg[5].how = REG_SAVED_OFFSET;
172227825Stheraven  fs->regs.reg[5].loc.offset = (long)&sc->REG_NAME(ebp) - new_cfa;
173227825Stheraven  fs->regs.reg[8].how = REG_SAVED_OFFSET;
174227825Stheraven  fs->regs.reg[8].loc.offset = (long)&sc->REG_NAME(eip) - new_cfa;
175227825Stheraven  fs->retaddr_column = 8;
176227825Stheraven  fs->signal_frame = 1;
177227825Stheraven  return _URC_NO_REASON;
178227825Stheraven}
179227825Stheraven#endif /* not glibc 2.0 */
180227825Stheraven#endif /* ifdef __x86_64__  */
181227825Stheraven#endif /* ifdef inhibit_libc  */
182227825Stheraven