Path: blob/main/contrib/llvm-project/llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFTargetStreamer.cpp
35294 views
//===-- X86WinCOFFTargetStreamer.cpp ----------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "X86MCTargetDesc.h"9#include "X86TargetStreamer.h"10#include "llvm/DebugInfo/CodeView/CodeView.h"11#include "llvm/MC/MCCodeView.h"12#include "llvm/MC/MCContext.h"13#include "llvm/MC/MCInstPrinter.h"14#include "llvm/MC/MCRegisterInfo.h"15#include "llvm/MC/MCSubtargetInfo.h"16#include "llvm/MC/MCSymbol.h"17#include "llvm/Support/FormattedStream.h"1819using namespace llvm;20using namespace llvm::codeview;2122namespace {23/// Implements Windows x86-only directives for assembly emission.24class X86WinCOFFAsmTargetStreamer : public X86TargetStreamer {25formatted_raw_ostream &OS;26MCInstPrinter &InstPrinter;2728public:29X86WinCOFFAsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS,30MCInstPrinter &InstPrinter)31: X86TargetStreamer(S), OS(OS), InstPrinter(InstPrinter) {}3233bool emitFPOProc(const MCSymbol *ProcSym, unsigned ParamsSize,34SMLoc L) override;35bool emitFPOEndPrologue(SMLoc L) override;36bool emitFPOEndProc(SMLoc L) override;37bool emitFPOData(const MCSymbol *ProcSym, SMLoc L) override;38bool emitFPOPushReg(unsigned Reg, SMLoc L) override;39bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) override;40bool emitFPOStackAlign(unsigned Align, SMLoc L) override;41bool emitFPOSetFrame(unsigned Reg, SMLoc L) override;42};4344/// Represents a single FPO directive.45struct FPOInstruction {46MCSymbol *Label;47enum Operation {48PushReg,49StackAlloc,50StackAlign,51SetFrame,52} Op;53unsigned RegOrOffset;54};5556struct FPOData {57const MCSymbol *Function = nullptr;58MCSymbol *Begin = nullptr;59MCSymbol *PrologueEnd = nullptr;60MCSymbol *End = nullptr;61unsigned ParamsSize = 0;6263SmallVector<FPOInstruction, 5> Instructions;64};6566/// Implements Windows x86-only directives for object emission.67class X86WinCOFFTargetStreamer : public X86TargetStreamer {68/// Map from function symbol to its FPO data.69DenseMap<const MCSymbol *, std::unique_ptr<FPOData>> AllFPOData;7071/// Current FPO data created by .cv_fpo_proc.72std::unique_ptr<FPOData> CurFPOData;7374bool haveOpenFPOData() { return !!CurFPOData; }7576/// Diagnoses an error at L if we are not in an FPO prologue. Return true on77/// error.78bool checkInFPOPrologue(SMLoc L);7980MCSymbol *emitFPOLabel();8182MCContext &getContext() { return getStreamer().getContext(); }8384public:85X86WinCOFFTargetStreamer(MCStreamer &S) : X86TargetStreamer(S) {}8687bool emitFPOProc(const MCSymbol *ProcSym, unsigned ParamsSize,88SMLoc L) override;89bool emitFPOEndPrologue(SMLoc L) override;90bool emitFPOEndProc(SMLoc L) override;91bool emitFPOData(const MCSymbol *ProcSym, SMLoc L) override;92bool emitFPOPushReg(unsigned Reg, SMLoc L) override;93bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) override;94bool emitFPOStackAlign(unsigned Align, SMLoc L) override;95bool emitFPOSetFrame(unsigned Reg, SMLoc L) override;96};97} // end namespace9899bool X86WinCOFFAsmTargetStreamer::emitFPOProc(const MCSymbol *ProcSym,100unsigned ParamsSize, SMLoc L) {101OS << "\t.cv_fpo_proc\t";102ProcSym->print(OS, getStreamer().getContext().getAsmInfo());103OS << ' ' << ParamsSize << '\n';104return false;105}106107bool X86WinCOFFAsmTargetStreamer::emitFPOEndPrologue(SMLoc L) {108OS << "\t.cv_fpo_endprologue\n";109return false;110}111112bool X86WinCOFFAsmTargetStreamer::emitFPOEndProc(SMLoc L) {113OS << "\t.cv_fpo_endproc\n";114return false;115}116117bool X86WinCOFFAsmTargetStreamer::emitFPOData(const MCSymbol *ProcSym,118SMLoc L) {119OS << "\t.cv_fpo_data\t";120ProcSym->print(OS, getStreamer().getContext().getAsmInfo());121OS << '\n';122return false;123}124125bool X86WinCOFFAsmTargetStreamer::emitFPOPushReg(unsigned Reg, SMLoc L) {126OS << "\t.cv_fpo_pushreg\t";127InstPrinter.printRegName(OS, Reg);128OS << '\n';129return false;130}131132bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc,133SMLoc L) {134OS << "\t.cv_fpo_stackalloc\t" << StackAlloc << '\n';135return false;136}137138bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlign(unsigned Align, SMLoc L) {139OS << "\t.cv_fpo_stackalign\t" << Align << '\n';140return false;141}142143bool X86WinCOFFAsmTargetStreamer::emitFPOSetFrame(unsigned Reg, SMLoc L) {144OS << "\t.cv_fpo_setframe\t";145InstPrinter.printRegName(OS, Reg);146OS << '\n';147return false;148}149150bool X86WinCOFFTargetStreamer::checkInFPOPrologue(SMLoc L) {151if (!haveOpenFPOData() || CurFPOData->PrologueEnd) {152getContext().reportError(153L,154"directive must appear between .cv_fpo_proc and .cv_fpo_endprologue");155return true;156}157return false;158}159160MCSymbol *X86WinCOFFTargetStreamer::emitFPOLabel() {161MCSymbol *Label = getContext().createTempSymbol("cfi", true);162getStreamer().emitLabel(Label);163return Label;164}165166bool X86WinCOFFTargetStreamer::emitFPOProc(const MCSymbol *ProcSym,167unsigned ParamsSize, SMLoc L) {168if (haveOpenFPOData()) {169getContext().reportError(170L, "opening new .cv_fpo_proc before closing previous frame");171return true;172}173CurFPOData = std::make_unique<FPOData>();174CurFPOData->Function = ProcSym;175CurFPOData->Begin = emitFPOLabel();176CurFPOData->ParamsSize = ParamsSize;177return false;178}179180bool X86WinCOFFTargetStreamer::emitFPOEndProc(SMLoc L) {181if (!haveOpenFPOData()) {182getContext().reportError(L, ".cv_fpo_endproc must appear after .cv_proc");183return true;184}185if (!CurFPOData->PrologueEnd) {186// Complain if there were prologue setup instructions but no end prologue.187if (!CurFPOData->Instructions.empty()) {188getContext().reportError(L, "missing .cv_fpo_endprologue");189CurFPOData->Instructions.clear();190}191192// Claim there is a zero-length prologue to make the label math work out193// later.194CurFPOData->PrologueEnd = CurFPOData->Begin;195}196197CurFPOData->End = emitFPOLabel();198const MCSymbol *Fn = CurFPOData->Function;199AllFPOData.insert({Fn, std::move(CurFPOData)});200return false;201}202203bool X86WinCOFFTargetStreamer::emitFPOSetFrame(unsigned Reg, SMLoc L) {204if (checkInFPOPrologue(L))205return true;206FPOInstruction Inst;207Inst.Label = emitFPOLabel();208Inst.Op = FPOInstruction::SetFrame;209Inst.RegOrOffset = Reg;210CurFPOData->Instructions.push_back(Inst);211return false;212}213214bool X86WinCOFFTargetStreamer::emitFPOPushReg(unsigned Reg, SMLoc L) {215if (checkInFPOPrologue(L))216return true;217FPOInstruction Inst;218Inst.Label = emitFPOLabel();219Inst.Op = FPOInstruction::PushReg;220Inst.RegOrOffset = Reg;221CurFPOData->Instructions.push_back(Inst);222return false;223}224225bool X86WinCOFFTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) {226if (checkInFPOPrologue(L))227return true;228FPOInstruction Inst;229Inst.Label = emitFPOLabel();230Inst.Op = FPOInstruction::StackAlloc;231Inst.RegOrOffset = StackAlloc;232CurFPOData->Instructions.push_back(Inst);233return false;234}235236bool X86WinCOFFTargetStreamer::emitFPOStackAlign(unsigned Align, SMLoc L) {237if (checkInFPOPrologue(L))238return true;239if (llvm::none_of(CurFPOData->Instructions, [](const FPOInstruction &Inst) {240return Inst.Op == FPOInstruction::SetFrame;241})) {242getContext().reportError(243L, "a frame register must be established before aligning the stack");244return true;245}246FPOInstruction Inst;247Inst.Label = emitFPOLabel();248Inst.Op = FPOInstruction::StackAlign;249Inst.RegOrOffset = Align;250CurFPOData->Instructions.push_back(Inst);251return false;252}253254bool X86WinCOFFTargetStreamer::emitFPOEndPrologue(SMLoc L) {255if (checkInFPOPrologue(L))256return true;257CurFPOData->PrologueEnd = emitFPOLabel();258return false;259}260261namespace {262struct RegSaveOffset {263RegSaveOffset(unsigned Reg, unsigned Offset) : Reg(Reg), Offset(Offset) {}264265unsigned Reg = 0;266unsigned Offset = 0;267};268269struct FPOStateMachine {270explicit FPOStateMachine(const FPOData *FPO) : FPO(FPO) {}271272const FPOData *FPO = nullptr;273unsigned FrameReg = 0;274unsigned FrameRegOff = 0;275unsigned CurOffset = 0;276unsigned LocalSize = 0;277unsigned SavedRegSize = 0;278unsigned StackOffsetBeforeAlign = 0;279unsigned StackAlign = 0;280unsigned Flags = 0; // FIXME: Set HasSEH / HasEH.281282SmallString<128> FrameFunc;283284SmallVector<RegSaveOffset, 4> RegSaveOffsets;285286void emitFrameDataRecord(MCStreamer &OS, MCSymbol *Label);287};288} // end namespace289290static Printable printFPOReg(const MCRegisterInfo *MRI, unsigned LLVMReg) {291return Printable([MRI, LLVMReg](raw_ostream &OS) {292switch (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.295case X86::EAX: OS << "$eax"; break;296case X86::EBX: OS << "$ebx"; break;297case X86::ECX: OS << "$ecx"; break;298case X86::EDX: OS << "$edx"; break;299case X86::EDI: OS << "$edi"; break;300case X86::ESI: OS << "$esi"; break;301case X86::ESP: OS << "$esp"; break;302case X86::EBP: OS << "$ebp"; break;303case X86::EIP: OS << "$eip"; break;304// Otherwise, get the codeview register number and print $N.305default:306OS << '$' << MRI->getCodeViewRegNum(LLVMReg);307break;308}309});310}311312void FPOStateMachine::emitFrameDataRecord(MCStreamer &OS, MCSymbol *Label) {313unsigned CurFlags = Flags;314if (Label == FPO->Begin)315CurFlags |= FrameData::IsFunctionStart;316317// Compute the new FrameFunc string.318FrameFunc.clear();319raw_svector_ostream FuncOS(FrameFunc);320const MCRegisterInfo *MRI = OS.getContext().getRegisterInfo();321assert((StackAlign == 0 || FrameReg != 0) &&322"cannot align stack without frame reg");323StringRef CFAVar = StackAlign == 0 ? "$T0" : "$T1";324325if (FrameReg) {326// CFA is FrameReg + FrameRegOff.327FuncOS << CFAVar << ' ' << printFPOReg(MRI, FrameReg) << ' ' << FrameRegOff328<< " + = ";329330// 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, and332// align the result. While we don't store any CSRs in this area, $T0 is used333// by S_DEFRANGE_FRAMEPOINTER_REL records to find local variables.334if (StackAlign) {335FuncOS << "$T0 " << CFAVar << ' ' << StackOffsetBeforeAlign << " - "336<< StackAlign << " @ = ";337}338} else {339// The address of return address is ESP + CurOffset, but we use .raSearch to340// match MSVC. This seems to ask the debugger to subtract some combination341// of LocalSize and SavedRegSize from ESP and grovel around in that memory342// to find the address of a plausible return address.343FuncOS << CFAVar << " .raSearch = ";344}345346// Caller's $eip should be dereferenced CFA, and $esp should be CFA plus 4.347FuncOS << "$eip " << CFAVar << " ^ = ";348FuncOS << "$esp " << CFAVar << " 4 + = ";349350// Each saved register is stored at an unchanging negative CFA offset.351for (RegSaveOffset RO : RegSaveOffsets)352FuncOS << printFPOReg(MRI, RO.Reg) << ' ' << CFAVar << ' ' << RO.Offset353<< " - ^ = ";354355// Add it to the CV string table.356CodeViewContext &CVCtx = OS.getContext().getCVContext();357unsigned FrameFuncStrTabOff = CVCtx.addToStringTable(FuncOS.str()).second;358359// MSVC has only ever been observed to emit a MaxStackSize of zero.360unsigned MaxStackSize = 0;361362// 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 offset369// ulittle16_t PrologSize;370// ulittle16_t SavedRegsSize;371// ulittle32_t Flags;372373OS.emitAbsoluteSymbolDiff(Label, FPO->Begin, 4); // RvaStart374OS.emitAbsoluteSymbolDiff(FPO->End, Label, 4); // CodeSize375OS.emitInt32(LocalSize);376OS.emitInt32(FPO->ParamsSize);377OS.emitInt32(MaxStackSize);378OS.emitInt32(FrameFuncStrTabOff); // FrameFunc379OS.emitAbsoluteSymbolDiff(FPO->PrologueEnd, Label, 2);380OS.emitInt16(SavedRegSize);381OS.emitInt32(CurFlags);382}383384/// Compute and emit the real CodeView FrameData subsection.385bool X86WinCOFFTargetStreamer::emitFPOData(const MCSymbol *ProcSym, SMLoc L) {386MCStreamer &OS = getStreamer();387MCContext &Ctx = OS.getContext();388389auto I = AllFPOData.find(ProcSym);390if (I == AllFPOData.end()) {391Ctx.reportError(L, Twine("no FPO data found for symbol ") +392ProcSym->getName());393return true;394}395const FPOData *FPO = I->second.get();396assert(FPO->Begin && FPO->End && FPO->PrologueEnd && "missing FPO label");397398MCSymbol *FrameBegin = Ctx.createTempSymbol(),399*FrameEnd = Ctx.createTempSymbol();400401OS.emitInt32(unsigned(DebugSubsectionKind::FrameData));402OS.emitAbsoluteSymbolDiff(FrameEnd, FrameBegin, 4);403OS.emitLabel(FrameBegin);404405// Start with the RVA of the function in question.406OS.emitValue(MCSymbolRefExpr::create(FPO->Function,407MCSymbolRefExpr::VK_COFF_IMGREL32, Ctx),4084);409410// Emit a sequence of FrameData records.411FPOStateMachine FSM(FPO);412413FSM.emitFrameDataRecord(OS, FPO->Begin);414for (const FPOInstruction &Inst : FPO->Instructions) {415switch (Inst.Op) {416case FPOInstruction::PushReg:417FSM.CurOffset += 4;418FSM.SavedRegSize += 4;419FSM.RegSaveOffsets.push_back({Inst.RegOrOffset, FSM.CurOffset});420break;421case FPOInstruction::SetFrame:422FSM.FrameReg = Inst.RegOrOffset;423FSM.FrameRegOff = FSM.CurOffset;424break;425case FPOInstruction::StackAlign:426FSM.StackOffsetBeforeAlign = FSM.CurOffset;427FSM.StackAlign = Inst.RegOrOffset;428break;429case FPOInstruction::StackAlloc:430FSM.CurOffset += Inst.RegOrOffset;431FSM.LocalSize += Inst.RegOrOffset;432// No need to emit FrameData for stack allocations with a frame pointer.433if (FSM.FrameReg)434continue;435break;436}437FSM.emitFrameDataRecord(OS, Inst.Label);438}439440OS.emitValueToAlignment(Align(4), 0);441OS.emitLabel(FrameEnd);442return false;443}444445MCTargetStreamer *llvm::createX86AsmTargetStreamer(MCStreamer &S,446formatted_raw_ostream &OS,447MCInstPrinter *InstPrinter) {448// FIXME: This makes it so we textually assemble COFF directives on ELF.449// That's kind of nonsensical.450return new X86WinCOFFAsmTargetStreamer(S, OS, *InstPrinter);451}452453MCTargetStreamer *454llvm::createX86ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) {455// No need to register a target streamer.456if (!STI.getTargetTriple().isOSBinFormatCOFF())457return nullptr;458// Registers itself to the MCStreamer.459return new X86WinCOFFTargetStreamer(S);460}461462463