ESPHome 2026.4.0-dev
Loading...
Searching...
No Matches
printf_stubs.cpp
Go to the documentation of this file.
1/*
2 * Linker wrap stubs for FILE*-based printf functions.
3 *
4 * ESP-IDF SDK components (gpio driver, ringbuf, log_write) reference
5 * fprintf(), printf(), vprintf(), and vfprintf() which pull in the full
6 * printf implementation (~11 KB on newlib's _vfprintf_r, ~2.8 KB on
7 * picolibc's vfprintf). This is a separate implementation from the one
8 * used by snprintf/vsnprintf that handles FILE* stream I/O with buffering
9 * and locking.
10 *
11 * ESPHome replaces the ESP-IDF log handler via esp_log_set_vprintf_(),
12 * so the SDK's vprintf() path is dead code at runtime. The fprintf()
13 * and printf() calls in SDK components are only in debug/assert paths
14 * (gpio_dump_io_configuration, ringbuf diagnostics) that are either
15 * GC'd or never called. Crash backtraces and panic output are
16 * unaffected — they use esp_rom_printf() which is a ROM function
17 * and does not go through libc.
18 *
19 * These stubs redirect through vsnprintf() (which uses _svfprintf_r
20 * already in the binary) and fwrite(), allowing the linker to
21 * dead-code eliminate _vfprintf_r.
22 *
23 * Saves ~11 KB of flash.
24 *
25 * To disable these wraps, set enable_full_printf: true in the esp32
26 * advanced config section.
27 */
28
29#if defined(USE_ESP_IDF) && !defined(USE_FULL_PRINTF)
30#include <cstdarg>
31#include <cstdio>
32
33#include "esp_system.h"
34
35namespace esphome::esp32 {}
36
37static constexpr size_t PRINTF_BUFFER_SIZE = 512;
38
39// These stubs are essentially dead code at runtime — ESPHome replaces the
40// ESP-IDF log handler, and the SDK's printf/fprintf calls only exist in
41// debug/assert paths that are never reached in normal operation.
42// The buffer overflow check is purely defensive and should never trigger.
43static int write_printf_buffer(FILE *stream, char *buf, int len) {
44 if (len < 0) {
45 return len;
46 }
47 size_t write_len = len;
48 if (write_len >= PRINTF_BUFFER_SIZE) {
49 fwrite(buf, 1, PRINTF_BUFFER_SIZE - 1, stream);
50 esp_system_abort("printf buffer overflow; set enable_full_printf: true in esp32 framework advanced config");
51 }
52 if (fwrite(buf, 1, write_len, stream) < write_len || ferror(stream)) {
53 return -1;
54 }
55 return len;
56}
57
58// NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
59extern "C" {
60
61int __wrap_vprintf(const char *fmt, va_list ap) {
62 char buf[PRINTF_BUFFER_SIZE];
63 return write_printf_buffer(stdout, buf, vsnprintf(buf, sizeof(buf), fmt, ap));
64}
65
66int __wrap_printf(const char *fmt, ...) {
67 va_list ap;
68 va_start(ap, fmt);
69 int len = __wrap_vprintf(fmt, ap);
70 va_end(ap);
71 return len;
72}
73
74int __wrap_vfprintf(FILE *stream, const char *fmt, va_list ap) {
75 char buf[PRINTF_BUFFER_SIZE];
76 return write_printf_buffer(stream, buf, vsnprintf(buf, sizeof(buf), fmt, ap));
77}
78
79int __wrap_fprintf(FILE *stream, const char *fmt, ...) {
80 va_list ap;
81 va_start(ap, fmt);
82 int len = __wrap_vfprintf(stream, fmt, ap);
83 va_end(ap);
84 return len;
85}
86
87} // extern "C"
88// NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
89
90#endif // USE_ESP_IDF && !USE_FULL_PRINTF
int __wrap_fprintf(FILE *stream, const char *fmt,...)
int __wrap_printf(const char *fmt,...)
int __wrap_vprintf(const char *fmt, va_list ap)
int __wrap_vfprintf(FILE *stream, const char *fmt, va_list ap)
uint32_t len