Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions Changelogs/0.11.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# 0.11.0

## 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.

Headline items:

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`.

## 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<GenericContextDescriptorHeader>` (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.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The changelog claims that genericContext(in:) and parent(in:) were promoted to protocol requirements on ContextDescriptorProtocol. However, in the provided source code (Sources/MachOSwiftSection/Models/Type/TypeContextDescriptorProtocol.swift, lines 17 and 52), these are still implemented as extension methods. To achieve witness-table dispatch as described, these should be declared within the protocol declaration itself.

- 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".
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This entry states that the SymbolIndexStore data race was fixed using @Mutex. However, in Sources/MachOSymbols/SymbolIndexStore.swift (line 155), demangledNodeBySymbol is still a standard Dictionary without synchronization, and the Storage class is still marked @unchecked Sendable. This suggests the actual implementation of the fix is missing from this branch.


### `__swift5_types` / `__swift5_protos` indirect entries

- The generic section reader was reading every entry as `RelativeDirectPointer<Descriptor>`. 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.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The changelog mentions that readers now decode the ABI correctly using new types like TypeMetadataRecord and ProtocolRecord, and throw strictly on error. However, these new model files are missing from the branch context, and MachOFile.Swift._readRelativeDescriptors (line 119) still uses compactMap with try? (swallowing errors) and RelativeDirectPointer (which does not appear to handle the tagged/indirect resolution described).


## 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).

## Dependencies

- **`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.

## CI

- 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/<BundledVersion.value>.md` does not exist.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The changelog mentions a new version-check.yml workflow and updates to release.yml. However, these files are not present in the provided branch context. If these were intended to be part of the 0.11.0 release, they appear to be missing from this pull request.

- 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.

## Docs

- 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.

## 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.
2 changes: 1 addition & 1 deletion Sources/swift-section/Version.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
// When bumping: also add Changelogs/<value>.md, then tag the release with the same string.
// Verified by .github/workflows/version-check.yml (PR) and .github/workflows/release.yml (tag).
enum BundledVersion {
static let value = "0.10.0"
static let value = "0.11.0"
}
Loading