Skip to content

Commit 93eb4ed

Browse files
authored
Specify the module to find the ABI entry point function in (in our own tests). (#801)
The order in which Xcode loads the Apple-vendored copy of Swift Testing vs. a developer-supplied XCTest bundle is unspecified. Our tests assume that the "current" copy of Swift Testing (that is, the copy they linked against) is the first one loaded, which is not always true when running tests in Xcode. The modified tests end up finding the wrong entry point functions when called within Xcode if the load order has changed. This PR explicitly specifies that we are looking for the entry point functions in the module the tests are linked against. The change is applied across all platforms that support dynamic linking for the sake of consistency as any of them might have linked a copy of Swift Testing from the toolchain _and_ the package itself. Resolves rdar://139140302. ### Checklist: - [x] Code and documentation should follow the style of the [Style Guide](https://github.com/apple/swift-testing/blob/main/Documentation/StyleGuide.md). - [x] If public symbols are renamed or modified, DocC references should be updated.
1 parent 759ce02 commit 93eb4ed

File tree

1 file changed

+46
-10
lines changed

1 file changed

+46
-10
lines changed

Tests/TestingTests/ABIEntryPointTests.swift

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,13 @@ struct ABIEntryPointTests {
5353
recordHandler: @escaping @Sendable (_ recordJSON: UnsafeRawBufferPointer) -> Void = { _ in }
5454
) async throws -> CInt {
5555
// Get the ABI entry point by dynamically looking it up at runtime.
56-
let copyABIEntryPoint_v0 = try #require(
57-
symbol(named: "swt_copyABIEntryPoint_v0").map {
58-
unsafeBitCast($0, to: (@convention(c) () -> UnsafeMutableRawPointer).self)
59-
}
60-
)
56+
let copyABIEntryPoint_v0 = try withTestingLibraryImageAddress { testingLibrary in
57+
try #require(
58+
symbol(in: testingLibrary, named: "swt_copyABIEntryPoint_v0").map {
59+
unsafeBitCast($0, to: (@convention(c) () -> UnsafeMutableRawPointer).self)
60+
}
61+
)
62+
}
6163
let abiEntryPoint = copyABIEntryPoint_v0().assumingMemoryBound(to: ABIEntryPoint_v0.self)
6264
defer {
6365
abiEntryPoint.deinitialize(count: 1)
@@ -133,11 +135,13 @@ struct ABIEntryPointTests {
133135
// NOTE: The standard Linux linker does not allow exporting symbols from
134136
// executables, so dlsym() does not let us find this function on that
135137
// platform when built as an executable rather than a dynamic library.
136-
let abiv0_getEntryPoint = try #require(
137-
symbol(named: "swt_abiv0_getEntryPoint").map {
138-
unsafeBitCast($0, to: (@convention(c) () -> UnsafeRawPointer).self)
139-
}
140-
)
138+
let abiv0_getEntryPoint = try withTestingLibraryImageAddress { testingLibrary in
139+
try #require(
140+
symbol(in: testingLibrary, named: "swt_abiv0_getEntryPoint").map {
141+
unsafeBitCast($0, to: (@convention(c) () -> UnsafeRawPointer).self)
142+
}
143+
)
144+
}
141145
#endif
142146
let abiEntryPoint = unsafeBitCast(abiv0_getEntryPoint(), to: ABIv0.EntryPoint.self)
143147

@@ -163,4 +167,36 @@ struct ABIEntryPointTests {
163167
}
164168
#endif
165169
}
170+
171+
#if !SWT_NO_DYNAMIC_LINKING
172+
private func withTestingLibraryImageAddress<R>(_ body: (ImageAddress?) throws -> R) throws -> R {
173+
let addressInTestingLibrary = unsafeBitCast(ABIv0.entryPoint, to: UnsafeRawPointer.self)
174+
175+
var testingLibraryAddress: ImageAddress?
176+
#if SWT_TARGET_OS_APPLE
177+
var info = Dl_info()
178+
try #require(0 != dladdr(addressInTestingLibrary, &info))
179+
180+
testingLibraryAddress = dlopen(info.dli_fname, RTLD_NOLOAD)
181+
try #require(testingLibraryAddress != nil)
182+
defer {
183+
dlclose(testingLibraryAddress)
184+
}
185+
#elseif os(Linux) || os(FreeBSD) || os(Android)
186+
// When using glibc, dladdr() is only available if __USE_GNU is specified.
187+
#elseif os(Windows)
188+
let flags = DWORD(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS)
189+
try addressInTestingLibrary.withMemoryRebound(to: wchar_t.self, capacity: MemoryLayout<UnsafeRawPointer>.stride / MemoryLayout<wchar_t>.stride) { addressInTestingLibrary in
190+
try #require(GetModuleHandleExW(flags, addressInTestingLibrary, &testingLibraryAddress))
191+
}
192+
defer {
193+
FreeLibrary(testingLibraryAddress)
194+
}
195+
#else
196+
#warning("Platform-specific implementation missing: cannot find the testing library image the test suite is linked against")
197+
#endif
198+
199+
return try body(testingLibraryAddress)
200+
}
201+
#endif
166202
#endif

0 commit comments

Comments
 (0)