win32-os.c: Split up handle_exception
[sbcl.git] / src / runtime / win32-os.c
1 /*
2  * the Win32 incarnation of OS-dependent routines.  See also
3  * $(sbcl_arch)-win32-os.c
4  *
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.
10  */
11
12 /*
13  * This software is part of the SBCL system. See the README file for
14  * more information.
15  *
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.
21  */
22
23 /*
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
26  * yet.
27  */
28
29 #include <malloc.h>
30 #include <stdio.h>
31 #include <sys/param.h>
32 #include <sys/file.h>
33 #include <io.h>
34 #include "sbcl.h"
35 #include "./signal.h"
36 #include "os.h"
37 #include "arch.h"
38 #include "globals.h"
39 #include "sbcl.h"
40 #include "interrupt.h"
41 #include "interr.h"
42 #include "lispregs.h"
43 #include "runtime.h"
44 #include "alloc.h"
45 #include "genesis/primitive-objects.h"
46 #include "dynbind.h"
47
48 #include <sys/types.h>
49 #include <signal.h>
50 #include <sys/time.h>
51 #include <sys/stat.h>
52 #include <unistd.h>
53
54 #include <math.h>
55 #include <float.h>
56
57 #include <excpt.h>
58
59 #include "validate.h"
60 #include "thread.h"
61 size_t os_vm_page_size;
62
63 #include "gc.h"
64 #include "gencgc-internal.h"
65
66 #if 0
67 int linux_sparc_siginfo_bug = 0;
68 int linux_supports_futex=0;
69 #endif
70
71 /* The exception handling function looks like this: */
72 EXCEPTION_DISPOSITION handle_exception(EXCEPTION_RECORD *,
73                                        struct lisp_exception_frame *,
74                                        CONTEXT *,
75                                        void *);
76
77 void *base_seh_frame;
78
79 static void *get_seh_frame(void)
80 {
81     void* retval;
82     asm volatile ("movl %%fs:0,%0": "=r" (retval));
83     return retval;
84 }
85
86 static void set_seh_frame(void *frame)
87 {
88     asm volatile ("movl %0,%%fs:0": : "r" (frame));
89 }
90
91 #if 0
92 static struct lisp_exception_frame *find_our_seh_frame(void)
93 {
94     struct lisp_exception_frame *frame = get_seh_frame();
95
96     while (frame->handler != handle_exception)
97         frame = frame->next_frame;
98
99     return frame;
100 }
101
102 inline static void *get_stack_frame(void)
103 {
104     void* retval;
105     asm volatile ("movl %%ebp,%0": "=r" (retval));
106     return retval;
107 }
108 #endif
109
110 void os_init(char *argv[], char *envp[])
111 {
112     SYSTEM_INFO system_info;
113
114     GetSystemInfo(&system_info);
115     os_vm_page_size = system_info.dwPageSize;
116
117     base_seh_frame = get_seh_frame();
118 }
119
120
121 /*
122  * So we have three fun scenarios here.
123  *
124  * First, we could be being called to reserve the memory areas
125  * during initialization (prior to loading the core file).
126  *
127  * Second, we could be being called by the GC to commit a page
128  * that has just been decommitted (for easy zero-fill).
129  *
130  * Third, we could be being called by create_thread_struct()
131  * in order to create the sundry and various stacks.
132  *
133  * The third case is easy to pick out because it passes an
134  * addr of 0.
135  *
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.
138  *
139  * The second case is also an easy implement, because we leave
140  * the memory as reserved (since we do lazy commits).
141  */
142
143 os_vm_address_t
144 os_validate(os_vm_address_t addr, os_vm_size_t len)
145 {
146     MEMORY_BASIC_INFORMATION mem_info;
147
148     if (!addr) {
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());
153             return 0;
154         }
155
156         return real_addr;
157     }
158
159     if (!VirtualQuery(addr, &mem_info, sizeof mem_info)) {
160         fprintf(stderr, "VirtualQuery: 0x%lx.\n", GetLastError());
161         return 0;
162     }
163
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.
169        *
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
173        * actually free.
174        */
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. */
178       return addr;
179     }
180
181     if (mem_info.State == MEM_RESERVE) {
182         fprintf(stderr, "validation of reserved space too short.\n");
183         fflush(stderr);
184     }
185
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());
188         return 0;
189     }
190
191     return addr;
192 }
193
194 /*
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
206  * for them.
207  */
208
209 void
210 os_invalidate(os_vm_address_t addr, os_vm_size_t len)
211 {
212     if (!VirtualFree(addr, len, MEM_DECOMMIT)) {
213         fprintf(stderr, "VirtualFree: 0x%lx.\n", GetLastError());
214     }
215 }
216
217 /*
218  * os_map() is called to map a chunk of the core file into memory.
219  *
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).
226  */
227
228 os_vm_address_t
229 os_map(int fd, int offset, os_vm_address_t addr, os_vm_size_t len)
230 {
231     os_vm_size_t count;
232
233 #if 0
234     fprintf(stderr, "os_map: %d, 0x%x, %p, 0x%x.\n", fd, offset, addr, len);
235     fflush(stderr);
236 #endif
237
238     if (!VirtualAlloc(addr, len, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) {
239         fprintf(stderr, "VirtualAlloc: 0x%lx.\n", GetLastError());
240         lose("os_map: VirtualAlloc failure");
241     }
242
243     if (lseek(fd, offset, SEEK_SET) == -1) {
244         lose("os_map: Seek failure.");
245     }
246
247     count = read(fd, addr, len);
248     if (count != len) {
249         fprintf(stderr, "expected 0x%x, read 0x%x.\n", len, count);
250         lose("os_map: Failed to read enough bytes.");
251     }
252
253     return addr;
254 }
255
256 static DWORD os_protect_modes[8] = {
257     PAGE_NOACCESS,
258     PAGE_READONLY,
259     PAGE_READWRITE,
260     PAGE_READWRITE,
261     PAGE_EXECUTE,
262     PAGE_EXECUTE_READ,
263     PAGE_EXECUTE_READWRITE,
264     PAGE_EXECUTE_READWRITE,
265 };
266
267 void
268 os_protect(os_vm_address_t address, os_vm_size_t length, os_vm_prot_t prot)
269 {
270     DWORD old_prot;
271
272     if (!VirtualProtect(address, length, os_protect_modes[prot], &old_prot)) {
273         fprintf(stderr, "VirtualProtect failed, code 0x%lx.\n", GetLastError());
274         fflush(stderr);
275     }
276 }
277
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. */
281 static boolean
282 in_range_p(os_vm_address_t a, lispobj sbeg, size_t slen)
283 {
284     char* beg = (char*)((long)sbeg);
285     char* end = (char*)((long)sbeg) + slen;
286     char* adr = (char*)a;
287     return (adr >= beg && adr < end);
288 }
289
290 boolean
291 is_linkage_table_addr(os_vm_address_t addr)
292 {
293     return in_range_p(addr, LINKAGE_TABLE_SPACE_START, LINKAGE_TABLE_SPACE_END);
294 }
295
296 boolean
297 is_valid_lisp_addr(os_vm_address_t addr)
298 {
299     struct thread *th;
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))
303         return 1;
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))
306             return 1;
307         if(in_range_p(addr, (unsigned long)th->binding_stack_start, BINDING_STACK_SIZE))
308             return 1;
309     }
310     return 0;
311 }
312
313 /* A tiny bit of interrupt.c state we want our paws on. */
314 extern boolean internal_errors_enabled;
315
316 #if defined(LISP_FEATURE_X86)
317 static int
318 handle_single_step(os_context_t *ctx)
319 {
320     if (!single_stepping)
321         return -1;
322
323     /* We are doing a displaced instruction. At least function
324      * end breakpoints use this. */
325     restore_breakpoint_from_single_step(ctx);
326
327     return 0;
328 }
329 #endif
330
331 #ifdef LISP_FEATURE_UD2_BREAKPOINTS
332 #define SBCL_EXCEPTION_BREAKPOINT EXCEPTION_ILLEGAL_INSTRUCTION
333 #define TRAP_CODE_WIDTH 2
334 #else
335 #define SBCL_EXCEPTION_BREAKPOINT EXCEPTION_BREAKPOINT
336 #define TRAP_CODE_WIDTH 1
337 #endif
338
339 static int
340 handle_breakpoint_trap(os_context_t *ctx)
341 {
342 #ifdef LISP_FEATURE_UD2_BREAKPOINTS
343     if (((unsigned short *)((ctx->win32_context)->Eip))[0] != 0x0b0f)
344         return -1;
345 #endif
346
347     /* Unlike some other operating systems, Win32 leaves EIP
348      * pointing to the breakpoint instruction. */
349     ctx->Eip += TRAP_CODE_WIDTH;
350
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));
354
355     /* This is just for info in case the monitor wants to print an
356      * approximation. */
357     current_control_stack_pointer =
358         (lispobj *)*os_context_sp_addr(ctx);
359
360     handle_trap(ctx, trap);
361
362     /* Done, we're good to go! */
363     return 0;
364 }
365
366 static int
367 handle_access_violation(os_context_t *ctx,
368                         EXCEPTION_RECORD *exception_record,
369                         void *fault_address)
370 {
371     if (!(is_valid_lisp_addr(fault_address)
372           || is_linkage_table_addr(fault_address)))
373         return -1;
374
375     /* Pick off GC-related memory fault next. */
376     MEMORY_BASIC_INFORMATION mem_info;
377
378     if (!VirtualQuery(fault_address, &mem_info, sizeof mem_info)) {
379         fprintf(stderr, "VirtualQuery: 0x%lx.\n", GetLastError());
380         lose("handle_exception: VirtualQuery failure");
381     }
382
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");
389
390         } else {
391             /*
392              * Now, if the page is supposedly write-protected and this
393              * is a write, tell the gc that it's been hit.
394              *
395              * FIXME: Are we supposed to fall-through to the Lisp
396              * exception handler if the gc doesn't take the wp violation?
397              */
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);
402                 }
403             }
404             return 0;
405         }
406
407     } else if (gencgc_handle_wp_violation(fault_address)) {
408         /* gc accepts the wp violation, so resume where we left off. */
409         return 0;
410     }
411
412     return -1;
413 }
414
415 static void
416 signal_internal_error_or_lose(os_context_t *ctx,
417                               EXCEPTION_RECORD *exception_record,
418                               void *fault_address)
419 {
420     /*
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.
424      */
425
426     if (internal_errors_enabled) {
427         lispobj context_sap;
428         lispobj exception_record_sap;
429
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
434          * anyway. */
435
436         fake_foreign_function_call(ctx);
437
438         /* Allocate the SAP objects while the "interrupts" are still
439          * disabled. */
440         context_sap = alloc_sap(ctx);
441         exception_record_sap = alloc_sap(exception_record);
442
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. */
446         _clearfp();
447
448         /* Call into lisp to handle things. */
449         funcall2(StaticSymbolFunction(HANDLE_WIN32_EXCEPTION), context_sap,
450                  exception_record_sap);
451
452         /* If Lisp doesn't nlx, we need to put things back. */
453         undo_fake_foreign_function_call(ctx);
454
455         /* FIXME: HANDLE-WIN32-EXCEPTION should be allowed to decline */
456         return;
457     }
458
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;
463
464         if (VirtualQuery(fault_address, &mem_info, sizeof mem_info)) {
465             fprintf(stderr, "page status: 0x%lx.\n", mem_info.State);
466         }
467
468         fprintf(stderr, "Was writing: %ld, where: 0x%lx.\n",
469                 exception_record->ExceptionInformation[0],
470                 (DWORD)fault_address);
471     }
472
473     fflush(stderr);
474
475     fake_foreign_function_call(ctx);
476     lose("Exception too early in cold init, cannot continue.");
477 }
478
479 /*
480  * A good explanation of the exception handling semantics is
481  * http://win32assembly.online.fr/Exceptionhandling.html .
482  */
483
484 EXCEPTION_DISPOSITION
485 handle_exception(EXCEPTION_RECORD *exception_record,
486                  struct lisp_exception_frame *exception_frame,
487                  CONTEXT *ctx,
488                  void *dispatcher_context)
489 {
490     if (exception_record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)) {
491         /* If we're being unwound, be graceful about it. */
492
493         /* Undo any dynamic bindings. */
494         unbind_to_here(exception_frame->bindstack_pointer,
495                        arch_os_get_current_thread());
496
497         return ExceptionContinueSearch;
498     }
499
500     DWORD code = exception_record->ExceptionCode;
501
502     /* For EXCEPTION_ACCESS_VIOLATION only. */
503     void *fault_address = (void *)exception_record->ExceptionInformation[1];
504
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
508      * isn't happy: */
509
510     int rc;
511     switch (code) {
512     case EXCEPTION_ACCESS_VIOLATION:
513         rc = handle_access_violation(
514             ctx, exception_record, fault_address);
515         break;
516
517     case SBCL_EXCEPTION_BREAKPOINT:
518         rc = handle_breakpoint_trap(ctx);
519         break;
520
521 #if defined(LISP_FEATURE_X86)
522     case EXCEPTION_SINGLE_STEP:
523         rc = handle_single_step(ctx);
524         break;
525 #endif
526
527     default:
528         rc = -1;
529     }
530
531     if (rc)
532         /* All else failed, drop through to the lisp-side exception handler. */
533         signal_internal_error_or_lose(ctx, exception_record, fault_address);
534
535     return ExceptionContinueExecution;
536 }
537
538 void
539 wos_install_interrupt_handlers(struct lisp_exception_frame *handler)
540 {
541     handler->next_frame = get_seh_frame();
542     handler->handler = &handle_exception;
543     set_seh_frame(handler);
544 }
545
546 void bcopy(const void *src, void *dest, size_t n)
547 {
548     MoveMemory(dest, src, n);
549 }
550
551 /*
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.
556  */
557
558 void *memmove(void *dest, const void *src, size_t n)
559 {
560     if (dest < src) {
561         int i;
562         for (i = 0; i < n; i++) *(((char *)dest)+i) = *(((char *)src)+i);
563     } else {
564         while (n--) *(((char *)dest)+n) = *(((char *)src)+n);
565     }
566     return dest;
567 }
568
569 void *memcpy(void *dest, const void *src, size_t n)
570 {
571     while (n--) *(((char *)dest)+n) = *(((char *)src)+n);
572     return dest;
573 }
574
575 char *dirname(char *path)
576 {
577     static char buf[PATH_MAX + 1];
578     size_t pathlen = strlen(path);
579     int i;
580
581     if (pathlen >= sizeof(buf)) {
582         lose("Pathname too long in dirname.\n");
583         return NULL;
584     }
585
586     strcpy(buf, path);
587     for (i = pathlen; i >= 0; --i) {
588         if (buf[i] == '/' || buf[i] == '\\') {
589             buf[i] = '\0';
590             break;
591         }
592     }
593
594     return buf;
595 }
596
597 /* This is a manually-maintained version of ldso_stubs.S. */
598
599 void __stdcall RtlUnwind(void *, void *, void *, void *); /* I don't have winternl.h */
600
601 void scratch(void)
602 {
603     CloseHandle(0);
604     FlushConsoleInputBuffer(0);
605     FormatMessageA(0, 0, 0, 0, 0, 0, 0);
606     FreeLibrary(0);
607     GetACP();
608     GetConsoleCP();
609     GetConsoleOutputCP();
610     GetCurrentProcess();
611     GetExitCodeProcess(0, 0);
612     GetLastError();
613     GetOEMCP();
614     GetProcAddress(0, 0);
615     GetProcessTimes(0, 0, 0, 0, 0);
616     GetSystemTimeAsFileTime(0);
617     LoadLibrary(0);
618     LocalFree(0);
619     PeekConsoleInput(0, 0, 0, 0);
620     PeekNamedPipe(0, 0, 0, 0, 0, 0);
621     ReadFile(0, 0, 0, 0, 0);
622     Sleep(0);
623     WriteFile(0, 0, 0, 0, 0);
624     _get_osfhandle(0);
625     _rmdir(0);
626     _pipe(0,0,0);
627     access(0,0);
628     close(0);
629     dup(0);
630     isatty(0);
631     strerror(42);
632     write(0, 0, 0);
633     RtlUnwind(0, 0, 0, 0);
634     MapViewOfFile(0,0,0,0,0);
635     UnmapViewOfFile(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);
645       GetVersionExA(0);
646       MoveFileA(0,0);
647       SHGetFolderPathA(0, 0, 0, 0, 0);
648       SetCurrentDirectoryA(0);
649       SetEnvironmentVariableA(0, 0);
650     #else
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);
659       GetVersionExW(0);
660       MoveFileW(0,0);
661       SHGetFolderPathW(0, 0, 0, 0, 0);
662       SetCurrentDirectoryW(0);
663       SetEnvironmentVariableW(0, 0);
664     #endif
665     _exit(0);
666 }
667
668 char *
669 os_get_runtime_executable_path(int external)
670 {
671     char path[MAX_PATH + 1];
672     DWORD bufsize = sizeof(path);
673     DWORD size;
674
675     if ((size = GetModuleFileNameA(NULL, path, bufsize)) == 0)
676         return NULL;
677     else if (size == bufsize && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
678         return NULL;
679
680     return copied_string(path);
681 }
682
683 /* EOF */