1 /*
2 * one_time_construction.cpp
3 *
4 * Copyright 2006 The Android Open Source Project
5 *
6 * This file contains C++ ABI support functions for one time
7 * constructors as defined in the "Run-time ABI for the ARM Architecture"
8 * section 4.4.2
9 */
10
11 #include <stddef.h>
12 #include <sys/atomics.h>
13 #include <endian.h>
14 #include <bionic_futex.h>
15 #include <bionic_atomic_inline.h>
16
17 // ARM C++ ABI and Itanium/x86 C++ ABI has different definition for
18 // one time construction:
19 //
20 // ARM C++ ABI defines the LSB of guard variable should be tested
21 // by compiler-generated code before calling __cxa_guard_acquire et al.
22 //
23 // The Itanium/x86 C++ ABI defines the low-order _byte_ should be
24 // tested instead.
25 //
26 // Meanwhile, guard variable are 32bit aligned for ARM, and 64bit
27 // aligned for x86.
28 //
29 // Reference documentation:
30 //
31 // section 3.2.3 of ARM IHI 0041C (for ARM)
32 // section 3.3.2 of the Itanium C++ ABI specification v1.83 (for x86).
33 //
34 // There is no C++ ABI available for other ARCH. But the gcc source
35 // shows all other ARCH follow the definition of Itanium/x86 C++ ABI.
36
37
38 #if defined(__arm__)
39 // The ARM C++ ABI mandates that guard variable are
40 // 32-bit aligned, 32-bit values. And only its LSB is tested by
41 // the compiler-generated code before calling
42 // __cxa_guard_acquire.
43 //
44 typedef union {
45 int volatile state;
46 int32_t aligner;
47 } _guard_t;
48
49 const static int ready = 0x1;
50 const static int pending = 0x2;
51 const static int waiting = 0x6;
52
53 #else // GCC sources indicates all none-arm follow the same ABI
54 // The Itanium/x86 C++ ABI mandates that guard variables
55 // are 64-bit aligned, 64-bit values. Also, the least-significant
56 // byte is tested by the compiler-generated code before, we calling
57 // __cxa_guard_acquire. We can access it through the first
58 // 32-bit word in the union below.
59 //
60 typedef union {
61 int volatile state;
62 int64_t aligner;
63 } _guard_t;
64
65 const static int ready = letoh32(0x1);
66 const static int pending = letoh32(0x100);
67 const static int waiting = letoh32(0x10000);
68 #endif
69
__cxa_guard_acquire(_guard_t * gv)70 extern "C" int __cxa_guard_acquire(_guard_t* gv)
71 {
72 // 0 -> pending, return 1
73 // pending -> waiting, wait and return 0
74 // waiting: untouched, wait and return 0
75 // ready: untouched, return 0
76
77 retry:
78 if (__bionic_cmpxchg(0, pending, &gv->state) == 0) {
79 ANDROID_MEMBAR_FULL();
80 return 1;
81 }
82 __bionic_cmpxchg(pending, waiting, &gv->state); // Indicate there is a waiter
83 __futex_wait(&gv->state, waiting, NULL);
84
85 if (gv->state != ready) // __cxa_guard_abort was called, let every thread try since there is no return code for this condition
86 goto retry;
87
88 ANDROID_MEMBAR_FULL();
89 return 0;
90 }
91
__cxa_guard_release(_guard_t * gv)92 extern "C" void __cxa_guard_release(_guard_t* gv)
93 {
94 // pending -> ready
95 // waiting -> ready, and wake
96
97 ANDROID_MEMBAR_FULL();
98 if (__bionic_cmpxchg(pending, ready, &gv->state) == 0) {
99 return;
100 }
101
102 gv->state = ready;
103 __futex_wake(&gv->state, 0x7fffffff);
104 }
105
__cxa_guard_abort(_guard_t * gv)106 extern "C" void __cxa_guard_abort(_guard_t* gv)
107 {
108 ANDROID_MEMBAR_FULL();
109 gv->state= 0;
110 __futex_wake(&gv->state, 0x7fffffff);
111 }
112