From 525555181e74be3e187bcbae02c9ba8f11e9bc34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Holz?= Date: Wed, 21 Feb 2024 20:22:53 +0100 Subject: [PATCH] LibELF: Add riscv64 PLT trampoline This code is based on the aarch64 implementation. --- .../LibELF/Arch/riscv64/plt_trampoline.S | 77 ++++++++++++++++++- Userland/Libraries/LibELF/DynamicLoader.cpp | 8 ++ 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/Userland/Libraries/LibELF/Arch/riscv64/plt_trampoline.S b/Userland/Libraries/LibELF/Arch/riscv64/plt_trampoline.S index a2a6203451..c2bc97295e 100644 --- a/Userland/Libraries/LibELF/Arch/riscv64/plt_trampoline.S +++ b/Userland/Libraries/LibELF/Arch/riscv64/plt_trampoline.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Sönke Holz + * Copyright (c) 2023-2024, Sönke Holz * * SPDX-License-Identifier: BSD-2-Clause */ @@ -9,6 +9,77 @@ .hidden _plt_trampoline .type _plt_trampoline,@function +// This function is called by the PLT stub to resolve functions lazily at runtime. +// It saves off any argument registers that might be clobbered by the symbol +// resolution code, calls that, and then jumps to the resolved function. +// +// See section 8.4.6 "Program Linkage Table" of the RISC-V psABI. +// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/releases/download/v1.0/riscv-abi.pdf +// +// The calling convention is: +// t0 = .got.plt[1] (DynamicObject*) +// t1 = .got.plt offset _plt_trampoline: - // FIXME: Implement the PLT trampoline. - unimp + // Save argument registers a0-a7, fa0-fa7 and ra. + addi sp, sp, -(18 * 8) + + sd a0, 0*8(sp) + sd a1, 1*8(sp) + sd a2, 2*8(sp) + sd a3, 3*8(sp) + sd a4, 4*8(sp) + sd a5, 5*8(sp) + sd a6, 6*8(sp) + sd a7, 7*8(sp) + + // NOTE: We only support ABI_FLEN=64 in LibELF/Validation.cpp, + // so we only save the lower 64 bits of the fa* registers. + fsd fa0, 8*8(sp) + fsd fa1, 9*8(sp) + fsd fa2, 10*8(sp) + fsd fa3, 11*8(sp) + fsd fa4, 12*8(sp) + fsd fa5, 13*8(sp) + fsd fa6, 14*8(sp) + fsd fa7, 15*8(sp) + + sd ra, 16*8(sp) + + // The DynamicObject* is in t0. + mv a0, t0 + + // GOT entries are 8 bytes, but sizeof(Elf64_Rela) == 24, so multiply + // t1 by 3 to get the relocation offset. + slli a1, t1, 1 + add a1, a1, t1 + + call _fixup_plt_entry + + // Save the resolved function's address. + mv t0, a0 + + // Restore argument registers and ra. + ld a0, 0*8(sp) + ld a1, 1*8(sp) + ld a2, 2*8(sp) + ld a3, 3*8(sp) + ld a4, 4*8(sp) + ld a5, 5*8(sp) + ld a6, 6*8(sp) + ld a7, 7*8(sp) + + fld fa0, 8*8(sp) + fld fa1, 9*8(sp) + fld fa2, 10*8(sp) + fld fa3, 11*8(sp) + fld fa4, 12*8(sp) + fld fa5, 13*8(sp) + fld fa6, 14*8(sp) + fld fa7, 15*8(sp) + + ld ra, 16*8(sp) + + addi sp, sp, 18 * 8 + + // Jump to the resolved function. + jr t0 diff --git a/Userland/Libraries/LibELF/DynamicLoader.cpp b/Userland/Libraries/LibELF/DynamicLoader.cpp index 6220989c42..56c7460301 100644 --- a/Userland/Libraries/LibELF/DynamicLoader.cpp +++ b/Userland/Libraries/LibELF/DynamicLoader.cpp @@ -790,8 +790,16 @@ void DynamicLoader::setup_plt_trampoline() VirtualAddress got_address = m_dynamic_object->plt_got_base_address(); auto* got_ptr = (FlatPtr*)got_address.as_ptr(); + +#if ARCH(AARCH64) || ARCH(X86_64) got_ptr[1] = (FlatPtr)m_dynamic_object.ptr(); got_ptr[2] = (FlatPtr)&_plt_trampoline; +#elif ARCH(RISCV64) + got_ptr[0] = (FlatPtr)&_plt_trampoline; + got_ptr[1] = (FlatPtr)m_dynamic_object.ptr(); +#else +# error Unknown architecture +#endif } // Called from our ASM routine _plt_trampoline.