pkg/tlsf: Fix the way system functions are overriden.

The correct way to overrride the malloc family of functions in newlib-nano is
to provide the *_r (reentrant) variants. Newlib implements the "normal"
functions on top of these (see the newlib source code). Also, internally it calls
the *_r functions when allocating buffers.

If only the "normal" non-reentrant functions are provided this will mean that
some of the code will still use the vanilla newlib allocator. Furthermore, if
one uses the whole heap as a pool for TLSF then the system may in the best case
crash as there is no enough memory for its internall allocations or in the worst
case function eratically (this depends on how the heap reserved, there is an
upcomming series of commits in that direction).

This commit splits the handling between newlib and native. It also prepares the
ground for future work on the pool initialization.

Right now I could only test this in ARM and native and I cannot ensure it will
work on other platforms. Replacing the system's memory allocator is not something
that can be taken lightly and will inevitably require diving into the depths of
the libc. Therefore I would say that using TLSF as a system wide allocator is ATM
supported officially only on those plaftorms.

Testing:

Aside from reading the newlib sources, you can see the issue in a live system
using the debugger.

Compile any example (with or without tlsf-malloc), grab a debugger and place
a breakpoint in sbrk and _sbrk_r. Doing a backtrace will reveal it gets called
by _malloc_r.
This commit is contained in:
Juan Carrano 2019-08-19 16:31:06 +02:00 committed by Juan I Carrano
parent ad4d883c4f
commit cc907fa54e
8 changed files with 314 additions and 90 deletions

9
pkg/tlsf/Makefile.dep Normal file
View File

@ -0,0 +1,9 @@
ifneq (,$(filter tlsf-malloc,$(USEMODULE)))
ifneq (,$(filter newlib,$(USEMODULE)))
USEMODULE += tlsf-malloc_newlib
else ifneq (,$(filter native,$(BOARD)))
USEMODULE += tlsf-malloc_native
else
$(warning tlsf-malloc can only be used on native or on platforms using newlib)
endif
endif

View File

@ -4,3 +4,12 @@ ifneq (,$(filter tlsf-malloc,$(USEMODULE)))
INCLUDES += -I$(RIOTPKG)/tlsf/contrib/include
DIRS += $(RIOTPKG)/tlsf/contrib
endif
PSEUDOMODULES += tlsf-malloc_newlib
PSEUDOMODULES += tlsf-malloc_native
ifneq (,$(filter tlsf-malloc_newlib,$(USEMODULE)))
UNDEF += $(BINDIR)/tlsf-malloc/newlib.o
else ifneq (,$(filter tlsf-malloc_native,$(BOARD)))
UNDEF += $(BINDIR)/tlsf-malloc/native.o
endif

View File

@ -1,3 +1,5 @@
MODULE := tlsf-malloc
MODULE = tlsf-malloc
SUBMODULES = 1
SRC = tlsf-malloc.c
include $(RIOTBASE)/Makefile.base

View File

@ -37,6 +37,7 @@
#define TLSF_MALLOC_H
#include <stddef.h>
#include "tlsf.h"
#ifdef __cplusplus

123
pkg/tlsf/contrib/native.c Normal file
View File

@ -0,0 +1,123 @@
/*
* Copyright (C) 2019 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_tlsf_malloc
* @ingroup pkg
* @ingroup sys
* @{
* @file
*
* @brief Definitions to use tlsf as malloc on native.
* @author Juan I Carrano
*
* This assumes glibc is bein used.
* see: https://www.gnu.org/software/libc/manual/html_node/Replacing-malloc.html
*
*/
#include <string.h>
#include <errno.h>
#include "irq.h"
#include "tlsf.h"
#include "tlsf-malloc.h"
#include "tlsf-malloc-internal.h"
/* TODO: Add defines for other compilers */
#if defined(__GNUC__) && !defined(__clang__) /* Clang supports __GNUC__ but
* not the alloc_size()
* attribute */
#define ATTR_MALLOC __attribute__((malloc, alloc_size(1)))
#define ATTR_CALLOC __attribute__((malloc, alloc_size(1,2)))
#define ATTR_MALIGN __attribute__((alloc_align(1), alloc_size(2), malloc))
#define ATTR_REALLOC __attribute__((alloc_size(2)))
#else /* No GNU C -> no alias attribute */
#define ATTR_MALLOC
#define ATTR_CALLOC
#define ATTR_MALIGN
#define ATTR_REALLOC
#endif /* __GNUC__ */
extern tlsf_t tlsf_malloc_gheap;
/**
* Allocate a block of size "bytes"
*/
ATTR_MALLOC void *malloc(size_t bytes)
{
unsigned old_state = irq_disable();
void *result = tlsf_malloc(tlsf_malloc_gheap, bytes);
if (result == NULL) {
errno = ENOMEM;
}
irq_restore(old_state);
return result;
}
/**
* Allocate and clear a block of size "bytes*count"
*/
ATTR_CALLOC void *calloc(size_t count, size_t bytes)
{
void *result = malloc(count * bytes);
if (result != NULL) {
memset(result, 0, count * bytes);
}
return result;
}
/**
* Allocate an aligned memory block.
*/
ATTR_MALIGN void *memalign(size_t align, size_t bytes)
{
unsigned old_state = irq_disable();
void *result = tlsf_memalign(tlsf_malloc_gheap, align, bytes);
if (result == NULL) {
errno = ENOMEM;
}
irq_restore(old_state);
return result;
}
/**
* Deallocate and reallocate with a different size.
*/
ATTR_REALLOC void *realloc(void *ptr, size_t size)
{
unsigned old_state = irq_disable();
void *result = tlsf_realloc(tlsf_malloc_gheap, ptr, size);
if (result == NULL) {
errno = ENOMEM;
}
irq_restore(old_state);
return result;
}
/**
* Deallocate a block of data.
*/
void free(void *ptr)
{
unsigned old_state = irq_disable();
tlsf_free(tlsf_malloc_gheap, ptr);
irq_restore(old_state);
}

128
pkg/tlsf/contrib/newlib.c Normal file
View File

@ -0,0 +1,128 @@
/*
* Copyright (C) 2019 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_tlsf_malloc
* @ingroup pkg
* @ingroup sys
* @{
* @file
*
* @brief Reentrant definitions to replace newlib's malloc with TLSF.
* @author Juan I Carrano
*
* Newlib-nano implements malloc/free/etc in terms of the reentrant definitions
* in _malloc_r/_free_r/etc so the latter are the one that have to be
* overwritten.
*
*/
#include <string.h>
#include <reent.h>
#include <errno.h>
#include "irq.h"
#include "tlsf.h"
#include "tlsf-malloc.h"
#include "tlsf-malloc-internal.h"
/* TODO: Add defines for other compilers */
#if defined(__GNUC__) && !defined(__clang__) /* Clang supports __GNUC__ but
* not the alloc_size()
* attribute */
#define ATTR_MALLOCR __attribute__((malloc, alloc_size(2)))
#define ATTR_CALLOCR __attribute__((malloc, alloc_size(2,3)))
#define ATTR_MALIGNR __attribute__((alloc_align(2), alloc_size(3), malloc))
#define ATTR_REALLOCR __attribute__((alloc_size(3)))
#else /* No GNU C -> no alias attribute */
#define ATTR_MALLOCR
#define ATTR_CALLOCR
#define ATTR_MALIGNR
#define ATTR_REALLOCR
#endif /* __GNUC__ */
/**
* Allocate a block of size "bytes"
*/
ATTR_MALLOCR void *_malloc_r(struct _reent *reent_ptr, size_t bytes)
{
unsigned old_state = irq_disable();
void *result = tlsf_malloc(tlsf_malloc_gheap, bytes);
if (result == NULL) {
reent_ptr->_errno = ENOMEM;
}
irq_restore(old_state);
return result;
}
/**
* Allocate and clear a block of size "bytes*count"
*/
ATTR_CALLOCR void *_calloc_r(struct _reent *reent_ptr, size_t count, size_t bytes)
{
void *result = _malloc_r(reent_ptr, count * bytes);
if (result != NULL) {
memset(result, 0, count * bytes);
}
return result;
}
/**
* Allocate an aligned memory block.
*/
ATTR_MALIGNR void *_memalign_r(struct _reent *reent_ptr, size_t align, size_t bytes)
{
unsigned old_state = irq_disable();
void *result = tlsf_memalign(tlsf_malloc_gheap, align, bytes);
if (result == NULL) {
reent_ptr->_errno = ENOMEM;
}
irq_restore(old_state);
return result;
}
/**
* Deallocate and reallocate with a different size.
*/
ATTR_REALLOCR void *_realloc_r(struct _reent *reent_ptr, void *ptr, size_t size)
{
unsigned old_state = irq_disable();
void *result = tlsf_realloc(tlsf_malloc_gheap, ptr, size);
if (result == NULL) {
reent_ptr->_errno = ENOMEM;
}
irq_restore(old_state);
return result;
}
/**
* Deallocate a block of data.
*/
void _free_r(struct _reent *reent_ptr, void *ptr)
{
unsigned old_state = irq_disable();
(void)reent_ptr;
tlsf_free(tlsf_malloc_gheap, ptr);
irq_restore(old_state);
}
/**
* @}
*/

View File

@ -0,0 +1,34 @@
/*
* Copyright (C) 2014-2018 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_tlsf_malloc
* @{
* @file
* @internal
*
* @brief TLSF/malloc internal definitions
* @author Juan I Carrano
*
*/
#ifndef TLSF_MALLOC_INTERNAL_H
#define TLSF_MALLOC_INTERNAL_H
#include "tlsf.h"
#ifdef __cplusplus
extern "C" {
#endif
extern tlsf_t tlsf_malloc_gheap;
#ifdef __cplusplus
}
#endif
#endif /* TLSF_MALLOC_INTERNAL_H */

View File

@ -13,56 +13,35 @@
* @file
*
* @brief TLSF-based global memory allocator.
* @author René Kijewski
* @author Juan I Carrano
*
*/
#include <stdio.h>
#include <string.h>
#include "irq.h"
#include "tlsf.h"
#include "tlsf-malloc.h"
#include "tlsf-malloc-internal.h"
/**
* Global memory heap (really a collection of pools, or areas)
**/
static tlsf_t gheap = NULL;
/* TODO: Add defines for other compilers */
#if defined(__GNUC__) && !defined(__clang__) /* Clang supports __GNUC__ but
* not the alloc_size()
* attribute */
#define ATTR_MALLOC __attribute__((malloc, alloc_size(1)))
#define ATTR_CALLOC __attribute__((malloc, alloc_size(1,2)))
#define ATTR_MALIGN __attribute__((alloc_align(1), alloc_size(2), malloc))
#define ATTR_REALLOC __attribute__((alloc_size(2)))
#else /* No GNU C -> no alias attribute */
#define ATTR_MALLOC
#define ATTR_CALLOC
#define ATTR_MALIGN
#define ATTR_REALLOC
#endif /* __GNUC__ */
tlsf_t tlsf_malloc_gheap = NULL;
int tlsf_add_global_pool(void *mem, size_t bytes)
{
if (gheap == NULL) {
gheap = tlsf_create_with_pool(mem, bytes);
return gheap == NULL;
if (tlsf_malloc_gheap == NULL) {
tlsf_malloc_gheap = tlsf_create_with_pool(mem, bytes);
return tlsf_malloc_gheap == NULL;
}
else {
return tlsf_add_pool(gheap, mem, bytes) == NULL;
return tlsf_add_pool(tlsf_malloc_gheap, mem, bytes) == NULL;
}
}
tlsf_t _tlsf_get_global_control(void)
{
return gheap;
return tlsf_malloc_gheap;
}
void tlsf_size_walker(void* ptr, size_t size, int used, void* user)
@ -77,67 +56,6 @@ void tlsf_size_walker(void* ptr, size_t size, int used, void* user)
}
}
/**
* Allocate a block of size "bytes"
*/
ATTR_MALLOC void *malloc(size_t bytes)
{
unsigned old_state = irq_disable();
void *result = tlsf_malloc(gheap, bytes);
irq_restore(old_state);
return result;
}
/**
* Allocate and clear a block of size "bytes*count"
*/
ATTR_CALLOC void *calloc(size_t count, size_t bytes)
{
void *result = malloc(count * bytes);
if (result) {
memset(result, 0, count * bytes);
}
return result;
}
/**
* Allocate an aligned memory block.
*/
ATTR_MALIGN void *memalign(size_t align, size_t bytes)
{
unsigned old_state = irq_disable();
void *result = tlsf_memalign(gheap, align, bytes);
irq_restore(old_state);
return result;
}
/**
* Deallocate and reallocate with a different size.
*/
ATTR_REALLOC void *realloc(void *ptr, size_t size)
{
unsigned old_state = irq_disable();
void *result = tlsf_realloc(gheap, ptr, size);
irq_restore(old_state);
return result;
}
/**
* Deallocate a block of data.
*/
void free(void *ptr)
{
unsigned old_state = irq_disable();
tlsf_free(gheap, ptr);
irq_restore(old_state);
}
/**
* @}
*/