Imported Upstream version 1.3.1
[platform/upstream/libunwind.git] / src / x86 / Gos-freebsd.c
1 /* libunwind - a platform-independent unwind library
2    Copyright (C) 2010 Konstantin Belousov <kib@freebsd.org>
3
4 This file is part of libunwind.
5
6 Permission is hereby granted, free of charge, to any person obtaining
7 a copy of this software and associated documentation files (the
8 "Software"), to deal in the Software without restriction, including
9 without limitation the rights to use, copy, modify, merge, publish,
10 distribute, sublicense, and/or sell copies of the Software, and to
11 permit persons to whom the Software is furnished to do so, subject to
12 the following conditions:
13
14 The above copyright notice and this permission notice shall be
15 included in all copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include <sys/types.h>
30 #include <signal.h>
31 #include <stddef.h>
32 #include <ucontext.h>
33 #include <machine/sigframe.h>
34
35 #include "unwind_i.h"
36 #include "offsets.h"
37
38 int
39 unw_is_signal_frame (unw_cursor_t *cursor)
40 {
41   struct cursor *c = (struct cursor *) cursor;
42   unw_word_t w0, w1, w2, w3, w4, w5, ip;
43   unw_addr_space_t as;
44   unw_accessors_t *a;
45   void *arg;
46   int ret;
47
48   as = c->dwarf.as;
49   a = unw_get_accessors_int (as);
50   arg = c->dwarf.as_arg;
51
52   /* Check if EIP points at sigreturn() sequence.  It can be:
53 sigcode+4: from amd64 freebsd32 environment
54 8d 44 24 20             lea    0x20(%esp),%eax
55 50                      push   %eax
56 b8 a1 01 00 00          mov    $0x1a1,%eax
57 50                      push   %eax
58 cd 80                   int    $0x80
59
60 sigcode+4: from real i386
61 8d 44 24 20             lea    0x20(%esp),%eax
62 50                      push   %eax
63 f7 40 54 00 02 00       testl  $0x20000,0x54(%eax)
64 75 03                   jne    sigcode+21
65 8e 68 14                mov    0x14(%eax),%gs
66 b8 a1 01 00 00          mov    $0x1a1,%eax
67 50                      push   %eax
68 cd 80                   int    $0x80
69
70 freebsd4_sigcode+4:
71 XXX
72 osigcode:
73 XXX
74   */
75   ip = c->dwarf.ip;
76   ret = X86_SCF_NONE;
77   c->sigcontext_format = ret;
78   if ((*a->access_mem) (as, ip, &w0, 0, arg) < 0 ||
79       (*a->access_mem) (as, ip + 4, &w1, 0, arg) < 0 ||
80       (*a->access_mem) (as, ip + 8, &w2, 0, arg) < 0 ||
81       (*a->access_mem) (as, ip + 12, &w3, 0, arg) < 0)
82     return ret;
83   if (w0 == 0x2024448d && w1 == 0x01a1b850 && w2 == 0xcd500000 &&
84       (w3 & 0xff) == 0x80)
85     ret = X86_SCF_FREEBSD_SIGFRAME;
86   else {
87     if ((*a->access_mem) (as, ip + 16, &w4, 0, arg) < 0 ||
88         (*a->access_mem) (as, ip + 20, &w5, 0, arg) < 0)
89       return ret;
90     if (w0 == 0x2024448d && w1 == 0x5440f750 && w2 == 0x75000200 &&
91         w3 == 0x14688e03 && w4 == 0x0001a1b8 && w5 == 0x80cd5000)
92       ret = X86_SCF_FREEBSD_SIGFRAME;
93   }
94
95   /* Check for syscall */
96   if (ret == X86_SCF_NONE && (*a->access_mem) (as, ip - 2, &w0, 0, arg) >= 0 &&
97       (w0 & 0xffff) == 0x80cd)
98     ret = X86_SCF_FREEBSD_SYSCALL;
99   Debug (16, "returning %d\n", ret);
100   c->sigcontext_format = ret;
101   return (ret);
102 }
103
104 HIDDEN int
105 x86_handle_signal_frame (unw_cursor_t *cursor)
106 {
107   struct cursor *c = (struct cursor *) cursor;
108   int ret;
109
110   if (c->sigcontext_format == X86_SCF_FREEBSD_SIGFRAME) {
111     struct sigframe *sf;
112     uintptr_t uc_addr;
113     struct dwarf_loc esp_loc;
114
115     sf = (struct sigframe *)c->dwarf.cfa;
116     uc_addr = (uintptr_t)&(sf->sf_uc);
117     c->sigcontext_addr = c->dwarf.cfa;
118
119     esp_loc = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_ESP_OFF, 0);
120     ret = dwarf_get (&c->dwarf, esp_loc, &c->dwarf.cfa);
121     if (ret < 0)
122     {
123             Debug (2, "returning 0\n");
124             return 0;
125     }
126
127     c->dwarf.loc[EIP] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EIP_OFF, 0);
128     c->dwarf.loc[ESP] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_ESP_OFF, 0);
129     c->dwarf.loc[EAX] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EAX_OFF, 0);
130     c->dwarf.loc[ECX] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_ECX_OFF, 0);
131     c->dwarf.loc[EDX] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EDX_OFF, 0);
132     c->dwarf.loc[EBX] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EBX_OFF, 0);
133     c->dwarf.loc[EBP] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EBP_OFF, 0);
134     c->dwarf.loc[ESI] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_ESI_OFF, 0);
135     c->dwarf.loc[EDI] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EDI_OFF, 0);
136     c->dwarf.loc[EFLAGS] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EFLAGS_OFF, 0);
137     c->dwarf.loc[TRAPNO] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_TRAPNO_OFF, 0);
138     c->dwarf.loc[ST0] = DWARF_NULL_LOC;
139   } else if (c->sigcontext_format == X86_SCF_FREEBSD_SYSCALL) {
140     c->dwarf.loc[EIP] = DWARF_LOC (c->dwarf.cfa, 0);
141     c->dwarf.loc[EAX] = DWARF_NULL_LOC;
142     c->dwarf.cfa += 4;
143     c->dwarf.use_prev_instr = 1;
144   } else {
145     Debug (8, "Gstep: not handling frame format %d\n", c->sigcontext_format);
146     abort();
147   }
148   return 0;
149 }
150
151 HIDDEN dwarf_loc_t
152 x86_get_scratch_loc (struct cursor *c, unw_regnum_t reg)
153 {
154   unw_word_t addr = c->sigcontext_addr, off, xmm_off;
155   unw_word_t fpstate, fpformat;
156   int ret, is_fpstate = 0, is_xmmstate = 0;
157
158   switch (c->sigcontext_format)
159     {
160     case X86_SCF_NONE:
161       return DWARF_REG_LOC (&c->dwarf, reg);
162
163     case X86_SCF_FREEBSD_SIGFRAME:
164       addr += offsetof(struct sigframe, sf_uc) + FREEBSD_UC_MCONTEXT_OFF;
165       break;
166
167     case X86_SCF_FREEBSD_SIGFRAME4:
168       abort();
169       break;
170
171     case X86_SCF_FREEBSD_OSIGFRAME:
172       /* XXXKIB */
173       abort();
174       break;
175
176     case X86_SCF_FREEBSD_SYSCALL:
177       /* XXXKIB */
178       abort();
179       break;
180
181     default:
182       /* XXXKIB */
183       abort();
184       break;
185     }
186
187   off = 0; /* shut gcc warning */
188   switch (reg)
189     {
190     case UNW_X86_GS: off = FREEBSD_UC_MCONTEXT_GS_OFF; break;
191     case UNW_X86_FS: off = FREEBSD_UC_MCONTEXT_FS_OFF; break;
192     case UNW_X86_ES: off = FREEBSD_UC_MCONTEXT_ES_OFF; break;
193     case UNW_X86_DS: off = FREEBSD_UC_MCONTEXT_SS_OFF; break;
194     case UNW_X86_EDI: off = FREEBSD_UC_MCONTEXT_EDI_OFF; break;
195     case UNW_X86_ESI: off = FREEBSD_UC_MCONTEXT_ESI_OFF; break;
196     case UNW_X86_EBP: off = FREEBSD_UC_MCONTEXT_EBP_OFF; break;
197     case UNW_X86_ESP: off = FREEBSD_UC_MCONTEXT_ESP_OFF; break;
198     case UNW_X86_EBX: off = FREEBSD_UC_MCONTEXT_EBX_OFF; break;
199     case UNW_X86_EDX: off = FREEBSD_UC_MCONTEXT_EDX_OFF; break;
200     case UNW_X86_ECX: off = FREEBSD_UC_MCONTEXT_ECX_OFF; break;
201     case UNW_X86_EAX: off = FREEBSD_UC_MCONTEXT_EAX_OFF; break;
202     case UNW_X86_TRAPNO: off = FREEBSD_UC_MCONTEXT_TRAPNO_OFF; break;
203     case UNW_X86_EIP: off = FREEBSD_UC_MCONTEXT_EIP_OFF; break;
204     case UNW_X86_CS: off = FREEBSD_UC_MCONTEXT_CS_OFF; break;
205     case UNW_X86_EFLAGS: off = FREEBSD_UC_MCONTEXT_EFLAGS_OFF; break;
206     case UNW_X86_SS: off = FREEBSD_UC_MCONTEXT_SS_OFF; break;
207
208     case UNW_X86_FCW:
209       is_fpstate = 1;
210       off = FREEBSD_UC_MCONTEXT_CW_OFF;
211       xmm_off = FREEBSD_UC_MCONTEXT_CW_XMM_OFF;
212       break;
213     case UNW_X86_FSW:
214       is_fpstate = 1;
215       off = FREEBSD_UC_MCONTEXT_SW_OFF;
216       xmm_off = FREEBSD_UC_MCONTEXT_SW_XMM_OFF;
217       break;
218     case UNW_X86_FTW:
219       is_fpstate = 1;
220       xmm_off = FREEBSD_UC_MCONTEXT_TAG_XMM_OFF;
221       off = FREEBSD_UC_MCONTEXT_TAG_OFF;
222       break;
223     case UNW_X86_FCS:
224       is_fpstate = 1;
225       off = FREEBSD_UC_MCONTEXT_CSSEL_OFF;
226       xmm_off = FREEBSD_UC_MCONTEXT_CSSEL_XMM_OFF;
227       break;
228     case UNW_X86_FIP:
229       is_fpstate = 1;
230       off = FREEBSD_UC_MCONTEXT_IPOFF_OFF;
231       xmm_off = FREEBSD_UC_MCONTEXT_IPOFF_XMM_OFF;
232       break;
233     case UNW_X86_FEA:
234       is_fpstate = 1;
235       off = FREEBSD_UC_MCONTEXT_DATAOFF_OFF;
236       xmm_off = FREEBSD_UC_MCONTEXT_DATAOFF_XMM_OFF;
237       break;
238     case UNW_X86_FDS:
239       is_fpstate = 1;
240       off = FREEBSD_US_MCONTEXT_DATASEL_OFF;
241       xmm_off = FREEBSD_US_MCONTEXT_DATASEL_XMM_OFF;
242       break;
243     case UNW_X86_MXCSR:
244       is_fpstate = 1;
245       is_xmmstate = 1;
246       xmm_off = FREEBSD_UC_MCONTEXT_MXCSR_XMM_OFF;
247       break;
248
249       /* stacked fp registers */
250     case UNW_X86_ST0: case UNW_X86_ST1: case UNW_X86_ST2: case UNW_X86_ST3:
251     case UNW_X86_ST4: case UNW_X86_ST5: case UNW_X86_ST6: case UNW_X86_ST7:
252       is_fpstate = 1;
253       off = FREEBSD_UC_MCONTEXT_ST0_OFF + 10*(reg - UNW_X86_ST0);
254       xmm_off = FREEBSD_UC_MCONTEXT_ST0_XMM_OFF + 10*(reg - UNW_X86_ST0);
255       break;
256
257      /* SSE fp registers */
258     case UNW_X86_XMM0_lo: case UNW_X86_XMM0_hi:
259     case UNW_X86_XMM1_lo: case UNW_X86_XMM1_hi:
260     case UNW_X86_XMM2_lo: case UNW_X86_XMM2_hi:
261     case UNW_X86_XMM3_lo: case UNW_X86_XMM3_hi:
262     case UNW_X86_XMM4_lo: case UNW_X86_XMM4_hi:
263     case UNW_X86_XMM5_lo: case UNW_X86_XMM5_hi:
264     case UNW_X86_XMM6_lo: case UNW_X86_XMM6_hi:
265     case UNW_X86_XMM7_lo: case UNW_X86_XMM7_hi:
266       is_fpstate = 1;
267       is_xmmstate = 1;
268       xmm_off = FREEBSD_UC_MCONTEXT_XMM0_OFF + 8*(reg - UNW_X86_XMM0_lo);
269       break;
270     case UNW_X86_XMM0:
271     case UNW_X86_XMM1:
272     case UNW_X86_XMM2:
273     case UNW_X86_XMM3:
274     case UNW_X86_XMM4:
275     case UNW_X86_XMM5:
276     case UNW_X86_XMM6:
277     case UNW_X86_XMM7:
278       is_fpstate = 1;
279       is_xmmstate = 1;
280       xmm_off = FREEBSD_UC_MCONTEXT_XMM0_OFF + 16*(reg - UNW_X86_XMM0);
281       break;
282
283     case UNW_X86_FOP:
284     case UNW_X86_TSS:
285     case UNW_X86_LDT:
286     default:
287       return DWARF_REG_LOC (&c->dwarf, reg);
288     }
289
290   if (is_fpstate)
291     {
292       if ((ret = dwarf_get (&c->dwarf,
293            DWARF_MEM_LOC (&c->dwarf, addr + FREEBSD_UC_MCONTEXT_FPSTATE_OFF),
294            &fpstate)) < 0)
295         return DWARF_NULL_LOC;
296       if (fpstate == FREEBSD_UC_MCONTEXT_FPOWNED_NONE)
297         return DWARF_NULL_LOC;
298       if ((ret = dwarf_get (&c->dwarf,
299            DWARF_MEM_LOC (&c->dwarf, addr + FREEBSD_UC_MCONTEXT_FPFORMAT_OFF),
300            &fpformat)) < 0)
301         return DWARF_NULL_LOC;
302       if (fpformat == FREEBSD_UC_MCONTEXT_FPFMT_NODEV ||
303           (is_xmmstate && fpformat != FREEBSD_UC_MCONTEXT_FPFMT_XMM))
304         return DWARF_NULL_LOC;
305       if (is_xmmstate)
306         off = xmm_off;
307     }
308
309     return DWARF_MEM_LOC (c, addr + off);
310 }
311
312 #ifndef UNW_REMOTE_ONLY
313 HIDDEN void *
314 x86_r_uc_addr (ucontext_t *uc, int reg)
315 {
316   void *addr;
317
318   switch (reg)
319     {
320     case UNW_X86_GS:  addr = &uc->uc_mcontext.mc_gs; break;
321     case UNW_X86_FS:  addr = &uc->uc_mcontext.mc_fs; break;
322     case UNW_X86_ES:  addr = &uc->uc_mcontext.mc_es; break;
323     case UNW_X86_DS:  addr = &uc->uc_mcontext.mc_ds; break;
324     case UNW_X86_EAX: addr = &uc->uc_mcontext.mc_eax; break;
325     case UNW_X86_EBX: addr = &uc->uc_mcontext.mc_ebx; break;
326     case UNW_X86_ECX: addr = &uc->uc_mcontext.mc_ecx; break;
327     case UNW_X86_EDX: addr = &uc->uc_mcontext.mc_edx; break;
328     case UNW_X86_ESI: addr = &uc->uc_mcontext.mc_esi; break;
329     case UNW_X86_EDI: addr = &uc->uc_mcontext.mc_edi; break;
330     case UNW_X86_EBP: addr = &uc->uc_mcontext.mc_ebp; break;
331     case UNW_X86_EIP: addr = &uc->uc_mcontext.mc_eip; break;
332     case UNW_X86_ESP: addr = &uc->uc_mcontext.mc_esp; break;
333     case UNW_X86_TRAPNO:  addr = &uc->uc_mcontext.mc_trapno; break;
334     case UNW_X86_CS:  addr = &uc->uc_mcontext.mc_cs; break;
335     case UNW_X86_EFLAGS:  addr = &uc->uc_mcontext.mc_eflags; break;
336     case UNW_X86_SS:  addr = &uc->uc_mcontext.mc_ss; break;
337
338     default:
339       addr = NULL;
340     }
341   return addr;
342 }
343
344 HIDDEN int
345 x86_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg)
346 {
347   struct cursor *c = (struct cursor *) cursor;
348   ucontext_t *uc = c->uc;
349
350   /* Ensure c->pi is up-to-date.  On x86, it's relatively common to be
351      missing DWARF unwind info.  We don't want to fail in that case,
352      because the frame-chain still would let us do a backtrace at
353      least.  */
354   dwarf_make_proc_info (&c->dwarf);
355
356   if (c->sigcontext_format == X86_SCF_NONE) {
357       Debug (8, "resuming at ip=%x via setcontext()\n", c->dwarf.ip);
358       setcontext (uc);
359       abort();
360   } else if (c->sigcontext_format == X86_SCF_FREEBSD_SIGFRAME) {
361       struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr;
362
363       Debug (8, "resuming at ip=%x via sigreturn(%p)\n", c->dwarf.ip, sc);
364       sigreturn((ucontext_t *)((const char *)sc + FREEBSD_SC_UCONTEXT_OFF));
365       abort();
366   } else {
367       Debug (8, "resuming at ip=%x for sigcontext format %d not implemented\n",
368       c->dwarf.ip, c->sigcontext_format);
369       abort();
370   }
371   return -UNW_EINVAL;
372 }
373
374 #endif