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/MCInstrDesc.h"
22#include "llvm/MC/MCInstrInfo.h"
23#include "llvm/MC/MCStreamer.h"
26
27using namespace llvm;
28
29static cl::opt<bool>
30 LFIGuardElim("aarch64-lfi-guard-elim", cl::Hidden,
31 cl::desc("Enable the LFI guard elimination optimization"),
32 cl::init(true));
33
34namespace llvm::AArch64 {
42 unsigned Inst;
43 bool IsPre;
45 unsigned BaseInst;
46};
48 unsigned Inst;
50 unsigned BaseInst;
51};
60
61// LFI addressing-mode codes (must match AArch64LFI.td's LFI_AM_* defs).
69
70#define GET_LFIVariantTable_DECL
71#define GET_PairVariantTable_DECL
72#define GET_SIMDPostTable_DECL
73#define GET_MemInfoTable_DECL
74#define GET_LFIVariantTable_IMPL
75#define GET_PairVariantTable_IMPL
76#define GET_SIMDPostTable_IMPL
77#define GET_MemInfoTable_IMPL
78// The LFI tables defined in AArch64LFI.td are emitted into this file alongside
79// the system operand tables (single -gen-searchable-tables output).
80#include "AArch64GenSystemOperands.inc"
81} // namespace llvm::AArch64
82
83// LFI reserved registers.
84static constexpr MCRegister LFIBaseReg = AArch64::X27;
85static constexpr MCRegister LFIAddrReg = AArch64::X28;
86static constexpr MCRegister LFIScratchReg = AArch64::X26;
87static constexpr MCRegister LFICtxReg = AArch64::X25;
88
89// Offset into the context register block (pointed to by LFICtxReg) where the
90// thread pointer is stored. This is a scaled offset (multiplied by 8 for
91// 64-bit loads), so a value of 2 means an actual byte offset of 16.
92static constexpr unsigned LFITPOffset = 2;
93
94// Byte offset from the sandbox base register where the syscall handler address
95// is stored (negative because it is below the sandbox base).
96static constexpr int LFISyscallOffset = -8;
97
98static bool isSyscall(const MCInst &Inst) {
99 return Inst.getOpcode() == AArch64::SVC;
100}
101
102static bool isPrivilegedTP(int64_t Reg) {
103 return Reg == AArch64SysReg::TPIDR_EL1 || Reg == AArch64SysReg::TPIDR_EL2 ||
104 Reg == AArch64SysReg::TPIDR_EL3;
105}
106
107static bool isTPRead(const MCInst &Inst) {
108 return Inst.getOpcode() == AArch64::MRS &&
109 Inst.getOperand(1).getImm() == AArch64SysReg::TPIDR_EL0;
110}
111
112static bool isTPWrite(const MCInst &Inst) {
113 return Inst.getOpcode() == AArch64::MSR &&
114 Inst.getOperand(0).getImm() == AArch64SysReg::TPIDR_EL0;
115}
116
117static bool isPrivilegedTPAccess(const MCInst &Inst) {
118 if (Inst.getOpcode() == AArch64::MRS)
119 return isPrivilegedTP(Inst.getOperand(1).getImm());
120 if (Inst.getOpcode() == AArch64::MSR)
121 return isPrivilegedTP(Inst.getOperand(0).getImm());
122 return false;
123}
124
125// Classification functions are limited to Armv8.1-A. Instructions outside of
126// this subset are not guaranteed to be rewritten and as a result may fail LFI
127// verification after compilation.
128
129// Instructions that have mayLoad/mayStore set in TableGen but don't actually
130// perform memory accesses.
131static bool isFakeMemAccess(const MCInst &Inst) {
132 switch (Inst.getOpcode()) {
133 case AArch64::CLREX:
134 case AArch64::DMB:
135 case AArch64::DSB:
136 case AArch64::ISB:
137 case AArch64::HINT:
138 // The range of sub-architectures supported by LFI do not include any load
139 // or store instructions in the HINT space.
140 return true;
141 default:
142 return false;
143 }
144}
145
146static bool mayPrefetch(const MCInst &Inst) {
147 switch (Inst.getOpcode()) {
148 case AArch64::PRFMl:
149 case AArch64::PRFMroW:
150 case AArch64::PRFMroX:
151 case AArch64::PRFMui:
152 case AArch64::PRFUMi:
153 return true;
154 default:
155 return false;
156 }
157}
158
159// User-mode DC/IC instructions that take a virtual address operand. Encoded as
160// SYSxt with op1=3, Cn=7, op2=1 where the Cm field selects the operation.
161static bool isVASysOp(const MCInst &Inst) {
162 if (Inst.getOpcode() != AArch64::SYSxt)
163 return false;
164 if (Inst.getOperand(0).getImm() != 3 || Inst.getOperand(1).getImm() != 7 ||
165 Inst.getOperand(3).getImm() != 1)
166 return false;
167 switch (Inst.getOperand(2).getImm()) {
168 case 4: // DC ZVA
169 case 5: // IC IVAU
170 case 10: // DC CVAC
171 case 11: // DC CVAU
172 case 12: // DC CVAP
173 case 13: // DC CVADP
174 case 14: // DC CIVAC
175 return true;
176 default:
177 return false;
178 }
179}
180
181static MCInst replaceRegAt(const MCInst &Inst, unsigned Idx,
182 MCRegister NewReg) {
183 MCInst New = Inst;
184 assert(New.getOperand(Idx).isReg());
185 New.getOperand(Idx).setReg(NewReg);
186 return New;
187}
188
189// AArch64 load/store opcode suffixes used throughout this file:
190// Ui: Unsigned immediate offset, scaled by access size: [Xn, #imm].
191// RoW: Register offset with 32-bit W register: [Xn, Wm, uxtw #shift].
192// RoX: Register offset with 64-bit X register: [Xn, Xm, lsl #shift].
193
194// Scalar load/store variant lookup. If Op is a scalar mem instruction with
195// addressing mode ExpectedMode, returns the RoW variant of the same family.
196// Returns INSTRUCTION_LIST_END otherwise.
197static unsigned convertVariantToRoW(unsigned Op, unsigned ExpectedMode) {
198 const AArch64::LFIVariantEntry *E = AArch64::lookupLFIVariantByOpcode(Op);
199 if (!E || E->AddrMode != ExpectedMode)
200 return AArch64::INSTRUCTION_LIST_END;
201 return E->RoWInst;
202}
203
204static unsigned convertRoXToRoW(unsigned Op, unsigned &Shift) {
205 Shift = 0;
206 const AArch64::LFIVariantEntry *E = AArch64::lookupLFIVariantByOpcode(Op);
207 if (!E || E->AddrMode != AArch64::LFI_AM_RoX)
208 return AArch64::INSTRUCTION_LIST_END;
209 Shift = E->Log2Size;
210 return E->RoWInst;
211}
212
213static bool getRoWShift(unsigned Op, unsigned &Shift) {
214 Shift = 0;
215 const AArch64::LFIVariantEntry *E = AArch64::lookupLFIVariantByOpcode(Op);
216 if (!E || E->AddrMode != AArch64::LFI_AM_RoW)
217 return false;
218 Shift = E->Log2Size;
219 return true;
220}
221
222// Pre/post-index conversion to base form. Both LDP/STP pair pre/post forms and
223// SIMD post-index forms come from generated lookup tables. The pair table sets
224// IsPre to distinguish pre-index from post-index. The SIMD table is
225// post-index-only so IsNoOffset is set to indicate the demoted base form takes
226// no immediate offset.
227static unsigned convertPrePostToBase(unsigned Op, bool &IsPre,
228 bool &IsNoOffset) {
229 IsPre = false;
230 IsNoOffset = false;
231 if (const auto *E = AArch64::lookupPairVariantByOpcode(Op)) {
232 IsPre = E->IsPre;
233 return E->BaseInst;
234 }
235 if (const auto *E = AArch64::lookupSIMDPostByOpcode(Op)) {
236 IsNoOffset = true;
237 return E->BaseInst;
238 }
239 return AArch64::INSTRUCTION_LIST_END;
240}
241
242bool AArch64MCLFIRewriter::mayModifySP(const MCInst &Inst) const {
243 return mayModifyRegister(Inst, AArch64::SP);
244}
245
246MCRegister AArch64MCLFIRewriter::mayModifyReserved(const MCInst &Inst) const {
247 for (MCRegister Reg : {LFIAddrReg, LFIBaseReg, LFICtxReg}) {
248 if (mayModifyRegister(Inst, Reg))
249 return Reg;
250 }
251 return {};
252}
253
255 // Invalidate guard state since the label is a potential branch target.
256 ActiveGuardReg = std::nullopt;
257}
258
259void AArch64MCLFIRewriter::emitInst(const MCInst &Inst, MCStreamer &Out,
260 const MCSubtargetInfo &STI) {
261 // Invalidate the active guard if this instruction modifies the guarded
262 // register, modifies x28 itself, or may affect control flow.
263 if (ActiveGuardReg) {
264 const MCInstrDesc &Desc = InstInfo->get(Inst.getOpcode());
265 if (Desc.mayAffectControlFlow(Inst, *RegInfo) ||
266 mayModifyRegister(Inst, *ActiveGuardReg) ||
267 mayModifyRegister(Inst, getWRegFromXReg(*ActiveGuardReg)) ||
269 ActiveGuardReg = std::nullopt;
270 }
271
272 Out.emitInstruction(Inst, STI);
273}
274
275void AArch64MCLFIRewriter::emitAddMask(MCRegister Dest, MCRegister Src,
276 MCStreamer &Out,
277 const MCSubtargetInfo &STI) {
278 // If x28 already holds the guarded value of Src, this guard is redundant and
279 // can be skipped.
280 if (LFIGuardElim && Dest == LFIAddrReg && ActiveGuardReg == Src)
281 return;
282
283 // add Dest, LFIBaseReg, W(Src), uxtw
284 MCInst Inst;
285 Inst.setOpcode(AArch64::ADDXrx);
289 Inst.addOperand(
291 emitInst(Inst, Out, STI);
292
293 // Record Src as the new active guard.
294 if (Dest == LFIAddrReg)
295 ActiveGuardReg = Src;
296}
297
298void AArch64MCLFIRewriter::emitBranch(unsigned Opcode, MCRegister Target,
299 MCStreamer &Out,
300 const MCSubtargetInfo &STI) {
301 MCInst Branch;
302 Branch.setOpcode(Opcode);
303 Branch.addOperand(MCOperand::createReg(Target));
304 emitInst(Branch, Out, STI);
305}
306
307void AArch64MCLFIRewriter::emitPendingTLSDescCall(MCStreamer &Out,
308 const MCSubtargetInfo &STI) {
309 if (!PendingTLSDescCall)
310 return;
311 MCInst Marker;
312 Marker.setOpcode(AArch64::TLSDESCCALL);
313 Marker.addOperand(MCOperand::createExpr(PendingTLSDescCall));
314 PendingTLSDescCall = nullptr;
315 emitInst(Marker, Out, STI);
316}
317
318void AArch64MCLFIRewriter::emitMov(MCRegister Dest, MCRegister Src,
319 MCStreamer &Out,
320 const MCSubtargetInfo &STI) {
321 // orr Dest, xzr, Src
322 MCInst Inst;
323 Inst.setOpcode(AArch64::ORRXrs);
325 Inst.addOperand(MCOperand::createReg(AArch64::XZR));
328 emitInst(Inst, Out, STI);
329}
330
331void AArch64MCLFIRewriter::emitAddImm(MCRegister Dest, MCRegister Src,
332 int64_t Imm, MCStreamer &Out,
333 const MCSubtargetInfo &STI) {
334 assert(std::abs(Imm) <= 4095);
335 MCInst Inst;
336 if (Imm >= 0) {
337 // add Dest, Src, Imm
338 Inst.setOpcode(AArch64::ADDXri);
342 Inst.addOperand(MCOperand::createImm(0)); // shift
343 } else {
344 // sub Dest, Src, -Imm
345 Inst.setOpcode(AArch64::SUBXri);
349 Inst.addOperand(MCOperand::createImm(0)); // shift
350 }
351 emitInst(Inst, Out, STI);
352}
353
354void AArch64MCLFIRewriter::emitAddReg(MCRegister Dest, MCRegister Src1,
355 MCRegister Src2, unsigned Shift,
356 MCStreamer &Out,
357 const MCSubtargetInfo &STI) {
358 // add Dest, Src1, Src2, lsl #Shift
359 MCInst Inst;
360 Inst.setOpcode(AArch64::ADDXrs);
364 Inst.addOperand(
366 emitInst(Inst, Out, STI);
367}
368
369void AArch64MCLFIRewriter::emitAddRegExtend(MCRegister Dest, MCRegister Src1,
370 MCRegister Src2,
372 unsigned Shift, MCStreamer &Out,
373 const MCSubtargetInfo &STI) {
374 // add Dest, Src1, Src2, ExtType #Shift
375 MCInst Inst;
376 if (ExtType == AArch64_AM::SXTX || ExtType == AArch64_AM::UXTX)
377 Inst.setOpcode(AArch64::ADDXrx64);
378 else
379 Inst.setOpcode(AArch64::ADDXrx);
383 Inst.addOperand(
385 emitInst(Inst, Out, STI);
386}
387
388void AArch64MCLFIRewriter::emitMemRoW(unsigned Opcode, const MCOperand &DataOp,
389 MCRegister BaseReg, MCStreamer &Out,
390 const MCSubtargetInfo &STI) {
391 // Op DataOp, [LFIBaseReg, W(BaseReg), uxtw]
392 MCInst Inst;
393 Inst.setOpcode(Opcode);
394 Inst.addOperand(DataOp);
397 Inst.addOperand(MCOperand::createImm(0)); // S bit = 0 (UXTW).
398 Inst.addOperand(MCOperand::createImm(0)); // Shift amount = 0 (unscaled).
399 emitInst(Inst, Out, STI);
400}
401
402// {br,blr} xN
403// ->
404// add x28, x27, wN, uxtw
405// {br,blr} x28
406void AArch64MCLFIRewriter::rewriteIndirectBranch(const MCInst &Inst,
407 MCStreamer &Out,
408 const MCSubtargetInfo &STI) {
409 assert(Inst.getNumOperands() >= 1 && Inst.getOperand(0).isReg() &&
410 "expected register operand");
411 MCRegister BranchReg = Inst.getOperand(0).getReg();
412
413 // Guard the branch target through X28.
414 emitAddMask(LFIAddrReg, BranchReg, Out, STI);
415
416 emitPendingTLSDescCall(Out, STI);
417
418 emitBranch(Inst.getOpcode(), LFIAddrReg, Out, STI);
419}
420
421// ret xN (where xN != x30)
422// ->
423// add x28, x27, wN, uxtw
424// ret x28
425//
426// ret (x30) is safe since x30 is always within the sandbox.
427void AArch64MCLFIRewriter::rewriteReturn(const MCInst &Inst, MCStreamer &Out,
428 const MCSubtargetInfo &STI) {
429 assert(Inst.getNumOperands() >= 1 && Inst.getOperand(0).isReg() &&
430 "expected register operand");
431 // RET through LR is safe since LR is always within sandbox.
432 if (Inst.getOperand(0).getReg() != AArch64::LR)
433 rewriteIndirectBranch(Inst, Out, STI);
434 else
435 emitInst(Inst, Out, STI);
436}
437
438// modify x30
439// ->
440// modify x30
441// add x30, x27, w30, uxtw
442void AArch64MCLFIRewriter::rewriteLRModification(const MCInst &Inst,
443 MCStreamer &Out,
444 const MCSubtargetInfo &STI) {
445 if (!isFakeMemAccess(Inst) &&
446 (mayLoad(Inst) || mayStore(Inst) || mayPrefetch(Inst)))
447 rewriteLoadStore(Inst, Out, STI);
448 else
449 emitInst(Inst, Out, STI);
450 emitAddMask(AArch64::LR, AArch64::LR, Out, STI);
451}
452
453// svc #0
454// ->
455// mov x26, x30
456// ldur x30, [x27, #-8]
457// blr x30
458// add x30, x27, w26, uxtw
459void AArch64MCLFIRewriter::rewriteSyscall(const MCInst &, MCStreamer &Out,
460 const MCSubtargetInfo &STI) {
461 // Save LR to scratch.
462 emitMov(LFIScratchReg, AArch64::LR, Out, STI);
463
464 // Load syscall handler address from negative offset from sandbox base.
465 MCInst Load;
466 Load.setOpcode(AArch64::LDURXi);
467 Load.addOperand(MCOperand::createReg(AArch64::LR));
470 emitInst(Load, Out, STI);
471
472 // Call the runtime.
473 emitBranch(AArch64::BLR, AArch64::LR, Out, STI);
474
475 // Restore LR with guard.
476 emitAddMask(AArch64::LR, LFIScratchReg, Out, STI);
477}
478
479// mrs xN, tpidr_el0
480// ->
481// ldr xN, [x25, #16]
482void AArch64MCLFIRewriter::rewriteTPRead(const MCInst &Inst, MCStreamer &Out,
483 const MCSubtargetInfo &STI) {
484 MCRegister DestReg = Inst.getOperand(0).getReg();
485
486 MCInst Load;
487 Load.setOpcode(AArch64::LDRXui);
488 Load.addOperand(MCOperand::createReg(DestReg));
491 emitInst(Load, Out, STI);
492}
493
494// msr tpidr_el0, xN
495// ->
496// str xN, [x25, #16]
497void AArch64MCLFIRewriter::rewriteTPWrite(const MCInst &Inst, MCStreamer &Out,
498 const MCSubtargetInfo &STI) {
499 MCRegister SrcReg = Inst.getOperand(1).getReg();
500
501 MCInst Store;
502 Store.setOpcode(AArch64::STRXui);
503 Store.addOperand(MCOperand::createReg(SrcReg));
506 emitInst(Store, Out, STI);
507}
508
509bool AArch64MCLFIRewriter::rewriteLoadStoreRoW(const MCInst &Inst,
510 MCStreamer &Out,
511 const MCSubtargetInfo &STI) {
512 unsigned Op = Inst.getOpcode();
513 unsigned MemOp;
514
515 // Case 1: Indexed load/store with zero immediate offset.
516 // ldr xN, [xM, #0] -> ldr xN, [x27, wM, uxtw]
517 if ((MemOp = convertVariantToRoW(Op, AArch64::LFI_AM_Ui)) !=
518 AArch64::INSTRUCTION_LIST_END) {
519 MCRegister BaseReg = Inst.getOperand(1).getReg();
520 if (BaseReg == AArch64::SP)
521 return false;
522 const MCOperand &OffsetOp = Inst.getOperand(2);
523 if (OffsetOp.isImm() && OffsetOp.getImm() == 0) {
524 emitMemRoW(MemOp, Inst.getOperand(0), BaseReg, Out, STI);
525 return true;
526 }
527 return false;
528 }
529
530 // Case 2: Pre-index load/store with writeback.
531 // ldr xN, [xM, #imm]! -> add xM, xM, #imm; ldr xN, [x27, wM, uxtw]
533 AArch64::INSTRUCTION_LIST_END) {
534 MCRegister BaseReg = Inst.getOperand(2).getReg();
535 if (BaseReg == AArch64::SP)
536 return false;
537 int64_t Imm = Inst.getOperand(3).getImm();
538 emitAddImm(BaseReg, BaseReg, Imm, Out, STI);
539 emitMemRoW(MemOp, Inst.getOperand(1), BaseReg, Out, STI);
540 return true;
541 }
542
543 // Case 3: Post-index load/store.
544 // ldr xN, [xM], #imm -> ldr xN, [x27, wM, uxtw]; add xM, xM, #imm
546 AArch64::INSTRUCTION_LIST_END) {
547 MCRegister BaseReg = Inst.getOperand(2).getReg();
548 if (BaseReg == AArch64::SP)
549 return false;
550 int64_t Imm = Inst.getOperand(3).getImm();
551 emitMemRoW(MemOp, Inst.getOperand(1), BaseReg, Out, STI);
552 emitAddImm(BaseReg, BaseReg, Imm, Out, STI);
553 return true;
554 }
555
556 // Case 4: Register-offset-X load/store.
557 // ldr xN, [xM1, xM2] -> add x26, xM1, xM2; ldr xN, [x27, w26, uxtw]
558 //
559 // In this case, even if xM1 is SP we must do a full rewrite, since an
560 // arbitrary register value is being added as the offset.
561 unsigned Shift;
562 if ((MemOp = convertRoXToRoW(Op, Shift)) != AArch64::INSTRUCTION_LIST_END) {
563 MCRegister Reg1 = Inst.getOperand(1).getReg();
564 MCRegister Reg2 = Inst.getOperand(2).getReg();
565 int64_t Extend = Inst.getOperand(3).getImm();
566 int64_t IsShift = Inst.getOperand(4).getImm();
567
568 if (!IsShift)
569 Shift = 0;
570
571 if (Extend)
572 emitAddRegExtend(LFIScratchReg, Reg1, Reg2, AArch64_AM::SXTX, Shift, Out,
573 STI);
574 else
575 emitAddReg(LFIScratchReg, Reg1, Reg2, Shift, Out, STI);
576 emitMemRoW(MemOp, Inst.getOperand(0), LFIScratchReg, Out, STI);
577 return true;
578 }
579
580 // Case 5: Register-offset-W load/store.
581 // ldr xN, [xM1, wM2, uxtw] -> add x26, xM1, wM2, uxtw;
582 // ldr xN, [x27, w26, uxtw]
583 if (getRoWShift(Op, Shift)) {
584 MCRegister Reg1 = Inst.getOperand(1).getReg();
585 MCRegister Reg2 = Inst.getOperand(2).getReg();
586 int64_t S = Inst.getOperand(3).getImm();
587 int64_t IsShift = Inst.getOperand(4).getImm();
588
589 if (!IsShift)
590 Shift = 0;
591
592 if (S)
593 emitAddRegExtend(LFIScratchReg, Reg1, Reg2, AArch64_AM::SXTW, Shift, Out,
594 STI);
595 else
596 emitAddRegExtend(LFIScratchReg, Reg1, Reg2, AArch64_AM::UXTW, Shift, Out,
597 STI);
598 emitMemRoW(Op, Inst.getOperand(0), LFIScratchReg, Out, STI);
599 return true;
600 }
601
602 return false;
603}
604
605void AArch64MCLFIRewriter::rewriteLoadStoreBase(const MCInst &Inst,
606 MCStreamer &Out,
607 const MCSubtargetInfo &STI) {
608 unsigned Opcode = Inst.getOpcode();
609 const AArch64::MemInfoEntry *Info = AArch64::lookupMemInfoByOpcode(Opcode);
610
611 if (!Info) {
612 warning(Inst, "unknown addressing mode for memory instruction in LFI");
613 return emitInst(Inst, Out, STI);
614 }
615
616 if (Info->IsLiteral)
617 return error(Inst, "PC-relative literal loads are not supported in LFI");
618
619 MCRegister BaseReg = Inst.getOperand(Info->BaseIdx).getReg();
620
621 // Stack accesses don't need address sandboxing, except when sp is modified
622 // with a non-zero register post-index operand.
623 bool BaseIsSP = BaseReg == AArch64::SP;
624 if (BaseIsSP) {
625 if (!Info->HasOffset || !Inst.getOperand(Info->OffsetIdx).isReg())
626 return emitInst(Inst, Out, STI);
627 MCRegister OffReg = Inst.getOperand(Info->OffsetIdx).getReg();
628 if (OffReg == AArch64::XZR || OffReg == AArch64::WZR)
629 return emitInst(Inst, Out, STI);
630 }
631
632 // Guard the base register, unless it is SP.
633 if (!BaseIsSP)
634 emitAddMask(LFIAddrReg, BaseReg, Out, STI);
635
636 if (!Info->IsPrePost) {
637 // Non-pre/post instruction: replace the base register operand.
638 MCInst NewInst = replaceRegAt(Inst, Info->BaseIdx, LFIAddrReg);
639 emitInst(NewInst, Out, STI);
640 return;
641 }
642
643 bool IsPre = false;
644 bool IsNoOffset = false;
645 unsigned BaseOpcode = convertPrePostToBase(Opcode, IsPre, IsNoOffset);
646
647 if (BaseOpcode == AArch64::INSTRUCTION_LIST_END)
648 return error(Inst, "unhandled pre/post-index instruction in LFI rewriter");
649
650 // Demote pre/post-index to base indexed form.
651 MCInst NewInst;
652 NewInst.setOpcode(BaseOpcode);
653 NewInst.setLoc(Inst.getLoc());
654
655 // Skip writeback operand (operand 0) and copy data operands up to base.
656 for (int I = 1; I < Info->BaseIdx; ++I)
657 NewInst.addOperand(Inst.getOperand(I));
658
659 // Add the access base register (LFIAddrReg or SP).
660 MCRegister AccessBase = BaseIsSP ? AArch64::SP : LFIAddrReg;
661 NewInst.addOperand(MCOperand::createReg(AccessBase));
662
663 // For pre-index, include the offset; for post-index, use zero.
664 if (IsPre && Info->HasOffset)
665 NewInst.addOperand(Inst.getOperand(Info->OffsetIdx));
666 else if (!IsNoOffset)
668
669 emitInst(NewInst, Out, STI);
670
671 if (!Info->HasOffset)
672 return;
673
674 // Update the base register with the offset. If the base is SP, a register
675 // offset must be sandboxed (the result is otherwise unbounded), and ADDXrs
676 // cannot take SP, so the extended-register form via the scratch register is
677 // used.
678 const MCOperand &OffsetOp = Inst.getOperand(Info->OffsetIdx);
679 if (OffsetOp.isImm()) {
680 // Pair pre/post immediates are scaled by element size; other pre/post
681 // forms (scalar, SIMD) use the raw immediate (scale = 1).
682 int64_t Scale = 1;
683 if (const auto *E = AArch64::lookupPairVariantByOpcode(Opcode))
684 Scale = E->Scale;
685 int64_t Offset = OffsetOp.getImm() * Scale;
686 emitAddImm(BaseReg, BaseReg, Offset, Out, STI);
687 } else if (OffsetOp.isReg()) {
688 // SIMD post-index uses a register offset (XZR for natural offset).
689 MCRegister OffReg = OffsetOp.getReg();
690 if (OffReg == AArch64::XZR) {
691 if (const auto *E = AArch64::lookupSIMDPostByOpcode(Opcode))
692 emitAddImm(BaseReg, BaseReg, E->NaturalOffset, Out, STI);
693 } else if (OffReg != AArch64::WZR) {
694 if (BaseIsSP) {
695 emitAddRegExtend(LFIScratchReg, AArch64::SP, OffReg, AArch64_AM::UXTX,
696 0, Out, STI);
697 emitAddMask(AArch64::SP, LFIScratchReg, Out, STI);
698 } else {
699 emitAddReg(BaseReg, BaseReg, OffReg, 0, Out, STI);
700 }
701 }
702 }
703}
704
705void AArch64MCLFIRewriter::rewriteLoadStore(const MCInst &Inst, MCStreamer &Out,
706 const MCSubtargetInfo &STI) {
707 bool IsStore = mayStore(Inst);
708 bool IsLoad = mayLoad(Inst) || mayPrefetch(Inst);
709
710 bool SkipLoads = STI.hasFeature(AArch64::FeatureNoLFILoads);
711 bool SkipStores = STI.hasFeature(AArch64::FeatureNoLFIStores);
712
713 if ((!IsLoad || SkipLoads) && (!IsStore || SkipStores))
714 return emitInst(Inst, Out, STI);
715
716 if (rewriteLoadStoreRoW(Inst, Out, STI))
717 return;
718
719 rewriteLoadStoreBase(Inst, Out, STI);
720}
721
722// modify sp
723// ->
724// modify x26
725// add sp, x27, w26, uxtw
726void AArch64MCLFIRewriter::rewriteSPModification(const MCInst &Inst,
727 MCStreamer &Out,
728 const MCSubtargetInfo &STI) {
729 // Route through rewriteLRModification or rewriteLoadStore for memory
730 // accesses. Those helpers automatically handle dangerous stack modifications
731 // that can happen via register post-index.
732 if (mayLoad(Inst) || mayStore(Inst)) {
733 if (mayModifyRegister(Inst, AArch64::LR))
734 return rewriteLRModification(Inst, Out, STI);
735 return rewriteLoadStore(Inst, Out, STI);
736 }
737
738 // No stack sandboxing if sandboxing is disabled for both loads and stores.
739 bool SkipLoads = STI.hasFeature(AArch64::FeatureNoLFILoads);
740 bool SkipStores = STI.hasFeature(AArch64::FeatureNoLFIStores);
741 if (SkipLoads && SkipStores)
742 return emitInst(Inst, Out, STI);
743
744 // Special case: mov sp, xN -> add sp, x27, wN, uxtw
745 if (Inst.getOpcode() == AArch64::ADDXri && Inst.getOperand(2).getImm() == 0 &&
746 Inst.getOperand(3).getImm() == 0)
747 return emitAddMask(AArch64::SP, Inst.getOperand(1).getReg(), Out, STI);
748
749 // Redirect SP modification destination to scratch, then sandbox.
750 MCInst ModInst = replaceRegAt(Inst, 0, LFIScratchReg);
751 emitInst(ModInst, Out, STI);
752 emitAddMask(AArch64::SP, LFIScratchReg, Out, STI);
753}
754
755// {dc,ic} <op>, xN
756// ->
757// add x28, x27, wN, uxtw
758// {dc,ic} <op>, x28
759void AArch64MCLFIRewriter::rewriteVASysOp(const MCInst &Inst, MCStreamer &Out,
760 const MCSubtargetInfo &STI) {
761 MCRegister AddrReg = Inst.getOperand(4).getReg();
762
763 emitAddMask(LFIAddrReg, AddrReg, Out, STI);
764
765 MCInst NewInst;
766 NewInst.setOpcode(AArch64::SYSxt);
767 NewInst.addOperand(Inst.getOperand(0));
768 NewInst.addOperand(Inst.getOperand(1));
769 NewInst.addOperand(Inst.getOperand(2));
770 NewInst.addOperand(Inst.getOperand(3));
772 emitInst(NewInst, Out, STI);
773}
774
775// NOTE: when adding new rewrites, the size estimates in
776// AArch64InstrInfo::getLFIInstSizeInBytes must be updated to match.
777void AArch64MCLFIRewriter::doRewriteInst(const MCInst &Inst, MCStreamer &Out,
778 const MCSubtargetInfo &STI) {
779 if (Inst.getOpcode() == AArch64::TLSDESCCALL) {
780 PendingTLSDescCall = Inst.getOperand(0).getExpr();
781 return;
782 }
783
784 // Reserved register modification is an error.
785 if (MCRegister Reg = mayModifyReserved(Inst)) {
786 error(Inst, Twine("illegal modification of reserved LFI register ") +
787 RegInfo->getName(Reg));
788 return;
789 }
790
791 // System instructions.
792 if (isSyscall(Inst))
793 return rewriteSyscall(Inst, Out, STI);
794
795 if (isTPRead(Inst))
796 return rewriteTPRead(Inst, Out, STI);
797
798 if (isTPWrite(Inst))
799 return rewriteTPWrite(Inst, Out, STI);
800
801 if (isPrivilegedTPAccess(Inst)) {
802 error(Inst, "illegal access to privileged thread pointer register");
803 return;
804 }
805
806 if (isVASysOp(Inst))
807 return rewriteVASysOp(Inst, Out, STI);
808
809 // Control flow.
810 switch (Inst.getOpcode()) {
811 case AArch64::RET:
812 return rewriteReturn(Inst, Out, STI);
813 case AArch64::BR:
814 case AArch64::BLR:
815 return rewriteIndirectBranch(Inst, Out, STI);
816 }
817
818 // Register modifications that require sandboxing.
819 if (mayModifySP(Inst))
820 return rewriteSPModification(Inst, Out, STI);
821
822 // Link register modification.
823 if (explicitlyModifiesRegister(Inst, AArch64::LR))
824 return rewriteLRModification(Inst, Out, STI);
825
826 // Memory access.
827 if (!isFakeMemAccess(Inst) &&
828 (mayLoad(Inst) || mayStore(Inst) || mayPrefetch(Inst)))
829 return rewriteLoadStore(Inst, Out, STI);
830
831 emitInst(Inst, Out, STI);
832}
833
834// This function is made available to the size estimator so that it can
835// classify Pre/Post-index instructions.
836bool llvm::isLFIPrePostMemAccess(unsigned Opcode) {
838 AArch64::INSTRUCTION_LIST_END)
839 return true;
841 AArch64::INSTRUCTION_LIST_END)
842 return true;
843 bool IsPre, IsNoOffset;
844 if (convertPrePostToBase(Opcode, IsPre, IsNoOffset) !=
845 AArch64::INSTRUCTION_LIST_END)
846 return true;
847 return false;
848}
849
851 const MCSubtargetInfo &STI) {
852 // Invalidate guard state if the rewriter was manually disabled.
853 if (!Enabled)
854 ActiveGuardReg = std::nullopt;
855
856 // This recursion guard prevents rewrite-recursion when we emit instructions
857 // from inside the rewriter (such instructions should not be rewritten).
858 if (!Enabled || Guard)
859 return false;
860 Guard = true;
861
862 doRewriteInst(Inst, Out, STI);
863
864 Guard = false;
865 return true;
866}
static unsigned convertPrePostToBase(unsigned Op, bool &IsPre, bool &IsNoOffset)
static bool isFakeMemAccess(const MCInst &Inst)
static constexpr unsigned LFITPOffset
static constexpr MCRegister LFIScratchReg
static bool isPrivilegedTPAccess(const MCInst &Inst)
static cl::opt< bool > LFIGuardElim("aarch64-lfi-guard-elim", cl::Hidden, cl::desc("Enable the LFI guard elimination optimization"), cl::init(true))
static constexpr MCRegister LFICtxReg
static bool isPrivilegedTP(int64_t Reg)
static bool getRoWShift(unsigned Op, unsigned &Shift)
static bool isVASysOp(const MCInst &Inst)
static bool isTPRead(const MCInst &Inst)
static bool mayPrefetch(const MCInst &Inst)
static bool isSyscall(const MCInst &Inst)
static MCInst replaceRegAt(const MCInst &Inst, unsigned Idx, MCRegister NewReg)
static unsigned convertVariantToRoW(unsigned Op, unsigned ExpectedMode)
static constexpr MCRegister LFIAddrReg
static unsigned convertRoXToRoW(unsigned Op, unsigned &Shift)
static constexpr MCRegister LFIBaseReg
static constexpr int LFISyscallOffset
static bool isTPWrite(const MCInst &Inst)
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
std::pair< Instruction::BinaryOps, Value * > OffsetOp
Find all possible pairs (BinOp, RHS) that BinOp V, RHS can be simplified.
#define I(x, y, z)
Definition MD5.cpp:57
Register Reg
#define error(X)
void onLabel(const MCSymbol *Symbol) override
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
SMLoc getLoc() const
Definition MCInst.h:208
void setLoc(SMLoc loc)
Definition MCInst.h:207
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
Describe properties that are true of each instruction in the target description file.
LLVM_ABI bool mayModifyRegister(const MCInst &Inst, MCRegister Reg) const
LLVM_ABI bool mayLoad(const MCInst &Inst) const
LLVM_ABI void warning(const MCInst &Inst, const Twine &Msg)
std::unique_ptr< MCRegisterInfo > RegInfo
LLVM_ABI bool mayStore(const MCInst &Inst) const
LLVM_ABI bool explicitlyModifiesRegister(const MCInst &Inst, MCRegister Reg) const
Instances of this class represent operands of the MCInst class.
Definition MCInst.h:40
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.
bool hasFeature(unsigned Feature) const
MCSymbol - Instances of this class represent a symbol name in the MC file, and MCSymbols are created ...
Definition MCSymbol.h:42
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...
static unsigned getShifterImm(AArch64_AM::ShiftExtendType ST, unsigned Imm)
getShifterImm - Encode the shift type and amount: imm: 6-bit shift amount shifter: 000 ==> lsl 001 ==...
initializer< Ty > init(const Ty &Val)
BaseReg
Stack frame base register. Bit 0 of FREInfo.Info.
Definition SFrame.h:77
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:573
Op::Description Desc
bool isLFIPrePostMemAccess(unsigned Opcode)
Returns true if Opcode is a pre- or post-indexed memory access that the LFI rewriter expands with a b...
DWARFExpression::Operation Op
static MCRegister getWRegFromXReg(MCRegister Reg)