/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "relative_patcher_riscv64.h" #include "base/bit_utils.h" #include "debug/method_debug_info.h" #include "linker/linker_patch.h" namespace art { namespace linker { Riscv64RelativePatcher::Riscv64RelativePatcher( [[maybe_unused]] RelativePatcherThunkProvider* thunk_provider, [[maybe_unused]] RelativePatcherTargetProvider* target_provider, [[maybe_unused]] const Riscv64InstructionSetFeatures* features) : RelativePatcher() { } uint32_t Riscv64RelativePatcher::ReserveSpace( uint32_t offset, [[maybe_unused]] const CompiledMethod* compiled_method, [[maybe_unused]] MethodReference method_ref) { // TODO(riscv64): Reduce code size for AOT by using shared trampolines for slow path // runtime calls across the entire oat file. These need space reserved here. return offset; } uint32_t Riscv64RelativePatcher::ReserveSpaceEnd(uint32_t offset) { // TODO(riscv64): Reduce code size for AOT by using shared trampolines for slow path // runtime calls across the entire oat file. These need space reserved here. return offset; } uint32_t Riscv64RelativePatcher::WriteThunks([[maybe_unused]] OutputStream* out, uint32_t offset) { // TODO(riscv64): Reduce code size for AOT by using shared trampolines for slow path // runtime calls across the entire oat file. These need to be written here. return offset; } void Riscv64RelativePatcher::PatchCall([[maybe_unused]] std::vector* code, [[maybe_unused]] uint32_t literal_offset, [[maybe_unused]] uint32_t patch_offset, [[maybe_unused]] uint32_t target_offset) { // Direct calls are currently not used on any architecture. UNIMPLEMENTED(FATAL) << "Unsupported direct call."; } void Riscv64RelativePatcher::PatchPcRelativeReference(std::vector* code, const LinkerPatch& patch, uint32_t patch_offset, uint32_t target_offset) { DCHECK_ALIGNED(patch_offset, 2u); DCHECK_ALIGNED(target_offset, 2u); uint32_t literal_offset = patch.LiteralOffset(); uint32_t insn = GetInsn(code, literal_offset); uint32_t pc_insn_offset = patch.PcInsnOffset(); uint32_t disp = target_offset - (patch_offset - literal_offset + pc_insn_offset); if (literal_offset == pc_insn_offset) { // Check it's an AUIPC with imm == 0x12345 (unset). DCHECK_EQ((insn & 0xfffff07fu), 0x12345017u) << literal_offset << ", " << pc_insn_offset << ", 0x" << std::hex << insn; insn = PatchAuipc(insn, disp); } else { DCHECK_EQ((insn & 0xfff00000u), 0x67800000u); CHECK((insn & 0x0000707fu) == 0x00000013u || // ADD (insn & 0x0000707fu) == 0x00006003u || // LWU (insn & 0x0000707fu) == 0x00003003u) // LD << "insn: 0x" << std::hex << insn << ", type: " << patch.GetType(); // Check that pc_insn_offset points to AUIPC with matching register. DCHECK_EQ(GetInsn(code, pc_insn_offset) & 0x00000fffu, 0x00000017 | (((insn >> 15) & 0x1fu) << 7)); uint32_t imm12 = disp & 0xfffu; // The instruction shall sign-extend this immediate. insn = (insn & ~(0xfffu << 20)) | (imm12 << 20); } SetInsn(code, literal_offset, insn); } void Riscv64RelativePatcher::PatchEntrypointCall([[maybe_unused]] std::vector* code, [[maybe_unused]] const LinkerPatch& patch, [[maybe_unused]] uint32_t patch_offset) { // TODO(riscv64): Reduce code size for AOT by using shared trampolines for slow path // runtime calls across the entire oat file. Calls to these trapolines need to be patched here. UNIMPLEMENTED(FATAL) << "Shared entrypoint trampolines are not implemented."; } void Riscv64RelativePatcher::PatchBakerReadBarrierBranch( [[maybe_unused]] std::vector* code, [[maybe_unused]] const LinkerPatch& patch, [[maybe_unused]] uint32_t patch_offset) { // Baker read barrier with introspection is not implemented. // Such implementation is impractical given the short reach of conditional branches. UNIMPLEMENTED(FATAL) << "Baker read barrier branches are not used on riscv64."; } std::vector Riscv64RelativePatcher::GenerateThunkDebugInfo( [[maybe_unused]] uint32_t executable_offset) { // TODO(riscv64): Reduce code size for AOT by using shared trampolines for slow path // runtime calls across the entire oat file. These need debug info generated here. return {}; } uint32_t Riscv64RelativePatcher::PatchAuipc(uint32_t auipc, int32_t offset) { // The highest 0x800 values are out of range. DCHECK_LT(offset, 0x7ffff800); // Round `offset` to nearest 4KiB offset because short offset has range [-0x800, 0x800). int32_t near_offset = (offset + 0x800) & ~0xfff; // Extract the `imm20`. uint32_t imm20 = static_cast(near_offset) >> 12; return (auipc & 0x00000fffu) | // Clear offset bits, keep AUIPC with destination reg. (imm20 << 12); // Encode the immediate. } void Riscv64RelativePatcher::SetInsn(std::vector* code, uint32_t offset, uint32_t value) { DCHECK_LE(offset + 4u, code->size()); DCHECK_ALIGNED(offset, 2u); uint8_t* addr = &(*code)[offset]; addr[0] = (value >> 0) & 0xff; addr[1] = (value >> 8) & 0xff; addr[2] = (value >> 16) & 0xff; addr[3] = (value >> 24) & 0xff; } uint32_t Riscv64RelativePatcher::GetInsn(ArrayRef code, uint32_t offset) { DCHECK_LE(offset + 4u, code.size()); DCHECK_ALIGNED(offset, 2u); const uint8_t* addr = &code[offset]; return (static_cast(addr[0]) << 0) + (static_cast(addr[1]) << 8) + (static_cast(addr[2]) << 16)+ (static_cast(addr[3]) << 24); } template uint32_t Riscv64RelativePatcher::GetInsn(std::vector* code, uint32_t offset) { return GetInsn(ArrayRef(*code), offset); } } // namespace linker } // namespace art