# Minimizing Binary Size

[中文]

The ESP-IDF build system compiles all source files in the project and ESP-IDF, but only functions and variables that are actually referenced by the program are linked into the final binary. In some cases, it is necessary to reduce the total size of the firmware binary, e.g., in order to fit it into the available flash partition size.

The first step to reducing the total firmware binary size is measuring what is causing the size to increase.

## Measuring Static Sizes

To optimize both the firmware binary size and the memory usage, it is necessary to measure statically-allocated RAM (`data`, `bss`), code (`text`), and read-only data (`rodata`) in your project. The idf.py sub-commands `size`, `size-components`, and `size-files` can be used to examine statically-allocated RAM usage at different levels of detail. For more information, please see the IDF Size tool.

## Linker Map File

Note

This is an advanced analysis method, but it can be very useful. Feel free to skip ahead to Reducing Overall Size and possibly come back to this later.

The `idf.py size` analysis tools all work by parsing the GNU binutils `linker map file`, which is a summary of everything the linker did when it created (i.e., linked) the final firmware binary file.

Linker map files themselves are plain text files, so it is possible to read them and find out exactly what the linker did. However, they are also very complex and long, often exceeding 100,000 lines.

The map file itself is broken into parts and each part has a heading. The parts are:

- `Archive member included to satisfy reference by file (symbol)`

  > - This shows you: for each object file included in the link, what symbol (function or variable) was the linker searching for when it included that object file.

  > - If you are wondering why some object file in particular was included in the binary, this part may give a clue. This part can be used in conjunction with the `Cross Reference Table` at the end of the file.

  > Note

  > Not every object file shown in this list ends up included in the final binary, some end up in the `Discarded input sections` list instead.

- `Allocating common symbols`

  > - This is a list of some global variables along with their sizes. Common symbols have a particular meaning in ELF binary files, but ESP-IDF does not make much use of them.

- `Discarded input sections`

  > - These sections were read by the linker as part of an object file to be linked into the final binary, but then nothing else referred to them, so they were discarded from the final binary.

  > - For ESP-IDF, this list can be very long, as we compile each function and static variable to a unique section in order to minimize the final binary size. Specifically, ESP-IDF uses compiler options `-ffunction-sections -fdata-sections` and linker option `--gc-sections`.

  > - Items mentioned in this list do not contribute to the final binary.

- `Memory Configuration`, `Linker script and memory map`

  > - These two parts go together. Some of the output comes directly from the linker command line and the Linker Script, both provided by Build System. The linker script is partially generated from the ESP-IDF project using the Linker Script Generation feature.

  > - As the output of the `Linker script and memory map` part of the map unfolds, you can see each symbol (function or static variable) linked into the final binary along with its address (as a 16 digit hex number), its length (also in hex), and the library and object file it was linked from (which can be used to determine the component and the source file).

  > - Following all of the output sections that take up space in the final `.bin` file, the `memory map` also includes some sections in the ELF file that are only used for debugging, e.g., ELF sections `.debug_*`, etc. These do not contribute to the final binary size. You can notice the address of these symbols is a very small number, starting from `0x0000000000000000` and counting up.

- `Cross Reference Table`

  > - This table shows the symbol (function or static variable) that the list of object file(s) refers to. If you are wondering why a particular thing is included in the binary, this will help determine what included it.

  > Note

  > Unfortunately, the `Cross Reference Table` does not only include symbols that made it into the final binary. It also includes symbols in discarded sections. Therefore, just because something is shown here does not mean that it was included in the final binary - this needs to be checked separately.

Note

Linker map files are generated by the GNU binutils linker `ld`, not ESP-IDF. You can find additional information online about the linker map file format. This quick summary is written from the perspective of ESP-IDF build system in particular.

## Reducing Overall Size

The following configuration options reduces the final binary size of almost any ESP-IDF project:

- Set CONFIG_COMPILER_OPTIMIZATION to `Optimize for size (-Os)`. In some cases, `Optimize for performance (-O2)` will also reduce the binary size compared to the default. Note that if your code contains C or C++ Undefined Behavior then increasing the compiler optimization level may expose bugs that otherwise do not happen.

- Reduce the compiled-in log output by lowering the app CONFIG_LOG_DEFAULT_LEVEL. If the CONFIG_LOG_MAXIMUM_LEVEL is changed from the default then this setting controls the binary size instead. Reducing compiled-in logging reduces the number of strings in the binary, and also the code size of the calls to logging functions.

- If your application does not require dynamic log level changes and you do not need to control logs per module using tags, consider disabling CONFIG_LOG_DYNAMIC_LEVEL_CONTROL and changing CONFIG_LOG_TAG_LEVEL_IMPL. It reduces IRAM usage by approximately 260 bytes, DRAM usage by approximately 264 bytes, and flash usage by approximately 1 KB compared to the default option, it also speeds up logging.

- Set the CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL to `Silent`. This avoids compiling in a dedicated assertion string and source file name for each assert that may fail. It is still possible to find the failed assert in the code by looking at the memory address where the assertion failed.

- Besides the CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL, you can disable or silent the assertion for the HAL component separately by setting CONFIG_HAL_DEFAULT_ASSERTION_LEVEL. In the bootloader, ESP-IDF lowers the HAL assertion level to be silent even if CONFIG_HAL_DEFAULT_ASSERTION_LEVEL is set to full-assertion level. This is to reduce the bootloader size.

- Setting CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT removes specific error messages for particular internal ESP-IDF error check macros. This may make it harder to debug some error conditions by reading the log output.

- Do not enable CONFIG_COMPILER_CXX_EXCEPTIONS, CONFIG_COMPILER_CXX_RTTI, or set the CONFIG_COMPILER_STACK_CHECK_MODE to Overall. All of these options are already disabled by default, but they have a large impact on binary size.

- Disabling CONFIG_ESP_ERR_TO_NAME_LOOKUP removes the lookup table to translate user-friendly names for error values (see Error Handling) in error logs, etc. This saves some binary size, but error values will be printed as integers only.

- Setting CONFIG_ESP_SYSTEM_PANIC to `Silent reboot` saves a small amount of binary size, however this is only recommended if no one will use UART output to debug the device.

- If the application binary uses only one of the security versions of the protocomm component, then the support for others can be disabled to save some code size. The support can be disabled through CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0, CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1 or CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2 respectively.

Note

In addition to the many configuration items shown here, there are a number of configuration options where changing the option from the default increases binary size. These are not noted here. Where the increase is significant is usually noted in the configuration item help text.

### Targeted Optimizations

The following binary size optimizations apply to a particular component or a function:

#### Wi-Fi

- Disabling CONFIG_ESP_WIFI_ENABLE_WPA3_SAE will save some Wi-Fi binary size if WPA3 support is not needed. Note that WPA3 is mandatory for new Wi-Fi device certifications.

- Disabling CONFIG_ESP_WIFI_SOFTAP_SUPPORT will save some Wi-Fi binary size if soft-AP support is not needed.

- Disabling CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT will save some Wi-Fi binary size if enterprise support is not needed.

- Disabling CONFIG_ESP_WIFI_ENABLE_SAE_H2E will save some Wi-Fi binary size if hash-to-element authentication method of WPA3-SAE is not needed. Note that hash-to-element is faster, more secure and immune to side channel attacks than the default authentication method of hunting-and-pecking.

#### Bluetooth NimBLE

If using NimBLE-based Host APIs then the following modifications can reduce binary size:

- Set CONFIG_BT_NIMBLE_MAX_CONNECTIONS to 1 if only one Bluetooth LE connection is needed.

- Disable either CONFIG_BT_NIMBLE_ROLE_CENTRAL or CONFIG_BT_NIMBLE_ROLE_OBSERVER if these roles are not needed.

- Reducing CONFIG_BT_NIMBLE_LOG_LEVEL can reduce binary size. Note that if the overall log level has been reduced as described above in Reducing Overall Size then this also reduces the NimBLE log level.

#### lwIP IPv6

- Setting CONFIG_LWIP_IPV6 to `false` will reduce the size of the lwIP TCP/IP stack, at the cost of only supporting IPv4.

  Note

  IPv6 is required by some components such as ASIO Port. These components will not be available if IPV6 is disabled.

#### lwIP IPv4

- If IPv4 connectivity is not required, setting CONFIG_LWIP_IPV4 to `false` will reduce the size of the lwIP, supporting IPv6-only TCP/IP stack.

  > Note

  > Before disabling IPv4 support, please note that IPv6 only network environments are not ubiquitous and must be supported in the local network, e.g., by your internet service provider or using constrained local network settings.

#### Picolibc instead of Newlib

By default, ESP-IDF uses the Picolibc C library. Newlib is also supported and can be selected with CONFIG_LIBC_NEWLIB.

Picolibc C library provides smaller `printf` family functions and can reduce the binary size by up to 30 KB, depending on your application.

#### Newlib Nano Formatting

By default, ESP-IDF uses Newlib "full" formatting for I/O functions (`printf()`, `scanf()`, etc.)

Enabling the config option CONFIG_LIBC_NEWLIB_NANO_FORMAT will switch Newlib to the "Nano" formatting mode. This is smaller in code size, and a large part of the implementation is compiled into the ESP32-S3 ROM, so it does not need to be included in the binary at all.

The exact difference in binary size depends on which features the firmware uses, but 25 KB ~ 50 KB is typical.

Enabling "Nano" formatting reduces the stack usage of each function that calls `printf()` or another string formatting function, see Determining Stack Size.

"Nano" formatting does not support 64-bit integers, or C99 formatting features. For a full list of restrictions, search for `--enable-newlib-nano-formatted-io` in the Newlib README file.

#### libstdc++

- Enable CONFIG_COMPILER_CXX_GLIBCXX_CONSTEXPR_COLD_CONSTEXPR or CONFIG_COMPILER_CXX_GLIBCXX_CONSTEXPR_COLD to observe the impact on your application's binary size.

#### MbedTLS Features

Under Component Config > mbedTLS, there are multiple mbedTLS features enabled default, some of which can be disabled if not needed to save code size.

These include:

- CONFIG_MBEDTLS_HAVE_TIME

- CONFIG_MBEDTLS_ECDSA_DETERMINISTIC

- CONFIG_MBEDTLS_SHA512_C

- CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS

- CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS

- CONFIG_MBEDTLS_SSL_CONTEXT_SERIALIZATION

- CONFIG_MBEDTLS_SSL_ALPN

- CONFIG_MBEDTLS_SSL_RENEGOTIATION

- CONFIG_MBEDTLS_CCM_C

- CONFIG_MBEDTLS_GCM_C

- CONFIG_MBEDTLS_ECP_C (Alternatively: Leave this option enabled but disable some of the elliptic curves listed in the sub-menu.)

- CONFIG_MBEDTLS_ECP_NIST_OPTIM

- CONFIG_MBEDTLS_ECP_FIXED_POINT_OPTIM

- Change CONFIG_MBEDTLS_TLS_MODE if both server & client functionalities are not needed.

- Consider disabling some cipher suites listed in the `TLS Key Exchange Methods` sub-menu (i.e., CONFIG_MBEDTLS_KEY_EXCHANGE_RSA).

- Consider disabling CONFIG_MBEDTLS_ERROR_STRINGS if the application is already pulling in mbedTLS error strings through using `mbedtls_strerror()`.

- Consider disabling :ref:CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM if the application does not involve or require performance optimization for operations that involve small data lengths, such as NVS encryption/decryption or TLS communication when performed on small data segments.

The help text for each option has some more information for reference.

Important

It is strongly not recommended to disable all these mbedTLS options. Only disable options of which you understand the functionality and are certain that it is not needed in the application. In particular:

- Ensure that any TLS server(s) the device connects to can still be used. If the server is controlled by a third party or a cloud service, it is recommended to ensure that the firmware supports at least two of the supported cipher suites in case one is disabled in a future update.

- Ensure that any TLS client(s) that connect to the device can still connect with supported/recommended cipher suites. Note that future versions of client operating systems may remove support for some features, so it is recommended to enable multiple supported cipher suites, or algorithms for redundancy.

If depending on third party clients or servers, always pay attention to announcements about future changes to supported TLS features. If not, the ESP32-S3 device may become inaccessible if support changes.

Note

Not every combination of mbedTLS compile-time config is tested in ESP-IDF. If you find a combination that fails to compile or function as expected, please report the details on GitHub.

#### VFS

Virtual Filesystem Component feature in ESP-IDF allows multiple filesystem drivers and file-like peripheral drivers to be accessed using standard I/O functions (`open`, `read`, `write`, etc.) and C library functions (`fopen`, `fread`, `fwrite`, etc.). When filesystem or file-like peripheral driver functionality is not used in the application, this feature can be fully or partially disabled. VFS component provides the following configuration options:

- CONFIG_VFS_SUPPORT_TERMIOS — can be disabled if the application does not use `termios` family of functions. Currently, these functions are implemented only for UART VFS driver. Most applications can disable this option. Disabling this option reduces the code size by about 1.8 KB.

- CONFIG_VFS_SUPPORT_SELECT — can be disabled if the application does not use the `select` function with file descriptors. Currently, only the UART and eventfd VFS drivers implement `select` support. Note that when this option is disabled, `select` can still be used for socket file descriptors. Disabling this option reduces the code size by about 2.7 KB.

- CONFIG_VFS_SUPPORT_DIR — can be disabled if the application does not use directory-related functions, such as `readdir` (see the description of this option for the complete list). Applications that only open, read and write specific files and do not need to enumerate or create directories can disable this option, reducing the code size by 0.5 KB or more, depending on the filesystem drivers in use.

- CONFIG_VFS_SUPPORT_IO — can be disabled if the application does not use filesystems or file-like peripheral drivers. This disables all VFS functionality, including the three options mentioned above. When this option is disabled, Console can not be used. Note that the application can still use standard I/O functions with socket file descriptors when this option is disabled. Compared to the default configuration, disabling this option reduces code size by about 9.4 KB.

#### HAL

- Enabling CONFIG_HAL_WDT_USE_ROM_IMPL can reduce the IRAM usage and binary size by linking in the watchdog HAL driver of ROM implementation.

#### Heap

- Enabling CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH can reduce the IRAM usage and binary size by placing the entirety of the heap functionalities in flash memory.

#### Console

For targets that support USB-Serial-JTAG, both the USB-Serial-JTAG and UART console output are enabled by default. If you only need one console, you can reduce the binary size and RAM usage by doing the following:

1. Disable the secondary console by setting CONFIG_ESP_CONSOLE_SECONDARY to `CONFIG_ESP_CONSOLE_SECONDARY_NONE`.

2. Set CONFIG_ESP_CONSOLE_UART to use one of the following:

   > - `UART` reduces the binary size by around 2.5 KB.

   > - `USB-Serial-JTAG` reduces the binary size by around 10 KB and DRAM usage by around 1.5 KB.

Please note that these size reductions assume the UART/USB-Serial-JTAG driver code is not pulled into the app. If these drivers are already used for other purposes, then the savings will be smaller.

## Bootloader Size

This document deals with the size of an ESP-IDF app binary only, and not the ESP-IDF Second Stage Bootloader.

For a discussion of ESP-IDF bootloader binary size, see Bootloader Size.

## IRAM Binary Size

If the IRAM section of a binary is too large, this issue can be resolved by reducing IRAM memory usage. See Optimizing IRAM Usage.

Was this page helpful?

- Thank you! We received your feedback.
- If you have any comments, fill in Espressif Documentation Feedback Form.

- We value your feedback.
- Let us know how we can improve this page by filling in Espressif Documentation Feedback Form.