Lely core libraries 2.3.4
mkjmp.c
Go to the documentation of this file.
1
24// _FORTIFY_SOURCE=2 breaks longjmp() with a different stack frame.
25#ifdef _FORTIFY_SOURCE
26#undef _FORTIFY_SOURCE
27#endif
28
29#include "util.h"
30#if !LELY_NO_THREADS
31#include <lely/libc/threads.h>
32#endif
33#include <lely/util/mkjmp.h>
34
35#include <stdlib.h>
36
37#if defined(__clang__) || defined(__GNUC__)
38#if defined(__arm__) || defined(__aarch64__)
39#define MKJMP_SET_SP(sp, size) \
40 __asm__("mov sp, %0" ::"r"((char *)(sp) + (size)))
41#elif defined(__i386__)
42#define MKJMP_SET_SP(sp, size) \
43 __asm__("movl %0, %%esp" ::"r"((char *)(sp) + (size)))
44#elif defined(__x86_64__)
45#define MKJMP_SET_SP(sp, size) \
46 __asm__("movq %0, %%rsp" ::"r"((char *)(sp) + (size)))
47#endif
48#elif defined(_MSC_VER)
49#if defined(_M_AMD64)
50#define MKJMP_SET_SP(sp, size) \
51 void *_sp = (char *)(sp) + (size); \
52 __asm mov rsp, _sp
53#elif defined(_M_IX86)
54#define MKJMP_SET_SP(sp, size) \
55 void *_sp = (char *)(sp) + (size); \
56 __asm mov esp, _sp
57#endif
58#endif
59
60#ifndef MKJMP_SET_SP
61#error Unsupported compiler or architecture for mkjmp()/sigmkjmp().
62#endif
63
64#if LELY_NO_THREADS
65static struct {
66#else
67static thread_local struct {
68#endif
69 void *penv;
70 void (*func)(void *);
71 void *arg;
72 jmp_buf env;
73} ctx;
74
75static _Noreturn void ctx_init(void *sp, size_t size);
76static _Noreturn void ctx_func(void);
77
78#if _POSIX_C_SOURCE >= 200112L && (!defined(__NEWLIB__) || defined(__CYGWIN__))
79
80#if LELY_NO_THREADS
81static struct sigctx {
82#else
83static thread_local struct {
84#endif
85 void *penv;
86 int savemask;
87 void (*func)(void *);
88 void *arg;
89 sigjmp_buf env;
90} sigctx;
91
92static _Noreturn void sigctx_init(void *sp, size_t size);
93static _Noreturn void sigctx_func(void);
94
95#endif // _POSIX_C_SOURCE >= 200112L && (!__NEWLIB__ || __CYGWIN__)
96
97int
98mkjmp(jmp_buf env, void (*func)(void *), void *arg, void *sp, size_t size)
99{
100 ctx.penv = env;
101 ctx.func = func;
102 ctx.arg = arg;
103
104 if (!setjmp(ctx.env))
105 ctx_init(sp, size);
106
107 return 0;
108}
109
110#if _POSIX_C_SOURCE >= 200112L && (!defined(__NEWLIB__) || defined(__CYGWIN__))
111
112int
113sigmkjmp(sigjmp_buf env, int savemask, void (*func)(void *), void *arg,
114 void *sp, size_t size)
115{
116 sigctx.penv = env;
117 sigctx.savemask = savemask;
118 sigctx.func = func;
119 sigctx.arg = arg;
120
121 if (!sigsetjmp(sigctx.env, 0))
122 sigctx_init(sp, size);
123
124 return 0;
125}
126
127#endif // _POSIX_C_SOURCE >= 200112L && (!__NEWLIB__ || __CYGWIN__)
128
129static _Noreturn void
130ctx_init(void *sp, size_t size)
131{
132 (void)size; // This argument may be unused on some platforms.
133
134 // Set the stack pointer.
135 MKJMP_SET_SP(sp, size);
136
137 // Setup a proper stack by jumping into a function.
138 ctx_func();
139}
140
141static _Noreturn void
142ctx_func(void)
143{
144 // Store the function to be invoked on the (new) stack. The static
145 // variables may be changed before this function is resumed.
146 void (*func)(void *) = ctx.func;
147 void *arg = ctx.arg;
148
149 // Save the current environment, on succes, and jump back to mkjmp().
150 if (!setjmp(ctx.penv))
151 longjmp(ctx.env, 1);
152
153 // The environment has been restored by longjmp(). Invoke the
154 // user-provided function on the current (new) stack.
155 func(arg);
156
157 // The user-provided function returned. Since we do not have an
158 // environment to restore, terminate the current thread.
159#if !LELY_NO_THREADS
160 thrd_exit(0);
161#else
162 exit(EXIT_SUCCESS);
163#endif
164 for (;;)
165 ;
166}
167
168#if _POSIX_C_SOURCE >= 200112L && (!defined(__NEWLIB__) || defined(__CYGWIN__))
169
170static _Noreturn void
171sigctx_init(void *sp, size_t size)
172{
173 (void)size; // This argument may be unused on some platforms.
174
175 // Set the stack pointer.
176 MKJMP_SET_SP(sp, size);
177
178 // Setup a proper stack by jumping into a function.
179 sigctx_func();
180}
181
182static _Noreturn void
183sigctx_func(void)
184{
185 // Store the function to be invoked on the (new) stack. The static
186 // variables may be changed before this function is resumed.
187 void (*func)(void *) = sigctx.func;
188 void *arg = sigctx.arg;
189
190 // Save the current environment, on success, and jump back to
191 // sigmkjmp().
192 if (!sigsetjmp(sigctx.penv, sigctx.savemask))
193 siglongjmp(sigctx.env, 1);
194
195 // The environment has been restored by siglongjmp(). Invoke the
196 // user-provided function on the current (new) stack.
197 func(arg);
198
199 // The user-provided function returned. Since we do not have an
200 // environment to restore, terminate the current thread.
201#if !LELY_NO_THREADS
202 thrd_exit(0);
203#else
204 exit(EXIT_SUCCESS);
205#endif
206 for (;;)
207 ;
208}
209
210#endif // _POSIX_C_SOURCE >= 200112L && (!__NEWLIB__ || __CYGWIN__)
#define _Noreturn
A function declared with a _Noreturn function specifier SHALL not return to its caller.
Definition: features.h:224
int mkjmp(jmp_buf env, void(*func)(void *), void *arg, void *sp, size_t size)
Creates and stores a calling environment with a user-provided stack suitable for use by longjmp().
Definition: mkjmp.c:98
int sigmkjmp(sigjmp_buf env, int savemask, void(*func)(void *), void *arg, void *sp, size_t size)
Creates and stores a calling environment with a user-provided stack suitable for use by siglongjmp().
Definition: mkjmp.c:113
This header file is part of the utilities library; it contains the mkjmp() and sigmkjmp() function de...
This is the internal header file of the utilities library.
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
This header file is part of the C11 and POSIX compatibility library; it includes <threads....
_Noreturn void thrd_exit(int res)
Terminates execution of the calling thread and sets its result code to res.