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// leaq .Ltmp(%rip), %r11
73// jmpq *(%r14)
74// .Ltmp:
75void X86::X86MCLFIRewriter::rewriteSyscall(const MCInst &Inst, MCStreamer &Out,
76 const MCSubtargetInfo &STI) {
78
79 // leaq .Ltmp(%rip), %r11
80 MCInst Lea;
81 Lea.setOpcode(X86::LEA64r);
83 Lea.addOperand(MCOperand::createReg(X86::RIP));
85 Lea.addOperand(MCOperand::createReg(X86::NoRegister));
86 Lea.addOperand(
88 Lea.addOperand(MCOperand::createReg(X86::NoRegister));
89 Out.emitInstruction(Lea, STI);
90
91 // jmpq *(%r14)
92 MCInst Jmp;
93 Jmp.setOpcode(X86::JMP64m);
96 Jmp.addOperand(MCOperand::createReg(X86::NoRegister));
98 Jmp.addOperand(MCOperand::createReg(X86::NoRegister));
99 Out.emitInstruction(Jmp, STI);
100
101 Out.emitLabel(Symbol);
102}
103
104// Emit: movq TPOffset(%r15), %Reg
106 const MCSubtargetInfo &STI) {
107 MCInst Mov;
108 Mov.setOpcode(X86::MOV64rm);
112 Mov.addOperand(MCOperand::createReg(X86::NoRegister));
114 Mov.addOperand(MCOperand::createReg(X86::NoRegister));
115 Out.emitInstruction(Mov, STI);
116}
117
118bool X86::X86MCLFIRewriter::isFSAccess(const MCInst &Inst) {
119 return (mayLoad(Inst) || mayStore(Inst)) &&
120 findFSMemOperand(Inst, *InstInfo) >= 0;
121}
122
123// Rewrite %fs-segment memory accesses to use the virtual thread pointer stored
124// at TPOffset(%r15). The actual memory access is currently unsandboxed because
125// load/store sandboxing is not yet supported. Example rewrites:
126//
127// movq %fs:0, %rax
128// ->
129// movq 16(%r15), %rax
130//
131// movq %fs:(%rdi), %rax
132// ->
133// movq 16(%r15), %rax
134// movq (%rax, %rdi), %rax
135//
136// movq %fs:8(%rdi, %rsi, 2), %rax
137// ->
138// movq 16(%r15), %rax
139// leaq (%rax, %rdi), %rax
140// movq 8(%rax, %rsi, 2), %rax
141void X86::X86MCLFIRewriter::rewriteFSAccess(const MCInst &Inst, MCStreamer &Out,
142 const MCSubtargetInfo &STI) {
143 int MemIdx = findFSMemOperand(Inst, *InstInfo);
144 assert(MemIdx >= 0);
145
146 MCRegister BaseReg = Inst.getOperand(MemIdx + X86::AddrBaseReg).getReg();
147 MCRegister IndexReg = Inst.getOperand(MemIdx + X86::AddrIndexReg).getReg();
148 bool HasBase = BaseReg != X86::NoRegister;
149 bool HasIndex = IndexReg != X86::NoRegister;
150 bool HasDisp = !Inst.getOperand(MemIdx + X86::AddrDisp).isImm() ||
151 Inst.getOperand(MemIdx + X86::AddrDisp).getImm() != 0;
152
153 // %fs:0 -> TPOffset(%r15)
154 if (!HasBase && !HasIndex && !HasDisp) {
155 MCInst Modified(Inst);
156 Modified.getOperand(MemIdx + X86::AddrBaseReg).setReg(LFITPReg);
157 Modified.getOperand(MemIdx + X86::AddrDisp).setImm(TPOffset);
158 Modified.getOperand(MemIdx + X86::AddrSegmentReg).setReg(X86::NoRegister);
159 return Out.emitInstruction(Modified, STI);
160 }
161
162 if (!isGR64OrNone(BaseReg) || !isGR64OrNone(IndexReg) ||
163 BaseReg == X86::RSP || BaseReg == X86::RIP)
164 return error(Inst, "unsupported addressing mode for %fs access");
165
166 const MCInstrDesc &Desc = InstInfo->get(Inst.getOpcode());
167
168 // Reuse operand 0 as the TP temporary when the instruction writes it without
169 // also reading it, otherwise use %r11.
170 MCRegister TPDest = LFIScratchReg;
171 if (MemIdx > 0 && Inst.getOperand(0).isReg()) {
172 MCRegister DestReg = Inst.getOperand(0).getReg();
173 if (Desc.getNumDefs() > 0 &&
174 getX86MCRegisterClass(X86::GR64RegClassID).contains(DestReg) &&
175 !readsRegister(Inst, Desc, DestReg, *RegInfo))
176 TPDest = DestReg;
177 }
178
179 if (TPDest == LFIScratchReg &&
180 readsRegister(Inst, Desc, LFIScratchReg, *RegInfo))
181 return error(Inst, "%fs access reads reserved register %r11");
182
183 emitTPLoad(TPDest, Out, STI);
184
185 // Both slots occupied: the compute base via lea. For example:
186 //
187 // movq %fs:8(%rdi,%rsi,2), %rax
188 // ->
189 // movq 16(%r15), %rax
190 // leaq (%rax,%rdi), %rax
191 // movq 8(%rax,%rsi,2), %rax
192 if (HasBase && HasIndex) {
193 MCInst Lea;
194 Lea.setOpcode(X86::LEA64r);
195 Lea.addOperand(MCOperand::createReg(TPDest));
196 Lea.addOperand(MCOperand::createReg(TPDest));
198 Lea.addOperand(MCOperand::createReg(BaseReg));
200 Lea.addOperand(MCOperand::createReg(X86::NoRegister));
201 Out.emitInstruction(Lea, STI);
202 }
203
204 // Emit the access with TPDest as the new base, and the original base
205 // (offset from %fs) as the new index. For example:
206 //
207 // movq %fs:(%rdi), %rax
208 // ->
209 // movq 16(%r15), %rax
210 // movq (%rax,%rdi), %rax
211 MCInst Modified(Inst);
212 Modified.getOperand(MemIdx + X86::AddrBaseReg).setReg(TPDest);
213 if (HasBase && !HasIndex)
214 Modified.getOperand(MemIdx + X86::AddrIndexReg).setReg(BaseReg);
215 Modified.getOperand(MemIdx + X86::AddrSegmentReg).setReg(X86::NoRegister);
216 Out.emitInstruction(Modified, STI);
217}
218
219void X86::X86MCLFIRewriter::doRewriteInst(const MCInst &Inst, MCStreamer &Out,
220 const MCSubtargetInfo &STI) {
221 if (mayModifyRegister(Inst, LFIBaseReg) || mayModifyRegister(Inst, LFITPReg))
222 return error(Inst, "illegal modification of reserved LFI register");
223
224 if (isSyscall(Inst))
225 return rewriteSyscall(Inst, Out, STI);
226
227 if (isFSAccess(Inst))
228 return rewriteFSAccess(Inst, Out, STI);
229
230 // Pass through all other instructions unchanged.
231 Out.emitInstruction(Inst, STI);
232}
233
235 const MCSubtargetInfo &STI) {
236 // The guard prevents rewrite-recursion when we emit instructions from inside
237 // the rewriter (such instructions should not be rewritten).
238 if (!Enabled || Guard)
239 return false;
240 Guard = true;
241
242 doRewriteInst(Inst, Out, STI);
243
244 Guard = false;
245 return true;
246}
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 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