Bläddra i källkod

newlib: Override __assert and __assert_func

Default assert implementation calls fiprintf, which tries to acquire a
lock and fails if it is executing in critical section or ISR
Sachin Parekh 4 år sedan
förälder
incheckning
41973b761e

+ 2 - 2
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;

+ 4 - 2
components/newlib/CMakeLists.txt

@@ -6,6 +6,7 @@ endif()
 
 set(srcs
     "abort.c"
+    "assert.c"
     "heap.c"
     "locks.c"
     "poll.c"
@@ -28,7 +29,7 @@ list(APPEND ldfragments "newlib.lf" "system_libs.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
@@ -37,11 +38,12 @@ target_link_libraries(${COMPONENT_LIB} INTERFACE c m gcc "$<TARGET_FILE:${newlib
 
 set_source_files_properties(heap.c PROPERTIES COMPILE_FLAGS -fno-builtin)
 
-# Forces the linker to include heap, syscall, pthread and retargetable locks from this component,
+# Forces the linker to include heap, syscall, pthread, assert, and retargetable locks from this component,
 # instead of the implementations provided by newlib.
 list(APPEND EXTRA_LINK_FLAGS "-u newlib_include_heap_impl")
 list(APPEND EXTRA_LINK_FLAGS "-u newlib_include_syscalls_impl")
 list(APPEND EXTRA_LINK_FLAGS "-u newlib_include_pthread_impl")
+list(APPEND EXTRA_LINK_FLAGS "-u newlib_include_assert_impl")
 target_link_libraries(${COMPONENT_LIB} INTERFACE "${EXTRA_LINK_FLAGS}")
 
 if(CONFIG_NEWLIB_NANO_FORMAT)

+ 97 - 0
components/newlib/assert.c

@@ -0,0 +1,97 @@
+// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string.h>
+#include <sys/param.h>
+#include "esp_system.h"
+#include "esp_spi_flash.h"
+#include "soc/soc_memory_layout.h"
+
+#define ASSERT_STR              "assert failed: "
+#define CACHE_DISABLED_STR      "<cached disabled>"
+
+#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)
+{
+}

+ 3 - 1
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 system_libs.lf
 

+ 1 - 0
components/newlib/newlib.lf

@@ -3,4 +3,5 @@ archive: libnewlib.a
 entries:
   heap (noflash)
   abort (noflash)
+  assert (noflash)
   stdatomic (noflash)

+ 9 - 3
components/newlib/platform_include/assert.h

@@ -19,6 +19,7 @@
 #pragma once
 #include <sdkconfig.h>
 #include <stdlib.h>
+#include <stdint.h>
 
 #include_next <assert.h>
 
@@ -31,16 +32,21 @@
  */
 #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))
+#define assert(__e) ((void)(__e))
 
 #elif CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT
 
-#define assert(__e) __builtin_expect(!!(__e), 1) ? (void)0 : abort()
+#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 (__FILE__, __LINE__, \
+#define assert(__e) (__builtin_expect(!!(__e), 1) ? (void)0 : __assert_func (__FILENAME__, __LINE__, \
                                                                              __ASSERT_FUNC, #__e))
 #endif