Reland "[CMake][libunwind] Use -nodefaultlibs for CMake checks"

This is a reland of commit r299796.

Turned out that we need gcc_s or compiler-rt on ARM when checking
the support for -funwind-tables which creates a dependency on
__aeabi_unwind_cpp_pr0 symbol that's provided by the compiler
runtime.

Differential Revision: https://reviews.llvm.org/D31858

git-svn-id: https://llvm.org/svn/llvm-project/libunwind/trunk@300020 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 41a771c..0b90cda 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,6 +8,13 @@
   cmake_policy(SET CMP0042 NEW) # Set MACOSX_RPATH=YES by default
 endif()
 
+# Add path for custom modules
+set(CMAKE_MODULE_PATH
+  "${CMAKE_CURRENT_SOURCE_DIR}/cmake"
+  "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules"
+  ${CMAKE_MODULE_PATH}
+  )
+
 if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
   project(libunwind)
 
@@ -111,6 +118,7 @@
 #===============================================================================
 # Setup CMake Options
 #===============================================================================
+include(HandleCompilerRT)
 
 # Define options.
 option(LIBUNWIND_BUILD_32_BITS "Build 32 bit libunwind" ${LLVM_BUILD_32_BITS})
@@ -122,6 +130,7 @@
 option(LIBUNWIND_ENABLE_CROSS_UNWINDING "Enable cross-platform unwinding support." OFF)
 option(LIBUNWIND_ENABLE_ARM_WMMX "Enable unwinding support for ARM WMMX registers." OFF)
 option(LIBUNWIND_ENABLE_THREADS "Build libunwind with threading support." ON)
+option(LIBUNWIND_USE_COMPILER_RT "Use compiler-rt instead of libgcc" OFF)
 option(LIBUNWIND_INCLUDE_DOCS "Build the libunwind documentation." ${LLVM_INCLUDE_DOCS})
 
 set(LIBUNWIND_TARGET_TRIPLE "" CACHE STRING "Target triple for cross compiling.")
@@ -187,6 +196,10 @@
 # Configure compiler.
 include(config-ix)
 
+if (LIBUNWIND_USE_COMPILER_RT)
+  list(APPEND LIBUNWIND_LINK_FLAGS "-rtlib=compiler-rt")
+endif()
+
 #===============================================================================
 # Setup Compiler Flags
 #===============================================================================
diff --git a/cmake/Modules/HandleCompilerRT.cmake b/cmake/Modules/HandleCompilerRT.cmake
new file mode 100644
index 0000000..5696ec3
--- /dev/null
+++ b/cmake/Modules/HandleCompilerRT.cmake
@@ -0,0 +1,58 @@
+function(find_compiler_rt_library name dest)
+  if (NOT DEFINED LIBUNWIND_COMPILE_FLAGS)
+    message(FATAL_ERROR "LIBUNWIND_COMPILE_FLAGS must be defined when using this function")
+  endif()
+  set(dest "" PARENT_SCOPE)
+  set(CLANG_COMMAND ${CMAKE_CXX_COMPILER} ${TARGET_TRIPLE} ${LIBUNWIND_COMPILE_FLAGS}
+      "--rtlib=compiler-rt" "--print-libgcc-file-name")
+  if (CMAKE_CXX_COMPILER_ID MATCHES Clang AND CMAKE_CXX_COMPILER_TARGET)
+    list(APPEND CLANG_COMMAND "--target=${CMAKE_CXX_COMPILER_TARGET}")
+  endif()
+  execute_process(
+      COMMAND ${CLANG_COMMAND}
+      RESULT_VARIABLE HAD_ERROR
+      OUTPUT_VARIABLE LIBRARY_FILE
+  )
+  string(STRIP "${LIBRARY_FILE}" LIBRARY_FILE)
+  string(REPLACE "builtins" "${name}" LIBRARY_FILE "${LIBRARY_FILE}")
+  if (NOT HAD_ERROR AND EXISTS "${LIBRARY_FILE}")
+    message(STATUS "Found compiler-rt library: ${LIBRARY_FILE}")
+    set(${dest} "${LIBRARY_FILE}" PARENT_SCOPE)
+  else()
+    message(STATUS "Failed to find compiler-rt library")
+  endif()
+endfunction()
+
+function(find_compiler_rt_dir dest)
+  if (NOT DEFINED LIBUNWIND_COMPILE_FLAGS)
+    message(FATAL_ERROR "LIBUNWIND_COMPILE_FLAGS must be defined when using this function")
+  endif()
+  set(dest "" PARENT_SCOPE)
+  if (APPLE)
+    set(CLANG_COMMAND ${CMAKE_CXX_COMPILER} ${LIBUNWIND_COMPILE_FLAGS}
+        "-print-file-name=lib")
+    execute_process(
+        COMMAND ${CLANG_COMMAND}
+        RESULT_VARIABLE HAD_ERROR
+        OUTPUT_VARIABLE LIBRARY_DIR
+    )
+    string(STRIP "${LIBRARY_DIR}" LIBRARY_DIR)
+    set(LIBRARY_DIR "${LIBRARY_DIR}/darwin")
+  else()
+    set(CLANG_COMMAND ${CMAKE_CXX_COMPILER} ${LIBUNWIND_COMPILE_FLAGS}
+        "--rtlib=compiler-rt" "--print-libgcc-file-name")
+    execute_process(
+        COMMAND ${CLANG_COMMAND}
+        RESULT_VARIABLE HAD_ERROR
+        OUTPUT_VARIABLE LIBRARY_FILE
+    )
+    string(STRIP "${LIBRARY_FILE}" LIBRARY_FILE)
+    get_filename_component(LIBRARY_DIR "${LIBRARY_FILE}" DIRECTORY)
+  endif()
+  if (NOT HAD_ERROR AND EXISTS "${LIBRARY_DIR}")
+    message(STATUS "Found compiler-rt directory: ${LIBRARY_DIR}")
+    set(${dest} "${LIBRARY_DIR}" PARENT_SCOPE)
+  else()
+    message(STATUS "Failed to find compiler-rt directory")
+  endif()
+endfunction()
diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake
index 0b0bb5c..40a5dd1 100644
--- a/cmake/config-ix.cmake
+++ b/cmake/config-ix.cmake
@@ -3,13 +3,46 @@
 include(CheckCXXCompilerFlag)
 include(CheckLibraryExists)
 
+check_library_exists(c fopen "" LIBUNWIND_HAS_C_LIB)
+
+if (NOT LIBUNWIND_USE_COMPILER_RT)
+  check_library_exists(gcc_s __gcc_personality_v0 "" LIBUNWIND_HAS_GCC_S_LIB)
+endif()
+
+# libunwind is built with -nodefaultlibs, so we want all our checks to also
+# use this option, otherwise we may end up with an inconsistency between
+# the flags we think we require during configuration (if the checks are
+# performed without -nodefaultlibs) and the flags that are actually
+# required during compilation (which has the -nodefaultlibs). libc is
+# required for the link to go through. We remove sanitizers from the
+# configuration checks to avoid spurious link errors.
+check_c_compiler_flag(-nodefaultlibs LIBUNWIND_HAS_NODEFAULTLIBS_FLAG)
+if (LIBUNWIND_HAS_NODEFAULTLIBS_FLAG)
+  set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -nodefaultlibs")
+  if (LIBUNWIND_HAS_C_LIB)
+    list(APPEND CMAKE_REQUIRED_LIBRARIES c)
+  endif ()
+  if (LIBUNWIND_USE_COMPILER_RT)
+    list(APPEND CMAKE_REQUIRED_FLAGS -rtlib=compiler-rt)
+    find_compiler_rt_library(builtins LIBUNWIND_BUILTINS_LIBRARY)
+    list(APPEND CMAKE_REQUIRED_LIBRARIES "${LIBUNWIND_BUILTINS_LIBRARY}")
+  elseif (LIBUNWIND_HAS_GCC_S_LIB)
+    list(APPEND CMAKE_REQUIRED_LIBRARIES gcc_s)
+  endif ()
+  if (CMAKE_C_FLAGS MATCHES -fsanitize OR CMAKE_CXX_FLAGS MATCHES -fsanitize)
+    set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fno-sanitize=all")
+  endif ()
+  if (CMAKE_C_FLAGS MATCHES -fsanitize-coverage OR CMAKE_CXX_FLAGS MATCHES -fsanitize-coverage)
+    set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fno-sanitize-coverage=edge,trace-cmp,indirect-calls,8bit-counters")
+  endif ()
+endif ()
+
 # Check compiler flags
 check_c_compiler_flag(-funwind-tables         LIBUNWIND_HAS_FUNWIND_TABLES)
 check_cxx_compiler_flag(-fPIC                 LIBUNWIND_HAS_FPIC_FLAG)
 check_cxx_compiler_flag(-fno-exceptions       LIBUNWIND_HAS_NO_EXCEPTIONS_FLAG)
 check_cxx_compiler_flag(-fno-rtti             LIBUNWIND_HAS_NO_RTTI_FLAG)
 check_cxx_compiler_flag(-fstrict-aliasing     LIBUNWIND_HAS_FSTRICT_ALIASING_FLAG)
-check_cxx_compiler_flag(-nodefaultlibs        LIBUNWIND_HAS_NODEFAULTLIBS_FLAG)
 check_cxx_compiler_flag(-nostdinc++           LIBUNWIND_HAS_NOSTDINCXX_FLAG)
 check_cxx_compiler_flag(-Wall                 LIBUNWIND_HAS_WALL_FLAG)
 check_cxx_compiler_flag(-W                    LIBUNWIND_HAS_W_FLAG)
@@ -44,7 +77,6 @@
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
 endif()
 
-check_library_exists(c fopen "" LIBUNWIND_HAS_C_LIB)
 check_library_exists(dl dladdr "" LIBUNWIND_HAS_DL_LIB)
 check_library_exists(pthread pthread_once "" LIBUNWIND_HAS_PTHREAD_LIB)