Build Multiarch OpenSSL on OS X
Solution 1:
./Configure so that I get it built for both architectures into same .a file?
You have to be careful with OpenSSL and multiarch libraries because the library is not multiarch safe. That's because each configuration has its own <openssl/opensslconf.h>
file, and each platform's BIGNUM
is different.
Supplying -arch x86_64 -arch i386
will result in a build failure because of the way OpenSSL's build system forms commands. Also see Getting libcrypto ar error while compiling OpenSSL for Mac.
The same procedure detailed below applies to iOS, too. The only thing that changes is the -arch
. For iOS, you will probably use armv7
, armv7s
, arm64
and i386
(for 32-bit simulator debug) and x86_64
(for 64-bit simulator debug).
There's another not-so-apparent trick required. OpenSSL hard codes some default paths based on --prefix
and --openssldir
, so you have to build 32-bit for the installation directory, install, and then move it; then build 64-bit for the installation directory, install, and then move it; and then create the fat library in the install directory. Also see How to determine the default location for openssl.cnf?
Finally, do not replace OS X's supplied OpenSSL. OpenSSL 1.0.x and 1.1.x are not binary compatible with the Apple's 0.9.8 version of OpenSSL. Because of incompatibilities, the procedures below uses $HOME/ssl
. You can use /usr/local/ssl
or any other location that suits your taste.
Before you begin, OpenSSL wiki has a page on Compilation and Installation. There's lots of options to supply to config
. Choose the ones that suit your taste. I always use no-ssl2
, and usually use no-ssl3
, no-comp
. On mobile devices I use no-srp
, no-psk
, no-hw
, no-dso
and no-engines
.
Here are the instructions for building the library. You will configure, build, install and then move for each architecture you are supporting in your multiarch build.
32-bit
make clean && make dclean
KERNEL_BITS=32 ./config no-ssl2 no-ssl3 --prefix=$HOME/ssl
make depend
make
make install_sw
mv $HOME/ssl/include/openssl/opensslconf.h $HOME/ssl/include/openssl/opensslconf-x86.h
mv $HOME/ssl/include/openssl/bn.h $HOME/ssl/include/openssl/bn-x86.h
mv $HOME/ssl/ $HOME/ssl-x86
64-bit
make clean && make dclean
KERNEL_BITS=64 ./config no-ssl2 no-ssl3 --prefix=$HOME/ssl
make depend
make
make install_sw
mv $HOME/ssl/include/openssl/opensslconf.h $HOME/ssl/include/openssl/opensslconf-x64.h
mv $HOME/ssl/include/openssl/bn.h $HOME/ssl/include/openssl/bn-x64.h
mv $HOME/ssl/ $HOME/ssl-x64
Headers
You need to copy one set of headers (it does not matter which), copy opensslconf-x86.h
, opensslconf-x64.h
bn-x86.h
and bn-x64.h
, create a new <openssl/opensslconf.h>
, create a new <openssl/bn.h>
, and finally create the multiarch libraries.
rm -rf $HOME/ssl
mkdir -p $HOME/ssl/bin
mkdir -p $HOME/ssl/include/openssl
mkdir -p $HOME/ssl/lib
cp $HOME/ssl-x86/openssl.cnf $HOME/ssl/openssl.cnf
cp $HOME/ssl-x86/include/openssl/* $HOME/ssl/include/openssl
cp $HOME/ssl-x86/include/openssl/opensslconf-x86.h $HOME/ssl/include/openssl/opensslconf-x86.h
cp $HOME/ssl-x64/include/openssl/opensslconf-x64.h $HOME/ssl/include/openssl/opensslconf-x64.h
cp $HOME/ssl-x86/include/openssl/bn-x86.h $HOME/ssl/include/openssl/bn-x86.h
cp $HOME/ssl-x64/include/openssl/bn-x64.h $HOME/ssl/include/openssl/bn-x64.h
New <opensslconf.h>
If you have not done so, create $HOME/ssl/include/openssl/opensslconf.h
. Be sure you use a new header guard (OPENSSL_MULTIARCH_CONF_HEADER
):
cat $HOME/ssl/include/openssl/opensslconf.h
#ifndef OPENSSL_MULTIARCH_CONF_HEADER
#define OPENSSL_MULTIARCH_CONF_HEADER
#if __i386 || __i386__
# include "opensslconf-x86.h"
#elif __x86_64 || __x86_64__ || __amd64 || __amd64__
# include "opensslconf-x64.h"
#else
# error Unknown architecture
#endif
#endif /* OPENSSL_MULTIARCH_CONF_HEADER */
New <bn.h>
Create $HOME/ssl/include/openssl/bn.h
. Be sure you use a new header guard (OPENSSL_MULTIARCH_BN_HEADER
):
cat $HOME/ssl/include/openssl/bn.h
#ifndef OPENSSL_MULTIARCH_BN_HEADER
#define OPENSSL_MULTIARCH_BN_HEADER
#if __i386 || __i386__
# include "bn-x86.h"
#elif __x86_64 || __x86_64__ || __amd64 || __amd64__
# include "bn-x64.h"
#else
# error Unknown architecture
#endif
#endif /* OPENSSL_MULTIARCH_BN_HEADER */
Libraries
At this point, you have a x86 build of the library located at $HOME/ssl-x86
and a x64 build of the library located at $HOME/ssl-x64
. You combine them with lipo
at $HOME/ssl
.
lipo -create $HOME/ssl-x86/lib/libcrypto.a \
$HOME/ssl-x64/lib/libcrypto.a \
-output $HOME/ssl/lib/libcrypto.a
lipo -create $HOME/ssl-x86/lib/libssl.a \
$HOME/ssl-x64/lib/libssl.a \
-output $HOME/ssl/lib/libssl.a
lipo -create $HOME/ssl-x86/bin/openssl \
$HOME/ssl-x64/bin/openssl \
-output $HOME/ssl/bin/openssl
Share Libraries
If you configured with shared
, then you need to perform:
lipo -create $HOME/ssl-x86/lib/libcrypto.1.0.0.dylib \
$HOME/ssl-x64/lib/libcrypto.1.0.0.dylib \
-output $HOME/ssl/lib/libcrypto.1.0.0.dylib
lipo -create $HOME/ssl-x86/lib/libssl.1.0.0.dylib \
$HOME/ssl-x64/lib/libssl.1.0.0.dylib \
-output $HOME/ssl/lib/libssl.1.0.0.dylib
Then, you need to recreate the softlinks:
ln -s $HOME/ssl/lib/libcrypto.dylib $HOME/ssl/lib/libcrypto.1.0.0.dylib
ln -s $HOME/ssl/lib/libssl.dylib $HOME/ssl/lib/libssl.1.0.0.dylib
Finally, test things. Verify the libraries are multiarch:
ls $HOME/ssl/lib/
libcrypto.a libssl.a
lipo -info $HOME/ssl/lib/libcrypto.a
Architectures in the fat file: $HOME/ssl/lib/libcrypto.a are: i386 x86_64
lipo -info $HOME/ssl/lib/libssl.a
Architectures in the fat file: $HOME/ssl/lib/libssl.a are: i386 x86_64
And then a test program:
#include <openssl/opensslconf.h>
#include <openssl/ssl.h>
int main(int argc, char* argv[])
{
SSL_library_init();
return 0;
}
And:
$ clang -arch i386 -arch x86_64 -I $HOME/ssl/include test.c -o test.exe -L $HOME/ssl/lib -lssl -lcrypto
$ DYLD_LIBRARY_PATH=$HOME/ssl/lib; ./test.exe
$
DYLD_LIBRARY_PATH
is used in case you built the dynamic libraries on OS X.
If desired, you can delete the non-multiarch installations:
rm -rf $HOME/ssl-x86
rm -rf $HOME/ssl-x64