| 1 | // RUN: %clangxx_tsan -O1 %s -DBUILD_SO -fPIC -shared -o %t-so.so |
| 2 | // RUN: %clangxx_tsan -O1 %s %link_libcxx_tsan -o %t && %run %t 2>&1 | FileCheck %s |
| 3 | |
| 4 | // A test for loading a dynamic library with static TLS. |
| 5 | // Such static TLS is a hack that allows a dynamic library to have faster TLS, |
| 6 | // but it can be loaded only iff all threads happened to allocate some excess |
| 7 | // of static TLS space for whatever reason. If it's not the case loading fails with: |
| 8 | // dlopen: cannot load any more object with static TLS |
| 9 | // We used to produce a false positive because dlopen will write into TLS |
| 10 | // of all existing threads to initialize/zero TLS region for the loaded library. |
| 11 | // And this appears to be racing with initialization of TLS in the thread |
| 12 | // since we model a write into the whole static TLS region (we don't know what part |
| 13 | // of it is currently unused): |
| 14 | // WARNING: ThreadSanitizer: data race (pid=2317365) |
| 15 | // Write of size 1 at 0x7f1fa9bfcdd7 by main thread: |
| 16 | // #0 memset |
| 17 | // #1 init_one_static_tls |
| 18 | // #2 __pthread_init_static_tls |
| 19 | // [[ this is where main calls dlopen ]] |
| 20 | // #3 main |
| 21 | // Previous write of size 8 at 0x7f1fa9bfcdd0 by thread T1: |
| 22 | // #0 __tsan_tls_initialization |
| 23 | |
| 24 | // Failing on bots: |
| 25 | // https://lab.llvm.org/buildbot#builders/184/builds/1580 |
| 26 | // https://lab.llvm.org/buildbot#builders/18/builds/3167 |
| 27 | // UNSUPPORTED: target={{(aarch64|powerpc64).*}} |
| 28 | |
| 29 | #ifdef BUILD_SO |
| 30 | |
| 31 | __attribute__((tls_model("initial-exec" ))) __thread char x = 42; |
| 32 | __attribute__((tls_model("initial-exec" ))) __thread char y; |
| 33 | |
| 34 | extern "C" int sofunc() { return ++x + ++y; } |
| 35 | |
| 36 | #else // BUILD_SO |
| 37 | |
| 38 | # include "../test.h" |
| 39 | # include <dlfcn.h> |
| 40 | # include <string> |
| 41 | |
| 42 | __thread int x[1023]; |
| 43 | |
| 44 | void *lib; |
| 45 | void (*func)(); |
| 46 | int ready; |
| 47 | |
| 48 | void *thread(void *arg) { |
| 49 | barrier_wait(barrier: &barrier); |
| 50 | if (__atomic_load_n(&ready, __ATOMIC_ACQUIRE)) |
| 51 | func(); |
| 52 | barrier_wait(barrier: &barrier); |
| 53 | if (dlclose(handle: lib)) { |
| 54 | printf(format: "error in dlclose: %s\n" , dlerror()); |
| 55 | exit(status: 1); |
| 56 | } |
| 57 | return 0; |
| 58 | } |
| 59 | |
| 60 | int main(int argc, char *argv[]) { |
| 61 | barrier_init(barrier: &barrier, count: 2); |
| 62 | pthread_t th; |
| 63 | pthread_create(newthread: &th, attr: 0, start_routine: thread, arg: 0); |
| 64 | lib = dlopen((std::string(argv[0]) + "-so.so" ).c_str(), RTLD_NOW); |
| 65 | if (lib == 0) { |
| 66 | printf(format: "error in dlopen: %s\n" , dlerror()); |
| 67 | return 1; |
| 68 | } |
| 69 | func = (void (*)())dlsym(handle: lib, name: "sofunc" ); |
| 70 | if (func == 0) { |
| 71 | printf(format: "error in dlsym: %s\n" , dlerror()); |
| 72 | return 1; |
| 73 | } |
| 74 | __atomic_store_n(&ready, 1, __ATOMIC_RELEASE); |
| 75 | barrier_wait(barrier: &barrier); |
| 76 | func(); |
| 77 | barrier_wait(barrier: &barrier); |
| 78 | pthread_join(th: th, thread_return: 0); |
| 79 | fprintf(stderr, format: "DONE\n" ); |
| 80 | return 0; |
| 81 | } |
| 82 | |
| 83 | #endif // BUILD_SO |
| 84 | |
| 85 | // CHECK: DONE |
| 86 | |