/*************************************************************************** * Copyright (C) 2008-2019 by Terraneo Federico * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * As a special exception, if other files instantiate templates or use * * macros or inline functions from this file, or you compile this file * * and link it with other works to produce a work based on this file, * * this file does not by itself cause the resulting work to be covered * * by the GNU General Public License. However the source code for this * * file must still be made available in accordance with the GNU General * * Public License. This exception does not invalidate any other reasons * * why a work based on this file might be covered by the GNU General * * Public License. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see * ***************************************************************************/ #include "libstdcpp_integration.h" #include #include #include #if _MIOSIX_GCC_PATCH_MAJOR > 2 #include #endif //// Settings #include "config/miosix_settings.h" //// Console #include "kernel/logging.h" //// kernel interface #include "kernel/kernel.h" using namespace std; // // C++ exception support // ===================== #if __cplusplus >= 201703L #warning: TODO: Override new with alignment (libsupc++/new_opa.cc, new_opv.cc, ... #warning: TODO: FIX __gthread_key_t in libstdc++/include/std/memory_resource #endif #ifdef __NO_EXCEPTIONS /* * If not using exceptions, ovverride the default new, delete with * an implementation that does not throw, to minimze code size */ void *operator new(size_t size) noexcept { return malloc(size); } void *operator new(size_t size, const std::nothrow_t&) noexcept { return malloc(size); } void *operator new[](size_t size) noexcept { return malloc(size); } void *operator new[](size_t size, const std::nothrow_t&) noexcept { return malloc(size); } void operator delete(void *p) noexcept { free(p); } void operator delete[](void *p) noexcept { free(p); } void operator delete(void *p, std::size_t size) noexcept { (void) size; free(p); } void operator delete[](void *p, std::size_t size) noexcept { (void) size; free(p); } /** * \internal * The default version of these functions provided with libstdc++ require * exception support. This means that a program using pure virtual functions * incurs in the code size penalty of exception support even when compiling * without exceptions. By replacing the default implementations with these one * the problem is fixed. */ extern "C" void __cxxabiv1::__cxa_pure_virtual(void) { errorLog("\n***Pure virtual method called\n"); _exit(1); } extern "C" void __cxxabiv1::__cxa_deleted_virtual(void) { errorLog("\n***Deleted virtual method called\n"); _exit(1); } #if _MIOSIX_GCC_PATCH_MAJOR > 2 namespace std { void terminate() noexcept { _exit(1); } //Since GCC 9.2.0 void unexpected() noexcept { _exit(1); } //Since GCC 9.2.0 /* * This one comes from thread.cc, the need to call the class destructor makes it * call __cxa_end_cleanup which pulls in exception code. */ extern "C" void* execute_native_thread_routine(void* __p) { thread::_State_ptr __t{ static_cast(__p) }; __t->_M_run(); return nullptr; } } //namespace std #endif /* * If not using exceptions, ovverride these functions with * an implementation that does not throw, to minimze code size */ namespace std { void __throw_bad_exception() { _exit(1); } void __throw_bad_alloc() { _exit(1); } void __throw_bad_cast() { _exit(1); } void __throw_bad_typeid() { _exit(1); } void __throw_logic_error(const char*) { _exit(1); } void __throw_domain_error(const char*) { _exit(1); } void __throw_invalid_argument(const char*) { _exit(1); } void __throw_length_error(const char*) { _exit(1); } void __throw_out_of_range(const char*) { _exit(1); } void __throw_out_of_range_fmt(const char*, ...) { exit(1); } //Since GCC 9.2.0 void __throw_runtime_error(const char*) { _exit(1); } void __throw_range_error(const char*) { _exit(1); } void __throw_overflow_error(const char*) { _exit(1); } void __throw_underflow_error(const char*) { _exit(1); } #if !defined(_MIOSIX_GCC_PATCH_MAJOR) || _MIOSIX_GCC_PATCH_MAJOR <= 2 void __throw_ios_failure(const char*) { _exit(1); } //Unused since GCC 9.2.0 #endif void __throw_system_error(int) { _exit(1); } void __throw_future_error(int) { _exit(1); } void __throw_bad_function_call() { _exit(1); } } //namespace std namespace __cxxabiv1 { extern "C" void __cxa_throw_bad_array_length() { exit(1); } //Since GCC 9.2.0 extern "C" void __cxa_bad_cast() { exit(1); } //Since GCC 9.2.0 extern "C" void __cxa_bad_typeid() { exit(1); } //Since GCC 9.2.0 extern "C" void __cxa_throw_bad_array_new_length() { exit(1); } //Since GCC 9.2.0 } //namespace __cxxabiv1 #endif //__NO_EXCEPTIONS namespace miosix { class CppReentrancyAccessor { public: static __cxxabiv1::__cxa_eh_globals *getEhGlobals() { return &miosix::Thread::getCurrentThread()->cppReentrancyData.eh_globals; } #ifndef __ARM_EABI__ static void *getSjljPtr() { return miosix::Thread::getCurrentThread()->cppReentrancyData.sjlj_ptr; } static void setSjljPtr(void *ptr) { miosix::Thread::getCurrentThread()->cppReentrancyData.sjlj_ptr=ptr; } #endif //__ARM_EABI__ }; } //namespace miosix /* * If exception support enabled, ensure it is thread safe. * The functions __cxa_get_globals_fast() and __cxa_get_globals() need to * return a per-thread data structure. Given that thread local storage isn't * implemented in Miosix, libstdc++ was patched to make these functions syscalls */ namespace __cxxabiv1 { extern "C" __cxa_eh_globals* __cxa_get_globals_fast() { return miosix::CppReentrancyAccessor::getEhGlobals(); } extern "C" __cxa_eh_globals* __cxa_get_globals() { return miosix::CppReentrancyAccessor::getEhGlobals(); } #ifndef __ARM_EABI__ extern "C" void _Miosix_set_sjlj_ptr(void* ptr) { miosix::CppReentrancyAccessor::setSjljPtr(ptr); } extern "C" void *_Miosix_get_sjlj_ptr() { return miosix::CppReentrancyAccessor::getSjljPtr(); } #endif //__ARM_EABI__ } //namespace __cxxabiv1 namespace __gnu_cxx { /** * \internal * Replaces the default verbose terminate handler. * Avoids the inclusion of code to demangle C++ names, which saves code size * when using exceptions. */ void __verbose_terminate_handler() { errorLog("\n***Unhandled exception thrown\n"); _exit(1); } } //namespace __gnu_cxx // // C++ static constructors support, to achieve thread safety // ========================================================= //This is weird, despite almost everywhere in GCC's documentation it is said //that __guard is 8 bytes, it is actually only four. union MiosixGuard { miosix::Thread *owner; unsigned int flag; }; namespace __cxxabiv1 { /** * Used to initialize static objects only once, in a threadsafe way * \param g guard struct * \return 0 if object already initialized, 1 if this thread has to initialize * it, or lock if another thread has already started initializing it */ extern "C" int __cxa_guard_acquire(__guard *g) { miosix::InterruptDisableLock dLock; volatile MiosixGuard *guard=reinterpret_cast(g); for(;;) { if(guard->flag==1) return 0; //Object already initialized, good if(guard->flag==0) { //Object uninitialized, and no other thread trying to initialize it guard->owner=miosix::Thread::IRQgetCurrentThread(); //guard->owner serves the double task of being the thread id of //the thread initializing the object, and being the flag to signal //that the object is initialized or not. If bit #0 of guard->owner //is @ 1 the object is initialized. All this works on the assumption //that Thread* pointers never have bit #0 @ 1, and this assetion //checks that this condition really holds if(guard->flag & 1) miosix::errorHandler(miosix::UNEXPECTED); return 1; } //If we get here, the object is being initialized by another thread if(guard->owner==miosix::Thread::IRQgetCurrentThread()) { //Wait, the other thread initializing the object is this thread?!? //We have a recursive initialization error. Not throwing an //exception to avoid pulling in exceptions even with -fno-exception IRQerrorLog("Recursive initialization\r\n"); _exit(1); } { miosix::InterruptEnableLock eLock(dLock); miosix::Thread::yield(); //Sort of a spinlock, a "yieldlock"... } } } /** * Called after the thread has successfully initialized the object * \param g guard struct */ extern "C" void __cxa_guard_release(__guard *g) noexcept { miosix::InterruptDisableLock dLock; MiosixGuard *guard=reinterpret_cast(g); guard->flag=1; } /** * Called if an exception was thrown while the object was being initialized * \param g guard struct */ extern "C" void __cxa_guard_abort(__guard *g) noexcept { miosix::InterruptDisableLock dLock; MiosixGuard *guard=reinterpret_cast(g); guard->flag=0; } } //namespace __cxxabiv1 // // libatomic support, to provide thread safe atomic operation fallbacks // ==================================================================== // Not using the fast version, as these may be used before the kernel is started extern "C" unsigned int libat_quick_lock_n(void *ptr) { (void) ptr; miosix::disableInterrupts(); return 0; } extern "C" void libat_quick_unlock_n(void *ptr, unsigned int token) { (void) ptr; (void) token; miosix::enableInterrupts(); } // These are to implement "heavy" atomic operations, which are not used in // libstdc++. For now let's keep them disbaled. // extern "C" void libat_lock_n(void *ptr, size_t n) // { // miosix::pauseKernel(); // } // // extern "C" void libat_unlock_n(void *ptr, size_t n) // { // miosix::restartKernel(); // }