1/* mingw32 host-specific hook definitions.
2   Copyright (C) 2004-2015 Free Software Foundation, Inc.
3
4   This file is part of GCC.
5
6   GCC is free software; you can redistribute it and/or modify it
7   under the terms of the GNU General Public License as published
8   by the Free Software Foundation; either version 3, or (at your
9   option) any later version.
10
11   GCC is distributed in the hope that it will be useful, but WITHOUT
12   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
14   License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with GCC; see the file COPYING3.  If not see
18   <http://www.gnu.org/licenses/>.  */
19
20#include "config.h"
21#include "system.h"
22#include "coretypes.h"
23#include "hosthooks.h"
24#include "hosthooks-def.h"
25#include "diagnostic.h"
26
27
28#define WIN32_LEAN_AND_MEAN  /* Not so important if we have windows.h.gch.  */
29#include <windows.h>
30#include <stdlib.h>
31
32static void * mingw32_gt_pch_get_address (size_t, int);
33static int mingw32_gt_pch_use_address (void *, size_t, int, size_t);
34static size_t mingw32_gt_pch_alloc_granularity (void);
35
36#undef HOST_HOOKS_GT_PCH_GET_ADDRESS
37#define HOST_HOOKS_GT_PCH_GET_ADDRESS mingw32_gt_pch_get_address
38#undef HOST_HOOKS_GT_PCH_USE_ADDRESS
39#define HOST_HOOKS_GT_PCH_USE_ADDRESS mingw32_gt_pch_use_address
40#undef HOST_HOOKS_GT_PCH_ALLOC_GRANULARITY
41#define HOST_HOOKS_GT_PCH_ALLOC_GRANULARITY mingw32_gt_pch_alloc_granularity
42
43static inline void w32_error(const char*, const char*, int, const char*);
44
45/* FIXME: Is this big enough?  */
46static const size_t pch_VA_max_size  = 128 * 1024 * 1024;
47
48/* Granularity for reserving address space.  */
49static size_t va_granularity = 0x10000;
50
51/* Print out the GetLastError() translation.  */
52static inline void
53w32_error (const char* function, const char* file, int line,
54	   const char* my_msg)
55{
56  LPSTR w32_msgbuf;
57  FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER
58		  | FORMAT_MESSAGE_FROM_SYSTEM
59		  | FORMAT_MESSAGE_IGNORE_INSERTS
60		  | FORMAT_MESSAGE_MAX_WIDTH_MASK,
61    		  NULL, GetLastError(),
62		  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
63		  (LPSTR) &w32_msgbuf, 0, NULL);
64  fprintf(stderr, "internal error in %s, at %s:%d: %s: %s\n",
65	  function, trim_filename (file), line, my_msg, w32_msgbuf);
66  LocalFree ((HLOCAL)w32_msgbuf);
67}
68
69/* Granularity for reserving address space.  */
70static size_t
71mingw32_gt_pch_alloc_granularity (void)
72{
73  SYSTEM_INFO si;
74
75  GetSystemInfo (&si);
76  va_granularity = (size_t) si.dwAllocationGranularity;
77
78  return va_granularity;
79}
80
81/* Identify an address that's likely to be free in a subsequent invocation
82   of the compiler.  The area should be able to hold SIZE bytes.  FD is an
83   open file descriptor if the host would like to probe with mmap.  */
84
85static void *
86mingw32_gt_pch_get_address (size_t size, int)
87{
88  void* res;
89  size = (size + va_granularity - 1) & ~(va_granularity - 1);
90  if (size > pch_VA_max_size)
91    return NULL;
92
93  /* FIXME: We let system determine base by setting first arg to NULL.
94     Allocating at top of available address space avoids unnecessary
95     fragmentation of "ordinary" (malloc's)  address space but may not
96     be safe  with delayed load of system dll's. Preferred addresses
97     for NT system dlls is in 0x70000000 to 0x78000000 range.
98     If we allocate at bottom we need to reserve the address as early
99     as possible and at the same point in each invocation. */
100
101  res = VirtualAlloc (NULL, pch_VA_max_size,
102		      MEM_RESERVE | MEM_TOP_DOWN,
103		      PAGE_NOACCESS);
104  if (!res)
105    w32_error (__FUNCTION__, __FILE__, __LINE__, "VirtualAlloc");
106  else
107    /* We do not need the address space for now, so free it.  */
108    VirtualFree (res, 0, MEM_RELEASE);
109
110  return res;
111}
112
113/* ADDR is an address returned by gt_pch_get_address.  Attempt to allocate
114   SIZE bytes at the same address and load it with the data from FD at
115   OFFSET.  Return -1 if we couldn't allocate memory at ADDR, return 0
116   if the memory is allocated but the data not loaded, return 1 if done.  */
117
118static int
119mingw32_gt_pch_use_address (void *addr, size_t size, int fd,
120			    size_t offset)
121{
122  void * mmap_addr;
123  HANDLE mmap_handle;
124
125  /* Apparently, MS Vista puts unnamed file mapping objects into Global
126     namespace when running an application in a Terminal Server
127     session.  This causes failure since, by default, applications
128     don't get SeCreateGlobalPrivilege. We don't need global
129     memory sharing so explicitly put object into Local namespace.
130
131     If multiple concurrent GCC processes are using PCH functionality,
132     MapViewOfFileEx returns "Access Denied" error.  So we ensure the
133     session-wide mapping name is unique by appending process ID.  */
134
135#define OBJECT_NAME_FMT "Local\\MinGWGCCPCH-"
136
137  char* object_name = NULL;
138  /* However, the documentation for CreateFileMapping says that on NT4
139     and earlier, backslashes are invalid in object name.  So, we need
140     to check if we are on Windows2000 or higher.  */
141  OSVERSIONINFO version_info;
142  int r;
143
144  version_info.dwOSVersionInfoSize = sizeof (version_info);
145
146  if (size == 0)
147    return 0;
148
149  /* Offset must be also be a multiple of allocation granularity for
150     this to work.  We can't change the offset. */
151  if ((offset & (va_granularity - 1)) != 0 || size > pch_VA_max_size)
152    return -1;
153
154
155  /* Determine the version of Windows we are running on and use a
156     uniquely-named local object if running > 4.  */
157  GetVersionEx (&version_info);
158  if (version_info.dwMajorVersion > 4)
159    {
160      char local_object_name [sizeof (OBJECT_NAME_FMT)
161			      + sizeof (DWORD) * 2];
162      snprintf (local_object_name, sizeof (local_object_name),
163		OBJECT_NAME_FMT "%lx", GetCurrentProcessId());
164      object_name = local_object_name;
165    }
166  mmap_handle = CreateFileMappingA ((HANDLE) _get_osfhandle (fd), NULL,
167				    PAGE_WRITECOPY | SEC_COMMIT, 0, 0,
168				    object_name);
169
170  if (mmap_handle == NULL)
171    {
172      w32_error (__FUNCTION__,  __FILE__, __LINE__, "CreateFileMapping");
173      return -1;
174    }
175
176  /* Retry five times, as here might occure a race with multiple gcc's
177     instances at same time.  */
178  for (r = 0; r < 5; r++)
179   {
180      mmap_addr = MapViewOfFileEx (mmap_handle, FILE_MAP_COPY, 0, offset,
181				   size, addr);
182      if (mmap_addr == addr)
183	break;
184      if (r != 4)
185        Sleep (500);
186   }
187
188  if (mmap_addr != addr)
189    {
190      w32_error (__FUNCTION__, __FILE__, __LINE__, "MapViewOfFileEx");
191      CloseHandle(mmap_handle);
192      return  -1;
193    }
194
195  return 1;
196}
197
198const struct host_hooks host_hooks = HOST_HOOKS_INITIALIZER;
199