]> gitweb.factorcode.org Git - factor.git/blob - vm/os-unix.cpp
vm: Fail with out_of_memory() if mprotect returns ENOMEM.
[factor.git] / vm / os-unix.cpp
1 #include "master.hpp"
2
3 namespace factor {
4
5 THREADHANDLE start_thread(void* (*start_routine)(void*), void* args) {
6   pthread_attr_t attr;
7   pthread_t thread;
8   if (pthread_attr_init(&attr) != 0)
9     fatal_error("pthread_attr_init() failed", 0);
10   if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) != 0)
11     fatal_error("pthread_attr_setdetachstate() failed", 0);
12   if (pthread_create(&thread, &attr, start_routine, args) != 0)
13     fatal_error("pthread_create() failed", 0);
14   pthread_attr_destroy(&attr);
15   return thread;
16 }
17
18 static void* null_dll;
19
20 void sleep_nanos(uint64_t nsec) {
21   timespec ts;
22   timespec ts_rem;
23   int ret;
24   ts.tv_sec = nsec / 1000000000;
25   ts.tv_nsec = nsec % 1000000000;
26   ret = nanosleep(&ts, &ts_rem);
27   while (ret == -1 && errno == EINTR) {
28     memcpy(&ts, &ts_rem, sizeof(ts));
29     ret = nanosleep(&ts, &ts_rem);
30   }
31
32   if (ret == -1)
33     fatal_error("nanosleep failed", 0);
34 }
35
36 void factor_vm::init_ffi() { null_dll = dlopen(NULL, RTLD_LAZY); }
37
38 void factor_vm::ffi_dlopen(dll* dll) {
39   dll->handle = dlopen(alien_offset(dll->path), RTLD_LAZY | RTLD_GLOBAL);
40 }
41
42 void* factor_vm::ffi_dlsym_raw(dll* dll, symbol_char* symbol) {
43   return dlsym(dll ? dll->handle : null_dll, symbol);
44 }
45
46 void* factor_vm::ffi_dlsym(dll* dll, symbol_char* symbol) {
47   return FUNCTION_CODE_POINTER(ffi_dlsym_raw(dll, symbol));
48 }
49
50 #ifdef FACTOR_PPC
51 void* factor_vm::ffi_dlsym_toc(dll* dll, symbol_char* symbol) {
52   return FUNCTION_TOC_POINTER(ffi_dlsym_raw(dll, symbol));
53 }
54 #endif
55
56 void factor_vm::ffi_dlclose(dll* dll) {
57   if (dlclose(dll->handle))
58     general_error(ERROR_FFI, false_object, false_object);
59   dll->handle = NULL;
60 }
61
62 void factor_vm::primitive_existsp() {
63   struct stat sb;
64   char* path = (char*)(untag_check<byte_array>(ctx->pop()) + 1);
65   ctx->push(tag_boolean(stat(path, &sb) >= 0));
66 }
67
68 void factor_vm::move_file(const vm_char* path1, const vm_char* path2) {
69   int ret = 0;
70   do {
71     ret = rename((path1), (path2));
72   } while (ret < 0 && errno == EINTR);
73
74   if (ret < 0)
75     general_error(ERROR_IO, tag_fixnum(errno), false_object);
76 }
77
78 void check_ENOMEM(const char* msg) {
79   if(errno == ENOMEM)
80     out_of_memory(msg);
81 }
82
83 segment::segment(cell size_, bool executable_p) {
84   size = size_;
85
86   int pagesize = getpagesize();
87
88   int prot;
89   if (executable_p)
90     prot = (PROT_READ | PROT_WRITE | PROT_EXEC);
91   else
92     prot = (PROT_READ | PROT_WRITE);
93
94   char* array = (char*)mmap(NULL, pagesize + size + pagesize, prot,
95                             MAP_ANON | MAP_PRIVATE, -1, 0);
96
97   if (array == (char*)- 1)
98     out_of_memory("mmap");
99
100   if (mprotect(array, pagesize, PROT_NONE) == -1) {
101     check_ENOMEM("mprotect low");
102     fatal_error("Cannot protect low guard page", (cell)array);
103   }
104
105   if (mprotect(array + pagesize + size, pagesize, PROT_NONE) == -1) {
106     check_ENOMEM("mprotect high");
107     fatal_error("Cannot protect high guard page", (cell)array);
108   }
109
110   start = (cell)(array + pagesize);
111   end = start + size;
112 }
113
114 segment::~segment() {
115   int pagesize = getpagesize();
116   int retval = munmap((void*)(start - pagesize), pagesize + size + pagesize);
117   if (retval)
118     fatal_error("Segment deallocation failed", 0);
119 }
120
121 void code_heap::guard_safepoint() {
122   if (mprotect(safepoint_page, getpagesize(), PROT_NONE) == -1)
123     fatal_error("Cannot protect safepoint guard page", (cell)safepoint_page);
124 }
125
126 void code_heap::unguard_safepoint() {
127   if (mprotect(safepoint_page, getpagesize(), PROT_WRITE) == -1)
128     fatal_error("Cannot unprotect safepoint guard page", (cell)safepoint_page);
129 }
130
131 void factor_vm::dispatch_signal(void* uap, void(handler)()) {
132   dispatch_signal_handler((cell*)&UAP_STACK_POINTER(uap),
133                           (cell*)&UAP_PROGRAM_COUNTER(uap),
134                           (cell)FUNCTION_CODE_POINTER(handler));
135   UAP_SET_TOC_POINTER(uap, (cell)FUNCTION_TOC_POINTER(handler));
136 }
137
138 void factor_vm::start_sampling_profiler_timer() {
139   struct itimerval timer;
140   memset((void*)&timer, 0, sizeof(struct itimerval));
141   timer.it_value.tv_usec = 1000000 / samples_per_second;
142   timer.it_interval.tv_usec = 1000000 / samples_per_second;
143   setitimer(ITIMER_REAL, &timer, NULL);
144 }
145
146 void factor_vm::end_sampling_profiler_timer() {
147   struct itimerval timer;
148   memset((void*)&timer, 0, sizeof(struct itimerval));
149   setitimer(ITIMER_REAL, &timer, NULL);
150 }
151
152 void memory_signal_handler(int signal, siginfo_t* siginfo, void* uap) {
153   factor_vm* vm = current_vm();
154   vm->verify_memory_protection_error((cell)siginfo->si_addr);
155   vm->signal_fault_addr = (cell)siginfo->si_addr;
156   vm->signal_fault_pc = (cell)UAP_PROGRAM_COUNTER(uap);
157   vm->dispatch_signal(uap, factor::memory_signal_handler_impl);
158 }
159
160 void synchronous_signal_handler(int signal, siginfo_t* siginfo, void* uap) {
161   if (factor_vm::fatal_erroring_p)
162     return;
163
164   factor_vm* vm = current_vm_p();
165   if (vm) {
166     vm->signal_number = signal;
167     vm->dispatch_signal(uap, factor::synchronous_signal_handler_impl);
168   } else
169     fatal_error("Foreign thread received signal", signal);
170 }
171
172 void safe_write_nonblock(int fd, void* data, ssize_t size);
173
174 static void enqueue_signal(factor_vm* vm, int signal) {
175   if (vm->signal_pipe_output != 0)
176     safe_write_nonblock(vm->signal_pipe_output, &signal, sizeof(int));
177 }
178
179 void enqueue_signal_handler(int signal, siginfo_t* siginfo, void* uap) {
180   if (factor_vm::fatal_erroring_p)
181     return;
182
183   factor_vm* vm = current_vm_p();
184   if (vm)
185     enqueue_signal(vm, signal);
186 }
187
188 void fep_signal_handler(int signal, siginfo_t* siginfo, void* uap) {
189   if (factor_vm::fatal_erroring_p)
190     return;
191
192   factor_vm* vm = current_vm_p();
193   if (vm) {
194     vm->safepoint.enqueue_fep(vm);
195     enqueue_signal(vm, signal);
196   } else
197     fatal_error("Foreign thread received signal", signal);
198 }
199
200 void sample_signal_handler(int signal, siginfo_t* siginfo, void* uap) {
201   factor_vm* vm = current_vm_p();
202   bool foreign_thread = false;
203   if (vm == NULL) {
204     foreign_thread = true;
205     vm = thread_vms.begin()->second;
206   }
207   if (atomic::load(&vm->sampling_profiler_p))
208     vm->safepoint.enqueue_samples(vm, 1, (cell)UAP_PROGRAM_COUNTER(uap),
209                                   foreign_thread);
210   else if (!foreign_thread)
211     enqueue_signal(vm, signal);
212 }
213
214 void ignore_signal_handler(int signal, siginfo_t* siginfo, void* uap) {}
215
216 void fpe_signal_handler(int signal, siginfo_t* siginfo, void* uap) {
217   factor_vm* vm = current_vm();
218   vm->signal_number = signal;
219   vm->signal_fpu_status = fpu_status(uap_fpu_status(uap));
220   uap_clear_fpu_status(uap);
221
222   vm->dispatch_signal(
223       uap, (siginfo->si_code == FPE_INTDIV || siginfo->si_code == FPE_INTOVF)
224                ? factor::synchronous_signal_handler_impl
225                : factor::fp_signal_handler_impl);
226 }
227
228 static void sigaction_safe(int signum, const struct sigaction* act,
229                            struct sigaction* oldact) {
230   int ret;
231   do {
232     ret = sigaction(signum, act, oldact);
233   } while (ret == -1 && errno == EINTR);
234
235   if (ret == -1)
236     fatal_error("sigaction failed", errno);
237 }
238
239 static void init_sigaction_with_handler(struct sigaction* act,
240                                         void (*handler)(int, siginfo_t*,
241                                                         void*)) {
242   memset(act, 0, sizeof(struct sigaction));
243   sigemptyset(&act->sa_mask);
244   act->sa_sigaction = handler;
245   act->sa_flags = SA_SIGINFO | SA_ONSTACK;
246 }
247
248 static void safe_pipe(int* in, int* out) {
249   int filedes[2];
250
251   if (pipe(filedes) < 0)
252     fatal_error("Error opening pipe", errno);
253
254   *in = filedes[0];
255   *out = filedes[1];
256
257   if (fcntl(*in, F_SETFD, FD_CLOEXEC) < 0)
258     fatal_error("Error with fcntl", errno);
259
260   if (fcntl(*out, F_SETFD, FD_CLOEXEC) < 0)
261     fatal_error("Error with fcntl", errno);
262 }
263
264 static void init_signal_pipe(factor_vm* vm) {
265   safe_pipe(&vm->signal_pipe_input, &vm->signal_pipe_output);
266
267   if (fcntl(vm->signal_pipe_output, F_SETFL, O_NONBLOCK) < 0)
268     fatal_error("Error with fcntl", errno);
269
270   vm->special_objects[OBJ_SIGNAL_PIPE] = tag_fixnum(vm->signal_pipe_input);
271 }
272
273 void factor_vm::unix_init_signals() {
274   init_signal_pipe(this);
275
276   signal_callstack_seg = new segment(callstack_size, false);
277
278   stack_t signal_callstack;
279   signal_callstack.ss_sp = (char*)signal_callstack_seg->start;
280   signal_callstack.ss_size = signal_callstack_seg->size;
281   signal_callstack.ss_flags = 0;
282
283   if (sigaltstack(&signal_callstack, (stack_t*)NULL) < 0)
284     fatal_error("sigaltstack() failed", 0);
285
286   {
287     struct sigaction memory_sigaction;
288     init_sigaction_with_handler(&memory_sigaction, memory_signal_handler);
289     sigaction_safe(SIGBUS, &memory_sigaction, NULL);
290     sigaction_safe(SIGSEGV, &memory_sigaction, NULL);
291     sigaction_safe(SIGTRAP, &memory_sigaction, NULL);
292   }
293
294   {
295     struct sigaction fpe_sigaction;
296     init_sigaction_with_handler(&fpe_sigaction, fpe_signal_handler);
297     sigaction_safe(SIGFPE, &fpe_sigaction, NULL);
298   }
299
300   {
301     struct sigaction synchronous_sigaction;
302     init_sigaction_with_handler(&synchronous_sigaction,
303                                 synchronous_signal_handler);
304     sigaction_safe(SIGILL, &synchronous_sigaction, NULL);
305     sigaction_safe(SIGABRT, &synchronous_sigaction, NULL);
306   }
307
308   {
309     struct sigaction enqueue_sigaction;
310     init_sigaction_with_handler(&enqueue_sigaction, enqueue_signal_handler);
311     sigaction_safe(SIGWINCH, &enqueue_sigaction, NULL);
312     sigaction_safe(SIGUSR1, &enqueue_sigaction, NULL);
313     sigaction_safe(SIGCONT, &enqueue_sigaction, NULL);
314     sigaction_safe(SIGURG, &enqueue_sigaction, NULL);
315     sigaction_safe(SIGIO, &enqueue_sigaction, NULL);
316     sigaction_safe(SIGPROF, &enqueue_sigaction, NULL);
317     sigaction_safe(SIGVTALRM, &enqueue_sigaction, NULL);
318 #ifdef SIGINFO
319     sigaction_safe(SIGINFO, &enqueue_sigaction, NULL);
320 #endif
321   }
322
323   handle_ctrl_c();
324
325   {
326     struct sigaction sample_sigaction;
327     init_sigaction_with_handler(&sample_sigaction, sample_signal_handler);
328     sigaction_safe(SIGALRM, &sample_sigaction, NULL);
329   }
330
331   /* We don't use SA_IGN here because then the ignore action is inherited
332      by subprocesses, which we don't want. There is a unit test in
333      io.launcher.unix for this. */
334   {
335     struct sigaction ignore_sigaction;
336     init_sigaction_with_handler(&ignore_sigaction, ignore_signal_handler);
337     sigaction_safe(SIGPIPE, &ignore_sigaction, NULL);
338     /* We send SIGUSR2 to the stdin_loop thread to interrupt it on FEP */
339     sigaction_safe(SIGUSR2, &ignore_sigaction, NULL);
340   }
341 }
342
343 /* On Unix, shared fds such as stdin cannot be set to non-blocking mode
344    (http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
345    so we kludge around this by spawning a thread, which waits on a control pipe
346    for a signal, upon receiving this signal it reads one block of data from
347    stdin and writes it to a data pipe. Upon completion, it writes a 4-byte
348    integer to the size pipe, indicating how much data was written to the data
349    pipe.
350
351    The read end of the size pipe can be set to non-blocking. */
352 extern "C" {
353 int stdin_read;
354 int stdin_write;
355
356 int control_read;
357 int control_write;
358
359 int size_read;
360 int size_write;
361
362 bool stdin_thread_initialized_p = false;
363 THREADHANDLE stdin_thread;
364 pthread_mutex_t stdin_mutex;
365 }
366
367 void safe_close(int fd) {
368   if (close(fd) < 0)
369     fatal_error("error closing fd", errno);
370 }
371
372 bool check_write(int fd, void* data, ssize_t size) {
373   if (write(fd, data, size) == size)
374     return true;
375   else {
376     if (errno == EINTR)
377       return check_write(fd, data, size);
378     else
379       return false;
380   }
381 }
382
383 void safe_write(int fd, void* data, ssize_t size) {
384   if (!check_write(fd, data, size))
385     fatal_error("error writing fd", errno);
386 }
387
388 void safe_write_nonblock(int fd, void* data, ssize_t size) {
389   if (!check_write(fd, data, size) && errno != EAGAIN)
390     fatal_error("error writing fd", errno);
391 }
392
393 bool safe_read(int fd, void* data, ssize_t size) {
394   ssize_t bytes = read(fd, data, size);
395   if (bytes < 0) {
396     if (errno == EINTR)
397       return safe_read(fd, data, size);
398     else {
399       fatal_error("error reading fd", errno);
400       return false;
401     }
402   } else
403     return (bytes == size);
404 }
405
406 void* stdin_loop(void* arg) {
407   unsigned char buf[4096];
408   bool loop_running = true;
409
410   sigset_t mask;
411   sigfillset(&mask);
412   sigdelset(&mask, SIGUSR2);
413   sigdelset(&mask, SIGTTIN);
414   sigdelset(&mask, SIGTERM);
415   sigdelset(&mask, SIGQUIT);
416   pthread_sigmask(SIG_SETMASK, &mask, NULL);
417
418   int unused;
419   pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &unused);
420   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &unused);
421
422   while (loop_running) {
423     if (!safe_read(control_read, buf, 1))
424       break;
425
426     if (buf[0] != 'X')
427       fatal_error("stdin_loop: bad data on control fd", buf[0]);
428
429     for (;;) {
430       /* If we fep, the parent thread will grab stdin_mutex and send us
431          SIGUSR2 to interrupt the read() call. */
432       pthread_mutex_lock(&stdin_mutex);
433       pthread_mutex_unlock(&stdin_mutex);
434       ssize_t bytes = read(0, buf, sizeof(buf));
435       if (bytes < 0) {
436         if (errno == EINTR)
437           continue;
438         else {
439           loop_running = false;
440           break;
441         }
442       } else if (bytes >= 0) {
443         safe_write(size_write, &bytes, sizeof(bytes));
444
445         if (!check_write(stdin_write, buf, bytes))
446           loop_running = false;
447         break;
448       }
449     }
450   }
451
452   safe_close(stdin_write);
453   safe_close(control_read);
454
455   return NULL;
456 }
457
458 void factor_vm::open_console() {
459   FACTOR_ASSERT(!stdin_thread_initialized_p);
460   safe_pipe(&control_read, &control_write);
461   safe_pipe(&size_read, &size_write);
462   safe_pipe(&stdin_read, &stdin_write);
463   stdin_thread = start_thread(stdin_loop, NULL);
464   stdin_thread_initialized_p = true;
465   pthread_mutex_init(&stdin_mutex, NULL);
466 }
467
468 /* This method is used to kill the stdin_loop before exiting from factor.
469    A Nvidia driver bug on Linux is the reason this has to be done, see:
470      http://www.nvnews.net/vbulletin/showthread.php?t=164619 */
471 void factor_vm::close_console() {
472   if (stdin_thread_initialized_p) {
473     pthread_cancel(stdin_thread);
474     pthread_join(stdin_thread, 0);
475   }
476 }
477
478 void factor_vm::lock_console() {
479   FACTOR_ASSERT(stdin_thread_initialized_p);
480   /* Lock the stdin_mutex and send the stdin_loop thread a signal to interrupt
481      any read() it has in progress. When the stdin loop iterates again, it will
482      try to lock the same mutex and wait until unlock_console() is called. */
483   pthread_mutex_lock(&stdin_mutex);
484   pthread_kill(stdin_thread, SIGUSR2);
485 }
486
487 void factor_vm::unlock_console() {
488   FACTOR_ASSERT(stdin_thread_initialized_p);
489   pthread_mutex_unlock(&stdin_mutex);
490 }
491
492 void factor_vm::ignore_ctrl_c() {
493   sig_t ret;
494   do {
495     ret = signal(SIGINT, SIG_DFL);
496   } while (ret == SIG_ERR && errno == EINTR);
497 }
498
499 void factor_vm::handle_ctrl_c() {
500   struct sigaction fep_sigaction;
501   init_sigaction_with_handler(&fep_sigaction, fep_signal_handler);
502   sigaction_safe(SIGINT, &fep_sigaction, NULL);
503 }
504
505 void abort() {
506   sig_t ret;
507   do {
508     ret = signal(SIGABRT, SIG_DFL);
509   } while (ret == SIG_ERR && errno == EINTR);
510
511   factor_vm::close_console();
512   ::abort();
513 }
514
515 }