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