Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/lib/DWARFCFIChecker/DWARFCFIAnalysis.cpp
213766 views
1
//===----------------------------------------------------------------------===//
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
#include "llvm/DWARFCFIChecker/DWARFCFIAnalysis.h"
10
#include "Registers.h"
11
#include "llvm/ADT/ArrayRef.h"
12
#include "llvm/ADT/SmallSet.h"
13
#include "llvm/ADT/SmallVector.h"
14
#include "llvm/ADT/Twine.h"
15
#include "llvm/DWARFCFIChecker/DWARFCFIState.h"
16
#include "llvm/DebugInfo/DWARF/LowLevel/DWARFUnwindTable.h"
17
#include "llvm/MC/MCAsmInfo.h"
18
#include "llvm/MC/MCContext.h"
19
#include "llvm/MC/MCDwarf.h"
20
#include "llvm/MC/MCExpr.h"
21
#include "llvm/MC/MCInst.h"
22
#include "llvm/MC/MCInstrInfo.h"
23
#include "llvm/MC/MCRegister.h"
24
#include "llvm/MC/MCRegisterInfo.h"
25
#include "llvm/MC/MCStreamer.h"
26
#include "llvm/MC/MCSubtargetInfo.h"
27
#include "llvm/MC/TargetRegistry.h"
28
#include "llvm/Support/ErrorHandling.h"
29
#include "llvm/Support/FormatVariadic.h"
30
#include <optional>
31
32
using namespace llvm;
33
34
struct CFARegOffsetInfo {
35
DWARFRegNum Reg;
36
int64_t Offset;
37
38
CFARegOffsetInfo(DWARFRegNum Reg, int64_t Offset)
39
: Reg(Reg), Offset(Offset) {}
40
41
bool operator==(const CFARegOffsetInfo &RHS) const {
42
return Reg == RHS.Reg && Offset == RHS.Offset;
43
}
44
};
45
46
static std::optional<CFARegOffsetInfo>
47
getCFARegOffsetInfo(const dwarf::UnwindRow &UnwindRow) {
48
auto CFALocation = UnwindRow.getCFAValue();
49
if (CFALocation.getLocation() !=
50
dwarf::UnwindLocation::Location::RegPlusOffset)
51
return std::nullopt;
52
53
return CFARegOffsetInfo(CFALocation.getRegister(), CFALocation.getOffset());
54
}
55
56
static SmallSet<DWARFRegNum, 4>
57
getUnwindRuleRegSet(const dwarf::UnwindRow &UnwindRow, DWARFRegNum Reg) {
58
auto MaybeLoc = UnwindRow.getRegisterLocations().getRegisterLocation(Reg);
59
assert(MaybeLoc && "the register should be included in the unwinding row");
60
auto Loc = *MaybeLoc;
61
62
switch (Loc.getLocation()) {
63
case dwarf::UnwindLocation::Location::Unspecified:
64
case dwarf::UnwindLocation::Location::Undefined:
65
case dwarf::UnwindLocation::Location::Constant:
66
case dwarf::UnwindLocation::Location::CFAPlusOffset:
67
// [CFA + offset] does not depend on any register because the CFA value is
68
// constant throughout the entire frame; only the way to calculate it might
69
// change.
70
case dwarf::UnwindLocation::Location::DWARFExpr:
71
// TODO: Expressions are not supported yet, but if they were to be
72
// supported, all the registers used in an expression should extracted and
73
// returned here.
74
return {};
75
case dwarf::UnwindLocation::Location::Same:
76
return {Reg};
77
case dwarf::UnwindLocation::Location::RegPlusOffset:
78
return {Loc.getRegister()};
79
}
80
llvm_unreachable("Unknown dwarf::UnwindLocation::Location enum");
81
}
82
83
DWARFCFIAnalysis::DWARFCFIAnalysis(MCContext *Context, MCInstrInfo const &MCII,
84
bool IsEH,
85
ArrayRef<MCCFIInstruction> Prologue)
86
: State(Context), Context(Context), MCII(MCII),
87
MCRI(Context->getRegisterInfo()), IsEH(IsEH) {
88
89
for (auto LLVMReg : getTrackingRegs(MCRI)) {
90
if (MCRI->get(LLVMReg).IsArtificial || MCRI->get(LLVMReg).IsConstant)
91
continue;
92
93
DWARFRegNum Reg = MCRI->getDwarfRegNum(LLVMReg, IsEH);
94
// TODO: this should be `undefined` instead of `same_value`, but because
95
// initial frame state doesn't have any directives about callee saved
96
// registers, every register is tracked. After initial frame state is
97
// corrected, this should be changed.
98
State.update(MCCFIInstruction::createSameValue(nullptr, Reg));
99
}
100
101
// TODO: Ignoring PC should be in the initial frame state.
102
State.update(MCCFIInstruction::createUndefined(
103
nullptr, MCRI->getDwarfRegNum(MCRI->getProgramCounter(), IsEH)));
104
105
for (auto &&InitialFrameStateCFIDirective :
106
Context->getAsmInfo()->getInitialFrameState())
107
State.update(InitialFrameStateCFIDirective);
108
109
auto MaybeCurrentRow = State.getCurrentUnwindRow();
110
assert(MaybeCurrentRow && "there should be at least one row");
111
auto MaybeCFA = getCFARegOffsetInfo(*MaybeCurrentRow);
112
assert(MaybeCFA &&
113
"the CFA information should be describable in [reg + offset] in here");
114
auto CFA = *MaybeCFA;
115
116
// TODO: CFA register callee value is CFA's value, this should be in initial
117
// frame state.
118
State.update(MCCFIInstruction::createOffset(nullptr, CFA.Reg, 0));
119
120
// Applying the prologue after default assumptions to overwrite them.
121
for (auto &&Directive : Prologue)
122
State.update(Directive);
123
}
124
125
void DWARFCFIAnalysis::update(const MCInst &Inst,
126
ArrayRef<MCCFIInstruction> Directives) {
127
const MCInstrDesc &MCInstInfo = MCII.get(Inst.getOpcode());
128
129
auto MaybePrevRow = State.getCurrentUnwindRow();
130
assert(MaybePrevRow && "the analysis should have initialized the "
131
"state with at least one row by now");
132
auto PrevRow = *MaybePrevRow;
133
134
for (auto &&Directive : Directives)
135
State.update(Directive);
136
137
SmallSet<DWARFRegNum, 4> Writes, Reads;
138
for (unsigned I = 0; I < MCInstInfo.NumImplicitUses; I++)
139
Reads.insert(MCRI->getDwarfRegNum(
140
getSuperReg(MCRI, MCInstInfo.implicit_uses()[I]), IsEH));
141
for (unsigned I = 0; I < MCInstInfo.NumImplicitDefs; I++)
142
Writes.insert(MCRI->getDwarfRegNum(
143
getSuperReg(MCRI, MCInstInfo.implicit_defs()[I]), IsEH));
144
145
for (unsigned I = 0; I < Inst.getNumOperands(); I++) {
146
auto &&Op = Inst.getOperand(I);
147
if (Op.isReg()) {
148
if (I < MCInstInfo.getNumDefs())
149
Writes.insert(
150
MCRI->getDwarfRegNum(getSuperReg(MCRI, Op.getReg()), IsEH));
151
else if (Op.getReg())
152
Reads.insert(
153
MCRI->getDwarfRegNum(getSuperReg(MCRI, Op.getReg()), IsEH));
154
}
155
}
156
157
auto MaybeNextRow = State.getCurrentUnwindRow();
158
assert(MaybeNextRow && "previous row existed, so should the current row");
159
auto NextRow = *MaybeNextRow;
160
161
checkCFADiff(Inst, PrevRow, NextRow, Reads, Writes);
162
163
for (auto LLVMReg : getTrackingRegs(MCRI)) {
164
DWARFRegNum Reg = MCRI->getDwarfRegNum(LLVMReg, IsEH);
165
166
checkRegDiff(Inst, Reg, PrevRow, NextRow, Reads, Writes);
167
}
168
}
169
170
void DWARFCFIAnalysis::checkRegDiff(const MCInst &Inst, DWARFRegNum Reg,
171
const dwarf::UnwindRow &PrevRow,
172
const dwarf::UnwindRow &NextRow,
173
const SmallSet<DWARFRegNum, 4> &Reads,
174
const SmallSet<DWARFRegNum, 4> &Writes) {
175
auto MaybePrevLoc = PrevRow.getRegisterLocations().getRegisterLocation(Reg);
176
auto MaybeNextLoc = NextRow.getRegisterLocations().getRegisterLocation(Reg);
177
178
// All the tracked registers are added during initiation. So if a register is
179
// not added, should stay the same during execution and vice versa.
180
if (!MaybePrevLoc) {
181
assert(!MaybeNextLoc && "the register unwind info suddenly appeared here");
182
return;
183
}
184
assert(MaybeNextLoc && "the register unwind info suddenly vanished here");
185
186
auto PrevLoc = MaybePrevLoc.value();
187
auto NextLoc = MaybeNextLoc.value();
188
189
auto MaybeLLVMReg = MCRI->getLLVMRegNum(Reg, IsEH);
190
if (!MaybeLLVMReg) {
191
if (!(PrevLoc == NextLoc))
192
Context->reportWarning(
193
Inst.getLoc(),
194
formatv("the dwarf register {0} does not have a LLVM number, but its "
195
"unwind info changed. Ignoring this change",
196
Reg));
197
return;
198
}
199
const char *RegName = MCRI->getName(*MaybeLLVMReg);
200
201
// Each case is annotated with its corresponding number as described in
202
// `llvm/include/llvm/DWARFCFIChecker/DWARFCFIAnalysis.h`.
203
204
// TODO: Expressions are not supported yet, but if they were to be supported,
205
// note that structure equality for expressions is defined as follows: Two
206
// expressions are structurally equal if they become the same after you
207
// replace every operand with a placeholder.
208
209
if (PrevLoc == NextLoc) { // Case 1
210
for (DWARFRegNum UsedReg : getUnwindRuleRegSet(PrevRow, Reg))
211
if (Writes.count(UsedReg)) { // Case 1.b
212
auto MaybeLLVMUsedReg = MCRI->getLLVMRegNum(UsedReg, IsEH);
213
assert(MaybeLLVMUsedReg && "instructions will always write to a "
214
"register that has an LLVM register number");
215
Context->reportError(
216
Inst.getLoc(),
217
formatv("changed register {1}, that register {0}'s unwinding rule "
218
"uses, but there is no CFI directives about it",
219
RegName, MCRI->getName(*MaybeLLVMUsedReg)));
220
return;
221
}
222
return; // Case 1.a
223
}
224
// Case 2
225
if (PrevLoc.getLocation() != NextLoc.getLocation()) { // Case 2.a
226
Context->reportWarning(
227
Inst.getLoc(),
228
formatv("validating changes happening to register {0} unwinding "
229
"rule structure is not implemented yet",
230
RegName));
231
return;
232
}
233
auto &&PrevRegSet = getUnwindRuleRegSet(PrevRow, Reg);
234
if (PrevRegSet != getUnwindRuleRegSet(NextRow, Reg)) { // Case 2.b
235
Context->reportWarning(
236
Inst.getLoc(),
237
formatv("validating changes happening to register {0} unwinding "
238
"rule register set is not implemented yet",
239
RegName));
240
return;
241
}
242
// Case 2.c
243
for (DWARFRegNum UsedReg : PrevRegSet)
244
if (Writes.count(UsedReg)) { // Case 2.c.i
245
Context->reportWarning(
246
Inst.getLoc(),
247
formatv("register {0} unwinding rule's offset is changed, and one of "
248
"the rule's registers is modified, but validating the "
249
"modification amount is not implemented yet",
250
RegName));
251
return;
252
}
253
// Case 2.c.ii
254
Context->reportError(
255
Inst.getLoc(), formatv("register {0} unwinding rule's offset is changed, "
256
"but not any of the rule's registers are modified",
257
RegName));
258
}
259
260
void DWARFCFIAnalysis::checkCFADiff(const MCInst &Inst,
261
const dwarf::UnwindRow &PrevRow,
262
const dwarf::UnwindRow &NextRow,
263
const SmallSet<DWARFRegNum, 4> &Reads,
264
const SmallSet<DWARFRegNum, 4> &Writes) {
265
266
auto MaybePrevCFA = getCFARegOffsetInfo(PrevRow);
267
auto MaybeNextCFA = getCFARegOffsetInfo(NextRow);
268
269
if (!MaybePrevCFA) {
270
if (MaybeNextCFA) {
271
Context->reportWarning(Inst.getLoc(),
272
"CFA rule changed to [reg + offset], this "
273
"transition will not be checked");
274
return;
275
}
276
277
Context->reportWarning(Inst.getLoc(),
278
"CFA rule is not [reg + offset], not checking it");
279
return;
280
}
281
282
if (!MaybeNextCFA) {
283
Context->reportWarning(Inst.getLoc(),
284
"CFA rule changed from [reg + offset], this "
285
"transition will not be checked");
286
return;
287
}
288
289
auto PrevCFA = *MaybePrevCFA;
290
auto NextCFA = *MaybeNextCFA;
291
292
auto MaybeLLVMPrevReg = MCRI->getLLVMRegNum(PrevCFA.Reg, IsEH);
293
const char *PrevCFARegName =
294
MaybeLLVMPrevReg ? MCRI->getName(*MaybeLLVMPrevReg) : "";
295
auto MaybeLLVMNextReg = MCRI->getLLVMRegNum(NextCFA.Reg, IsEH);
296
const char *NextCFARegName =
297
MaybeLLVMNextReg ? MCRI->getName(*MaybeLLVMNextReg) : "";
298
299
if (PrevCFA == NextCFA) { // Case 1
300
if (!Writes.count(PrevCFA.Reg)) // Case 1.a
301
return;
302
// Case 1.b
303
Context->reportError(
304
Inst.getLoc(),
305
formatv("modified CFA register {0} but not changed CFA rule",
306
PrevCFARegName));
307
return;
308
}
309
310
if (PrevCFA.Reg != NextCFA.Reg) { // Case 2.b
311
Context->reportWarning(
312
Inst.getLoc(),
313
formatv("CFA register changed from register {0} to register {1}, "
314
"validating this change is not implemented yet",
315
PrevCFARegName, NextCFARegName));
316
return;
317
}
318
// Case 2.c
319
if (Writes.count(PrevCFA.Reg)) { // Case 2.c.i
320
Context->reportWarning(
321
Inst.getLoc(), formatv("CFA offset is changed from {0} to {1}, and CFA "
322
"register {2} is modified, but validating the "
323
"modification amount is not implemented yet",
324
PrevCFA.Offset, NextCFA.Offset, PrevCFARegName));
325
return;
326
}
327
// Case 2.c.ii
328
Context->reportError(
329
Inst.getLoc(),
330
formatv("did not modify CFA register {0} but changed CFA rule",
331
PrevCFARegName));
332
}
333
334