From ad4d883c4fceb29281c5e8bdf25fe3679449f0f2 Mon Sep 17 00:00:00 2001 From: Juan Carrano Date: Fri, 16 Aug 2019 19:17:32 +0200 Subject: [PATCH 1/2] pkg/tlsf: fix double pointer. A (void*) function was declared as (void**) because one of the void pointers was hidden behind a typedef. Because of the way a void* works, this has no consequences, but it is confusing. --- pkg/tlsf/contrib/include/tlsf-malloc.h | 2 +- pkg/tlsf/contrib/tlsf-malloc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/tlsf/contrib/include/tlsf-malloc.h b/pkg/tlsf/contrib/include/tlsf-malloc.h index e99523fc9961..4aee8d60f881 100644 --- a/pkg/tlsf/contrib/include/tlsf-malloc.h +++ b/pkg/tlsf/contrib/include/tlsf-malloc.h @@ -86,7 +86,7 @@ int tlsf_add_global_pool(void *mem, size_t bytes); * * Use for debugging purposes only. */ -tlsf_t *_tlsf_get_global_control(void); +tlsf_t _tlsf_get_global_control(void); #ifdef __cplusplus diff --git a/pkg/tlsf/contrib/tlsf-malloc.c b/pkg/tlsf/contrib/tlsf-malloc.c index 117b5607173f..0d11a3506af0 100644 --- a/pkg/tlsf/contrib/tlsf-malloc.c +++ b/pkg/tlsf/contrib/tlsf-malloc.c @@ -60,7 +60,7 @@ int tlsf_add_global_pool(void *mem, size_t bytes) } } -tlsf_t *_tlsf_get_global_control(void) +tlsf_t _tlsf_get_global_control(void) { return gheap; } From cc907fa54eb62dbfce7f0a1c0a43cbd3cbb83dbd Mon Sep 17 00:00:00 2001 From: Juan Carrano Date: Mon, 19 Aug 2019 16:31:06 +0200 Subject: [PATCH 2/2] 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. --- pkg/tlsf/Makefile.dep | 9 ++ pkg/tlsf/Makefile.include | 9 ++ pkg/tlsf/contrib/Makefile | 4 +- pkg/tlsf/contrib/include/tlsf-malloc.h | 1 + pkg/tlsf/contrib/native.c | 123 +++++++++++++++++++++++ pkg/tlsf/contrib/newlib.c | 128 ++++++++++++++++++++++++ pkg/tlsf/contrib/tlsf-malloc-internal.h | 34 +++++++ pkg/tlsf/contrib/tlsf-malloc.c | 96 ++---------------- 8 files changed, 314 insertions(+), 90 deletions(-) create mode 100644 pkg/tlsf/Makefile.dep create mode 100644 pkg/tlsf/contrib/native.c create mode 100644 pkg/tlsf/contrib/newlib.c create mode 100644 pkg/tlsf/contrib/tlsf-malloc-internal.h diff --git a/pkg/tlsf/Makefile.dep b/pkg/tlsf/Makefile.dep new file mode 100644 index 000000000000..f83eaa710f4b --- /dev/null +++ b/pkg/tlsf/Makefile.dep @@ -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 diff --git a/pkg/tlsf/Makefile.include b/pkg/tlsf/Makefile.include index 632832797236..f5780a61130d 100644 --- a/pkg/tlsf/Makefile.include +++ b/pkg/tlsf/Makefile.include @@ -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 diff --git a/pkg/tlsf/contrib/Makefile b/pkg/tlsf/contrib/Makefile index 290de6a16c8d..161477a4e393 100644 --- a/pkg/tlsf/contrib/Makefile +++ b/pkg/tlsf/contrib/Makefile @@ -1,3 +1,5 @@ -MODULE := tlsf-malloc +MODULE = tlsf-malloc +SUBMODULES = 1 +SRC = tlsf-malloc.c include $(RIOTBASE)/Makefile.base diff --git a/pkg/tlsf/contrib/include/tlsf-malloc.h b/pkg/tlsf/contrib/include/tlsf-malloc.h index 4aee8d60f881..943b800402bb 100644 --- a/pkg/tlsf/contrib/include/tlsf-malloc.h +++ b/pkg/tlsf/contrib/include/tlsf-malloc.h @@ -37,6 +37,7 @@ #define TLSF_MALLOC_H #include + #include "tlsf.h" #ifdef __cplusplus diff --git a/pkg/tlsf/contrib/native.c b/pkg/tlsf/contrib/native.c new file mode 100644 index 000000000000..9bf7421b4aae --- /dev/null +++ b/pkg/tlsf/contrib/native.c @@ -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 +#include + +#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); +} diff --git a/pkg/tlsf/contrib/newlib.c b/pkg/tlsf/contrib/newlib.c new file mode 100644 index 000000000000..c90535635c56 --- /dev/null +++ b/pkg/tlsf/contrib/newlib.c @@ -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 +#include +#include + +#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); +} + +/** + * @} + */ diff --git a/pkg/tlsf/contrib/tlsf-malloc-internal.h b/pkg/tlsf/contrib/tlsf-malloc-internal.h new file mode 100644 index 000000000000..1268877e1d0d --- /dev/null +++ b/pkg/tlsf/contrib/tlsf-malloc-internal.h @@ -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 */ diff --git a/pkg/tlsf/contrib/tlsf-malloc.c b/pkg/tlsf/contrib/tlsf-malloc.c index 0d11a3506af0..4683065b3d76 100644 --- a/pkg/tlsf/contrib/tlsf-malloc.c +++ b/pkg/tlsf/contrib/tlsf-malloc.c @@ -13,56 +13,35 @@ * @file * * @brief TLSF-based global memory allocator. - * @author René Kijewski * @author Juan I Carrano * */ #include -#include -#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); -} - /** * @} */