diff --git a/components/esp_rom/esp32s2/ld/esp32s2.rom.newlib-funcs.ld b/components/esp_rom/esp32s2/ld/esp32s2.rom.newlib-funcs.ld index c859f655b4..fc976bbbaf 100644 --- a/components/esp_rom/esp32s2/ld/esp32s2.rom.newlib-funcs.ld +++ b/components/esp_rom/esp32s2/ld/esp32s2.rom.newlib-funcs.ld @@ -10,8 +10,8 @@ abs = 0x40000618; __ascii_mbtowc = 0x40007a04; __ascii_wctomb = 0x400018d0; -__assert = 0x4001a430; -__assert_func = 0x4001a408; +PROVIDE ( __assert = 0x4001a430 ); +PROVIDE ( __assert_func = 0x4001a408 ); bzero = 0x400078c8; _cleanup_r = 0x4001a480; creat = 0x4000788c; diff --git a/components/newlib/CMakeLists.txt b/components/newlib/CMakeLists.txt index 924a27212b..077db45299 100644 --- a/components/newlib/CMakeLists.txt +++ b/components/newlib/CMakeLists.txt @@ -6,6 +6,7 @@ endif() set(srcs "abort.c" + "assert.c" "heap.c" "locks.c" "poll.c" @@ -27,7 +28,7 @@ list(APPEND ldfragments newlib.lf) idf_component_register(SRCS "${srcs}" INCLUDE_DIRS "${include_dirs}" PRIV_INCLUDE_DIRS priv_include - PRIV_REQUIRES soc + PRIV_REQUIRES soc spi_flash LDFRAGMENTS "${ldfragments}") # Toolchain libraries require code defined in this component @@ -36,11 +37,12 @@ target_link_libraries(${COMPONENT_LIB} INTERFACE c m gcc "$ +#include +#include "esp_system.h" +#include "esp_spi_flash.h" +#include "soc/soc_memory_layout.h" + +#define ASSERT_STR "assert failed: " +#define CACHE_DISABLED_STR "" + +#if __XTENSA__ +#define INST_LEN 3 +#elif __riscv +#define INST_LEN 4 +#endif + +static inline void ra_to_str(char *addr) +{ + addr[0] = '0'; + addr[1] = 'x'; + itoa((uint32_t)(__builtin_return_address(0) - INST_LEN), addr + 2, 16); +} + +/* Overriding assert function so that whenever assert is called from critical section, + * it does not lead to a crash of its own. + */ +void __attribute__((noreturn)) __assert_func(const char *file, int line, const char *func, const char *expr) +{ +#if CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT + char buff[sizeof(ASSERT_STR) + 11 + 1] = ASSERT_STR; + + ra_to_str(&buff[sizeof(ASSERT_STR) - 1]); + + esp_system_abort(buff); +#else + char addr[11] = { 0 }; + char buff[200]; + char lbuf[5]; + uint32_t rem_len = sizeof(buff) - 1; + uint32_t off = 0; + + itoa(line, lbuf, 10); + + if (!spi_flash_cache_enabled()) { + if (esp_ptr_in_drom(file)) { + file = CACHE_DISABLED_STR; + } + + if (esp_ptr_in_drom(func)) { + ra_to_str(addr); + func = addr; + } + + if (esp_ptr_in_drom(expr)) { + expr = CACHE_DISABLED_STR; + } + } + + const char *str[] = {ASSERT_STR, func ? func : "\b", " ", file, ":", lbuf, " (", expr, ")"}; + + for (int i = 0; i < sizeof(str) / sizeof(str[0]); i++) { + uint32_t len = strlen(str[i]); + uint32_t cpy_len = MIN(len, rem_len); + memcpy(buff + off, str[i], cpy_len); + rem_len -= cpy_len; + off += cpy_len; + if (rem_len == 0) { + break; + } + } + buff[off] = '\0'; + esp_system_abort(buff); +#endif /* CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT */ +} + +void __attribute__((noreturn)) __assert(const char *file, int line, const char *failedexpr) +{ + __assert_func(file, line, NULL, failedexpr); +} + +/* No-op function, used to force linker to include these changes */ +void newlib_include_assert_impl(void) +{ +} diff --git a/components/newlib/component.mk b/components/newlib/component.mk index 5f79c910e7..4cca270a4c 100644 --- a/components/newlib/component.mk +++ b/components/newlib/component.mk @@ -15,10 +15,12 @@ endif COMPONENT_PRIV_INCLUDEDIRS := priv_include COMPONENT_SRCDIRS := . port -# Forces the linker to include heap, syscalls, and pthread from this component, +# Forces the linker to include heap, syscalls, pthread, and assert from this component, # instead of the implementations provided by newlib. COMPONENT_ADD_LDFLAGS += -u newlib_include_heap_impl COMPONENT_ADD_LDFLAGS += -u newlib_include_syscalls_impl +COMPONENT_ADD_LDFLAGS += -u newlib_include_pthread_impl +COMPONENT_ADD_LDFLAGS += -u newlib_include_assert_impl COMPONENT_ADD_LDFRAGMENTS += newlib.lf diff --git a/components/newlib/newlib.lf b/components/newlib/newlib.lf index 63a93164a2..5f1c382f8d 100644 --- a/components/newlib/newlib.lf +++ b/components/newlib/newlib.lf @@ -3,3 +3,4 @@ archive: libnewlib.a entries: heap (noflash) abort (noflash) + assert (noflash) diff --git a/components/newlib/platform_include/assert.h b/components/newlib/platform_include/assert.h index 77d4cf08f5..39db39a6f0 100644 --- a/components/newlib/platform_include/assert.h +++ b/components/newlib/platform_include/assert.h @@ -19,23 +19,34 @@ #pragma once #include #include -#include "esp_compiler.h" +#include #include_next -#if defined(CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT) && !defined(NDEBUG) - #undef assert - #define assert(__e) (likely(__e)) ? (void)0 : abort() -#else - /* moved part of toolchain provided assert to there then - * we can tweak the original assert macro to perform likely - * before deliver it to original toolchain implementation - */ - #undef assert - #ifdef NDEBUG - # define assert(__e) ((void)0) - #else - # define assert(__e) (likely(__e) ? (void)0 : __assert_func (__FILE__, __LINE__, \ - __ASSERT_FUNC, #__e)) - #endif +/* moved part of libc provided assert to here allows + * tweaking the assert macro to use __builtin_expect() + * and reduce jumps in the "asserts OK" code path + * + * Note: using __builtin_expect() not likely() to avoid defining the likely + * macro in namespace of non-IDF code that may include this standard header. + */ +#undef assert + +/* __FILENAME__ points to the file name instead of path + filename + * e.g __FILE__ points to "/apps/test.c" where as __FILENAME__ points to "test.c" + */ +#define __FILENAME__ (__builtin_strrchr( "/" __FILE__, '/') + 1) + +#if defined(NDEBUG) + +#define assert(__e) ((void)(__e)) + +#elif CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT + +#define assert(__e) (__builtin_expect(!!(__e), 1) ? (void)0 : __assert_func(NULL, 0, NULL, NULL)) + +#else // !CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT + +#define assert(__e) (__builtin_expect(!!(__e), 1) ? (void)0 : __assert_func (__FILENAME__, __LINE__, \ + __ASSERT_FUNC, #__e)) #endif