LLVM 23.0.0git
AArch64MCLFIRewriter.cpp
Go to the documentation of this file.
1//===- AArch64MCLFIRewriter.cpp ---------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the AArch64MCLFIRewriter class, the AArch64 specific
10// subclass of MCLFIRewriter.
11//
12//===----------------------------------------------------------------------===//
13
18
19#include "llvm/ADT/Twine.h"
20#include "llvm/MC/MCInst.h"
21#include "llvm/MC/MCStreamer.h"
23
24using namespace llvm;
25
26// LFI reserved registers.
27static constexpr MCRegister LFIBaseReg = AArch64::X27;
28static constexpr MCRegister LFIAddrReg = AArch64::X28;
29static constexpr MCRegister LFIScratchReg = AArch64::X26;
30static constexpr MCRegister LFICtxReg = AArch64::X25;
31
32// Offset into the context register block (pointed to by LFICtxReg) where the
33// thread pointer is stored. This is a scaled offset (multiplied by 8 for
34// 64-bit loads), so a value of 2 means an actual byte offset of 16.
35static constexpr unsigned LFITPOffset = 2;
36
37// Byte offset from the sandbox base register where the syscall handler address
38// is stored (negative because it is below the sandbox base).
39static constexpr int LFISyscallOffset = -8;
40
41static bool isSyscall(const MCInst &Inst) {
42 return Inst.getOpcode() == AArch64::SVC;
43}
44
45static bool isPrivilegedTP(int64_t Reg) {
46 return Reg == AArch64SysReg::TPIDR_EL1 || Reg == AArch64SysReg::TPIDR_EL2 ||
47 Reg == AArch64SysReg::TPIDR_EL3;
48}
49
50static bool isTPRead(const MCInst &Inst) {
51 return Inst.getOpcode() == AArch64::MRS &&
52 Inst.getOperand(1).getImm() == AArch64SysReg::TPIDR_EL0;
53}
54
55static bool isTPWrite(const MCInst &Inst) {
56 return Inst.getOpcode() == AArch64::MSR &&
57 Inst.getOperand(0).getImm() == AArch64SysReg::TPIDR_EL0;
58}
59
60static bool isPrivilegedTPAccess(const MCInst &Inst) {
61 if (Inst.getOpcode() == AArch64::MRS)
62 return isPrivilegedTP(Inst.getOperand(1).getImm());
63 if (Inst.getOpcode() == AArch64::MSR)
64 return isPrivilegedTP(Inst.getOperand(0).getImm());
65 return false;
66}
67
68MCRegister AArch64MCLFIRewriter::mayModifyReserved(const MCInst &Inst) const {
69 for (MCRegister Reg : {LFIAddrReg, LFIBaseReg, LFICtxReg}) {
70 if (mayModifyRegister(Inst, Reg))
71 return Reg;
72 }
73 return {};
74}
75
76void AArch64MCLFIRewriter::emitInst(const MCInst &Inst, MCStreamer &Out,
77 const MCSubtargetInfo &STI) {
78 Out.emitInstruction(Inst, STI);
79}
80
81void AArch64MCLFIRewriter::emitAddMask(MCRegister Dest, MCRegister Src,
82 MCStreamer &Out,
83 const MCSubtargetInfo &STI) {
84 // add Dest, LFIBaseReg, W(Src), uxtw
85 MCInst Inst;
86 Inst.setOpcode(AArch64::ADDXrx);
90 Inst.addOperand(
92 emitInst(Inst, Out, STI);
93}
94
95void AArch64MCLFIRewriter::emitBranch(unsigned Opcode, MCRegister Target,
96 MCStreamer &Out,
97 const MCSubtargetInfo &STI) {
98 MCInst Branch;
99 Branch.setOpcode(Opcode);
100 Branch.addOperand(MCOperand::createReg(Target));
101 emitInst(Branch, Out, STI);
102}
103
104void AArch64MCLFIRewriter::emitMov(MCRegister Dest, MCRegister Src,
105 MCStreamer &Out,
106 const MCSubtargetInfo &STI) {
107 // orr Dest, xzr, Src
108 MCInst Inst;
109 Inst.setOpcode(AArch64::ORRXrs);
111 Inst.addOperand(MCOperand::createReg(AArch64::XZR));
114 emitInst(Inst, Out, STI);
115}
116
117// {br,blr} xN
118// ->
119// add x28, x27, wN, uxtw
120// {br,blr} x28
121void AArch64MCLFIRewriter::rewriteIndirectBranch(const MCInst &Inst,
122 MCStreamer &Out,
123 const MCSubtargetInfo &STI) {
124 assert(Inst.getNumOperands() >= 1 && Inst.getOperand(0).isReg() &&
125 "expected register operand");
126 MCRegister BranchReg = Inst.getOperand(0).getReg();
127
128 // Guard the branch target through X28.
129 emitAddMask(LFIAddrReg, BranchReg, Out, STI);
130 emitBranch(Inst.getOpcode(), LFIAddrReg, Out, STI);
131}
132
133// ret xN (where xN != x30)
134// ->
135// add x28, x27, wN, uxtw
136// ret x28
137//
138// ret (x30) is safe since x30 is always within the sandbox.
139void AArch64MCLFIRewriter::rewriteReturn(const MCInst &Inst, MCStreamer &Out,
140 const MCSubtargetInfo &STI) {
141 assert(Inst.getNumOperands() >= 1 && Inst.getOperand(0).isReg() &&
142 "expected register operand");
143 // RET through LR is safe since LR is always within sandbox.
144 if (Inst.getOperand(0).getReg() != AArch64::LR)
145 rewriteIndirectBranch(Inst, Out, STI);
146 else
147 emitInst(Inst, Out, STI);
148}
149
150// modify x30
151// ->
152// modify x30
153// add x30, x27, w30, uxtw
154void AArch64MCLFIRewriter::rewriteLRModification(const MCInst &Inst,
155 MCStreamer &Out,
156 const MCSubtargetInfo &STI) {
157 emitInst(Inst, Out, STI);
158 emitAddMask(AArch64::LR, AArch64::LR, Out, STI);
159}
160
161// svc #0
162// ->
163// mov x26, x30
164// ldur x30, [x27, #-8]
165// blr x30
166// add x30, x27, w26, uxtw
167void AArch64MCLFIRewriter::rewriteSyscall(const MCInst &, MCStreamer &Out,
168 const MCSubtargetInfo &STI) {
169 // Save LR to scratch.
170 emitMov(LFIScratchReg, AArch64::LR, Out, STI);
171
172 // Load syscall handler address from negative offset from sandbox base.
173 MCInst Load;
174 Load.setOpcode(AArch64::LDURXi);
175 Load.addOperand(MCOperand::createReg(AArch64::LR));
178 emitInst(Load, Out, STI);
179
180 // Call the runtime.
181 emitBranch(AArch64::BLR, AArch64::LR, Out, STI);
182
183 // Restore LR with guard.
184 emitAddMask(AArch64::LR, LFIScratchReg, Out, STI);
185}
186
187// mrs xN, tpidr_el0
188// ->
189// ldr xN, [x25, #16]
190void AArch64MCLFIRewriter::rewriteTPRead(const MCInst &Inst, MCStreamer &Out,
191 const MCSubtargetInfo &STI) {
192 MCRegister DestReg = Inst.getOperand(0).getReg();
193
194 MCInst Load;
195 Load.setOpcode(AArch64::LDRXui);
196 Load.addOperand(MCOperand::createReg(DestReg));
199 emitInst(Load, Out, STI);
200}
201
202// msr tpidr_el0, xN
203// ->
204// str xN, [x25, #16]
205void AArch64MCLFIRewriter::rewriteTPWrite(const MCInst &Inst, MCStreamer &Out,
206 const MCSubtargetInfo &STI) {
207 MCRegister SrcReg = Inst.getOperand(1).getReg();
208
209 MCInst Store;
210 Store.setOpcode(AArch64::STRXui);
211 Store.addOperand(MCOperand::createReg(SrcReg));
214 emitInst(Store, Out, STI);
215}
216
217// NOTE: when adding new rewrites, the size estimates in
218// AArch64InstrInfo::getLFIInstSizeInBytes must be updated to match.
219void AArch64MCLFIRewriter::doRewriteInst(const MCInst &Inst, MCStreamer &Out,
220 const MCSubtargetInfo &STI) {
221 // Reserved register modification is an error.
222 if (MCRegister Reg = mayModifyReserved(Inst)) {
223 error(Inst, Twine("illegal modification of reserved LFI register ") +
224 RegInfo->getName(Reg));
225 return;
226 }
227
228 // System instructions.
229 if (isSyscall(Inst))
230 return rewriteSyscall(Inst, Out, STI);
231
232 if (isTPRead(Inst))
233 return rewriteTPRead(Inst, Out, STI);
234
235 if (isTPWrite(Inst))
236 return rewriteTPWrite(Inst, Out, STI);
237
238 if (isPrivilegedTPAccess(Inst)) {
239 error(Inst, "illegal access to privileged thread pointer register");
240 return;
241 }
242
243 // Control flow.
244 switch (Inst.getOpcode()) {
245 case AArch64::RET:
246 return rewriteReturn(Inst, Out, STI);
247 case AArch64::BR:
248 case AArch64::BLR:
249 return rewriteIndirectBranch(Inst, Out, STI);
250 }
251
252 // Link register modification.
253 if (explicitlyModifiesRegister(Inst, AArch64::LR))
254 return rewriteLRModification(Inst, Out, STI);
255
256 emitInst(Inst, Out, STI);
257}
258
260 const MCSubtargetInfo &STI) {
261 // The guard prevents rewrite-recursion when we emit instructions from inside
262 // the rewriter (such instructions should not be rewritten).
263 if (!Enabled || Guard)
264 return false;
265 Guard = true;
266
267 doRewriteInst(Inst, Out, STI);
268
269 Guard = false;
270 return true;
271}
static constexpr unsigned LFITPOffset
static constexpr MCRegister LFIScratchReg
static bool isPrivilegedTPAccess(const MCInst &Inst)
static constexpr MCRegister LFICtxReg
static bool isPrivilegedTP(int64_t Reg)
static bool isTPRead(const MCInst &Inst)
static bool isSyscall(const MCInst &Inst)
static constexpr MCRegister LFIAddrReg
static constexpr MCRegister LFIBaseReg
static constexpr int LFISyscallOffset
static bool isTPWrite(const MCInst &Inst)
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
Register Reg
#define error(X)
bool rewriteInst(const MCInst &Inst, MCStreamer &Out, const MCSubtargetInfo &STI) override
Instances of this class represent a single low-level machine instruction.
Definition MCInst.h:188
unsigned getNumOperands() const
Definition MCInst.h:212
unsigned getOpcode() const
Definition MCInst.h:202
void addOperand(const MCOperand Op)
Definition MCInst.h:215
void setOpcode(unsigned Op)
Definition MCInst.h:201
const MCOperand & getOperand(unsigned i) const
Definition MCInst.h:210
LLVM_ABI bool mayModifyRegister(const MCInst &Inst, MCRegister Reg) const
std::unique_ptr< MCRegisterInfo > RegInfo
LLVM_ABI bool explicitlyModifiesRegister(const MCInst &Inst, MCRegister Reg) const
int64_t getImm() const
Definition MCInst.h:84
static MCOperand createReg(MCRegister Reg)
Definition MCInst.h:138
static MCOperand createImm(int64_t Val)
Definition MCInst.h:145
bool isReg() const
Definition MCInst.h:65
MCRegister getReg() const
Returns the register number.
Definition MCInst.h:73
Wrapper class representing physical registers. Should be passed by value.
Definition MCRegister.h:41
Streaming machine code generation interface.
Definition MCStreamer.h:222
virtual void emitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI)
Emit the given Instruction into the current section.
Generic base class for all target subtargets.
Target - Wrapper for Target specific information.
static unsigned getArithExtendImm(AArch64_AM::ShiftExtendType ET, unsigned Imm)
getArithExtendImm - Encode the extend type and shift amount for an arithmetic instruction: imm: 3-bit...
This is an optimization pass for GlobalISel generic memory operations.
static MCRegister getWRegFromXReg(MCRegister Reg)