diff --git a/pkg/tlsf/contrib/Makefile.include b/pkg/tlsf/contrib/Makefile.include new file mode 100644 index 000000000000..77d812faad9b --- /dev/null +++ b/pkg/tlsf/contrib/Makefile.include @@ -0,0 +1 @@ +UNDEF += $(BINDIR)/tlsf-malloc/tlsf-malloc.o diff --git a/pkg/tlsf/contrib/native.c b/pkg/tlsf/contrib/native.c index 9bf7421b4aae..bcaeafc35a7e 100644 --- a/pkg/tlsf/contrib/native.c +++ b/pkg/tlsf/contrib/native.c @@ -28,6 +28,19 @@ #include "tlsf-malloc.h" #include "tlsf-malloc-internal.h" +/* + * On native the linker dos not define the Heap area. + * It is handled by the C library (it requests memory from the OS ??) + * TODO: make this configurable + */ +static char _sheap[TLSF_NATIVE_HEAPSIZE]; + +/* Define the initialization function */ +void init_tlsf_malloc(void) +{ + tlsf_add_global_pool(_sheap, ROUND_DOWN4(TLSF_NATIVE_HEAPSIZE)); +} + /* TODO: Add defines for other compilers */ #if defined(__GNUC__) && !defined(__clang__) /* Clang supports __GNUC__ but * not the alloc_size() diff --git a/pkg/tlsf/contrib/newlib.c b/pkg/tlsf/contrib/newlib.c index c90535635c56..98c0024c494b 100644 --- a/pkg/tlsf/contrib/newlib.c +++ b/pkg/tlsf/contrib/newlib.c @@ -21,15 +21,58 @@ * */ +#define _DEFAULT_SOURCE #include #include #include +#include #include "irq.h" +#include "panic.h" #include "tlsf.h" #include "tlsf-malloc.h" #include "tlsf-malloc-internal.h" +/* + * The linker script for embedded target defines _sheap and _eheap: + * + * / * heap section * / + * . = ALIGN(4); + * _sheap = . ; + * _eheap = ORIGIN(ram) + LENGTH(ram); + * + * For some reason, at program startup there is something at _sbreak. so + * initializing the tlsf heap there leads to failure. In fact, sbrk(0) + * points quite some bytes after _sheap. + * The solution here is to request new memory to sbrk, use only that and + * request all memory up to _eheap. + * Keep in mind that this may mean less memory available for growing the + * stack! The way to solve it is to configure a smaller _eheap. + */ +extern char _eheap[]; /* FIXME: what to do with platforms without this symbol? */ + +/* Define the initialization function */ +void init_tlsf_malloc(void) +{ + /* Use sbrk(0) instead if _sheap since they may not be equal. */ + size_t request_size = ROUND_DOWN4(_eheap - (char*)sbrk(0)); + void *mem_start = sbrk(request_size); + + if (mem_start == (void *)(-1)) { + /* FIXME: This message does not show. The UART is probably not yet + * initialized. + */ + core_panic(PANIC_GENERAL_ERROR, "Could not enlarge heap"); + } + + /* Why do we use sbrk instead of _sheap and _eheap? + * Because of the (unlikely) possibility that some other libc procedure is + * using sbrk, which - if we were to bypass it by directly using _{s,e}heap + * would result in weird and hard to debug memory errors. + */ + tlsf_add_global_pool(mem_start, request_size); +} + /* TODO: Add defines for other compilers */ #if defined(__GNUC__) && !defined(__clang__) /* Clang supports __GNUC__ but diff --git a/pkg/tlsf/contrib/tlsf-malloc-internal.h b/pkg/tlsf/contrib/tlsf-malloc-internal.h index 1268877e1d0d..abf1b1f01dd9 100644 --- a/pkg/tlsf/contrib/tlsf-malloc-internal.h +++ b/pkg/tlsf/contrib/tlsf-malloc-internal.h @@ -25,8 +25,24 @@ extern "C" { #endif +#define ROUND_DOWN4(x) (((x)/4)*4) /* Is this necessary??? */ + +#ifndef TLSF_NATIVE_HEAPSIZE +/** + * Fixed heap size to use in native. + * + * In native there is virtually unlimited memory and no predefined heap area + * in the linker script, so one needs to define it manually. + * The default here is 8MiB which should be plenty for RIOT. + * Note that this has no effect on other platforms. + */ +#define TLSF_NATIVE_HEAPSIZE (0x800000) +#endif + extern tlsf_t tlsf_malloc_gheap; +void init_tlsf_malloc(void); + #ifdef __cplusplus } #endif diff --git a/pkg/tlsf/contrib/tlsf-malloc.c b/pkg/tlsf/contrib/tlsf-malloc.c index 4683065b3d76..d153b4957469 100644 --- a/pkg/tlsf/contrib/tlsf-malloc.c +++ b/pkg/tlsf/contrib/tlsf-malloc.c @@ -15,6 +15,16 @@ * @brief TLSF-based global memory allocator. * @author Juan I Carrano * + * # About initialization + * + * Some system (standard library) routines trigger the allocation of buffers + * which results in segmentation faults/ hard faults if using tlsf and the + * heap is not yet allocated. This function can possibly be used super early + * in the boot process, so the TLSF initialization must run earlier still. + * + * On native we define a static array and on embedded platforms we use the + * _sheap and _eheap linker symbols and sbrk. + * */ #include @@ -56,6 +66,14 @@ void tlsf_size_walker(void* ptr, size_t size, int used, void* user) } } +/* The C library runs the functions in this array before it initializes itself. + * This section constitutes a cross-file array. + * Note that in order for this to work this object file has to be added to + * UNDEF (see ../Makefile.include) otherwise this symbol gets discarded. + */ +void (* const init_tlsf_malloc_p) (void) __attribute__((section (".preinit_array"))) = + init_tlsf_malloc; + /** * @} */ diff --git a/tests/pkg_tlsf_malloc/Makefile b/tests/pkg_tlsf_malloc/Makefile index 8f2541cb5efe..39919e3a826b 100644 --- a/tests/pkg_tlsf_malloc/Makefile +++ b/tests/pkg_tlsf_malloc/Makefile @@ -4,6 +4,7 @@ include ../Makefile.tests_common # The bug is not fixed yet, so blacklist this test. TEST_ON_CI_BLACKLIST += all +TEST_ON_CI_WHITELIST += native samr21-xpro USEMODULE += tlsf-malloc