Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFTargetStreamer.cpp
35294 views
1
//===-- X86WinCOFFTargetStreamer.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
#include "X86MCTargetDesc.h"
10
#include "X86TargetStreamer.h"
11
#include "llvm/DebugInfo/CodeView/CodeView.h"
12
#include "llvm/MC/MCCodeView.h"
13
#include "llvm/MC/MCContext.h"
14
#include "llvm/MC/MCInstPrinter.h"
15
#include "llvm/MC/MCRegisterInfo.h"
16
#include "llvm/MC/MCSubtargetInfo.h"
17
#include "llvm/MC/MCSymbol.h"
18
#include "llvm/Support/FormattedStream.h"
19
20
using namespace llvm;
21
using namespace llvm::codeview;
22
23
namespace {
24
/// Implements Windows x86-only directives for assembly emission.
25
class X86WinCOFFAsmTargetStreamer : public X86TargetStreamer {
26
formatted_raw_ostream &OS;
27
MCInstPrinter &InstPrinter;
28
29
public:
30
X86WinCOFFAsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS,
31
MCInstPrinter &InstPrinter)
32
: X86TargetStreamer(S), OS(OS), InstPrinter(InstPrinter) {}
33
34
bool emitFPOProc(const MCSymbol *ProcSym, unsigned ParamsSize,
35
SMLoc L) override;
36
bool emitFPOEndPrologue(SMLoc L) override;
37
bool emitFPOEndProc(SMLoc L) override;
38
bool emitFPOData(const MCSymbol *ProcSym, SMLoc L) override;
39
bool emitFPOPushReg(unsigned Reg, SMLoc L) override;
40
bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) override;
41
bool emitFPOStackAlign(unsigned Align, SMLoc L) override;
42
bool emitFPOSetFrame(unsigned Reg, SMLoc L) override;
43
};
44
45
/// Represents a single FPO directive.
46
struct FPOInstruction {
47
MCSymbol *Label;
48
enum Operation {
49
PushReg,
50
StackAlloc,
51
StackAlign,
52
SetFrame,
53
} Op;
54
unsigned RegOrOffset;
55
};
56
57
struct FPOData {
58
const MCSymbol *Function = nullptr;
59
MCSymbol *Begin = nullptr;
60
MCSymbol *PrologueEnd = nullptr;
61
MCSymbol *End = nullptr;
62
unsigned ParamsSize = 0;
63
64
SmallVector<FPOInstruction, 5> Instructions;
65
};
66
67
/// Implements Windows x86-only directives for object emission.
68
class X86WinCOFFTargetStreamer : public X86TargetStreamer {
69
/// Map from function symbol to its FPO data.
70
DenseMap<const MCSymbol *, std::unique_ptr<FPOData>> AllFPOData;
71
72
/// Current FPO data created by .cv_fpo_proc.
73
std::unique_ptr<FPOData> CurFPOData;
74
75
bool haveOpenFPOData() { return !!CurFPOData; }
76
77
/// Diagnoses an error at L if we are not in an FPO prologue. Return true on
78
/// error.
79
bool checkInFPOPrologue(SMLoc L);
80
81
MCSymbol *emitFPOLabel();
82
83
MCContext &getContext() { return getStreamer().getContext(); }
84
85
public:
86
X86WinCOFFTargetStreamer(MCStreamer &S) : X86TargetStreamer(S) {}
87
88
bool emitFPOProc(const MCSymbol *ProcSym, unsigned ParamsSize,
89
SMLoc L) override;
90
bool emitFPOEndPrologue(SMLoc L) override;
91
bool emitFPOEndProc(SMLoc L) override;
92
bool emitFPOData(const MCSymbol *ProcSym, SMLoc L) override;
93
bool emitFPOPushReg(unsigned Reg, SMLoc L) override;
94
bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) override;
95
bool emitFPOStackAlign(unsigned Align, SMLoc L) override;
96
bool emitFPOSetFrame(unsigned Reg, SMLoc L) override;
97
};
98
} // end namespace
99
100
bool X86WinCOFFAsmTargetStreamer::emitFPOProc(const MCSymbol *ProcSym,
101
unsigned ParamsSize, SMLoc L) {
102
OS << "\t.cv_fpo_proc\t";
103
ProcSym->print(OS, getStreamer().getContext().getAsmInfo());
104
OS << ' ' << ParamsSize << '\n';
105
return false;
106
}
107
108
bool X86WinCOFFAsmTargetStreamer::emitFPOEndPrologue(SMLoc L) {
109
OS << "\t.cv_fpo_endprologue\n";
110
return false;
111
}
112
113
bool X86WinCOFFAsmTargetStreamer::emitFPOEndProc(SMLoc L) {
114
OS << "\t.cv_fpo_endproc\n";
115
return false;
116
}
117
118
bool X86WinCOFFAsmTargetStreamer::emitFPOData(const MCSymbol *ProcSym,
119
SMLoc L) {
120
OS << "\t.cv_fpo_data\t";
121
ProcSym->print(OS, getStreamer().getContext().getAsmInfo());
122
OS << '\n';
123
return false;
124
}
125
126
bool X86WinCOFFAsmTargetStreamer::emitFPOPushReg(unsigned Reg, SMLoc L) {
127
OS << "\t.cv_fpo_pushreg\t";
128
InstPrinter.printRegName(OS, Reg);
129
OS << '\n';
130
return false;
131
}
132
133
bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc,
134
SMLoc L) {
135
OS << "\t.cv_fpo_stackalloc\t" << StackAlloc << '\n';
136
return false;
137
}
138
139
bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlign(unsigned Align, SMLoc L) {
140
OS << "\t.cv_fpo_stackalign\t" << Align << '\n';
141
return false;
142
}
143
144
bool X86WinCOFFAsmTargetStreamer::emitFPOSetFrame(unsigned Reg, SMLoc L) {
145
OS << "\t.cv_fpo_setframe\t";
146
InstPrinter.printRegName(OS, Reg);
147
OS << '\n';
148
return false;
149
}
150
151
bool X86WinCOFFTargetStreamer::checkInFPOPrologue(SMLoc L) {
152
if (!haveOpenFPOData() || CurFPOData->PrologueEnd) {
153
getContext().reportError(
154
L,
155
"directive must appear between .cv_fpo_proc and .cv_fpo_endprologue");
156
return true;
157
}
158
return false;
159
}
160
161
MCSymbol *X86WinCOFFTargetStreamer::emitFPOLabel() {
162
MCSymbol *Label = getContext().createTempSymbol("cfi", true);
163
getStreamer().emitLabel(Label);
164
return Label;
165
}
166
167
bool X86WinCOFFTargetStreamer::emitFPOProc(const MCSymbol *ProcSym,
168
unsigned ParamsSize, SMLoc L) {
169
if (haveOpenFPOData()) {
170
getContext().reportError(
171
L, "opening new .cv_fpo_proc before closing previous frame");
172
return true;
173
}
174
CurFPOData = std::make_unique<FPOData>();
175
CurFPOData->Function = ProcSym;
176
CurFPOData->Begin = emitFPOLabel();
177
CurFPOData->ParamsSize = ParamsSize;
178
return false;
179
}
180
181
bool X86WinCOFFTargetStreamer::emitFPOEndProc(SMLoc L) {
182
if (!haveOpenFPOData()) {
183
getContext().reportError(L, ".cv_fpo_endproc must appear after .cv_proc");
184
return true;
185
}
186
if (!CurFPOData->PrologueEnd) {
187
// Complain if there were prologue setup instructions but no end prologue.
188
if (!CurFPOData->Instructions.empty()) {
189
getContext().reportError(L, "missing .cv_fpo_endprologue");
190
CurFPOData->Instructions.clear();
191
}
192
193
// Claim there is a zero-length prologue to make the label math work out
194
// later.
195
CurFPOData->PrologueEnd = CurFPOData->Begin;
196
}
197
198
CurFPOData->End = emitFPOLabel();
199
const MCSymbol *Fn = CurFPOData->Function;
200
AllFPOData.insert({Fn, std::move(CurFPOData)});
201
return false;
202
}
203
204
bool X86WinCOFFTargetStreamer::emitFPOSetFrame(unsigned Reg, SMLoc L) {
205
if (checkInFPOPrologue(L))
206
return true;
207
FPOInstruction Inst;
208
Inst.Label = emitFPOLabel();
209
Inst.Op = FPOInstruction::SetFrame;
210
Inst.RegOrOffset = Reg;
211
CurFPOData->Instructions.push_back(Inst);
212
return false;
213
}
214
215
bool X86WinCOFFTargetStreamer::emitFPOPushReg(unsigned Reg, SMLoc L) {
216
if (checkInFPOPrologue(L))
217
return true;
218
FPOInstruction Inst;
219
Inst.Label = emitFPOLabel();
220
Inst.Op = FPOInstruction::PushReg;
221
Inst.RegOrOffset = Reg;
222
CurFPOData->Instructions.push_back(Inst);
223
return false;
224
}
225
226
bool X86WinCOFFTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) {
227
if (checkInFPOPrologue(L))
228
return true;
229
FPOInstruction Inst;
230
Inst.Label = emitFPOLabel();
231
Inst.Op = FPOInstruction::StackAlloc;
232
Inst.RegOrOffset = StackAlloc;
233
CurFPOData->Instructions.push_back(Inst);
234
return false;
235
}
236
237
bool X86WinCOFFTargetStreamer::emitFPOStackAlign(unsigned Align, SMLoc L) {
238
if (checkInFPOPrologue(L))
239
return true;
240
if (llvm::none_of(CurFPOData->Instructions, [](const FPOInstruction &Inst) {
241
return Inst.Op == FPOInstruction::SetFrame;
242
})) {
243
getContext().reportError(
244
L, "a frame register must be established before aligning the stack");
245
return true;
246
}
247
FPOInstruction Inst;
248
Inst.Label = emitFPOLabel();
249
Inst.Op = FPOInstruction::StackAlign;
250
Inst.RegOrOffset = Align;
251
CurFPOData->Instructions.push_back(Inst);
252
return false;
253
}
254
255
bool X86WinCOFFTargetStreamer::emitFPOEndPrologue(SMLoc L) {
256
if (checkInFPOPrologue(L))
257
return true;
258
CurFPOData->PrologueEnd = emitFPOLabel();
259
return false;
260
}
261
262
namespace {
263
struct RegSaveOffset {
264
RegSaveOffset(unsigned Reg, unsigned Offset) : Reg(Reg), Offset(Offset) {}
265
266
unsigned Reg = 0;
267
unsigned Offset = 0;
268
};
269
270
struct FPOStateMachine {
271
explicit FPOStateMachine(const FPOData *FPO) : FPO(FPO) {}
272
273
const FPOData *FPO = nullptr;
274
unsigned FrameReg = 0;
275
unsigned FrameRegOff = 0;
276
unsigned CurOffset = 0;
277
unsigned LocalSize = 0;
278
unsigned SavedRegSize = 0;
279
unsigned StackOffsetBeforeAlign = 0;
280
unsigned StackAlign = 0;
281
unsigned Flags = 0; // FIXME: Set HasSEH / HasEH.
282
283
SmallString<128> FrameFunc;
284
285
SmallVector<RegSaveOffset, 4> RegSaveOffsets;
286
287
void emitFrameDataRecord(MCStreamer &OS, MCSymbol *Label);
288
};
289
} // end namespace
290
291
static Printable printFPOReg(const MCRegisterInfo *MRI, unsigned LLVMReg) {
292
return Printable([MRI, LLVMReg](raw_ostream &OS) {
293
switch (LLVMReg) {
294
// MSVC only seems to emit symbolic register names for EIP, EBP, and ESP,
295
// but the format seems to support more than that, so we emit them.
296
case X86::EAX: OS << "$eax"; break;
297
case X86::EBX: OS << "$ebx"; break;
298
case X86::ECX: OS << "$ecx"; break;
299
case X86::EDX: OS << "$edx"; break;
300
case X86::EDI: OS << "$edi"; break;
301
case X86::ESI: OS << "$esi"; break;
302
case X86::ESP: OS << "$esp"; break;
303
case X86::EBP: OS << "$ebp"; break;
304
case X86::EIP: OS << "$eip"; break;
305
// Otherwise, get the codeview register number and print $N.
306
default:
307
OS << '$' << MRI->getCodeViewRegNum(LLVMReg);
308
break;
309
}
310
});
311
}
312
313
void FPOStateMachine::emitFrameDataRecord(MCStreamer &OS, MCSymbol *Label) {
314
unsigned CurFlags = Flags;
315
if (Label == FPO->Begin)
316
CurFlags |= FrameData::IsFunctionStart;
317
318
// Compute the new FrameFunc string.
319
FrameFunc.clear();
320
raw_svector_ostream FuncOS(FrameFunc);
321
const MCRegisterInfo *MRI = OS.getContext().getRegisterInfo();
322
assert((StackAlign == 0 || FrameReg != 0) &&
323
"cannot align stack without frame reg");
324
StringRef CFAVar = StackAlign == 0 ? "$T0" : "$T1";
325
326
if (FrameReg) {
327
// CFA is FrameReg + FrameRegOff.
328
FuncOS << CFAVar << ' ' << printFPOReg(MRI, FrameReg) << ' ' << FrameRegOff
329
<< " + = ";
330
331
// Assign $T0, the VFRAME register, the value of ESP after it is aligned.
332
// Starting from the CFA, we subtract the size of all pushed registers, and
333
// align the result. While we don't store any CSRs in this area, $T0 is used
334
// by S_DEFRANGE_FRAMEPOINTER_REL records to find local variables.
335
if (StackAlign) {
336
FuncOS << "$T0 " << CFAVar << ' ' << StackOffsetBeforeAlign << " - "
337
<< StackAlign << " @ = ";
338
}
339
} else {
340
// The address of return address is ESP + CurOffset, but we use .raSearch to
341
// match MSVC. This seems to ask the debugger to subtract some combination
342
// of LocalSize and SavedRegSize from ESP and grovel around in that memory
343
// to find the address of a plausible return address.
344
FuncOS << CFAVar << " .raSearch = ";
345
}
346
347
// Caller's $eip should be dereferenced CFA, and $esp should be CFA plus 4.
348
FuncOS << "$eip " << CFAVar << " ^ = ";
349
FuncOS << "$esp " << CFAVar << " 4 + = ";
350
351
// Each saved register is stored at an unchanging negative CFA offset.
352
for (RegSaveOffset RO : RegSaveOffsets)
353
FuncOS << printFPOReg(MRI, RO.Reg) << ' ' << CFAVar << ' ' << RO.Offset
354
<< " - ^ = ";
355
356
// Add it to the CV string table.
357
CodeViewContext &CVCtx = OS.getContext().getCVContext();
358
unsigned FrameFuncStrTabOff = CVCtx.addToStringTable(FuncOS.str()).second;
359
360
// MSVC has only ever been observed to emit a MaxStackSize of zero.
361
unsigned MaxStackSize = 0;
362
363
// The FrameData record format is:
364
// ulittle32_t RvaStart;
365
// ulittle32_t CodeSize;
366
// ulittle32_t LocalSize;
367
// ulittle32_t ParamsSize;
368
// ulittle32_t MaxStackSize;
369
// ulittle32_t FrameFunc; // String table offset
370
// ulittle16_t PrologSize;
371
// ulittle16_t SavedRegsSize;
372
// ulittle32_t Flags;
373
374
OS.emitAbsoluteSymbolDiff(Label, FPO->Begin, 4); // RvaStart
375
OS.emitAbsoluteSymbolDiff(FPO->End, Label, 4); // CodeSize
376
OS.emitInt32(LocalSize);
377
OS.emitInt32(FPO->ParamsSize);
378
OS.emitInt32(MaxStackSize);
379
OS.emitInt32(FrameFuncStrTabOff); // FrameFunc
380
OS.emitAbsoluteSymbolDiff(FPO->PrologueEnd, Label, 2);
381
OS.emitInt16(SavedRegSize);
382
OS.emitInt32(CurFlags);
383
}
384
385
/// Compute and emit the real CodeView FrameData subsection.
386
bool X86WinCOFFTargetStreamer::emitFPOData(const MCSymbol *ProcSym, SMLoc L) {
387
MCStreamer &OS = getStreamer();
388
MCContext &Ctx = OS.getContext();
389
390
auto I = AllFPOData.find(ProcSym);
391
if (I == AllFPOData.end()) {
392
Ctx.reportError(L, Twine("no FPO data found for symbol ") +
393
ProcSym->getName());
394
return true;
395
}
396
const FPOData *FPO = I->second.get();
397
assert(FPO->Begin && FPO->End && FPO->PrologueEnd && "missing FPO label");
398
399
MCSymbol *FrameBegin = Ctx.createTempSymbol(),
400
*FrameEnd = Ctx.createTempSymbol();
401
402
OS.emitInt32(unsigned(DebugSubsectionKind::FrameData));
403
OS.emitAbsoluteSymbolDiff(FrameEnd, FrameBegin, 4);
404
OS.emitLabel(FrameBegin);
405
406
// Start with the RVA of the function in question.
407
OS.emitValue(MCSymbolRefExpr::create(FPO->Function,
408
MCSymbolRefExpr::VK_COFF_IMGREL32, Ctx),
409
4);
410
411
// Emit a sequence of FrameData records.
412
FPOStateMachine FSM(FPO);
413
414
FSM.emitFrameDataRecord(OS, FPO->Begin);
415
for (const FPOInstruction &Inst : FPO->Instructions) {
416
switch (Inst.Op) {
417
case FPOInstruction::PushReg:
418
FSM.CurOffset += 4;
419
FSM.SavedRegSize += 4;
420
FSM.RegSaveOffsets.push_back({Inst.RegOrOffset, FSM.CurOffset});
421
break;
422
case FPOInstruction::SetFrame:
423
FSM.FrameReg = Inst.RegOrOffset;
424
FSM.FrameRegOff = FSM.CurOffset;
425
break;
426
case FPOInstruction::StackAlign:
427
FSM.StackOffsetBeforeAlign = FSM.CurOffset;
428
FSM.StackAlign = Inst.RegOrOffset;
429
break;
430
case FPOInstruction::StackAlloc:
431
FSM.CurOffset += Inst.RegOrOffset;
432
FSM.LocalSize += Inst.RegOrOffset;
433
// No need to emit FrameData for stack allocations with a frame pointer.
434
if (FSM.FrameReg)
435
continue;
436
break;
437
}
438
FSM.emitFrameDataRecord(OS, Inst.Label);
439
}
440
441
OS.emitValueToAlignment(Align(4), 0);
442
OS.emitLabel(FrameEnd);
443
return false;
444
}
445
446
MCTargetStreamer *llvm::createX86AsmTargetStreamer(MCStreamer &S,
447
formatted_raw_ostream &OS,
448
MCInstPrinter *InstPrinter) {
449
// FIXME: This makes it so we textually assemble COFF directives on ELF.
450
// That's kind of nonsensical.
451
return new X86WinCOFFAsmTargetStreamer(S, OS, *InstPrinter);
452
}
453
454
MCTargetStreamer *
455
llvm::createX86ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) {
456
// No need to register a target streamer.
457
if (!STI.getTargetTriple().isOSBinFormatCOFF())
458
return nullptr;
459
// Registers itself to the MCStreamer.
460
return new X86WinCOFFTargetStreamer(S);
461
}
462
463