Calling into Swift from Rust on Linux

I've got a project which is primarily written in Rust that runs on both Linux and macOS, and I want to use a library that's written in Swift. I've written a Swift @_cdecl function that I can call from Rust using extern "C".

On macOS, this works fine. However, when running on Linux, it segfaults inside libswiftCore.

Passing data from Rust to Swift works fine, the segfault occurs within the Swift runtime in ValueWitness::initializeWithCopy, seemingly whenever any generic specialization is called.

The beginning of the backtrace from the crash:

* thread #2, name = 'tests::test_hig', stop reason = signal SIGSEGV: invalid address (fault address: 0xfffffffffffffff8)
  * frame #0: 0x0000fffff7541a78 libswiftCore.so`swift::metadataimpl::ValueWitnesses<swift::metadataimpl::OpaqueExistentialBox<0u> >::initializeWithCopy(swift::OpaqueValue*, swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*) + 28
    frame #1: 0x0000fffff74e7158 libswiftCore.so`outlined init with copy of Any + 44
    frame #2: 0x0000fffff739e198 libswiftCore.so`generic specialization <Swift._Stdout> of Swift._debugPrint<τ_0_0 where τ_0_0: Swift.TextOutputStream>(_: Swift.Array<Any>, separator: Swift.String, terminator: Swift.String, to: inout τ_0_0) -> () + 128
    frame #3: 0x0000aaaaaaae5640 splash_rs-53f1675cf4435848`highlight(codePtr=0xaaaaaab5d7ce, codeLen=3, htmlLenPtr=0xfffff4944fe0) at highlight_swift.swift:17:5

And exactly where the crash is:

* thread #2, name = 'tests::test_hig', stop reason = signal SIGSEGV: invalid address (fault address: 0xfffffffffffffff8)
    frame #0: 0x0000fffff7541a78 libswiftCore.so`swift::metadataimpl::ValueWitnesses<swift::metadataimpl::OpaqueExistentialBox<0u> >::initializeWithCopy(swift::OpaqueValue*, swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*) + 28
libswiftCore.so`swift::metadataimpl::ValueWitnesses<swift::metadataimpl::OpaqueExistentialBox<0u> >::initializeWithCopy:
->  0xfffff7541a78 <+28>: ldur   x8, [x2, #-0x8]
    0xfffff7541a7c <+32>: ldrb   w9, [x8, #0x52]
    0xfffff7541a80 <+36>: tbnz   w9, #0x1, 0xfffff7541a94  ; <+56>
    0xfffff7541a84 <+40>: ldr    x8, [x8, #0x10]

My code can be found here: https://git.shadowfacts.net/shadowfacts/splash-rs, and this is readily reproducible on Ubuntu 20.04 (with both Rust and Swift installed) by running cargo test.

So, my question is: Do I need to do anything to initialize the Swift runtime from my Rust program, before I call into Swift, or is this something that should work automatically?

Yes. dyld handles initializing the swift runtimes on macOS, but you'll need to link against swiftrt.o on Linux.

5 Likes

Aha, that did the trick, thank you! I was linking against all the dynamic libraries, but missed that I also needed to include swiftrt.o.