1 /***********************************************************************/
5 /* Xavier Leroy, projet Cristal, INRIA Rocquencourt */
7 /* Copyright 1996 Institut National de Recherche en Informatique et */
8 /* en Automatique. All rights reserved. This file is distributed */
9 /* under the terms of the GNU Library General Public License, with */
10 /* the special exception on linking described in file ../LICENSE. */
12 /***********************************************************************/
16 /* Win32-specific stuff */
23 #include <sys/types.h>
36 #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
39 char * caml_decompose_path(struct ext_table
* tbl
, char * path
)
44 if (path
== NULL
) return NULL
;
45 p
= caml_stat_alloc(strlen(path
) + 1);
49 for (n
= 0; q
[n
] != 0 && q
[n
] != ';'; n
++) /*nothing*/;
50 caml_ext_table_add(tbl
, q
);
59 char * caml_search_in_path(struct ext_table
* path
, char * name
)
65 for (p
= name
; *p
!= 0; p
++) {
66 if (*p
== '/' || *p
== '\\') goto not_found
;
68 for (i
= 0; i
< path
->size
; i
++) {
69 fullname
= caml_stat_alloc(strlen((char *)(path
->contents
[i
])) +
71 strcpy(fullname
, (char *)(path
->contents
[i
]));
72 strcat(fullname
, "\\");
73 strcat(fullname
, name
);
74 caml_gc_message(0x100, "Searching %s\n", (uintnat
) fullname
);
75 if (stat(fullname
, &st
) == 0 && S_ISREG(st
.st_mode
)) return fullname
;
76 caml_stat_free(fullname
);
79 caml_gc_message(0x100, "%s not found in search path\n", (uintnat
) name
);
80 fullname
= caml_stat_alloc(strlen(name
) + 1);
81 strcpy(fullname
, name
);
85 CAMLexport
char * caml_search_exe_in_path(char * name
)
87 char * fullname
, * filepart
;
88 DWORD pathlen
, retcode
;
90 pathlen
= strlen(name
) + 1;
91 if (pathlen
< 256) pathlen
= 256;
93 fullname
= stat_alloc(pathlen
);
94 retcode
= SearchPath(NULL
, /* use system search path */
96 ".exe", /* add .exe extension if needed */
101 caml_gc_message(0x100, "%s not found in search path\n",
103 strcpy(fullname
, name
);
106 if (retcode
< pathlen
) break;
108 pathlen
= retcode
+ 1;
113 char * caml_search_dll_in_path(struct ext_table
* path
, char * name
)
115 char * dllname
= caml_stat_alloc(strlen(name
) + 5);
117 strcpy(dllname
, name
);
118 strcat(dllname
, ".dll");
119 res
= caml_search_in_path(path
, dllname
);
120 caml_stat_free(dllname
);
124 void * caml_dlopen(char * libname
, int for_execution
)
127 m
= LoadLibraryEx(libname
, NULL
,
128 for_execution
? 0 : DONT_RESOLVE_DLL_REFERENCES
);
129 /* Under Win 95/98/ME, LoadLibraryEx can fail in cases where LoadLibrary
130 would succeed. Just try again with LoadLibrary for good measure. */
131 if (m
== NULL
) m
= LoadLibrary(libname
);
135 void caml_dlclose(void * handle
)
137 FreeLibrary((HMODULE
) handle
);
140 void * caml_dlsym(void * handle
, char * name
)
142 return (void *) GetProcAddress((HMODULE
) handle
, name
);
145 char * caml_dlerror(void)
147 static char dlerror_buffer
[256];
149 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
,
150 NULL
, /* message source */
151 GetLastError(), /* error number */
152 0, /* default language */
153 dlerror_buffer
, /* destination */
154 sizeof(dlerror_buffer
), /* size of destination */
155 NULL
); /* no inserts */
157 return "unknown error";
159 return dlerror_buffer
;
162 /* Proper emulation of signal(), including ctrl-C and ctrl-break */
164 typedef void (*sighandler
)(int sig
);
165 static int ctrl_handler_installed
= 0;
166 static volatile sighandler ctrl_handler_action
= SIG_DFL
;
168 static BOOL WINAPI
ctrl_handler(DWORD event
)
172 /* Only ctrl-C and ctrl-Break are handled */
173 if (event
!= CTRL_C_EVENT
&& event
!= CTRL_BREAK_EVENT
) return FALSE
;
174 /* Default behavior is to exit, which we get by not handling the event */
175 if (ctrl_handler_action
== SIG_DFL
) return FALSE
;
176 /* Ignore behavior is to do nothing, which we get by claiming that we
177 have handled the event */
178 if (ctrl_handler_action
== SIG_IGN
) return TRUE
;
179 /* Win32 doesn't like it when we do a longjmp() at this point
180 (it looks like we're running in a different thread than
181 the main program!). So, just record the signal. */
182 caml_record_signal(SIGINT
);
183 /* We have handled the event */
187 sighandler
caml_win32_signal(int sig
, sighandler action
)
189 sighandler oldaction
;
191 if (sig
!= SIGINT
) return signal(sig
, action
);
192 if (! ctrl_handler_installed
) {
193 SetConsoleCtrlHandler(ctrl_handler
, TRUE
);
194 ctrl_handler_installed
= 1;
196 oldaction
= ctrl_handler_action
;
197 ctrl_handler_action
= action
;
201 /* Expansion of @responsefile and *? file patterns in the command line */
207 static void store_argument(char * arg
);
208 static void expand_argument(char * arg
);
209 static void expand_pattern(char * arg
);
210 static void expand_diversion(char * filename
);
212 static void out_of_memory(void)
214 fprintf(stderr
, "Out of memory while expanding command line\n");
218 static void store_argument(char * arg
)
220 if (argc
+ 1 >= argvsize
) {
222 argv
= (char **) realloc(argv
, argvsize
* sizeof(char *));
223 if (argv
== NULL
) out_of_memory();
228 static void expand_argument(char * arg
)
233 expand_diversion(arg
+ 1);
236 for (p
= arg
; *p
!= 0; p
++) {
237 if (*p
== '*' || *p
== '?') {
245 static void expand_pattern(char * pat
)
248 struct _finddata_t ffblk
;
251 handle
= _findfirst(pat
, &ffblk
);
253 store_argument(pat
); /* a la Bourne shell */
256 for (preflen
= strlen(pat
); preflen
> 0; preflen
--) {
257 char c
= pat
[preflen
- 1];
258 if (c
== '\\' || c
== '/' || c
== ':') break;
261 char * name
= malloc(preflen
+ strlen(ffblk
.name
) + 1);
262 if (name
== NULL
) out_of_memory();
263 memcpy(name
, pat
, preflen
);
264 strcpy(name
+ preflen
, ffblk
.name
);
265 store_argument(name
);
266 } while (_findnext(handle
, &ffblk
) != -1);
270 static void expand_diversion(char * filename
)
274 char * buf
, * endbuf
, * p
, * q
, * s
;
277 if (_stat(filename
, &stat
) == -1 ||
278 (fd
= _open(filename
, O_RDONLY
| O_BINARY
, 0)) == -1) {
279 fprintf(stderr
, "Cannot open file %s\n", filename
);
282 buf
= (char *) malloc(stat
.st_size
+ 1);
283 if (buf
== NULL
) out_of_memory();
284 _read(fd
, buf
, stat
.st_size
);
285 endbuf
= buf
+ stat
.st_size
;
287 for (p
= buf
; p
< endbuf
; /*nothing*/) {
288 /* Skip leading blanks */
289 while (p
< endbuf
&& isspace(*p
)) p
++;
290 if (p
>= endbuf
) break;
292 /* Skip to end of argument, taking quotes into account */
297 if (isspace(*p
)) break;
298 if (*p
== '"') { inquote
= 1; p
++; continue; }
303 inquote
= 0; p
++; continue;
305 if (p
+ 4 <= endbuf
&& strncmp(p
, "\\\\\\\"", 4) == 0) {
306 p
+= 4; *q
++ = '\\'; *q
++ = '"'; continue;
308 if (p
+ 3 <= endbuf
&& strncmp(p
, "\\\\\"", 3) == 0) {
309 p
+= 3; *q
++ = '\\'; inquote
= 0; continue;
311 if (p
+ 2 <= endbuf
&& p
[1] == '"') {
312 p
+= 2; *q
++ = '"'; continue;
320 /* Delimit argument and expand it */
327 CAMLexport
void caml_expand_command_line(int * argcp
, char *** argvp
)
332 argv
= (char **) malloc(argvsize
* sizeof(char *));
333 if (argv
== NULL
) out_of_memory();
334 for (i
= 0; i
< *argcp
; i
++) expand_argument((*argvp
)[i
]);
340 /* Add to [contents] the (short) names of the files contained in
341 the directory named [dirname]. No entries are added for [.] and [..].
342 Return 0 on success, -1 on error; set errno in the case of error. */
344 int caml_read_directory(char * dirname
, struct ext_table
* contents
)
353 struct _finddata_t fileinfo
;
356 dirnamelen
= strlen(dirname
);
357 template = caml_stat_alloc(dirnamelen
+ 5);
358 strcpy(template, dirname
);
359 switch (dirname
[dirnamelen
- 1]) {
360 case '/': case '\\': case ':':
361 strcat(template, "*.*"); break;
363 strcat(template, "\\*.*");
365 h
= _findfirst(template, &fileinfo
);
366 caml_stat_free(template);
367 if (h
== -1) return errno
== ENOENT
? 0 : -1;
369 if (strcmp(fileinfo
.name
, ".") != 0 && strcmp(fileinfo
.name
, "..") != 0) {
370 p
= caml_stat_alloc(strlen(fileinfo
.name
) + 1);
371 strcpy(p
, fileinfo
.name
);
372 caml_ext_table_add(contents
, p
);
374 } while (_findnext(h
, &fileinfo
) == 0);
381 /* Set up a new thread for control-C emulation and termination */
383 void caml_signal_thread(void * lpParam
)
387 /* Get an hexa-code raw handle through the environment */
388 h
= (HANDLE
) strtol(getenv("CAMLSIGPIPE"), &endptr
, 16);
393 /* This shall always return a single character */
394 ret
= ReadFile(h
, iobuf
, 1, &numread
, NULL
);
395 if (!ret
|| numread
!= 1) caml_sys_exit(Val_int(2));
398 caml_record_signal(SIGINT
);
407 #endif /* NATIVE_CODE */
409 #if defined(NATIVE_CODE) && !defined(_WIN64)
411 /* Handling of system stack overflow.
412 * Based on code provided by Olivier Andrieu.
414 * An EXCEPTION_STACK_OVERFLOW is signaled when the guard page at the
415 * end of the stack has been accessed. Windows clears the PAGE_GUARD
416 * protection (making it a regular PAGE_READWRITE) and then calls our
417 * exception handler. This means that although we're handling an "out
418 * of stack" condition, there is a bit of stack available to call
419 * functions and allocate temporaries.
421 * PAGE_GUARD is a one-shot access protection mechanism: we need to
422 * restore the PAGE_GUARD protection on this page otherwise the next
423 * stack overflow won't be detected and the program will abruptly exit
424 * with STATUS_ACCESS_VIOLATION.
426 * Visual Studio 2003 and later (_MSC_VER >= 1300) have a
427 * _resetstkoflw() function that resets this protection.
428 * Unfortunately, it cannot work when called directly from the
429 * exception handler because at this point we are using the page that
430 * is to be protected.
432 * A solution is to used an alternate stack when restoring the
433 * protection. However it's not possible to use _resetstkoflw() then
434 * since it determines the stack pointer by calling alloca(): it would
435 * try to protect the alternate stack.
437 * Finally, we call caml_raise_stack_overflow; it will either call
438 * caml_raise_exception which switches back to the normal stack, or
439 * call caml_fatal_uncaught_exception which terminates the program
442 * NB: The PAGE_GUARD protection is only available on WinNT, not
443 * Win9x. There is an equivalent mechanism on Win9x with
446 * Currently, does not work under Win64.
449 static uintnat win32_alt_stack
[0x80];
451 static void caml_reset_stack (void *faulting_address
)
456 MEMORY_BASIC_INFORMATION mbi
;
459 /* get the os version (Win9x or WinNT ?) */
460 osi
.dwOSVersionInfoSize
= sizeof osi
;
461 if (! GetVersionEx (&osi
))
464 /* get the system's page size. */
466 page_size
= si
.dwPageSize
;
468 /* get some information on the page the fault occurred */
469 if (! VirtualQuery (faulting_address
, &mbi
, sizeof mbi
))
472 /* restore the PAGE_GUARD protection on this page */
473 switch (osi
.dwPlatformId
) {
474 case VER_PLATFORM_WIN32_NT
:
475 VirtualProtect (mbi
.BaseAddress
, page_size
,
476 mbi
.Protect
| PAGE_GUARD
, &oldprot
);
478 case VER_PLATFORM_WIN32_WINDOWS
:
479 VirtualProtect (mbi
.BaseAddress
, page_size
,
480 PAGE_NOACCESS
, &oldprot
);
485 caml_raise_stack_overflow();
488 extern char * caml_code_area_start
, * caml_code_area_end
;
490 #define In_code_area(pc) \
491 ((char *)(pc) >= caml_code_area_start && \
492 (char *)(pc) <= caml_code_area_end)
495 caml_UnhandledExceptionFilter (EXCEPTION_POINTERS
* exn_info
)
497 DWORD code
= exn_info
->ExceptionRecord
->ExceptionCode
;
498 CONTEXT
*ctx
= exn_info
->ContextRecord
;
499 DWORD
*ctx_ip
= &(ctx
->Eip
);
500 DWORD
*ctx_sp
= &(ctx
->Esp
);
502 if (code
== EXCEPTION_STACK_OVERFLOW
&& In_code_area (*ctx_ip
))
504 uintnat faulting_address
;
507 /* grab the address that caused the fault */
508 faulting_address
= exn_info
->ExceptionRecord
->ExceptionInformation
[1];
510 /* call caml_reset_stack(faulting_address) using the alternate stack */
511 alt_esp
= win32_alt_stack
+ sizeof(win32_alt_stack
) / sizeof(uintnat
);
512 *--alt_esp
= faulting_address
;
513 *ctx_sp
= (uintnat
) (alt_esp
- 1);
514 *ctx_ip
= (uintnat
) &caml_reset_stack
;
516 return EXCEPTION_CONTINUE_EXECUTION
;
519 return EXCEPTION_CONTINUE_SEARCH
;
522 void caml_win32_overflow_detection()
524 SetUnhandledExceptionFilter (caml_UnhandledExceptionFilter
);
529 /* Seeding of pseudo-random number generators */
531 intnat
caml_win32_random_seed (void)
538 seed
= (seed
<< 5) ^ t
.wDay
;
539 seed
= (seed
<< 4) ^ t
.wHour
;
540 seed
= (seed
<< 5) ^ t
.wMinute
;
541 seed
= (seed
<< 5) ^ t
.wSecond
;
542 seed
= (seed
<< 9) ^ t
.wMilliseconds
;
543 seed
^= GetCurrentProcessId();