X86WinCOFFTargetStreamer.cpp revision 326938
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
20using namespace llvm;
21using namespace llvm::codeview;
22
23namespace {
24/// Implements Windows x86-only directives for assembly emission.
25class X86WinCOFFAsmTargetStreamer : public X86TargetStreamer {
26  formatted_raw_ostream &OS;
27  MCInstPrinter &InstPrinter;
28
29public:
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.
45struct FPOInstruction {
46  MCSymbol *Label;
47  enum Operation {
48    PushReg,
49    StackAlloc,
50    SetFrame,
51  } Op;
52  unsigned RegOrOffset;
53};
54
55struct 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.
66class 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
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
81  MCContext &getContext() { return getStreamer().getContext(); }
82
83public:
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
97bool 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
105bool X86WinCOFFAsmTargetStreamer::emitFPOEndPrologue(SMLoc L) {
106  OS << "\t.cv_fpo_endprologue\n";
107  return false;
108}
109
110bool X86WinCOFFAsmTargetStreamer::emitFPOEndProc(SMLoc L) {
111  OS << "\t.cv_fpo_endproc\n";
112  return false;
113}
114
115bool 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
123bool 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
130bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc,
131                                                    SMLoc L) {
132  OS << "\t.cv_fpo_stackalloc\t" << StackAlloc << '\n';
133  return false;
134}
135
136bool 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
143bool 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
153MCSymbol *X86WinCOFFTargetStreamer::emitFPOLabel() {
154  MCSymbol *Label = getContext().createTempSymbol("cfi", true);
155  getStreamer().EmitLabel(Label);
156  return Label;
157}
158
159bool 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
173bool 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
196bool 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
207bool 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
218bool 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
229bool X86WinCOFFTargetStreamer::emitFPOEndPrologue(SMLoc L) {
230  if (checkInFPOPrologue(L))
231    return true;
232  CurFPOData->PrologueEnd = emitFPOLabel();
233  return false;
234}
235
236namespace {
237struct RegSaveOffset {
238  RegSaveOffset(unsigned Reg, unsigned Offset) : Reg(Reg), Offset(Offset) {}
239
240  unsigned Reg = 0;
241  unsigned Offset = 0;
242};
243
244struct FPOStateMachine {
245  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
263static 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
285void 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.
343bool 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
399MCTargetStreamer *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
408MCTargetStreamer *
409llvm::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