mirror of
https://github.com/esphome/esphome.git
synced 2026-09-11 15:27:33 +00:00
[esp32] Intercept the Log V2 formatter with linker wrap
A plain strong definition of esp_log_format collides with liblog's copy on IDF 6.1: log.c.obj references the symbol and the linker resolves it from log_format_text.c.obj inside the same archive before reaching ESPHome's archive, producing a duplicate definition error. Use -Wl,--wrap=esp_log_format and define __wrap_esp_log_format instead, which also lets the unused liblog formatter be dead stripped.
This commit is contained in:
@@ -2697,6 +2697,10 @@ async def to_code(config):
|
||||
add_idf_sdkconfig_option("CONFIG_LOG_VERSION_2", True)
|
||||
add_idf_sdkconfig_option("CONFIG_LOG_API_CONSTRAINED_ENV_SAFE", False)
|
||||
cg.add_define("USE_ESP32_LOG_V2")
|
||||
# Intercept liblog's formatter via --wrap; a plain strong definition
|
||||
# collides instead of overriding because liblog resolves the symbol
|
||||
# within its own archive (see core/log.cpp).
|
||||
cg.add_build_flag("-Wl,--wrap=esp_log_format")
|
||||
|
||||
# Reduce PHY TX power in the event of a brownout
|
||||
add_idf_sdkconfig_option("CONFIG_ESP_PHY_REDUCE_TX_POWER", True)
|
||||
|
||||
+13
-9
@@ -85,8 +85,11 @@ int HOT esp_idf_log_vprintf_(const char *format, va_list args) { // NOLINT
|
||||
// Override esp_log_format to prevent V2's 3-call vprintf fragmentation.
|
||||
// Without this, Log V2 calls the vprintf hook 3 times per message (header,
|
||||
// body, newline) which creates 3 separate log entries in ESPHome's logger.
|
||||
// This strong definition overrides the archive symbol from ESP-IDF's liblog,
|
||||
// affecting all callers including precompiled blobs (e.g. wifi).
|
||||
// Interception uses the linker's --wrap (added in esp32/__init__.py when Log
|
||||
// V2 is enabled): a plain strong definition cannot win here because liblog's
|
||||
// log.c.obj references esp_log_format and the linker resolves it from
|
||||
// log_format_text.c.obj within the same archive, before ever reaching
|
||||
// ESPHome's archive, which then collides as a duplicate definition.
|
||||
#include <esp_private/log_message.h>
|
||||
#include <esp_log_write.h>
|
||||
|
||||
@@ -136,13 +139,14 @@ static void __attribute__((noinline)) esp_log_format_direct_(esp_log_msg_t *mess
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
// Override esp_log_format from liblog.a to prevent V2's 3-call vprintf
|
||||
// fragmentation. Deliberately NOT IRAM_ATTR: every caller that must work with
|
||||
// flash cache disabled (ESP_DRAM_LOGx, ESP_EARLY_LOGx) bypasses esp_log()
|
||||
// entirely under CONFIG_LOG_API_CONSTRAINED_ENV_SAFE=n, and both paths below
|
||||
// immediately call flash-resident code anyway, so IRAM placement would only
|
||||
// spend the IRAM this change exists to save.
|
||||
void esp_log_format(esp_log_msg_t *message) {
|
||||
// Wrap of esp_log_format from liblog.a (via -Wl,--wrap=esp_log_format) to
|
||||
// prevent V2's 3-call vprintf fragmentation. Deliberately NOT IRAM_ATTR: every
|
||||
// caller that must work with flash cache disabled (ESP_DRAM_LOGx,
|
||||
// ESP_EARLY_LOGx) bypasses esp_log() entirely under
|
||||
// CONFIG_LOG_API_CONSTRAINED_ENV_SAFE=n, and both paths below immediately call
|
||||
// flash-resident code anyway, so IRAM placement would only spend the IRAM this
|
||||
// change exists to save.
|
||||
void __wrap_esp_log_format(esp_log_msg_t *message) { // NOLINT
|
||||
extern vprintf_like_t esp_log_vprint_func;
|
||||
extern int vprintf(const char *, __gnuc_va_list); // NOLINT
|
||||
if (esp_log_vprint_func == &vprintf || message->config.opts.constrained_env) [[unlikely]] {
|
||||
|
||||
Reference in New Issue
Block a user