Reduce efficiency notes for complex type checks.
[sbcl.git] / src / runtime / os-common.c
blob626d7f5bf90a2a0f6d393678469b2e21512b4723
1 /*
2 * This software is part of the SBCL system. See the README file for
3 * more information.
5 * This software is derived from the CMU CL system, which was
6 * written at Carnegie Mellon University and released into the
7 * public domain. The software is in the public domain and is
8 * provided with absolutely no warranty. See the COPYING and CREDITS
9 * files for more information.
11 # define _GNU_SOURCE /* needed for RTLD_DEFAULT from dlfcn.h */
12 #include <stdio.h>
13 #include <errno.h>
14 #include <string.h>
15 #include <stdlib.h>
17 #include "genesis/sbcl.h"
18 #include "globals.h"
19 #include "runtime.h"
20 #include "genesis/cons.h"
21 #include "genesis/vector.h"
22 #include "genesis/symbol.h"
23 #include "genesis/static-symbols.h"
24 #include "thread.h"
25 #include "os.h"
26 #include "arch.h"
27 #include "interr.h"
28 #include "immobile-space.h"
29 #include "code.h"
30 #if defined(LISP_FEATURE_OS_PROVIDES_DLOPEN) && !defined(LISP_FEATURE_WIN32)
31 # include <dlfcn.h>
32 #endif
33 #if defined LISP_FEATURE_UNIX && defined LISP_FEATURE_SOFT_CARD_MARKS
34 #include "gc.h" // for find_page_index
35 #endif
38 * historically, this used sysconf to select the runtime page size
39 * per recent changes on other arches and discussion on sbcl-devel,
40 * however, this is not necessary -- the VM page size need not match
41 * the OS page size (and the default backend page size has been
42 * ramped up accordingly for efficiency reasons).
44 os_vm_size_t os_vm_page_size = BACKEND_PAGE_BYTES;
46 /* Expose to Lisp the value of the preprocessor define. Don't touch! */
47 int install_sig_memory_fault_handler = INSTALL_SIG_MEMORY_FAULT_HANDLER;
49 /* Except for os_zero, these routines are only called by Lisp code.
50 * These routines may also be replaced by os-dependent versions
51 * instead. */
53 #ifdef LISP_FEATURE_CHENEYGC
54 void
55 os_zero(os_vm_address_t addr, os_vm_size_t length)
57 os_vm_address_t block_start;
58 os_vm_size_t block_size;
60 #ifdef DEBUG
61 fprintf(stderr,";;; os_zero: addr: 0x%08x, len: 0x%08x\n",addr,length);
62 #endif
64 block_start = os_round_up_to_page(addr);
66 length -= block_start-addr;
67 block_size = os_trunc_size_to_page(length);
69 if (block_start > addr)
70 bzero((char *)addr, block_start-addr);
71 if (block_size < length)
72 bzero((char *)block_start+block_size, length-block_size);
74 if (block_size != 0) {
75 /* Now deallocate and allocate the block so that it faults in
76 * zero-filled. */
78 os_deallocate(block_start, block_size);
79 addr = os_alloc_gc_space(0, NOT_MOVABLE, block_start, block_size);
81 if (addr == NULL || addr != block_start)
82 lose("os_zero: block moved! %p ==> %p", block_start, addr);
85 #endif
87 #include "sys_mmap.inc"
88 #ifdef LISP_FEATURE_USE_SYS_MMAP
89 os_vm_address_t os_allocate(os_vm_size_t len) {
90 void* answer = sbcl_mmap(0, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
91 if (answer == MAP_FAILED) return 0;
92 return answer;
94 void os_deallocate(os_vm_address_t addr, os_vm_size_t len) {
95 sbcl_munmap(addr, len);
97 #else
98 os_vm_address_t
99 os_allocate(os_vm_size_t len)
101 return os_alloc_gc_space(0, MOVABLE, (os_vm_address_t)NULL, len);
104 void
105 os_deallocate(os_vm_address_t addr, os_vm_size_t len)
107 #ifdef LISP_FEATURE_WIN32
108 gc_assert(VirtualFree(addr, 0, MEM_RELEASE));
109 #else
110 if (sbcl_munmap(addr, len) == -1) perror("munmap");
111 #endif
113 #endif
116 os_get_errno(void)
118 return errno;
121 void
122 os_set_errno(int new_errno)
124 errno = new_errno;
127 #if defined LISP_FEATURE_SB_THREAD && defined LISP_FEATURE_UNIX && !defined USE_DARWIN_GCD_SEMAPHORES && !defined CANNOT_USE_POSIX_SEM_T
128 void
129 os_sem_init(os_sem_t *sem, unsigned int value)
131 if (-1==sem_init(sem, 0, value))
132 lose("os_sem_init(%p, %u): %s", sem, value, strerror(errno));
135 void
136 os_sem_wait(os_sem_t *sem)
138 while (-1 == sem_wait(sem))
139 if (EINTR!=errno)
140 lose("os_sem_wait(%p): %s", sem, strerror(errno));
143 void
144 os_sem_post(sem_t *sem)
146 if (-1 == sem_post(sem))
147 lose("os_sem_post(%p): %s", sem, strerror(errno));
150 void
151 os_sem_destroy(os_sem_t *sem)
153 if (-1==sem_destroy(sem))
154 lose("os_sem_destroy(%p): %s", sem, strerror(errno));
157 #endif
159 /* Genesis-time foreign fixups are resolved to linkage table locations
160 * and for each of them a record is added to the REQUIRED_FOREIGN_SYMBOLS
161 * vector, of the form "name" for a function reference,
162 * or ("name") for a data reference. "name" is a base-string.
164 * Before any code in lisp image can be called, we have to resolve all
165 * references to runtime foreign symbols that used to be static, adding linkage
166 * table entry for each element of REQUIRED_FOREIGN_SYMBOLS.
169 #ifndef LISP_FEATURE_WIN32
170 void *
171 os_dlsym_default(char *name)
173 void *frob = dlsym(RTLD_DEFAULT, name);
174 return frob;
176 #endif
178 int alien_linkage_table_n_prelinked;
179 extern lispobj* get_alien_linkage_table_initializer();
180 void os_link_runtime()
182 lispobj* table = get_alien_linkage_table_initializer();
183 if (table) {
184 // Prefill the alien linkage table so that shrinkwrapped executables which
185 // link in all their C library dependencies can avoid linking with -ldl
186 // but extern-alien still works for newly compiled code.
187 lispobj* ptr = table;
188 int n = alien_linkage_table_n_prelinked = *ptr++;
189 int index = 0;
190 for ( ; n-- ; index++ ) {
191 bool datap = *ptr == (lispobj)-1; // -1 can't be a function address
192 if (datap) ++ptr;
193 arch_write_linkage_table_entry(index, (void*)*ptr++, datap);
195 return;
198 struct vector* symbols = VECTOR(SymbolValue(REQUIRED_FOREIGN_SYMBOLS,0));
199 int n = alien_linkage_table_n_prelinked = vector_len(symbols);
200 int index;
201 for (index = 0 ; index < n ; ++index)
203 lispobj item = symbols->data[index];
204 bool datap = listp(item);
205 lispobj symbol_name = datap ? CONS(item)->car : item;
206 char *namechars = vector_sap(symbol_name);
207 void* result = os_dlsym_default(namechars);
209 if (result) {
210 arch_write_linkage_table_entry(index, result, datap);
211 } else { // startup might or might not work. ymmv
212 fprintf(stderr, "Missing required foreign symbol '%s'\n", namechars);
217 void os_unlink_runtime()
221 bool gc_managed_heap_space_p(lispobj addr)
223 if ((READ_ONLY_SPACE_START <= addr && addr < READ_ONLY_SPACE_END)
224 || (STATIC_SPACE_START <= addr && addr < STATIC_SPACE_END)
225 #if defined LISP_FEATURE_GENERATIONAL
226 || (DYNAMIC_SPACE_START <= addr &&
227 addr < (DYNAMIC_SPACE_START + dynamic_space_size))
228 || immobile_space_p(addr)
229 #else
230 || (DYNAMIC_0_SPACE_START <= addr &&
231 addr < DYNAMIC_0_SPACE_START + dynamic_space_size)
232 || (DYNAMIC_1_SPACE_START <= addr &&
233 addr < DYNAMIC_1_SPACE_START + dynamic_space_size)
234 #endif
235 #ifdef LISP_FEATURE_PERMGEN
236 || (PERMGEN_SPACE_START <= addr && addr < (uword_t)permgen_space_free_pointer)
237 #endif
238 #ifdef LISP_FEATURE_DARWIN_JIT
239 || (STATIC_CODE_SPACE_START <= addr && addr < STATIC_CODE_SPACE_END)
240 #endif
242 return 1;
243 return 0;
246 #ifndef LISP_FEATURE_WIN32
248 #if defined LISP_FEATURE_MIPS
249 #include <sys/utsname.h>
250 #endif
251 /* Remap a part of an already existing memory mapping from a file,
252 * and/or create a new mapping as need be */
253 void* load_core_bytes(int fd, os_vm_offset_t offset, os_vm_address_t addr, os_vm_size_t len,
254 int is_readonly_space)
256 #if defined LISP_FEATURE_MIPS
257 /* Of the few MIPS machines I have access to, one definitely exhibits a
258 * horrible bug that mmap() persists MAP_PRIVATE pages back to disk,
259 * even though we alwayas open a core file as O_RDONLY. This is a kooky criterion
260 * to restrict the test by, but I didn't want it to be more general */
261 static int buggy_map_private;
262 if (!buggy_map_private) {
263 struct utsname name;
264 uname(&name);
265 if (!strcmp(name.version, "#1 SMP PREEMPT Mon Aug 3 14:22:54 PDT 2015") &&
266 !strcmp(name.release, "4.1.4")) {
267 buggy_map_private = 1;
268 fprintf(stderr, "WARNING: assuming that MAP_PRIVATE does not work on this kernel\n");
269 } else {
270 // fprintf(stderr, "INFO: kernel looks OK: [%s] [%s]\n", name.release, name.version);
271 buggy_map_private = -1;
274 if (buggy_map_private == 1) {
275 off_t old = lseek(fd, 0, SEEK_CUR);
276 lseek(fd, offset, SEEK_SET);
277 read(fd, addr, len);
278 lseek(fd, old, SEEK_SET);
279 return addr;
281 #endif
282 int fail = 0;
283 os_vm_address_t actual;
284 int protection = 0, sharing = MAP_PRIVATE;
286 #ifdef LISP_FEATURE_DARWIN_JIT
287 protection = OS_VM_PROT_READ | (is_readonly_space ? OS_VM_PROT_EXECUTE : OS_VM_PROT_WRITE);
288 #else
289 /* If mapping to an OS-chosen address, then the assumption is that we're not going to
290 * execute nor write at the mapped address. (Because why would we ? The spaces from
291 * the core have a chosen address at this point) However, the addr=0 case is for
292 * 'editcore' which unfortunately _does_ write the memory. I'd prefer that it not,
293 * but that's not the concern here. */
294 protection = (addr ? (is_readonly_space ? OS_VM_PROT_READ : OS_VM_PROT_ALL)
295 : OS_VM_PROT_READ | OS_VM_PROT_WRITE);
296 if (is_readonly_space) sharing = MAP_SHARED;
297 #endif
299 #ifdef LISP_FEATURE_64_BIT
300 actual = sbcl_mmap(
301 #else
302 /* FIXME: why does using sbcl_mmap cause failure here? I would guess that it can't
303 * pass 'offset' correctly if LARGEFILE is mandatory, which it isn't on 64-bit.
304 * Deadlock should be impossible this early in core loading, I suppose, hence
305 * on one hand I don't care; but on the other, it would be nice to not to see
306 * any use of a potentially hooked mmap() API within this file. */
307 actual = mmap(
308 #endif
309 addr, len, protection,
310 // Do not pass MAP_FIXED with addr of 0, because most OSes disallow that.
311 sharing | (addr ? MAP_FIXED : 0),
312 fd, (off_t) offset);
313 if (actual == MAP_FAILED) {
314 if (errno == ENOMEM)
315 fprintf(stderr, "load_core_bytes: mmap(%p,%zu,...) failed with ENOMEM\n", addr, len);
316 else
317 perror("mmap");
318 fail = 1;
319 } else if (addr && (addr != actual)) {
320 fail = 1;
322 if (fail)
323 lose("load_core_bytes(%d,%p,%p,%zx) failed", fd, (void*)(uintptr_t)offset, addr, len);
324 return (void*)actual;
327 #ifdef LISP_FEATURE_DARWIN_JIT
328 void* load_core_bytes_jit(int fd, os_vm_offset_t offset, os_vm_address_t addr, os_vm_size_t len)
330 ssize_t count;
332 lseek(fd, offset, SEEK_SET);
334 size_t n_bytes = 65536;
335 char* buf = malloc(n_bytes);
337 while (len) {
338 count = read(fd, buf, n_bytes);
340 if (count <= -1) {
341 perror("read");
344 memcpy(addr, buf, count);
345 addr += count;
346 len -= count;
348 free(buf);
349 return (void*)0;
351 #endif
353 #endif
355 bool is_in_stack_space(lispobj ptr)
357 struct thread *th;
358 for_each_thread(th) {
359 if ((th->control_stack_start <= (lispobj *)ptr) &&
360 (th->control_stack_end >= (lispobj *)ptr)) {
361 return 1;
364 return 0;
367 bool gc_managed_addr_p(lispobj addr)
369 struct thread *th;
371 if (gc_managed_heap_space_p(addr))
372 return 1;
373 for_each_thread(th) {
374 if(th->control_stack_start <= (lispobj*)addr
375 && (lispobj*)addr < th->control_stack_end)
376 return 1;
377 if(th->binding_stack_start <= (lispobj*)addr
378 && (lispobj*)addr < th->binding_stack_start + BINDING_STACK_SIZE)
379 return 1;
381 return 0;
384 uword_t os_context_pc(os_context_t* context) {
385 return OS_CONTEXT_PC(context);
387 void set_os_context_pc(os_context_t* context, uword_t pc) {
388 OS_CONTEXT_PC(context) = pc;
390 os_context_register_t* os_context_pc_addr(os_context_t* context) {
391 return (os_context_register_t*)&(OS_CONTEXT_PC(context));
394 void *successful_malloc(size_t size)
396 void* result = malloc(size);
397 if (0 == result) {
398 lose("malloc(%zu) failure", size);
399 } else {
400 return result;
402 return (void *) NULL; /* dummy value: return something ... */
405 char *copied_string(char *string)
407 return strcpy(successful_malloc(1+strlen(string)), string);
410 lispobj* duplicate_codeblob_offheap(lispobj code)
412 int nwords = code_total_nwords((struct code*)(code-OTHER_POINTER_LOWTAG));
413 lispobj* mem = malloc((nwords+1) * N_WORD_BYTES);
414 if ((uword_t)mem & LOWTAG_MASK) lose("this is unexpected\n");
415 // add 1 word if not dualword aligned
416 lispobj* copy = (lispobj*)((uword_t)mem + ((uword_t)mem & N_WORD_BYTES));
417 memcpy(copy, (char*)code-OTHER_POINTER_LOWTAG, nwords<<WORD_SHIFT);
418 return mem;
421 #ifdef LISP_FEATURE_UNIX
422 void
423 os_protect(os_vm_address_t address, os_vm_size_t length, os_vm_prot_t prot)
425 #if defined LISP_FEATURE_SOFT_CARD_MARKS && !defined LISP_FEATURE_DARWIN_JIT
426 // dynamic space should not have protections manipulated
427 if (find_page_index(address) >= 0)
428 lose("unexpected call to os_protect with software card marks");
429 #endif
430 if (sbcl_mprotect(address, length, prot) < 0) {
431 #ifdef LISP_FEATURE_LINUX
432 if (errno == ENOMEM) {
433 lose("An mprotect call failed with ENOMEM. This probably means that the maximum amount\n"
434 "of separate memory mappings was exceeded. To fix the problem, either increase\n"
435 "the maximum with e.g. 'echo 262144 > /proc/sys/vm/max_map_count' or recompile\n"
436 "SBCL with a larger value for GENCGC-PAGE-BYTES in\n"
437 "'src/compiler/"SBCL_TARGET_ARCHITECTURE_STRING"/parms.lisp'.");
439 #endif
440 lose("mprotect(%p,%lx,%d) error %d", address, (long)length, prot, errno);
443 #endif
445 #ifdef TRACE_MMAP_SYSCALLS
446 FILE* mmgr_debug_logfile;
447 // interceptors for debugging so I don't have to reinvent them every time
448 static void decode_protbits(int prot, char result[4]) {
449 result[0] = (prot & PROT_READ) ? 'r' : '-';
450 result[1] = (prot & PROT_WRITE) ? 'w' : '-';
451 result[2] = (prot & PROT_EXEC) ? 'x' : '-';
452 result[3] = 0;
454 static void decode_flagbits(int flags, char result[40]) {
455 char *p = result;
456 char delim = '{';
457 #define APPEND(str) { *p++ = delim; delim = '|'; strcpy(p, str); p += sizeof str-1; }
458 if (flags & MAP_PRIVATE) APPEND("Pvt");
459 if (flags & MAP_ANON) APPEND("Anon");
460 if (flags & MAP_NORESERVE) APPEND("NoRsv");
461 if (flags & MAP_JIT) APPEND("JIT");
462 #undef APPEND
463 strcpy(p, "}");
465 void* traced_mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset) {
466 char decoded_prot[4], decoded_flags[40];
467 decode_protbits(prot, decoded_prot);
468 decode_flagbits(flags, decoded_flags);
469 void* result = mmap(addr, length, prot, flags, fd, offset);
470 fprintf(mmgr_debug_logfile, "mmap(%p,%lx,%s,%s,%d,%llx)=%p\n", addr, length,
471 decoded_prot, decoded_flags, fd, offset, result);
472 return result;
474 int traced_munmap(void* addr, size_t length) {
475 int result = munmap(addr, length);
476 fprintf(mmgr_debug_logfile, "munmap(%p,%lx)=%d\n", addr, length, result);
477 return result;
479 int sbcl_mprotect(void* addr, size_t length, int prot) {
480 char decoded_prot[4];
481 decode_protbits(prot, decoded_prot);
482 int result = mprotect(addr, length, prot);
483 fprintf(mmgr_debug_logfile, "mprotect(%p,%lx,%s)=%d\n", addr, length, decoded_prot, result);
484 return result;
486 #endif
488 #ifdef LISP_FEATURE_ELF
489 #include <elf.h>
491 static off_t lisp_rel_section_offset;
492 static ssize_t lisp_rel_section_size;
494 // Return the offset to a file section named 'lisp.core' if there is one
495 off_t search_for_elf_core(int fd)
497 Elf64_Ehdr ehdr;
499 if (lseek(fd, 0, SEEK_SET) != 0 ||
500 read(fd, &ehdr, sizeof ehdr) != sizeof ehdr) {
501 fprintf(stderr, "failed to read elf header\n");
502 return 0;
504 unsigned long result = 0;
505 char* shdrs = 0;
506 char * shstrtab_strbuf = 0;
508 // Slurp in all the section headers
509 int nbytes = ehdr.e_shentsize * ehdr.e_shnum;
510 if ((shdrs = malloc(nbytes)) == NULL ||
511 lseek(fd, ehdr.e_shoff, SEEK_SET) != (Elf64_Sxword)ehdr.e_shoff ||
512 read(fd, shdrs, nbytes) != nbytes)
513 goto done;
514 Elf64_Shdr* shdr = (Elf64_Shdr*)(shdrs + ehdr.e_shentsize * ehdr.e_shstrndx);
515 // Slurp the string table
516 if ((shstrtab_strbuf = malloc(shdr->sh_size)) == NULL ||
517 lseek(fd, shdr->sh_offset, SEEK_SET) != (Elf64_Sxword)shdr->sh_offset ||
518 read(fd, shstrtab_strbuf, shdr->sh_size) != (Elf64_Sxword)shdr->sh_size)
519 goto done;
520 // Scan the section header string table to locate both the 'lisp.core' and
521 // 'lisp.rel' sections. There might not be a lisp.rel section, but don't stop
522 // looking after seeing just one. The order is unspecified.
523 int i;
524 for(i=1;i<ehdr.e_shnum;++i) { // skip section 0 which is the null section
525 Elf64_Shdr* h = (Elf64_Shdr*)(shdrs + ehdr.e_shentsize * i);
526 if (!strcmp(&shstrtab_strbuf[h->sh_name], "lisp.core")) {
527 gc_assert(!result); // there can be only 1
528 result = h->sh_offset;
529 if (lisp_rel_section_offset) break; // stop when both sections seen
530 } else if (!strcmp(&shstrtab_strbuf[h->sh_name], "lisp.rel")) {
531 gc_assert(!lisp_rel_section_offset); // there can be only 1
532 lisp_rel_section_offset = h->sh_offset;
533 lisp_rel_section_size = h->sh_size;
534 if (result) break; // stop when both sections seen
537 done:
538 if (shstrtab_strbuf) free(shstrtab_strbuf);
539 if (shdrs) free(shdrs);
540 return result;
543 int apply_pie_relocs(long code_space_translation,
544 long dynamic_space_translation,
545 int fd)
547 // If dynamic space was relocated, let coreparse fix everything by itself.
548 // The entire heap must be walked anyway to fix intra-dynamic-space pointers.
549 if (dynamic_space_translation != 0 || lisp_rel_section_size == 0)
550 return 0;
551 // Otherwise, we're going to make it appear that code space was supposed
552 // to have been mapped where it actually was.
553 int n_relocs = lisp_rel_section_size / sizeof (long);
554 unsigned long **ptrs = malloc(n_relocs * sizeof (long));
555 if (!ptrs) return 0;
556 int success = 0;
557 if (lseek(fd, lisp_rel_section_offset, SEEK_SET) == lisp_rel_section_offset &&
558 read(fd, ptrs, lisp_rel_section_size) == lisp_rel_section_size) {
559 int i;
560 // element 0 of the array is not used
561 for (i = 1; i<n_relocs; ++i) {
562 unsigned long *vaddr = ptrs[i];
563 *vaddr += code_space_translation;
565 success = 1;
567 free(ptrs);
568 return success;
570 #endif