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