asan_win.cc revision 7ebac9534c484b06a0c2822d83f082a127e743bc
1//===-- asan_win.cc -------------------------------------------------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file is a part of AddressSanitizer, an address sanity checker.
11//
12// Windows-specific details.
13//===----------------------------------------------------------------------===//
14#ifdef _WIN32
15#include <windows.h>
16
17#include <dbghelp.h>
18#include <stdlib.h>
19
20#include <new>  // FIXME: temporarily needed for placement new in AsanLock.
21
22#include "asan_interceptors.h"
23#include "asan_internal.h"
24#include "asan_lock.h"
25#include "asan_procmaps.h"
26#include "asan_thread.h"
27
28// Should not add dependency on libstdc++,
29// since most of the stuff here is inlinable.
30#include <algorithm>
31
32namespace __asan {
33
34// ---------------------- Memory management ---------------- {{{1
35void *AsanMmapFixedNoReserve(uptr fixed_addr, uptr size) {
36  return VirtualAlloc((LPVOID)fixed_addr, size,
37                      MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
38}
39
40void *AsanMmapSomewhereOrDie(uptr size, const char *mem_type) {
41  void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
42  if (rv == 0)
43    OutOfMemoryMessageAndDie(mem_type, size);
44  return rv;
45}
46
47void *AsanMprotect(uptr fixed_addr, uptr size) {
48  return VirtualAlloc((LPVOID)fixed_addr, size,
49                      MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
50}
51
52void AsanUnmapOrDie(void *addr, uptr size) {
53  CHECK(VirtualFree(addr, size, MEM_DECOMMIT));
54}
55
56// ---------------------- Stacktraces, symbols, etc. ---------------- {{{1
57static AsanLock dbghelp_lock(LINKER_INITIALIZED);
58static bool dbghelp_initialized = false;
59#pragma comment(lib, "dbghelp.lib")
60
61void AsanThread::SetThreadStackTopAndBottom() {
62  MEMORY_BASIC_INFORMATION mbi;
63  CHECK(VirtualQuery(&mbi /* on stack */,
64                    &mbi, sizeof(mbi)) != 0);
65  // FIXME: is it possible for the stack to not be a single allocation?
66  // Are these values what ASan expects to get (reserved, not committed;
67  // including stack guard page) ?
68  stack_top_ = (uptr)mbi.BaseAddress + mbi.RegionSize;
69  stack_bottom_ = (uptr)mbi.AllocationBase;
70}
71
72void AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) {
73  max_size = max_s;
74  void *tmp[kStackTraceMax];
75
76  // FIXME: CaptureStackBackTrace might be too slow for us.
77  // FIXME: Compare with StackWalk64.
78  // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc
79  uptr cs_ret = CaptureStackBackTrace(1, max_size, tmp, 0),
80         offset = 0;
81  // Skip the RTL frames by searching for the PC in the stacktrace.
82  // FIXME: this doesn't work well for the malloc/free stacks yet.
83  for (uptr i = 0; i < cs_ret; i++) {
84    if (pc != (uptr)tmp[i])
85      continue;
86    offset = i;
87    break;
88  }
89
90  size = cs_ret - offset;
91  for (uptr i = 0; i < size; i++)
92    trace[i] = (uptr)tmp[i + offset];
93}
94
95bool __asan_WinSymbolize(const void *addr, char *out_buffer, int buffer_size) {
96  ScopedLock lock(&dbghelp_lock);
97  if (!dbghelp_initialized) {
98    SymSetOptions(SYMOPT_DEFERRED_LOADS |
99                  SYMOPT_UNDNAME |
100                  SYMOPT_LOAD_LINES);
101    CHECK(SymInitialize(GetCurrentProcess(), 0, TRUE));
102    // FIXME: We don't call SymCleanup() on exit yet - should we?
103    dbghelp_initialized = true;
104  }
105
106  // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
107  char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
108  PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
109  symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
110  symbol->MaxNameLen = MAX_SYM_NAME;
111  DWORD64 offset = 0;
112  BOOL got_objname = SymFromAddr(GetCurrentProcess(),
113                                 (DWORD64)addr, &offset, symbol);
114  if (!got_objname)
115    return false;
116
117  DWORD  unused;
118  IMAGEHLP_LINE64 info;
119  info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
120  BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(),
121                                           (DWORD64)addr, &unused, &info);
122  int written = 0;
123  out_buffer[0] = '\0';
124  // FIXME: it might be useful to print out 'obj' or 'obj+offset' info too.
125  if (got_fileline) {
126    written += SNPrintf(out_buffer + written, buffer_size - written,
127                        " %s %s:%d", symbol->Name,
128                        info.FileName, info.LineNumber);
129  } else {
130    written += SNPrintf(out_buffer + written, buffer_size - written,
131                        " %s+0x%p", symbol->Name, offset);
132  }
133  return true;
134}
135
136// ---------------------- AsanLock ---------------- {{{1
137enum LockState {
138  LOCK_UNINITIALIZED = 0,
139  LOCK_READY = -1,
140};
141
142AsanLock::AsanLock(LinkerInitialized li) {
143  // FIXME: see comments in AsanLock::Lock() for the details.
144  CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED);
145
146  CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
147  InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
148  owner_ = LOCK_READY;
149}
150
151void AsanLock::Lock() {
152  if (owner_ == LOCK_UNINITIALIZED) {
153    // FIXME: hm, global AsanLock objects are not initialized?!?
154    // This might be a side effect of the clang+cl+link Frankenbuild...
155    new(this) AsanLock((LinkerInitialized)(LINKER_INITIALIZED + 1));
156
157    // FIXME: If it turns out the linker doesn't invoke our
158    // constructors, we should probably manually Lock/Unlock all the global
159    // locks while we're starting in one thread to avoid double-init races.
160  }
161  EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
162  CHECK(owner_ == LOCK_READY);
163  owner_ = GetThreadSelf();
164}
165
166void AsanLock::Unlock() {
167  CHECK(owner_ == GetThreadSelf());
168  owner_ = LOCK_READY;
169  LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
170}
171
172// ---------------------- TSD ---------------- {{{1
173static bool tsd_key_inited = false;
174
175static __declspec(thread) void *fake_tsd = 0;
176
177void AsanTSDInit(void (*destructor)(void *tsd)) {
178  // FIXME: we're ignoring the destructor for now.
179  tsd_key_inited = true;
180}
181
182void *AsanTSDGet() {
183  CHECK(tsd_key_inited);
184  return fake_tsd;
185}
186
187void AsanTSDSet(void *tsd) {
188  CHECK(tsd_key_inited);
189  fake_tsd = tsd;
190}
191
192// ---------------------- Various stuff ---------------- {{{1
193void *AsanDoesNotSupportStaticLinkage() {
194#if defined(_DEBUG)
195#error Please build the runtime with a non-debug CRT: /MD or /MT
196#endif
197  return 0;
198}
199
200bool AsanShadowRangeIsAvailable() {
201  // FIXME: shall we do anything here on Windows?
202  return true;
203}
204
205int AtomicInc(int *a) {
206  return InterlockedExchangeAdd((LONG*)a, 1) + 1;
207}
208
209u16 AtomicExchange(u16 *a, u16 new_val) {
210  // InterlockedExchange16 seems unavailable on some MSVS installations.
211  // Everybody stand back, I pretend to know inline assembly!
212  // FIXME: I assume VC is smart enough to save/restore eax/ecx?
213  __asm {
214    mov eax, a
215    mov cx, new_val
216    xchg [eax], cx  ; NOLINT
217    mov new_val, cx
218  }
219  return new_val;
220}
221
222u16 AtomicExchange(u16 *a, u16 new_val) {
223  // FIXME: can we do this with a proper xchg intrinsic?
224  u8 t = *a;
225  *a = new_val;
226  return t;
227}
228
229const char* AsanGetEnv(const char* name) {
230  static char env_buffer[32767] = {};
231
232  // Note: this implementation stores the result in a static buffer so we only
233  // allow it to be called just once.
234  static bool called_once = false;
235  if (called_once)
236    UNIMPLEMENTED();
237  called_once = true;
238
239  DWORD rv = GetEnvironmentVariableA(name, env_buffer, sizeof(env_buffer));
240  if (rv > 0 && rv < sizeof(env_buffer))
241    return env_buffer;
242  return 0;
243}
244
245void AsanDumpProcessMap() {
246  UNIMPLEMENTED();
247}
248
249uptr GetThreadSelf() {
250  return GetCurrentThreadId();
251}
252
253void SetAlternateSignalStack() {
254  // FIXME: Decide what to do on Windows.
255}
256
257void UnsetAlternateSignalStack() {
258  // FIXME: Decide what to do on Windows.
259}
260
261void InstallSignalHandlers() {
262  // FIXME: Decide what to do on Windows.
263}
264
265void AsanDisableCoreDumper() {
266  UNIMPLEMENTED();
267}
268
269void SleepForSeconds(int seconds) {
270  Sleep(seconds * 1000);
271}
272
273void Exit(int exitcode) {
274  _exit(exitcode);
275}
276
277void Abort() {
278  abort();
279  _exit(-1);  // abort is not NORETURN on Windows.
280}
281
282int Atexit(void (*function)(void)) {
283  return atexit(function);
284}
285
286void SortArray(uptr *array, uptr size) {
287  std::sort(array, array + size);
288}
289
290}  // namespace __asan
291
292#endif  // _WIN32
293