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"
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 int MemIdx = X86II::getMemoryOperandIdx(InstInfo.get(Inst.getOpcode()));
42 if (MemIdx < 0)
43 return -1;
44 const MCOperand &Seg = Inst.getOperand(MemIdx + X86::AddrSegmentReg);
45 if (Seg.isReg() && Seg.getReg() == X86::FS)
46 return MemIdx;
47 return -1;
48}
49
50// Return true if the instruction reads from Reg.
51static bool readsRegister(const MCInst &Inst, const MCInstrDesc &Desc,
52 MCRegister Reg, const MCRegisterInfo &RI) {
53 for (unsigned I = Desc.getNumDefs(), E = Inst.getNumOperands(); I < E; ++I) {
54 const MCOperand &Op = Inst.getOperand(I);
55 if (Op.isReg() && Op.getReg() && RI.regsOverlap(Op.getReg(), Reg))
56 return true;
57 }
58 for (MCPhysReg Use : Desc.implicit_uses())
59 if (RI.regsOverlap(Use, Reg))
60 return true;
61 return false;
62}
63
64// Return true if Reg is absent or a 64-bit general-purpose register.
66 return Reg == X86::NoRegister ||
67 getX86MCRegisterClass(X86::GR64RegClassID).contains(Reg);
68}
69
70// syscall
71// ->
72// .bundle_lock
73// leaq .Ltmp(%rip), %r11
74// jmpq *(%r14)
75// .Ltmp:
76// .bundle_unlock
77void X86::X86MCLFIRewriter::rewriteSyscall(const MCInst &Inst, MCStreamer &Out,
78 const MCSubtargetInfo &STI) {
79 Out.emitBundleLock(/*AlignToEnd=*/false, STI);
80
82
83 // leaq .Ltmp(%rip), %r11
84 MCInst Lea;
85 Lea.setOpcode(X86::LEA64r);
87 Lea.addOperand(MCOperand::createReg(X86::RIP));
89 Lea.addOperand(MCOperand::createReg(X86::NoRegister));
90 Lea.addOperand(
92 Lea.addOperand(MCOperand::createReg(X86::NoRegister));
93 Out.emitInstruction(Lea, STI);
94
95 // jmpq *(%r14)
96 MCInst Jmp;
97 Jmp.setOpcode(X86::JMP64m);
100 Jmp.addOperand(MCOperand::createReg(X86::NoRegister));
102 Jmp.addOperand(MCOperand::createReg(X86::NoRegister));
103 Out.emitInstruction(Jmp, STI);
104
105 Out.emitLabel(Symbol);
106 Out.emitBundleUnlock(STI);
107}
108
109// Emit: movq TPOffset(%r15), %Reg
111 const MCSubtargetInfo &STI) {
112 MCInst Mov;
113 Mov.setOpcode(X86::MOV64rm);
117 Mov.addOperand(MCOperand::createReg(X86::NoRegister));
119 Mov.addOperand(MCOperand::createReg(X86::NoRegister));
120 Out.emitInstruction(Mov, STI);
121}
122
123bool X86::X86MCLFIRewriter::isFSAccess(const MCInst &Inst) {
124 return (mayLoad(Inst) || mayStore(Inst)) &&
125 findFSMemOperand(Inst, *InstInfo) >= 0;
126}
127
128// Rewrite %fs-segment memory accesses to use the virtual thread pointer stored
129// at TPOffset(%r15). The actual memory access is currently unsandboxed because
130// load/store sandboxing is not yet supported. Example rewrites:
131//
132// movq %fs:0, %rax
133// ->
134// movq 16(%r15), %rax
135//
136// movq %fs:(%rdi), %rax
137// ->
138// movq 16(%r15), %rax
139// movq (%rax, %rdi), %rax
140//
141// movq %fs:8(%rdi, %rsi, 2), %rax
142// ->
143// movq 16(%r15), %rax
144// leaq (%rax, %rdi), %rax
145// movq 8(%rax, %rsi, 2), %rax
146void X86::X86MCLFIRewriter::rewriteFSAccess(const MCInst &Inst, MCStreamer &Out,
147 const MCSubtargetInfo &STI) {
148 int MemIdx = findFSMemOperand(Inst, *InstInfo);
149 assert(MemIdx >= 0);
150
151 MCRegister BaseReg = Inst.getOperand(MemIdx + X86::AddrBaseReg).getReg();
152 MCRegister IndexReg = Inst.getOperand(MemIdx + X86::AddrIndexReg).getReg();
153 bool HasBase = BaseReg != X86::NoRegister;
154 bool HasIndex = IndexReg != X86::NoRegister;
155 bool HasDisp = !Inst.getOperand(MemIdx + X86::AddrDisp).isImm() ||
156 Inst.getOperand(MemIdx + X86::AddrDisp).getImm() != 0;
157
158 // %fs:0 -> TPOffset(%r15)
159 if (!HasBase && !HasIndex && !HasDisp) {
160 MCInst Modified(Inst);
161 Modified.getOperand(MemIdx + X86::AddrBaseReg).setReg(LFITPReg);
162 Modified.getOperand(MemIdx + X86::AddrDisp).setImm(TPOffset);
163 Modified.getOperand(MemIdx + X86::AddrSegmentReg).setReg(X86::NoRegister);
164 return Out.emitInstruction(Modified, STI);
165 }
166
167 if (!isGR64OrNone(BaseReg) || !isGR64OrNone(IndexReg) ||
168 BaseReg == X86::RSP || BaseReg == X86::RIP)
169 return error(Inst, "unsupported addressing mode for %fs access");
170
171 const MCInstrDesc &Desc = InstInfo->get(Inst.getOpcode());
172
173 // Reuse operand 0 as the TP temporary when the instruction writes it without
174 // also reading it, otherwise use %r11.
175 MCRegister TPDest = LFIScratchReg;
176 if (MemIdx > 0 && Inst.getOperand(0).isReg()) {
177 MCRegister DestReg = Inst.getOperand(0).getReg();
178 if (Desc.getNumDefs() > 0 &&
179 getX86MCRegisterClass(X86::GR64RegClassID).contains(DestReg) &&
180 !readsRegister(Inst, Desc, DestReg, *RegInfo))
181 TPDest = DestReg;
182 }
183
184 if (TPDest == LFIScratchReg &&
185 readsRegister(Inst, Desc, LFIScratchReg, *RegInfo))
186 return error(Inst, "%fs access reads reserved register %r11");
187
188 emitTPLoad(TPDest, Out, STI);
189
190 // Both slots occupied: the compute base via lea. For example:
191 //
192 // movq %fs:8(%rdi,%rsi,2), %rax
193 // ->
194 // movq 16(%r15), %rax
195 // leaq (%rax,%rdi), %rax
196 // movq 8(%rax,%rsi,2), %rax
197 if (HasBase && HasIndex) {
198 MCInst Lea;
199 Lea.setOpcode(X86::LEA64r);
200 Lea.addOperand(MCOperand::createReg(TPDest));
201 Lea.addOperand(MCOperand::createReg(TPDest));
203 Lea.addOperand(MCOperand::createReg(BaseReg));
205 Lea.addOperand(MCOperand::createReg(X86::NoRegister));
206 Out.emitInstruction(Lea, STI);
207 }
208
209 // Emit the access with TPDest as the new base, and the original base
210 // (offset from %fs) as the new index. For example:
211 //
212 // movq %fs:(%rdi), %rax
213 // ->
214 // movq 16(%r15), %rax
215 // movq (%rax,%rdi), %rax
216 MCInst Modified(Inst);
217 Modified.getOperand(MemIdx + X86::AddrBaseReg).setReg(TPDest);
218 if (HasBase && !HasIndex)
219 Modified.getOperand(MemIdx + X86::AddrIndexReg).setReg(BaseReg);
220 Modified.getOperand(MemIdx + X86::AddrSegmentReg).setReg(X86::NoRegister);
221 Out.emitInstruction(Modified, STI);
222}
223
224void X86::X86MCLFIRewriter::doRewriteInst(const MCInst &Inst, MCStreamer &Out,
225 const MCSubtargetInfo &STI) {
226 if (mayModifyRegister(Inst, LFIBaseReg) || mayModifyRegister(Inst, LFITPReg))
227 return error(Inst, "illegal modification of reserved LFI register");
228
229 if (isSyscall(Inst))
230 return rewriteSyscall(Inst, Out, STI);
231
232 if (isFSAccess(Inst))
233 return rewriteFSAccess(Inst, Out, STI);
234
235 // Pass through all other instructions unchanged.
236 Out.emitInstruction(Inst, STI);
237}
238
240 const MCSubtargetInfo &STI) {
241 // The guard prevents rewrite-recursion when we emit instructions from inside
242 // the rewriter (such instructions should not be rewritten).
243 if (!Enabled || Guard)
244 return false;
245 Guard = true;
246
247 doRewriteInst(Inst, Out, STI);
248
249 Guard = false;
250 return true;
251}
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 emitBundleLock(bool AlignToEnd, const MCSubtargetInfo &STI)
The following instructions are a bundle-locked group.
virtual void emitLabel(MCSymbol *Symbol, SMLoc Loc=SMLoc())
Emit a label for Symbol into the current section.
virtual void emitBundleUnlock(const MCSubtargetInfo &STI)
Ends a bundle-locked group.
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 getMemoryOperandIdx(const MCInstrDesc &Desc)
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