Skip to content

[6/10] libs/libc/machine/arm: Relocate FDPIC function descriptors - #20089

Open
casaroli wants to merge 1 commit into
apache:masterfrom
casaroli:fdpic-arm-reloc
Open

[6/10] libs/libc/machine/arm: Relocate FDPIC function descriptors#20089
casaroli wants to merge 1 commit into
apache:masterfrom
casaroli:fdpic-arm-reloc

Conversation

@casaroli

@casaroli casaroli commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Summary

[5/10] #19942 places an FDPIC object's segments and reads its dynamic tags. This relocates it.

A function pointer under FDPIC is not a code address. The two PT_LOAD segments are placed independently, so a pointer has to carry the data base its callee needs: a two word descriptor of entry point and base. R_ARM_FUNCDESC_VALUE says the word being patched is such a descriptor; R_ARM_FUNCDESC says to manufacture one and store its address.

Both need state a relocation cannot carry. The second word is the object's own data base, from DT_PLTGOT, and R_ARM_FUNCDESC takes descriptors from a pool whose cursor must survive from one relocation to the next. up_relocate() gets only a relocation, a symbol and an address.

arch_data is the existing channel for that; RISC-V already uses it to hold a HI20 relocation while its LO12 partner is processed. Nothing has ever seeded it from the loader, so ARCH_ELFDATA_INIT and ARCH_ELFDATA_FINI are added. Both default to nothing.

The GOT written into a descriptor is always the loading object's own, even for an imported function. That is what makes a callback work: when the firmware's qsort() calls into a module's comparison function, the module needs its own base in the PIC register.

Impact

Nothing changes without CONFIG_FDPIC. ARCH_ELFDATA_INIT/FINI are empty when an architecture does not define them, and RISC-V's use of arch_data is untouched.

An FDPIC relocation in an object whose OS/ABI byte does not say FDPIC is now refused rather than mis-applied.

Implemented for armv7-m and armv8-m, the profiles FDPIC targets. Other ARM variants gain the arch_elfdata_t fields but no new relocations.

Testing

mps3-an547:picostest on QEMU, which is the ELF PIC user of this code: it loads PIC ELF modules from a romfs, hello prints and ostest runs through the timed mutex test, the same as without this patch.

pimoroni-pico-2-plus:xipfs-fdpic builds with CONFIG_FDPIC on and off.

tools/checkpatch.sh -c -u -m -g passes.

Comment thread arch/arm/include/elf.h Outdated
@github-actions github-actions Bot added Arch: arm Issues related to ARM (32-bit) architecture 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
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

s698pm-dkit

@casaroli
casaroli force-pushed the fdpic-arm-reloc branch 2 times, most recently from 7f42251 to cfce0a3 Compare September 8, 2026 19:57
@github-actions github-actions Bot added Area: OS Components OS Components issues 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
Comment thread libs/libc/elf/elf_bind.c Outdated
Comment thread libs/libc/elf/elf_bind.c Outdated
Comment thread libs/libc/machine/arm/armv8-m/arch_elf.c Outdated
Comment thread libs/libc/elf/elf_bind.c
*(FAR uintptr_t *)addr = (uintptr_t)ep;
}
}
else if (loadinfo->fdpic)

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.

can we share the same else if and else block, let up_relocate handle the difference?

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.

but no change here?

A function pointer under FDPIC is not a code address.  Because each
PT_LOAD segment is placed independently, a pointer has to carry the data
base its callee will need, so it is a two-word descriptor: the entry
point, and the base to install in the PIC register before branching.
R_ARM_FUNCDESC_VALUE says "the thing you are patching is such a
descriptor", and R_ARM_FUNCDESC says "manufacture one and give me its
address".

Both need state a relocation cannot carry.  A descriptor's second word is
the *object's* data base, from DT_PLTGOT, and R_ARM_FUNCDESC carves
descriptors from a pool whose cursor has to survive from one relocation
to the next.  up_relocate() is handed only a relocation, a resolved
symbol and an address to patch.

arch_data is the existing channel for exactly this -- RISC-V already uses
it to remember a HI20 relocation while its LO12 partner is processed --
but nothing has ever put loader state into it: it is declared zeroed and
written only by up_relocate() itself.  So ARCH_ELFDATA_INIT and
ARCH_ELFDATA_FINI are added, seeding the block from the loadinfo before
the relocation loop and reading the cursor back after.  Both default to
nothing, so an architecture that does not define them is unaffected, and
RISC-V's use of arch_data is untouched.  libelf_relocatedyn() walks both
dynamic tables under one arch_data, so the cursor spans the whole object.

The addend handling is the part that is easy to get wrong.  REL format
keeps the addend in place, in the word about to become the entry point,
and a pointer to a static function is referenced through its *section*
symbol -- the value is the section base and the offset, including the
Thumb bit, is entirely in the addend.  Dropping it yields an even address
and the core faults trying to execute it as ARM code.

The GOT written into a descriptor is the loading object's own, even for
an imported function, which is what makes a callback work: when the base
firmware's qsort() calls back into a module's comparison function, the
module needs its own data base in the PIC register.

libelf_relocatedyn()'s imported-symbol path needed a change to suit.  It
stores the resolved address directly and never calls up_relocate(), which
cannot produce a two-word descriptor, so under FDPIC the resolved value
now goes through up_relocate() and the relocation type decides what to
write.

Implemented for armv7-m and armv8-m, the profiles FDPIC targets; the
other ARM variants gain the arch_data block but no new relocations.
Built and booted mps3-an547:picostest and lm3s6965-ek:qemu-nxflat, the
ELF PIC and NXFLAT users of this code, both unchanged.

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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: M The size of the change in this PR is medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants