- Use internal_{mmap,munmap,mprotect} functions in sanitizer_common
- Remove Darwin platform-specific TLS code
- Tweak comment
- Remove Makefile stuff
- Add test descriptions
- Rename flag to -fsanitize=safe-stack


http://reviews.llvm.org/D6096

Files:
  CMakeLists.txt
  cmake/config-ix.cmake
  lib/CMakeLists.txt
  lib/safestack/CMakeLists.txt
  lib/safestack/safestack.cc
  test/CMakeLists.txt
  test/safestack/CMakeLists.txt
  test/safestack/check-buffer-copy.c
  test/safestack/check-init.c
  test/safestack/check-overflow.c
  test/safestack/check-pthread-cleanup.c
  test/safestack/check-pthread.c
  test/safestack/lit.cfg
  test/safestack/lit.site.cfg.in

EMAIL PREFERENCES
  http://reviews.llvm.org/settings/panel/emailpreferences/
Index: CMakeLists.txt
===================================================================
--- CMakeLists.txt
+++ CMakeLists.txt
@@ -207,6 +207,7 @@
 append_list_if(COMPILER_RT_HAS_FOMIT_FRAME_POINTER_FLAG -fomit-frame-pointer SANITIZER_COMMON_CFLAGS)
 append_list_if(COMPILER_RT_HAS_FUNWIND_TABLES_FLAG -funwind-tables SANITIZER_COMMON_CFLAGS)
 append_list_if(COMPILER_RT_HAS_FNO_STACK_PROTECTOR_FLAG -fno-stack-protector SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_FNO_SAFE_STACK_FLAG -fno-safe-stack SANITIZER_COMMON_CFLAGS)
 append_list_if(COMPILER_RT_HAS_FVISIBILITY_HIDDEN_FLAG -fvisibility=hidden SANITIZER_COMMON_CFLAGS)
 append_list_if(COMPILER_RT_HAS_FNO_FUNCTION_SECTIONS_FLAG -fno-function-sections SANITIZER_COMMON_CFLAGS)
 append_list_if(COMPILER_RT_HAS_FNO_LTO_FLAG -fno-lto SANITIZER_COMMON_CFLAGS)
Index: cmake/config-ix.cmake
===================================================================
--- cmake/config-ix.cmake
+++ cmake/config-ix.cmake
@@ -19,6 +19,7 @@
 check_cxx_compiler_flag(-fomit-frame-pointer COMPILER_RT_HAS_FOMIT_FRAME_POINTER_FLAG)
 check_cxx_compiler_flag(-funwind-tables      COMPILER_RT_HAS_FUNWIND_TABLES_FLAG)
 check_cxx_compiler_flag(-fno-stack-protector COMPILER_RT_HAS_FNO_STACK_PROTECTOR_FLAG)
+check_cxx_compiler_flag(-fno-safe-stack      COMPILER_RT_HAS_FNO_SAFE_STACK_FLAG)
 check_cxx_compiler_flag(-fvisibility=hidden  COMPILER_RT_HAS_FVISIBILITY_HIDDEN_FLAG)
 check_cxx_compiler_flag(-fno-rtti            COMPILER_RT_HAS_FNO_RTTI_FLAG)
 check_cxx_compiler_flag(-ffreestanding       COMPILER_RT_HAS_FFREESTANDING_FLAG)
@@ -254,6 +255,7 @@
 filter_available_targets(TSAN_SUPPORTED_ARCH x86_64 mips64 mips64el)
 filter_available_targets(UBSAN_SUPPORTED_ARCH x86_64 i386 i686 arm aarch64 mips
   mipsel mips64 mips64el powerpc64 powerpc64le)
+filter_available_targets(SAFESTACK_SUPPORTED_ARCH x86_64 i386 i686)
 
 if(ANDROID)
   set(OS_NAME "Android")
@@ -332,3 +334,10 @@
 if("${LLVM_NATIVE_ARCH}" STREQUAL "Mips")
   set(COMPILER_RT_HAS_MSSE3_FLAG FALSE)
 endif()
+
+if (SAFESTACK_SUPPORTED_ARCH AND
+    OS_NAME MATCHES "Darwin|Linux|FreeBSD")
+  set(COMPILER_RT_HAS_SAFESTACK TRUE)
+else()
+  set(COMPILER_RT_HAS_SAFESTACK FALSE)
+endif()
Index: lib/CMakeLists.txt
===================================================================
--- lib/CMakeLists.txt
+++ lib/CMakeLists.txt
@@ -9,6 +9,7 @@
   add_subdirectory(sanitizer_common)
   add_subdirectory(lsan)
   add_subdirectory(ubsan)
+  add_subdirectory(safestack)
 endif()
 
 if(COMPILER_RT_HAS_ASAN)
@@ -33,4 +34,3 @@
   add_subdirectory(tsan)
   add_subdirectory(tsan/dd)
 endif()
-
Index: lib/safestack/CMakeLists.txt
===================================================================
--- /dev/null
+++ lib/safestack/CMakeLists.txt
@@ -0,0 +1,28 @@
+add_custom_target(safestack)
+
+set(SAFESTACK_SOURCES safestack.cc)
+
+include_directories(..)
+
+set(SAFESTACK_CFLAGS ${SANITIZER_COMMON_CFLAGS})
+
+if(APPLE)
+  # Build universal binary on APPLE.
+  add_compiler_rt_osx_static_runtime(clang_rt.safestack_osx
+    ARCH ${SAFESTACK_SUPPORTED_ARCH}
+    SOURCES ${SAFESTACK_SOURCES}
+            $<TARGET_OBJECTS:RTInterception.osx>
+            $<TARGET_OBJECTS:RTSanitizerCommon.osx>
+    CFLAGS ${SAFESTACK_CFLAGS})
+  add_dependencies(safestack clang_rt.safestack_osx)
+else()
+  # Otherwise, build separate libraries for each target.
+  foreach(arch ${SAFESTACK_SUPPORTED_ARCH})
+    add_compiler_rt_runtime(clang_rt.safestack-${arch} ${arch} STATIC
+      SOURCES ${SAFESTACK_SOURCES}
+              $<TARGET_OBJECTS:RTInterception.${arch}>
+              $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
+      CFLAGS ${SAFESTACK_CFLAGS})
+    add_dependencies(safestack clang_rt.safestack-${arch})
+  endforeach()
+endif()
Index: lib/safestack/safestack.cc
===================================================================
--- /dev/null
+++ lib/safestack/safestack.cc
@@ -0,0 +1,302 @@
+//===-- safestack.cc --------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the runtime support for the safe stack protection
+// mechanism. The runtime manages allocation/deallocation of the unsafe stack
+// for the main thread, as well as all pthreads that are created/destroyed
+// during program execution.
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdio.h>
+#include <assert.h>
+#include <dlfcn.h>
+#include <limits.h>
+#include <sys/mman.h>
+#include <sys/user.h>
+#include <sys/resource.h>
+#include <pthread.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#if defined(__linux__)
+#include <unistd.h>
+#include <sys/syscall.h>
+#endif
+
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_posix.h"
+
+/// Minimum stack alignment for the unsafe stack.
+/// FIXME: is this in some header?
+#define STACK_ALIGN 16
+
+/// Default size of the unsafe stack. This value is only used if the stack
+/// size rlimit is set to infinity.
+#define DEFAULT_UNSAFE_STACK_SIZE 0x2800000
+
+
+// define MAP_STACK if undefined, this flag is used as a hint
+#ifndef MAP_STACK
+# define MAP_STACK 0
+#endif
+
+// define MAP_GROWSDOWN if undefined, this flag is used as a hint
+#ifndef MAP_GROWSDOWN
+# define MAP_GROWSDOWN 0
+#endif
+
+// We don't know whether pthread is linked in or not, so we resolve
+// all symbols from pthread that we use dynamically
+#define __DECLARE_WRAPPER(fn) static __typeof__(fn)* __d_ ## fn = NULL;
+
+__DECLARE_WRAPPER(pthread_attr_init)
+__DECLARE_WRAPPER(pthread_attr_destroy)
+__DECLARE_WRAPPER(pthread_attr_getstacksize)
+__DECLARE_WRAPPER(pthread_attr_getguardsize)
+__DECLARE_WRAPPER(pthread_key_create)
+__DECLARE_WRAPPER(pthread_setspecific)
+
+// TODO: To make accessing the unsafe stack pointer faster, we plan to
+// eventually store it directly in the thread control block data structure on
+// platforms where this structure is pointed to by %fs or %gs. This is exactly
+// the same mechanism as currently being used by the traditional stack
+// protector pass to store the stack guard (see getStackCookieLocation()
+// function above). Doing so requires changing the tcbhead_t struct in glibc
+// on Linux and tcb struct in libc on FreeBSD.
+//
+// For now, store it in a thread-local variable.
+extern "C" {
+  __attribute__((visibility ("default")))
+  __thread void  *__safestack_unsafe_stack_ptr = 0;
+}
+
+#define __GET_UNSAFE_STACK_PTR()         __safestack_unsafe_stack_ptr
+#define __SET_UNSAFE_STACK_PTR(value)    __safestack_unsafe_stack_ptr = (value)
+
+// Per-thread unsafe stack information. It's not frequently accessed, so there
+// it can be kept out of the tcb in normal thread-local variables.
+static __thread void  *unsafe_stack_start = 0;
+static __thread size_t unsafe_stack_size = 0;
+static __thread size_t unsafe_stack_guard = 0;
+
+# define __GET_UNSAFE_STACK_START()       unsafe_stack_start
+# define __SET_UNSAFE_STACK_START(value)  unsafe_stack_start = (value)
+
+# define __GET_UNSAFE_STACK_SIZE()        unsafe_stack_size
+# define __SET_UNSAFE_STACK_SIZE(value)   unsafe_stack_size = (value)
+
+# define __GET_UNSAFE_STACK_GUARD()       unsafe_stack_guard
+# define __SET_UNSAFE_STACK_GUARD(value)  unsafe_stack_guard = (value)
+
+static inline void *unsafe_stack_alloc(size_t size, size_t guard) {
+  // We need the mmap system call without any LD_PRELOAD overrides
+  // (such overrides might crash is they use the unsafe stack themselves)
+  void *addr = (void *)internal_mmap(
+      NULL, size + guard, PROT_WRITE | PROT_READ,
+      MAP_PRIVATE | MAP_ANON | MAP_STACK | MAP_GROWSDOWN, -1, 0);
+
+  if (addr == MAP_FAILED)
+    return nullptr;
+
+  internal_mprotect(addr, guard, PROT_NONE);
+  return (char*) addr + guard;
+}
+
+static inline void unsafe_stack_setup(void *start, size_t size, size_t guard) {
+  void* stack_ptr = (char*) start + size;
+  assert((((size_t)stack_ptr) & (STACK_ALIGN-1)) == 0);
+
+  __SET_UNSAFE_STACK_PTR(stack_ptr);
+  __SET_UNSAFE_STACK_START(start);
+  __SET_UNSAFE_STACK_SIZE(size);
+  __SET_UNSAFE_STACK_GUARD(guard);
+}
+
+static void unsafe_stack_free() {
+  if (__GET_UNSAFE_STACK_START()) {
+    // We need the munmap system call without any LD_PRELOAD overrides
+    // (such overrides might crash is they use the unsafe stack themselves)
+    (void)internal_munmap(
+      (char*) __GET_UNSAFE_STACK_START() - __GET_UNSAFE_STACK_GUARD(),
+      __GET_UNSAFE_STACK_SIZE() + __GET_UNSAFE_STACK_GUARD());
+  }
+  __SET_UNSAFE_STACK_START(0);
+}
+
+/// Thread data for the cleanup handler
+static pthread_key_t thread_cleanup_key;
+
+/// Safe stack per-thread information passed to the thread_start function
+struct tinfo {
+  void *(*start_routine)(void*);
+  void *start_routine_arg;
+
+  void *unsafe_stack_start;
+  size_t unsafe_stack_size;
+  size_t unsafe_stack_guard;
+};
+
+/// Wrap the thread function in order to deallocate the unsafe stack when the
+/// thread terminates by returning from its main function.
+static void* thread_start(void *arg) {
+  struct tinfo *tinfo = (struct tinfo*) arg;
+
+  void *(*start_routine)(void*) = tinfo->start_routine;
+  void *start_routine_arg = tinfo->start_routine_arg;
+
+  // Setup the unsafe stack; this will destroy tinfo content
+  unsafe_stack_setup(tinfo->unsafe_stack_start,
+                     tinfo->unsafe_stack_size,
+                     tinfo->unsafe_stack_guard);
+
+  // Make sure out thread-specific destructor will be called
+  // FIXME: we can do this only any other specific key is set by
+  // intersepting the pthread_setspecific function itself
+  __d_pthread_setspecific(thread_cleanup_key, (void*) 1);
+
+  // Start the original thread rutine
+  return start_routine(start_routine_arg);
+}
+
+/// Thread-specific data destructor
+static void thread_cleanup_handler(void* _iter) {
+  // We want to free the unsafe stack only after all other destructors
+  // have already run. We force this function to be called multiple times.
+  // User destructors that might run more then PTHREAD_DESTRUCTOR_ITERATIONS-1
+  // times might still end up executing after the unsafe stack is deallocated,
+  // so such descructors must have __attribute__((no_safe_stack)).
+  size_t iter = (size_t) _iter;
+  if (iter < PTHREAD_DESTRUCTOR_ITERATIONS) {
+    __d_pthread_setspecific(thread_cleanup_key, (void*) (iter + 1));
+  } else {
+    // This is the last iteration
+    unsafe_stack_free();
+  }
+}
+
+/// Intercept thread creation operation to allocate and setup the unsafe stack
+INTERCEPTOR(int, pthread_create, pthread_t *thread,
+            const pthread_attr_t *attr,
+            void *(*start_routine)(void*), void *arg) {
+
+  size_t size = 0;
+  size_t guard = 0;
+
+  if (attr != NULL) {
+    __d_pthread_attr_getstacksize(attr, &size);
+    __d_pthread_attr_getguardsize(attr, &guard);
+  } else {
+    // get pthread default stack size
+    pthread_attr_t tmpattr;
+    __d_pthread_attr_init(&tmpattr);
+    __d_pthread_attr_getstacksize(&tmpattr, &size);
+    __d_pthread_attr_getguardsize(&tmpattr, &guard);
+    __d_pthread_attr_destroy(&tmpattr);
+  }
+
+  assert(size != 0);
+  assert((size & (STACK_ALIGN-1)) == 0);
+  assert((guard & (PAGE_SIZE-1)) == 0);
+
+  void *addr = unsafe_stack_alloc(size, guard);
+  if (!addr) {
+    // failed to allocate the unsafe stack
+    errno = EAGAIN;
+    return -1;
+  }
+  struct tinfo *tinfo = (struct tinfo*) (
+        ((char*)addr) + size - sizeof(struct tinfo));
+  tinfo->start_routine = start_routine;
+  tinfo->start_routine_arg = arg;
+  tinfo->unsafe_stack_start = addr;
+  tinfo->unsafe_stack_size = size;
+  tinfo->unsafe_stack_guard = guard;
+
+  return REAL(pthread_create)(thread, attr, thread_start, tinfo);
+}
+
+extern "C"
+__attribute__((visibility ("default")))
+#ifndef __ELF__
+// On ELF platforms, the constructor is invoked using .preinit_array (see below)
+__attribute__((constructor(0)))
+#endif
+void __safestack_init() {
+  static int initialized = 0;
+
+  if (initialized)
+    return;
+
+  initialized = 1;
+
+  // Determine the stack size for the main thread.
+  size_t size = DEFAULT_UNSAFE_STACK_SIZE;
+  size_t guard = 4096;
+
+  struct rlimit limit;
+  if (getrlimit(RLIMIT_STACK, &limit) == 0
+      && limit.rlim_cur != RLIM_INFINITY)
+    size = limit.rlim_cur;
+
+  // Allocate unsafe stack for main thread
+  void *addr = unsafe_stack_alloc(size, guard);
+  if (!addr)
+    // Failed to allocate the unsafe stack.
+    abort();
+
+  unsafe_stack_setup(addr, size, guard);
+
+  // Initialize pthread interceptors for thread allocation
+  INTERCEPT_FUNCTION(pthread_create);
+
+  #define __FIND_FUNCTION(fn) \
+    __d_ ## fn = __extension__ (__typeof__(__d_ ## fn)) dlsym(RTLD_DEFAULT, #fn);
+
+  // Find pthread functions that we need
+  __FIND_FUNCTION(pthread_attr_init)
+  __FIND_FUNCTION(pthread_attr_destroy)
+  __FIND_FUNCTION(pthread_attr_getstacksize)
+  __FIND_FUNCTION(pthread_attr_getguardsize)
+  __FIND_FUNCTION(pthread_key_create)
+  __FIND_FUNCTION(pthread_setspecific)
+
+  if (__d_pthread_key_create != NULL) {
+    // We're using pthreads, setup the cleanup handler
+    __d_pthread_key_create(&thread_cleanup_key, thread_cleanup_handler);
+  }
+}
+
+#ifdef __ELF__
+// Run safestack initialization before any other constructors.
+// FIXME: can we do something similar on non-ELF platforms, e.g., on Mac?
+extern "C" {
+__attribute__((section(".preinit_array"), used))
+void (*__safestack_preinit)(void) = __safestack_init;
+}
+#endif
+
+extern "C"
+__attribute__((visibility ("default")))
+void *__get_unsafe_stack_start() {
+  return __GET_UNSAFE_STACK_START();
+}
+
+extern "C"
+__attribute__((visibility ("default")))
+void *__get_unsafe_stack_ptr() {
+  return __GET_UNSAFE_STACK_PTR();
+}
+
+extern "C"
+__attribute__((visibility ("default")))
+__attribute__((noinline)) // required for __builtin_frame_address(0) to work
+void *__get_safe_stack_ptr() {
+  return (char*) __builtin_frame_address(0) + 2*sizeof(void*);
+}
Index: test/CMakeLists.txt
===================================================================
--- test/CMakeLists.txt
+++ test/CMakeLists.txt
@@ -58,6 +58,9 @@
     add_subdirectory(ubsan)
   endif()
   add_subdirectory(cfi)
+  if(COMPILER_RT_HAS_SAFESTACK)
+    add_subdirectory(safestack)
+  endif()
 endif()
 
 if(COMPILER_RT_STANDALONE_BUILD)
Index: test/safestack/CMakeLists.txt
===================================================================
--- /dev/null
+++ test/safestack/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(SAFESTACK_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+set(SAFESTACK_LIT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
+
+if(NOT COMPILER_RT_STANDALONE_BUILD)
+  list(APPEND SAFESTACK_TEST_DEPS safestack clang)
+endif()
+
+configure_lit_site_cfg(
+  ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+  ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+  )
+
+add_lit_testsuite(check-safestack "Running the SafeStack tests"
+  ${CMAKE_CURRENT_BINARY_DIR}
+  DEPENDS ${SAFESTACK_TEST_DEPS})
+set_target_properties(check-safestack PROPERTIES FOLDER "SafeStack tests")
Index: test/safestack/check-buffer-copy.c
===================================================================
--- /dev/null
+++ test/safestack/check-buffer-copy.c
@@ -0,0 +1,21 @@
+// RUN: %clang_safestack %s -o %t
+// RUN: %run %t
+
+// Test that loads/stores work correctly for variables on the unsafe stack.
+
+int main(int argc, char **argv)
+{
+  int i;
+  char buffer[128];
+
+  // check that we can write to a buffer
+  for (i = 0; argv[0][i] && i < sizeof (buffer) - 1; ++i)
+    buffer[i] = argv[0][i];
+  buffer[i] = '\0';
+
+  // check that we can read from a buffer
+  for (i = 0; argv[0][i] && i < sizeof (buffer) - 1; ++i)
+    if (buffer[i] != argv[0][i])
+      return 1;
+  return 0;
+}
Index: test/safestack/check-init.c
===================================================================
--- /dev/null
+++ test/safestack/check-init.c
@@ -0,0 +1,9 @@
+// RUN: %clang_safestack %s -o %t
+// RUN: %run %t
+
+// Basic smoke test for the runtime library.
+
+int main(int argc, char **argv)
+{
+  return 0;
+}
Index: test/safestack/check-overflow.c
===================================================================
--- /dev/null
+++ test/safestack/check-overflow.c
@@ -0,0 +1,23 @@
+// RUN: %clang_safestack %s -o %t
+// RUN: %run %t
+
+// RUN: %clang_nosafestack -fno-stack-protector %s -o %t
+// RUN: %run ! %t
+
+// Test that buffer overflows on the unsafe stack do not affect variables on the
+// safe stack.
+
+void fct(int *buffer)
+{
+  buffer[-1] = 36;
+  buffer[6] = 36;
+}
+
+int main(int argc, char **argv)
+{
+  int value1 = 42;
+  int buffer[5];
+  int value2 = 42;
+  fct(buffer);
+  return value1 != 42 || value2 != 42;
+}
Index: test/safestack/check-pthread-cleanup.c
===================================================================
--- /dev/null
+++ test/safestack/check-pthread-cleanup.c
@@ -0,0 +1,31 @@
+// RUN: %clang_safestack %s -pthread -o %t
+// RUN: %run ! %t
+
+// Test that unsafe stacks are deallocated correctly on thread exit.
+
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+#define BUFFER_SIZE (1 << 15)
+
+void *t1_start(void *ptr)
+{
+  char buffer[BUFFER_SIZE];
+  return buffer;
+}
+
+int main(int argc, char **argv)
+{
+  pthread_t t1;
+  char *buffer = NULL;
+
+  if (pthread_create(&t1, NULL, t1_start, NULL))
+    abort();
+  if (pthread_join(t1, &buffer))
+    abort();
+
+  // should segfault here
+  memset(buffer, 0, BUFFER_SIZE);
+  return 0;
+}
Index: test/safestack/check-pthread.c
===================================================================
--- /dev/null
+++ test/safestack/check-pthread.c
@@ -0,0 +1,38 @@
+// RUN: %clang_safestack %s -pthread -o %t
+// RUN: %run %t
+
+// Test that pthreads receive their own unsafe stack.
+
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+static int ptr_test = 42;
+
+void *t1_start(void *ptr)
+{
+  if (ptr != &ptr_test)
+    abort();
+
+  // safe stack
+  int val = ptr_test * 5;
+
+  // unsafe stack
+  char buffer[8096]; // two pages
+  memset(buffer, val, sizeof (buffer));
+
+  return ptr;
+}
+
+int main(int argc, char **argv)
+{
+  pthread_t t1;
+  void *ptr = NULL;
+  if (pthread_create(&t1, NULL, t1_start, &ptr_test))
+    abort();
+  if (pthread_join(t1, &ptr))
+    abort();
+  if (ptr != &ptr_test)
+    abort();
+  return 0;
+}
Index: test/safestack/lit.cfg
===================================================================
--- /dev/null
+++ test/safestack/lit.cfg
@@ -0,0 +1,20 @@
+# -*- Python -*-
+
+import os
+
+# Setup config name.
+config.name = 'SafeStack'
+
+# Setup source root.
+config.test_source_root = os.path.dirname(__file__)
+
+# Test suffixes.
+config.suffixes = ['.c', '.cc', '.cpp', '.m', '.mm', '.ll', '.test']
+
+# Add clang substitutions.
+config.substitutions.append( ("%clang_nosafestack ", config.clang + " -O0 -fno-sanitize=safe-stack ") )
+config.substitutions.append( ("%clang_safestack ", config.clang + " -O0 -fsanitize=safe-stack ") )
+
+# SafeStack tests are currently supported on Linux, FreeBSD and Darwin only.
+if config.host_os not in ['Linux', 'FreeBSD', 'Darwin']:
+   config.unsupported = True
Index: test/safestack/lit.site.cfg.in
===================================================================
--- /dev/null
+++ test/safestack/lit.site.cfg.in
@@ -0,0 +1,11 @@
+## Autogenerated by LLVM/Clang configuration.
+# Do not edit!
+
+# Tool-specific config options.
+config.safestack_lit_binary_dir = "@SAFESTACK_LIT_BINARY_DIR@"
+
+# Load common config for all compiler-rt lit tests.
+lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured")
+
+# Load tool-specific config that would do the real work.
+lit_config.load_config(config, "@SAFESTACK_LIT_SOURCE_DIR@/lit.cfg")
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to