Skip to content

[5.5/10] arch/arm, cmake: Build FDPIC modules in the normal ELF build. - #19940

Open
casaroli wants to merge 3 commits into
apache:masterfrom
casaroli:tools-fdpic
Open

[5.5/10] arch/arm, cmake: Build FDPIC modules in the normal ELF build.#19940
casaroli wants to merge 3 commits into
apache:masterfrom
casaroli:tools-fdpic

Conversation

@casaroli

@casaroli casaroli commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

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, and tools/fdpic is not added at all.

With CONFIG_FDPIC, a module built by apps/Application.mk is an FDPIC shared object. Same MODULE = m, same crt0.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 an arm-uclinuxfdpiceabi linker. 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 V and turns 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. The module links cleanly and then calls out of itself with the caller's data base still in r9.

gnu-elf.ld.in gains the two segments an FDPIC module needs, under CONFIG_FDPIC, and names .dynamic. The sections and the symbols crt0.c walks are untouched, so one script serves both cases and both build systems.

.bss moves to the end of the script, for every configuration and not only FDPIC. It holds 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 goes 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.

The __dso_handle commit 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, because CONFIG_FDPIC is defined there.

Impact

Nothing changes with CONFIG_FDPIC off, which is every configuration in the tree.

With it on, a module needs the arm-uclinuxfdpiceabi linker. Anyone selecting the option needs it anyway.

.bss last 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/_ebss symbols are unchanged.

Testing

mps3-an547:picostest with apps/examples/elf, CONFIG_FDPIC both ways, through make and cmake.

$ readelf -h apps/bin/hello++3
  OS/ABI:      ARM FDPIC
  Type:        DYN (Shared object file)
  Entry point: 0x81
$ readelf -d apps/bin/hello++3
  0x19 (INIT_ARRAY)  0x1018
  0x1a (FINI_ARRAY)  0x101c

Every module in apps/bin is an FDPIC shared object with two PT_LOAD segments entering at _start. hello++3 has a static C++ object, so it carries both arrays; it needs #20048 to link at all.

With CONFIG_FDPIC off the tree builds as before and the generated script has no PHDRS. With the linker off PATH a module link stops with a message naming it, and make distclean still works.

tools/checkpatch.sh -c -u -m -g and cmake-format --check pass.

The fixtures in apps/examples/fdpicxip keep a build of their own, because they are loader edge cases Application.mk cannot express. apache/nuttx-apps#3762 moves it there so NuttX carries none of it.

@github-actions

github-actions Bot commented Aug 23, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

No memory changes detected for:

@casaroli casaroli changed the title [3/4] tools/fdpic: Add the module build helpers the demo apps use. [3/10] tools/fdpic: Add the module build helpers the demo apps use. Aug 23, 2026
@casaroli
casaroli force-pushed the tools-fdpic branch 3 times, most recently from f874bb2 to 78e4fec Compare August 27, 2026 12:33
@casaroli
casaroli marked this pull request as ready for review August 27, 2026 21:48
Comment thread Documentation/components/tools/fdpic.rst Outdated
Comment thread Documentation/components/tools/fdpic.rst Outdated
Comment thread Documentation/components/tools/fdpic.rst Outdated
Comment thread Documentation/components/tools/fdpic.rst Outdated
Comment thread tools/fdpic/build-binutils.sh Outdated
Comment thread tools/fdpic/build-binutils.sh Outdated
Comment thread tools/fdpic/fdpic-embed.py Outdated
Comment thread tools/fdpic/init-array.ld Outdated
Comment thread tools/fdpic/nuttx-fdpic.mk Outdated
Comment thread tools/fdpic/nuttx-fdpic.mk Outdated
@casaroli casaroli changed the title [3/10] tools/fdpic: Add the module build helpers the demo apps use. arch/arm, cmake: Build FDPIC modules in the normal ELF build. Aug 28, 2026
casaroli added a commit to casaroli/nuttx-apps that referenced this pull request Aug 28, 2026
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>
@github-actions

Copy link
Copy Markdown

🔗 Cross-repo PR dependencies

The 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

@github-actions

Copy link
Copy Markdown

🔗 Cross-repo PR dependencies

The 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

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

🔗 Cross-repo PR dependencies

The 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

Comment thread arch/arm/src/common/Toolchain.defs
Comment thread arch/arm/src/common/Toolchain.defs
Comment thread libs/libc/elf/gnu-elf.ld.in
Comment thread libs/libc/elf/gnu-elf.ld.in Outdated
Comment thread arch/arm/src/cmake/elf.cmake Outdated
Comment thread arch/arm/src/cmake/elf.cmake
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

🔗 Cross-repo PR dependencies

The 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

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

🔗 Cross-repo PR dependencies

The 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>
casaroli added a commit to casaroli/nuttx-apps that referenced this pull request Sep 8, 2026
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>
@github-actions github-actions Bot added Area: Build system Size: M The size of the change in this PR is medium and removed Size: XL The size of the change in this PR is very large. Consider breaking down the PR into smaller pieces. Area: BINFMT Board: arm labels Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Arch: arm Issues related to ARM (32-bit) architecture Area: Build system Size: M The size of the change in this PR is medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants