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