Skip to content

Commit 5d3e8cb

Browse files
committed
Implement SymbolInfo::getFilename() on Win32
1 parent c889270 commit 5d3e8cb

File tree

12 files changed

+208
-82
lines changed

12 files changed

+208
-82
lines changed

include/swift/Runtime/Win32Strings.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//===--- Win32Strings.h ---------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Includes conversion functions for use with Windows API that produces or
14+
// consumes wide strings (wchar_t) which, on Windows, are UTF-16.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#ifndef SWIFT_RUNTIME_WIN32STRINGS_H
19+
#define SWIFT_RUNTIME_WIN32STRINGS_H
20+
21+
#if defined(_WIN32)
22+
namespace swift {
23+
/// Convert an argument, represented by a wide string, to UTF-8.
24+
///
25+
/// @param str The string to convert.
26+
///
27+
/// @returns The string, converted to UTF-8. The caller is responsible for
28+
/// freeing this string when done with it.
29+
///
30+
/// If @a str cannot be converted to UTF-8, @c nullptr is returned.
31+
SWIFT_RUNTIME_STDLIB_INTERNAL
32+
char *swift_copyUTF8FromWide(const wchar_t *str);
33+
}
34+
#endif
35+
#endif

stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,10 @@ internal func _getMetadataSection(_ index: UInt) -> UnsafeRawPointer?
138138
@_silgen_name("swift_getMetadataSectionCount")
139139
internal func _getMetadataSectionCount() -> UInt
140140

141-
@_silgen_name("swift_getMetadataSectionName")
142-
internal func _getMetadataSectionName(
141+
@_silgen_name("swift_copyMetadataSectionName")
142+
internal func _copyMetadataSectionName(
143143
_ metadata_section: UnsafeRawPointer
144-
) -> UnsafePointer<CChar>
144+
) -> UnsafeMutablePointer<CChar>?
145145
#endif
146146

147147
extension Section {
@@ -154,9 +154,13 @@ extension Section {
154154
internal func getReflectionInfoForImage(atIndex i: UInt32) -> ReflectionInfo? {
155155
#if INTERNAL_CHECKS_ENABLED
156156
return _getMetadataSection(UInt(i)).map { rawPointer in
157-
let name = _getMetadataSectionName(rawPointer)
157+
let cName = _copyMetadataSectionName(rawPointer)
158+
defer {
159+
cName.deallocate()
160+
}
161+
let name = cName.flatMap { String(validatingUTF8: $0) } ?? "<unavailable>"
158162
let metadataSection = rawPointer.bindMemory(to: MetadataSections.self, capacity: 1).pointee
159-
return ReflectionInfo(imageName: String(validatingUTF8: name)!,
163+
return ReflectionInfo(imageName: name,
160164
fieldmd: Section(range: metadataSection.swift5_fieldmd),
161165
assocty: Section(range: metadataSection.swift5_assocty),
162166
builtin: Section(range: metadataSection.swift5_builtin),

stdlib/public/CommandLineSupport/CommandLine.cpp

Lines changed: 8 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <string>
2626

2727
#include "swift/Runtime/Debug.h"
28+
#include "swift/Runtime/Win32Strings.h"
2829

2930
#include "swift/shims/GlobalObjects.h"
3031
#include "swift/shims/RuntimeStubs.h"
@@ -193,48 +194,6 @@ static void swift::enumerateUnsafeArgv(const F& body) {
193194
#elif defined(_WIN32)
194195
#include <stdlib.h>
195196

196-
namespace swift {
197-
/// Convert an argument, represented by a wide string, to UTF-8.
198-
///
199-
/// @param str The string to convert.
200-
///
201-
/// @returns The string, converted to UTF-8. The caller is responsible for
202-
/// freeing this string when done with it.
203-
///
204-
/// If @a str cannot be converted to UTF-8, a fatal error occurs.
205-
static char *copyUTF8FromWide(wchar_t *str) {
206-
char *result = nullptr;
207-
208-
int resultLength = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, str,
209-
-1, nullptr, 0, nullptr, nullptr);
210-
if (resultLength <= 0) {
211-
swift::fatalError(0,
212-
"Fatal error: Could not get length of commandline "
213-
"argument '%ls': %lu\n",
214-
str, GetLastError());
215-
}
216-
217-
result = reinterpret_cast<char *>(malloc(resultLength));
218-
if (!result) {
219-
swift::fatalError(0,
220-
"Fatal error: Could not allocate space for commandline "
221-
"argument '%ls': %d\n",
222-
str, errno);
223-
}
224-
225-
resultLength = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, str, -1,
226-
result, resultLength, nullptr, nullptr);
227-
if (resultLength <= 0) {
228-
swift::fatalError(0,
229-
"Fatal error: Conversion to UTF-8 failed for "
230-
"commandline argument '%ls': %lu\n",
231-
str, GetLastError());
232-
}
233-
234-
return result;
235-
}
236-
}
237-
238197
static char **swift::getUnsafeArgvArgc(int *outArgLen) {
239198
return nullptr;
240199
}
@@ -244,7 +203,13 @@ static void swift::enumerateUnsafeArgv(const F& body) {
244203
int argc = 0;
245204
if (LPWSTR *wargv = CommandLineToArgvW(GetCommandLineW(), &argc)) {
246205
std::for_each(wargv, wargv + argc, [=] (wchar_t *warg) {
247-
auto arg = copyUTF8FromWide(warg);
206+
auto arg = swift_copyUTF8FromWide(warg);
207+
if (!arg) {
208+
swift::fatalError(0,
209+
"Fatal error: Could not convert commandline argument "
210+
"'%ls' to UTF-8: Windows error %lu, C error %d.\n",
211+
warg, GetLastError(), errno);
212+
}
248213
body(argc, arg);
249214
free(arg);
250215
});

stdlib/public/runtime/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ set(swift_runtime_sources
6969
SwiftDtoa.cpp
7070
SwiftTLSContext.cpp
7171
ThreadingError.cpp
72-
AccessibleFunction.cpp)
72+
AccessibleFunction.cpp
73+
Win32Strings.cpp)
7374

7475
# Acknowledge that the following sources are known.
7576
set(LLVM_OPTIONAL_SOURCES

stdlib/public/runtime/Errors.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,12 +149,15 @@ void swift::dumpStackTraceEntry(unsigned index, void *framePC,
149149
return;
150150
}
151151

152-
// If SymbolInfo:lookup succeeded then fileName is non-null. Thus, we find the
152+
// If SymbolInfo:lookup succeeded and fileName is non-null, we can find the
153153
// library name here. Avoid using StringRef::rsplit because its definition
154154
// is not provided in the header so that it requires linking with
155155
// libSupport.a.
156-
llvm::StringRef libraryName{syminfo->getFilename()};
157-
libraryName = libraryName.substr(libraryName.rfind('/')).substr(1);
156+
llvm::StringRef libraryName{"<unavailable>"};
157+
if (const char *filename = syminfo->getFilename()) {
158+
libraryName = filename;
159+
libraryName = libraryName.substr(libraryName.rfind('/')).substr(1);
160+
}
158161

159162
// Next we get the symbol name that we are going to use in our backtrace.
160163
std::string symbolName;
@@ -168,7 +171,7 @@ void swift::dumpStackTraceEntry(unsigned index, void *framePC,
168171
if (foundSymbol) {
169172
offset = ptrdiff_t(uintptr_t(framePC) - symbolAddr);
170173
} else {
171-
auto baseAddress = syminfo->getBaseAddress();
174+
const void *baseAddress = syminfo->getBaseAddress();
172175
offset = ptrdiff_t(uintptr_t(framePC) - uintptr_t(baseAddress));
173176
symbolAddr = uintptr_t(framePC);
174177
symbolName = "<unavailable>";

stdlib/public/runtime/ImageInspectionCommon.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,10 @@ const swift::MetadataSections *swift_getMetadataSection(size_t index) {
188188
}
189189

190190
SWIFT_RUNTIME_EXPORT
191-
const char *
192-
swift_getMetadataSectionName(const swift::MetadataSections *section) {
191+
char *swift_copyMetadataSectionName(const swift::MetadataSections *section) {
193192
if (auto info = SymbolInfo::lookup(section)) {
194193
if (info->getFilename()) {
195-
return info->getFilename();
194+
return strdup(info->getFilename());
196195
}
197196
}
198197
return "";

stdlib/public/runtime/ImageInspectionCommon.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ void swift_enumerateAllMetadataSections(
8686
#ifndef NDEBUG
8787

8888
SWIFT_RUNTIME_EXPORT
89-
const char *
90-
swift_getMetadataSectionName(const struct swift::MetadataSections *section);
89+
char *
90+
swift_copyMetadataSectionName(const struct swift::MetadataSections *section);
9191

9292
SWIFT_RUNTIME_EXPORT
9393
void swift_getMetadataSectionBaseAddress(

stdlib/public/runtime/ReflectionMirror.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,10 +1116,9 @@ id swift_reflectionMirror_quickLookObject(OpaqueValue *value, const Metadata *T)
11161116

11171117
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL
11181118
const char *swift_keyPath_copySymbolName(void *address) {
1119-
if (auto info = SymbolInfo::lookup(address)) {
1120-
if (info->getSymbolName()) {
1121-
return strdup(info->getSymbolName());
1122-
}
1119+
auto info = SymbolInfo::lookup(address);
1120+
if (info.has_value() && info->getSymbolName()) {
1121+
return strdup(info->getSymbolName());
11231122
}
11241123
return nullptr;
11251124
}

stdlib/public/runtime/SymbolInfo.cpp

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
//===--- ImageInspectionWin32.cpp - Win32 image inspection ----------------===//
1+
//===--- SymbolInfo.cpp ---------------------------------------------------===//
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -17,17 +17,62 @@
1717
#define NOMINMAX
1818
#include <Windows.h>
1919
#include <DbgHelp.h>
20+
#include <Psapi.h>
2021
#elif SWIFT_STDLIB_HAS_DLADDR
2122
#include <dlfcn.h>
2223
#endif
2324

25+
#include <array>
26+
#include <cstdlib>
27+
2428
#include "ImageInspection.h"
29+
#include "swift/Runtime/Win32Strings.h"
2530

2631
using namespace swift;
2732

33+
SymbolInfo::SymbolInfo()
34+
#if defined(_WIN32) && !defined(__CYGWIN__)
35+
: _package({}),
36+
#elif SWIFT_STDLIB_HAS_DLADDR
37+
: _info({}),
38+
#endif
39+
_address(nullptr)
40+
{
41+
}
42+
43+
SymbolInfo::~SymbolInfo() {
44+
#if defined(_WIN32) && !defined(__CYGWIN__)
45+
if (char *filename = _filename.getValueOr(nullptr)) {
46+
free(filename);
47+
}
48+
#endif
49+
}
50+
51+
const void *SymbolInfo::getAddress() const {
52+
return _address;
53+
}
54+
2855
const char *SymbolInfo::getFilename() const {
2956
#if defined(_WIN32) && !defined(__CYGWIN__)
30-
return nullptr;
57+
if (!_filename.has_value()) {
58+
// On Win32/Win64, the base address of a loaded image and its HMODULE are
59+
// the same pointer, so we can simply cast from one to the other.
60+
HMODULE hModule = reinterpret_cast<HMODULE>(getBaseAddress());
61+
if (!hModule) {
62+
_filename = nullptr;
63+
return;
64+
}
65+
66+
// Call GetModuleFileNameW() instead of SymGetModuleInfoW() because it
67+
// doesn't require the use of the global DbgHelp lock and doesn't fetch
68+
// other information we don't need.
69+
std::array<wchar_t, MAX_PATH> filename;
70+
if (GetModuleFileNameW(hModule, filename.data(), filename.size())) {
71+
_filename = swift_copyUTF8FromWide(filename.data());
72+
}
73+
}
74+
75+
return _filename.getValueOr(nullptr);
3176
#elif SWIFT_STDLIB_HAS_DLADDR
3277
return _info.dli_fname;
3378
#else
@@ -76,14 +121,25 @@ llvm::Optional<SymbolInfo> SymbolInfo::lookup(const void *address) {
76121
#elif defined(_WIN32) && !defined(__CYGWIN__)
77122
_swift_win32_withDbgHelpLibrary([&] (HANDLE hProcess) {
78123
if (!hProcess) {
79-
return;
124+
return;
80125
}
81126

127+
// If there are real-world examples of symbol names with Unicode characters
128+
// that we need to consider, SymbolInfo can hold a SYMBOL_INFO_PACKAGEW and
129+
// call SymFromAddrW(), then lazily convert to UTF-8 on the first call to
130+
// getSymbolName() as is done in getFilename().
82131
SymbolInfo info;
83132
info._package.si.SizeOfStruct = sizeof(SYMBOL_INFO);
84133
info._package.si.MaxNameLen = MAX_SYM_NAME;
85134
if (SymFromAddr(hProcess, reinterpret_cast<const DWORD64>(address),
86135
nullptr, &info._package.si)) {
136+
// Symbols without debug information available, such as those from the OS,
137+
// may not resolve a module base address. We can resolve it manually by
138+
// calling SymGetModuleBase() here.
139+
if (info._package.si.Address != 0 && info._package.si.ModBase == 0) {
140+
info._package.si.ModBase = SymGetModuleBase(hProcess,
141+
info._package.si.Address);
142+
}
87143
result = info;
88144
}
89145
});
@@ -94,5 +150,10 @@ llvm::Optional<SymbolInfo> SymbolInfo::lookup(const void *address) {
94150
}
95151
#endif
96152

153+
if (result.has_value()) {
154+
result->_address = address;
155+
}
156+
97157
return result;
98158
}
159+

stdlib/public/runtime/SymbolInfo.h

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
1-
//===--- ImageInspection.h - Image inspection routines ----------*- C++ -*-===//
1+
//===--- SymbolInfo.h -----------------------------------------------------===//
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
12-
///
13-
/// \file
14-
///
15-
/// This file includes routines that extract metadata from executable and
16-
/// dynamic library image files generated by the Swift compiler. The concrete
17-
/// implementations vary greatly by platform.
18-
///
19-
//===----------------------------------------------------------------------===//
2012

2113
#ifndef SWIFT_RUNTIME_SYMBOLINFO_H
2214
#define SWIFT_RUNTIME_SYMBOLINFO_H
@@ -44,13 +36,20 @@ struct SymbolInfo {
4436
private:
4537
#if defined(_WIN32) && !defined(__CYGWIN__)
4638
SYMBOL_INFO_PACKAGE _package;
39+
mutable llvm::Optional<char *> _filename;
4740
#elif SWIFT_STDLIB_HAS_DLADDR
4841
Dl_info _info;
4942
#endif
43+
const void *_address;
5044

51-
public:
5245
SymbolInfo() {}
5346

47+
public:
48+
~SymbolInfo();
49+
50+
/// Get the address that was originally passed when this instance was created.
51+
const void *getAddress() const;
52+
5453
/// Get the file name of the image where the symbol was found.
5554
///
5655
/// The resulting C string is only valid for the lifetime of \c this.
@@ -61,10 +60,16 @@ struct SymbolInfo {
6160

6261
/// Get the name of the symbol.
6362
///
63+
/// If the input address is valid within a loaded library, but does not
64+
/// correspond to any known symbol, the resulting pointer may be \c nullptr.
65+
///
6466
/// The resulting C string is only valid for the lifetime of \c this.
6567
const char *getSymbolName() const;
6668

6769
/// Get the address of the symbol.
70+
///
71+
/// If the input address is valid within a loaded library, but does not
72+
/// correspond to any known symbol, the resulting pointer may be \c nullptr.
6873
const void *getSymbolAddress() const;
6974

7075
/// Look up a symbol by address.

0 commit comments

Comments
 (0)