/src/libunwind/src/x86_64/Gos-linux.c
Line | Count | Source |
1 | | /* libunwind - a platform-independent unwind library |
2 | | Copyright (C) 2002-2003 Hewlett-Packard Co |
3 | | Contributed by David Mosberger-Tang <davidm@hpl.hp.com> |
4 | | |
5 | | Modified for x86_64 by Max Asbock <masbock@us.ibm.com> |
6 | | |
7 | | This file is part of libunwind. |
8 | | |
9 | | Permission is hereby granted, free of charge, to any person obtaining |
10 | | a copy of this software and associated documentation files (the |
11 | | "Software"), to deal in the Software without restriction, including |
12 | | without limitation the rights to use, copy, modify, merge, publish, |
13 | | distribute, sublicense, and/or sell copies of the Software, and to |
14 | | permit persons to whom the Software is furnished to do so, subject to |
15 | | the following conditions: |
16 | | |
17 | | The above copyright notice and this permission notice shall be |
18 | | included in all copies or substantial portions of the Software. |
19 | | |
20 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
21 | | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
22 | | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
23 | | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
24 | | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
25 | | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
26 | | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
27 | | |
28 | | #ifdef HAVE_ASM_VSYSCALL_H |
29 | | #include <asm/vsyscall.h> |
30 | | #endif |
31 | | |
32 | | #include "libunwind_i.h" |
33 | | #include "unwind_i.h" |
34 | | #include "ucontext_i.h" |
35 | | |
36 | | #include <sys/syscall.h> |
37 | | |
38 | | HIDDEN void |
39 | | tdep_fetch_frame (struct dwarf_cursor *dw, unw_word_t ip UNUSED, int need_unwind_info UNUSED) |
40 | 15 | { |
41 | 15 | struct cursor *c = (struct cursor *) dw; |
42 | 15 | assert(! need_unwind_info || dw->pi_valid); |
43 | 15 | assert(! need_unwind_info || dw->pi.unwind_info); |
44 | 15 | if (dw->pi_valid |
45 | 15 | && dw->pi.unwind_info |
46 | 15 | && ((struct dwarf_cie_info *) dw->pi.unwind_info)->signal_frame) |
47 | 0 | c->sigcontext_format = X86_64_SCF_LINUX_RT_SIGFRAME; |
48 | 15 | else |
49 | 15 | c->sigcontext_format = X86_64_SCF_NONE; |
50 | | |
51 | 15 | Debug(5, "fetch frame ip=0x%lx cfa=0x%lx format=%d\n", |
52 | 15 | dw->ip, dw->cfa, c->sigcontext_format); |
53 | 15 | } |
54 | | |
55 | | HIDDEN int |
56 | | tdep_cache_frame (struct dwarf_cursor *dw) |
57 | 15 | { |
58 | 15 | struct cursor *c = (struct cursor *) dw; |
59 | | |
60 | 15 | Debug(5, "cache frame ip=0x%lx cfa=0x%lx format=%d\n", |
61 | 15 | dw->ip, dw->cfa, c->sigcontext_format); |
62 | 15 | return c->sigcontext_format; |
63 | 15 | } |
64 | | |
65 | | HIDDEN void |
66 | | tdep_reuse_frame (struct dwarf_cursor *dw, int frame) |
67 | 51.3k | { |
68 | 51.3k | struct cursor *c = (struct cursor *) dw; |
69 | 51.3k | c->sigcontext_format = frame; |
70 | 51.3k | if (c->sigcontext_format == X86_64_SCF_LINUX_RT_SIGFRAME) |
71 | 0 | { |
72 | 0 | c->frame_info.frame_type = UNW_X86_64_FRAME_SIGRETURN; |
73 | | /* Offset from cfa to ucontext_t in signal frame. */ |
74 | 0 | c->frame_info.cfa_reg_offset = 0; |
75 | 0 | c->sigcontext_addr = dw->cfa; |
76 | 0 | } |
77 | | |
78 | 51.3k | Debug(5, "reuse frame ip=0x%lx cfa=0x%lx format=%d addr=0x%lx offset=%+d\n", |
79 | 51.3k | dw->ip, dw->cfa, c->sigcontext_format, c->sigcontext_addr, |
80 | 51.3k | (c->sigcontext_format == X86_64_SCF_LINUX_RT_SIGFRAME |
81 | 51.3k | ? c->frame_info.cfa_reg_offset : 0)); |
82 | 51.3k | } |
83 | | |
84 | | int |
85 | | unw_is_signal_frame (unw_cursor_t *cursor) |
86 | 1.43k | { |
87 | 1.43k | struct cursor *c = (struct cursor *) cursor; |
88 | 1.43k | return c->sigcontext_format != X86_64_SCF_NONE; |
89 | 1.43k | } |
90 | | |
91 | | HIDDEN int |
92 | | x86_64_handle_signal_frame (unw_cursor_t *cursor UNUSED) |
93 | 0 | { |
94 | | #if UNW_DEBUG /* To silence compiler warnings */ |
95 | | /* Should not get here because we now use kernel-provided dwarf |
96 | | information for the signal trampoline and dwarf_step() works. |
97 | | Hence unw_step() should never call this function. Maybe |
98 | | restore old non-dwarf signal handling here, but then the |
99 | | gating on unw_is_signal_frame() needs to be removed. */ |
100 | | struct cursor *c = (struct cursor *) cursor; |
101 | | Debug(1, "old format signal frame? format=%d addr=0x%lx cfa=0x%lx\n", |
102 | | c->sigcontext_format, c->sigcontext_addr, c->dwarf.cfa); |
103 | | #endif |
104 | 0 | return -UNW_EBADFRAME; |
105 | 0 | } |
106 | | |
107 | | #ifndef UNW_REMOTE_ONLY |
108 | | HIDDEN void * |
109 | | x86_64_r_uc_addr (ucontext_t *uc, int reg) |
110 | 5.71k | { |
111 | | /* NOTE: common_init() in init.h inlines these for fast path access. */ |
112 | 5.71k | void *addr; |
113 | | |
114 | 5.71k | switch (reg) |
115 | 5.71k | { |
116 | 0 | case UNW_X86_64_R8: addr = &uc->uc_mcontext.gregs[REG_R8]; break; |
117 | 0 | case UNW_X86_64_R9: addr = &uc->uc_mcontext.gregs[REG_R9]; break; |
118 | 0 | case UNW_X86_64_R10: addr = &uc->uc_mcontext.gregs[REG_R10]; break; |
119 | 0 | case UNW_X86_64_R11: addr = &uc->uc_mcontext.gregs[REG_R11]; break; |
120 | 0 | case UNW_X86_64_R12: addr = &uc->uc_mcontext.gregs[REG_R12]; break; |
121 | 0 | case UNW_X86_64_R13: addr = &uc->uc_mcontext.gregs[REG_R13]; break; |
122 | 0 | case UNW_X86_64_R14: addr = &uc->uc_mcontext.gregs[REG_R14]; break; |
123 | 0 | case UNW_X86_64_R15: addr = &uc->uc_mcontext.gregs[REG_R15]; break; |
124 | 0 | case UNW_X86_64_RDI: addr = &uc->uc_mcontext.gregs[REG_RDI]; break; |
125 | 0 | case UNW_X86_64_RSI: addr = &uc->uc_mcontext.gregs[REG_RSI]; break; |
126 | 0 | case UNW_X86_64_RBP: addr = &uc->uc_mcontext.gregs[REG_RBP]; break; |
127 | 0 | case UNW_X86_64_RBX: addr = &uc->uc_mcontext.gregs[REG_RBX]; break; |
128 | 0 | case UNW_X86_64_RDX: addr = &uc->uc_mcontext.gregs[REG_RDX]; break; |
129 | 0 | case UNW_X86_64_RAX: addr = &uc->uc_mcontext.gregs[REG_RAX]; break; |
130 | 0 | case UNW_X86_64_RCX: addr = &uc->uc_mcontext.gregs[REG_RCX]; break; |
131 | 5.71k | case UNW_X86_64_RSP: addr = &uc->uc_mcontext.gregs[REG_RSP]; break; |
132 | 0 | case UNW_X86_64_RIP: addr = &uc->uc_mcontext.gregs[REG_RIP]; break; |
133 | | |
134 | 0 | default: |
135 | 0 | addr = NULL; |
136 | 5.71k | } |
137 | 5.71k | return addr; |
138 | 5.71k | } |
139 | | |
140 | | /* sigreturn() is a no-op on x86_64 glibc. */ |
141 | | HIDDEN NORETURN void |
142 | | x86_64_sigreturn (unw_cursor_t *cursor) |
143 | 0 | { |
144 | 0 | struct cursor *c = (struct cursor *) cursor; |
145 | 0 | struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr; |
146 | 0 | mcontext_t *sc_mcontext = &((ucontext_t*)sc)->uc_mcontext; |
147 | | /* Copy in saved uc - all preserved regs are at the start of sigcontext */ |
148 | 0 | memcpy(sc_mcontext, &dwarf_get_uc(&c->dwarf)->uc_mcontext, |
149 | 0 | DWARF_NUM_PRESERVED_REGS * sizeof(unw_word_t)); |
150 | |
|
151 | 0 | Debug (8, "resuming at ip=%llx via sigreturn(%p)\n", |
152 | 0 | (unsigned long long) c->dwarf.ip, sc); |
153 | | |
154 | | /* Pop 4 shadow stack frames: |
155 | | |
156 | | #0 _Ux86_64_sigreturn (cursor=0x7fffffffc990) at x86_64/Gos-linux.c:144 |
157 | | #1 0x00007ffff7f9d23f in _Ux86_64_local_resume ( |
158 | | as=0x7ffff7fb63c0 <local_addr_space>, cursor=0x7fffffffc990, arg=0x2) |
159 | | at x86_64/Gresume.c:50 |
160 | | #2 0x00007ffff7f9d44b in _Ux86_64_resume (cursor=0x7fffffffc990) |
161 | | at x86_64/Gresume.c:123 |
162 | | #3 0x000000000040094b in handler (sig=10) at Gtest-resume-sig.c:127 |
163 | | #4 <signal handler called> |
164 | | #5 0x00007ffff7d80e7b in kill () from /lib64/libc.so.6 |
165 | | |
166 | | */ |
167 | 0 | POP_SHADOW_STACK_FRAMES (4); |
168 | |
|
169 | 0 | __asm__ __volatile__ ("mov %0, %%rsp;" |
170 | 0 | "mov %1, %%rax;" |
171 | 0 | "syscall" |
172 | 0 | :: "r"((uint64_t)sc), "i"(SYS_rt_sigreturn) |
173 | 0 | : "memory"); |
174 | 0 | abort(); |
175 | 0 | } |
176 | | |
177 | | #endif |
178 | | |
179 | | static int |
180 | | is_vsyscall (struct dwarf_cursor *c UNUSED) |
181 | 0 | { |
182 | | #if defined(VSYSCALL_START) && defined(VSYSCALL_END) |
183 | | return c->ip >= VSYSCALL_START && c->ip < VSYSCALL_END; |
184 | | #elif defined(VSYSCALL_ADDR) |
185 | | /* Linux 3.16 removes `VSYSCALL_START` and `VSYSCALL_END`. Assume |
186 | | a single page is mapped for vsyscalls. */ |
187 | | return c->ip >= VSYSCALL_ADDR && c->ip < VSYSCALL_ADDR + unw_page_size; |
188 | | #else |
189 | 0 | return 0; |
190 | 0 | #endif |
191 | 0 | } |
192 | | |
193 | | HIDDEN int |
194 | | x86_64_os_step(struct cursor *c) |
195 | 0 | { |
196 | 0 | if (is_vsyscall (&c->dwarf)) |
197 | 0 | { |
198 | 0 | Debug (2, "in vsyscall region\n"); |
199 | 0 | c->frame_info.cfa_reg_offset = 8; |
200 | 0 | c->frame_info.cfa_reg_rsp = -1; |
201 | 0 | c->frame_info.frame_type = UNW_X86_64_FRAME_GUESSED; |
202 | 0 | c->dwarf.loc[RIP] = DWARF_LOC (c->dwarf.cfa, 0); |
203 | 0 | c->dwarf.cfa += 8; |
204 | 0 | return (1); |
205 | 0 | } |
206 | 0 | return (0); |
207 | 0 | } |