From 1912dcf03da6f0d1590f3708cf8ddad9b404ef04 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 26 Feb 2026 12:07:42 -0700 Subject: [PATCH] [core] Use placement new for global Application instance (#14052) --- esphome/core/application.cpp | 10 +++++++++- esphome/core/config.py | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index fd6a14b50f..a9753da1b5 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -713,7 +713,15 @@ void Application::yield_with_select_(uint32_t delay_ms) { #endif } -Application App; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +// App storage — asm label shares the linker symbol with "extern Application App". +// char[] is trivially destructible, so no __cxa_atexit or destructor chain is emitted. +// Constructed via placement new in the generated setup(). +#ifndef __GXX_ABI_VERSION +#error "Application placement new requires Itanium C++ ABI (GCC/Clang)" +#endif +static_assert(std::is_default_constructible::value, "Application must be default-constructible"); +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +alignas(Application) char app_storage[sizeof(Application)] asm("_ZN7esphome3AppE"); #if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE) diff --git a/esphome/core/config.py b/esphome/core/config.py index 21ed8ced1a..215432835a 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -512,6 +512,9 @@ async def to_code(config: ConfigType) -> None: cg.add_global(cg.RawExpression("using std::min")) cg.add_global(cg.RawExpression("using std::max")) + # Construct App via placement new — see application.cpp for storage details + cg.add_global(cg.RawStatement("#include ")) + cg.add(cg.RawExpression("new (&App) Application()")) cg.add( cg.App.pre_setup( config[CONF_NAME],