!arch/arm: Use r9 as the PIC base register, and bring mknxflat in-tree - #19600
!arch/arm: Use r9 as the PIC base register, and bring mknxflat in-tree#19600casaroli wants to merge 4 commits into
Conversation
An NXFLAT module reaches the base firmware through a "thunk" file: one assembly stub per imported function, generated by mknxflat. That tool has always lived outside this repository, in the NuttX buildroot NXFLAT toolchain, so building an NXFLAT module needs a separate checkout and a separate build of a tool that links against libbfd. libbfd is why it stayed out. It is GPL, which an Apache project cannot depend on, and it is awkward to obtain besides -- a stock binutils install often ships libbfd without the libiberty it needs to link. But the dependency was never deep. mknxflat used libbfd for eight calls, all of them opening the file and walking the symbol table; it never relocates or rewrites anything. That is replaced here by reading the ELF symbol table directly, which removes the dependency outright and costs about a hundred lines. The emitted text is unchanged. The format strings live in the .def files, which are carried here byte-for-byte from upstream, and the selection rule for what becomes a thunk is the upstream one: everything undefined that is not explicitly an object. Symbol typing cannot be trusted for this -- imported functions are routinely emitted as STT_NOTYPE rather than STT_FUNC, while a weakly defined object does appear as an undefined object -- so the test is on what a symbol is not. Upstream chose the instruction set at compile time through an "arch" symlink pointing at either arm/ or thumb2/. A symlink cannot be carried in the repository, and one host binary has to serve boards of both flavours, since lpc31xx is ARM while lpc17xx, tiva, stm32f1 and rp23xx are Thumb-2. That choice becomes a runtime "-a" option. The "-f" option, which read further command line arguments from a file, is dropped; nothing in the tree used it. This commit changes no output. Against the upstream tool, for both architectures, with and without -w, over modules exercising the plain, weak and non-returning thunk paths, the generated thunk files are byte-identical. Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
|
@patacongo — a licensing question, since you are the copyright holder. This PR imports mknxflat into the tree as tools/nxflat/. The reason is in the PR body: moving the ARM PIC base register to r9 breaks NXFLAT silently, because mknxflat bakes the register into the import thunks it generates as add ip,ip,sl, and while the tool lives outside this repository it can drift out of step with the kernel. The three files taken are mknxflat.c and arm/dyncall_skeleton.def / thumb2/dyncall_skeleton.def from the buildroot NXFLAT toolchain. Each carries a BSD-3-Clause header naming you and Cadenux LLC, and notes that the code derives from XFLAT's ldelflib. The containing repository's COPYING is GPL-2.0, so I would like that confirmed rather than assumed: is the per-file BSD-3 grant the operative licence for those three files, and are you content for them to be imported here with their headers intact? ldnxflat is deliberately not imported. It carries an explicit GPL-2.0-or-later notice and descends from elf2flt, with roughly ten copyright holders going back to 1996, so it stays where it is and NXFLAT continues to need it from buildroot. The only substantive change to the imported code is dropping the libbfd dependency, which was used solely to open the file and enumerate symbols; the tool now reads the ELF symbol table directly. The generated thunk files are byte-identical to what the current tool produces, for both ARM and Thumb-2, with and without -w. |
|
I have no issue with the BSD code being converted to pure Apache 2.0 following the ASF headers. Some files were jointly copyrighted with Cadenux LLC which I owned many years ago and I was the only person doing the development. So I am comfortable with all of that.
The only concern would be the code that has a some ancient uCLinux influenced logic in it. This is original code but derives from ancient GPL uClinux code in some logical sense (although I am avoiding the word "derived" because of its copyright implications). I think that is only one file (?)
If everyone else is comfortable, then I am too. I would wait until people in Europe, especially Alin, have a chance to weigh in, however. Alin did all of the original BSD/GPL to Apache licensing analysis and changes when we first joined the ASF so his opinion carries the greatest weight.
…________________________________
From: CeDeROM ***@***.***>
Sent: Monday, August 3, 2026 11:12 AM
To: apache/nuttx ***@***.***>
Cc: patacongo ***@***.***>; Mention ***@***.***>
Subject: Re: [apache/nuttx] arch/arm: Use r9 as the PIC base register, and bring mknxflat in-tree (PR #19600)
@cederom requested changes on this pull request.
Thank you @casaroli<https://github.com/casaroli> lets just wait for Gregory Nutt states agreement on the mailing list to relicense files from BSD to Apache so we can already merge Apache :-)
—
Reply to this email directly, view it on GitHub<#19600?email_source=notifications&email_token=ABFUG6W5TEHGI4E3YHBABTD5IDIXBA5CNFSNUABKM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UKJSXM2LFO4XTIOBUG4ZDOMJZHE2KM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLDGN5XXIZLSL5RWY2LDNM#pullrequestreview-4847271994>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/ABFUG6X4HD26HE42SA2WVW35IDIXBAVCNFSNUABFKJSXA33TNF2G64TZHMZDEOBRGAZTENZTHNEXG43VMU5TKMBUGQ3TKNJYGE22C5QC>.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS<https://github.com/notifications/mobile/ios/ABFUG6RR5PATD5EWRTV6ZW35IDIXBA5CNFSNUABKM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UKJSXM2LFO4XTIOBUG4ZDOMJZHE2KM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJKTGN5XXIZLSL5UW64Y> and Android<https://github.com/notifications/mobile/android/ABFUG6WEZGOHULTOHFQ43BD5IDIXBA5CNFSNUABKM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UKJSXM2LFO4XTIOBUG4ZDOMJZHE2KM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLTGN5XXIZLSL5QW4ZDSN5UWI>. Download it today!
You are receiving this because you were mentioned.Message ID: ***@***.***>
|
|
@jerpelea what do you think?? :-) |
|
Cadenus License Some of the code lists Cadenux LLC as the copyright holder. Cadenux was Linux engineering services company that existed from around 2002-2004. I and a partner were the owners of Cadenux LLC. I was the only partner involved with XFLAT. I grant that software which included the Cadenux license to the Apache Software Foundation. I did not discuss that with my ex-partner but I think I would have his concurrence and, in any event, I think I do have the authority to grant that code to the ASF even without concurrence. |
The tool arrived from the buildroot NXFLAT toolchain under BSD-3-Clause, jointly copyright Gregory Nutt and Cadenux, LLC. Gregory Nutt owned Cadenux and was its only developer on this code, and has agreed to the conversion, so the six files take the ASF header like the rest of the NuttX code he donated. Copyright attribution moves to NOTICE, which is where the donation put it for everything else of his in the tree. This covers only what was imported: mknxflat and the thunk skeletons it emits from. ldnxflat is the file with an elf2flt lineage, and it is not here -- it stays out of tree in buildroot, and NuttX keeps calling it as an external tool. The .def files also gain their in-tree path on the first line, which the import had left pointing at the buildroot layout. Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Thanks Greg, and the file you are thinking of is not in this PR. That is ldnxflat.c: its header says "converted from elf2flt.c" and carries Greg Ungerer's and Phil Blundell's copyrights. It stays out of tree in buildroot, and NuttX keeps calling it as an external tool exactly as today. What is imported is six files: mknxflat.c, nxflat_thunk.h, thunk_arm.c, thunk_thumb2.c and the two dyncall skeletons. mknxflat.c says "Modified from ldelflib", the code you describe as yours and Cadenux's, and I grepped all six for elf2flt, uClinux, XFLAT and GPL — there is nothing. I have converted those six to the ASF header in a separate commit, so the change is reviewable on its own and easy to drop or reword. Waiting on Alin before this goes anywhere. |
ARM PIC has used r10 as the base register, but the tree has never been consistent about it. Toolchain.defs gives CONFIG_BUILD_PIC -mpic-register=r9 and CONFIG_PIC -mpic-register=r10, twenty-five lines apart, and arm_initialstate.c sets REG_R9 from inline assembly under one and REG_PIC under the other, with a comment reading "Set the PIC base register (probably R10)". This settles it on r9 for all of PIC: NXFLAT, ELF PIC and CONFIG_BUILD_PIC alike. r9 is the right choice rather than an arbitrary one. It is the AAPCS platform register, the "static base", and it is what GCC itself picks for -msingle-pic-base on an EABI target; r10 is the non-EABI default. It also removes a combination that cannot build today. Stack checking adds -ffixed-r10 in armv7-m/Toolchain.defs and armv8-m/Toolchain.defs, while CONFIG_PIC adds -mpic-register=r10, and GCC rejects the pair with "unable to use 'r10' for PIC register". The comment above REG_PIC has always said the register "can be R9 if stack checking is enabled", but the definition was unconditionally REG_R10, so it would have named the wrong register even had the build succeeded. The thunk generator moves with the firmware. NXFLAT import stubs had the register baked in as "add ip,ip,sl", so a module built for r9 would load and then branch to a wild address on its first call out. The stubs now come from NXFLAT_PIC_REG in the in-tree tool, which is built only when CONFIG_NXFLAT is set, following the CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE precedent in tools/Unix.mk. That leaves modules built before this change, and they are the reason for the ABI marker. The NXFLAT header cannot carry a version: h_magic is written by ldnxflat, which is GPL, derived from elf2flt, and stays out of this repository, so it can never be changed in step with the loader. The import table can, because both of its ends are in-tree -- mknxflat emits it and nxflat_bindimports() reads it -- and ldnxflat passes it through untouched. So every module now imports __nxflat_abi_v2, the base firmware defines it, and a module that does not import it is refused. Making the marker a real exported symbol rather than a name the loader special-cases is what keeps it out of the build system's way: a board's symbol table picks it up exactly as it picks up printf, so mksymtab.sh and its equivalents need no change. It also gives the reverse direction a diagnosis for free -- a module built against a newer ABI than its firmware fails with "Exported symbol __nxflat_abi_v2 not found". Most of the remaining churn is boards restating a default. ARCHPICFLAGS is a "?=" default so that a board only speaks up when it differs, and twenty-six were assigning the value the default already had. MKNXFLAT gets the same treatment: thirteen boards named the same tool, and the only thing that varies is ARM versus Thumb-2, which falls out of CONFIG_ARM_THUMB. LDNXFLAT gains a default too -- it stays an out-of-tree PATH lookup, but naming it centrally fixes boards that never assigned it, where it expanded to nothing and handed make a recipe beginning "-e", whose leading dash make ate as "ignore errors". The non-ARM boards carrying -mpic-register=r10 lose it: it is an ARM-only option, reachable only through CPICFLAGS, which is only used to build NXFLAT modules, and no non-ARM board enables NXFLAT. Boards keep nothing about PIC flags any more. ARCHPICFLAGS was set by sixty-three of them and only ever fed CPICFLAGS, which is only used to build NXFLAT modules; no board outside arch/arm enables NXFLAT, so every non-ARM copy was setting a variable nothing read. Those are removed rather than moved somewhere more central, which would only make dead text look load-bearing. LDNXFLAT goes the same way as MKNXFLAT, for the same reason: thirteen boards named the same tool that Toolchain.defs now names once. One of them was not merely redundant. am67/t3-gem-o1 asked for "-mpic-register=r10 -ffixed-r10", which GCC refuses outright with "unable to use 'r10' for PIC register" -- the very combination the filter-out machinery in Toolchain.defs exists to prevent. It has survived because that board does not build NXFLAT modules, so the flags are never handed to a compiler. Renaming the register would have carried the fault forward unchanged, so the line goes. Tested on lm3s6965-ek:qemu-nxflat under QEMU, configured and built with no overrides. The nxflat example runs the errno, hello and struct modules with output identical to the same config built from master. Built with the old out-of-tree thunk generator instead, the same firmware refuses all three with ENOEXEC rather than locking up in a HardFault, which is what this change is for. mps3-an547:picostest, which is CONFIG_PIC without CONFIG_NXFLAT, builds clean and does not build the thunk generator. The .def files pick up two cosmetic changes here alongside the register: a "Dyanamic" typo that codespell rejects, and a reworded comment in each thunk_*.c. Neither appears in the emitted thunk -- both are in C comments -- so the generated text is still what the upstream tool produces, modulo the register itself. BREAKING CHANGE: ARM PIC moves from r10 to r9. An NXFLAT module built before this change has r10 baked into its import stubs and will not run against a firmware carrying it; the two cannot be mixed. The module is refused with ENOEXEC rather than branching to a wild address, by way of the __nxflat_abi_v2 marker described below. Quick fix: rebuild the module against this tree. Its source needs no change. A board that reserved r10 by hand, or that assigned ARCHPICFLAGS or MKNXFLAT to restate a default, should drop those assignments; nothing else is affected, and CONFIG_PIC without CONFIG_NXFLAT needs no action. Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
mknxflat is built from tools/nxflat by the NuttX build itself, so the toolchain section no longer sends the reader to buildroot for it; only ldnxflat still comes from there. Bring the mknxflat usage text in line with the tool, note that MKNXFLAT and LDNXFLAT are supplied by the ARM Toolchain.defs, and correct the r10 references left in the PIC descriptions. Describe the module ABI marker, so that a user whose prebuilt module starts failing exec() with ENOEXEC finds out that the loader refuses a module whose import table does not name __nxflat_abi_v2, and that rebuilding the module is the fix. Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Summary
ARM PIC uses r10 as the base register, but the tree has never been consistent about it.
arch/arm/src/common/Toolchain.defsgivesCONFIG_BUILD_PIC-mpic-register=r9at line 594 andCONFIG_PIC-mpic-register=r10at line 620, andarm_initialstate.csetsREG_R9from inline assembly under one andREG_PICunder the other, with a comment that reads "Set the PIC base register (probably R10)". This settles it on r9 for all of PIC: NXFLAT, ELF PIC andCONFIG_BUILD_PICalike.r9 is the right register rather than an arbitrary one. It is the AAPCS platform register, the "static base", and it is what GCC itself selects for
-msingle-pic-baseon an EABI target; r10 is the non-EABI default.It also fixes a configuration that cannot build today. Stack checking adds
-ffixed-r10inarmv7-m/Toolchain.defs:149andarmv8-m/Toolchain.defs:168whileCONFIG_PICadds-mpic-register=r10, and GCC rejects the pair with "unable to use 'r10' for PIC register". The comment aboveREG_PIChas always said the register "can be R9 if stack checking is enabled", but the definition was unconditionallyREG_R10, so it would have named the wrong register even if the build had succeeded.Why the NXFLAT thunk generator comes with it
Moving the register on its own breaks NXFLAT, silently. Import thunks are generated by
mknxflat, which had the register baked into its template asadd ip,ip,sl, so a module compiled-mpic-register=r9reaches its data through r9 while its import stubs still add r10, and it branches to a wild address on its first call into the base firmware.mknxflatlives outside this repository, in the buildroot NXFLAT toolchain, so the kernel and the tool could drift with nothing to detect it. The first commit therefore brings it in-tree astools/nxflat/, with libbfd replaced by reading the ELF symbol table directly — libbfd was only ever used to open the file and enumerate symbols, and it is GPL, which we cannot depend on. The thunks are then generated from a singleNXFLAT_PIC_REGon every build, so the two cannot drift.That commit changes no output. Against the upstream tool, for both ARM and Thumb-2, with and without
-w, over modules exercising the plain, weak and non-returning thunk paths, the generated thunk files are byte-identical.Modules built before this change
They are refused at load rather than left to crash.
The NXFLAT header cannot carry a version:
h_magicis written byldnxflat, which is GPL, derived from elf2flt, and stays out of this repository, so it can never be changed in step with the loader. The import table can, because both of its ends are in-tree —mknxflatemits it andnxflat_bindimports()reads it — andldnxflatpasses it through untouched. Every module now imports__nxflat_abi_v2, the base firmware defines it, and a module that does not import it is refused with-ENOEXEC.Making the marker a real exported symbol rather than a name the loader special-cases keeps it out of the build system's way: a board's symbol table picks it up exactly as it picks up
printf, somksymtab.shand its equivalents need no change. It also diagnoses the reverse direction for free — a module built against a newer ABI than its firmware fails withExported symbol "__nxflat_abi_v2" not found.Impact
Warning
BREAKING CHANGE: ARM PIC moves from r10 to r9. An NXFLAT module built before this has r10 baked into its import stubs and will not run against a firmware carrying it; the loader refuses it with
-ENOEXECrather than branching to a wild address.Quick fix: rebuild the module against this tree — its source needs no change. A board that reserved r10 by hand, or that assigned
ARCHPICFLAGSorMKNXFLATto restate a default, should drop those assignments.CONFIG_PICwithoutCONFIG_NXFLATneeds no action, and configurations with neither are untouched.Most of the churn is boards restating a default.
ARCHPICFLAGSis a?=default so that a board only speaks up when it differs, and twenty-six were assigning the value the default already had; those lines are removed rather than edited.MKNXFLATgets the same treatment: thirteen boards named the same tool, and the only thing that varies is ARM versus Thumb-2, which falls out ofCONFIG_ARM_THUMB.LDNXFLATgains a default too — it stays an out-of-tree PATH lookup, but naming it centrally fixes boards that never assigned it, where it expanded to nothing and handed make a recipe beginning-e, whose leading dash make ate as "ignore errors".The non-ARM boards carrying
-mpic-register=r10lose it. It is an ARM-only option, reachable only throughCPICFLAGS, which is only used to build NXFLAT modules, and no non-ARM board enables NXFLAT; six other RISC-V boards already omitted it.mknxflatis built only whenCONFIG_NXFLATis set, following theCONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLEprecedent intools/Unix.mk.Licensing
tools/nxflat/mknxflat.cand the two.deftemplates are BSD-3-Clause, copyright Gregory Nutt and Cadenux LLC, imported from the NuttX buildroot NXFLAT toolchain with their headers intact. The containing repository'sCOPYINGis GPL-2.0, but these particular files carry their own BSD-3 grant, name only Nutt and Cadenux as copyright holders, and descend from XFLAT'sldelflibrather than from elf2flt. @patacongo for confirmation.ldnxflatis deliberately not imported. It is GPL-2.0-or-later and derives from elf2flt, with roughly ten copyright holders reaching back to 1996 — it cannot be relicensed and cannot live in an Apache repository. NXFLAT continues to need it from the buildroot toolchain, exactly as today.Here is the licensing agreement mailing list archive:
https://www.mail-archive.com/dev@nuttx.apache.org/msg14863.html
How to verify this on your board
Help with this is very welcome — §1.14 asks for runtime logs from more than one real architecture, and I have only an RP2350 here.
Any board, ARM or not (two minutes). Build your usual configuration, boot it, and run
ostest. That is the whole test for most boards: the only thing that touches them is the PIC flag cleanup, which is a no-op unless the board setsARCHPICFLAGSor enables NXFLAT. Non-ARM boards lose-mpic-register=r10, which was an ARM-only option reachable only throughCPICFLAGS, andCPICFLAGSis only used to build NXFLAT modules — so nothing should change at all.If your board enables
CONFIG_NXFLAT. Build and run a module, for exampleolimex-lpc1766stk:thttpd-nxflatorapps/examples/nxflat.mknxflatnow comes fromtools/nxflatand the build makes it for you;ldnxflatis still an out-of-tree tool from buildroot, unchanged.If you have an NXFLAT module built before this change. Run it without rebuilding. It must be refused, with
and
exec()failing with-ENOEXEC(-8). A crash or a wild branch instead of that message is a bug — please report it. Rebuilding the module is the fix; its source needs no change.Build logs and
ostestoutput from any architecture are welcome and I will collect them here.Testing
Host: macOS 26.5.1 on arm64, Arm GNU Toolchain 14.2.Rel1, QEMU 11.0.3.
lm3s6965-ek:qemu-nxflatunder QEMU, configured and built with no overrides. Thenxflatexample runs the errno, hello and struct modules with output identical to the same configuration built from master:The same firmware, with the modules rebuilt by the old out-of-tree generator, refuses all three:
Before the ABI marker that same combination locked up in a HardFault with no console output, which is what the marker is for.
mps3-an547:picostest, which isCONFIG_PICwithoutCONFIG_NXFLAT, builds clean and does not build the thunk generator.tools/checkpatch.sh -fpasses on every changed C file and header.Not covered: ELF PIC has no runtime coverage here.
mps3-an547:picostestbuilds and boots identically to master, but NSH takes no console input in that configuration under QEMU — on master as well — so no module is actually executed. The modules were confirmed statically to use r9 as their PIC base.