From a38e52fff59d627c9beb739714a8067ebbfe2f50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Fri, 31 Aug 2018 12:30:05 +0300 Subject: [PATCH] Build address sanitizer --- Dockerfile | 5 +++++ Dockerfile.dev | 19 ++++++++++++++++++ README.md | 4 ++++ build-all.sh | 1 + build-compiler-rt.sh | 47 +++++++++++++++++++++++++++++++++++++------- run-tests.sh | 19 +++++++++++++++++- 6 files changed, 87 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index e9823d8..fd7ed97 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,6 +43,11 @@ COPY build-libcxx.sh merge-archives.sh ./ RUN ./build-libcxx.sh $TOOLCHAIN_PREFIX && \ rm -rf /build +# Build sanitizers +COPY build-compiler-rt.sh . +RUN ./build-compiler-rt.sh $TOOLCHAIN_PREFIX --build-sanitizers && \ + rm -rf /build + # Build libssp COPY build-libssp.sh libssp-Makefile ./ RUN ./build-libssp.sh $TOOLCHAIN_PREFIX && \ diff --git a/Dockerfile.dev b/Dockerfile.dev index 267def6..a09fe98 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -83,6 +83,25 @@ RUN cd test && \ done; \ done +# Build sanitizers. Ubsan includes from the C++ headers, so +# we need to build this after libcxx. +RUN ./build-compiler-rt.sh $TOOLCHAIN_PREFIX --build-sanitizers + +# Sanitizers on windows only support x86. +RUN cd test && \ + for arch in $TOOLCHAIN_ARCHS; do \ + case $arch in \ + i686|x86_64) \ + ;; \ + *) \ + continue \ + ;; \ + esac && \ + for test in stacksmash; do \ + $arch-w64-mingw32-clang $test.c -o $arch/$test-asan.exe -fsanitize=address -g -gcodeview -Wl,-pdb,$arch/$test-asan.pdb || exit 1; \ + done; \ + done + # Build libssp COPY build-libssp.sh libssp-Makefile ./ RUN ./build-libssp.sh $TOOLCHAIN_PREFIX diff --git a/README.md b/README.md index 879221e..d6b7bd1 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Benefits of a LLVM based MinGW toolchain are: armv7 and arm64) instead of separate compiler binaries for each architecture - Support for generating debug info in PDB format +- Support for Address Sanitizer Clang on its own can also be used as compiler in the normal GNU binutils based environments though, so the main difference lies in replacing @@ -80,6 +81,9 @@ normal GCC/binutils based MinGW. variables. - The runtime libraries libunwind, libcxxabi and libcxx also assume that the target is Vista or newer. +- Address Sanitizer doesn't produce working backtraces for i686. Address + Sanitizer requires using a PDB file for symbolizing the error location and + backtraces. Additionally, one may run into other minor differences between GCC and clang. diff --git a/build-all.sh b/build-all.sh index c02b25a..b4d6a03 100755 --- a/build-all.sh +++ b/build-all.sh @@ -14,4 +14,5 @@ PREFIX="$1" ./build-compiler-rt.sh $PREFIX ./build-mingw-w64-libraries.sh $PREFIX ./build-libcxx.sh $PREFIX +./build-compiler-rt.sh $PREFIX --build-sanitizers ./build-libssp.sh $PREFIX diff --git a/build-compiler-rt.sh b/build-compiler-rt.sh index 43ccd83..757a89d 100755 --- a/build-compiler-rt.sh +++ b/build-compiler-rt.sh @@ -2,11 +2,23 @@ set -e -if [ $# -lt 1 ]; then - echo $0 dest +SRC_DIR=../lib/builtins +BUILD_SUFFIX= + +while [ $# -gt 0 ]; do + if [ "$1" = "--build-sanitizers" ]; then + SRC_DIR=.. + BUILD_SUFFIX=-sanitizers + SANITIZERS=1 + else + PREFIX="$1" + fi + shift +done +if [ -z "$PREFIX" ]; then + echo $0 [--build-sanitizers] dest exit 1 fi -PREFIX="$1" export PATH=$PREFIX/bin:$PATH : ${CORES:=4} @@ -34,6 +46,16 @@ fi for arch in $ARCHS; do buildarchname=$arch libarchname=$arch + if [ -n "$SANITIZERS" ]; then + case $arch in + i686|x86_64) + # Sanitizers on windows only support x86. + ;; + *) + continue + ;; + esac + fi case $arch in armv7) libarchname=arm @@ -43,20 +65,31 @@ for arch in $ARCHS; do libarchname=i386 ;; esac - mkdir -p build-$arch - cd build-$arch + mkdir -p build-$arch$BUILD_SUFFIX + cd build-$arch$BUILD_SUFFIX cmake \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_C_COMPILER=$arch-w64-mingw32-clang \ + -DCMAKE_CXX_COMPILER=$arch-w64-mingw32-clang++ \ -DCMAKE_SYSTEM_NAME=Windows \ -DCMAKE_AR=$PREFIX/bin/llvm-ar \ -DCMAKE_RANLIB=$PREFIX/bin/llvm-ranlib \ -DCMAKE_C_COMPILER_WORKS=1 \ + -DCMAKE_CXX_COMPILER_WORKS=1 \ -DCMAKE_C_COMPILER_TARGET=$buildarchname-windows-gnu \ -DCOMPILER_RT_DEFAULT_TARGET_ONLY=TRUE \ - ../lib/builtins + -DCOMPILER_RT_USE_BUILTINS_LIBRARY=TRUE \ + $SRC_DIR make -j$CORES mkdir -p $PREFIX/lib/clang/$CLANG_VERSION/lib/windows - cp lib/windows/libclang_rt.builtins-$buildarchname.a $PREFIX/lib/clang/$CLANG_VERSION/lib/windows/libclang_rt.builtins-$libarchname.a + mkdir -p $PREFIX/$arch-w64-mingw32/bin + for i in lib/windows/libclang_rt.*-$buildarchname*.a; do + cp $i $PREFIX/lib/clang/$CLANG_VERSION/lib/windows/$(basename $i | sed s/$buildarchname/$libarchname/) + done + for i in lib/windows/libclang_rt.*-$buildarchname*.dll; do + if [ -f $i ]; then + cp $i $PREFIX/$arch-w64-mingw32/bin + fi + done cd .. done diff --git a/run-tests.sh b/run-tests.sh index 219caba..56244d1 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -19,6 +19,7 @@ TESTS_C_NO_BUILTIN="crt-test" TESTS_CPP="hello-cpp hello-exception tlstest-main exception-locale" TESTS_CPP_DLL="tlstest-lib" TESTS_SSP="stacksmash" +TESTS_ASAN="stacksmash" for arch in $ARCHS; do mkdir -p $arch for test in $TESTS_C; do @@ -44,6 +45,18 @@ for arch in $ARCHS; do for test in $TESTS_SSP; do $arch-w64-mingw32-clang $test.c -o $arch/$test.exe -fstack-protector-strong done + # These aren't run, since asan doesn't work within wine. + for test in $TESTS_ASAN; do + case $arch in + i686|x86_64) + # Sanitizers on windows only support x86. + ;; + *) + continue + ;; + esac + $arch-w64-mingw32-clang $test.c -o $arch/$test-asan.exe -fsanitize=address -g -gcodeview -Wl,-pdb,$arch/$test-asan.pdb + done DLL="$TESTS_C_DLL $TESTS_CPP_DLL" case $arch in i686|x86_64) @@ -59,7 +72,11 @@ for arch in $ARCHS; do COPY="$COPY_AARCH64" ;; esac - for i in libc++ libunwind libssp-0; do + compiler_rt_arch=$arch + if [ "$arch" = "i686" ]; then + compiler_rt_arch=i386 + fi + for i in libc++ libunwind libssp-0 libclang_rt.asan_dynamic-$compiler_rt_arch; do if [ -f $PREFIX/$arch-w64-mingw32/bin/$i.dll ]; then cp $PREFIX/$arch-w64-mingw32/bin/$i.dll $arch DLL="$DLL $i"