DylibForge: relinking static archives into dynamic libraries without source code

Static linkage is a common choice for Swift package products in iOS projects. For example, Tuist links package products statically by default.

It is often appropriate for release builds but can introduce friction during development. For LLDB to import Swift modules from statically linked archives during expression evaluation, the relevant module AST paths must be available, for example, through -add_ast_path. Otherwise, evaluating Swift expressions in the debugger may fail - Debug Swift debugging with LLDB

In modular projects with many test targets, the same static dependency may be linked into multiple final binaries. The result is duplicated machine code, larger CI/CD artifacts, and longer link times.

A dynamic library can avoid duplication when shared by multiple final binaries, while also providing a smoother debugging experience. However, a dynamic library is not always an option: a package may depend on a prebuilt static target, or a vendor may distribute a static framework without source code.

DylibForge addresses that case:

It relinks a static archive as a dynamic library without requiring access to its source code. The tool extracts object files, preserves autolink directives, patches Objective-C symbol visibility, skips byte-identical objects, localizes selected duplicate native symbols, and invokes clang -dynamiclib.

For example:

dylib-forge ./AbstractMaps.framework/AbstractMaps
    --output ./AbstractMaps.framework/AbstractMaps
    --sdk iphoneos
    --install-name @rpath/AbstractMaps.framework/AbstractMaps
    --linker-arg "-Wl,-undefined,dynamic_lookup"

For more control:

dylib-forge ./AbstractMaps.framework/AbstractMaps
    --output ./AbstractMaps.framework/AbstractMaps
    --sdk iphoneos
    --install-name @rpath/AbstractMaps.framework/AbstractMaps
    --linker-arg -framework
    --linker-arg Foundation
    --linker-arg -lc++
    --linker-arg -F"Framework/Search/Path"
    --linker-arg -Wl,-U,_some_undefined_symbol
    --ignore-autolink PrivateVendorShim
    --exclude-object LegacySimulatorOnly

Any feedback, ideas, or suggestions are very welcome.

3 Likes

Quick update on DylibForge - 1.1.0!

I have improved the autolinking and SDK handling paths. The tool now parses TAPI .tbd metadata structurally, respects allowable-clients, supports weak autolink libraries, and skips header-only SDK modules such as CoreAudioTypes that cannot be passed to the linker.

I also added a README tip for setting a minimum OS version through linker arguments.

Feedback and archives that expose edge cases are especially welcome :-)

1 Like

Quick update on DylibForge - 1.2.0!

I have improved Mach-O parsing, autolinking, and SDK handling for static archives and prebuilt frameworks. The tool now validates more binary metadata before using it, handles SDK TAPI metadata when filtering restricted frameworks, and prevents unfiltered autolink directives from reaching the linker.

This removes common warnings caused by internal SDK frameworks such as UIUtilities and redundant system-library linkage. I also made duplicate-symbol handling deterministic when archives are repacked or contain repeated native definitions.

Feedback and archives that expose edge cases are especially welcome :-)