swift-dwarf

0.1.1

0xpablo/swift-dwarf

What's New

swift-dwarf v0.1.1

2025-11-18T10:44:33Z

Initial Release

This is the first public release of swift-dwarf, a Swift wrapper for libdwarf providing type-safe access to DWARF debugging information.

Features

  • DWARF Parsing: Read and parse DWARF debug information from binaries, dSYM bundles, and object files
  • Symbolication: Convert addresses to source locations with inline frame support
  • Type-Safe APIs: Swift-native wrappers around libdwarf's C interface
  • Symbol Demangling: Built-in support for Swift and C++ symbol demangling
  • UUID Extraction: Extract UUIDs from Mach-O binaries for dSYM matching
  • Universal Binary Support: Handle multi-architecture Mach-O files
  • Cross-Platform: Works on both macOS and Linux

Documentation

Documentation is available on Swift Package Index.

Acknowledgments

This library includes a patched version of libdwarf with some fixes that will be upstreamed.

swift-dwarf

A Swift wrapper for libdwarf, providing type-safe access to DWARF debugging information on macOS and Linux.

CI License

Overview

This library wraps the C libdwarf library to enable Swift programs to read and parse DWARF debugging information from binaries, dSYM bundles, and object files. It's particularly useful for:

  • Symbolicating crash addresses to function names and source locations
  • Building debugging tools and profilers
  • Analyzing binary debug information
  • Understanding compiler output and optimization

Usage

import DWARF

// Open a binary or dSYM file
let session = try DWARFSession(path: "/path/to/binary")
defer { session.close() }

// Iterate through compilation units
for unit in session.compilationUnits() {
    print("CU Version: \(unit.header.version)")
    // Access DIEs (Debug Information Entries)
    if let child = try unit.die.firstChild() {
        // Process debug info...
    }
}

Extracting Binary UUID

For Mach-O binaries, you can extract the UUID (from LC_UUID load command) to match binaries with their dSYMs:

let session = try DWARFSession(path: "/path/to/binary.dSYM/Contents/Resources/DWARF/binary")
defer { session.close() }

let info = try session.objectInfo()
if let uuid = info.uuid {
    print("Binary UUID: \(uuid.uuidString)")
    // Use UUID for matching with crash reports, symbol servers, etc.
}

Working with Universal Binaries

For Mach-O universal binaries (containing multiple architectures), you can specify which slice to use:

// Determine which architecture slice you want
// You can use `lipo -info` or `otool -f` to see available architectures
let options = DWARFSession.Options(architectureIndex: 1) // e.g., arm64 slice
let session = try DWARFSession(path: "/path/to/universal/binary", options: options)

Symbolicating Addresses (With Inline Frames)

import DWARFSymbolication

let session = try DWARFSession(path: "/path/to/binary")
defer { session.close() }

if let result = try session.symbolicate(address: 0x100000ae0) {
    print("== \(String(format: "%llx", result.address)) ==")
    for frame in result.frames {
        let inlineMarker = frame.isInlined ? "[inlined] " : ""
        print("  \(inlineMarker)\(frame.function) \(frame.file):\(frame.line)")
    }

    if !result.issues.isEmpty {
        print("Symbolication warnings:")
        result.issues.forEach { print("\($0)") }
    }
}

result.frames contains every inline expansion (innermost first, outermost last), so consumers can render complete call stacks without manually traversing DIEs.

To find available architectures in a universal binary:

# Show architecture indices
otool -f /path/to/binary

# Example output:
# Fat headers
# fat_magic 0xcafebabe
# nfat_arch 2
# architecture 0         <- x86_64 (index 0)
#     cputype 16777223
#     ...
# architecture 1         <- arm64 (index 1)
#     cputype 16777228
#     ...

Building

# Clone with submodules
git clone --recursive https://github.com/0xpablo/swift-dwarf.git

# Or if already cloned, initialize submodules
git submodule update --init --recursive

# Build
swift build
swift test

Requirements

  • Swift 6.2+
  • macOS 10.15+ or Linux
  • libdwarf is included as a submodule (using fork with patches)

License

This Swift wrapper is available under the MIT license. See LICENSE for details.

The libdwarf library (included as a submodule) is licensed under LGPL 2.1. This means:

  • You can use this Swift wrapper in commercial/proprietary applications
  • The Swift wrapper code can remain MIT licensed
  • Any modifications to libdwarf itself must remain LGPL 2.1
  • Users must be able to replace the libdwarf component (satisfied by the submodule structure)

See NOTICE for detailed licensing information.

Contributing

Contributions are welcome! Please ensure tests pass before submitting PRs.

libdwarf Submodule

This repository uses a forked version of libdwarf with patches applied:

The fork contains patches, each in its own branch for upstream contribution:

  1. fix-commandsizetotal-bug: Fixes incorrect calculation of commandsizetotal in Mach-O headers
  2. increase-max-commands-size: Increases MAX_COMMANDS_SIZE to 256KB for modern macOS binaries
  3. add-macho-uuid-support: Adds UUID extraction from LC_UUID for binary/dSYM matching
  4. fix-integer-precision-warnings: Fixes integer precision loss warnings on 64-bit systems

To update the submodule:

cd libdwarf
git fetch origin
git checkout swift-dwarf-patches
git pull origin swift-dwarf-patches
cd ..
git add libdwarf
git commit -m "Update libdwarf submodule"

TODO: Expand DWARF 5 Test Coverage

The bundled Tests/Fixtures/TestProgram.dSYM fixture is built with DWARF 4, so newer features like .debug_rnglists (DW_AT_ranges with DW_FORM_rnglistx) never execute in the current tests. We should generate an additional DWARF 5 fixture (for example by compiling the fixture program with swiftc -g -gdwarf-5 ...) and add focused tests that assert symbolication/range queries succeed when data lives in .debug_rnglists instead of .debug_ranges.

References

Description

  • Swift Tools 6.2.0
View More Packages from this Author

Dependencies

  • None
Last updated: Sun Nov 23 2025 01:27:32 GMT-1000 (Hawaii-Aleutian Standard Time)