Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to link against third party library like openssl #192

Open
dscreve opened this issue Mar 2, 2025 · 10 comments
Open

Unable to link against third party library like openssl #192

dscreve opened this issue Mar 2, 2025 · 10 comments

Comments

@dscreve
Copy link

dscreve commented Mar 2, 2025

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 ?

@xtremekforever
Copy link
Contributor

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?

@dscreve
Copy link
Author

dscreve commented Mar 2, 2025

I have a systemLibrary :

  .systemLibrary(
        name: "OpenSSL",
        pkgConfig: "openssl",
        providers: [
            .apt(["openssl libssl-dev"]),
            .brew(["openssl"]),
        ])

@xtremekforever
Copy link
Contributor

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!

@dscreve
Copy link
Author

dscreve commented Mar 2, 2025

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.

@t089
Copy link

t089 commented Mar 2, 2025

Try setting the pkg config search path. See here: #89 (comment)

@xtremekforever
Copy link
Contributor

xtremekforever commented Mar 3, 2025

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? swiftlang/swift-package-manager#7472

@euanh
Copy link
Contributor

euanh commented Mar 3, 2025

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? swiftlang/swift-package-manager#7472

This is swiftlang/swift-package-manager#7409

@dscreve
Copy link
Author

dscreve commented Mar 3, 2025

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? 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.

@euanh
Copy link
Contributor

euanh commented Mar 6, 2025

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 swiftlang/swift#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

@dscreve
Copy link
Author

dscreve commented Mar 8, 2025

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants