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