From 80be5a0eecadcb554e1d0c07abaf78a4010aabb5 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Fri, 10 Jan 2025 20:37:55 -0500 Subject: [PATCH 1/8] Remove some remnants of the C++ section discovery code that we no longer need. This PR removes additional C++ section discovery code that is no longer needed. --- Sources/Testing/Test+Discovery+Legacy.swift | 20 ++-- Sources/_TestingInternals/Discovery.cpp | 105 +++++------------- Sources/_TestingInternals/include/Discovery.h | 2 +- 3 files changed, 35 insertions(+), 92 deletions(-) diff --git a/Sources/Testing/Test+Discovery+Legacy.swift b/Sources/Testing/Test+Discovery+Legacy.swift index d330104ef..e24fca55d 100644 --- a/Sources/Testing/Test+Discovery+Legacy.swift +++ b/Sources/Testing/Test+Discovery+Legacy.swift @@ -57,15 +57,13 @@ let exitTestContainerTypeNameMagic = "__🟠$exit_test_body__" /// /// - Returns: A sequence of Swift types whose names contain `nameSubstring`. func types(withNamesContaining nameSubstring: String) -> some Sequence { - SectionBounds.all(.typeMetadata).lazy - .map { sb in - var count = 0 - let start = swt_copyTypes(in: sb.buffer.baseAddress!, sb.buffer.count, withNamesContaining: nameSubstring, count: &count) - defer { - free(start) - } - return start.withMemoryRebound(to: Any.Type.self, capacity: count) { start in - Array(UnsafeBufferPointer(start: start, count: count)) - } - }.joined() + SectionBounds.all(.typeMetadata).lazy.flatMap { sb in + var count = 0 + let start = swt_copyTypes(in: sb.buffer.baseAddress!, sb.buffer.count, withNamesContaining: nameSubstring, count: &count) + defer { + free(start) + } + return UnsafeBufferPointer(start: start, count: count) + .withMemoryRebound(to: Any.Type.self) { Array($0) } + } } diff --git a/Sources/_TestingInternals/Discovery.cpp b/Sources/_TestingInternals/Discovery.cpp index 08ffc5f47..2b968cca3 100644 --- a/Sources/_TestingInternals/Discovery.cpp +++ b/Sources/_TestingInternals/Discovery.cpp @@ -48,86 +48,20 @@ const void *_Nonnull const SWTTypeMetadataSectionBounds[2] = { #pragma mark - Legacy test discovery -#include -#include -#include -#include -#include +#include +#include #include -#include #include #include -#include - -/// Enumerate over all Swift type metadata sections in the current process. -/// -/// - Parameters: -/// - body: A function to call once for every section in the current process. -/// A pointer to the first type metadata record and the number of records -/// are passed to this function. -template -static void enumerateTypeMetadataSections(const SectionEnumerator& body); - -/// A type that acts as a C++ [Allocator](https://en.cppreference.com/w/cpp/named_req/Allocator) -/// without using global `operator new` or `operator delete`. -/// -/// This type is necessary because global `operator new` and `operator delete` -/// can be overridden in developer-supplied code and cause deadlocks or crashes -/// when subsequently used while holding a dyld- or libobjc-owned lock. Using -/// `std::malloc()` and `std::free()` allows the use of C++ container types -/// without this risk. -template -struct SWTHeapAllocator { - using value_type = T; - - T *allocate(size_t count) { - return reinterpret_cast(std::calloc(count, sizeof(T))); - } - - void deallocate(T *ptr, size_t count) { - std::free(ptr); - } -}; - -/// A structure describing the bounds of a Swift metadata section. -/// -/// The template argument `T` is the element type of the metadata section. -/// Instances of this type can be used with a range-based `for`-loop to iterate -/// the contents of the section. -template -struct SWTSectionBounds { - /// The base address of the image containing the section, if known. - const void *imageAddress; - - /// The base address of the section. - const void *start; - - /// The size of the section in bytes. - size_t size; - - const struct SWTTypeMetadataRecord *begin(void) const { - return reinterpret_cast(start); - } - - const struct SWTTypeMetadataRecord *end(void) const { - return reinterpret_cast(reinterpret_cast(start) + size); - } -}; - -/// A type that acts as a C++ [Container](https://en.cppreference.com/w/cpp/named_req/Container) -/// and which contains a sequence of instances of `SWTSectionBounds`. -template -using SWTSectionBoundsList = std::vector, SWTHeapAllocator>>; #pragma mark - Swift ABI #if defined(__PTRAUTH_INTRINSICS__) #include -#define SWT_PTRAUTH __ptrauth +#define SWT_PTRAUTH_SWIFT_TYPE_DESCRIPTOR __ptrauth(ptrauth_key_process_independent_data, 1, 0xae86) #else -#define SWT_PTRAUTH(...) +#define SWT_PTRAUTH_SWIFT_TYPE_DESCRIPTOR #endif -#define SWT_PTRAUTH_SWIFT_TYPE_DESCRIPTOR SWT_PTRAUTH(ptrauth_key_process_independent_data, 1, 0xae86) /// A type representing a pointer relative to itself. /// @@ -273,10 +207,16 @@ struct SWTTypeMetadataRecord { #pragma mark - void **swt_copyTypesWithNamesContaining(const void *sectionBegin, size_t sectionSize, const char *nameSubstring, size_t *outCount) { - SWTSectionBounds sb = { nullptr, sectionBegin, sectionSize }; - std::vector> result; + auto records = reinterpret_cast(sectionBegin); + size_t recordCount = sectionSize / sizeof(SWTTypeMetadataRecord); + + // The buffer we'll return and how many types we've placed in it. (We only + // allocate the buffer if at least one type in the section matches.) + void **result = nullptr; + auto resultCount = 0; - for (const auto& record : sb) { + for (size_t i = 0; i < recordCount; i++) { + const auto& record = records[i]; auto contextDescriptor = record.getContextDescriptor(); if (!contextDescriptor) { // This type metadata record is invalid (or we don't understand how to @@ -297,14 +237,19 @@ void **swt_copyTypesWithNamesContaining(const void *sectionBegin, size_t section } if (void *typeMetadata = contextDescriptor->getMetadata()) { - result.push_back(typeMetadata); + if (!result) { + // This is the first matching type we've found. That presumably means + // we'll find more, so allocate enough space for all remaining types in + // the section. Is this necessarily space-efficient? No, but this + // allocation is short-lived and is immediately copied and freed in the + // Swift caller. + result = reinterpret_cast(std::calloc(recordCount - i, sizeof(void *))); + } + result[resultCount] = typeMetadata; + resultCount += 1; } } - auto resultCopy = reinterpret_cast(std::calloc(sizeof(void *), result.size())); - if (resultCopy) { - std::uninitialized_move(result.begin(), result.end(), resultCopy); - *outCount = result.size(); - } - return resultCopy; + *outCount = resultCount; + return result; } diff --git a/Sources/_TestingInternals/include/Discovery.h b/Sources/_TestingInternals/include/Discovery.h index 2cfd63339..b0cfe070a 100644 --- a/Sources/_TestingInternals/include/Discovery.h +++ b/Sources/_TestingInternals/include/Discovery.h @@ -96,7 +96,7 @@ SWT_EXTERN const void *_Nonnull const SWTTypeMetadataSectionBounds[2]; /// /// - Returns: A pointer to an array of type metadata pointers. The caller is /// responsible for freeing this memory with `free()` when done. -SWT_EXTERN void *_Nonnull *_Nonnull swt_copyTypesWithNamesContaining( +SWT_EXTERN void *_Nonnull *_Nullable swt_copyTypesWithNamesContaining( const void *sectionBegin, size_t sectionSize, const char *nameSubstring, From 980fccef173bdbecb447193df6f094b0107f289a Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Fri, 10 Jan 2025 20:45:25 -0500 Subject: [PATCH 2/8] We don't use these member operators, drop them too --- Sources/_TestingInternals/Discovery.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Sources/_TestingInternals/Discovery.cpp b/Sources/_TestingInternals/Discovery.cpp index 2b968cca3..c20aad1d5 100644 --- a/Sources/_TestingInternals/Discovery.cpp +++ b/Sources/_TestingInternals/Discovery.cpp @@ -98,10 +98,6 @@ struct SWTRelativePointer { #endif return reinterpret_cast(result); } - - const T *_Nullable operator ->(void) const& { - return get(); - } }; /// A type representing a 32-bit absolute function pointer, usually used on platforms @@ -118,10 +114,6 @@ struct SWTAbsoluteFunctionPointer { const T *_Nullable get(void) const & { return _pointer; } - - const T *_Nullable operator ->(void) const & { - return get(); - } }; /// A type representing a pointer relative to itself with low bits reserved for From b11f70257ebbe7551d1b4d245fc5c4fb9adfc171 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Fri, 10 Jan 2025 20:50:38 -0500 Subject: [PATCH 3/8] Missing std header --- Sources/_TestingInternals/Discovery.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/_TestingInternals/Discovery.cpp b/Sources/_TestingInternals/Discovery.cpp index c20aad1d5..d163dd8d7 100644 --- a/Sources/_TestingInternals/Discovery.cpp +++ b/Sources/_TestingInternals/Discovery.cpp @@ -50,6 +50,7 @@ const void *_Nonnull const SWTTypeMetadataSectionBounds[2] = { #include #include +#include #include #include #include From b7e3c67dfd8884eb237f624695bf0968fe9203c1 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Sun, 12 Jan 2025 11:06:09 -0500 Subject: [PATCH 4/8] A bit of refactoring, don't need anymore while we're here --- Sources/_TestingInternals/Discovery.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/Sources/_TestingInternals/Discovery.cpp b/Sources/_TestingInternals/Discovery.cpp index d163dd8d7..42c0a7817 100644 --- a/Sources/_TestingInternals/Discovery.cpp +++ b/Sources/_TestingInternals/Discovery.cpp @@ -10,6 +10,12 @@ #include "Discovery.h" +#include +#include +#include +#include +#include + #if defined(SWT_NO_DYNAMIC_LINKING) #pragma mark - Statically-linked section bounds @@ -46,15 +52,6 @@ const void *_Nonnull const SWTTypeMetadataSectionBounds[2] = { }; #endif -#pragma mark - Legacy test discovery - -#include -#include -#include -#include -#include -#include - #pragma mark - Swift ABI #if defined(__PTRAUTH_INTRINSICS__) @@ -197,7 +194,7 @@ struct SWTTypeMetadataRecord { } }; -#pragma mark - +#pragma mark - Legacy test discovery void **swt_copyTypesWithNamesContaining(const void *sectionBegin, size_t sectionSize, const char *nameSubstring, size_t *outCount) { auto records = reinterpret_cast(sectionBegin); @@ -206,7 +203,7 @@ void **swt_copyTypesWithNamesContaining(const void *sectionBegin, size_t section // The buffer we'll return and how many types we've placed in it. (We only // allocate the buffer if at least one type in the section matches.) void **result = nullptr; - auto resultCount = 0; + size_t resultCount = 0; for (size_t i = 0; i < recordCount; i++) { const auto& record = records[i]; From 0b12286969d3db4439fa4bc6272de246e78af215 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 13 Jan 2025 09:19:19 -0500 Subject: [PATCH 5/8] Bit more cheese --- Sources/_TestingInternals/Discovery.cpp | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/Sources/_TestingInternals/Discovery.cpp b/Sources/_TestingInternals/Discovery.cpp index 42c0a7817..f66eaf874 100644 --- a/Sources/_TestingInternals/Discovery.cpp +++ b/Sources/_TestingInternals/Discovery.cpp @@ -37,25 +37,18 @@ static const char typeMetadataSectionBegin = 0; static const char& typeMetadataSectionEnd = typeMetadataSectionBegin; #endif -/// The bounds of the test content section statically linked into the image -/// containing Swift Testing. const void *_Nonnull const SWTTestContentSectionBounds[2] = { - &testContentSectionBegin, - &testContentSectionEnd + &testContentSectionBegin, &testContentSectionEnd }; -/// The bounds of the type metadata section statically linked into the image -/// containing Swift Testing. const void *_Nonnull const SWTTypeMetadataSectionBounds[2] = { - &typeMetadataSectionBegin, - &typeMetadataSectionEnd + &typeMetadataSectionBegin, &typeMetadataSectionEnd }; #endif #pragma mark - Swift ABI #if defined(__PTRAUTH_INTRINSICS__) -#include #define SWT_PTRAUTH_SWIFT_TYPE_DESCRIPTOR __ptrauth(ptrauth_key_process_independent_data, 1, 0xae86) #else #define SWT_PTRAUTH_SWIFT_TYPE_DESCRIPTOR @@ -197,17 +190,13 @@ struct SWTTypeMetadataRecord { #pragma mark - Legacy test discovery void **swt_copyTypesWithNamesContaining(const void *sectionBegin, size_t sectionSize, const char *nameSubstring, size_t *outCount) { - auto records = reinterpret_cast(sectionBegin); - size_t recordCount = sectionSize / sizeof(SWTTypeMetadataRecord); - - // The buffer we'll return and how many types we've placed in it. (We only - // allocate the buffer if at least one type in the section matches.) void **result = nullptr; size_t resultCount = 0; + auto records = reinterpret_cast(sectionBegin); + size_t recordCount = sectionSize / sizeof(SWTTypeMetadataRecord); for (size_t i = 0; i < recordCount; i++) { - const auto& record = records[i]; - auto contextDescriptor = record.getContextDescriptor(); + auto contextDescriptor = records[i].getContextDescriptor(); if (!contextDescriptor) { // This type metadata record is invalid (or we don't understand how to // get its context descriptor), so skip it. From 5927c8618066822b32d93523122af815565ffdc7 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 13 Jan 2025 09:34:53 -0500 Subject: [PATCH 6/8] Remove long-dead _SWT_TESTING_LIBRARY_VERSION check --- Sources/_TestingInternals/Versions.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Sources/_TestingInternals/Versions.cpp b/Sources/_TestingInternals/Versions.cpp index 9af19c50c..97eace99e 100644 --- a/Sources/_TestingInternals/Versions.cpp +++ b/Sources/_TestingInternals/Versions.cpp @@ -10,12 +10,6 @@ #include "Versions.h" -#if defined(_SWT_TESTING_LIBRARY_VERSION) && !defined(SWT_TESTING_LIBRARY_VERSION) -#warning _SWT_TESTING_LIBRARY_VERSION is deprecated -#warning Define SWT_TESTING_LIBRARY_VERSION and optionally SWT_TARGET_TRIPLE instead -#define SWT_TESTING_LIBRARY_VERSION _SWT_TESTING_LIBRARY_VERSION -#endif - const char *swt_getTestingLibraryVersion(void) { #if defined(SWT_TESTING_LIBRARY_VERSION) return SWT_TESTING_LIBRARY_VERSION; From 57f374c9a854fa15b0544bdb65886f5034fca593 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 13 Jan 2025 11:42:23 -0500 Subject: [PATCH 7/8] Reduce code duplication in the Darwin version of the replacement Swift code --- Sources/Testing/Discovery+Platform.swift | 32 ++++++------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/Sources/Testing/Discovery+Platform.swift b/Sources/Testing/Discovery+Platform.swift index 7fcb94fe5..1615c2384 100644 --- a/Sources/Testing/Discovery+Platform.swift +++ b/Sources/Testing/Discovery+Platform.swift @@ -72,36 +72,20 @@ private let _startCollectingSectionBounds: Void = { return } - // If this image contains the Swift section we need, acquire the lock and + // If this image contains the Swift section(s) we need, acquire the lock and // store the section's bounds. - let testContentSectionBounds: SectionBounds? = { + func findSectionBounds(forSectionNamed segmentName: String, _ sectionName: String, ofKind kind: SectionBounds.Kind) { var size = CUnsignedLong(0) - if let start = getsectiondata(mh, "__DATA_CONST", "__swift5_tests", &size), size > 0 { + if let start = getsectiondata(mh, segmentName, sectionName, &size), size > 0 { let buffer = UnsafeRawBufferPointer(start: start, count: Int(clamping: size)) - return SectionBounds(imageAddress: mh, buffer: buffer) - } - return nil - }() - - let typeMetadataSectionBounds: SectionBounds? = { - var size = CUnsignedLong(0) - if let start = getsectiondata(mh, "__TEXT", "__swift5_types", &size), size > 0 { - let buffer = UnsafeRawBufferPointer(start: start, count: Int(clamping: size)) - return SectionBounds(imageAddress: mh, buffer: buffer) - } - return nil - }() - - if testContentSectionBounds != nil || typeMetadataSectionBounds != nil { - _sectionBounds.withLock { sectionBounds in - if let testContentSectionBounds { - sectionBounds[.testContent]!.append(testContentSectionBounds) - } - if let typeMetadataSectionBounds { - sectionBounds[.typeMetadata]!.append(typeMetadataSectionBounds) + let sb = SectionBounds(imageAddress: mh, buffer: buffer) + _sectionBounds.withLock { sectionBounds in + sectionBounds[kind]!.append(sb) } } } + findSectionBounds(forSectionNamed: "__DATA_CONST", "__swift5_tests", ofKind: .testContent) + findSectionBounds(forSectionNamed: "__TEXT", "__swift5_types", ofKind: .typeMetadata) } #if _runtime(_ObjC) From 5af173d272c19fc38fb142bcd28cf10bffdde27e Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 13 Jan 2025 13:58:53 -0500 Subject: [PATCH 8/8] Incorporate feedback --- Sources/_TestingInternals/include/Discovery.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/_TestingInternals/include/Discovery.h b/Sources/_TestingInternals/include/Discovery.h index b0cfe070a..cbcfc2d79 100644 --- a/Sources/_TestingInternals/include/Discovery.h +++ b/Sources/_TestingInternals/include/Discovery.h @@ -94,8 +94,9 @@ SWT_EXTERN const void *_Nonnull const SWTTypeMetadataSectionBounds[2]; /// - nameSubstring: A string which the names of matching classes all contain. /// - outCount: On return, the number of type metadata pointers returned. /// -/// - Returns: A pointer to an array of type metadata pointers. The caller is -/// responsible for freeing this memory with `free()` when done. +/// - Returns: A pointer to an array of type metadata pointers, or `nil` if no +/// matching types were found. The caller is responsible for freeing this +/// memory with `free()` when done. SWT_EXTERN void *_Nonnull *_Nullable swt_copyTypesWithNamesContaining( const void *sectionBegin, size_t sectionSize,