While working on our Creating a Cross-Platform Build System for Embedded Projects using Meson course, I fell into a rabbit hole while writing the lesson on link-time optimization (LTO).
I enabled LTO support using Meson’s built-in option:
meson buildresults -Db_lto=true
Everything worked just fine with the native toolchain setup.
Next, I tested LTO support with cross-compilation:
meson buildresults -Db_lto=true --cross-file=build/cross/arm.txt --cross-file=build/cross/cortex-m4_hardfloat.txt
Our cross-compilation rules use the gnu-arm-none-eabi toolchain:
[binaries]
c = 'arm-none-eabi-gcc'
cpp = 'arm-none-eabi-c++'
ar = 'arm-none-eabi-ar'
strip = 'arm-none-eabi-strip'
When I ran the build, I was surprised to receive a flood of warnings from arm-none-eabi-ar:
arm-none-eabi-ar: src/25a6634@@c@sta/stdlib_imaxabs.c.o: plugin needed to handle lto object
arm-none-eabi-ar: src/25a6634@@c@sta/stdlib_imaxdiv.c.o: plugin needed to handle lto object
arm-none-eabi-ar: src/25a6634@@c@sta/stdlib_labs.c.o: plugin needed to handle lto object
arm-none-eabi-ar: src/25a6634@@c@sta/stdlib_ldiv.c.o: plugin needed to handle lto object
I researched the problem and found multiple recommendations to add a --plugin argument to the arm-none-eabi-ar call to fix the problem:
--plugin=$(arm-none-eabi-gcc --print-file-name=liblto_plugin.so)
I tested this approach by manually adding the argument to the generated -ar command, and it did address the problem. However, I just couldn’t believe this was actually the solution. It’s such a hacky flag to add, there’s no way everyone is doing this when they’re using LTO.
I did a bit more research, and this Stack Overflow thread keyed me into the solution: use arm-none-eabi-gcc-ar instead of arm-none-eabi-ar. The *-gcc-ar variant is a wrapper for *-ar that provides the plugin argument(s) we need.
gcc-ar ... => ar --plugin=/path/to/liblto_plugin.so ...
Once I switched my toolchain to use *-gcc-ar, the warnings vanished.
[binaries]
c = 'arm-none-eabi-gcc'
cpp = 'arm-none-eabi-c++'
# *-gcc-ar is used over *-ar to support LTO flags.
# Without -gcc-ar, LTO will generate a linker warning:
# arm-none-eabi-ar: file.o: plugin needed to handle lto object
ar = 'arm-none-eabi-gcc-ar'
strip = 'arm-none-eabi-strip'
My conclusion is that we should prefer gcc-ar over ar by default in our build systems. This way, we can support LTO as an option for our projects.
It is also worth noting that both nm and ranlib have gcc-nm and gcc-ranlib equivalents which provide the proper LTO plugin arguments. They should be used whenever you are working with LTO files.
