]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
vm: allocate an extra canary page before callstack
[factor.git] / vm / os-unix.cpp
1 #include "master.hpp"
2
3 namespace factor
4 {
5
6 THREADHANDLE start_thread(void *(*start_routine)(void *),void *args)
7 {
8         pthread_attr_t attr;
9         pthread_t thread;
10         if (pthread_attr_init (&attr) != 0)
11                 fatal_error("pthread_attr_init() failed",0);
12         if (pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE) != 0)
13                 fatal_error("pthread_attr_setdetachstate() failed",0);
14         if (pthread_create (&thread, &attr, start_routine, args) != 0)
15                 fatal_error("pthread_create() failed",0);
16         pthread_attr_destroy(&attr);
17         return thread;
18 }
19
20 static void *null_dll;
21
22 void sleep_nanos(u64 nsec)
23 {
24         timespec ts;
25         timespec ts_rem;
26         int ret;
27         ts.tv_sec = nsec / 1000000000;
28         ts.tv_nsec = nsec % 1000000000;
29         ret = nanosleep(&ts,&ts_rem);
30         while(ret == -1 && errno == EINTR)
31         {
32                 memcpy(&ts, &ts_rem, sizeof(ts));
33                 ret = nanosleep(&ts, &ts_rem);
34         }
35
36         if(ret == -1)
37                 fatal_error("nanosleep failed", 0);
38 }
39
40 void factor_vm::init_ffi()
41 {
42         null_dll = dlopen(NULL,RTLD_LAZY);
43 }
44
45 void factor_vm::ffi_dlopen(dll *dll)
46 {
47         dll->handle = dlopen(alien_offset(dll->path), RTLD_LAZY);
48 }
49
50 void *factor_vm::ffi_dlsym_raw(dll *dll, symbol_char *symbol)
51 {
52         return dlsym(dll ? dll->handle : null_dll, symbol);
53 }
54
55 void *factor_vm::ffi_dlsym(dll *dll, symbol_char *symbol)
56 {
57         return FUNCTION_CODE_POINTER(ffi_dlsym_raw(dll, symbol));
58 }
59
60 #ifdef FACTOR_PPC
61 void *factor_vm::ffi_dlsym_toc(dll *dll, symbol_char *symbol)
62 {
63         return FUNCTION_TOC_POINTER(ffi_dlsym_raw(dll, symbol));
64 }
65 #endif
66
67 void factor_vm::ffi_dlclose(dll *dll)
68 {
69         if(dlclose(dll->handle))
70                 general_error(ERROR_FFI,false_object,false_object);
71         dll->handle = NULL;
72 }
73
74 void factor_vm::primitive_existsp()
75 {
76         struct stat sb;
77         char *path = (char *)(untag_check<byte_array>(ctx->pop()) + 1);
78         ctx->push(tag_boolean(stat(path,&sb) >= 0));
79 }
80
81 void factor_vm::move_file(const vm_char *path1, const vm_char *path2)
82 {
83         int ret = 0;
84         do
85         {
86                 ret = rename((path1),(path2));
87         }
88         while(ret < 0 && errno == EINTR);
89
90         if(ret < 0)
91                 general_error(ERROR_IO,tag_fixnum(errno),false_object);
92 }
93
94 segment::segment(cell size_, bool executable_p, bool canary_page_p)
95 {
96         size = size_;
97
98         cell pagesize = getpagesize();
99
100         int prot;
101         if(executable_p)
102                 prot = (PROT_READ | PROT_WRITE | PROT_EXEC);
103         else
104                 prot = (PROT_READ | PROT_WRITE);
105
106         cell startsize = canary_page_p ? 2*pagesize : pagesize;
107
108         char *array = (char *)mmap(NULL,startsize + size + pagesize,prot,MAP_ANON | MAP_PRIVATE,-1,0);
109         if(array == (char*)-1) out_of_memory();
110
111         if(mprotect(array,startsize,PROT_NONE) == -1)
112                 fatal_error("Cannot protect low guard page",(cell)array);
113
114         if(mprotect(array + startsize + size,pagesize,PROT_NONE) == -1)
115                 fatal_error("Cannot protect high guard page",(cell)array);
116
117         start = (cell)(array + startsize);
118         end = start + size;
119 }
120
121 segment::~segment()
122 {
123         int pagesize = getpagesize();
124         int retval = munmap((void*)(start - pagesize),pagesize + size + pagesize);
125         if(retval)
126                 fatal_error("Segment deallocation failed",0);
127 }
128
129 void code_heap::guard_safepoint()
130 {
131         if(mprotect(safepoint_page,getpagesize(),PROT_NONE) == -1)
132                 fatal_error("Cannot protect safepoint guard page",(cell)safepoint_page);
133 }
134
135 void code_heap::unguard_safepoint()
136 {
137         if(mprotect(safepoint_page,getpagesize(),PROT_WRITE) == -1)
138                 fatal_error("Cannot unprotect safepoint guard page",(cell)safepoint_page);
139 }
140
141 void factor_vm::dispatch_signal(void *uap, void (handler)())
142 {
143         dispatch_signal_handler(
144                 &UAP_STACK_POINTER(uap),
145                 &UAP_PROGRAM_COUNTER(uap),
146                 FUNCTION_CODE_POINTER(handler)
147         );
148         UAP_SET_TOC_POINTER(uap, (cell)FUNCTION_TOC_POINTER(handler));
149 }
150
151 void memory_signal_handler(int signal, siginfo_t *siginfo, void *uap)
152 {
153         factor_vm *vm = current_vm();
154         vm->signal_fault_addr = (cell)siginfo->si_addr;
155         vm->dispatch_signal(uap,factor::memory_signal_handler_impl);
156 }
157
158 void synchronous_signal_handler(int signal, siginfo_t *siginfo, void *uap)
159 {
160         factor_vm *vm = current_vm_p();
161         if (vm)
162         {
163                 vm->signal_number = signal;
164                 vm->dispatch_signal(uap,factor::synchronous_signal_handler_impl);
165         } else
166                 fatal_error("Foreign thread received signal ", signal);
167 }
168
169 void enqueue_signal_handler(int signal, siginfo_t *siginfo, void *uap)
170 {
171         factor_vm *vm = current_vm_p();
172         if (vm)
173                 vm->enqueue_safepoint_signal(signal);
174         else
175                 fatal_error("Foreign thread received signal ", signal);
176 }
177
178 void fep_signal_handler(int signal, siginfo_t *siginfo, void *uap)
179 {
180         factor_vm *vm = current_vm_p();
181         if (vm)
182                 vm->enqueue_safepoint_fep();
183         else
184                 fatal_error("Foreign thread received signal ", signal);
185 }
186
187 void sample_signal_handler(int signal, siginfo_t *siginfo, void *uap)
188 {
189         factor_vm *vm = current_vm_p();
190         if (vm)
191                 vm->enqueue_safepoint_sample();
192         else
193                 fatal_error("Foreign thread received signal ", signal);
194 }
195
196 void ignore_signal_handler(int signal, siginfo_t *siginfo, void *uap)
197 {
198 }
199
200 void fpe_signal_handler(int signal, siginfo_t *siginfo, void *uap)
201 {
202         factor_vm *vm = current_vm();
203         vm->signal_number = signal;
204         vm->signal_fpu_status = fpu_status(uap_fpu_status(uap));
205         uap_clear_fpu_status(uap);
206
207         vm->dispatch_signal(uap,
208                 (siginfo->si_code == FPE_INTDIV || siginfo->si_code == FPE_INTOVF)
209                 ? factor::synchronous_signal_handler_impl
210                 : factor::fp_signal_handler_impl);
211 }
212
213 static void sigaction_safe(int signum, const struct sigaction *act, struct sigaction *oldact)
214 {
215         int ret;
216         do
217         {
218                 ret = sigaction(signum, act, oldact);
219         }
220         while(ret == -1 && errno == EINTR);
221
222         if(ret == -1)
223                 fatal_error("sigaction failed", 0);
224 }
225
226 static void init_sigaction_with_handler(struct sigaction *act,
227         void (*handler)(int, siginfo_t*, void*))
228 {
229         memset(act, 0, sizeof(struct sigaction));
230         sigemptyset(&act->sa_mask);
231         act->sa_sigaction = handler;
232         act->sa_flags = SA_SIGINFO | SA_ONSTACK;
233 }
234
235 void factor_vm::unix_init_signals()
236 {
237         /* OpenBSD doesn't support sigaltstack() if we link against
238         libpthread. See http://redmine.ruby-lang.org/issues/show/1239 */
239
240 #ifndef __OpenBSD__
241         signal_callstack_seg = new segment(callstack_size,false,false);
242
243         stack_t signal_callstack;
244         signal_callstack.ss_sp = (char *)signal_callstack_seg->start;
245         signal_callstack.ss_size = signal_callstack_seg->size;
246         signal_callstack.ss_flags = 0;
247
248         if(sigaltstack(&signal_callstack,(stack_t *)NULL) < 0)
249                 fatal_error("sigaltstack() failed",0);
250 #endif
251
252         struct sigaction memory_sigaction;
253         struct sigaction synchronous_sigaction;
254         struct sigaction enqueue_sigaction;
255         struct sigaction fep_sigaction;
256         struct sigaction sample_sigaction;
257         struct sigaction fpe_sigaction;
258         struct sigaction ignore_sigaction;
259
260         init_sigaction_with_handler(&memory_sigaction, memory_signal_handler);
261         sigaction_safe(SIGBUS,&memory_sigaction,NULL);
262         sigaction_safe(SIGSEGV,&memory_sigaction,NULL);
263         sigaction_safe(SIGTRAP,&memory_sigaction,NULL);
264
265         init_sigaction_with_handler(&fpe_sigaction, fpe_signal_handler);
266         sigaction_safe(SIGFPE,&fpe_sigaction,NULL);
267
268         init_sigaction_with_handler(&synchronous_sigaction, synchronous_signal_handler);
269         sigaction_safe(SIGILL,&synchronous_sigaction,NULL);
270         sigaction_safe(SIGABRT,&synchronous_sigaction,NULL);
271
272         init_sigaction_with_handler(&enqueue_sigaction, enqueue_signal_handler);
273         sigaction_safe(SIGUSR1,&enqueue_sigaction,NULL);
274         sigaction_safe(SIGUSR2,&enqueue_sigaction,NULL);
275         sigaction_safe(SIGWINCH,&enqueue_sigaction,NULL);
276 #ifdef SIGINFO
277         sigaction_safe(SIGINFO,&enqueue_sigaction,NULL);
278 #endif
279
280         init_sigaction_with_handler(&fep_sigaction, fep_signal_handler);
281         sigaction_safe(SIGQUIT,&fep_sigaction,NULL);
282         sigaction_safe(SIGINT,&fep_sigaction,NULL);
283
284         init_sigaction_with_handler(&sample_sigaction, sample_signal_handler);
285         sigaction_safe(SIGALRM,&sample_sigaction,NULL);
286
287         /* We don't use SA_IGN here because then the ignore action is inherited
288         by subprocesses, which we don't want. There is a unit test in
289         io.launcher.unix for this. */
290         init_sigaction_with_handler(&ignore_sigaction, ignore_signal_handler);
291         sigaction_safe(SIGPIPE,&ignore_sigaction,NULL);
292 }
293
294 /* On Unix, shared fds such as stdin cannot be set to non-blocking mode
295 (http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
296 so we kludge around this by spawning a thread, which waits on a control pipe
297 for a signal, upon receiving this signal it reads one block of data from stdin
298 and writes it to a data pipe. Upon completion, it writes a 4-byte integer to
299 the size pipe, indicating how much data was written to the data pipe.
300
301 The read end of the size pipe can be set to non-blocking. */
302 extern "C" {
303         int stdin_read;
304         int stdin_write;
305
306         int control_read;
307         int control_write;
308
309         int size_read;
310         int size_write;
311 }
312
313 void safe_close(int fd)
314 {
315         if(close(fd) < 0)
316                 fatal_error("error closing fd",errno);
317 }
318
319 bool check_write(int fd, void *data, ssize_t size)
320 {
321         if(write(fd,data,size) == size)
322                 return true;
323         else
324         {
325                 if(errno == EINTR)
326                         return check_write(fd,data,size);
327                 else
328                         return false;
329         }
330 }
331
332 void safe_write(int fd, void *data, ssize_t size)
333 {
334         if(!check_write(fd,data,size))
335                 fatal_error("error writing fd",errno);
336 }
337
338 bool safe_read(int fd, void *data, ssize_t size)
339 {
340         ssize_t bytes = read(fd,data,size);
341         if(bytes < 0)
342         {
343                 if(errno == EINTR)
344                         return safe_read(fd,data,size);
345                 else
346                 {
347                         fatal_error("error reading fd",errno);
348                         return false;
349                 }
350         }
351         else
352                 return (bytes == size);
353 }
354
355 void *stdin_loop(void *arg)
356 {
357         unsigned char buf[4096];
358         bool loop_running = true;
359
360         sigset_t mask;
361         sigfillset(&mask);
362         pthread_sigmask(SIG_BLOCK, &mask, NULL);
363
364         while(loop_running)
365         {
366                 if(!safe_read(control_read,buf,1))
367                         break;
368
369                 if(buf[0] != 'X')
370                         fatal_error("stdin_loop: bad data on control fd",buf[0]);
371
372                 for(;;)
373                 {
374                         ssize_t bytes = read(0,buf,sizeof(buf));
375                         if(bytes < 0)
376                         {
377                                 if(errno == EINTR)
378                                         continue;
379                                 else
380                                 {
381                                         loop_running = false;
382                                         break;
383                                 }
384                         }
385                         else if(bytes >= 0)
386                         {
387                                 safe_write(size_write,&bytes,sizeof(bytes));
388
389                                 if(!check_write(stdin_write,buf,bytes))
390                                         loop_running = false;
391                                 break;
392                         }
393                 }
394         }
395
396         safe_close(stdin_write);
397         safe_close(control_read);
398
399         return NULL;
400 }
401
402 void safe_pipe(int *in, int *out)
403 {
404         int filedes[2];
405
406         if(pipe(filedes) < 0)
407                 fatal_error("Error opening pipe",errno);
408
409         *in = filedes[0];
410         *out = filedes[1];
411
412         if(fcntl(*in,F_SETFD,FD_CLOEXEC) < 0)
413                 fatal_error("Error with fcntl",errno);
414
415         if(fcntl(*out,F_SETFD,FD_CLOEXEC) < 0)
416                 fatal_error("Error with fcntl",errno);
417 }
418
419 void open_console()
420 {
421         safe_pipe(&control_read,&control_write);
422         safe_pipe(&size_read,&size_write);
423         safe_pipe(&stdin_read,&stdin_write);
424         start_thread(stdin_loop,NULL);
425 }
426
427 }