Basics of Design CC Blog Research & Design Hub

Static Libraries in the ESP IoT Development Framework

Written by Pedro Bertoleti

Protecting Proprietary Source Code…and More

Static libraries within the ESP-IDF + VS Code development environment safeguard intellectual property and modularize firmware development, by transitioning from source code to pre-compiled binaries. In this article, Pedro explains how static libraries allow developers to distribute sophisticated features to third parties as “black boxes.” He describes the critical distinctions between static and dynamic linking, the legal nuances of software licensing, and practical workflows for generating and consuming protected static libraries.


  • What are static libraries in ESP32 ESP-IDF development?
  • How do static and dynamic libraries differ?
  • How can static libraries protect proprietary firmware source code?
  • How do you create and integrate ESP-IDF static libraries?
  • What software licensing considerations apply to embedded static libraries?
  • ESP32
  • ESP-IDF
  • VS Code
  • CMake
  • C/C++
  • Static Libraries (.a files)
  • Dynamic Libraries (.so files)
  • FreeRTOS
  • Firmware Development
  • Embedded Systems
  • Espressif Systems | espressif.com
  • VS Code | code.visualstudio.com

In the rapidly evolving landscape of embedded systems, the value of a product is increasingly defined by its firmware. From sophisticated signal processing algorithms to proprietary communication protocols, the “secret sauce” of a modern embedded systems device resides within its source code.

As companies look to collaborate with third-party vendors or distribute specialized modules, the need to share functionality without exposing the underlying implementation becomes paramount.

The ESP32 SoC, programmed using its powerful framework called ESP-IDF (Espressif IoT Development Framework) [1], offers a professional-grade build system that allows developers to encapsulate their logic into static libraries, to safely distribute functionalities without revealing its source-code. This approach not only facilitates modularity and code reuse across different teams, but also serves as a critical layer of intellectual property (IP) protection.

In this article, I explore the fundamental differences between static and dynamic libraries, and explain why static linking is the standard for the ESP32.

I detail the primary use cases for these libraries in the embedded field, with a specific focus on proprietary code protection. Furthermore, I address the legal complexities of software licensing when linking binaries, and provide a step-by-step technical walkthrough on generating, distributing, and consuming static libraries using ESP-IDF version 5.4.2 and Virtual Studio (VS) Code.

COMPARISON OF STATIC AND DYNAMIC LIBRARIES

To understand the role of libraries in the ESP32 (and all other microcontrollers with build systems that support library usage), one must first distinguish between the two primary methods of linking code: static and dynamic. These methods differ significantly in how they handle the integration of library code into the final executable, which affects memory usage, distribution, and execution.

Static libraries are collections of object (.o) files, bundled together into a single archive file, typically carrying the .a file extension. During the linking stage of the compilation process, the linker extracts the necessary machine code from the static library, and copies it directly into the final application binary.

It means that static libraries, despite being distributed as isolated files, fully integrate to the final binary of an embedded application. They work like ingredients that we add to a recipe when cooking a meal; despite being added separately, after “integrating” into the recipe, they become part of the meal.

The general features of static libraries include:

Linking—Occurs at compile-time. The code that static libraries carry are merged into the final .bin file.

Distribution—You only need to provide the .a file and the header (.h) files to the end-user or third-party developers. Therefore, those who use static libraries only know which functions/methods of static libraries are allowed to be used, without having access to its implementation, working as a “black box.”

Storage—Since the static library content is copied into the final binary, its size increases according to the static libraries functions used in the code that makes usage of these libraries.

RAM—RAM usage behaves the same way that the routines in static libraries were directly implemented in the target embedded software (code that uses static libraries). Code resides in the flash memory (or Instruction RAM) as part of the monolithic application image.

Dynamic libraries (also called “shared objects” or “dynamic link” libraries), like static libraries, are also object files condensed in a single file (often, .so files). They differ from static libraries, however, by remaining separate from the main application binary. The linking occurs at runtime by a dynamic loader provided by the operating system (embedded Linux, for example).

Dynamic libraries therefore are not integrated to the final binary, but rather, dynamically called when they are needed. They work like forks, knives and spoons when eating a meal; they are not ingredients in the food, but they must be available to complete the process of eating the meal.

The general features of dynamic libraries are as follows:

LinkingOccurs at runtime. The application contains “references” to the library rather than the code itself, and the application requests the operating system (by using the dynamic loader provided by OS) to load dynamic library into memory.

Note that when using dynamic libraries, if the library gets unloaded from memory and it’s requested again (by the same process or another one), it must be reloaded into memory again. This means reading it from disk or flash memory again, which increases the IOWait and, consequently, contributes to an overall performance decrease. Therefore, the time to load/unload the dynamic library must be chosen wisely to maintain good overall system performance.

DistributionThe library must be present on the target system’s file system, alongside the application. In terms of deployment, the application and its needed dynamic libraries can be distributed and deployed separately, but the referred libraries must be available when the application executes; otherwise it will result in fatal errors and an unexpected application crash.

StorageMultiple applications can share a single dynamic library file, thereby saving disk space in multi-process environments and contributing to more code reuse.

RAMOnly one copy of the library is loaded into RAM, even if multiple programs are using it. This saves substantial RAM compared to the static library approach.

Figure 1
a and dynamic libraries handling processes during compilation.
Figure 1
a and dynamic libraries handling processes during compilation.

The handling of static and dynamic libraries during the compilation process is shown in Figure 1. Table 1 shows a comparison of static and dynamic libraries in terms of link time, binary size, RAM usage, IP protection and dependency criteria.

Table 1
Comparison of static and dynamic libraries.
Table 1
Comparison of static and dynamic libraries.

Unfortunately, with the ESP32, there’s no way to use dynamic libraries when out-of-the-box ESP-IDF is used. ESP32 software, when developed using ESP-IDF, runs on FreeRTOS by default. Consequently, the ESP-IDF does not support dynamic libraries, since the FreeRTOS final compilation result is a single binary file.

This means that all external dependencies must be linked statically. Because only static libraries are allowed, every function call made to a library is resolved during the build process, and the machine code (final binary) is flashed into the ESP32’s flash memory. This constraint reinforces the importance of understanding static libraries as the sole mechanism for binary-level code distribution on this platform.

STATIC LIBRARy Use

Static libraries are the Swiss army knives of professional embedded software engineering. They are utilized for several strategic reasons, from better organization and encapsulation to security and IP protection. The most common uses of static libraries in real-world embedded software solutions are illustrated in Figure 2, detailed in later sections of this article.

Figure 2
The most common uses of static libraries.
Figure 2
The most common uses of static libraries.

Protecting Proprietary Code—This is the most critical use case for commercial entities. By shipping a .a file, a company can sell a software solution without handing over the source code. A complete, low-level hardware access handling, ensuring the developer cannot cause a malfunction by wrongly accessing some peripheral, or to protect proprietary communication protocols implementation, is used, for example in the automotive industry. This prevents competitors from cloning the logic, while allowing third-party developers to build solutions on top of it.

Reducing Build Times—In large-scale projects, recompiling every single source file every time can slow down the whole development and testing processes significantly. If certain modules (such as a hardware abstraction layer or a graphics library) are stable and rarely change, they can be pre-compiled into a static library. The linker then only needs to process the archive, significantly speeding up the daily development cycle.

Modularizing and Team Collaborating—Large organizations often have different teams working on specific subsystems, for example, a “Security Team” and an “Application Team.” The Security Team could provide a static library containing encrypted bootloader logic or key management. This would ensure that the Application Team could use these features without needing to understand or potentially break the underlying security implementation.

Version Control and Release Management—Distributing a library as a single binary file significantly simplifies versioning. Instead of managing dozens of source files, the integrator manages a single .a file and its required header files. This reduces “dependency hell,” and ensures that the version tested by the provider is exactly the version that is used by the consumer.

Binary Size Management (Selective Linking)—Linkers are often configured to perform “dead code stripping.” When using a static library, the linker only pulls in the specific object files or functions that are actually called by the application. This allows for a comprehensive library to be distributed, without necessarily bloating the final firmware of a project that only uses 10% of its features.

PROTECTING PROPRIETARY CODE WITH STATIC LIBRARIES

The use of static libraries in embedded software development allows for the distribution of pre-compiled object code. By providing a compiled binary and a corresponding set of header files, a developer can grant others the ability to invoke complex functions, while keeping the logic, internal structures, and sensitive algorithms hidden from plain view.

The core of the “Proprietary Protection” strategy lies in the transformation of human-readable C/C++ code into machine-readable object code, and then putting it all together into a single static library file (.a file). Unlike open-source components, where the logic is transparent and use is typically governed by one of the licenses shown in Table 2 and continued in Table 3, a static library acts as a “black box” that can be integrated into a larger project without revealing the developer’s methodology or allowing unauthorized modifications to the core logic.

Table 2
Common copyleft (non-permissive) licenses.
Table 2
Common copyleft (non-permissive) licenses.
Table 3
Common permissive licenses.
Table 3
Common permissive licenses.

When third-party developers receive a static library (and the corresponding header files) from a vendor, they receive all the functionalities provided by the vendor as a series of instructions and symbols into this static library file governed by licensing agreements such as those shown in Figure 3. Although it is technically possible to “disassemble” a binary back into assembly code, it is by far not viable (nor even possible in some cases) to reconstruct the original C logic, variable names, and architectural intent.

Figure 3
The most common permissive and non-permissive software licenses.
Figure 3
The most common permissive and non-permissive software licenses.

For most commercial purposes, this provides a sufficient “moat.” The third-party developers use the provided .h files to see the “Interface”—the function signatures they are allowed to call—but the “Implementation” remains hidden within the .a file.

This approach is widely used in several industries that involve embedded software development in a multi-vendor and multi-developer ecosystem, when protecting source-codes is a “must-have” requirement. This includes the automotive industry, where proprietary communication protocols and certain functionalities algorithms are industrial secrets. For security reasons, they must be protected even from developers in the company itself.

This means that only few developers have full access to full source-code, while everyone else only uses it as libraries, in a “black box” approach.

LEGAL issues OF ESP32 STATIC LIBRARies

Importing and Distributing Static LibrariesIn ESP-IDF, the build system is based on CMake [2]. The ESP-IDF framework treats every directory containing source code as an independent “component.” By default, the ESP-IDF build system compiles each component into a static library during the build process, and then links them all into the final .elf file. If you observe the contents of a build directory after compiling an ESP32 firmware, you will find out each component is encapsulated into a static library (.a file).

This means that generating a static library for ESP32 doesn’t differ very much from generating a common firmware The piece of software from which you want to generate a static library must be developed as a component; then CMake will automatically build it as a static library.

There are several steps used to generate a static library out from a project using the ESP-IDF + VSCode development environment.

In your VSCode+ESP-IDF extension project, create a folder named components/my_protected_lib. Into this folder, you need to place the following files:

A CMakeLists.txt file

An include folder with your public header files (my_lib.h, for example).

An src folder with your private code (my_lib.c, for example).

The CMakeLists.txt content to generate a static library out of a component source-code is shown below:
idf_component_register(SRCS “src/my_lib.c” INCLUDE_DIRS “include”)

— ADVERTISMENT—

Advertise Here

When you run the build command in VSCode, the ESP-IDF build system creates a build directory inside your project folder. Then, inside build/esp-idf/my_protected_lib/, you will find a file named libmy_protected_lib.a. This is your static library.

To distribute your library, you need to collect only two files:

The .a file:

build/esp-idf/my_protected_lib/libmy_protected_lib.a

The header file:

components/my_protected_lib/include/my_lib.h

Therefore, the .a file contains the object files (result of the compilations of the component source-code), allowing distribution of the protected component without revealing its secrets.

Important Note: Before distributing the header files, ensure they contain only the necessary content: definitions, prototype, sensitive comments, and anything else that you allow the third-party to know about a static library.

Otherwise, the third-party can use non-necessary content to exploit and hack the static library, which could result in product malfunction.

Importing and Using Static LibrariesThe integration process of static libraries in a ESP32 project is basically to inform the CMake build system that it must use a static library file (by providing its path) and, in the source-code of the project that uses this static library, include static libraries header files and call its functions.

Critical Note—The ESP-IDF version used to generate the static library must be the same used in the project that makes use of this library. Otherwise, it will probably result in failures and crashes that are hard to debug.

Now, it’s time to be in a third-party developer’s shoes. Assuming you have been provided with a static library file (libmy_protected_lib.a) and its corresponding header file (my_lib.h), use the following three steps to integrate the static library into a third-party project:

  • Create a subfolder inside your project component folder: components/prebuilt_lib. There, place the libmy_protected_lib.a in a subfolder called lib and the header in a subfolder called include.
  • Tell CMake that this component does not have source code, but instead uses a “pre-built” binary. The CMakeLists.txt file content inside components/prebuilt_lib should look like the code shown in Listing1.
  • Finally, in the application code of the project that uses this static library, just include the static library’s header files and call/use the functions provided in the static library.
idf_component_register(INCLUDE_DIRS “include” LDFRAGMENTS “linker.lf”)# Link the static librarytarget_link_libraries(${COMPONENT_LIB} INTERFACE “-L${CMAKE_CURRENT_SOURCE_DIR}/lib”)target_link_libraries(${COMPONENT_LIB} INTERFACE “my_protected_lib”)

Listing 1
You must tell CLIST whether a file contains source code or not using instructions like these.
NECESSARY TOOLS

The use of static libraries in ESP32 development is a professional and effective method for distributing software while maintaining the confidentiality of intellectual property. By converting source code into pre-compiled object archives, developers can provide powerful functionality to partners and customers, without the risks associated with open-source-code distribution. However, this technical convenience comes with the responsibility of careful license management and a strict requirement for toolchain synchronization between the producer and the consumer.

The ESP-IDF + VSCode development environment provides the necessary CMake tools to streamline both the generation and integration of these libraries. For any organization looking to commercialize their embedded innovations on the ESP32 platform, mastering the static library workflow is not merely a technical skill; it is also a strategic necessity to protect the value of their engineering efforts.

REFERENCES
[1] Espressif IoT Development Framework: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/build-system.html
[2] CMake: https://cmake.org/

RESOURCES
Espressif Systems | espressif.com
VS Code | code.visualstudio.com

PUBLISHED IN CIRCUIT CELLAR MAGAZINE • APRIL 2026 #429 – Get a PDF of the issue

Keep up-to-date with our FREE Weekly Newsletter!

Don't miss out on upcoming issues of Circuit Cellar.


Note: We’ve made the Dec 2022 issue of Circuit Cellar available as a free sample issue. In it, you’ll find a rich variety of the kinds of articles and information that exemplify a typical issue of the current magazine.

Would you like to write for Circuit Cellar? We are always accepting articles/posts from the technical community. Get in touch with us and let's discuss your ideas.

— ADVERTISMENT—

Advertise Here

Sponsor this Article

Supporting Companies

Upcoming Events


Copyright © KCK Media Corp.
All Rights Reserved

Copyright © 2026 KCK Media Corp.

Static Libraries in the ESP IoT Development Framework

by Pedro Bertoleti time to read: 11 min