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