mirror of
https://github.com/esphome/esphome.git
synced 2026-05-21 02:01:57 +08:00
[rp2040] Wrap printf/vprintf/fprintf to eliminate _vfprintf_r (~9.2 KB flash) (#14622)
This commit is contained in:
@@ -21,7 +21,13 @@ from esphome.const import (
|
||||
from esphome.core import CORE, CoroPriority, EsphomeError, coroutine_with_priority
|
||||
from esphome.helpers import copy_file_if_changed, read_file, write_file_if_changed
|
||||
|
||||
from .const import KEY_BOARD, KEY_PIO_FILES, KEY_RP2040, rp2040_ns
|
||||
from .const import (
|
||||
CONF_ENABLE_FULL_PRINTF,
|
||||
KEY_BOARD,
|
||||
KEY_PIO_FILES,
|
||||
KEY_RP2040,
|
||||
rp2040_ns,
|
||||
)
|
||||
|
||||
# force import gpio to register pin schema
|
||||
from .gpio import rp2040_pin_to_code # noqa
|
||||
@@ -153,6 +159,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.positive_time_period_milliseconds,
|
||||
cv.Range(max=cv.TimePeriod(milliseconds=8388)),
|
||||
),
|
||||
cv.Optional(CONF_ENABLE_FULL_PRINTF, default=False): cv.boolean,
|
||||
}
|
||||
),
|
||||
set_core_data,
|
||||
@@ -190,6 +197,14 @@ async def to_code(config):
|
||||
],
|
||||
)
|
||||
|
||||
# Wrap FILE*-based printf functions to eliminate newlib's _vfprintf_r
|
||||
# (~9.2 KB). See printf_stubs.cpp for implementation.
|
||||
if config.get(CONF_ENABLE_FULL_PRINTF):
|
||||
cg.add_define("USE_FULL_PRINTF")
|
||||
else:
|
||||
for symbol in ("vprintf", "printf", "fprintf"):
|
||||
cg.add_build_flag(f"-Wl,--wrap={symbol}")
|
||||
|
||||
cg.add_platformio_option("board_build.core", "earlephilhower")
|
||||
cg.add_platformio_option("board_build.filesystem_size", "1m")
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import esphome.codegen as cg
|
||||
|
||||
CONF_ENABLE_FULL_PRINTF = "enable_full_printf"
|
||||
KEY_BOARD = "board"
|
||||
KEY_RP2040 = "rp2040"
|
||||
KEY_PIO_FILES = "pio_files"
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Linker wrap stubs for FILE*-based printf functions.
|
||||
*
|
||||
* The RP2040 Arduino framework and libraries may reference printf(),
|
||||
* vprintf(), and fprintf() which pull in newlib's _vfprintf_r (~8.9 KB).
|
||||
* ESPHome never uses these — all logging goes through the logger component
|
||||
* which uses snprintf/vsnprintf, so the libc FILE*-based printf path is
|
||||
* dead code.
|
||||
*
|
||||
* These stubs redirect through vsnprintf() (which is already in the binary)
|
||||
* and fwrite(), allowing the linker to dead-code eliminate _vfprintf_r.
|
||||
*
|
||||
* Saves ~8.9 KB of flash.
|
||||
*/
|
||||
|
||||
#if defined(USE_RP2040) && !defined(USE_FULL_PRINTF)
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace esphome::rp2040 {}
|
||||
|
||||
static constexpr size_t PRINTF_BUFFER_SIZE = 512;
|
||||
|
||||
// These stubs are essentially dead code at runtime — ESPHome uses its own
|
||||
// logging through snprintf/vsnprintf, not libc printf.
|
||||
// The buffer overflow check is purely defensive and should never trigger.
|
||||
static int write_printf_buffer(FILE *stream, char *buf, int len) {
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
size_t write_len = len;
|
||||
if (write_len >= PRINTF_BUFFER_SIZE) {
|
||||
fwrite(buf, 1, PRINTF_BUFFER_SIZE - 1, stream);
|
||||
// Use fwrite for the message to avoid recursive __wrap_printf call
|
||||
static const char msg[] = "\nprintf buffer overflow\n";
|
||||
fwrite(msg, 1, sizeof(msg) - 1, stream);
|
||||
abort();
|
||||
}
|
||||
if (fwrite(buf, 1, write_len, stream) < write_len || ferror(stream)) {
|
||||
return -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
// NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
|
||||
extern "C" {
|
||||
|
||||
int __wrap_vprintf(const char *fmt, va_list ap) {
|
||||
char buf[PRINTF_BUFFER_SIZE];
|
||||
return write_printf_buffer(stdout, buf, vsnprintf(buf, sizeof(buf), fmt, ap));
|
||||
}
|
||||
|
||||
int __wrap_printf(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int len = __wrap_vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
return len;
|
||||
}
|
||||
|
||||
int __wrap_fprintf(FILE *stream, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
char buf[PRINTF_BUFFER_SIZE];
|
||||
int len = write_printf_buffer(stream, buf, vsnprintf(buf, sizeof(buf), fmt, ap));
|
||||
va_end(ap);
|
||||
return len;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
// NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
|
||||
|
||||
#endif // USE_RP2040 && !USE_FULL_PRINTF
|
||||
@@ -1,3 +1,6 @@
|
||||
rp2040:
|
||||
enable_full_printf: false
|
||||
|
||||
logger:
|
||||
level: VERBOSE
|
||||
|
||||
|
||||
Reference in New Issue
Block a user