diff --git a/Libraries/CMakeLists.txt b/Libraries/CMakeLists.txt index 3954f99c43..9bce4e6bbd 100644 --- a/Libraries/CMakeLists.txt +++ b/Libraries/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(LibAudio) add_subdirectory(LibC) add_subdirectory(LibCore) add_subdirectory(LibCrypto) +add_subdirectory(LibCxx) add_subdirectory(LibDebug) add_subdirectory(LibDesktop) add_subdirectory(LibGemini) diff --git a/Libraries/LibC/CMakeLists.txt b/Libraries/LibC/CMakeLists.txt index d27a5b4719..d5e94d1b96 100644 --- a/Libraries/LibC/CMakeLists.txt +++ b/Libraries/LibC/CMakeLists.txt @@ -3,7 +3,7 @@ set(LIBC_SOURCES assert.cpp crt0.cpp ctype.cpp - cxxabi.cpp + cxaatexit.cpp dirent.cpp dlfcn.cpp fcntl.cpp @@ -51,8 +51,6 @@ file(GLOB AK_SOURCES "../../AK/*.cpp") file(GLOB ELF_SOURCES "../LibELF/*.cpp") set(ELF_SOURCES ${ELF_SOURCES} ../LibELF/Arch/i386/plt_trampoline.S) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSERENITY_LIBC_BUILD") - add_library(crt0 STATIC crt0.cpp) add_custom_command( TARGET crt0 @@ -62,4 +60,4 @@ add_custom_command( set(SOURCES ${LIBC_SOURCES} ${AK_SOURCES} ${ELF_SOURCES}) serenity_libc(LibC c) target_link_libraries(LibC crt0) -add_dependencies(LibC LibM) +add_dependencies(LibC LibM LibCxx) diff --git a/Libraries/LibC/cxxabi.cpp b/Libraries/LibC/cxaatexit.cpp similarity index 99% rename from Libraries/LibC/cxxabi.cpp rename to Libraries/LibC/cxaatexit.cpp index 68c9e33f3e..1b59e1d32c 100644 --- a/Libraries/LibC/cxxabi.cpp +++ b/Libraries/LibC/cxaatexit.cpp @@ -25,7 +25,6 @@ */ #include -#include #include #include diff --git a/Libraries/LibCxx/CMakeLists.txt b/Libraries/LibCxx/CMakeLists.txt new file mode 100644 index 0000000000..f9cec683bb --- /dev/null +++ b/Libraries/LibCxx/CMakeLists.txt @@ -0,0 +1,7 @@ +set(SOURCES + CxaGuard.cpp +) + +# Shhh don't worry gcc, we are totally libstdc++ +serenity_libc(LibCxx stdc++) +target_link_libraries(LibCxx LibC) diff --git a/Libraries/LibCxx/CxaGuard.cpp b/Libraries/LibCxx/CxaGuard.cpp new file mode 100644 index 0000000000..dc8b011a43 --- /dev/null +++ b/Libraries/LibCxx/CxaGuard.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2020, Andrew Kaster + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +// Static variable guard logic, using futexes +// Written while looking at libc++ from a distance ;) This is super tricky... +// From itanium spec: Get passed a pointer to a 64 bit guard "object" +// [ 1 guard byte (checked by compiler), 7 bytes to work with ] +class CxaGuard { +public: + enum GuardState : uint8_t { + NotInitialized = 0, + DoneInit = 1, + InitInProgress = 2, + WaitingOnInit = 4 + }; + + CxaGuard(uint64_t* guard_pointer) + : m_full_guard_var(guard_pointer) + , m_compiler_guard_byte((uint8_t*)guard_pointer) + , m_local_state_byte(&((uint8_t*)guard_pointer)[1]) + { + } + + int acquire() + { + // Someone else already initailzied this by storing to the 'really done' slot + if (GuardState::NotInitialized != m_compiler_guard_byte.load(AK::memory_order_acquire)) + return 0; + + // Time to begin the waiting game.. + while (true) { + // Try to claim initialization for this thread from the initial state + uint8_t last_state = GuardState::NotInitialized; + if (m_local_state_byte.compare_exchange_strong(last_state, GuardState::InitInProgress, AK::memory_order_acq_rel)) + return 1; // Tell compiler-generated code to init the variable + + // Someone else set the state to done, we're done here + if (last_state == DoneInit) + return 0; + + // Someone else set InitInProgress, time to wait (maybe) + if (last_state & GuardState::InitInProgress) { + // Try to set the in progress + done bits + if ((last_state & GuardState::WaitingOnInit) == 0) { + if (!m_local_state_byte.compare_exchange_strong(last_state, (GuardState::InitInProgress | GuardState::WaitingOnInit), AK::memory_order_acq_rel)) { + if (last_state == GuardState::DoneInit) + return 0; + if (last_state == GuardState::NotInitialized) + continue; // start over, and try to initialize in this thread + // else wait, someone else set the wait bit before us + } + } + // State is InitInProgress | WaitingOnInit, time to sleep + wait_on_futex(); + } + } + } + + void release() + { + // Store the value the compiler will look at, we're done! yay + m_compiler_guard_byte.store(GuardState::DoneInit, AK::memory_order_release); + + // Set the value other threads chilling in our loop will look at to done + uint8_t old_local_state = m_local_state_byte.exchange(GuardState::DoneInit, AK::memory_order_acq_rel); + + // Wake up sleepy heads, the variable is initialized! + if (old_local_state & GuardState::WaitingOnInit) + wake_futex(); + } + + void abort() + { + uint8_t old_local_state = m_local_state_byte.exchange(GuardState::NotInitialized, AK::memory_order_acq_rel); + + // Wake up sleepy heads, someone else gets to try... + if (old_local_state & GuardState::WaitingOnInit) + wake_futex(); + } + +private: + void wait_on_futex() + { + int futex_value = (GuardState::InitInProgress | GuardState::WaitingOnInit) << 8; + futex((int32_t*)m_full_guard_var, FUTEX_WAIT, futex_value, nullptr); + } + + void wake_futex() + { + futex((int32_t*)m_full_guard_var, FUTEX_WAKE, INT32_MAX, nullptr); + } + + void* m_full_guard_var; + AtomicRef m_compiler_guard_byte; + AtomicRef m_local_state_byte; // we'll load and set this atomically + + // Note that we're not using 6/8 bytes of the guard object... +}; + +extern "C" { + +int __cxa_guard_acquire(uint64_t* guard_object) +{ + CxaGuard g(guard_object); + return g.acquire(); +} + +void __cxa_guard_release(uint64_t* guard_object) +{ + CxaGuard g(guard_object); + g.release(); +} + +void __cxa_guard_abort(uint64_t* guard_object) +{ + CxaGuard g(guard_object); + g.abort(); +} + +} // extern "C" diff --git a/Toolchain/BuildIt.sh b/Toolchain/BuildIt.sh index ab499897c4..b50ed23f22 100755 --- a/Toolchain/BuildIt.sh +++ b/Toolchain/BuildIt.sh @@ -230,7 +230,7 @@ pushd "$DIR/Build/" pushd "$BUILD" cmake .. "$MAKE" LibC - install -D Libraries/LibC/libc.a Libraries/LibM/libm.a Root/usr/lib/ + install -D Libraries/LibC/libc.a Libraries/LibM/libm.a Libraries/LibCxx/libstdc++.a Root/usr/lib/ SRC_ROOT=$(realpath "$DIR"/..) for header in "$SRC_ROOT"/Libraries/Lib{C,M}/**/*.h; do target=$(echo "$header" | sed -e "s@$SRC_ROOT/Libraries/LibC@@" -e "s@$SRC_ROOT/Libraries/LibM@@") @@ -239,11 +239,6 @@ pushd "$DIR/Build/" unset SRC_ROOT popd - echo "XXX build libstdc++" - "$MAKE" all-target-libstdc++-v3 || exit 1 - echo "XXX install libstdc++" - "$MAKE" install-target-libstdc++-v3 || exit 1 - if [ "$(uname -s)" = "OpenBSD" ]; then cd "$DIR/Local/libexec/gcc/i686-pc-serenity/$GCC_VERSION" && ln -sf liblto_plugin.so.0.0 liblto_plugin.so fi