Skip to content

Instantly share code, notes, and snippets.

@thomask77
Last active March 27, 2024 12:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save thomask77/3a2d54a482c294beec5d87730e163bdd to your computer and use it in GitHub Desktop.
Save thomask77/3a2d54a482c294beec5d87730e163bdd to your computer and use it in GitHub Desktop.
newlib retarget locks for FreeRTOS (dynamic version)
// -------------------- 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);
}
@berkakinci
Copy link

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.

@Meumeu
Copy link

Meumeu commented Jul 22, 2019

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.

@thomask77
Copy link
Author

thomask77 commented Jul 25, 2019

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 to
    1. register_fini - does nothing, as long as __libc_fini is not defined (who uses that feature?)
      -> BUT otherwise, it will call atexit() which takes locks!
    2. 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.
    3. .. 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.

@thomask77
Copy link
Author

thomask77 commented Jul 25, 2019

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 };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment