Files
obsidian-vault/personal/tech/Dyld dynamic interposing.md

5.2 KiB
Executable File
Raw Permalink Blame History

# Summary: Dynamic Interposing, Linking, and Build Configuration (macOS / Xcode / SwiftPM)

## Main Topic
Techniques and configuration patterns for:
- **Intercepting and shadowing C/C++ functions** using `dyld` interposing
- **Hooking low-level functions** like `__cxa_throw` to capture stack traces
- **Controlling Xcodes preview dylib linking**
- **Propagating defines and optional frameworks** across Xcode projects and Swift packages
- **Detecting sandbox vs. DMG runtime environments**

---

## Key Decisions & Solutions

### 1. Shadowing System C Functions via `dyld` Interposing
- Use `__DATA,__interpose` section with dyld to redirect system calls.
- Example interpose tuple:
  ```c
  __attribute__((used))
  static struct {
      void *new_func;
      void *orig_func;
  } _interposers[] __attribute__((section("__DATA, __interpose"))) = {
      { (void*)my_open, (void*)open }
  };


2. Interposing __cxa_throw to Capture Stack Traces

  • Replace C++ ABIs __cxa_throw to log stack traces at throw time.

  • Example implementation:

    extern "C" void __cxa_throw(void*, std::type_info*, void (*)(void*)) __attribute__((noreturn));
    
    extern "C" void my___cxa_throw(void* thrown_exception,
                                   std::type_info* tinfo,
                                   void (*dest)(void*)) __attribute__((noreturn)) {
        void* frames[128];
        int n = backtrace(frames, 128);
        // Store stack trace...
        __real___cxa_throw(thrown_exception, tinfo, dest);
    }
    
    __attribute__((used))
    static struct {
        const void* replacement;
        const void* replacee;
    } _interposers[] __attribute__((section("__DATA,__interpose"))) = {
        { (const void*)my___cxa_throw, (const void*)__cxa_throw },
    };
    
    
  • Hook is used for debugging; not recommended in production.


3. Disable __preview.dylib Linking in Xcode

  • Build setting: ENABLE_DEBUG_DYLIB = NO

  • Steps:

    1. Select target → Build Settings

    2. Search for ENABLE_DEBUG_DYLIB

    3. Set to NO (for Debug or all configurations)

    4. Clean and rebuild

  • Prevents automatic SwiftUI preview dylib injection.


4. Propagating #define Flags into Swift Packages

  • Not supported directly.

  • Packages build in isolation; app-level defines (SWIFT_ACTIVE_COMPILATION_CONDITIONS) dont propagate.

  • Solutions:

    • Define flags in the package:

      .target(
        name: "MyLib",
        swiftSettings: [.define("USE_FOO")]
      )
      
      
    • Pass global flags: swift build -Xswiftc -D USE_FOO

    • Expose feature variants via separate targets or products.


5. Detecting Sandbox vs. DMG App Runtime

Detect App Sandbox

import Security

func isSandboxed() -> Bool {
    let task = SecTaskCreateFromSelf(nil)!
    if let v = SecTaskCopyValueForEntitlement(task, "com.apple.security.app-sandbox" as CFString, nil) as? NSNumber {
        return v.boolValue
    }
    return false
}

Detect App Store vs. DMG

  • Modern (macOS 14+):

    import StoreKit
    
    func installOrigin() async -> Bool {
        if case .verified(_) = try? await AppTransaction.shared {
            return true // App Store
        }
        return false
    }
    
    
  • Legacy:

    if let url = Bundle.main.appStoreReceiptURL,
       FileManager.default.fileExists(atPath: url.path) {
        // App Store receipt found
    }
    
    

6. Using Optional Frameworks from a Swift Package

  • Apps optional linkage does not propagate to packages.

  • Alternatives:

    1. Add linkerSettings or unsafeFlags in the package:

      .target(
        name: "Child",
        linkerSettings: [
          .unsafeFlags(["-weak_framework", "Foo"])
        ]
      )
      
      
    2. Use #if canImport(Foo) inside the package to guard code.

    3. Offer multiple package products (e.g., Core and WithFoo).

    4. As last resort, dlopen() the framework dynamically.


Important Snippets Collected

  • Dyld Interpose Example

  • __cxa_throw hook for backtrace

  • Disabling SwiftUI __preview.dylib

  • Sandbox/App Store detection functions

  • SwiftPM swiftSettings and weak framework linking


Unresolved / Future Considerations

  • Testing cross-target define propagation via unified SwiftPM workspace.

  • Verifying dyld_dynamic_interpose() availability on latest macOS.

  • Providing a reusable utility to report sandbox + install origin combined.