Help with dynamic loading

I have a package that needs to dynamically load some functions depending on their availability. The functions the package has needed to load historically are pretty simple: they only have a few parameters and all the types are defined in the standard library. These are the basic steps I've taken:

func wrapper(_ string: String, _ int: Int) {
  guard
    let ptr = dlsym(
      dlopen(nil /* or explicit module */, RTLD_LAZY),
      "$s{mangled name}"
    )
  else { return }
  withUnsafePointer(to: ptr) {
    UnsafeRawPointer($0).assumingMemoryBound(
      to: (@convention(thin) (String, Int) -> Void).self
    )
    .pointee(string, int)
  }
}

I now need to load some more complex functions that work with types defined inside of the dynamically-loaded library. I tried defining my own local "shim" types that match the original memory layout, and while this works great in debug builds, it causes the compiler to crash when building for release when the build includes the .pointee(string, int) line.

Does anyone have any suggestions that might work around this problem?

1 Like

You can't reliably dynamically look up Swift function symbols and form function values from them. The most reliable thing to do would be to export the symbols you need to find as C entry points using @_cdecl and form @convention(c) function pointers to them. If loading the dynamic library has the effect of overriding specific entry points in your main program, you might be able to use @_dynamicReplacement(for:) and let the Swift runtime dynamically replace the implementation for you:

// in main program 
struct Foo {
  dynamic func doStuff() -> ReturnValue? {
    // don't know what to do yet
    return nil
  }
}

// in vX.Y dylib
extension Foo {
  @_dynamicReplacement(for: doStuff())
  func doStuffForX_Y() -> ReturnValue? {
    return /*stuff to do under X.Y*/
  }
}
5 Likes

Thanks for the info! I'm not entirely sure I can leverage it in this case, since I don't own the dynamic library I'm loading, but I do think I might have stumbled onto a path forward using an intermediate XCFramework. The XCFramework links directly to the upstream library, defines simpler overlays, and then my package dynamically loads this XCFramework's symbols, instead.

4 Likes