2 * the Win32 incarnation of OS-dependent routines. See also
3 * $(sbcl_arch)-win32-os.c
5 * This file (along with os.h) exports an OS-independent interface to
6 * the operating system VM facilities. Surprise surprise, this
7 * interface looks a lot like the Mach interface (but simpler in some
8 * places). For some operating systems, a subset of these functions
9 * will have to be emulated.
13 * This software is part of the SBCL system. See the README file for
16 * This software is derived from the CMU CL system, which was
17 * written at Carnegie Mellon University and released into the
18 * public domain. The software is in the public domain and is
19 * provided with absolutely no warranty. See the COPYING and CREDITS
20 * files for more information.
24 * This file was copied from the Linux version of the same, and
25 * likely still has some linuxisms in it have haven't been elimiated
31 #include <sys/param.h>
40 #include "interrupt.h"
45 #include "genesis/primitive-objects.h"
48 #include <sys/types.h>
61 size_t os_vm_page_size;
64 #include "gencgc-internal.h"
67 int linux_sparc_siginfo_bug = 0;
68 int linux_supports_futex=0;
71 /* The exception handling function looks like this: */
72 EXCEPTION_DISPOSITION handle_exception(EXCEPTION_RECORD *,
73 struct lisp_exception_frame *,
79 static void *get_seh_frame(void)
82 asm volatile ("movl %%fs:0,%0": "=r" (retval));
86 static void set_seh_frame(void *frame)
88 asm volatile ("movl %0,%%fs:0": : "r" (frame));
92 static struct lisp_exception_frame *find_our_seh_frame(void)
94 struct lisp_exception_frame *frame = get_seh_frame();
96 while (frame->handler != handle_exception)
97 frame = frame->next_frame;
102 inline static void *get_stack_frame(void)
105 asm volatile ("movl %%ebp,%0": "=r" (retval));
110 void os_init(char *argv[], char *envp[])
112 SYSTEM_INFO system_info;
114 GetSystemInfo(&system_info);
115 os_vm_page_size = system_info.dwPageSize;
117 base_seh_frame = get_seh_frame();
122 * So we have three fun scenarios here.
124 * First, we could be being called to reserve the memory areas
125 * during initialization (prior to loading the core file).
127 * Second, we could be being called by the GC to commit a page
128 * that has just been decommitted (for easy zero-fill).
130 * Third, we could be being called by create_thread_struct()
131 * in order to create the sundry and various stacks.
133 * The third case is easy to pick out because it passes an
136 * The second case is easy to pick out because it will be for
137 * a range of memory that is MEM_RESERVE rather than MEM_FREE.
139 * The second case is also an easy implement, because we leave
140 * the memory as reserved (since we do lazy commits).
144 os_validate(os_vm_address_t addr, os_vm_size_t len)
146 MEMORY_BASIC_INFORMATION mem_info;
149 /* the simple case first */
150 os_vm_address_t real_addr;
151 if (!(real_addr = VirtualAlloc(addr, len, MEM_COMMIT, PAGE_EXECUTE_READWRITE))) {
152 fprintf(stderr, "VirtualAlloc: 0x%lx.\n", GetLastError());
159 if (!VirtualQuery(addr, &mem_info, sizeof mem_info)) {
160 fprintf(stderr, "VirtualQuery: 0x%lx.\n", GetLastError());
164 if ((mem_info.State == MEM_RESERVE) && (mem_info.RegionSize >=len)) {
165 /* It would be correct to return here. However, support for Wine
166 * is beneficial, and Wine has a strange behavior in this
167 * department. It reports all memory below KERNEL32.DLL as
168 * reserved, but disallows MEM_COMMIT.
170 * Let's work around it: reserve the region we need for a second
171 * time. The second reservation is documented to fail on normal NT
172 * family, but it will succeed on Wine if this region is
175 VirtualAlloc(addr, len, MEM_RESERVE, PAGE_EXECUTE_READWRITE);
176 /* If it is wine, the second call has succeded, and now the region
177 * is really reserved. */
181 if (mem_info.State == MEM_RESERVE) {
182 fprintf(stderr, "validation of reserved space too short.\n");
186 if (!VirtualAlloc(addr, len, (mem_info.State == MEM_RESERVE)? MEM_COMMIT: MEM_RESERVE, PAGE_EXECUTE_READWRITE)) {
187 fprintf(stderr, "VirtualAlloc: 0x%lx.\n", GetLastError());
195 * For os_invalidate(), we merely decommit the memory rather than
196 * freeing the address space. This loses when freeing per-thread
197 * data and related memory since it leaks address space. It's not
198 * too lossy, however, since the two scenarios I'm aware of are
199 * fd-stream buffers, which are pooled rather than torched, and
200 * thread information, which I hope to pool (since windows creates
201 * threads at its own whim, and we probably want to be able to
202 * have them callback without funky magic on the part of the user,
203 * and full-on thread allocation is fairly heavyweight). Someone
204 * will probably shoot me down on this with some pithy comment on
205 * the use of (setf symbol-value) on a special variable. I'm happy
210 os_invalidate(os_vm_address_t addr, os_vm_size_t len)
212 if (!VirtualFree(addr, len, MEM_DECOMMIT)) {
213 fprintf(stderr, "VirtualFree: 0x%lx.\n", GetLastError());
218 * os_map() is called to map a chunk of the core file into memory.
220 * Unfortunately, Windows semantics completely screws this up, so
221 * we just add backing store from the swapfile to where the chunk
222 * goes and read it up like a normal file. We could consider using
223 * a lazy read (demand page) setup, but that would mean keeping an
224 * open file pointer for the core indefinately (and be one more
225 * thing to maintain).
229 os_map(int fd, int offset, os_vm_address_t addr, os_vm_size_t len)
234 fprintf(stderr, "os_map: %d, 0x%x, %p, 0x%x.\n", fd, offset, addr, len);
238 if (!VirtualAlloc(addr, len, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) {
239 fprintf(stderr, "VirtualAlloc: 0x%lx.\n", GetLastError());
240 lose("os_map: VirtualAlloc failure");
243 if (lseek(fd, offset, SEEK_SET) == -1) {
244 lose("os_map: Seek failure.");
247 count = read(fd, addr, len);
249 fprintf(stderr, "expected 0x%x, read 0x%x.\n", len, count);
250 lose("os_map: Failed to read enough bytes.");
256 static DWORD os_protect_modes[8] = {
263 PAGE_EXECUTE_READWRITE,
264 PAGE_EXECUTE_READWRITE,
268 os_protect(os_vm_address_t address, os_vm_size_t length, os_vm_prot_t prot)
272 if (!VirtualProtect(address, length, os_protect_modes[prot], &old_prot)) {
273 fprintf(stderr, "VirtualProtect failed, code 0x%lx.\n", GetLastError());
278 /* FIXME: Now that FOO_END, rather than FOO_SIZE, is the fundamental
279 * description of a space, we could probably punt this and just do
280 * (FOO_START <= x && x < FOO_END) everywhere it's called. */
282 in_range_p(os_vm_address_t a, lispobj sbeg, size_t slen)
284 char* beg = (char*)((long)sbeg);
285 char* end = (char*)((long)sbeg) + slen;
286 char* adr = (char*)a;
287 return (adr >= beg && adr < end);
291 is_linkage_table_addr(os_vm_address_t addr)
293 return in_range_p(addr, LINKAGE_TABLE_SPACE_START, LINKAGE_TABLE_SPACE_END);
297 is_valid_lisp_addr(os_vm_address_t addr)
300 if(in_range_p(addr, READ_ONLY_SPACE_START, READ_ONLY_SPACE_SIZE) ||
301 in_range_p(addr, STATIC_SPACE_START , STATIC_SPACE_SIZE) ||
302 in_range_p(addr, DYNAMIC_SPACE_START , dynamic_space_size))
304 for_each_thread(th) {
305 if(((os_vm_address_t)th->control_stack_start <= addr) && (addr < (os_vm_address_t)th->control_stack_end))
307 if(in_range_p(addr, (unsigned long)th->binding_stack_start, BINDING_STACK_SIZE))
313 /* A tiny bit of interrupt.c state we want our paws on. */
314 extern boolean internal_errors_enabled;
316 #if defined(LISP_FEATURE_X86)
318 handle_single_step(os_context_t *ctx)
320 if (!single_stepping)
323 /* We are doing a displaced instruction. At least function
324 * end breakpoints use this. */
325 restore_breakpoint_from_single_step(ctx);
331 #ifdef LISP_FEATURE_UD2_BREAKPOINTS
332 #define SBCL_EXCEPTION_BREAKPOINT EXCEPTION_ILLEGAL_INSTRUCTION
333 #define TRAP_CODE_WIDTH 2
335 #define SBCL_EXCEPTION_BREAKPOINT EXCEPTION_BREAKPOINT
336 #define TRAP_CODE_WIDTH 1
340 handle_breakpoint_trap(os_context_t *ctx)
342 #ifdef LISP_FEATURE_UD2_BREAKPOINTS
343 if (((unsigned short *)((ctx->win32_context)->Eip))[0] != 0x0b0f)
347 /* Unlike some other operating systems, Win32 leaves EIP
348 * pointing to the breakpoint instruction. */
349 ctx->Eip += TRAP_CODE_WIDTH;
351 /* Now EIP points just after the INT3 byte and aims at the
352 * 'kind' value (eg trap_Cerror). */
353 unsigned char trap = *(unsigned char *)(*os_context_pc_addr(ctx));
355 /* This is just for info in case the monitor wants to print an
357 current_control_stack_pointer =
358 (lispobj *)*os_context_sp_addr(ctx);
360 handle_trap(ctx, trap);
362 /* Done, we're good to go! */
367 handle_access_violation(os_context_t *ctx,
368 EXCEPTION_RECORD *exception_record,
371 if (!(is_valid_lisp_addr(fault_address)
372 || is_linkage_table_addr(fault_address)))
375 /* Pick off GC-related memory fault next. */
376 MEMORY_BASIC_INFORMATION mem_info;
378 if (!VirtualQuery(fault_address, &mem_info, sizeof mem_info)) {
379 fprintf(stderr, "VirtualQuery: 0x%lx.\n", GetLastError());
380 lose("handle_exception: VirtualQuery failure");
383 if (mem_info.State == MEM_RESERVE) {
384 /* First use new page, lets get some memory for it. */
385 if (!VirtualAlloc(mem_info.BaseAddress, os_vm_page_size,
386 MEM_COMMIT, PAGE_EXECUTE_READWRITE)) {
387 fprintf(stderr, "VirtualAlloc: 0x%lx.\n", GetLastError());
388 lose("handle_exception: VirtualAlloc failure");
392 * Now, if the page is supposedly write-protected and this
393 * is a write, tell the gc that it's been hit.
395 * FIXME: Are we supposed to fall-through to the Lisp
396 * exception handler if the gc doesn't take the wp violation?
398 if (exception_record->ExceptionInformation[0]) {
399 page_index_t index = find_page_index(fault_address);
400 if ((index != -1) && (page_table[index].write_protected)) {
401 gencgc_handle_wp_violation(fault_address);
407 } else if (gencgc_handle_wp_violation(fault_address)) {
408 /* gc accepts the wp violation, so resume where we left off. */
416 signal_internal_error_or_lose(os_context_t *ctx,
417 EXCEPTION_RECORD *exception_record,
421 * If we fall through to here then we need to either forward
422 * the exception to the lisp-side exception handler if it's
423 * set up, or drop to LDB.
426 if (internal_errors_enabled) {
428 lispobj exception_record_sap;
430 /* We're making the somewhat arbitrary decision that having
431 * internal errors enabled means that lisp has sufficient
432 * marbles to be able to handle exceptions, but exceptions
433 * aren't supposed to happen during cold init or reinit
436 fake_foreign_function_call(ctx);
438 /* Allocate the SAP objects while the "interrupts" are still
440 context_sap = alloc_sap(ctx);
441 exception_record_sap = alloc_sap(exception_record);
443 /* The exception system doesn't automatically clear pending
444 * exceptions, so we lose as soon as we execute any FP
445 * instruction unless we do this first. */
448 /* Call into lisp to handle things. */
449 funcall2(StaticSymbolFunction(HANDLE_WIN32_EXCEPTION), context_sap,
450 exception_record_sap);
452 /* If Lisp doesn't nlx, we need to put things back. */
453 undo_fake_foreign_function_call(ctx);
455 /* FIXME: HANDLE-WIN32-EXCEPTION should be allowed to decline */
459 fprintf(stderr, "Exception Code: 0x%lx.\n", exception_record->ExceptionCode);
460 fprintf(stderr, "Faulting IP: 0x%lx.\n", (DWORD)exception_record->ExceptionAddress);
461 if (exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
462 MEMORY_BASIC_INFORMATION mem_info;
464 if (VirtualQuery(fault_address, &mem_info, sizeof mem_info)) {
465 fprintf(stderr, "page status: 0x%lx.\n", mem_info.State);
468 fprintf(stderr, "Was writing: %ld, where: 0x%lx.\n",
469 exception_record->ExceptionInformation[0],
470 (DWORD)fault_address);
475 fake_foreign_function_call(ctx);
476 lose("Exception too early in cold init, cannot continue.");
480 * A good explanation of the exception handling semantics is
481 * http://win32assembly.online.fr/Exceptionhandling.html .
484 EXCEPTION_DISPOSITION
485 handle_exception(EXCEPTION_RECORD *exception_record,
486 struct lisp_exception_frame *exception_frame,
488 void *dispatcher_context)
490 if (exception_record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)) {
491 /* If we're being unwound, be graceful about it. */
493 /* Undo any dynamic bindings. */
494 unbind_to_here(exception_frame->bindstack_pointer,
495 arch_os_get_current_thread());
497 return ExceptionContinueSearch;
500 DWORD code = exception_record->ExceptionCode;
502 /* For EXCEPTION_ACCESS_VIOLATION only. */
503 void *fault_address = (void *)exception_record->ExceptionInformation[1];
505 /* This function will become unwieldy. Let's cut it down into
506 * pieces based on the different exception codes. Each exception
507 * code handler gets the chance to decline by returning non-zero if it
512 case EXCEPTION_ACCESS_VIOLATION:
513 rc = handle_access_violation(
514 ctx, exception_record, fault_address);
517 case SBCL_EXCEPTION_BREAKPOINT:
518 rc = handle_breakpoint_trap(ctx);
521 #if defined(LISP_FEATURE_X86)
522 case EXCEPTION_SINGLE_STEP:
523 rc = handle_single_step(ctx);
532 /* All else failed, drop through to the lisp-side exception handler. */
533 signal_internal_error_or_lose(ctx, exception_record, fault_address);
535 return ExceptionContinueExecution;
539 wos_install_interrupt_handlers(struct lisp_exception_frame *handler)
541 handler->next_frame = get_seh_frame();
542 handler->handler = &handle_exception;
543 set_seh_frame(handler);
546 void bcopy(const void *src, void *dest, size_t n)
548 MoveMemory(dest, src, n);
552 * The stubs below are replacements for the windows versions,
553 * which can -fail- when used in our memory spaces because they
554 * validate the memory spaces they are passed in a way that
555 * denies our exception handler a chance to run.
558 void *memmove(void *dest, const void *src, size_t n)
562 for (i = 0; i < n; i++) *(((char *)dest)+i) = *(((char *)src)+i);
564 while (n--) *(((char *)dest)+n) = *(((char *)src)+n);
569 void *memcpy(void *dest, const void *src, size_t n)
571 while (n--) *(((char *)dest)+n) = *(((char *)src)+n);
575 char *dirname(char *path)
577 static char buf[PATH_MAX + 1];
578 size_t pathlen = strlen(path);
581 if (pathlen >= sizeof(buf)) {
582 lose("Pathname too long in dirname.\n");
587 for (i = pathlen; i >= 0; --i) {
588 if (buf[i] == '/' || buf[i] == '\\') {
597 /* This is a manually-maintained version of ldso_stubs.S. */
599 void __stdcall RtlUnwind(void *, void *, void *, void *); /* I don't have winternl.h */
604 FlushConsoleInputBuffer(0);
605 FormatMessageA(0, 0, 0, 0, 0, 0, 0);
609 GetConsoleOutputCP();
611 GetExitCodeProcess(0, 0);
614 GetProcAddress(0, 0);
615 GetProcessTimes(0, 0, 0, 0, 0);
616 GetSystemTimeAsFileTime(0);
619 PeekConsoleInput(0, 0, 0, 0);
620 PeekNamedPipe(0, 0, 0, 0, 0, 0);
621 ReadFile(0, 0, 0, 0, 0);
623 WriteFile(0, 0, 0, 0, 0);
633 RtlUnwind(0, 0, 0, 0);
634 MapViewOfFile(0,0,0,0,0);
636 FlushViewOfFile(0,0);
637 #ifndef LISP_FEATURE_SB_UNICODE
638 CreateDirectoryA(0,0);
639 CreateFileMappingA(0,0,0,0,0,0);
640 CreateFileA(0,0,0,0,0,0,0);
641 GetComputerNameA(0, 0);
642 GetCurrentDirectoryA(0,0);
643 GetEnvironmentVariableA(0, 0, 0);
644 GetFileAttributesA(0);
647 SHGetFolderPathA(0, 0, 0, 0, 0);
648 SetCurrentDirectoryA(0);
649 SetEnvironmentVariableA(0, 0);
651 CreateDirectoryW(0,0);
652 CreateFileMappingW(0,0,0,0,0,0);
653 CreateFileW(0,0,0,0,0,0,0);
654 FormatMessageW(0, 0, 0, 0, 0, 0, 0);
655 GetComputerNameW(0, 0);
656 GetCurrentDirectoryW(0,0);
657 GetEnvironmentVariableW(0, 0, 0);
658 GetFileAttributesW(0);
661 SHGetFolderPathW(0, 0, 0, 0, 0);
662 SetCurrentDirectoryW(0);
663 SetEnvironmentVariableW(0, 0);
669 os_get_runtime_executable_path(int external)
671 char path[MAX_PATH + 1];
672 DWORD bufsize = sizeof(path);
675 if ((size = GetModuleFileNameA(NULL, path, bufsize)) == 0)
677 else if (size == bufsize && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
680 return copied_string(path);