Repository navigation
arch: Compile an LTO module to machine code when gcc links it. - #20846
Merged
Merged
Conversation
|
With CONFIG_LTO_FULL, the Toolchain.defs of arm, arm64 and risc-v set LD to the gcc driver, and a loadable module (and a program of a kernel build) is linked with it, because MODULELD defaults to LD. The partial link (-r) of LTO objects keeps only LTO data, which the loader cannot use: "Failed to execute program 'hello': -22", and nm says "plugin needed to handle lto object". Where these Toolchain.defs set LD to gcc for LTO, they now also set LDMODULELTOFLAGS and LDELFLTOFLAGS: -flinker-output=nolto-rel and the compile options of the module. LDMODULEFLAGS and LDELFFLAGS use them next to -r. The link then compiles the objects to machine code with the options of the module (-mthumb, -mlong-calls, ...). Without these options the arm-none-eabi assembler stopped with "invalid constant (3e8) after fixup". crt0.o is now compiled without -flto. It must be machine code in any case, and as LTO data it made LTO warn that crt0 and the program declare main() differently, which -Werror makes an error. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
casaroli
force-pushed
the
module-lto-link
branch
from
October 21, 2026 07:47
ac0a52b to
167adab
Compare
xiaoxiang781216
previously approved these changes
Oct 21, 2026
jerpelea
previously approved these changes
Oct 21, 2026
LDELFFLAGS has "--oformat elf32-littleriscv" (or elf64), an option of the linker. The link of an ELF program uses MODULELD, which is LD by default, and with clang, or with gcc and CONFIG_LTO_FULL, LD is the compiler driver. The driver stops: "riscv-none-elf-gcc: error: unrecognized command-line option '--oformat'", "clang: error: unknown argument: '--oformat'". The branches that set LD to a driver now set LDXLINKER to -Xlinker, and LDELFFLAGS has "$(LDXLINKER) --oformat=elf32-littleriscv" (or elf64). With ld the option is the same as before. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
casaroli
dismissed stale reviews from jerpelea and xiaoxiang781216
via
October 21, 2026 08:20
b7abc05
jerpelea
previously approved these changes
Oct 21, 2026
With CONFIG_LTO_FULL and GCC, gcc links nuttx. Toolchain.defs adds -nostdlib to LDFLAGS, and the Makefile gives LDFLAGS to ld through -Xlinker, so gcc still added its start files (crtbegin.o, ...) and its default libraries. The linker script of qemu-armv8a:knsh discards .eh_frame, and the link stopped: `.eh_frame' referenced in section `.text.frame_dummy' of .../crtbegin.o: defined in discarded section `.eh_frame' of .../crtbegin.o Give -nostdlib to gcc too, as arch/arm does. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
With CONFIG_LTO_FULL and GCC on arm, arm64 and risc-v, or with clang on risc-v, LD is the compiler driver. The arch Makefile then links nuttx with -Wl,--start-group and -Wl,--end-group. The export of a kernel build (mkexport.sh -u) did not have LDSTARTGROUP and LDENDGROUP, so apps linked each program with its default --start-group, and the driver stopped: riscv-none-elf-gcc: error: unrecognized command-line option '--start-group' The Toolchain.defs branches that set LD to the driver now set LDSTARTGROUP and LDENDGROUP, and mkexport.sh writes them for every export. Without a value the apps default stays --start-group. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
…ions. Without CONFIG_BINFMT_ELF_RELOCATABLE (rv-virt:knsh64), a program is linked to an executable, and LDELFFLAGS has no compile options. With CONFIG_LTO_FULL, gcc links it, adds the start files of its C library, and compiles the LTO objects without the ABI of the program: multiple definition of `_start'; .../riscv-none-elf/lib/crt0.o: first defined here streams.h:783:5: internal compiler error: Segmentation fault: 11 The gcc branch now sets LDELFEXEFLAGS to the ELF compile options (-nostdlib, -march, -mabi, ...), and LDELFFLAGS has them when the program is not relocatable, as it has them with -r. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
With CONFIG_PIC (not FDPIC), or with CONFIG_BINFMT_ELF_EXECUTABLE, a program is linked to an executable. With CONFIG_LTO_FULL, gcc links it: the PIC link gave it options of ld, and both links gave it no compile options, so gcc added its start files: arm-none-eabi-gcc: error: unrecognized command-line option '--unresolved-symbols=ignore-in-object-files' crt0.c:169: multiple definition of `_start' The gcc branch now sets LDXLINKER to -Xlinker and LDELFEXEFLAGS to the ELF compile options and -nostdlib. The PIC link gives its options of ld with $(LDXLINKER), and both executable links add $(LDELFEXEFLAGS). Without LTO the options are the same as before. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
xiaoxiang781216
approved these changes
Oct 21, 2026
jerpelea
approved these changes
Oct 21, 2026
acassis
approved these changes
Oct 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
With
CONFIG_LTO_FULL(GCC), theToolchain.defsof arm, arm64 and risc-v setLDto the gcc driver, and a loadable module (and a program of a kernel build) is linked with it, becauseMODULELDdefaults toLD. The partial link (-r) of LTO objects keeps only LTO data, which the loader cannot use:exec failed: 22, andnmsaysplugin needed to handle lto object.Where these
Toolchain.defssetLDto gcc for LTO, they now also setLDMODULELTOFLAGSandLDELFLTOFLAGS:-flinker-output=nolto-reland the compile options of the module.LDMODULEFLAGSandLDELFFLAGSuse them next to-r. The link then compiles the objects to machine code with the options of the module (-mthumb,-mlong-calls, ...). Without these options the arm-none-eabi assembler stopped withinvalid constant (3e8) after fixup.crt0.ois now compiled without-flto. It must be machine code in any case, and as LTO data it made LTO warn that crt0 and the program declaremain()differently, which-Werrormakes an error.The other commits fix the other links that gcc makes with LTO:
LDELFFLAGShas--oformat elf32-littleriscv(or elf64), an option of the linker, and the gcc driver (LTO) and clang (the clang toolchain setsLDto clang) stop withunrecognized command-line option '--oformat'. The branches that setLDto a driver now setLDXLINKERto-Xlinker, andLDELFFLAGShas$(LDXLINKER) --oformat=.... With ld the option is the same as before.Toolchain.defsadds-nostdlibtoLDFLAGS, and the Makefile givesLDFLAGSto ld through-Xlinker, so gcc still added its start files.qemu-armv8a:knshthen stopped on the.eh_frameofcrtbegin.o.-nostdlibnow goes to gcc too, as on arm.mkexport.sh -u) had noLDSTARTGROUPandLDENDGROUP, so apps linked each program with--start-group, which gcc rejects. The branches that setLDto the driver now set them to-Wl,--start-groupand-Wl,--end-group, andmkexport.shwrites them for every export.CONFIG_BINFMT_ELF_RELOCATABLE(rv-virt:knsh64): a program is linked to an executable with no compile options, so gcc added thecrt0.oof its C library (multiple definition of '_start') and crashed in LTRANS without the ABI options.LDELFEXEFLAGSnow gives the ELF options to this link.CONFIG_PIC(not FDPIC) orCONFIG_BINFMT_ELF_EXECUTABLE: a program is linked to an executable too. The PIC link gave it options of ld (unrecognized command-line option '--unresolved-symbols=ignore-in-object-files'), and gcc added its start files. These links now give the options of ld with$(LDXLINKER)and add$(LDELFEXEFLAGS)(the ELF options and-nostdlib).This replaces the
Make.defspart of apache/nuttx-apps#3884, as asked there. That PR keeps only the-Wl,--defsym=form of the program attributes for the gcc driver; a module links only with both.Impact
CONFIG_LTO_FULLwith GCC on arm, arm64 and risc-v: the modules (flat build) and the programs (kernel build) link to machine code and load.LDELFFLAGSno longer stops the driver.CONFIG_LTO_FULL: a protected build fails on master already in the link of the user image (the boardkernel/Makefilegives--undefined=and--start-groupto$(LD)). Theqemu-armv7a:knshimage (armv7-a, also armv7-r and armv8-r) fails on master already:dispatch_syscall()readsg_stublookuponly in assembly, which LTO does not see (undefined reference to 'g_stublookup').LDto a compiler driver: no change.MODULELDis ld): no change.crt0.ois machine code also with LTO.Testing
qemu-armv8a:nsh(flat, Arm GNU 14.2, QEMU HVF) withCONFIG_LTO_FULL,CONFIG_ELF,CONFIG_EXECFUNCS_SYSTEM_SYMTABandCONFIG_EXAMPLES_HELLO=m, apps with apache/nuttx-apps#3884,helloloaded from a 9p share:hellolink-Wl,--defsym=...only-flinker-output=nolto-reland the module options (with-Werror)nm helloplugin needed to handle lto objectnxposix_spawn_exec: ERROR: exec failed: 22Hello, World!!The same test with QEMU TCG and
helloon a semihosting hostfs, apps with apache/nuttx-apps#3884:hellorv-virt:nsh64,CONFIG_LTO_FULL(master:unrecognized command-line option '--oformat')Hello, World!!rv-virt:nsh64, no LTO (ld)Hello, World!!mps2-an500:nsh,CONFIG_LTO_FULL, Arm GNU 15.3Hello, World!!rv-virt:knsh64, no LTOExiting with status 0rv-virt:knsh64,CONFIG_LTO_FULL(master:unrecognized command-line option '--start-group')Exiting with status 0qemu-armv8a:knsh,CONFIG_LTO_FULL, HVF (master:.eh_frameofcrtbegin.o)Exiting with status 0qemu-armv8a:nsh,CONFIG_LTO_FULL, HVFExiting with status 0qemu-armv8a:knshand:nsh, no LTOExiting with status 0mps3-an547:picostest(CONFIG_PIC),CONFIG_LTO_FULL, romfs on/pic(master:unrecognized command-line option '--unresolved-symbols=ignore-in-object-files')/pic/helloHello, World!!,/pic/ostestExiting with status 0mps3-an547:picostest, no LTOqemu-armv7a:knsh(CONFIG_BINFMT_ELF_EXECUTABLE), no LTOExiting with status 0LDELFFLAGSandLDMODULEFLAGSas make expands them, before and after this change: the same forqemu-armv8a:nsh,qemu-armv8a:knsh,mps2-an500:nsh,rv-virt:nshandrv-virt:knsh(risc-v:--oformat=elf32-littleriscvin place of--oformat elf32-littleriscv), and formps3-an547:picostestandqemu-armv7a:knsh; withCONFIG_LTO_FULL(qemu-armv8a:nsh,mps2-an500:nsh,rv-virt:nsh) they have-flinker-output=nolto-reland the module options.tools/checkpatch.sh -c -u -m -gpasses.