blob: 09b7ac85dff5b136df86114115794d73d42a16a8 [file] [log] [blame]
/*
* 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<uint8_t>* 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<uint8_t>* 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<uint8_t>* 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<uint8_t>* 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<debug::MethodDebugInfo> 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<uint32_t>(near_offset) >> 12;
return (auipc & 0x00000fffu) | // Clear offset bits, keep AUIPC with destination reg.
(imm20 << 12); // Encode the immediate.
}
void Riscv64RelativePatcher::SetInsn(std::vector<uint8_t>* 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<const uint8_t> code, uint32_t offset) {
DCHECK_LE(offset + 4u, code.size());
DCHECK_ALIGNED(offset, 2u);
const uint8_t* addr = &code[offset];
return
(static_cast<uint32_t>(addr[0]) << 0) +
(static_cast<uint32_t>(addr[1]) << 8) +
(static_cast<uint32_t>(addr[2]) << 16)+
(static_cast<uint32_t>(addr[3]) << 24);
}
template <typename Alloc>
uint32_t Riscv64RelativePatcher::GetInsn(std::vector<uint8_t, Alloc>* code, uint32_t offset) {
return GetInsn(ArrayRef<const uint8_t>(*code), offset);
}
} // namespace linker
} // namespace art