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/MC/MCInst.h"
20#include "llvm/MC/MCStreamer.h"
22
23using namespace llvm;
24
25// LFI reserved registers.
26static constexpr MCRegister LFIBaseReg = AArch64::X27;
27static constexpr MCRegister LFIAddrReg = AArch64::X28;
28static constexpr MCRegister LFIScratchReg = AArch64::X26;
29static constexpr MCRegister LFICtxReg = AArch64::X25;
30
31// Offset into the context register block (pointed to by LFICtxReg) where the
32// thread pointer is stored. This is a scaled offset (multiplied by 8 for
33// 64-bit loads), so a value of 2 means an actual byte offset of 16.
34static constexpr unsigned LFITPOffset = 2;
35
36// Byte offset from the sandbox base register where the syscall handler address
37// is stored (negative because it is below the sandbox base).
38static constexpr int LFISyscallOffset = -8;
39
40static bool isSyscall(const MCInst &Inst) {
41 return Inst.getOpcode() == AArch64::SVC;
42}
43
44static bool isPrivilegedTP(int64_t Reg) {
45 return Reg == AArch64SysReg::TPIDR_EL1 || Reg == AArch64SysReg::TPIDR_EL2 ||
46 Reg == AArch64SysReg::TPIDR_EL3;
47}
48
49static bool isTPRead(const MCInst &Inst) {
50 return Inst.getOpcode() == AArch64::MRS &&
51 Inst.getOperand(1).getImm() == AArch64SysReg::TPIDR_EL0;
52}
53
54static bool isTPWrite(const MCInst &Inst) {
55 return Inst.getOpcode() == AArch64::MSR &&
56 Inst.getOperand(0).getImm() == AArch64SysReg::TPIDR_EL0;
57}
58
59static bool isPrivilegedTPAccess(const MCInst &Inst) {
60 if (Inst.getOpcode() == AArch64::MRS)
61 return isPrivilegedTP(Inst.getOperand(1).getImm());
62 if (Inst.getOpcode() == AArch64::MSR)
63 return isPrivilegedTP(Inst.getOperand(0).getImm());
64 return false;
65}
66
67bool AArch64MCLFIRewriter::mayModifyReserved(const MCInst &Inst) const {
68 return mayModifyRegister(Inst, LFIAddrReg) ||
71}
72
73void AArch64MCLFIRewriter::emitInst(const MCInst &Inst, MCStreamer &Out,
74 const MCSubtargetInfo &STI) {
75 Out.emitInstruction(Inst, STI);
76}
77
78void AArch64MCLFIRewriter::emitAddMask(MCRegister Dest, MCRegister Src,
79 MCStreamer &Out,
80 const MCSubtargetInfo &STI) {
81 // add Dest, LFIBaseReg, W(Src), uxtw
82 MCInst Inst;
83 Inst.setOpcode(AArch64::ADDXrx);
87 Inst.addOperand(
89 emitInst(Inst, Out, STI);
90}
91
92void AArch64MCLFIRewriter::emitBranch(unsigned Opcode, MCRegister Target,
93 MCStreamer &Out,
94 const MCSubtargetInfo &STI) {
95 MCInst Branch;
96 Branch.setOpcode(Opcode);
97 Branch.addOperand(MCOperand::createReg(Target));
98 emitInst(Branch, Out, STI);
99}
100
101void AArch64MCLFIRewriter::emitMov(MCRegister Dest, MCRegister Src,
102 MCStreamer &Out,
103 const MCSubtargetInfo &STI) {
104 // orr Dest, xzr, Src
105 MCInst Inst;
106 Inst.setOpcode(AArch64::ORRXrs);
108 Inst.addOperand(MCOperand::createReg(AArch64::XZR));
111 emitInst(Inst, Out, STI);
112}
113
114// {br,blr} xN
115// ->
116// add x28, x27, wN, uxtw
117// {br,blr} x28
118void AArch64MCLFIRewriter::rewriteIndirectBranch(const MCInst &Inst,
119 MCStreamer &Out,
120 const MCSubtargetInfo &STI) {
121 assert(Inst.getNumOperands() >= 1 && Inst.getOperand(0).isReg() &&
122 "expected register operand");
123 MCRegister BranchReg = Inst.getOperand(0).getReg();
124
125 // Guard the branch target through X28.
126 emitAddMask(LFIAddrReg, BranchReg, Out, STI);
127 emitBranch(Inst.getOpcode(), LFIAddrReg, Out, STI);
128}
129
130// ret xN (where xN != x30)
131// ->
132// add x28, x27, wN, uxtw
133// ret x28
134//
135// ret (x30) is safe since x30 is always within the sandbox.
136void AArch64MCLFIRewriter::rewriteReturn(const MCInst &Inst, MCStreamer &Out,
137 const MCSubtargetInfo &STI) {
138 assert(Inst.getNumOperands() >= 1 && Inst.getOperand(0).isReg() &&
139 "expected register operand");
140 // RET through LR is safe since LR is always within sandbox.
141 if (Inst.getOperand(0).getReg() != AArch64::LR)
142 rewriteIndirectBranch(Inst, Out, STI);
143 else
144 emitInst(Inst, Out, STI);
145}
146
147// modify x30
148// ->
149// modify x30
150// add x30, x27, w30, uxtw
151void AArch64MCLFIRewriter::rewriteLRModification(const MCInst &Inst,
152 MCStreamer &Out,
153 const MCSubtargetInfo &STI) {
154 emitInst(Inst, Out, STI);
155 emitAddMask(AArch64::LR, AArch64::LR, Out, STI);
156}
157
158// svc #0
159// ->
160// mov x26, x30
161// ldur x30, [x27, #-8]
162// blr x30
163// add x30, x27, w26, uxtw
164void AArch64MCLFIRewriter::rewriteSyscall(const MCInst &, MCStreamer &Out,
165 const MCSubtargetInfo &STI) {
166 // Save LR to scratch.
167 emitMov(LFIScratchReg, AArch64::LR, Out, STI);
168
169 // Load syscall handler address from negative offset from sandbox base.
170 MCInst Load;
171 Load.setOpcode(AArch64::LDURXi);
172 Load.addOperand(MCOperand::createReg(AArch64::LR));
175 emitInst(Load, Out, STI);
176
177 // Call the runtime.
178 emitBranch(AArch64::BLR, AArch64::LR, Out, STI);
179
180 // Restore LR with guard.
181 emitAddMask(AArch64::LR, LFIScratchReg, Out, STI);
182}
183
184// mrs xN, tpidr_el0
185// ->
186// ldr xN, [x25, #16]
187void AArch64MCLFIRewriter::rewriteTPRead(const MCInst &Inst, MCStreamer &Out,
188 const MCSubtargetInfo &STI) {
189 MCRegister DestReg = Inst.getOperand(0).getReg();
190
191 MCInst Load;
192 Load.setOpcode(AArch64::LDRXui);
193 Load.addOperand(MCOperand::createReg(DestReg));
196 emitInst(Load, Out, STI);
197}
198
199// msr tpidr_el0, xN
200// ->
201// str xN, [x25, #16]
202void AArch64MCLFIRewriter::rewriteTPWrite(const MCInst &Inst, MCStreamer &Out,
203 const MCSubtargetInfo &STI) {
204 MCRegister SrcReg = Inst.getOperand(1).getReg();
205
206 MCInst Store;
207 Store.setOpcode(AArch64::STRXui);
208 Store.addOperand(MCOperand::createReg(SrcReg));
211 emitInst(Store, Out, STI);
212}
213
214// NOTE: when adding new rewrites, the size estimates in
215// AArch64InstrInfo::getLFIInstSizeInBytes must be updated to match.
216void AArch64MCLFIRewriter::doRewriteInst(const MCInst &Inst, MCStreamer &Out,
217 const MCSubtargetInfo &STI) {
218 // Reserved register modification is an error.
219 if (mayModifyReserved(Inst)) {
220 error(Inst, "illegal modification of reserved LFI register");
221 return;
222 }
223
224 // System instructions.
225 if (isSyscall(Inst))
226 return rewriteSyscall(Inst, Out, STI);
227
228 if (isTPRead(Inst))
229 return rewriteTPRead(Inst, Out, STI);
230
231 if (isTPWrite(Inst))
232 return rewriteTPWrite(Inst, Out, STI);
233
234 if (isPrivilegedTPAccess(Inst)) {
235 error(Inst, "illegal access to privileged thread pointer register");
236 return;
237 }
238
239 // Control flow.
240 switch (Inst.getOpcode()) {
241 case AArch64::RET:
242 return rewriteReturn(Inst, Out, STI);
243 case AArch64::BR:
244 case AArch64::BLR:
245 return rewriteIndirectBranch(Inst, Out, STI);
246 }
247
248 // Link register modification.
249 if (explicitlyModifiesRegister(Inst, AArch64::LR))
250 return rewriteLRModification(Inst, Out, STI);
251
252 emitInst(Inst, Out, STI);
253}
254
256 const MCSubtargetInfo &STI) {
257 // The guard prevents rewrite-recursion when we emit instructions from inside
258 // the rewriter (such instructions should not be rewritten).
259 if (!Enabled || Guard)
260 return false;
261 Guard = true;
262
263 doRewriteInst(Inst, Out, STI);
264
265 Guard = false;
266 return true;
267}
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
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)