Solaris (Command Line)

From Crypto++ Wiki
Jump to navigation Jump to search

Crypto++ supports the Solaris operating system, including x86, Sparc, GCC and SunCC. This wiki page will detail how to compile the Crypto++ library and programs on Solaris and how to avoid some of the incumbent platform problems, like SunCC crashes.

Crypto++ 5.6.3 and below had mediocre support for Solaris and Sun Studio because it was effectively running with CRYPTOPP_DISABLE_ASM, even on i86pc platforms. Crypto++ 5.6.4 added first class support for the Solaris Intel platform and benchmarks for 5.6.4 will run considerably faster than 5.6.3 and below. At Crypto++ 7.1 we got access to the OpenCSW compile farm and were able to clear several issues with Sparc. OpenCSW allowed us to test Solaris 9 through 11 on x86 and Sparc.

The Sun C++ compiler was endowed with AT&T-style inline ASM at SunCC 5.10, which is part of Sun Studio 12. Also see GCC-style asm inlining support in Sun Studio 12 compilers. The inline assembly support means there is opportunity to have Crypto++ perform as well on Solaris as it does other Linux and Unix platforms. Crypto++ first offered Sun C++ compiler integration at 5.6.4 with Commit b1df5736a7191eb1.

Be certain you have enough virtual memory before you attempt to compile some of the heavier files, like bench.cpp or regetst.cpp. If you don't have enough virtual memory, then you will experience bizarre unexplained failures when running gmake. We were surprised to learn our test machines were running out of virtual memory. Also see Verify there's enough memory and storage to compile a file? on Super User and How much swap space does Solaris 11.3 need to run a compiler? on U&L Stack Exchange.

Finally, be certain you use the same compiler, the same compiler options, and the same C++ runtimes to build the library and your programs. Do not mix and match them. Also see GNUmakefile | Creating Programs on the Crypto++ wiki. This nuance has caused so many issues over the years we cannot recount them all.

Make and GNUmake

You must use GNU's make to build the library on Solaris. Sun's default make program will produce unexplained errors, and CMake will use random flags.

$ cd cryptopp
$ make
make: Fatal error: No arguments to build

$ make -f GNUmakefile
make: Fatal error in reader: GNUmakefile, line 5: Badly formed macro assignment

By default, the GNUmakefile will use whatever the C++ compiler is. To build the library with the default compiler run gmake:

$ gmake -j 4
g++ -DNDEBUG -g2 -O2 -fPIC -march=native -m64 -Wa,--divide -pipe -c cryptlib.cpp
g++ -DNDEBUG -g2 -O2 -fPIC -march=native -m64 -Wa,--divide -pipe -c cpu.cpp
g++ -DNDEBUG -g2 -O2 -fPIC -march=native -m64 -Wa,--divide -pipe -c integer.cpp
...

If you want to use Sun's C++ compiler, then specify it in CXX. Also see C++ Compiler below for more on the Sun compiler.

$ CXX=/opt/solarisstudio12.4/bin/CC gmake -j 4
/opt/solarisstudio12.4/bin/CC -DNDEBUG -g -xO2 -m64 -KPIC -template=no%extdef -c cryptlib.cpp
/opt/solarisstudio12.4/bin/CC -DNDEBUG -g -xO2 -m64 -KPIC -template=no%extdef -c cpu.cpp
/opt/solarisstudio12.4/bin/CC -DNDEBUG -g -xO2 -m64 -KPIC -template=no%extdef -c integer.cpp
...

C++ Compiler

Oracle's C++ compiler is known as SunCC in Crypto++. Each version of Sun Studio or Solaris Studio will supply the compiler called CC. There will probably be a few of them installed if different version of Sun Studio are available:

$ find /opt -name CC | grep bin/CC
/opt/developerstudio12.5/bin/CC
/opt/solarisstudio12.4/bin/CC
/opt/solarisstudio12.3/bin/CC
/opt/solstudio12.2/bin/CC

SunCC differs from GCC in a number of ways. The SunCC does not consume -march=native, and there's no way to tell which -xarch was specified because the compiler does not signal it during compile time. Preprocessor defines, like __SSE2__, __SSE3__, __SSE4_1__, __SSE4_2__, __AES__, __BMI__, and __AVX__, are simply missing. This detail was the biggest gap to close when providing better SunCC support.

As of this writing, -std=c++03 and -std=c++11 are almost incompatible with -xarch when -xarch uses aes and above (i.e., -xarch=aes or -xarch=avx). See C++03 and C++11 below for more details.

Library Defines

Th Crypto++ library depends upon GCC style preprocessor macros like __SSE2__, __SSE3__ and __AES__ to enable code paths. Here's an example of a simple one-liner from misc.h that uses BMI's BLSR instruction:

#if defined(__GNUC__) && defined(__BMI__)
template <>
inline bool IsPowerOf2<word32>(const word32 &value)
{
    return value > 0 && _blsr_u32(value) == 0;
}
#endif

At Crypto++ 5.6.4 the library unconditionally defined __SSE2__ in config.h for SunCC at Commit b1df5736a7191eb1. By defining __SSE2__ in config.h all users enjoy at least SSE2 support. SSE2 is the majority of the specialized implementations, and it includes both SSE2 ASM and SSE2 intrinsics.

#if !defined(CRYPTOPP_DISABLE_ASM) && !defined(__SSE2__) && defined(__x86_64__) && (__SUNPRO_CC >= 0x5100)
# define __SSE2__ 1
#endif

To take advantage of additional CPU features you will have to manually define the missing preprocessor macros. The library's test script cryptest.sh does so in an effort to test the code paths by checking the cpu flags with isainfo -v, and then manually adding -D__XXX__ defines to CXXFLAGS. isainfo is similar to Linux's /proc/cpuinfo. Below is a sample output from isainfo.

$ isainfo -v
64-bit amd64 applications
        avx xsave pclmulqdq aes sse4.2 sse4.1 ssse3 popcnt tscp ahf cx16 sse3 
        sse2 sse fxsr mmx cmov amd_sysc cx8 tsc fpu rdrand 
32-bit i386 applications
        avx xsave pclmulqdq aes sse4.2 sse4.1 ssse3 popcnt tscp ahf cx16 sse3 
        sse2 sse fxsr mmx cmov sep cx8 tsc fpu rdrand

If all you want are the CXXFLAGS, then simply run cryptest.sh and note the PLATFORM_CXXFLAGS. Below is from a HP Proliant G5 with Dual-Xeon's.

$ ./cryptest.sh 

IS_SOLARIS: 1
IS_X64: 1
...
Compiler: Studio 12.5 Sun C++ 5.14 SunOS_i386 2016/05/31
Pathname: /opt/developerstudio12.5/bin/CC
...
PLATFORM_CXXFLAGS: -D__SSE2__ -D__SSE3__ -D__SSSE3__ -xarch=ssse3

Here is another example from a Solaris workstation running on a 4th generation Core i5. The 4th gen Core i5 provides up to AVX.

$ ./cryptest.sh 

IS_SOLARIS: 1
IS_X64: 1
...
Compiler: Sun C++ 5.13 SunOS_i386 2014/10/20
Pathname: /opt/solarisstudio12.4/bin/CC
...
PLATFORM_CXXFLAGS: -D__SSE2__ -D__SSE3__ -D__SSSE3__ -D__SSE4_1__ -D__SSE4_2__ -D__PCLMUL__
                   -D__AES__ -D__RDRND__ -D__AVX__ -xarch=avx

And here is one from a 5th generation Core i5, which includes BMI and ADX.

$ ./cryptest.sh 

IS_SOLARIS: 1
IS_X64: 1
...
Compiler: Studio 12.5 Sun C++ 5.14 SunOS_i386 2016/05/31
Pathname: /opt/developerstudio12.5/bin/CC
...
PLATFORM_CXXFLAGS: -D__SSE2__ -D__SSE3__ -D__SSSE3__ -D__SSE4_1__ -D__SSE4_2__ -D__PCLMUL__
                   -D__AES__ -D__RDRND__ -D__RDSEED__ -D__AVX__ -D__AVX2__ -D__BMI__ -D__BMI2__
                   -D__ADX__ -xarch=avx2_i

From the three example above, it can be seen -xarch=XXX is also a moving target dependent upon both CPU feature flags and compiler version. The compiler will give you a good error message with respect to -xarch, so it will be fairly easy to get right. Also see ube error: _mm_aeskeygenassist_si128 intrinsic requires at least -xarch=aes on Stack Overflow.

One non-obvious note you need -xarch=avx2_i to enable ADX. ADX provides Add-with-Carry/Add-with-Overflow, which allows pipelining some big integer operations. Also see New Instructions Supporting Large Integer Arithmetic on Intel Architecture Processors whitepaper.

Building Crypto++

Now that you know where the compiler and how to define flags, all you have to do is build the library and test it. All of this is covered in GNUmakefile, but its restated here for completeness. Since you are modifying the default CXXFLAGS, you also have to set the Debug/Release build configuration information. The build configuration information is the -DNDEBUG -g -O2 below.

The makefile will add the remainder of the flags, like -KPIC. If you need to change flags like -m64 or -native, then you will need to edit the makefile or pass them through CXXFLAGS as discussed in GNUMakefile.

# Set the preprocessor macros to enable code paths
$ export CXXFLAGS="-DNDEBUG -g -O2 -D__SSE2__ -D__SSE3__ -D__SSSE3__ -D__SSE4_1
__ -D__SSE4_2__ -D__PCLMUL__ -D__AES__ -D__RDRND__ -D__RDSEED__ -D__AVX__ -D__AV
X2__ -D__BMI__ -D__BMI2__ -D__ADX__ -xarch=avx2_i"

# Build it!
$ CXX=/opt/developerstudio12.5/bin/CC gmake -j 2

/opt/developerstudio12.5/bin/CC -DNDEBUG -g -O2 -D__SSE2__ -D__SSE3__ -D__SSSE3
__ -D__SSE4_1__ -D__SSE4_2__ -D__AES__ -D__PCLMUL__ -D__RDRND__ -D__RDSEED__ -D_
_AVX__ -D__AVX2__ -D__BMI__ -D__BMI2__ -D__ADX__ -xarch=avx2_i -m64 -KPI
C -template=no%extdef -c cryptlib.cpp
/opt/developerstudio12.5/bin/CC -DNDEBUG -g -O2 -D__SSE2__ -D__SSE3__ -D__SSSE3
__ -D__SSE4_1__ -D__SSE4_2__ -D__AES__ -D__PCLMUL__ -D__RDRND__ -D__RDSEED__ -D_
_AVX__ -D__AVX2__ -D__BMI__ -D__BMI2__ -D__ADX__ -xarch=avx2_i -m64 -KPI
C -template=no%extdef -c cpu.cpp
/opt/developerstudio12.5/bin/CC -DNDEBUG -g -O2 -D__SSE2__ -D__SSE3__ -D__SSSE3
__ -D__SSE4_1__ -D__SSE4_2__ -D__AES__ -D__PCLMUL__ -D__RDRND__ -D__RDSEED__ -D_
_AVX__ -D__AVX2__ -D__BMI__ -D__BMI2__ -D__ADX__ -xarch=avx2_i -m64 -KPI
C -template=no%extdef -c integer.cpp
/opt/developerstudio12.5/bin/CC -DNDEBUG -g -O2 -D__SSE2__ -D__SSE3__ -D__SSSE3
__ -D__SSE4_1__ -D__SSE4_2__ -D__AES__ -D__PCLMUL__ -D__RDRND__ -D__RDSEED__ -D_
_AVX__ -D__AVX2__ -D__BMI__ -D__BMI2__ -D__ADX__ -xarch=avx2_i -m64 -KPI
C -template=no%extdef -c shacal2.cpp
...

Testing Crypto++

Once the library builds, you must run the validation suite and test vectors. Solaris is effectively a new platform since the ASM was enabled en masse, so the build must be functionally tested to provide assurances that are taken for granted on Linux and Unix.

You run the tests with cryptest.exe v and cryptest.exe tv all. There should be 0 failures as shown below.

$ ./cryptest.exe v
Using seed: 1473740352

Testing Settings...
...
All tests passed!

And:

$ ./cryptest.exe tv all
Using seed: 1473740479

Testing FileList algorithm all.txt collection.
...
Tests complete. Total tests = 5248. Failed tests = 0.

Once the library tests OK, its ready to be installed and used by programs.

Mapfile

The makefile uses a mapfile on i86pc targets to allow object files to mask or hide additional hardware capability. The mapfile is named cryptopp.mapfile, and the recipe is shown below. The library does so because the it will often build for more capable machines. For example, the library will include SHA hardware instructions on an early iCore even though an early iCore cannot execute them.

You can disable the extra code with CRYPTOPP_DISABLE_XXX, where XXX is a feature like AESNI, AVX or SHA.

# For SunOS, create a Mapfile that allows our object files to
# contain additional bits (like SSE4 and AES on old Xeon)
ifeq ($(IS_SUN)$(SUN_COMPILER),11)
ifneq ($(IS_X86)$(IS_X32)$(IS_X64),000)
ifeq ($(findstring -DCRYPTOPP_DISABLE_ASM,$(CXXFLAGS)),)
ifeq ($(wildcard cryptopp.mapfile),)
$(shell echo "hwcap_1 = SSE SSE2 OVERRIDE;" > cryptopp.mapfile)
$(shell echo "" >> cryptopp.mapfile)
endif  # Write mapfile
LDFLAGS += -M cryptopp.mapfile
endif  # No CRYPTOPP_DISABLE_ASM
endif  # X86/X32/X64
endif  # SunOS

C++03 and C++11

As of this writing (September 2016), the library will fail to compile with Sun Studio 12.4/SunCC 5.13 with -std=c++03 or -std=c++11. We don't know why the compiler crashes as shown below, but we have an open question on Stack Overflow and we reached out to a friend of the project who works for Oracle.

First, this how the compile is supposed to look (using Sun Studio 12.5):

$ /opt/developerstudio12.5/bin/CC -std=c++03 -DNDEBUG -g -O2 -D__SSE2__ -D__SSE
3__ -D__SSSE3__ -D__SSE4_1__ -D__SSE4_2__ -D__AES__ -D__PCLMUL__ -D__RDRND__ -D_
_RDSEED__ -D__AVX__ -D__AVX2__ -D__BMI__ -D__BMI2__ -D__ADX__ -xarch=avx2_i -m64
 -KPIC -template=no%extdef -c gcm.cpp

The next two are the failures when using -std=c++03 or -std=c++11.

# Fail with C++03
$ /opt/solarisstudio12.4/bin/CC -std=c++03 -DNDEBUG -g -O2 -D__SSE2__ -D__SSE3_
_ -D__SSSE3__ -D__SSE4_1__ -D__SSE4_2__ -D__AES__ -D__PCLMUL__ -D__RDRND__ -D__A
VX__ -xarch=avx -m64 -KPIC -template=no%extdef -c gcm.cpp
 >> Assertion:   (../lnk/g3mangler.cc, line 825)
    while processing gcm.cpp at line 413.
# Fail with C++11
$ /opt/solarisstudio12.4/bin/CC -std=c++11 -DNDEBUG -g -O2 -D__SSE2__ -D__SSE3_
_ -D__SSSE3__ -D__SSE4_1__ -D__SSE4_2__ -D__AES__ -D__PCLMUL__ -D__RDRND__ -D__A
VX__ -xarch=avx -m64 -KPIC -template=no%extdef -c gcm.cpp
 >> Assertion:   (../lnk/g3mangler.cc, line 825)
    while processing gcm.cpp at line 413.

The -std=c++03 or -std=c++11 crash can be reproduced using Sun Studio 12.3/SunCC 5.12 by using upto -D__AES__ -D__PCLMUL__ and -xarch=aes.

We know of two rather poor work-arounds. First, you can clamp features at -D__SSE4_1__ -D__SSE4_2__ and -xarch=sse4_2. Second, you can avoid using -std=c++03 or -std=c++11.

AES-NI and CLMUL

As of this writing (September 2016), AES-NI and Carryless Multiply under Sun Studio 12.2/SunCC 5.11 is another Solaris issue we are struggling with. It does not appear to be related to C++03 and C++011 errors:

$ /opt/solarisstudio12.3/bin/CC -DNDEBUG -g -O2 -D__SSE2__ -D__SSE3__ -D__SSSE3_
_ -D__SSE4_1__ -D__SSE4_2__ -D__AES__ -D__PCLMUL__ -xarch=aes -m64 -KPIC -templa
te=no%extdef -c gcm.cpp

assertion failed in function bfd_asm_lf_dump() @ bfd_asm.c:3286
assert(mit_alternates_has_(op, IMM_ALTERNATE))

CC: ube failed for gcm.cpp

We isolated the issue to GCM_Reduce_CLMUL and reported it to Oracle though a private contact (we don't have a service contract). The work around for the issue is to avoid CLMUL when using Sun Studio 12.2 through 12.6. We disabled CLMUL in GCM for SunCC 5.11 through 5.15 in config.h:

// http://github.com/weidai11/cryptopp/issues/226
#if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x5150)
# undef CRYPTOPP_CLMUL_AVAILABLE
#endif

Additionally, testing reveals AES-NI fails to validate with SunStudio 12.3/SunCC 5.12 and below. AES-NI is disabled for SunStudio 12.3/SunCC 5.12 and below in config.h:

#if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x5130)
# undef CRYPTOPP_AESNI_AVAILABLE
#endif

SSE4.1 Block Ciphers

Crypto++ 6.0 added SIMON and SPECK block ciphers. Crypto++ 7.0 added CHAM, LEA and SIMECK lightweight block ciphers. The ciphers use SSE 4.1 if available. 32-bit i386 Solaris systems do not pass self tests when the library is built with SunStudio 12.3/SunCC 5.12 or lower. Later versions of SunStudio and the compiler are OK, and x86_64 is OK.

The following was to the top of header files simon.h, speck.h, cham.h, lea.h or simeck.h. If you are working on SunStudio 12.4/SunCC 5.13 or higher then you can safely remove the workarounds.

#if (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X86)
# define CRYPTOPP_CHAM_ADVANCED_PROCESS_BLOCKS 1
#endif

// Yet another SunStudio/SunCC workaround. Failed self tests
// in SSE code paths on i386 for SunStudio 12.3 and below.
#if defined(__SUNPRO_CC)
# undef CRYPTOPP_CHAM_ADVANCED_PROCESS_BLOCKS
#endif

String Folding

Sun Studio 12.6/SunCC 5.15 introduced a new bug where similar strings are incorrectly folded. It can be observed on Debug builds when the ASN.1 parser tests run. The failed self tests look like the following. Notice the same string produces a failure message because it is the wrong string. For example, the first failed test should use the string 038200020843, not 038200020043.

Testing ASN.1 parser...

passed:  accept BIT_STRING 030100
passed:  reject BIT_STRING 030108
...

passed:  accept BIT_STRING 038200020043
FAILED:  reject BIT_STRING 038200020043
...

passed:  accept BIT_STRING 03830000020043
FAILED:  reject BIT_STRING 03830000020043
...

We have an open question at Stack Overflow.

Multithreaded Program

If you are building multithreaded programs with Solaris the be sure to visit Compiling a Multithreaded Application in the Sun Technology Network pages. The Sun article discusses Solaris threads, Pthreads, fork behavior and options like -mt.

Also keep in mind that Crypto++ is thread safe at the object level, meaning each object has its own set of instance data. If multiple threads access the same object then the calling code must supply the locks.

It used to work!!!

If Crypto++ used to work for you under Crypto++ 5.6.2, but fails to work as expected under 5.6.3 or 5.6.4, then its probably due to GNUmakefile changes or changes in the source code. One of the biggest changes was the removal of CRYPTOPP_DISABLE_ASM for the i86pc platform.

You may be able to use Git to go back in time to help isolate the problem. But keep in mind Crypto++ 5.6.2's CRYPTOPP_DISABLE_ASM has a big impact on the way source code is compiled. The example below uses the Crypto++ 5.6.2 makefile to build the latest sources.

$ git clone http://github.com/weidai11/cryptopp cryptopp-past-and-present
Cloning into 'cryptopp-past-and-present'...
...

$ cd cryptopp-past-and-present
$ git checkout CRYPTOPP_5_6_2
Note: checking out 'CRYPTOPP_5_6_2'. You are in 'detached HEAD' state...
$ cp GNUmakefile GNUmakefile-5.6.2   # save the old makefile

$ git checkout master -f             # or 'checkout CRYPTOPP_5_6_5', etc

$ make -f GNUmakefile-5.6.2          # use the old makefile
c++ -DNDEBUG -g -O2 -DCRYPTOPP_DISABLE_ASM -pipe -c shacal2.cpp
c++ -DNDEBUG -g -O2 -DCRYPTOPP_DISABLE_ASM -pipe -c md5.cpp
c++ -DNDEBUG -g -O2 -DCRYPTOPP_DISABLE_ASM -pipe -c shark.cpp
c++ -DNDEBUG -g -O2 -DCRYPTOPP_DISABLE_ASM -pipe -c zinflate.cpp
...

We use the technique above often to determine if we introduced a break.