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