CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
291 lines
9.5 KiB
C++
291 lines
9.5 KiB
C++
//===- AArch64MCLFIRewriter.cpp ---------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the AArch64MCLFIRewriter class, the AArch64 specific
|
|
// subclass of MCLFIRewriter.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "AArch64MCLFIRewriter.h"
|
|
#include "AArch64AddressingModes.h"
|
|
#include "MCTargetDesc/AArch64MCTargetDesc.h"
|
|
#include "Utils/AArch64BaseInfo.h"
|
|
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/MC/MCInst.h"
|
|
#include "llvm/MC/MCStreamer.h"
|
|
#include "llvm/MC/MCSubtargetInfo.h"
|
|
|
|
using namespace llvm;
|
|
|
|
// LFI reserved registers.
|
|
static constexpr MCRegister LFIBaseReg = AArch64::X27;
|
|
static constexpr MCRegister LFIAddrReg = AArch64::X28;
|
|
static constexpr MCRegister LFIScratchReg = AArch64::X26;
|
|
static constexpr MCRegister LFICtxReg = AArch64::X25;
|
|
|
|
// Offset into the context register block (pointed to by LFICtxReg) where the
|
|
// thread pointer is stored. This is a scaled offset (multiplied by 8 for
|
|
// 64-bit loads), so a value of 2 means an actual byte offset of 16.
|
|
static constexpr unsigned LFITPOffset = 2;
|
|
|
|
// Byte offset from the sandbox base register where the syscall handler address
|
|
// is stored (negative because it is below the sandbox base).
|
|
static constexpr int LFISyscallOffset = -8;
|
|
|
|
static bool isSyscall(const MCInst &Inst) {
|
|
return Inst.getOpcode() == AArch64::SVC;
|
|
}
|
|
|
|
static bool isPrivilegedTP(int64_t Reg) {
|
|
return Reg == AArch64SysReg::TPIDR_EL1 || Reg == AArch64SysReg::TPIDR_EL2 ||
|
|
Reg == AArch64SysReg::TPIDR_EL3;
|
|
}
|
|
|
|
static bool isTPRead(const MCInst &Inst) {
|
|
return Inst.getOpcode() == AArch64::MRS &&
|
|
Inst.getOperand(1).getImm() == AArch64SysReg::TPIDR_EL0;
|
|
}
|
|
|
|
static bool isTPWrite(const MCInst &Inst) {
|
|
return Inst.getOpcode() == AArch64::MSR &&
|
|
Inst.getOperand(0).getImm() == AArch64SysReg::TPIDR_EL0;
|
|
}
|
|
|
|
static bool isPrivilegedTPAccess(const MCInst &Inst) {
|
|
if (Inst.getOpcode() == AArch64::MRS)
|
|
return isPrivilegedTP(Inst.getOperand(1).getImm());
|
|
if (Inst.getOpcode() == AArch64::MSR)
|
|
return isPrivilegedTP(Inst.getOperand(0).getImm());
|
|
return false;
|
|
}
|
|
|
|
MCRegister AArch64MCLFIRewriter::mayModifyReserved(const MCInst &Inst) const {
|
|
for (MCRegister Reg : {LFIAddrReg, LFIBaseReg, LFICtxReg}) {
|
|
if (mayModifyRegister(Inst, Reg))
|
|
return Reg;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
void AArch64MCLFIRewriter::emitInst(const MCInst &Inst, MCStreamer &Out,
|
|
const MCSubtargetInfo &STI) {
|
|
Out.emitInstruction(Inst, STI);
|
|
}
|
|
|
|
void AArch64MCLFIRewriter::emitAddMask(MCRegister Dest, MCRegister Src,
|
|
MCStreamer &Out,
|
|
const MCSubtargetInfo &STI) {
|
|
// add Dest, LFIBaseReg, W(Src), uxtw
|
|
MCInst Inst;
|
|
Inst.setOpcode(AArch64::ADDXrx);
|
|
Inst.addOperand(MCOperand::createReg(Dest));
|
|
Inst.addOperand(MCOperand::createReg(LFIBaseReg));
|
|
Inst.addOperand(MCOperand::createReg(getWRegFromXReg(Src)));
|
|
Inst.addOperand(
|
|
MCOperand::createImm(AArch64_AM::getArithExtendImm(AArch64_AM::UXTW, 0)));
|
|
emitInst(Inst, Out, STI);
|
|
}
|
|
|
|
void AArch64MCLFIRewriter::emitBranch(unsigned Opcode, MCRegister Target,
|
|
MCStreamer &Out,
|
|
const MCSubtargetInfo &STI) {
|
|
MCInst Branch;
|
|
Branch.setOpcode(Opcode);
|
|
Branch.addOperand(MCOperand::createReg(Target));
|
|
emitInst(Branch, Out, STI);
|
|
}
|
|
|
|
void AArch64MCLFIRewriter::emitPendingTLSDescCall(MCStreamer &Out,
|
|
const MCSubtargetInfo &STI) {
|
|
if (!PendingTLSDescCall)
|
|
return;
|
|
MCInst Marker;
|
|
Marker.setOpcode(AArch64::TLSDESCCALL);
|
|
Marker.addOperand(MCOperand::createExpr(PendingTLSDescCall));
|
|
PendingTLSDescCall = nullptr;
|
|
emitInst(Marker, Out, STI);
|
|
}
|
|
|
|
void AArch64MCLFIRewriter::emitMov(MCRegister Dest, MCRegister Src,
|
|
MCStreamer &Out,
|
|
const MCSubtargetInfo &STI) {
|
|
// orr Dest, xzr, Src
|
|
MCInst Inst;
|
|
Inst.setOpcode(AArch64::ORRXrs);
|
|
Inst.addOperand(MCOperand::createReg(Dest));
|
|
Inst.addOperand(MCOperand::createReg(AArch64::XZR));
|
|
Inst.addOperand(MCOperand::createReg(Src));
|
|
Inst.addOperand(MCOperand::createImm(0));
|
|
emitInst(Inst, Out, STI);
|
|
}
|
|
|
|
// {br,blr} xN
|
|
// ->
|
|
// add x28, x27, wN, uxtw
|
|
// {br,blr} x28
|
|
void AArch64MCLFIRewriter::rewriteIndirectBranch(const MCInst &Inst,
|
|
MCStreamer &Out,
|
|
const MCSubtargetInfo &STI) {
|
|
assert(Inst.getNumOperands() >= 1 && Inst.getOperand(0).isReg() &&
|
|
"expected register operand");
|
|
MCRegister BranchReg = Inst.getOperand(0).getReg();
|
|
|
|
// Guard the branch target through X28.
|
|
emitAddMask(LFIAddrReg, BranchReg, Out, STI);
|
|
|
|
emitPendingTLSDescCall(Out, STI);
|
|
|
|
emitBranch(Inst.getOpcode(), LFIAddrReg, Out, STI);
|
|
}
|
|
|
|
// ret xN (where xN != x30)
|
|
// ->
|
|
// add x28, x27, wN, uxtw
|
|
// ret x28
|
|
//
|
|
// ret (x30) is safe since x30 is always within the sandbox.
|
|
void AArch64MCLFIRewriter::rewriteReturn(const MCInst &Inst, MCStreamer &Out,
|
|
const MCSubtargetInfo &STI) {
|
|
assert(Inst.getNumOperands() >= 1 && Inst.getOperand(0).isReg() &&
|
|
"expected register operand");
|
|
// RET through LR is safe since LR is always within sandbox.
|
|
if (Inst.getOperand(0).getReg() != AArch64::LR)
|
|
rewriteIndirectBranch(Inst, Out, STI);
|
|
else
|
|
emitInst(Inst, Out, STI);
|
|
}
|
|
|
|
// modify x30
|
|
// ->
|
|
// modify x30
|
|
// add x30, x27, w30, uxtw
|
|
void AArch64MCLFIRewriter::rewriteLRModification(const MCInst &Inst,
|
|
MCStreamer &Out,
|
|
const MCSubtargetInfo &STI) {
|
|
emitInst(Inst, Out, STI);
|
|
emitAddMask(AArch64::LR, AArch64::LR, Out, STI);
|
|
}
|
|
|
|
// svc #0
|
|
// ->
|
|
// mov x26, x30
|
|
// ldur x30, [x27, #-8]
|
|
// blr x30
|
|
// add x30, x27, w26, uxtw
|
|
void AArch64MCLFIRewriter::rewriteSyscall(const MCInst &, MCStreamer &Out,
|
|
const MCSubtargetInfo &STI) {
|
|
// Save LR to scratch.
|
|
emitMov(LFIScratchReg, AArch64::LR, Out, STI);
|
|
|
|
// Load syscall handler address from negative offset from sandbox base.
|
|
MCInst Load;
|
|
Load.setOpcode(AArch64::LDURXi);
|
|
Load.addOperand(MCOperand::createReg(AArch64::LR));
|
|
Load.addOperand(MCOperand::createReg(LFIBaseReg));
|
|
Load.addOperand(MCOperand::createImm(LFISyscallOffset));
|
|
emitInst(Load, Out, STI);
|
|
|
|
// Call the runtime.
|
|
emitBranch(AArch64::BLR, AArch64::LR, Out, STI);
|
|
|
|
// Restore LR with guard.
|
|
emitAddMask(AArch64::LR, LFIScratchReg, Out, STI);
|
|
}
|
|
|
|
// mrs xN, tpidr_el0
|
|
// ->
|
|
// ldr xN, [x25, #16]
|
|
void AArch64MCLFIRewriter::rewriteTPRead(const MCInst &Inst, MCStreamer &Out,
|
|
const MCSubtargetInfo &STI) {
|
|
MCRegister DestReg = Inst.getOperand(0).getReg();
|
|
|
|
MCInst Load;
|
|
Load.setOpcode(AArch64::LDRXui);
|
|
Load.addOperand(MCOperand::createReg(DestReg));
|
|
Load.addOperand(MCOperand::createReg(LFICtxReg));
|
|
Load.addOperand(MCOperand::createImm(LFITPOffset));
|
|
emitInst(Load, Out, STI);
|
|
}
|
|
|
|
// msr tpidr_el0, xN
|
|
// ->
|
|
// str xN, [x25, #16]
|
|
void AArch64MCLFIRewriter::rewriteTPWrite(const MCInst &Inst, MCStreamer &Out,
|
|
const MCSubtargetInfo &STI) {
|
|
MCRegister SrcReg = Inst.getOperand(1).getReg();
|
|
|
|
MCInst Store;
|
|
Store.setOpcode(AArch64::STRXui);
|
|
Store.addOperand(MCOperand::createReg(SrcReg));
|
|
Store.addOperand(MCOperand::createReg(LFICtxReg));
|
|
Store.addOperand(MCOperand::createImm(LFITPOffset));
|
|
emitInst(Store, Out, STI);
|
|
}
|
|
|
|
// NOTE: when adding new rewrites, the size estimates in
|
|
// AArch64InstrInfo::getLFIInstSizeInBytes must be updated to match.
|
|
void AArch64MCLFIRewriter::doRewriteInst(const MCInst &Inst, MCStreamer &Out,
|
|
const MCSubtargetInfo &STI) {
|
|
if (Inst.getOpcode() == AArch64::TLSDESCCALL) {
|
|
PendingTLSDescCall = Inst.getOperand(0).getExpr();
|
|
return;
|
|
}
|
|
|
|
// Reserved register modification is an error.
|
|
if (MCRegister Reg = mayModifyReserved(Inst)) {
|
|
error(Inst, Twine("illegal modification of reserved LFI register ") +
|
|
RegInfo->getName(Reg));
|
|
return;
|
|
}
|
|
|
|
// System instructions.
|
|
if (isSyscall(Inst))
|
|
return rewriteSyscall(Inst, Out, STI);
|
|
|
|
if (isTPRead(Inst))
|
|
return rewriteTPRead(Inst, Out, STI);
|
|
|
|
if (isTPWrite(Inst))
|
|
return rewriteTPWrite(Inst, Out, STI);
|
|
|
|
if (isPrivilegedTPAccess(Inst)) {
|
|
error(Inst, "illegal access to privileged thread pointer register");
|
|
return;
|
|
}
|
|
|
|
// Control flow.
|
|
switch (Inst.getOpcode()) {
|
|
case AArch64::RET:
|
|
return rewriteReturn(Inst, Out, STI);
|
|
case AArch64::BR:
|
|
case AArch64::BLR:
|
|
return rewriteIndirectBranch(Inst, Out, STI);
|
|
}
|
|
|
|
// Link register modification.
|
|
if (explicitlyModifiesRegister(Inst, AArch64::LR))
|
|
return rewriteLRModification(Inst, Out, STI);
|
|
|
|
emitInst(Inst, Out, STI);
|
|
}
|
|
|
|
bool AArch64MCLFIRewriter::rewriteInst(const MCInst &Inst, MCStreamer &Out,
|
|
const MCSubtargetInfo &STI) {
|
|
// The guard prevents rewrite-recursion when we emit instructions from inside
|
|
// the rewriter (such instructions should not be rewritten).
|
|
if (!Enabled || Guard)
|
|
return false;
|
|
Guard = true;
|
|
|
|
doRewriteInst(Inst, Out, STI);
|
|
|
|
Guard = false;
|
|
return true;
|
|
}
|