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