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