How to build openssl for M1 and for Intel?
Asked Answered
W

2

15

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!

Whipping answered 31/8, 2021 at 16:41 Comment(4)
Use command lipo to combine. cutecoder.org/programming/compile-open-ssl-apple-silicon (Creating Universal Libraries). Some parts of the tutorial have already been implemented by openssl team.Zuckerman
I'm confused as to why your artifact is called lcrypto.a in the first place. Intuitively, that seems wrong as a compiler/linker would never find that file when using -lcrypto.Hesiod
@MarekH if I could mark your answer as correct, I would. That was just the ticket. Thank you.Whipping
Does this answer your question? Build Multiarch OpenSSL on OS XZuckerman
Z
21

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.

Zuckerman answered 2/9, 2021 at 11:31 Comment(3)
while using those commands, don't forget to switch architectures, either with switching terminals or by using the command arch -arch x86_64 ./Configure ... or arch -arch arm64 ./Configure ....Nagoya
That's very succinct; thanks.About install_name_tool: Does the name have to match the filename? My application expects merely libssl.dylib and libcrypto.dylib. I don't know what this tool does, so is having a non-versioned generic name problematic? And is its current path significant?Groome
I used this for openssl-3.1.4, and it worked successfully. Thanks !!Okoka
F
3

Even though this question already has an accepted answer, I'd like to mention that I found an easier way to do this that doesn't require using lipo, just in case it helps make someone else's life easier.

The trick is to force it to compile for both architectures simultaneously.

Before calling Configure in the openssl source directory, create a file somewhere convenient (for the purposes of explaining I'll just have it in the home folder) named cc, and have it contain the following text:

#!/bin/bash

if [[ $* == *-arch\ x86_64* ]] && ! [[ $* == *-arch\ arm64* ]]; then
    echo Forcing compilation with arm64
    cc -arch arm64 $@
else
    cc $@
fi

That script will automatically add -arch arm64 to any compilation command that only includes -arch x86_64, and leave all other compilation commands unmodified.

Give it execute permissions:

chmod a+x ~/cc

Then execute the following in your shell to force compilation with this shell script:

export CC=/Users/yourname/cc

Then proceed with configuring and building as though for arm64, but tell it to compile as x86_64:

./Configure enable-rc5 zlib no-asm darwin64-x86_64-cc
make
make install

The resulting static libs and dylibs will already be x86_64 / arm64 universal!

Fealty answered 26/1, 2023 at 18:26 Comment(2)
What if I am on M1 and want to build for Intel/universal? Will the script change? And can I use the same method for any other package like krb, nghttp2, curl etc?Woorali
I think this same approach will work on M1 as well, since building on Apple Silicon doesn't really work any differently compared to Intel in that you can still target either platform you want. And in theory you could use this same approach to build universal versions of any C or C++ binary, though I'm sure it'll require tinkering depending on what build system the project uses and how it constructs its compilation commands.Fealty

© 2022 - 2024 — McMap. All rights reserved.