Lely core libraries  2.2.5
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
65 static struct {
66 #else
67 static thread_local struct {
68 #endif
69  void *penv;
70  void (*func)(void *);
71  void *arg;
72  jmp_buf env;
73 } ctx;
74 
75 static _Noreturn void ctx_init(void *sp, size_t size);
76 static _Noreturn void ctx_func(void);
77 
78 #if _POSIX_C_SOURCE >= 200112L && (!defined(__NEWLIB__) || defined(__CYGWIN__))
79 
80 #if LELY_NO_THREADS
81 static struct sigctx {
82 #else
83 static 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 
92 static _Noreturn void sigctx_init(void *sp, size_t size);
93 static _Noreturn void sigctx_func(void);
94 
95 #endif // _POSIX_C_SOURCE >= 200112L && (!__NEWLIB__ || __CYGWIN__)
96 
97 int
98 mkjmp(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 
112 int
113 sigmkjmp(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 
129 static _Noreturn void
130 ctx_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 
141 static _Noreturn void
142 ctx_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 
170 static _Noreturn void
171 sigctx_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 
182 static _Noreturn void
183 sigctx_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 succes, and jump back to sigmkjmp().
191  if (!sigsetjmp(sigctx.penv, sigctx.savemask))
192  siglongjmp(sigctx.env, 1);
193 
194  // The environment has been restored by siglongjmp(). Invoke the
195  // user-provided function on the current (new) stack.
196  func(arg);
197 
198  // The user-provided function returned. Since we do not have an
199  // environment to restore, terminate the current thread.
200 #if !LELY_NO_THREADS
201  thrd_exit(0);
202 #else
203  exit(EXIT_SUCCESS);
204 #endif
205  for (;;)
206  ;
207 }
208 
209 #endif // _POSIX_C_SOURCE >= 200112L && (!__NEWLIB__ || __CYGWIN__)
mkjmp.h
threads.h
thrd_exit
_Noreturn void thrd_exit(int res)
Terminates execution of the calling thread and sets its result code to res.
Definition: threads-pthread.c:231
util.h
_Noreturn
#define _Noreturn
A function declared with a _Noreturn function specifier SHALL not return to its caller.
Definition: features.h:214
mkjmp
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
stdlib.h
sigmkjmp
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