macosopensslx86-64i386

Build Multiarch OpenSSL on OS X


I need to build OpenSSL on OS X for 32 and 64 bit architectures. What are the options I need to give to ./Configure so that I get it built for both architectures into same .a file?


Solution

  • ./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