]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* |
2 | * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers | |
3 | * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. | |
4 | * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. | |
5 | * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved. | |
6 | * | |
7 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED | |
8 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. | |
9 | * | |
10 | * Permission is hereby granted to use or copy this program | |
11 | * for any purpose, provided the above notices are retained on all copies. | |
12 | * Permission to modify the code and to distribute modified code is granted, | |
13 | * provided the above notices are retained, and a notice that the code was | |
14 | * modified is included with the above copyright notice. | |
15 | */ | |
16 | ||
17 | # include "private/gc_priv.h" | |
18 | # ifdef THREADS | |
19 | # include "atomic_ops.h" | |
20 | # endif | |
21 | ||
22 | # if defined(LINUX) && !defined(POWERPC) | |
23 | # include <linux/version.h> | |
24 | # if (LINUX_VERSION_CODE <= 0x10400) | |
25 | /* Ugly hack to get struct sigcontext_struct definition. Required */ | |
26 | /* for some early 1.3.X releases. Will hopefully go away soon. */ | |
27 | /* in some later Linux releases, asm/sigcontext.h may have to */ | |
28 | /* be included instead. */ | |
29 | # define __KERNEL__ | |
30 | # include <asm/signal.h> | |
31 | # undef __KERNEL__ | |
32 | # else | |
33 | /* Kernels prior to 2.1.1 defined struct sigcontext_struct instead of */ | |
34 | /* struct sigcontext. libc6 (glibc2) uses "struct sigcontext" in */ | |
35 | /* prototypes, so we have to include the top-level sigcontext.h to */ | |
36 | /* make sure the former gets defined to be the latter if appropriate. */ | |
37 | # include <features.h> | |
38 | # if 2 <= __GLIBC__ | |
39 | # if 2 == __GLIBC__ && 0 == __GLIBC_MINOR__ | |
40 | /* glibc 2.1 no longer has sigcontext.h. But signal.h */ | |
41 | /* has the right declaration for glibc 2.1. */ | |
42 | # include <sigcontext.h> | |
43 | # endif /* 0 == __GLIBC_MINOR__ */ | |
44 | # else /* not 2 <= __GLIBC__ */ | |
45 | /* libc5 doesn't have <sigcontext.h>: go directly with the kernel */ | |
46 | /* one. Check LINUX_VERSION_CODE to see which we should reference. */ | |
47 | # include <asm/sigcontext.h> | |
48 | # endif /* 2 <= __GLIBC__ */ | |
49 | # endif | |
50 | # endif | |
51 | # if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS) \ | |
52 | && !defined(MSWINCE) | |
53 | # include <sys/types.h> | |
54 | # if !defined(MSWIN32) | |
55 | # include <unistd.h> | |
56 | # endif | |
57 | # endif | |
58 | ||
59 | # include <stdio.h> | |
60 | # if defined(MSWINCE) | |
61 | # define SIGSEGV 0 /* value is irrelevant */ | |
62 | # else | |
63 | # include <signal.h> | |
64 | # endif | |
65 | ||
66 | #ifdef UNIX_LIKE | |
67 | # include <fcntl.h> | |
68 | #endif | |
69 | ||
70 | #if defined(LINUX) || defined(LINUX_STACKBOTTOM) | |
71 | # include <ctype.h> | |
72 | #endif | |
73 | ||
74 | /* Blatantly OS dependent routines, except for those that are related */ | |
75 | /* to dynamic loading. */ | |
76 | ||
77 | #ifdef AMIGA | |
78 | # define GC_AMIGA_DEF | |
79 | # include "AmigaOS.c" | |
80 | # undef GC_AMIGA_DEF | |
81 | #endif | |
82 | ||
83 | #if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) | |
84 | # define WIN32_LEAN_AND_MEAN | |
85 | # define NOSERVICE | |
86 | # include <windows.h> | |
87 | /* It's not clear this is completely kosher under Cygwin. But it */ | |
88 | /* allows us to get a working GC_get_stack_base. */ | |
89 | #endif | |
90 | ||
91 | #ifdef MACOS | |
92 | # include <Processes.h> | |
93 | #endif | |
94 | ||
95 | #ifdef IRIX5 | |
96 | # include <sys/uio.h> | |
97 | # include <malloc.h> /* for locking */ | |
98 | #endif | |
99 | ||
100 | #if defined(LINUX) || defined(FREEBSD) || defined(SOLARIS) || defined(IRIX5) \ | |
101 | || defined(USE_MMAP) || defined(USE_MUNMAP) | |
102 | # define MMAP_SUPPORTED | |
103 | #endif | |
104 | ||
105 | #if defined(MMAP_SUPPORTED) || defined(ADD_HEAP_GUARD_PAGES) | |
106 | # if defined(USE_MUNMAP) && !defined(USE_MMAP) | |
107 | --> USE_MUNMAP requires USE_MMAP | |
108 | # endif | |
109 | # include <sys/types.h> | |
110 | # include <sys/mman.h> | |
111 | # include <sys/stat.h> | |
112 | # include <errno.h> | |
113 | #endif | |
114 | ||
115 | #ifdef DARWIN | |
116 | /* for get_etext and friends */ | |
117 | #include <mach-o/getsect.h> | |
118 | #endif | |
119 | ||
120 | #ifdef DJGPP | |
121 | /* Apparently necessary for djgpp 2.01. May cause problems with */ | |
122 | /* other versions. */ | |
123 | typedef long unsigned int caddr_t; | |
124 | #endif | |
125 | ||
126 | #ifdef PCR | |
127 | # include "il/PCR_IL.h" | |
128 | # include "th/PCR_ThCtl.h" | |
129 | # include "mm/PCR_MM.h" | |
130 | #endif | |
131 | ||
132 | #if !defined(NO_EXECUTE_PERMISSION) | |
133 | # define OPT_PROT_EXEC PROT_EXEC | |
134 | #else | |
135 | # define OPT_PROT_EXEC 0 | |
136 | #endif | |
137 | ||
138 | #if defined(LINUX) && \ | |
139 | (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64) || !defined(SMALL_CONFIG)) | |
140 | # define NEED_PROC_MAPS | |
141 | #endif | |
142 | ||
143 | #ifdef NEED_PROC_MAPS | |
144 | /* We need to parse /proc/self/maps, either to find dynamic libraries, */ | |
145 | /* and/or to find the register backing store base (IA64). Do it once */ | |
146 | /* here. */ | |
147 | ||
148 | #define READ read | |
149 | ||
150 | /* Repeatedly perform a read call until the buffer is filled or */ | |
151 | /* we encounter EOF. */ | |
152 | ssize_t GC_repeat_read(int fd, char *buf, size_t count) | |
153 | { | |
154 | ssize_t num_read = 0; | |
155 | ssize_t result; | |
156 | ||
157 | while (num_read < count) { | |
158 | result = READ(fd, buf + num_read, count - num_read); | |
159 | if (result < 0) return result; | |
160 | if (result == 0) break; | |
161 | num_read += result; | |
162 | } | |
163 | return num_read; | |
164 | } | |
165 | ||
166 | /* Determine the length of a file by incrementally reading it into a */ | |
167 | /* This would be sily to use on a file supporting lseek, but Linux */ | |
168 | /* /proc files usually do not. */ | |
169 | size_t GC_get_file_len(int f) | |
170 | { | |
171 | size_t total = 0; | |
172 | ssize_t result; | |
173 | # define GET_FILE_LEN_BUF_SZ 500 | |
174 | char buf[GET_FILE_LEN_BUF_SZ]; | |
175 | ||
176 | do { | |
177 | result = read(f, buf, GET_FILE_LEN_BUF_SZ); | |
178 | if (result == -1) return 0; | |
179 | total += result; | |
180 | } while (result > 0); | |
181 | return total; | |
182 | } | |
183 | ||
184 | size_t GC_get_maps_len(void) | |
185 | { | |
186 | int f = open("/proc/self/maps", O_RDONLY); | |
187 | size_t result = GC_get_file_len(f); | |
188 | close(f); | |
189 | return result; | |
190 | } | |
191 | ||
192 | /* | |
193 | * Copy the contents of /proc/self/maps to a buffer in our address space. | |
194 | * Return the address of the buffer, or zero on failure. | |
195 | * This code could be simplified if we could determine its size | |
196 | * ahead of time. | |
197 | */ | |
198 | char * GC_get_maps(void) | |
199 | { | |
200 | int f; | |
201 | int result; | |
202 | static char init_buf[1]; | |
203 | static char *maps_buf = init_buf; | |
204 | static size_t maps_buf_sz = 1; | |
205 | size_t maps_size, old_maps_size = 0; | |
206 | ||
207 | /* The buffer is essentially static, so there must be a single client. */ | |
208 | GC_ASSERT(I_HOLD_LOCK()); | |
209 | ||
210 | /* Note that in the presence of threads, the maps file can */ | |
211 | /* essentially shrink asynchronously and unexpectedly as */ | |
212 | /* threads that we already think of as dead release their */ | |
213 | /* stacks. And there is no easy way to read the entire */ | |
214 | /* file atomically. This is arguably a misfeature of the */ | |
215 | /* /proc/.../maps interface. */ | |
216 | ||
217 | /* Since we dont believe the file can grow */ | |
218 | /* asynchronously, it should suffice to first determine */ | |
219 | /* the size (using lseek or read), and then to reread the */ | |
220 | /* file. If the size is inconsistent we have to retry. */ | |
221 | /* This only matters with threads enabled, and if we use */ | |
222 | /* this to locate roots (not the default). */ | |
223 | ||
224 | /* Determine the initial size of /proc/self/maps. */ | |
225 | /* Note that lseek doesn't work, at least as of 2.6.15. */ | |
226 | # ifdef THREADS | |
227 | maps_size = GC_get_maps_len(); | |
228 | if (0 == maps_size) return 0; | |
229 | # else | |
230 | maps_size = 4000; /* Guess */ | |
231 | # endif | |
232 | ||
233 | /* Read /proc/self/maps, growing maps_buf as necessary. */ | |
234 | /* Note that we may not allocate conventionally, and */ | |
235 | /* thus can't use stdio. */ | |
236 | do { | |
237 | while (maps_size >= maps_buf_sz) { | |
238 | /* Grow only by powers of 2, since we leak "too small" buffers. */ | |
239 | while (maps_size >= maps_buf_sz) maps_buf_sz *= 2; | |
240 | maps_buf = GC_scratch_alloc(maps_buf_sz); | |
241 | # ifdef THREADS | |
242 | /* Recompute initial length, since we allocated. */ | |
243 | /* This can only happen a few times per program */ | |
244 | /* execution. */ | |
245 | maps_size = GC_get_maps_len(); | |
246 | if (0 == maps_size) return 0; | |
247 | # endif | |
248 | if (maps_buf == 0) return 0; | |
249 | } | |
250 | GC_ASSERT(maps_buf_sz >= maps_size + 1); | |
251 | f = open("/proc/self/maps", O_RDONLY); | |
252 | if (-1 == f) return 0; | |
253 | # ifdef THREADS | |
254 | old_maps_size = maps_size; | |
255 | # endif | |
256 | maps_size = 0; | |
257 | do { | |
258 | result = GC_repeat_read(f, maps_buf, maps_buf_sz-1); | |
259 | if (result <= 0) return 0; | |
260 | maps_size += result; | |
261 | } while (result == maps_buf_sz-1); | |
262 | close(f); | |
263 | # ifdef THREADS | |
264 | if (maps_size > old_maps_size) { | |
265 | GC_err_printf("Old maps size = %d, new maps size = %d\n", | |
266 | old_maps_size, maps_size); | |
267 | ABORT("Unexpected asynchronous /proc/self/maps growth: " | |
268 | "Unregistered thread?"); | |
269 | } | |
270 | # endif | |
271 | } while (maps_size >= maps_buf_sz || maps_size < old_maps_size); | |
272 | /* In the single-threaded case, the second clause is false. */ | |
273 | maps_buf[maps_size] = '\0'; | |
274 | ||
275 | /* Apply fn to result. */ | |
276 | return maps_buf; | |
277 | } | |
278 | ||
279 | // | |
280 | // GC_parse_map_entry parses an entry from /proc/self/maps so we can | |
281 | // locate all writable data segments that belong to shared libraries. | |
282 | // The format of one of these entries and the fields we care about | |
283 | // is as follows: | |
284 | // XXXXXXXX-XXXXXXXX r-xp 00000000 30:05 260537 name of mapping...\n | |
285 | // ^^^^^^^^ ^^^^^^^^ ^^^^ ^^ | |
286 | // start end prot maj_dev | |
287 | // | |
288 | // Note that since about august 2003 kernels, the columns no longer have | |
289 | // fixed offsets on 64-bit kernels. Hence we no longer rely on fixed offsets | |
290 | // anywhere, which is safer anyway. | |
291 | // | |
292 | ||
293 | /* | |
294 | * Assign various fields of the first line in buf_ptr to *start, *end, | |
295 | * *prot, *maj_dev and *mapping_name. Mapping_name may be NULL. | |
296 | * *prot and *mapping_name are assigned pointers into the original | |
297 | * buffer. | |
298 | */ | |
299 | char *GC_parse_map_entry(char *buf_ptr, ptr_t *start, ptr_t *end, | |
300 | char **prot, unsigned int *maj_dev, | |
301 | char **mapping_name) | |
302 | { | |
303 | char *start_start, *end_start, *maj_dev_start; | |
304 | char *p; | |
305 | char *endp; | |
306 | ||
307 | if (buf_ptr == NULL || *buf_ptr == '\0') { | |
308 | return NULL; | |
309 | } | |
310 | ||
311 | p = buf_ptr; | |
312 | while (isspace(*p)) ++p; | |
313 | start_start = p; | |
314 | GC_ASSERT(isxdigit(*start_start)); | |
315 | *start = (ptr_t)strtoul(start_start, &endp, 16); p = endp; | |
316 | GC_ASSERT(*p=='-'); | |
317 | ||
318 | ++p; | |
319 | end_start = p; | |
320 | GC_ASSERT(isxdigit(*end_start)); | |
321 | *end = (ptr_t)strtoul(end_start, &endp, 16); p = endp; | |
322 | GC_ASSERT(isspace(*p)); | |
323 | ||
324 | while (isspace(*p)) ++p; | |
325 | GC_ASSERT(*p == 'r' || *p == '-'); | |
326 | *prot = p; | |
327 | /* Skip past protection field to offset field */ | |
328 | while (!isspace(*p)) ++p; while (isspace(*p)) ++p; | |
329 | GC_ASSERT(isxdigit(*p)); | |
330 | /* Skip past offset field, which we ignore */ | |
331 | while (!isspace(*p)) ++p; while (isspace(*p)) ++p; | |
332 | maj_dev_start = p; | |
333 | GC_ASSERT(isxdigit(*maj_dev_start)); | |
334 | *maj_dev = strtoul(maj_dev_start, NULL, 16); | |
335 | ||
336 | if (mapping_name == 0) { | |
337 | while (*p && *p++ != '\n'); | |
338 | } else { | |
339 | while (*p && *p != '\n' && *p != '/' && *p != '[') p++; | |
340 | *mapping_name = p; | |
341 | while (*p && *p++ != '\n'); | |
342 | } | |
343 | ||
344 | return p; | |
345 | } | |
346 | ||
347 | /* Try to read the backing store base from /proc/self/maps. */ | |
348 | /* Return the bounds of the writable mapping with a 0 major device, */ | |
349 | /* which includes the address passed as data. */ | |
350 | /* Return FALSE if there is no such mapping. */ | |
351 | GC_bool GC_enclosing_mapping(ptr_t addr, ptr_t *startp, ptr_t *endp) | |
352 | { | |
353 | char *prot; | |
354 | ptr_t my_start, my_end; | |
355 | unsigned int maj_dev; | |
356 | char *maps = GC_get_maps(); | |
357 | char *buf_ptr = maps; | |
358 | ||
359 | if (0 == maps) return(FALSE); | |
360 | for (;;) { | |
361 | buf_ptr = GC_parse_map_entry(buf_ptr, &my_start, &my_end, | |
362 | &prot, &maj_dev, 0); | |
363 | ||
364 | if (buf_ptr == NULL) return FALSE; | |
365 | if (prot[1] == 'w' && maj_dev == 0) { | |
366 | if (my_end > addr && my_start <= addr) { | |
367 | *startp = my_start; | |
368 | *endp = my_end; | |
369 | return TRUE; | |
370 | } | |
371 | } | |
372 | } | |
373 | return FALSE; | |
374 | } | |
375 | ||
376 | /* Find the text(code) mapping for the library whose name starts with nm. */ | |
377 | GC_bool GC_text_mapping(char *nm, ptr_t *startp, ptr_t *endp) | |
378 | { | |
379 | size_t nm_len = strlen(nm); | |
380 | char *prot; | |
381 | char *map_path; | |
382 | ptr_t my_start, my_end; | |
383 | unsigned int maj_dev; | |
384 | char *maps = GC_get_maps(); | |
385 | char *buf_ptr = maps; | |
386 | ||
387 | if (0 == maps) return(FALSE); | |
388 | for (;;) { | |
389 | buf_ptr = GC_parse_map_entry(buf_ptr, &my_start, &my_end, | |
390 | &prot, &maj_dev, &map_path); | |
391 | ||
392 | if (buf_ptr == NULL) return FALSE; | |
393 | if (prot[0] == 'r' && prot[1] == '-' && prot[2] == 'x' && | |
394 | strncmp(nm, map_path, nm_len) == 0) { | |
395 | *startp = my_start; | |
396 | *endp = my_end; | |
397 | return TRUE; | |
398 | } | |
399 | } | |
400 | return FALSE; | |
401 | } | |
402 | ||
403 | #ifdef IA64 | |
404 | static ptr_t backing_store_base_from_proc(void) | |
405 | { | |
406 | ptr_t my_start, my_end; | |
407 | if (!GC_enclosing_mapping(GC_save_regs_in_stack(), &my_start, &my_end)) { | |
408 | if (GC_print_stats) { | |
409 | GC_log_printf("Failed to find backing store base from /proc\n"); | |
410 | } | |
411 | return 0; | |
412 | } | |
413 | return my_start; | |
414 | } | |
415 | #endif | |
416 | ||
417 | #endif /* NEED_PROC_MAPS */ | |
418 | ||
419 | #if defined(SEARCH_FOR_DATA_START) | |
420 | /* The I386 case can be handled without a search. The Alpha case */ | |
421 | /* used to be handled differently as well, but the rules changed */ | |
422 | /* for recent Linux versions. This seems to be the easiest way to */ | |
423 | /* cover all versions. */ | |
424 | ||
425 | # if defined(LINUX) || defined(HURD) | |
426 | /* Some Linux distributions arrange to define __data_start. Some */ | |
427 | /* define data_start as a weak symbol. The latter is technically */ | |
428 | /* broken, since the user program may define data_start, in which */ | |
429 | /* case we lose. Nonetheless, we try both, prefering __data_start. */ | |
430 | /* We assume gcc-compatible pragmas. */ | |
431 | # pragma weak __data_start | |
432 | extern int __data_start[]; | |
433 | # pragma weak data_start | |
434 | extern int data_start[]; | |
435 | # endif /* LINUX */ | |
436 | extern int _end[]; | |
437 | ||
438 | ptr_t GC_data_start; | |
439 | ||
440 | void GC_init_linux_data_start() | |
441 | { | |
442 | extern ptr_t GC_find_limit(ptr_t, GC_bool); | |
443 | ||
444 | # if defined(LINUX) || defined(HURD) | |
445 | /* Try the easy approaches first: */ | |
446 | if ((ptr_t)__data_start != 0) { | |
447 | GC_data_start = (ptr_t)(__data_start); | |
448 | return; | |
449 | } | |
450 | if ((ptr_t)data_start != 0) { | |
451 | GC_data_start = (ptr_t)(data_start); | |
452 | return; | |
453 | } | |
454 | # endif /* LINUX */ | |
455 | GC_data_start = GC_find_limit((ptr_t)(_end), FALSE); | |
456 | } | |
457 | #endif | |
458 | ||
459 | # ifdef ECOS | |
460 | ||
461 | # ifndef ECOS_GC_MEMORY_SIZE | |
462 | # define ECOS_GC_MEMORY_SIZE (448 * 1024) | |
463 | # endif /* ECOS_GC_MEMORY_SIZE */ | |
464 | ||
465 | // FIXME: This is a simple way of allocating memory which is | |
466 | // compatible with ECOS early releases. Later releases use a more | |
467 | // sophisticated means of allocating memory than this simple static | |
468 | // allocator, but this method is at least bound to work. | |
469 | static char memory[ECOS_GC_MEMORY_SIZE]; | |
470 | static char *brk = memory; | |
471 | ||
472 | static void *tiny_sbrk(ptrdiff_t increment) | |
473 | { | |
474 | void *p = brk; | |
475 | ||
476 | brk += increment; | |
477 | ||
478 | if (brk > memory + sizeof memory) | |
479 | { | |
480 | brk -= increment; | |
481 | return NULL; | |
482 | } | |
483 | ||
484 | return p; | |
485 | } | |
486 | #define sbrk tiny_sbrk | |
487 | # endif /* ECOS */ | |
488 | ||
489 | #if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__) | |
490 | ptr_t GC_data_start; | |
491 | ||
492 | void GC_init_netbsd_elf(void) | |
493 | { | |
494 | extern ptr_t GC_find_limit(ptr_t, GC_bool); | |
495 | extern char **environ; | |
496 | /* This may need to be environ, without the underscore, for */ | |
497 | /* some versions. */ | |
498 | GC_data_start = GC_find_limit((ptr_t)&environ, FALSE); | |
499 | } | |
500 | #endif | |
501 | ||
502 | # ifdef OS2 | |
503 | ||
504 | # include <stddef.h> | |
505 | ||
506 | # if !defined(__IBMC__) && !defined(__WATCOMC__) /* e.g. EMX */ | |
507 | ||
508 | struct exe_hdr { | |
509 | unsigned short magic_number; | |
510 | unsigned short padding[29]; | |
511 | long new_exe_offset; | |
512 | }; | |
513 | ||
514 | #define E_MAGIC(x) (x).magic_number | |
515 | #define EMAGIC 0x5A4D | |
516 | #define E_LFANEW(x) (x).new_exe_offset | |
517 | ||
518 | struct e32_exe { | |
519 | unsigned char magic_number[2]; | |
520 | unsigned char byte_order; | |
521 | unsigned char word_order; | |
522 | unsigned long exe_format_level; | |
523 | unsigned short cpu; | |
524 | unsigned short os; | |
525 | unsigned long padding1[13]; | |
526 | unsigned long object_table_offset; | |
527 | unsigned long object_count; | |
528 | unsigned long padding2[31]; | |
529 | }; | |
530 | ||
531 | #define E32_MAGIC1(x) (x).magic_number[0] | |
532 | #define E32MAGIC1 'L' | |
533 | #define E32_MAGIC2(x) (x).magic_number[1] | |
534 | #define E32MAGIC2 'X' | |
535 | #define E32_BORDER(x) (x).byte_order | |
536 | #define E32LEBO 0 | |
537 | #define E32_WORDER(x) (x).word_order | |
538 | #define E32LEWO 0 | |
539 | #define E32_CPU(x) (x).cpu | |
540 | #define E32CPU286 1 | |
541 | #define E32_OBJTAB(x) (x).object_table_offset | |
542 | #define E32_OBJCNT(x) (x).object_count | |
543 | ||
544 | struct o32_obj { | |
545 | unsigned long size; | |
546 | unsigned long base; | |
547 | unsigned long flags; | |
548 | unsigned long pagemap; | |
549 | unsigned long mapsize; | |
550 | unsigned long reserved; | |
551 | }; | |
552 | ||
553 | #define O32_FLAGS(x) (x).flags | |
554 | #define OBJREAD 0x0001L | |
555 | #define OBJWRITE 0x0002L | |
556 | #define OBJINVALID 0x0080L | |
557 | #define O32_SIZE(x) (x).size | |
558 | #define O32_BASE(x) (x).base | |
559 | ||
560 | # else /* IBM's compiler */ | |
561 | ||
562 | /* A kludge to get around what appears to be a header file bug */ | |
563 | # ifndef WORD | |
564 | # define WORD unsigned short | |
565 | # endif | |
566 | # ifndef DWORD | |
567 | # define DWORD unsigned long | |
568 | # endif | |
569 | ||
570 | # define EXE386 1 | |
571 | # include <newexe.h> | |
572 | # include <exe386.h> | |
573 | ||
574 | # endif /* __IBMC__ */ | |
575 | ||
576 | # define INCL_DOSEXCEPTIONS | |
577 | # define INCL_DOSPROCESS | |
578 | # define INCL_DOSERRORS | |
579 | # define INCL_DOSMODULEMGR | |
580 | # define INCL_DOSMEMMGR | |
581 | # include <os2.h> | |
582 | ||
583 | ||
584 | /* Disable and enable signals during nontrivial allocations */ | |
585 | ||
586 | void GC_disable_signals(void) | |
587 | { | |
588 | ULONG nest; | |
589 | ||
590 | DosEnterMustComplete(&nest); | |
591 | if (nest != 1) ABORT("nested GC_disable_signals"); | |
592 | } | |
593 | ||
594 | void GC_enable_signals(void) | |
595 | { | |
596 | ULONG nest; | |
597 | ||
598 | DosExitMustComplete(&nest); | |
599 | if (nest != 0) ABORT("GC_enable_signals"); | |
600 | } | |
601 | ||
602 | ||
603 | # else | |
604 | ||
605 | # if !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \ | |
606 | && !defined(MSWINCE) \ | |
607 | && !defined(MACOS) && !defined(DJGPP) && !defined(DOS4GW) \ | |
608 | && !defined(NOSYS) && !defined(ECOS) | |
609 | ||
610 | # if 0 | |
611 | /* Use the traditional BSD interface */ | |
612 | # define SIGSET_T int | |
613 | # define SIG_DEL(set, signal) (set) &= ~(sigmask(signal)) | |
614 | # define SIG_FILL(set) (set) = 0x7fffffff | |
615 | /* Setting the leading bit appears to provoke a bug in some */ | |
616 | /* longjmp implementations. Most systems appear not to have */ | |
617 | /* a signal 32. */ | |
618 | # define SIGSETMASK(old, new) (old) = sigsetmask(new) | |
619 | # endif | |
620 | ||
621 | /* Use POSIX/SYSV interface */ | |
622 | # define SIGSET_T sigset_t | |
623 | # define SIG_DEL(set, signal) sigdelset(&(set), (signal)) | |
624 | # define SIG_FILL(set) sigfillset(&set) | |
625 | # define SIGSETMASK(old, new) sigprocmask(SIG_SETMASK, &(new), &(old)) | |
626 | ||
627 | ||
628 | static GC_bool mask_initialized = FALSE; | |
629 | ||
630 | static SIGSET_T new_mask; | |
631 | ||
632 | static SIGSET_T old_mask; | |
633 | ||
634 | static SIGSET_T dummy; | |
635 | ||
636 | #if defined(GC_ASSERTIONS) && !defined(THREADS) | |
637 | # define CHECK_SIGNALS | |
638 | int GC_sig_disabled = 0; | |
639 | #endif | |
640 | ||
641 | void GC_disable_signals(void) | |
642 | { | |
643 | if (!mask_initialized) { | |
644 | SIG_FILL(new_mask); | |
645 | ||
646 | SIG_DEL(new_mask, SIGSEGV); | |
647 | SIG_DEL(new_mask, SIGILL); | |
648 | SIG_DEL(new_mask, SIGQUIT); | |
649 | # ifdef SIGBUS | |
650 | SIG_DEL(new_mask, SIGBUS); | |
651 | # endif | |
652 | # ifdef SIGIOT | |
653 | SIG_DEL(new_mask, SIGIOT); | |
654 | # endif | |
655 | # ifdef SIGEMT | |
656 | SIG_DEL(new_mask, SIGEMT); | |
657 | # endif | |
658 | # ifdef SIGTRAP | |
659 | SIG_DEL(new_mask, SIGTRAP); | |
660 | # endif | |
661 | mask_initialized = TRUE; | |
662 | } | |
663 | # ifdef CHECK_SIGNALS | |
664 | if (GC_sig_disabled != 0) ABORT("Nested disables"); | |
665 | GC_sig_disabled++; | |
666 | # endif | |
667 | SIGSETMASK(old_mask,new_mask); | |
668 | } | |
669 | ||
670 | void GC_enable_signals(void) | |
671 | { | |
672 | # ifdef CHECK_SIGNALS | |
673 | if (GC_sig_disabled != 1) ABORT("Unmatched enable"); | |
674 | GC_sig_disabled--; | |
675 | # endif | |
676 | SIGSETMASK(dummy,old_mask); | |
677 | } | |
678 | ||
679 | # endif /* !PCR */ | |
680 | ||
681 | # endif /*!OS/2 */ | |
682 | ||
683 | /* Ivan Demakov: simplest way (to me) */ | |
684 | #if defined (DOS4GW) | |
685 | void GC_disable_signals() { } | |
686 | void GC_enable_signals() { } | |
687 | #endif | |
688 | ||
689 | /* Find the page size */ | |
690 | word GC_page_size; | |
691 | ||
692 | # if defined(MSWIN32) || defined(MSWINCE) | |
693 | void GC_setpagesize(void) | |
694 | { | |
695 | GetSystemInfo(&GC_sysinfo); | |
696 | GC_page_size = GC_sysinfo.dwPageSize; | |
697 | } | |
698 | ||
699 | # else | |
700 | # if defined(MPROTECT_VDB) || defined(PROC_VDB) || defined(USE_MMAP) | |
701 | void GC_setpagesize(void) | |
702 | { | |
703 | GC_page_size = GETPAGESIZE(); | |
704 | } | |
705 | # else | |
706 | /* It's acceptable to fake it. */ | |
707 | void GC_setpagesize(void) | |
708 | { | |
709 | GC_page_size = HBLKSIZE; | |
710 | } | |
711 | # endif | |
712 | # endif | |
713 | ||
714 | /* | |
715 | * Find the base of the stack. | |
716 | * Used only in single-threaded environment. | |
717 | * With threads, GC_mark_roots needs to know how to do this. | |
718 | * Called with allocator lock held. | |
719 | */ | |
720 | # if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) | |
721 | # define is_writable(prot) ((prot) == PAGE_READWRITE \ | |
722 | || (prot) == PAGE_WRITECOPY \ | |
723 | || (prot) == PAGE_EXECUTE_READWRITE \ | |
724 | || (prot) == PAGE_EXECUTE_WRITECOPY) | |
725 | /* Return the number of bytes that are writable starting at p. */ | |
726 | /* The pointer p is assumed to be page aligned. */ | |
727 | /* If base is not 0, *base becomes the beginning of the */ | |
728 | /* allocation region containing p. */ | |
729 | word GC_get_writable_length(ptr_t p, ptr_t *base) | |
730 | { | |
731 | MEMORY_BASIC_INFORMATION buf; | |
732 | word result; | |
733 | word protect; | |
734 | ||
735 | result = VirtualQuery(p, &buf, sizeof(buf)); | |
736 | if (result != sizeof(buf)) ABORT("Weird VirtualQuery result"); | |
737 | if (base != 0) *base = (ptr_t)(buf.AllocationBase); | |
738 | protect = (buf.Protect & ~(PAGE_GUARD | PAGE_NOCACHE)); | |
739 | if (!is_writable(protect)) { | |
740 | return(0); | |
741 | } | |
742 | if (buf.State != MEM_COMMIT) return(0); | |
743 | return(buf.RegionSize); | |
744 | } | |
745 | ||
746 | int GC_get_stack_base(struct GC_stack_base *sb) | |
747 | { | |
748 | int dummy; | |
749 | ptr_t sp = (ptr_t)(&dummy); | |
750 | ptr_t trunc_sp = (ptr_t)((word)sp & ~(GC_page_size - 1)); | |
751 | word size = GC_get_writable_length(trunc_sp, 0); | |
752 | ||
753 | sb -> mem_base = trunc_sp + size; | |
754 | return GC_SUCCESS; | |
755 | } | |
756 | ||
757 | #define HAVE_GET_STACK_BASE | |
758 | ||
759 | /* This is always called from the main thread. */ | |
760 | ptr_t GC_get_main_stack_base(void) | |
761 | { | |
762 | struct GC_stack_base sb; | |
763 | ||
764 | GC_get_stack_base(&sb); | |
765 | return (ptr_t)sb.mem_base; | |
766 | } | |
767 | ||
768 | # endif /* MS Windows */ | |
769 | ||
770 | # ifdef BEOS | |
771 | # include <kernel/OS.h> | |
772 | ptr_t GC_get_main_stack_base(void){ | |
773 | thread_info th; | |
774 | get_thread_info(find_thread(NULL),&th); | |
775 | return th.stack_end; | |
776 | } | |
777 | # endif /* BEOS */ | |
778 | ||
779 | ||
780 | # ifdef HAIKU | |
781 | # include <OS.h> | |
782 | ptr_t GC_get_main_stack_base(void) | |
783 | { | |
784 | thread_info th; | |
785 | get_thread_info(find_thread(NULL), &th); | |
786 | return th.stack_end; | |
787 | } | |
788 | # endif | |
789 | ||
790 | ||
791 | # ifdef OS2 | |
792 | ||
793 | ptr_t GC_get_main_stack_base(void) | |
794 | { | |
795 | PTIB ptib; | |
796 | PPIB ppib; | |
797 | ||
798 | if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) { | |
799 | GC_err_printf("DosGetInfoBlocks failed\n"); | |
800 | ABORT("DosGetInfoBlocks failed\n"); | |
801 | } | |
802 | return((ptr_t)(ptib -> tib_pstacklimit)); | |
803 | } | |
804 | ||
805 | # endif /* OS2 */ | |
806 | ||
807 | # ifdef AMIGA | |
808 | # define GC_AMIGA_SB | |
809 | # include "AmigaOS.c" | |
810 | # undef GC_AMIGA_SB | |
811 | # endif /* AMIGA */ | |
812 | ||
813 | # if defined(NEED_FIND_LIMIT) || defined(UNIX_LIKE) | |
814 | ||
815 | typedef void (*handler)(int); | |
816 | ||
817 | # if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) \ | |
818 | || defined(HURD) || defined(NETBSD) | |
819 | static struct sigaction old_segv_act; | |
820 | # if defined(_sigargs) /* !Irix6.x */ || defined(HPUX) \ | |
821 | || defined(HURD) || defined(NETBSD) | |
822 | static struct sigaction old_bus_act; | |
823 | # endif | |
824 | # else | |
825 | static handler old_segv_handler, old_bus_handler; | |
826 | # endif | |
827 | ||
828 | void GC_set_and_save_fault_handler(handler h) | |
829 | { | |
830 | # if defined(SUNOS5SIGS) || defined(IRIX5) \ | |
831 | || defined(OSF1) || defined(HURD) || defined(NETBSD) | |
832 | struct sigaction act; | |
833 | ||
834 | act.sa_handler = h; | |
835 | # if 0 /* Was necessary for Solaris 2.3 and very temporary */ | |
836 | /* NetBSD bugs. */ | |
837 | act.sa_flags = SA_RESTART | SA_NODEFER; | |
838 | # else | |
839 | act.sa_flags = SA_RESTART; | |
840 | # endif | |
841 | ||
842 | (void) sigemptyset(&act.sa_mask); | |
843 | # ifdef GC_IRIX_THREADS | |
844 | /* Older versions have a bug related to retrieving and */ | |
845 | /* and setting a handler at the same time. */ | |
846 | (void) sigaction(SIGSEGV, 0, &old_segv_act); | |
847 | (void) sigaction(SIGSEGV, &act, 0); | |
848 | # else | |
849 | (void) sigaction(SIGSEGV, &act, &old_segv_act); | |
850 | # if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \ | |
851 | || defined(HPUX) || defined(HURD) || defined(NETBSD) | |
852 | /* Under Irix 5.x or HP/UX, we may get SIGBUS. */ | |
853 | /* Pthreads doesn't exist under Irix 5.x, so we */ | |
854 | /* don't have to worry in the threads case. */ | |
855 | (void) sigaction(SIGBUS, &act, &old_bus_act); | |
856 | # endif | |
857 | # endif /* GC_IRIX_THREADS */ | |
858 | # else | |
859 | old_segv_handler = signal(SIGSEGV, h); | |
860 | # ifdef SIGBUS | |
861 | old_bus_handler = signal(SIGBUS, h); | |
862 | # endif | |
863 | # endif | |
864 | } | |
865 | # endif /* NEED_FIND_LIMIT || UNIX_LIKE */ | |
866 | ||
867 | # if defined(NEED_FIND_LIMIT) || \ | |
868 | defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS) | |
869 | /* Some tools to implement HEURISTIC2 */ | |
870 | # define MIN_PAGE_SIZE 256 /* Smallest conceivable page size, bytes */ | |
871 | ||
872 | /*ARGSUSED*/ | |
873 | void GC_fault_handler(int sig) | |
874 | { | |
875 | LONGJMP(GC_jmp_buf, 1); | |
876 | } | |
877 | ||
878 | void GC_setup_temporary_fault_handler(void) | |
879 | { | |
880 | /* Handler is process-wide, so this should only happen in */ | |
881 | /* one thread at a time. */ | |
882 | GC_ASSERT(I_HOLD_LOCK()); | |
883 | GC_set_and_save_fault_handler(GC_fault_handler); | |
884 | } | |
885 | ||
886 | void GC_reset_fault_handler(void) | |
887 | { | |
888 | # if defined(SUNOS5SIGS) || defined(IRIX5) \ | |
889 | || defined(OSF1) || defined(HURD) || defined(NETBSD) | |
890 | (void) sigaction(SIGSEGV, &old_segv_act, 0); | |
891 | # if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \ | |
892 | || defined(HPUX) || defined(HURD) || defined(NETBSD) | |
893 | (void) sigaction(SIGBUS, &old_bus_act, 0); | |
894 | # endif | |
895 | # else | |
896 | (void) signal(SIGSEGV, old_segv_handler); | |
897 | # ifdef SIGBUS | |
898 | (void) signal(SIGBUS, old_bus_handler); | |
899 | # endif | |
900 | # endif | |
901 | } | |
902 | ||
903 | /* Return the first nonaddressible location > p (up) or */ | |
904 | /* the smallest location q s.t. [q,p) is addressable (!up). */ | |
905 | /* We assume that p (up) or p-1 (!up) is addressable. */ | |
906 | /* Requires allocation lock. */ | |
907 | ptr_t GC_find_limit_with_bound(ptr_t p, GC_bool up, ptr_t bound) | |
908 | { | |
909 | static volatile ptr_t result; | |
910 | /* Safer if static, since otherwise it may not be */ | |
911 | /* preserved across the longjmp. Can safely be */ | |
912 | /* static since it's only called with the */ | |
913 | /* allocation lock held. */ | |
914 | ||
915 | GC_ASSERT(I_HOLD_LOCK()); | |
916 | GC_setup_temporary_fault_handler(); | |
917 | if (SETJMP(GC_jmp_buf) == 0) { | |
918 | result = (ptr_t)(((word)(p)) | |
919 | & ~(MIN_PAGE_SIZE-1)); | |
920 | for (;;) { | |
921 | if (up) { | |
922 | result += MIN_PAGE_SIZE; | |
923 | if (result >= bound) return bound; | |
924 | } else { | |
925 | result -= MIN_PAGE_SIZE; | |
926 | if (result <= bound) return bound; | |
927 | } | |
928 | GC_noop1((word)(*result)); | |
929 | } | |
930 | } | |
931 | GC_reset_fault_handler(); | |
932 | if (!up) { | |
933 | result += MIN_PAGE_SIZE; | |
934 | } | |
935 | return(result); | |
936 | } | |
937 | ||
938 | ptr_t GC_find_limit(ptr_t p, GC_bool up) | |
939 | { | |
940 | if (up) { | |
941 | return GC_find_limit_with_bound(p, up, (ptr_t)(word)(-1)); | |
942 | } else { | |
943 | return GC_find_limit_with_bound(p, up, 0); | |
944 | } | |
945 | } | |
946 | # endif | |
947 | ||
948 | #if defined(ECOS) || defined(NOSYS) | |
949 | ptr_t GC_get_main_stack_base(void) | |
950 | { | |
951 | return STACKBOTTOM; | |
952 | } | |
953 | #endif | |
954 | ||
955 | #ifdef HPUX_STACKBOTTOM | |
956 | ||
957 | #include <sys/param.h> | |
958 | #include <sys/pstat.h> | |
959 | ||
960 | ptr_t GC_get_register_stack_base(void) | |
961 | { | |
962 | struct pst_vm_status vm_status; | |
963 | ||
964 | int i = 0; | |
965 | while (pstat_getprocvm(&vm_status, sizeof(vm_status), 0, i++) == 1) { | |
966 | if (vm_status.pst_type == PS_RSESTACK) { | |
967 | return (ptr_t) vm_status.pst_vaddr; | |
968 | } | |
969 | } | |
970 | ||
971 | /* old way to get the register stackbottom */ | |
972 | return (ptr_t)(((word)GC_stackbottom - BACKING_STORE_DISPLACEMENT - 1) | |
973 | & ~(BACKING_STORE_ALIGNMENT - 1)); | |
974 | } | |
975 | ||
976 | #endif /* HPUX_STACK_BOTTOM */ | |
977 | ||
978 | #ifdef LINUX_STACKBOTTOM | |
979 | ||
980 | #include <sys/types.h> | |
981 | #include <sys/stat.h> | |
982 | ||
983 | # define STAT_SKIP 27 /* Number of fields preceding startstack */ | |
984 | /* field in /proc/self/stat */ | |
985 | ||
986 | #ifdef USE_LIBC_PRIVATES | |
987 | # pragma weak __libc_stack_end | |
988 | extern ptr_t __libc_stack_end; | |
989 | #endif | |
990 | ||
991 | # ifdef IA64 | |
992 | # ifdef USE_LIBC_PRIVATES | |
993 | # pragma weak __libc_ia64_register_backing_store_base | |
994 | extern ptr_t __libc_ia64_register_backing_store_base; | |
995 | # endif | |
996 | ||
997 | ptr_t GC_get_register_stack_base(void) | |
998 | { | |
999 | ptr_t result; | |
1000 | ||
1001 | # ifdef USE_LIBC_PRIVATES | |
1002 | if (0 != &__libc_ia64_register_backing_store_base | |
1003 | && 0 != __libc_ia64_register_backing_store_base) { | |
1004 | /* Glibc 2.2.4 has a bug such that for dynamically linked */ | |
1005 | /* executables __libc_ia64_register_backing_store_base is */ | |
1006 | /* defined but uninitialized during constructor calls. */ | |
1007 | /* Hence we check for both nonzero address and value. */ | |
1008 | return __libc_ia64_register_backing_store_base; | |
1009 | } | |
1010 | # endif | |
1011 | result = backing_store_base_from_proc(); | |
1012 | if (0 == result) { | |
1013 | result = GC_find_limit(GC_save_regs_in_stack(), FALSE); | |
1014 | /* Now seems to work better than constant displacement */ | |
1015 | /* heuristic used in 6.X versions. The latter seems to */ | |
1016 | /* fail for 2.6 kernels. */ | |
1017 | } | |
1018 | return result; | |
1019 | } | |
1020 | # endif | |
1021 | ||
1022 | ptr_t GC_linux_stack_base(void) | |
1023 | { | |
1024 | /* We read the stack base value from /proc/self/stat. We do this */ | |
1025 | /* using direct I/O system calls in order to avoid calling malloc */ | |
1026 | /* in case REDIRECT_MALLOC is defined. */ | |
1027 | # define STAT_BUF_SIZE 4096 | |
1028 | # define STAT_READ read | |
1029 | /* Should probably call the real read, if read is wrapped. */ | |
1030 | char stat_buf[STAT_BUF_SIZE]; | |
1031 | int f; | |
1032 | char c; | |
1033 | word result = 0; | |
1034 | size_t i, buf_offset = 0; | |
1035 | ||
1036 | /* First try the easy way. This should work for glibc 2.2 */ | |
1037 | /* This fails in a prelinked ("prelink" command) executable */ | |
1038 | /* since the correct value of __libc_stack_end never */ | |
1039 | /* becomes visible to us. The second test works around */ | |
1040 | /* this. */ | |
1041 | # ifdef USE_LIBC_PRIVATES | |
1042 | if (0 != &__libc_stack_end && 0 != __libc_stack_end ) { | |
1043 | # if defined(IA64) | |
1044 | /* Some versions of glibc set the address 16 bytes too */ | |
1045 | /* low while the initialization code is running. */ | |
1046 | if (((word)__libc_stack_end & 0xfff) + 0x10 < 0x1000) { | |
1047 | return __libc_stack_end + 0x10; | |
1048 | } /* Otherwise it's not safe to add 16 bytes and we fall */ | |
1049 | /* back to using /proc. */ | |
1050 | # elif defined(SPARC) | |
1051 | /* Older versions of glibc for 64-bit Sparc do not set | |
1052 | * this variable correctly, it gets set to either zero | |
1053 | * or one. | |
1054 | */ | |
1055 | if (__libc_stack_end != (ptr_t) (unsigned long)0x1) | |
1056 | return __libc_stack_end; | |
1057 | # else | |
1058 | return __libc_stack_end; | |
1059 | # endif | |
1060 | } | |
1061 | # endif | |
1062 | f = open("/proc/self/stat", O_RDONLY); | |
1063 | if (f < 0 || STAT_READ(f, stat_buf, STAT_BUF_SIZE) < 2 * STAT_SKIP) { | |
1064 | ABORT("Couldn't read /proc/self/stat"); | |
1065 | } | |
1066 | c = stat_buf[buf_offset++]; | |
1067 | /* Skip the required number of fields. This number is hopefully */ | |
1068 | /* constant across all Linux implementations. */ | |
1069 | for (i = 0; i < STAT_SKIP; ++i) { | |
1070 | while (isspace(c)) c = stat_buf[buf_offset++]; | |
1071 | while (!isspace(c)) c = stat_buf[buf_offset++]; | |
1072 | } | |
1073 | while (isspace(c)) c = stat_buf[buf_offset++]; | |
1074 | while (isdigit(c)) { | |
1075 | result *= 10; | |
1076 | result += c - '0'; | |
1077 | c = stat_buf[buf_offset++]; | |
1078 | } | |
1079 | close(f); | |
1080 | if (result < 0x10000000) ABORT("Absurd stack bottom value"); | |
1081 | return (ptr_t)result; | |
1082 | } | |
1083 | ||
1084 | #endif /* LINUX_STACKBOTTOM */ | |
1085 | ||
1086 | #ifdef FREEBSD_STACKBOTTOM | |
1087 | ||
1088 | /* This uses an undocumented sysctl call, but at least one expert */ | |
1089 | /* believes it will stay. */ | |
1090 | ||
1091 | #include <unistd.h> | |
1092 | #include <sys/types.h> | |
1093 | #include <sys/sysctl.h> | |
1094 | ||
1095 | ptr_t GC_freebsd_stack_base(void) | |
1096 | { | |
1097 | int nm[2] = {CTL_KERN, KERN_USRSTACK}; | |
1098 | ptr_t base; | |
1099 | size_t len = sizeof(ptr_t); | |
1100 | int r = sysctl(nm, 2, &base, &len, NULL, 0); | |
1101 | ||
1102 | if (r) ABORT("Error getting stack base"); | |
1103 | ||
1104 | return base; | |
1105 | } | |
1106 | ||
1107 | #endif /* FREEBSD_STACKBOTTOM */ | |
1108 | ||
1109 | #if !defined(BEOS) && !defined(AMIGA) && !defined(MSWIN32) \ | |
1110 | && !defined(MSWINCE) && !defined(OS2) && !defined(NOSYS) && !defined(ECOS) \ | |
1111 | && !defined(CYGWIN32) && !defined(HAIKU) | |
1112 | ||
1113 | ptr_t GC_get_main_stack_base(void) | |
1114 | { | |
1115 | # if defined(HEURISTIC1) || defined(HEURISTIC2) | |
1116 | word dummy; | |
1117 | # endif | |
1118 | ptr_t result; | |
1119 | ||
1120 | # define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1) | |
1121 | ||
1122 | # ifdef STACKBOTTOM | |
1123 | return(STACKBOTTOM); | |
1124 | # else | |
1125 | # ifdef HEURISTIC1 | |
1126 | # ifdef STACK_GROWS_DOWN | |
1127 | result = (ptr_t)((((word)(&dummy)) | |
1128 | + STACKBOTTOM_ALIGNMENT_M1) | |
1129 | & ~STACKBOTTOM_ALIGNMENT_M1); | |
1130 | # else | |
1131 | result = (ptr_t)(((word)(&dummy)) | |
1132 | & ~STACKBOTTOM_ALIGNMENT_M1); | |
1133 | # endif | |
1134 | # endif /* HEURISTIC1 */ | |
1135 | # ifdef LINUX_STACKBOTTOM | |
1136 | result = GC_linux_stack_base(); | |
1137 | # endif | |
1138 | # ifdef FREEBSD_STACKBOTTOM | |
1139 | result = GC_freebsd_stack_base(); | |
1140 | # endif | |
1141 | # ifdef HEURISTIC2 | |
1142 | # ifdef STACK_GROWS_DOWN | |
1143 | result = GC_find_limit((ptr_t)(&dummy), TRUE); | |
1144 | # ifdef HEURISTIC2_LIMIT | |
1145 | if (result > HEURISTIC2_LIMIT | |
1146 | && (ptr_t)(&dummy) < HEURISTIC2_LIMIT) { | |
1147 | result = HEURISTIC2_LIMIT; | |
1148 | } | |
1149 | # endif | |
1150 | # else | |
1151 | result = GC_find_limit((ptr_t)(&dummy), FALSE); | |
1152 | # ifdef HEURISTIC2_LIMIT | |
1153 | if (result < HEURISTIC2_LIMIT | |
1154 | && (ptr_t)(&dummy) > HEURISTIC2_LIMIT) { | |
1155 | result = HEURISTIC2_LIMIT; | |
1156 | } | |
1157 | # endif | |
1158 | # endif | |
1159 | ||
1160 | # endif /* HEURISTIC2 */ | |
1161 | # ifdef STACK_GROWS_DOWN | |
1162 | if (result == 0) result = (ptr_t)(signed_word)(-sizeof(ptr_t)); | |
1163 | # endif | |
1164 | return(result); | |
1165 | # endif /* STACKBOTTOM */ | |
1166 | } | |
1167 | ||
1168 | # endif /* ! AMIGA, !OS 2, ! MS Windows, !BEOS, !NOSYS, !ECOS, !HAIKU */ | |
1169 | ||
1170 | #if defined(GC_LINUX_THREADS) && !defined(HAVE_GET_STACK_BASE) | |
1171 | ||
1172 | #include <pthread.h> | |
1173 | ||
1174 | #ifdef IA64 | |
1175 | ptr_t GC_greatest_stack_base_below(ptr_t bound); | |
1176 | /* From pthread_support.c */ | |
1177 | #endif | |
1178 | ||
1179 | int GC_get_stack_base(struct GC_stack_base *b) | |
1180 | { | |
1181 | pthread_attr_t attr; | |
1182 | size_t size; | |
1183 | ||
1184 | if (pthread_getattr_np(pthread_self(), &attr) != 0) { | |
1185 | WARN("pthread_getattr_np failed\n", 0); | |
1186 | return GC_UNIMPLEMENTED; | |
1187 | } | |
1188 | if (pthread_attr_getstack(&attr, &(b -> mem_base), &size) != 0) { | |
1189 | ABORT("pthread_attr_getstack failed"); | |
1190 | } | |
1191 | # ifdef STACK_GROWS_DOWN | |
1192 | b -> mem_base = (char *)(b -> mem_base) + size; | |
1193 | # endif | |
1194 | # ifdef IA64 | |
1195 | /* We could try backing_store_base_from_proc, but that's safe */ | |
1196 | /* only if no mappings are being asynchronously created. */ | |
1197 | /* Subtracting the size from the stack base doesn't work for at */ | |
1198 | /* least the main thread. */ | |
1199 | LOCK(); | |
1200 | { | |
1201 | ptr_t bsp = GC_save_regs_in_stack(); | |
1202 | ptr_t next_stack = GC_greatest_stack_base_below(bsp); | |
1203 | if (0 == next_stack) { | |
1204 | b -> reg_base = GC_find_limit(bsp, FALSE); | |
1205 | } else { | |
1206 | /* Avoid walking backwards into preceding memory stack and */ | |
1207 | /* growing it. */ | |
1208 | b -> reg_base = GC_find_limit_with_bound(bsp, FALSE, next_stack); | |
1209 | } | |
1210 | } | |
1211 | UNLOCK(); | |
1212 | # endif | |
1213 | return GC_SUCCESS; | |
1214 | } | |
1215 | ||
1216 | #define HAVE_GET_STACK_BASE | |
1217 | ||
1218 | #endif /* GC_LINUX_THREADS */ | |
1219 | ||
1220 | #ifndef HAVE_GET_STACK_BASE | |
1221 | /* Retrieve stack base. */ | |
1222 | /* Using the GC_find_limit version is risky. */ | |
1223 | /* On IA64, for example, there is no guard page between the */ | |
1224 | /* stack of one thread and the register backing store of the */ | |
1225 | /* next. Thus this is likely to identify way too large a */ | |
1226 | /* "stack" and thus at least result in disastrous performance. */ | |
1227 | /* FIXME - Implement better strategies here. */ | |
1228 | int GC_get_stack_base(struct GC_stack_base *b) | |
1229 | { | |
1230 | int dummy; | |
1231 | ||
1232 | # ifdef NEED_FIND_LIMIT | |
1233 | # ifdef STACK_GROWS_DOWN | |
1234 | b -> mem_base = GC_find_limit((ptr_t)(&dummy), TRUE); | |
1235 | # ifdef IA64 | |
1236 | b -> reg_base = GC_find_limit(GC_save_regs_in_stack(), FALSE); | |
1237 | # endif | |
1238 | # else | |
1239 | b -> mem_base = GC_find_limit(&dummy, FALSE); | |
1240 | # endif | |
1241 | return GC_SUCCESS; | |
1242 | # else | |
1243 | return GC_UNIMPLEMENTED; | |
1244 | # endif | |
1245 | } | |
1246 | #endif | |
1247 | ||
1248 | /* | |
1249 | * Register static data segment(s) as roots. | |
1250 | * If more data segments are added later then they need to be registered | |
1251 | * add that point (as we do with SunOS dynamic loading), | |
1252 | * or GC_mark_roots needs to check for them (as we do with PCR). | |
1253 | * Called with allocator lock held. | |
1254 | */ | |
1255 | ||
1256 | # ifdef OS2 | |
1257 | ||
1258 | void GC_register_data_segments(void) | |
1259 | { | |
1260 | PTIB ptib; | |
1261 | PPIB ppib; | |
1262 | HMODULE module_handle; | |
1263 | # define PBUFSIZ 512 | |
1264 | UCHAR path[PBUFSIZ]; | |
1265 | FILE * myexefile; | |
1266 | struct exe_hdr hdrdos; /* MSDOS header. */ | |
1267 | struct e32_exe hdr386; /* Real header for my executable */ | |
1268 | struct o32_obj seg; /* Currrent segment */ | |
1269 | int nsegs; | |
1270 | ||
1271 | ||
1272 | if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) { | |
1273 | GC_err_printf("DosGetInfoBlocks failed\n"); | |
1274 | ABORT("DosGetInfoBlocks failed\n"); | |
1275 | } | |
1276 | module_handle = ppib -> pib_hmte; | |
1277 | if (DosQueryModuleName(module_handle, PBUFSIZ, path) != NO_ERROR) { | |
1278 | GC_err_printf("DosQueryModuleName failed\n"); | |
1279 | ABORT("DosGetInfoBlocks failed\n"); | |
1280 | } | |
1281 | myexefile = fopen(path, "rb"); | |
1282 | if (myexefile == 0) { | |
1283 | GC_err_puts("Couldn't open executable "); | |
1284 | GC_err_puts(path); GC_err_puts("\n"); | |
1285 | ABORT("Failed to open executable\n"); | |
1286 | } | |
1287 | if (fread((char *)(&hdrdos), 1, sizeof hdrdos, myexefile) < sizeof hdrdos) { | |
1288 | GC_err_puts("Couldn't read MSDOS header from "); | |
1289 | GC_err_puts(path); GC_err_puts("\n"); | |
1290 | ABORT("Couldn't read MSDOS header"); | |
1291 | } | |
1292 | if (E_MAGIC(hdrdos) != EMAGIC) { | |
1293 | GC_err_puts("Executable has wrong DOS magic number: "); | |
1294 | GC_err_puts(path); GC_err_puts("\n"); | |
1295 | ABORT("Bad DOS magic number"); | |
1296 | } | |
1297 | if (fseek(myexefile, E_LFANEW(hdrdos), SEEK_SET) != 0) { | |
1298 | GC_err_puts("Seek to new header failed in "); | |
1299 | GC_err_puts(path); GC_err_puts("\n"); | |
1300 | ABORT("Bad DOS magic number"); | |
1301 | } | |
1302 | if (fread((char *)(&hdr386), 1, sizeof hdr386, myexefile) < sizeof hdr386) { | |
1303 | GC_err_puts("Couldn't read MSDOS header from "); | |
1304 | GC_err_puts(path); GC_err_puts("\n"); | |
1305 | ABORT("Couldn't read OS/2 header"); | |
1306 | } | |
1307 | if (E32_MAGIC1(hdr386) != E32MAGIC1 || E32_MAGIC2(hdr386) != E32MAGIC2) { | |
1308 | GC_err_puts("Executable has wrong OS/2 magic number:"); | |
1309 | GC_err_puts(path); GC_err_puts("\n"); | |
1310 | ABORT("Bad OS/2 magic number"); | |
1311 | } | |
1312 | if ( E32_BORDER(hdr386) != E32LEBO || E32_WORDER(hdr386) != E32LEWO) { | |
1313 | GC_err_puts("Executable %s has wrong byte order: "); | |
1314 | GC_err_puts(path); GC_err_puts("\n"); | |
1315 | ABORT("Bad byte order"); | |
1316 | } | |
1317 | if ( E32_CPU(hdr386) == E32CPU286) { | |
1318 | GC_err_puts("GC can't handle 80286 executables: "); | |
1319 | GC_err_puts(path); GC_err_puts("\n"); | |
1320 | EXIT(); | |
1321 | } | |
1322 | if (fseek(myexefile, E_LFANEW(hdrdos) + E32_OBJTAB(hdr386), | |
1323 | SEEK_SET) != 0) { | |
1324 | GC_err_puts("Seek to object table failed: "); | |
1325 | GC_err_puts(path); GC_err_puts("\n"); | |
1326 | ABORT("Seek to object table failed"); | |
1327 | } | |
1328 | for (nsegs = E32_OBJCNT(hdr386); nsegs > 0; nsegs--) { | |
1329 | int flags; | |
1330 | if (fread((char *)(&seg), 1, sizeof seg, myexefile) < sizeof seg) { | |
1331 | GC_err_puts("Couldn't read obj table entry from "); | |
1332 | GC_err_puts(path); GC_err_puts("\n"); | |
1333 | ABORT("Couldn't read obj table entry"); | |
1334 | } | |
1335 | flags = O32_FLAGS(seg); | |
1336 | if (!(flags & OBJWRITE)) continue; | |
1337 | if (!(flags & OBJREAD)) continue; | |
1338 | if (flags & OBJINVALID) { | |
1339 | GC_err_printf("Object with invalid pages?\n"); | |
1340 | continue; | |
1341 | } | |
1342 | GC_add_roots_inner(O32_BASE(seg), O32_BASE(seg)+O32_SIZE(seg), FALSE); | |
1343 | } | |
1344 | } | |
1345 | ||
1346 | # else /* !OS2 */ | |
1347 | ||
1348 | # if defined(MSWIN32) || defined(MSWINCE) | |
1349 | ||
1350 | # ifdef MSWIN32 | |
1351 | /* Unfortunately, we have to handle win32s very differently from NT, */ | |
1352 | /* Since VirtualQuery has very different semantics. In particular, */ | |
1353 | /* under win32s a VirtualQuery call on an unmapped page returns an */ | |
1354 | /* invalid result. Under NT, GC_register_data_segments is a noop and */ | |
1355 | /* all real work is done by GC_register_dynamic_libraries. Under */ | |
1356 | /* win32s, we cannot find the data segments associated with dll's. */ | |
1357 | /* We register the main data segment here. */ | |
1358 | GC_bool GC_no_win32_dlls = FALSE; | |
1359 | /* This used to be set for gcc, to avoid dealing with */ | |
1360 | /* the structured exception handling issues. But we now have */ | |
1361 | /* assembly code to do that right. */ | |
1362 | ||
1363 | # if defined(GWW_VDB) | |
1364 | ||
1365 | # ifndef _BASETSD_H_ | |
1366 | typedef ULONG * PULONG_PTR; | |
1367 | # endif | |
1368 | typedef UINT (WINAPI * GetWriteWatch_type)( | |
1369 | DWORD, PVOID, SIZE_T, PVOID*, PULONG_PTR, PULONG); | |
1370 | static GetWriteWatch_type GetWriteWatch_func; | |
1371 | static DWORD GetWriteWatch_alloc_flag; | |
1372 | ||
1373 | # define GC_GWW_AVAILABLE() (GetWriteWatch_func != NULL) | |
1374 | ||
1375 | static void detect_GetWriteWatch(void) | |
1376 | { | |
1377 | static GC_bool done; | |
1378 | if (done) | |
1379 | return; | |
1380 | ||
1381 | GetWriteWatch_func = (GetWriteWatch_type) | |
1382 | GetProcAddress(GetModuleHandle("kernel32.dll"), "GetWriteWatch"); | |
1383 | if (GetWriteWatch_func != NULL) { | |
1384 | /* Also check whether VirtualAlloc accepts MEM_WRITE_WATCH, */ | |
1385 | /* as some versions of kernel32.dll have one but not the */ | |
1386 | /* other, making the feature completely broken. */ | |
1387 | void * page = VirtualAlloc(NULL, GC_page_size, | |
1388 | MEM_WRITE_WATCH | MEM_RESERVE, | |
1389 | PAGE_READWRITE); | |
1390 | if (page != NULL) { | |
1391 | PVOID pages[16]; | |
1392 | ULONG_PTR count = 16; | |
1393 | DWORD page_size; | |
1394 | /* Check that it actually works. In spite of some */ | |
1395 | /* documentation it actually seems to exist on W2K. */ | |
1396 | /* This test may be unnecessary, but ... */ | |
1397 | if (GetWriteWatch_func(WRITE_WATCH_FLAG_RESET, | |
1398 | page, GC_page_size, | |
1399 | pages, | |
1400 | &count, | |
1401 | &page_size) != 0) { | |
1402 | /* GetWriteWatch always fails. */ | |
1403 | GetWriteWatch_func = NULL; | |
1404 | } else { | |
1405 | GetWriteWatch_alloc_flag = MEM_WRITE_WATCH; | |
1406 | } | |
1407 | VirtualFree(page, GC_page_size, MEM_RELEASE); | |
1408 | } else { | |
1409 | /* GetWriteWatch will be useless. */ | |
1410 | GetWriteWatch_func = NULL; | |
1411 | } | |
1412 | } | |
1413 | if (GC_print_stats) { | |
1414 | if (GetWriteWatch_func == NULL) { | |
1415 | GC_log_printf("Did not find a usable GetWriteWatch()\n"); | |
1416 | } else { | |
1417 | GC_log_printf("Using GetWriteWatch()\n"); | |
1418 | } | |
1419 | } | |
1420 | done = TRUE; | |
1421 | } | |
1422 | ||
1423 | # endif /* GWW_VDB */ | |
1424 | ||
1425 | GC_bool GC_wnt = FALSE; | |
1426 | /* This is a Windows NT derivative, i.e. NT, W2K, XP or later. */ | |
1427 | ||
1428 | void GC_init_win32(void) | |
1429 | { | |
1430 | /* Set GC_wnt. */ | |
1431 | /* If we're running under win32s, assume that no DLLs will be loaded */ | |
1432 | /* I doubt anyone still runs win32s, but ... */ | |
1433 | DWORD v = GetVersion(); | |
1434 | GC_wnt = !(v & 0x80000000); | |
1435 | GC_no_win32_dlls |= ((!GC_wnt) && (v & 0xff) <= 3); | |
1436 | } | |
1437 | ||
1438 | /* Return the smallest address a such that VirtualQuery */ | |
1439 | /* returns correct results for all addresses between a and start. */ | |
1440 | /* Assumes VirtualQuery returns correct information for start. */ | |
1441 | ptr_t GC_least_described_address(ptr_t start) | |
1442 | { | |
1443 | MEMORY_BASIC_INFORMATION buf; | |
1444 | size_t result; | |
1445 | LPVOID limit; | |
1446 | ptr_t p; | |
1447 | LPVOID q; | |
1448 | ||
1449 | limit = GC_sysinfo.lpMinimumApplicationAddress; | |
1450 | p = (ptr_t)((word)start & ~(GC_page_size - 1)); | |
1451 | for (;;) { | |
1452 | q = (LPVOID)(p - GC_page_size); | |
1453 | if ((ptr_t)q > (ptr_t)p /* underflow */ || q < limit) break; | |
1454 | result = VirtualQuery(q, &buf, sizeof(buf)); | |
1455 | if (result != sizeof(buf) || buf.AllocationBase == 0) break; | |
1456 | p = (ptr_t)(buf.AllocationBase); | |
1457 | } | |
1458 | return p; | |
1459 | } | |
1460 | # endif | |
1461 | ||
1462 | # ifndef REDIRECT_MALLOC | |
1463 | /* We maintain a linked list of AllocationBase values that we know */ | |
1464 | /* correspond to malloc heap sections. Currently this is only called */ | |
1465 | /* during a GC. But there is some hope that for long running */ | |
1466 | /* programs we will eventually see most heap sections. */ | |
1467 | ||
1468 | /* In the long run, it would be more reliable to occasionally walk */ | |
1469 | /* the malloc heap with HeapWalk on the default heap. But that */ | |
1470 | /* apparently works only for NT-based Windows. */ | |
1471 | ||
1472 | /* In the long run, a better data structure would also be nice ... */ | |
1473 | struct GC_malloc_heap_list { | |
1474 | void * allocation_base; | |
1475 | struct GC_malloc_heap_list *next; | |
1476 | } *GC_malloc_heap_l = 0; | |
1477 | ||
1478 | /* Is p the base of one of the malloc heap sections we already know */ | |
1479 | /* about? */ | |
1480 | GC_bool GC_is_malloc_heap_base(ptr_t p) | |
1481 | { | |
1482 | struct GC_malloc_heap_list *q = GC_malloc_heap_l; | |
1483 | ||
1484 | while (0 != q) { | |
1485 | if (q -> allocation_base == p) return TRUE; | |
1486 | q = q -> next; | |
1487 | } | |
1488 | return FALSE; | |
1489 | } | |
1490 | ||
1491 | void *GC_get_allocation_base(void *p) | |
1492 | { | |
1493 | MEMORY_BASIC_INFORMATION buf; | |
1494 | size_t result = VirtualQuery(p, &buf, sizeof(buf)); | |
1495 | if (result != sizeof(buf)) { | |
1496 | ABORT("Weird VirtualQuery result"); | |
1497 | } | |
1498 | return buf.AllocationBase; | |
1499 | } | |
1500 | ||
1501 | size_t GC_max_root_size = 100000; /* Appr. largest root size. */ | |
1502 | ||
1503 | void GC_add_current_malloc_heap() | |
1504 | { | |
1505 | struct GC_malloc_heap_list *new_l = | |
1506 | malloc(sizeof(struct GC_malloc_heap_list)); | |
1507 | void * candidate = GC_get_allocation_base(new_l); | |
1508 | ||
1509 | if (new_l == 0) return; | |
1510 | if (GC_is_malloc_heap_base(candidate)) { | |
1511 | /* Try a little harder to find malloc heap. */ | |
1512 | size_t req_size = 10000; | |
1513 | do { | |
1514 | void *p = malloc(req_size); | |
1515 | if (0 == p) { free(new_l); return; } | |
1516 | candidate = GC_get_allocation_base(p); | |
1517 | free(p); | |
1518 | req_size *= 2; | |
1519 | } while (GC_is_malloc_heap_base(candidate) | |
1520 | && req_size < GC_max_root_size/10 && req_size < 500000); | |
1521 | if (GC_is_malloc_heap_base(candidate)) { | |
1522 | free(new_l); return; | |
1523 | } | |
1524 | } | |
1525 | if (GC_print_stats) | |
1526 | GC_log_printf("Found new system malloc AllocationBase at %p\n", | |
1527 | candidate); | |
1528 | new_l -> allocation_base = candidate; | |
1529 | new_l -> next = GC_malloc_heap_l; | |
1530 | GC_malloc_heap_l = new_l; | |
1531 | } | |
1532 | # endif /* REDIRECT_MALLOC */ | |
1533 | ||
1534 | /* Is p the start of either the malloc heap, or of one of our */ | |
1535 | /* heap sections? */ | |
1536 | GC_bool GC_is_heap_base (ptr_t p) | |
1537 | { | |
1538 | ||
1539 | unsigned i; | |
1540 | ||
1541 | # ifndef REDIRECT_MALLOC | |
1542 | static word last_gc_no = (word)(-1); | |
1543 | ||
1544 | if (last_gc_no != GC_gc_no) { | |
1545 | GC_add_current_malloc_heap(); | |
1546 | last_gc_no = GC_gc_no; | |
1547 | } | |
1548 | if (GC_root_size > GC_max_root_size) GC_max_root_size = GC_root_size; | |
1549 | if (GC_is_malloc_heap_base(p)) return TRUE; | |
1550 | # endif | |
1551 | for (i = 0; i < GC_n_heap_bases; i++) { | |
1552 | if (GC_heap_bases[i] == p) return TRUE; | |
1553 | } | |
1554 | return FALSE ; | |
1555 | } | |
1556 | ||
1557 | # ifdef MSWIN32 | |
1558 | void GC_register_root_section(ptr_t static_root) | |
1559 | { | |
1560 | MEMORY_BASIC_INFORMATION buf; | |
1561 | size_t result; | |
1562 | DWORD protect; | |
1563 | LPVOID p; | |
1564 | char * base; | |
1565 | char * limit, * new_limit; | |
1566 | ||
1567 | if (!GC_no_win32_dlls) return; | |
1568 | p = base = limit = GC_least_described_address(static_root); | |
1569 | while (p < GC_sysinfo.lpMaximumApplicationAddress) { | |
1570 | result = VirtualQuery(p, &buf, sizeof(buf)); | |
1571 | if (result != sizeof(buf) || buf.AllocationBase == 0 | |
1572 | || GC_is_heap_base(buf.AllocationBase)) break; | |
1573 | new_limit = (char *)p + buf.RegionSize; | |
1574 | protect = buf.Protect; | |
1575 | if (buf.State == MEM_COMMIT | |
1576 | && is_writable(protect)) { | |
1577 | if ((char *)p == limit) { | |
1578 | limit = new_limit; | |
1579 | } else { | |
1580 | if (base != limit) GC_add_roots_inner(base, limit, FALSE); | |
1581 | base = p; | |
1582 | limit = new_limit; | |
1583 | } | |
1584 | } | |
1585 | if (p > (LPVOID)new_limit /* overflow */) break; | |
1586 | p = (LPVOID)new_limit; | |
1587 | } | |
1588 | if (base != limit) GC_add_roots_inner(base, limit, FALSE); | |
1589 | } | |
1590 | #endif | |
1591 | ||
1592 | void GC_register_data_segments() | |
1593 | { | |
1594 | # ifdef MSWIN32 | |
1595 | static char dummy; | |
1596 | GC_register_root_section((ptr_t)(&dummy)); | |
1597 | # endif | |
1598 | } | |
1599 | ||
1600 | # else /* !OS2 && !Windows */ | |
1601 | ||
1602 | # if (defined(SVR4) || defined(AUX) || defined(DGUX) \ | |
1603 | || (defined(LINUX) && defined(SPARC))) && !defined(PCR) | |
1604 | ptr_t GC_SysVGetDataStart(size_t max_page_size, ptr_t etext_addr) | |
1605 | { | |
1606 | word text_end = ((word)(etext_addr) + sizeof(word) - 1) | |
1607 | & ~(sizeof(word) - 1); | |
1608 | /* etext rounded to word boundary */ | |
1609 | word next_page = ((text_end + (word)max_page_size - 1) | |
1610 | & ~((word)max_page_size - 1)); | |
1611 | word page_offset = (text_end & ((word)max_page_size - 1)); | |
1612 | volatile char * result = (char *)(next_page + page_offset); | |
1613 | /* Note that this isnt equivalent to just adding */ | |
1614 | /* max_page_size to &etext if &etext is at a page boundary */ | |
1615 | ||
1616 | GC_setup_temporary_fault_handler(); | |
1617 | if (SETJMP(GC_jmp_buf) == 0) { | |
1618 | /* Try writing to the address. */ | |
1619 | *result = *result; | |
1620 | GC_reset_fault_handler(); | |
1621 | } else { | |
1622 | GC_reset_fault_handler(); | |
1623 | /* We got here via a longjmp. The address is not readable. */ | |
1624 | /* This is known to happen under Solaris 2.4 + gcc, which place */ | |
1625 | /* string constants in the text segment, but after etext. */ | |
1626 | /* Use plan B. Note that we now know there is a gap between */ | |
1627 | /* text and data segments, so plan A bought us something. */ | |
1628 | result = (char *)GC_find_limit((ptr_t)(DATAEND), FALSE); | |
1629 | } | |
1630 | return((ptr_t)result); | |
1631 | } | |
1632 | # endif | |
1633 | ||
1634 | # if defined(FREEBSD) && (defined(I386) || defined(X86_64) || defined(powerpc) || defined(__powerpc__)) && !defined(PCR) | |
1635 | /* Its unclear whether this should be identical to the above, or */ | |
1636 | /* whether it should apply to non-X86 architectures. */ | |
1637 | /* For now we don't assume that there is always an empty page after */ | |
1638 | /* etext. But in some cases there actually seems to be slightly more. */ | |
1639 | /* This also deals with holes between read-only data and writable data. */ | |
1640 | ptr_t GC_FreeBSDGetDataStart(size_t max_page_size, ptr_t etext_addr) | |
1641 | { | |
1642 | word text_end = ((word)(etext_addr) + sizeof(word) - 1) | |
1643 | & ~(sizeof(word) - 1); | |
1644 | /* etext rounded to word boundary */ | |
1645 | volatile word next_page = (text_end + (word)max_page_size - 1) | |
1646 | & ~((word)max_page_size - 1); | |
1647 | volatile ptr_t result = (ptr_t)text_end; | |
1648 | GC_setup_temporary_fault_handler(); | |
1649 | if (SETJMP(GC_jmp_buf) == 0) { | |
1650 | /* Try reading at the address. */ | |
1651 | /* This should happen before there is another thread. */ | |
1652 | for (; next_page < (word)(DATAEND); next_page += (word)max_page_size) | |
1653 | *(volatile char *)next_page; | |
1654 | GC_reset_fault_handler(); | |
1655 | } else { | |
1656 | GC_reset_fault_handler(); | |
1657 | /* As above, we go to plan B */ | |
1658 | result = GC_find_limit((ptr_t)(DATAEND), FALSE); | |
1659 | } | |
1660 | return(result); | |
1661 | } | |
1662 | ||
1663 | # endif | |
1664 | ||
1665 | ||
1666 | #ifdef AMIGA | |
1667 | ||
1668 | # define GC_AMIGA_DS | |
1669 | # include "AmigaOS.c" | |
1670 | # undef GC_AMIGA_DS | |
1671 | ||
1672 | #else /* !OS2 && !Windows && !AMIGA */ | |
1673 | ||
1674 | void GC_register_data_segments(void) | |
1675 | { | |
1676 | # if !defined(PCR) && !defined(MACOS) | |
1677 | # if defined(REDIRECT_MALLOC) && defined(GC_SOLARIS_THREADS) | |
1678 | /* As of Solaris 2.3, the Solaris threads implementation */ | |
1679 | /* allocates the data structure for the initial thread with */ | |
1680 | /* sbrk at process startup. It needs to be scanned, so that */ | |
1681 | /* we don't lose some malloc allocated data structures */ | |
1682 | /* hanging from it. We're on thin ice here ... */ | |
1683 | extern caddr_t sbrk(); | |
1684 | ||
1685 | GC_add_roots_inner(DATASTART, (ptr_t)sbrk(0), FALSE); | |
1686 | # else | |
1687 | GC_add_roots_inner(DATASTART, (ptr_t)(DATAEND), FALSE); | |
1688 | # if defined(DATASTART2) | |
1689 | GC_add_roots_inner(DATASTART2, (ptr_t)(DATAEND2), FALSE); | |
1690 | # endif | |
1691 | # endif | |
1692 | # endif | |
1693 | # if defined(MACOS) | |
1694 | { | |
1695 | # if defined(THINK_C) | |
1696 | extern void* GC_MacGetDataStart(void); | |
1697 | /* globals begin above stack and end at a5. */ | |
1698 | GC_add_roots_inner((ptr_t)GC_MacGetDataStart(), | |
1699 | (ptr_t)LMGetCurrentA5(), FALSE); | |
1700 | # else | |
1701 | # if defined(__MWERKS__) | |
1702 | # if !__POWERPC__ | |
1703 | extern void* GC_MacGetDataStart(void); | |
1704 | /* MATTHEW: Function to handle Far Globals (CW Pro 3) */ | |
1705 | # if __option(far_data) | |
1706 | extern void* GC_MacGetDataEnd(void); | |
1707 | # endif | |
1708 | /* globals begin above stack and end at a5. */ | |
1709 | GC_add_roots_inner((ptr_t)GC_MacGetDataStart(), | |
1710 | (ptr_t)LMGetCurrentA5(), FALSE); | |
1711 | /* MATTHEW: Handle Far Globals */ | |
1712 | # if __option(far_data) | |
1713 | /* Far globals follow he QD globals: */ | |
1714 | GC_add_roots_inner((ptr_t)LMGetCurrentA5(), | |
1715 | (ptr_t)GC_MacGetDataEnd(), FALSE); | |
1716 | # endif | |
1717 | # else | |
1718 | extern char __data_start__[], __data_end__[]; | |
1719 | GC_add_roots_inner((ptr_t)&__data_start__, | |
1720 | (ptr_t)&__data_end__, FALSE); | |
1721 | # endif /* __POWERPC__ */ | |
1722 | # endif /* __MWERKS__ */ | |
1723 | # endif /* !THINK_C */ | |
1724 | } | |
1725 | # endif /* MACOS */ | |
1726 | ||
1727 | /* Dynamic libraries are added at every collection, since they may */ | |
1728 | /* change. */ | |
1729 | } | |
1730 | ||
1731 | # endif /* ! AMIGA */ | |
1732 | # endif /* ! MSWIN32 && ! MSWINCE*/ | |
1733 | # endif /* ! OS2 */ | |
1734 | ||
1735 | /* | |
1736 | * Auxiliary routines for obtaining memory from OS. | |
1737 | */ | |
1738 | ||
1739 | # if !defined(OS2) && !defined(PCR) && !defined(AMIGA) \ | |
1740 | && !defined(MSWIN32) && !defined(MSWINCE) \ | |
1741 | && !defined(MACOS) && !defined(DOS4GW) && !defined(NONSTOP) | |
1742 | ||
1743 | # define SBRK_ARG_T ptrdiff_t | |
1744 | ||
1745 | #if defined(MMAP_SUPPORTED) | |
1746 | ||
1747 | #ifdef USE_MMAP_FIXED | |
1748 | # define GC_MMAP_FLAGS MAP_FIXED | MAP_PRIVATE | |
1749 | /* Seems to yield better performance on Solaris 2, but can */ | |
1750 | /* be unreliable if something is already mapped at the address. */ | |
1751 | #else | |
1752 | # define GC_MMAP_FLAGS MAP_PRIVATE | |
1753 | #endif | |
1754 | ||
1755 | #ifdef USE_MMAP_ANON | |
1756 | # define zero_fd -1 | |
1757 | # if defined(MAP_ANONYMOUS) | |
1758 | # define OPT_MAP_ANON MAP_ANONYMOUS | |
1759 | # else | |
1760 | # define OPT_MAP_ANON MAP_ANON | |
1761 | # endif | |
1762 | #else | |
1763 | static int zero_fd; | |
1764 | # define OPT_MAP_ANON 0 | |
1765 | #endif | |
1766 | ||
1767 | #ifndef HEAP_START | |
1768 | # define HEAP_START 0 | |
1769 | #endif | |
1770 | ||
1771 | ptr_t GC_unix_mmap_get_mem(word bytes) | |
1772 | { | |
1773 | void *result; | |
1774 | static ptr_t last_addr = HEAP_START; | |
1775 | ||
1776 | # ifndef USE_MMAP_ANON | |
1777 | static GC_bool initialized = FALSE; | |
1778 | ||
1779 | if (!initialized) { | |
1780 | zero_fd = open("/dev/zero", O_RDONLY); | |
1781 | fcntl(zero_fd, F_SETFD, FD_CLOEXEC); | |
1782 | initialized = TRUE; | |
1783 | } | |
1784 | # endif | |
1785 | ||
1786 | if (bytes & (GC_page_size -1)) ABORT("Bad GET_MEM arg"); | |
1787 | result = mmap(last_addr, bytes, PROT_READ | PROT_WRITE | OPT_PROT_EXEC, | |
1788 | GC_MMAP_FLAGS | OPT_MAP_ANON, zero_fd, 0/* offset */); | |
1789 | if (result == MAP_FAILED) return(0); | |
1790 | last_addr = (ptr_t)result + bytes + GC_page_size - 1; | |
1791 | last_addr = (ptr_t)((word)last_addr & ~(GC_page_size - 1)); | |
1792 | # if !defined(LINUX) | |
1793 | if (last_addr == 0) { | |
1794 | /* Oops. We got the end of the address space. This isn't */ | |
1795 | /* usable by arbitrary C code, since one-past-end pointers */ | |
1796 | /* don't work, so we discard it and try again. */ | |
1797 | munmap(result, (size_t)(-GC_page_size) - (size_t)result); | |
1798 | /* Leave last page mapped, so we can't repeat. */ | |
1799 | return GC_unix_mmap_get_mem(bytes); | |
1800 | } | |
1801 | # else | |
1802 | GC_ASSERT(last_addr != 0); | |
1803 | # endif | |
1804 | return((ptr_t)result); | |
1805 | } | |
1806 | ||
1807 | # endif /* MMAP_SUPPORTED */ | |
1808 | ||
1809 | #if defined(USE_MMAP) | |
1810 | ||
1811 | ptr_t GC_unix_get_mem(word bytes) | |
1812 | { | |
1813 | return GC_unix_mmap_get_mem(bytes); | |
1814 | } | |
1815 | ||
1816 | #else /* Not USE_MMAP */ | |
1817 | ||
1818 | ptr_t GC_unix_sbrk_get_mem(word bytes) | |
1819 | { | |
1820 | ptr_t result; | |
1821 | # ifdef IRIX5 | |
1822 | /* Bare sbrk isn't thread safe. Play by malloc rules. */ | |
1823 | /* The equivalent may be needed on other systems as well. */ | |
1824 | __LOCK_MALLOC(); | |
1825 | # endif | |
1826 | { | |
1827 | ptr_t cur_brk = (ptr_t)sbrk(0); | |
1828 | SBRK_ARG_T lsbs = (word)cur_brk & (GC_page_size-1); | |
1829 | ||
1830 | if ((SBRK_ARG_T)bytes < 0) { | |
1831 | result = 0; /* too big */ | |
1832 | goto out; | |
1833 | } | |
1834 | if (lsbs != 0) { | |
1835 | if((ptr_t)sbrk(GC_page_size - lsbs) == (ptr_t)(-1)) { | |
1836 | result = 0; | |
1837 | goto out; | |
1838 | } | |
1839 | } | |
1840 | # ifdef ADD_HEAP_GUARD_PAGES | |
1841 | /* This is useful for catching severe memory overwrite problems that */ | |
1842 | /* span heap sections. It shouldn't otherwise be turned on. */ | |
1843 | { | |
1844 | ptr_t guard = (ptr_t)sbrk((SBRK_ARG_T)GC_page_size); | |
1845 | if (mprotect(guard, GC_page_size, PROT_NONE) != 0) | |
1846 | ABORT("ADD_HEAP_GUARD_PAGES: mprotect failed"); | |
1847 | } | |
1848 | # endif /* ADD_HEAP_GUARD_PAGES */ | |
1849 | result = (ptr_t)sbrk((SBRK_ARG_T)bytes); | |
1850 | if (result == (ptr_t)(-1)) result = 0; | |
1851 | } | |
1852 | out: | |
1853 | # ifdef IRIX5 | |
1854 | __UNLOCK_MALLOC(); | |
1855 | # endif | |
1856 | return(result); | |
1857 | } | |
1858 | ||
1859 | #if defined(MMAP_SUPPORTED) | |
1860 | ||
1861 | /* By default, we try both sbrk and mmap, in that order. */ | |
1862 | ptr_t GC_unix_get_mem(word bytes) | |
1863 | { | |
1864 | static GC_bool sbrk_failed = FALSE; | |
1865 | ptr_t result = 0; | |
1866 | ||
1867 | if (!sbrk_failed) result = GC_unix_sbrk_get_mem(bytes); | |
1868 | if (0 == result) { | |
1869 | sbrk_failed = TRUE; | |
1870 | result = GC_unix_mmap_get_mem(bytes); | |
1871 | } | |
1872 | if (0 == result) { | |
1873 | /* Try sbrk again, in case sbrk memory became available. */ | |
1874 | result = GC_unix_sbrk_get_mem(bytes); | |
1875 | } | |
1876 | return result; | |
1877 | } | |
1878 | ||
1879 | #else /* !MMAP_SUPPORTED */ | |
1880 | ||
1881 | ptr_t GC_unix_get_mem(word bytes) | |
1882 | { | |
1883 | return GC_unix_sbrk_get_mem(bytes); | |
1884 | } | |
1885 | ||
1886 | #endif | |
1887 | ||
1888 | #endif /* Not USE_MMAP */ | |
1889 | ||
1890 | # endif /* UN*X */ | |
1891 | ||
1892 | # ifdef OS2 | |
1893 | ||
1894 | void * os2_alloc(size_t bytes) | |
1895 | { | |
1896 | void * result; | |
1897 | ||
1898 | if (DosAllocMem(&result, bytes, PAG_EXECUTE | PAG_READ | | |
1899 | PAG_WRITE | PAG_COMMIT) | |
1900 | != NO_ERROR) { | |
1901 | return(0); | |
1902 | } | |
1903 | if (result == 0) return(os2_alloc(bytes)); | |
1904 | return(result); | |
1905 | } | |
1906 | ||
1907 | # endif /* OS2 */ | |
1908 | ||
1909 | ||
1910 | # if defined(MSWIN32) || defined(MSWINCE) | |
1911 | SYSTEM_INFO GC_sysinfo; | |
1912 | # endif | |
1913 | ||
1914 | # ifdef MSWIN32 | |
1915 | ||
1916 | # ifdef USE_GLOBAL_ALLOC | |
1917 | # define GLOBAL_ALLOC_TEST 1 | |
1918 | # else | |
1919 | # define GLOBAL_ALLOC_TEST GC_no_win32_dlls | |
1920 | # endif | |
1921 | ||
1922 | word GC_n_heap_bases = 0; | |
1923 | ||
1924 | word GC_mem_top_down = 0; /* Change to MEM_TOP_DOWN for better 64-bit */ | |
1925 | /* testing. Otherwise all addresses tend to */ | |
1926 | /* end up in first 4GB, hiding bugs. */ | |
1927 | ||
1928 | ptr_t GC_win32_get_mem(word bytes) | |
1929 | { | |
1930 | ptr_t result; | |
1931 | ||
1932 | if (GLOBAL_ALLOC_TEST) { | |
1933 | /* VirtualAlloc doesn't like PAGE_EXECUTE_READWRITE. */ | |
1934 | /* There are also unconfirmed rumors of other */ | |
1935 | /* problems, so we dodge the issue. */ | |
1936 | result = (ptr_t) GlobalAlloc(0, bytes + HBLKSIZE); | |
1937 | result = (ptr_t)(((word)result + HBLKSIZE - 1) & ~(HBLKSIZE-1)); | |
1938 | } else { | |
1939 | /* VirtualProtect only works on regions returned by a */ | |
1940 | /* single VirtualAlloc call. Thus we allocate one */ | |
1941 | /* extra page, which will prevent merging of blocks */ | |
1942 | /* in separate regions, and eliminate any temptation */ | |
1943 | /* to call VirtualProtect on a range spanning regions. */ | |
1944 | /* This wastes a small amount of memory, and risks */ | |
1945 | /* increased fragmentation. But better alternatives */ | |
1946 | /* would require effort. */ | |
1947 | /* Pass the MEM_WRITE_WATCH only if GetWriteWatch-based */ | |
1948 | /* VDBs are enabled and the GetWriteWatch function is */ | |
1949 | /* available. Otherwise we waste resources or possibly */ | |
1950 | /* cause VirtualAlloc to fail (observed in Windows 2000 */ | |
1951 | /* SP2). */ | |
1952 | result = (ptr_t) VirtualAlloc(NULL, bytes + 1, | |
1953 | # ifdef GWW_VDB | |
1954 | GetWriteWatch_alloc_flag | | |
1955 | # endif | |
1956 | MEM_COMMIT | MEM_RESERVE | |
1957 | | GC_mem_top_down, | |
1958 | PAGE_EXECUTE_READWRITE); | |
1959 | } | |
1960 | if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result"); | |
1961 | /* If I read the documentation correctly, this can */ | |
1962 | /* only happen if HBLKSIZE > 64k or not a power of 2. */ | |
1963 | if (GC_n_heap_bases >= MAX_HEAP_SECTS) ABORT("Too many heap sections"); | |
1964 | GC_heap_bases[GC_n_heap_bases++] = result; | |
1965 | return(result); | |
1966 | } | |
1967 | ||
1968 | void GC_win32_free_heap(void) | |
1969 | { | |
1970 | if (GC_no_win32_dlls) { | |
1971 | while (GC_n_heap_bases > 0) { | |
1972 | GlobalFree (GC_heap_bases[--GC_n_heap_bases]); | |
1973 | GC_heap_bases[GC_n_heap_bases] = 0; | |
1974 | } | |
1975 | } | |
1976 | } | |
1977 | # endif | |
1978 | ||
1979 | #ifdef AMIGA | |
1980 | # define GC_AMIGA_AM | |
1981 | # include "AmigaOS.c" | |
1982 | # undef GC_AMIGA_AM | |
1983 | #endif | |
1984 | ||
1985 | ||
1986 | # ifdef MSWINCE | |
1987 | word GC_n_heap_bases = 0; | |
1988 | ||
1989 | ptr_t GC_wince_get_mem(word bytes) | |
1990 | { | |
1991 | ptr_t result; | |
1992 | word i; | |
1993 | ||
1994 | /* Round up allocation size to multiple of page size */ | |
1995 | bytes = (bytes + GC_page_size-1) & ~(GC_page_size-1); | |
1996 | ||
1997 | /* Try to find reserved, uncommitted pages */ | |
1998 | for (i = 0; i < GC_n_heap_bases; i++) { | |
1999 | if (((word)(-(signed_word)GC_heap_lengths[i]) | |
2000 | & (GC_sysinfo.dwAllocationGranularity-1)) | |
2001 | >= bytes) { | |
2002 | result = GC_heap_bases[i] + GC_heap_lengths[i]; | |
2003 | break; | |
2004 | } | |
2005 | } | |
2006 | ||
2007 | if (i == GC_n_heap_bases) { | |
2008 | /* Reserve more pages */ | |
2009 | word res_bytes = (bytes + GC_sysinfo.dwAllocationGranularity-1) | |
2010 | & ~(GC_sysinfo.dwAllocationGranularity-1); | |
2011 | /* If we ever support MPROTECT_VDB here, we will probably need to */ | |
2012 | /* ensure that res_bytes is strictly > bytes, so that VirtualProtect */ | |
2013 | /* never spans regions. It seems to be OK for a VirtualFree */ | |
2014 | /* argument to span regions, so we should be OK for now. */ | |
2015 | result = (ptr_t) VirtualAlloc(NULL, res_bytes, | |
2016 | MEM_RESERVE | MEM_TOP_DOWN, | |
2017 | PAGE_EXECUTE_READWRITE); | |
2018 | if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result"); | |
2019 | /* If I read the documentation correctly, this can */ | |
2020 | /* only happen if HBLKSIZE > 64k or not a power of 2. */ | |
2021 | if (GC_n_heap_bases >= MAX_HEAP_SECTS) ABORT("Too many heap sections"); | |
2022 | GC_heap_bases[GC_n_heap_bases] = result; | |
2023 | GC_heap_lengths[GC_n_heap_bases] = 0; | |
2024 | GC_n_heap_bases++; | |
2025 | } | |
2026 | ||
2027 | /* Commit pages */ | |
2028 | result = (ptr_t) VirtualAlloc(result, bytes, | |
2029 | MEM_COMMIT, | |
2030 | PAGE_EXECUTE_READWRITE); | |
2031 | if (result != NULL) { | |
2032 | if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result"); | |
2033 | GC_heap_lengths[i] += bytes; | |
2034 | } | |
2035 | ||
2036 | return(result); | |
2037 | } | |
2038 | # endif | |
2039 | ||
2040 | #ifdef USE_MUNMAP | |
2041 | ||
2042 | /* For now, this only works on Win32/WinCE and some Unix-like */ | |
2043 | /* systems. If you have something else, don't define */ | |
2044 | /* USE_MUNMAP. */ | |
2045 | /* We assume ANSI C to support this feature. */ | |
2046 | ||
2047 | #if !defined(MSWIN32) && !defined(MSWINCE) | |
2048 | ||
2049 | #include <unistd.h> | |
2050 | #include <sys/mman.h> | |
2051 | #include <sys/stat.h> | |
2052 | #include <sys/types.h> | |
2053 | ||
2054 | #endif | |
2055 | ||
2056 | /* Compute a page aligned starting address for the unmap */ | |
2057 | /* operation on a block of size bytes starting at start. */ | |
2058 | /* Return 0 if the block is too small to make this feasible. */ | |
2059 | ptr_t GC_unmap_start(ptr_t start, size_t bytes) | |
2060 | { | |
2061 | ptr_t result = start; | |
2062 | /* Round start to next page boundary. */ | |
2063 | result += GC_page_size - 1; | |
2064 | result = (ptr_t)((word)result & ~(GC_page_size - 1)); | |
2065 | if (result + GC_page_size > start + bytes) return 0; | |
2066 | return result; | |
2067 | } | |
2068 | ||
2069 | /* Compute end address for an unmap operation on the indicated */ | |
2070 | /* block. */ | |
2071 | ptr_t GC_unmap_end(ptr_t start, size_t bytes) | |
2072 | { | |
2073 | ptr_t end_addr = start + bytes; | |
2074 | end_addr = (ptr_t)((word)end_addr & ~(GC_page_size - 1)); | |
2075 | return end_addr; | |
2076 | } | |
2077 | ||
2078 | /* Under Win32/WinCE we commit (map) and decommit (unmap) */ | |
2079 | /* memory using VirtualAlloc and VirtualFree. These functions */ | |
2080 | /* work on individual allocations of virtual memory, made */ | |
2081 | /* previously using VirtualAlloc with the MEM_RESERVE flag. */ | |
2082 | /* The ranges we need to (de)commit may span several of these */ | |
2083 | /* allocations; therefore we use VirtualQuery to check */ | |
2084 | /* allocation lengths, and split up the range as necessary. */ | |
2085 | ||
2086 | /* We assume that GC_remap is called on exactly the same range */ | |
2087 | /* as a previous call to GC_unmap. It is safe to consistently */ | |
2088 | /* round the endpoints in both places. */ | |
2089 | void GC_unmap(ptr_t start, size_t bytes) | |
2090 | { | |
2091 | ptr_t start_addr = GC_unmap_start(start, bytes); | |
2092 | ptr_t end_addr = GC_unmap_end(start, bytes); | |
2093 | word len = end_addr - start_addr; | |
2094 | if (0 == start_addr) return; | |
2095 | # if defined(MSWIN32) || defined(MSWINCE) | |
2096 | while (len != 0) { | |
2097 | MEMORY_BASIC_INFORMATION mem_info; | |
2098 | GC_word free_len; | |
2099 | if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info)) | |
2100 | != sizeof(mem_info)) | |
2101 | ABORT("Weird VirtualQuery result"); | |
2102 | free_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize; | |
2103 | if (!VirtualFree(start_addr, free_len, MEM_DECOMMIT)) | |
2104 | ABORT("VirtualFree failed"); | |
2105 | GC_unmapped_bytes += free_len; | |
2106 | start_addr += free_len; | |
2107 | len -= free_len; | |
2108 | } | |
2109 | # else | |
2110 | /* We immediately remap it to prevent an intervening mmap from */ | |
2111 | /* accidentally grabbing the same address space. */ | |
2112 | { | |
2113 | void * result; | |
2114 | result = mmap(start_addr, len, PROT_NONE, | |
2115 | MAP_PRIVATE | MAP_FIXED | OPT_MAP_ANON, | |
2116 | zero_fd, 0/* offset */); | |
2117 | if (result != (void *)start_addr) ABORT("mmap(...PROT_NONE...) failed"); | |
2118 | } | |
2119 | GC_unmapped_bytes += len; | |
2120 | # endif | |
2121 | } | |
2122 | ||
2123 | ||
2124 | void GC_remap(ptr_t start, size_t bytes) | |
2125 | { | |
2126 | ptr_t start_addr = GC_unmap_start(start, bytes); | |
2127 | ptr_t end_addr = GC_unmap_end(start, bytes); | |
2128 | word len = end_addr - start_addr; | |
2129 | ||
2130 | # if defined(MSWIN32) || defined(MSWINCE) | |
2131 | ptr_t result; | |
2132 | ||
2133 | if (0 == start_addr) return; | |
2134 | while (len != 0) { | |
2135 | MEMORY_BASIC_INFORMATION mem_info; | |
2136 | GC_word alloc_len; | |
2137 | if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info)) | |
2138 | != sizeof(mem_info)) | |
2139 | ABORT("Weird VirtualQuery result"); | |
2140 | alloc_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize; | |
2141 | result = VirtualAlloc(start_addr, alloc_len, | |
2142 | MEM_COMMIT, | |
2143 | PAGE_EXECUTE_READWRITE); | |
2144 | if (result != start_addr) { | |
2145 | ABORT("VirtualAlloc remapping failed"); | |
2146 | } | |
2147 | GC_unmapped_bytes -= alloc_len; | |
2148 | start_addr += alloc_len; | |
2149 | len -= alloc_len; | |
2150 | } | |
2151 | # else | |
2152 | /* It was already remapped with PROT_NONE. */ | |
2153 | int result; | |
2154 | ||
2155 | if (0 == start_addr) return; | |
2156 | result = mprotect(start_addr, len, | |
2157 | PROT_READ | PROT_WRITE | OPT_PROT_EXEC); | |
2158 | if (result != 0) { | |
2159 | GC_err_printf( | |
2160 | "Mprotect failed at %p (length %ld) with errno %d\n", | |
2161 | start_addr, (unsigned long)len, errno); | |
2162 | ABORT("Mprotect remapping failed"); | |
2163 | } | |
2164 | GC_unmapped_bytes -= len; | |
2165 | # endif | |
2166 | } | |
2167 | ||
2168 | /* Two adjacent blocks have already been unmapped and are about to */ | |
2169 | /* be merged. Unmap the whole block. This typically requires */ | |
2170 | /* that we unmap a small section in the middle that was not previously */ | |
2171 | /* unmapped due to alignment constraints. */ | |
2172 | void GC_unmap_gap(ptr_t start1, size_t bytes1, ptr_t start2, size_t bytes2) | |
2173 | { | |
2174 | ptr_t start1_addr = GC_unmap_start(start1, bytes1); | |
2175 | ptr_t end1_addr = GC_unmap_end(start1, bytes1); | |
2176 | ptr_t start2_addr = GC_unmap_start(start2, bytes2); | |
2177 | ptr_t end2_addr = GC_unmap_end(start2, bytes2); | |
2178 | ptr_t start_addr = end1_addr; | |
2179 | ptr_t end_addr = start2_addr; | |
2180 | size_t len; | |
2181 | GC_ASSERT(start1 + bytes1 == start2); | |
2182 | if (0 == start1_addr) start_addr = GC_unmap_start(start1, bytes1 + bytes2); | |
2183 | if (0 == start2_addr) end_addr = GC_unmap_end(start1, bytes1 + bytes2); | |
2184 | if (0 == start_addr) return; | |
2185 | len = end_addr - start_addr; | |
2186 | # if defined(MSWIN32) || defined(MSWINCE) | |
2187 | while (len != 0) { | |
2188 | MEMORY_BASIC_INFORMATION mem_info; | |
2189 | GC_word free_len; | |
2190 | if (VirtualQuery(start_addr, &mem_info, sizeof(mem_info)) | |
2191 | != sizeof(mem_info)) | |
2192 | ABORT("Weird VirtualQuery result"); | |
2193 | free_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize; | |
2194 | if (!VirtualFree(start_addr, free_len, MEM_DECOMMIT)) | |
2195 | ABORT("VirtualFree failed"); | |
2196 | GC_unmapped_bytes += free_len; | |
2197 | start_addr += free_len; | |
2198 | len -= free_len; | |
2199 | } | |
2200 | # else | |
2201 | if (len != 0 && munmap(start_addr, len) != 0) ABORT("munmap failed"); | |
2202 | GC_unmapped_bytes += len; | |
2203 | # endif | |
2204 | } | |
2205 | ||
2206 | #endif /* USE_MUNMAP */ | |
2207 | ||
2208 | /* Routine for pushing any additional roots. In THREADS */ | |
2209 | /* environment, this is also responsible for marking from */ | |
2210 | /* thread stacks. */ | |
2211 | #ifndef THREADS | |
2212 | void (*GC_push_other_roots)(void) = 0; | |
2213 | #else /* THREADS */ | |
2214 | ||
2215 | # ifdef PCR | |
2216 | PCR_ERes GC_push_thread_stack(PCR_Th_T *t, PCR_Any dummy) | |
2217 | { | |
2218 | struct PCR_ThCtl_TInfoRep info; | |
2219 | PCR_ERes result; | |
2220 | ||
2221 | info.ti_stkLow = info.ti_stkHi = 0; | |
2222 | result = PCR_ThCtl_GetInfo(t, &info); | |
2223 | GC_push_all_stack((ptr_t)(info.ti_stkLow), (ptr_t)(info.ti_stkHi)); | |
2224 | return(result); | |
2225 | } | |
2226 | ||
2227 | /* Push the contents of an old object. We treat this as stack */ | |
2228 | /* data only becasue that makes it robust against mark stack */ | |
2229 | /* overflow. */ | |
2230 | PCR_ERes GC_push_old_obj(void *p, size_t size, PCR_Any data) | |
2231 | { | |
2232 | GC_push_all_stack((ptr_t)p, (ptr_t)p + size); | |
2233 | return(PCR_ERes_okay); | |
2234 | } | |
2235 | ||
2236 | ||
2237 | void GC_default_push_other_roots(void) | |
2238 | { | |
2239 | /* Traverse data allocated by previous memory managers. */ | |
2240 | { | |
2241 | extern struct PCR_MM_ProcsRep * GC_old_allocator; | |
2242 | ||
2243 | if ((*(GC_old_allocator->mmp_enumerate))(PCR_Bool_false, | |
2244 | GC_push_old_obj, 0) | |
2245 | != PCR_ERes_okay) { | |
2246 | ABORT("Old object enumeration failed"); | |
2247 | } | |
2248 | } | |
2249 | /* Traverse all thread stacks. */ | |
2250 | if (PCR_ERes_IsErr( | |
2251 | PCR_ThCtl_ApplyToAllOtherThreads(GC_push_thread_stack,0)) | |
2252 | || PCR_ERes_IsErr(GC_push_thread_stack(PCR_Th_CurrThread(), 0))) { | |
2253 | ABORT("Thread stack marking failed\n"); | |
2254 | } | |
2255 | } | |
2256 | ||
2257 | # endif /* PCR */ | |
2258 | ||
2259 | ||
2260 | # if defined(GC_PTHREADS) || defined(GC_WIN32_THREADS) | |
2261 | ||
2262 | extern void GC_push_all_stacks(void); | |
2263 | ||
2264 | void GC_default_push_other_roots(void) | |
2265 | { | |
2266 | GC_push_all_stacks(); | |
2267 | } | |
2268 | ||
2269 | # endif /* GC_WIN32_THREADS || GC_PTHREADS */ | |
2270 | ||
2271 | void (*GC_push_other_roots)(void) = GC_default_push_other_roots; | |
2272 | ||
2273 | #endif /* THREADS */ | |
2274 | ||
2275 | /* | |
2276 | * Routines for accessing dirty bits on virtual pages. | |
2277 | * There are six ways to maintain this information: | |
2278 | * DEFAULT_VDB: A simple dummy implementation that treats every page | |
2279 | * as possibly dirty. This makes incremental collection | |
2280 | * useless, but the implementation is still correct. | |
2281 | * MANUAL_VDB: Stacks and static data are always considered dirty. | |
2282 | * Heap pages are considered dirty if GC_dirty(p) has been | |
2283 | * called on some pointer p pointing to somewhere inside | |
2284 | * an object on that page. A GC_dirty() call on a large | |
2285 | * object directly dirties only a single page, but for | |
2286 | * MANUAL_VDB we are careful to treat an object with a dirty | |
2287 | * page as completely dirty. | |
2288 | * In order to avoid races, an object must be marked dirty | |
2289 | * after it is written, and a reference to the object | |
2290 | * must be kept on a stack or in a register in the interim. | |
2291 | * With threads enabled, an object directly reachable from the | |
2292 | * stack at the time of a collection is treated as dirty. | |
2293 | * In single-threaded mode, it suffices to ensure that no | |
2294 | * collection can take place between the pointer assignment | |
2295 | * and the GC_dirty() call. | |
2296 | * PCR_VDB: Use PPCRs virtual dirty bit facility. | |
2297 | * PROC_VDB: Use the /proc facility for reading dirty bits. Only | |
2298 | * works under some SVR4 variants. Even then, it may be | |
2299 | * too slow to be entirely satisfactory. Requires reading | |
2300 | * dirty bits for entire address space. Implementations tend | |
2301 | * to assume that the client is a (slow) debugger. | |
2302 | * MPROTECT_VDB:Protect pages and then catch the faults to keep track of | |
2303 | * dirtied pages. The implementation (and implementability) | |
2304 | * is highly system dependent. This usually fails when system | |
2305 | * calls write to a protected page. We prevent the read system | |
2306 | * call from doing so. It is the clients responsibility to | |
2307 | * make sure that other system calls are similarly protected | |
2308 | * or write only to the stack. | |
2309 | * GWW_VDB: Use the Win32 GetWriteWatch functions, if available, to | |
2310 | * read dirty bits. In case it is not available (because we | |
2311 | * are running on Windows 95, Windows 2000 or earlier), | |
2312 | * MPROTECT_VDB may be defined as a fallback strategy. | |
2313 | */ | |
2314 | GC_bool GC_dirty_maintained = FALSE; | |
2315 | ||
2316 | #if defined(PROC_VDB) || defined(GWW_VDB) | |
2317 | ||
2318 | /* Add all pages in pht2 to pht1 */ | |
2319 | void GC_or_pages(page_hash_table pht1, page_hash_table pht2) | |
2320 | { | |
2321 | register int i; | |
2322 | ||
2323 | for (i = 0; i < PHT_SIZE; i++) pht1[i] |= pht2[i]; | |
2324 | } | |
2325 | ||
2326 | #endif | |
2327 | ||
2328 | #ifdef GWW_VDB | |
2329 | ||
2330 | # define GC_GWW_BUF_LEN 1024 | |
2331 | static PVOID gww_buf[GC_GWW_BUF_LEN]; | |
2332 | ||
2333 | # ifdef MPROTECT_VDB | |
2334 | GC_bool GC_gww_dirty_init(void) | |
2335 | { | |
2336 | detect_GetWriteWatch(); | |
2337 | return GC_GWW_AVAILABLE(); | |
2338 | } | |
2339 | # else | |
2340 | void GC_dirty_init(void) | |
2341 | { | |
2342 | detect_GetWriteWatch(); | |
2343 | GC_dirty_maintained = GC_GWW_AVAILABLE(); | |
2344 | } | |
2345 | # endif | |
2346 | ||
2347 | # ifdef MPROTECT_VDB | |
2348 | static void GC_gww_read_dirty(void) | |
2349 | # else | |
2350 | void GC_read_dirty(void) | |
2351 | # endif | |
2352 | { | |
2353 | word i; | |
2354 | ||
2355 | BZERO(GC_grungy_pages, sizeof(GC_grungy_pages)); | |
2356 | ||
2357 | for (i = 0; i != GC_n_heap_sects; ++i) { | |
2358 | ULONG_PTR count; | |
2359 | ||
2360 | do { | |
2361 | PVOID * pages, * pages_end; | |
2362 | DWORD page_size; | |
2363 | ||
2364 | pages = gww_buf; | |
2365 | count = GC_GWW_BUF_LEN; | |
2366 | /* | |
2367 | * GetWriteWatch is documented as returning non-zero when it fails, | |
2368 | * but the documentation doesn't explicitly say why it would fail or | |
2369 | * what its behaviour will be if it fails. | |
2370 | * It does appear to fail, at least on recent W2K instances, if | |
2371 | * the underlying memory was not allocated with the appropriate | |
2372 | * flag. This is common if GC_enable_incremental is called | |
2373 | * shortly after GC initialization. To avoid modifying the | |
2374 | * interface, we silently work around such a failure, it it only | |
2375 | * affects the initial (small) heap allocation. | |
2376 | * If there are more dirty | |
2377 | * pages than will fit in the buffer, this is not treated as a | |
2378 | * failure; we must check the page count in the loop condition. | |
2379 | * Since each partial call will reset the status of some | |
2380 | * pages, this should eventually terminate even in the overflow | |
2381 | * case. | |
2382 | */ | |
2383 | if (GetWriteWatch_func(WRITE_WATCH_FLAG_RESET, | |
2384 | GC_heap_sects[i].hs_start, | |
2385 | GC_heap_sects[i].hs_bytes, | |
2386 | pages, | |
2387 | &count, | |
2388 | &page_size) != 0) { | |
2389 | static int warn_count = 0; | |
2390 | unsigned j; | |
2391 | struct hblk * start = (struct hblk *)GC_heap_sects[i].hs_start; | |
2392 | static struct hblk *last_warned = 0; | |
2393 | size_t nblocks = divHBLKSZ(GC_heap_sects[i].hs_bytes); | |
2394 | ||
2395 | if ( i != 0 && last_warned != start && warn_count++ < 5) { | |
2396 | last_warned = start; | |
2397 | WARN( | |
2398 | "GC_gww_read_dirty unexpectedly failed at %ld: " | |
2399 | "Falling back to marking all pages dirty\n", start); | |
2400 | } | |
2401 | for (j = 0; j < nblocks; ++j) { | |
2402 | word hash = PHT_HASH(start + j); | |
2403 | set_pht_entry_from_index(GC_grungy_pages, hash); | |
2404 | } | |
2405 | count = 1; /* Done with this section. */ | |
2406 | } else /* succeeded */{ | |
2407 | pages_end = pages + count; | |
2408 | while (pages != pages_end) { | |
2409 | struct hblk * h = (struct hblk *) *pages++; | |
2410 | struct hblk * h_end = (struct hblk *) ((char *) h + page_size); | |
2411 | do | |
2412 | set_pht_entry_from_index(GC_grungy_pages, PHT_HASH(h)); | |
2413 | while (++h < h_end); | |
2414 | } | |
2415 | } | |
2416 | } while (count == GC_GWW_BUF_LEN); | |
2417 | } | |
2418 | ||
2419 | GC_or_pages(GC_written_pages, GC_grungy_pages); | |
2420 | } | |
2421 | ||
2422 | # ifdef MPROTECT_VDB | |
2423 | static GC_bool GC_gww_page_was_dirty(struct hblk * h) | |
2424 | # else | |
2425 | GC_bool GC_page_was_dirty(struct hblk * h) | |
2426 | # endif | |
2427 | { | |
2428 | return HDR(h) == 0 || get_pht_entry_from_index(GC_grungy_pages, PHT_HASH(h)); | |
2429 | } | |
2430 | ||
2431 | # ifdef MPROTECT_VDB | |
2432 | static GC_bool GC_gww_page_was_ever_dirty(struct hblk * h) | |
2433 | # else | |
2434 | GC_bool GC_page_was_ever_dirty(struct hblk * h) | |
2435 | # endif | |
2436 | { | |
2437 | return HDR(h) == 0 || get_pht_entry_from_index(GC_written_pages, PHT_HASH(h)); | |
2438 | } | |
2439 | ||
2440 | # ifndef MPROTECT_VDB | |
2441 | void GC_remove_protection(struct hblk *h, word nblocks, GC_bool is_ptrfree) | |
2442 | {} | |
2443 | # endif | |
2444 | ||
2445 | # endif /* GWW_VDB */ | |
2446 | ||
2447 | # ifdef DEFAULT_VDB | |
2448 | ||
2449 | /* All of the following assume the allocation lock is held, and */ | |
2450 | /* signals are disabled. */ | |
2451 | ||
2452 | /* The client asserts that unallocated pages in the heap are never */ | |
2453 | /* written. */ | |
2454 | ||
2455 | /* Initialize virtual dirty bit implementation. */ | |
2456 | void GC_dirty_init(void) | |
2457 | { | |
2458 | if (GC_print_stats == VERBOSE) | |
2459 | GC_log_printf("Initializing DEFAULT_VDB...\n"); | |
2460 | GC_dirty_maintained = TRUE; | |
2461 | } | |
2462 | ||
2463 | /* Retrieve system dirty bits for heap to a local buffer. */ | |
2464 | /* Restore the systems notion of which pages are dirty. */ | |
2465 | void GC_read_dirty(void) | |
2466 | {} | |
2467 | ||
2468 | /* Is the HBLKSIZE sized page at h marked dirty in the local buffer? */ | |
2469 | /* If the actual page size is different, this returns TRUE if any */ | |
2470 | /* of the pages overlapping h are dirty. This routine may err on the */ | |
2471 | /* side of labelling pages as dirty (and this implementation does). */ | |
2472 | /*ARGSUSED*/ | |
2473 | GC_bool GC_page_was_dirty(struct hblk *h) | |
2474 | { | |
2475 | return(TRUE); | |
2476 | } | |
2477 | ||
2478 | /* | |
2479 | * The following two routines are typically less crucial. They matter | |
2480 | * most with large dynamic libraries, or if we can't accurately identify | |
2481 | * stacks, e.g. under Solaris 2.X. Otherwise the following default | |
2482 | * versions are adequate. | |
2483 | */ | |
2484 | ||
2485 | /* Could any valid GC heap pointer ever have been written to this page? */ | |
2486 | /*ARGSUSED*/ | |
2487 | GC_bool GC_page_was_ever_dirty(struct hblk *h) | |
2488 | { | |
2489 | return(TRUE); | |
2490 | } | |
2491 | ||
2492 | /* A call that: */ | |
2493 | /* I) hints that [h, h+nblocks) is about to be written. */ | |
2494 | /* II) guarantees that protection is removed. */ | |
2495 | /* (I) may speed up some dirty bit implementations. */ | |
2496 | /* (II) may be essential if we need to ensure that */ | |
2497 | /* pointer-free system call buffers in the heap are */ | |
2498 | /* not protected. */ | |
2499 | /*ARGSUSED*/ | |
2500 | void GC_remove_protection(struct hblk *h, word nblocks, GC_bool is_ptrfree) | |
2501 | { | |
2502 | } | |
2503 | ||
2504 | # endif /* DEFAULT_VDB */ | |
2505 | ||
2506 | # ifdef MANUAL_VDB | |
2507 | ||
2508 | /* Initialize virtual dirty bit implementation. */ | |
2509 | void GC_dirty_init(void) | |
2510 | { | |
2511 | if (GC_print_stats == VERBOSE) | |
2512 | GC_log_printf("Initializing MANUAL_VDB...\n"); | |
2513 | /* GC_dirty_pages and GC_grungy_pages are already cleared. */ | |
2514 | GC_dirty_maintained = TRUE; | |
2515 | } | |
2516 | ||
2517 | /* Retrieve system dirty bits for heap to a local buffer. */ | |
2518 | /* Restore the systems notion of which pages are dirty. */ | |
2519 | void GC_read_dirty(void) | |
2520 | { | |
2521 | BCOPY((word *)GC_dirty_pages, GC_grungy_pages, | |
2522 | (sizeof GC_dirty_pages)); | |
2523 | BZERO((word *)GC_dirty_pages, (sizeof GC_dirty_pages)); | |
2524 | } | |
2525 | ||
2526 | /* Is the HBLKSIZE sized page at h marked dirty in the local buffer? */ | |
2527 | /* If the actual page size is different, this returns TRUE if any */ | |
2528 | /* of the pages overlapping h are dirty. This routine may err on the */ | |
2529 | /* side of labelling pages as dirty (and this implementation does). */ | |
2530 | /*ARGSUSED*/ | |
2531 | GC_bool GC_page_was_dirty(struct hblk *h) | |
2532 | { | |
2533 | register word index; | |
2534 | ||
2535 | index = PHT_HASH(h); | |
2536 | return(HDR(h) == 0 || get_pht_entry_from_index(GC_grungy_pages, index)); | |
2537 | } | |
2538 | ||
2539 | /* Could any valid GC heap pointer ever have been written to this page? */ | |
2540 | /*ARGSUSED*/ | |
2541 | GC_bool GC_page_was_ever_dirty(struct hblk *h) | |
2542 | { | |
2543 | /* FIXME - implement me. */ | |
2544 | return(TRUE); | |
2545 | } | |
2546 | ||
2547 | /* Mark the page containing p as dirty. Logically, this dirties the */ | |
2548 | /* entire object. */ | |
2549 | void GC_dirty(ptr_t p) | |
2550 | { | |
2551 | word index = PHT_HASH(p); | |
2552 | async_set_pht_entry_from_index(GC_dirty_pages, index); | |
2553 | } | |
2554 | ||
2555 | /*ARGSUSED*/ | |
2556 | void GC_remove_protection(struct hblk *h, word nblocks, GC_bool is_ptrfree) | |
2557 | { | |
2558 | } | |
2559 | ||
2560 | # endif /* MANUAL_VDB */ | |
2561 | ||
2562 | ||
2563 | # ifdef MPROTECT_VDB | |
2564 | ||
2565 | /* | |
2566 | * See DEFAULT_VDB for interface descriptions. | |
2567 | */ | |
2568 | ||
2569 | /* | |
2570 | * This implementation maintains dirty bits itself by catching write | |
2571 | * faults and keeping track of them. We assume nobody else catches | |
2572 | * SIGBUS or SIGSEGV. We assume no write faults occur in system calls. | |
2573 | * This means that clients must ensure that system calls don't write | |
2574 | * to the write-protected heap. Probably the best way to do this is to | |
2575 | * ensure that system calls write at most to POINTERFREE objects in the | |
2576 | * heap, and do even that only if we are on a platform on which those | |
2577 | * are not protected. Another alternative is to wrap system calls | |
2578 | * (see example for read below), but the current implementation holds | |
2579 | * applications. | |
2580 | * We assume the page size is a multiple of HBLKSIZE. | |
2581 | * We prefer them to be the same. We avoid protecting POINTERFREE | |
2582 | * objects only if they are the same. | |
2583 | */ | |
2584 | ||
2585 | # if !defined(MSWIN32) && !defined(MSWINCE) && !defined(DARWIN) | |
2586 | ||
2587 | # include <sys/mman.h> | |
2588 | # include <signal.h> | |
2589 | # include <sys/syscall.h> | |
2590 | ||
2591 | # define PROTECT(addr, len) \ | |
2592 | if (mprotect((caddr_t)(addr), (size_t)(len), \ | |
2593 | PROT_READ | OPT_PROT_EXEC) < 0) { \ | |
2594 | ABORT("mprotect failed"); \ | |
2595 | } | |
2596 | # define UNPROTECT(addr, len) \ | |
2597 | if (mprotect((caddr_t)(addr), (size_t)(len), \ | |
2598 | PROT_WRITE | PROT_READ | OPT_PROT_EXEC ) < 0) { \ | |
2599 | ABORT("un-mprotect failed"); \ | |
2600 | } | |
2601 | ||
2602 | # else | |
2603 | ||
2604 | # ifdef DARWIN | |
2605 | /* Using vm_protect (mach syscall) over mprotect (BSD syscall) seems to | |
2606 | decrease the likelihood of some of the problems described below. */ | |
2607 | #include <mach/vm_map.h> | |
2608 | static mach_port_t GC_task_self; | |
2609 | #define PROTECT(addr,len) \ | |
2610 | if(vm_protect(GC_task_self,(vm_address_t)(addr),(vm_size_t)(len), \ | |
2611 | FALSE,VM_PROT_READ) != KERN_SUCCESS) { \ | |
2612 | ABORT("vm_portect failed"); \ | |
2613 | } | |
2614 | #define UNPROTECT(addr,len) \ | |
2615 | if(vm_protect(GC_task_self,(vm_address_t)(addr),(vm_size_t)(len), \ | |
2616 | FALSE,VM_PROT_READ|VM_PROT_WRITE) != KERN_SUCCESS) { \ | |
2617 | ABORT("vm_portect failed"); \ | |
2618 | } | |
2619 | # else | |
2620 | ||
2621 | # ifndef MSWINCE | |
2622 | # include <signal.h> | |
2623 | # endif | |
2624 | ||
2625 | static DWORD protect_junk; | |
2626 | # define PROTECT(addr, len) \ | |
2627 | if (!VirtualProtect((addr), (len), PAGE_EXECUTE_READ, \ | |
2628 | &protect_junk)) { \ | |
2629 | DWORD last_error = GetLastError(); \ | |
2630 | GC_printf("Last error code: %lx\n", last_error); \ | |
2631 | ABORT("VirtualProtect failed"); \ | |
2632 | } | |
2633 | # define UNPROTECT(addr, len) \ | |
2634 | if (!VirtualProtect((addr), (len), PAGE_EXECUTE_READWRITE, \ | |
2635 | &protect_junk)) { \ | |
2636 | ABORT("un-VirtualProtect failed"); \ | |
2637 | } | |
2638 | # endif /* !DARWIN */ | |
2639 | # endif /* MSWIN32 || MSWINCE || DARWIN */ | |
2640 | ||
2641 | #if defined(MSWIN32) | |
2642 | typedef LPTOP_LEVEL_EXCEPTION_FILTER SIG_HNDLR_PTR; | |
2643 | # undef SIG_DFL | |
2644 | # define SIG_DFL (LPTOP_LEVEL_EXCEPTION_FILTER) (-1) | |
2645 | #elif defined(MSWINCE) | |
2646 | typedef LONG (WINAPI *SIG_HNDLR_PTR)(struct _EXCEPTION_POINTERS *); | |
2647 | # undef SIG_DFL | |
2648 | # define SIG_DFL (SIG_HNDLR_PTR) (-1) | |
2649 | #elif defined(DARWIN) | |
2650 | typedef void (* SIG_HNDLR_PTR)(); | |
2651 | #else | |
2652 | typedef void (* SIG_HNDLR_PTR)(int, siginfo_t *, void *); | |
2653 | typedef void (* PLAIN_HNDLR_PTR)(int); | |
2654 | #endif | |
2655 | ||
2656 | #if defined(__GLIBC__) | |
2657 | # if __GLIBC__ < 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ < 2 | |
2658 | # error glibc too old? | |
2659 | # endif | |
2660 | #endif | |
2661 | ||
2662 | #ifndef DARWIN | |
2663 | SIG_HNDLR_PTR GC_old_bus_handler; | |
2664 | GC_bool GC_old_bus_handler_used_si; | |
2665 | SIG_HNDLR_PTR GC_old_segv_handler; | |
2666 | /* Also old MSWIN32 ACCESS_VIOLATION filter */ | |
2667 | GC_bool GC_old_segv_handler_used_si; | |
2668 | #endif /* !DARWIN */ | |
2669 | ||
2670 | #if defined(THREADS) | |
2671 | /* We need to lock around the bitmap update in the write fault handler */ | |
2672 | /* in order to avoid the risk of losing a bit. We do this with a */ | |
2673 | /* test-and-set spin lock if we know how to do that. Otherwise we */ | |
2674 | /* check whether we are already in the handler and use the dumb but */ | |
2675 | /* safe fallback algorithm of setting all bits in the word. */ | |
2676 | /* Contention should be very rare, so we do the minimum to handle it */ | |
2677 | /* correctly. */ | |
2678 | #ifdef AO_HAVE_test_and_set_acquire | |
2679 | static volatile AO_TS_t fault_handler_lock = 0; | |
2680 | void async_set_pht_entry_from_index(volatile page_hash_table db, size_t index) { | |
2681 | while (AO_test_and_set_acquire(&fault_handler_lock) == AO_TS_SET) {} | |
2682 | /* Could also revert to set_pht_entry_from_index_safe if initial */ | |
2683 | /* GC_test_and_set fails. */ | |
2684 | set_pht_entry_from_index(db, index); | |
2685 | AO_CLEAR(&fault_handler_lock); | |
2686 | } | |
2687 | #else /* !AO_have_test_and_set_acquire */ | |
2688 | # error No test_and_set operation: Introduces a race. | |
2689 | /* THIS WOULD BE INCORRECT! */ | |
2690 | /* The dirty bit vector may be temporarily wrong, */ | |
2691 | /* just before we notice the conflict and correct it. We may end up */ | |
2692 | /* looking at it while it's wrong. But this requires contention */ | |
2693 | /* exactly when a GC is triggered, which seems far less likely to */ | |
2694 | /* fail than the old code, which had no reported failures. Thus we */ | |
2695 | /* leave it this way while we think of something better, or support */ | |
2696 | /* GC_test_and_set on the remaining platforms. */ | |
2697 | static volatile word currently_updating = 0; | |
2698 | void async_set_pht_entry_from_index(volatile page_hash_table db, size_t index) { | |
2699 | unsigned int update_dummy; | |
2700 | currently_updating = (word)(&update_dummy); | |
2701 | set_pht_entry_from_index(db, index); | |
2702 | /* If we get contention in the 10 or so instruction window here, */ | |
2703 | /* and we get stopped by a GC between the two updates, we lose! */ | |
2704 | if (currently_updating != (word)(&update_dummy)) { | |
2705 | set_pht_entry_from_index_safe(db, index); | |
2706 | /* We claim that if two threads concurrently try to update the */ | |
2707 | /* dirty bit vector, the first one to execute UPDATE_START */ | |
2708 | /* will see it changed when UPDATE_END is executed. (Note that */ | |
2709 | /* &update_dummy must differ in two distinct threads.) It */ | |
2710 | /* will then execute set_pht_entry_from_index_safe, thus */ | |
2711 | /* returning us to a safe state, though not soon enough. */ | |
2712 | } | |
2713 | } | |
2714 | #endif /* !AO_HAVE_test_and_set_acquire */ | |
2715 | #else /* !THREADS */ | |
2716 | # define async_set_pht_entry_from_index(db, index) \ | |
2717 | set_pht_entry_from_index(db, index) | |
2718 | #endif /* !THREADS */ | |
2719 | ||
2720 | #if !defined(DARWIN) | |
2721 | # include <errno.h> | |
2722 | # if defined(FREEBSD) | |
2723 | # define SIG_OK TRUE | |
2724 | # define CODE_OK (code == BUS_PAGE_FAULT) | |
2725 | # elif defined(OSF1) | |
2726 | # define SIG_OK (sig == SIGSEGV) | |
2727 | # define CODE_OK (code == 2 /* experimentally determined */) | |
2728 | # elif defined(IRIX5) | |
2729 | # define SIG_OK (sig == SIGSEGV) | |
2730 | # define CODE_OK (code == EACCES) | |
2731 | # elif defined(HURD) | |
2732 | # define SIG_OK (sig == SIGBUS || sig == SIGSEGV) | |
2733 | # define CODE_OK TRUE | |
2734 | # elif defined(LINUX) | |
2735 | # define SIG_OK (sig == SIGSEGV) | |
2736 | # define CODE_OK TRUE | |
2737 | /* Empirically c.trapno == 14, on IA32, but is that useful? */ | |
2738 | /* Should probably consider alignment issues on other */ | |
2739 | /* architectures. */ | |
2740 | # elif defined(HPUX) | |
2741 | # define SIG_OK (sig == SIGSEGV || sig == SIGBUS) | |
2742 | # define CODE_OK (si -> si_code == SEGV_ACCERR) \ | |
2743 | || (si -> si_code == BUS_ADRERR) \ | |
2744 | || (si -> si_code == BUS_UNKNOWN) \ | |
2745 | || (si -> si_code == SEGV_UNKNOWN) \ | |
2746 | || (si -> si_code == BUS_OBJERR) | |
2747 | # elif defined(FREEBSD) | |
2748 | # define SIG_OK (sig == SIGBUS) | |
2749 | # define CODE_OK (si -> si_code == BUS_PAGE_FAULT) | |
2750 | # elif defined(SUNOS5SIGS) | |
2751 | # define SIG_OK (sig == SIGSEGV) | |
2752 | # define CODE_OK (si -> si_code == SEGV_ACCERR) | |
2753 | # elif defined(MSWIN32) || defined(MSWINCE) | |
2754 | # define SIG_OK (exc_info -> ExceptionRecord -> ExceptionCode \ | |
2755 | == STATUS_ACCESS_VIOLATION) | |
2756 | # define CODE_OK (exc_info -> ExceptionRecord -> ExceptionInformation[0] \ | |
2757 | == 1) /* Write fault */ | |
2758 | # endif | |
2759 | ||
2760 | # if defined(MSWIN32) || defined(MSWINCE) | |
2761 | LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info) | |
2762 | # else | |
2763 | # include <ucontext.h> | |
2764 | /*ARGSUSED*/ | |
2765 | void GC_write_fault_handler(int sig, siginfo_t *si, void *raw_sc) | |
2766 | # endif /* MSWIN32 || MSWINCE */ | |
2767 | { | |
2768 | # if !defined(MSWIN32) && !defined(MSWINCE) | |
2769 | int code = si -> si_code; /* Ignore gcc unused var. warning. */ | |
2770 | ucontext_t * scp = (ucontext_t *)raw_sc; | |
2771 | /* Ignore gcc unused var. warning. */ | |
2772 | char *addr = si -> si_addr; | |
2773 | # endif | |
2774 | # if defined(MSWIN32) || defined(MSWINCE) | |
2775 | char * addr = (char *) (exc_info -> ExceptionRecord | |
2776 | -> ExceptionInformation[1]); | |
2777 | # define sig SIGSEGV | |
2778 | # endif | |
2779 | unsigned i; | |
2780 | ||
2781 | if (SIG_OK && CODE_OK) { | |
2782 | register struct hblk * h = | |
2783 | (struct hblk *)((word)addr & ~(GC_page_size-1)); | |
2784 | GC_bool in_allocd_block; | |
2785 | ||
2786 | # ifdef SUNOS5SIGS | |
2787 | /* Address is only within the correct physical page. */ | |
2788 | in_allocd_block = FALSE; | |
2789 | for (i = 0; i < divHBLKSZ(GC_page_size); i++) { | |
2790 | if (HDR(h+i) != 0) { | |
2791 | in_allocd_block = TRUE; | |
2792 | } | |
2793 | } | |
2794 | # else | |
2795 | in_allocd_block = (HDR(addr) != 0); | |
2796 | # endif | |
2797 | if (!in_allocd_block) { | |
2798 | /* FIXME - We should make sure that we invoke the */ | |
2799 | /* old handler with the appropriate calling */ | |
2800 | /* sequence, which often depends on SA_SIGINFO. */ | |
2801 | ||
2802 | /* Heap blocks now begin and end on page boundaries */ | |
2803 | SIG_HNDLR_PTR old_handler; | |
2804 | GC_bool used_si; | |
2805 | ||
2806 | if (sig == SIGSEGV) { | |
2807 | old_handler = GC_old_segv_handler; | |
2808 | used_si = GC_old_segv_handler_used_si; | |
2809 | } else { | |
2810 | old_handler = GC_old_bus_handler; | |
2811 | used_si = GC_old_bus_handler_used_si; | |
2812 | } | |
2813 | if (old_handler == (SIG_HNDLR_PTR)SIG_DFL) { | |
2814 | # if !defined(MSWIN32) && !defined(MSWINCE) | |
2815 | GC_err_printf("Segfault at %p\n", addr); | |
2816 | ABORT("Unexpected bus error or segmentation fault"); | |
2817 | # else | |
2818 | return(EXCEPTION_CONTINUE_SEARCH); | |
2819 | # endif | |
2820 | } else { | |
2821 | /* | |
2822 | * FIXME: This code should probably check if the | |
2823 | * old signal handler used the traditional style and | |
2824 | * if so call it using that style. | |
2825 | */ | |
2826 | # ifdef MSWIN32 | |
2827 | return((*old_handler)(exc_info)); | |
2828 | # else | |
2829 | if (used_si) | |
2830 | ((SIG_HNDLR_PTR)old_handler) (sig, si, raw_sc); | |
2831 | else | |
2832 | /* FIXME: should pass nonstandard args as well. */ | |
2833 | ((PLAIN_HNDLR_PTR)old_handler) (sig); | |
2834 | return; | |
2835 | # endif | |
2836 | } | |
2837 | } | |
2838 | UNPROTECT(h, GC_page_size); | |
2839 | /* We need to make sure that no collection occurs between */ | |
2840 | /* the UNPROTECT and the setting of the dirty bit. Otherwise */ | |
2841 | /* a write by a third thread might go unnoticed. Reversing */ | |
2842 | /* the order is just as bad, since we would end up unprotecting */ | |
2843 | /* a page in a GC cycle during which it's not marked. */ | |
2844 | /* Currently we do this by disabling the thread stopping */ | |
2845 | /* signals while this handler is running. An alternative might */ | |
2846 | /* be to record the fact that we're about to unprotect, or */ | |
2847 | /* have just unprotected a page in the GC's thread structure, */ | |
2848 | /* and then to have the thread stopping code set the dirty */ | |
2849 | /* flag, if necessary. */ | |
2850 | for (i = 0; i < divHBLKSZ(GC_page_size); i++) { | |
2851 | size_t index = PHT_HASH(h+i); | |
2852 | ||
2853 | async_set_pht_entry_from_index(GC_dirty_pages, index); | |
2854 | } | |
2855 | /* The write may not take place before dirty bits are read. */ | |
2856 | /* But then we'll fault again ... */ | |
2857 | # if defined(MSWIN32) || defined(MSWINCE) | |
2858 | return(EXCEPTION_CONTINUE_EXECUTION); | |
2859 | # else | |
2860 | return; | |
2861 | # endif | |
2862 | } | |
2863 | #if defined(MSWIN32) || defined(MSWINCE) | |
2864 | return EXCEPTION_CONTINUE_SEARCH; | |
2865 | #else | |
2866 | GC_err_printf("Segfault at %p\n", addr); | |
2867 | ABORT("Unexpected bus error or segmentation fault"); | |
2868 | #endif | |
2869 | } | |
2870 | #endif /* !DARWIN */ | |
2871 | ||
2872 | /* | |
2873 | * We hold the allocation lock. We expect block h to be written | |
2874 | * shortly. Ensure that all pages containing any part of the n hblks | |
2875 | * starting at h are no longer protected. If is_ptrfree is false, | |
2876 | * also ensure that they will subsequently appear to be dirty. | |
2877 | */ | |
2878 | void GC_remove_protection(struct hblk *h, word nblocks, GC_bool is_ptrfree) | |
2879 | { | |
2880 | struct hblk * h_trunc; /* Truncated to page boundary */ | |
2881 | struct hblk * h_end; /* Page boundary following block end */ | |
2882 | struct hblk * current; | |
2883 | GC_bool found_clean; | |
2884 | ||
2885 | # if defined(GWW_VDB) | |
2886 | if (GC_GWW_AVAILABLE()) return; | |
2887 | # endif | |
2888 | if (!GC_dirty_maintained) return; | |
2889 | h_trunc = (struct hblk *)((word)h & ~(GC_page_size-1)); | |
2890 | h_end = (struct hblk *)(((word)(h + nblocks) + GC_page_size-1) | |
2891 | & ~(GC_page_size-1)); | |
2892 | found_clean = FALSE; | |
2893 | for (current = h_trunc; current < h_end; ++current) { | |
2894 | size_t index = PHT_HASH(current); | |
2895 | ||
2896 | if (!is_ptrfree || current < h || current >= h + nblocks) { | |
2897 | async_set_pht_entry_from_index(GC_dirty_pages, index); | |
2898 | } | |
2899 | } | |
2900 | UNPROTECT(h_trunc, (ptr_t)h_end - (ptr_t)h_trunc); | |
2901 | } | |
2902 | ||
2903 | #if !defined(DARWIN) | |
2904 | void GC_dirty_init(void) | |
2905 | { | |
2906 | # if !defined(MSWIN32) && !defined(MSWINCE) | |
2907 | struct sigaction act, oldact; | |
2908 | act.sa_flags = SA_RESTART | SA_SIGINFO; | |
2909 | act.sa_sigaction = GC_write_fault_handler; | |
2910 | (void)sigemptyset(&act.sa_mask); | |
2911 | # ifdef SIG_SUSPEND | |
2912 | /* Arrange to postpone SIG_SUSPEND while we're in a write fault */ | |
2913 | /* handler. This effectively makes the handler atomic w.r.t. */ | |
2914 | /* stopping the world for GC. */ | |
2915 | (void)sigaddset(&act.sa_mask, SIG_SUSPEND); | |
2916 | # endif /* SIG_SUSPEND */ | |
2917 | # endif | |
2918 | if (GC_print_stats == VERBOSE) | |
2919 | GC_log_printf( | |
2920 | "Initializing mprotect virtual dirty bit implementation\n"); | |
2921 | GC_dirty_maintained = TRUE; | |
2922 | if (GC_page_size % HBLKSIZE != 0) { | |
2923 | GC_err_printf("Page size not multiple of HBLKSIZE\n"); | |
2924 | ABORT("Page size not multiple of HBLKSIZE"); | |
2925 | } | |
2926 | # if !defined(MSWIN32) && !defined(MSWINCE) | |
2927 | # if defined(GC_IRIX_THREADS) | |
2928 | sigaction(SIGSEGV, 0, &oldact); | |
2929 | sigaction(SIGSEGV, &act, 0); | |
2930 | # else | |
2931 | { | |
2932 | int res = sigaction(SIGSEGV, &act, &oldact); | |
2933 | if (res != 0) ABORT("Sigaction failed"); | |
2934 | } | |
2935 | # endif | |
2936 | if (oldact.sa_flags & SA_SIGINFO) { | |
2937 | GC_old_segv_handler = oldact.sa_sigaction; | |
2938 | GC_old_segv_handler_used_si = TRUE; | |
2939 | } else { | |
2940 | GC_old_segv_handler = (SIG_HNDLR_PTR)oldact.sa_handler; | |
2941 | GC_old_segv_handler_used_si = FALSE; | |
2942 | } | |
2943 | if (GC_old_segv_handler == (SIG_HNDLR_PTR)SIG_IGN) { | |
2944 | GC_err_printf("Previously ignored segmentation violation!?"); | |
2945 | GC_old_segv_handler = (SIG_HNDLR_PTR)SIG_DFL; | |
2946 | } | |
2947 | if (GC_old_segv_handler != (SIG_HNDLR_PTR)SIG_DFL) { | |
2948 | if (GC_print_stats == VERBOSE) | |
2949 | GC_log_printf("Replaced other SIGSEGV handler\n"); | |
2950 | } | |
2951 | # endif /* ! MS windows */ | |
2952 | # if defined(HPUX) || defined(LINUX) || defined(HURD) \ | |
2953 | || (defined(FREEBSD) && defined(SUNOS5SIGS)) | |
2954 | sigaction(SIGBUS, &act, &oldact); | |
2955 | if (oldact.sa_flags & SA_SIGINFO) { | |
2956 | GC_old_bus_handler = oldact.sa_sigaction; | |
2957 | GC_old_bus_handler_used_si = TRUE; | |
2958 | } else { | |
2959 | GC_old_bus_handler = (SIG_HNDLR_PTR)oldact.sa_handler; | |
2960 | GC_old_bus_handler_used_si = FALSE; | |
2961 | } | |
2962 | if (GC_old_bus_handler == (SIG_HNDLR_PTR)SIG_IGN) { | |
2963 | GC_err_printf("Previously ignored bus error!?"); | |
2964 | GC_old_bus_handler = (SIG_HNDLR_PTR)SIG_DFL; | |
2965 | } | |
2966 | if (GC_old_bus_handler != (SIG_HNDLR_PTR)SIG_DFL) { | |
2967 | if (GC_print_stats == VERBOSE) | |
2968 | GC_log_printf("Replaced other SIGBUS handler\n"); | |
2969 | } | |
2970 | # endif /* HPUX || LINUX || HURD || (FREEBSD && SUNOS5SIGS) */ | |
2971 | # if defined(MSWIN32) | |
2972 | # if defined(GWW_VDB) | |
2973 | if (GC_gww_dirty_init()) | |
2974 | return; | |
2975 | # endif | |
2976 | GC_old_segv_handler = SetUnhandledExceptionFilter(GC_write_fault_handler); | |
2977 | if (GC_old_segv_handler != NULL) { | |
2978 | if (GC_print_stats) | |
2979 | GC_log_printf("Replaced other UnhandledExceptionFilter\n"); | |
2980 | } else { | |
2981 | GC_old_segv_handler = SIG_DFL; | |
2982 | } | |
2983 | # endif | |
2984 | } | |
2985 | #endif /* !DARWIN */ | |
2986 | ||
2987 | int GC_incremental_protection_needs(void) | |
2988 | { | |
2989 | if (GC_page_size == HBLKSIZE) { | |
2990 | return GC_PROTECTS_POINTER_HEAP; | |
2991 | } else { | |
2992 | return GC_PROTECTS_POINTER_HEAP | GC_PROTECTS_PTRFREE_HEAP; | |
2993 | } | |
2994 | } | |
2995 | ||
2996 | #define HAVE_INCREMENTAL_PROTECTION_NEEDS | |
2997 | ||
2998 | #define IS_PTRFREE(hhdr) ((hhdr)->hb_descr == 0) | |
2999 | ||
3000 | #define PAGE_ALIGNED(x) !((word)(x) & (GC_page_size - 1)) | |
3001 | void GC_protect_heap(void) | |
3002 | { | |
3003 | ptr_t start; | |
3004 | size_t len; | |
3005 | struct hblk * current; | |
3006 | struct hblk * current_start; /* Start of block to be protected. */ | |
3007 | struct hblk * limit; | |
3008 | unsigned i; | |
3009 | GC_bool protect_all = | |
3010 | (0 != (GC_incremental_protection_needs() & GC_PROTECTS_PTRFREE_HEAP)); | |
3011 | for (i = 0; i < GC_n_heap_sects; i++) { | |
3012 | start = GC_heap_sects[i].hs_start; | |
3013 | len = GC_heap_sects[i].hs_bytes; | |
3014 | if (protect_all) { | |
3015 | PROTECT(start, len); | |
3016 | } else { | |
3017 | GC_ASSERT(PAGE_ALIGNED(len)) | |
3018 | GC_ASSERT(PAGE_ALIGNED(start)) | |
3019 | current_start = current = (struct hblk *)start; | |
3020 | limit = (struct hblk *)(start + len); | |
3021 | while (current < limit) { | |
3022 | hdr * hhdr; | |
3023 | word nhblks; | |
3024 | GC_bool is_ptrfree; | |
3025 | ||
3026 | GC_ASSERT(PAGE_ALIGNED(current)); | |
3027 | GET_HDR(current, hhdr); | |
3028 | if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { | |
3029 | /* This can happen only if we're at the beginning of a */ | |
3030 | /* heap segment, and a block spans heap segments. */ | |
3031 | /* We will handle that block as part of the preceding */ | |
3032 | /* segment. */ | |
3033 | GC_ASSERT(current_start == current); | |
3034 | current_start = ++current; | |
3035 | continue; | |
3036 | } | |
3037 | if (HBLK_IS_FREE(hhdr)) { | |
3038 | GC_ASSERT(PAGE_ALIGNED(hhdr -> hb_sz)); | |
3039 | nhblks = divHBLKSZ(hhdr -> hb_sz); | |
3040 | is_ptrfree = TRUE; /* dirty on alloc */ | |
3041 | } else { | |
3042 | nhblks = OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); | |
3043 | is_ptrfree = IS_PTRFREE(hhdr); | |
3044 | } | |
3045 | if (is_ptrfree) { | |
3046 | if (current_start < current) { | |
3047 | PROTECT(current_start, (ptr_t)current - (ptr_t)current_start); | |
3048 | } | |
3049 | current_start = (current += nhblks); | |
3050 | } else { | |
3051 | current += nhblks; | |
3052 | } | |
3053 | } | |
3054 | if (current_start < current) { | |
3055 | PROTECT(current_start, (ptr_t)current - (ptr_t)current_start); | |
3056 | } | |
3057 | } | |
3058 | } | |
3059 | } | |
3060 | ||
3061 | /* We assume that either the world is stopped or its OK to lose dirty */ | |
3062 | /* bits while this is happenning (as in GC_enable_incremental). */ | |
3063 | void GC_read_dirty(void) | |
3064 | { | |
3065 | # if defined(GWW_VDB) | |
3066 | if (GC_GWW_AVAILABLE()) { | |
3067 | GC_gww_read_dirty(); | |
3068 | return; | |
3069 | } | |
3070 | # endif | |
3071 | BCOPY((word *)GC_dirty_pages, GC_grungy_pages, | |
3072 | (sizeof GC_dirty_pages)); | |
3073 | BZERO((word *)GC_dirty_pages, (sizeof GC_dirty_pages)); | |
3074 | GC_protect_heap(); | |
3075 | } | |
3076 | ||
3077 | GC_bool GC_page_was_dirty(struct hblk *h) | |
3078 | { | |
3079 | register word index; | |
3080 | ||
3081 | # if defined(GWW_VDB) | |
3082 | if (GC_GWW_AVAILABLE()) | |
3083 | return GC_gww_page_was_dirty(h); | |
3084 | # endif | |
3085 | ||
3086 | index = PHT_HASH(h); | |
3087 | return(HDR(h) == 0 || get_pht_entry_from_index(GC_grungy_pages, index)); | |
3088 | } | |
3089 | ||
3090 | /* | |
3091 | * Acquiring the allocation lock here is dangerous, since this | |
3092 | * can be called from within GC_call_with_alloc_lock, and the cord | |
3093 | * package does so. On systems that allow nested lock acquisition, this | |
3094 | * happens to work. | |
3095 | * On other systems, SET_LOCK_HOLDER and friends must be suitably defined. | |
3096 | */ | |
3097 | ||
3098 | static GC_bool syscall_acquired_lock = FALSE; /* Protected by GC lock. */ | |
3099 | ||
3100 | #if 0 | |
3101 | void GC_begin_syscall(void) | |
3102 | { | |
3103 | /* FIXME: Resurrecting this code would require fixing the */ | |
3104 | /* test, which can spuriously return TRUE. */ | |
3105 | if (!I_HOLD_LOCK()) { | |
3106 | LOCK(); | |
3107 | syscall_acquired_lock = TRUE; | |
3108 | } | |
3109 | } | |
3110 | ||
3111 | void GC_end_syscall(void) | |
3112 | { | |
3113 | if (syscall_acquired_lock) { | |
3114 | syscall_acquired_lock = FALSE; | |
3115 | UNLOCK(); | |
3116 | } | |
3117 | } | |
3118 | ||
3119 | void GC_unprotect_range(ptr_t addr, word len) | |
3120 | { | |
3121 | struct hblk * start_block; | |
3122 | struct hblk * end_block; | |
3123 | register struct hblk *h; | |
3124 | ptr_t obj_start; | |
3125 | ||
3126 | if (!GC_dirty_maintained) return; | |
3127 | obj_start = GC_base(addr); | |
3128 | if (obj_start == 0) return; | |
3129 | if (GC_base(addr + len - 1) != obj_start) { | |
3130 | ABORT("GC_unprotect_range(range bigger than object)"); | |
3131 | } | |
3132 | start_block = (struct hblk *)((word)addr & ~(GC_page_size - 1)); | |
3133 | end_block = (struct hblk *)((word)(addr + len - 1) & ~(GC_page_size - 1)); | |
3134 | end_block += GC_page_size/HBLKSIZE - 1; | |
3135 | for (h = start_block; h <= end_block; h++) { | |
3136 | register word index = PHT_HASH(h); | |
3137 | ||
3138 | async_set_pht_entry_from_index(GC_dirty_pages, index); | |
3139 | } | |
3140 | UNPROTECT(start_block, | |
3141 | ((ptr_t)end_block - (ptr_t)start_block) + HBLKSIZE); | |
3142 | } | |
3143 | ||
3144 | ||
3145 | /* We no longer wrap read by default, since that was causing too many */ | |
3146 | /* problems. It is preferred that the client instead avoids writing */ | |
3147 | /* to the write-protected heap with a system call. */ | |
3148 | /* This still serves as sample code if you do want to wrap system calls.*/ | |
3149 | ||
3150 | #if !defined(MSWIN32) && !defined(MSWINCE) && !defined(GC_USE_LD_WRAP) | |
3151 | /* Replacement for UNIX system call. */ | |
3152 | /* Other calls that write to the heap should be handled similarly. */ | |
3153 | /* Note that this doesn't work well for blocking reads: It will hold */ | |
3154 | /* the allocation lock for the entire duration of the call. Multithreaded */ | |
3155 | /* clients should really ensure that it won't block, either by setting */ | |
3156 | /* the descriptor nonblocking, or by calling select or poll first, to */ | |
3157 | /* make sure that input is available. */ | |
3158 | /* Another, preferred alternative is to ensure that system calls never */ | |
3159 | /* write to the protected heap (see above). */ | |
3160 | # include <unistd.h> | |
3161 | # include <sys/uio.h> | |
3162 | ssize_t read(int fd, void *buf, size_t nbyte) | |
3163 | { | |
3164 | int result; | |
3165 | ||
3166 | GC_begin_syscall(); | |
3167 | GC_unprotect_range(buf, (word)nbyte); | |
3168 | # if defined(IRIX5) || defined(GC_LINUX_THREADS) | |
3169 | /* Indirect system call may not always be easily available. */ | |
3170 | /* We could call _read, but that would interfere with the */ | |
3171 | /* libpthread interception of read. */ | |
3172 | /* On Linux, we have to be careful with the linuxthreads */ | |
3173 | /* read interception. */ | |
3174 | { | |
3175 | struct iovec iov; | |
3176 | ||
3177 | iov.iov_base = buf; | |
3178 | iov.iov_len = nbyte; | |
3179 | result = readv(fd, &iov, 1); | |
3180 | } | |
3181 | # else | |
3182 | # if defined(HURD) | |
3183 | result = __read(fd, buf, nbyte); | |
3184 | # else | |
3185 | /* The two zero args at the end of this list are because one | |
3186 | IA-64 syscall() implementation actually requires six args | |
3187 | to be passed, even though they aren't always used. */ | |
3188 | result = syscall(SYS_read, fd, buf, nbyte, 0, 0); | |
3189 | # endif /* !HURD */ | |
3190 | # endif | |
3191 | GC_end_syscall(); | |
3192 | return(result); | |
3193 | } | |
3194 | #endif /* !MSWIN32 && !MSWINCE && !GC_LINUX_THREADS */ | |
3195 | ||
3196 | #if defined(GC_USE_LD_WRAP) && !defined(THREADS) | |
3197 | /* We use the GNU ld call wrapping facility. */ | |
3198 | /* This requires that the linker be invoked with "--wrap read". */ | |
3199 | /* This can be done by passing -Wl,"--wrap read" to gcc. */ | |
3200 | /* I'm not sure that this actually wraps whatever version of read */ | |
3201 | /* is called by stdio. That code also mentions __read. */ | |
3202 | # include <unistd.h> | |
3203 | ssize_t __wrap_read(int fd, void *buf, size_t nbyte) | |
3204 | { | |
3205 | int result; | |
3206 | ||
3207 | GC_begin_syscall(); | |
3208 | GC_unprotect_range(buf, (word)nbyte); | |
3209 | result = __real_read(fd, buf, nbyte); | |
3210 | GC_end_syscall(); | |
3211 | return(result); | |
3212 | } | |
3213 | ||
3214 | /* We should probably also do this for __read, or whatever stdio */ | |
3215 | /* actually calls. */ | |
3216 | #endif | |
3217 | ||
3218 | #endif /* 0 */ | |
3219 | ||
3220 | /*ARGSUSED*/ | |
3221 | GC_bool GC_page_was_ever_dirty(struct hblk *h) | |
3222 | { | |
3223 | # if defined(GWW_VDB) | |
3224 | if (GC_GWW_AVAILABLE()) | |
3225 | return GC_gww_page_was_ever_dirty(h); | |
3226 | # endif | |
3227 | return(TRUE); | |
3228 | } | |
3229 | ||
3230 | # endif /* MPROTECT_VDB */ | |
3231 | ||
3232 | # ifdef PROC_VDB | |
3233 | ||
3234 | /* | |
3235 | * See DEFAULT_VDB for interface descriptions. | |
3236 | */ | |
3237 | ||
3238 | /* | |
3239 | * This implementaion assumes a Solaris 2.X like /proc pseudo-file-system | |
3240 | * from which we can read page modified bits. This facility is far from | |
3241 | * optimal (e.g. we would like to get the info for only some of the | |
3242 | * address space), but it avoids intercepting system calls. | |
3243 | */ | |
3244 | ||
3245 | #include <errno.h> | |
3246 | #include <sys/types.h> | |
3247 | #include <sys/signal.h> | |
3248 | #include <sys/fault.h> | |
3249 | #include <sys/syscall.h> | |
3250 | #include <sys/procfs.h> | |
3251 | #include <sys/stat.h> | |
3252 | ||
3253 | #define INITIAL_BUF_SZ 16384 | |
3254 | word GC_proc_buf_size = INITIAL_BUF_SZ; | |
3255 | char *GC_proc_buf; | |
3256 | ||
3257 | int GC_proc_fd; | |
3258 | ||
3259 | void GC_dirty_init(void) | |
3260 | { | |
3261 | int fd; | |
3262 | char buf[30]; | |
3263 | ||
3264 | GC_dirty_maintained = TRUE; | |
3265 | if (GC_bytes_allocd != 0 || GC_bytes_allocd_before_gc != 0) { | |
3266 | register int i; | |
3267 | ||
3268 | for (i = 0; i < PHT_SIZE; i++) GC_written_pages[i] = (word)(-1); | |
3269 | if (GC_print_stats == VERBOSE) | |
3270 | GC_log_printf( | |
3271 | "Allocated bytes:%lu:all pages may have been written\n", | |
3272 | (unsigned long) | |
3273 | (GC_bytes_allocd + GC_bytes_allocd_before_gc)); | |
3274 | } | |
3275 | sprintf(buf, "/proc/%d", getpid()); | |
3276 | fd = open(buf, O_RDONLY); | |
3277 | if (fd < 0) { | |
3278 | ABORT("/proc open failed"); | |
3279 | } | |
3280 | GC_proc_fd = syscall(SYS_ioctl, fd, PIOCOPENPD, 0); | |
3281 | close(fd); | |
3282 | syscall(SYS_fcntl, GC_proc_fd, F_SETFD, FD_CLOEXEC); | |
3283 | if (GC_proc_fd < 0) { | |
3284 | ABORT("/proc ioctl failed"); | |
3285 | } | |
3286 | GC_proc_buf = GC_scratch_alloc(GC_proc_buf_size); | |
3287 | } | |
3288 | ||
3289 | /* Ignore write hints. They don't help us here. */ | |
3290 | /*ARGSUSED*/ | |
3291 | void GC_remove_protection(h, nblocks, is_ptrfree) | |
3292 | struct hblk *h; | |
3293 | word nblocks; | |
3294 | GC_bool is_ptrfree; | |
3295 | { | |
3296 | } | |
3297 | ||
3298 | # define READ(fd,buf,nbytes) read(fd, buf, nbytes) | |
3299 | ||
3300 | void GC_read_dirty(void) | |
3301 | { | |
3302 | unsigned long ps, np; | |
3303 | int nmaps; | |
3304 | ptr_t vaddr; | |
3305 | struct prasmap * map; | |
3306 | char * bufp; | |
3307 | ptr_t current_addr, limit; | |
3308 | int i; | |
3309 | ||
3310 | BZERO(GC_grungy_pages, (sizeof GC_grungy_pages)); | |
3311 | ||
3312 | bufp = GC_proc_buf; | |
3313 | if (READ(GC_proc_fd, bufp, GC_proc_buf_size) <= 0) { | |
3314 | if (GC_print_stats) | |
3315 | GC_log_printf("/proc read failed: GC_proc_buf_size = %lu\n", | |
3316 | (unsigned long)GC_proc_buf_size); | |
3317 | { | |
3318 | /* Retry with larger buffer. */ | |
3319 | word new_size = 2 * GC_proc_buf_size; | |
3320 | char * new_buf = GC_scratch_alloc(new_size); | |
3321 | ||
3322 | if (new_buf != 0) { | |
3323 | GC_proc_buf = bufp = new_buf; | |
3324 | GC_proc_buf_size = new_size; | |
3325 | } | |
3326 | if (READ(GC_proc_fd, bufp, GC_proc_buf_size) <= 0) { | |
3327 | WARN("Insufficient space for /proc read\n", 0); | |
3328 | /* Punt: */ | |
3329 | memset(GC_grungy_pages, 0xff, sizeof (page_hash_table)); | |
3330 | memset(GC_written_pages, 0xff, sizeof(page_hash_table)); | |
3331 | return; | |
3332 | } | |
3333 | } | |
3334 | } | |
3335 | /* Copy dirty bits into GC_grungy_pages */ | |
3336 | nmaps = ((struct prpageheader *)bufp) -> pr_nmap; | |
3337 | /* printf( "nmaps = %d, PG_REFERENCED = %d, PG_MODIFIED = %d\n", | |
3338 | nmaps, PG_REFERENCED, PG_MODIFIED); */ | |
3339 | bufp = bufp + sizeof(struct prpageheader); | |
3340 | for (i = 0; i < nmaps; i++) { | |
3341 | map = (struct prasmap *)bufp; | |
3342 | vaddr = (ptr_t)(map -> pr_vaddr); | |
3343 | ps = map -> pr_pagesize; | |
3344 | np = map -> pr_npage; | |
3345 | /* printf("vaddr = 0x%X, ps = 0x%X, np = 0x%X\n", vaddr, ps, np); */ | |
3346 | limit = vaddr + ps * np; | |
3347 | bufp += sizeof (struct prasmap); | |
3348 | for (current_addr = vaddr; | |
3349 | current_addr < limit; current_addr += ps){ | |
3350 | if ((*bufp++) & PG_MODIFIED) { | |
3351 | register struct hblk * h = (struct hblk *) current_addr; | |
3352 | ||
3353 | while ((ptr_t)h < current_addr + ps) { | |
3354 | register word index = PHT_HASH(h); | |
3355 | ||
3356 | set_pht_entry_from_index(GC_grungy_pages, index); | |
3357 | h++; | |
3358 | } | |
3359 | } | |
3360 | } | |
3361 | bufp += sizeof(long) - 1; | |
3362 | bufp = (char *)((unsigned long)bufp & ~(sizeof(long)-1)); | |
3363 | } | |
3364 | /* Update GC_written_pages. */ | |
3365 | GC_or_pages(GC_written_pages, GC_grungy_pages); | |
3366 | } | |
3367 | ||
3368 | #undef READ | |
3369 | ||
3370 | GC_bool GC_page_was_dirty(struct hblk *h) | |
3371 | { | |
3372 | register word index = PHT_HASH(h); | |
3373 | register GC_bool result; | |
3374 | ||
3375 | result = get_pht_entry_from_index(GC_grungy_pages, index); | |
3376 | return(result); | |
3377 | } | |
3378 | ||
3379 | GC_bool GC_page_was_ever_dirty(struct hblk *h) | |
3380 | { | |
3381 | register word index = PHT_HASH(h); | |
3382 | register GC_bool result; | |
3383 | ||
3384 | result = get_pht_entry_from_index(GC_written_pages, index); | |
3385 | return(result); | |
3386 | } | |
3387 | ||
3388 | # endif /* PROC_VDB */ | |
3389 | ||
3390 | ||
3391 | # ifdef PCR_VDB | |
3392 | ||
3393 | # include "vd/PCR_VD.h" | |
3394 | ||
3395 | # define NPAGES (32*1024) /* 128 MB */ | |
3396 | ||
3397 | PCR_VD_DB GC_grungy_bits[NPAGES]; | |
3398 | ||
3399 | ptr_t GC_vd_base; /* Address corresponding to GC_grungy_bits[0] */ | |
3400 | /* HBLKSIZE aligned. */ | |
3401 | ||
3402 | void GC_dirty_init(void) | |
3403 | { | |
3404 | GC_dirty_maintained = TRUE; | |
3405 | /* For the time being, we assume the heap generally grows up */ | |
3406 | GC_vd_base = GC_heap_sects[0].hs_start; | |
3407 | if (GC_vd_base == 0) { | |
3408 | ABORT("Bad initial heap segment"); | |
3409 | } | |
3410 | if (PCR_VD_Start(HBLKSIZE, GC_vd_base, NPAGES*HBLKSIZE) | |
3411 | != PCR_ERes_okay) { | |
3412 | ABORT("dirty bit initialization failed"); | |
3413 | } | |
3414 | } | |
3415 | ||
3416 | void GC_read_dirty(void) | |
3417 | { | |
3418 | /* lazily enable dirty bits on newly added heap sects */ | |
3419 | { | |
3420 | static int onhs = 0; | |
3421 | int nhs = GC_n_heap_sects; | |
3422 | for( ; onhs < nhs; onhs++ ) { | |
3423 | PCR_VD_WriteProtectEnable( | |
3424 | GC_heap_sects[onhs].hs_start, | |
3425 | GC_heap_sects[onhs].hs_bytes ); | |
3426 | } | |
3427 | } | |
3428 | ||
3429 | ||
3430 | if (PCR_VD_Clear(GC_vd_base, NPAGES*HBLKSIZE, GC_grungy_bits) | |
3431 | != PCR_ERes_okay) { | |
3432 | ABORT("dirty bit read failed"); | |
3433 | } | |
3434 | } | |
3435 | ||
3436 | GC_bool GC_page_was_dirty(struct hblk *h) | |
3437 | { | |
3438 | if((ptr_t)h < GC_vd_base || (ptr_t)h >= GC_vd_base + NPAGES*HBLKSIZE) { | |
3439 | return(TRUE); | |
3440 | } | |
3441 | return(GC_grungy_bits[h - (struct hblk *)GC_vd_base] & PCR_VD_DB_dirtyBit); | |
3442 | } | |
3443 | ||
3444 | /*ARGSUSED*/ | |
3445 | void GC_remove_protection(struct hblk *h, word nblocks, GC_bool is_ptrfree) | |
3446 | { | |
3447 | PCR_VD_WriteProtectDisable(h, nblocks*HBLKSIZE); | |
3448 | PCR_VD_WriteProtectEnable(h, nblocks*HBLKSIZE); | |
3449 | } | |
3450 | ||
3451 | # endif /* PCR_VDB */ | |
3452 | ||
3453 | #if defined(MPROTECT_VDB) && defined(DARWIN) | |
3454 | /* The following sources were used as a *reference* for this exception handling | |
3455 | code: | |
3456 | 1. Apple's mach/xnu documentation | |
3457 | 2. Timothy J. Wood's "Mach Exception Handlers 101" post to the | |
3458 | omnigroup's macosx-dev list. | |
3459 | www.omnigroup.com/mailman/archive/macosx-dev/2000-June/014178.html | |
3460 | 3. macosx-nat.c from Apple's GDB source code. | |
3461 | */ | |
3462 | ||
3463 | /* The bug that caused all this trouble should now be fixed. This should | |
3464 | eventually be removed if all goes well. */ | |
3465 | ||
3466 | /* #define BROKEN_EXCEPTION_HANDLING */ | |
3467 | ||
3468 | #include <mach/mach.h> | |
3469 | #include <mach/mach_error.h> | |
3470 | #include <mach/thread_status.h> | |
3471 | #include <mach/exception.h> | |
3472 | #include <mach/task.h> | |
3473 | #include <pthread.h> | |
3474 | ||
3475 | extern void GC_darwin_register_mach_handler_thread(mach_port_t); | |
3476 | ||
3477 | /* These are not defined in any header, although they are documented */ | |
3478 | extern boolean_t | |
3479 | exc_server(mach_msg_header_t *, mach_msg_header_t *); | |
3480 | ||
3481 | extern kern_return_t | |
3482 | exception_raise(mach_port_t, mach_port_t, mach_port_t, exception_type_t, | |
3483 | exception_data_t, mach_msg_type_number_t); | |
3484 | ||
3485 | extern kern_return_t | |
3486 | exception_raise_state(mach_port_t, mach_port_t, mach_port_t, exception_type_t, | |
3487 | exception_data_t, mach_msg_type_number_t, | |
3488 | thread_state_flavor_t*, thread_state_t, | |
3489 | mach_msg_type_number_t, thread_state_t, | |
3490 | mach_msg_type_number_t*); | |
3491 | ||
3492 | extern kern_return_t | |
3493 | exception_raise_state_identity(mach_port_t, mach_port_t, mach_port_t, | |
3494 | exception_type_t, exception_data_t, | |
3495 | mach_msg_type_number_t, thread_state_flavor_t*, | |
3496 | thread_state_t, mach_msg_type_number_t, | |
3497 | thread_state_t, mach_msg_type_number_t*); | |
3498 | ||
3499 | ||
3500 | #define MAX_EXCEPTION_PORTS 16 | |
3501 | ||
3502 | static struct { | |
3503 | mach_msg_type_number_t count; | |
3504 | exception_mask_t masks[MAX_EXCEPTION_PORTS]; | |
3505 | exception_handler_t ports[MAX_EXCEPTION_PORTS]; | |
3506 | exception_behavior_t behaviors[MAX_EXCEPTION_PORTS]; | |
3507 | thread_state_flavor_t flavors[MAX_EXCEPTION_PORTS]; | |
3508 | } GC_old_exc_ports; | |
3509 | ||
3510 | static struct { | |
3511 | mach_port_t exception; | |
3512 | #if defined(THREADS) | |
3513 | mach_port_t reply; | |
3514 | #endif | |
3515 | } GC_ports; | |
3516 | ||
3517 | typedef struct { | |
3518 | mach_msg_header_t head; | |
3519 | } GC_msg_t; | |
3520 | ||
3521 | typedef enum { | |
3522 | GC_MP_NORMAL, GC_MP_DISCARDING, GC_MP_STOPPED | |
3523 | } GC_mprotect_state_t; | |
3524 | ||
3525 | /* FIXME: 1 and 2 seem to be safe to use in the msgh_id field, | |
3526 | but it isn't documented. Use the source and see if they | |
3527 | should be ok. */ | |
3528 | #define ID_STOP 1 | |
3529 | #define ID_RESUME 2 | |
3530 | ||
3531 | /* These values are only used on the reply port */ | |
3532 | #define ID_ACK 3 | |
3533 | ||
3534 | #if defined(THREADS) | |
3535 | ||
3536 | GC_mprotect_state_t GC_mprotect_state; | |
3537 | ||
3538 | /* The following should ONLY be called when the world is stopped */ | |
3539 | static void GC_mprotect_thread_notify(mach_msg_id_t id) | |
3540 | { | |
3541 | ||
3542 | struct { | |
3543 | GC_msg_t msg; | |
3544 | mach_msg_trailer_t trailer; | |
3545 | } buf; | |
3546 | ||
3547 | mach_msg_return_t r; | |
3548 | /* remote, local */ | |
3549 | buf.msg.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); | |
3550 | buf.msg.head.msgh_size = sizeof(buf.msg); | |
3551 | buf.msg.head.msgh_remote_port = GC_ports.exception; | |
3552 | buf.msg.head.msgh_local_port = MACH_PORT_NULL; | |
3553 | buf.msg.head.msgh_id = id; | |
3554 | ||
3555 | r = mach_msg(&buf.msg.head, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_LARGE, | |
3556 | sizeof(buf.msg), sizeof(buf), GC_ports.reply, | |
3557 | MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); | |
3558 | if(r != MACH_MSG_SUCCESS) | |
3559 | ABORT("mach_msg failed in GC_mprotect_thread_notify"); | |
3560 | if(buf.msg.head.msgh_id != ID_ACK) | |
3561 | ABORT("invalid ack in GC_mprotect_thread_notify"); | |
3562 | } | |
3563 | ||
3564 | /* Should only be called by the mprotect thread */ | |
3565 | static void GC_mprotect_thread_reply(void) | |
3566 | { | |
3567 | ||
3568 | GC_msg_t msg; | |
3569 | mach_msg_return_t r; | |
3570 | /* remote, local */ | |
3571 | msg.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); | |
3572 | msg.head.msgh_size = sizeof(msg); | |
3573 | msg.head.msgh_remote_port = GC_ports.reply; | |
3574 | msg.head.msgh_local_port = MACH_PORT_NULL; | |
3575 | msg.head.msgh_id = ID_ACK; | |
3576 | ||
3577 | r = mach_msg(&msg.head, MACH_SEND_MSG, sizeof(msg), 0, MACH_PORT_NULL, | |
3578 | MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); | |
3579 | if(r != MACH_MSG_SUCCESS) | |
3580 | ABORT("mach_msg failed in GC_mprotect_thread_reply"); | |
3581 | } | |
3582 | ||
3583 | void GC_mprotect_stop(void) | |
3584 | { | |
3585 | GC_mprotect_thread_notify(ID_STOP); | |
3586 | } | |
3587 | void GC_mprotect_resume(void) | |
3588 | { | |
3589 | GC_mprotect_thread_notify(ID_RESUME); | |
3590 | } | |
3591 | ||
3592 | #else /* !THREADS */ | |
3593 | /* The compiler should optimize away any GC_mprotect_state computations */ | |
3594 | #define GC_mprotect_state GC_MP_NORMAL | |
3595 | #endif | |
3596 | ||
3597 | static void *GC_mprotect_thread(void *arg) | |
3598 | { | |
3599 | mach_msg_return_t r; | |
3600 | /* These two structures contain some private kernel data. We don't need to | |
3601 | access any of it so we don't bother defining a proper struct. The | |
3602 | correct definitions are in the xnu source code. */ | |
3603 | struct { | |
3604 | mach_msg_header_t head; | |
3605 | char data[256]; | |
3606 | } reply; | |
3607 | struct { | |
3608 | mach_msg_header_t head; | |
3609 | mach_msg_body_t msgh_body; | |
3610 | char data[1024]; | |
3611 | } msg; | |
3612 | ||
3613 | mach_msg_id_t id; | |
3614 | ||
3615 | GC_darwin_register_mach_handler_thread(mach_thread_self()); | |
3616 | ||
3617 | for(;;) { | |
3618 | r = mach_msg(&msg.head, MACH_RCV_MSG | MACH_RCV_LARGE | | |
3619 | (GC_mprotect_state == GC_MP_DISCARDING ? MACH_RCV_TIMEOUT : 0), | |
3620 | 0, sizeof(msg), GC_ports.exception, | |
3621 | GC_mprotect_state == GC_MP_DISCARDING ? 0 | |
3622 | : MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); | |
3623 | ||
3624 | id = r == MACH_MSG_SUCCESS ? msg.head.msgh_id : -1; | |
3625 | ||
3626 | # if defined(THREADS) | |
3627 | if(GC_mprotect_state == GC_MP_DISCARDING) { | |
3628 | if(r == MACH_RCV_TIMED_OUT) { | |
3629 | GC_mprotect_state = GC_MP_STOPPED; | |
3630 | GC_mprotect_thread_reply(); | |
3631 | continue; | |
3632 | } | |
3633 | if(r == MACH_MSG_SUCCESS && (id == ID_STOP || id == ID_RESUME)) | |
3634 | ABORT("out of order mprotect thread request"); | |
3635 | } | |
3636 | # endif /* THREADS */ | |
3637 | ||
3638 | if(r != MACH_MSG_SUCCESS) { | |
3639 | GC_err_printf("mach_msg failed with %d %s\n", (int)r, | |
3640 | mach_error_string(r)); | |
3641 | ABORT("mach_msg failed"); | |
3642 | } | |
3643 | ||
3644 | switch(id) { | |
3645 | # if defined(THREADS) | |
3646 | case ID_STOP: | |
3647 | if(GC_mprotect_state != GC_MP_NORMAL) | |
3648 | ABORT("Called mprotect_stop when state wasn't normal"); | |
3649 | GC_mprotect_state = GC_MP_DISCARDING; | |
3650 | break; | |
3651 | case ID_RESUME: | |
3652 | if(GC_mprotect_state != GC_MP_STOPPED) | |
3653 | ABORT("Called mprotect_resume when state wasn't stopped"); | |
3654 | GC_mprotect_state = GC_MP_NORMAL; | |
3655 | GC_mprotect_thread_reply(); | |
3656 | break; | |
3657 | # endif /* THREADS */ | |
3658 | default: | |
3659 | /* Handle the message (calls catch_exception_raise) */ | |
3660 | if(!exc_server(&msg.head, &reply.head)) | |
3661 | ABORT("exc_server failed"); | |
3662 | /* Send the reply */ | |
3663 | r = mach_msg(&reply.head, MACH_SEND_MSG, reply.head.msgh_size, 0, | |
3664 | MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, | |
3665 | MACH_PORT_NULL); | |
3666 | if(r != MACH_MSG_SUCCESS) { | |
3667 | /* This will fail if the thread dies, but the thread */ | |
3668 | /* shouldn't die... */ | |
3669 | # ifdef BROKEN_EXCEPTION_HANDLING | |
3670 | GC_err_printf("mach_msg failed with %d %s while sending" | |
3671 | "exc reply\n", (int)r,mach_error_string(r)); | |
3672 | # else | |
3673 | ABORT("mach_msg failed while sending exception reply"); | |
3674 | # endif | |
3675 | } | |
3676 | } /* switch */ | |
3677 | } /* for(;;) */ | |
3678 | /* NOT REACHED */ | |
3679 | return NULL; | |
3680 | } | |
3681 | ||
3682 | /* All this SIGBUS code shouldn't be necessary. All protection faults should | |
3683 | be going throught the mach exception handler. However, it seems a SIGBUS is | |
3684 | occasionally sent for some unknown reason. Even more odd, it seems to be | |
3685 | meaningless and safe to ignore. */ | |
3686 | #ifdef BROKEN_EXCEPTION_HANDLING | |
3687 | ||
3688 | static SIG_HNDLR_PTR GC_old_bus_handler; | |
3689 | ||
3690 | /* Updates to this aren't atomic, but the SIGBUSs seem pretty rare. | |
3691 | Even if this doesn't get updated property, it isn't really a problem */ | |
3692 | static int GC_sigbus_count; | |
3693 | ||
3694 | static void GC_darwin_sigbus(int num, siginfo_t *sip, void *context) | |
3695 | { | |
3696 | if(num != SIGBUS) | |
3697 | ABORT("Got a non-sigbus signal in the sigbus handler"); | |
3698 | ||
3699 | /* Ugh... some seem safe to ignore, but too many in a row probably means | |
3700 | trouble. GC_sigbus_count is reset for each mach exception that is | |
3701 | handled */ | |
3702 | if(GC_sigbus_count >= 8) { | |
3703 | ABORT("Got more than 8 SIGBUSs in a row!"); | |
3704 | } else { | |
3705 | GC_sigbus_count++; | |
3706 | WARN("Ignoring SIGBUS.\n", 0); | |
3707 | } | |
3708 | } | |
3709 | #endif /* BROKEN_EXCEPTION_HANDLING */ | |
3710 | ||
3711 | void GC_dirty_init(void) | |
3712 | { | |
3713 | kern_return_t r; | |
3714 | mach_port_t me; | |
3715 | pthread_t thread; | |
3716 | pthread_attr_t attr; | |
3717 | exception_mask_t mask; | |
3718 | ||
3719 | if (GC_print_stats == VERBOSE) | |
3720 | GC_log_printf("Inititalizing mach/darwin mprotect virtual dirty bit " | |
3721 | "implementation\n"); | |
3722 | # ifdef BROKEN_EXCEPTION_HANDLING | |
3723 | WARN("Enabling workarounds for various darwin " | |
3724 | "exception handling bugs.\n", 0); | |
3725 | # endif | |
3726 | GC_dirty_maintained = TRUE; | |
3727 | if (GC_page_size % HBLKSIZE != 0) { | |
3728 | GC_err_printf("Page size not multiple of HBLKSIZE\n"); | |
3729 | ABORT("Page size not multiple of HBLKSIZE"); | |
3730 | } | |
3731 | ||
3732 | GC_task_self = me = mach_task_self(); | |
3733 | ||
3734 | r = mach_port_allocate(me, MACH_PORT_RIGHT_RECEIVE, &GC_ports.exception); | |
3735 | if(r != KERN_SUCCESS) | |
3736 | ABORT("mach_port_allocate failed (exception port)"); | |
3737 | ||
3738 | r = mach_port_insert_right(me, GC_ports.exception, GC_ports.exception, | |
3739 | MACH_MSG_TYPE_MAKE_SEND); | |
3740 | if(r != KERN_SUCCESS) | |
3741 | ABORT("mach_port_insert_right failed (exception port)"); | |
3742 | ||
3743 | # if defined(THREADS) | |
3744 | r = mach_port_allocate(me, MACH_PORT_RIGHT_RECEIVE, &GC_ports.reply); | |
3745 | if(r != KERN_SUCCESS) | |
3746 | ABORT("mach_port_allocate failed (reply port)"); | |
3747 | # endif | |
3748 | ||
3749 | /* The exceptions we want to catch */ | |
3750 | mask = EXC_MASK_BAD_ACCESS; | |
3751 | ||
3752 | r = task_get_exception_ports(me, mask, GC_old_exc_ports.masks, | |
3753 | &GC_old_exc_ports.count, GC_old_exc_ports.ports, | |
3754 | GC_old_exc_ports.behaviors, | |
3755 | GC_old_exc_ports.flavors); | |
3756 | if(r != KERN_SUCCESS) | |
3757 | ABORT("task_get_exception_ports failed"); | |
3758 | ||
3759 | r = task_set_exception_ports(me, mask, GC_ports.exception, EXCEPTION_DEFAULT, | |
3760 | GC_MACH_THREAD_STATE); | |
3761 | if(r != KERN_SUCCESS) | |
3762 | ABORT("task_set_exception_ports failed"); | |
3763 | if(pthread_attr_init(&attr) != 0) | |
3764 | ABORT("pthread_attr_init failed"); | |
3765 | if(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) | |
3766 | ABORT("pthread_attr_setdetachedstate failed"); | |
3767 | ||
3768 | # undef pthread_create | |
3769 | /* This will call the real pthread function, not our wrapper */ | |
3770 | if(pthread_create(&thread, &attr, GC_mprotect_thread, NULL) != 0) | |
3771 | ABORT("pthread_create failed"); | |
3772 | pthread_attr_destroy(&attr); | |
3773 | ||
3774 | /* Setup the sigbus handler for ignoring the meaningless SIGBUSs */ | |
3775 | # ifdef BROKEN_EXCEPTION_HANDLING | |
3776 | { | |
3777 | struct sigaction sa, oldsa; | |
3778 | sa.sa_handler = (SIG_HNDLR_PTR)GC_darwin_sigbus; | |
3779 | sigemptyset(&sa.sa_mask); | |
3780 | sa.sa_flags = SA_RESTART|SA_SIGINFO; | |
3781 | if(sigaction(SIGBUS, &sa, &oldsa) < 0) | |
3782 | ABORT("sigaction"); | |
3783 | GC_old_bus_handler = (SIG_HNDLR_PTR)oldsa.sa_handler; | |
3784 | if (GC_old_bus_handler != SIG_DFL) { | |
3785 | if (GC_print_stats == VERBOSE) | |
3786 | GC_err_printf("Replaced other SIGBUS handler\n"); | |
3787 | } | |
3788 | } | |
3789 | # endif /* BROKEN_EXCEPTION_HANDLING */ | |
3790 | } | |
3791 | ||
3792 | /* The source code for Apple's GDB was used as a reference for the exception | |
3793 | forwarding code. This code is similar to be GDB code only because there is | |
3794 | only one way to do it. */ | |
3795 | static kern_return_t GC_forward_exception(mach_port_t thread, mach_port_t task, | |
3796 | exception_type_t exception, | |
3797 | exception_data_t data, | |
3798 | mach_msg_type_number_t data_count) | |
3799 | { | |
3800 | unsigned int i; | |
3801 | kern_return_t r; | |
3802 | mach_port_t port; | |
3803 | exception_behavior_t behavior; | |
3804 | thread_state_flavor_t flavor; | |
3805 | ||
3806 | thread_state_t thread_state = NULL; | |
3807 | mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX; | |
3808 | ||
3809 | for(i=0; i < GC_old_exc_ports.count; i++) | |
3810 | if(GC_old_exc_ports.masks[i] & (1 << exception)) | |
3811 | break; | |
3812 | if(i==GC_old_exc_ports.count) | |
3813 | ABORT("No handler for exception!"); | |
3814 | ||
3815 | port = GC_old_exc_ports.ports[i]; | |
3816 | behavior = GC_old_exc_ports.behaviors[i]; | |
3817 | flavor = GC_old_exc_ports.flavors[i]; | |
3818 | ||
3819 | if(behavior != EXCEPTION_DEFAULT) { | |
3820 | r = thread_get_state(thread, flavor, thread_state, &thread_state_count); | |
3821 | if(r != KERN_SUCCESS) | |
3822 | ABORT("thread_get_state failed in forward_exception"); | |
3823 | } | |
3824 | ||
3825 | switch(behavior) { | |
3826 | case EXCEPTION_DEFAULT: | |
3827 | r = exception_raise(port, thread, task, exception, data, data_count); | |
3828 | break; | |
3829 | case EXCEPTION_STATE: | |
3830 | r = exception_raise_state(port, thread, task, exception, data, data_count, | |
3831 | &flavor, thread_state, thread_state_count, | |
3832 | thread_state, &thread_state_count); | |
3833 | break; | |
3834 | case EXCEPTION_STATE_IDENTITY: | |
3835 | r = exception_raise_state_identity(port, thread, task, exception, data, | |
3836 | data_count, &flavor, thread_state, | |
3837 | thread_state_count, thread_state, | |
3838 | &thread_state_count); | |
3839 | break; | |
3840 | default: | |
3841 | r = KERN_FAILURE; /* make gcc happy */ | |
3842 | ABORT("forward_exception: unknown behavior"); | |
3843 | break; | |
3844 | } | |
3845 | ||
3846 | if(behavior != EXCEPTION_DEFAULT) { | |
3847 | r = thread_set_state(thread, flavor, thread_state, thread_state_count); | |
3848 | if(r != KERN_SUCCESS) | |
3849 | ABORT("thread_set_state failed in forward_exception"); | |
3850 | } | |
3851 | ||
3852 | return r; | |
3853 | } | |
3854 | ||
3855 | #define FWD() GC_forward_exception(thread, task, exception, code, code_count) | |
3856 | ||
3857 | /* This violates the namespace rules but there isn't anything that can be done | |
3858 | about it. The exception handling stuff is hard coded to call this */ | |
3859 | kern_return_t | |
3860 | catch_exception_raise(mach_port_t exception_port, mach_port_t thread, | |
3861 | mach_port_t task, exception_type_t exception, | |
3862 | exception_data_t code, mach_msg_type_number_t code_count) | |
3863 | { | |
3864 | kern_return_t r; | |
3865 | char *addr; | |
3866 | struct hblk *h; | |
3867 | unsigned int i; | |
3868 | # if defined(POWERPC) | |
3869 | # if CPP_WORDSZ == 32 | |
3870 | thread_state_flavor_t flavor = PPC_EXCEPTION_STATE; | |
3871 | mach_msg_type_number_t exc_state_count = PPC_EXCEPTION_STATE_COUNT; | |
3872 | ppc_exception_state_t exc_state; | |
3873 | # else | |
3874 | thread_state_flavor_t flavor = PPC_EXCEPTION_STATE64; | |
3875 | mach_msg_type_number_t exc_state_count = PPC_EXCEPTION_STATE64_COUNT; | |
3876 | ppc_exception_state64_t exc_state; | |
3877 | # endif | |
3878 | # elif defined(I386) || defined(X86_64) | |
3879 | # if CPP_WORDSZ == 32 | |
3880 | thread_state_flavor_t flavor = x86_EXCEPTION_STATE32; | |
3881 | mach_msg_type_number_t exc_state_count = x86_EXCEPTION_STATE32_COUNT; | |
3882 | x86_exception_state32_t exc_state; | |
3883 | # else | |
3884 | thread_state_flavor_t flavor = x86_EXCEPTION_STATE64; | |
3885 | mach_msg_type_number_t exc_state_count = x86_EXCEPTION_STATE64_COUNT; | |
3886 | x86_exception_state64_t exc_state; | |
3887 | # endif | |
3888 | # else | |
3889 | # error FIXME for non-ppc/x86 darwin | |
3890 | # endif | |
3891 | ||
3892 | ||
3893 | if(exception != EXC_BAD_ACCESS || code[0] != KERN_PROTECTION_FAILURE) { | |
3894 | # ifdef DEBUG_EXCEPTION_HANDLING | |
3895 | /* We aren't interested, pass it on to the old handler */ | |
3896 | GC_printf("Exception: 0x%x Code: 0x%x 0x%x in catch....\n", exception, | |
3897 | code_count > 0 ? code[0] : -1, code_count > 1 ? code[1] : -1); | |
3898 | # endif | |
3899 | return FWD(); | |
3900 | } | |
3901 | ||
3902 | r = thread_get_state(thread, flavor, (natural_t*)&exc_state, | |
3903 | &exc_state_count); | |
3904 | if(r != KERN_SUCCESS) { | |
3905 | /* The thread is supposed to be suspended while the exception handler | |
3906 | is called. This shouldn't fail. */ | |
3907 | # ifdef BROKEN_EXCEPTION_HANDLING | |
3908 | GC_err_printf("thread_get_state failed in catch_exception_raise\n"); | |
3909 | return KERN_SUCCESS; | |
3910 | # else | |
3911 | ABORT("thread_get_state failed in catch_exception_raise"); | |
3912 | # endif | |
3913 | } | |
3914 | ||
3915 | /* This is the address that caused the fault */ | |
3916 | # if defined(POWERPC) | |
3917 | addr = (char*) exc_state. THREAD_FLD(dar); | |
3918 | # elif defined (I386) || defined (X86_64) | |
3919 | addr = (char*) exc_state. THREAD_FLD(faultvaddr); | |
3920 | # else | |
3921 | # error FIXME for non POWERPC/I386 | |
3922 | # endif | |
3923 | ||
3924 | if((HDR(addr)) == 0) { | |
3925 | /* Ugh... just like the SIGBUS problem above, it seems we get a bogus | |
3926 | KERN_PROTECTION_FAILURE every once and a while. We wait till we get | |
3927 | a bunch in a row before doing anything about it. If a "real" fault | |
3928 | ever occurres it'll just keep faulting over and over and we'll hit | |
3929 | the limit pretty quickly. */ | |
3930 | # ifdef BROKEN_EXCEPTION_HANDLING | |
3931 | static char *last_fault; | |
3932 | static int last_fault_count; | |
3933 | ||
3934 | if(addr != last_fault) { | |
3935 | last_fault = addr; | |
3936 | last_fault_count = 0; | |
3937 | } | |
3938 | if(++last_fault_count < 32) { | |
3939 | if(last_fault_count == 1) | |
3940 | WARN("Ignoring KERN_PROTECTION_FAILURE at %lx\n", (GC_word)addr); | |
3941 | return KERN_SUCCESS; | |
3942 | } | |
3943 | ||
3944 | GC_err_printf("Unexpected KERN_PROTECTION_FAILURE at %p\n",addr); | |
3945 | /* Can't pass it along to the signal handler because that is | |
3946 | ignoring SIGBUS signals. We also shouldn't call ABORT here as | |
3947 | signals don't always work too well from the exception handler. */ | |
3948 | GC_err_printf("Aborting\n"); | |
3949 | exit(EXIT_FAILURE); | |
3950 | # else /* BROKEN_EXCEPTION_HANDLING */ | |
3951 | /* Pass it along to the next exception handler | |
3952 | (which should call SIGBUS/SIGSEGV) */ | |
3953 | return FWD(); | |
3954 | # endif /* !BROKEN_EXCEPTION_HANDLING */ | |
3955 | } | |
3956 | ||
3957 | # ifdef BROKEN_EXCEPTION_HANDLING | |
3958 | /* Reset the number of consecutive SIGBUSs */ | |
3959 | GC_sigbus_count = 0; | |
3960 | # endif | |
3961 | ||
3962 | if(GC_mprotect_state == GC_MP_NORMAL) { /* common case */ | |
3963 | h = (struct hblk*)((word)addr & ~(GC_page_size-1)); | |
3964 | UNPROTECT(h, GC_page_size); | |
3965 | for (i = 0; i < divHBLKSZ(GC_page_size); i++) { | |
3966 | register int index = PHT_HASH(h+i); | |
3967 | async_set_pht_entry_from_index(GC_dirty_pages, index); | |
3968 | } | |
3969 | } else if(GC_mprotect_state == GC_MP_DISCARDING) { | |
3970 | /* Lie to the thread for now. No sense UNPROTECT()ing the memory | |
3971 | when we're just going to PROTECT() it again later. The thread | |
3972 | will just fault again once it resumes */ | |
3973 | } else { | |
3974 | /* Shouldn't happen, i don't think */ | |
3975 | GC_printf("KERN_PROTECTION_FAILURE while world is stopped\n"); | |
3976 | return FWD(); | |
3977 | } | |
3978 | return KERN_SUCCESS; | |
3979 | } | |
3980 | #undef FWD | |
3981 | ||
3982 | /* These should never be called, but just in case... */ | |
3983 | kern_return_t | |
3984 | catch_exception_raise_state(mach_port_name_t exception_port, int exception, | |
3985 | exception_data_t code, | |
3986 | mach_msg_type_number_t codeCnt, int flavor, | |
3987 | thread_state_t old_state, int old_stateCnt, | |
3988 | thread_state_t new_state, int new_stateCnt) | |
3989 | { | |
3990 | ABORT("catch_exception_raise_state"); | |
3991 | return(KERN_INVALID_ARGUMENT); | |
3992 | } | |
3993 | ||
3994 | kern_return_t | |
3995 | catch_exception_raise_state_identity(mach_port_name_t exception_port, | |
3996 | mach_port_t thread, mach_port_t task, | |
3997 | int exception, exception_data_t code, | |
3998 | mach_msg_type_number_t codeCnt, int flavor, | |
3999 | thread_state_t old_state, int old_stateCnt, | |
4000 | thread_state_t new_state, int new_stateCnt) | |
4001 | { | |
4002 | ABORT("catch_exception_raise_state_identity"); | |
4003 | return(KERN_INVALID_ARGUMENT); | |
4004 | } | |
4005 | ||
4006 | ||
4007 | #endif /* DARWIN && MPROTECT_VDB */ | |
4008 | ||
4009 | # ifndef HAVE_INCREMENTAL_PROTECTION_NEEDS | |
4010 | int GC_incremental_protection_needs() | |
4011 | { | |
4012 | return GC_PROTECTS_NONE; | |
4013 | } | |
4014 | # endif /* !HAVE_INCREMENTAL_PROTECTION_NEEDS */ | |
4015 | ||
4016 | /* | |
4017 | * Call stack save code for debugging. | |
4018 | * Should probably be in mach_dep.c, but that requires reorganization. | |
4019 | */ | |
4020 | ||
4021 | /* I suspect the following works for most X86 *nix variants, so */ | |
4022 | /* long as the frame pointer is explicitly stored. In the case of gcc, */ | |
4023 | /* compiler flags (e.g. -fomit-frame-pointer) determine whether it is. */ | |
4024 | #if defined(I386) && defined(LINUX) && defined(SAVE_CALL_CHAIN) | |
4025 | # include <features.h> | |
4026 | ||
4027 | struct frame { | |
4028 | struct frame *fr_savfp; | |
4029 | long fr_savpc; | |
4030 | long fr_arg[NARGS]; /* All the arguments go here. */ | |
4031 | }; | |
4032 | #endif | |
4033 | ||
4034 | #if defined(SPARC) | |
4035 | # if defined(LINUX) | |
4036 | # include <features.h> | |
4037 | ||
4038 | struct frame { | |
4039 | long fr_local[8]; | |
4040 | long fr_arg[6]; | |
4041 | struct frame *fr_savfp; | |
4042 | long fr_savpc; | |
4043 | # ifndef __arch64__ | |
4044 | char *fr_stret; | |
4045 | # endif | |
4046 | long fr_argd[6]; | |
4047 | long fr_argx[0]; | |
4048 | }; | |
4049 | # elif defined (DRSNX) | |
4050 | # include <sys/sparc/frame.h> | |
4051 | # elif defined(OPENBSD) | |
4052 | # include <frame.h> | |
4053 | # elif defined(FREEBSD) || defined(NETBSD) | |
4054 | # include <machine/frame.h> | |
4055 | # else | |
4056 | # include <sys/frame.h> | |
4057 | # endif | |
4058 | # if NARGS > 6 | |
4059 | # error We only know how to to get the first 6 arguments | |
4060 | # endif | |
4061 | #endif /* SPARC */ | |
4062 | ||
4063 | #ifdef NEED_CALLINFO | |
4064 | /* Fill in the pc and argument information for up to NFRAMES of my */ | |
4065 | /* callers. Ignore my frame and my callers frame. */ | |
4066 | ||
4067 | #ifdef LINUX | |
4068 | # include <unistd.h> | |
4069 | #endif | |
4070 | ||
4071 | #endif /* NEED_CALLINFO */ | |
4072 | ||
4073 | #if defined(GC_HAVE_BUILTIN_BACKTRACE) | |
4074 | # ifdef _MSC_VER | |
4075 | # include "private/msvc_dbg.h" | |
4076 | # else | |
4077 | # include <execinfo.h> | |
4078 | # endif | |
4079 | #endif | |
4080 | ||
4081 | #ifdef SAVE_CALL_CHAIN | |
4082 | ||
4083 | #if NARGS == 0 && NFRAMES % 2 == 0 /* No padding */ \ | |
4084 | && defined(GC_HAVE_BUILTIN_BACKTRACE) | |
4085 | ||
4086 | #ifdef REDIRECT_MALLOC | |
4087 | /* Deal with possible malloc calls in backtrace by omitting */ | |
4088 | /* the infinitely recursing backtrace. */ | |
4089 | # ifdef THREADS | |
4090 | __thread /* If your compiler doesn't understand this */ | |
4091 | /* you could use something like pthread_getspecific. */ | |
4092 | # endif | |
4093 | GC_in_save_callers = FALSE; | |
4094 | #endif | |
4095 | ||
4096 | void GC_save_callers (struct callinfo info[NFRAMES]) | |
4097 | { | |
4098 | void * tmp_info[NFRAMES + 1]; | |
4099 | int npcs, i; | |
4100 | # define IGNORE_FRAMES 1 | |
4101 | ||
4102 | /* We retrieve NFRAMES+1 pc values, but discard the first, since it */ | |
4103 | /* points to our own frame. */ | |
4104 | # ifdef REDIRECT_MALLOC | |
4105 | if (GC_in_save_callers) { | |
4106 | info[0].ci_pc = (word)(&GC_save_callers); | |
4107 | for (i = 1; i < NFRAMES; ++i) info[i].ci_pc = 0; | |
4108 | return; | |
4109 | } | |
4110 | GC_in_save_callers = TRUE; | |
4111 | # endif | |
4112 | GC_ASSERT(sizeof(struct callinfo) == sizeof(void *)); | |
4113 | npcs = backtrace((void **)tmp_info, NFRAMES + IGNORE_FRAMES); | |
4114 | BCOPY(tmp_info+IGNORE_FRAMES, info, (npcs - IGNORE_FRAMES) * sizeof(void *)); | |
4115 | for (i = npcs - IGNORE_FRAMES; i < NFRAMES; ++i) info[i].ci_pc = 0; | |
4116 | # ifdef REDIRECT_MALLOC | |
4117 | GC_in_save_callers = FALSE; | |
4118 | # endif | |
4119 | } | |
4120 | ||
4121 | #else /* No builtin backtrace; do it ourselves */ | |
4122 | ||
4123 | #if (defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD)) && defined(SPARC) | |
4124 | # define FR_SAVFP fr_fp | |
4125 | # define FR_SAVPC fr_pc | |
4126 | #else | |
4127 | # define FR_SAVFP fr_savfp | |
4128 | # define FR_SAVPC fr_savpc | |
4129 | #endif | |
4130 | ||
4131 | #if defined(SPARC) && (defined(__arch64__) || defined(__sparcv9)) | |
4132 | # define BIAS 2047 | |
4133 | #else | |
4134 | # define BIAS 0 | |
4135 | #endif | |
4136 | ||
4137 | void GC_save_callers (struct callinfo info[NFRAMES]) | |
4138 | { | |
4139 | struct frame *frame; | |
4140 | struct frame *fp; | |
4141 | int nframes = 0; | |
4142 | # ifdef I386 | |
4143 | /* We assume this is turned on only with gcc as the compiler. */ | |
4144 | asm("movl %%ebp,%0" : "=r"(frame)); | |
4145 | fp = frame; | |
4146 | # else | |
4147 | frame = (struct frame *) GC_save_regs_in_stack (); | |
4148 | fp = (struct frame *)((long) frame -> FR_SAVFP + BIAS); | |
4149 | #endif | |
4150 | ||
4151 | for (; (!(fp HOTTER_THAN frame) && !(GC_stackbottom HOTTER_THAN (ptr_t)fp) | |
4152 | && (nframes < NFRAMES)); | |
4153 | fp = (struct frame *)((long) fp -> FR_SAVFP + BIAS), nframes++) { | |
4154 | register int i; | |
4155 | ||
4156 | info[nframes].ci_pc = fp->FR_SAVPC; | |
4157 | # if NARGS > 0 | |
4158 | for (i = 0; i < NARGS; i++) { | |
4159 | info[nframes].ci_arg[i] = ~(fp->fr_arg[i]); | |
4160 | } | |
4161 | # endif /* NARGS > 0 */ | |
4162 | } | |
4163 | if (nframes < NFRAMES) info[nframes].ci_pc = 0; | |
4164 | } | |
4165 | ||
4166 | #endif /* No builtin backtrace */ | |
4167 | ||
4168 | #endif /* SAVE_CALL_CHAIN */ | |
4169 | ||
4170 | #ifdef NEED_CALLINFO | |
4171 | ||
4172 | /* Print info to stderr. We do NOT hold the allocation lock */ | |
4173 | void GC_print_callers (struct callinfo info[NFRAMES]) | |
4174 | { | |
4175 | register int i; | |
4176 | static int reentry_count = 0; | |
4177 | GC_bool stop = FALSE; | |
4178 | ||
4179 | /* FIXME: This should probably use a different lock, so that we */ | |
4180 | /* become callable with or without the allocation lock. */ | |
4181 | LOCK(); | |
4182 | ++reentry_count; | |
4183 | UNLOCK(); | |
4184 | ||
4185 | # if NFRAMES == 1 | |
4186 | GC_err_printf("\tCaller at allocation:\n"); | |
4187 | # else | |
4188 | GC_err_printf("\tCall chain at allocation:\n"); | |
4189 | # endif | |
4190 | for (i = 0; i < NFRAMES && !stop ; i++) { | |
4191 | if (info[i].ci_pc == 0) break; | |
4192 | # if NARGS > 0 | |
4193 | { | |
4194 | int j; | |
4195 | ||
4196 | GC_err_printf("\t\targs: "); | |
4197 | for (j = 0; j < NARGS; j++) { | |
4198 | if (j != 0) GC_err_printf(", "); | |
4199 | GC_err_printf("%d (0x%X)", ~(info[i].ci_arg[j]), | |
4200 | ~(info[i].ci_arg[j])); | |
4201 | } | |
4202 | GC_err_printf("\n"); | |
4203 | } | |
4204 | # endif | |
4205 | if (reentry_count > 1) { | |
4206 | /* We were called during an allocation during */ | |
4207 | /* a previous GC_print_callers call; punt. */ | |
4208 | GC_err_printf("\t\t##PC##= 0x%lx\n", info[i].ci_pc); | |
4209 | continue; | |
4210 | } | |
4211 | { | |
4212 | # ifdef LINUX | |
4213 | FILE *pipe; | |
4214 | # endif | |
4215 | # if defined(GC_HAVE_BUILTIN_BACKTRACE) \ | |
4216 | && !defined(GC_BACKTRACE_SYMBOLS_BROKEN) | |
4217 | char **sym_name = | |
4218 | backtrace_symbols((void **)(&(info[i].ci_pc)), 1); | |
4219 | char *name = sym_name[0]; | |
4220 | # else | |
4221 | char buf[40]; | |
4222 | char *name = buf; | |
4223 | sprintf(buf, "##PC##= 0x%lx", info[i].ci_pc); | |
4224 | # endif | |
4225 | # if defined(LINUX) && !defined(SMALL_CONFIG) | |
4226 | /* Try for a line number. */ | |
4227 | { | |
4228 | # define EXE_SZ 100 | |
4229 | static char exe_name[EXE_SZ]; | |
4230 | # define CMD_SZ 200 | |
4231 | char cmd_buf[CMD_SZ]; | |
4232 | # define RESULT_SZ 200 | |
4233 | static char result_buf[RESULT_SZ]; | |
4234 | size_t result_len; | |
4235 | char *old_preload; | |
4236 | # define PRELOAD_SZ 200 | |
4237 | char preload_buf[PRELOAD_SZ]; | |
4238 | static GC_bool found_exe_name = FALSE; | |
4239 | static GC_bool will_fail = FALSE; | |
4240 | int ret_code; | |
4241 | /* Try to get it via a hairy and expensive scheme. */ | |
4242 | /* First we get the name of the executable: */ | |
4243 | if (will_fail) goto out; | |
4244 | if (!found_exe_name) { | |
4245 | ret_code = readlink("/proc/self/exe", exe_name, EXE_SZ); | |
4246 | if (ret_code < 0 || ret_code >= EXE_SZ | |
4247 | || exe_name[0] != '/') { | |
4248 | will_fail = TRUE; /* Dont try again. */ | |
4249 | goto out; | |
4250 | } | |
4251 | exe_name[ret_code] = '\0'; | |
4252 | found_exe_name = TRUE; | |
4253 | } | |
4254 | /* Then we use popen to start addr2line -e <exe> <addr> */ | |
4255 | /* There are faster ways to do this, but hopefully this */ | |
4256 | /* isn't time critical. */ | |
4257 | sprintf(cmd_buf, "/usr/bin/addr2line -f -e %s 0x%lx", exe_name, | |
4258 | (unsigned long)info[i].ci_pc); | |
4259 | old_preload = getenv ("LD_PRELOAD"); | |
4260 | if (0 != old_preload) { | |
4261 | if (strlen (old_preload) >= PRELOAD_SZ) { | |
4262 | will_fail = TRUE; | |
4263 | goto out; | |
4264 | } | |
4265 | strcpy (preload_buf, old_preload); | |
4266 | unsetenv ("LD_PRELOAD"); | |
4267 | } | |
4268 | pipe = popen(cmd_buf, "r"); | |
4269 | if (0 != old_preload | |
4270 | && 0 != setenv ("LD_PRELOAD", preload_buf, 0)) { | |
4271 | WARN("Failed to reset LD_PRELOAD\n", 0); | |
4272 | } | |
4273 | if (pipe == NULL | |
4274 | || (result_len = fread(result_buf, 1, RESULT_SZ - 1, pipe)) | |
4275 | == 0) { | |
4276 | if (pipe != NULL) pclose(pipe); | |
4277 | will_fail = TRUE; | |
4278 | goto out; | |
4279 | } | |
4280 | if (result_buf[result_len - 1] == '\n') --result_len; | |
4281 | result_buf[result_len] = 0; | |
4282 | if (result_buf[0] == '?' | |
4283 | || (result_buf[result_len-2] == ':' | |
4284 | && result_buf[result_len-1] == '0')) { | |
4285 | pclose(pipe); | |
4286 | goto out; | |
4287 | } | |
4288 | /* Get rid of embedded newline, if any. Test for "main" */ | |
4289 | { | |
4290 | char * nl = strchr(result_buf, '\n'); | |
4291 | if (nl != NULL && nl < result_buf + result_len) { | |
4292 | *nl = ':'; | |
4293 | } | |
4294 | if (strncmp(result_buf, "main", nl - result_buf) == 0) { | |
4295 | stop = TRUE; | |
4296 | } | |
4297 | } | |
4298 | if (result_len < RESULT_SZ - 25) { | |
4299 | /* Add in hex address */ | |
4300 | sprintf(result_buf + result_len, " [0x%lx]", | |
4301 | (unsigned long)info[i].ci_pc); | |
4302 | } | |
4303 | name = result_buf; | |
4304 | pclose(pipe); | |
4305 | out:; | |
4306 | } | |
4307 | # endif /* LINUX */ | |
4308 | GC_err_printf("\t\t%s\n", name); | |
4309 | # if defined(GC_HAVE_BUILTIN_BACKTRACE) \ | |
4310 | && !defined(GC_BACKTRACE_SYMBOLS_BROKEN) | |
4311 | free(sym_name); /* May call GC_free; that's OK */ | |
4312 | # endif | |
4313 | } | |
4314 | } | |
4315 | LOCK(); | |
4316 | --reentry_count; | |
4317 | UNLOCK(); | |
4318 | } | |
4319 | ||
4320 | #endif /* NEED_CALLINFO */ | |
4321 | ||
4322 | ||
4323 | ||
4324 | #if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG) | |
4325 | ||
4326 | /* Dump /proc/self/maps to GC_stderr, to enable looking up names for | |
4327 | addresses in FIND_LEAK output. */ | |
4328 | ||
4329 | static word dump_maps(char *maps) | |
4330 | { | |
4331 | GC_err_write(maps, strlen(maps)); | |
4332 | return 1; | |
4333 | } | |
4334 | ||
4335 | void GC_print_address_map(void) | |
4336 | { | |
4337 | GC_err_printf("---------- Begin address map ----------\n"); | |
4338 | dump_maps(GC_get_maps()); | |
4339 | GC_err_printf("---------- End address map ----------\n"); | |
4340 | } | |
4341 | ||
4342 | #endif | |
4343 | ||
4344 |