| /* mingw32 host-specific hook definitions. |
| Copyright (C) 2004-2015 Free Software Foundation, Inc. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it |
| under the terms of the GNU General Public License as published |
| by the Free Software Foundation; either version 3, or (at your |
| option) any later version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT |
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
| License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "hosthooks.h" |
| #include "hosthooks-def.h" |
| #include "diagnostic.h" |
| |
| |
| #define WIN32_LEAN_AND_MEAN /* Not so important if we have windows.h.gch. */ |
| #include <windows.h> |
| #include <stdlib.h> |
| |
| static void * mingw32_gt_pch_get_address (size_t, int); |
| static int mingw32_gt_pch_use_address (void *, size_t, int, size_t); |
| static size_t mingw32_gt_pch_alloc_granularity (void); |
| |
| #undef HOST_HOOKS_GT_PCH_GET_ADDRESS |
| #define HOST_HOOKS_GT_PCH_GET_ADDRESS mingw32_gt_pch_get_address |
| #undef HOST_HOOKS_GT_PCH_USE_ADDRESS |
| #define HOST_HOOKS_GT_PCH_USE_ADDRESS mingw32_gt_pch_use_address |
| #undef HOST_HOOKS_GT_PCH_ALLOC_GRANULARITY |
| #define HOST_HOOKS_GT_PCH_ALLOC_GRANULARITY mingw32_gt_pch_alloc_granularity |
| |
| static inline void w32_error(const char*, const char*, int, const char*); |
| |
| /* FIXME: Is this big enough? */ |
| static const size_t pch_VA_max_size = 128 * 1024 * 1024; |
| |
| /* Granularity for reserving address space. */ |
| static size_t va_granularity = 0x10000; |
| |
| /* Print out the GetLastError() translation. */ |
| static inline void |
| w32_error (const char* function, const char* file, int line, |
| const char* my_msg) |
| { |
| LPSTR w32_msgbuf; |
| FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER |
| | FORMAT_MESSAGE_FROM_SYSTEM |
| | FORMAT_MESSAGE_IGNORE_INSERTS |
| | FORMAT_MESSAGE_MAX_WIDTH_MASK, |
| NULL, GetLastError(), |
| MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
| (LPSTR) &w32_msgbuf, 0, NULL); |
| fprintf(stderr, "internal error in %s, at %s:%d: %s: %s\n", |
| function, trim_filename (file), line, my_msg, w32_msgbuf); |
| LocalFree ((HLOCAL)w32_msgbuf); |
| } |
| |
| /* Granularity for reserving address space. */ |
| static size_t |
| mingw32_gt_pch_alloc_granularity (void) |
| { |
| SYSTEM_INFO si; |
| |
| GetSystemInfo (&si); |
| va_granularity = (size_t) si.dwAllocationGranularity; |
| |
| return va_granularity; |
| } |
| |
| /* Identify an address that's likely to be free in a subsequent invocation |
| of the compiler. The area should be able to hold SIZE bytes. FD is an |
| open file descriptor if the host would like to probe with mmap. */ |
| |
| static void * |
| mingw32_gt_pch_get_address (size_t size, int) |
| { |
| void* res; |
| size = (size + va_granularity - 1) & ~(va_granularity - 1); |
| if (size > pch_VA_max_size) |
| return NULL; |
| |
| /* FIXME: We let system determine base by setting first arg to NULL. |
| Allocating at top of available address space avoids unnecessary |
| fragmentation of "ordinary" (malloc's) address space but may not |
| be safe with delayed load of system dll's. Preferred addresses |
| for NT system dlls is in 0x70000000 to 0x78000000 range. |
| If we allocate at bottom we need to reserve the address as early |
| as possible and at the same point in each invocation. */ |
| |
| res = VirtualAlloc (NULL, pch_VA_max_size, |
| MEM_RESERVE | MEM_TOP_DOWN, |
| PAGE_NOACCESS); |
| if (!res) |
| w32_error (__FUNCTION__, __FILE__, __LINE__, "VirtualAlloc"); |
| else |
| /* We do not need the address space for now, so free it. */ |
| VirtualFree (res, 0, MEM_RELEASE); |
| |
| return res; |
| } |
| |
| /* ADDR is an address returned by gt_pch_get_address. Attempt to allocate |
| SIZE bytes at the same address and load it with the data from FD at |
| OFFSET. Return -1 if we couldn't allocate memory at ADDR, return 0 |
| if the memory is allocated but the data not loaded, return 1 if done. */ |
| |
| static int |
| mingw32_gt_pch_use_address (void *addr, size_t size, int fd, |
| size_t offset) |
| { |
| void * mmap_addr; |
| HANDLE mmap_handle; |
| |
| /* Apparently, MS Vista puts unnamed file mapping objects into Global |
| namespace when running an application in a Terminal Server |
| session. This causes failure since, by default, applications |
| don't get SeCreateGlobalPrivilege. We don't need global |
| memory sharing so explicitly put object into Local namespace. |
| |
| If multiple concurrent GCC processes are using PCH functionality, |
| MapViewOfFileEx returns "Access Denied" error. So we ensure the |
| session-wide mapping name is unique by appending process ID. */ |
| |
| #define OBJECT_NAME_FMT "Local\\MinGWGCCPCH-" |
| |
| char* object_name = NULL; |
| /* However, the documentation for CreateFileMapping says that on NT4 |
| and earlier, backslashes are invalid in object name. So, we need |
| to check if we are on Windows2000 or higher. */ |
| OSVERSIONINFO version_info; |
| int r; |
| |
| version_info.dwOSVersionInfoSize = sizeof (version_info); |
| |
| if (size == 0) |
| return 0; |
| |
| /* Offset must be also be a multiple of allocation granularity for |
| this to work. We can't change the offset. */ |
| if ((offset & (va_granularity - 1)) != 0 || size > pch_VA_max_size) |
| return -1; |
| |
| |
| /* Determine the version of Windows we are running on and use a |
| uniquely-named local object if running > 4. */ |
| GetVersionEx (&version_info); |
| if (version_info.dwMajorVersion > 4) |
| { |
| char local_object_name [sizeof (OBJECT_NAME_FMT) |
| + sizeof (DWORD) * 2]; |
| snprintf (local_object_name, sizeof (local_object_name), |
| OBJECT_NAME_FMT "%lx", GetCurrentProcessId()); |
| object_name = local_object_name; |
| } |
| mmap_handle = CreateFileMappingA ((HANDLE) _get_osfhandle (fd), NULL, |
| PAGE_WRITECOPY | SEC_COMMIT, 0, 0, |
| object_name); |
| |
| if (mmap_handle == NULL) |
| { |
| w32_error (__FUNCTION__, __FILE__, __LINE__, "CreateFileMapping"); |
| return -1; |
| } |
| |
| /* Retry five times, as here might occure a race with multiple gcc's |
| instances at same time. */ |
| for (r = 0; r < 5; r++) |
| { |
| mmap_addr = MapViewOfFileEx (mmap_handle, FILE_MAP_COPY, 0, offset, |
| size, addr); |
| if (mmap_addr == addr) |
| break; |
| if (r != 4) |
| Sleep (500); |
| } |
| |
| if (mmap_addr != addr) |
| { |
| w32_error (__FUNCTION__, __FILE__, __LINE__, "MapViewOfFileEx"); |
| CloseHandle(mmap_handle); |
| return -1; |
| } |
| |
| return 1; |
| } |
| |
| const struct host_hooks host_hooks = HOST_HOOKS_INITIALIZER; |