Skip to content

Commit c30f62f

Browse files
authored
Merge pull request #62294 from grynspan/jgrynspan/dbghelp-pass-handle
Refactor `_swift_withWin32DbgHelpLibrary()` to avoid using `GetCurrentProcess()` per Microsoft documentation
2 parents 25e36ad + 024f018 commit c30f62f

File tree

3 files changed

+102
-41
lines changed

3 files changed

+102
-41
lines changed

stdlib/public/runtime/Errors.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ static bool getSymbolNameAddr(llvm::StringRef libraryName,
9999
#if defined(_WIN32)
100100
char szUndName[1024];
101101
DWORD dwResult;
102-
dwResult = _swift_withWin32DbgHelpLibrary([&] (bool isInitialized) -> DWORD {
103-
if (!isInitialized) {
102+
dwResult = _swift_win32_withDbgHelpLibrary([&] (HANDLE hProcess) -> DWORD {
103+
if (!hProcess) {
104104
return 0;
105105
}
106106

stdlib/public/runtime/ImageInspection.h

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929
#include <memory>
3030
#include <type_traits>
3131

32+
#if defined(_WIN32)
33+
#define WIN32_LEAN_AND_MEAN
34+
#define NOMINMAX
35+
#include <Windows.h>
36+
#endif
37+
3238
namespace swift {
3339

3440
/// This is a platform independent version of Dl_info from dlfcn.h
@@ -112,55 +118,73 @@ int lookupSymbol(const void *address, SymbolInfo *info);
112118
/// Configure the environment to allow calling into the Debug Help library.
113119
///
114120
/// \param body A function to invoke. This function attempts to first initialize
115-
/// the Debug Help library. The result of that operation is passed to this
116-
/// function.
121+
/// the Debug Help library. If it did so successfully, the handle used during
122+
/// initialization is passed to this function and should be used with
123+
/// subsequent calls to the Debug Help library. Do not close this handle.
117124
/// \param context A caller-supplied value to pass to \a body.
118125
///
119126
/// On Windows, the Debug Help library (DbgHelp.lib) is not thread-safe. All
120127
/// calls into it from the Swift runtime and stdlib should route through this
121128
/// function.
129+
///
130+
/// This function sets the Debug Help library's options by calling
131+
/// \c SymSetOptions() before \a body is invoked, and then resets them back to
132+
/// their old value before returning. \a body can also call \c SymSetOptions()
133+
/// if needed.
122134
SWIFT_RUNTIME_STDLIB_SPI
123-
void _swift_withWin32DbgHelpLibrary(
124-
void (* body)(bool isInitialized, void *context), void *context);
135+
void _swift_win32_withDbgHelpLibrary(
136+
void (* body)(HANDLE hProcess, void *context), void *context);
125137

126138
/// Configure the environment to allow calling into the Debug Help library.
127139
///
128140
/// \param body A function to invoke. This function attempts to first initialize
129-
/// the Debug Help library. The result of that operation is passed to this
130-
/// function.
141+
/// the Debug Help library. If it did so successfully, the handle used during
142+
/// initialization is passed to this function and should be used with
143+
/// subsequent calls to the Debug Help library. Do not close this handle.
131144
///
132145
/// On Windows, the Debug Help library (DbgHelp.lib) is not thread-safe. All
133146
/// calls into it from the Swift runtime and stdlib should route through this
134147
/// function.
135-
static inline void _swift_withWin32DbgHelpLibrary(
136-
const std::function<void(bool /*isInitialized*/)> &body) {
137-
_swift_withWin32DbgHelpLibrary([](bool isInitialized, void *context) {
138-
auto bodyp = reinterpret_cast<std::function<void(bool)> *>(context);
139-
(* bodyp)(isInitialized);
148+
///
149+
/// This function sets the Debug Help library's options by calling
150+
/// \c SymSetOptions() before \a body is invoked, and then resets them back to
151+
/// their old value before returning. \a body can also call \c SymSetOptions()
152+
/// if needed.
153+
static inline void _swift_win32_withDbgHelpLibrary(
154+
const std::function<void(HANDLE /*hProcess*/)> &body) {
155+
_swift_win32_withDbgHelpLibrary([](HANDLE hProcess, void *context) {
156+
auto bodyp = reinterpret_cast<std::function<void(HANDLE)> *>(context);
157+
(* bodyp)(hProcess);
140158
}, const_cast<void *>(reinterpret_cast<const void *>(&body)));
141159
}
142160

143161
/// Configure the environment to allow calling into the Debug Help library.
144162
///
145163
/// \param body A function to invoke. This function attempts to first initialize
146-
/// the Debug Help library. The result of that operation is passed to this
147-
/// function.
164+
/// the Debug Help library. If it did so successfully, the handle used during
165+
/// initialization is passed to this function and should be used with
166+
/// subsequent calls to the Debug Help library. Do not close this handle.
148167
///
149168
/// \returns Whatever is returned from \a body.
150169
///
151170
/// On Windows, the Debug Help library (DbgHelp.lib) is not thread-safe. All
152171
/// calls into it from the Swift runtime and stdlib should route through this
153172
/// function.
173+
///
174+
/// This function sets the Debug Help library's options by calling
175+
/// \c SymSetOptions() before \a body is invoked, and then resets them back to
176+
/// their old value before returning. \a body can also call \c SymSetOptions()
177+
/// if needed.
154178
template <
155179
typename F,
156-
typename R = typename std::result_of_t<F&(bool /*isInitialized*/)>,
180+
typename R = typename std::result_of_t<F&(HANDLE /*hProcess*/)>,
157181
typename = typename std::enable_if_t<!std::is_same<void, R>::value>
158182
>
159-
static inline R _swift_withWin32DbgHelpLibrary(const F& body) {
183+
static inline R _swift_win32_withDbgHelpLibrary(const F& body) {
160184
R result;
161185

162-
_swift_withWin32DbgHelpLibrary([&body, &result] (bool isInitialized) {
163-
result = body(isInitialized);
186+
_swift_win32_withDbgHelpLibrary([&body, &result] (HANDLE hProcess) {
187+
result = body(hProcess);
164188
});
165189

166190
return result;

stdlib/public/runtime/ImageInspectionCOFF.cpp

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -40,30 +40,25 @@ int swift::lookupSymbol(const void *address, SymbolInfo *info) {
4040
info->symbolAddress = dli_saddr;
4141
return 1;
4242
#elif defined(_WIN32)
43-
return _swift_withWin32DbgHelpLibrary([&] (bool isInitialized) {
43+
return _swift_win32_withDbgHelpLibrary([&] (HANDLE hProcess) {
4444
static const constexpr size_t kSymbolMaxNameLen = 1024;
4545

46-
if (!isInitialized) {
46+
if (!hProcess) {
4747
return 0;
4848
}
4949

50-
char buffer[sizeof(SYMBOL_INFO) + kSymbolMaxNameLen];
51-
PSYMBOL_INFO pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer);
52-
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
53-
pSymbol->MaxNameLen = kSymbolMaxNameLen;
54-
55-
DWORD64 dwDisplacement = 0;
56-
57-
if (SymFromAddr(GetCurrentProcess(),
58-
reinterpret_cast<const DWORD64>(address),
59-
&dwDisplacement, pSymbol) == FALSE) {
50+
SYMBOL_INFO_PACKAGE symbol = {};
51+
symbol.si.SizeOfStruct = sizeof(SYMBOL_INFO);
52+
symbol.si.MaxNameLen = MAX_SYM_NAME;
53+
if (SymFromAddr(hProcess, reinterpret_cast<const DWORD64>(address),
54+
nullptr, &symbol.si) == FALSE) {
6055
return 0;
6156
}
6257

6358
info->fileName = NULL;
64-
info->baseAddress = reinterpret_cast<void *>(pSymbol->ModBase);
65-
info->symbolName.reset(_strdup(pSymbol->Name));
66-
info->symbolAddress = reinterpret_cast<void *>(pSymbol->Address);
59+
info->baseAddress = reinterpret_cast<void *>(symbol.si.ModBase);
60+
info->symbolName.reset(_strdup(symbol.si.Name));
61+
info->symbolAddress = reinterpret_cast<void *>(symbol.si.Address);
6762

6863
return 1;
6964
});
@@ -74,16 +69,58 @@ int swift::lookupSymbol(const void *address, SymbolInfo *info) {
7469

7570
#if defined(_WIN32)
7671
static LazyMutex mutex;
77-
static bool isDbgHelpInitialized = false;
72+
static HANDLE dbgHelpHandle = nullptr;
7873

79-
void swift::_swift_withWin32DbgHelpLibrary(
80-
void (* body)(bool isInitialized, void *context), void *context) {
74+
void swift::_swift_win32_withDbgHelpLibrary(
75+
void (* body)(HANDLE hProcess, void *context), void *context) {
8176
mutex.withLock([=] () {
82-
if (!isDbgHelpInitialized) {
83-
SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
84-
isDbgHelpInitialized = SymInitialize(GetCurrentProcess(), nullptr, true);
77+
// If we have not previously created a handle to use with the library, do so
78+
// now. This handle belongs to the Swift runtime and should not be closed by
79+
// `body` (or anybody else.)
80+
if (!dbgHelpHandle) {
81+
// Per the documentation for the Debug Help library, we should not use the
82+
// current process handle because other subsystems might also use it and
83+
// end up stomping on each other. So we'll try to duplicate that handle to
84+
// get a unique one that still fulfills the needs of the library. If that
85+
// fails (presumably because the current process doesn't have the
86+
// PROCESS_DUP_HANDLE access right) then fall back to using the original
87+
// process handle and hope nobody else is using it too.
88+
HANDLE currentProcess = GetCurrentProcess();
89+
if (!DuplicateHandle(currentProcess, currentProcess, currentProcess,
90+
&dbgHelpHandle, 0, false, DUPLICATE_SAME_ACCESS)) {
91+
dbgHelpHandle = currentProcess;
92+
}
93+
}
94+
95+
// If we have not previously initialized the Debug Help library, do so now.
96+
bool isDbgHelpInitialized = false;
97+
if (dbgHelpHandle) {
98+
isDbgHelpInitialized = SymInitialize(dbgHelpHandle, nullptr, true);
99+
}
100+
101+
if (isDbgHelpInitialized) {
102+
// Set the library's options to what the Swift runtime generally expects.
103+
// If the options aren't going to change, we can skip the call and save a
104+
// few CPU cycles on the library call.
105+
constexpr const DWORD options = SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS;
106+
DWORD oldOptions = SymGetOptions();
107+
if (oldOptions != options) {
108+
SymSetOptions(options);
109+
}
110+
111+
body(dbgHelpHandle, context);
112+
113+
// Before returning, reset the library's options back to their previous
114+
// value. No need to call if the options didn't change because LazyMutex
115+
// is not recursive, so there shouldn't be an outer call expecting the
116+
// original options, and a subsequent call to this function will set them
117+
// to the defaults above.
118+
if (oldOptions != options) {
119+
SymSetOptions(oldOptions);
120+
}
121+
} else {
122+
body(nullptr, context);
85123
}
86-
body(isDbgHelpInitialized, context);
87124
});
88125
}
89126
#endif

0 commit comments

Comments
 (0)