Linker problems using STM32CubeMX HAL libraries

I created my project for NUCLEO F756ZG board from STM. Project was initially created using the SMT32CubeMX framework, however I commented in out of the .ini file because I’m using HAL libraries, generated from CubeMX (and included under src folder). Additionally, I provided include paths into .ini file.

Originally I had only C files. Build was just fine at this point. But then I added app.cpp and corresponding app.h file. Both provide declaration and definition of a simple “Hello World!” function, which is called from main.c. Here, at the end of build, I get the following linker error (this is generated after all .o files have been generated):

Linking .pio\build\nucleo_f756zg\firmware.elf
.pio\build\nucleo_f756zg\src\Core\Src\sysmem.o: In function `_sbrk':
sysmem.c:(.text._sbrk+0x38): undefined reference to `_estack'
sysmem.c:(.text._sbrk+0x3c): undefined reference to `_Min_Stack_Size'
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\nucleo_f756zg\firmware.elf] Error 1

I searched for this problem and found that most likely these two options should be added to compiler options (therefore build_flags ?): -specs=nano.specs and -specs=nosys.specs. After adding them to my .ini file, it looks like this:

Just in case I’m pasting my .cpp and its .h file here:

#ifndef APP_H
#define APP_H

#include "main.h"

#ifdef __cplusplus
extern "C" {
#endif

int App(void);

#ifdef __cplusplus
}
#endif

#endif
#include <iostream>
#include "app.h"

extern int App(void)
{
    //std::cout << "Hello, world!" << std::endl;
    return 0;
}

I think that those options I added myself don’t have any effect. Or maybe those two aren’t even included into a compilation process. Additionally, I added the -lstdc++ options, which is used to link standard C++ libraries (I think) however no change in the build log. Does anyone have any idea what can I do in such case? I’m I doing something obviously wrong here?

Without you using framework = stm32cube the burden of providing all the right files and settings lies upon you.

The missing thing is that you’ve not copied the STM32CubeMX auto-generated linker file (STM32F756ZG_FLASH.ld or something) and referenced in the platformio.ini by means of board_build.ldscript.

How should I handle things then? Use existing framework PlatformIO has to offer and somehow exclude generated files from CubeMX? Or maybe just copy main.c from CubeMX’s generated code and put it into PlatformIO project?

Yes exactly. You just don’t put startup file, linker file, CMSIS core and STM32HAL code into the project. Only application code and middlewares, if any, need to be added and configured (build_flags as needed). Certain components from STM32Cube framework, like BSPs, can also be activated. All that is described in

https://docs.platformio.org/en/latest/frameworks/stm32cube.html

and the reference examples

1 Like

The files you see are the ones CubeMX generated. Unless I should provide somewhere that STM32F756ZG_FLASH.ld should be used instead. However I’m not sure if this would solve anything, since STM32F756ZG_FLASH.ld can probably take into account just initially generated C files (HAL libraries). My additionally added C++ files cannot be accounted into since I created it after CubeMX code was already generated.

Ah, indeed that .ld file is already there. But you haven’t referenced it in the platformio.ini

And of course it will solve the linking problem. The linker script is defining the symbols missing in the final link.

There might be a better solution to this by using this stm32pio script (as suggested by PlatformIO documentation for integration of STM32CubeMX code). However, if I will have enough problems with current setup, I will be forced to try this out :sweat_smile:

How do I do that? Also, can you elaborate on the “final link” term?

You need to follow the link there–.

At first the .c / .cpp files are compiled to object (.o) files, .S/.s files will be assembled, and the final step in the firmware build process is the linking of it all together. There it will resolve references made by the code against other files. The linker script (.ld) can actually create symbols that code files can reference against.

The two missing symbols that sbrk() (for malloc()) needs, being _Min_StackSize and _estack (end of stack) are defined by the linker script.

And these symbols are not defined by the default linker script from PlatformIO? I think they are provided inside .ld. FLASH file from CubeMX.

Thanks for the valuable info. I will provide some feedback after trying this out :slight_smile:

Okay, I tried this out. Provided .ini file with option board_build.ldscript and providing absolute path to CubeMX linker, project build and link is done without errors.

However, since you mentioned that one way to do things in situation of using STM32CubeMX is to only use main.c from it and use default HAL libraries that are provided from PlatformIO folders. Do you think this approach is better (from any perspective)? I find my current approach more practical in terms I have to change something in CubeMX and regenerate code. Because I don’t need to copy anything each time I do this. And separating main.c from HAL libraries where both were created by one utility, I would say this might cause errors eventually.

Sure, if you often regenerate the code using STM32CubeMX, then your workflow is easier.