• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- X86WinCOFFTargetStreamer.cpp ----------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "X86MCTargetDesc.h"
11 #include "X86TargetStreamer.h"
12 #include "llvm/DebugInfo/CodeView/CodeView.h"
13 #include "llvm/MC/MCCodeView.h"
14 #include "llvm/MC/MCContext.h"
15 #include "llvm/MC/MCInstPrinter.h"
16 #include "llvm/MC/MCRegisterInfo.h"
17 #include "llvm/MC/MCSubtargetInfo.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 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     SetFrame,
51   } Op;
52   unsigned RegOrOffset;
53 };
54 
55 struct FPOData {
56   const MCSymbol *Function = nullptr;
57   MCSymbol *Begin = nullptr;
58   MCSymbol *PrologueEnd = nullptr;
59   MCSymbol *End = nullptr;
60   unsigned ParamsSize = 0;
61 
62   SmallVector<FPOInstruction, 5> Instructions;
63 };
64 
65 /// Implements Windows x86-only directives for object emission.
66 class X86WinCOFFTargetStreamer : public X86TargetStreamer {
67   /// Map from function symbol to its FPO data.
68   DenseMap<const MCSymbol *, std::unique_ptr<FPOData>> AllFPOData;
69 
70   /// Current FPO data created by .cv_fpo_proc.
71   std::unique_ptr<FPOData> CurFPOData;
72 
haveOpenFPOData()73   bool haveOpenFPOData() { return !!CurFPOData; }
74 
75   /// Diagnoses an error at L if we are not in an FPO prologue. Return true on
76   /// error.
77   bool checkInFPOPrologue(SMLoc L);
78 
79   MCSymbol *emitFPOLabel();
80 
getContext()81   MCContext &getContext() { return getStreamer().getContext(); }
82 
83 public:
X86WinCOFFTargetStreamer(MCStreamer & S)84   X86WinCOFFTargetStreamer(MCStreamer &S) : X86TargetStreamer(S) {}
85 
86   bool emitFPOProc(const MCSymbol *ProcSym, unsigned ParamsSize,
87                    SMLoc L) override;
88   bool emitFPOEndPrologue(SMLoc L) override;
89   bool emitFPOEndProc(SMLoc L) override;
90   bool emitFPOData(const MCSymbol *ProcSym, SMLoc L) override;
91   bool emitFPOPushReg(unsigned Reg, SMLoc L) override;
92   bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) override;
93   bool emitFPOSetFrame(unsigned Reg, SMLoc L) override;
94 };
95 } // end namespace
96 
emitFPOProc(const MCSymbol * ProcSym,unsigned ParamsSize,SMLoc L)97 bool X86WinCOFFAsmTargetStreamer::emitFPOProc(const MCSymbol *ProcSym,
98                                               unsigned ParamsSize, SMLoc L) {
99   OS << "\t.cv_fpo_proc\t";
100   ProcSym->print(OS, getStreamer().getContext().getAsmInfo());
101   OS << ' ' << ParamsSize << '\n';
102   return false;
103 }
104 
emitFPOEndPrologue(SMLoc L)105 bool X86WinCOFFAsmTargetStreamer::emitFPOEndPrologue(SMLoc L) {
106   OS << "\t.cv_fpo_endprologue\n";
107   return false;
108 }
109 
emitFPOEndProc(SMLoc L)110 bool X86WinCOFFAsmTargetStreamer::emitFPOEndProc(SMLoc L) {
111   OS << "\t.cv_fpo_endproc\n";
112   return false;
113 }
114 
emitFPOData(const MCSymbol * ProcSym,SMLoc L)115 bool X86WinCOFFAsmTargetStreamer::emitFPOData(const MCSymbol *ProcSym,
116                                               SMLoc L) {
117   OS << "\t.cv_fpo_data\t";
118   ProcSym->print(OS, getStreamer().getContext().getAsmInfo());
119   OS << '\n';
120   return false;
121 }
122 
emitFPOPushReg(unsigned Reg,SMLoc L)123 bool X86WinCOFFAsmTargetStreamer::emitFPOPushReg(unsigned Reg, SMLoc L) {
124   OS << "\t.cv_fpo_pushreg\t";
125   InstPrinter.printRegName(OS, Reg);
126   OS << '\n';
127   return false;
128 }
129 
emitFPOStackAlloc(unsigned StackAlloc,SMLoc L)130 bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc,
131                                                     SMLoc L) {
132   OS << "\t.cv_fpo_stackalloc\t" << StackAlloc << '\n';
133   return false;
134 }
135 
emitFPOSetFrame(unsigned Reg,SMLoc L)136 bool X86WinCOFFAsmTargetStreamer::emitFPOSetFrame(unsigned Reg, SMLoc L) {
137   OS << "\t.cv_fpo_setframe\t";
138   InstPrinter.printRegName(OS, Reg);
139   OS << '\n';
140   return false;
141 }
142 
checkInFPOPrologue(SMLoc L)143 bool X86WinCOFFTargetStreamer::checkInFPOPrologue(SMLoc L) {
144   if (!haveOpenFPOData() || CurFPOData->PrologueEnd) {
145     getContext().reportError(
146         L,
147         "directive must appear between .cv_fpo_proc and .cv_fpo_endprologue");
148     return true;
149   }
150   return false;
151 }
152 
emitFPOLabel()153 MCSymbol *X86WinCOFFTargetStreamer::emitFPOLabel() {
154   MCSymbol *Label = getContext().createTempSymbol("cfi", true);
155   getStreamer().EmitLabel(Label);
156   return Label;
157 }
158 
emitFPOProc(const MCSymbol * ProcSym,unsigned ParamsSize,SMLoc L)159 bool X86WinCOFFTargetStreamer::emitFPOProc(const MCSymbol *ProcSym,
160                                            unsigned ParamsSize, SMLoc L) {
161   if (haveOpenFPOData()) {
162     getContext().reportError(
163         L, "opening new .cv_fpo_proc before closing previous frame");
164     return true;
165   }
166   CurFPOData = llvm::make_unique<FPOData>();
167   CurFPOData->Function = ProcSym;
168   CurFPOData->Begin = emitFPOLabel();
169   CurFPOData->ParamsSize = ParamsSize;
170   return false;
171 }
172 
emitFPOEndProc(SMLoc L)173 bool X86WinCOFFTargetStreamer::emitFPOEndProc(SMLoc L) {
174   if (!haveOpenFPOData()) {
175     getContext().reportError(L, ".cv_fpo_endproc must appear after .cv_proc");
176     return true;
177   }
178   if (!CurFPOData->PrologueEnd) {
179     // Complain if there were prologue setup instructions but no end prologue.
180     if (!CurFPOData->Instructions.empty()) {
181       getContext().reportError(L, "missing .cv_fpo_endprologue");
182       CurFPOData->Instructions.clear();
183     }
184 
185     // Claim there is a zero-length prologue to make the label math work out
186     // later.
187     CurFPOData->PrologueEnd = CurFPOData->Begin;
188   }
189 
190   CurFPOData->End = emitFPOLabel();
191   const MCSymbol *Fn = CurFPOData->Function;
192   AllFPOData.insert({Fn, std::move(CurFPOData)});
193   return false;
194 }
195 
emitFPOSetFrame(unsigned Reg,SMLoc L)196 bool X86WinCOFFTargetStreamer::emitFPOSetFrame(unsigned Reg, SMLoc L) {
197   if (checkInFPOPrologue(L))
198     return true;
199   FPOInstruction Inst;
200   Inst.Label = emitFPOLabel();
201   Inst.Op = FPOInstruction::SetFrame;
202   Inst.RegOrOffset = Reg;
203   CurFPOData->Instructions.push_back(Inst);
204   return false;
205 }
206 
emitFPOPushReg(unsigned Reg,SMLoc L)207 bool X86WinCOFFTargetStreamer::emitFPOPushReg(unsigned Reg, SMLoc L) {
208   if (checkInFPOPrologue(L))
209     return true;
210   FPOInstruction Inst;
211   Inst.Label = emitFPOLabel();
212   Inst.Op = FPOInstruction::PushReg;
213   Inst.RegOrOffset = Reg;
214   CurFPOData->Instructions.push_back(Inst);
215   return false;
216 }
217 
emitFPOStackAlloc(unsigned StackAlloc,SMLoc L)218 bool X86WinCOFFTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) {
219   if (checkInFPOPrologue(L))
220     return true;
221   FPOInstruction Inst;
222   Inst.Label = emitFPOLabel();
223   Inst.Op = FPOInstruction::StackAlloc;
224   Inst.RegOrOffset = StackAlloc;
225   CurFPOData->Instructions.push_back(Inst);
226   return false;
227 }
228 
emitFPOEndPrologue(SMLoc L)229 bool X86WinCOFFTargetStreamer::emitFPOEndPrologue(SMLoc L) {
230   if (checkInFPOPrologue(L))
231     return true;
232   CurFPOData->PrologueEnd = emitFPOLabel();
233   return false;
234 }
235 
236 namespace {
237 struct RegSaveOffset {
RegSaveOffset__anonc9a401df0211::RegSaveOffset238   RegSaveOffset(unsigned Reg, unsigned Offset) : Reg(Reg), Offset(Offset) {}
239 
240   unsigned Reg = 0;
241   unsigned Offset = 0;
242 };
243 
244 struct FPOStateMachine {
FPOStateMachine__anonc9a401df0211::FPOStateMachine245   explicit FPOStateMachine(const FPOData *FPO) : FPO(FPO) {}
246 
247   const FPOData *FPO = nullptr;
248   unsigned FrameReg = 0;
249   unsigned FrameRegOff = 0;
250   unsigned CurOffset = 0;
251   unsigned LocalSize = 0;
252   unsigned SavedRegSize = 0;
253   unsigned Flags = 0; // FIXME: Set HasSEH / HasEH.
254 
255   SmallString<128> FrameFunc;
256 
257   SmallVector<RegSaveOffset, 4> RegSaveOffsets;
258 
259   void emitFrameDataRecord(MCStreamer &OS, MCSymbol *Label);
260 };
261 } // end namespace
262 
printFPOReg(const MCRegisterInfo * MRI,unsigned LLVMReg)263 static Printable printFPOReg(const MCRegisterInfo *MRI, unsigned LLVMReg) {
264   return Printable([MRI, LLVMReg](raw_ostream &OS) {
265     switch (LLVMReg) {
266     // MSVC only seems to emit symbolic register names for EIP, EBP, and ESP,
267     // but the format seems to support more than that, so we emit them.
268     case X86::EAX: OS << "$eax"; break;
269     case X86::EBX: OS << "$ebx"; break;
270     case X86::ECX: OS << "$ecx"; break;
271     case X86::EDX: OS << "$edx"; break;
272     case X86::EDI: OS << "$edi"; break;
273     case X86::ESI: OS << "$esi"; break;
274     case X86::ESP: OS << "$esp"; break;
275     case X86::EBP: OS << "$ebp"; break;
276     case X86::EIP: OS << "$eip"; break;
277     // Otherwise, get the codeview register number and print $N.
278     default:
279       OS << '$' << MRI->getCodeViewRegNum(LLVMReg);
280       break;
281     }
282   });
283 }
284 
emitFrameDataRecord(MCStreamer & OS,MCSymbol * Label)285 void FPOStateMachine::emitFrameDataRecord(MCStreamer &OS, MCSymbol *Label) {
286   unsigned CurFlags = Flags;
287   if (Label == FPO->Begin)
288     CurFlags |= FrameData::IsFunctionStart;
289 
290   // Compute the new FrameFunc string.
291   FrameFunc.clear();
292   raw_svector_ostream FuncOS(FrameFunc);
293   const MCRegisterInfo *MRI = OS.getContext().getRegisterInfo();
294   if (FrameReg) {
295     // CFA is FrameReg + FrameRegOff.
296     FuncOS << "$T0 " << printFPOReg(MRI, FrameReg) << " " << FrameRegOff
297            << " + = ";
298   } else {
299     // The address of return address is ESP + CurOffset, but we use .raSearch to
300     // match MSVC. This seems to ask the debugger to subtract some combination
301     // of LocalSize and SavedRegSize from ESP and grovel around in that memory
302     // to find the address of a plausible return address.
303     FuncOS << "$T0 .raSearch = ";
304   }
305 
306   // Caller's $eip should be dereferenced CFA, and $esp should be CFA plus 4.
307   FuncOS << "$eip $T0 ^ = $esp $T0 4 + = ";
308 
309   // Each saved register is stored at an unchanging negative CFA offset.
310   for (RegSaveOffset RO : RegSaveOffsets)
311     FuncOS << printFPOReg(MRI, RO.Reg) << " $T0 " << RO.Offset << " - ^ = ";
312 
313   // Add it to the CV string table.
314   CodeViewContext &CVCtx = OS.getContext().getCVContext();
315   unsigned FrameFuncStrTabOff = CVCtx.addToStringTable(FuncOS.str()).second;
316 
317   // MSVC has only ever been observed to emit a MaxStackSize of zero.
318   unsigned MaxStackSize = 0;
319 
320   // The FrameData record format is:
321   //   ulittle32_t RvaStart;
322   //   ulittle32_t CodeSize;
323   //   ulittle32_t LocalSize;
324   //   ulittle32_t ParamsSize;
325   //   ulittle32_t MaxStackSize;
326   //   ulittle32_t FrameFunc; // String table offset
327   //   ulittle16_t PrologSize;
328   //   ulittle16_t SavedRegsSize;
329   //   ulittle32_t Flags;
330 
331   OS.emitAbsoluteSymbolDiff(Label, FPO->Begin, 4); // RvaStart
332   OS.emitAbsoluteSymbolDiff(FPO->End, Label, 4);   // CodeSize
333   OS.EmitIntValue(LocalSize, 4);
334   OS.EmitIntValue(FPO->ParamsSize, 4);
335   OS.EmitIntValue(MaxStackSize, 4);
336   OS.EmitIntValue(FrameFuncStrTabOff, 4); // FrameFunc
337   OS.emitAbsoluteSymbolDiff(FPO->PrologueEnd, Label, 2);
338   OS.EmitIntValue(SavedRegSize, 2);
339   OS.EmitIntValue(CurFlags, 4);
340 }
341 
342 /// Compute and emit the real CodeView FrameData subsection.
emitFPOData(const MCSymbol * ProcSym,SMLoc L)343 bool X86WinCOFFTargetStreamer::emitFPOData(const MCSymbol *ProcSym, SMLoc L) {
344   MCStreamer &OS = getStreamer();
345   MCContext &Ctx = OS.getContext();
346 
347   auto I = AllFPOData.find(ProcSym);
348   if (I == AllFPOData.end()) {
349     Ctx.reportError(L, Twine("no FPO data found for symbol ") +
350                            ProcSym->getName());
351     return true;
352   }
353   const FPOData *FPO = I->second.get();
354   assert(FPO->Begin && FPO->End && FPO->PrologueEnd && "missing FPO label");
355 
356   MCSymbol *FrameBegin = Ctx.createTempSymbol(),
357            *FrameEnd = Ctx.createTempSymbol();
358 
359   OS.EmitIntValue(unsigned(DebugSubsectionKind::FrameData), 4);
360   OS.emitAbsoluteSymbolDiff(FrameEnd, FrameBegin, 4);
361   OS.EmitLabel(FrameBegin);
362 
363   // Start with the RVA of the function in question.
364   OS.EmitValue(MCSymbolRefExpr::create(FPO->Function,
365                                        MCSymbolRefExpr::VK_COFF_IMGREL32, Ctx),
366                4);
367 
368   // Emit a sequence of FrameData records.
369   FPOStateMachine FSM(FPO);
370 
371   FSM.emitFrameDataRecord(OS, FPO->Begin);
372   for (const FPOInstruction &Inst : FPO->Instructions) {
373     switch (Inst.Op) {
374     case FPOInstruction::PushReg:
375       FSM.CurOffset += 4;
376       FSM.SavedRegSize += 4;
377       FSM.RegSaveOffsets.push_back({Inst.RegOrOffset, FSM.CurOffset});
378       break;
379     case FPOInstruction::SetFrame:
380       FSM.FrameReg = Inst.RegOrOffset;
381       FSM.FrameRegOff = FSM.CurOffset;
382       break;
383     case FPOInstruction::StackAlloc:
384       FSM.CurOffset += Inst.RegOrOffset;
385       FSM.LocalSize += Inst.RegOrOffset;
386       // No need to emit FrameData for stack allocations with a frame pointer.
387       if (FSM.FrameReg)
388         continue;
389       break;
390     }
391     FSM.emitFrameDataRecord(OS, Inst.Label);
392   }
393 
394   OS.EmitValueToAlignment(4, 0);
395   OS.EmitLabel(FrameEnd);
396   return false;
397 }
398 
createX86AsmTargetStreamer(MCStreamer & S,formatted_raw_ostream & OS,MCInstPrinter * InstPrinter,bool IsVerboseAsm)399 MCTargetStreamer *llvm::createX86AsmTargetStreamer(MCStreamer &S,
400                                                    formatted_raw_ostream &OS,
401                                                    MCInstPrinter *InstPrinter,
402                                                    bool IsVerboseAsm) {
403   // FIXME: This makes it so we textually assemble COFF directives on ELF.
404   // That's kind of nonsensical.
405   return new X86WinCOFFAsmTargetStreamer(S, OS, *InstPrinter);
406 }
407 
408 MCTargetStreamer *
createX86ObjectTargetStreamer(MCStreamer & S,const MCSubtargetInfo & STI)409 llvm::createX86ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) {
410   // No need to register a target streamer.
411   if (!STI.getTargetTriple().isOSBinFormatCOFF())
412     return nullptr;
413   // Registers itself to the MCStreamer.
414   return new X86WinCOFFTargetStreamer(S);
415 }
416