1 /*
2 * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
3 * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved.
4 * Copyright (c) 2008-2022 Ivan Maidanski
5 *
6 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
7 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
8 *
9 * Permission is hereby granted to use or copy this program
10 * for any purpose, provided the above notices are retained on all copies.
11 * Permission to modify the code and to distribute modified code is granted,
12 * provided the above notices are retained, and a notice that the code was
13 * modified is included with the above copyright notice.
14 */
15 16 #include "private/gc_priv.h"
17 18 #if !defined(PLATFORM_MACH_DEP) && !defined(SN_TARGET_PSP2)
19 20 # if defined(IA64) && !defined(THREADS)
21 GC_INNER ptr_t GC_save_regs_ret_val = NULL;
22 # endif
23 24 # if defined(UNIX_LIKE) && !defined(STACK_NOT_SCANNED)
25 # include <signal.h>
26 # ifndef NO_GETCONTEXT
27 # if defined(DARWIN) \
28 && (MAC_OS_X_VERSION_MAX_ALLOWED \
29 >= 1060 /* `MAC_OS_X_VERSION_10_6` */)
30 # include <sys/ucontext.h>
31 # else
32 # include <ucontext.h>
33 # endif /* !DARWIN */
34 # ifdef GETCONTEXT_FPU_EXCMASK_BUG
35 # include <fenv.h>
36 # endif
37 # endif /* !NO_GETCONTEXT */
38 # endif
39 40 GC_ATTR_NOINLINE GC_ATTR_NO_SANITIZE_ADDR GC_INNER void
41 GC_with_callee_saves_pushed(GC_with_callee_saves_func fn, ptr_t arg)
42 {
43 volatile int dummy;
44 volatile ptr_t context = 0;
45 # if defined(EMSCRIPTEN) || defined(HAVE_BUILTIN_UNWIND_INIT) \
46 || defined(STACK_NOT_SCANNED) || (defined(NO_CRT) && defined(MSWIN32)) \
47 || !defined(NO_UNDERSCORE_SETJMP)
48 # define volatile_arg arg
49 # else
50 /*
51 * Note: `volatile` to avoid "arg might be clobbered by setjmp" warning
52 * produced by some compilers.
53 */
54 volatile ptr_t volatile_arg = arg;
55 # endif
56 57 # if defined(EMSCRIPTEN) || defined(STACK_NOT_SCANNED)
58 /* No-op, "registers" are pushed in `GC_push_other_roots()`. */
59 # else
60 # if defined(UNIX_LIKE) && !defined(NO_GETCONTEXT)
61 /*
62 * Older versions of Darwin seem to lack `getcontext()`.
63 * Linux on ARM and MIPS often does not provide a real `getcontext()`.
64 */
65 66 /* The variable is set to -1 (means broken) or 1 (means it works). */
67 static signed char getcontext_works = 0;
68 ucontext_t ctxt;
69 # ifdef GETCONTEXT_FPU_EXCMASK_BUG
70 /*
71 * Workaround a bug (clearing the FPU exception mask) in `getcontext`
72 * on Linux/x86_64.
73 */
74 # ifdef X86_64
75 /*
76 * We manipulate FPU control word here just not to force the client
77 * application to use `-lm` linker option.
78 */
79 unsigned short old_fcw;
80 81 # if defined(CPPCHECK)
82 GC_noop1_ptr(&old_fcw);
83 # endif
84 __asm__ __volatile__("fstcw %0" : "=m"(*&old_fcw));
85 # else
86 int except_mask = fegetexcept();
87 # endif
88 # endif
89 90 if (getcontext_works >= 0) {
91 if (getcontext(&ctxt) < 0) {
92 WARN("getcontext failed:"
93 " using another register retrieval method...\n",
94 0);
95 /*
96 * `getcontext()` is broken, do not try again.
97 * E.g., to workaround a bug in Docker `ubuntu_32bit`.
98 */
99 } else {
100 context = (ptr_t)&ctxt;
101 }
102 if (UNLIKELY(0 == getcontext_works))
103 getcontext_works = context != NULL ? 1 : -1;
104 }
105 # ifdef GETCONTEXT_FPU_EXCMASK_BUG
106 # ifdef X86_64
107 __asm__ __volatile__("fldcw %0" : : "m"(*&old_fcw));
108 {
109 unsigned mxcsr;
110 /* And now correct the exception mask in SSE `mxcsr`. */
111 __asm__ __volatile__("stmxcsr %0" : "=m"(*&mxcsr));
112 mxcsr = (mxcsr & ~(FE_ALL_EXCEPT << 7)) | ((old_fcw & FE_ALL_EXCEPT) << 7);
113 __asm__ __volatile__("ldmxcsr %0" : : "m"(*&mxcsr));
114 }
115 # else /* !X86_64 */
116 if (feenableexcept(except_mask) < 0)
117 ABORT("feenableexcept failed");
118 # endif
119 # endif /* GETCONTEXT_FPU_EXCMASK_BUG */
120 # if defined(IA64) || defined(SPARC)
121 /*
122 * On a register-window machine, we need to save register contents on
123 * the stack for this to work. This may already be subsumed by the
124 * `getcontext()` call.
125 */
126 # if defined(IA64) && !defined(THREADS)
127 GC_save_regs_ret_val =
128 # endif
129 GC_save_regs_in_stack();
130 # endif
131 if (NULL == context) /*< `getcontext` failed */
132 # endif /* !NO_GETCONTEXT */
133 {
134 # if defined(HAVE_BUILTIN_UNWIND_INIT)
135 /*
136 * This was suggested as the way to force callee-save registers and
137 * register windows onto the stack.
138 */
139 __builtin_unwind_init();
140 # elif defined(NO_CRT) && defined(MSWIN32)
141 CONTEXT ctx;
142 143 RtlCaptureContext(&ctx);
144 # else
145 /* Generic code. */
146 jmp_buf regs;
147 148 /*
149 * `setjmp()` does not always clear all of the buffer.
150 * That tends to preserve garbage. Clear it.
151 */
152 BZERO(regs, sizeof(regs));
153 # ifdef NO_UNDERSCORE_SETJMP
154 (void)setjmp(regs);
155 # else
156 /*
157 * We do not want to mess with signals. According to the SUSv3
158 * (Single UNIX Specification v3), `setjmp` may or may not save
159 * signal mask. `_setjmp` will not, but is less portable.
160 */
161 (void)_setjmp(regs);
162 # endif
163 # endif
164 }
165 # endif
166 /*
167 * TODO: `context` here is sometimes just zero. At the moment, the
168 * callees do not really need it. Cast `fn` to a `volatile` type
169 * to prevent call inlining.
170 */
171 (*(GC_with_callee_saves_func volatile *)&fn)(
172 volatile_arg, CAST_AWAY_VOLATILE_PVOID(context));
173 /*
174 * Strongly discourage the compiler from treating the above as a tail-call,
175 * since that would pop the register contents before we get a chance to
176 * look at them.
177 */
178 GC_noop1(COVERT_DATAFLOW(ADDR(&dummy)));
179 # undef volatile_arg
180 }
181 182 #endif /* !PLATFORM_MACH_DEP && !SN_TARGET_PSP2 */
183