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