• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
unw_is_signal_frame(unw_cursor_t * cursor)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
x86_handle_signal_frame(unw_cursor_t * cursor)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     for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
128       c->dwarf.loc[i] = DWARF_NULL_LOC;
129 
130     c->dwarf.loc[EIP] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EIP_OFF, 0);
131     c->dwarf.loc[ESP] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_ESP_OFF, 0);
132     c->dwarf.loc[EAX] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EAX_OFF, 0);
133     c->dwarf.loc[ECX] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_ECX_OFF, 0);
134     c->dwarf.loc[EDX] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EDX_OFF, 0);
135     c->dwarf.loc[EBX] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EBX_OFF, 0);
136     c->dwarf.loc[EBP] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EBP_OFF, 0);
137     c->dwarf.loc[ESI] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_ESI_OFF, 0);
138     c->dwarf.loc[EDI] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EDI_OFF, 0);
139     c->dwarf.loc[EFLAGS] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_EFLAGS_OFF, 0);
140     c->dwarf.loc[TRAPNO] = DWARF_LOC (uc_addr + FREEBSD_UC_MCONTEXT_TRAPNO_OFF, 0);
141     c->dwarf.loc[ST0] = DWARF_NULL_LOC;
142   } else if (c->sigcontext_format == X86_SCF_FREEBSD_SYSCALL) {
143     c->dwarf.loc[EIP] = DWARF_LOC (c->dwarf.cfa, 0);
144     c->dwarf.loc[ESP] = DWARF_VAL_LOC (c, c->dwarf.cfa + 4);
145     c->dwarf.loc[EAX] = DWARF_NULL_LOC;
146     c->dwarf.cfa += 4;
147     c->dwarf.use_prev_instr = 1;
148   } else {
149     Debug (8, "Gstep: not handling frame format %d\n", c->sigcontext_format);
150     abort();
151   }
152   return 0;
153 }
154 
155 HIDDEN dwarf_loc_t
x86_get_scratch_loc(struct cursor * c,unw_regnum_t reg)156 x86_get_scratch_loc (struct cursor *c, unw_regnum_t reg)
157 {
158   unw_word_t addr = c->sigcontext_addr, off, xmm_off;
159   unw_word_t fpstate, fpformat;
160   int ret, is_fpstate = 0, is_xmmstate = 0;
161 
162   switch (c->sigcontext_format)
163     {
164     case X86_SCF_NONE:
165       return DWARF_REG_LOC (&c->dwarf, reg);
166 
167     case X86_SCF_FREEBSD_SIGFRAME:
168       addr += offsetof(struct sigframe, sf_uc) + FREEBSD_UC_MCONTEXT_OFF;
169       break;
170 
171     case X86_SCF_FREEBSD_SIGFRAME4:
172       abort();
173       break;
174 
175     case X86_SCF_FREEBSD_OSIGFRAME:
176       /* XXXKIB */
177       abort();
178       break;
179 
180     case X86_SCF_FREEBSD_SYSCALL:
181       /* XXXKIB */
182       abort();
183       break;
184 
185     default:
186       /* XXXKIB */
187       abort();
188       break;
189     }
190 
191   off = 0; /* shut gcc warning */
192   switch (reg)
193     {
194     case UNW_X86_GS: off = FREEBSD_UC_MCONTEXT_GS_OFF; break;
195     case UNW_X86_FS: off = FREEBSD_UC_MCONTEXT_FS_OFF; break;
196     case UNW_X86_ES: off = FREEBSD_UC_MCONTEXT_ES_OFF; break;
197     case UNW_X86_DS: off = FREEBSD_UC_MCONTEXT_SS_OFF; break;
198     case UNW_X86_EDI: off = FREEBSD_UC_MCONTEXT_EDI_OFF; break;
199     case UNW_X86_ESI: off = FREEBSD_UC_MCONTEXT_ESI_OFF; break;
200     case UNW_X86_EBP: off = FREEBSD_UC_MCONTEXT_EBP_OFF; break;
201     case UNW_X86_ESP: off = FREEBSD_UC_MCONTEXT_ESP_OFF; break;
202     case UNW_X86_EBX: off = FREEBSD_UC_MCONTEXT_EBX_OFF; break;
203     case UNW_X86_EDX: off = FREEBSD_UC_MCONTEXT_EDX_OFF; break;
204     case UNW_X86_ECX: off = FREEBSD_UC_MCONTEXT_ECX_OFF; break;
205     case UNW_X86_EAX: off = FREEBSD_UC_MCONTEXT_EAX_OFF; break;
206     case UNW_X86_TRAPNO: off = FREEBSD_UC_MCONTEXT_TRAPNO_OFF; break;
207     case UNW_X86_EIP: off = FREEBSD_UC_MCONTEXT_EIP_OFF; break;
208     case UNW_X86_CS: off = FREEBSD_UC_MCONTEXT_CS_OFF; break;
209     case UNW_X86_EFLAGS: off = FREEBSD_UC_MCONTEXT_EFLAGS_OFF; break;
210     case UNW_X86_SS: off = FREEBSD_UC_MCONTEXT_SS_OFF; break;
211 
212     case UNW_X86_FCW:
213       is_fpstate = 1;
214       off = FREEBSD_UC_MCONTEXT_CW_OFF;
215       xmm_off = FREEBSD_UC_MCONTEXT_CW_XMM_OFF;
216       break;
217     case UNW_X86_FSW:
218       is_fpstate = 1;
219       off = FREEBSD_UC_MCONTEXT_SW_OFF;
220       xmm_off = FREEBSD_UC_MCONTEXT_SW_XMM_OFF;
221       break;
222     case UNW_X86_FTW:
223       is_fpstate = 1;
224       xmm_off = FREEBSD_UC_MCONTEXT_TAG_XMM_OFF;
225       off = FREEBSD_UC_MCONTEXT_TAG_OFF;
226       break;
227     case UNW_X86_FCS:
228       is_fpstate = 1;
229       off = FREEBSD_UC_MCONTEXT_CSSEL_OFF;
230       xmm_off = FREEBSD_UC_MCONTEXT_CSSEL_XMM_OFF;
231       break;
232     case UNW_X86_FIP:
233       is_fpstate = 1;
234       off = FREEBSD_UC_MCONTEXT_IPOFF_OFF;
235       xmm_off = FREEBSD_UC_MCONTEXT_IPOFF_XMM_OFF;
236       break;
237     case UNW_X86_FEA:
238       is_fpstate = 1;
239       off = FREEBSD_UC_MCONTEXT_DATAOFF_OFF;
240       xmm_off = FREEBSD_UC_MCONTEXT_DATAOFF_XMM_OFF;
241       break;
242     case UNW_X86_FDS:
243       is_fpstate = 1;
244       off = FREEBSD_US_MCONTEXT_DATASEL_OFF;
245       xmm_off = FREEBSD_US_MCONTEXT_DATASEL_XMM_OFF;
246       break;
247     case UNW_X86_MXCSR:
248       is_fpstate = 1;
249       is_xmmstate = 1;
250       xmm_off = FREEBSD_UC_MCONTEXT_MXCSR_XMM_OFF;
251       break;
252 
253       /* stacked fp registers */
254     case UNW_X86_ST0: case UNW_X86_ST1: case UNW_X86_ST2: case UNW_X86_ST3:
255     case UNW_X86_ST4: case UNW_X86_ST5: case UNW_X86_ST6: case UNW_X86_ST7:
256       is_fpstate = 1;
257       off = FREEBSD_UC_MCONTEXT_ST0_OFF + 10*(reg - UNW_X86_ST0);
258       xmm_off = FREEBSD_UC_MCONTEXT_ST0_XMM_OFF + 10*(reg - UNW_X86_ST0);
259       break;
260 
261      /* SSE fp registers */
262     case UNW_X86_XMM0_lo: case UNW_X86_XMM0_hi:
263     case UNW_X86_XMM1_lo: case UNW_X86_XMM1_hi:
264     case UNW_X86_XMM2_lo: case UNW_X86_XMM2_hi:
265     case UNW_X86_XMM3_lo: case UNW_X86_XMM3_hi:
266     case UNW_X86_XMM4_lo: case UNW_X86_XMM4_hi:
267     case UNW_X86_XMM5_lo: case UNW_X86_XMM5_hi:
268     case UNW_X86_XMM6_lo: case UNW_X86_XMM6_hi:
269     case UNW_X86_XMM7_lo: case UNW_X86_XMM7_hi:
270       is_fpstate = 1;
271       is_xmmstate = 1;
272       xmm_off = FREEBSD_UC_MCONTEXT_XMM0_OFF + 8*(reg - UNW_X86_XMM0_lo);
273       break;
274     case UNW_X86_XMM0:
275     case UNW_X86_XMM1:
276     case UNW_X86_XMM2:
277     case UNW_X86_XMM3:
278     case UNW_X86_XMM4:
279     case UNW_X86_XMM5:
280     case UNW_X86_XMM6:
281     case UNW_X86_XMM7:
282       is_fpstate = 1;
283       is_xmmstate = 1;
284       xmm_off = FREEBSD_UC_MCONTEXT_XMM0_OFF + 16*(reg - UNW_X86_XMM0);
285       break;
286 
287     case UNW_X86_FOP:
288     case UNW_X86_TSS:
289     case UNW_X86_LDT:
290     default:
291       return DWARF_REG_LOC (&c->dwarf, reg);
292     }
293 
294   if (is_fpstate)
295     {
296       if ((ret = dwarf_get (&c->dwarf,
297            DWARF_MEM_LOC (&c->dwarf, addr + FREEBSD_UC_MCONTEXT_FPSTATE_OFF),
298            &fpstate)) < 0)
299         return DWARF_NULL_LOC;
300       if (fpstate == FREEBSD_UC_MCONTEXT_FPOWNED_NONE)
301         return DWARF_NULL_LOC;
302       if ((ret = dwarf_get (&c->dwarf,
303            DWARF_MEM_LOC (&c->dwarf, addr + FREEBSD_UC_MCONTEXT_FPFORMAT_OFF),
304            &fpformat)) < 0)
305         return DWARF_NULL_LOC;
306       if (fpformat == FREEBSD_UC_MCONTEXT_FPFMT_NODEV ||
307           (is_xmmstate && fpformat != FREEBSD_UC_MCONTEXT_FPFMT_XMM))
308         return DWARF_NULL_LOC;
309       if (is_xmmstate)
310         off = xmm_off;
311     }
312 
313     return DWARF_MEM_LOC (c, addr + off);
314 }
315 
316 #ifndef UNW_REMOTE_ONLY
317 HIDDEN void *
x86_r_uc_addr(ucontext_t * uc,int reg)318 x86_r_uc_addr (ucontext_t *uc, int reg)
319 {
320   void *addr;
321 
322   switch (reg)
323     {
324     case UNW_X86_GS:  addr = &uc->uc_mcontext.mc_gs; break;
325     case UNW_X86_FS:  addr = &uc->uc_mcontext.mc_fs; break;
326     case UNW_X86_ES:  addr = &uc->uc_mcontext.mc_es; break;
327     case UNW_X86_DS:  addr = &uc->uc_mcontext.mc_ds; break;
328     case UNW_X86_EAX: addr = &uc->uc_mcontext.mc_eax; break;
329     case UNW_X86_EBX: addr = &uc->uc_mcontext.mc_ebx; break;
330     case UNW_X86_ECX: addr = &uc->uc_mcontext.mc_ecx; break;
331     case UNW_X86_EDX: addr = &uc->uc_mcontext.mc_edx; break;
332     case UNW_X86_ESI: addr = &uc->uc_mcontext.mc_esi; break;
333     case UNW_X86_EDI: addr = &uc->uc_mcontext.mc_edi; break;
334     case UNW_X86_EBP: addr = &uc->uc_mcontext.mc_ebp; break;
335     case UNW_X86_EIP: addr = &uc->uc_mcontext.mc_eip; break;
336     case UNW_X86_ESP: addr = &uc->uc_mcontext.mc_esp; break;
337     case UNW_X86_TRAPNO:  addr = &uc->uc_mcontext.mc_trapno; break;
338     case UNW_X86_CS:  addr = &uc->uc_mcontext.mc_cs; break;
339     case UNW_X86_EFLAGS:  addr = &uc->uc_mcontext.mc_eflags; break;
340     case UNW_X86_SS:  addr = &uc->uc_mcontext.mc_ss; break;
341 
342     default:
343       addr = NULL;
344     }
345   return addr;
346 }
347 
348 HIDDEN int
x86_local_resume(unw_addr_space_t as,unw_cursor_t * cursor,void * arg)349 x86_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg)
350 {
351   struct cursor *c = (struct cursor *) cursor;
352   ucontext_t *uc = c->uc;
353 
354   /* Ensure c->pi is up-to-date.  On x86, it's relatively common to be
355      missing DWARF unwind info.  We don't want to fail in that case,
356      because the frame-chain still would let us do a backtrace at
357      least.  */
358   dwarf_make_proc_info (&c->dwarf);
359 
360   if (c->sigcontext_format == X86_SCF_NONE) {
361       Debug (8, "resuming at ip=%x via setcontext()\n", c->dwarf.ip);
362       setcontext (uc);
363       abort();
364   } else if (c->sigcontext_format == X86_SCF_FREEBSD_SIGFRAME) {
365       struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr;
366 
367       Debug (8, "resuming at ip=%x via sigreturn(%p)\n", c->dwarf.ip, sc);
368       sigreturn((ucontext_t *)((const char *)sc + FREEBSD_SC_UCONTEXT_OFF));
369       abort();
370   } else {
371       Debug (8, "resuming at ip=%x for sigcontext format %d not implemented\n",
372       c->dwarf.ip, c->sigcontext_format);
373       abort();
374   }
375   return -UNW_EINVAL;
376 }
377 
378 #endif
379