kopia lustrzana https://github.com/OpenRTX/OpenRTX
371 wiersze
11 KiB
C++
371 wiersze
11 KiB
C++
/***************************************************************************
|
|
* 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 <http://www.gnu.org/licenses/> *
|
|
***************************************************************************/
|
|
|
|
#include "libstdcpp_integration.h"
|
|
#include <cstdlib>
|
|
#include <unistd.h>
|
|
#include <cxxabi.h>
|
|
#if _MIOSIX_GCC_PATCH_MAJOR > 2
|
|
#include <thread>
|
|
#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<thread::_State*>(__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<volatile MiosixGuard*>(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<MiosixGuard*>(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<MiosixGuard*>(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();
|
|
// }
|