1 /** 2 * Contains forward references to the AddressSanitizer interface. 3 * 4 * Copyright: Authors 2019-2019 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 * Authors: LDC Developers 7 */ 8 9 module ldc.sanitizers_optionally_linked; 10 11 version (SupportSanitizers) 12 { 13 version (OSX) 14 version = Darwin; 15 else version (iOS) 16 version = Darwin; 17 else version (TVOS) 18 version = Darwin; 19 else version (WatchOS) 20 version = Darwin; 21 22 version (Darwin) {} 23 else version (Posix) 24 { 25 version = ELF; 26 } 27 28 // Forward declarations of sanitizer functions (only ELF supports optional static linking). 29 extern(C) @system @nogc nothrow 30 { 31 version (ELF) 32 enum pragmastring = "pragma(LDC_extern_weak):\n"; 33 else 34 enum pragmastring = ""; 35 36 mixin(pragmastring ~ q{ 37 void __sanitizer_start_switch_fiber(void** fake_stack_save, const(void)* bottom, size_t size); 38 void __sanitizer_finish_switch_fiber(void* fake_stack_save, const(void)** bottom_old, size_t* size_old); 39 void* __asan_get_current_fake_stack(); 40 void* __asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg, void **end); 41 }); 42 } 43 44 45 nothrow @nogc 46 void informSanitizerOfStartSwitchFiber(void** fake_stack_save, const(void)* bottom, size_t size) 47 { 48 auto fptr = getOptionalSanitizerFunc!"__sanitizer_start_switch_fiber"(); 49 if (fptr) 50 fptr(fake_stack_save, bottom, size); 51 } 52 53 nothrow @nogc 54 void informSanitizerOfFinishSwitchFiber(void* fake_stack_save, const(void)** bottom_old, size_t* size_old) 55 { 56 auto fptr = getOptionalSanitizerFunc!"__sanitizer_finish_switch_fiber"(); 57 if (fptr) 58 fptr(fake_stack_save, bottom_old, size_old); 59 } 60 61 nothrow @nogc 62 void* asanGetCurrentFakeStack() 63 { 64 auto fptr = getOptionalSanitizerFunc!"__asan_get_current_fake_stack"(); 65 if (fptr) 66 return fptr(); 67 else 68 return null; 69 } 70 71 nothrow @nogc 72 void* asanAddressIsInFakeStack(void *fake_stack, void *addr, void **beg, void **end) 73 { 74 auto fptr = getOptionalSanitizerFunc!"__asan_addr_is_in_fake_stack"(); 75 if (fptr) 76 return fptr(fake_stack, addr, beg, end); 77 else 78 return null; 79 } 80 81 // This uses the forward declaration of `functionName` and returns a pointer to that function 82 // if it is found in the executable, and `null` otherwise. Templated such that it can internally 83 // cache the function pointer. Thread-safe. 84 private auto getOptionalSanitizerFunc(string functionName)() 85 { 86 import ldc.intrinsics: llvm_expect; 87 import core.atomic: atomicLoad, atomicStore, MemoryOrder; 88 89 // If `fptr` is null, it's not initialized yet. 90 // If `fptr` is 1, the function has not been found. 91 // Otherwise, `fptr` is a valid function pointer. 92 static shared typeof(mixin("&" ~ functionName)) fptr = null; 93 enum FUNC_NOT_FOUND = cast(void*) 1; 94 95 // Because `fptr` will never change after it's been initialized, we only have to make sure 96 // that the read is atomic for thread safety. 97 void* foundptr = atomicLoad!(MemoryOrder.raw)(fptr); 98 99 if (llvm_expect(foundptr is null, false)) 100 { 101 // Multiple threads may enter this branch. It is fine to do the redundant work. 102 // The obtained `foundptr` should be the same for all threads and we can safely store it 103 // atomically and use the local value afterwards. 104 version (Darwin) 105 { 106 // On Darwin, ASan is always dynamically linked. 107 import core.sys.posix.dlfcn : dlsym, dlopen; 108 foundptr = dlsym(dlopen(null, 0), functionName); 109 } 110 else version (ELF) 111 { 112 // Check statically linked symbols 113 foundptr = mixin("&" ~ functionName); 114 if (!foundptr) { 115 // Check dynamically linked symbols 116 import core.sys.posix.dlfcn : dlsym, dlopen; 117 foundptr = dlsym(dlopen(null, 0), functionName); 118 } 119 } 120 else version (Windows) 121 { 122 import core.sys.windows.windows : GetModuleHandleA, GetProcAddress; 123 foundptr = GetProcAddress(GetModuleHandleA(null), functionName); 124 } 125 126 if (foundptr is null) 127 foundptr = FUNC_NOT_FOUND; 128 129 // It's ok if all threads write to `fptr` because it's the same value anyway, as long as 130 // the write is atomic. 131 atomicStore!(MemoryOrder.raw)(fptr, cast(typeof(fptr))foundptr); 132 } 133 134 // Expect false to maximize performance when sanitizers are not active. 135 if (llvm_expect(foundptr != FUNC_NOT_FOUND, false)) 136 return cast(typeof(fptr)) foundptr; 137 else 138 return null; 139 } 140 141 } // version (SupportSanitizers)