Board_build.embed_txtfiles does not create symbol names

Hey,

I have a problem with embedding and using a certificate in my ESP32 application (ESP-IDF framework, not Arduino). I am using PlatformIO 4.3.1 and the Espressif32 framework 1.12 (ESP-IDF 4.0).

Within platformio.ini I specified my .pem file to be included into my binary. This seems to work as I do not get an error message. Within my source code (main.c) I am using

extern const uint8_t server_cert_pem_start[] asm("_binary_src_cert_pem_start");

to reference the certificate. The linker, however, tells me that it cannot find _binary_src_cert_pem_start

Here is the full error message:

/Users/Mr_Blonde/.platformio/packages/toolchain-xtensa32/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld: .pio/build/pico32/esp-idf/src/main.c.o:(.literal.simple_ota_example_task+0xc): undefined reference to `_binary_src_cert_pem_start'
collect2: error: ld returned 1 exit status
*** [.pio/build/pico32/firmware.elf] Error 1

For me it seems that the linker reference / symbol name is not properly created by the build environment. I tried to use:

board_build.embed_txtfiles =
    src/cert.pem

as well as

build_flags = -DCOMPONENT_EMBED_TXTFILES=src/cert.pem

within platformio.ini. But neither worked. I also modified the reference in the source code to exclude the _src (as mentioned in the PlatformIO documentation) with, again, no success. My question is: What am I doing wrong? Do I have to add/modify something else besides platformio.ini and my source code? Do I need to add something to CMakeLists.txt (or other files)? I have seen modifications within CMakeLists.txt but my understanding is that this is only required for using the ESP-IDF natively (without platformio).

Thanks in advance!

my platformio.ini:

[env:pico32]
platform = espressif32
board = pico32
framework = espidf
board_build.partitions = partition_ota.csv
monitor_speed = 115200
monitor_port = /dev/cu.usbserial-14201
upload_port = /dev/cu.usbserial-14201
board_build.embed_txtfiles =
    src/cert.pem
;build_flags = -DCOMPONENT_EMBED_TXTFILES=src/cert.pem

Directory structure:
[project-folder]
- src/
- - main.c
- - cert.pem
- platformio.ini

Source snippet:

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_ota_ops.h"
#include "esp_http_client.h"
#include "esp_https_ota.h"
#include "string.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "esp_wifi.h"

extern const uint8_t server_cert_pem_start[] asm("_binary_src_cert_pem_start");
extern const uint8_t server_cert_pem_end[] asm("_binary_src_cert_pem_end");

void simple_ota_example_task(void *pvParameter) {
    ESP_LOGI(TAG, "Starting OTA example");
    esp_http_client_config_t config = {
        .url = FIRMWARE_UPGRADE_URL,
        .cert_pem = (char *)server_cert_pem_start,      // that's the line the linker has its issue with
        .event_handler = _http_event_handler,
    };
   // More code to follow
}

void app_main(void) {
 // DO STUFF HERE
}

Can you build platform-espressif32/examples/espidf-aws-iot at develop · platformio/platform-espressif32 · GitHub with the CONFIG_EXAMPLE_EMBEDDED_CERTS configuration (pio run -t menuconfig)

Have you tried adding the CMakeLists commands equivalent to those in the example?

1 Like

Mhmm, interesting, I can compile the AWS example without a problem.

Modifying my CMakeLists.txt, however, does not solve my problem. I even copied over the AWS certificate (to rule out a certificate related problem), left the path from the example as is (src/certs/aws-root-ca.pem) and added CONFIG_EXAMPLE_EMBEDDED_CERTS=y to my sdkconfig file. But still: undefined reference.

It seems that my project (which is basically a plain platform generated ESP-IDF project) has some setting, which interferes with my symbol table. Do I have to modify something in sdkconfig, maybe?

Does this project compile and run?

1 Like

@maxgerhardt Thank you very much for your help and the extra mile you went by providing me with your simplistic example! Much appreciated!

(for people with the same problem, a TL:DR version is below)

Your example worked right away (as did the AWS-IOT one you posted above). That left me puzzled as to why my own did not work. Therefore, I also created a new simplistic project right out of PlatformIO (very similar to yours). But it didn’t work either. For the first version I did not modify src/CMakeLists.txt. After the first failed attempt, I remembered to include a line there as well and also modified CMakeList.txt. Again, no success. I then compared your project against mine and couldn’t find a significant difference.

Because I got mixed up with my project folders I deleted my project folder from VisualStudioCode’s Workspace and reimported it. All of a sudden, it worked (with no modification to any files).

Again, being puzzled, I created yet a new project and modified platform.ini, src/CMakeList.txt as well as the source code (empty.c) and it worked right away. Strange, I thought. So, I created another project (third dummy project by now). Deliberately missed to modify CMakeList.txt, which resulted in the known linker error (as expected). Afterwards I modified CMakeList.txt and rebuild the project -> which failed. Even using platformio’s clean function (and rebuild) did not solve the problem.

Finally I figured out what does: One has to delete .pio/build manually after modifying CMakeList.txt in order for the changes to take any effect. It seems that something within the build system is not refreshed after the first time CMakeList.txt has been read. All subsequent modifications are ignored. Deleting .pio/buid forces the build system to have another look at the file and taking the modifications into account.

Is this an intended behavior? At least for me it is not obvious that one has to delete .pio/build in order to make effective changes to CMakeList.txt (besides the option to have everything correct before the very first build run).


TL:DR

There are three things one needs to take care of in order for it to work:

  1. add board_build.embed_txtfiles = src/cert.pem to platform.ini
  2. add target_add_binary_data(${COMPONENT_TARGET} "cert.pem" TEXT) to src/CMakeList.txt
  3. do not include the folder structure in your symbol name (extern const uint8_t cert_pem_start[] asm("_binary_cert_pem_start");)

In case you have already build your project before modifying src/CMakeList.txt, make sure to delete .pio/build in order for the changes to take effect.

1 Like