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