How to use .a(static) Library

I created a simple libtest.cpp file and used g++ to build it into libtest.a, then placed it in projectname/lib/test/src along with libtest.h.

projectname/lib/test/src/libtest.h
projectname/lib/test/src/libtest.a

However, my problem is that libtest.h is not linking to libtest.a. What else do I need to modify?

libtest.cpp:

int test_static(int i) { return i * 2; }

libtest.h:

#ifndef __LIBTEST_H__
#define __LIBTEST_H__
extern "C" { int test(int i); }
#endif

main.cpp:

#include "libtest.h"

void setup() {
    ...
    int number_test = test_static(30);
    ...
}

platformio.ini:

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino

lib_deps = 
    knolleary/PubSubClient@^2.8
    WiFiClientSecure@^2.0

build_flags =
    -Llib/test/src
    -ltest

Error =

project/path/projectname//src/main.cpp:247: undefined reference to `test_static`

In your post you named your method in the header file like this

but in your setup()-Method you’re calling

That ends in a compiler (not linker :wink:) error like that

src/main.cpp: In function 'void setup()':
src/main.cpp:7:23: error: 'test_static' was not declared in this scope
     int number_test = test_static(30);
                       ^~~~~~~~~~~
src/main.cpp:7:23: note: suggested alternative: 'setstate'
     int number_test = test_static(30);
                       ^~~~~~~~~~~
                       setstate
*** [.pio/build/esp32dev/src/main.cpp.o] Error 1

I assume that this is a typo in your posting. After setting both names to the same, I got your error message.

To fix that you have to add

#include "libtest.h"

to your libtest.cpp file too. Without that the compiler generates two different symbols for your method

 nm .pio/build/esp32dev/src/main.cpp.o 
         U test_static
         U yield
00000000 T _Z4loopv
00000000 T _Z5setupv

vs

 nm .pio/build/esp32dev/lib525/libmylib.a 

libtest.cpp.o:
00000000 T _Z11test_statici

that do not match. After adding the include nm shows that

nm .pio/build/esp32dev/lib525/libmylib.a 

libtest.cpp.o:
00000000 T test_static

and linking succeeds.

And then there is no need for additional build_flags - platformio manages your private lib without that

Oh, My translator missed the problem. In libtest.h, it is written as test_static(int i); instead of int test(int i);.
However, I’m still getting the /src/main.cpp:12: undefined reference to 'test_static' error.

Also, I can check the results with the nm .pio/build/esp32dev/src/main.cpp.o command, but in the command nm .pio/build/esp32dev/lib525/libmylib.a, the lib525 folder does not exist in my build folder.

I was able to find similar results in the following path.

What compiler are you using to compile libtest.a? It can’t be the system compiler, it must be esp32’s.


So here’s how you would do it: assume this is your src/main.cpp:

#include <Arduino.h>
#include "testlib.h"

void setup() {
  pinMode(func_from_lib(), HIGH);
}

void loop() {
}

include/testlib.h:

#ifndef __TESTLIB_H__
#define __TESTLIB_H__

#ifdef __cplusplus
extern "C" {
#endif

int func_from_lib(void);

#ifdef __cplusplus
}
#endif

#endif

A libtest.cpp, somewhere outside src:

#include "testlib.h"

int func_from_lib(void) {
  return 4;
}

Now, instead of system clang++ (which I assume you are on an Apple Silicon, me too) which is an aarch64 architecture compiler, esp32 is an Xtensa architecture chip, so aarch64 libraries won’t link to that. You need to:

pio pkg exec -p toolchain-xtensa-esp32 -- xtensa-esp32-elf-g++ libtest.cpp -Iinclude -c
pio pkg exec -p toolchain-xtensa-esp32 -- xtensa-esp32-elf-ar rs libtest.a libtest.o

This instructs PIO CLI to find the correct toolchain for esp32, then compile using compilers from there. Replace -Iinclude with path to your project include library, and also take a look at $HOME/.platformio/packages for the correct package name if anything goes wrong.

I used the following commands and confirmed that function names in MacOS have an underscore (“_”) prefix:

~/.platformio/packages/toolchain-xtensa-esp32/bin/xtensa-esp32-elf-g++ -c libtest.cpp -o libttest.o
~/.platformio/packages/toolchain-xtensa-esp32/bin/xtensa-esp32-elf-ar rcs libtest.a libtest.o

So, I compiled it in a Windows environment, inserted it into the project, and verified with the nm command that it was included correctly. However, the error still persists.
(In the image below, I replaced the actual function name with test_static to aid understanding through image modification.)

.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pio/build/esp32dev/src/main.cpp.o:(.literal._Z5setupv+0x14): undefined reference to `test_static'

Can you confirm that your libtest.cpp has a #include "libtest.h" directive? Name mangling happens only if the source file doesn’t recognize that a symbol is declared in extern "C" block

The numbering for dependencies libs inside .pio/build varies (don’t known how pio exactly decides which number is used. As I deleted my small reproducer in the meantime since my last post and had to reconstruct it and now the private lib resides in lib4bd/ :wink:).

My small reproducer looks like that

platformio.ini
lib/
-- mylib/
---- libtest.cpp  
---- libtest.h
src/
-- main.cpp

with this platformio.ini

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino

compiling is done simply with pio run

pio run
Processing esp32dev (platform: espressif32; board: esp32dev; framework: arduino)
------------------------------------------------------------------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/espressif32/esp32dev.html
PLATFORM: Espressif 32 (6.7.0) > Espressif ESP32 Dev Module
HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
DEBUG: Current (cmsis-dap) External (cmsis-dap, esp-bridge, esp-prog, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa)
PACKAGES: 
 - framework-arduinoespressif32 @ 3.20016.0 (2.0.16) 
 - tool-esptoolpy @ 1.40501.0 (4.5.1) 
 - toolchain-xtensa-esp32 @ 8.4.0+2021r2-patch5
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 34 compatible libraries
Scanning dependencies...
Dependency Graph
|-- mylib
Building in release mode
Compiling .pio/build/esp32dev/src/main.cpp.o
Building .pio/build/esp32dev/bootloader.bin
Generating partitions .pio/build/esp32dev/partitions.bin
Compiling .pio/build/esp32dev/lib4bd/mylib/libtest.cpp.o
esptool.py v4.5.1
Creating esp32 image...
Merged 1 ELF section
Successfully created esp32 image.
Compiling .pio/build/esp32dev/FrameworkArduino/Esp.cpp.o
Archiving .pio/build/esp32dev/lib4bd/libmylib.a
Indexing .pio/build/esp32dev/lib4bd/libmylib.a
Compiling .pio/build/esp32dev/FrameworkArduino/FirmwareMSC.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/FunctionalInterrupt.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/HWCDC.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/HardwareSerial.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/IPAddress.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/IPv6Address.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/MD5Builder.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/Print.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/Stream.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/StreamString.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/Tone.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/USB.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/USBCDC.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/USBMSC.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/WMath.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/WString.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/base64.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/cbuf.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-adc.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-bt.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-cpu.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-dac.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-gpio.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-i2c-slave.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-i2c.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-ledc.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-matrix.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-misc.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-psram.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-rgb-led.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-rmt.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-sigmadelta.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-spi.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-time.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-timer.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-tinyusb.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-touch.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-uart.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/firmware_msc_fat.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/libb64/cdecode.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/libb64/cencode.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/main.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/stdlib_noniso.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/wiring_pulse.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/wiring_shift.c.o
Archiving .pio/build/esp32dev/libFrameworkArduino.a
Indexing .pio/build/esp32dev/libFrameworkArduino.a
Linking .pio/build/esp32dev/firmware.elf
Retrieving maximum program size .pio/build/esp32dev/firmware.elf
Checking size .pio/build/esp32dev/firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [=         ]   6.4% (used 21032 bytes from 327680 bytes)
Flash: [==        ]  17.8% (used 233269 bytes from 1310720 bytes)
Building .pio/build/esp32dev/firmware.bin
esptool.py v4.5.1
Creating esp32 image...
Merged 2 ELF sections
Successfully created esp32 image.
========================================================= [SUCCESS] Took 6.34 seconds =========================================================

As you can see - no need to build your private lib manually

I resolved it by adding extern "C" {} to the .cpp file and .h header, and by adding the following code to platformio.ini.

build_flags = -Llib/test -lTest