---
title: mxiris-reverse-engineering/machoswiftsection
framework: Swift Package Catalog
role: article
path: packages/mxiris-reverse-engineering/machoswiftsection
---

# mxiris-reverse-engineering/machoswiftsection

A Swift library for parsing mach-o files to obtain Swift information.

## Requirements

- Swift 6.2+ - Xcode 26.0+ - macOS 10.15+ / iOS 13+ / tvOS 13+ / watchOS 6+ / visionOS 1+

## MachOSwiftSection Library

### Roadmap

- [x] Protocol Descriptors - [x] Protocol Conformance Descriptors - [x] Type Context Descriptors - [x] Associated Type Descriptors - [x] Method Symbol For Dyld Caches - [x] Builtin Type Descriptors - [x] Swift Interface Support - [x] Runtime Metadata Inspection (`SwiftInspection`) - [ ] Type Member Layout (WIP, MachOImage only) - [ ] Swift Section MCP

### Swift Package Manager

Add the package to your `Package.swift`:

```swift dependencies: [     .package(url: "https://github.com/MxIris-Reverse-Engineering/MachOSwiftSection", from: "0.10.0"), ], targets: [     .target(         name: "YourTarget",         dependencies: [             .product(name: "MachOSwiftSection", package: "MachOSwiftSection"),             // Optional higher-level products:             .product(name: "SwiftInspection", package: "MachOSwiftSection"),             .product(name: "SwiftDump", package: "MachOSwiftSection"),             .product(name: "SwiftInterface", package: "MachOSwiftSection"),             .product(name: "TypeIndexing", package: "MachOSwiftSection"),         ]     ), ] ```

| Product | Purpose | | --- | --- | | `MachOSwiftSection` | Low-level parsing of `__swift5_*` sections (raw descriptors). | | `SwiftInspection` | Runtime metadata inspection — `EnumLayoutCalculator` (multi-payload enum layouts), `ClassHierarchyDumper`, `MetadataReader`. | | `SwiftDump` | High-level type wrappers (`Struct`, `Enum`, `Class`, `Protocol`, `ProtocolConformance`, …). | | `SwiftInterface` | End-to-end Swift interface generation. | | `TypeIndexing` | Index types / extensions / conformances for cross-binary analysis. |

### Usage

#### Basic

Swift information from MachOImage or MachOFile can be retrieved via the `swift` property.

```swift import MachOKit import MachOSwiftSection

let machO //` MachOFile` or `MachOImage`

// Protocol Descriptors let protocolDescriptors = try machO.swift.protocolDescriptors for protocolDescriptor in protocolDescriptors {     let protocolType = try Protocol(descriptor: protocolDescriptor, in: machO)     // do somethings ... }

// Protocol Conformance Descriptors let protocolConformanceDescriptors = try machO.swift.protocolConformanceDescriptors for protocolConformanceDescriptor in protocolConformanceDescriptors {     let protocolConformance = try ProtocolConformance(descriptor: protocolConformanceDescriptor, in: machO)     // do somethings ... }

// Type/Nominal Descriptors let typeContextDescriptors = try machO.swift.typesContextDescriptors for typeContextDescriptor in typeContextDescriptors {     switch typeContextDescriptor {     case .type(let typeContextDescriptorWrapper):         switch typeContextDescriptorWrapper {         case .enum(let enumDescriptor):             let enumType = try Enum(descriptor: enumDescriptor, in: machO)             // do somethings ...         case .struct(let structDescriptor):             let structType = try Struct(descriptor: structDescriptor, in: machO)             // do somethings ...         case .class(let classDescriptor):             let classType = try Class(descriptor: classDescriptor, in: machO)             // do somethings ...         }     default:         break     } } ```

#### Generate Complete Swift Interface

For generating complete Swift interface files, you can use the `SwiftInterface` library which provides a more comprehensive interface generation capability.

```swift import MachOKit import SwiftInterface

let builder = try SwiftInterfaceBuilder(configuration: .init(), eventHandlers: [], in: machO) try await builder.prepare() let result = try await builder.printRoot() ```

Generated interfaces reflect a wide range of Swift language features:

- Type / member attributes: `@objc`, `@nonobjc`, `dynamic`, `@retroactive`, `@globalActor`, `@escaping`, `consuming` / `borrowing` parameter modifiers - `distributed actor` declarations and `distributed func` members - `deinit` for classes and noncopyable types - VTable offset comments alongside class members, ordered to match the on-disk layout - Expanded field offsets for nested struct fields, rendered as a tree - Inverted protocols (`~Copyable`, `~Escapable`) on types and generic requirements

#### Inspect Runtime Metadata

`SwiftInspection` exposes higher-level inspection utilities built on top of `MachOSwiftSection`:

- `EnumLayoutCalculator` — compute the on-disk layout of Swift enums, including single-payload and multi-payload (tagged and untagged) cases. Mirrors the ABI rules in `swift/ABI/Enum.h`. - `ClassHierarchyDumper` — walk a class's inheritance chain across Swift/ObjC boundaries (requires `@_spi(Internals) import SwiftInspection`, `MachOImage` only). - `MetadataReader` — demangle types, symbols, context descriptors, and build generic signatures against a Mach-O.

## swift-section CLI Tool

### Installation

You can get the swift-section CLI tool in three ways:

- **GitHub Releases**: Download from [GitHub releases](https://github.com/MxIris-Reverse-Engineering/MachOSwiftSection/releases) - **Homebrew**: Install via `brew install swift-section` - **Build from Source**: Build with `./build-executable-product.sh` (requires Xcode 26.0 / Swift 6.2+ toolchain)

### Usage

The swift-section CLI tool provides six subcommands: `dump`, `interface`, `diff`, `snapshot`, `evolution`, and `transformer`.

> [!IMPORTANT] > As of 0.10.0, when the input is a fat / universal binary you must pass `--architecture <arch>`. The tool no longer picks a default slice silently.

#### dump - Dump Swift Information

Dump Swift information from a Mach-O file or dyld shared cache.

```bash swift-section dump [options] [file-path] ```

**Basic usage:** ```bash # Dump all Swift information from a Mach-O file swift-section dump /path/to/binary

# Dump only types and protocols swift-section dump --sections types,protocols /path/to/binary

# Save output to file swift-section dump --output-path output.txt /path/to/binary

# Use specific architecture (required for fat binaries) swift-section dump --architecture arm64 /path/to/binary ```

**Static memory-layout comments (computed offline, no process loaded):** ```bash # Field offsets for struct/class stored properties swift-section dump --emit-field-offsets /path/to/binary

# Field offsets + per-field type layout (size / stride / alignment) swift-section dump --emit-field-offsets --emit-type-layout /path/to/binary

# Expand nested struct fields with their absolute offsets swift-section dump --emit-expanded-field-offsets /path/to/binary

# Enum layout (strategy / per-case / spare bits) swift-section dump --emit-enum-layout /path/to/binary

# Enum layout with a different comment style — detailed (default), explained # (bit ranges in plain words), standard (no per-byte lines), inline (one line # per case with the byte summary), or compact swift-section dump --enum-layout-style explained /path/to/binary ```

**Header and export-status annotations:** ```bash # Leading header block: generator, image path, UUID, architecture, # library-evolution detection (dispatch-thunk count), and a short digest of # facts the binary provably cannot recover (IUO spelling, @available, …) swift-section dump --emit-header /path/to/binary

# Annotate member-symbol lines whose symbol (including its Tj/Tq/Tu derived # forms) has no export-trie entry — a symbol-table fact, not an access-level # guess. Override implementation symbols and @objc members are exempt (they # are reachable through the parent's dispatch thunk / objc_msgSend without # any exported symbol of their own). Nothing is emitted when the image # carries no export information. swift-section dump --emit-export-status /path/to/binary ```

Every comment kind above can also be reformatted with your own template — see [transformer](#transformer---customize-comment-formats). Passing a template option implies the matching `--emit-…` flag.

These offsets are computed statically by the `SwiftLayout` engine — no runtime, no metadata accessor, no loading the binary into a process — so they work on any on-disk Mach-O file (including resilient classes and cross-module field types, resolved through the dependency closure over the dyld shared cache, as well as value-generic and parameter-pack instantiations such as `InlineArray<5, Int8>` or `Variadic<Int, String>` fields). The `interface` command's `--emit-offset-comments` / `--emit-expanded-field-offsets` flags use the same static engine.

**Working with dyld shared cache:** ```bash # Dump from system dyld shared cache swift-section dump --uses-system-dyld-shared-cache --cache-image-name SwiftUICore

# Dump from specific dyld shared cache swift-section dump --dyld-shared-cache --cache-image-path /path/to/cache /path/to/dyld_shared_cache ```

Dump output includes richer annotations:

- Protocol witness table (PWT) entries are annotated with the requirement they satisfy - Inverted protocol constraints (`~Copyable`, `~Escapable`) are rendered on types and generic requirements - Protocol conformances can include the PWT address

#### interface - Generate Swift Interface

Generate a complete Swift interface file from a Mach-O file, similar to Swift's generated interfaces.

```bash swift-section interface [options] [file-path] ```

**Basic usage:**

```bash # Generate Swift interface from a Mach-O file swift-section interface /path/to/binary

# Save interface to file swift-section interface --output-path interface.swiftinterface /path/to/binary

# Use specific architecture (required for fat binaries) swift-section interface --architecture arm64 /path/to/binary ```

**Static memory-layout comments:**

```bash # Field offsets (and PWT offsets) on the generated interface swift-section interface --emit-offset-comments /path/to/binary

# Per-field type layout (size / stride / alignment) and enum layout swift-section interface --emit-type-layout --emit-enum-layout /path/to/binary ```

These use the same static `SwiftLayout` engine as `dump`, and accept the same comment-template options — see [transformer](#transformer---customize-comment-formats).

**Header and export-status annotations:**

```bash # Leading header block ahead of the imports: generator, image path, UUID, # architecture, library-evolution detection (dispatch-thunk count), and a # short digest of facts the binary provably cannot recover swift-section interface --emit-header /path/to/binary

# Annotate members none of whose symbols (including Tj/Tq/Tu derived forms) # have an export-trie entry with a `// not exported` comment. `override` and # `@objc` members are exempt — they are reachable through the parent's # dispatch thunk / objc_msgSend without any exported symbol of their own. swift-section interface --emit-export-status /path/to/binary

# Print only the declarations the image exports — the filtering counterpart of # --emit-export-status. Types and protocols are ruled by their descriptor # symbol's export-trie entry, extensions by whether their target is an # in-image non-exported declaration, members by the same derived-form verdict # the annotation uses. Still a symbol-table fact, never an access-level guess: # anything without export evidence (and every `override` / `@objc` member) is # kept, so an `-enable-testing` build keeps its `internal` declarations. swift-section interface --exported-only /path/to/binary ```

All three flags default to off, keeping default output byte-identical.

**Working with dyld shared cache:**

```bash # Dump from system dyld shared cache swift-section interface --uses-system-dyld-shared-cache --cache-image-name SwiftUICore

# Dump from specific dyld shared cache swift-section interface --dyld-shared-cache --cache-image-path /path/to/cache /path/to/dyld_shared_cache ```

#### diff - Compare the ABI of Two Versions

Diff the Swift ABI of two versions of the same module at the **binary** level — field retypes, enum-case tag renumbering, accessor changes, added/removed conformances — details a `.swiftinterface` diff cannot see. Extension changes are attributed **per conformance / per conditional block** (`Target: Protocol where …`), so adding or dropping a single conformance reads as one container-level change. Protocols whose requirement symbols are stripped (the OS-framework norm) still diff by their **witness-table slots**, so a protocol gaining or losing a requirement is visible with zero symbols; compare binaries in similar strip states, since a symbol-rich vs stripped pair reports the same requirement as a member swap.

```bash # Change-list report with a breaking/backward-compatible verdict swift-section diff old/Foo.framework/Foo new/Foo.framework/Foo

# Either side may be a persisted baseline produced by `snapshot` swift-section diff baseline.json new/Foo.framework/Foo

# Machine-readable output / CI gating swift-section diff old.dylib new.dylib --json swift-section diff old.dylib new.dylib --summary-only --fail-on-breaking

# Full interface annotated with +/- diff markers (needs two binaries) swift-section diff old.dylib new.dylib --interface --format unified ```

#### snapshot - Persist an ABI Baseline

Index a binary once and freeze its ABI into a versioned JSON baseline; later diffs and evolution runs can consume the JSON without the original binary.

```bash swift-section snapshot /path/to/binary --label 1.0 -o baseline-1.0.json

# From a dyld shared cache image swift-section snapshot --dyld-shared-cache -n SwiftUICore /path/to/dyld_shared_cache --label 26.0 -o swiftuicore-26.0.json ```

#### evolution - Track ABI Across Many Versions

Track one module's ABI across an ordered series of versions (oldest first) and report each declaration's lifeline: introduced / modified / removed / re-added, with a per-transition additive-or-breaking verdict. Inputs mix freely between binaries, dyld shared caches, and `snapshot` baselines.

```bash # Three OS versions of the same framework, one report swift-section evolution 17.0.json 18.0.json /path/to/Foo-26.0.dylib --labels 17.0,18.0,26.0

# Across dyld shared caches (extracts the same image from each cache) swift-section evolution --dyld-shared-cache -n SwiftUICore cache-17 cache-18 cache-26

# Summary or JSON, and CI gating on any breaking transition swift-section evolution v1.json v2.json v3.json --summary-only --fail-on-breaking swift-section evolution v1.json v2.json v3.json --json ```

With `--interface`, the same axis renders as a single **annotated union interface** instead of the lineage list: every declaration that ever existed appears once (rendered from the last version that has it), declarations that changed carry a trailing `// [●●○] removed in 26.0`-style comment (presence bitmap + event phrases; the legend at the top maps bitmap positions to version labels), and declarations present throughout with no changes stay bare. A member whose signature changed shows its newest generation, with the old shape in the comment (`modified in 26.0: old → new`). Because the interface renders from live models, every input must be a binary or dyld shared cache in this mode — snapshot JSON inputs are rejected.

```bash # The union interface with lifecycle annotations, colorized on a terminal swift-section evolution --interface v17/Foo.dylib v18/Foo.dylib v26/Foo.dylib --labels 17.0,18.0,26.0

# Across dyld shared caches, written to a file, gating CI on breaking changes swift-section evolution --interface --dyld-shared-cache -n SwiftUICore cache-17 cache-18 cache-26 --fail-on-breaking -o SwiftUICore-evolution.swift ```

#### transformer - Customize Comment Formats

The memory-layout comments `dump` and `interface` emit are rendered from token templates. Each of the five comment kinds — field offset, vtable offset, member address, type layout, enum layout — has its own template, its own `${token}` placeholders, and a set of built-in templates you can name. This subcommand lists them and builds reusable configurations; the template options themselves are accepted directly by `dump` and `interface`.

```bash # What can a template say? swift-section transformer tokens                        # every module swift-section transformer tokens --module enum-layout   # one module

# Built-in templates — their names are accepted by the template options swift-section transformer templates --module field-offset ```

**Using a template.** Pass either a built-in template name or a literal template containing `${token}` placeholders. A name that matches nothing is an error rather than a silently constant comment.

```bash # Built-in template by name: "// 0x0 ..< 0x10" swift-section dump --field-offset-template range /path/to/binary

# Literal template: "// @0x0" swift-section dump --field-offset-template '@${startOffset}' /path/to/binary

# Enum layout is three templates: strategy line, per case, per fixed byte swift-section dump \     --enum-layout-template strategyOnly \     --enum-layout-case-template inlineSummary \     /path/to/binary ```

Passing any template option enables the comment kind it formats, so no separate `--emit-…` flag is needed. Numbers can be switched between hexadecimal and decimal per module (`--field-offset-hex` / `--no-field-offset-hex`, and so on).

**Reusable configurations.** A whole set of templates can be frozen into a JSON file and replayed with `--transformer-config`. The file format is the one RuntimeViewer persists, so a configuration tuned in its settings UI works here unchanged.

```bash # Freeze a command line into a file swift-section transformer config \     --field-offset-template range \     --enum-layout-style compact \     --output-path comments.json

# Replay it swift-section dump --transformer-config comments.json /path/to/binary swift-section interface --transformer-config comments.json /path/to/binary ```

## Running Tests

The snapshot tests in this repository rely on a fixture framework (`SymbolTestsCore`) built from an Xcode project in `Tests/Projects/SymbolTests/`. The framework binary is not checked in — rebuild it once after cloning:

```bash ./Scripts/build-test-fixtures.sh ```

Then run the tests:

```bash swift package update swift test ```

Skipping the fixture build causes `MachOFileTests` to throw a "file not found" error at `Tests/Projects/SymbolTests/DerivedData/.../SymbolTestsCore` during test `init()`, before any assertions run.

To regenerate snapshots after a legitimate Swift-compiler / metadata change:

```bash SNAPSHOT_TESTING_RECORD=all swift test \     --filter SymbolTestsCoreDumpSnapshotTests \     --filter SymbolTestsCoreInterfaceSnapshotTests ```

Commit the updated `__Snapshots__/` files alongside the source change that prompted the regeneration.

## License

[MachOObjCSection](https://github.com/p-x9/MachOObjCSection)

[MachOKit](https://github.com/p-x9/MachOKit)

[CwlDemangle](https://github.com/mattgallagher/CwlDemangle)

MachOSwiftSection is released under the MIT License. See [LICENSE](./LICENSE)

## Package Metadata

Repository: mxiris-reverse-engineering/machoswiftsection

Default branch: main

README: README.md
