mirror of
https://github.com/esphome/esphome.git
synced 2026-10-14 18:22:48 +00:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75dd7b1a94 | ||
|
|
4cb335934e | ||
|
|
736f6b53d2 | ||
|
|
a715bdae7d | ||
|
|
fe151ba802 | ||
|
|
5da9cbdefa | ||
|
|
016ee8a62d | ||
|
|
51a8a56335 |
@@ -2949,6 +2949,9 @@ async def to_code(config):
|
||||
"_ZSt25__throw_bad_function_callv",
|
||||
]:
|
||||
cg.add_build_flag(f"-Wl,--wrap={mangled}")
|
||||
# Replace operator new so nothrow new returns nullptr. See operator_new.cpp.
|
||||
# --undefined pulls it from libsrc.a before libstdc++ (_Znwj: 32 bit size_t on every ESP32).
|
||||
cg.add_build_flag("-Wl,--undefined=_Znwj")
|
||||
|
||||
# Wrap FILE*-based printf functions to eliminate newlib's _vfprintf_r
|
||||
# (~11 KB). See printf_stubs.cpp for implementation.
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Replaces operator new for ESP-IDF builds without C++ exceptions. libstdc++'s nothrow forms call the
|
||||
* throwing form and catch std::bad_alloc, but the throw aborts here, so they abort instead of returning
|
||||
* nullptr. All forms stay in this file: --undefined=_Znwj pulls it in ahead of libstdc++.
|
||||
*/
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#ifdef USE_ESP_IDF
|
||||
#include <sdkconfig.h>
|
||||
#ifndef CONFIG_COMPILER_CXX_EXCEPTIONS
|
||||
|
||||
#include <cstdlib>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include "esp_system.h"
|
||||
|
||||
#ifdef __cpp_exceptions
|
||||
#error "CONFIG_COMPILER_CXX_EXCEPTIONS no longer matches -fexceptions"
|
||||
#endif
|
||||
|
||||
// libstdc++'s aligned delete calls free() only when built with one of these
|
||||
#if !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_GLIBCXX_HAVE_POSIX_MEMALIGN) && !defined(_GLIBCXX_HAVE_MEMALIGN)
|
||||
#error "libstdc++'s aligned operator delete no longer frees with free()"
|
||||
#endif
|
||||
|
||||
#ifndef CLANG_TIDY // clang-tidy parses Xtensa with a 64 bit host target
|
||||
// --undefined=_Znwj in __init__.py relies on this mangling
|
||||
static_assert(std::is_same_v<std::size_t, unsigned int>, "update --undefined=_Znwj");
|
||||
#endif
|
||||
|
||||
namespace esphome::esp32 {} // namespace esphome::esp32
|
||||
|
||||
namespace {
|
||||
|
||||
// Standard new_handler retry loop; returns nullptr once no handler is installed
|
||||
template<typename Alloc> void *alloc_or_null(std::size_t size, Alloc alloc) {
|
||||
// malloc(0) may return NULL, but operator new must return a unique pointer
|
||||
if (size == 0)
|
||||
size = 1;
|
||||
for (;;) {
|
||||
void *ptr = alloc(size);
|
||||
if (ptr != nullptr)
|
||||
return ptr;
|
||||
std::new_handler handler = std::get_new_handler();
|
||||
if (handler == nullptr)
|
||||
return nullptr;
|
||||
handler();
|
||||
}
|
||||
}
|
||||
|
||||
void *abort_if_null(void *ptr) {
|
||||
if (ptr == nullptr)
|
||||
esp_system_abort("std::bad_alloc");
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *plain_alloc(std::size_t size) {
|
||||
return alloc_or_null(size, [](std::size_t n) { return std::malloc(n); }); // NOLINT(cppcoreguidelines-no-malloc)
|
||||
}
|
||||
|
||||
// IDF's aligned_alloc accepts any size (no C11 multiple of alignment rule) and pairs with free(),
|
||||
// which libstdc++'s aligned delete uses (checked above)
|
||||
void *aligned_alloc_or_null(std::size_t size, std::align_val_t align) {
|
||||
return alloc_or_null(size, [align](std::size_t n) { return ::aligned_alloc(static_cast<std::size_t>(align), n); });
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// libstdc++'s operator delete (free) already matches these
|
||||
// NOLINTBEGIN(cert-dcl54-cpp,misc-new-delete-overloads)
|
||||
void *operator new(std::size_t size) { return abort_if_null(plain_alloc(size)); }
|
||||
void *operator new[](std::size_t size) { return abort_if_null(plain_alloc(size)); }
|
||||
void *operator new(std::size_t size, const std::nothrow_t & /*tag*/) noexcept { return plain_alloc(size); }
|
||||
void *operator new[](std::size_t size, const std::nothrow_t & /*tag*/) noexcept { return plain_alloc(size); }
|
||||
|
||||
void *operator new(std::size_t size, std::align_val_t align) {
|
||||
return abort_if_null(aligned_alloc_or_null(size, align));
|
||||
}
|
||||
void *operator new[](std::size_t size, std::align_val_t align) {
|
||||
return abort_if_null(aligned_alloc_or_null(size, align));
|
||||
}
|
||||
void *operator new(std::size_t size, std::align_val_t align, const std::nothrow_t & /*tag*/) noexcept {
|
||||
return aligned_alloc_or_null(size, align);
|
||||
}
|
||||
void *operator new[](std::size_t size, std::align_val_t align, const std::nothrow_t & /*tag*/) noexcept {
|
||||
return aligned_alloc_or_null(size, align);
|
||||
}
|
||||
// NOLINTEND(cert-dcl54-cpp,misc-new-delete-overloads)
|
||||
|
||||
#endif // CONFIG_COMPILER_CXX_EXCEPTIONS
|
||||
#endif // USE_ESP_IDF
|
||||
+2
-3
@@ -1257,9 +1257,8 @@ def lint_no_std_bind(fname, match):
|
||||
)
|
||||
def lint_no_std_nothrow(fname, match):
|
||||
return (
|
||||
f"{highlight('new (std::nothrow)')} aborts on ESP-IDF when the allocation fails, exceptions are disabled "
|
||||
f"there, so it never returns nullptr.\n"
|
||||
f"Please use {highlight('RAMAllocator')} from esphome/core/helpers.h, which does.\n"
|
||||
f"Use {highlight('RAMAllocator')} from esphome/core/helpers.h instead of {highlight('new (std::nothrow)')}; "
|
||||
f"it returns nullptr on failure on every platform and can choose PSRAM or internal RAM.\n"
|
||||
f" Before: {highlight('auto *buf = new (std::nothrow) uint8_t[n];')}\n"
|
||||
f" After: {highlight('auto buf = RAMAllocator<uint8_t>().make_unique_array_for_overwrite(n);')}\n"
|
||||
f"For one object use {highlight('RAMAllocator<T>().make_unique(args...)')}; both return empty on failure.\n"
|
||||
|
||||
Reference in New Issue
Block a user