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