mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
Compare commits
1 Commits
v0.6.5
...
cbuttner/s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ab5fdf954 |
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
127
lib/std/core/sanitizer/asan.c3
Normal file
127
lib/std/core/sanitizer/asan.c3
Normal 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);
|
||||
80
lib/std/core/sanitizer/sanitizer.c3
Normal file
80
lib/std/core/sanitizer/sanitizer.c3
Normal 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);
|
||||
39
lib/std/core/sanitizer/tsan.c3
Normal file
39
lib/std/core/sanitizer/tsan.c3
Normal 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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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++)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -276,6 +276,7 @@ typedef enum
|
||||
ATTRIBUTE_NOINLINE,
|
||||
ATTRIBUTE_NOPADDING,
|
||||
ATTRIBUTE_NORETURN,
|
||||
ATTRIBUTE_NOSANITIZE,
|
||||
ATTRIBUTE_NOSTRIP,
|
||||
ATTRIBUTE_OBFUSCATE,
|
||||
ATTRIBUTE_OPERATOR,
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user