Experimenting with concurrency: linker can't find /usr/lib/swift/libswift_Concurrency.dylib

I'm trying to experiment with the new concurrency APIs, but I'm getting a linker error on macOS.

I'm on macOS 10.15.7 with Xcode 12.2.

I installed the swift-DEVELOPMENT-SNAPSHOT-2020-11-17-a toolchain.

I created a new SwiftPM executable package and put this code in main.swift:

func computeInt() async -> Int {
  return 42
}

let task = Task.runDetached {
  let answer = await computeInt()
  print(answer)
}

Then I run it like this:

swift run \
  --toolchain /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2020-11-17-a.xctoolchain/ \
  -Xswiftc -Xfrontend -Xswiftc -enable-experimental-concurrency

This compiles, but I'm getting a linker error because the linker is looking for /usr/lib/swift/libswift_Concurrency.dylib, which obviously doesn't exist:

[3/3] Linking AsyncAwait
dyld: Library not loaded: /usr/lib/swift/libswift_Concurrency.dylib
  Referenced from: /Users/elo/code/AsyncAwait/.build/x86_64-apple-macosx/debug/AsyncAwait
  Reason: image not found

How can I tell the linker to look for libswift_Concurrency in the toolchain directory?

By the way, I'm getting the same results when building from an Xcode command line project. I can run the SwiftPM package on Linux (tested in a Docker container using the swiftlang/swift:nightly-focal image).

This looks like an @rpath issue. One workaround should be:

install_name_tool -rpath /usr/lib/swift /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2020-11-17-a.xctoolchain/ /Users/elo/code/AsyncAwait/.build/x86_64-apple-macosx/debug/AsyncAwait

which changes the @rpath from the system to the path in the toolchain for this specific application. There's probably a Swift flag somewhere that can be set to change the @rpath of the resulting executable.

1 Like

It looks like you're expected to set DYLD_LIBRARY_PATH to test with the set of Swift libraries in a toolchain. See [build] Adjust install name for Differentiation and Concurrency by edymtt · Pull Request #34216 · apple/swift · GitHub

1 Like

Thanks for the tip, Joe, I hadn't seen that PR. It sounds like it should work, but it doesn't for me, I'm still getting the same error:

$ echo $DYLD_LIBRARY_PATH
/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2020-11-17-a.xctoolchain/usr/lib/swift/macosx

$ xcrun --toolchain org.swift.50202011171a \
  swift run \
    -Xswiftc -Xfrontend -Xswiftc -enable-experimental-concurrency
dyld: Library not loaded: /usr/lib/swift/libswift_Concurrency.dylib
  Referenced from: /Users/elo/code/AsyncAwait/.build/x86_64-apple-macosx/debug/AsyncAwait
  Reason: image not found

(I tried using swift run --toolchain … instead of xcrun --toolchain … swift run, with the same result.)

Thank you too, Alejandro. Your idea didn't work right away, but it got me on the right track. Turns out that the toolchain's lib/swift directory is present in my executable's @rpath (I checked this with otool -l). But here's otool -L for the executable, showing that libswift_Concurrency.dylib doesn't use @rpath and is hardcoded to /usr/lib/swift instead:

otool -L .build/x86_64-apple-macosx/debug/AsyncAwait
.build/x86_64-apple-macosx/debug/AsyncAwait:
	/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1770.106.0)
	/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.0.0)
	/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork (compatibility version 1.0.0, current version 1207.0.0)
	/usr/lib/swift/libswift_Concurrency.dylib (compatibility version 1.0.0, current version 0.0.0)
	@rpath/libswiftCore.dylib (compatibility version 1.0.0, current version 0.0.0)
	@rpath/libswiftCoreFoundation.dylib (compatibility version 1.0.0, current version 0.0.0, weak)
	@rpath/libswiftCoreGraphics.dylib (compatibility version 1.0.0, current version 0.0.0, weak)
	@rpath/libswiftDarwin.dylib (compatibility version 1.0.0, current version 0.0.0, weak)
	@rpath/libswiftDispatch.dylib (compatibility version 1.0.0, current version 0.0.0)
	@rpath/libswiftFoundation.dylib (compatibility version 1.0.0, current version 0.0.0)
	@rpath/libswiftIOKit.dylib (compatibility version 1.0.0, current version 1.0.0, weak)
	@rpath/libswiftObjectiveC.dylib (compatibility version 1.0.0, current version 0.0.0, weak)
	@rpath/libswiftSwiftOnoneSupport.dylib (compatibility version 1.0.0, current version 0.0.0)
	@rpath/libswiftXPC.dylib (compatibility version 1.0.0, current version 1.1.0, weak)

I was able to fix the path with this command:

 install_name_tool -change \
  /usr/lib/swift/libswift_Concurrency.dylib \
  /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2020-11-17-a.xctoolchain/usr/lib/swift/macosx/libswift_Concurrency.dylib \ 
  .build/x86_64-apple-macosx/debug/AsyncAwait

Is it a bug that libswift_Concurrency.dylib isn't linked using @rpath?

Does it work if you build a binary then execute it with DYLD_LIBRARY_PATH set? I wonder if the toolchains build with library validation or other security flags set on the swift binary that might cause the interpreter to disregard DYLD_LIBRARY_PATH.

1 Like

Yeah, that works! I can't say I understand why, but thanks!

This seems to be a result of System Integrity Protection on macOS. See my post here (and then one after that).

2 Likes

Wow. This is an exact validation of the closely-related issue SR-14008:

Library not loaded: /usr/lib/swift/libswift_Differentiation.dylib

@compnerd's System Integrity Protection hypothesis seems correct then. Thanks @ole!

1 Like

For others stuck trying to make this work while using a custom toolchain in Xcode (rather than on the command line), SIP only prevents using DYLD_LIBRARY_PATH for signed binaries.

If you build without signing (ie. set the "code signing identity" to blank in Build Settings) then you just need to set the right DYLD_LIBRARY_PATH in "Edit Scheme" to run your binary successfully from Xcode.

Note that you can use the shell substitutions in the Xcode run scheme Environment Variables UI -- in my case for last night's build, that is $(dirname $(xcrun --toolchain org.swift.50202104181a --find swift))/../lib/swift/macosx.

1 Like

One more long-tail follow up for anyone finding this thread by searching.

If you are using Xcode (vs CLI) to play around with this stuff, you don't need to change your signing or manually change your env vars.

First make sure that you have selected the version of the Swift toolchain you want from Xcode prefs.

In your target's Signing & Capabilities tab, scroll down to Hardened Runtime, and check the "Disable Library Validation" checkbox. Read the description; naturally you probably don't want this for shipping projects with 3rd-party libraries involved.

But for playing around, this seems to be enough to exempt your target from the DYLD_LIBRARY_PATH env var filtering being done for SIP-protected processes.

You don't need to manually add the env var to your scheme; it's automatically registered for the appropriate toolchain you've selected in Xcode's prefs.

I did, however, need to add to my Other Swift Flags both -Xfrontend -enable-experimental-concurrency (as documented in the related experimental concurrency proposals) and also -Xfrontend -disable-availability-checking in order to stop getting "detach is only available on macOS version 9999 and later" etc. errors.

2 Likes