From c8171a5513d6d0576c03f7438224cc0e0fc3401c Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Thu, 23 Apr 2026 09:43:52 +0800 Subject: [PATCH] docs(changelog): streamline 0.11.0 notes for end users Drop deep root-cause analysis (byte-offset / bit-level rationale, Swift source line references, CI workflow internals) that only matters to contributors, and reorganize around user-facing impact: - Add "What this unlocks" summarizing the concrete capabilities the three fixes restore (generic introspection, cross-module binaries, deterministic parallel tests). - Replace the long "Dependencies" paragraph with an "Upgrading" section containing a before/after snippet for the swift-demangling 0.3.0 async migration. - Drop "CI", "Docs", and "Known Issues" sections. Release notes on GitHub already synced to this content. --- Changelogs/0.11.0.md | 65 ++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 42 deletions(-) diff --git a/Changelogs/0.11.0.md b/Changelogs/0.11.0.md index 7ed6865c..6e22b864 100644 --- a/Changelogs/0.11.0.md +++ b/Changelogs/0.11.0.md @@ -2,66 +2,47 @@ ## Highlights -Minor release on top of `0.10.0`, folding in the previously-planned `0.10.1` content plus the parallel-test race fix and a set of ABI-accurate Swift section readers. +Minor release on top of `0.10.0`. Fixes two crashes and a correctness issue in the Swift section readers, adds public record types mirroring the Swift ABI, and bumps `swift-demangling` to `0.3.0` (async APIs). -Headline items: +## What this unlocks -1. Fixes the parallel-test `SymbolIndexStore` data race (previously tracked in `KNOWN_ISSUES.md`) that surfaced intermittently as SIGSEGV or as `.invalidContextDescriptor` failures on CI. -2. Swift section readers now mirror the ABI in `swift/include/swift/ABI/Metadata.h` — `__swift5_types` / `__swift5_protos` indirect entries are read correctly (previously misread as direct pointers, which on cross-module binaries produced garbage context-descriptor kinds). -3. Carries forward the generic-context dispatch signal-11 fix and the `swift-demangling` 0.3.0 async-API migration originally planned for `0.10.1`. +- **Generic type introspection** — `GenericSpecializer`, `EnumLayoutCalculator`, and any code iterating `typeContextDescriptors` of generic struct / class / enum types is now crash-free. Previously surfaced as `EXC_BAD_ACCESS` from reading the generic header at the wrong offset. +- **Cross-module binaries** — `machO.swift.contextDescriptors` and `protocolDescriptors` now return valid descriptors on binaries that reference types from other modules (emitted as `IndirectTypeDescriptor` entries). Previously surfaced as `.invalidContextDescriptor`. +- **Parallel test execution** — `swift test` under swift-testing's default parallel mode is now deterministic. The `--no-parallel` / `@Suite(.serialized)` workaround is no longer needed in downstream consumers. ## Bug Fixes -### Generic context dispatch (signal-11 crash) - -- The `ReadingContext` overload of `genericContext(in:)` introduced during the reading-context refactor was declared as a protocol extension only, with no override on `TypeContextDescriptorProtocol`. Calls through `any ContextDescriptorProtocol` therefore statically dispatched to the base implementation, which built a `TargetGenericContext` (8-byte header) even when the descriptor was a `StructDescriptor` / `ClassDescriptor` / `EnumDescriptor`. Those descriptors actually start with `TypeGenericContextDescriptorHeader` (16 bytes — two relative offsets preceding the `base` header), so the read was misaligned by 8 bytes. The misaligned bytes usually surfaced as a garbage `valueHeader.numValues` of ~`UInt32.max`, after which `readWrapperElements` walked ~4 billion fabricated `GenericValueDescriptor` slots and crashed with `EXC_BAD_ACCESS`. - - Promoted `genericContext(in:)` and `parent(in:)` to protocol requirements on `ContextDescriptorProtocol` so they participate in witness-table dispatch. - - Added matching overrides on `TypeContextDescriptorProtocol` that route through `typeGenericContext(in:)?.asGenericContext()`, mirroring the `MachOSwiftSectionRepresentableWithCache` path. - - Mirrored the new overloads on `TypeContextDescriptorWrapper` / `ValueTypeDescriptorWrapper` for API parity. - - Resolves `EXC_BAD_ACCESS` in `MultiPayloadEnumTests` and in `GenericSpecializationTests.main` via `SwiftInterfaceIndexer.indexTypes`. - -### `SymbolIndexStore` parallel-test data race - -- `Storage.demangledNodeBySymbol` was previously a plain `Dictionary` mutated by `setDemangledNode(_:for:)` without synchronization. When swift-testing ran sibling suites in parallel, two builders could concurrently hit a cache miss on the same `SymbolIndexStore.Storage`; both would insert into the dictionary, and the unsynchronized read+write corrupted its internal layout — surfacing intermittently as `NSInvalidArgumentException: -[NSTaggedPointerString objectForKey:]`, SIGSEGV in the hash lookup, or later as `.invalidContextDescriptor` when the trashed bytes were reinterpreted as context-descriptor flags. -- Fixed by guarding the dictionary with `@Mutex` (`FrameworkToolbox` / `SwiftStdlibToolbox`). The CI workaround (`--no-parallel` and `@Suite(.serialized)`) is no longer needed and has been removed. This moves the entry out of `KNOWN_ISSUES.md` "deferred". - -### `__swift5_types` / `__swift5_protos` indirect entries - -- The generic section reader was reading every entry as `RelativeDirectPointer`. Per the ABI (`swift/include/swift/ABI/Metadata.h:2720, 2766`), entries in these sections are tagged pointers — the low bits encode a `TypeReferenceKind` (for `__swift5_types`) or an indirect flag + reserved bit (for `__swift5_protos`). On a cross-module binary any entry emitted as `IndirectTypeDescriptor` (see the direct-vs-indirect decision at `lib/IRGen/GenDecl.cpp:4195-4224`) was read at the wrong offset, producing a descriptor pointer to unrelated bytes and surfacing as `.invalidContextDescriptor`. -- Readers now decode the ABI correctly: `TypeMetadataRecord` branches on `TypeReferenceKind` to pick direct / indirect resolution (and drops ObjC kinds to `nil`, matching the runtime `nullptr` fallback at `Metadata.h:2753`); `ProtocolRecord` uses `RelativeIndirectablePointerIntPair` with nullable `Optional` pointee to match `MetadataRef.h:109`. Both `MachOFile.Swift` and `MachOImage.Swift` readers now throw strictly on error and are symmetric across ends. +- **`EXC_BAD_ACCESS` in generic context dispatch** — reading generic struct / class / enum descriptors through `any ContextDescriptorProtocol` could misread the generic header and walk billions of fabricated entries. +- **Parallel-test data race in `SymbolIndexStore`** — the demangled-node cache is now guarded by `@Mutex`. +- **`__swift5_types` / `__swift5_protos` reader correctness** — entries in these sections are tagged pointers (low bits encode a `TypeReferenceKind` or an indirect flag). They were being read as plain direct pointers. Both `MachOFile.Swift` and `MachOImage.Swift` readers now decode the ABI correctly and throw strictly on error. ## Library New public types for downstream section-level analysis: -- `TypeMetadataRecord` (`Models/Type/TypeMetadataRecord.swift`) — mirrors `TargetTypeMetadataRecord`; one per entry of `__swift5_types` / `__swift5_types2`, with a `contextDescriptor(in:)` method that branches on `TypeReferenceKind`. -- `ProtocolRecord` (`Models/Protocol/ProtocolRecord.swift`) — mirrors `TargetProtocolRecord`; one per entry of `__swift5_protos`, with a `protocolDescriptor(in:)` accessor. -- `RelativeDirectPointerIntPair` / `TargetRelativeDirectPointerIntPair` / `RelativeDirectPointerIntPairProtocol` (`MachOPointers`) — mirror `swift/include/swift/Basic/RelativePointer.h:575-619`. The int-tagged direct relative pointer variant needed to express `TypeMetadataRecord`'s layout; distinct from the existing `RelativeIndirectablePointerIntPair` (low 2 bits = int value, no indirect bit). -- The shape of `MachOFile.Swift.contextDescriptors` / `protocolDescriptors` / `protocolConformanceDescriptors` is unchanged (same return types). +- `TypeMetadataRecord` — mirrors `TargetTypeMetadataRecord`; one per entry of `__swift5_types` / `__swift5_types2`, with `contextDescriptor(in:)` that branches on `TypeReferenceKind`. +- `ProtocolRecord` — mirrors `TargetProtocolRecord`; one per entry of `__swift5_protos`, with `protocolDescriptor(in:)` accessor. +- `RelativeDirectPointerIntPair` in `MachOPointers` — int-tagged direct relative pointer variant (distinct from the existing `RelativeIndirectablePointerIntPair`). -## Dependencies +The existing `MachOFile.Swift.contextDescriptors` / `protocolDescriptors` / `protocolConformanceDescriptors` return types are unchanged — they now transparently handle the tagged pointer format under the hood. -- **`swift-demangling` minimum bumped to `0.3.0`**. 0.3.0 makes `Node.print`, `demangleAsNode`, and `mangleAsString` async (with stack-safe execution) and adds the `DemanglingTestingSupport` module. Call sites in `SwiftDump`, `SwiftInterface`, and the test suites have been migrated to `await` the new entry points. Downstream consumers using these public demangling APIs will need to `await` them as well. +## Upgrading -## CI +`swift-demangling` minimum is now `0.3.0`. `Node.print`, `demangleAsNode`, and `mangleAsString` are now async. Call sites need `await`: -- Added `GenericSpecializationTests`, `MultiPayloadEnumTests`, and `MetadataReaderDemanglingTests` to the macOS matrix filter so future regressions in the generic-context dispatch path or the section reader path surface in CI instead of as local crashes. `GenericSpecializerAPITests` was folded into `GenericSpecializationTests`, and the corresponding filter entry removed. -- `Sources/swift-section/Version.swift` is the single source of truth for the CLI version: - - `release.yml` no longer injects the version via heredoc; it fails a tagged release when `BundledVersion.value != github.ref_name`. - - A new `version-check.yml` workflow runs on every push to `main` and every PR, and fails when `Changelogs/.md` does not exist. - - The extraction shell is whitespace-tolerant and guards `grep`'s exit status so formatter changes cannot silently break the check. - - Source builds (including Homebrew's source path) now report the correct version without depending on CI injection. +```swift +// Before (0.2.x) +let node = try demangleAsNode(mangled) +let string = node.print(using: .default) -## Docs +// After (0.3.0) +let node = try await demangleAsNode(mangled) +let string = await node.print(using: .default) +``` -- Corrected MachOKit version in the `0.10.0` notes: `0.49.100` (based on upstream `0.49.0`), not `0.47.100`. -- `KNOWN_ISSUES.md` annotated recent CI runs that reproduced the `SymbolIndexStore.demangledNode` data race so its flaky, parallel-harness-only nature was easier to recognize during triage. A follow-up will move that entry to the "fixed" section once `0.11.0` has been green across a few CI runs. +No changes required for `MachOFile.Swift.*` section APIs, `SwiftDump`, `SwiftInterface`, or `SwiftInspection` — all internal call sites are already migrated. ## Requirements - Swift 6.2+ - Xcode 26.0+ (CI validates on Xcode 26.4 / macOS 26) - -## Known Issues - -See [KNOWN_ISSUES.md](https://github.com/MxIris-Reverse-Engineering/MachOSwiftSection/blob/0.11.0/KNOWN_ISSUES.md). The `SymbolIndexStore.demangledNode` race is resolved in this release; the SharedCache global-lock item remains deferred.