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::emitPendingTLSDescCall(MCStreamer &Out,
105 const MCSubtargetInfo &STI) {
106 if (!PendingTLSDescCall)
107 return;
108 MCInst Marker;
109 Marker.setOpcode(AArch64::TLSDESCCALL);
110 Marker.addOperand(MCOperand::createExpr(PendingTLSDescCall));
111 PendingTLSDescCall = nullptr;
112 emitInst(Marker, Out, STI);
113}
114
115void AArch64MCLFIRewriter::emitMov(MCRegister Dest, MCRegister Src,
116 MCStreamer &Out,
117 const MCSubtargetInfo &STI) {
118 // orr Dest, xzr, Src
119 MCInst Inst;
120 Inst.setOpcode(AArch64::ORRXrs);
122 Inst.addOperand(MCOperand::createReg(AArch64::XZR));
125 emitInst(Inst, Out, STI);
126}
127
128// {br,blr} xN
129// ->
130// add x28, x27, wN, uxtw
131// {br,blr} x28
132void AArch64MCLFIRewriter::rewriteIndirectBranch(const MCInst &Inst,
133 MCStreamer &Out,
134 const MCSubtargetInfo &STI) {
135 assert(Inst.getNumOperands() >= 1 && Inst.getOperand(0).isReg() &&
136 "expected register operand");
137 MCRegister BranchReg = Inst.getOperand(0).getReg();
138
139 // Guard the branch target through X28.
140 emitAddMask(LFIAddrReg, BranchReg, Out, STI);
141
142 emitPendingTLSDescCall(Out, STI);
143
144 emitBranch(Inst.getOpcode(), LFIAddrReg, Out, STI);
145}
146
147// ret xN (where xN != x30)
148// ->
149// add x28, x27, wN, uxtw
150// ret x28
151//
152// ret (x30) is safe since x30 is always within the sandbox.
153void AArch64MCLFIRewriter::rewriteReturn(const MCInst &Inst, MCStreamer &Out,
154 const MCSubtargetInfo &STI) {
155 assert(Inst.getNumOperands() >= 1 && Inst.getOperand(0).isReg() &&
156 "expected register operand");
157 // RET through LR is safe since LR is always within sandbox.
158 if (Inst.getOperand(0).getReg() != AArch64::LR)
159 rewriteIndirectBranch(Inst, Out, STI);
160 else
161 emitInst(Inst, Out, STI);
162}
163
164// modify x30
165// ->
166// modify x30
167// add x30, x27, w30, uxtw
168void AArch64MCLFIRewriter::rewriteLRModification(const MCInst &Inst,
169 MCStreamer &Out,
170 const MCSubtargetInfo &STI) {
171 emitInst(Inst, Out, STI);
172 emitAddMask(AArch64::LR, AArch64::LR, Out, STI);
173}
174
175// svc #0
176// ->
177// mov x26, x30
178// ldur x30, [x27, #-8]
179// blr x30
180// add x30, x27, w26, uxtw
181void AArch64MCLFIRewriter::rewriteSyscall(const MCInst &, MCStreamer &Out,
182 const MCSubtargetInfo &STI) {
183 // Save LR to scratch.
184 emitMov(LFIScratchReg, AArch64::LR, Out, STI);
185
186 // Load syscall handler address from negative offset from sandbox base.
187 MCInst Load;
188 Load.setOpcode(AArch64::LDURXi);
189 Load.addOperand(MCOperand::createReg(AArch64::LR));
192 emitInst(Load, Out, STI);
193
194 // Call the runtime.
195 emitBranch(AArch64::BLR, AArch64::LR, Out, STI);
196
197 // Restore LR with guard.
198 emitAddMask(AArch64::LR, LFIScratchReg, Out, STI);
199}
200
201// mrs xN, tpidr_el0
202// ->
203// ldr xN, [x25, #16]
204void AArch64MCLFIRewriter::rewriteTPRead(const MCInst &Inst, MCStreamer &Out,
205 const MCSubtargetInfo &STI) {
206 MCRegister DestReg = Inst.getOperand(0).getReg();
207
208 MCInst Load;
209 Load.setOpcode(AArch64::LDRXui);
210 Load.addOperand(MCOperand::createReg(DestReg));
213 emitInst(Load, Out, STI);
214}
215
216// msr tpidr_el0, xN
217// ->
218// str xN, [x25, #16]
219void AArch64MCLFIRewriter::rewriteTPWrite(const MCInst &Inst, MCStreamer &Out,
220 const MCSubtargetInfo &STI) {
221 MCRegister SrcReg = Inst.getOperand(1).getReg();
222
223 MCInst Store;
224 Store.setOpcode(AArch64::STRXui);
225 Store.addOperand(MCOperand::createReg(SrcReg));
228 emitInst(Store, Out, STI);
229}
230
231// NOTE: when adding new rewrites, the size estimates in
232// AArch64InstrInfo::getLFIInstSizeInBytes must be updated to match.
233void AArch64MCLFIRewriter::doRewriteInst(const MCInst &Inst, MCStreamer &Out,
234 const MCSubtargetInfo &STI) {
235 if (Inst.getOpcode() == AArch64::TLSDESCCALL) {
236 PendingTLSDescCall = Inst.getOperand(0).getExpr();
237 return;
238 }
239
240 // Reserved register modification is an error.
241 if (MCRegister Reg = mayModifyReserved(Inst)) {
242 error(Inst, Twine("illegal modification of reserved LFI register ") +
243 RegInfo->getName(Reg));
244 return;
245 }
246
247 // System instructions.
248 if (isSyscall(Inst))
249 return rewriteSyscall(Inst, Out, STI);
250
251 if (isTPRead(Inst))
252 return rewriteTPRead(Inst, Out, STI);
253
254 if (isTPWrite(Inst))
255 return rewriteTPWrite(Inst, Out, STI);
256
257 if (isPrivilegedTPAccess(Inst)) {
258 error(Inst, "illegal access to privileged thread pointer register");
259 return;
260 }
261
262 // Control flow.
263 switch (Inst.getOpcode()) {
264 case AArch64::RET:
265 return rewriteReturn(Inst, Out, STI);
266 case AArch64::BR:
267 case AArch64::BLR:
268 return rewriteIndirectBranch(Inst, Out, STI);
269 }
270
271 // Link register modification.
272 if (explicitlyModifiesRegister(Inst, AArch64::LR))
273 return rewriteLRModification(Inst, Out, STI);
274
275 emitInst(Inst, Out, STI);
276}
277
279 const MCSubtargetInfo &STI) {
280 // The guard prevents rewrite-recursion when we emit instructions from inside
281 // the rewriter (such instructions should not be rewritten).
282 if (!Enabled || Guard)
283 return false;
284 Guard = true;
285
286 doRewriteInst(Inst, Out, STI);
287
288 Guard = false;
289 return true;
290}
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
static MCOperand createExpr(const MCExpr *Val)
Definition MCInst.h:166
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
const MCExpr * getExpr() const
Definition MCInst.h:118
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)