xref: /freebsd/contrib/llvm-project/llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFTargetStreamer.cpp (revision 85868e8a1daeaae7a0e48effb2ea2310ae3b02c6)
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:
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 
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 
82   MCContext &getContext() { return getStreamer().getContext(); }
83 
84 public:
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 
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 
107 bool X86WinCOFFAsmTargetStreamer::emitFPOEndPrologue(SMLoc L) {
108   OS << "\t.cv_fpo_endprologue\n";
109   return false;
110 }
111 
112 bool X86WinCOFFAsmTargetStreamer::emitFPOEndProc(SMLoc L) {
113   OS << "\t.cv_fpo_endproc\n";
114   return false;
115 }
116 
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 
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 
132 bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc,
133                                                     SMLoc L) {
134   OS << "\t.cv_fpo_stackalloc\t" << StackAlloc << '\n';
135   return false;
136 }
137 
138 bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlign(unsigned Align, SMLoc L) {
139   OS << "\t.cv_fpo_stackalign\t" << Align << '\n';
140   return false;
141 }
142 
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 
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 
160 MCSymbol *X86WinCOFFTargetStreamer::emitFPOLabel() {
161   MCSymbol *Label = getContext().createTempSymbol("cfi", true);
162   getStreamer().EmitLabel(Label);
163   return Label;
164 }
165 
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 
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 
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 
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 
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 
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 
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 {
263   RegSaveOffset(unsigned Reg, unsigned Offset) : Reg(Reg), Offset(Offset) {}
264 
265   unsigned Reg = 0;
266   unsigned Offset = 0;
267 };
268 
269 struct FPOStateMachine {
270   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 
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 
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.EmitIntValue(LocalSize, 4);
376   OS.EmitIntValue(FPO->ParamsSize, 4);
377   OS.EmitIntValue(MaxStackSize, 4);
378   OS.EmitIntValue(FrameFuncStrTabOff, 4); // FrameFunc
379   OS.emitAbsoluteSymbolDiff(FPO->PrologueEnd, Label, 2);
380   OS.EmitIntValue(SavedRegSize, 2);
381   OS.EmitIntValue(CurFlags, 4);
382 }
383 
384 /// Compute and emit the real CodeView FrameData subsection.
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.EmitIntValue(unsigned(DebugSubsectionKind::FrameData), 4);
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 
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 *
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