aboutsummaryrefslogtreecommitdiff
path: root/lib/sanitizer_common/sanitizer_symbolizer_win.cc
blob: 31f374687e964e46bc0d8ddde4a35a4cabe6a827 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//===-- sanitizer_symbolizer_win.cc ---------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is shared between AddressSanitizer and ThreadSanitizer
// run-time libraries.
// Windows-specific implementation of symbolizer parts.
//===----------------------------------------------------------------------===//

#include "sanitizer_platform.h"
#if SANITIZER_WINDOWS
#include <windows.h>
#include <dbghelp.h>
#pragma comment(lib, "dbghelp.lib")

#include "sanitizer_symbolizer_win.h"
#include "sanitizer_symbolizer_internal.h"

namespace __sanitizer {

namespace {

bool is_dbghelp_initialized = false;

bool TrySymInitialize() {
  SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
  return SymInitialize(GetCurrentProcess(), 0, TRUE);
  // FIXME: We don't call SymCleanup() on exit yet - should we?
}

// Initializes DbgHelp library, if it's not yet initialized. Calls to this
// function should be synchronized with respect to other calls to DbgHelp API
// (e.g. from WinSymbolizerTool).
void InitializeDbgHelpIfNeeded() {
  if (is_dbghelp_initialized)
    return;
  if (!TrySymInitialize()) {
    // OK, maybe the client app has called SymInitialize already.
    // That's a bit unfortunate for us as all the DbgHelp functions are
    // single-threaded and we can't coordinate with the app.
    // FIXME: Can we stop the other threads at this point?
    // Anyways, we have to reconfigure stuff to make sure that SymInitialize
    // has all the appropriate options set.
    // Cross our fingers and reinitialize DbgHelp.
    Report("*** WARNING: Failed to initialize DbgHelp!              ***\n");
    Report("*** Most likely this means that the app is already      ***\n");
    Report("*** using DbgHelp, possibly with incompatible flags.    ***\n");
    Report("*** Due to technical reasons, symbolization might crash ***\n");
    Report("*** or produce wrong results.                           ***\n");
    SymCleanup(GetCurrentProcess());
    TrySymInitialize();
  }
  is_dbghelp_initialized = true;

  // When an executable is run from a location different from the one where it
  // was originally built, we may not see the nearby PDB files.
  // To work around this, let's append the directory of the main module
  // to the symbol search path.  All the failures below are not fatal.
  const size_t kSymPathSize = 2048;
  static wchar_t path_buffer[kSymPathSize + 1 + MAX_PATH];
  if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer, kSymPathSize)) {
    Report("*** WARNING: Failed to SymGetSearchPathW ***\n");
    return;
  }
  size_t sz = wcslen(path_buffer);
  if (sz) {
    CHECK_EQ(0, wcscat_s(path_buffer, L";"));
    sz++;
  }
  DWORD res = GetModuleFileNameW(NULL, path_buffer + sz, MAX_PATH);
  if (res == 0 || res == MAX_PATH) {
    Report("*** WARNING: Failed to getting the EXE directory ***\n");
    return;
  }
  // Write the zero character in place of the last backslash to get the
  // directory of the main module at the end of path_buffer.
  wchar_t *last_bslash = wcsrchr(path_buffer + sz, L'\\');
  CHECK_NE(last_bslash, 0);
  *last_bslash = L'\0';
  if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer)) {
    Report("*** WARNING: Failed to SymSetSearchPathW\n");
    return;
  }
}

}  // namespace

bool WinSymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *frame) {
  InitializeDbgHelpIfNeeded();

  // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
  char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
  PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
  symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
  symbol->MaxNameLen = MAX_SYM_NAME;
  DWORD64 offset = 0;
  BOOL got_objname = SymFromAddr(GetCurrentProcess(),
                                 (DWORD64)addr, &offset, symbol);
  if (!got_objname)
    return false;

  DWORD unused;
  IMAGEHLP_LINE64 line_info;
  line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
  BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addr,
                                           &unused, &line_info);
  frame->info.function = internal_strdup(symbol->Name);
  frame->info.function_offset = (uptr)offset;
  if (got_fileline) {
    frame->info.file = internal_strdup(line_info.FileName);
    frame->info.line = line_info.LineNumber;
  }
  return true;
}

const char *WinSymbolizerTool::Demangle(const char *name) {
  CHECK(is_dbghelp_initialized);
  static char demangle_buffer[1000];
  if (name[0] == '\01' &&
      UnDecorateSymbolName(name + 1, demangle_buffer, sizeof(demangle_buffer),
                           UNDNAME_NAME_ONLY))
    return demangle_buffer;
  else
    return name;
}

const char *Symbolizer::PlatformDemangle(const char *name) {
  return name;
}

void Symbolizer::PlatformPrepareForSandboxing() {
  // Do nothing.
}

Symbolizer *Symbolizer::PlatformInit() {
  IntrusiveList<SymbolizerTool> list;
  list.clear();
  list.push_back(new(symbolizer_allocator_) WinSymbolizerTool());
  return new(symbolizer_allocator_) Symbolizer(list);
}

}  // namespace __sanitizer

#endif  // _WIN32