LLVM 24.0.0git
X86MCLFIRewriter.cpp
Go to the documentation of this file.
1//===- X86MCLFIRewriter.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 X86MCLFIRewriter class, which rewrites X86-64
10// instructions for LFI (Lightweight Fault Isolation) sandboxing.
11//
12//===----------------------------------------------------------------------===//
13
14#include "X86MCLFIRewriter.h"
15#include "X86BaseInfo.h"
16#include "X86MCTargetDesc.h"
17#include "llvm/MC/MCContext.h"
18#include "llvm/MC/MCExpr.h"
19#include "llvm/MC/MCInst.h"
21#include "llvm/MC/MCStreamer.h"
23#include <algorithm>
24#include <optional>
25
26using namespace llvm;
27
28// LFI reserved registers.
29static constexpr MCRegister LFIBaseReg = X86::R14;
30static constexpr MCRegister LFIScratchReg = X86::R11;
31static constexpr MCRegister LFITPReg = X86::R15;
32
33// Segment register whose base always holds the sandbox base address.
34static constexpr MCRegister LFIBaseSeg = X86::GS;
35
36// Byte offset into the context register file (pointed to by R15) where the
37// thread pointer is stored.
38static constexpr int TPOffset = 16;
39
40static bool isPrefix(const MCInst &Inst, const MCInstrInfo &InstInfo) {
41 return X86II::isPrefix(InstInfo.get(Inst.getOpcode()).TSFlags);
42}
43
44// Return true if Inst is a supported stand-alone prefix.
45static bool isSupportedPrefix(const MCInst &Inst) {
46 switch (Inst.getOpcode()) {
47 case X86::FS_PREFIX:
48 case X86::GS_PREFIX:
49 case X86::ADDR16_PREFIX:
50 case X86::ADDR32_PREFIX:
51 return false;
52 default:
53 return true;
54 }
55}
56
57static bool isSyscall(const MCInst &Inst) {
58 return Inst.getOpcode() == X86::SYSCALL;
59}
60
61static bool isDirectCall(const MCInst &Inst) {
62 return Inst.getOpcode() == X86::CALL64pcrel32;
63}
64
65static bool isSupportedIndirectBranch(const MCInst &Inst) {
66 switch (Inst.getOpcode()) {
67 case X86::JMP64r:
68 case X86::JMP64r_NT:
69 case X86::JMP64m:
70 case X86::JMP64m_NT:
71 case X86::CALL64r:
72 case X86::CALL64r_NT:
73 case X86::CALL64m:
74 case X86::CALL64m_NT:
75 return true;
76 default:
77 return false;
78 }
79}
80
81static bool isWriteSegmentBase(unsigned Opcode) {
82 switch (Opcode) {
83 case X86::WRFSBASE:
84 case X86::WRFSBASE64:
85 case X86::WRGSBASE:
86 case X86::WRGSBASE64:
87 return true;
88 default:
89 return false;
90 }
91}
92
93static bool hasNoTrackPrefix(const MCInst &Inst, const MCInstrInfo &InstInfo) {
94 return (InstInfo.get(Inst.getOpcode()).TSFlags & X86II::NOTRACK) ||
96}
97
98// Find the index of the memory operand if it has an %fs segment override.
99// Returns -1 if there is no memory operand or no %fs override.
100static int findFSMemOperand(const MCInst &Inst, const MCInstrInfo &InstInfo) {
101 int MemIdx = X86II::getMemoryOperandIdx(InstInfo.get(Inst.getOpcode()));
102 if (MemIdx < 0)
103 return -1;
104 const MCOperand &Seg = Inst.getOperand(MemIdx + X86::AddrSegmentReg);
105 if (Seg.isReg() && Seg.getReg() == X86::FS)
106 return MemIdx;
107 return -1;
108}
109
110// Return true if the instruction reads from Reg.
111static bool readsRegister(const MCInst &Inst, const MCInstrDesc &Desc,
112 MCRegister Reg, const MCRegisterInfo &RI) {
113 for (unsigned I = Desc.getNumDefs(), E = Inst.getNumOperands(); I < E; ++I) {
114 const MCOperand &Op = Inst.getOperand(I);
115 if (Op.isReg() && Op.getReg() && RI.regsOverlap(Op.getReg(), Reg))
116 return true;
117 }
118 for (MCPhysReg Use : Desc.implicit_uses())
119 if (RI.regsOverlap(Use, Reg))
120 return true;
121 return false;
122}
123
124// Return true if Reg is absent or a 64-bit general-purpose register.
126 return Reg == X86::NoRegister ||
127 getX86MCRegisterClass(X86::GR64RegClassID).contains(Reg);
128}
129
130MCRegister X86::X86MCLFIRewriter::getReg32(MCRegister Reg) const {
131 if (getX86MCRegisterClass(X86::GR32RegClassID).contains(Reg))
132 return Reg;
133 return RegInfo->getSubReg(Reg, X86::sub_32bit);
134}
135
136void X86::X86MCLFIRewriter::emitWithPrefixes(const MCInst &Inst,
137 MCStreamer &Out,
138 const MCSubtargetInfo &STI) {
139 for (const MCInst &Prefix : Prefixes)
140 Out.emitInstruction(Prefix, STI);
141 Prefixes.clear();
142 Out.emitInstruction(Inst, STI);
143}
144
145void X86::X86MCLFIRewriter::discardPrefixes() {
146 for (const MCInst &Prefix : Prefixes)
147 error(Prefix, "unsupported instruction prefix");
148 Prefixes.clear();
149}
150
151// syscall
152// ->
153// .bundle_lock
154// leaq .Ltmp(%rip), %r11
155// jmpq *(%r14)
156// .Ltmp:
157// .bundle_unlock
158void X86::X86MCLFIRewriter::rewriteSyscall(const MCInst &Inst, MCStreamer &Out,
159 const MCSubtargetInfo &STI) {
160 Out.emitBundleLock(/*AlignToEnd=*/false, STI);
161
162 MCSymbol *Symbol = Out.getContext().createTempSymbol();
163
164 // leaq .Ltmp(%rip), %r11
165 Out.emitInstruction(
166 MCInstBuilder(X86::LEA64r)
167 .addReg(LFIScratchReg)
168 .addReg(X86::RIP)
169 .addImm(1)
170 .addReg(X86::NoRegister)
171 .addExpr(MCSymbolRefExpr::create(Symbol, Out.getContext()))
172 .addReg(X86::NoRegister),
173 STI);
174
175 // jmpq *-8(%r14)
176 Out.emitInstruction(MCInstBuilder(X86::JMP64m)
177 .addReg(LFIBaseReg)
178 .addImm(1)
179 .addReg(X86::NoRegister)
180 .addImm(-8)
181 .addReg(X86::NoRegister),
182 STI);
183
184 Out.emitLabel(Symbol);
185 Out.emitBundleUnlock(STI);
186}
187
188// andl $-LFIBundleSize, %eX
189// addq %r14, %rX
190void X86::X86MCLFIRewriter::emitSandboxBranchReg(MCRegister Reg,
191 MCStreamer &Out,
192 const MCSubtargetInfo &STI) {
193 MCRegister Reg32 = RegInfo->getSubReg(Reg, X86::sub_32bit);
194
195 Out.emitInstruction(MCInstBuilder(X86::AND32ri8)
196 .addReg(Reg32)
197 .addReg(Reg32)
198 .addImm(-static_cast<int64_t>(LFIBundleSize)),
199 STI);
200
201 Out.emitInstruction(
202 MCInstBuilder(X86::ADD64rr).addReg(Reg).addReg(Reg).addReg(LFIBaseReg),
203 STI);
204}
205
206// Rewrite an indirect jump or call so that it can only target a bundle
207// boundary inside the sandbox.
208//
209// jmpq *%rX
210// ->
211// .bundle_lock
212// andl $-32, %eX
213// addq %r14, %rX
214// jmpq *%rX
215// .bundle_unlock
216//
217// A branch through memory loads its target into the scratch register first,
218// and then dispatches through it.
219//
220// jmpq *(%rdi)
221// ->
222// movq %gs:(%edi), %r11
223// .bundle_lock
224// andl $-32, %r11d
225// addq %r14, %r11
226// jmpq *%r11
227// .bundle_unlock
228void X86::X86MCLFIRewriter::rewriteIndirectBranch(const MCInst &Inst,
229 MCStreamer &Out,
230 const MCSubtargetInfo &STI) {
231 discardPrefixes();
232
233 MCRegister Target;
234 int MemIdx = X86II::getMemoryOperandIdx(InstInfo->get(Inst.getOpcode()));
235 if (MemIdx >= 0) {
237
238 // Construct the load and then apply the rewriter to it.
239 MCInstBuilder Mov(X86::MOV64rm);
240 Mov.addReg(Target);
241 for (unsigned I = 0; I < X86::AddrNumOperands; ++I)
242 Mov.addOperand(Inst.getOperand(MemIdx + I));
243 Mov.setLoc(Inst.getLoc());
244 doRewriteInst(Mov, Out, STI);
245 } else {
246 Target = Inst.getOperand(0).getReg();
247
248 if (Target == LFIBaseReg || Target == LFITPReg || Target == X86::RSP)
249 return error(Inst, "indirect branch through reserved register");
250 }
251
252 Out.emitBundleLock(/*AlignToEnd=*/isCall(Inst), STI);
253
254 emitSandboxBranchReg(Target, Out, STI);
255
256 MCInst Branch =
257 MCInstBuilder(isCall(Inst) ? X86::CALL64r : X86::JMP64r).addReg(Target);
258 if (hasNoTrackPrefix(Inst, *InstInfo))
259 Branch.setFlags(Branch.getFlags() | X86::IP_HAS_NOTRACK);
260 Out.emitInstruction(Branch, STI);
261
262 Out.emitBundleUnlock(STI);
263}
264
265// Direct calls are not rewritten, but must be placed at the end of a bundle
266// so that the return address they push is bundle-aligned.
267void X86::X86MCLFIRewriter::rewriteDirectCall(const MCInst &Inst,
268 MCStreamer &Out,
269 const MCSubtargetInfo &STI) {
270 Out.emitBundleLock(/*AlignToEnd=*/true, STI);
271 emitWithPrefixes(Inst, Out, STI);
272 Out.emitBundleUnlock(STI);
273}
274
275// ret
276// ->
277// popq %r11
278// .bundle_lock
279// andl $-32, %r11d
280// addq %r14, %r11
281// jmpq *%r11
282// .bundle_unlock
283//
284// A return with an immediate additionally pops the immediate off the stack
285// after loading the return address.
286//
287// retq $16
288// ->
289// popq %r11
290// addq $16, %rsp
291// .bundle_lock
292// andl $-32, %r11d
293// addq %r14, %r11
294// jmpq *%r11
295// .bundle_unlock
296void X86::X86MCLFIRewriter::rewriteReturn(const MCInst &Inst, MCStreamer &Out,
297 const MCSubtargetInfo &STI) {
298 if (Inst.getOpcode() != X86::RET64 && Inst.getOpcode() != X86::RETI64)
299 return error(Inst, "unsupported return instruction");
300
301 discardPrefixes();
302
303 Out.emitInstruction(MCInstBuilder(X86::POP64r).addReg(LFIScratchReg), STI);
304
305 if (Inst.getOpcode() == X86::RETI64) {
306 // Return with an immediate is rewritten recursively so that the stack
307 // pointer modification goes through the rewriter.
308 doRewriteInst(MCInstBuilder(X86::ADD64ri32)
309 .addReg(X86::RSP)
310 .addReg(X86::RSP)
311 .addOperand(Inst.getOperand(0))
312 .setLoc(Inst.getLoc()),
313 Out, STI);
314 }
315
316 Out.emitBundleLock(/*AlignToEnd=*/false, STI);
317
318 emitSandboxBranchReg(LFIScratchReg, Out, STI);
319
320 Out.emitInstruction(MCInstBuilder(X86::JMP64r).addReg(LFIScratchReg), STI);
321
322 Out.emitBundleUnlock(STI);
323}
324
325// Emit: movq TPOffset(%r15), %Reg
327 const MCSubtargetInfo &STI) {
328 Out.emitInstruction(MCInstBuilder(X86::MOV64rm)
329 .addReg(Reg)
330 .addReg(LFITPReg)
331 .addImm(1)
332 .addReg(X86::NoRegister)
333 .addImm(TPOffset)
334 .addReg(X86::NoRegister),
335 STI);
336}
337
338bool X86::X86MCLFIRewriter::isFSAccess(const MCInst &Inst) {
339 return (mayLoad(Inst) || mayStore(Inst)) &&
340 findFSMemOperand(Inst, *InstInfo) >= 0;
341}
342
343// Rewrite %fs-segment memory accesses to use the virtual thread pointer stored
344// at TPOffset(%r15). The rewritten access is fed back through the rewriter so
345// that it is sandboxed like other memory accesses. Example rewrites:
346//
347// movq %fs:0, %rax
348// ->
349// movq 16(%r15), %rax
350//
351// movq %fs:(%rdi), %rax
352// ->
353// movq 16(%r15), %rax
354// movq %gs:(%eax,%edi), %rax
355//
356// movq %fs:8(%rdi, %rsi, 2), %rax
357// ->
358// movq 16(%r15), %rax
359// leaq (%rax, %rdi), %rax
360// movq %gs:8(%eax, %esi, 2), %rax
361void X86::X86MCLFIRewriter::rewriteFSAccess(const MCInst &Inst, MCStreamer &Out,
362 const MCSubtargetInfo &STI) {
363 int MemIdx = findFSMemOperand(Inst, *InstInfo);
364 assert(MemIdx >= 0 && "expected an %fs memory operand");
365
366 MCRegister BaseReg = Inst.getOperand(MemIdx + X86::AddrBaseReg).getReg();
367 MCRegister IndexReg = Inst.getOperand(MemIdx + X86::AddrIndexReg).getReg();
368 bool HasBase = BaseReg != X86::NoRegister;
369 bool HasIndex = IndexReg != X86::NoRegister;
370 bool HasDisp = !Inst.getOperand(MemIdx + X86::AddrDisp).isImm() ||
371 Inst.getOperand(MemIdx + X86::AddrDisp).getImm() != 0;
372
373 // %fs:0 -> TPOffset(%r15). The context register always points into the
374 // runtime's thread-local block, so this access is safe.
375 if (!HasBase && !HasIndex && !HasDisp) {
376 MCInst Modified(Inst);
377 Modified.getOperand(MemIdx + X86::AddrBaseReg).setReg(LFITPReg);
378 Modified.getOperand(MemIdx + X86::AddrDisp).setImm(TPOffset);
379 Modified.getOperand(MemIdx + X86::AddrSegmentReg).setReg(X86::NoRegister);
380 return emitWithPrefixes(Modified, Out, STI);
381 }
382
383 if (!isGR64OrNone(BaseReg) || !isGR64OrNone(IndexReg) ||
384 BaseReg == X86::RSP || BaseReg == X86::RIP)
385 return error(Inst, "unsupported addressing mode for %fs access");
386
387 const MCInstrDesc &Desc = InstInfo->get(Inst.getOpcode());
388
389 // Reuse operand 0 as the TP temporary when the instruction writes it without
390 // also reading it, otherwise use %r11.
391 MCRegister TPDest = LFIScratchReg;
392 if (MemIdx > 0 && Inst.getOperand(0).isReg()) {
393 MCRegister DestReg = Inst.getOperand(0).getReg();
394 if (Desc.getNumDefs() > 0 &&
395 getX86MCRegisterClass(X86::GR64RegClassID).contains(DestReg) &&
396 !readsRegister(Inst, Desc, DestReg, *RegInfo))
397 TPDest = DestReg;
398 }
399
400 if (TPDest == LFIScratchReg &&
401 readsRegister(Inst, Desc, LFIScratchReg, *RegInfo))
402 return error(Inst, "%fs access reads reserved register %r11");
403
404 emitTPLoad(TPDest, Out, STI);
405
406 // Both slots occupied: the compute base via lea. For example:
407 //
408 // movq %fs:8(%rdi,%rsi,2), %rax
409 // ->
410 // movq 16(%r15), %rax
411 // leaq (%rax,%rdi), %rax
412 // movq %gs:8(%eax,%esi,2), %rax
413 if (HasBase && HasIndex) {
414 Out.emitInstruction(MCInstBuilder(X86::LEA64r)
415 .addReg(TPDest)
416 .addReg(TPDest)
417 .addImm(1)
418 .addReg(BaseReg)
419 .addImm(0)
420 .addReg(X86::NoRegister),
421 STI);
422 }
423
424 // Emit the access with TPDest as the new base, and the original base
425 // (offset from %fs) as the new index. For example:
426 //
427 // movq %fs:(%rdi), %rax
428 // ->
429 // movq 16(%r15), %rax
430 // movq %gs:(%eax,%edi), %rax
431 MCInst Modified(Inst);
432 Modified.getOperand(MemIdx + X86::AddrBaseReg).setReg(TPDest);
433 if (HasBase && !HasIndex)
434 Modified.getOperand(MemIdx + X86::AddrIndexReg).setReg(BaseReg);
435 Modified.getOperand(MemIdx + X86::AddrSegmentReg).setReg(X86::NoRegister);
436 doRewriteInst(Modified, Out, STI);
437}
438
439// Return true if Reg always contains an address within the sandbox.
441 return Reg == LFIBaseReg || Reg == X86::RSP || Reg == X86::RIP;
442}
443
444// Return true if Inst reads or writes memory through a pointer register
445// instead of an addressing mode, and so cannot be sandboxed by rewriting its
446// operands.
447static bool hasImplicitMemPointer(unsigned Opcode) {
448 switch (Opcode) {
449 // xlat loads from (%rbx,%al).
450 case X86::XLAT:
451 // maskmov stores to (%rdi).
452 case X86::MASKMOVDQU:
453 case X86::MASKMOVDQU64:
454 case X86::VMASKMOVDQU:
455 case X86::VMASKMOVDQU64:
456 case X86::MMX_MASKMOVQ:
457 case X86::MMX_MASKMOVQ64:
458 // clzero zeroes the cache line at (%rax).
459 case X86::CLZERO32r:
460 case X86::CLZERO64r:
461 // movdir64b and enqcmd store to the address in their register operand.
462 case X86::MOVDIR64B32:
463 case X86::MOVDIR64B64:
464 case X86::MOVDIR64B32_EVEX:
465 case X86::MOVDIR64B64_EVEX:
466 case X86::ENQCMD32:
467 case X86::ENQCMD64:
468 case X86::ENQCMD32_EVEX:
469 case X86::ENQCMD64_EVEX:
470 case X86::ENQCMDS32:
471 case X86::ENQCMDS64:
472 case X86::ENQCMDS32_EVEX:
473 case X86::ENQCMDS64_EVEX:
474 return true;
475 default:
476 return false;
477 }
478}
479
480// Return true if Inst is a bit test of memory with a register bit offset. The
481// offset is added to the address of the memory operand, so the bit it accesses
482// may be beyond the address computed by the addressing mode.
483static bool isBitTestWithRegOffset(unsigned Opcode) {
484 switch (Opcode) {
485 case X86::BT16mr:
486 case X86::BT32mr:
487 case X86::BT64mr:
488 case X86::BTC16mr:
489 case X86::BTC32mr:
490 case X86::BTC64mr:
491 case X86::BTR16mr:
492 case X86::BTR32mr:
493 case X86::BTR64mr:
494 case X86::BTS16mr:
495 case X86::BTS32mr:
496 case X86::BTS64mr:
497 case X86::LOCK_BTC_RM16rm:
498 case X86::LOCK_BTC_RM32rm:
499 case X86::LOCK_BTC_RM64rm:
500 case X86::LOCK_BTR_RM16rm:
501 case X86::LOCK_BTR_RM32rm:
502 case X86::LOCK_BTR_RM64rm:
503 case X86::LOCK_BTS_RM16rm:
504 case X86::LOCK_BTS_RM32rm:
505 case X86::LOCK_BTS_RM64rm:
506 return true;
507 default:
508 return false;
509 }
510}
511
512// Return true if Inst has a memory operand that is not dereferenced.
513static bool ignoresMemOperand(unsigned Opcode) {
514 switch (Opcode) {
515 case X86::LEA16r:
516 case X86::LEA32r:
517 case X86::LEA64r:
518 case X86::LEA64_32r:
519 case X86::NOOPW:
520 case X86::NOOPL:
521 case X86::NOOPQ:
522 return true;
523 default:
524 return false;
525 }
526}
527
528// Return true if the index register of the memory operand at MemIdx is a
529// general-purpose register, and so can be truncated along with the base.
530static bool hasGPRIndex(const MCInst &Inst, int MemIdx) {
531 MCRegister Index = Inst.getOperand(MemIdx + X86::AddrIndexReg).getReg();
532 return Index && getX86MCRegisterClass(X86::GR64RegClassID).contains(Index);
533}
534
535// Return true if the memory operand at MemIdx uses a 32-bit address size,
536// which is selected by a 32-bit base or index register.
537static bool has32BitAddress(const MCInst &Inst, int MemIdx) {
539 MCRegister Index = Inst.getOperand(MemIdx + X86::AddrIndexReg).getReg();
540 const MCRegisterClass &GR32 = getX86MCRegisterClass(X86::GR32RegClassID);
541 return Base == X86::EIP || GR32.contains(Base) || Index == X86::EIZ ||
542 GR32.contains(Index);
543}
544
545// Rewrite the addressing mode of the memory operand at MemIdx so that the
546// access it performs stays inside the sandbox.
547//
548// movq 8(%rax,%rcx,4), %rdi
549// ->
550// movq %gs:8(%eax,%ecx,4), %rdi
551void X86::X86MCLFIRewriter::sandboxMemOperand(MCInst &Inst, int MemIdx) const {
552 MCOperand &Base = Inst.getOperand(MemIdx + X86::AddrBaseReg);
553 MCOperand &Scale = Inst.getOperand(MemIdx + X86::AddrScaleAmt);
554 MCOperand &Index = Inst.getOperand(MemIdx + X86::AddrIndexReg);
555 MCOperand &Segment = Inst.getOperand(MemIdx + X86::AddrSegmentReg);
556
557 // %riz only encodes the absence of an index register, so drop it and handle
558 // the access as one without an index.
559 if (Index.getReg() == X86::RIZ) {
560 Index.setReg(X86::NoRegister);
561 Scale.setImm(1);
562 }
563
564 // A base register that always points into the sandbox needs no rewriting, as
565 // long as there is no index register.
566 if (isSandboxBaseReg(Base.getReg()) && !Index.getReg())
567 return;
568
569 // An absolute address has no register to truncate, so make it relative to
570 // the sandbox base instead.
571 if (!Base.getReg() && !Index.getReg()) {
572 Base.setReg(LFIBaseReg);
573 return;
574 }
575
576 // If there is no Base and the Index is not a GPR, then we use r14d (all
577 // zeroes) as the base for the rewritten addressing mode.
578 bool GPRIndex = hasGPRIndex(Inst, MemIdx);
579
580 Segment.setReg(LFIBaseSeg);
581 if (Base.getReg())
582 Base.setReg(getReg32(Base.getReg()));
583 else if (!GPRIndex)
584 Base.setReg(getReg32(LFIBaseReg));
585 if (GPRIndex)
586 Index.setReg(getReg32(Index.getReg()));
587}
588
589void X86::X86MCLFIRewriter::sandboxMemAccess(MCInst &Inst) const {
590 if (ignoresMemOperand(Inst.getOpcode()))
591 return;
592
593 int MemIdx = X86II::getMemoryOperandIdx(InstInfo->get(Inst.getOpcode()));
594 if (MemIdx >= 0)
595 sandboxMemOperand(Inst, MemIdx);
596}
597
598void X86::X86MCLFIRewriter::rewriteMemAccess(const MCInst &Inst,
599 MCStreamer &Out,
600 const MCSubtargetInfo &STI) {
601 MCInst Sandboxed(Inst);
602 sandboxMemAccess(Sandboxed);
603 emitWithPrefixes(Sandboxed, Out, STI);
604}
605
606namespace {
607struct StringOpPointers {
608 bool UsesDest;
609 bool UsesSrc;
610};
611} // namespace
612
613static std::optional<StringOpPointers> getStringOpPointers(unsigned Opcode) {
614 switch (Opcode) {
615 case X86::STOSB:
616 case X86::STOSW:
617 case X86::STOSL:
618 case X86::STOSQ:
619 case X86::REP_STOSB_64:
620 case X86::REP_STOSW_64:
621 case X86::REP_STOSD_64:
622 case X86::REP_STOSQ_64:
623 case X86::SCASB:
624 case X86::SCASW:
625 case X86::SCASL:
626 case X86::SCASQ:
627 return StringOpPointers{true, false};
628 case X86::MOVSB:
629 case X86::MOVSW:
630 case X86::MOVSL:
631 case X86::MOVSQ:
632 case X86::REP_MOVSB_64:
633 case X86::REP_MOVSW_64:
634 case X86::REP_MOVSD_64:
635 case X86::REP_MOVSQ_64:
636 case X86::CMPSB:
637 case X86::CMPSW:
638 case X86::CMPSL:
639 case X86::CMPSQ:
640 return StringOpPointers{true, true};
641 case X86::LODSB:
642 case X86::LODSW:
643 case X86::LODSL:
644 case X86::LODSQ:
645 return StringOpPointers{false, true};
646 default:
647 return std::nullopt;
648 }
649}
650
652 MCStreamer &Out, const MCSubtargetInfo &STI) {
653 Out.emitInstruction(MCInstBuilder(X86::MOV32rr).addReg(Reg32).addReg(Reg32),
654 STI);
655
656 Out.emitInstruction(MCInstBuilder(X86::LEA64r)
657 .addReg(Reg)
658 .addReg(LFIBaseReg)
659 .addImm(1)
660 .addReg(Reg)
661 .addImm(0)
662 .addReg(X86::NoRegister),
663 STI);
664}
665
666// rep movsq
667// ->
668// .bundle_lock
669// movl %edi, %edi
670// leaq (%r14,%rdi), %rdi
671// movl %esi, %esi
672// leaq (%r14,%rsi), %rsi
673// rep movsq
674// .bundle_unlock
675void X86::X86MCLFIRewriter::rewriteStringOperation(const MCInst &Inst,
676 MCStreamer &Out,
677 const MCSubtargetInfo &STI) {
678 std::optional<StringOpPointers> Ptrs = getStringOpPointers(Inst.getOpcode());
679 assert(Ptrs && "expected a string instruction");
680
681 // The guards below sandbox the full 64-bit %rdi and %rsi, so the access must
682 // use them with a 64-bit address size and no %fs segment override.
683 for (const MCOperand &Op : Inst) {
684 if (!Op.isReg())
685 continue;
686 if (Op.getReg() == X86::EDI || Op.getReg() == X86::ESI)
687 return error(Inst, "unsupported address size for string instruction");
688 if (Op.getReg() == X86::FS)
689 return error(Inst, "unsupported segment override for string instruction");
690 }
691
692 Out.emitBundleLock(/*AlignToEnd=*/false, STI);
693
694 if (Ptrs->UsesDest)
695 emitStringPtrFixup(X86::RDI, X86::EDI, Out, STI);
696 if (Ptrs->UsesSrc)
697 emitStringPtrFixup(X86::RSI, X86::ESI, Out, STI);
698
699 emitWithPrefixes(Inst, Out, STI);
700
701 Out.emitBundleUnlock(STI);
702}
703
704// The register form of an instruction that modifies the stack and reads its
705// source from memory, if there is one.
706static std::optional<unsigned> memToRegOpcode(unsigned Opcode) {
707 switch (Opcode) {
708 case X86::MOV64rm:
709 return X86::MOV64rr;
710 case X86::ADD64rm:
711 return X86::ADD64rr;
712 case X86::SUB64rm:
713 return X86::SUB64rr;
714 case X86::AND64rm:
715 return X86::AND64rr;
716 case X86::OR64rm:
717 return X86::OR64rr;
718 case X86::XOR64rm:
719 return X86::XOR64rr;
720 default:
721 return std::nullopt;
722 }
723}
724
725// The 32-bit form of a 64-bit instruction, if it has one that can be used to
726// modify the stack pointer.
727static std::optional<unsigned> demote64BitOpcode(unsigned Opcode) {
728 switch (Opcode) {
729 case X86::MOV64rr:
730 return X86::MOV32rr;
731 case X86::MOV64ri32:
732 return X86::MOV32ri;
733 case X86::LEA64r:
734 return X86::LEA64_32r;
735 case X86::ADD64rr:
736 return X86::ADD32rr;
737 case X86::ADD64ri8:
738 return X86::ADD32ri8;
739 case X86::ADD64ri32:
740 return X86::ADD32ri;
741 case X86::SUB64rr:
742 return X86::SUB32rr;
743 case X86::SUB64ri8:
744 return X86::SUB32ri8;
745 case X86::SUB64ri32:
746 return X86::SUB32ri;
747 case X86::AND64rr:
748 return X86::AND32rr;
749 case X86::AND64ri8:
750 return X86::AND32ri8;
751 case X86::AND64ri32:
752 return X86::AND32ri;
753 case X86::OR64rr:
754 return X86::OR32rr;
755 case X86::OR64ri8:
756 return X86::OR32ri8;
757 case X86::OR64ri32:
758 return X86::OR32ri;
759 case X86::XOR64rr:
760 return X86::XOR32rr;
761 case X86::XOR64ri8:
762 return X86::XOR32ri8;
763 case X86::XOR64ri32:
764 return X86::XOR32ri;
765 default:
766 return std::nullopt;
767 }
768}
769
770MCRegister X86::X86MCLFIRewriter::getWrittenStackReg(const MCInst &Inst) const {
771 const MCInstrDesc &Desc = InstInfo->get(Inst.getOpcode());
772 for (unsigned I = 0, E = Desc.getNumDefs(); I < E; ++I) {
773 const MCOperand &Op = Inst.getOperand(I);
774 if (Op.isReg() && RegInfo->regsOverlap(Op.getReg(), X86::RSP))
775 return Op.getReg();
776 }
777 return X86::NoRegister;
778}
779
780// Emit leaq (%rsp,%r14), %rsp
781static void emitStackFixup(MCStreamer &Out, const MCSubtargetInfo &STI) {
782 Out.emitInstruction(MCInstBuilder(X86::LEA64r)
783 .addReg(X86::RSP)
784 .addReg(X86::RSP)
785 .addImm(1)
786 .addReg(LFIBaseReg)
787 .addImm(0)
788 .addReg(X86::NoRegister),
789 STI);
790}
791
792// addq $8, %rsp
793// ->
794// .bundle_lock
795// addl $8, %esp
796// leaq (%rsp,%r14), %rsp
797// .bundle_unlock
798void X86::X86MCLFIRewriter::rewriteStackModification(
799 MCRegister StackReg, const MCInst &Inst, MCStreamer &Out,
800 const MCSubtargetInfo &STI) {
801 if (StackReg != X86::RSP)
802 return error(Inst, "unsupported modification of the stack pointer");
803
804 discardPrefixes();
805
806 // popq %rsp loads through the stack pointer that it overwrites, so it cannot
807 // simply be demoted. Pop into the scratch register instead, and move the
808 // result into %esp.
809 if (Inst.getOpcode() == X86::POP64r) {
810 Out.emitInstruction(MCInstBuilder(X86::POP64r).addReg(LFIScratchReg), STI);
811
812 Out.emitBundleLock(/*AlignToEnd=*/false, STI);
813 Out.emitInstruction(
814 MCInstBuilder(X86::MOV32rr).addReg(X86::ESP).addReg(X86::R11D), STI);
815 emitStackFixup(Out, STI);
816 Out.emitBundleUnlock(STI);
817 return;
818 }
819
820 // movq 8(%rdi), %rsp
821 // ->
822 // movq %gs:8(%edi), %r11
823 // .bundle_lock
824 // movl %r11d, %esp
825 // leaq (%rsp,%r14), %rsp
826 // .bundle_unlock
827 int MemIdx = X86II::getMemoryOperandIdx(InstInfo->get(Inst.getOpcode()));
828 if (MemIdx >= 0 && !ignoresMemOperand(Inst.getOpcode())) {
829 std::optional<unsigned> RegOpcode = memToRegOpcode(Inst.getOpcode());
830 if (!RegOpcode)
831 return error(Inst, "unsupported modification of the stack pointer");
832
833 MCInstBuilder Load(X86::MOV64rm);
834 Load.addReg(LFIScratchReg);
835 for (unsigned I = 0; I < X86::AddrNumOperands; ++I)
836 Load.addOperand(Inst.getOperand(MemIdx + I));
837 Load.setLoc(Inst.getLoc());
838 doRewriteInst(Load, Out, STI);
839
840 MCInstBuilder RegForm(*RegOpcode);
841 for (int I = 0; I < MemIdx; ++I)
842 RegForm.addOperand(Inst.getOperand(I));
843 RegForm.addReg(LFIScratchReg);
844 RegForm.setLoc(Inst.getLoc());
845 return doRewriteInst(RegForm, Out, STI);
846 }
847
848 std::optional<unsigned> Opcode = demote64BitOpcode(Inst.getOpcode());
849 if (!Opcode)
850 return error(Inst, "unsupported modification of the stack pointer");
851
852 MCInst Modified(Inst);
853 Modified.setOpcode(*Opcode);
854
855 ArrayRef<MCOperandInfo> OpInfo = InstInfo->get(*Opcode).operands();
856 for (unsigned I = 0, E = std::min<unsigned>(OpInfo.size(),
857 Modified.getNumOperands());
858 I < E; ++I) {
859 MCOperand &Op = Modified.getOperand(I);
860 if (OpInfo[I].OperandType != MCOI::OPERAND_REGISTER || !Op.isReg())
861 continue;
862 if (getX86MCRegisterClass(X86::GR64RegClassID).contains(Op.getReg()))
863 Op.setReg(RegInfo->getSubReg(Op.getReg(), X86::sub_32bit));
864 }
865
866 Out.emitBundleLock(/*AlignToEnd=*/false, STI);
867 Out.emitInstruction(Modified, STI);
868 emitStackFixup(Out, STI);
869 Out.emitBundleUnlock(STI);
870}
871
872// leave
873// ->
874// .bundle_lock
875// movl %ebp, %esp
876// leaq (%rsp,%r14), %rsp
877// .bundle_unlock
878// popq %rbp
879void X86::X86MCLFIRewriter::rewriteLeave(const MCInst &Inst, MCStreamer &Out,
880 const MCSubtargetInfo &STI) {
881 doRewriteInst(MCInstBuilder(X86::MOV64rr)
882 .addReg(X86::RSP)
883 .addReg(X86::RBP)
884 .setLoc(Inst.getLoc()),
885 Out, STI);
886 Out.emitInstruction(MCInstBuilder(X86::POP64r).addReg(X86::RBP), STI);
887}
888
889void X86::X86MCLFIRewriter::doRewriteInst(const MCInst &Inst, MCStreamer &Out,
890 const MCSubtargetInfo &STI) {
891 if (!STI.hasFeature(X86::Is64Bit))
892 return error(Inst, "LFI only supports 64-bit mode");
893
894 // Hold a stand-alone prefix back, so it can be applied to the rewritten
895 // instruction.
896 if (isPrefix(Inst, *InstInfo)) {
897 if (!isSupportedPrefix(Inst))
898 return error(Inst, "unsupported instruction prefix");
899 Prefixes.push_back(Inst);
900 return;
901 }
902
903 // A lock prefix is only valid on an instruction that writes memory, and
904 // would otherwise be dropped by the rewrites below.
905 if ((Inst.getFlags() & X86::IP_HAS_LOCK) && !mayStore(Inst))
906 return error(Inst, "unsupported lock prefix");
907
908 if (mayModifyRegister(Inst, LFIBaseReg) ||
909 mayModifyRegister(Inst, LFITPReg) || mayModifyRegister(Inst, LFIBaseSeg))
910 return error(Inst, "illegal modification of reserved LFI register");
911
912 if (isWriteSegmentBase(Inst.getOpcode()))
913 return error(Inst, "illegal modification of reserved segment base");
914
915 if (isSyscall(Inst))
916 return rewriteSyscall(Inst, Out, STI);
917
918 if (isReturn(Inst))
919 return rewriteReturn(Inst, Out, STI);
920
921 if (isDirectCall(Inst))
922 return rewriteDirectCall(Inst, Out, STI);
923
924 // jmpabs is disallowed since it jumps to an absolute address.
925 if (Inst.getOpcode() == X86::JMPABS64i)
926 return error(Inst, "unsupported branch instruction");
927
928 if (isIndirectBranch(Inst) || isCall(Inst)) {
929 if (!isSupportedIndirectBranch(Inst))
930 return error(Inst, "unsupported indirect branch");
931 return rewriteIndirectBranch(Inst, Out, STI);
932 }
933
934 for (const MCOperand &Op : Inst)
935 if (Op.isReg() && Op.getReg() == LFIBaseSeg)
936 return error(Inst, "invalid use of reserved segment register %gs");
937
938 if (MCRegister StackReg = getWrittenStackReg(Inst))
939 return rewriteStackModification(StackReg, Inst, Out, STI);
940
941 // These accesses cannot be sandboxed by rewriting the addressing mode. The
942 // moffs form of mov encodes an absolute address in the instruction instead
943 // of an addressing mode.
944 if ((InstInfo->get(Inst.getOpcode()).TSFlags & X86II::FormMask) ==
946 hasImplicitMemPointer(Inst.getOpcode()))
947 return error(Inst, "unsupported memory access");
948
949 if (isBitTestWithRegOffset(Inst.getOpcode()))
950 return error(Inst, "unsupported bit test with a register bit offset");
951
952 if (isFSAccess(Inst))
953 return rewriteFSAccess(Inst, Out, STI);
954
955 // The compiler never generates 32-bit addressing in LP64 mode.
956 int MemIdx = X86II::getMemoryOperandIdx(InstInfo->get(Inst.getOpcode()));
957 if (MemIdx >= 0 && !ignoresMemOperand(Inst.getOpcode()) &&
958 has32BitAddress(Inst, MemIdx))
959 return error(Inst, "unsupported 32-bit address size");
960
961 if (getStringOpPointers(Inst.getOpcode()))
962 return rewriteStringOperation(Inst, Out, STI);
963
964 if (Inst.getOpcode() == X86::LEAVE64)
965 return rewriteLeave(Inst, Out, STI);
966
967 if (Inst.getOpcode() == X86::ENTER &&
968 (!Inst.getOperand(1).isImm() ||
969 static_cast<uint64_t>(Inst.getOperand(1).getImm()) > 1))
970 return error(Inst, "unsupported nesting level for enter");
971
972 rewriteMemAccess(Inst, Out, STI);
973}
974
976 const MCSubtargetInfo &STI) {
977 // The guard prevents rewrite-recursion when we emit instructions from inside
978 // the rewriter (such instructions should not be rewritten).
979 if (!Enabled || Guard)
980 return false;
981 Guard = true;
982
983 size_t PrefixesBefore = Prefixes.size();
984
985 doRewriteInst(Inst, Out, STI);
986
987 if (Prefixes.size() == PrefixesBefore)
988 discardPrefixes();
989
990 Guard = false;
991 return true;
992}
993
995 discardPrefixes();
996}
997
998void X86::X86MCLFIRewriter::finish(MCStreamer &Out) { discardPrefixes(); }
static constexpr MCRegister LFIScratchReg
static bool isSyscall(const MCInst &Inst)
static constexpr MCRegister LFIBaseReg
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
uint64_t uint64_t
static MCDisassembler::DecodeStatus addOperand(MCInst &Inst, const MCOperand &Opnd)
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define I(x, y, z)
Definition MD5.cpp:57
Register Reg
static bool contains(SmallPtrSetImpl< ConstantExpr * > &Cache, ConstantExpr *Expr, Constant *C)
Definition Value.cpp:479
#define error(X)
static bool isPrefix(unsigned Opcode, const MCInstrInfo &MCII)
Check if the instruction is a prefix.
static bool isWriteSegmentBase(unsigned Opcode)
static void emitStackFixup(MCStreamer &Out, const MCSubtargetInfo &STI)
static std::optional< unsigned > memToRegOpcode(unsigned Opcode)
static bool has32BitAddress(const MCInst &Inst, int MemIdx)
static bool isDirectCall(const MCInst &Inst)
static void emitTPLoad(MCRegister Reg, MCStreamer &Out, const MCSubtargetInfo &STI)
static bool hasNoTrackPrefix(const MCInst &Inst, const MCInstrInfo &InstInfo)
static bool isGR64OrNone(MCRegister Reg)
static bool isPrefix(const MCInst &Inst, const MCInstrInfo &InstInfo)
static bool isSupportedIndirectBranch(const MCInst &Inst)
static int findFSMemOperand(const MCInst &Inst, const MCInstrInfo &InstInfo)
static bool hasGPRIndex(const MCInst &Inst, int MemIdx)
static bool isSupportedPrefix(const MCInst &Inst)
static constexpr MCRegister LFITPReg
static constexpr int TPOffset
static void emitStringPtrFixup(MCRegister Reg, MCRegister Reg32, MCStreamer &Out, const MCSubtargetInfo &STI)
static bool isSyscall(const MCInst &Inst)
static bool hasImplicitMemPointer(unsigned Opcode)
static constexpr MCRegister LFIBaseSeg
static bool readsRegister(const MCInst &Inst, const MCInstrDesc &Desc, MCRegister Reg, const MCRegisterInfo &RI)
static bool isSandboxBaseReg(MCRegister Reg)
static std::optional< unsigned > demote64BitOpcode(unsigned Opcode)
static bool ignoresMemOperand(unsigned Opcode)
static bool isBitTestWithRegOffset(unsigned Opcode)
static std::optional< StringOpPointers > getStringOpPointers(unsigned Opcode)
size_t size() const
Get the array size.
Definition ArrayRef.h:141
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 getFlags() const
Definition MCInst.h:205
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.
Interface to description of machine instruction set.
Definition MCInstrInfo.h:27
std::unique_ptr< MCRegisterInfo > RegInfo
Instances of this class represent operands of the MCInst class.
Definition MCInst.h:40
void setImm(int64_t Val)
Definition MCInst.h:89
int64_t getImm() const
Definition MCInst.h:84
bool isImm() const
Definition MCInst.h:66
void setReg(MCRegister Reg)
Set the register number.
Definition MCInst.h:79
bool isReg() const
Definition MCInst.h:65
MCRegister getReg() const
Returns the register number.
Definition MCInst.h:73
MCRegisterClass - Base class of TargetRegisterClass.
bool contains(MCRegister Reg) const
contains - Return true if the specified register is included in this register class.
MCRegisterInfo base class - We assume that the target defines a static array of MCRegisterDesc object...
bool regsOverlap(MCRegister RegA, MCRegister RegB) const
Returns true if the two registers are equal or alias each other.
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
static const MCSymbolRefExpr * create(const MCSymbol *Symbol, MCContext &Ctx, SMLoc Loc=SMLoc())
Definition MCExpr.h:213
MCSymbol - Instances of this class represent a symbol name in the MC file, and MCSymbols are created ...
Definition MCSymbol.h:42
A Use represents the edge between a Value definition and its users.
Definition Use.h:35
void finish(MCStreamer &Out) override
bool rewriteInst(const MCInst &Inst, MCStreamer &Out, const MCSubtargetInfo &STI) override
void onLabel(const MCSymbol *Symbol, MCStreamer &Out) override
bool isPrefix(uint64_t TSFlags)
int getMemoryOperandIdx(const MCInstrDesc &Desc)
@ RawFrmMemOffs
RawFrmMemOffs - This form is for instructions that store an absolute memory offset as an immediate wi...
@ AddrNumOperands
Definition X86BaseInfo.h:37
constexpr unsigned LFIBundleSize
BaseReg
Stack frame base register. Bit 0 of FREInfo.Info.
Definition SFrame.h:77
This is an optimization pass for GlobalISel generic memory operations.
@ Load
The value being inserted comes from a load (InsertElement only).
Op::Description Desc
uint16_t MCPhysReg
An unsigned integer type large enough to represent all physical registers, but not necessarily virtua...
Definition MCRegister.h:21
DWARFExpression::Operation Op
ArrayRef(const T &OneElt) -> ArrayRef< T >