Universal Binaries
A Universal Binary is an executable or library with multiple CPU architectures on Apple platforms. Universal Binaries are sometimes referred to as "fat binaries" or "fat libraries". This wiki page will show you how to create a Universal Binary with multiple architectures using Apple's lipo
tool. The article is similar Build Multiarch OpenSSL on OS X on Stack Overflow. Also see Compiling for Multiple CPU Architectures and Universal Binary Programming Guidelines on the Apple Developer site.
The Crypto++ library is NOT safe to build Universal Binaries out of the box. The reason is, the Clang compiler does not accurately reflect the ISA using preprocessor macros, so config_asm.h
is not accurate and plain wrong is some instances. For example, Arm64 lacks CRC32 and polynomial multiplies even though Clang and the processor support the instructions. Because of problems with config_asm.h
, you often have to update config_asm.h
manually or with configure.sh. configure.sh
only handles one ISA, so it is not possible to generate a new config_asm.h
with multiple CPU architectures.
configure.sh
updates both config_asm.h
and config_cxx.h
. Even though we only need to fixup config_asm.h
, the procedure below fixes both config_asm.h
and config_cxx.h
. config_cxx.h
is a problem because Clang is also inaccurate with respect to C++ features. Also see Bug 39631, __has_feature(cxx_exceptions) returns true but fails to compile std::uncaught_exceptions().
The same procedure applies to other Apple platforms, like armv7
and arm64
. ARM builds are a little trickier and require use of setenv-ios.sh
to setup the cross-compile environment. They also require more unique names based on SDK. For example, both iPads and AppleTV are arm64, but they use different SDKs. Also see iOS (Xcode) and iOS (Command Line) wiki pages.
Universal Binary
The following is a procedure you can use to build an Universal Binary on OS X that include 32-bit i386 and 64-bit x86_64. The procedure uses an install or destination directory of cryptopp-universal
. The working or source directory to build the artifacts is cryptopp
.
The procedure below uses configure.sh
to configure the library for the architecture under the Clang compiler. Before you begin you should copy configure.sh
to the working directory. Detailed instructions on using the script are located at configure.sh.
$ cd cryptopp $ cp TestScripts/configure.sh . $ chmod u+x *.sh
32-bit i386
The first step is to build the library for i386 with an install directory of cryptopp-universal
. Before building you have to reconfigure the library for i386 using configure.sh
. You should use the same CXX
and CXXFLAGS
for this part of the configure and make steps.
$ CXX=clang++ CXXFLAGS="-DNDEBUG -g2 -O3 -arch i386" ./configure.sh Configuring for x86 Compiler: /usr/bin/clang++ Linker: /usr/bin/ld
Once reconfigured build the library. Use the same CXX
and CXXFLAGS
for make
.
$ CXX=clang++ CXXFLAGS="-DNDEBUG -g2 -O3 -arch i386" make -j 5 Using testing flags: -DNDEBUG -g2 -O3 -arch i386 clang++ -pthread -pipe -DNDEBUG -g2 -O3 -arch i386 -c cryptlib.cpp clang++ -pthread -pipe -DNDEBUG -g2 -O3 -arch i386 -c cpu.cpp clang++ -pthread -pipe -DNDEBUG -g2 -O3 -arch i386 -c integer.cpp ...
Next, install the library at cryptopp-universal
.
$ make install PREFIX=../cryptopp-universal cp *.h ../cryptopp-universal/include/cryptopp chmod 0644 ../cryptopp-universal/include/cryptopp/*.h cp libcryptopp.a ../cryptopp-universal/lib chmod 0644 ../cryptopp-universal/lib/libcryptopp.a cp cryptest.exe ../cryptopp-universal/bin chmod 0755 ../cryptopp-universal/bin/cryptest.exe ...
Once the library is installed you have to rename config_asm.h
and config_cxx.h
. This keeps the arch specific headers from being overwritten for the next architecture. The missing config_asm.h
and config_cxx.h
will be fixed later.
mv ../cryptopp-universal/include/cryptopp/config_asm.h ../cryptopp-universal/include/cryptopp/config_asm_i386.h mv ../cryptopp-universal/include/cryptopp/config_cxx.h ../cryptopp-universal/include/cryptopp/config_cxx_i386.h
Once the headers are renamed the headers guards need to be fixed.
sed -i '' 's/CRYPTOPP_CONFIG_ASM_H/CRYPTOPP_CONFIG_ASM_i386_H/g' ../cryptopp-universal/include/cryptopp/config_asm_i386.h sed -i '' 's/CRYPTOPP_CONFIG_CXX_H/CRYPTOPP_CONFIG_CXX_i386_H/g' ../cryptopp-universal/include/cryptopp/config_cxx_i386.h
Rename the library like the headers. The missing libcryptopp.a
will be fixed later.
mv ../cryptopp-universal/lib/libcryptopp.a ../cryptopp-universal/lib/libcryptopp_i386.a
Rename the executable like the headers. The missing cryptest.exe
will be fixed later.
mv ../cryptopp-universal/bin/cryptest.exe ../cryptopp-universal/bin/cryptest_i386.exe
Finally, clean the artifacts.
$ make distclean ...
64-bit x86_64
The second step is to build the library for x86_64 with an install directory of cryptopp-universal
. Before building you have to reconfigure the library for x86_64 using configure.sh
. You should use the same CXX
and CXXFLAGS
for this part of the configure and make steps.
$ CXX=clang++ CXXFLAGS="-DNDEBUG -g2 -O3 -arch x86_64" ./configure.sh Configuring for x86_64 Compiler: /usr/bin/clang++ Linker: /usr/bin/ld
Once reconfigured build the library. Use the same CXX
and CXXFLAGS
for make
.
$ CXX=clang++ CXXFLAGS="-DNDEBUG -g2 -O3 -arch x86_64" make -j 5 Using testing flags: -DNDEBUG -g2 -O3 -arch x86_64 clang++ -pthread -pipe -DNDEBUG -g2 -O3 -arch x86_64 -c cryptlib.cpp clang++ -pthread -pipe -DNDEBUG -g2 -O3 -arch x86_64 -c cpu.cpp clang++ -pthread -pipe -DNDEBUG -g2 -O3 -arch x86_64 -c integer.cpp ...
Next, install the library at cryptopp-universal
.
$ make install PREFIX=../cryptopp-universal cp *.h ../cryptopp-universal/include/cryptopp chmod 0644 ../cryptopp-universal/include/cryptopp/*.h cp libcryptopp.a ../cryptopp-universal/lib chmod 0644 ../cryptopp-universal/lib/libcryptopp.a cp cryptest.exe ../cryptopp-universal/bin chmod 0755 ../cryptopp-universal/bin/cryptest.exe ...
Once the library is installed you have to rename config_asm.h
and config_cxx.h
. This keeps the arch specific headers from being overwritten for the next architecture. The missing config_asm.h
and config_cxx.h
will be fixed later.
mv ../cryptopp-universal/include/cryptopp/config_asm.h ../cryptopp-universal/include/cryptopp/config_asm_x86_64.h mv ../cryptopp-universal/include/cryptopp/config_cxx.h ../cryptopp-universal/include/cryptopp/config_cxx_x86_64.h
Once the headers are renamed the headers guards need to be fixed.
sed -i '' 's/CRYPTOPP_CONFIG_ASM_H/CRYPTOPP_CONFIG_ASM_x86_64_H/g' ../cryptopp-universal/include/cryptopp/config_asm_x86_64.h sed -i '' 's/CRYPTOPP_CONFIG_CXX_H/CRYPTOPP_CONFIG_CXX_x86_64_H/g' ../cryptopp-universal/include/cryptopp/config_cxx_x86_64.h
Rename the library like the headers. The missing libcryptopp.a
will be fixed later.
mv ../cryptopp-universal/lib/libcryptopp.a ../cryptopp-universal/lib/libcryptopp_x86_64.a
Rename the executable like the headers. The missing cryptest.exe
will be fixed later.
mv ../cryptopp-universal/bin/cryptest.exe ../cryptopp-universal/bin/cryptest_x86_64.exe
Finally, clean the artifacts.
$ make distclean ...
Combining artifacts
At this point all of the artifacts have been installed in cryptopp-universal
. Some of the artifacts have an i386
suffix, and some of the artifacts have an x86_64
suffix. They need to be combined using lipo
. The missing config_asm.h
and config_cxx.h
also need to be fixed.
The first step is fix the missing config_asm.h
and config_cxx.h
. Add a new config_asm.h
and config_cxx.h
with the contents shown below.
$ cat ../cryptopp-universal/include/cryptopp/config_asm.h #ifndef CRYPTOPP_CONFIG_ASM_H #define CRYPTOPP_CONFIG_ASM_H #if __x86_64 || __x86_64__ || __amd64 || __amd64__ # include "config_asm_x86_64.h" #elif __i386 || __i386__ # include "config_asm_i386.h" #else # error Unknown architecture #endif #endif // CRYPTOPP_CONFIG_ASM_H
And:
$ cat ../cryptopp-universal/include/cryptopp/config_cxx.h #ifndef CRYPTOPP_CONFIG_CXX_H #define CRYPTOPP_CONFIG_CXX_H #if __x86_64 || __x86_64__ || __amd64 || __amd64__ # include "config_cxx_x86_64.h" #elif __i386 || __i386__ # include "config_cxx_i386.h" #else # error Unknown architecture #endif #endif // CRYPTOPP_CONFIG_CXX_H
Next, use lipo
to combine libcryptopp.a
and cryptest.exe
.
lipo -create \ ../cryptopp-universal/lib/libcryptopp_i386.a \ ../cryptopp-universal/lib/libcryptopp_x86_64.a \ -output ../cryptopp-universal/lib/libcryptopp.a lipo -create \ ../cryptopp-universal/bin/cryptest_i386.exe \ ../cryptopp-universal/bin/cryptest_x86_64.exe \ -output ../cryptopp-universal/bin/cryptest.exe
Verifying artifacts
You can use lipo
to verify the universal binariues.
$ lipo -info ../cryptopp-universal/lib/libcryptopp.a Architectures in the fat file: ../cryptopp-universal/lib/libcryptopp.a are: i386 x86_64 $ lipo -info ../cryptopp-universal/bin/cryptest.exe Architectures in the fat file: ../cryptopp-universal/bin/cryptest.exe are: i386 x86_64
You can also run cryptest.exe
under each architecture. Notice sizeof(word) == 4
on i386.
$ cd cryptopp-universal/bin $ arch -i386 ./cryptest.exe v Using seed: 1608249965 Testing Settings... passed: Your machine is little endian. passed: Aligned data access. passed: sizeof(byte) == 1 passed: sizeof(word16) == 2 passed: sizeof(word32) == 4 passed: sizeof(word64) == 8 passed: sizeof(hword) == 2, sizeof(word) == 4, sizeof(dword) == 8 passed: cacheLineSize == 64 hasSSE2 == 1, hasSSSE3 == 1, hasSSE4.1 == 1, hasSSE4.2 == 0, hasAVX == 0, hasAVX 2 == 0, hasAESNI == 0, hasCLMUL == 0, hasRDRAND == 0, hasRDSEED == 0, hasSHA == 0, isP4 == 0 ...
And x86_64. Notice sizeof(word) == 8
on x86_64.
$ cd cryptopp-universal/bin $ arch -x86_64 ./cryptest.exe v Using seed: 1608250036 Testing Settings... passed: Your machine is little endian. passed: Aligned data access. passed: sizeof(byte) == 1 passed: sizeof(word16) == 2 passed: sizeof(word32) == 4 passed: sizeof(word64) == 8 passed: sizeof(word128) == 16 passed: sizeof(hword) == 4, sizeof(word) == 8, sizeof(dword) == 16 passed: cacheLineSize == 64 hasSSE2 == 1, hasSSSE3 == 1, hasSSE4.1 == 1, hasSSE4.2 == 0, hasAVX == 0, hasAVX 2 == 0, hasAESNI == 0, hasCLMUL == 0, hasRDRAND == 0, hasRDSEED == 0, hasSHA == 0, isP4 == 0 ...
Dynamic library
Crypto++'s default make
rule does not build a dynamic library. The default recipe in GNUmakefile only builds cryptest.exe
and libcryptopp.a
:
LINK_LIBRARY ?= libcryptopp.a ... .PHONY: default default: cryptest.exe ... cryptest.exe: $(LINK_LIBRARY) $(TESTOBJS) $(CXX) -o $@ ...
If you want to build libcryptopp.dylib
with cryptest.exe
and libcryptopp.a
, then issue make all
:
.PHONY: all static dynamic all: static dynamic cryptest.exe
You will also need to rename the dynamic libraries, and then combine them using lipo
:
lipo -create \ ../cryptopp-universal/lib/libcryptopp_i386.dylib \ ../cryptopp-universal/lib/libcryptopp_x86_64.dylib \ -output ../cryptopp-universal/lib/libcryptopp.dylib
Downloads
No downloads available.