Merge remote-tracking branch 'offical/bill/raddebugger-custom-section'

This commit is contained in:
2025-05-25 09:25:29 -04:00
266 changed files with 20393 additions and 4512 deletions
+2 -1
View File
@@ -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 ---
+12 -2
View File
@@ -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 ---
+4 -1
View File
@@ -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
}
+29
View File
@@ -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
}
+10 -2
View File
@@ -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 {
+8
View File
@@ -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) ---
}
}
+19 -6
View File
@@ -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 ---
}
+601
View File
@@ -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
}
}
+38
View File
@@ -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