Compare commits

...

1 Commits

Author SHA1 Message Date
Christian Buttner
5ab5fdf954 Address/memory/thread sanitizer. 2024-08-21 16:01:45 +02:00
24 changed files with 788 additions and 137 deletions

View File

@@ -94,7 +94,9 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: c3-windows-${{ matrix.build_type }}
path: build\${{ matrix.build_type }}\c3c.exe
path: |
build\${{ matrix.build_type }}\c3c.exe
build\${{ matrix.build_type }}\c3c_rt
build-msys2-mingw:
runs-on: windows-latest

View File

@@ -116,12 +116,14 @@ if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message("Loading Windows LLVM debug libraries, this may take a while...")
FetchContent_MakeAvailable(LLVM_Windows_debug)
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_windows_debug_SOURCE_DIR} ${CMAKE_SYSTEM_PREFIX_PATH})
set(llvm_dir ${llvm_windows_debug_SOURCE_DIR})
else()
message("Loading Windows LLVM libraries, this may take a while...")
FetchContent_MakeAvailable(LLVM_Windows)
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_windows_SOURCE_DIR} ${CMAKE_SYSTEM_PREFIX_PATH})
set(llvm_dir ${llvm_windows_SOURCE_DIR})
endif()
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_dir} ${CMAKE_SYSTEM_PREFIX_PATH})
find_package(LLVM REQUIRED CONFIG)
find_package(LLD REQUIRED CONFIG)
else()
@@ -146,6 +148,9 @@ else()
message(STATUS "LLVM was not built with RTTI")
endif()
string(REPLACE "." ";" VERSION_LIST ${LLVM_PACKAGE_VERSION})
list(GET VERSION_LIST 0 LLVM_MAJOR_VERSION)
include_directories(${LLVM_INCLUDE_DIRS})
link_directories(${LLVM_LIBRARY_DIRS})
add_definitions(${LLVM_DEFINITIONS})
@@ -210,30 +215,30 @@ if (NOT(${CMAKE_BINARY_DIR} EQUAL ${CMAKE_SOURCE_DIR}))
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
endif()
if (${LLVM_PACKAGE_VERSION} VERSION_GREATER_EQUAL 16)
find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
set(lld_libs
${LLD_COFF}
${LLD_COMMON}
${LLD_WASM}
${LLD_MINGW}
${LLD_ELF}
${LLD_MACHO}
)
else()
set(lld_libs
${LLD_COFF}
${LLD_COMMON}
${LLD_WASM}
${LLD_MINGW}
${LLD_ELF}
${LLD_MACHO}
)
endif()
find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
set(lld_libs
${LLD_COFF}
${LLD_COMMON}
${LLD_WASM}
${LLD_MINGW}
${LLD_ELF}
${LLD_MACHO}
)
if (APPLE)
set(lld_libs ${lld_libs} xar)
endif ()
find_file(RT_ASAN_DYNAMIC NAMES libclang_rt.asan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIRS}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
find_file(RT_TSAN_DYNAMIC NAMES libclang_rt.tsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIRS}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
find_file(RT_UBSAN_DYNAMIC NAMES libclang_rt.ubsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIRS}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
find_file(RT_LSAN_DYNAMIC NAMES libclang_rt.lsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIRS}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
set(sanitizer_runtime_libraries
${RT_ASAN_DYNAMIC}
${RT_TSAN_DYNAMIC}
# Unused
# ${RT_UBSAN_DYNAMIC}
# ${RT_LSAN_DYNAMIC}
)
endif()
message(STATUS "linking to llvm libs ${lld_libs}")
message(STATUS "Found lld libs ${lld_libs}")
@@ -424,6 +429,12 @@ if(MSVC)
target_compile_options(tilde-backend PUBLIC /MT)
endif()
endif()
set(clang_lib_dir ${llvm_dir}/lib/clang/${C3_LLVM_VERSION}/lib/windows)
set(sanitizer_runtime_libraries
${clang_lib_dir}/clang_rt.asan-x86_64.lib
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.lib
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.dll
${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
else()
message(STATUS "using gcc/clang warning switches")
target_link_options(c3c PRIVATE -pthread)
@@ -434,8 +445,25 @@ else()
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
endif()
install(TARGETS c3c DESTINATION bin)
install(DIRECTORY lib/ DESTINATION lib/c3)
if (DEFINED sanitizer_runtime_libraries)
add_custom_command(TARGET c3c POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E rm -rf -- $<TARGET_FILE_DIR:c3c>/c3c_rt
COMMAND "${CMAKE_COMMAND}" -E make_directory $<TARGET_FILE_DIR:c3c>/c3c_rt
COMMAND "${CMAKE_COMMAND}" -E copy ${sanitizer_runtime_libraries} $<TARGET_FILE_DIR:c3c>/c3c_rt
VERBATIM
COMMENT "Copying sanitizer runtime libraries to output directory")
if (APPLE)
# Change LC_ID_DYLIB to be rpath-based instead of having an absolute path
add_custom_command(TARGET c3c POST_BUILD
COMMAND find $<TARGET_FILE_DIR:c3c>/c3c_rt -type f -name "*.dylib" -execdir ${LLVM_TOOLS_BINARY_DIR}/llvm-install-name-tool -id @rpath/{} {} $<SEMICOLON>
VERBATIM)
endif()
install(DIRECTORY $<TARGET_FILE_DIR:c3c>/c3c_rt/ DESTINATION bin/c3c_rt)
endif()
feature_summary(WHAT ALL)

View File

@@ -27,20 +27,9 @@ fn List* List.new_init(&self, usz initial_capacity = 16, Allocator allocator = a
{
self.allocator = allocator;
self.size = 0;
if (initial_capacity > 0)
{
initial_capacity = math::next_power_of_2(initial_capacity);
$if type_is_overaligned():
self.entries = allocator::malloc_aligned(allocator, Type.sizeof * initial_capacity, .alignment = Type[1].alignof)!!;
$else
self.entries = allocator::malloc(allocator, Type.sizeof * initial_capacity);
$endif
}
else
{
self.entries = null;
}
self.capacity = initial_capacity;
self.capacity = 0;
self.entries = null;
self.reserve(initial_capacity);
return self;
}
@@ -81,14 +70,14 @@ fn List* List.temp_init_with_array(&self, Type[] values)
}
/**
* @require self.size == 0 "The List must be empty"
* @require self.capacity == 0 "The List must not be allocated"
**/
fn void List.init_wrapping_array(&self, Type[] types, Allocator allocator = allocator::heap())
{
self.allocator = allocator;
self.size = types.len;
self.capacity = types.len;
self.entries = types.ptr;
self.set_size(types.len);
}
fn usz! List.to_format(&self, Formatter* formatter) @dynamic
@@ -124,18 +113,19 @@ fn String List.to_tstring(&self)
fn void List.push(&self, Type element) @inline
{
self.reserve(1);
self.entries[self.size++] = element;
self.entries[self.set_size(self.size + 1)] = element;
}
fn Type! List.pop(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
return self.entries[--self.size];
defer self.set_size(self.size - 1);
return self.entries[self.size - 1];
}
fn void List.clear(&self)
{
self.size = 0;
self.set_size(0);
}
/**
@@ -153,7 +143,8 @@ fn Type! List.pop_first(&self)
**/
fn void List.remove_at(&self, usz index)
{
if (!--self.size || index == self.size) return;
self.set_size(self.size - 1);
if (!self.size || index == self.size) return;
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
}
@@ -161,9 +152,10 @@ fn void List.add_all(&self, List* other_list)
{
if (!other_list.size) return;
self.reserve(other_list.size);
usz index = self.set_size(self.size + other_list.size);
foreach (&value : other_list)
{
self.entries[self.size++] = *value;
self.entries[index++] = *value;
}
}
@@ -216,8 +208,8 @@ fn void List.add_array(&self, Type[] array)
{
if (!array.len) return;
self.reserve(array.len);
self.entries[self.size : array.len] = array[..];
self.size += array.len;
usz index = self.set_size(self.size + array.len);
self.entries[index : array.len] = array[..];
}
fn void List.push_front(&self, Type type) @inline
@@ -235,7 +227,7 @@ fn void List.insert_at(&self, usz index, Type type)
{
self.entries[i] = self.entries[i - 1];
}
self.size++;
self.set_size(self.size + 1);
self.entries[index] = type;
}
@@ -250,7 +242,7 @@ fn void List.set_at(&self, usz index, Type type)
fn void! List.remove_last(&self) @maydiscard
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
self.size--;
self.set_size(self.size - 1);
}
fn void! List.remove_first(&self) @maydiscard
@@ -293,11 +285,14 @@ fn Type List.get(&self, usz index) @inline
fn void List.free(&self)
{
if (!self.allocator) return;
if (!self.allocator || !self.capacity) return;
self.pre_free(); // Remove sanitizer annotation
$if type_is_overaligned():
allocator::free_aligned(self.allocator, self.entries);
allocator::free_aligned(self.allocator, self.entries);
$else
allocator::free(self.allocator, self.entries);
allocator::free(self.allocator, self.entries);
$endif;
self.capacity = 0;
self.size = 0;
@@ -329,11 +324,21 @@ fn usz List.retain_if(&self, ElementPredicate selection)
fn usz List.remove_using_test(&self, ElementTest filter, any context)
{
usz old_size = self.size;
defer {
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
return list_common::list_remove_using_test(self, filter, false, context);
}
fn usz List.retain_using_test(&self, ElementTest filter, any context)
{
usz old_size = self.size;
defer {
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
return list_common::list_remove_using_test(self, filter, true, context);
}
@@ -342,13 +347,18 @@ fn void List.ensure_capacity(&self, usz min_capacity) @local
if (!min_capacity) return;
if (self.capacity >= min_capacity) return;
if (!self.allocator) self.allocator = allocator::heap();
self.pre_free(); // Remove sanitizer annotation
min_capacity = math::next_power_of_2(min_capacity);
$if type_is_overaligned():
self.entries = allocator::realloc_aligned(self.allocator, self.entries, Type.sizeof * min_capacity, .alignment = Type[1].alignof)!!;
self.entries = allocator::realloc_aligned(self.allocator, self.entries, Type.sizeof * min_capacity, .alignment = Type[1].alignof)!!;
$else
self.entries = allocator::realloc(self.allocator, self.entries, Type.sizeof * min_capacity);
self.entries = allocator::realloc(self.allocator, self.entries, Type.sizeof * min_capacity);
$endif;
self.capacity = min_capacity;
self.post_alloc(); // Add sanitizer annotation
}
macro Type List.@item_at(&self, usz index) @operator([])
@@ -377,6 +387,39 @@ fn void List.reserve(&self, usz added)
self.ensure_capacity(new_capacity);
}
fn void List._update_size_change(&self,usz old_size, usz new_size)
{
if (old_size == new_size) return;
sanitizer::annotate_contiguous_container(self.entries,
&self.entries[self.capacity],
&self.entries[old_size],
&self.entries[new_size]);
}
/**
* @require new_size == 0 || self.capacity != 0
**/
fn usz List.set_size(&self, usz new_size) @inline @private
{
usz old_size = self.size;
self._update_size_change(old_size, new_size);
self.size = new_size;
return old_size;
}
macro void List.pre_free(&self) @private
{
if (!self.capacity) return;
self._update_size_change(self.size, self.capacity);
}
/**
* @require self.capacity
**/
macro void List.post_alloc(&self) @private
{
self._update_size_change(self.capacity, self.size);
}
// Functions for equatable types
@@ -443,7 +486,6 @@ fn bool List.remove_first_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
{
return @ok(self.remove_at(self.index_of(value)));
}
/**
* @param [&inout] self "The list to remove elements from"
* @param value "The value to remove"
@@ -451,6 +493,10 @@ fn bool List.remove_first_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
**/
fn usz List.remove_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
{
usz old_size = self.size;
defer {
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
return list_common::list_remove_item(self, value);
}
@@ -459,6 +505,10 @@ fn usz List.remove_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
fn void List.remove_all_from(&self, List* other_list) @if(ELEMENT_IS_EQUATABLE)
{
if (!other_list.size) return;
usz old_size = self.size;
defer {
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
foreach (v : other_list) self.remove_item(v);
}
@@ -475,6 +525,10 @@ fn usz List.compact_count(&self) @if(ELEMENT_IS_POINTER)
fn usz List.compact(&self) @if(ELEMENT_IS_POINTER)
{
usz old_size = self.size;
defer {
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
return list_common::list_compact(self);
}

View File

@@ -60,21 +60,12 @@ fn void TempAllocator.release(&self, void* old_pointer, bool) @dynamic
if (old_pointer + old_size == &self.data[self.used])
{
self.used -= old_size;
asan::poison_memory_region(&self.data[self.used], old_size);
}
}
fn void TempAllocator.reset(&self, usz mark) @dynamic
{
TempAllocatorPage *last_page = self.last_page;
$if env::COMPILER_SAFE_MODE:
if (!last_page)
{
usz cleaned = self.used - mark;
if (cleaned > 0)
{
self.data[mark : cleaned] = 0xAA;
}
}
$endif
while (last_page && last_page.mark > mark)
{
TempAllocatorPage *to_free = last_page;
@@ -82,6 +73,19 @@ fn void TempAllocator.reset(&self, usz mark) @dynamic
self._free_page(to_free)!!;
}
self.last_page = last_page;
$if env::COMPILER_SAFE_MODE || env::ADDRESS_SANITIZER:
if (!last_page)
{
usz cleaned = self.used - mark;
if (cleaned > 0)
{
$if env::COMPILER_SAFE_MODE:
self.data[mark : cleaned] = 0xAA;
$endif
asan::poison_memory_region(&self.data[mark], cleaned);
}
}
$endif
self.used = mark;
}
@@ -147,9 +151,10 @@ fn void*! TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz al
}
usz new_usage = (usz)(mem - start_mem) + size;
// Arena alignment, simple!
// Arena allocation, simple!
if (new_usage <= self.capacity)
{
asan::unpoison_memory_region(starting_ptr, new_usage - self.used);
TempAllocatorChunk* chunk_start = mem - TempAllocatorChunk.sizeof;
chunk_start.size = size;
self.used = new_usage;

View File

@@ -19,6 +19,8 @@ fault SearchResult { MISSING }
**/
fault CastResult { TYPE_MISMATCH }
def VoidFn = fn void();
/**
* Stores a variable on the stack, then restores it at the end of the
* macro scope.

View File

@@ -148,6 +148,10 @@ const bool FREEBSD = LIBC && OS_TYPE == FREEBSD;
const bool NETBSD = LIBC && OS_TYPE == NETBSD;
const bool WASI = LIBC && OS_TYPE == WASI;
const bool WASM_NOLIBC @builtin = !LIBC && ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64;
const bool ADDRESS_SANITIZER = $$ADDRESS_SANITIZER;
const bool MEMORY_SANITIZER = $$MEMORY_SANITIZER;
const bool THREAD_SANITIZER = $$THREAD_SANITIZER;
const bool ANY_SANITIZER = ADDRESS_SANITIZER || MEMORY_SANITIZER || THREAD_SANITIZER;
macro bool os_is_darwin() @const
{

View File

@@ -0,0 +1,127 @@
// Add this to your code to suppress leak detection or set other default options
// fn ZString __asan_default_options() @export("__asan_default_options") @if(env::ADDRESS_SANITIZER)
// {
// return "detect_leaks=0";
// }
// Add this to break on error
// asan::set_error_report_callback(fn (ZString err)
// {
// breakpoint();
// });
module std::core::sanitizer::asan;
def ErrorCallback = fn void (ZString);
/**
* Marks a memory region ([addr, addr+size)) as unaddressable.
*
* This memory must be previously allocated by your program. Instrumented
* code is forbidden from accessing addresses in this region until it is
* unpoisoned. This function is not guaranteed to poison the entire region -
* it could poison only a subregion of [addr, addr+size) due to ASan
* alignment restrictions.
*
* NOTE This function is not thread-safe because no two threads can poison or
* unpoison memory in the same memory region simultaneously.
*
* @param addr "Start of memory region."
* @param size "Size of memory region."
**/
macro poison_memory_region(void* addr, usz size)
{
$if env::ADDRESS_SANITIZER:
__asan_poison_memory_region(addr, size);
$endif
}
/**
* Marks a memory region ([addr, addr+size)) as addressable.
*
* This memory must be previously allocated by your program. Accessing
* addresses in this region is allowed until this region is poisoned again.
* This function could unpoison a super-region of [addr, addr+size) due
* to ASan alignment restrictions.
*
* NOTE This function is not thread-safe because no two threads can
* poison or unpoison memory in the same memory region simultaneously.
*
* @param addr "Start of memory region."
* @param size "Size of memory region."
**/
macro unpoison_memory_region(void* addr, usz size)
{
$if env::ADDRESS_SANITIZER:
__asan_unpoison_memory_region(addr, size);
$endif
}
/**
* Checks if an address is poisoned.
* @return "True if 'addr' is poisoned (that is, 1-byte read/write access to this address would result in an error report from ASan). Otherwise returns false."
* @param addr "Address to check."
**/
macro bool address_is_poisoned(void* addr)
{
$if env::ADDRESS_SANITIZER:
return (bool)__asan_address_is_poisoned(addr);
$else
return false;
$endif
}
/**
* Checks if a region is poisoned.
*
* If at least one byte in [beg, beg+size) is poisoned, returns the
* address of the first such byte. Otherwise returns 0.
*
* @param beg "Start of memory region."
* @param size "Start of memory region."
* @return "Address of first poisoned byte."
**/
macro void* region_is_poisoned(void* beg, usz size)
{
$if env::ADDRESS_SANITIZER:
return __asan_region_is_poisoned(addr);
$else
return null;
$endif
}
/**
* Sets the callback function to be called during ASan error reporting.
**/
fn void set_error_report_callback(ErrorCallback callback)
{
$if env::ADDRESS_SANITIZER:
__asan_set_error_report_callback(callback);
$endif
}
module std::core::sanitizer::asan @if(env::ADDRESS_SANITIZER);
extern fn void __asan_poison_memory_region(void* addr, usz size);
extern fn void __asan_unpoison_memory_region(void* addr, usz size);
extern fn CInt __asan_address_is_poisoned(void* addr);
extern fn void* __asan_region_is_poisoned(void* beg, usz size);
extern fn void __asan_describe_address(void* addr);
extern fn CInt __asan_report_present();
extern fn void* __asan_get_report_pc();
extern fn void* __asan_get_report_bp();
extern fn void* __asan_get_report_sp();
extern fn void* __asan_get_report_address();
extern fn CInt __asan_get_report_access_type();
extern fn usz __asan_get_report_access_size();
extern fn ZString __asan_get_report_description();
extern fn ZString __asan_locate_address(void* addr, char* name, usz name_size, void** region_address, usz* region_size);
extern fn usz __asan_get_alloc_stack(void* addr, void** trace, usz size, CInt* thread_id);
extern fn usz __asan_get_free_stack(void* addr, void** trace, usz size, CInt* thread_id);
extern fn void __asan_get_shadow_mapping(usz* shadow_scale, usz* shadow_offset);
extern fn void __asan_set_error_report_callback(ErrorCallback callback);
extern fn void __asan_print_accumulated_stats();
extern fn void* __asan_get_current_fake_stack();
extern fn void* __asan_addr_is_in_fake_stack(void* fake_stack, void* addr, void** beg, void** end);
extern fn void __asan_handle_no_return();
extern fn CInt __asan_update_allocation_context(void* addr);

View File

@@ -0,0 +1,80 @@
module std::core::sanitizer;
macro void annotate_contiguous_container(void* beg, void* end, void* old_mid, void* new_mid)
{
$if env::ADDRESS_SANITIZER:
__sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid);
$endif
}
macro void annotate_double_ended_contiguous_container(void* storage_beg, void* storage_end, void* old_container_beg, void* old_container_end, void* new_container_beg, void* new_container_end)
{
$if env::ADDRESS_SANITIZER:
__sanitizer_annotate_double_ended_contiguous_container(storage_beg, storage_end, old_container_beg, old_container_end, new_container_beg, new_container_end);
$endif
}
macro void print_stack_trace()
{
$if env::ADDRESS_SANITIZER:
__sanitizer_print_stack_trace();
$endif
}
fn void set_death_callback(VoidFn callback)
{
$if env::ANY_SANITIZER:
__sanitizer_set_death_callback(callback);
$endif
}
module std::core::sanitizer @if (env::ANY_SANITIZER);
struct __Sanitizer_sandbox_arguments
{
CInt coverage_sandboxed;
iptr coverage_fd;
CUInt coverage_max_block_size;
}
extern fn void __sanitizer_set_report_path(ZString path);
extern fn void __sanitizer_set_report_fd(void* fd);
extern fn ZString __sanitizer_get_report_path();
extern fn void __sanitizer_sandbox_on_notify(__Sanitizer_sandbox_arguments* args);
extern fn void __sanitizer_report_error_summary(ZString error_summary);
extern fn ushort __sanitizer_unaligned_load16(void* p);
extern fn uint __sanitizer_unaligned_load32(void* p);
extern fn ulong __sanitizer_unaligned_load64(void* p);
extern fn void __sanitizer_unaligned_store16(void* p, ushort x);
extern fn void __sanitizer_unaligned_store32(void* p, uint x);
extern fn void __sanitizer_unaligned_store64(void* p, ulong x);
extern fn CInt __sanitizer_acquire_crash_state();
extern fn void __sanitizer_annotate_contiguous_container(void* beg, void* end, void* old_mid, void* new_mid);
extern fn void __sanitizer_annotate_double_ended_contiguous_container(void* storage_beg, void* storage_end,
void* old_container_beg, void* old_container_end,
void* new_container_beg, void* new_container_end);
extern fn CInt __sanitizer_verify_contiguous_container(void* beg, void* mid, void* end);
extern fn CInt __sanitizer_verify_double_ended_contiguous_container(
void* storage_beg, void* container_beg,
void* container_end, void* storage_end);
extern fn void* __sanitizer_contiguous_container_find_bad_address(void* beg, void* mid, void* end);
extern fn void* __sanitizer_double_ended_contiguous_container_find_bad_address(
void* storage_beg, void* container_beg,
void* container_end, void* storage_end);
extern fn void __sanitizer_print_stack_trace();
extern fn void __sanitizer_symbolize_pc(void* pc, ZString fmt, char* out_buf, usz out_buf_size);
extern fn void __sanitizer_symbolize_global(void* data_ptr, ZString fmt, char* out_buf, usz out_buf_size);
extern fn void __sanitizer_set_death_callback(VoidFn callback);
extern fn void __sanitizer_weak_hook_memcmp(void* called_pc, void* s1, void* s2, usz n, CInt result);
extern fn void __sanitizer_weak_hook_strncmp(void* called_pc, ZString s1, ZString s2, usz n, CInt result);
extern fn void __sanitizer_weak_hook_strncasecmp(void* called_pc, ZString s1, ZString s2, usz n, CInt result);
extern fn void __sanitizer_weak_hook_strcmp(void* called_pc, ZString s1, ZString s2, CInt result);
extern fn void __sanitizer_weak_hook_strcasecmp(void* called_pc, ZString s1, ZString s2, CInt result);
extern fn void __sanitizer_weak_hook_strstr(void* called_pc, ZString s1, ZString s2, char* result);
extern fn void __sanitizer_weak_hook_strcasestr(void* called_pc, ZString s1, ZString s2, char* result);
extern fn void __sanitizer_weak_hook_memmem(void* called_pc, void* s1, usz len1, void* s2, usz len2, void* result);
extern fn void __sanitizer_print_memory_profile(usz top_percent, usz max_number_of_contexts);
extern fn void __sanitizer_start_switch_fiber(void** fake_stack_save, void* bottom, usz size);
extern fn void __sanitizer_finish_switch_fiber(void* fake_stack_save, void** bottom_old, usz* size_old);
extern fn CInt __sanitizer_get_module_and_offset_for_pc(void* pc, char* module_path, usz module_path_len, void** pc_offset);

View File

@@ -0,0 +1,39 @@
module std::core::sanitizer::tsan;
distinct MutexFlags = inline CUInt;
const MutexFlags MUTEX_LINKER_INIT = 1 << 0;
const MutexFlags MUTEX_WRITE_REENTRANT = 1 << 1;
const MutexFlags MUTEX_READ_REENTRANT = 1 << 2;
const MutexFlags MUTEX_NOT_STATIC = 1 << 8;
const MutexFlags MUTEX_READ_LOCK = 1 << 3;
const MutexFlags MUTEX_TRY_LOCK = 1 << 4;
const MutexFlags MUTEX_TRY_LOCK_FAILED = 1 << 5;
const MutexFlags MUTEX_RECURSIVE_LOCK = 1 << 6;
const MutexFlags MUTEX_RECURSIVE_UNLOCK = 1 << 7;
const MutexFlags MUTEX_TRY_READ_LOCK = MUTEX_READ_LOCK | MUTEX_TRY_LOCK;
const MutexFlags MUTEX_TRY_READ_LOCK_FAILED = MUTEX_TRY_READ_LOCK | MUTEX_TRY_LOCK_FAILED;
macro void mutex_create(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_create(addr, flags); $endif }
macro void mutex_destroy(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_destroy(addr, flags); $endif }
macro void mutex_pre_lock(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_pre_lock(addr, flags); $endif }
macro void mutex_post_lock(void* addr, MutexFlags flags, CInt recursion) { $if env::THREAD_SANITIZER: __tsan_mutex_post_lock(addr, flags, recursion); $endif }
macro CInt mutex_pre_unlock(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: return __tsan_mutex_pre_unlock(addr, flags); $else return 0; $endif }
macro void mutex_post_unlock(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_post_unlock(addr, flags); $endif }
macro void mutex_pre_signal(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_pre_signal(addr, flags); $endif }
macro void mutex_post_signal(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_post_signal(addr, flags); $endif }
macro void mutex_pre_divert(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_pre_divert(addr, flags); $endif }
macro void mutex_post_divert(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_post_divert(addr, flags); $endif }
module std::core::sanitizer::tsan @if(env::THREAD_SANITIZER) @private;
extern fn void __tsan_mutex_create(void* addr, CUInt flags);
extern fn void __tsan_mutex_destroy(void* addr, CUInt flags);
extern fn void __tsan_mutex_pre_lock(void* addr, CUInt flags);
extern fn void __tsan_mutex_post_lock(void* addr, CUInt flags, CInt recursion);
extern fn CInt __tsan_mutex_pre_unlock(void* addr, CUInt flags);
extern fn void __tsan_mutex_post_unlock(void* addr, CUInt flags);
extern fn void __tsan_mutex_pre_signal(void* addr, CUInt flags);
extern fn void __tsan_mutex_post_signal(void* addr, CUInt flags);
extern fn void __tsan_mutex_pre_divert(void* addr, CUInt flags);
extern fn void __tsan_mutex_post_divert(void* addr, CUInt flags);

View File

@@ -291,7 +291,9 @@ typedef enum
WIN_CRT_DEFAULT = -1,
WIN_CRT_NONE = 0,
WIN_CRT_DYNAMIC = 1,
WIN_CRT_STATIC = 2,
WIN_CRT_DYNAMIC_DEBUG = 2,
WIN_CRT_STATIC = 3,
WIN_CRT_STATIC_DEBUG = 4,
} WinCrtLinking;
typedef enum
@@ -349,6 +351,15 @@ typedef enum
ARCH_OS_TARGET_LAST = WINDOWS_X64
} ArchOsTarget;
typedef enum
{
SANITIZE_NOT_SET = -1,
SANITIZE_NONE,
SANITIZE_ADDRESS,
SANITIZE_MEMORY,
SANITIZE_THREAD,
} SanitizeMode;
#define ANY_WINDOWS_ARCH_OS WINDOWS_AARCH64: case WINDOWS_X64: case MINGW_X64
typedef enum
@@ -482,6 +493,7 @@ typedef struct BuildOptions_
SizeOptimizationLevel optsize;
RiscvFloatCapability riscv_float_capability;
MemoryEnvironment memory_environment;
SanitizeMode sanitize_mode;
bool print_keywords;
bool print_attributes;
bool print_builtins;
@@ -614,8 +626,11 @@ typedef struct
StructReturn x86_struct_return : 3;
X86VectorCapability x86_vector_capability : 4;
RiscvFloatCapability riscv_float_capability : 4;
bool trap_on_wrap : 1;
Win64Simd pass_win64_simd_as_arrays : 3;
bool trap_on_wrap : 1;
bool sanitize_address : 1;
bool sanitize_memory : 1;
bool sanitize_thread : 1;
FpOpt fp_math;
SafetyLevel safe_mode;
PanicLevel panic_level;

View File

@@ -26,10 +26,12 @@ static const char *memory_environment[6] = {
[MEMORY_ENV_NONE] = "none",
};
static const char *wincrt_linking[3] = {
static const char *wincrt_linking[5] = {
[WIN_CRT_NONE] = "none",
[WIN_CRT_DYNAMIC] = "dynamic",
[WIN_CRT_DYNAMIC_DEBUG] = "dynamic-debug",
[WIN_CRT_STATIC] = "static",
[WIN_CRT_STATIC_DEBUG] = "static-debug",
};
static const char *vector_conv[2] = {
@@ -99,6 +101,13 @@ static const char *reloc_models[5] = {
[RELOC_BIG_PIE] = "PIE",
};
static const char *sanitize_modes[4] = {
[SANITIZE_NONE] = "none",
[SANITIZE_ADDRESS] = "address",
[SANITIZE_MEMORY] = "memory",
[SANITIZE_THREAD] = "thread",
};
Project *project_load(void);
BuildTarget *project_select_target(Project *project, const char *optional_target);
void update_feature_flags(const char ***flags, const char ***removed_flag, const char *arg, bool add);

View File

@@ -179,7 +179,7 @@ static void usage(void)
PRINTF(" --print-input - Print inputted C3 files to stdout.");
PRINTF("");
PRINTF(" --winsdk <dir> - Set the directory for Windows system library files for cross compilation.");
PRINTF(" --wincrt=<option> - Windows CRT linking: none, static, dynamic (default).");
PRINTF(" --wincrt=<option> - Windows CRT linking: none, static-debug, static, dynamic-debug (default if debug info enabled), dynamic (default).");
PRINTF(" --windef <file> - Use Windows 'def' file for function exports instead of 'dllexport'.");
PRINTF("");
PRINTF(" --macossdk <dir> - Set the directory for the MacOS SDK for cross compilation.");
@@ -190,6 +190,7 @@ static void usage(void)
PRINTF(" --linux-crtbegin <dir> - Set the directory to use for finding crtbegin.o and related files.");
PRINTF("");
PRINTF(" --vector-conv=<option> - Set vector conversion behaviour: default, old.");
PRINTF(" --sanitize=<option> - Enable sanitizer: address, memory, thread.");
}
@@ -1052,7 +1053,12 @@ static void parse_option(BuildOptions *options)
}
if ((argopt = match_argopt("wincrt")))
{
options->win.crt_linking = (WinCrtLinking)parse_multi_option(argopt, 3, wincrt_linking);
options->win.crt_linking = (WinCrtLinking)parse_multi_option(argopt, 5, wincrt_linking);
return;
}
if ((argopt = match_argopt("sanitize")))
{
options->sanitize_mode = (SanitizeMode)parse_multi_option(argopt, 4, sanitize_modes);
return;
}
if (match_longopt("macos-sdk-version"))
@@ -1238,6 +1244,7 @@ BuildOptions parse_arguments(int argc, const char *argv[])
.linker_type = LINKER_TYPE_NOT_SET,
.strip_unused = STRIP_UNUSED_NOT_SET,
.single_module = SINGLE_MODULE_NOT_SET,
.sanitize_mode = SANITIZE_NOT_SET,
.unroll_loops = UNROLL_LOOPS_NOT_SET,
.merge_functions = MERGE_FUNCTIONS_NOT_SET,
.slp_vectorization = VECTORIZATION_NOT_SET,
@@ -1246,7 +1253,6 @@ BuildOptions parse_arguments(int argc, const char *argv[])
.build_dir = NULL,
.output_dir = NULL,
.script_dir = NULL,
};
for (int i = DIAG_NONE; i < DIAG_WARNING_TYPE; i++)
{

View File

@@ -372,6 +372,19 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
if (options->linuxpaths.crt) target->linuxpaths.crt = options->linuxpaths.crt;
if (options->linuxpaths.crtbegin) target->linuxpaths.crtbegin = options->linuxpaths.crtbegin;
if (options->fp_math != FP_DEFAULT) target->feature.fp_math = options->fp_math;
switch (options->sanitize_mode)
{
case SANITIZE_NOT_SET: break;
case SANITIZE_NONE:
target->feature.sanitize_address = false;
target->feature.sanitize_memory = false;
target->feature.sanitize_thread = false;
break;
case SANITIZE_ADDRESS: target->feature.sanitize_address = true; break;
case SANITIZE_MEMORY: target->feature.sanitize_memory = true; break;
case SANITIZE_THREAD: target->feature.sanitize_thread = true; break;
default: UNREACHABLE;
}
if (options->x86_vector_capability != X86VECTOR_DEFAULT)
{
target->feature.x86_vector_capability = options->x86_vector_capability;

View File

@@ -38,6 +38,7 @@ const char *project_default_keys[][2] = {
{"panic-msg", "Turn panic message output on or off."},
{"reloc", "Relocation model: none, pic, PIC, pie, PIE."},
{"safe", "Set safety (contracts, runtime bounds checking, null pointer checks etc) on or off."},
{"sanitize", "Enable sanitizer: none, address, memory, thread."},
{"show-backtrace", "Print backtrace on signals."},
{"script-dir", "The directory where 'exec' is run."},
{"single-module", "Compile all modules together, enables more inlining."},
@@ -52,7 +53,7 @@ const char *project_default_keys[][2] = {
{"use-stdlib", "Include the standard library (default: true)."},
{"version", "Version using semantic versioning."},
{"warnings", "Warnings used for all targets."},
{"wincrt", "Windows CRT linking: none, static, dynamic (default)."},
{"wincrt", "Windows CRT linking: none, static-debug, static, dynamic-debug (default if debug info enabled), dynamic (default)."},
{"windef", "Windows def file, used as an alternative to dllexport when exporting a DLL."},
{"winsdk", "Set the path to Windows system library files for cross compilation."},
{"x86cpu", "Set general level of x64 cpu: baseline, ssse3, sse4, avx1, avx2-v1, avx2-v2 (Skylake/Zen1+), avx512 (Icelake/Zen4+), native."},
@@ -107,6 +108,7 @@ const char* project_target_keys[][2] = {
{"panic-msg", "Turn panic message output on or off."},
{"reloc", "Relocation model: none, pic, PIC, pie, PIE."},
{"safe", "Set safety (contracts, runtime bounds checking, null pointer checks etc) on or off."},
{"sanitize", "Enable sanitizer: none, address, memory, thread."},
{"script-dir", "The directory where 'exec' is run."},
{"single-module", "Compile all modules together, enables more inlining."},
{"show-backtrace", "Print backtrace on signals."},
@@ -122,7 +124,7 @@ const char* project_target_keys[][2] = {
{"use-stdlib", "Include the standard library (default: true)."},
{"version", "Version using semantic versioning."},
{"warnings", "Warnings used for all targets."},
{"wincrt", "Windows CRT linking: none, static, dynamic (default)."},
{"wincrt", "Windows CRT linking: none, static-debug, static, dynamic-debug (default if debug info enabled), dynamic (default)."},
{"windef", "Windows def file, used as an alternative to dllexport when exporting a DLL."},
{"winsdk", "Set the path to Windows system library files for cross compilation."},
{"x86cpu", "Set general level of x64 cpu: baseline, ssse3, sse4, avx1, avx2-v1, avx2-v2 (Skylake/Zen1+), avx512 (Icelake/Zen4+), native."},
@@ -274,11 +276,27 @@ static void load_into_build_target(JSONObject *json, const char *target_name, Bu
RelocModel reloc = GET_SETTING(RelocModel, "reloc", reloc_models, "'none', 'pic', 'PIC', 'pie' or 'PIE'.");
if (reloc != RELOC_DEFAULT) target->reloc_model = reloc;
// Sanitize
SanitizeMode sanitize_mode = GET_SETTING(SanitizeMode, "sanitize", sanitize_modes, "'none', 'address', 'memory' or 'thread'.");
switch (sanitize_mode)
{
case SANITIZE_NOT_SET: break;
case SANITIZE_NONE:
target->feature.sanitize_address = false;
target->feature.sanitize_memory = false;
target->feature.sanitize_thread = false;
break;
case SANITIZE_ADDRESS: target->feature.sanitize_address = true; break;
case SANITIZE_MEMORY: target->feature.sanitize_memory = true; break;
case SANITIZE_THREAD: target->feature.sanitize_thread = true; break;
default: UNREACHABLE;
}
// Cpu
target->cpu = get_string(PROJECT_JSON, target_name, json, "cpu", target->cpu);
// WinCRT
WinCrtLinking wincrt = GET_SETTING(WinCrtLinking, "wincrt", wincrt_linking, "'none', 'static' or 'dynamic'.");
WinCrtLinking wincrt = GET_SETTING(WinCrtLinking, "wincrt", wincrt_linking, "'none', 'static-debug', 'staticdebug, 'dynamic-debug' or 'dynamic'.");
if (wincrt != WIN_CRT_DEFAULT) target->win.crt_linking = wincrt;
// fp-math

View File

@@ -1068,6 +1068,104 @@ static void execute_scripts(void)
dir_change(old_path);
free(old_path);
}
static void check_sanitizer_options(BuildTarget *target)
{
if (target->feature.sanitize_address)
{
if (target->feature.sanitize_memory || target->feature.sanitize_thread)
{
error_exit("Address sanitizer cannot be used together with memory or thread sanitizer.");
}
switch (target->arch_os_target)
{
case WINDOWS_X64:
{
WinCrtLinking crt_linking = target->win.crt_linking;
if (crt_linking == WIN_CRT_DEFAULT)
{
error_exit("Please specify `static` or `dynamic` for `wincrt` when using address sanitizer.");
}
if (crt_linking == WIN_CRT_STATIC_DEBUG || crt_linking == WIN_CRT_DYNAMIC_DEBUG)
{
// We currently don't have ASan runtime libraries linked against debug CRT.
error_exit("Address sanitizer cannot be used when using `static-debug` or `dynamic-debug` for `wincrt`. Please use `static` or `dynamic` instead.");
}
WARNING("Using address sanitizer on Windows requires the sanitizer option `detect_odr_violation=0`, either set by returning it from `__asan_default_options`, or via an environment variable `ASAN_OPTIONS=detect_odr_violation=0`");
break;
}
case LINUX_X86:
case LINUX_X64:
case MACOS_AARCH64:
case MACOS_X64:
case FREEBSD_X86:
case FREEBSD_X64:
case NETBSD_X86:
case NETBSD_X64:
break;
default:
error_exit("Address sanitizer is only supported on Linux, Darwin and Windows.");
}
if (target->type == TARGET_TYPE_BENCHMARK)
{
WARNING("Running benchmarks with address sanitizer enabled!");
}
}
if (target->feature.sanitize_memory)
{
if (target->feature.sanitize_address || target->feature.sanitize_thread)
{
error_exit("Memory sanitizer cannot be used together with address or thread sanitizer.");
}
switch (target->arch_os_target)
{
case LINUX_AARCH64:
case LINUX_X86:
case LINUX_X64:
case FREEBSD_X86:
case FREEBSD_X64:
case NETBSD_X86:
case NETBSD_X64:
break;
default:
error_exit("Memory sanitizer is only supported on Linux.");
}
if (target->reloc_model != RELOC_BIG_PIE)
{
error_exit("Memory sanitizer requires `PIE` relocation model.");
}
if (target->type == TARGET_TYPE_BENCHMARK)
{
WARNING("Running benchmarks with memory sanitizer enabled!");
}
}
if (target->feature.sanitize_thread)
{
if (target->feature.sanitize_address || target->feature.sanitize_memory)
{
error_exit("Thread sanitizer cannot be used together with address or memory sanitizer.");
}
switch (target->arch_os_target)
{
case LINUX_AARCH64:
case LINUX_X64:
case MACOS_AARCH64:
case MACOS_X64:
case FREEBSD_X64:
case NETBSD_X64:
break;
default:
error_exit("Thread sanitizer is only supported on 64-bit Linux and Darwin.");
}
if (target->type == TARGET_TYPE_BENCHMARK)
{
WARNING("Running benchmarks with thread sanitizer enabled!");
}
}
}
void compile()
{
symtab_init(compiler.build.symtab_size);
@@ -1093,6 +1191,7 @@ void compile()
unit->module = compiler.context.core_module;
compiler.context.core_unit = unit;
target_setup(&compiler.build);
check_sanitizer_options(&compiler.build);
resolve_libraries(&compiler.build);
compiler.context.sources = compiler.build.sources;
FOREACH(LibraryTarget *, lib, compiler.build.ccompiling_libraries)
@@ -1130,6 +1229,9 @@ void compile()
setup_bool_define("BENCHMARKING", compiler.build.benchmarking);
setup_int_define("JMP_BUF_SIZE", jump_buffer_size(), type_int);
setup_bool_define("TESTING", compiler.build.testing);
setup_bool_define("ADDRESS_SANITIZER", compiler.build.feature.sanitize_address);
setup_bool_define("MEMORY_SANITIZER", compiler.build.feature.sanitize_memory);
setup_bool_define("THREAD_SANITIZER", compiler.build.feature.sanitize_thread);
type_init_cint();

View File

@@ -494,6 +494,9 @@ typedef struct
bool attr_finalizer : 1;
bool attr_interface_method : 1;
bool attr_dynamic : 1;
bool attr_nosanitize_address : 1;
bool attr_nosanitize_memory : 1;
bool attr_nosanitize_thread : 1;
bool is_lambda : 1;
union
{

View File

@@ -276,6 +276,7 @@ typedef enum
ATTRIBUTE_NOINLINE,
ATTRIBUTE_NOPADDING,
ATTRIBUTE_NORETURN,
ATTRIBUTE_NOSANITIZE,
ATTRIBUTE_NOSTRIP,
ATTRIBUTE_OBFUSCATE,
ATTRIBUTE_OPERATOR,

View File

@@ -1,4 +1,5 @@
#include "compiler_internal.h"
#include "../utils/whereami.h"
#include "c3_llvm.h"
#if PLATFORM_POSIX
@@ -8,12 +9,12 @@
const char *quote_arg = "\"";
const char *concat_arg = ":";
const char *concat_quote_arg = "+";
const char *quote_concat_arg = "++";
const char *concat_file_arg = "/";
#define add_quote_arg(arg_) vec_add(*args_ref, quote_arg); vec_add(*args_ref, (arg_))
#define add_plain_arg(arg_) vec_add(*args_ref, (arg_))
#define add_concat_file_arg(arg_, arg2_) vec_add(*args_ref, concat_file_arg); vec_add(*args_ref, (arg_)); vec_add(*args_ref, (arg2_))
#define add_concat_arg(arg_, arg2_) vec_add(*args_ref, concat_arg); vec_add(*args_ref, (arg_)); vec_add(*args_ref, (arg2_))
#define add_concat_quote_arg(arg_, arg2_) vec_add(*args_ref, concat_quote_arg); vec_add(*args_ref, (arg_)); vec_add(*args_ref, (arg2_))
#define add_quote_concat_arg(arg_, arg2_) vec_add(*args_ref, quote_concat_arg); vec_add(*args_ref, (arg_)); vec_add(*args_ref, (arg2_))
static char *assemble_linker_command(const char **args, bool extra_quote);
static unsigned assemble_link_arguments(const char **arguments, unsigned len);
@@ -49,7 +50,7 @@ static const char *ld_target(ArchType arch_type)
static void linker_setup_windows(const char ***args_ref, Linker linker_type)
static void linker_setup_windows(const char ***args_ref, Linker linker_type, const char *output_file)
{
add_plain_arg(compiler.build.win.use_win_subsystem ? "/SUBSYSTEM:WINDOWS" : "/SUBSYSTEM:CONSOLE");
if (link_libc()) linking_add_link(&compiler.linking, "dbghelp");
@@ -141,6 +142,37 @@ static void linker_setup_windows(const char ***args_ref, Linker linker_type)
add_concat_quote_arg("/LIBPATH:", windows_sdk->windows_sdk_ucrt_library_path);
add_concat_quote_arg("/LIBPATH:", windows_sdk->vs_library_path);
}
// Link sanitizer runtime libraries
const char *compiler_path = find_executable_path();
const char *asan_dll_src_path = file_append_path(compiler_path, "c3c_rt/clang_rt.asan_dynamic-x86_64.dll");
const char *output_dir = "";
if (compiler.build.output_dir)
{
output_dir = compiler.build.output_dir;
}
else
{
char *filename;
file_namesplit(output_file, &filename, (char**)&output_dir);
}
const char *asan_dll_dst_path = file_append_path(output_dir, "clang_rt.asan_dynamic-x86_64.dll");
file_delete_file(asan_dll_dst_path);
if (compiler.build.feature.sanitize_address)
{
if (compiler.build.win.crt_linking == WIN_CRT_STATIC)
{
add_concat_file_arg(compiler_path, "c3c_rt/clang_rt.asan-x86_64.lib");
}
else
{
add_concat_file_arg(compiler_path, "c3c_rt/clang_rt.asan_dynamic-x86_64.lib");
add_concat_file_arg(compiler_path, "c3c_rt/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib");
DEBUG_LOG("Copying '%s' to '%s'\n", asan_dll_src_path, asan_dll_dst_path);
file_copy_file(asan_dll_src_path, asan_dll_dst_path, true);
}
}
linking_add_link(&compiler.linking, "kernel32");
linking_add_link(&compiler.linking, "ntdll");
linking_add_link(&compiler.linking, "user32");
@@ -149,45 +181,46 @@ static void linker_setup_windows(const char ***args_ref, Linker linker_type)
linking_add_link(&compiler.linking, "Ws2_32");
linking_add_link(&compiler.linking, "legacy_stdio_definitions");
WinCrtLinking crt_linking = compiler.build.win.crt_linking;
// Do not link any.
if (compiler.build.win.crt_linking == WIN_CRT_NONE) return;
if (crt_linking == WIN_CRT_NONE) return;
if (compiler.build.win.crt_linking == WIN_CRT_STATIC)
{
if (is_debug)
{
linking_add_link(&compiler.linking, "libucrtd");
linking_add_link(&compiler.linking, "libvcruntimed");
linking_add_link(&compiler.linking, "libcmtd");
linking_add_link(&compiler.linking, "libcpmtd");
}
else
{
linking_add_link(&compiler.linking, "libucrt");
linking_add_link(&compiler.linking, "libvcruntime");
linking_add_link(&compiler.linking, "libcmt");
linking_add_link(&compiler.linking, "libcpmt");
}
}
else
if (crt_linking == WIN_CRT_DEFAULT)
{
// When cross compiling we might not have the relevant debug libraries.
// if so, then exclude them.
if (is_debug && link_with_dynamic_debug_libc)
{
linking_add_link(&compiler.linking, "ucrtd");
linking_add_link(&compiler.linking, "vcruntimed");
linking_add_link(&compiler.linking, "msvcrtd");
linking_add_link(&compiler.linking, "msvcprtd");
}
else
{
linking_add_link(&compiler.linking, "ucrt");
linking_add_link(&compiler.linking, "vcruntime");
linking_add_link(&compiler.linking, "msvcrt");
linking_add_link(&compiler.linking, "msvcprt");
}
crt_linking = is_debug && link_with_dynamic_debug_libc ? WIN_CRT_DYNAMIC_DEBUG : WIN_CRT_DYNAMIC;
}
if (crt_linking == WIN_CRT_STATIC_DEBUG)
{
linking_add_link(&compiler.linking, "libucrtd");
linking_add_link(&compiler.linking, "libvcruntimed");
linking_add_link(&compiler.linking, "libcmtd");
linking_add_link(&compiler.linking, "libcpmtd");
}
else if (crt_linking == WIN_CRT_STATIC)
{
linking_add_link(&compiler.linking, "libucrt");
linking_add_link(&compiler.linking, "libvcruntime");
linking_add_link(&compiler.linking, "libcmt");
linking_add_link(&compiler.linking, "libcpmt");
}
else if (crt_linking == WIN_CRT_DYNAMIC_DEBUG)
{
linking_add_link(&compiler.linking, "ucrtd");
linking_add_link(&compiler.linking, "vcruntimed");
linking_add_link(&compiler.linking, "msvcrtd");
linking_add_link(&compiler.linking, "msvcprtd");
}
else
{
linking_add_link(&compiler.linking, "ucrt");
linking_add_link(&compiler.linking, "vcruntime");
linking_add_link(&compiler.linking, "msvcrt");
linking_add_link(&compiler.linking, "msvcprt");
}
add_plain_arg("/NOLOGO");
}
@@ -344,19 +377,19 @@ static void linker_setup_linux(const char ***args_ref, Linker linker_type)
if (is_pie_pic(compiler.platform.reloc_model))
{
add_plain_arg("-pie");
add_quote_concat_arg(crt_dir, "Scrt1.o");
add_quote_concat_arg(crt_begin_dir, "crtbeginS.o");
add_quote_concat_arg(crt_dir, "crti.o");
add_quote_concat_arg(crt_begin_dir, "crtendS.o");
add_concat_file_arg(crt_dir, "Scrt1.o");
add_concat_file_arg(crt_begin_dir, "crtbeginS.o");
add_concat_file_arg(crt_dir, "crti.o");
add_concat_file_arg(crt_begin_dir, "crtendS.o");
}
else
{
add_quote_concat_arg(crt_dir, "crt1.o");
add_quote_concat_arg(crt_begin_dir, "crtbegin.o");
add_quote_concat_arg(crt_dir, "crti.o");
add_quote_concat_arg(crt_begin_dir, "crtend.o");
add_concat_file_arg(crt_dir, "crt1.o");
add_concat_file_arg(crt_begin_dir, "crtbegin.o");
add_concat_file_arg(crt_dir, "crti.o");
add_concat_file_arg(crt_begin_dir, "crtend.o");
}
add_quote_concat_arg(crt_dir, "crtn.o");
add_concat_file_arg(crt_dir, "crtn.o");
add_concat_quote_arg("-L", crt_dir);
add_plain_arg("-L/usr/lib/x86_64-linux-gnu/libdl.so");
add_plain_arg("--dynamic-linker=/lib64/ld-linux-x86-64.so.2");
@@ -391,19 +424,19 @@ static void linker_setup_freebsd(const char ***args_ref, Linker linker_type)
if (is_pie_pic(compiler.platform.reloc_model))
{
add_plain_arg("-pie");
add_quote_concat_arg(crt_dir, "Scrt1.o");
add_quote_concat_arg(crt_dir, "crtbeginS.o");
add_quote_concat_arg(crt_dir, "crti.o");
add_quote_concat_arg(crt_dir, "crtendS.o");
add_concat_file_arg(crt_dir, "Scrt1.o");
add_concat_file_arg(crt_dir, "crtbeginS.o");
add_concat_file_arg(crt_dir, "crti.o");
add_concat_file_arg(crt_dir, "crtendS.o");
}
else
{
add_quote_concat_arg(crt_dir, "crt1.o");
add_quote_concat_arg(crt_dir, "crtbegin.o");
add_quote_concat_arg(crt_dir, "crti.o");
add_quote_concat_arg(crt_dir, "crtend.o");
add_concat_file_arg(crt_dir, "crt1.o");
add_concat_file_arg(crt_dir, "crtbegin.o");
add_concat_file_arg(crt_dir, "crti.o");
add_concat_file_arg(crt_dir, "crtend.o");
}
add_quote_concat_arg(crt_dir, "crtn.o");
add_concat_file_arg(crt_dir, "crtn.o");
add_concat_quote_arg("-L", crt_dir);
add_plain_arg("--dynamic-linker=/libexec/ld-elf.so.1");
linking_add_link(&compiler.linking, "c");
@@ -499,7 +532,7 @@ static bool linker_setup(const char ***args_ref, const char **files_to_link, uns
case OS_UNSUPPORTED:
UNREACHABLE
case OS_TYPE_WIN32:
linker_setup_windows(args_ref, linker_type);
linker_setup_windows(args_ref, linker_type, output_file);
break;
case OS_TYPE_MACOSX:
linker_setup_macos(args_ref, linker_type);
@@ -526,6 +559,7 @@ static bool linker_setup(const char ***args_ref, const char **files_to_link, uns
case OS_TYPE_NONE:
break;
}
for (unsigned i = 0; i < file_count; i++)
{
add_quote_arg(files_to_link[i]);
@@ -533,7 +567,7 @@ static bool linker_setup(const char ***args_ref, const char **files_to_link, uns
FOREACH(const char *, dir, compiler.build.linker_libdirs)
{
add_quote_concat_arg(lib_path_opt, dir);
add_concat_file_arg(lib_path_opt, dir);
}
FOREACH(const char *, arg, compiler.build.link_args)
{
@@ -547,6 +581,34 @@ static bool linker_setup(const char ***args_ref, const char **files_to_link, uns
add_linked_libs(args_ref, target->linked_libs, use_win);
}
add_linked_libs(args_ref, linking->links, use_win);
// Link sanitizer runtime libraries
if (compiler.platform.os == OS_TYPE_MACOSX)
{
if (compiler.build.feature.sanitize_address || compiler.build.feature.sanitize_thread)
{
const char *compiler_path = find_executable_path();
if (compiler.build.feature.sanitize_address)
{
add_concat_file_arg(compiler_path, "c3c_rt/libclang_rt.asan_osx_dynamic.dylib");
}
if (compiler.build.feature.sanitize_thread)
{
add_concat_file_arg(compiler_path, "c3c_rt/libclang_rt.tsan_osx_dynamic.dylib");
}
// Add rpath for sanitizer runtime libraries last, after user-provided link args have been added.
add_plain_arg("-rpath");
add_concat_file_arg(compiler_path, "c3c_rt");
}
}
else if (compiler.platform.os == OS_TYPE_LINUX)
{
if (compiler.build.feature.sanitize_address) add_plain_arg("-fsanitize=address");
if (compiler.build.feature.sanitize_memory) add_plain_arg("-fsanitize=memory");
if (compiler.build.feature.sanitize_thread) add_plain_arg("-fsanitize=thread");
}
return true;
}
#undef add_arg2
@@ -614,7 +676,13 @@ static unsigned assemble_link_arguments(const char **arguments, unsigned len)
{
const char *arg = arguments[i];
if (arg == quote_arg) continue;
if (arg == concat_arg || arg == quote_concat_arg || arg == concat_quote_arg)
if (arg == concat_file_arg)
{
const char *a = arguments[++i];
const char *b = arguments[++i];
arguments[count++] = file_append_path(a, b);
}
if (arg == concat_arg || arg == concat_quote_arg)
{
const char *a = arguments[++i];
const char *b = arguments[++i];
@@ -714,10 +782,17 @@ static char *assemble_linker_command(const char **args, bool extra_quote)
scratch_buffer_append(args[++i]);
continue;
}
if (arg == quote_concat_arg)
if (arg == concat_file_arg)
{
scratch_buffer_append_char('"');
scratch_buffer_append_in_quote(args[++i]);
const char *a = args[++i];
scratch_buffer_append_in_quote(a);
size_t len = strlen(a);
char c = len ? a[len - 1] : '/';
if (c != '/' && !(PLATFORM_WINDOWS && c == '\\'))
{
scratch_buffer_append(PLATFORM_WINDOWS ? "\\\\" : "/");
}
scratch_buffer_append_in_quote(args[++i]);
scratch_buffer_append_char('"');
continue;
@@ -786,7 +861,9 @@ void platform_linker(const char *output_file, const char **files, unsigned file_
{
// Create .dSYM
scratch_buffer_clear();
scratch_buffer_printf("dsymutil -arch %s \"%s\"", arch_to_linker_arch(compiler.platform.arch), output_file);
scratch_buffer_printf("dsymutil -arch %s \"", arch_to_linker_arch(compiler.platform.arch));
scratch_buffer_append_in_quote(output_file);
scratch_buffer_append("\"");
if (compiler.build.print_linking) puts(scratch_buffer_to_string());
if (system(scratch_buffer_to_string()) != 0)
{

View File

@@ -840,6 +840,9 @@ static void llvm_codegen_setup()
attribute_id.optnone = lookup_attribute("optnone");
attribute_id.readonly = lookup_attribute("readonly");
attribute_id.reassoc = lookup_attribute("reassoc");
attribute_id.sanitize_address = lookup_attribute("sanitize_address");
attribute_id.sanitize_memory = lookup_attribute("sanitize_memory");
attribute_id.sanitize_thread = lookup_attribute("sanitize_thread");
attribute_id.sext = lookup_attribute("signext");
attribute_id.sret = lookup_attribute("sret");
attribute_id.ssp = lookup_attribute("ssp");
@@ -988,7 +991,10 @@ static inline void llvm_optimize(GenContext *c)
.opt.slp_vectorize = compiler.build.slp_vectorization == VECTORIZATION_ON,
.opt.unroll_loops = compiler.build.unroll_loops == UNROLL_LOOPS_ON,
.opt.interleave_loops = compiler.build.unroll_loops == UNROLL_LOOPS_ON,
.opt.merge_functions = compiler.build.merge_functions == MERGE_FUNCTIONS_ON
.opt.merge_functions = compiler.build.merge_functions == MERGE_FUNCTIONS_ON,
.sanitizer.address_sanitize = compiler.build.feature.sanitize_address,
.sanitizer.mem_sanitize = compiler.build.feature.sanitize_memory,
.sanitizer.thread_sanitize = compiler.build.feature.sanitize_thread
};
if (!llvm_run_passes(c->module, c->machine, &passes))
{
@@ -1187,6 +1193,20 @@ void llvm_append_function_attributes(GenContext *c, Decl *decl)
{
llvm_attribute_add(c, function, attribute_id.naked, -1);
}
if (compiler.build.feature.sanitize_address && !decl->func_decl.attr_nosanitize_address)
{
llvm_attribute_add(c, function, attribute_id.sanitize_address, -1);
}
if (compiler.build.feature.sanitize_memory && !decl->func_decl.attr_nosanitize_memory)
{
llvm_attribute_add(c, function, attribute_id.sanitize_memory, -1);
}
if (compiler.build.feature.sanitize_thread && !decl->func_decl.attr_nosanitize_thread)
{
llvm_attribute_add(c, function, attribute_id.sanitize_thread, -1);
}
LLVMSetFunctionCallConv(function, llvm_call_convention_from_call(prototype->call_abi));
}
LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl)

View File

@@ -277,6 +277,9 @@ typedef struct
unsigned optnone; // No optimization
unsigned readonly; // No reads on pointer
unsigned reassoc; // allow reassociateion
unsigned sanitize_address; // enable address sanitizer (address)
unsigned sanitize_memory; // enable address sanitizer (memory)
unsigned sanitize_thread; // enable address sanitizer (thread)
unsigned sext; // sign extend
unsigned sret; // struct return pointer
unsigned ssp; // safe stack protection

View File

@@ -2386,6 +2386,7 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_
[ATTRIBUTE_NOINLINE] = ATTR_FUNC | ATTR_CALL,
[ATTRIBUTE_NOPADDING] = ATTR_STRUCT | ATTR_UNION | ATTR_MEMBER,
[ATTRIBUTE_NORETURN] = CALLABLE_TYPE,
[ATTRIBUTE_NOSANITIZE] = ATTR_FUNC,
[ATTRIBUTE_NOSTRIP] = ATTR_FUNC | ATTR_GLOBAL | ATTR_CONST | EXPORTED_USER_DEFINED_TYPES,
[ATTRIBUTE_OBFUSCATE] = ATTR_ENUM | ATTR_FAULT,
[ATTRIBUTE_OPERATOR] = ATTR_MACRO | ATTR_FUNC,
@@ -2686,6 +2687,34 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_
case ATTRIBUTE_NORETURN:
decl->func_decl.signature.attrs.noreturn = true;
break;
case ATTRIBUTE_NOSANITIZE:
if (!expr)
{
RETURN_SEMA_ERROR(attr, "'%s' requires a string argument, e.g. %s(\"address\").", attr->name, attr->name);
}
if (!sema_analyse_expr(context, expr)) return false;
if (!expr_is_const_string(expr))
{
RETURN_SEMA_ERROR(expr, "Expected a constant string value as argument.");
}
const char *str = expr->const_expr.bytes.ptr;
if (str_eq(str, "address"))
{
decl->func_decl.attr_nosanitize_address = true;
}
else if (str_eq(str, "memory"))
{
decl->func_decl.attr_nosanitize_memory = true;
}
else if (str_eq(str, "thread"))
{
decl->func_decl.attr_nosanitize_thread = true;
}
else
{
RETURN_SEMA_ERROR(expr, "Expected \"address\", \"memory\" or \"thread\" as argument.");
}
return true;
case ATTRIBUTE_WEAK:
if (domain == ATTR_DEF)
{

View File

@@ -337,6 +337,7 @@ void symtab_init(uint32_t capacity)
attribute_list[ATTRIBUTE_NOINLINE] = KW_DEF("@noinline");
attribute_list[ATTRIBUTE_NOPADDING] = KW_DEF("@nopadding");
attribute_list[ATTRIBUTE_NORETURN] = KW_DEF("@noreturn");
attribute_list[ATTRIBUTE_NOSANITIZE] = KW_DEF("@nosanitize");
attribute_list[ATTRIBUTE_NOSTRIP] = KW_DEF("@nostrip");
attribute_list[ATTRIBUTE_OBFUSCATE] = KW_DEF("@obfuscate");
attribute_list[ATTRIBUTE_OPERATOR] = KW_DEF("@operator");

View File

@@ -487,6 +487,18 @@ extern int _getdrive(void);
extern int _chdrive(int drive);
#endif
void file_copy_file(const char *src_path, const char *dst_path, bool overwrite)
{
assert(src_path);
assert(dst_path);
#if (_MSC_VER)
CopyFileW(win_utf8to16(src_path), win_utf8to16(dst_path), !overwrite);
#else
const char *cmd = "cp %s %s %s";
execute_cmd(str_printf(cmd, !overwrite ? "--update=none" : "--update=all", src_path, dst_path), true, NULL);
#endif
}
bool file_delete_file(const char *path)
{
assert(path);

View File

@@ -73,6 +73,7 @@ const char* file_expand_path(const char* path);
const char* find_lib_dir(void);
const char *find_rel_exe_dir(const char *dir);
void file_copy_file(const char *src_path, const char *dst_path, bool overwrite);
void file_delete_all_files_in_dir_with_suffix(const char *dir, const char *suffix);
bool file_delete_file(const char *path);
bool file_is_dir(const char *file);