blob: 27636f4ce04bab3403bd00827a4e8eaf0ddd323d [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#ifndef ZIRCON_KERNEL_LIB_CXXABI_DYNAMIC_INIT_INCLUDE_LIB_CXXABI_DYNAMIC_INIT_CXXABI_DYNAMIC_INIT_H_
#define ZIRCON_KERNEL_LIB_CXXABI_DYNAMIC_INIT_INCLUDE_LIB_CXXABI_DYNAMIC_INIT_CXXABI_DYNAMIC_INIT_H_
#include <stdint.h>
#include <zircon/assert.h>
// This header provides Acquire, Release, and Abort operations for static initializer guard
// variables. The functions __cxa_guard_acquire, __cxa_guard_release, and __cxa_guard_abort are
// defined in the .cc file and are simply trivial wrappers around Acquire, Release, and Abort in
// order to facilitate testing.
//
// See also //zircon/kernel/lib/cxxabi-dynamic-init/README.md.
namespace cxxabi_dynamic_init {
namespace internal {
// Returns true iff global constructors have completed.
//
// This function must be implemented by the kernel or by tests.
bool ConstructorsCalled();
// Itanium C++ ABI for initialization guard variables says:
//
// "The size of the guard variable is 64 bits. The first byte (i.e. the byte at the address of the
// full variable) shall contain the value 0 prior to initialization of the associated variable, and
// 1 after initialization is complete. Usage of the other bytes of the guard variable is
// implementation-defined."
//
// See https://itanium-cxx-abi.github.io/cxx-abi/abi.html#guards.
// ABI dictates that the first byte (byte at the address of the guard object) is either 0x00 or 0x1
// to indicate whether the object has been initialized (0x01) or not (0x00). The rest of guard
// object is free to be used as we see fit. We'll use the LSB of the second byte to indicate
// whether the guard object is in use (i.e. held).
constexpr uint64_t kFirstByteMask = 0x00000000000000ff;
constexpr uint64_t kInUseMask = 0x0000000000000100;
// Returns true iff the object guarded by |guard_object| is already initialized.
inline bool IsObjectInitialized(const uint64_t* guard_object) {
return (*guard_object & kFirstByteMask) != 0;
}
// Marks the object guarded by |guard_object| as initialized.
inline void SetObjectInitialized(uint64_t* guard_object) {
*guard_object = (*guard_object & ~kFirstByteMask) | 1;
}
// Returns true iff |guard_object| is in use.
inline bool IsInUse(const uint64_t* guard_object) { return (*guard_object & kInUseMask) != 0; }
// Indicate that |guard_object| is in use (i.e. initialization is in progress).
inline void SetInUse(uint64_t* guard_object) { *guard_object |= kInUseMask; }
// Indicate that |guard_object| is no longer in use (i.e. initialization has been aborted).
inline void SetNotInUse(uint64_t* guard_object) { *guard_object &= ~kInUseMask; }
} // namespace internal
inline int Acquire(uint64_t* guard_object) {
ZX_DEBUG_ASSERT(!internal::ConstructorsCalled());
if (internal::IsObjectInitialized(guard_object)) {
return 0;
}
ZX_DEBUG_ASSERT(!internal::IsInUse(guard_object));
internal::SetInUse(guard_object);
return 1;
}
inline void Release(uint64_t* guard_object) {
ZX_DEBUG_ASSERT(!internal::ConstructorsCalled());
internal::SetObjectInitialized(guard_object);
}
inline void Abort(uint64_t* guard_object) {
ZX_DEBUG_ASSERT(!internal::ConstructorsCalled());
internal::SetNotInUse(guard_object);
}
} // namespace cxxabi_dynamic_init
#endif // ZIRCON_KERNEL_LIB_CXXABI_DYNAMIC_INIT_INCLUDE_LIB_CXXABI_DYNAMIC_INIT_CXXABI_DYNAMIC_INIT_H_