LLVM 23.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"
20#include "llvm/MC/MCStreamer.h"
22
23using namespace llvm;
24
25// LFI reserved registers.
26static constexpr MCRegister LFIBaseReg = X86::R14;
27static constexpr MCRegister LFIScratchReg = X86::R11;
28static constexpr MCRegister LFITPReg = X86::R15;
29
30// Byte offset into the context register file (pointed to by R15) where the
31// thread pointer is stored.
32static constexpr int TPOffset = 16;
33
34static bool isSyscall(const MCInst &Inst) {
35 return Inst.getOpcode() == X86::SYSCALL;
36}
37
38// Find the index of the memory operand if it has an %fs segment override.
39// Returns -1 if there is no memory operand or no %fs override.
40static int findFSMemOperand(const MCInst &Inst, const MCInstrInfo &InstInfo) {
41 const MCInstrDesc &Desc = InstInfo.get(Inst.getOpcode());
42 int MemRefIdx = X86II::getMemoryOperandNo(Desc.TSFlags);
43 if (MemRefIdx < 0)
44 return -1;
45 int MemIdx = MemRefIdx + X86II::getOperandBias(Desc);
46 const MCOperand &Seg = Inst.getOperand(MemIdx + X86::AddrSegmentReg);
47 if (Seg.isReg() && Seg.getReg() == X86::FS)
48 return MemIdx;
49 return -1;
50}
51
52// Return true if the instruction reads from Reg.
53static bool readsRegister(const MCInst &Inst, const MCInstrDesc &Desc,
54 MCRegister Reg, const MCRegisterInfo &RI) {
55 for (unsigned I = Desc.getNumDefs(), E = Inst.getNumOperands(); I < E; ++I) {
56 const MCOperand &Op = Inst.getOperand(I);
57 if (Op.isReg() && Op.getReg() && RI.regsOverlap(Op.getReg(), Reg))
58 return true;
59 }
60 for (MCPhysReg Use : Desc.implicit_uses())
61 if (RI.regsOverlap(Use, Reg))
62 return true;
63 return false;
64}
65
66// Return true if Reg is absent or a 64-bit general-purpose register.
68 return Reg == X86::NoRegister ||
69 getX86MCRegisterClass(X86::GR64RegClassID).contains(Reg);
70}
71
72// syscall
73// ->
74// leaq .Ltmp(%rip), %r11
75// jmpq *(%r14)
76// .Ltmp:
77void X86::X86MCLFIRewriter::rewriteSyscall(const MCInst &Inst, MCStreamer &Out,
78 const MCSubtargetInfo &STI) {
80
81 // leaq .Ltmp(%rip), %r11
82 MCInst Lea;
83 Lea.setOpcode(X86::LEA64r);
85 Lea.addOperand(MCOperand::createReg(X86::RIP));
87 Lea.addOperand(MCOperand::createReg(X86::NoRegister));
88 Lea.addOperand(
90 Lea.addOperand(MCOperand::createReg(X86::NoRegister));
91 Out.emitInstruction(Lea, STI);
92
93 // jmpq *(%r14)
94 MCInst Jmp;
95 Jmp.setOpcode(X86::JMP64m);
98 Jmp.addOperand(MCOperand::createReg(X86::NoRegister));
100 Jmp.addOperand(MCOperand::createReg(X86::NoRegister));
101 Out.emitInstruction(Jmp, STI);
102
103 Out.emitLabel(Symbol);
104}
105
106// Emit: movq TPOffset(%r15), %Reg
108 const MCSubtargetInfo &STI) {
109 MCInst Mov;
110 Mov.setOpcode(X86::MOV64rm);
114 Mov.addOperand(MCOperand::createReg(X86::NoRegister));
116 Mov.addOperand(MCOperand::createReg(X86::NoRegister));
117 Out.emitInstruction(Mov, STI);
118}
119
120bool X86::X86MCLFIRewriter::isFSAccess(const MCInst &Inst) {
121 return (mayLoad(Inst) || mayStore(Inst)) &&
122 findFSMemOperand(Inst, *InstInfo) >= 0;
123}
124
125// Rewrite %fs-segment memory accesses to use the virtual thread pointer stored
126// at TPOffset(%r15). The actual memory access is currently unsandboxed because
127// load/store sandboxing is not yet supported. Example rewrites:
128//
129// movq %fs:0, %rax
130// ->
131// movq 16(%r15), %rax
132//
133// movq %fs:(%rdi), %rax
134// ->
135// movq 16(%r15), %rax
136// movq (%rax, %rdi), %rax
137//
138// movq %fs:8(%rdi, %rsi, 2), %rax
139// ->
140// movq 16(%r15), %rax
141// leaq (%rax, %rdi), %rax
142// movq 8(%rax, %rsi, 2), %rax
143void X86::X86MCLFIRewriter::rewriteFSAccess(const MCInst &Inst, MCStreamer &Out,
144 const MCSubtargetInfo &STI) {
145 int MemIdx = findFSMemOperand(Inst, *InstInfo);
146 assert(MemIdx >= 0);
147
148 MCRegister BaseReg = Inst.getOperand(MemIdx + X86::AddrBaseReg).getReg();
149 MCRegister IndexReg = Inst.getOperand(MemIdx + X86::AddrIndexReg).getReg();
150 bool HasBase = BaseReg != X86::NoRegister;
151 bool HasIndex = IndexReg != X86::NoRegister;
152 bool HasDisp = !Inst.getOperand(MemIdx + X86::AddrDisp).isImm() ||
153 Inst.getOperand(MemIdx + X86::AddrDisp).getImm() != 0;
154
155 // %fs:0 -> TPOffset(%r15)
156 if (!HasBase && !HasIndex && !HasDisp) {
157 MCInst Modified(Inst);
158 Modified.getOperand(MemIdx + X86::AddrBaseReg).setReg(LFITPReg);
159 Modified.getOperand(MemIdx + X86::AddrDisp).setImm(TPOffset);
160 Modified.getOperand(MemIdx + X86::AddrSegmentReg).setReg(X86::NoRegister);
161 return Out.emitInstruction(Modified, STI);
162 }
163
164 if (!isGR64OrNone(BaseReg) || !isGR64OrNone(IndexReg) ||
165 BaseReg == X86::RSP || BaseReg == X86::RIP)
166 return error(Inst, "unsupported addressing mode for %fs access");
167
168 const MCInstrDesc &Desc = InstInfo->get(Inst.getOpcode());
169
170 // Reuse operand 0 as the TP temporary when the instruction writes it without
171 // also reading it, otherwise use %r11.
172 MCRegister TPDest = LFIScratchReg;
173 if (MemIdx > 0 && Inst.getOperand(0).isReg()) {
174 MCRegister DestReg = Inst.getOperand(0).getReg();
175 if (Desc.getNumDefs() > 0 &&
176 getX86MCRegisterClass(X86::GR64RegClassID).contains(DestReg) &&
177 !readsRegister(Inst, Desc, DestReg, *RegInfo))
178 TPDest = DestReg;
179 }
180
181 if (TPDest == LFIScratchReg &&
182 readsRegister(Inst, Desc, LFIScratchReg, *RegInfo))
183 return error(Inst, "%fs access reads reserved register %r11");
184
185 emitTPLoad(TPDest, Out, STI);
186
187 // Both slots occupied: the compute base via lea. For example:
188 //
189 // movq %fs:8(%rdi,%rsi,2), %rax
190 // ->
191 // movq 16(%r15), %rax
192 // leaq (%rax,%rdi), %rax
193 // movq 8(%rax,%rsi,2), %rax
194 if (HasBase && HasIndex) {
195 MCInst Lea;
196 Lea.setOpcode(X86::LEA64r);
197 Lea.addOperand(MCOperand::createReg(TPDest));
198 Lea.addOperand(MCOperand::createReg(TPDest));
200 Lea.addOperand(MCOperand::createReg(BaseReg));
202 Lea.addOperand(MCOperand::createReg(X86::NoRegister));
203 Out.emitInstruction(Lea, STI);
204 }
205
206 // Emit the access with TPDest as the new base, and the original base
207 // (offset from %fs) as the new index. For example:
208 //
209 // movq %fs:(%rdi), %rax
210 // ->
211 // movq 16(%r15), %rax
212 // movq (%rax,%rdi), %rax
213 MCInst Modified(Inst);
214 Modified.getOperand(MemIdx + X86::AddrBaseReg).setReg(TPDest);
215 if (HasBase && !HasIndex)
216 Modified.getOperand(MemIdx + X86::AddrIndexReg).setReg(BaseReg);
217 Modified.getOperand(MemIdx + X86::AddrSegmentReg).setReg(X86::NoRegister);
218 Out.emitInstruction(Modified, STI);
219}
220
221void X86::X86MCLFIRewriter::doRewriteInst(const MCInst &Inst, MCStreamer &Out,
222 const MCSubtargetInfo &STI) {
223 if (mayModifyRegister(Inst, LFIBaseReg) || mayModifyRegister(Inst, LFITPReg))
224 return error(Inst, "illegal modification of reserved LFI register");
225
226 if (isSyscall(Inst))
227 return rewriteSyscall(Inst, Out, STI);
228
229 if (isFSAccess(Inst))
230 return rewriteFSAccess(Inst, Out, STI);
231
232 // Pass through all other instructions unchanged.
233 Out.emitInstruction(Inst, STI);
234}
235
237 const MCSubtargetInfo &STI) {
238 // The guard prevents rewrite-recursion when we emit instructions from inside
239 // the rewriter (such instructions should not be rewritten).
240 if (!Enabled || Guard)
241 return false;
242 Guard = true;
243
244 doRewriteInst(Inst, Out, STI);
245
246 Guard = false;
247 return true;
248}
static constexpr MCRegister LFIScratchReg
static bool isSyscall(const MCInst &Inst)
static constexpr MCRegister LFIBaseReg
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
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:484
#define error(X)
static void emitTPLoad(MCRegister Reg, MCStreamer &Out, const MCSubtargetInfo &STI)
static bool isGR64OrNone(MCRegister Reg)
static int findFSMemOperand(const MCInst &Inst, const MCInstrInfo &InstInfo)
static constexpr MCRegister LFITPReg
static constexpr int TPOffset
static bool isSyscall(const MCInst &Inst)
static bool readsRegister(const MCInst &Inst, const MCInstrDesc &Desc, MCRegister Reg, const MCRegisterInfo &RI)
LLVM_ABI MCSymbol * createTempSymbol()
Create a temporary symbol with a unique name.
Instances of this class represent a single low-level machine instruction.
Definition MCInst.h:188
unsigned getNumOperands() const
Definition MCInst.h:212
unsigned getOpcode() const
Definition MCInst.h:202
void addOperand(const MCOperand Op)
Definition MCInst.h:215
void setOpcode(unsigned Op)
Definition MCInst.h:201
const MCOperand & getOperand(unsigned i) const
Definition MCInst.h:210
Describe properties that are true of each instruction in the target description file.
Interface to description of machine instruction set.
Definition MCInstrInfo.h:27
Instances of this class represent operands of the MCInst class.
Definition MCInst.h:40
static MCOperand createExpr(const MCExpr *Val)
Definition MCInst.h:166
int64_t getImm() const
Definition MCInst.h:84
static MCOperand createReg(MCRegister Reg)
Definition MCInst.h:138
static MCOperand createImm(int64_t Val)
Definition MCInst.h:145
bool isImm() const
Definition MCInst.h:66
bool isReg() const
Definition MCInst.h:65
MCRegister getReg() const
Returns the register number.
Definition MCInst.h:73
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
virtual void emitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI)
Emit the given Instruction into the current section.
MCContext & getContext() const
Definition MCStreamer.h:326
virtual void emitLabel(MCSymbol *Symbol, SMLoc Loc=SMLoc())
Emit a label for Symbol into the current section.
Generic base class for all target subtargets.
static const MCSymbolRefExpr * create(const MCSymbol *Symbol, MCContext &Ctx, SMLoc Loc=SMLoc())
Definition MCExpr.h:213
A Use represents the edge between a Value definition and its users.
Definition Use.h:35
bool rewriteInst(const MCInst &Inst, MCStreamer &Out, const MCSubtargetInfo &STI) override
int getMemoryOperandNo(uint64_t TSFlags)
unsigned getOperandBias(const MCInstrDesc &Desc)
Compute whether all of the def operands are repeated in the uses and therefore should be skipped.
BaseReg
Stack frame base register. Bit 0 of FREInfo.Info.
Definition SFrame.h:77
This is an optimization pass for GlobalISel generic memory operations.
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