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/ucontext.h>
30#include <machine/sigframe.h>
31#include <signal.h>
32#include <stddef.h>
33#include "unwind_i.h"
34#include "ucontext_i.h"
35
36PROTECTED int
37unw_is_signal_frame (unw_cursor_t *cursor)
38{
39  /* XXXKIB */
40  struct cursor *c = (struct cursor *) cursor;
41  unw_word_t w0, w1, w2, b0, ip;
42  unw_addr_space_t as;
43  unw_accessors_t *a;
44  void *arg;
45  int ret;
46
47  as = c->dwarf.as;
48  a = unw_get_accessors (as);
49  arg = c->dwarf.as_arg;
50
51  /* Check if RIP points at sigreturn sequence.
5248 8d 7c 24 10		lea	SIGF_UC(%rsp),%rdi
536a 00			pushq	$0
5448 c7 c0 a1 01 00 00	movq	$SYS_sigreturn,%rax
550f 05			syscall
56f4		0:	hlt
57eb fd			jmp	0b
58  */
59
60  ip = c->dwarf.ip;
61  c->sigcontext_format = X86_64_SCF_NONE;
62  if ((ret = (*a->access_mem) (as, ip, &w0, 0, arg)) < 0
63      || (ret = (*a->access_mem) (as, ip + 8, &w1, 0, arg)) < 0
64      || (ret = (*a->access_mem) (as, ip + 16, &w2, 0, arg)) < 0)
65    return 0;
66  w2 &= 0xffffff;
67  if (w0 == 0x48006a10247c8d48 &&
68      w1 == 0x050f000001a1c0c7 &&
69      w2 == 0x0000000000fdebf4)
70   {
71     c->sigcontext_format = X86_64_SCF_FREEBSD_SIGFRAME;
72     return (c->sigcontext_format);
73   }
74  /* Check if RIP points at standard syscall sequence.
7549 89 ca	mov    %rcx,%r10
760f 05		syscall
77  */
78  if ((ret = (*a->access_mem) (as, ip - 5, &b0, 0, arg)) < 0)
79    return (0);
80  Debug (12, "b0 0x%lx\n", b0);
81  if ((b0 & 0xffffffffffffff) == 0x050fca89490000 ||
82      (b0 & 0xffffffffff) == 0x050fca8949)
83   {
84    c->sigcontext_format = X86_64_SCF_FREEBSD_SYSCALL;
85    return (c->sigcontext_format);
86   }
87  return (X86_64_SCF_NONE);
88}
89
90PROTECTED int
91unw_handle_signal_frame (unw_cursor_t *cursor)
92{
93  struct cursor *c = (struct cursor *) cursor;
94  unw_word_t ucontext;
95  int ret;
96
97  if (c->sigcontext_format == X86_64_SCF_FREEBSD_SIGFRAME)
98   {
99    ucontext = c->dwarf.cfa + offsetof(struct sigframe, sf_uc);
100    c->sigcontext_addr = c->dwarf.cfa;
101    Debug(1, "signal frame, skip over trampoline\n");
102
103    struct dwarf_loc rsp_loc = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RSP, 0);
104    ret = dwarf_get (&c->dwarf, rsp_loc, &c->dwarf.cfa);
105    if (ret < 0)
106     {
107       Debug (2, "returning %d\n", ret);
108       return ret;
109     }
110
111    c->dwarf.loc[RAX] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RAX, 0);
112    c->dwarf.loc[RDX] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RDX, 0);
113    c->dwarf.loc[RCX] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RCX, 0);
114    c->dwarf.loc[RBX] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RBX, 0);
115    c->dwarf.loc[RSI] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RSI, 0);
116    c->dwarf.loc[RDI] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RDI, 0);
117    c->dwarf.loc[RBP] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RBP, 0);
118    c->dwarf.loc[RSP] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RSP, 0);
119    c->dwarf.loc[ R8] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_R8, 0);
120    c->dwarf.loc[ R9] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_R9, 0);
121    c->dwarf.loc[R10] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_R10, 0);
122    c->dwarf.loc[R11] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_R11, 0);
123    c->dwarf.loc[R12] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_R12, 0);
124    c->dwarf.loc[R13] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_R13, 0);
125    c->dwarf.loc[R14] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_R14, 0);
126    c->dwarf.loc[R15] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_R15, 0);
127    c->dwarf.loc[RIP] = DWARF_LOC (ucontext + UC_MCONTEXT_GREGS_RIP, 0);
128
129    return 0;
130   }
131  else if (c->sigcontext_format == X86_64_SCF_FREEBSD_SYSCALL)
132   {
133    c->dwarf.loc[RCX] = c->dwarf.loc[R10];
134    /*  rsp_loc = DWARF_LOC(c->dwarf.cfa - 8, 0);	*/
135    /*	rbp_loc = c->dwarf.loc[RBP];			*/
136    c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0);
137    ret = dwarf_get (&c->dwarf, c->dwarf.loc[RIP], &c->dwarf.ip);
138    Debug (1, "Frame Chain [RIP=0x%Lx] = 0x%Lx\n",
139	   (unsigned long long) DWARF_GET_LOC (c->dwarf.loc[RIP]),
140	   (unsigned long long) c->dwarf.ip);
141    if (ret < 0)
142     {
143       Debug (2, "returning %d\n", ret);
144       return ret;
145     }
146    c->dwarf.cfa += 8;
147    return 1;
148   }
149  else
150    return -UNW_EBADFRAME;
151
152}
153
154#ifndef UNW_REMOTE_ONLY
155HIDDEN void *
156x86_64_r_uc_addr (ucontext_t *uc, int reg)
157{
158  /* NOTE: common_init() in init.h inlines these for fast path access. */
159  void *addr;
160
161  switch (reg)
162    {
163    case UNW_X86_64_R8: addr = &uc->uc_mcontext.mc_r8; break;
164    case UNW_X86_64_R9: addr = &uc->uc_mcontext.mc_r9; break;
165    case UNW_X86_64_R10: addr = &uc->uc_mcontext.mc_r10; break;
166    case UNW_X86_64_R11: addr = &uc->uc_mcontext.mc_r11; break;
167    case UNW_X86_64_R12: addr = &uc->uc_mcontext.mc_r12; break;
168    case UNW_X86_64_R13: addr = &uc->uc_mcontext.mc_r13; break;
169    case UNW_X86_64_R14: addr = &uc->uc_mcontext.mc_r14; break;
170    case UNW_X86_64_R15: addr = &uc->uc_mcontext.mc_r15; break;
171    case UNW_X86_64_RDI: addr = &uc->uc_mcontext.mc_rdi; break;
172    case UNW_X86_64_RSI: addr = &uc->uc_mcontext.mc_rsi; break;
173    case UNW_X86_64_RBP: addr = &uc->uc_mcontext.mc_rbp; break;
174    case UNW_X86_64_RBX: addr = &uc->uc_mcontext.mc_rbx; break;
175    case UNW_X86_64_RDX: addr = &uc->uc_mcontext.mc_rdx; break;
176    case UNW_X86_64_RAX: addr = &uc->uc_mcontext.mc_rax; break;
177    case UNW_X86_64_RCX: addr = &uc->uc_mcontext.mc_rcx; break;
178    case UNW_X86_64_RSP: addr = &uc->uc_mcontext.mc_rsp; break;
179    case UNW_X86_64_RIP: addr = &uc->uc_mcontext.mc_rip; break;
180
181    default:
182      addr = NULL;
183    }
184  return addr;
185}
186
187HIDDEN NORETURN void
188x86_64_sigreturn (unw_cursor_t *cursor)
189{
190  struct cursor *c = (struct cursor *) cursor;
191  ucontext_t *uc = (ucontext_t *)(c->sigcontext_addr +
192    offsetof(struct sigframe, sf_uc));
193
194  Debug (8, "resuming at ip=%llx via sigreturn(%p)\n",
195	     (unsigned long long) c->dwarf.ip, uc);
196  sigreturn(uc);
197  abort();
198}
199#endif
200