Undefined reference for external function in pre-built library

arex-ebee
Posts: 10
Joined: Thu Dec 16, 2021 1:44 pm

Undefined reference for external function in pre-built library

Postby arex-ebee » Fri Jan 05, 2024 3:06 pm

I have a curious linker problem with an "extern" declared function in a prebuilt library which I don't really understand.

Short background: I got a prebuilt static library (.a) that requires some functions to be defined by the application to result in an executable. Let's call such a function prebuilt_ext_func().

I was able to break down my use case to the IDF example at build_system/cmake/import_prebuild. There, only the declaration and call of the prebuilt_ext_func() was added to the prebuilt.c file

prebuild.c:

Code: Select all

#include "esp_ota_ops.h"
#include "esp_partition.h"
#include "esp_log.h"

extern void prebuilt_ext_func(const char *);

const char *TAG = "prebuilt";

void prebuilt_func(void)
{
    const esp_partition_t* running_partition = esp_ota_get_running_partition();
    ESP_LOGI(TAG, "The running partition is '%s'!", running_partition->label);
    prebuilt_ext_func("Hello from prebuilt lib");
}
The example trims the whole build process to only build the components without linking them to an executable resulting in a "libprebuilt.a" file.

Code: Select all

 CMakeLists.txt
...
# Since we're only interested in the prebuilt library, trim
# the build
set(COMPONENTS prebuilt main esptool_py)
...
The according lib is then copied into the actual application's "main" component directory and referenced in its CMakeLists.txt via add_prebuilt_library().

If I now define the prebuilt_ext_func() in the application's main component main.c file everything compiles and links (and actually runs on target). The symbol is also present in libmain.a:

Code: Select all

$ nm -g -C build/esp-idf/main/libmain.a 

main.c.obj:
00000000 T app_main
00000000 T prebuilt_ext_func
         U prebuilt_func
         U printf
But if I add an additional source file named prebuilt_ext_func.c to the main component instead (and include them in CMakeLists.txt file) I get an "undefined reference to `prebuilt_ext_func'" error from the linker.

The compiler actually runs through prebuilt_ext_func.c (validated via #error statement in code), performing an nm call to libmain.a also shows that the symbol prebuilt_ext_func() is present:

Code: Select all

$ nm -g -C build/esp-idf/main/libmain.a 

main.c.obj:
00000000 T app_main
         U prebuilt_func

prebuilt_ext_func.c.obj:
00000000 T prebuilt_ext_func
         U printf


For a complete overview I attached the modified example project.

The simple question I have: Where is the error? From my thoughts both approaches should behave identical from linker's perspective. I would appreciate any hints...


Thanks,
André
Attachments
import_prebuilt_ext_func.zip
(5.15 KiB) Downloaded 456 times

SparkyNZ
Posts: 48
Joined: Thu Jan 04, 2024 9:01 pm

Re: Undefined reference for external function in pre-built library

Postby SparkyNZ » Sat Jan 06, 2024 4:06 am

I can see both your files are .c files. Do you have .cpp files in the mix anywhere? I've had problems in the past where I've included C and C++ files with software.. but usually that related to my incorrect of extern "C" and the likes.

Probably has nothing to do with your problem but just thought I'd put it out there.

arex-ebee
Posts: 10
Joined: Thu Dec 16, 2021 1:44 pm

Re: Undefined reference for external function in pre-built library

Postby arex-ebee » Mon Jan 08, 2024 6:22 am

Hi SparkyNZ,

thanks for your thoughts. I intentionally avoided to mix up the example with C++ files as I'm aware of the name mangling pitfall in that case. To be honest, my real application uses C++. But as the simplified, plain C example revealed it's not caused by that topic.

Cheers
André

MicroController
Posts: 1696
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Undefined reference for external function in pre-built library

Postby MicroController » Mon Jan 08, 2024 10:44 am

Another thought:
Inside your project, you have another project "prebuilt" which has its own project-local sub-component "prebuilt" and a "main"; and you want to 'expose' the "prebuilt" sub-component as a lib. Maybe something is going wrong here because of the 'nesting' of projects and the special role of "main" in the build process.
Have you tried making the prebuilt lib a 'bare' component, i.e. without 'wrapping' it into its own project?

arex-ebee
Posts: 10
Joined: Thu Dec 16, 2021 1:44 pm

Re: Undefined reference for external function in pre-built library

Postby arex-ebee » Mon Jan 08, 2024 11:40 am

As my demo only was a modification of the official IDF "import_prebuilt" example I expected this construct being correct. Nevertheless, I tried to extract the 'prebuilt' subproject from the example as you proposed. But the result is exactly the same :(.

arex-ebee
Posts: 10
Joined: Thu Dec 16, 2021 1:44 pm

Re: Undefined reference for external function in pre-built library

Postby arex-ebee » Mon Jan 15, 2024 9:03 am

Update: I was able to find a solution for this rather generic problem.

The root cause was a circular dependency between the prebuilt library and the application code: The library depends on the application to implement "prebuilt_ext_func()" while the application depends on the library to implement "prebuilt_func()". Such circular dependency requires some attentation a get it handled correctly by the linker. There is also an appropriate paragraph in the IDF Build System documentation about (https://docs.espressif.com/projects/esp ... pendencies) and others obviously also stumbled upon that pitfall (see viewtopic.php?f=13&t=18832)

But my focus was to avoid that situation at all. In result I introduced an additional component "prebuilt_support" that implements the external library calls instead of mixing them up with the main component. The component of the "prebuilt" library only requires an additionally declared dependency to "prebuilt_support". By doing that I have a straightforward dependency graph again as the prebuilt component now only depends on "prebuilt_support" component:

main --> prebuilt --> prebuilt_support


This solved my problem in result although it is basically more a workaround than a solution as I changed my starting point ;). Thanks to all for your input.

Regards
André

Who is online

Users browsing this forum: No registered users and 80 guests