[5.5/10] arch/arm, cmake: Build FDPIC modules in the normal ELF build. - #19940
[5.5/10] arch/arm, cmake: Build FDPIC modules in the normal ELF build.#19940casaroli wants to merge 3 commits into
Conversation
f874bb2 to
78e4fec
Compare
The modules this example and testing/fs/xipfs carry are built by an explicit 'make regen', which reached into nuttx/tools/fdpic for a makefile that builds a module, a script that turns one into a header, and two more that checked its imports. Review of apache/nuttx#19940 asked that NuttX not carry a module build of its own, and it no longer does: with CONFIG_FDPIC an ordinary FDPIC module is built by apps/Application.mk like any other. These are not ordinary modules, which is why they keep a build of their own. They are fixtures for loader edge cases: a library with a SONAME, a module with more DT_NEEDED entries than the loader will follow, one whose imports stay in the lazy binding table, and one naming a symbol the firmware does not export, which exists to be refused. Application.mk cannot say any of that. So the build stays, and it is here beside them rather than in NuttX. It is also much smaller. The generic module makefile is gone: it existed to be included by anything, and only this one directory ever did, so its dozen useful lines are rules here. fdpic-embed.py is gone: xxd does that, as examples/elf already does it, and the license header it also wrote is a template beside it. fdpic-verify.sh and nuttx-exports.sh are gone with no replacement; they checked at build time what the xipfs suite already asserts at run time, for two hundred lines. What the fixtures no longer carry is a crt0 and a linker script. Both come from the tree named by NUTTX_DIR, which is where the in-tree module build takes them, so a fixture is built the way a module is. The crt0 source is compiled here rather than the built object taken, because these are deliberately built for cortex-m3 while the firmware is not: a v7-M module runs on both the v7-M and v8-M targets, so one set of headers serves the RP2350 and mps2-an500 alike. Regenerated qsorter, libshape and cxxuser against a tree configured with CONFIG_FDPIC. qsorter is ARM FDPIC, v7-M, two PT_LOAD segments, entering at _start; libshape carries its SONAME and its DT_INIT_ARRAY. The committed headers are left as they are. They will change when they are next regenerated, because a fixture now carries the tree's crt0 rather than one of its own, and that is a change the xipfs suite should be run against rather than made blind. Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
🔗 Cross-repo PR dependenciesThe read-only Build run reported the following dependent PR(s) and fetched head SHA(s): CI run: https://github.com/apache/nuttx/actions/runs/33191894877 |
🔗 Cross-repo PR dependenciesThe read-only Build run reported the following dependent PR(s) and fetched head SHA(s): CI run: https://github.com/apache/nuttx/actions/runs/33433035705 |
7bc1b05 to
f5baea6
Compare
🔗 Cross-repo PR dependenciesThe read-only Build run reported the following dependent PR(s) and fetched head SHA(s): CI run: https://github.com/apache/nuttx/actions/runs/33497498910 |
f5baea6 to
bf4dbc4
Compare
d4c4510 to
ad76834
Compare
b3a98c1 to
3d843c9
Compare
🔗 Cross-repo PR dependenciesThe read-only Build run reported the following dependent PR(s) and fetched head SHA(s): CI run: https://github.com/apache/nuttx/actions/runs/33693298203 |
3d843c9 to
8f34ef8
Compare
🔗 Cross-repo PR dependenciesThe read-only Build run reported the following dependent PR(s) and fetched head SHA(s): CI run: https://github.com/apache/nuttx/actions/runs/34204867930 |
With CONFIG_FDPIC selected, a module built by apps/Application.mk is now an FDPIC shared object. Nothing about how a module is written or built changes: the same MODULE = m in the same Makefile, the same crt0 and the same linker script. Two things differ from the position independent build beside it. The compiler is told -mfdpic -fPIC, and the link is done by an arm-uclinuxfdpiceabi linker. The stock arm-none-eabi compiler emits correct FDPIC objects for both C and C++, so only the link needs it: the stock linker carries the armelf emulation alone and would turn every import into an R_ARM_JUMP_SLOT, one word, where the ABI wants an R_ARM_FUNCDESC_VALUE, which is two, a code address and the data base that goes with it. Such a module links cleanly and then calls out of itself with the caller's data base still in r9. That linker is in the CI image. gnu-elf.ld.in gains the two segments an FDPIC module needs, under CONFIG_FDPIC, because the loader places its read-only and writable segments independently, and names .dynamic, because a shared object is bound through it. The sections themselves are untouched and so are the symbols crt0.c walks, so one script serves both and both build systems get it. .bss moves to the end of the script, for every configuration and not only FDPIC. It held no file content but sat ahead of .got and .dynamic, which do, so the writable segment's p_filesz had to span it and the module file carried the whole of .bss. A module with 16 KiB of .bss went from 26724 to 10340 bytes, and its writable segment from p_filesz 0x40ac to 0xac against an unchanged p_memsz. The loader reads p_filesz off the media, so it read those bytes too. Built for mps3-an547:picostest with apps/examples/elf, CONFIG_FDPIC both ways. With it on, every module in apps/bin is ARM FDPIC with two PT_LOAD segments and enters at _start; hello++3, which has a static C++ object, carries DT_INIT_ARRAY and DT_FINI_ARRAY. With it off the generated script has no PHDRS and the modules are what they were. Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
The same two differences as in common/Toolchain.defs: the compiler is told -mfdpic -fPIC, and the module link is done by an arm-uclinuxfdpiceabi linker. That linker is not the one that links the firmware, so the module link needs a variable of its own. CMAKE_ELF_LD is the ordinary linker unless the architecture sets it, which arm does under CONFIG_FDPIC. The linker script needs nothing here: it is generated from libs/libc/elf/gnu-elf.ld.in, which both build systems preprocess, and the FDPIC segments are already in it. -r is now conditional on CONFIG_PIC being off, which is what common/Toolchain.defs has always done and the cmake build did not: a position independent module is linked as an executable, and an FDPIC one as a shared object, so neither wants it. -fno-use-cxa-atexit mirrors CXXELFFLAGS for the same reason it was added there. Configured and built mps3-an547:picostest with CONFIG_FDPIC through cmake and ninja: the modules in bin/ are ARM FDPIC with two PT_LOAD segments. Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Without this the build says "arm-uclinuxfdpiceabi-ld: Command not found", which does not say what that is, where to get it, or that the prefix can be changed. The make build reports at the link rather than while parsing, so that a tree configured for FDPIC on a host without the linker can still be cleaned and reconfigured: an error at parse time takes make distclean with it. The cmake build reports while configuring, where nothing is built yet. Both name FDPIC_CROSSDEV, so a linker under another prefix can be used. Checked on mps3-an547:picostest with CONFIG_FDPIC and the linker off PATH: make distclean succeeds, and a module link stops with the message. With the linker present the modules build as before. Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
8f34ef8 to
dfbab0c
Compare
The fixtures were built by makefiles and scripts in nuttx/tools/fdpic. Review of apache/nuttx#19940 asked that NuttX not carry a module build of its own, so the build moves here, beside the sources it builds. They keep a build of their own because they are loader edge cases: a library with a SONAME, a module with more DT_NEEDED entries than the loader follows, one whose imports stay in the lazy binding table, and one naming a symbol the firmware does not export. Application.mk cannot say any of that. It is also smaller. The generic module makefile becomes a dozen rules here, fdpic-embed.py becomes xxd and a template, and fdpic-verify.sh and nuttx-exports.sh go with no replacement: they checked at build time what the xipfs suite already asserts at run time. crt0 and the linker script come from the tree named by NUTTX_DIR. The crt0 source is compiled here rather than the built object taken, because the fixtures are built for cortex-m3: a v7-M module runs on both the v7-M and v8-M targets, so one set of headers serves the RP2350 and mps2-an500 alike. Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Summary
This replaces what this PR was. It added
tools/fdpic, a module build of its own; review asked instead that FDPIC go into the normal in-tree ELF build. So it does, andtools/fdpicis not added at all.With
CONFIG_FDPIC, a module built byapps/Application.mkis an FDPIC shared object. SameMODULE = m, samecrt0.c, same linker script.Two things differ from the position independent build beside it: the compiler is told
-mfdpic -fPIC, and the link is done by anarm-uclinuxfdpiceabilinker. Only the link needs it; the stock compiler emits correct FDPIC objects for C and C++, its assembler included. That linker is in the CI image since #19992, and the build says so if it is missing.The stock linker must not be fallen back to, because it does not refuse FDPIC objects. It marks the output
UNIX - System Vand turns every import into anR_ARM_JUMP_SLOT, one word, where the ABI wants anR_ARM_FUNCDESC_VALUE, which is two: a code address and the data base that goes with it. The module links cleanly and then calls out of itself with the caller's data base still inr9.gnu-elf.ld.ingains the two segments an FDPIC module needs, underCONFIG_FDPIC, and names.dynamic. The sections and the symbolscrt0.cwalks are untouched, so one script serves both cases and both build systems..bssmoves to the end of the script, for every configuration and not only FDPIC. It holds no file content but sat ahead of.gotand.dynamic, which do, so the writable segment'sp_fileszhad to span it and the module file carried the whole of.bss. A module with 16 KiB of.bssgoes from 26724 to 10340 bytes, and its writable segment fromp_filesz0x40acto0xacagainst an unchangedp_memsz. The loader readsp_fileszoff the media, so it read those bytes too.The
__dso_handlecommit that was here is now #20048. It is not FDPIC's, and review asked for it across the other architectures.This comes after
[5/10]#19942 rather than before it, becauseCONFIG_FDPICis defined there.Impact
Nothing changes with
CONFIG_FDPICoff, which is every configuration in the tree.With it on, a module needs the
arm-uclinuxfdpiceabilinker. Anyone selecting the option needs it anyway..bsslast changes the layout of every ELF module, not only FDPIC ones. The module gets smaller and the loader reads less; nothing else moves, because the loader places by section flags and the_sbss/_ebsssymbols are unchanged.Testing
mps3-an547:picostestwithapps/examples/elf,CONFIG_FDPICboth ways, through make and cmake.Every module in
apps/binis an FDPIC shared object with twoPT_LOADsegments entering at_start.hello++3has a static C++ object, so it carries both arrays; it needs #20048 to link at all.With
CONFIG_FDPICoff the tree builds as before and the generated script has noPHDRS. With the linker offPATHa module link stops with a message naming it, andmake distcleanstill works.tools/checkpatch.sh -c -u -m -gandcmake-format --checkpass.The fixtures in
apps/examples/fdpicxipkeep a build of their own, because they are loader edge casesApplication.mkcannot express. apache/nuttx-apps#3762 moves it there so NuttX carries none of it.