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:
X86WinCOFFAsmTargetStreamer(MCStreamer & S,formatted_raw_ostream & OS,MCInstPrinter & InstPrinter)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
haveOpenFPOData()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
getContext()83 MCContext &getContext() { return getStreamer().getContext(); }
84
85 public:
X86WinCOFFTargetStreamer(MCStreamer & S)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
emitFPOProc(const MCSymbol * ProcSym,unsigned ParamsSize,SMLoc L)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
emitFPOEndPrologue(SMLoc L)108 bool X86WinCOFFAsmTargetStreamer::emitFPOEndPrologue(SMLoc L) {
109 OS << "\t.cv_fpo_endprologue\n";
110 return false;
111 }
112
emitFPOEndProc(SMLoc L)113 bool X86WinCOFFAsmTargetStreamer::emitFPOEndProc(SMLoc L) {
114 OS << "\t.cv_fpo_endproc\n";
115 return false;
116 }
117
emitFPOData(const MCSymbol * ProcSym,SMLoc L)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
emitFPOPushReg(unsigned Reg,SMLoc L)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
emitFPOStackAlloc(unsigned StackAlloc,SMLoc L)133 bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc,
134 SMLoc L) {
135 OS << "\t.cv_fpo_stackalloc\t" << StackAlloc << '\n';
136 return false;
137 }
138
emitFPOStackAlign(unsigned Align,SMLoc L)139 bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlign(unsigned Align, SMLoc L) {
140 OS << "\t.cv_fpo_stackalign\t" << Align << '\n';
141 return false;
142 }
143
emitFPOSetFrame(unsigned Reg,SMLoc L)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
checkInFPOPrologue(SMLoc L)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
emitFPOLabel()161 MCSymbol *X86WinCOFFTargetStreamer::emitFPOLabel() {
162 MCSymbol *Label = getContext().createTempSymbol("cfi", true);
163 getStreamer().emitLabel(Label);
164 return Label;
165 }
166
emitFPOProc(const MCSymbol * ProcSym,unsigned ParamsSize,SMLoc L)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
emitFPOEndProc(SMLoc L)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
emitFPOSetFrame(unsigned Reg,SMLoc L)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
emitFPOPushReg(unsigned Reg,SMLoc L)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
emitFPOStackAlloc(unsigned StackAlloc,SMLoc L)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
emitFPOStackAlign(unsigned Align,SMLoc L)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
emitFPOEndPrologue(SMLoc L)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 {
RegSaveOffset__anone4092d9c0311::RegSaveOffset264 RegSaveOffset(unsigned Reg, unsigned Offset) : Reg(Reg), Offset(Offset) {}
265
266 unsigned Reg = 0;
267 unsigned Offset = 0;
268 };
269
270 struct FPOStateMachine {
FPOStateMachine__anone4092d9c0311::FPOStateMachine271 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
printFPOReg(const MCRegisterInfo * MRI,unsigned LLVMReg)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
emitFrameDataRecord(MCStreamer & OS,MCSymbol * Label)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.
emitFPOData(const MCSymbol * ProcSym,SMLoc L)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
createX86AsmTargetStreamer(MCStreamer & S,formatted_raw_ostream & OS,MCInstPrinter * InstPrinter)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 *
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