Unable to link against third party library like openssl
Running an SDK on a Mac.
I'm linking against libcrypto (openSSL) and it does not work.
I have installed libssl-dev in docker Image as below : RUN apt update && apt -y install libssl-dev && apt -y clean
But I don't know how to tell him to add it in the generated SDK and I have the following error : ld.lld: error: undefined symbol: RAND_bytes (and many others for the all call to libcrypto).
I have openSSL installed on the Mac, and It seems to try to look in the Mac library : ld.lld: warning: /opt/homebrew/Cellar/openssl@3/3.0.7/lib/libssl.a: archive member 'libssl-lib-bio_ssl.o' is neither ET_REL nor LLVM bitcode
How to fix this ?
So my question is, how are you trying to link against libcrypto? Are you trying to use an -Xswiftc flag, or do you have it in your Package.swift as a systemLibrary?
I have a systemLibrary :
.systemLibrary(
name: "OpenSSL",
pkgConfig: "openssl",
providers: [
.apt(["openssl libssl-dev"]),
.brew(["openssl"]),
])
Okay, I did a little testing on my Linux system and found that if I include the pkgConfig: "openssl" line that swiftpm tries to look for -lssl on the host instead of in the target Swift SDK. So, if you remove that line, then it should start looking for libssl inside of the Swift SDK sysroot instead.
I didn't generate a Swift SDK with libssl-dev installed myself, but I got this result:
ld.lld: error: unable to find library -lssl
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Please give this a try!
This works fine. But there somethings strange when compiling on the mac using the SDK.
When compiling C source code with SDK, it seems to use local header and not headers in SDK and does not conform to header search path. For example, it uses /opt/local/include/openssl/ossl_typ.h instead of /opt/homebrew/Cellar/openssl@3/3.0.7/include/openssl/ossl_typ.h and not the file in the SDK.
Try setting the pkg config search path. See here: https://github.com/swiftlang/swift-sdk-generator/issues/89#issuecomment-2071971884
On my end in Linux, looks like setting these paths causes pkgconfig to work properly with the Swift SDK:
export PKG_CONFIG_SYSROOT_DIR=~/.swiftpm/swift
-sdks/6.0.3-RELEASE_ubuntu_jammy_aarch64.artifactbundle/6.0.3-RELEASE_ubuntu_jammy_aarch64/aarch64-unknown-linux-gnu/ubuntu-jammy.sdk
export PKG_CONFIG_PATH=$PKG_CONFIG_SYSROOT_DIR/usr/lib/aarch64-linux-gnu/pkgconfig
The only problem is that this seems like it's a lot of work to set these variables just for pkgconfig to point to the Swift SDK directory. I wonder if this should be moved into SwiftPM to automatically set these when a --swift-sdk is used...or has it been in some way? https://github.com/swiftlang/swift-package-manager/pull/7472
The only problem is that this seems like it's a lot of work to set these variables just for pkgconfig to point to the Swift SDK directory. I wonder if this should be moved into SwiftPM to automatically set these when a --swift-sdk is used...or has it been in some way? https://github.com/swiftlang/swift-package-manager/pull/7472
This is https://github.com/swiftlang/swift-package-manager/issues/7409
On my end in Linux, looks like setting these paths causes pkgconfig to work properly with the Swift SDK:
export PKG_CONFIG_SYSROOT_DIR=~/.swiftpm/swift -sdks/6.0.3-RELEASE_ubuntu_jammy_aarch64.artifactbundle/6.0.3-RELEASE_ubuntu_jammy_aarch64/aarch64-unknown-linux-gnu/ubuntu-jammy.sdk export PKG_CONFIG_PATH=$PKG_CONFIG_SYSROOT_DIR/usr/lib/aarch64-linux-gnu/pkgconfigThe only problem is that this seems like it's a lot of work to set these variables just for pkgconfig to point to the Swift SDK directory. I wonder if this should be moved into SwiftPM to automatically set these when a --swift-sdk is used...or has it been in some way? swiftlang/swift-package-manager#7472
This does not work when host is macOS. it still continues to use the macOS header : I use the function EVP_MD_get_type() which is declared in ubuntu-jammy.sdk/usr/include/openssl/evp.h but it tried to open /usr/local/include/openssl/evp.h on macOS, which does not contains declaration. It seems to ignore openssl.pc configuration.
I was able to build a binary on macOS and run it on Linux using the following steps. It seems to have found and linked OpenSSL and a very basic function call seems to return reasonable output. Does this work for you?
Build the container image
FROM swift:6.0.3-bookworm
RUN apt update && apt -y install openssl libssl-dev pkg-config && apt -y clean
It's not necessary to install pkg-config in the container, but I left it in.
docker build --platform linux/arm64 -t dlta-swift-docker-arm64 -f Dockerfile .
Build the SDK
On a macOS host (aarch64, in this case), run SDK generator to extract the SDK. If you are using the OSS toolchain on macOS, -Xswiftc -disable-round-trip-debug-types works around https://github.com/swiftlang/swift/issues/79722:
swift run -Xswiftc -disable-round-trip-debug-types swift-sdk-generator make-linux-sdk --from-container-image dlta-swift-docker-arm64 --sdk-name 6.0.3-bookworm-arm64
Note: I simplified this command by removing --with-docker, which is implied by --from-container-image, and --target, which does not make any difference in this context. SDKs generated with and without these flags are identical.
Install the SDK
swift sdk install Bundles/6.0.3-bookworm-arm64
Prepare a demo project
Package.swift
// swift-tools-version: 6.0
import PackageDescription
let package = Package(
name: "openssl-test",
targets: [
.executableTarget(
name: "openssl-test",
dependencies: ["OpenSSL"]),
.systemLibrary(
name: "OpenSSL",
pkgConfig: "openssl",
providers: [
.apt(["openssl libssl-dev"]),
.brew(["openssl"]),
])
]
)
Sources/openssl-test/main.swift
import OpenSSL
guard let sha256 = EVP_MD_fetch(nil, "sha256", nil) else {
fatalError("failed to fetch sha256")
}
print("sha256 ptr: \(sha256)")
let oid = EVP_MD_get_type(sha256)
print("sha256 oid: \(oid)")
print("expected NID_sha256: \(NID_sha256)") // defined in 6.0.3-bookworm-arm64/aarch64-unknown-linux-gnu/ubuntu-jammy.sdk/usr/include/openssl/obj_mac.h
Sources/OpenSSL/module.modulemap
module OpenSSL [system] {
header "openssl_shim.h"
export *
}
Sources/OpenSSL/openssl_shim.h
// openssl.h
#pragma once
#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
Build the binary using the SDK
⚠ Please double-check that $TOOLCHAINS, $PKG_CONFIG_SYSROOT_DIR and $PKG_CONFIG_PATH are set before building.
% export TOOLCHAINS=$(plutil -extract CFBundleIdentifier raw $HOME/Library/Developer/Toolchains/swift-latest.xctoolchain/Info.plist)
% export PKG_CONFIG_SYSROOT_DIR=$HOME/.swiftpm/swift-sdks/6.0.3-bookworm-arm64.artifactbundle/6.0.3-bookworm-arm64/aarch64-unknown-linux-gnu/ubuntu-jammy.sdk
% export PKG_CONFIG_PATH=$PKG_CONFIG_SYSROOT_DIR/usr/lib/aarch64-linux-gnu/pkgconfig
% swift build --swift-sdk 6.0.3-bookworm-arm64 --static-swift-stdlib
Building for debugging...
<unknown>:0: warning: libc not found for 'aarch64-unknown-linux-gnu'; C stdlib may be unavailable
<unknown>:0: warning: libc not found for 'aarch64-unknown-linux-gnu'; C stdlib may be unavailable
[8/8] Linking openssl-test
Build complete! (1.96s)
% file .build/aarch64-unknown-linux-gnu/debug/openssl-test
.build/aarch64-unknown-linux-gnu/debug/openssl-test: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, with debug_info, not stripped
I'm using --static-swift-stdlib so this binary will run on any Linux machine with OpenSSL, without requiring the Swift runtime libraries to be installed. Omitting --static-swift-stdlib also works, but you need to install the Swift runtime on the target system.
I'm setting TOOLCHAINS to pick up my locally-installed OSS toolchain; you may have installed it somewhere different.
I do have a copy of openssl installed by brew on this system:
% brew list | grep openssl
openssl@3
To make sure that the headers from brew are not being used for the cross build, I commented out the definition of EVP_MD_get_type in /opt/homebrew/Cellar/openssl@3/3.3.2/include/openssl/evp.h and /opt/homebrew/Cellar/openssl@3/3.4.0/include/openssl/evp.h and verified that this breaks the build when building on macOS:
% unset PKG_CONFIG_SYSROOT_DIR
% unset PKG_CONFIG_PATH
% swift build
Building for debugging...
error: emit-module command failed with exit code 1 (use -v to see invocation)
.../Sources/openssl-test/main.swift:11:11: error: cannot find 'EVP_MD_get_type' in scope
9 | print("sha256 ptr: \(sha256)")
10 |
11 | let oid = EVP_MD_get_type(sha256)
| `- error: cannot find 'EVP_MD_get_type' in scope
12 | print("sha256 oid: \(oid)")
13 | print("NID_sha256: \(NID_sha256)")
.../Sources/openssl-test/main.swift:11:11: error: cannot find 'EVP_MD_get_type' in scope
9 | print("sha256 ptr: \(sha256)")
10 |
11 | let oid = EVP_MD_get_type(sha256)
| `- error: cannot find 'EVP_MD_get_type' in scope
12 | print("sha256 oid: \(oid)")
13 | print("NID_sha256: \(NID_sha256)")
This implies that these headers are not being used for the successful cross-build above.
Run the binary on Linux
On Ubuntu aarch64:
$ ldd openssl-test
linux-vdso.so.1 (0x0000ffffa0f25000)
libstdc++.so.6 => /lib/aarch64-linux-gnu/libstdc++.so.6 (0x0000ffffa0690000)
libm.so.6 => /lib/aarch64-linux-gnu/libm.so.6 (0x0000ffffa05e0000)
libssl.so.3 => /lib/aarch64-linux-gnu/libssl.so.3 (0x0000ffffa0510000)
libcrypto.so.3 => /lib/aarch64-linux-gnu/libcrypto.so.3 (0x0000ffffa0090000)
libgcc_s.so.1 => /lib/aarch64-linux-gnu/libgcc_s.so.1 (0x0000ffffa0050000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffff9fe90000)
/lib/ld-linux-aarch64.so.1 (0x0000ffffa0ee8000)
$ openssl-test
sha256 ptr: 0x0000aaab16f73b50
sha256 oid: 672
expected NID_sha256: 672
I did exactly what you describe (except the use of -disable-round-trip-debug-types that do not cause issue while compiling dk-generator), and It does not work.
% swift build --swift-sdk 6.0.3-bookworm-arm64 --static-swift-stdlib
Building for debugging...
warning: Could not read SDKSettings.json for SDK at: /Users/dscreve/Library/org.swift.swiftpm/swift-sdks/6.0.3-bookworm-arm64.artifactbundle/6.0.3-bookworm-arm64/aarch64-unknown-linux-gnu/ubuntu-jammy.sdk
error: emit-module command failed with exit code 1 (use -v to see invocation)
/Users/dscreve/Desktop/opensslDemo/Sources/openssl-test/main.swift:3:20: error: cannot find 'EVP_MD_fetch' in scope