Linking problem with the 'blufi' example

Testing out the ‘blufi’ example led to a lot of linking problems. The project compiles fine but can’t link (just to take some of the errors see below). I guess it’s something trivial, but I have apparently gone “blind”, it seems to be related to the Bluetooth component. What am I missing here, some special menu config perhaps?

c:/users/niels/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: .pio\build\nodemcu-32s\src\blufi_example_main.o:(.literal.example_event_callback+0x84): undefined reference to `esp_blufi_adv_start'
c:/users/niels/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: .pio\build\nodemcu-32s\src\blufi_example_main.o:(.literal.example_event_callback+0x88): undefined reference to `esp_blufi_adv_stop'
c:/users/niels/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: .pio\build\nodemcu-32s\src\blufi_example_main.o:(.literal.example_event_callback+0x98): undefined reference to `esp_blufi_send_error_info'
c:/users/niels/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: .pio\build\nodemcu-32s\src\blufi_example_main.o:(.literal.example_event_callback+0xa4): undefined reference to `esp_blufi_send_wifi_conn_report'
c:/users/niels/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: .pio\build\nodemcu-32s\src\blufi_example_main.o:(.literal.example_event_callback+0xa8): undefined reference to `esp_blufi_disconnect'
.
.
.

In ESP32C2,Use the blufi interface, enable nimble, and use esp_bt_controller_disable() to turn off bt, a halt occurs · Issue #9792 · espressif/esp-idf · GitHub I see the config option

CONFIG_BT_NIMBLE_BLUFI_ENABLE=y

Assuming you are using using ESP-IDF, did you run the menuconfig to enable this option?

@maxgerhardt Thanks for the support!
I can’t seem to find that setting in menuconfig and the example doesn’t have a Kconfig file that I need to add to the project in order to get this setting? Adding the setting directly to the sdkconfig file doesn’t help on the linkling problem either.

Reading the referenced github issue doesn’t seem to relate to my issue.

When you open the menuconfig, the help says that pressing / enters the search. You can enter the symbol name there.

If you don’t have this optio, update your espressif32 platform in the CLI with

pio pkg update -g -p espressif32

Enabling blufi like shown above and having a src/main.c of

#include "esp_bt.h"
#include "esp_blufi_api.h"
#include "esp_blufi.h"

void app_main() {
 esp_blufi_adv_start();
}

results in a successfull compilation for me.

Checking size .pio/build/esp32dev/firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [=         ]  10.3% (used 33840 bytes from 327680 bytes)
Flash: [======    ]  63.5% (used 665905 bytes from 1048576 bytes)
Building .pio/build/esp32dev/firmware.bin

Okay I found it in menuconfig - and I learned something new, the ‘/’ function, thanks!

Although it helped with some of the link problems, now it seems to have to do with security stuff. I wonder what setting I am missing now and need to search for?

c:/users/niels/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: .pio\build\nodemcu-32s\src\blufi_security.o:(.literal.blufi_dh_negotiate_data_handler+0x24): undefined reference to `mbedtls_aes_setkey_enc'
c:/users/niels/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: .pio\build\nodemcu-32s\src\blufi_security.o:(.literal.blufi_aes_encrypt+0x0): undefined reference to `mbedtls_aes_crypt_cfb128'
c:/users/niels/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: .pio\build\nodemcu-32s\src\blufi_security.o:(.literal.blufi_security_init+0x0): undefined reference to `mbedtls_aes_init'
c:/users/niels/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: .pio\build\nodemcu-32s\src\blufi_security.o:(.literal.blufi_security_deinit+0x0): undefined reference to `mbedtls_aes_free'
c:/users/niels/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: .pio\build\nodemcu-32s\src\blufi_security.o: in function `blufi_dh_negotiate_data_handler':
C:\Users\niels\Documents\PlatformIO\Projects\BlueConfig/src/blufi_security.c:112: undefined reference to `mbedtls_aes_setkey_enc'
.
.
.

Make sure you have the appropriate mbedtls functions turned on too:

But actually, if that option was turned on, it shows that most likely you didn’t copy over or regenerate the sdkconfig files from the example. They already configure ESP-IDF exactly how the example needs it.

So to avoid to play whack-a-mole with every missing setting (or worse, a slightly different setting that leads to subtle change in behavior), I recommend you to delete all sdkconfig.* files from the root of the PIO project, copy over the referenced sdkconfig.defaults into your project and (since I presume you compile for the ESP32), copy the sdkconfig.defaults.esp32 sdkconfig.defaults.<environment name> (e.g. esp32dev) into your project.

PlatformIO might have a slightly different name regarding the skdconfig.defaults.<board type/environment> naming.

Yes you are right I didn’t copy the sdkconfig files (not that much into this menuconfig stuff).
So, I copied the two files ‘sdkconfig.defaults’ and ‘sdkconfig.defaults.esp32’ to my project root. I removed the old sdkconfig files and renamed ‘sdkconfig.defaults.esp32’ to ‘sdkconfig.defaults.nodemcu-32s’. But I still get the mbedtls linking errors above.

Funny thing when running the menuconfig it makes a sdkconfig file named ‘sdkconfig.nodemcu-32s’ (without the ‘default’ in the middel) as if the ‘sdkconfig.defaults.nodemcu-32s’ is ignored.

Can you copy the entire content of sdkconfig.defaults.esp32 into sdkconfig.nodemcu-32s and rebuild the project?

Copied the sdk file as requested and rebuild, still the same issue.

I tried to find the CONFIG_MBEDTLS_HARDWARE_MPI, CONFIG_MBEDTLS_DHM_C and the CONFIG_MBEDTLS_AES_C in the sdkconfig file but could not locate the CONFIG_MBEDTLS_DHM_C setting. The other two were as you described above.

I can see that the aes.c, where all the ‘undefined reference…’ are located, has been compiled (aes.o) and placed in the build folder of the project. So, why can’t the linker “see” these references?

If MBEDTLS_AES_C is not defined, the aes.c file will result in a basically empty aes.o file with no implemented functionality.

Very interesting. There seems to be an actual bug in the linking logic. The aes.o file is not added to any .a archive that goes into the final linking stage.

The mbedtls libraries

.pio/build/esp32dev/esp-idf/mbedtls/libmbedtls.a
.pio/build/esp32dev/esp-idf/mbedtls/mbedtls/library/libmbedcrypto.a
.pio/build/esp32dev/esp-idf/mbedtls/mbedtls/library/libmbedtls.a
.pio/build/esp32dev/esp-idf/mbedtls/mbedtls/library/libmbedx509.a 

that are linked to not contain the aes.o file or the required functions (as can be inspected with the nm tool).

I have this in the sdkconfig file:
CONFIG_MBEDTLS_AES_C=y
and the aes.o in the project build folder (‘C:\Users\niels\Documents\PlatformIO\Projects\BlueConfig.pio\build\nodemcu-32s\mbedtls\mbedtls\library’) is 44KB, but of cause it might not be correct.

Is CONFIG_MBEDTLS_AES_C translated to MBEDTLS_AES_C before compilation?

Hm no that’s actually not true. It does get added to an archive file and linked in, but the mbedtls_aes_init function is not there.

xtensa-esp32-elf-ar rc .pio/build/esp32dev/esp-idf/mbedtls/mbedtls/library/libmbedcrypto.a .pio/build/esp32dev/mbedtls/mbedtls/library/aes.o [...]

$ nm .pio/build/esp32dev/esp-idf/mbedtls/mbedtls/library/libmbedcrypto.a | grep "mbedtls_aes"
00000000 T mbedtls_aes_self_test
$

There may be some special logic here for mbedtls to redirect it to ROM or something.

The aes.o file already doesn’t contain the file, it has U for undefined.

i$ nm .pio/build/esp32dev//mbedtls/mbedtls/library/aes.o
00000000 r aes_test_cbc_dec
00000000 r aes_test_cbc_enc
00000000 r aes_test_cfb128_ct
00000000 r aes_test_cfb128_iv
00000000 r aes_test_cfb128_key
00000000 r aes_test_cfb128_pt
00000000 r aes_test_ctr_ct
00000000 r aes_test_ctr_key
00000000 r aes_test_ctr_len
00000000 r aes_test_ctr_nonce_counter
00000000 r aes_test_ctr_pt
00000000 r aes_test_ecb_dec
00000000 r aes_test_ecb_enc
00000000 r aes_test_ofb_ct
00000000 r aes_test_ofb_iv
00000000 r aes_test_ofb_key
00000000 r aes_test_ofb_pt
00000000 r aes_test_xts_ct32
00000000 r aes_test_xts_data_unit
00000000 r aes_test_xts_key
00000000 r aes_test_xts_pt32
         U esp_aes_crypt_cbc
         U esp_aes_crypt_cfb128
         U esp_aes_crypt_ctr
         U esp_aes_crypt_ecb
         U esp_aes_crypt_ofb
         U esp_aes_crypt_xts
         U esp_aes_free
         U esp_aes_init
         U esp_aes_setkey
         U esp_aes_xts_free
         U esp_aes_xts_init
         U esp_aes_xts_setkey_dec
         U esp_aes_xts_setkey_enc
00000000 T mbedtls_aes_self_test
         U memcmp
         U memcpy
         U printf
         U putchar
         U put

Aha, this might give a clue:

Aha. When compiling the sources in src/, it missing the macro

-DMBEDTLS_CONFIG_FILE=\"mbedtls/esp_config.h\"

in the compile flags, that is given when everything else is compiled. That cauases blufi_security.c to use a standard config file and try to find the symbol mbedtls_aes_init, when due to the real config file applied earlier it should be looking for esp_aes_init etc. Let’s try and fix this with build_flags…

Successfull compilation with

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = espidf
; remove for everyone
build_unflags = 
  '-DMBEDTLS_CONFIG_FILE="mbedtls/esp_config.h"'
; include for everyone (applies to esp-idf **and** our sources)
build_flags = 
  '-DMBEDTLS_CONFIG_FILE="mbedtls/esp_config.h"'

That seems to be defined in ‘esp_config.h’

/* The following units have ESP32 hardware support,
   uncommenting each _ALT macro will use the
   hardware-accelerated implementation. */
#ifdef CONFIG_MBEDTLS_HARDWARE_AES
#define MBEDTLS_AES_ALT
#else
#undef MBEDTLS_AES_ALT
#endif

But how it’s mapped in is beyond me.
The CONFIG_MBEDTLS_HARDWARE_AES is defined in the sdkconfig file.

What actually happens with this build flag? It looks like you are un-flagging the same build flag that you flag in the line below?