arm64: enable kmod ldscript that puts ELF header inside PT_LOAD[0]#2228
arm64: enable kmod ldscript that puts ELF header inside PT_LOAD[0]#2228b1nc0d3x wants to merge 1 commit into
Conversation
kostikbel
left a comment
There was a problem hiding this comment.
The working ldscript is what I would expect.
We have some special sections like .data.read_frequently, .data.read_mostly, and so on. They should be handled in the script, and not mixed into the .data section by incident. Please take the arm64 kernel ldscript as the starting point, and copy/adjust it for modules.
|
@zxombie probably wants to see this PR |
|
@kostikbel thanks — that's a fair call. Will rebase the ldscript on Also pulling in @zxombie as you suggested — thanks for the routing. |
Loadable kernel modules on FreeBSD/arm64 have no per-arch kmod ldscript,
so ld picks the default layout. Together with
CONSTANT(COMMONPAGESIZE)=0x10000 that default pushes the first PT_LOAD
to file offset 0x10000, leaving the Elf64_Ehdr at file offset 0 outside
every loadable segment. __elfN(loadimage)() in stand/common/load_elf.c
only copies PT_LOAD content to memory, so when the kernel later runs
link_elf_link_preload() and dereferences ef->address as Elf_Ehdr it
reads .text bytes instead of the header. The resulting bogus e_phoff
walks off the end of memory and faults in preload_protect():
panic: vm_fault failed: ... preload_protect+0x54
x8 = ef->address (module base)
x9 = 0xf9443a1190000070 (.text bytes read as e_phoff)
x21 = ef->address + x9 (unmappable, triggers far)
Reproduces with any preloaded arm64 module whose default lld layout
puts PT_LOAD[0] above the header — i.e., effectively every aarch64
.ko built without an explicit override. Workaround until now has been
to load modules at runtime via /etc/rc.conf kld_list= rather than at
boot via /boot/loader.conf, which adds ~1 s of console-blank time and
loses the per-arch loader cache.
Add a kmod ldscript for arm64 that declares PHDRS with FILEHDR + PHDRS
attributes on the first PT_LOAD, so the ELF header and program headers
are part of PT_LOAD[0]'s file content and reach memory at the module's
load address. Section coalescing matches sys/conf/ldscript.kmod.amd64.
kmod.mk already does .if exists(${SYSDIR}/conf/ldscript.kmod.${MACHINE})
so just dropping the file in enables it for every in-base arm64 module
build.
Tested on aarch64 QEMU virt:
- Pre-patch (default lld layout): preload via /boot/loader.conf of an
out-of-tree virtio_drm.ko panics at preload_protect+0x54 every
boot. PT_LOAD[0] starts at file_offset 0x10000, header at offset
0 unmapped.
- Post-patch (this ldscript): same module rebuilt, PT_LOAD[0] now
at file_offset 0 vaddr 0 covering header+phdrs+.plt+.text (verified
with readelf -l). Preload completes cleanly, module reaches
kldstat at the same load address that used to fault, and the rest
of bring-up proceeds normally.
amd64 is unaffected. i386 already has its own ldscript.kmod.i386.
Signed-off-by: Kyle Crenshaw <B1nc0d3x@gmail.com>
5154422 to
a554bc7
Compare
|
Updated per @kostikbel's feedback (force-push, since no inline review comments exist yet — top-level review thread is preserved). Rebased on
Re-tested on aarch64 QEMU virt + stock pre-patch FreeBSD-15.0 kernel + loader:
|
Summary
Loadable kernel modules on FreeBSD/arm64 currently have no per-arch kmod
ldscript, so ld picks the default layout. Combined with
CONSTANT(COMMONPAGESIZE)=0x10000, that default pushes the firstPT_LOADto file offset0x10000, leaving theElf64_Ehdrat fileoffset 0 outside every loadable segment.
__elfN(loadimage)()instand/common/load_elf.conly copiesPT_LOADcontent to memory, sowhen the kernel later runs
link_elf_link_preload()and dereferencesef->addressasElf_Ehdr, it reads.textbytes instead of theheader. The bogus
e_phoffwalks off the end of memory and faults:Reproduces with any preloaded arm64 module whose default lld layout
puts
PT_LOAD[0]above the header — which is effectively every aarch64.kobuilt without an explicit override. The user-visible workaroundhas been to load modules at runtime via
/etc/rc.conf kld_list=ratherthan at boot via
/boot/loader.conf, which adds ~1 s of console-blanktime and loses the per-arch loader cache.
Fix
Add a kmod ldscript for arm64 that declares
PHDRSwithFILEHDR PHDRSattributes on the firstPT_LOAD, so the ELF header andprogram headers become part of
PT_LOAD[0]'s file content and reachmemory at the module's load address. Section coalescing mirrors
sys/conf/ldscript.kmod.amd64.sys/conf/kmod.mkalready does:so just dropping the file in enables it for every in-base arm64 module
build with no Makefile churn.
amd64 is unaffected. i386 has its own ldscript.kmod.i386 and is
unaffected.
Test plan
Validated on aarch64 QEMU
virt(cortex-a72), stockFreeBSD-15.0-RELEASE-p4 kernel + loader, out-of-tree
virtio_drm.ko:.kobuilt with default lld layout.readelf -lshows
PT_LOAD[0]atOffset 0x10000, header at0unmapped./boot/loader.confpreload → panics atpreload_protect+0x54withthe register dump above on every boot. Just reproduced today on
the same VM.
readelf -lnow showsPT_LOAD[0]atOffset 0 VirtAddr 0 FileSiz 0x7000, covering header + phdrs +.plt+.text. Samepreload path completes cleanly, module reaches
kldstatat thesame load address that previously faulted, no functional
regression.
Suggested reviewers
Per recent committers to neighbouring files:
sys/conf/ldscript.kmod.amd64author; loader / kern_linkerwork)
this approach on stand+kern: pass ELF header and phdrs from loader to kernel via metadata #2224)
Notes
Supersedes PR #2224, which proposed a metadata-based fix in the loader +
kern_linker for the same panic. Re-testing today showed the ldscript
approach is sufficient on its own, contrary to what was claimed in that
PR's body. #2224 is being closed in favour of this smaller, more
surgical fix.