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