blob: 7ff873644fc778b3f6f0e8b9964730cecc875d19 [file] [log] [blame]
/*
* Copyright (C) 2016 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 "target_aarch64.h"
#include <cassert>
#include <memory>
#include "MCTargetDesc/AArch64MCTargetDesc.h"
#include "llvm/ADT/Triple.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstBuilder.h"
#include "code_generator.h"
#include "disassembler.h"
using namespace interceptor;
static llvm::Triple GetTriple() {
llvm::Triple triple(llvm::sys::getProcessTriple());
assert(triple.getArch() == llvm::Triple::aarch64 &&
"Invalid default host triple for target");
return triple;
}
CodeGenerator *TargetAARCH64::GetCodeGenerator(void *address,
size_t start_alignment) {
return CodeGenerator::Create(GetTriple(), start_alignment);
}
Disassembler *TargetAARCH64::CreateDisassembler(void *address) {
return Disassembler::Create(GetTriple());
}
std::vector<TrampolineConfig> TargetAARCH64::GetTrampolineConfigs(
uintptr_t start_address) const {
std::vector<TrampolineConfig> configs;
configs.push_back({FIRST_4G_TRAMPOLINE, false, 0x10000, 0xffffffff});
configs.push_back({FULL_TRAMPOLINE, false, 0, 0xffffffffffffffff});
return configs;
}
Error TargetAARCH64::EmitTrampoline(const TrampolineConfig &config,
CodeGenerator &codegen, void *source,
void *target) {
switch (config.type) {
case FIRST_4G_TRAMPOLINE: {
uint64_t target_addr = reinterpret_cast<uintptr_t>(target);
if (target_addr > 0xffffffff)
return Error("Target address is out of range for the trampoline");
uint32_t target_addr32 = target_addr;
codegen.AddInstruction(
llvm::MCInstBuilder(llvm::AArch64::LDRWl)
.addReg(llvm::AArch64::X17)
.addExpr(codegen.CreateDataExpr(target_addr32)));
codegen.AddInstruction(
llvm::MCInstBuilder(llvm::AArch64::BR).addReg(llvm::AArch64::X17));
return Error();
}
case FULL_TRAMPOLINE: {
uint64_t target_addr = reinterpret_cast<uintptr_t>(target);
codegen.AddInstruction(llvm::MCInstBuilder(llvm::AArch64::LDRXl)
.addReg(llvm::AArch64::X17)
.addExpr(codegen.CreateDataExpr(target_addr)));
codegen.AddInstruction(
llvm::MCInstBuilder(llvm::AArch64::BR).addReg(llvm::AArch64::X17));
return Error();
}
}
return Error("Unsupported trampoline type");
}
static void *calculatePcRelativeAddress(void *data, size_t pc_offset,
size_t offset, bool page_align) {
uintptr_t data_addr = reinterpret_cast<uintptr_t>(data);
assert((data_addr & 3) == 0 && "Unaligned data address");
assert((pc_offset & 3) == 0 && "Unaligned PC offset");
data_addr += pc_offset; // Add the PC
if (page_align) {
data_addr &= ~0x0fff; // Align to 4KB
offset <<= 12;
}
data_addr += offset; // Add the offset
return reinterpret_cast<void *>(data_addr);
}
// IP1 (second intra-procedure-call scratch register) is X17 and it is used in
// the trampoline so we need special handling for it.
static bool hasIP1Operand(const llvm::MCInst &inst) {
for (size_t i = 0; i < inst.getNumOperands(); ++i) {
const llvm::MCOperand &op = inst.getOperand(i);
if (op.isReg() && op.getReg() == llvm::AArch64::X17) return true;
}
return false;
}
Error TargetAARCH64::RewriteInstruction(const llvm::MCInst &inst,
CodeGenerator &codegen, void *data,
size_t offset,
bool &possible_end_of_function) {
switch (inst.getOpcode()) {
case llvm::AArch64::ADDXri:
case llvm::AArch64::ANDXri:
case llvm::AArch64::LDRXui:
case llvm::AArch64::MOVNWi:
case llvm::AArch64::MOVNXi:
case llvm::AArch64::MOVZWi:
case llvm::AArch64::MOVZXi:
case llvm::AArch64::MRS:
case llvm::AArch64::ORRWrs:
case llvm::AArch64::ORRXrs:
case llvm::AArch64::STPDi:
case llvm::AArch64::STPXi:
case llvm::AArch64::STPXpre:
case llvm::AArch64::STRBBui:
case llvm::AArch64::STRSui:
case llvm::AArch64::STRWui:
case llvm::AArch64::STRXpre:
case llvm::AArch64::STRXui:
case llvm::AArch64::SUBSWri:
case llvm::AArch64::SUBSXri:
case llvm::AArch64::SUBXri: {
if (hasIP1Operand(inst))
return Error(
"Instruction not handled yet when one of the operand is IP1 (%s "
"(OpcodeId: %d))",
codegen.PrintInstruction(inst).c_str(), inst.getOpcode());
possible_end_of_function = false;
codegen.AddInstruction(inst);
break;
}
case llvm::AArch64::ADRP: {
uint32_t Rd = inst.getOperand(0).getReg();
uint64_t imm = inst.getOperand(1).getImm();
possible_end_of_function = false;
if (hasIP1Operand(inst))
return Error(
"Instruction not handled yet when one of the operand is IP1 (%s "
"(OpcodeId: %d))",
codegen.PrintInstruction(inst).c_str(), inst.getOpcode());
uint64_t addr = reinterpret_cast<uintptr_t>(
calculatePcRelativeAddress(data, offset, imm, true));
codegen.AddInstruction(llvm::MCInstBuilder(llvm::AArch64::LDRXl)
.addReg(Rd)
.addExpr(codegen.CreateDataExpr(addr)));
break;
}
case llvm::AArch64::B: {
uint64_t imm = inst.getOperand(0).getImm() << 2;
possible_end_of_function = true;
uint64_t addr = reinterpret_cast<uintptr_t>(
calculatePcRelativeAddress(data, offset, imm, false));
codegen.AddInstruction(llvm::MCInstBuilder(llvm::AArch64::LDRXl)
.addReg(llvm::AArch64::X17)
.addExpr(codegen.CreateDataExpr(addr)));
codegen.AddInstruction(
llvm::MCInstBuilder(llvm::AArch64::BR).addReg(llvm::AArch64::X17));
break;
}
case llvm::AArch64::BL: {
uint64_t imm = inst.getOperand(0).getImm() << 2;
possible_end_of_function = true;
uint64_t addr = reinterpret_cast<uintptr_t>(
calculatePcRelativeAddress(data, offset, imm, false));
codegen.AddInstruction(llvm::MCInstBuilder(llvm::AArch64::LDRXl)
.addReg(llvm::AArch64::X17)
.addExpr(codegen.CreateDataExpr(addr)));
codegen.AddInstruction(
llvm::MCInstBuilder(llvm::AArch64::BLR).addReg(llvm::AArch64::X17));
break;
}
case llvm::AArch64::CBZX: {
uint32_t Rt = inst.getOperand(0).getReg();
uint64_t imm = inst.getOperand(1).getImm() << 2;
possible_end_of_function = false;
if (hasIP1Operand(inst))
return Error(
"Instruction not handled yet when one of the operand is IP0 (%s "
"(OpcodeId: %d))",
codegen.PrintInstruction(inst).c_str(), inst.getOpcode());
uint64_t addr = reinterpret_cast<uintptr_t>(
calculatePcRelativeAddress(data, offset, imm, false));
codegen.AddInstruction(
llvm::MCInstBuilder(llvm::AArch64::CBNZX).addReg(Rt).addImm(12 >> 2));
codegen.AddInstruction(llvm::MCInstBuilder(llvm::AArch64::LDRXl)
.addReg(llvm::AArch64::X17)
.addExpr(codegen.CreateDataExpr(addr)));
codegen.AddInstruction(
llvm::MCInstBuilder(llvm::AArch64::BR).addReg(llvm::AArch64::X17));
break;
}
default: {
possible_end_of_function = true;
return Error("Unhandled instruction: %s (OpcodeId: %d)",
codegen.PrintInstruction(inst).c_str(), inst.getOpcode());
}
}
return Error();
}