ESP-IDF Component Linker Issue (Undefined Reference)

Minimal example repository: GitHub - atfox98/golioth-pio

I’m trying to include an ESP-IDF component in my project, golioth-firmware-sdk. I am pretty sure the component is configured and added correctly: it’s options show up in the menuconfig, I can see that it is building correctly, the archive is in the build directory, and using nm shows that all of the functions are in there. When using verbose build, it looks like the directory is included correctly in the link step and all is well. At this point, however, there is a linker error. Undefined references to every function I call from the component. Looking at the map file that is generated, the component functions aren’t there.

I’ve tried the following things to resolve this issue:

  • Change LINK_INTERFACE_MULTIPLICITY in CMakeLists.txt for the golioth component. The build system page in the espressif documentation under “Circular Dependencies” mentions this can fix some undefined reference problems. Even making the value ridiciously high (100+) doesn’t work.
  • Add the linker flags manually in PlatformIO build flags (-Lpath/to/golioth_sdk and -lgolioth_sdk.a)
  • Use a pre: script to pass the same link flags as above, per this documentation

At this point I have no idea what to do and any insight would be greatly appreciated. I think this has something to do with the PlatformIO build system, I just do not know what that thing is.

It also looks like this poster was having the same issue (undefined references with a component) but there were never any responses.

The headers in golioth-firmware-sdk/include/golioth are trash. They do not contain the code to export the functions they have written in C correctly to external C++ code (source, source). That is, they don’t follow the

#ifdef __cplusplus
extern "C" {
#endif

/* all functions, structs etc. */

#ifdef __cplusplus
}
#endif

idiom.

Thus when you attempt to just include them in your main.cpp file with

the C functions are not found. You can compensate for the SDK’s failure by doing

extern "C" {
#include <golioth/client.h>
#include <golioth/config.h>
#include <golioth/fw_update.h>
#include <golioth/golioth_debug.h>
#include <golioth/golioth_status.h>
#include <golioth/golioth_sys.h>
#include <golioth/lightdb_state.h>
#include <golioth/log.h>
#include <golioth/ota.h>
#include <golioth/payload_utils.h>
#include <golioth/rpc.h>
#include <golioth/settings.h>
#include <golioth/stream.h>
#include <golioth/zcbor_utils.h>
}

in your main.cpp instead. If you used main.c it would have worked, too.

To prove my point: After doing the above two-line extern "C" fix on the application code side, your project builds just fine.

Thank you! I’m so glad the fix was only 2 lines instead of 200. I also learned how to use github actions to build thanks to your PR.

I’ll pass this on to the golioth folks too.