platformio / platform-ststm32

ST STM32: development platform for PlatformIO
https://registry.platformio.org/platforms/platformio/ststm32
Apache License 2.0
387 stars 305 forks source link

Reconsider default link flags #360

Open puzrin opened 4 years ago

puzrin commented 4 years ago

Ref: https://community.platformio.org/t/why-impure-data-appears-on-custom-build-and-eats-1k-memory-need-fix/11353

I currently use this script to fix build size. IMO this could work by default.

valeros commented 4 years ago

Hi @puzrin, thanks for the report ! What exactly are you proposing? I see in your extra script you removed -nostartfiles and -nostdlib flags? Is there a reason for this?

puzrin commented 4 years ago

It would be nice to apply ALL changes from that script. Because:

  1. Defaults produce bigger output file (see forum thread).
  2. Build fails without e.Replace(AS = '$CC', ASCOM = '$ASPPCOM')
  3. Impossible to pass linker flags without -Wl, prefix, and different kind of issues with that.

You can delete any line in that script an check difference. I'm not expert in this area, so only did things work the same way as with your "stmcube framework". May be can be done better.

Consider it from another point of view:

valeros commented 4 years ago

Defaults produce bigger output file (see forum thread).

Answered in that topic.

Build fails without e.Replace(AS = '$CC', ASCOM = '$ASPPCOM')

What's the error exactly?

defaults (bare.py) and stmcube framework (ARM) options are out of sync, and it's not clear why.

They are not out of sync. _bare.py contains only a minimal set of flags required to compile framework-less projects.

problem with passing linker options without prefix.

Starting with the next release, you don't need to use -Wl option instead use board_build.ldscript = path/to/script.ld in your platfromio.ini.

puzrin commented 4 years ago

What's the error exactly?

arm-none-eabi-as: unrecognized parameter «-x»

They are not out of sync. _bare.py contains only a minimal set of flags required to compile framework-less projects.

I don't like this approach. As user, i expect not "minimal set" but "useable set". Also, without change -nostartfiles -nostdlib to --specs=nano.specs --specs=nosys.specs build fails:

/home/vitaly/.platformio/packages/toolchain-gccarmnoneeabi/bin/../lib/gcc/arm-none-eabi/7.2.1/../../../../arm-none-eabi/bin/ld: .pio/build/hardware_usb/firmware.elf section `.text' will not fit in region `FLASH'
/home/vitaly/.platformio/packages/toolchain-gccarmnoneeabi/bin/../lib/gcc/arm-none-eabi/7.2.1/../../../../arm-none-eabi/bin/ld: .pio/build/hardware_usb/firmware.elf section `._user_heap_stack' will not fit in region `RAM'
/home/vitaly/.platformio/packages/toolchain-gccarmnoneeabi/bin/../lib/gcc/arm-none-eabi/7.2.1/../../../../arm-none-eabi/bin/ld: region `RAM' overflowed by 960 bytes
/home/vitaly/.platformio/packages/toolchain-gccarmnoneeabi/bin/../lib/gcc/arm-none-eabi/7.2.1/../../../../arm-none-eabi/bin/ld: region `FLASH' overflowed by 53288 bytes
.pio/build/hardware_usb/hal/stm32f072cb/app_hal.o: In function `_GLOBAL__sub_I__ZN3hal12key_start_onEv':
/home/vitaly/Dropbox/Coding/dispenser/hal/stm32f072cb/app_hal.cpp:267: undefined reference to `__dso_handle'
.pio/build/hardware_usb/src/screen_flow.o: In function `_GLOBAL__sub_I__Z20base_update_value_fnPK11_param_datahi':
/home/vitaly/Dropbox/Coding/dispenser/src/screen_flow.cpp:225: undefined reference to `__dso_handle'
.pio/build/hardware_usb/src/screen_settings.o: In function `base_get_text_fn':
/home/vitaly/Dropbox/Coding/dispenser/src/screen_settings.cpp:71: undefined reference to `__dso_handle'
/home/vitaly/.platformio/packages/toolchain-gccarmnoneeabi/bin/../lib/gcc/arm-none-eabi/7.2.1/../../../../arm-none-eabi/lib/thumb/v6-m/libc.a(lib_a-init.o): In function `__libc_init_array':
init.c:(.text.__libc_init_array+0x1a): undefined reference to `_init'
/home/vitaly/.platformio/packages/toolchain-gccarmnoneeabi/bin/../lib/gcc/arm-none-eabi/7.2.1/../../../../arm-none-eabi/lib/thumb/v6-m/libc.a(lib_a-fini.o): In function `__libc_fini_array':
fini.c:(.text.__libc_fini_array+0x20): undefined reference to `_fini'
/home/vitaly/.platformio/packages/toolchain-gccarmnoneeabi/bin/../lib/gcc/arm-none-eabi/7.2.1/../../../../arm-none-eabi/bin/ld: .pio/build/hardware_usb/firmware.elf: hidden symbol `__dso_handle' isn't defined
/home/vitaly/.platformio/packages/toolchain-gccarmnoneeabi/bin/../lib/gcc/arm-none-eabi/7.2.1/../../../../arm-none-eabi/bin/ld: final link failed: Bad value

You can clone repo and experiment with py file content as you wish. The only reducible thing is moving ldscript definition.


May be i do something wrong, i have no experience with gcc toolchain. IMO i have a very simple need - build project without built-in pio framework (project uses more fresh version from cubemx for integrity reasons). _bare.py is intended to solve such task, but doesn't look ready at this moment (needs hacking).

PS. I've updated ldscript definition as suggested, thanks.

valeros commented 4 years ago

arm-none-eabi-as: unrecognized parameter «-x»

If I'm not mistaken, you can specify the extension of assembly files in uppercase .S, instead of replacing ASM command.

undefined reference to `__dso_handle'

A bunch of errors with __dso_handle and _fini are caused by static objects you have somewhere in your code. Firmware never exits in the same way as a usual program does. On embedded systems you can use -fno-use-cxa-atexit in CXXFLAGS in order to disable teardown code (including global destructors).

undefined reference to `_init'

As for missing _init. In my opinion that's only related to STM32Cube since their startup files are using __libc_init_array routine. We cannot focus only on this framework, the flags in _bare.py is a suite that's not supposed to work out of the box, it provides only a minimal base for users to construct their build environment.

--specs=nano.specs --specs=nosys.specs

The same thing here, there are other possible specs (e.g rdimon), we don't want to force users to use only newlib-nano version of standard libraries.

puzrin commented 4 years ago

If I'm not mistaken, you can specify the extension of assembly files in uppercase .S, instead of replacing ASM command.

That's not good idea for maintainability. See this folder. If i need to update pins, i just do changes in cubemx and push "generate" button. Then this code used from main app. That's convenient:

Also note, you use the same approach with ASM in stm32cube framework, i did not invented anything.

We cannot focus only on this framework, the flags in _bare.py is a suite that's not supposed to work out of the box, it provides only a minimal base for users to construct their build environment.

--specs=nano.specs --specs=nosys.specs

The same thing here, there are other possible specs (e.g rdimon), we don't want to force users to use only newlib-nano version of standard libraries.

Well, may be i misunderstand your expectations about standalone projects. But do you have working examples of those? Where user is expected to take .s & .ld files? I've taken my one from the most obvious place - stm32cube (= provided by vendor). And those are not "compatible" with your _bare.py. If better sources available - let me know.

I understand desire to avoid push for certain libs. But i don't understand how to make work your defaults. IMO default should work somehow.

valeros commented 4 years ago

That's not good idea for maintainability. See this folder. If i need to update pins, i just do changes in cubemx and push "generate" button. Then this code used from main app. That's convenient:

Sounds reasonable, so e.Replace(AS = '$CC', ASCOM = '$ASPPCOM') is indeed necessary. As you correctly noticed, it's used only for STM32Cube, so maybe it's a good idea not to pollute the bare environment.

Well, may be i misunderstand your expectations about standalone projects. But do you have working examples of those? Where user is expected to take .s & .ld files?

There are no such examples, it's totally up to the user where to locate startup files, HAL, linker scripts (src folder, external library, etc). The user is responsible for the correct set of build flags.

_bare.py script was created mainly to avoid code duplication as it's reused in build scripts for CMSIS and SPL. There is a build script for stm32cube and it should work out of the box, but I can admit that support for this framework is a bit outdated. You decided that it's not suitable for your project, as you need keep it compatible with the official STM32 tools, therefore now you're responsible for the correct build environment for your project. Feel free to completely replace build environment in an extra script, it's totally fine to do so if bare flags are not good for you.

puzrin commented 4 years ago

As you correctly noticed, it's used only for STM32Cube, so maybe it's a good idea not to pollute the bare environment.

I said a bit different thing. Cube is the most obvious source of .s & .ld files for stm32xxx.

_bare.py script was created mainly to avoid code duplication as it's reused in build scripts for CMSIS and SPL.

If it's a kind of base for programming inherinance, i'd suggest to consider 2 points:

  1. May be, -nostartfiles & -nostdlib should be moved to end scripts, where those really needed (those are not universal).
  2. Consider consistent "framework-less" option. Ma be not very flexible, but working. It's not obvious, how to make build without framework work. Some templates could save a lot of time to next users.

I can not insist on concrete choices due limited experience. Feel free to close this issue anytime if you think it's ok.

valeros commented 4 years ago

I don't know alternative sources (and i doubt anybody write those files from scratch)

I agree that STM32Cube is the first option that comes into mind, but there are a few less popular libraries like stm32plus, modm or even libopencm3.

Maybe, -nostartfiles & -nostdlib should be moved to end scripts, where those really needed (those are not universal).

I agree on this one, at the moment it seems more like a legacy thing as there are projects that don't use any framework and rely on these flags, so I'd prefer not to remove them, at least for now.

Consider consistent "framework-less" option. Ma be not very flexible, but working. It's not obvious, how to make build without framework work. Some templates could save a lot of time to next users.

Still, I consider "framework-less" as an advanced way of compilation projects. Users who choose this option should know what the startup files and linker scripts are and how to properly configure linker to use the correct standard libraries.

puzrin commented 4 years ago

Still, I consider "framework-less" as an advanced way of compilation projects. Users who choose this option should know what the startup files and linker scripts are and how to properly configure linker to use the correct standard libraries.

That's not enough. They should also fight with pio defaults & specifics :)

Note, decribed things are not about build tools. Those are specific to pio. May be, some examples (like one provided by me) could help to next followers.

I don't think my situation is unordinary. Just follow this logic:

IMO, enhance docs with real example "how to integrate CubeMX with PIO in painless way" would be useful. That will solve popular demand and will not require any script change at your side.

valeros commented 4 years ago

Don't you agree that your project is not quite "framework-less"? In my opinion, the main problem here is that the current support for STM32Cube is not good enough to work with CubeMX tools.

Just to wrap up:

So I'm leaving this issue open until we come up with a more proper integration for STM32Cube framework.

Sounds good?

puzrin commented 4 years ago

Sounds good?

As you wish. My intent was to share collected experience, because it's not obvious, even when user has correct Makefile by CubeMX.

A good idea might be an "importer" that can work with native .ioc projects.

IMO:

In my quest i needed a simple thing: just build sm32 app with cube's .S & .ld files. Small .py options patcher looks more simple and works "right now". But i don't insist, only share existing solution. If that will be useful - nice, if not - no problem.

valeros commented 4 years ago

BTW, have you seen this project https://github.com/ussserrr/stm32pio ?

puzrin commented 4 years ago

Yes. As you can see in my example - need to copy 1 .py file and add 3-5 lines to platform.ini. I could not find reasonable added value to do something similar via additional tool (may be i missed something).

pfeerick commented 4 years ago

Wouldn't adding functionality such as stm32pio's resolve the underlying issues, as the user would be able simply import the .ioc once they've done what they need in CubeMX? Plus it looks like it can be run again at any time if any later changes are made, keeping the PlatformIO project up to date. Making the end-user experience much better, although it's not as simple as the five or so lines used in the extra_script here ;)

puzrin commented 4 years ago

stm32cubemx change ioc internals and firmware versions very often. I see nothing amazing in duplicating code generation functionality.

once they've done what they need in CubeMX?

You can just press "generate" there and use folder with generated code in your project, without any edit. See refered project in first post for example.

puzrin commented 4 years ago

Update for 7.0.0.

I see, v7 has some flags removed. So, current custom script to organize build "without framework" is

Import("env", "projenv")

for e in [ env, projenv ]:
    e.Append(LINKFLAGS = [ "--specs=nano.specs", "--specs=nosys.specs" ])
    e.Replace(AS = '$CC', ASCOM = '$ASPPCOM')

Last line needed only because of x flag in startup file from CubeMX (no idea why it exists and what it does):

    .section .text.Default_Handler,"ax",%progbits
canardos commented 3 years ago

I also found myself fighting the build system due to issues related to this, although with the CMSIS framework rather than no framework.

I'm certainly no expert in this area, so I may be mistaken, but it seems that for a basic C++ application using CMSIS headers and stdlib functions, the current CSMSIS build defaults cause issues.

Most embedded C++ code will require static construction and thus require __libc_init_array. The current startup file used when the CMSIS framework is specified excludes this (it's commented out). Although it's simple enough to reenable, doing so will trigger linker errors as _init will be missing.

The flags causing problems can be removed in platformio.ini

build_unflags =
    -nostartfiles
    -nostdlib

but this will lead to further linker areas if the specs aren't specified.

For some reason using -Wl, didn't work, and I needed to modify cmsis.py to add the linker flags --specs=nosys.specs and --specs=nano.specs (by default CMSIS is linking to the full libc). As pointed out above, this is a pretty fragile solution.

env.Append(LINKFLAGS = [ "--specs=nano.specs", "--specs=nosys.specs" ])

Of some concern is the fact that a typical C++ project will compile and link without error using framework = cmsis, but no static construction code has been created. This will cause subtle errors. The only hint of a problem is the very slightly smaller binary when using framework = cmsis vs framework = stm32cube.

I understand the intention of the bare script is to provide a bare metal, do everything yourself approach when no framework is specified, but the CMSIS framework script has default startup/link scripts etc., so the no startup/init or specs defaults were a bit unexpected.

valeros commented 3 years ago

Hi @canardos ! What version of the platform are you using? Starting from ST STM32 v7.0.0 we use the CMSIS packages directly from the STMicroelectronics repositories without any modifications. Besides, the updated CMSIS build script already contains the --specs=nosys.specs and --specs=nano.specs flags and doesn't use -nostartfiles and -nostdlib.

canardos commented 3 years ago

Ah, that's great news. I am using the latest pio, but you are right that I was using an older version of the ST STM32 platform (v6.0.0). I just updated and reviewed, and all of the above issues have been resolved in the interim updates. I also noticed that the updated package now uses the STMicro repo CMSIS headers, which is a welcome addition.

Many thanks for the prompt response and for PlatformIO in general.