Skip to content

[9.25/10] libs/libc/elf, arch/arm: Start an FDPIC module linked by the tree's module flags - #20388

Open
casaroli wants to merge 2 commits into
apache:masterfrom
casaroli:fdpic-fileoff-crt0
Open

casaroli wants to merge 2 commits into
apache:masterfrom
casaroli:fdpic-fileoff-crt0

Conversation

@casaroli

@casaroli casaroli commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Part of the FDPIC series. Two bugs stop an FDPIC module from starting when it is linked the way the tree's own module flags link it (CMODULEFLAGS, LDMODULEFLAGS, gnu-elf.ld): its text starts at file offset 0x1000, and it carries the tree's crt0. The modules the xipfs tests carry today are linked differently and do not hit either one.

DT_REL and DT_JMPREL hold the link-time address of their table, and the loader read the table at that value as a file offset. The two are equal only when the segment starts at file offset 0. Otherwise the relocations were read from padding and none were applied, so _start called main() through an unrelocated pointer. The address is now translated through the PT_LOAD headers first.

Under FDPIC an .init_array entry is a code address, but a C function pointer is a descriptor. crt0 called each entry through a function pointer, so with CONFIG_HAVE_CXXINITIALIZE it read a constructor's first instructions as a descriptor. It now calls each entry with fdpic_call() and the module's own data base from fdpic_base().

Impact

Only FDPIC modules change. An object whose segments start at file offset 0 translates to the same offset as before, and without CONFIG_FDPIC crt0 makes the same direct call as before.

Testing

mps2-an500:xipfs with CONFIG_FDPIC under QEMU, xipfs_test fdpic, with the module fixtures rebuilt by the tree's module flags (apache/nuttx-apps#3762):

nuttx result
master crash in the first test, funcdesc
master + this PR 28 passed, 6 failed: the six library tests, which need #20368
master + this PR + #20368 34 passed, 0 failed

With the fixtures on apps master the result is unchanged by this PR.

qemu-armv7a:knsh builds with -Werror, including make export (which builds crt0.o) and the apps import. tools/checkpatch.sh -c -u -m -g passes.

DT_REL and DT_JMPREL hold the link-time address of their table, and the
loader read the table at that value as a file offset.  The two are equal
only when the segment that holds it starts at file offset 0.  An object
linked with its text at file offset 0x1000, as the tree's gnu-elf.ld does,
had its relocations read from padding, so none were applied and the
module called through unrelocated pointers.

Translate the address through the PT_LOAD headers first.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
@casaroli
casaroli requested a review from anchao as a code owner September 28, 2026 18:41
casaroli added a commit to casaroli/nuttx-apps that referenced this pull request Sep 28, 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.

The build uses the configured tree's module flags (CMODULEFLAGS,
CXXMODULEFLAGS, LDMODULEFLAGS), linker script and crt0 source.  Three
things are pinned instead, so that the committed headers do not depend
on how the tree is configured: the CPU (cortex-m3, so one set of headers
runs on both v7-M and v8-M), no debug information, and -Os.  A library
is compiled with default visibility, because the module flags hide every
symbol and a library must export its functions.

fdpic-embed.py becomes xxd and a template.  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.

The headers are regenerated, and the .fdpic suffix is gone: a module is
named like any other module.  A fixture now starts in the tree's crt0,
which needs apache/nuttx#20388 to run its constructors, and the library
fixtures need apache/nuttx#20368.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
@github-actions github-actions Bot added Arch: arm Issues related to ARM (32-bit) architecture Area: OS Components OS Components issues Size: S The size of the change in this PR is small labels Sep 28, 2026
@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

MemBrowse Memory Report

s698pm-dkit

(*ctor)();
call_initializer(*ctor);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not call fdpic_call or fdpic_invoke

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Under FDPIC an .init_array or .fini_array entry is a code address, but a
C function pointer is a function descriptor.  crt0 called each entry
through a function pointer, so it read the constructor's first
instructions as a descriptor and jumped to garbage.

Call each entry with fdpic_call() and the data base from fdpic_base(),
which is the module's own.  Without CONFIG_FDPIC both are a direct call,
as before.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
casaroli added a commit to casaroli/nuttx-apps that referenced this pull request Sep 29, 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.

The build uses the configured tree's module flags (CMODULEFLAGS,
CXXMODULEFLAGS, LDMODULEFLAGS), linker script and crt0 source.  Three
things are pinned instead, so that the committed headers do not depend
on how the tree is configured: the CPU (cortex-m3, so one set of headers
runs on both v7-M and v8-M), no debug information, and -Os.  A library
is compiled with default visibility, because the module flags hide every
symbol and a library must export its functions.

fdpic-embed.py becomes xxd and a template.  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.

The headers are regenerated, and the .fdpic suffix is gone: a module is
named like any other module.  A fixture now starts in the tree's crt0,
which needs apache/nuttx#20388 to run its constructors, and the library
fixtures need apache/nuttx#20368.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
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: OS Components OS Components issues Size: S The size of the change in this PR is small

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants