mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-13 01:21:38 -07:00
Merge remote-tracking branch 'offical/bill/raddebugger-custom-section'
This commit is contained in:
@@ -119,7 +119,8 @@ jmag :: proc(value: Quaternion) -> Float ---
|
||||
kmag :: proc(value: Quaternion) -> Float ---
|
||||
conj :: proc(value: Complex_Or_Quaternion) -> Complex_Or_Quaternion ---
|
||||
|
||||
expand_values :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
|
||||
expand_values :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
|
||||
compress_values :: proc(values: ...) -> Struct_Or_Array_Like_Type ---
|
||||
|
||||
min :: proc(values: ..T) -> T ---
|
||||
max :: proc(values: ..T) -> T ---
|
||||
|
||||
@@ -221,6 +221,9 @@ type_map_cell_info :: proc($T: typeid) -> ^runtime.Map_Cell_Info ---
|
||||
type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
|
||||
type_merge :: proc($U, $V: typeid) -> typeid where type_is_union(U), type_is_union(V) ---
|
||||
|
||||
type_integer_to_unsigned :: proc($T: typeid) -> type where type_is_integer(T), !type_is_unsigned(T) ---
|
||||
type_integer_to_signed :: proc($T: typeid) -> type where type_is_integer(T), type_is_unsigned(T) ---
|
||||
|
||||
type_has_shared_fields :: proc($U, $V: typeid) -> bool where type_is_struct(U), type_is_struct(V) ---
|
||||
|
||||
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
|
||||
@@ -274,8 +277,12 @@ simd_lanes_ge :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
|
||||
simd_extract :: proc(a: #simd[N]T, idx: uint) -> T ---
|
||||
simd_replace :: proc(a: #simd[N]T, idx: uint, elem: T) -> #simd[N]T ---
|
||||
|
||||
simd_reduce_add_bisect :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||
simd_reduce_mul_bisect :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||
simd_reduce_add_ordered :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||
simd_reduce_mul_ordered :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||
simd_reduce_add_pairs :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||
simd_reduce_mul_pairs :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||
simd_reduce_min :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||
simd_reduce_max :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||
simd_reduce_and :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||
@@ -298,7 +305,7 @@ simd_masked_store :: proc(ptr: rawptr, val: #simd[N]T, mask: #simd[N]U)
|
||||
simd_masked_expand_load :: proc(ptr: rawptr, val: #simd[N]T, mask: #simd[N]U) -> #simd[N]T where type_is_integer(U) || type_is_boolean(U) ---
|
||||
simd_masked_compress_store :: proc(ptr: rawptr, val: #simd[N]T, mask: #simd[N]U) where type_is_integer(U) || type_is_boolean(U) ---
|
||||
|
||||
|
||||
simd_indices :: proc($T: typeid/#simd[$N]$E) -> T where type_is_numeric(T) ---
|
||||
|
||||
simd_shuffle :: proc(a, b: #simd[N]T, indices: ..int) -> #simd[len(indices)]T ---
|
||||
simd_select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T ---
|
||||
@@ -353,15 +360,18 @@ x86_xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
|
||||
objc_object :: struct{}
|
||||
objc_selector :: struct{}
|
||||
objc_class :: struct{}
|
||||
objc_ivar :: struct{}
|
||||
|
||||
objc_id :: ^objc_object
|
||||
objc_SEL :: ^objc_selector
|
||||
objc_Class :: ^objc_class
|
||||
objc_Ivar :: ^objc_ivar
|
||||
|
||||
objc_find_selector :: proc($name: string) -> objc_SEL ---
|
||||
objc_register_selector :: proc($name: string) -> objc_SEL ---
|
||||
objc_find_class :: proc($name: string) -> objc_Class ---
|
||||
objc_register_class :: proc($name: string) -> objc_Class ---
|
||||
|
||||
objc_ivar_get :: proc(self: ^$T) -> ^$U ---
|
||||
|
||||
valgrind_client_request :: proc(default: uintptr, request: uintptr, a0, a1, a2, a3, a4: uintptr) -> uintptr ---
|
||||
|
||||
|
||||
@@ -110,7 +110,6 @@ Type_Info_Parameters :: struct { // Only used for procedures parameters and resu
|
||||
types: []^Type_Info,
|
||||
names: []string,
|
||||
}
|
||||
Type_Info_Tuple :: Type_Info_Parameters // Will be removed eventually
|
||||
|
||||
Type_Info_Struct_Flags :: distinct bit_set[Type_Info_Struct_Flag; u8]
|
||||
Type_Info_Struct_Flag :: enum u8 {
|
||||
@@ -559,10 +558,14 @@ ALL_ODIN_OS_TYPES :: Odin_OS_Types{
|
||||
Odin_Platform_Subtarget_Type :: enum int {
|
||||
Default,
|
||||
iOS,
|
||||
Android,
|
||||
}
|
||||
*/
|
||||
Odin_Platform_Subtarget_Type :: type_of(ODIN_PLATFORM_SUBTARGET)
|
||||
|
||||
Odin_Platform_Subtarget_Types :: bit_set[Odin_Platform_Subtarget_Type]
|
||||
|
||||
|
||||
/*
|
||||
// Defined internally by the compiler
|
||||
Odin_Sanitizer_Flag :: enum u32 {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:sanitizer"
|
||||
|
||||
DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: uint(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE)
|
||||
|
||||
@@ -43,6 +44,8 @@ memory_block_alloc :: proc(allocator: Allocator, capacity: uint, alignment: uint
|
||||
block.base = ([^]byte)(uintptr(block) + base_offset)
|
||||
block.capacity = uint(end - uintptr(block.base))
|
||||
|
||||
sanitizer.address_poison(block.base, block.capacity)
|
||||
|
||||
// Should be zeroed
|
||||
assert(block.used == 0)
|
||||
assert(block.prev == nil)
|
||||
@@ -52,6 +55,7 @@ memory_block_alloc :: proc(allocator: Allocator, capacity: uint, alignment: uint
|
||||
memory_block_dealloc :: proc(block_to_free: ^Memory_Block, loc := #caller_location) {
|
||||
if block_to_free != nil {
|
||||
allocator := block_to_free.allocator
|
||||
sanitizer.address_unpoison(block_to_free.base, block_to_free.capacity)
|
||||
mem_free(block_to_free, allocator, loc)
|
||||
}
|
||||
}
|
||||
@@ -83,6 +87,7 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint)
|
||||
return
|
||||
}
|
||||
data = block.base[block.used+alignment_offset:][:min_size]
|
||||
sanitizer.address_unpoison(block.base[block.used:block.used+size])
|
||||
block.used += size
|
||||
return
|
||||
}
|
||||
@@ -164,6 +169,7 @@ arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
|
||||
if arena.curr_block != nil {
|
||||
intrinsics.mem_zero(arena.curr_block.base, arena.curr_block.used)
|
||||
arena.curr_block.used = 0
|
||||
sanitizer.address_poison(arena.curr_block.base, arena.curr_block.capacity)
|
||||
}
|
||||
arena.total_used = 0
|
||||
}
|
||||
@@ -228,6 +234,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
// grow data in-place, adjusting next allocation
|
||||
block.used = uint(new_end)
|
||||
data = block.base[start:new_end]
|
||||
sanitizer.address_unpoison(data)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -301,6 +308,7 @@ arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
|
||||
assert(block.used >= temp.used, "out of order use of arena_temp_end", loc)
|
||||
amount_to_zero := block.used-temp.used
|
||||
intrinsics.mem_zero(block.base[temp.used:], amount_to_zero)
|
||||
sanitizer.address_poison(block.base[temp.used:block.capacity])
|
||||
block.used = temp.used
|
||||
arena.total_used -= amount_to_zero
|
||||
}
|
||||
|
||||
@@ -1033,3 +1033,32 @@ default_hasher_cstring :: proc "contextless" (data: rawptr, seed: uintptr) -> ui
|
||||
h &= HASH_MASK
|
||||
return uintptr(h) | uintptr(uintptr(h) == 0)
|
||||
}
|
||||
|
||||
default_hasher_f64 :: proc "contextless" (f: f64, seed: uintptr) -> uintptr {
|
||||
f := f
|
||||
buf: [size_of(f)]u8
|
||||
if f == 0 {
|
||||
return default_hasher(&buf, seed, size_of(buf))
|
||||
}
|
||||
if f != f {
|
||||
// TODO(bill): What should the logic be for NaNs?
|
||||
return default_hasher(&f, seed, size_of(f))
|
||||
}
|
||||
return default_hasher(&f, seed, size_of(f))
|
||||
}
|
||||
|
||||
default_hasher_complex128 :: proc "contextless" (x, y: f64, seed: uintptr) -> uintptr {
|
||||
seed := seed
|
||||
seed = default_hasher_f64(x, seed)
|
||||
seed = default_hasher_f64(y, seed)
|
||||
return seed
|
||||
}
|
||||
|
||||
default_hasher_quaternion256 :: proc "contextless" (x, y, z, w: f64, seed: uintptr) -> uintptr {
|
||||
seed := seed
|
||||
seed = default_hasher_f64(x, seed)
|
||||
seed = default_hasher_f64(y, seed)
|
||||
seed = default_hasher_f64(z, seed)
|
||||
seed = default_hasher_f64(w, seed)
|
||||
return seed
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package runtime
|
||||
|
||||
import "../sanitizer"
|
||||
|
||||
foreign import kernel32 "system:Kernel32.lib"
|
||||
|
||||
@(private="file")
|
||||
@@ -16,7 +18,10 @@ foreign kernel32 {
|
||||
|
||||
_heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
|
||||
HEAP_ZERO_MEMORY :: 0x00000008
|
||||
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
|
||||
ptr := HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
|
||||
// NOTE(lucas): asan not guarunteed to unpoison win32 heap out of the box, do it ourselves
|
||||
sanitizer.address_unpoison(ptr, size)
|
||||
return ptr
|
||||
}
|
||||
_heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
|
||||
if new_size == 0 {
|
||||
@@ -28,7 +33,10 @@ _heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
|
||||
}
|
||||
|
||||
HEAP_ZERO_MEMORY :: 0x00000008
|
||||
return HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptr, uint(new_size))
|
||||
new_ptr := HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptr, uint(new_size))
|
||||
// NOTE(lucas): asan not guarunteed to unpoison win32 heap out of the box, do it ourselves
|
||||
sanitizer.address_unpoison(new_ptr, new_size)
|
||||
return new_ptr
|
||||
}
|
||||
_heap_free :: proc "contextless" (ptr: rawptr) {
|
||||
if ptr == nil {
|
||||
|
||||
@@ -1106,3 +1106,11 @@ __read_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uin
|
||||
dst[j>>3] |= the_bit<<(j&7)
|
||||
}
|
||||
}
|
||||
|
||||
when .Address in ODIN_SANITIZER_FLAGS {
|
||||
foreign {
|
||||
@(require)
|
||||
__asan_unpoison_memory_region :: proc "system" (address: rawptr, size: uint) ---
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,21 +2,34 @@
|
||||
package runtime
|
||||
|
||||
@(priority_index=-1e6)
|
||||
foreign import "system:Foundation.framework"
|
||||
foreign import ObjC "system:objc"
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
objc_id :: ^intrinsics.objc_object
|
||||
objc_id :: ^intrinsics.objc_object
|
||||
objc_Class :: ^intrinsics.objc_class
|
||||
objc_SEL :: ^intrinsics.objc_selector
|
||||
objc_SEL :: ^intrinsics.objc_selector
|
||||
objc_Ivar :: ^intrinsics.objc_ivar
|
||||
objc_BOOL :: bool
|
||||
|
||||
foreign Foundation {
|
||||
objc_lookUpClass :: proc "c" (name: cstring) -> objc_Class ---
|
||||
|
||||
objc_IMP :: proc "c" (object: objc_id, sel: objc_SEL, #c_vararg args: ..any) -> objc_id
|
||||
|
||||
foreign ObjC {
|
||||
sel_registerName :: proc "c" (name: cstring) -> objc_SEL ---
|
||||
objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) -> objc_Class ---
|
||||
|
||||
objc_msgSend :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
|
||||
objc_msgSend_fpret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> f64 ---
|
||||
objc_msgSend_fp2ret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> complex128 ---
|
||||
objc_msgSend_stret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
|
||||
|
||||
objc_lookUpClass :: proc "c" (name: cstring) -> objc_Class ---
|
||||
objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) -> objc_Class ---
|
||||
objc_registerClassPair :: proc "c" (cls : objc_Class) ---
|
||||
class_addMethod :: proc "c" (cls: objc_Class, name: objc_SEL, imp: objc_IMP, types: cstring) -> objc_BOOL ---
|
||||
class_addIvar :: proc "c" (cls: objc_Class, name: cstring, size: uint, alignment: u8, types: cstring) -> objc_BOOL ---
|
||||
class_getInstanceVariable :: proc "c" (cls : objc_Class, name: cstring) -> objc_Ivar ---
|
||||
class_getInstanceSize :: proc "c" (cls : objc_Class) -> uint ---
|
||||
ivar_getOffset :: proc "c" (v: objc_Ivar) -> uintptr ---
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,601 @@
|
||||
#+no-instrumentation
|
||||
package sanitizer
|
||||
|
||||
Address_Death_Callback :: #type proc "c" (pc: rawptr, bp: rawptr, sp: rawptr, addr: rawptr, is_write: i32, access_size: uint)
|
||||
|
||||
@(private="file")
|
||||
ASAN_ENABLED :: .Address in ODIN_SANITIZER_FLAGS
|
||||
|
||||
@(private="file")
|
||||
@(default_calling_convention="system")
|
||||
foreign {
|
||||
__asan_poison_memory_region :: proc(address: rawptr, size: uint) ---
|
||||
__asan_unpoison_memory_region :: proc(address: rawptr, size: uint) ---
|
||||
__sanitizer_set_death_callback :: proc(callback: Address_Death_Callback) ---
|
||||
__asan_region_is_poisoned :: proc(begin: rawptr, size: uint) -> rawptr ---
|
||||
__asan_address_is_poisoned :: proc(addr: rawptr) -> i32 ---
|
||||
__asan_describe_address :: proc(addr: rawptr) ---
|
||||
__asan_report_present :: proc() -> i32 ---
|
||||
__asan_get_report_pc :: proc() -> rawptr ---
|
||||
__asan_get_report_bp :: proc() -> rawptr ---
|
||||
__asan_get_report_sp :: proc() -> rawptr ---
|
||||
__asan_get_report_address :: proc() -> rawptr ---
|
||||
__asan_get_report_access_type :: proc() -> i32 ---
|
||||
__asan_get_report_access_size :: proc() -> uint ---
|
||||
__asan_get_report_description :: proc() -> cstring ---
|
||||
__asan_locate_address :: proc(addr: rawptr, name: rawptr, name_size: uint, region_address: ^rawptr, region_size: ^uint) -> cstring ---
|
||||
__asan_get_alloc_stack :: proc(addr: rawptr, trace: rawptr, size: uint, thread_id: ^i32) -> uint ---
|
||||
__asan_get_free_stack :: proc(addr: rawptr, trace: rawptr, size: uint, thread_id: ^i32) -> uint ---
|
||||
__asan_get_shadow_mapping :: proc(shadow_scale: ^uint, shadow_offset: ^uint) ---
|
||||
__asan_print_accumulated_stats :: proc() ---
|
||||
__asan_get_current_fake_stack :: proc() -> rawptr ---
|
||||
__asan_addr_is_in_fake_stack :: proc(fake_stack: rawptr, addr: rawptr, beg: ^rawptr, end: ^rawptr) -> rawptr ---
|
||||
__asan_handle_no_return :: proc() ---
|
||||
__asan_update_allocation_context :: proc(addr: rawptr) -> i32 ---
|
||||
}
|
||||
|
||||
Address_Access_Type :: enum {
|
||||
none,
|
||||
read,
|
||||
write,
|
||||
}
|
||||
|
||||
Address_Located_Address :: struct {
|
||||
category: string,
|
||||
name: string,
|
||||
region: []byte,
|
||||
}
|
||||
|
||||
Address_Shadow_Mapping :: struct {
|
||||
scale: uint,
|
||||
offset: uint,
|
||||
}
|
||||
|
||||
/*
|
||||
Marks a slice as unaddressable
|
||||
|
||||
Code instrumented with `-sanitize:address` is forbidden from accessing any address
|
||||
within the slice. This procedure is not thread-safe because no two threads can
|
||||
poison or unpoison memory in the same memory region region simultaneously.
|
||||
|
||||
When asan is not enabled this procedure does nothing.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_poison_slice :: proc "contextless" (region: $T/[]$E) {
|
||||
when ASAN_ENABLED {
|
||||
__asan_poison_memory_region(raw_data(region), size_of(E) * len(region))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Marks a slice as addressable
|
||||
|
||||
Code instrumented with `-sanitize:address` is allowed to access any address
|
||||
within the slice again. This procedure is not thread-safe because no two threads
|
||||
can poison or unpoison memory in the same memory region region simultaneously.
|
||||
|
||||
When asan is not enabled this procedure does nothing.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_unpoison_slice :: proc "contextless" (region: $T/[]$E) {
|
||||
when ASAN_ENABLED {
|
||||
__asan_unpoison_memory_region(raw_data(region), size_of(E) * len(region))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Marks a pointer as unaddressable
|
||||
|
||||
Code instrumented with `-sanitize:address` is forbidden from accessing any address
|
||||
within the region the pointer points to. This procedure is not thread-safe because no
|
||||
two threads can poison or unpoison memory in the same memory region region simultaneously.
|
||||
|
||||
When asan is not enabled this procedure does nothing.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_poison_ptr :: proc "contextless" (ptr: ^$T) {
|
||||
when ASAN_ENABLED {
|
||||
__asan_poison_memory_region(ptr, size_of(T))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Marks a pointer as addressable
|
||||
|
||||
Code instrumented with `-sanitize:address` is allowed to access any address
|
||||
within the region the pointer points to again. This procedure is not thread-safe
|
||||
because no two threads can poison or unpoison memory in the same memory region
|
||||
region simultaneously.
|
||||
|
||||
When asan is not enabled this procedure does nothing.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_unpoison_ptr :: proc "contextless" (ptr: ^$T) {
|
||||
when ASAN_ENABLED {
|
||||
__asan_unpoison_memory_region(ptr, size_of(T))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Marks the region covering `[ptr, ptr+len)` as unaddressable
|
||||
|
||||
Code instrumented with `-sanitize:address` is forbidden from accessing any address
|
||||
within the region. This procedure is not thread-safe because no two threads can
|
||||
poison or unpoison memory in the same memory region region simultaneously.
|
||||
|
||||
When asan is not enabled this procedure does nothing.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_poison_rawptr :: proc "contextless" (ptr: rawptr, len: int) {
|
||||
when ASAN_ENABLED {
|
||||
assert_contextless(len >= 0)
|
||||
__asan_poison_memory_region(ptr, uint(len))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Marks the region covering `[ptr, ptr+len)` as unaddressable
|
||||
|
||||
Code instrumented with `-sanitize:address` is forbidden from accessing any address
|
||||
within the region. This procedure is not thread-safe because no two threads can
|
||||
poison or unpoison memory in the same memory region region simultaneously.
|
||||
|
||||
When asan is not enabled this procedure does nothing.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_poison_rawptr_uint :: proc "contextless" (ptr: rawptr, len: uint) {
|
||||
when ASAN_ENABLED {
|
||||
__asan_poison_memory_region(ptr, len)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Marks the region covering `[ptr, ptr+len)` as addressable
|
||||
|
||||
Code instrumented with `-sanitize:address` is allowed to access any address
|
||||
within the region again. This procedure is not thread-safe because no two
|
||||
threads can poison or unpoison memory in the same memory region region simultaneously.
|
||||
|
||||
When asan is not enabled this procedure does nothing.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_unpoison_rawptr :: proc "contextless" (ptr: rawptr, len: int) {
|
||||
when ASAN_ENABLED {
|
||||
assert_contextless(len >= 0)
|
||||
__asan_unpoison_memory_region(ptr, uint(len))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Marks the region covering `[ptr, ptr+len)` as addressable
|
||||
|
||||
Code instrumented with `-sanitize:address` is allowed to access any address
|
||||
within the region again. This procedure is not thread-safe because no two
|
||||
threads can poison or unpoison memory in the same memory region region simultaneously.
|
||||
|
||||
When asan is not enabled this procedure does nothing.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_unpoison_rawptr_uint :: proc "contextless" (ptr: rawptr, len: uint) {
|
||||
when ASAN_ENABLED {
|
||||
__asan_unpoison_memory_region(ptr, len)
|
||||
}
|
||||
}
|
||||
|
||||
address_poison :: proc {
|
||||
address_poison_slice,
|
||||
address_poison_ptr,
|
||||
address_poison_rawptr,
|
||||
address_poison_rawptr_uint,
|
||||
}
|
||||
|
||||
address_unpoison :: proc {
|
||||
address_unpoison_slice,
|
||||
address_unpoison_ptr,
|
||||
address_unpoison_rawptr,
|
||||
address_unpoison_rawptr_uint,
|
||||
}
|
||||
|
||||
/*
|
||||
Registers a callback to be run when asan detects a memory error right before terminating
|
||||
the process.
|
||||
|
||||
This can be used for logging and/or debugging purposes.
|
||||
|
||||
When asan is not enabled this procedure does nothing.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_set_death_callback :: proc "contextless" (callback: Address_Death_Callback) {
|
||||
when ASAN_ENABLED {
|
||||
__sanitizer_set_death_callback(callback)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Checks if the memory region covered by the slice is poisoned.
|
||||
|
||||
If it is poisoned this procedure returns the address which would result
|
||||
in an asan error.
|
||||
|
||||
When asan is not enabled this procedure returns `nil`.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_region_is_poisoned_slice :: proc "contextless" (region: $T/[]$E) -> rawptr {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_region_is_poisoned(raw_data(region), size_of(E) * len(region))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Checks if the memory region pointed to by the pointer is poisoned.
|
||||
|
||||
If it is poisoned this procedure returns the address which would result
|
||||
in an asan error.
|
||||
|
||||
When asan is not enabled this procedure returns `nil`.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_region_is_poisoned_ptr :: proc "contextless" (ptr: ^$T) -> rawptr {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_region_is_poisoned(ptr, size_of(T))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Checks if the memory region covered by `[ptr, ptr+len)` is poisoned.
|
||||
|
||||
If it is poisoned this procedure returns the address which would result
|
||||
in an asan error.
|
||||
|
||||
When asan is not enabled this procedure returns `nil`.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_region_is_poisoned_rawptr :: proc "contextless" (region: rawptr, len: int) -> rawptr {
|
||||
when ASAN_ENABLED {
|
||||
assert_contextless(len >= 0)
|
||||
return __asan_region_is_poisoned(region, uint(len))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Checks if the memory region covered by `[ptr, ptr+len)` is poisoned.
|
||||
|
||||
If it is poisoned this procedure returns the address which would result
|
||||
in an asan error.
|
||||
|
||||
When asan is not enabled this procedure returns `nil`.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_region_is_poisoned_rawptr_uint :: proc "contextless" (region: rawptr, len: uint) -> rawptr {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_region_is_poisoned(region, len)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
address_region_is_poisoned :: proc {
|
||||
address_region_is_poisoned_slice,
|
||||
address_region_is_poisoned_ptr,
|
||||
address_region_is_poisoned_rawptr,
|
||||
address_region_is_poisoned_rawptr_uint,
|
||||
}
|
||||
|
||||
/*
|
||||
Checks if the address is poisoned.
|
||||
|
||||
If it is poisoned this procedure returns `true`, otherwise it returns
|
||||
`false`.
|
||||
|
||||
When asan is not enabled this procedure returns `false`.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_is_poisoned :: proc "contextless" (address: rawptr) -> bool {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_address_is_poisoned(address) != 0
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Describes the sanitizer state for an address.
|
||||
|
||||
This procedure prints the description out to `stdout`.
|
||||
|
||||
When asan is not enabled this procedure does nothing.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_describe_address :: proc "contextless" (address: rawptr) {
|
||||
when ASAN_ENABLED {
|
||||
__asan_describe_address(address)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns `true` if an asan error has occured, otherwise it returns
|
||||
`false`.
|
||||
|
||||
When asan is not enabled this procedure returns `false`.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_report_present :: proc "contextless" () -> bool {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_report_present() != 0
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the program counter register value of an asan error.
|
||||
|
||||
If no asan error has occurd `nil` is returned.
|
||||
|
||||
When asan is not enabled this procedure returns `nil`.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_get_report_pc :: proc "contextless" () -> rawptr {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_get_report_pc()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the base pointer register value of an asan error.
|
||||
|
||||
If no asan error has occurd `nil` is returned.
|
||||
|
||||
When asan is not enabled this procedure returns `nil`.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_get_report_bp :: proc "contextless" () -> rawptr {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_get_report_bp()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the stack pointer register value of an asan error.
|
||||
|
||||
If no asan error has occurd `nil` is returned.
|
||||
|
||||
When asan is not enabled this procedure returns `nil`.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_get_report_sp :: proc "contextless" () -> rawptr {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_get_report_sp()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the report buffer address of an asan error.
|
||||
|
||||
If no asan error has occurd `nil` is returned.
|
||||
|
||||
When asan is not enabled this procedure returns `nil`.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_get_report_address :: proc "contextless" () -> rawptr {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_get_report_address()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the address access type of an asan error.
|
||||
|
||||
If no asan error has occurd `.none` is returned.
|
||||
|
||||
When asan is not enabled this procedure returns `.none`.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_get_report_access_type :: proc "contextless" () -> Address_Access_Type {
|
||||
when ASAN_ENABLED {
|
||||
if ! address_report_present() {
|
||||
return .none
|
||||
}
|
||||
return __asan_get_report_access_type() == 0 ? .read : .write
|
||||
} else {
|
||||
return .none
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the access size of an asan error.
|
||||
|
||||
If no asan error has occurd `0` is returned.
|
||||
|
||||
When asan is not enabled this procedure returns `0`.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_get_report_access_size :: proc "contextless" () -> uint {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_get_report_access_size()
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the bug description of an asan error.
|
||||
|
||||
If no asan error has occurd an empty string is returned.
|
||||
|
||||
When asan is not enabled this procedure returns an empty string.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_get_report_description :: proc "contextless" () -> string {
|
||||
when ASAN_ENABLED {
|
||||
return string(__asan_get_report_description())
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns asan information about the address provided, writing the category into `data`.
|
||||
|
||||
The information provided include:
|
||||
* The category of the address, i.e. stack, global, heap, etc.
|
||||
* The name of the variable this address belongs to
|
||||
* The memory region of the address
|
||||
|
||||
When asan is not enabled this procedure returns zero initialised values.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_locate_address :: proc "contextless" (addr: rawptr, data: []byte) -> Address_Located_Address {
|
||||
when ASAN_ENABLED {
|
||||
out_addr: rawptr
|
||||
out_size: uint
|
||||
str := __asan_locate_address(addr, raw_data(data), len(data), &out_addr, &out_size)
|
||||
return { string(str), string(cstring(raw_data(data))), (cast([^]byte)out_addr)[:out_size] },
|
||||
} else {
|
||||
return { "", "", {} }
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the allocation stack trace and thread id for a heap address.
|
||||
|
||||
The stack trace is filled into the `data` slice.
|
||||
|
||||
When asan is not enabled this procedure returns a zero initialised value.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_get_alloc_stack_trace :: proc "contextless" (addr: rawptr, data: []rawptr) -> ([]rawptr, int) {
|
||||
when ASAN_ENABLED {
|
||||
out_thread: i32
|
||||
__asan_get_alloc_stack(addr, raw_data(data), len(data), &out_thread)
|
||||
return data, int(out_thread)
|
||||
} else {
|
||||
return {}, 0
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the free stack trace and thread id for a heap address.
|
||||
|
||||
The stack trace is filled into the `data` slice.
|
||||
|
||||
When asan is not enabled this procedure returns zero initialised values.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_get_free_stack_trace :: proc "contextless" (addr: rawptr, data: []rawptr) -> ([]rawptr, int) {
|
||||
when ASAN_ENABLED {
|
||||
out_thread: i32
|
||||
__asan_get_free_stack(addr, raw_data(data), len(data), &out_thread)
|
||||
return data, int(out_thread)
|
||||
} else {
|
||||
return {}, 0
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the current asan shadow memory mapping.
|
||||
|
||||
When asan is not enabled this procedure returns a zero initialised value.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_get_shadow_mapping :: proc "contextless" () -> Address_Shadow_Mapping {
|
||||
when ASAN_ENABLED {
|
||||
result: Address_Shadow_Mapping
|
||||
__asan_get_shadow_mapping(&result.scale, &result.offset)
|
||||
return result
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Prints asan statistics to `stderr`
|
||||
|
||||
When asan is not enabled this procedure does nothing.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_print_accumulated_stats :: proc "contextless" () {
|
||||
when ASAN_ENABLED {
|
||||
__asan_print_accumulated_stats()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the address of the current fake stack used by asan.
|
||||
|
||||
This pointer can be then used for `address_is_in_fake_stack`.
|
||||
|
||||
When asan is not enabled this procedure returns `nil`.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_get_current_fake_stack :: proc "contextless" () -> rawptr {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_get_current_fake_stack()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns if an address belongs to a given fake stack and if so the region of the fake frame.
|
||||
|
||||
When asan is not enabled this procedure returns zero initialised values.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_is_in_fake_stack :: proc "contextless" (fake_stack: rawptr, addr: rawptr) -> ([]byte, bool) {
|
||||
when ASAN_ENABLED {
|
||||
begin: rawptr
|
||||
end: rawptr
|
||||
if __asan_addr_is_in_fake_stack(fake_stack, addr, &begin, &end) == nil {
|
||||
return {}, false
|
||||
}
|
||||
return ((cast([^]byte)begin)[:uintptr(end)-uintptr(begin)]), true
|
||||
} else {
|
||||
return {}, false
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Performs shadow memory cleanup for the current thread before a procedure with no return is called
|
||||
i.e. a procedure such as `panic` and `os.exit`.
|
||||
|
||||
When asan is not enabled this procedure does nothing.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_handle_no_return :: proc "contextless" () {
|
||||
when ASAN_ENABLED {
|
||||
__asan_handle_no_return()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Updates the allocation stack trace for the given address.
|
||||
|
||||
Returns `true` if successful, otherwise it returns `false`.
|
||||
|
||||
When asan is not enabled this procedure returns `false`.
|
||||
*/
|
||||
@(no_sanitize_address)
|
||||
address_update_allocation_context :: proc "contextless" (addr: rawptr) -> bool {
|
||||
when ASAN_ENABLED {
|
||||
return __asan_update_allocation_context(addr) != 0
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
The `sanitizer` package implements various procedures for interacting with sanitizers
|
||||
from user code.
|
||||
|
||||
An odin project can be linked with various sanitizers to help identify various different
|
||||
bugs. These sanitizers are:
|
||||
|
||||
## Address
|
||||
|
||||
Enabled with `-sanitize:address` when building an odin project.
|
||||
|
||||
The address sanitizer (asan) is a runtime memory error detector used to help find common memory
|
||||
related bugs. Typically asan interacts with libc but Odin code can be marked up to interact
|
||||
with the asan runtime to extend the memory error detection outside of libc using this package.
|
||||
For more information about asan see: https://clang.llvm.org/docs/AddressSanitizer.html
|
||||
|
||||
Procedures can be made exempt from asan when marked up with @(no_sanitize_address)
|
||||
|
||||
## Memory
|
||||
|
||||
Enabled with `-sanitize:memory` when building an odin project.
|
||||
|
||||
The memory sanitizer is another runtime memory error detector with the sole purpose to catch the
|
||||
use of uninitialized memory. This is not a very common bug in Odin as by default everything is
|
||||
set to zero when initialised (ZII).
|
||||
For more information about the memory sanitizer see: https://clang.llvm.org/docs/MemorySanitizer.html
|
||||
|
||||
## Thread
|
||||
|
||||
Enabled with `-sanitize:thread` when building an odin project.
|
||||
|
||||
The thread sanitizer is a runtime data race detector. It can be used to detect if multiple threads
|
||||
are concurrently writing and accessing a memory location without proper syncronisation.
|
||||
For more information about the thread sanitizer see: https://clang.llvm.org/docs/ThreadSanitizer.html
|
||||
|
||||
*/
|
||||
package sanitizer
|
||||
|
||||
Reference in New Issue
Block a user