]>
Commit | Line | Data |
---|---|---|
f35db108 WM |
1 | //===-- sanitizer_win.cc --------------------------------------------------===// |
2 | // | |
3 | // This file is distributed under the University of Illinois Open Source | |
4 | // License. See LICENSE.TXT for details. | |
5 | // | |
6 | //===----------------------------------------------------------------------===// | |
7 | // | |
8 | // This file is shared between AddressSanitizer and ThreadSanitizer | |
9 | // run-time libraries and implements windows-specific functions from | |
10 | // sanitizer_libc.h. | |
11 | //===----------------------------------------------------------------------===// | |
ef1b3fda KS |
12 | |
13 | #include "sanitizer_platform.h" | |
14 | #if SANITIZER_WINDOWS | |
15 | ||
e297eb60 KS |
16 | #define WIN32_LEAN_AND_MEAN |
17 | #define NOGDI | |
f35db108 | 18 | #include <windows.h> |
866e32ad KS |
19 | #include <dbghelp.h> |
20 | #include <io.h> | |
696d846a | 21 | #include <psapi.h> |
866e32ad | 22 | #include <stdlib.h> |
f35db108 WM |
23 | |
24 | #include "sanitizer_common.h" | |
25 | #include "sanitizer_libc.h" | |
2660d12d | 26 | #include "sanitizer_mutex.h" |
ef1b3fda KS |
27 | #include "sanitizer_placement_new.h" |
28 | #include "sanitizer_stacktrace.h" | |
f35db108 WM |
29 | |
30 | namespace __sanitizer { | |
31 | ||
ef1b3fda KS |
32 | #include "sanitizer_syscall_generic.inc" |
33 | ||
f35db108 | 34 | // --------------------- sanitizer_common.h |
4ba5ca46 | 35 | uptr GetPageSize() { |
696d846a MO |
36 | // FIXME: there is an API for getting the system page size (GetSystemInfo or |
37 | // GetNativeSystemInfo), but if we use it here we get test failures elsewhere. | |
38 | return 1U << 14; | |
4ba5ca46 KS |
39 | } |
40 | ||
41 | uptr GetMmapGranularity() { | |
42 | return 1U << 16; // FIXME: is this configurable? | |
43 | } | |
44 | ||
ef1b3fda KS |
45 | uptr GetMaxVirtualAddress() { |
46 | SYSTEM_INFO si; | |
47 | GetSystemInfo(&si); | |
48 | return (uptr)si.lpMaximumApplicationAddress; | |
49 | } | |
50 | ||
e297eb60 | 51 | bool FileExists(const char *filename) { |
696d846a | 52 | return ::GetFileAttributesA(filename) != INVALID_FILE_ATTRIBUTES; |
e297eb60 KS |
53 | } |
54 | ||
ef1b3fda | 55 | uptr internal_getpid() { |
f35db108 WM |
56 | return GetProcessId(GetCurrentProcess()); |
57 | } | |
58 | ||
ef1b3fda KS |
59 | // In contrast to POSIX, on Windows GetCurrentThreadId() |
60 | // returns a system-unique identifier. | |
61 | uptr GetTid() { | |
f35db108 WM |
62 | return GetCurrentThreadId(); |
63 | } | |
64 | ||
ef1b3fda KS |
65 | uptr GetThreadSelf() { |
66 | return GetTid(); | |
67 | } | |
68 | ||
866e32ad | 69 | #if !SANITIZER_GO |
f35db108 WM |
70 | void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, |
71 | uptr *stack_bottom) { | |
72 | CHECK(stack_top); | |
73 | CHECK(stack_bottom); | |
74 | MEMORY_BASIC_INFORMATION mbi; | |
75 | CHECK_NE(VirtualQuery(&mbi /* on stack */, &mbi, sizeof(mbi)), 0); | |
76 | // FIXME: is it possible for the stack to not be a single allocation? | |
77 | // Are these values what ASan expects to get (reserved, not committed; | |
78 | // including stack guard page) ? | |
79 | *stack_top = (uptr)mbi.BaseAddress + mbi.RegionSize; | |
80 | *stack_bottom = (uptr)mbi.AllocationBase; | |
81 | } | |
866e32ad | 82 | #endif // #if !SANITIZER_GO |
f35db108 | 83 | |
f35db108 WM |
84 | void *MmapOrDie(uptr size, const char *mem_type) { |
85 | void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); | |
696d846a | 86 | if (rv == 0) |
55aea9f5 | 87 | ReportMmapFailureAndDie(size, mem_type, "allocate", GetLastError()); |
f35db108 WM |
88 | return rv; |
89 | } | |
90 | ||
91 | void UnmapOrDie(void *addr, uptr size) { | |
696d846a MO |
92 | if (!size || !addr) |
93 | return; | |
94 | ||
f35db108 | 95 | if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) { |
dee5ea7a KS |
96 | Report("ERROR: %s failed to " |
97 | "deallocate 0x%zx (%zd) bytes at address %p (error code: %d)\n", | |
98 | SanitizerToolName, size, size, addr, GetLastError()); | |
f35db108 WM |
99 | CHECK("unable to unmap" && 0); |
100 | } | |
101 | } | |
102 | ||
696d846a | 103 | void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) { |
e9772e16 KS |
104 | // FIXME: is this really "NoReserve"? On Win32 this does not matter much, |
105 | // but on Win64 it does. | |
696d846a | 106 | (void)name; // unsupported |
e297eb60 KS |
107 | void *p = VirtualAlloc((LPVOID)fixed_addr, size, |
108 | MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); | |
109 | if (p == 0) | |
dee5ea7a KS |
110 | Report("ERROR: %s failed to " |
111 | "allocate %p (%zd) bytes at %p (error code: %d)\n", | |
112 | SanitizerToolName, size, size, fixed_addr, GetLastError()); | |
e297eb60 | 113 | return p; |
f35db108 WM |
114 | } |
115 | ||
e9772e16 KS |
116 | void *MmapFixedOrDie(uptr fixed_addr, uptr size) { |
117 | return MmapFixedNoReserve(fixed_addr, size); | |
118 | } | |
119 | ||
dee5ea7a KS |
120 | void *MmapNoReserveOrDie(uptr size, const char *mem_type) { |
121 | // FIXME: make this really NoReserve? | |
122 | return MmapOrDie(size, mem_type); | |
123 | } | |
124 | ||
696d846a MO |
125 | void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name) { |
126 | (void)name; // unsupported | |
127 | void *res = VirtualAlloc((LPVOID)fixed_addr, size, | |
128 | MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS); | |
129 | if (res == 0) | |
130 | Report("WARNING: %s failed to " | |
131 | "mprotect %p (%zd) bytes at %p (error code: %d)\n", | |
132 | SanitizerToolName, size, size, fixed_addr, GetLastError()); | |
133 | return res; | |
134 | } | |
135 | ||
136 | bool MprotectNoAccess(uptr addr, uptr size) { | |
137 | DWORD old_protection; | |
138 | return VirtualProtect((LPVOID)addr, size, PAGE_NOACCESS, &old_protection); | |
f35db108 WM |
139 | } |
140 | ||
696d846a | 141 | |
b4ab7d34 KS |
142 | void FlushUnneededShadowMemory(uptr addr, uptr size) { |
143 | // This is almost useless on 32-bits. | |
696d846a MO |
144 | // FIXME: add madvise-analog when we move to 64-bits. |
145 | } | |
146 | ||
147 | void NoHugePagesInRegion(uptr addr, uptr size) { | |
148 | // FIXME: probably similar to FlushUnneededShadowMemory. | |
149 | } | |
150 | ||
151 | void DontDumpShadowMemory(uptr addr, uptr length) { | |
152 | // This is almost useless on 32-bits. | |
153 | // FIXME: add madvise-analog when we move to 64-bits. | |
b4ab7d34 KS |
154 | } |
155 | ||
f35db108 | 156 | bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { |
696d846a MO |
157 | MEMORY_BASIC_INFORMATION mbi; |
158 | CHECK(VirtualQuery((void *)range_start, &mbi, sizeof(mbi))); | |
159 | return mbi.Protect == PAGE_NOACCESS && | |
160 | (uptr)mbi.BaseAddress + mbi.RegionSize >= range_end; | |
f35db108 WM |
161 | } |
162 | ||
163 | void *MapFileToMemory(const char *file_name, uptr *buff_size) { | |
164 | UNIMPLEMENTED(); | |
f35db108 WM |
165 | } |
166 | ||
696d846a | 167 | void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) { |
866e32ad KS |
168 | UNIMPLEMENTED(); |
169 | } | |
170 | ||
ef1b3fda KS |
171 | static const int kMaxEnvNameLength = 128; |
172 | static const DWORD kMaxEnvValueLength = 32767; | |
f35db108 | 173 | |
ef1b3fda KS |
174 | namespace { |
175 | ||
176 | struct EnvVariable { | |
177 | char name[kMaxEnvNameLength]; | |
178 | char value[kMaxEnvValueLength]; | |
179 | }; | |
f35db108 | 180 | |
ef1b3fda KS |
181 | } // namespace |
182 | ||
183 | static const int kEnvVariables = 5; | |
184 | static EnvVariable env_vars[kEnvVariables]; | |
185 | static int num_env_vars; | |
186 | ||
187 | const char *GetEnv(const char *name) { | |
188 | // Note: this implementation caches the values of the environment variables | |
189 | // and limits their quantity. | |
190 | for (int i = 0; i < num_env_vars; i++) { | |
191 | if (0 == internal_strcmp(name, env_vars[i].name)) | |
192 | return env_vars[i].value; | |
193 | } | |
194 | CHECK_LT(num_env_vars, kEnvVariables); | |
195 | DWORD rv = GetEnvironmentVariableA(name, env_vars[num_env_vars].value, | |
196 | kMaxEnvValueLength); | |
197 | if (rv > 0 && rv < kMaxEnvValueLength) { | |
198 | CHECK_LT(internal_strlen(name), kMaxEnvNameLength); | |
199 | internal_strncpy(env_vars[num_env_vars].name, name, kMaxEnvNameLength); | |
200 | num_env_vars++; | |
201 | return env_vars[num_env_vars - 1].value; | |
202 | } | |
f35db108 WM |
203 | return 0; |
204 | } | |
205 | ||
206 | const char *GetPwd() { | |
207 | UNIMPLEMENTED(); | |
f35db108 WM |
208 | } |
209 | ||
7df59255 KS |
210 | u32 GetUid() { |
211 | UNIMPLEMENTED(); | |
212 | } | |
213 | ||
696d846a MO |
214 | namespace { |
215 | struct ModuleInfo { | |
216 | const char *filepath; | |
217 | uptr base_address; | |
218 | uptr end_address; | |
219 | }; | |
220 | ||
55aea9f5 | 221 | #ifndef SANITIZER_GO |
696d846a MO |
222 | int CompareModulesBase(const void *pl, const void *pr) { |
223 | const ModuleInfo *l = (ModuleInfo *)pl, *r = (ModuleInfo *)pr; | |
224 | if (l->base_address < r->base_address) | |
225 | return -1; | |
226 | return l->base_address > r->base_address; | |
227 | } | |
55aea9f5 | 228 | #endif |
696d846a MO |
229 | } // namespace |
230 | ||
231 | #ifndef SANITIZER_GO | |
f35db108 | 232 | void DumpProcessMap() { |
696d846a MO |
233 | Report("Dumping process modules:\n"); |
234 | InternalScopedBuffer<LoadedModule> modules(kMaxNumberOfModules); | |
235 | uptr num_modules = | |
236 | GetListOfModules(modules.data(), kMaxNumberOfModules, nullptr); | |
237 | ||
238 | InternalScopedBuffer<ModuleInfo> module_infos(num_modules); | |
239 | for (size_t i = 0; i < num_modules; ++i) { | |
240 | module_infos[i].filepath = modules[i].full_name(); | |
241 | module_infos[i].base_address = modules[i].base_address(); | |
242 | module_infos[i].end_address = modules[i].ranges().next()->end; | |
243 | } | |
244 | qsort(module_infos.data(), num_modules, sizeof(ModuleInfo), | |
245 | CompareModulesBase); | |
246 | ||
247 | for (size_t i = 0; i < num_modules; ++i) { | |
248 | const ModuleInfo &mi = module_infos[i]; | |
249 | if (mi.end_address != 0) { | |
250 | Printf("\t%p-%p %s\n", mi.base_address, mi.end_address, | |
251 | mi.filepath[0] ? mi.filepath : "[no name]"); | |
252 | } else if (mi.filepath[0]) { | |
253 | Printf("\t??\?-??? %s\n", mi.filepath); | |
254 | } else { | |
255 | Printf("\t???\n"); | |
256 | } | |
257 | } | |
f35db108 | 258 | } |
696d846a | 259 | #endif |
f35db108 | 260 | |
866e32ad | 261 | void DisableCoreDumperIfNecessary() { |
dee5ea7a | 262 | // Do nothing. |
f35db108 WM |
263 | } |
264 | ||
265 | void ReExec() { | |
266 | UNIMPLEMENTED(); | |
267 | } | |
268 | ||
dee5ea7a | 269 | void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) { |
696d846a MO |
270 | #if !SANITIZER_GO |
271 | CovPrepareForSandboxing(args); | |
272 | #endif | |
e9772e16 KS |
273 | } |
274 | ||
f35db108 WM |
275 | bool StackSizeIsUnlimited() { |
276 | UNIMPLEMENTED(); | |
f35db108 WM |
277 | } |
278 | ||
279 | void SetStackSizeLimitInBytes(uptr limit) { | |
280 | UNIMPLEMENTED(); | |
281 | } | |
282 | ||
866e32ad KS |
283 | bool AddressSpaceIsUnlimited() { |
284 | UNIMPLEMENTED(); | |
285 | } | |
286 | ||
287 | void SetAddressSpaceUnlimited() { | |
288 | UNIMPLEMENTED(); | |
289 | } | |
290 | ||
696d846a MO |
291 | bool IsPathSeparator(const char c) { |
292 | return c == '\\' || c == '/'; | |
293 | } | |
294 | ||
295 | bool IsAbsolutePath(const char *path) { | |
296 | UNIMPLEMENTED(); | |
ef1b3fda KS |
297 | } |
298 | ||
f35db108 WM |
299 | void SleepForSeconds(int seconds) { |
300 | Sleep(seconds * 1000); | |
301 | } | |
302 | ||
303 | void SleepForMillis(int millis) { | |
304 | Sleep(millis); | |
305 | } | |
306 | ||
ef1b3fda KS |
307 | u64 NanoTime() { |
308 | return 0; | |
309 | } | |
310 | ||
f35db108 | 311 | void Abort() { |
696d846a MO |
312 | if (::IsDebuggerPresent()) |
313 | __debugbreak(); | |
314 | internal__exit(3); | |
315 | } | |
316 | ||
317 | // Read the file to extract the ImageBase field from the PE header. If ASLR is | |
318 | // disabled and this virtual address is available, the loader will typically | |
319 | // load the image at this address. Therefore, we call it the preferred base. Any | |
320 | // addresses in the DWARF typically assume that the object has been loaded at | |
321 | // this address. | |
322 | static uptr GetPreferredBase(const char *modname) { | |
323 | fd_t fd = OpenFile(modname, RdOnly, nullptr); | |
324 | if (fd == kInvalidFd) | |
325 | return 0; | |
326 | FileCloser closer(fd); | |
327 | ||
328 | // Read just the DOS header. | |
329 | IMAGE_DOS_HEADER dos_header; | |
330 | uptr bytes_read; | |
331 | if (!ReadFromFile(fd, &dos_header, sizeof(dos_header), &bytes_read) || | |
332 | bytes_read != sizeof(dos_header)) | |
333 | return 0; | |
334 | ||
335 | // The file should start with the right signature. | |
336 | if (dos_header.e_magic != IMAGE_DOS_SIGNATURE) | |
337 | return 0; | |
338 | ||
339 | // The layout at e_lfanew is: | |
340 | // "PE\0\0" | |
341 | // IMAGE_FILE_HEADER | |
342 | // IMAGE_OPTIONAL_HEADER | |
343 | // Seek to e_lfanew and read all that data. | |
344 | char buf[4 + sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_OPTIONAL_HEADER)]; | |
345 | if (::SetFilePointer(fd, dos_header.e_lfanew, nullptr, FILE_BEGIN) == | |
346 | INVALID_SET_FILE_POINTER) | |
347 | return 0; | |
348 | if (!ReadFromFile(fd, &buf[0], sizeof(buf), &bytes_read) || | |
349 | bytes_read != sizeof(buf)) | |
350 | return 0; | |
351 | ||
352 | // Check for "PE\0\0" before the PE header. | |
353 | char *pe_sig = &buf[0]; | |
354 | if (internal_memcmp(pe_sig, "PE\0\0", 4) != 0) | |
355 | return 0; | |
356 | ||
357 | // Skip over IMAGE_FILE_HEADER. We could do more validation here if we wanted. | |
358 | IMAGE_OPTIONAL_HEADER *pe_header = | |
359 | (IMAGE_OPTIONAL_HEADER *)(pe_sig + 4 + sizeof(IMAGE_FILE_HEADER)); | |
360 | ||
361 | // Check for more magic in the PE header. | |
362 | if (pe_header->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) | |
363 | return 0; | |
364 | ||
365 | // Finally, return the ImageBase. | |
366 | return (uptr)pe_header->ImageBase; | |
f35db108 WM |
367 | } |
368 | ||
55aea9f5 | 369 | #ifndef SANITIZER_GO |
ef1b3fda KS |
370 | uptr GetListOfModules(LoadedModule *modules, uptr max_modules, |
371 | string_predicate_t filter) { | |
696d846a MO |
372 | HANDLE cur_process = GetCurrentProcess(); |
373 | ||
374 | // Query the list of modules. Start by assuming there are no more than 256 | |
375 | // modules and retry if that's not sufficient. | |
376 | HMODULE *hmodules = 0; | |
377 | uptr modules_buffer_size = sizeof(HMODULE) * 256; | |
378 | DWORD bytes_required; | |
379 | while (!hmodules) { | |
380 | hmodules = (HMODULE *)MmapOrDie(modules_buffer_size, __FUNCTION__); | |
381 | CHECK(EnumProcessModules(cur_process, hmodules, modules_buffer_size, | |
382 | &bytes_required)); | |
383 | if (bytes_required > modules_buffer_size) { | |
384 | // Either there turned out to be more than 256 hmodules, or new hmodules | |
385 | // could have loaded since the last try. Retry. | |
386 | UnmapOrDie(hmodules, modules_buffer_size); | |
387 | hmodules = 0; | |
388 | modules_buffer_size = bytes_required; | |
389 | } | |
390 | } | |
391 | ||
392 | // |num_modules| is the number of modules actually present, | |
393 | // |count| is the number of modules we return. | |
394 | size_t nun_modules = bytes_required / sizeof(HMODULE), | |
395 | count = 0; | |
396 | for (size_t i = 0; i < nun_modules && count < max_modules; ++i) { | |
397 | HMODULE handle = hmodules[i]; | |
398 | MODULEINFO mi; | |
399 | if (!GetModuleInformation(cur_process, handle, &mi, sizeof(mi))) | |
400 | continue; | |
401 | ||
402 | // Get the UTF-16 path and convert to UTF-8. | |
403 | wchar_t modname_utf16[kMaxPathLength]; | |
404 | int modname_utf16_len = | |
405 | GetModuleFileNameW(handle, modname_utf16, kMaxPathLength); | |
406 | if (modname_utf16_len == 0) | |
407 | modname_utf16[0] = '\0'; | |
408 | char module_name[kMaxPathLength]; | |
409 | int module_name_len = | |
410 | ::WideCharToMultiByte(CP_UTF8, 0, modname_utf16, modname_utf16_len + 1, | |
411 | &module_name[0], kMaxPathLength, NULL, NULL); | |
412 | module_name[module_name_len] = '\0'; | |
413 | ||
414 | if (filter && !filter(module_name)) | |
415 | continue; | |
416 | ||
417 | uptr base_address = (uptr)mi.lpBaseOfDll; | |
418 | uptr end_address = (uptr)mi.lpBaseOfDll + mi.SizeOfImage; | |
419 | ||
420 | // Adjust the base address of the module so that we get a VA instead of an | |
421 | // RVA when computing the module offset. This helps llvm-symbolizer find the | |
422 | // right DWARF CU. In the common case that the image is loaded at it's | |
423 | // preferred address, we will now print normal virtual addresses. | |
424 | uptr preferred_base = GetPreferredBase(&module_name[0]); | |
425 | uptr adjusted_base = base_address - preferred_base; | |
426 | ||
427 | LoadedModule *cur_module = &modules[count]; | |
428 | cur_module->set(module_name, adjusted_base); | |
429 | // We add the whole module as one single address range. | |
430 | cur_module->addAddressRange(base_address, end_address, /*executable*/ true); | |
431 | count++; | |
432 | } | |
433 | UnmapOrDie(hmodules, modules_buffer_size); | |
434 | ||
435 | return count; | |
ef1b3fda KS |
436 | }; |
437 | ||
696d846a MO |
438 | // We can't use atexit() directly at __asan_init time as the CRT is not fully |
439 | // initialized at this point. Place the functions into a vector and use | |
440 | // atexit() as soon as it is ready for use (i.e. after .CRT$XIC initializers). | |
441 | InternalMmapVectorNoCtor<void (*)(void)> atexit_functions; | |
442 | ||
f35db108 | 443 | int Atexit(void (*function)(void)) { |
696d846a MO |
444 | atexit_functions.push_back(function); |
445 | return 0; | |
f35db108 WM |
446 | } |
447 | ||
696d846a MO |
448 | static int RunAtexit() { |
449 | int ret = 0; | |
450 | for (uptr i = 0; i < atexit_functions.size(); ++i) { | |
451 | ret |= atexit(atexit_functions[i]); | |
452 | } | |
453 | return ret; | |
f35db108 WM |
454 | } |
455 | ||
696d846a MO |
456 | #pragma section(".CRT$XID", long, read) // NOLINT |
457 | __declspec(allocate(".CRT$XID")) int (*__run_atexit)() = RunAtexit; | |
458 | #endif | |
f35db108 | 459 | |
696d846a MO |
460 | // ------------------ sanitizer_libc.h |
461 | fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *last_error) { | |
462 | fd_t res; | |
463 | if (mode == RdOnly) { | |
464 | res = CreateFile(filename, GENERIC_READ, | |
465 | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |
466 | nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); | |
467 | } else if (mode == WrOnly) { | |
468 | res = CreateFile(filename, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, | |
469 | FILE_ATTRIBUTE_NORMAL, nullptr); | |
470 | } else { | |
471 | UNIMPLEMENTED(); | |
472 | } | |
473 | CHECK(res != kStdoutFd || kStdoutFd == kInvalidFd); | |
474 | CHECK(res != kStderrFd || kStderrFd == kInvalidFd); | |
475 | if (res == kInvalidFd && last_error) | |
476 | *last_error = GetLastError(); | |
477 | return res; | |
e297eb60 KS |
478 | } |
479 | ||
696d846a MO |
480 | void CloseFile(fd_t fd) { |
481 | CloseHandle(fd); | |
f35db108 WM |
482 | } |
483 | ||
696d846a MO |
484 | bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read, |
485 | error_t *error_p) { | |
486 | CHECK(fd != kInvalidFd); | |
b4ab7d34 | 487 | |
696d846a MO |
488 | // bytes_read can't be passed directly to ReadFile: |
489 | // uptr is unsigned long long on 64-bit Windows. | |
490 | unsigned long num_read_long; | |
b4ab7d34 | 491 | |
696d846a MO |
492 | bool success = ::ReadFile(fd, buff, buff_size, &num_read_long, nullptr); |
493 | if (!success && error_p) | |
494 | *error_p = GetLastError(); | |
495 | if (bytes_read) | |
496 | *bytes_read = num_read_long; | |
497 | return success; | |
f35db108 WM |
498 | } |
499 | ||
696d846a MO |
500 | bool SupportsColoredOutput(fd_t fd) { |
501 | // FIXME: support colored output. | |
502 | return false; | |
f35db108 WM |
503 | } |
504 | ||
696d846a MO |
505 | bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written, |
506 | error_t *error_p) { | |
507 | CHECK(fd != kInvalidFd); | |
dee5ea7a | 508 | |
696d846a MO |
509 | // Handle null optional parameters. |
510 | error_t dummy_error; | |
511 | error_p = error_p ? error_p : &dummy_error; | |
512 | uptr dummy_bytes_written; | |
513 | bytes_written = bytes_written ? bytes_written : &dummy_bytes_written; | |
dee5ea7a | 514 | |
696d846a MO |
515 | // Initialize output parameters in case we fail. |
516 | *error_p = 0; | |
517 | *bytes_written = 0; | |
518 | ||
519 | // Map the conventional Unix fds 1 and 2 to Windows handles. They might be | |
520 | // closed, in which case this will fail. | |
521 | if (fd == kStdoutFd || fd == kStderrFd) { | |
522 | fd = GetStdHandle(fd == kStdoutFd ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE); | |
523 | if (fd == 0) { | |
524 | *error_p = ERROR_INVALID_HANDLE; | |
525 | return false; | |
dee5ea7a KS |
526 | } |
527 | } | |
528 | ||
696d846a MO |
529 | DWORD bytes_written_32; |
530 | if (!WriteFile(fd, buff, buff_size, &bytes_written_32, 0)) { | |
531 | *error_p = GetLastError(); | |
532 | return false; | |
533 | } else { | |
534 | *bytes_written = bytes_written_32; | |
535 | return true; | |
dee5ea7a | 536 | } |
b4ab7d34 KS |
537 | } |
538 | ||
696d846a | 539 | bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) { |
f35db108 | 540 | UNIMPLEMENTED(); |
f35db108 WM |
541 | } |
542 | ||
ef1b3fda | 543 | uptr internal_sched_yield() { |
e297eb60 | 544 | Sleep(0); |
f35db108 WM |
545 | return 0; |
546 | } | |
547 | ||
7df59255 | 548 | void internal__exit(int exitcode) { |
df77f0e4 | 549 | ExitProcess(exitcode); |
7df59255 KS |
550 | } |
551 | ||
866e32ad KS |
552 | uptr internal_ftruncate(fd_t fd, uptr size) { |
553 | UNIMPLEMENTED(); | |
554 | } | |
555 | ||
696d846a MO |
556 | uptr GetRSS() { |
557 | return 0; | |
866e32ad KS |
558 | } |
559 | ||
696d846a MO |
560 | void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; } |
561 | void internal_join_thread(void *th) { } | |
562 | ||
2660d12d | 563 | // ---------------------- BlockingMutex ---------------- {{{1 |
b4ab7d34 KS |
564 | const uptr LOCK_UNINITIALIZED = 0; |
565 | const uptr LOCK_READY = (uptr)-1; | |
2660d12d KS |
566 | |
567 | BlockingMutex::BlockingMutex(LinkerInitialized li) { | |
568 | // FIXME: see comments in BlockingMutex::Lock() for the details. | |
569 | CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED); | |
570 | ||
571 | CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_)); | |
572 | InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_); | |
573 | owner_ = LOCK_READY; | |
574 | } | |
575 | ||
ef1b3fda KS |
576 | BlockingMutex::BlockingMutex() { |
577 | CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_)); | |
578 | InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_); | |
579 | owner_ = LOCK_READY; | |
580 | } | |
581 | ||
2660d12d KS |
582 | void BlockingMutex::Lock() { |
583 | if (owner_ == LOCK_UNINITIALIZED) { | |
584 | // FIXME: hm, global BlockingMutex objects are not initialized?!? | |
585 | // This might be a side effect of the clang+cl+link Frankenbuild... | |
586 | new(this) BlockingMutex((LinkerInitialized)(LINKER_INITIALIZED + 1)); | |
587 | ||
588 | // FIXME: If it turns out the linker doesn't invoke our | |
589 | // constructors, we should probably manually Lock/Unlock all the global | |
590 | // locks while we're starting in one thread to avoid double-init races. | |
591 | } | |
592 | EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_); | |
b4ab7d34 | 593 | CHECK_EQ(owner_, LOCK_READY); |
2660d12d KS |
594 | owner_ = GetThreadSelf(); |
595 | } | |
596 | ||
597 | void BlockingMutex::Unlock() { | |
b4ab7d34 | 598 | CHECK_EQ(owner_, GetThreadSelf()); |
2660d12d KS |
599 | owner_ = LOCK_READY; |
600 | LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_); | |
601 | } | |
602 | ||
ef1b3fda KS |
603 | void BlockingMutex::CheckLocked() { |
604 | CHECK_EQ(owner_, GetThreadSelf()); | |
605 | } | |
606 | ||
607 | uptr GetTlsSize() { | |
608 | return 0; | |
609 | } | |
610 | ||
611 | void InitTlsSize() { | |
612 | } | |
613 | ||
614 | void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, | |
615 | uptr *tls_addr, uptr *tls_size) { | |
616 | #ifdef SANITIZER_GO | |
617 | *stk_addr = 0; | |
618 | *stk_size = 0; | |
619 | *tls_addr = 0; | |
620 | *tls_size = 0; | |
621 | #else | |
622 | uptr stack_top, stack_bottom; | |
623 | GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); | |
624 | *stk_addr = stack_bottom; | |
625 | *stk_size = stack_top - stack_bottom; | |
626 | *tls_addr = 0; | |
627 | *tls_size = 0; | |
628 | #endif | |
629 | } | |
630 | ||
866e32ad | 631 | #if !SANITIZER_GO |
696d846a | 632 | void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) { |
dee5ea7a | 633 | CHECK_GE(max_depth, 2); |
ef1b3fda KS |
634 | // FIXME: CaptureStackBackTrace might be too slow for us. |
635 | // FIXME: Compare with StackWalk64. | |
636 | // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc | |
df77f0e4 KS |
637 | size = CaptureStackBackTrace(2, Min(max_depth, kStackTraceMax), |
638 | (void**)trace, 0); | |
dee5ea7a KS |
639 | if (size == 0) |
640 | return; | |
641 | ||
ef1b3fda | 642 | // Skip the RTL frames by searching for the PC in the stacktrace. |
df77f0e4 KS |
643 | uptr pc_location = LocatePcInTrace(pc); |
644 | PopStackFrames(pc_location); | |
ef1b3fda KS |
645 | } |
646 | ||
c5be964a | 647 | void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context, |
696d846a | 648 | u32 max_depth) { |
866e32ad KS |
649 | CONTEXT ctx = *(CONTEXT *)context; |
650 | STACKFRAME64 stack_frame; | |
651 | memset(&stack_frame, 0, sizeof(stack_frame)); | |
652 | size = 0; | |
653 | #if defined(_WIN64) | |
654 | int machine_type = IMAGE_FILE_MACHINE_AMD64; | |
655 | stack_frame.AddrPC.Offset = ctx.Rip; | |
656 | stack_frame.AddrFrame.Offset = ctx.Rbp; | |
657 | stack_frame.AddrStack.Offset = ctx.Rsp; | |
658 | #else | |
659 | int machine_type = IMAGE_FILE_MACHINE_I386; | |
660 | stack_frame.AddrPC.Offset = ctx.Eip; | |
661 | stack_frame.AddrFrame.Offset = ctx.Ebp; | |
662 | stack_frame.AddrStack.Offset = ctx.Esp; | |
663 | #endif | |
664 | stack_frame.AddrPC.Mode = AddrModeFlat; | |
665 | stack_frame.AddrFrame.Mode = AddrModeFlat; | |
666 | stack_frame.AddrStack.Mode = AddrModeFlat; | |
667 | while (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(), | |
668 | &stack_frame, &ctx, NULL, &SymFunctionTableAccess64, | |
669 | &SymGetModuleBase64, NULL) && | |
670 | size < Min(max_depth, kStackTraceMax)) { | |
c5be964a | 671 | trace_buffer[size++] = (uptr)stack_frame.AddrPC.Offset; |
866e32ad | 672 | } |
dee5ea7a | 673 | } |
866e32ad | 674 | #endif // #if !SANITIZER_GO |
dee5ea7a | 675 | |
696d846a MO |
676 | void ReportFile::Write(const char *buffer, uptr length) { |
677 | SpinMutexLock l(mu); | |
678 | ReopenIfNecessary(); | |
679 | if (!WriteToFile(fd, buffer, length)) { | |
ef1b3fda KS |
680 | // stderr may be closed, but we may be able to print to the debugger |
681 | // instead. This is the case when launching a program from Visual Studio, | |
682 | // and the following routine should write to its console. | |
683 | OutputDebugStringA(buffer); | |
684 | } | |
685 | } | |
686 | ||
dee5ea7a KS |
687 | void SetAlternateSignalStack() { |
688 | // FIXME: Decide what to do on Windows. | |
689 | } | |
690 | ||
691 | void UnsetAlternateSignalStack() { | |
692 | // FIXME: Decide what to do on Windows. | |
693 | } | |
694 | ||
695 | void InstallDeadlySignalHandlers(SignalHandlerType handler) { | |
696 | (void)handler; | |
697 | // FIXME: Decide what to do on Windows. | |
698 | } | |
699 | ||
700 | bool IsDeadlySignal(int signum) { | |
701 | // FIXME: Decide what to do on Windows. | |
702 | return false; | |
703 | } | |
704 | ||
866e32ad | 705 | bool IsAccessibleMemoryRange(uptr beg, uptr size) { |
696d846a MO |
706 | SYSTEM_INFO si; |
707 | GetNativeSystemInfo(&si); | |
708 | uptr page_size = si.dwPageSize; | |
709 | uptr page_mask = ~(page_size - 1); | |
710 | ||
711 | for (uptr page = beg & page_mask, end = (beg + size - 1) & page_mask; | |
712 | page <= end;) { | |
713 | MEMORY_BASIC_INFORMATION info; | |
714 | if (VirtualQuery((LPCVOID)page, &info, sizeof(info)) != sizeof(info)) | |
715 | return false; | |
716 | ||
717 | if (info.Protect == 0 || info.Protect == PAGE_NOACCESS || | |
718 | info.Protect == PAGE_EXECUTE) | |
719 | return false; | |
720 | ||
721 | if (info.RegionSize == 0) | |
722 | return false; | |
723 | ||
724 | page += info.RegionSize; | |
725 | } | |
726 | ||
866e32ad KS |
727 | return true; |
728 | } | |
729 | ||
696d846a MO |
730 | SignalContext SignalContext::Create(void *siginfo, void *context) { |
731 | EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD*)siginfo; | |
732 | CONTEXT *context_record = (CONTEXT*)context; | |
733 | ||
734 | uptr pc = (uptr)exception_record->ExceptionAddress; | |
735 | #ifdef _WIN64 | |
736 | uptr bp = (uptr)context_record->Rbp; | |
737 | uptr sp = (uptr)context_record->Rsp; | |
738 | #else | |
739 | uptr bp = (uptr)context_record->Ebp; | |
740 | uptr sp = (uptr)context_record->Esp; | |
741 | #endif | |
742 | uptr access_addr = exception_record->ExceptionInformation[1]; | |
743 | ||
744 | return SignalContext(context, access_addr, pc, sp, bp); | |
745 | } | |
746 | ||
747 | uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { | |
748 | // FIXME: Actually implement this function. | |
749 | CHECK_GT(buf_len, 0); | |
750 | buf[0] = 0; | |
751 | return 0; | |
752 | } | |
753 | ||
754 | uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) { | |
755 | return ReadBinaryName(buf, buf_len); | |
756 | } | |
757 | ||
758 | void CheckVMASize() { | |
759 | // Do nothing. | |
760 | } | |
761 | ||
f35db108 WM |
762 | } // namespace __sanitizer |
763 | ||
764 | #endif // _WIN32 |