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__anon01a812640311::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__anon01a812640311::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,bool IsVerboseAsm)446 MCTargetStreamer *llvm::createX86AsmTargetStreamer(MCStreamer &S,
447                                                    formatted_raw_ostream &OS,
448                                                    MCInstPrinter *InstPrinter,
449                                                    bool IsVerboseAsm) {
450   // FIXME: This makes it so we textually assemble COFF directives on ELF.
451   // That's kind of nonsensical.
452   return new X86WinCOFFAsmTargetStreamer(S, OS, *InstPrinter);
453 }
454 
455 MCTargetStreamer *
createX86ObjectTargetStreamer(MCStreamer & S,const MCSubtargetInfo & STI)456 llvm::createX86ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) {
457   // No need to register a target streamer.
458   if (!STI.getTargetTriple().isOSBinFormatCOFF())
459     return nullptr;
460   // Registers itself to the MCStreamer.
461   return new X86WinCOFFTargetStreamer(S);
462 }
463