1/* libunwind - a platform-independent unwind library
2   Copyright (C) 2010 Konstantin Belousov <kib@freebsd.org>
3
4This file is part of libunwind.
5
6Permission is hereby granted, free of charge, to any person obtaining
7a copy of this software and associated documentation files (the
8"Software"), to deal in the Software without restriction, including
9without limitation the rights to use, copy, modify, merge, publish,
10distribute, sublicense, and/or sell copies of the Software, and to
11permit persons to whom the Software is furnished to do so, subject to
12the following conditions:
13
14The above copyright notice and this permission notice shall be
15included in all copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23WITH 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
38PROTECTED int
39unw_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:
53sigcode+4: from amd64 freebsd32 environment
548d 44 24 20		lea    0x20(%esp),%eax
5550			push   %eax
56b8 a1 01 00 00		mov    $0x1a1,%eax
5750			push   %eax
58cd 80			int    $0x80
59
60sigcode+4: from real i386
618d 44 24 20		lea    0x20(%esp),%eax
6250			push   %eax
63f7 40 54 00 02 00	testl  $0x20000,0x54(%eax)
6475 03			jne    sigcode+21
658e 68 14		mov    0x14(%eax),%gs
66b8 a1 01 00 00		mov    $0x1a1,%eax
6750			push   %eax
68cd 80			int    $0x80
69
70freebsd4_sigcode+4:
71XXX
72osigcode:
73XXX
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
99PROTECTED int
100unw_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
141HIDDEN dwarf_loc_t
142x86_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
303HIDDEN void *
304x86_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
334HIDDEN int
335x86_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