Search code examples
macosopensslapple-siliconlibcrypto

How to build openssl for M1 and for Intel?


I have a project which needs to use Libcrypto - and I have two versions of Libcrypto (libcrypto.a (from OpenSSL 1.1.1) built for ARM64) and (lcrypto.a (from OpenSSL 1.0.2) for Intel). Leaving aside the issues of whether it's good practice or not to have two different versions, I can say that if I include libcrypto.a then I can build and run on M1 and it works fine on M1. If I include lcrypto.a then I can build and run on Intel and it works fine on Intel. What I can't do is include them both (linker error - The linked library 'lcrypto.a' is missing one or more architectures required by this target: arm64.) - and if I can't include them both then I can't build a fat binary, and my app is less than entirely useful!

My question is How can I include both in my project - or where can I get (and how can I include) a fat version of Libcrypto? I've looked at this https://github.com/balthisar/openssl-xcframeworks/releases and this https://developer.apple.com/forums/thread/670631 but I'm none the wiser. I think I built a Fat Binary - but the Fat Binary I thought that I built doesn't work for either architecture!


Solution

  • Use command lipo to combine binaries

    Compile Intel and ARM versions separately (arm version requires Xcode 12).

    export MACOSX_DEPLOYMENT_TARGET=10.9
    cp -r openssl-1.1.1w openssl-1.1.1w-arm64 
    cp -r openssl-1.1.1w openssl-1.1.1w-x86_x64 
    

    Build the Intel half

    cd openssl-1.1.1w-x86_x64 
    ./Configure darwin64-x86_64-cc shared
    make
    

    NOTE: For openssl-1.1.1q use -Wno-error=implicit-function-declaration as a configure parameter

    Build the Arm half

    export MACOSX_DEPLOYMENT_TARGET=10.15 /* arm64 only with Big Sur -> minimum might be 10.16 or 11.0 */)
    cd ../openssl-1.1.1w-arm64 
    ./Configure enable-rc5 zlib darwin64-arm64-cc no-asm
    make
    

    NOTE: For openssl-1.1.1q use -Wno-error=implicit-function-declaration as a configure parameter

    To create universal binary use command lipo:

    cd ..
    mkdir openssl-mac
    lipo -create openssl-1.1.1w-arm64/libcrypto.a openssl-1.1.1w-x86_x64/libcrypto.a -output openssl-mac/libcrypto.a
    lipo -create openssl-1.1.1w-arm64/libssl.a openssl-1.1.1w-x86_x64/libssl.a -output openssl-mac/libssl.a
    

    Verify that resulting binary contains both architectures:

    file libcrypto.a libssl.a
    libcrypto.a: Mach-O universal binary with 2 architectures: [x86_64:current ar archive random library] [arm64]
    libcrypto.a (for architecture x86_64):  current ar archive random library
    libcrypto.a (for architecture arm64):   current ar archive random library
    libssl.a: Mach-O universal binary with 2 architectures: [x86_64:current ar archive random library] [arm64]
    libssl.a (for architecture x86_64):  current ar archive random library
    libssl.a (for architecture arm64):   current ar archive random library
    

    PS: If you plan to use dynamic library combine dylib files using lipo and run instal_name_tool

    cd openssl-mac
    install_name_tool -id '@rpath/libcrypto.1.1.1.dylib' libcrypto.1.1.1.dylib
    install_name_tool -id '@rpath/libssl.1.1.dylib' libssl.1.1.dylib
    otool -D libssl.1.1.dylib /* to verify */
    

    Result:

    libssl.1.1.dylib:
    @rpath/libssl.1.1.dylib
    

    If you have issues using the library with XCode try to add -lz/-lcrypto into other linker flags (XCode Build settings) if you get compilation errors. Clean build folder after adding this flag.