]>
Commit | Line | Data |
---|---|---|
ada7c492 A |
1 | /* |
2 | * Copyright (c) 2007, 2009 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | /* | |
25 | * Copyright (c) 2001 Daniel M. Eischen <deischen@freebsd.org> | |
26 | * All rights reserved. | |
27 | * | |
28 | * Redistribution and use in source and binary forms, with or without | |
29 | * modification, are permitted provided that the following conditions | |
30 | * are met: | |
31 | * 1. Redistributions of source code must retain the above copyright | |
32 | * notice, this list of conditions and the following disclaimer. | |
33 | * 2. Neither the name of the author nor the names of its contributors | |
34 | * may be used to endorse or promote products derived from this software | |
35 | * without specific prior written permission. | |
36 | * | |
37 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
38 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
39 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
40 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
41 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
42 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
43 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
44 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
45 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
46 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
47 | * SUCH DAMAGE. | |
48 | */ | |
49 | ||
50 | #define _XOPEN_SOURCE 600L | |
442fbc9d | 51 | #define _DARWIN_C_SOURCE |
ada7c492 A |
52 | #include <ucontext.h> |
53 | #include <errno.h> | |
54 | ||
442fbc9d A |
55 | #include <stdlib.h> |
56 | #include <stdarg.h> | |
57 | #include <unistd.h> | |
58 | #include <sys/param.h> | |
59 | ||
60 | #include <TargetConditionals.h> | |
61 | ||
62 | /* This is a macro to capture all the code added in here that is purely to make | |
63 | * conformance tests pass and seems to have no functional reason nor is it | |
64 | * required by the standard */ | |
65 | #define CONFORMANCE_SPECIFIC_HACK 1 | |
66 | ||
67 | #if TARGET_OS_OSX || TARGET_OS_DRIVERKIT | |
68 | ||
ada7c492 A |
69 | #if defined(__x86_64__) || defined(__i386__) |
70 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" | |
71 | ||
ada7c492 | 72 | #include <stddef.h> |
ada7c492 A |
73 | |
74 | /* Prototypes */ | |
75 | extern void _ctx_start(ucontext_t *, int argc, ...); | |
76 | ||
77 | __attribute__((visibility("hidden"))) | |
78 | void | |
79 | _ctx_done (ucontext_t *ucp) | |
80 | { | |
81 | if (ucp->uc_link == NULL) | |
82 | _exit(0); | |
83 | else { | |
84 | /* | |
85 | * Since this context has finished, don't allow it | |
86 | * to be restarted without being reinitialized (via | |
87 | * setcontext or swapcontext). | |
88 | */ | |
89 | ucp->uc_mcsize = 0; | |
90 | ||
91 | /* Set context to next one in link */ | |
92 | /* XXX - what to do for error, abort? */ | |
93 | setcontext((const ucontext_t *)ucp->uc_link); | |
94 | __builtin_trap(); /* should never get here */ | |
95 | } | |
96 | } | |
97 | ||
98 | void | |
99 | makecontext(ucontext_t *ucp, void (*start)(), int argc, ...) | |
100 | { | |
101 | va_list ap; | |
102 | char *stack_top; | |
103 | intptr_t *argp; | |
104 | int i; | |
105 | ||
106 | if (ucp == NULL) | |
107 | return; | |
442fbc9d | 108 | else if (ucp->uc_stack.ss_sp == NULL) { |
ada7c492 A |
109 | /* |
110 | * This should really return -1 with errno set to ENOMEM | |
111 | * or something, but the spec says that makecontext is | |
112 | * a void function. At least make sure that the context | |
113 | * isn't valid so it can't be used without an error. | |
114 | */ | |
115 | ucp->uc_mcsize = 0; | |
116 | } | |
117 | /* XXX - Do we want to sanity check argc? */ | |
118 | else if ((argc < 0) || (argc > NCARGS)) { | |
119 | ucp->uc_mcsize = 0; | |
120 | } | |
121 | /* Make sure the context is valid. */ | |
122 | else { | |
123 | /* | |
124 | * Arrange the stack as follows: | |
125 | * | |
442fbc9d A |
126 | * Bottom of the stack |
127 | * | |
ada7c492 A |
128 | * _ctx_start() - context start wrapper |
129 | * start() - user start routine | |
130 | * arg1 - first argument, aligned(16) | |
131 | * ... | |
132 | * argn | |
133 | * ucp - this context, %rbp/%ebp points here | |
134 | * | |
442fbc9d A |
135 | * stack top |
136 | * | |
ada7c492 A |
137 | * When the context is started, control will return to |
138 | * the context start wrapper which will pop the user | |
139 | * start routine from the top of the stack. After that, | |
140 | * the top of the stack will be setup with all arguments | |
141 | * necessary for calling the start routine. When the | |
142 | * start routine returns, the context wrapper then sets | |
143 | * the stack pointer to %rbp/%ebp which was setup to point to | |
144 | * the base of the stack (and where ucp is stored). It | |
145 | * will then call _ctx_done() to swap in the next context | |
146 | * (uc_link != 0) or exit the program (uc_link == 0). | |
147 | */ | |
ada7c492 A |
148 | stack_top = (char *)(ucp->uc_stack.ss_sp + |
149 | ucp->uc_stack.ss_size - sizeof(intptr_t)); | |
150 | ||
151 | int minargc = argc; | |
152 | #if defined(__x86_64__) | |
153 | /* Give 6 stack slots to _ctx_start */ | |
154 | if (minargc < 6) | |
155 | minargc = 6; | |
156 | #endif | |
157 | /* | |
158 | * Adjust top of stack to allow for 3 pointers (return | |
159 | * address, _ctx_start, and ucp) and argc arguments. | |
160 | * We allow the arguments to be pointers also. The first | |
161 | * argument to the user function must be properly aligned. | |
162 | */ | |
163 | ||
164 | stack_top = stack_top - (sizeof(intptr_t) * (1 + minargc)); | |
165 | stack_top = (char *)((intptr_t)stack_top & ~15); | |
166 | stack_top = stack_top - (2 * sizeof(intptr_t)); | |
167 | argp = (intptr_t *)stack_top; | |
168 | ||
169 | /* | |
170 | * Setup the top of the stack with the user start routine | |
171 | * followed by all of its aguments and the pointer to the | |
172 | * ucontext. We need to leave a spare spot at the top of | |
173 | * the stack because setcontext will move rip/eip to the top | |
174 | * of the stack before returning. | |
175 | */ | |
176 | *argp = (intptr_t)_ctx_start; /* overwritten with same value */ | |
177 | argp++; | |
178 | *argp = (intptr_t)start; | |
179 | argp++; | |
180 | ||
181 | /* Add all the arguments: */ | |
182 | va_start(ap, argc); | |
183 | for (i = 0; i < argc; i++) { | |
184 | *argp = va_arg(ap, intptr_t); | |
185 | argp++; | |
186 | } | |
187 | va_end(ap); | |
188 | ||
189 | #if defined(__x86_64__) | |
190 | /* Always provide space for ctx_start to pop the parameter registers */ | |
191 | for (;argc < minargc; argc++) { | |
192 | *argp++ = 0; | |
193 | } | |
194 | ||
195 | /* Keep stack aligned */ | |
196 | if (argc & 1) { | |
197 | *argp++ = 0; | |
198 | } | |
199 | #endif | |
200 | ||
201 | /* The ucontext is placed at the bottom of the stack. */ | |
202 | *argp = (intptr_t)ucp; | |
203 | ||
442fbc9d A |
204 | #if CONFORMANCE_SPECIFIC_HACK |
205 | // There is a conformance test which initialized a ucontext A by memcpy-ing | |
206 | // a ucontext B that was previously initialized with getcontext. | |
207 | // getcontext(B) modified B such that B.uc_mcontext = &B.__mcontext_data; | |
208 | // But by doing the memcpy of B to A, A.uc_mcontext = &B.__mcontext_data | |
209 | // when that's not necessarily what we want. We therefore have to | |
210 | // unfortunately reassign A.uc_mccontext = &A.__mcontext_data even though we | |
211 | // don't know if A.__mcontext_data was properly initialized before we use | |
212 | // it. This is really because the conformance test doesn't initialize | |
213 | // properly with multiple getcontexts and instead copies contexts around. | |
214 | ucp->uc_mcontext = (mcontext_t) &ucp->__mcontext_data; | |
215 | #endif | |
216 | ||
ada7c492 A |
217 | /* |
218 | * Set the machine context to point to the top of the | |
219 | * stack and the program counter to the context start | |
220 | * wrapper. Note that setcontext() pushes the return | |
221 | * address onto the top of the stack, so allow for this | |
222 | * by adjusting the stack downward 1 slot. Also set | |
223 | * %r12/%esi to point to the base of the stack where ucp | |
224 | * is stored. | |
225 | */ | |
442fbc9d | 226 | mcontext_t mc = ucp->uc_mcontext; |
ada7c492 A |
227 | #if defined(__x86_64__) |
228 | /* Use callee-save and match _ctx_start implementation */ | |
229 | mc->__ss.__r12 = (intptr_t)argp; | |
230 | mc->__ss.__rbp = 0; | |
231 | mc->__ss.__rsp = (intptr_t)stack_top + sizeof(caddr_t); | |
232 | mc->__ss.__rip = (intptr_t)_ctx_start; | |
233 | #else | |
234 | mc->__ss.__esi = (int)argp; | |
235 | mc->__ss.__ebp = 0; | |
236 | mc->__ss.__esp = (int)stack_top + sizeof(caddr_t); | |
237 | mc->__ss.__eip = (int)_ctx_start; | |
238 | #endif | |
239 | } | |
240 | } | |
241 | ||
442fbc9d A |
242 | #elif defined(__arm64__) |
243 | ||
244 | /* | |
245 | * _STRUCT_UCONTEXT { | |
246 | * int uc_onstack; | |
247 | * __darwin_sigset_t uc_sigmask; // signal mask used by this context | |
248 | * _STRUCT_SIGALTSTACK uc_stack; // stack used by this context | |
249 | * _STRUCT_UCONTEXT *uc_link; // pointer to resuming context | |
250 | * __darwin_size_t uc_mcsize; // size of the machine context passed in | |
251 | * _STRUCT_MCONTEXT *uc_mcontext; // pointer to machine specific context | |
252 | * #ifdef _XOPEN_SOURCE | |
253 | * _STRUCT_MCONTEXT __mcontext_data; | |
254 | * #endif | |
255 | * }; | |
256 | * | |
257 | * From the standard: | |
258 | * The makecontext() function shall modify the context specified by uctx, which | |
259 | * has been initialized using getcontext(). When this context is resumed using | |
260 | * swapcontext() or setcontext(), program execution shall continue by calling | |
261 | * func, passing it the arguments that follow argc in the makecontext() call. | |
262 | * | |
263 | * Before a call is made to makecontext(), the application shall ensure that the | |
264 | * context being modified has a stack allocated for it. The application shall | |
265 | * ensure that the value of argc matches the number of arguments of type int | |
266 | * passed to func; otherwise, the behavior is undefined. | |
267 | * | |
268 | * makecontext will set up the uc_stack such that when setcontext or swapcontext | |
269 | * is called on the ucontext, it will first execute a helper function _ctx_start() | |
270 | * which will call the client specified function and then call a second | |
271 | * helper _ctx_done() (which will either follow the ctxt specified by uc_link or | |
272 | * exit.) | |
273 | * | |
274 | * void _ctx_start((void *func)(int arg1, ...), ...) | |
275 | * void _ctx_done(ucontext_t *uctx); | |
276 | * | |
277 | * makecontext modifies the uc_stack as specified: | |
278 | * | |
279 | * High addresses | |
280 | * __________________ <---- fp in context | |
281 | * | arg n-1, arg n | | |
282 | * | ... | | |
283 | * | arg1, arg2 | | |
284 | * | _______________ | <----- sp in mcontext | |
285 | * | | | |
286 | * | | | |
287 | * | | | |
288 | * | | | |
289 | * | | | |
290 | * | | | |
291 | * Low addresses | |
292 | * | |
293 | * The mcontext is also modified such that: | |
294 | * - sp points to the end of the arguments on the stack | |
295 | * - fp points to the stack top | |
296 | * - lr points to _ctx_start. | |
297 | * - x19 = uctx | |
298 | * - x20 = user func | |
299 | * Note: It is fine to modify register state since we'll never go back to | |
300 | * the state we getcontext-ed from. We modify callee save registers so that | |
301 | * they are a) actually set by setcontext b) still present when we return | |
302 | * from user_func in _ctx_start | |
303 | * | |
304 | * The first thing which _ctx_start will do is pop the first 8 arguments off the | |
305 | * stack and then branch to user_func. This works because it leaves the | |
306 | * remaining arguments after the first 8 from the stack. Once the client | |
307 | * function returns in _ctx_start, we'll be back to the current state as | |
308 | * specified above in the diagram. | |
309 | * | |
310 | * We can then set up the stack for calling _ctx_done | |
311 | * a) Set sp = fp. | |
312 | * b) Move x19 (which is callee save and therefore restored if used by user_func), to x0 | |
313 | * c) Call _ctx_done() | |
314 | */ | |
315 | ||
316 | #include <ptrauth.h> | |
317 | #include <os/tsd.h> | |
318 | #include <platform/compat.h> | |
319 | #include <platform/string.h> | |
320 | #include <mach/arm/thread_status.h> | |
321 | ||
322 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" | |
323 | ||
324 | extern void _ctx_start(void (*user_func)()); | |
ada7c492 A |
325 | |
326 | void | |
442fbc9d | 327 | _ctx_done(ucontext_t *uctx) |
ada7c492 | 328 | { |
442fbc9d A |
329 | if (uctx->uc_link == NULL) { |
330 | _exit(0); | |
331 | } else { | |
332 | uctx->uc_mcsize = 0; /* To make sure that this is not called again without reinitializing */ | |
333 | setcontext((ucontext_t *) uctx->uc_link); | |
334 | __builtin_trap(); /* should never get here */ | |
335 | } | |
ada7c492 A |
336 | } |
337 | ||
442fbc9d A |
338 | #define ALIGN_TO_16_BYTES(addr) (addr & ~0xf) |
339 | #define ARM64_REGISTER_ARGS 8 | |
340 | ||
341 | void | |
342 | makecontext(ucontext_t *uctx, void (*func)(void), int argc, ...) | |
343 | { | |
344 | if (uctx == NULL) { | |
345 | return; | |
346 | } | |
347 | ||
348 | if (uctx->uc_stack.ss_sp == NULL) { | |
349 | goto error; | |
350 | } | |
351 | ||
352 | if (argc < 0 || argc > NCARGS) { | |
353 | goto error; | |
354 | } | |
355 | ||
356 | #if CONFORMANCE_SPECIFIC_HACK | |
357 | // There is a conformance test which initialized a ucontext A by memcpy-ing | |
358 | // a ucontext B that was previously initialized with getcontext. | |
359 | // getcontext(B) modified B such that B.uc_mcontext = &B.__mcontext_data; | |
360 | // But by doing the memcpy of B to A, A.uc_mcontext = &B.__mcontext_data | |
361 | // when that's not necessarily what we want. We therefore have to | |
362 | // unfortunately reassign A.uc_mccontext = &A.__mcontext_data even though we | |
363 | // don't know if A.__mcontext_data was properly initialized before we use | |
364 | // it. This is really because the conformance test doesn't initialize | |
365 | // properly with multiple getcontexts and instead copies contexts around. | |
366 | uctx->uc_mcontext = (mcontext_t) &uctx->__mcontext_data; | |
367 | #endif | |
368 | ||
369 | bzero(uctx->uc_stack.ss_sp, uctx->uc_stack.ss_size); | |
370 | ||
371 | uintptr_t fp = (char *) uctx->uc_stack.ss_sp + uctx->uc_stack.ss_size; | |
372 | fp = ALIGN_TO_16_BYTES(fp); | |
373 | ||
374 | // All args are set up on the stack. We also make sure that we also have at | |
375 | // least 8 args on the stack (and populate with 0 if the input argc < 8). | |
376 | // This way _ctx_start will always have 8 args to pop out from the stack | |
377 | // before it calls the client function. | |
378 | int padded_argc = (argc < ARM64_REGISTER_ARGS) ? ARM64_REGISTER_ARGS : argc; | |
379 | ||
380 | uintptr_t sp = fp - (sizeof(int) * padded_argc); | |
381 | sp = ALIGN_TO_16_BYTES(sp); | |
382 | ||
383 | // Populate the stack with all the args. Per arm64 calling convention ABI, we | |
384 | // do not need to pad and make sure that the arguments are aligned in any | |
385 | // manner. | |
386 | int *current_arg_addr = (int *) sp; | |
387 | ||
388 | va_list argv; | |
389 | va_start(argv, argc); | |
390 | for (int i = 0; i < argc; i++) { | |
391 | *current_arg_addr = va_arg(argv, int); | |
392 | current_arg_addr++; | |
393 | } | |
394 | va_end(argv); | |
395 | ||
396 | mcontext_t mctx = uctx->uc_mcontext; | |
397 | ||
398 | #if defined(__arm64e__) | |
399 | // The set macros below read from the opaque_flags to decide how to set the | |
400 | // fields (including whether to sign them) and so we need to make sure that | |
401 | // we require signing always. | |
402 | mctx->__ss.__opaque_flags &= ~__DARWIN_ARM_THREAD_STATE64_FLAGS_NO_PTRAUTH; | |
ada7c492 | 403 | #endif |
442fbc9d A |
404 | |
405 | arm_thread_state64_set_fp(mctx->__ss, fp); | |
406 | arm_thread_state64_set_sp(mctx->__ss, sp); | |
407 | arm_thread_state64_set_lr_fptr(mctx->__ss, (void *) _ctx_start); | |
408 | ||
409 | mctx->__ss.__x[19] = uctx; | |
410 | mctx->__ss.__x[20] = _OS_PTR_MUNGE(func); | |
411 | return; | |
412 | error: | |
413 | uctx->uc_mcsize = 0; | |
414 | return; | |
415 | } | |
416 | ||
417 | #endif /* arm64 || x86_64 || i386 */ | |
418 | ||
419 | #else /* TARGET_OS_OSX || TARGET_OS_DRIVERKIT */ | |
420 | ||
421 | void | |
422 | makecontext(ucontext_t *u, void (*f)(void), int argc, ...) | |
423 | { | |
424 | } | |
425 | ||
426 | #endif /* TARGET_OS_OSX || TARGET_OS_DRIVERKIT */ |