

                                 Compiling C++


SymCC has full support for C++ code and provides a wrapper "sym++" around
clang++. Since C++ programs typically depend on the C++ standard library, we
have two options when building them with SymCC:

1. Use the C++ standard library provided by the system. This is the easiest,
   requiring no additional effort, but it has an important drawback: data that
   passes through the standard library will be concretized, i.e., we lose track
   of the corresponding symbolic expressions.
2. The alternative is to build an instrumented C++ standard library. This means
   that we can track data through the library, but it requires building the
   library and compiling all code against it.

We discuss both approaches in more detail below.


               Building against the system's C++ standard library


In order to use the regular (uninstrumented) C++ standard library that the
system provides, just call sym++ as a drop-in replacement for clang++:

$ export SYMCC_REGULAR_LIBCXX=yes
$ sym++ -o myprogram mysource.cpp
$ ./myprogram

The program will execute and produce alternative outputs as usual with SymCC,
but it will not be able to trace operations that happen in C++ standard classes,
such as std::vector.


                     Instrumenting the C++ standard library


Building an instrumented C++ standard library is a one-time effort; the library
can then be used in all subsequent C++ compilations. We use "libc++", the LLVM
project's implementation of the standard library. First, get the source code:

$ git clone --depth 1 https://github.com/llvm/llvm-project.git

Then build the library with SymCC:

$ mkdir libcxx_symcc
$ cd libcxx_symcc
$ export SYMCC_REGULAR_LIBCXX=yes
$ export SYMCC_NO_SYMBOLIC_INPUT=yes
$ cmake -G Ninja /path-to-llvm-project/llvm \
    -DLLVM_ENABLE_PROJECTS="libcxx;libcxxabi" \
    -DLLVM_TARGETS_TO_BUILD="X86" \
    -DLLVM_DISTRIBUTION_COMPONENTS="cxx;cxxabi;cxx-headers" \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_PREFIX=/some/convenient/location \
    -DCMAKE_C_COMPILER=/path-to-symcc-with-simple-backend/symcc \
    -DCMAKE_CXX_COMPILER=/path-to-symcc-with-simple-backend/sym++
$ ninja distribution
$ ninja install-distribution
$ unset SYMCC_REGULAR_LIBCXX SYMCC_NO_SYMBOLIC_INPUT

Note the two environment variables: SYMCC_REGULAR_LIBCXX avoids a
chicken-and-egg problem - without it, SymCC would expect to compile against the
instrumented C++ standard library. SYMCC_NO_SYMBOLIC_INPUT disables symbolic
handling of input data - the build process of libc++ involves the creation of
helper programs that are subsequently run, and we do not want them to perform
symbolic analysis.

A word on the choice of backends: While the instrumented libc++ will work with
both backends, building it currently doesn't work with the QSYM backend. Just
use the simple backend for the build process - there is no problem in using the
library with the QSYM backend later. For very interested readers, here is an
explanation of the problem: libc++ is an LLVM project and as such uses LLVM
support code. During the build process, it builds a code-generation tool that is
subsequently invoked (hence the recommendation to set SYMCC_NO_SYMBOLIC_INPUT).
At run-time, the tool loads code built from the LLVM sources we obtained via git
above. Why is this a problem for the QSYM backend? QSYM uses support code from
LLVM as well, which means that the QSYM backend is linked against your system's
LLVM libraries. If we build libc++ with the QSYM backend, the code-generation
tool loads the QSYM code at run time and, via dependency resolution, also the
system's LLVM installation. The end result is that we have two versions of LLVM
support code in the same process - the system version and the one built from git
- which will most likely collide. Using the simple backend avoids the problem
because it doesn't depend on the system installation of LLVM.

Once the library is ready, tell SymCC where to find it and compile C++ code as
usual:

$ export SYMCC_LIBCXX_PATH=/path-provided-as-cmake-install-prefix-for-libcxx
$ sym++ -o myprogram mysource.cpp
$ ./myprogram

Now the program will use the instrumented C++ standard library, which enables it
to trace computations inside the library. Note that you need to compile all code
against the instrumented standard library - attempts to mix it with code
compiled against the system's C++ standard library will lead to linker errors.
And if you're so brave as to mix it with code compiled against an uninstrumented
libc++, a run-time crash is the best you can hope for...
