Skip to content

Commit

Permalink
Merge branch 'feature/tlsf-dynamic-control-size_v5.0' into 'release/v…
Browse files Browse the repository at this point in the history
…5.0'

heap: Update to the new tlsf implementation of dynamic metadata size (backport v5.0)

See merge request espressif/esp-idf!20774
  • Loading branch information
jack0c committed Dec 20, 2022
2 parents 133184b + 0fa3443 commit c173845
Show file tree
Hide file tree
Showing 17 changed files with 216 additions and 131 deletions.
1 change: 0 additions & 1 deletion components/cxx/test_apps/general/pytest_cxx_general.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,4 @@ def test_cxx_stack_smash(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('\"stack smashing protection CXX\"')
dut.expect_exact('Stack smashing protect failure!')
dut.expect_exact('abort() was called')
dut.expect_exact('Rebooting...')
5 changes: 2 additions & 3 deletions components/esp_system/stack_check.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,9 @@ __esp_stack_guard_setup (void)
__stack_chk_guard = (void *)esp_random();
}

void __stack_chk_fail (void)
IRAM_ATTR void __stack_chk_fail (void)
{
esp_rom_printf("\r\nStack smashing protect failure!\r\n\r\n");
abort();
esp_system_abort(DRAM_STR("Stack smashing protect failure!"));
}

#endif
5 changes: 0 additions & 5 deletions components/heap/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ set(includes "include")
if(NOT CONFIG_HEAP_TLSF_USE_ROM_IMPL)
set(priv_includes "tlsf")
list(APPEND srcs "tlsf/tlsf.c")
if(NOT CMAKE_BUILD_EARLY_EXPANSION)
set_source_files_properties(tlsf/tlsf.c
PROPERTIES COMPILE_FLAGS
"-include ../tlsf_platform.h")
endif()
endif()

if(NOT CONFIG_HEAP_POISONING_DISABLED)
Expand Down
3 changes: 1 addition & 2 deletions components/heap/heap_caps.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#include "heap_private.h"
#include "esp_system.h"


/* Forward declaration for base function, put in IRAM.
* These functions don't check for errors after trying to allocate memory. */
static void *heap_caps_realloc_base( void *ptr, size_t size, uint32_t caps );
Expand Down Expand Up @@ -56,7 +55,7 @@ IRAM_ATTR static void *dram_alloc_to_iram_addr(void *addr, size_t len)
}


static void heap_caps_alloc_failed(size_t requested_size, uint32_t caps, const char *function_name)
IRAM_ATTR NOINLINE_ATTR static void heap_caps_alloc_failed(size_t requested_size, uint32_t caps, const char *function_name)
{
if (alloc_failed_callback) {
alloc_failed_callback(requested_size, caps, function_name);
Expand Down
2 changes: 1 addition & 1 deletion components/heap/heap_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ extern SLIST_HEAD(registered_heap_ll, heap_t_) registered_heaps;
bool heap_caps_match(const heap_t *heap, uint32_t caps);

/* return all possible capabilities (across all priorities) for a given heap */
inline static IRAM_ATTR uint32_t get_all_caps(const heap_t *heap)
inline static __attribute__((always_inline)) uint32_t get_all_caps(const heap_t *heap)
{
if (heap->heap == NULL) {
return 0;
Expand Down
9 changes: 9 additions & 0 deletions components/heap/internals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Function placement in IRAM section

The heap component is compiled and linked in a way that minimizes the utilization of the IRAM section of memory without impacting the performance of its core functionalities. For this reason, the heap component API provided through [esp_heap_caps.h](./include/esp_heap_caps.h) and [esp_heap_caps_init.h](./include/esp_heap_caps_init.h) can be sorted into two sets of functions.

1. The performance related functions placed into the IRAM by using the `IRAM_ATTR` defined in [esp_attr.h](./../../components/esp_common/include/esp_attr.h) (e.g., `heap_caps_malloc`, `heap_caps_free`, `heap_caps_realloc`, etc.)

2. The functions that does not require the best of performance placed in the flash (e.g., `heap_caps_print_heap_info`, `heap_caps_dump`, `heap_caps_dump_all`, etc.)

With that in mind, all the functions defined in [multi_heap.c](./multi_heap.c), [multi_heap_poisoning.c](./multi_heap_poisoning.c) and [tlsf.c](./tlsf/tlsf.c) that are directly or indirectly called from one of the heap component API functions placed in IRAM have to also be placed in IRAM. Symmetrically, the functions directly or indirectly called from one of the heap component API functions placed in flash will also be placed in flash.
52 changes: 49 additions & 3 deletions components/heap/linker.lf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,53 @@
archive: libheap.a
entries:
if HEAP_TLSF_USE_ROM_IMPL = n:
tlsf (noflash)
multi_heap (noflash)
tlsf:tlsf_block_size (noflash)
tlsf:tlsf_size (noflash)
tlsf:tlsf_align_size (noflash)
tlsf:tlsf_block_size_min (noflash)
tlsf:tlsf_block_size_max (noflash)
tlsf:tlsf_alloc_overhead (noflash)
tlsf:tlsf_get_pool (noflash)
tlsf:tlsf_malloc (noflash)
tlsf:tlsf_memalign_offs (noflash)
tlsf:tlsf_memalign (noflash)
tlsf:tlsf_free (noflash)
tlsf:tlsf_realloc (noflash)

multi_heap:multi_heap_get_block_address_impl (noflash)
multi_heap:multi_heap_get_allocated_size_impl (noflash)
multi_heap:multi_heap_set_lock (noflash)
multi_heap:multi_heap_get_first_block (noflash)
multi_heap:multi_heap_get_next_block (noflash)
multi_heap:multi_heap_is_free (noflash)
multi_heap:multi_heap_malloc_impl (noflash)
multi_heap:multi_heap_free_impl (noflash)
multi_heap:multi_heap_realloc_impl (noflash)
multi_heap:multi_heap_aligned_alloc_impl_offs (noflash)
multi_heap:multi_heap_aligned_alloc_impl (noflash)
multi_heap:multi_heap_internal_lock (noflash)
multi_heap:multi_heap_internal_unlock (noflash)
multi_heap:assert_valid_block (noflash)

if HEAP_TLSF_USE_ROM_IMPL = y:
multi_heap:_multi_heap_lock (noflash)
multi_heap:_multi_heap_unlock (noflash)
multi_heap:multi_heap_in_rom_init (noflash)

if HEAP_POISONING_DISABLED = n:
multi_heap_poisoning (noflash)
multi_heap_poisoning:poison_allocated_region (noflash)
multi_heap_poisoning:verify_allocated_region (noflash)
multi_heap_poisoning:multi_heap_aligned_alloc (noflash)
multi_heap_poisoning:multi_heap_malloc (noflash)
multi_heap_poisoning:multi_heap_free (noflash)
multi_heap_poisoning:multi_heap_aligned_free (noflash)
multi_heap_poisoning:multi_heap_realloc (noflash)
multi_heap_poisoning:multi_heap_get_block_address (noflash)
multi_heap_poisoning:multi_heap_get_block_owner (noflash)
multi_heap_poisoning:multi_heap_get_allocated_size (noflash)
multi_heap_poisoning:multi_heap_internal_check_block_poisoning (noflash)
multi_heap_poisoning:multi_heap_internal_poison_fill_region (noflash)

if HEAP_POISONING_COMPREHENSIVE = y:
multi_heap_poisoning:verify_fill_pattern (noflash)
multi_heap_poisoning:block_absorb_post_hook (noflash)
46 changes: 16 additions & 30 deletions components/heap/multi_heap.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,20 +105,8 @@ void multi_heap_in_rom_init(void)

#else // CONFIG_HEAP_TLSF_USE_ROM_IMPL

/* Return true if this block is free. */
static inline bool is_free(const block_header_t *block)
{
return ((block->size & 0x01) != 0);
}

/* Data size of the block (excludes this block's header) */
static inline size_t block_data_size(const block_header_t *block)
{
return (block->size & ~0x03);
}

/* Check a block is valid for this heap. Used to verify parameters. */
static void assert_valid_block(const heap_t *heap, const block_header_t *block)
__attribute__((noinline)) NOCLONE_ATTR static void assert_valid_block(const heap_t *heap, const block_header_t *block)
{
pool_t pool = tlsf_get_pool(heap->heap_data);
void *ptr = block_to_ptr(block);
Expand All @@ -130,8 +118,7 @@ static void assert_valid_block(const heap_t *heap, const block_header_t *block)

void *multi_heap_get_block_address_impl(multi_heap_block_handle_t block)
{
void *ptr = block_to_ptr(block);
return (ptr);
return block_to_ptr(block);
}

size_t multi_heap_get_allocated_size_impl(multi_heap_handle_t heap, void *p)
Expand All @@ -142,21 +129,24 @@ size_t multi_heap_get_allocated_size_impl(multi_heap_handle_t heap, void *p)
multi_heap_handle_t multi_heap_register_impl(void *start_ptr, size_t size)
{
assert(start_ptr);
if(size < (tlsf_size() + tlsf_block_size_min() + sizeof(heap_t))) {
if(size < (sizeof(heap_t))) {
//Region too small to be a heap.
return NULL;
}

heap_t *result = (heap_t *)start_ptr;
size -= sizeof(heap_t);

result->heap_data = tlsf_create_with_pool(start_ptr + sizeof(heap_t), size);
/* Do not specify any maximum size for the allocations so that the default configuration is used */
const size_t max_bytes = 0;

result->heap_data = tlsf_create_with_pool(start_ptr + sizeof(heap_t), size, max_bytes);
if(!result->heap_data) {
return NULL;
}

result->lock = NULL;
result->free_bytes = size - tlsf_size();
result->free_bytes = size - tlsf_size(result->heap_data);
result->pool_size = size;
result->minimum_free_bytes = result->free_bytes;
return result;
Expand All @@ -167,12 +157,12 @@ void multi_heap_set_lock(multi_heap_handle_t heap, void *lock)
heap->lock = lock;
}

void inline multi_heap_internal_lock(multi_heap_handle_t heap)
void multi_heap_internal_lock(multi_heap_handle_t heap)
{
MULTI_HEAP_LOCK(heap->lock);
}

void inline multi_heap_internal_unlock(multi_heap_handle_t heap)
void multi_heap_internal_unlock(multi_heap_handle_t heap)
{
MULTI_HEAP_UNLOCK(heap->lock);
}
Expand All @@ -192,7 +182,7 @@ multi_heap_block_handle_t multi_heap_get_next_block(multi_heap_handle_t heap, mu
assert_valid_block(heap, block);
block_header_t* next = block_next(block);

if(block_data_size(next) == 0) {
if(block_size(next) == 0) {
//Last block:
return NULL;
} else {
Expand All @@ -203,7 +193,7 @@ multi_heap_block_handle_t multi_heap_get_next_block(multi_heap_handle_t heap, mu

bool multi_heap_is_free(multi_heap_block_handle_t block)
{
return is_free(block);
return block_is_free(block);
}

void *multi_heap_malloc_impl(multi_heap_handle_t heap, size_t size)
Expand Down Expand Up @@ -361,7 +351,7 @@ bool multi_heap_check(multi_heap_handle_t heap, bool print_errors)
return valid;
}

static void multi_heap_dump_tlsf(void* ptr, size_t size, int used, void* user)
__attribute__((noinline)) static void multi_heap_dump_tlsf(void* ptr, size_t size, int used, void* user)
{
(void)user;
MULTI_HEAP_STDERR_PRINTF("Block %p data, size: %d bytes, Free: %s \n",
Expand Down Expand Up @@ -398,7 +388,7 @@ size_t multi_heap_minimum_free_size_impl(multi_heap_handle_t heap)
return heap->minimum_free_bytes;
}

static void multi_heap_get_info_tlsf(void* ptr, size_t size, int used, void* user)
__attribute__((noinline)) static void multi_heap_get_info_tlsf(void* ptr, size_t size, int used, void* user)
{
multi_heap_info_t *info = user;

Expand All @@ -417,7 +407,6 @@ static void multi_heap_get_info_tlsf(void* ptr, size_t size, int used, void* use

void multi_heap_get_info_impl(multi_heap_handle_t heap, multi_heap_info_t *info)
{
uint32_t sl_interval;
uint32_t overhead;

memset(info, 0, sizeof(multi_heap_info_t));
Expand All @@ -431,13 +420,10 @@ void multi_heap_get_info_impl(multi_heap_handle_t heap, multi_heap_info_t *info)
/* TLSF has an overhead per block. Calculate the total amount of overhead, it shall not be
* part of the allocated bytes */
overhead = info->allocated_blocks * tlsf_alloc_overhead();
info->total_allocated_bytes = (heap->pool_size - tlsf_size()) - heap->free_bytes - overhead;
info->total_allocated_bytes = (heap->pool_size - tlsf_size(heap->heap_data)) - heap->free_bytes - overhead;
info->minimum_free_bytes = heap->minimum_free_bytes;
info->total_free_bytes = heap->free_bytes;
if (info->largest_free_block) {
sl_interval = (1 << (31 - __builtin_clz(info->largest_free_block))) / SL_INDEX_COUNT;
info->largest_free_block = info->largest_free_block & ~(sl_interval - 1);
}
info->largest_free_block = tlsf_fit_size(heap->heap_data, info->largest_free_block);
multi_heap_internal_unlock(heap);
}
#endif
8 changes: 8 additions & 0 deletions components/heap/multi_heap_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
*/
#pragma once

/* Define a noclone attribute when compiled with GCC as certain functions
* in the heap component should not be cloned by the compiler */
#if defined __has_attribute && __has_attribute(noclone)
#define NOCLONE_ATTR __attribute((noclone))
#else
#define NOCLONE_ATTR
#endif

/* Define a structure that contains some function pointers that point to OS-related functions.
An instance of this structure will be provided to the heap in ROM for use if needed.
*/
Expand Down
14 changes: 10 additions & 4 deletions components/heap/multi_heap_poisoning.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ typedef struct {
Returns the pointer to the actual usable data buffer (ie after 'head')
*/
static uint8_t *poison_allocated_region(poison_head_t *head, size_t alloc_size)
__attribute__((noinline)) static uint8_t *poison_allocated_region(poison_head_t *head, size_t alloc_size)
{
uint8_t *data = (uint8_t *)(&head[1]); /* start of data ie 'real' allocated buffer */
poison_tail_t *tail = (poison_tail_t *)(data + alloc_size);
Expand All @@ -90,7 +90,7 @@ static uint8_t *poison_allocated_region(poison_head_t *head, size_t alloc_size)
Returns a pointer to the poison header structure, or NULL if the poison structures are corrupt.
*/
static poison_head_t *verify_allocated_region(void *data, bool print_errors)
__attribute__((noinline)) static poison_head_t *verify_allocated_region(void *data, bool print_errors)
{
poison_head_t *head = (poison_head_t *)((intptr_t)data - sizeof(poison_head_t));
poison_tail_t *tail = (poison_tail_t *)((intptr_t)data + head->alloc_size);
Expand Down Expand Up @@ -132,8 +132,12 @@ static poison_head_t *verify_allocated_region(void *data, bool print_errors)
if swap_pattern is true, swap patterns in the buffer (ie replace MALLOC_FILL_PATTERN with FREE_FILL_PATTERN, and vice versa.)
Returns true if verification checks out.
This function has the attribute noclone to prevent the compiler to create a clone on flash where expect_free is removed (as this
function is called only with expect_free == true throughout the component).
*/
static bool verify_fill_pattern(void *data, size_t size, bool print_errors, bool expect_free, bool swap_pattern)
__attribute__((noinline)) NOCLONE_ATTR
static bool verify_fill_pattern(void *data, size_t size, const bool print_errors, const bool expect_free, bool swap_pattern)
{
const uint32_t FREE_FILL_WORD = (FREE_FILL_PATTERN << 24) | (FREE_FILL_PATTERN << 16) | (FREE_FILL_PATTERN << 8) | FREE_FILL_PATTERN;
const uint32_t MALLOC_FILL_WORD = (MALLOC_FILL_PATTERN << 24) | (MALLOC_FILL_PATTERN << 16) | (MALLOC_FILL_PATTERN << 8) | MALLOC_FILL_PATTERN;
Expand Down Expand Up @@ -259,7 +263,9 @@ void *multi_heap_malloc(multi_heap_handle_t heap, size_t size)
return data;
}

void multi_heap_free(multi_heap_handle_t heap, void *p)
/* This function has the noclone attribute to prevent the compiler to optimize out the
* check for p == NULL and create a clone function placed in flash. */
NOCLONE_ATTR void multi_heap_free(multi_heap_handle_t heap, void *p)
{
if (p == NULL) {
return;
Expand Down
15 changes: 15 additions & 0 deletions components/heap/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,18 @@ idf_component_register(SRC_DIRS "."
PRIV_INCLUDE_DIRS "."
PRIV_REQUIRES cmock test_utils heap spi_flash)
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

if(CONFIG_COMPILER_DUMP_RTL_FILES)
idf_build_get_property(elf_file_name EXECUTABLE GENERATOR_EXPRESSION)
add_custom_target(check_test_app_sections ALL
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
--rtl-dir ${CMAKE_BINARY_DIR}/esp-idf/heap/
--elf-file ${CMAKE_BINARY_DIR}/${elf_file_name}
find-refs
--from-sections=.iram0.text
--to-sections=.flash.text,.flash.rodata
--ignore-symbols=__func__/__assert_func,__func__/heap_caps_alloc_failed
--exit-code
DEPENDS ${elf_file_name}
)
endif()
Loading

0 comments on commit c173845

Please sign in to comment.