From 84ab54a1f6ec8d206c68127de01b45200171ad4a Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 12 Dec 2022 11:26:03 -0500 Subject: [PATCH 1/2] Implement SymbolInfo::getFilename() on Win32 --- include/swift/Runtime/Win32.h | 7 +- .../SwiftReflectionTest.swift | 19 ++-- stdlib/public/runtime/CMakeLists.txt | 2 - stdlib/public/runtime/Errors.cpp | 22 ++--- stdlib/public/runtime/ImageInspectionCOFF.cpp | 89 ------------------- .../public/runtime/ImageInspectionCommon.cpp | 24 ++--- stdlib/public/runtime/ImageInspectionCommon.h | 7 +- stdlib/public/runtime/ImageInspectionELF.cpp | 25 ------ stdlib/public/runtime/ReflectionMirror.cpp | 4 +- stdlib/public/runtime/SymbolInfo.cpp | 83 +++++++++++++++-- stdlib/public/runtime/SymbolInfo.h | 44 ++++++--- stdlib/public/runtime/Win32.cpp | 64 ++++++++++++- test/Runtime/loaded_image_uniqueness.swift | 19 ++-- 13 files changed, 235 insertions(+), 174 deletions(-) delete mode 100644 stdlib/public/runtime/ImageInspectionCOFF.cpp delete mode 100644 stdlib/public/runtime/ImageInspectionELF.cpp diff --git a/include/swift/Runtime/Win32.h b/include/swift/Runtime/Win32.h index ee9729b05ca35..f8187579219b4 100644 --- a/include/swift/Runtime/Win32.h +++ b/include/swift/Runtime/Win32.h @@ -21,15 +21,16 @@ #include "swift/shims/Visibility.h" +#include #include #include // For HANDLE #define WIN32_LEAN_AND_MEAN #define NOMINMAX -#include +#include -#include +#pragma mark - Wide-character string conversion /// Convert a wide string to UTF-8. /// @@ -53,6 +54,8 @@ char *_swift_win32_copyUTF8FromWide(const wchar_t *str); SWIFT_RUNTIME_STDLIB_SPI wchar_t *_swift_win32_copyWideFromUTF8(const char *str); +#pragma mark - DbgHelp library thread-safety + /// Configure the environment to allow calling into the Debug Help library. /// /// \param body A function to invoke. This function attempts to first initialize diff --git a/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift b/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift index cb153b8daf618..da26929e028cb 100644 --- a/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift +++ b/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift @@ -138,10 +138,15 @@ internal func _getMetadataSection(_ index: UInt) -> UnsafeRawPointer? @_silgen_name("swift_getMetadataSectionCount") internal func _getMetadataSectionCount() -> UInt -@_silgen_name("swift_getMetadataSectionName") -internal func _getMetadataSectionName( +@_silgen_name("swift_copyMetadataSectionName") +internal func _copyMetadataSectionName( _ metadata_section: UnsafeRawPointer -) -> UnsafePointer +) -> UnsafeMutablePointer? + +@_silgen_name("swift_freeMetadataSectionName") +internal func _freeMetadataSectionName( + _ name: UnsafeMutablePointer +) -> Void #endif extension Section { @@ -154,9 +159,13 @@ extension Section { internal func getReflectionInfoForImage(atIndex i: UInt32) -> ReflectionInfo? { #if INTERNAL_CHECKS_ENABLED return _getMetadataSection(UInt(i)).map { rawPointer in - let name = _getMetadataSectionName(rawPointer) + let cName = _copyMetadataSectionName(rawPointer) + defer { + _freeMetadataSectionName(cName) + } + let name = cName.flatMap { String(validatingUTF8: $0) } ?? "" let metadataSection = rawPointer.bindMemory(to: MetadataSections.self, capacity: 1).pointee - return ReflectionInfo(imageName: String(validatingUTF8: name)!, + return ReflectionInfo(imageName: name, fieldmd: Section(range: metadataSection.swift5_fieldmd), assocty: Section(range: metadataSection.swift5_assocty), builtin: Section(range: metadataSection.swift5_builtin), diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index 3aacf32cb352d..5aa4324242ac5 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -50,8 +50,6 @@ set(swift_runtime_sources HeapObject.cpp ImageInspectionCommon.cpp ImageInspectionMachO.cpp - ImageInspectionELF.cpp - ImageInspectionCOFF.cpp ImageInspectionStatic.cpp ImageInspectionWasm.cpp SymbolInfo.cpp diff --git a/stdlib/public/runtime/Errors.cpp b/stdlib/public/runtime/Errors.cpp index b126245d14bbe..6c7733028fa3d 100644 --- a/stdlib/public/runtime/Errors.cpp +++ b/stdlib/public/runtime/Errors.cpp @@ -79,8 +79,7 @@ enum: uint32_t { using namespace swift; #if SWIFT_STDLIB_SUPPORTS_BACKTRACE_REPORTING && SWIFT_STDLIB_HAS_DLADDR -static bool getSymbolNameAddr(llvm::StringRef libraryName, - const SymbolInfo &syminfo, +static bool getSymbolNameAddr(const SymbolInfo &syminfo, std::string &symbolName, uintptr_t &addrOut) { // If we failed to find a symbol and thus dlinfo->dli_sname is nullptr, we // need to use the hex address. @@ -150,12 +149,14 @@ void swift::dumpStackTraceEntry(unsigned index, void *framePC, return; } - // If SymbolInfo:lookup succeeded then fileName is non-null. Thus, we find the + // If SymbolInfo:lookup succeeded and fileName is non-null, we can find the // library name here. Avoid using StringRef::rsplit because its definition // is not provided in the header so that it requires linking with // libSupport.a. - llvm::StringRef libraryName{syminfo->getFilename()}; - libraryName = libraryName.substr(libraryName.rfind('/')).substr(1); + const char *libraryName = syminfo->getImageFilename(); + if (!libraryName) { + libraryName = ""; + } // Next we get the symbol name that we are going to use in our backtrace. std::string symbolName; @@ -164,12 +165,12 @@ void swift::dumpStackTraceEntry(unsigned index, void *framePC, // we just get HexAddr + 0. uintptr_t symbolAddr = uintptr_t(framePC); bool foundSymbol = - getSymbolNameAddr(libraryName, syminfo.value(), symbolName, symbolAddr); + getSymbolNameAddr(syminfo.value(), symbolName, symbolAddr); ptrdiff_t offset = 0; if (foundSymbol) { offset = ptrdiff_t(uintptr_t(framePC) - symbolAddr); } else { - auto baseAddress = syminfo->getBaseAddress(); + const void *baseAddress = syminfo->getImageBaseAddress(); offset = ptrdiff_t(uintptr_t(framePC) - uintptr_t(baseAddress)); symbolAddr = uintptr_t(framePC); symbolName = ""; @@ -183,12 +184,11 @@ void swift::dumpStackTraceEntry(unsigned index, void *framePC, // This gives enough info to reconstruct identical debugging target after // this process terminates. if (shortOutput) { - fprintf(stderr, "%s`%s + %td", libraryName.data(), symbolName.c_str(), - offset); + fprintf(stderr, "%s`%s + %td", libraryName, symbolName.c_str(), offset); } else { constexpr const char *format = "%-4u %-34s 0x%0.16" PRIxPTR " %s + %td\n"; - fprintf(stderr, format, index, libraryName.data(), symbolAddr, - symbolName.c_str(), offset); + fprintf(stderr, format, index, libraryName, symbolAddr, symbolName.c_str(), + offset); } #else if (shortOutput) { diff --git a/stdlib/public/runtime/ImageInspectionCOFF.cpp b/stdlib/public/runtime/ImageInspectionCOFF.cpp deleted file mode 100644 index f9803345ff747..0000000000000 --- a/stdlib/public/runtime/ImageInspectionCOFF.cpp +++ /dev/null @@ -1,89 +0,0 @@ -//===--- ImageInspectionWin32.cpp - Win32 image inspection ----------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -#if !defined(__ELF__) && !defined(__MACH__) - -#include "ImageInspection.h" - -#if defined(__CYGWIN__) -#include -#elif defined(_WIN32) -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include -#include -#endif - -#include "swift/Runtime/Win32.h" -#include "swift/Threading/Mutex.h" - -using namespace swift; - -#if defined(_WIN32) -static LazyMutex mutex; -static HANDLE dbgHelpHandle = nullptr; - -void _swift_win32_withDbgHelpLibrary( - void (* body)(HANDLE hProcess, void *context), void *context) { - mutex.withLock([=] () { - // If we have not previously created a handle to use with the library, do so - // now. This handle belongs to the Swift runtime and should not be closed by - // `body` (or anybody else.) - if (!dbgHelpHandle) { - // Per the documentation for the Debug Help library, we should not use the - // current process handle because other subsystems might also use it and - // end up stomping on each other. So we'll try to duplicate that handle to - // get a unique one that still fulfills the needs of the library. If that - // fails (presumably because the current process doesn't have the - // PROCESS_DUP_HANDLE access right) then fall back to using the original - // process handle and hope nobody else is using it too. - HANDLE currentProcess = GetCurrentProcess(); - if (!DuplicateHandle(currentProcess, currentProcess, currentProcess, - &dbgHelpHandle, 0, false, DUPLICATE_SAME_ACCESS)) { - dbgHelpHandle = currentProcess; - } - } - - // If we have not previously initialized the Debug Help library, do so now. - bool isDbgHelpInitialized = false; - if (dbgHelpHandle) { - isDbgHelpInitialized = SymInitialize(dbgHelpHandle, nullptr, true); - } - - if (isDbgHelpInitialized) { - // Set the library's options to what the Swift runtime generally expects. - // If the options aren't going to change, we can skip the call and save a - // few CPU cycles on the library call. - constexpr const DWORD options = SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS; - DWORD oldOptions = SymGetOptions(); - if (oldOptions != options) { - SymSetOptions(options); - } - - body(dbgHelpHandle, context); - - // Before returning, reset the library's options back to their previous - // value. No need to call if the options didn't change because LazyMutex - // is not recursive, so there shouldn't be an outer call expecting the - // original options, and a subsequent call to this function will set them - // to the defaults above. - if (oldOptions != options) { - SymSetOptions(oldOptions); - } - } else { - body(nullptr, context); - } - }); -} -#endif - -#endif // !defined(__ELF__) && !defined(__MACH__) diff --git a/stdlib/public/runtime/ImageInspectionCommon.cpp b/stdlib/public/runtime/ImageInspectionCommon.cpp index eb8861d31df7f..badc23403d6cf 100644 --- a/stdlib/public/runtime/ImageInspectionCommon.cpp +++ b/stdlib/public/runtime/ImageInspectionCommon.cpp @@ -64,10 +64,10 @@ static void fixupMetadataSectionBaseAddress(swift::MetadataSections *sections) { if (fixupNeeded) { // We need to fix up the base address. We'll need a known-good address in // the same image: `sections` itself will work nicely. - auto symbolInfo = SymbolInfo::lookup(sections); - if (symbolInfo.has_value() && symbolInfo->getBaseAddress()) { - sections->baseAddress.store(symbolInfo->getBaseAddress(), - std::memory_order_relaxed); + if (auto symbolInfo = SymbolInfo::lookup(sections)) { + if (const void *baseAddress = symbolInfo->getImageBaseAddress()) { + sections->baseAddress.store(baseAddress, std::memory_order_relaxed); + } } } } @@ -200,14 +200,18 @@ const swift::MetadataSections *swift_getMetadataSection(size_t index) { } SWIFT_RUNTIME_EXPORT -const char * -swift_getMetadataSectionName(const swift::MetadataSections *section) { +char *swift_copyMetadataSectionName(const swift::MetadataSections *section) { if (auto info = swift::SymbolInfo::lookup(section)) { - if (info->getFilename()) { - return info->getFilename(); + if (const char *imagePath = info->getImagePath()) { + return strdup(imagePath); } } - return ""; + return nullptr; +} + +SWIFT_RUNTIME_EXPORT +void swift_freeMetadataSectionName(char *name) { + free(name); } SWIFT_RUNTIME_EXPORT @@ -215,7 +219,7 @@ void swift_getMetadataSectionBaseAddress(const swift::MetadataSections *section, void const **out_actual, void const **out_expected) { if (auto info = swift::SymbolInfo::lookup(section)) { - *out_actual = info->getBaseAddress(); + *out_actual = info->getImageBaseAddress(); } else { *out_actual = nullptr; } diff --git a/stdlib/public/runtime/ImageInspectionCommon.h b/stdlib/public/runtime/ImageInspectionCommon.h index 5e7d765d22fc7..f8b90cfaff58e 100644 --- a/stdlib/public/runtime/ImageInspectionCommon.h +++ b/stdlib/public/runtime/ImageInspectionCommon.h @@ -89,8 +89,11 @@ void swift_enumerateAllMetadataSections( #ifndef NDEBUG SWIFT_RUNTIME_EXPORT -const char * -swift_getMetadataSectionName(const struct swift::MetadataSections *section); +char * +swift_copyMetadataSectionName(const struct swift::MetadataSections *section); + +SWIFT_RUNTIME_EXPORT +void swift_freeMetadataSectionName(char *name); SWIFT_RUNTIME_EXPORT void swift_getMetadataSectionBaseAddress( diff --git a/stdlib/public/runtime/ImageInspectionELF.cpp b/stdlib/public/runtime/ImageInspectionELF.cpp deleted file mode 100644 index 7d8c3970fccf5..0000000000000 --- a/stdlib/public/runtime/ImageInspectionELF.cpp +++ /dev/null @@ -1,25 +0,0 @@ -//===--- ImageInspectionELF.cpp - ELF image inspection --------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// -/// This file includes routines that interact with ld*.so on ELF-based platforms -/// to extract runtime metadata embedded in dynamically linked ELF images -/// generated by the Swift compiler. -/// -//===----------------------------------------------------------------------===// - -#if defined(__ELF__) - -#include "ImageInspection.h" - -#endif // defined(__ELF__) diff --git a/stdlib/public/runtime/ReflectionMirror.cpp b/stdlib/public/runtime/ReflectionMirror.cpp index 22351a58644f4..2279fb4bd60ae 100644 --- a/stdlib/public/runtime/ReflectionMirror.cpp +++ b/stdlib/public/runtime/ReflectionMirror.cpp @@ -1117,8 +1117,8 @@ id swift_reflectionMirror_quickLookObject(OpaqueValue *value, const Metadata *T) SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL const char *swift_keyPath_copySymbolName(void *address) { if (auto info = SymbolInfo::lookup(address)) { - if (info->getSymbolName()) { - return strdup(info->getSymbolName()); + if (const char *symbolName = info->getSymbolName()) { + return strdup(symbolName); } } return nullptr; diff --git a/stdlib/public/runtime/SymbolInfo.cpp b/stdlib/public/runtime/SymbolInfo.cpp index f51e46b02aa95..d0c41d9820cd7 100644 --- a/stdlib/public/runtime/SymbolInfo.cpp +++ b/stdlib/public/runtime/SymbolInfo.cpp @@ -1,8 +1,8 @@ -//===--- ImageInspectionWin32.cpp - Win32 image inspection ----------------===// +//===--- SymbolInfo.cpp ---------------------------------------------------===// // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -17,19 +17,55 @@ #define NOMINMAX #include #include +#include +#include #elif SWIFT_STDLIB_HAS_DLADDR #include #endif -#include "swift/Runtime/Win32.h" +#include +#include #include "ImageInspection.h" +#include "swift/Runtime/Win32.h" using namespace swift; -const char *SymbolInfo::getFilename() const { +SymbolInfo::SymbolInfo() #if defined(_WIN32) && !defined(__CYGWIN__) - return nullptr; +: _package({}), +#elif SWIFT_STDLIB_HAS_DLADDR +: _info({}), +#endif +_address(nullptr) +{ +} + +SymbolInfo::~SymbolInfo() { +#if defined(_WIN32) && !defined(__CYGWIN__) + if (char *imagePath = _imagePath.getValueOr(nullptr)) { + free(imagePath); + } +#endif +} + +const void *SymbolInfo::getAddress() const { + return _address; +} + +const char *SymbolInfo::getImagePath() const { +#if defined(_WIN32) && !defined(__CYGWIN__) + if (!_imagePath.has_value()) { + // Call GetMappedFileNameW() instead of SymGetModuleInfoW() because it + // doesn't require the use of the global DbgHelp lock and doesn't fetch + // other information we don't need. + std::array filename; + if (GetMappedFileNameW(GetCurrentProcess(), _address, filename.data(), filename.size())) { + _imagePath = _swift_win32_copyUTF8FromWide(filename.data()); + } + } + + return _imagePath.getValueOr(nullptr); #elif SWIFT_STDLIB_HAS_DLADDR return _info.dli_fname; #else @@ -37,7 +73,24 @@ const char *SymbolInfo::getFilename() const { #endif } -const void *SymbolInfo::getBaseAddress() const { +const char *SymbolInfo::getImageFilename() const { + // TODO: if we adopt C++17, consider using std::filesystem::path::filename() + const char *imagePath = getImagePath(); + if (!imagePath) { + return nullptr; + } +#if defined(_WIN32) && !defined(__CYGWIN__) + return PathFindFileNameA(imagePath); +#else + if (const char *lastSlash = strrchr(imagePath, '/')) { + return lastSlash + 1; + } else { + return imagePath; + } +#endif +} + +const void *SymbolInfo::getImageBaseAddress() const { #if defined(_WIN32) && !defined(__CYGWIN__) return reinterpret_cast(_package.si.ModBase); #elif SWIFT_STDLIB_HAS_DLADDR @@ -78,14 +131,25 @@ llvm::Optional SymbolInfo::lookup(const void *address) { #elif defined(_WIN32) && !defined(__CYGWIN__) _swift_win32_withDbgHelpLibrary([&] (HANDLE hProcess) { if (!hProcess) { - return; + return; } + // If there are real-world examples of symbol names with Unicode characters + // that we need to consider, SymbolInfo can hold a SYMBOL_INFO_PACKAGEW and + // call SymFromAddrW(), then lazily convert to UTF-8 on the first call to + // getSymbolName() as is done in getImagePath(). SymbolInfo info; info._package.si.SizeOfStruct = sizeof(SYMBOL_INFO); info._package.si.MaxNameLen = MAX_SYM_NAME; if (SymFromAddr(hProcess, reinterpret_cast(address), nullptr, &info._package.si)) { + // Symbols without debug information available, such as those from the OS, + // may not resolve a module base address. We can resolve it manually by + // calling SymGetModuleBase() here. + if (info._package.si.Address != 0 && info._package.si.ModBase == 0) { + info._package.si.ModBase = SymGetModuleBase(hProcess, + info._package.si.Address); + } result = info; } }); @@ -96,5 +160,10 @@ llvm::Optional SymbolInfo::lookup(const void *address) { } #endif + if (result.has_value()) { + result->_address = address; + } + return result; } + diff --git a/stdlib/public/runtime/SymbolInfo.h b/stdlib/public/runtime/SymbolInfo.h index c6868afe05a59..b347515c95623 100644 --- a/stdlib/public/runtime/SymbolInfo.h +++ b/stdlib/public/runtime/SymbolInfo.h @@ -1,22 +1,14 @@ -//===--- ImageInspection.h - Image inspection routines ----------*- C++ -*-===// +//===--- SymbolInfo.h -----------------------------------------------------===// // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// -/// -/// \file -/// -/// This file includes routines that extract metadata from executable and -/// dynamic library image files generated by the Swift compiler. The concrete -/// implementations vary greatly by platform. -/// -//===----------------------------------------------------------------------===// #ifndef SWIFT_RUNTIME_SYMBOLINFO_H #define SWIFT_RUNTIME_SYMBOLINFO_H @@ -44,27 +36,53 @@ struct SymbolInfo { private: #if defined(_WIN32) && !defined(__CYGWIN__) SYMBOL_INFO_PACKAGE _package; + mutable llvm::Optional _imagePath; #elif SWIFT_STDLIB_HAS_DLADDR Dl_info _info; #endif + const void *_address; + + SymbolInfo(); public: - SymbolInfo() {} + ~SymbolInfo(); + + /// Get the address that was originally passed when this instance was created. + const void *getAddress() const; + + /// Get the path to the image where the symbol was found. + /// + /// The image path uses the platform path convention. To consistently get just + /// the filename of the image, use \c getImageFilename(). + /// + /// The resulting C string is only valid for the lifetime of \c this. + const char *getImagePath() const; /// Get the file name of the image where the symbol was found. /// + /// The file name is the last path component of the image path. If the + /// filename cannot be determined from the image path (for instance, because + /// it is a relative path or was invalidly specified), the complete image path + /// is returned. + /// /// The resulting C string is only valid for the lifetime of \c this. - const char *getFilename() const; + const char *getImageFilename() const; /// Get the base address of the image where the symbol was found. - const void *getBaseAddress() const; + const void *getImageBaseAddress() const; /// Get the name of the symbol. /// + /// If the input address is valid within a loaded image, but does not + /// correspond to any known symbol, the resulting pointer may be \c nullptr. + /// /// The resulting C string is only valid for the lifetime of \c this. const char *getSymbolName() const; /// Get the address of the symbol. + /// + /// If the input address is valid within a loaded image, but does not + /// correspond to any known symbol, the resulting pointer may be \c nullptr. const void *getSymbolAddress() const; /// Look up a symbol by address. diff --git a/stdlib/public/runtime/Win32.cpp b/stdlib/public/runtime/Win32.cpp index 14eb627d052a1..28f6384f929d4 100644 --- a/stdlib/public/runtime/Win32.cpp +++ b/stdlib/public/runtime/Win32.cpp @@ -19,7 +19,12 @@ #ifdef _WIN32 -#include +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#include + +#pragma mark - Wide-character string conversion char * _swift_win32_copyUTF8FromWide(const wchar_t *str) { @@ -71,4 +76,61 @@ _swift_win32_copyWideFromUTF8(const char *str) { return nullptr; } +#pragma mark - DbgHelp library thread-safety + +static LazyMutex dbgHelpMutex; +static HANDLE dbgHelpHandle = nullptr; + +void _swift_win32_withDbgHelpLibrary( + void (* body)(HANDLE hProcess, void *context), void *context) { + dbgHelpMutex.withLock([=] () { + // If we have not previously created a handle to use with the library, do so + // now. This handle belongs to the Swift runtime and should not be closed by + // `body` (or anybody else.) + if (!dbgHelpHandle) { + // Per the documentation for the Debug Help library, we should not use the + // current process handle because other subsystems might also use it and + // end up stomping on each other. So we'll try to duplicate that handle to + // get a unique one that still fulfills the needs of the library. If that + // fails (presumably because the current process doesn't have the + // PROCESS_DUP_HANDLE access right) then fall back to using the original + // process handle and hope nobody else is using it too. + HANDLE currentProcess = GetCurrentProcess(); + if (!DuplicateHandle(currentProcess, currentProcess, currentProcess, + &dbgHelpHandle, 0, false, DUPLICATE_SAME_ACCESS)) { + dbgHelpHandle = currentProcess; + } + } + + // If we have not previously initialized the Debug Help library, do so now. + bool isDbgHelpInitialized = false; + if (dbgHelpHandle) { + isDbgHelpInitialized = SymInitialize(dbgHelpHandle, nullptr, true); + } + + if (isDbgHelpInitialized) { + // Set the library's options to what the Swift runtime generally expects. + // If the options aren't going to change, we can skip the call and save a + // few CPU cycles on the library call. + constexpr const DWORD options = SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS; + DWORD oldOptions = SymGetOptions(); + if (oldOptions != options) { + SymSetOptions(options); + } + + body(dbgHelpHandle, context); + + // Before returning, reset the library's options back to their previous + // value. No need to call if the options didn't change because LazyMutex + // is not recursive, so there shouldn't be an outer call expecting the + // original options, and a subsequent call to this function will set them + // to the defaults above. + if (oldOptions != options) { + SymSetOptions(oldOptions); + } + } else { + body(nullptr, context); + } + }); +} #endif // defined(_WIN32) diff --git a/test/Runtime/loaded_image_uniqueness.swift b/test/Runtime/loaded_image_uniqueness.swift index f6c81dc81e4f0..04024f946cd75 100644 --- a/test/Runtime/loaded_image_uniqueness.swift +++ b/test/Runtime/loaded_image_uniqueness.swift @@ -12,10 +12,15 @@ internal func _getMetadataSection(_ index: UInt) -> UnsafeRawPointer? @_silgen_name("swift_getMetadataSectionCount") internal func _getMetadataSectionCount() -> UInt -@_silgen_name("swift_getMetadataSectionName") -internal func _getMetadataSectionName( - _ metadata_section: UnsafeRawPointer -) -> UnsafePointer +@_silgen_name("swift_copyMetadataSectionName") +internal func _copyMetadataSectionName( + _ metadata_section: UnsafeRawPointer +) -> UnsafeMutablePointer? + +@_silgen_name("swift_freeMetadataSectionName") +internal func _freeMetadataSectionName( + _ name: UnsafeMutablePointer +) -> Void @_silgen_name("swift_getMetadataSectionBaseAddress") internal func _getMetadataSectionBaseAddress( @@ -34,7 +39,11 @@ do { guard let section = _getMetadataSection(i) else { fatalError("Section \(i) failed to resolve.") } - let name = String(cString: _getMetadataSectionName(section)) + let cName = _copyMetadataSectionName(rawPointer) + defer { + _freeMetadataSectionName(cName) + } + let name = cName.flatMap { String(validatingUTF8: $0) } ?? "" var actual: UnsafeRawPointer? = nil var expected: UnsafeRawPointer? = nil From d0dd958ce85da4c9b6588e3361d896c861459ba3 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Sat, 7 Jan 2023 00:02:11 -0500 Subject: [PATCH 2/2] GetMappedFileNameW() wants a non-const input pointer --- stdlib/public/runtime/SymbolInfo.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stdlib/public/runtime/SymbolInfo.cpp b/stdlib/public/runtime/SymbolInfo.cpp index d0c41d9820cd7..4024cef677120 100644 --- a/stdlib/public/runtime/SymbolInfo.cpp +++ b/stdlib/public/runtime/SymbolInfo.cpp @@ -60,7 +60,8 @@ const char *SymbolInfo::getImagePath() const { // doesn't require the use of the global DbgHelp lock and doesn't fetch // other information we don't need. std::array filename; - if (GetMappedFileNameW(GetCurrentProcess(), _address, filename.data(), filename.size())) { + if (GetMappedFileNameW(GetCurrentProcess(), const_cast(_address), + filename.data(), filename.size())) { _imagePath = _swift_win32_copyUTF8FromWide(filename.data()); } }