-
-
Save thomask77/3a2d54a482c294beec5d87730e163bdd to your computer and use it in GitHub Desktop.
// -------------------- Retarget Locks -------------------- | |
// | |
// share/doc/gcc-arm-none-eabi/pdf/libc.pdf: | |
// | |
// Newlib was configured to allow the target platform to provide the locking routines and | |
// static locks at link time. As such, a dummy default implementation of these routines and | |
// static locks is provided for single-threaded application to link successfully out of the box on | |
// bare-metal systems. | |
// | |
// For multi-threaded applications the target platform is required to provide an implementa- | |
// tion for *all* these routines and static locks. If some routines or static locks are missing, the | |
// link will fail with doubly defined symbols. | |
// | |
// (see also newlib/libc/misc/lock.c) | |
// | |
#include <sys/lock.h> | |
#include "FreeRTOS.h" | |
#include "semphr.h" | |
#include "task.h" | |
struct __lock { | |
SemaphoreHandle_t sem; | |
}; | |
struct __lock __lock___sinit_recursive_mutex; | |
struct __lock __lock___sfp_recursive_mutex; | |
struct __lock __lock___atexit_recursive_mutex; | |
struct __lock __lock___at_quick_exit_mutex; | |
struct __lock __lock___malloc_recursive_mutex; | |
struct __lock __lock___env_recursive_mutex; | |
struct __lock __lock___tz_mutex; | |
struct __lock __lock___dd_hash_mutex; | |
struct __lock __lock___arc4random_mutex; | |
__attribute__((constructor)) | |
static void init_retarget_locks(void) | |
{ | |
__lock___sinit_recursive_mutex.sem = xSemaphoreCreateRecursiveMutex(); | |
__lock___sfp_recursive_mutex.sem = xSemaphoreCreateRecursiveMutex(); | |
__lock___atexit_recursive_mutex.sem = xSemaphoreCreateRecursiveMutex(); | |
__lock___at_quick_exit_mutex.sem = xSemaphoreCreateMutex(); | |
// __lock___malloc_recursive_mutex.sem = xSemaphoreCreateRecursiveMutex(); // see below | |
__lock___env_recursive_mutex.sem = xSemaphoreCreateRecursiveMutex(); | |
__lock___tz_mutex.sem = xSemaphoreCreateMutex(); | |
__lock___dd_hash_mutex.sem = xSemaphoreCreateMutex(); | |
__lock___arc4random_mutex.sem = xSemaphoreCreateMutex(); | |
} | |
// Special case for malloc/free. Without this, the full | |
// malloc_recursive_mutex would be used, which is much slower. | |
// | |
void __malloc_lock(struct _reent *r) | |
{ | |
(void)r; | |
taskENTER_CRITICAL(); | |
} | |
void __malloc_unlock(struct _reent *r) | |
{ | |
(void)r; | |
taskEXIT_CRITICAL(); | |
} | |
void __retarget_lock_init(_LOCK_T *lock_ptr) | |
{ | |
*lock_ptr = pvPortMalloc(sizeof(struct __lock)); | |
(*lock_ptr)->sem = xSemaphoreCreateMutex(); | |
} | |
void __retarget_lock_init_recursive(_LOCK_T *lock_ptr) | |
{ | |
*lock_ptr = pvPortMalloc(sizeof(struct __lock)); | |
(*lock_ptr)->sem = xSemaphoreCreateRecursiveMutex(); | |
} | |
void __retarget_lock_close(_LOCK_T lock) | |
{ | |
vSemaphoreDelete(lock->sem); | |
vPortFree(lock); | |
} | |
void __retarget_lock_close_recursive(_LOCK_T lock) | |
{ | |
vSemaphoreDelete(lock->sem); | |
vPortFree(lock); | |
} | |
void __retarget_lock_acquire(_LOCK_T lock) | |
{ | |
xSemaphoreTake(lock->sem, portMAX_DELAY); | |
} | |
void __retarget_lock_acquire_recursive(_LOCK_T lock) | |
{ | |
xSemaphoreTakeRecursive(lock->sem, portMAX_DELAY); | |
} | |
int __retarget_lock_try_acquire(_LOCK_T lock) | |
{ | |
return xSemaphoreTake(lock->sem, 0); | |
} | |
int __retarget_lock_try_acquire_recursive(_LOCK_T lock) | |
{ | |
return xSemaphoreTakeRecursive(lock->sem, 0); | |
} | |
void __retarget_lock_release(_LOCK_T lock) | |
{ | |
xSemaphoreGive(lock->sem); | |
} | |
void __retarget_lock_release_recursive(_LOCK_T lock) | |
{ | |
xSemaphoreGiveRecursive(lock->sem); | |
} |
I found a problem with this approach.
In startup.S, "__libc_init_array" is called. That function uses a bunch of locks. One of them is before "init_retarget_locks()" constructor has run. That means a lock is taken on an FreeRTOS semaphore structure that has not been initialized.
I haven't fixed this yet, but It'll probably be a flag that bypasses the use of these locks until I'm about to start the scheduler.
I've had the same issue and I fixed that by setting a priority on the attribute like __attribute__((constructor(101)))
, the values between 101 and 65535 are available.
What toolchain and C library do you use?
With the GNU Arm Embedded Toolchain and the newlib C-library, __libc_init_array
itself doesn't take any locks:
For normal C-projects:
__preinit_array
is empty,HAVE_INIT_FINISH
seems to be not defined (at least, there's no_init
call in the disassembly)__init_array
contains calls toregister_fini
- does nothing, as long as__libc_fini
is not defined (who uses that feature?)
-> BUT otherwise, it will callatexit()
which takes locks!- Some code from
/opt/arm-none-eabi-gcc/bin/../lib/gcc/arm-none-eabi/7.3.1/thumb/v7e-m/fpv5/hard/crtbegin.o
I can't find the source, but it does nothing when single-stepping. - .. and then our
init_retarget_locks
Perhaps you use C++ and have other global constructors/destructors?
Setting the priority will not help either, because register_fini
already runs at priority 0.
(priority is appended to the section name, and the linker script sorts them alphabetically).
-> better solution would be to call it directly from your start script.
Other solution:
// init_retarget_locks must run before any other code
//
__attribute__(( section(".preinit_array"), used ))
static void (* const preinit_array[])(void) = { &init_retarget_locks };
I found a problem with this approach.
In startup.S, "__libc_init_array" is called. That function uses a bunch of locks. One of them is before "init_retarget_locks()" constructor has run. That means a lock is taken on an FreeRTOS semaphore structure that has not been initialized.
I haven't fixed this yet, but It'll probably be a flag that bypasses the use of these locks until I'm about to start the scheduler.