📄 os_dep.c
字号:
/* * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved. * * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. * * Permission is hereby granted to use or copy this program * for any purpose, provided the above notices are retained on all copies. * Permission to modify the code and to distribute modified code is granted, * provided the above notices are retained, and a notice that the code was * modified is included with the above copyright notice. */# include "private/gc_priv.h"# ifdef THREADS# include "atomic_ops.h"# endif# if defined(LINUX) && !defined(POWERPC)# include <linux/version.h># if (LINUX_VERSION_CODE <= 0x10400) /* Ugly hack to get struct sigcontext_struct definition. Required */ /* for some early 1.3.X releases. Will hopefully go away soon. */ /* in some later Linux releases, asm/sigcontext.h may have to */ /* be included instead. */# define __KERNEL__# include <asm/signal.h># undef __KERNEL__# else /* Kernels prior to 2.1.1 defined struct sigcontext_struct instead of */ /* struct sigcontext. libc6 (glibc2) uses "struct sigcontext" in */ /* prototypes, so we have to include the top-level sigcontext.h to */ /* make sure the former gets defined to be the latter if appropriate. */# include <features.h># if 2 <= __GLIBC__# if 2 == __GLIBC__ && 0 == __GLIBC_MINOR__ /* glibc 2.1 no longer has sigcontext.h. But signal.h */ /* has the right declaration for glibc 2.1. */# include <sigcontext.h># endif /* 0 == __GLIBC_MINOR__ */# else /* not 2 <= __GLIBC__ */ /* libc5 doesn't have <sigcontext.h>: go directly with the kernel */ /* one. Check LINUX_VERSION_CODE to see which we should reference. */# include <asm/sigcontext.h># endif /* 2 <= __GLIBC__ */# endif# endif# if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS) \ && !defined(MSWINCE)# include <sys/types.h># if !defined(MSWIN32)# include <unistd.h># endif# endif# include <stdio.h># if defined(MSWINCE)# define SIGSEGV 0 /* value is irrelevant */# else# include <signal.h># endif#ifdef UNIX_LIKE# include <fcntl.h>#endif#if defined(LINUX) || defined(LINUX_STACKBOTTOM)# include <ctype.h>#endif/* Blatantly OS dependent routines, except for those that are related *//* to dynamic loading. */#ifdef AMIGA# define GC_AMIGA_DEF# include "AmigaOS.c"# undef GC_AMIGA_DEF#endif#if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)# define WIN32_LEAN_AND_MEAN# define NOSERVICE# include <windows.h> /* It's not clear this is completely kosher under Cygwin. But it */ /* allows us to get a working GC_get_stack_base. */#endif#ifdef MACOS# include <Processes.h>#endif#ifdef IRIX5# include <sys/uio.h># include <malloc.h> /* for locking */#endif#if defined(LINUX) || defined(FREEBSD) || defined(SOLARIS) || defined(IRIX5) \ || defined(USE_MMAP) || defined(USE_MUNMAP)# define MMAP_SUPPORTED#endif#if defined(MMAP_SUPPORTED) || defined(ADD_HEAP_GUARD_PAGES)# if defined(USE_MUNMAP) && !defined(USE_MMAP) --> USE_MUNMAP requires USE_MMAP# endif# include <sys/types.h># include <sys/mman.h># include <sys/stat.h># include <errno.h>#endif#ifdef DARWIN/* for get_etext and friends */#include <mach-o/getsect.h>#endif#ifdef DJGPP /* Apparently necessary for djgpp 2.01. May cause problems with */ /* other versions. */ typedef long unsigned int caddr_t;#endif#ifdef PCR# include "il/PCR_IL.h"# include "th/PCR_ThCtl.h"# include "mm/PCR_MM.h"#endif#if !defined(NO_EXECUTE_PERMISSION)# define OPT_PROT_EXEC PROT_EXEC#else# define OPT_PROT_EXEC 0#endif#if defined(LINUX) && \ (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64) || !defined(SMALL_CONFIG))# define NEED_PROC_MAPS#endif#ifdef NEED_PROC_MAPS/* We need to parse /proc/self/maps, either to find dynamic libraries, *//* and/or to find the register backing store base (IA64). Do it once *//* here. */#define READ read/* Repeatedly perform a read call until the buffer is filled or *//* we encounter EOF. */ssize_t GC_repeat_read(int fd, char *buf, size_t count){ ssize_t num_read = 0; ssize_t result; while (num_read < count) { result = READ(fd, buf + num_read, count - num_read); if (result < 0) return result; if (result == 0) break; num_read += result; } return num_read;}/* Determine the length of a file by incrementally reading it into a *//* This would be sily to use on a file supporting lseek, but Linux *//* /proc files usually do not. */size_t GC_get_file_len(int f){ size_t total = 0; ssize_t result;# define GET_FILE_LEN_BUF_SZ 500 char buf[GET_FILE_LEN_BUF_SZ]; do { result = read(f, buf, GET_FILE_LEN_BUF_SZ); if (result == -1) return 0; total += result; } while (result > 0); return total;}size_t GC_get_maps_len(void){ int f = open("/proc/self/maps", O_RDONLY); size_t result = GC_get_file_len(f); close(f); return result;}/* * Copy the contents of /proc/self/maps to a buffer in our address space. * Return the address of the buffer, or zero on failure. * This code could be simplified if we could determine its size * ahead of time. */char * GC_get_maps(void){ int f; int result; static char init_buf[1]; static char *maps_buf = init_buf; static size_t maps_buf_sz = 1; size_t maps_size, old_maps_size = 0; /* The buffer is essentially static, so there must be a single client. */ GC_ASSERT(I_HOLD_LOCK()); /* Note that in the presence of threads, the maps file can */ /* essentially shrink asynchronously and unexpectedly as */ /* threads that we already think of as dead release their */ /* stacks. And there is no easy way to read the entire */ /* file atomically. This is arguably a misfeature of the */ /* /proc/.../maps interface. */ /* Since we dont believe the file can grow */ /* asynchronously, it should suffice to first determine */ /* the size (using lseek or read), and then to reread the */ /* file. If the size is inconsistent we have to retry. */ /* This only matters with threads enabled, and if we use */ /* this to locate roots (not the default). */ /* Determine the initial size of /proc/self/maps. */ /* Note that lseek doesn't work, at least as of 2.6.15. */# ifdef THREADS maps_size = GC_get_maps_len(); if (0 == maps_size) return 0;# else maps_size = 4000; /* Guess */# endif /* Read /proc/self/maps, growing maps_buf as necessary. */ /* Note that we may not allocate conventionally, and */ /* thus can't use stdio. */ do { while (maps_size >= maps_buf_sz) { /* Grow only by powers of 2, since we leak "too small" buffers. */ while (maps_size >= maps_buf_sz) maps_buf_sz *= 2; maps_buf = GC_scratch_alloc(maps_buf_sz);# ifdef THREADS /* Recompute initial length, since we allocated. */ /* This can only happen a few times per program */ /* execution. */ maps_size = GC_get_maps_len(); if (0 == maps_size) return 0;# endif if (maps_buf == 0) return 0; } GC_ASSERT(maps_buf_sz >= maps_size + 1); f = open("/proc/self/maps", O_RDONLY); if (-1 == f) return 0;# ifdef THREADS old_maps_size = maps_size;# endif maps_size = 0; do { result = GC_repeat_read(f, maps_buf, maps_buf_sz-1); if (result <= 0) return 0; maps_size += result; } while (result == maps_buf_sz-1); close(f);# ifdef THREADS if (maps_size > old_maps_size) { GC_err_printf("Old maps size = %d, new maps size = %d\n", old_maps_size, maps_size); ABORT("Unexpected asynchronous /proc/self/maps growth: " "Unregistered thread?"); }# endif } while (maps_size >= maps_buf_sz || maps_size < old_maps_size); /* In the single-threaded case, the second clause is false. */ maps_buf[maps_size] = '\0'; /* Apply fn to result. */ return maps_buf;}//// GC_parse_map_entry parses an entry from /proc/self/maps so we can// locate all writable data segments that belong to shared libraries.// The format of one of these entries and the fields we care about// is as follows:// XXXXXXXX-XXXXXXXX r-xp 00000000 30:05 260537 name of mapping...\n// ^^^^^^^^ ^^^^^^^^ ^^^^ ^^// start end prot maj_dev//// Note that since about august 2003 kernels, the columns no longer have// fixed offsets on 64-bit kernels. Hence we no longer rely on fixed offsets// anywhere, which is safer anyway.///* * Assign various fields of the first line in buf_ptr to *start, *end, * *prot, *maj_dev and *mapping_name. Mapping_name may be NULL. * *prot and *mapping_name are assigned pointers into the original * buffer. */char *GC_parse_map_entry(char *buf_ptr, ptr_t *start, ptr_t *end, char **prot, unsigned int *maj_dev, char **mapping_name){ char *start_start, *end_start, *maj_dev_start; char *p; char *endp; if (buf_ptr == NULL || *buf_ptr == '\0') { return NULL; } p = buf_ptr; while (isspace(*p)) ++p; start_start = p; GC_ASSERT(isxdigit(*start_start)); *start = (ptr_t)strtoul(start_start, &endp, 16); p = endp; GC_ASSERT(*p=='-'); ++p; end_start = p; GC_ASSERT(isxdigit(*end_start)); *end = (ptr_t)strtoul(end_start, &endp, 16); p = endp; GC_ASSERT(isspace(*p)); while (isspace(*p)) ++p; GC_ASSERT(*p == 'r' || *p == '-'); *prot = p; /* Skip past protection field to offset field */ while (!isspace(*p)) ++p; while (isspace(*p)) ++p; GC_ASSERT(isxdigit(*p)); /* Skip past offset field, which we ignore */ while (!isspace(*p)) ++p; while (isspace(*p)) ++p; maj_dev_start = p; GC_ASSERT(isxdigit(*maj_dev_start)); *maj_dev = strtoul(maj_dev_start, NULL, 16); if (mapping_name == 0) { while (*p && *p++ != '\n'); } else { while (*p && *p != '\n' && *p != '/' && *p != '[') p++; *mapping_name = p; while (*p && *p++ != '\n'); } return p;}/* Try to read the backing store base from /proc/self/maps. *//* Return the bounds of the writable mapping with a 0 major device, *//* which includes the address passed as data. *//* Return FALSE if there is no such mapping. */GC_bool GC_enclosing_mapping(ptr_t addr, ptr_t *startp, ptr_t *endp){ char *prot; ptr_t my_start, my_end; unsigned int maj_dev; char *maps = GC_get_maps(); char *buf_ptr = maps; if (0 == maps) return(FALSE); for (;;) { buf_ptr = GC_parse_map_entry(buf_ptr, &my_start, &my_end, &prot, &maj_dev, 0); if (buf_ptr == NULL) return FALSE; if (prot[1] == 'w' && maj_dev == 0) { if (my_end > addr && my_start <= addr) { *startp = my_start; *endp = my_end; return TRUE; } } } return FALSE;}/* Find the text(code) mapping for the library whose name starts with nm. */GC_bool GC_text_mapping(char *nm, ptr_t *startp, ptr_t *endp){ size_t nm_len = strlen(nm); char *prot; char *map_path; ptr_t my_start, my_end; unsigned int maj_dev; char *maps = GC_get_maps(); char *buf_ptr = maps; if (0 == maps) return(FALSE); for (;;) { buf_ptr = GC_parse_map_entry(buf_ptr, &my_start, &my_end, &prot, &maj_dev, &map_path); if (buf_ptr == NULL) return FALSE; if (prot[0] == 'r' && prot[1] == '-' && prot[2] == 'x' && strncmp(nm, map_path, nm_len) == 0) { *startp = my_start; *endp = my_end; return TRUE; } } return FALSE;}#ifdef IA64static ptr_t backing_store_base_from_proc(void){ ptr_t my_start, my_end; if (!GC_enclosing_mapping(GC_save_regs_in_stack(), &my_start, &my_end)) { if (GC_print_stats) { GC_log_printf("Failed to find backing store base from /proc\n"); } return 0; } return my_start;}#endif#endif /* NEED_PROC_MAPS */ #if defined(SEARCH_FOR_DATA_START) /* The I386 case can be handled without a search. The Alpha case */ /* used to be handled differently as well, but the rules changed */ /* for recent Linux versions. This seems to be the easiest way to */ /* cover all versions. */# if defined(LINUX) || defined(HURD) /* Some Linux distributions arrange to define __data_start. Some */ /* define data_start as a weak symbol. The latter is technically */ /* broken, since the user program may define data_start, in which */ /* case we lose. Nonetheless, we try both, prefering __data_start. */ /* We assume gcc-compatible pragmas. */# pragma weak __data_start extern int __data_start[];# pragma weak data_start extern int data_start[];# endif /* LINUX */ extern int _end[]; ptr_t GC_data_start;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -