LLVM 24.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"
22#include "llvm/MC/MCInstrDesc.h"
23#include "llvm/MC/MCInstrInfo.h"
24#include "llvm/MC/MCStreamer.h"
27
28using namespace llvm;
29
30static cl::opt<bool>
31 LFIGuardElim("aarch64-lfi-guard-elim", cl::Hidden,
32 cl::desc("Enable the LFI guard elimination optimization"),
33 cl::init(true));
34
35namespace llvm::AArch64 {
43 unsigned Inst;
44 bool IsPre;
46 unsigned BaseInst;
47};
49 unsigned Inst;
51 unsigned BaseInst;
52};
61
62// LFI addressing-mode codes (must match AArch64LFI.td's LFI_AM_* defs).
70
71#define GET_LFIVariantTable_DECL
72#define GET_PairVariantTable_DECL
73#define GET_SIMDPostTable_DECL
74#define GET_MemInfoTable_DECL
75#define GET_LFIVariantTable_IMPL
76#define GET_PairVariantTable_IMPL
77#define GET_SIMDPostTable_IMPL
78#define GET_MemInfoTable_IMPL
79// The LFI tables defined in AArch64LFI.td are emitted into this file alongside
80// the system operand tables (single -gen-searchable-tables output).
81#include "AArch64GenSystemOperands.inc"
82} // namespace llvm::AArch64
83
84// LFI reserved registers.
85static constexpr MCRegister LFIBaseReg = AArch64::X27;
86static constexpr MCRegister LFIAddrReg = AArch64::X28;
87static constexpr MCRegister LFIScratchReg = AArch64::X26;
88static constexpr MCRegister LFICtxReg = AArch64::X25;
89
90// Offset into the context register block (pointed to by LFICtxReg) where the
91// thread pointer is stored. This is a scaled offset (multiplied by 8 for
92// 64-bit loads), so a value of 2 means an actual byte offset of 16.
93static constexpr unsigned LFITPOffset = 2;
94
95// Byte offset from the sandbox base register where the syscall handler address
96// is stored (negative because it is below the sandbox base).
97static constexpr int LFISyscallOffset = -8;
98
99static bool isSyscall(const MCInst &Inst) {
100 return Inst.getOpcode() == AArch64::SVC;
101}
102
103static bool isPrivilegedTP(int64_t Reg) {
104 return Reg == AArch64SysReg::TPIDR_EL1 || Reg == AArch64SysReg::TPIDR_EL2 ||
105 Reg == AArch64SysReg::TPIDR_EL3;
106}
107
108static bool isTPRead(const MCInst &Inst) {
109 return Inst.getOpcode() == AArch64::MRS &&
110 Inst.getOperand(1).getImm() == AArch64SysReg::TPIDR_EL0;
111}
112
113static bool isTPWrite(const MCInst &Inst) {
114 return Inst.getOpcode() == AArch64::MSR &&
115 Inst.getOperand(0).getImm() == AArch64SysReg::TPIDR_EL0;
116}
117
118static bool isPrivilegedTPAccess(const MCInst &Inst) {
119 if (Inst.getOpcode() == AArch64::MRS)
120 return isPrivilegedTP(Inst.getOperand(1).getImm());
121 if (Inst.getOpcode() == AArch64::MSR)
122 return isPrivilegedTP(Inst.getOperand(0).getImm());
123 return false;
124}
125
126// Classification functions are limited to Armv8.1-A. Instructions outside of
127// this subset are not guaranteed to be rewritten and as a result may fail LFI
128// verification after compilation.
129
130// Instructions that have mayLoad/mayStore set in TableGen but don't actually
131// perform memory accesses.
132static bool isFakeMemAccess(const MCInst &Inst) {
133 switch (Inst.getOpcode()) {
134 case AArch64::CLREX:
135 case AArch64::DMB:
136 case AArch64::DSB:
137 case AArch64::ISB:
138 case AArch64::HINT:
139 // The range of sub-architectures supported by LFI do not include any load
140 // or store instructions in the HINT space.
141 return true;
142 default:
143 return false;
144 }
145}
146
147static bool mayPrefetch(const MCInst &Inst) {
148 switch (Inst.getOpcode()) {
149 case AArch64::PRFMl:
150 case AArch64::PRFMroW:
151 case AArch64::PRFMroX:
152 case AArch64::PRFMui:
153 case AArch64::PRFUMi:
154 return true;
155 default:
156 return false;
157 }
158}
159
160static bool isAuthenticatedBranch(unsigned Opcode) {
161 switch (Opcode) {
162 case AArch64::BRAA:
163 case AArch64::BRAAZ:
164 case AArch64::BRAB:
165 case AArch64::BRABZ:
166 return true;
167 default:
168 return false;
169 }
170}
171
172static bool isAuthenticatedCall(unsigned Opcode) {
173 switch (Opcode) {
174 case AArch64::BLRAA:
175 case AArch64::BLRAAZ:
176 case AArch64::BLRAB:
177 case AArch64::BLRABZ:
178 return true;
179 default:
180 return false;
181 }
182}
183
184static bool isAuthenticatedReturn(unsigned Opcode) {
185 return Opcode == AArch64::RETAA || Opcode == AArch64::RETAB;
186}
187
188static bool isExceptionReturn(unsigned Opcode) {
189 return Opcode == AArch64::ERET || Opcode == AArch64::ERETAA ||
190 Opcode == AArch64::ERETAB;
191}
192
193static bool pacWritesLR(const MCInst &Inst) {
194 switch (Inst.getOpcode()) {
195 case AArch64::AUTIASP:
196 case AArch64::AUTIBSP:
197 case AArch64::AUTIAZ:
198 case AArch64::AUTIBZ:
199 case AArch64::XPACLRI:
200 return true;
201 default:
202 return false;
203 }
204}
205
206// User-mode DC/IC instructions that take a virtual address operand. Encoded as
207// SYSxt with op1=3, Cn=7, op2=1 where the Cm field selects the operation.
208static bool isVASysOp(const MCInst &Inst) {
209 if (Inst.getOpcode() != AArch64::SYSxt)
210 return false;
211 if (Inst.getOperand(0).getImm() != 3 || Inst.getOperand(1).getImm() != 7 ||
212 Inst.getOperand(3).getImm() != 1)
213 return false;
214 switch (Inst.getOperand(2).getImm()) {
215 case 4: // DC ZVA
216 case 5: // IC IVAU
217 case 10: // DC CVAC
218 case 11: // DC CVAU
219 case 12: // DC CVAP
220 case 13: // DC CVADP
221 case 14: // DC CIVAC
222 return true;
223 default:
224 return false;
225 }
226}
227
228static MCInst replaceRegAt(const MCInst &Inst, unsigned Idx,
229 MCRegister NewReg) {
230 MCInst New = Inst;
231 assert(New.getOperand(Idx).isReg());
232 New.getOperand(Idx).setReg(NewReg);
233 return New;
234}
235
236// AArch64 load/store opcode suffixes used throughout this file:
237// Ui: Unsigned immediate offset, scaled by access size: [Xn, #imm].
238// RoW: Register offset with 32-bit W register: [Xn, Wm, uxtw #shift].
239// RoX: Register offset with 64-bit X register: [Xn, Xm, lsl #shift].
240
241// Scalar load/store variant lookup. If Op is a scalar mem instruction with
242// addressing mode ExpectedMode, returns the RoW variant of the same family.
243// Returns INSTRUCTION_LIST_END otherwise.
244static unsigned convertVariantToRoW(unsigned Op, unsigned ExpectedMode) {
245 const AArch64::LFIVariantEntry *E = AArch64::lookupLFIVariantByOpcode(Op);
246 if (!E || E->AddrMode != ExpectedMode)
247 return AArch64::INSTRUCTION_LIST_END;
248 return E->RoWInst;
249}
250
251static unsigned convertRoXToRoW(unsigned Op, unsigned &Shift) {
252 Shift = 0;
253 const AArch64::LFIVariantEntry *E = AArch64::lookupLFIVariantByOpcode(Op);
254 if (!E || E->AddrMode != AArch64::LFI_AM_RoX)
255 return AArch64::INSTRUCTION_LIST_END;
256 Shift = E->Log2Size;
257 return E->RoWInst;
258}
259
260static bool getRoWShift(unsigned Op, unsigned &Shift) {
261 Shift = 0;
262 const AArch64::LFIVariantEntry *E = AArch64::lookupLFIVariantByOpcode(Op);
263 if (!E || E->AddrMode != AArch64::LFI_AM_RoW)
264 return false;
265 Shift = E->Log2Size;
266 return true;
267}
268
269// Pre/post-index conversion to base form. Both LDP/STP pair pre/post forms and
270// SIMD post-index forms come from generated lookup tables. The pair table sets
271// IsPre to distinguish pre-index from post-index. The SIMD table is
272// post-index-only so IsNoOffset is set to indicate the demoted base form takes
273// no immediate offset.
274static unsigned convertPrePostToBase(unsigned Op, bool &IsPre,
275 bool &IsNoOffset) {
276 IsPre = false;
277 IsNoOffset = false;
278 if (const auto *E = AArch64::lookupPairVariantByOpcode(Op)) {
279 IsPre = E->IsPre;
280 return E->BaseInst;
281 }
282 if (const auto *E = AArch64::lookupSIMDPostByOpcode(Op)) {
283 IsNoOffset = true;
284 return E->BaseInst;
285 }
286 return AArch64::INSTRUCTION_LIST_END;
287}
288
289bool AArch64MCLFIRewriter::mayModifySP(const MCInst &Inst) const {
290 return mayModifyRegister(Inst, AArch64::SP);
291}
292
293MCRegister AArch64MCLFIRewriter::mayModifyReserved(const MCInst &Inst) const {
294 for (MCRegister Reg : {LFIAddrReg, LFIBaseReg, LFICtxReg}) {
295 if (mayModifyRegister(Inst, Reg))
296 return Reg;
297 }
298 return {};
299}
300
302 if (Guard)
303 return;
304
305 // Flush a deferred LR guard before the label, since the label is a potential
306 // branch target and code reached through it may use LR for control flow.
307 if (DeferredLRGuard && LastSTI) {
308 emitAddMask(AArch64::LR, AArch64::LR, Out, *LastSTI);
309 DeferredLRGuard = false;
310 }
311
312 // Invalidate guard state since the label is a potential branch target.
313 ActiveGuardReg = std::nullopt;
314}
315
317 // Flush a deferred LR guard at the end of the stream.
318 if (DeferredLRGuard && LastSTI) {
319 emitAddMask(AArch64::LR, AArch64::LR, Out, *LastSTI);
320 DeferredLRGuard = false;
321 }
322}
323
324void AArch64MCLFIRewriter::emitInst(const MCInst &Inst, MCStreamer &Out,
325 const MCSubtargetInfo &STI) {
326 // Invalidate the active guard if this instruction modifies the guarded
327 // register, modifies x28 itself, or may affect control flow.
328 if (ActiveGuardReg) {
329 const MCInstrDesc &Desc = InstInfo->get(Inst.getOpcode());
330 if (Desc.mayAffectControlFlow(Inst, *RegInfo) ||
331 mayModifyRegister(Inst, *ActiveGuardReg) ||
332 mayModifyRegister(Inst, getWRegFromXReg(*ActiveGuardReg)) ||
334 ActiveGuardReg = std::nullopt;
335 }
336
337 Out.emitInstruction(Inst, STI);
338}
339
340void AArch64MCLFIRewriter::emitAddMask(MCRegister Dest, MCRegister Src,
341 MCStreamer &Out,
342 const MCSubtargetInfo &STI) {
343 // If x28 already holds the guarded value of Src, this guard is redundant and
344 // can be skipped.
345 if (LFIGuardElim && Dest == LFIAddrReg && ActiveGuardReg == Src)
346 return;
347
348 // add Dest, LFIBaseReg, W(Src), uxtw
349 emitInst(MCInstBuilder(AArch64::ADDXrx)
350 .addReg(Dest)
351 .addReg(LFIBaseReg)
352 .addReg(getWRegFromXReg(Src))
354 Out, STI);
355
356 // Record Src as the new active guard.
357 if (Dest == LFIAddrReg)
358 ActiveGuardReg = Src;
359}
360
361void AArch64MCLFIRewriter::emitBranch(unsigned Opcode, MCRegister Target,
362 MCStreamer &Out,
363 const MCSubtargetInfo &STI) {
364 emitInst(MCInstBuilder(Opcode).addReg(Target), Out, STI);
365}
366
367void AArch64MCLFIRewriter::emitPendingTLSDescCall(MCStreamer &Out,
368 const MCSubtargetInfo &STI) {
369 if (!PendingTLSDescCall)
370 return;
371 const MCExpr *Expr = PendingTLSDescCall;
372 PendingTLSDescCall = nullptr;
373 emitInst(MCInstBuilder(AArch64::TLSDESCCALL).addExpr(Expr), Out, STI);
374}
375
376void AArch64MCLFIRewriter::emitMov(MCRegister Dest, MCRegister Src,
377 MCStreamer &Out,
378 const MCSubtargetInfo &STI) {
379 // orr Dest, xzr, Src
380 emitInst(MCInstBuilder(AArch64::ORRXrs)
381 .addReg(Dest)
382 .addReg(AArch64::XZR)
383 .addReg(Src)
384 .addImm(0),
385 Out, STI);
386}
387
388void AArch64MCLFIRewriter::emitAddImm(MCRegister Dest, MCRegister Src,
389 int64_t Imm, MCStreamer &Out,
390 const MCSubtargetInfo &STI) {
391 assert(std::abs(Imm) <= 4095);
392 // add Dest, Src, Imm (or sub Dest, Src, -Imm for negative offsets)
393 unsigned Opcode = Imm >= 0 ? AArch64::ADDXri : AArch64::SUBXri;
394 emitInst(MCInstBuilder(Opcode)
395 .addReg(Dest)
396 .addReg(Src)
397 .addImm(std::abs(Imm))
398 .addImm(0), // shift
399 Out, STI);
400}
401
402void AArch64MCLFIRewriter::emitAddReg(MCRegister Dest, MCRegister Src1,
403 MCRegister Src2, unsigned Shift,
404 MCStreamer &Out,
405 const MCSubtargetInfo &STI) {
406 // add Dest, Src1, Src2, lsl #Shift
407 emitInst(MCInstBuilder(AArch64::ADDXrs)
408 .addReg(Dest)
409 .addReg(Src1)
410 .addReg(Src2)
412 Out, STI);
413}
414
415void AArch64MCLFIRewriter::emitAddRegExtend(MCRegister Dest, MCRegister Src1,
416 MCRegister Src2,
418 unsigned Shift, MCStreamer &Out,
419 const MCSubtargetInfo &STI) {
420 // add Dest, Src1, Src2, ExtType #Shift
421 unsigned Opcode = ExtType == AArch64_AM::SXTX || ExtType == AArch64_AM::UXTX
422 ? AArch64::ADDXrx64
423 : AArch64::ADDXrx;
424 emitInst(MCInstBuilder(Opcode).addReg(Dest).addReg(Src1).addReg(Src2).addImm(
425 AArch64_AM::getArithExtendImm(ExtType, Shift)),
426 Out, STI);
427}
428
429void AArch64MCLFIRewriter::emitMemRoW(unsigned Opcode, const MCOperand &DataOp,
430 MCRegister BaseReg, MCStreamer &Out,
431 const MCSubtargetInfo &STI) {
432 // Op DataOp, [LFIBaseReg, W(BaseReg), uxtw]
433 emitInst(MCInstBuilder(Opcode)
434 .addOperand(DataOp)
435 .addReg(LFIBaseReg)
436 .addReg(getWRegFromXReg(BaseReg))
437 .addImm(0) // S bit = 0 (UXTW).
438 .addImm(0), // Shift amount = 0 (unscaled).
439 Out, STI);
440}
441
442// {br,blr} xN
443// ->
444// add x28, x27, wN, uxtw
445// {br,blr} x28
446void AArch64MCLFIRewriter::rewriteIndirectBranch(const MCInst &Inst,
447 MCStreamer &Out,
448 const MCSubtargetInfo &STI) {
449 assert(Inst.getNumOperands() >= 1 && Inst.getOperand(0).isReg() &&
450 "expected register operand");
451 MCRegister BranchReg = Inst.getOperand(0).getReg();
452
453 // Guard the branch target through X28.
454 emitAddMask(LFIAddrReg, BranchReg, Out, STI);
455
456 emitPendingTLSDescCall(Out, STI);
457
458 emitBranch(Inst.getOpcode(), LFIAddrReg, Out, STI);
459}
460
461// ret xN (where xN != x30)
462// ->
463// add x28, x27, wN, uxtw
464// ret x28
465//
466// ret (x30) is safe since x30 is always within the sandbox.
467void AArch64MCLFIRewriter::rewriteReturn(const MCInst &Inst, MCStreamer &Out,
468 const MCSubtargetInfo &STI) {
469 assert(Inst.getNumOperands() >= 1 && Inst.getOperand(0).isReg() &&
470 "expected register operand");
471 // RET through LR is safe since LR is always within sandbox.
472 if (Inst.getOperand(0).getReg() != AArch64::LR)
473 rewriteIndirectBranch(Inst, Out, STI);
474 else
475 emitInst(Inst, Out, STI);
476}
477
478// modify x30
479// ->
480// modify x30
481// add x30, x27, w30, uxtw (deferred)
482void AArch64MCLFIRewriter::rewriteLRModification(const MCInst &Inst,
483 MCStreamer &Out,
484 const MCSubtargetInfo &STI) {
485 if (!isFakeMemAccess(Inst) &&
486 (mayLoad(Inst) || mayStore(Inst) || mayPrefetch(Inst)))
487 rewriteLoadStore(Inst, Out, STI);
488 else
489 emitInst(Inst, Out, STI);
490
491 // Defer the LR guard until the next control-flow instruction or label. This
492 // keeps a signed return address intact so that an authentication instruction
493 // can run before the mask destroys the PAC bits.
494 DeferredLRGuard = true;
495}
496
497// retaa / retab
498// ->
499// autiasp / autibsp
500// add x30, x27, w30, uxtw
501// ret
502void AArch64MCLFIRewriter::rewriteAuthenticatedReturn(
503 const MCInst &Inst, MCStreamer &Out, const MCSubtargetInfo &STI) {
504 emitInst(MCInstBuilder(Inst.getOpcode() == AArch64::RETAA ? AArch64::AUTIASP
505 : AArch64::AUTIBSP),
506 Out, STI);
507
508 emitAddMask(AArch64::LR, AArch64::LR, Out, STI);
509 emitBranch(AArch64::RET, AArch64::LR, Out, STI);
510}
511
512// {braa,brab,braaz,brabz} xN[, xM] (blra* for calls)
513// ->
514// {autia,autib,autiza,autizb} xN[, xM]
515// add x28, x27, wN, uxtw
516// {br,blr} x28
517void AArch64MCLFIRewriter::rewriteAuthenticatedBranchOrCall(
518 const MCInst &Inst, unsigned BranchOpcode, MCStreamer &Out,
519 const MCSubtargetInfo &STI) {
520 MCRegister TargetReg = Inst.getOperand(0).getReg();
521
522 // Select the authentication opcode for the target register.
523 unsigned AuthOpcode;
524 switch (Inst.getOpcode()) {
525 case AArch64::BRAA:
526 case AArch64::BLRAA:
527 AuthOpcode = AArch64::AUTIA;
528 break;
529 case AArch64::BRAB:
530 case AArch64::BLRAB:
531 AuthOpcode = AArch64::AUTIB;
532 break;
533 case AArch64::BRAAZ:
534 case AArch64::BLRAAZ:
535 AuthOpcode = AArch64::AUTIZA;
536 break;
537 case AArch64::BRABZ:
538 case AArch64::BLRABZ:
539 AuthOpcode = AArch64::AUTIZB;
540 break;
541 default:
542 llvm_unreachable("unexpected authenticated branch/call opcode");
543 }
544
545 MCInstBuilder Auth(AuthOpcode);
546 Auth.addReg(TargetReg); // dst
547 Auth.addReg(TargetReg); // src (tied to dst)
548 if (AuthOpcode == AArch64::AUTIA || AuthOpcode == AArch64::AUTIB)
549 Auth.addOperand(Inst.getOperand(1)); // modifier
550 emitInst(Auth, Out, STI);
551
552 // Guard the authenticated target and branch/call through x28.
553 emitAddMask(LFIAddrReg, TargetReg, Out, STI);
554 emitBranch(BranchOpcode, LFIAddrReg, Out, STI);
555}
556
557// svc #0
558// ->
559// mov x26, x30
560// ldur x30, [x27, #-8]
561// blr x30
562// add x30, x27, w26, uxtw
563void AArch64MCLFIRewriter::rewriteSyscall(const MCInst &, MCStreamer &Out,
564 const MCSubtargetInfo &STI) {
565 // Save LR to scratch.
566 emitMov(LFIScratchReg, AArch64::LR, Out, STI);
567
568 // Load syscall handler address from negative offset from sandbox base.
569 emitInst(MCInstBuilder(AArch64::LDURXi)
570 .addReg(AArch64::LR)
571 .addReg(LFIBaseReg)
572 .addImm(LFISyscallOffset),
573 Out, STI);
574
575 // Call the runtime.
576 emitBranch(AArch64::BLR, AArch64::LR, Out, STI);
577
578 // Restore LR with guard.
579 emitAddMask(AArch64::LR, LFIScratchReg, Out, STI);
580}
581
582// mrs xN, tpidr_el0
583// ->
584// ldr xN, [x25, #16]
585void AArch64MCLFIRewriter::rewriteTPRead(const MCInst &Inst, MCStreamer &Out,
586 const MCSubtargetInfo &STI) {
587 MCRegister DestReg = Inst.getOperand(0).getReg();
588
589 emitInst(MCInstBuilder(AArch64::LDRXui)
590 .addReg(DestReg)
591 .addReg(LFICtxReg)
592 .addImm(LFITPOffset),
593 Out, STI);
594}
595
596// msr tpidr_el0, xN
597// ->
598// str xN, [x25, #16]
599void AArch64MCLFIRewriter::rewriteTPWrite(const MCInst &Inst, MCStreamer &Out,
600 const MCSubtargetInfo &STI) {
601 MCRegister SrcReg = Inst.getOperand(1).getReg();
602
603 emitInst(MCInstBuilder(AArch64::STRXui)
604 .addReg(SrcReg)
605 .addReg(LFICtxReg)
606 .addImm(LFITPOffset),
607 Out, STI);
608}
609
610bool AArch64MCLFIRewriter::rewriteLoadStoreRoW(const MCInst &Inst,
611 MCStreamer &Out,
612 const MCSubtargetInfo &STI) {
613 unsigned Op = Inst.getOpcode();
614 unsigned MemOp;
615
616 // Case 1: Indexed load/store with zero immediate offset.
617 // ldr xN, [xM, #0] -> ldr xN, [x27, wM, uxtw]
618 if ((MemOp = convertVariantToRoW(Op, AArch64::LFI_AM_Ui)) !=
619 AArch64::INSTRUCTION_LIST_END) {
620 MCRegister BaseReg = Inst.getOperand(1).getReg();
621 if (BaseReg == AArch64::SP)
622 return false;
623 const MCOperand &OffsetOp = Inst.getOperand(2);
624 if (OffsetOp.isImm() && OffsetOp.getImm() == 0) {
625 emitMemRoW(MemOp, Inst.getOperand(0), BaseReg, Out, STI);
626 return true;
627 }
628 return false;
629 }
630
631 // Case 2: Pre-index load/store with writeback.
632 // ldr xN, [xM, #imm]! -> add xM, xM, #imm; ldr xN, [x27, wM, uxtw]
634 AArch64::INSTRUCTION_LIST_END) {
635 MCRegister BaseReg = Inst.getOperand(2).getReg();
636 if (BaseReg == AArch64::SP)
637 return false;
638 int64_t Imm = Inst.getOperand(3).getImm();
639 emitAddImm(BaseReg, BaseReg, Imm, Out, STI);
640 emitMemRoW(MemOp, Inst.getOperand(1), BaseReg, Out, STI);
641 return true;
642 }
643
644 // Case 3: Post-index load/store.
645 // ldr xN, [xM], #imm -> ldr xN, [x27, wM, uxtw]; add xM, xM, #imm
647 AArch64::INSTRUCTION_LIST_END) {
648 MCRegister BaseReg = Inst.getOperand(2).getReg();
649 if (BaseReg == AArch64::SP)
650 return false;
651 int64_t Imm = Inst.getOperand(3).getImm();
652 emitMemRoW(MemOp, Inst.getOperand(1), BaseReg, Out, STI);
653 emitAddImm(BaseReg, BaseReg, Imm, Out, STI);
654 return true;
655 }
656
657 // Case 4: Register-offset-X load/store.
658 // ldr xN, [xM1, xM2] -> add x26, xM1, xM2; ldr xN, [x27, w26, uxtw]
659 //
660 // In this case, even if xM1 is SP we must do a full rewrite, since an
661 // arbitrary register value is being added as the offset.
662 unsigned Shift;
663 if ((MemOp = convertRoXToRoW(Op, Shift)) != AArch64::INSTRUCTION_LIST_END) {
664 MCRegister Reg1 = Inst.getOperand(1).getReg();
665 MCRegister Reg2 = Inst.getOperand(2).getReg();
666 int64_t Extend = Inst.getOperand(3).getImm();
667 int64_t IsShift = Inst.getOperand(4).getImm();
668
669 if (!IsShift)
670 Shift = 0;
671
672 if (Extend)
673 emitAddRegExtend(LFIScratchReg, Reg1, Reg2, AArch64_AM::SXTX, Shift, Out,
674 STI);
675 else
676 emitAddReg(LFIScratchReg, Reg1, Reg2, Shift, Out, STI);
677 emitMemRoW(MemOp, Inst.getOperand(0), LFIScratchReg, Out, STI);
678 return true;
679 }
680
681 // Case 5: Register-offset-W load/store.
682 // ldr xN, [xM1, wM2, uxtw] -> add x26, xM1, wM2, uxtw;
683 // ldr xN, [x27, w26, uxtw]
684 if (getRoWShift(Op, Shift)) {
685 MCRegister Reg1 = Inst.getOperand(1).getReg();
686 MCRegister Reg2 = Inst.getOperand(2).getReg();
687 int64_t S = Inst.getOperand(3).getImm();
688 int64_t IsShift = Inst.getOperand(4).getImm();
689
690 if (!IsShift)
691 Shift = 0;
692
693 if (S)
694 emitAddRegExtend(LFIScratchReg, Reg1, Reg2, AArch64_AM::SXTW, Shift, Out,
695 STI);
696 else
697 emitAddRegExtend(LFIScratchReg, Reg1, Reg2, AArch64_AM::UXTW, Shift, Out,
698 STI);
699 emitMemRoW(Op, Inst.getOperand(0), LFIScratchReg, Out, STI);
700 return true;
701 }
702
703 return false;
704}
705
706void AArch64MCLFIRewriter::rewriteLoadStoreBase(const MCInst &Inst,
707 MCStreamer &Out,
708 const MCSubtargetInfo &STI) {
709 unsigned Opcode = Inst.getOpcode();
710 const AArch64::MemInfoEntry *Info = AArch64::lookupMemInfoByOpcode(Opcode);
711
712 if (!Info) {
713 warning(Inst, "unknown addressing mode for memory instruction in LFI");
714 return emitInst(Inst, Out, STI);
715 }
716
717 if (Info->IsLiteral)
718 return error(Inst, "PC-relative literal loads are not supported in LFI");
719
720 MCRegister BaseReg = Inst.getOperand(Info->BaseIdx).getReg();
721
722 // Stack accesses don't need address sandboxing, except when sp is modified
723 // with a non-zero register post-index operand.
724 bool BaseIsSP = BaseReg == AArch64::SP;
725 if (BaseIsSP) {
726 if (!Info->HasOffset || !Inst.getOperand(Info->OffsetIdx).isReg())
727 return emitInst(Inst, Out, STI);
728 MCRegister OffReg = Inst.getOperand(Info->OffsetIdx).getReg();
729 if (OffReg == AArch64::XZR || OffReg == AArch64::WZR)
730 return emitInst(Inst, Out, STI);
731 }
732
733 // Guard the base register, unless it is SP.
734 if (!BaseIsSP)
735 emitAddMask(LFIAddrReg, BaseReg, Out, STI);
736
737 if (!Info->IsPrePost) {
738 // Non-pre/post instruction: replace the base register operand.
739 MCInst NewInst = replaceRegAt(Inst, Info->BaseIdx, LFIAddrReg);
740 emitInst(NewInst, Out, STI);
741 return;
742 }
743
744 bool IsPre = false;
745 bool IsNoOffset = false;
746 unsigned BaseOpcode = convertPrePostToBase(Opcode, IsPre, IsNoOffset);
747
748 if (BaseOpcode == AArch64::INSTRUCTION_LIST_END)
749 return error(Inst, "unhandled pre/post-index instruction in LFI rewriter");
750
751 // Demote pre/post-index to base indexed form.
752 MCInstBuilder NewInst(BaseOpcode);
753 NewInst.setLoc(Inst.getLoc());
754
755 // Skip writeback operand (operand 0) and copy data operands up to base.
756 for (int I = 1; I < Info->BaseIdx; ++I)
757 NewInst.addOperand(Inst.getOperand(I));
758
759 // Add the access base register (LFIAddrReg or SP).
760 NewInst.addReg(BaseIsSP ? AArch64::SP : LFIAddrReg);
761
762 // For pre-index, include the offset; for post-index, use zero.
763 if (IsPre && Info->HasOffset)
764 NewInst.addOperand(Inst.getOperand(Info->OffsetIdx));
765 else if (!IsNoOffset)
766 NewInst.addImm(0);
767
768 emitInst(NewInst, Out, STI);
769
770 if (!Info->HasOffset)
771 return;
772
773 // Update the base register with the offset. If the base is SP, a register
774 // offset must be sandboxed (the result is otherwise unbounded), and ADDXrs
775 // cannot take SP, so the extended-register form via the scratch register is
776 // used.
777 const MCOperand &OffsetOp = Inst.getOperand(Info->OffsetIdx);
778 if (OffsetOp.isImm()) {
779 // Pair pre/post immediates are scaled by element size; other pre/post
780 // forms (scalar, SIMD) use the raw immediate (scale = 1).
781 int64_t Scale = 1;
782 if (const auto *E = AArch64::lookupPairVariantByOpcode(Opcode))
783 Scale = E->Scale;
784 int64_t Offset = OffsetOp.getImm() * Scale;
785 emitAddImm(BaseReg, BaseReg, Offset, Out, STI);
786 } else if (OffsetOp.isReg()) {
787 // SIMD post-index uses a register offset (XZR for natural offset).
788 MCRegister OffReg = OffsetOp.getReg();
789 if (OffReg == AArch64::XZR) {
790 if (const auto *E = AArch64::lookupSIMDPostByOpcode(Opcode))
791 emitAddImm(BaseReg, BaseReg, E->NaturalOffset, Out, STI);
792 } else if (OffReg != AArch64::WZR) {
793 if (BaseIsSP) {
794 emitAddRegExtend(LFIScratchReg, AArch64::SP, OffReg, AArch64_AM::UXTX,
795 0, Out, STI);
796 emitAddMask(AArch64::SP, LFIScratchReg, Out, STI);
797 } else {
798 emitAddReg(BaseReg, BaseReg, OffReg, 0, Out, STI);
799 }
800 }
801 }
802}
803
804void AArch64MCLFIRewriter::rewriteLoadStore(const MCInst &Inst, MCStreamer &Out,
805 const MCSubtargetInfo &STI) {
806 bool IsStore = mayStore(Inst);
807 bool IsLoad = mayLoad(Inst) || mayPrefetch(Inst);
808
809 bool SkipLoads = STI.hasFeature(AArch64::FeatureNoLFILoads);
810 bool SkipStores = STI.hasFeature(AArch64::FeatureNoLFIStores);
811
812 if ((!IsLoad || SkipLoads) && (!IsStore || SkipStores))
813 return emitInst(Inst, Out, STI);
814
815 if (rewriteLoadStoreRoW(Inst, Out, STI))
816 return;
817
818 rewriteLoadStoreBase(Inst, Out, STI);
819}
820
821// modify sp
822// ->
823// modify x26
824// add sp, x27, w26, uxtw
825void AArch64MCLFIRewriter::rewriteSPModification(const MCInst &Inst,
826 MCStreamer &Out,
827 const MCSubtargetInfo &STI) {
828 // Route through rewriteLRModification or rewriteLoadStore for memory
829 // accesses. Those helpers automatically handle dangerous stack modifications
830 // that can happen via register post-index.
831 if (mayLoad(Inst) || mayStore(Inst)) {
832 if (mayModifyRegister(Inst, AArch64::LR))
833 return rewriteLRModification(Inst, Out, STI);
834 return rewriteLoadStore(Inst, Out, STI);
835 }
836
837 // No stack sandboxing if sandboxing is disabled for both loads and stores.
838 bool SkipLoads = STI.hasFeature(AArch64::FeatureNoLFILoads);
839 bool SkipStores = STI.hasFeature(AArch64::FeatureNoLFIStores);
840 if (SkipLoads && SkipStores)
841 return emitInst(Inst, Out, STI);
842
843 // Special case: mov sp, xN -> add sp, x27, wN, uxtw
844 if (Inst.getOpcode() == AArch64::ADDXri && Inst.getOperand(2).getImm() == 0 &&
845 Inst.getOperand(3).getImm() == 0)
846 return emitAddMask(AArch64::SP, Inst.getOperand(1).getReg(), Out, STI);
847
848 // Redirect SP modification destination to scratch, then sandbox.
849 MCInst ModInst = replaceRegAt(Inst, 0, LFIScratchReg);
850 emitInst(ModInst, Out, STI);
851 emitAddMask(AArch64::SP, LFIScratchReg, Out, STI);
852}
853
854// {dc,ic} <op>, xN
855// ->
856// add x28, x27, wN, uxtw
857// {dc,ic} <op>, x28
858void AArch64MCLFIRewriter::rewriteVASysOp(const MCInst &Inst, MCStreamer &Out,
859 const MCSubtargetInfo &STI) {
860 MCRegister AddrReg = Inst.getOperand(4).getReg();
861
862 emitAddMask(LFIAddrReg, AddrReg, Out, STI);
863
864 emitInst(MCInstBuilder(AArch64::SYSxt)
865 .addOperand(Inst.getOperand(0))
866 .addOperand(Inst.getOperand(1))
867 .addOperand(Inst.getOperand(2))
868 .addOperand(Inst.getOperand(3))
869 .addReg(LFIAddrReg),
870 Out, STI);
871}
872
873// NOTE: when adding new rewrites, the size estimates in
874// AArch64InstrInfo::getLFIInstSizeInBytes must be updated to match.
875void AArch64MCLFIRewriter::doRewriteInst(const MCInst &Inst, MCStreamer &Out,
876 const MCSubtargetInfo &STI) {
877 if (Inst.getOpcode() == AArch64::TLSDESCCALL) {
878 PendingTLSDescCall = Inst.getOperand(0).getExpr();
879 return;
880 }
881
882 // Reserved register modification is an error.
883 if (MCRegister Reg = mayModifyReserved(Inst)) {
884 error(Inst, Twine("illegal modification of reserved LFI register ") +
885 RegInfo->getName(Reg));
886 return;
887 }
888
889 // System instructions.
890 if (isSyscall(Inst))
891 return rewriteSyscall(Inst, Out, STI);
892
893 if (isTPRead(Inst))
894 return rewriteTPRead(Inst, Out, STI);
895
896 if (isTPWrite(Inst))
897 return rewriteTPWrite(Inst, Out, STI);
898
899 if (isPrivilegedTPAccess(Inst)) {
900 error(Inst, "illegal access to privileged thread pointer register");
901 return;
902 }
903
904 if (isVASysOp(Inst))
905 return rewriteVASysOp(Inst, Out, STI);
906
907 if (isExceptionReturn(Inst.getOpcode())) {
908 error(Inst, "exception returns are not supported by LFI");
909 return;
910 }
911
912 // PAC authenticated returns expand to authenticate + guarded RET. The
913 // expansion emits its own LR guard, so discard any deferred guard: masking
914 // before the authentication would corrupt the signed return address.
915 if (isAuthenticatedReturn(Inst.getOpcode())) {
916 DeferredLRGuard = false;
917 return rewriteAuthenticatedReturn(Inst, Out, STI);
918 }
919
920 // Flush a deferred LR guard before any control-flow instruction, so that a
921 // modified LR is sandboxed before it can be used to transfer control.
922 if (DeferredLRGuard && (isReturn(Inst) || isIndirectBranch(Inst) ||
923 isCall(Inst) || isBranch(Inst))) {
924 emitAddMask(AArch64::LR, AArch64::LR, Out, STI);
925 DeferredLRGuard = false;
926 }
927
928 // PAC authenticated branches/calls expand to authenticate + guarded branch.
930 return rewriteAuthenticatedBranchOrCall(Inst, AArch64::BR, Out, STI);
931 if (isAuthenticatedCall(Inst.getOpcode()))
932 return rewriteAuthenticatedBranchOrCall(Inst, AArch64::BLR, Out, STI);
933
934 // Control flow.
935 switch (Inst.getOpcode()) {
936 case AArch64::RET:
937 return rewriteReturn(Inst, Out, STI);
938 case AArch64::BR:
939 case AArch64::BLR:
940 return rewriteIndirectBranch(Inst, Out, STI);
941 }
942
943 // Register modifications that require sandboxing.
944 if (mayModifySP(Inst))
945 return rewriteSPModification(Inst, Out, STI);
946
947 // Link register modification. This covers explicit writes to x30 as well as
948 // PAC instructions that write LR in place, which define LR implicitly.
949 if (explicitlyModifiesRegister(Inst, AArch64::LR) || pacWritesLR(Inst))
950 return rewriteLRModification(Inst, Out, STI);
951
952 // Memory access.
953 if (!isFakeMemAccess(Inst) &&
954 (mayLoad(Inst) || mayStore(Inst) || mayPrefetch(Inst)))
955 return rewriteLoadStore(Inst, Out, STI);
956
957 emitInst(Inst, Out, STI);
958}
959
960// This function is made available to the size estimator so that it can
961// classify Pre/Post-index instructions.
962bool llvm::isLFIPrePostMemAccess(unsigned Opcode) {
964 AArch64::INSTRUCTION_LIST_END)
965 return true;
967 AArch64::INSTRUCTION_LIST_END)
968 return true;
969 bool IsPre, IsNoOffset;
970 if (convertPrePostToBase(Opcode, IsPre, IsNoOffset) !=
971 AArch64::INSTRUCTION_LIST_END)
972 return true;
973 return false;
974}
975
977 const MCSubtargetInfo &STI) {
978 // Invalidate guard state if the rewriter was manually disabled.
979 if (!Enabled)
980 ActiveGuardReg = std::nullopt;
981
982 // This recursion guard prevents rewrite-recursion when we emit instructions
983 // from inside the rewriter (such instructions should not be rewritten).
984 if (!Enabled || Guard)
985 return false;
986 Guard = true;
987
988 // Record the subtarget so a deferred LR guard can be emitted from
989 // onLabel/finish, which are not given an MCSubtargetInfo.
990 LastSTI = &STI;
991
992 doRewriteInst(Inst, Out, STI);
993
994 Guard = false;
995 return true;
996}
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 pacWritesLR(const MCInst &Inst)
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 bool isAuthenticatedBranch(unsigned Opcode)
static constexpr MCRegister LFICtxReg
static bool isPrivilegedTP(int64_t Reg)
static bool isAuthenticatedReturn(unsigned Opcode)
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 bool isExceptionReturn(unsigned Opcode)
static constexpr MCRegister LFIBaseReg
static constexpr int LFISyscallOffset
static bool isTPWrite(const MCInst &Inst)
static bool isAuthenticatedCall(unsigned Opcode)
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned Imm
static MCDisassembler::DecodeStatus addOperand(MCInst &Inst, const MCOperand &Opnd)
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, MCStreamer &Out) override
bool rewriteInst(const MCInst &Inst, MCStreamer &Out, const MCSubtargetInfo &STI) override
void finish(MCStreamer &Out) 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
unsigned getOpcode() const
Definition MCInst.h:202
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 bool isIndirectBranch(const MCInst &Inst) const
LLVM_ABI void warning(const MCInst &Inst, const Twine &Msg)
LLVM_ABI bool isCall(const MCInst &Inst) const
LLVM_ABI bool isReturn(const MCInst &Inst) const
std::unique_ptr< MCRegisterInfo > RegInfo
LLVM_ABI bool mayStore(const MCInst &Inst) const
LLVM_ABI bool explicitlyModifiesRegister(const MCInst &Inst, MCRegister Reg) const
LLVM_ABI bool isBranch(const MCInst &Inst) const
Instances of this class represent operands of the MCInst class.
Definition MCInst.h:40
int64_t getImm() const
Definition MCInst.h:84
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.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
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:577
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)