The OpenD Programming Language

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)