asan_win.cc revision fa3daaf1d66314658e7c05bf63dc825d179f2faf
11aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca//===-- asan_win.cc -------------------------------------------------------===//
21aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca//
31aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca//                     The LLVM Compiler Infrastructure
41aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca//
51aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca// This file is distributed under the University of Illinois Open Source
61aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca// License. See LICENSE.TXT for details.
71aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca//
81aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca//===----------------------------------------------------------------------===//
91aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca//
101aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca// This file is a part of AddressSanitizer, an address sanity checker.
111aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca//
121aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca// Windows-specific details.
131aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca//===----------------------------------------------------------------------===//
141aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca#ifdef _WIN32
151aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca#include <windows.h>
161aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca
171aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca#include <dbghelp.h>
181aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca#include <stdlib.h>
191aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca
201aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca#include <new>  // FIXME: temporarily needed for placement new in AsanLock.
211aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca
221aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca#include "asan_interceptors.h"
231aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca#include "asan_internal.h"
241aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca#include "asan_lock.h"
251aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca#include "asan_thread.h"
261aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca
271aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca// Should not add dependency on libstdc++,
285811ed87d732101ab8cfbd087bc99d8c6c963f30José Fonseca// since most of the stuff here is inlinable.
295811ed87d732101ab8cfbd087bc99d8c6c963f30José Fonseca#include <algorithm>
305811ed87d732101ab8cfbd087bc99d8c6c963f30José Fonseca
315811ed87d732101ab8cfbd087bc99d8c6c963f30José Fonsecanamespace __asan {
325811ed87d732101ab8cfbd087bc99d8c6c963f30José Fonseca
335811ed87d732101ab8cfbd087bc99d8c6c963f30José Fonseca// ---------------------- Stacktraces, symbols, etc. ---------------- {{{1
345811ed87d732101ab8cfbd087bc99d8c6c963f30José Fonsecastatic AsanLock dbghelp_lock(LINKER_INITIALIZED);
351aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonsecastatic bool dbghelp_initialized = false;
367cda8ea44c2b65265cefa79bd29a4990ac81cee6José Fonseca#pragma comment(lib, "dbghelp.lib")
3788b6abfba5e95866877dd3939ae43c6dfd71422cJosé Fonseca
38854627387db5c1bf3f69d56a638fab2af0c46010José Fonsecavoid AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) {
397cda8ea44c2b65265cefa79bd29a4990ac81cee6José Fonseca  max_size = max_s;
401aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  void *tmp[kStackTraceMax];
4109a7b011acb3957725bb7e1bc4125f0939a295e7José Fonseca
42efc82aef35a2aac5d2ed9774f6d28f2626796416Brian Paul  // FIXME: CaptureStackBackTrace might be too slow for us.
431aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  // FIXME: Compare with StackWalk64.
44dcc5d7f67220bc93aa7a351658649877c7e4cf69José Fonseca  // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc
451aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  uptr cs_ret = CaptureStackBackTrace(1, max_size, tmp, 0),
461aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca         offset = 0;
471aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  // Skip the RTL frames by searching for the PC in the stacktrace.
48a75519cb43c85b99cd54bc46dd83accda0cbd6cdJosé Fonseca  // FIXME: this doesn't work well for the malloc/free stacks yet.
49a75519cb43c85b99cd54bc46dd83accda0cbd6cdJosé Fonseca  for (uptr i = 0; i < cs_ret; i++) {
50a75519cb43c85b99cd54bc46dd83accda0cbd6cdJosé Fonseca    if (pc != (uptr)tmp[i])
51a75519cb43c85b99cd54bc46dd83accda0cbd6cdJosé Fonseca      continue;
52a75519cb43c85b99cd54bc46dd83accda0cbd6cdJosé Fonseca    offset = i;
53a75519cb43c85b99cd54bc46dd83accda0cbd6cdJosé Fonseca    break;
54a75519cb43c85b99cd54bc46dd83accda0cbd6cdJosé Fonseca  }
553469715a8a171512cf9b528702e70393f01c6041José Fonseca
563469715a8a171512cf9b528702e70393f01c6041José Fonseca  size = cs_ret - offset;
57a75519cb43c85b99cd54bc46dd83accda0cbd6cdJosé Fonseca  for (uptr i = 0; i < size; i++)
58a75519cb43c85b99cd54bc46dd83accda0cbd6cdJosé Fonseca    trace[i] = (uptr)tmp[i + offset];
59a75519cb43c85b99cd54bc46dd83accda0cbd6cdJosé Fonseca}
60a75519cb43c85b99cd54bc46dd83accda0cbd6cdJosé Fonseca
61a75519cb43c85b99cd54bc46dd83accda0cbd6cdJosé Fonsecabool __asan_WinSymbolize(const void *addr, char *out_buffer, int buffer_size) {
62a75519cb43c85b99cd54bc46dd83accda0cbd6cdJosé Fonseca  ScopedLock lock(&dbghelp_lock);
63a75519cb43c85b99cd54bc46dd83accda0cbd6cdJosé Fonseca  if (!dbghelp_initialized) {
64a75519cb43c85b99cd54bc46dd83accda0cbd6cdJosé Fonseca    SymSetOptions(SYMOPT_DEFERRED_LOADS |
65a75519cb43c85b99cd54bc46dd83accda0cbd6cdJosé Fonseca                  SYMOPT_UNDNAME |
66a75519cb43c85b99cd54bc46dd83accda0cbd6cdJosé Fonseca                  SYMOPT_LOAD_LINES);
67a75519cb43c85b99cd54bc46dd83accda0cbd6cdJosé Fonseca    CHECK(SymInitialize(GetCurrentProcess(), 0, TRUE));
68e01fa1eaec34675d0b30127de4f78b020a092a83Brian Paul    // FIXME: We don't call SymCleanup() on exit yet - should we?
692297bc9233be014b7b5aa037769209fbe9f6a66cBrian Paul    dbghelp_initialized = true;
702297bc9233be014b7b5aa037769209fbe9f6a66cBrian Paul  }
71877f2356b2ab7caa16beed496f36eca64ee201e1Brian Paul
72e01fa1eaec34675d0b30127de4f78b020a092a83Brian Paul  // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
731aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
74efc82aef35a2aac5d2ed9774f6d28f2626796416Brian Paul  PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
752297bc9233be014b7b5aa037769209fbe9f6a66cBrian Paul  symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
762297bc9233be014b7b5aa037769209fbe9f6a66cBrian Paul  symbol->MaxNameLen = MAX_SYM_NAME;
772297bc9233be014b7b5aa037769209fbe9f6a66cBrian Paul  DWORD64 offset = 0;
782297bc9233be014b7b5aa037769209fbe9f6a66cBrian Paul  BOOL got_objname = SymFromAddr(GetCurrentProcess(),
791aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca                                 (DWORD64)addr, &offset, symbol);
80efc82aef35a2aac5d2ed9774f6d28f2626796416Brian Paul  if (!got_objname)
81efc82aef35a2aac5d2ed9774f6d28f2626796416Brian Paul    return false;
821aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca
831aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  DWORD  unused;
841aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  IMAGEHLP_LINE64 info;
85241c3a1d8001fc5a30e2af4b4636b48e6f99690aJosé Fonseca  info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
861aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(),
87e01fa1eaec34675d0b30127de4f78b020a092a83Brian Paul                                           (DWORD64)addr, &unused, &info);
88e01fa1eaec34675d0b30127de4f78b020a092a83Brian Paul  int written = 0;
89a44a6960fab8c0053678fe74ce4c978ef40b06ffnobled  out_buffer[0] = '\0';
90a44a6960fab8c0053678fe74ce4c978ef40b06ffnobled  // FIXME: it might be useful to print out 'obj' or 'obj+offset' info too.
91e01fa1eaec34675d0b30127de4f78b020a092a83Brian Paul  if (got_fileline) {
921aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca    written += SNPrintf(out_buffer + written, buffer_size - written,
931aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca                        " %s %s:%d", symbol->Name,
941aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca                        info.FileName, info.LineNumber);
951aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  } else {
961aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca    written += SNPrintf(out_buffer + written, buffer_size - written,
976b0c79e058d4d008cad32c0ff06de9757ccd6fa0José Fonseca                        " %s+0x%p", symbol->Name, offset);
986b0c79e058d4d008cad32c0ff06de9757ccd6fa0José Fonseca  }
996b0c79e058d4d008cad32c0ff06de9757ccd6fa0José Fonseca  return true;
1006b0c79e058d4d008cad32c0ff06de9757ccd6fa0José Fonseca}
1011aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca
1026b0c79e058d4d008cad32c0ff06de9757ccd6fa0José Fonseca// ---------------------- AsanLock ---------------- {{{1
1036b0c79e058d4d008cad32c0ff06de9757ccd6fa0José Fonsecaenum LockState {
1046b0c79e058d4d008cad32c0ff06de9757ccd6fa0José Fonseca  LOCK_UNINITIALIZED = 0,
1056b0c79e058d4d008cad32c0ff06de9757ccd6fa0José Fonseca  LOCK_READY = -1,
1066b0c79e058d4d008cad32c0ff06de9757ccd6fa0José Fonseca};
1076b0c79e058d4d008cad32c0ff06de9757ccd6fa0José Fonseca
1086b0c79e058d4d008cad32c0ff06de9757ccd6fa0José FonsecaAsanLock::AsanLock(LinkerInitialized li) {
1096b0c79e058d4d008cad32c0ff06de9757ccd6fa0José Fonseca  // FIXME: see comments in AsanLock::Lock() for the details.
1106b0c79e058d4d008cad32c0ff06de9757ccd6fa0José Fonseca  CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED);
1116b0c79e058d4d008cad32c0ff06de9757ccd6fa0José Fonseca
1126b0c79e058d4d008cad32c0ff06de9757ccd6fa0José Fonseca  CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
1136b0c79e058d4d008cad32c0ff06de9757ccd6fa0José Fonseca  InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
1141aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  owner_ = LOCK_READY;
115489af2a3ba467e4341cb8504a0e59cf5828864d4Brian Paul}
1161aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca
1171aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonsecavoid AsanLock::Lock() {
1187cda8ea44c2b65265cefa79bd29a4990ac81cee6José Fonseca  if (owner_ == LOCK_UNINITIALIZED) {
119e01fa1eaec34675d0b30127de4f78b020a092a83Brian Paul    // FIXME: hm, global AsanLock objects are not initialized?!?
120efc82aef35a2aac5d2ed9774f6d28f2626796416Brian Paul    // This might be a side effect of the clang+cl+link Frankenbuild...
1211aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca    new(this) AsanLock((LinkerInitialized)(LINKER_INITIALIZED + 1));
1221aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca
1231aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca    // FIXME: If it turns out the linker doesn't invoke our
1241aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca    // constructors, we should probably manually Lock/Unlock all the global
1251aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca    // locks while we're starting in one thread to avoid double-init races.
1261aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  }
1271aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
1281aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  CHECK(owner_ == LOCK_READY);
1291aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  owner_ = GetThreadSelf();
1301aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca}
1311aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca
1321aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonsecavoid AsanLock::Unlock() {
1331aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  CHECK(owner_ == GetThreadSelf());
1341aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  owner_ = LOCK_READY;
1351aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
1361aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca}
1371aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca
1381aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca// ---------------------- TSD ---------------- {{{1
1391aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonsecastatic bool tsd_key_inited = false;
1401aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca
1411aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonsecastatic __declspec(thread) void *fake_tsd = 0;
1421aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca
1431aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonsecavoid AsanTSDInit(void (*destructor)(void *tsd)) {
1441aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  // FIXME: we're ignoring the destructor for now.
1451aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  tsd_key_inited = true;
1461aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca}
1471aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca
1481aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonsecavoid *AsanTSDGet() {
149efc82aef35a2aac5d2ed9774f6d28f2626796416Brian Paul  CHECK(tsd_key_inited);
1501aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  return fake_tsd;
1511aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca}
1521aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca
1531aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonsecavoid AsanTSDSet(void *tsd) {
1541aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  CHECK(tsd_key_inited);
1551aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  fake_tsd = tsd;
1561aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca}
1571aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca
1581aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca// ---------------------- Various stuff ---------------- {{{1
1591aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonsecavoid *AsanDoesNotSupportStaticLinkage() {
1601aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca#if defined(_DEBUG)
161efc82aef35a2aac5d2ed9774f6d28f2626796416Brian Paul#error Please build the runtime with a non-debug CRT: /MD or /MT
1622297bc9233be014b7b5aa037769209fbe9f6a66cBrian Paul#endif
1631aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  return 0;
1641aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca}
1651aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca
1662297bc9233be014b7b5aa037769209fbe9f6a66cBrian Paulbool AsanShadowRangeIsAvailable() {
1671aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  // FIXME: shall we do anything here on Windows?
1681aede69d3a8d288af11c2ef620b51e71c2ce89b2José Fonseca  return true;
1697cda8ea44c2b65265cefa79bd29a4990ac81cee6José Fonseca}
170e01fa1eaec34675d0b30127de4f78b020a092a83Brian Paul
171d07b0383660cd318ba43abad11bb80de4a124951José Fonsecaint AtomicInc(int *a) {
172d07b0383660cd318ba43abad11bb80de4a124951José Fonseca  return InterlockedExchangeAdd((LONG*)a, 1) + 1;
173d07b0383660cd318ba43abad11bb80de4a124951José Fonseca}
174d07b0383660cd318ba43abad11bb80de4a124951José Fonseca
175d07b0383660cd318ba43abad11bb80de4a124951José Fonsecau16 AtomicExchange(u16 *a, u16 new_val) {
176d07b0383660cd318ba43abad11bb80de4a124951José Fonseca  // InterlockedExchange16 seems unavailable on some MSVS installations.
177d07b0383660cd318ba43abad11bb80de4a124951José Fonseca  // Everybody stand back, I pretend to know inline assembly!
178d07b0383660cd318ba43abad11bb80de4a124951José Fonseca  // FIXME: I assume VC is smart enough to save/restore eax/ecx?
179d07b0383660cd318ba43abad11bb80de4a124951José Fonseca  __asm {
180d07b0383660cd318ba43abad11bb80de4a124951José Fonseca    mov eax, a
181d07b0383660cd318ba43abad11bb80de4a124951José Fonseca    mov cx, new_val
182d07b0383660cd318ba43abad11bb80de4a124951José Fonseca    xchg [eax], cx  ; NOLINT
183d07b0383660cd318ba43abad11bb80de4a124951José Fonseca    mov new_val, cx
184d07b0383660cd318ba43abad11bb80de4a124951José Fonseca  }
185d07b0383660cd318ba43abad11bb80de4a124951José Fonseca  return new_val;
186d07b0383660cd318ba43abad11bb80de4a124951José Fonseca}
187d07b0383660cd318ba43abad11bb80de4a124951José Fonseca
188d07b0383660cd318ba43abad11bb80de4a124951José Fonsecau8 AtomicExchange(u8 *a, u8 new_val) {
189d07b0383660cd318ba43abad11bb80de4a124951José Fonseca  // FIXME: can we do this with a proper xchg intrinsic?
190efc82aef35a2aac5d2ed9774f6d28f2626796416Brian Paul  u8 t = *a;
191d07b0383660cd318ba43abad11bb80de4a124951José Fonseca  *a = new_val;
192d07b0383660cd318ba43abad11bb80de4a124951José Fonseca  return t;
193d07b0383660cd318ba43abad11bb80de4a124951José Fonseca}
194d07b0383660cd318ba43abad11bb80de4a124951José Fonseca
195d07b0383660cd318ba43abad11bb80de4a124951José Fonsecavoid SetAlternateSignalStack() {
196d07b0383660cd318ba43abad11bb80de4a124951José Fonseca  // FIXME: Decide what to do on Windows.
197d07b0383660cd318ba43abad11bb80de4a124951José Fonseca}
198d07b0383660cd318ba43abad11bb80de4a124951José Fonseca
199d07b0383660cd318ba43abad11bb80de4a124951José Fonsecavoid UnsetAlternateSignalStack() {
200d07b0383660cd318ba43abad11bb80de4a124951José Fonseca  // FIXME: Decide what to do on Windows.
201d07b0383660cd318ba43abad11bb80de4a124951José Fonseca}
202d07b0383660cd318ba43abad11bb80de4a124951José Fonseca
203d07b0383660cd318ba43abad11bb80de4a124951José Fonsecavoid InstallSignalHandlers() {
204d07b0383660cd318ba43abad11bb80de4a124951José Fonseca  // FIXME: Decide what to do on Windows.
205d07b0383660cd318ba43abad11bb80de4a124951José Fonseca}
206d07b0383660cd318ba43abad11bb80de4a124951José Fonseca
207efc82aef35a2aac5d2ed9774f6d28f2626796416Brian Paulvoid SortArray(uptr *array, uptr size) {
208d07b0383660cd318ba43abad11bb80de4a124951José Fonseca  std::sort(array, array + size);
209d07b0383660cd318ba43abad11bb80de4a124951José Fonseca}
210b3d4e5bd26a44870af7d2413cca7a6f576a0984aJosé Fonseca
211b3d4e5bd26a44870af7d2413cca7a6f576a0984aJosé Fonseca}  // namespace __asan
212d07b0383660cd318ba43abad11bb80de4a124951José Fonseca
213b3d4e5bd26a44870af7d2413cca7a6f576a0984aJosé Fonseca#endif  // _WIN32
214efc82aef35a2aac5d2ed9774f6d28f2626796416Brian Paul