123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- //===-- X86WinCOFFTargetStreamer.cpp ----------------------------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #include "X86MCTargetDesc.h"
- #include "X86TargetStreamer.h"
- #include "llvm/DebugInfo/CodeView/CodeView.h"
- #include "llvm/MC/MCCodeView.h"
- #include "llvm/MC/MCContext.h"
- #include "llvm/MC/MCInstPrinter.h"
- #include "llvm/MC/MCRegisterInfo.h"
- #include "llvm/MC/MCSubtargetInfo.h"
- #include "llvm/Support/FormattedStream.h"
- using namespace llvm;
- using namespace llvm::codeview;
- namespace {
- /// Implements Windows x86-only directives for assembly emission.
- class X86WinCOFFAsmTargetStreamer : public X86TargetStreamer {
- formatted_raw_ostream &OS;
- MCInstPrinter &InstPrinter;
- public:
- X86WinCOFFAsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS,
- MCInstPrinter &InstPrinter)
- : X86TargetStreamer(S), OS(OS), InstPrinter(InstPrinter) {}
- bool emitFPOProc(const MCSymbol *ProcSym, unsigned ParamsSize,
- SMLoc L) override;
- bool emitFPOEndPrologue(SMLoc L) override;
- bool emitFPOEndProc(SMLoc L) override;
- bool emitFPOData(const MCSymbol *ProcSym, SMLoc L) override;
- bool emitFPOPushReg(unsigned Reg, SMLoc L) override;
- bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) override;
- bool emitFPOStackAlign(unsigned Align, SMLoc L) override;
- bool emitFPOSetFrame(unsigned Reg, SMLoc L) override;
- };
- /// Represents a single FPO directive.
- struct FPOInstruction {
- MCSymbol *Label;
- enum Operation {
- PushReg,
- StackAlloc,
- StackAlign,
- SetFrame,
- } Op;
- unsigned RegOrOffset;
- };
- struct FPOData {
- const MCSymbol *Function = nullptr;
- MCSymbol *Begin = nullptr;
- MCSymbol *PrologueEnd = nullptr;
- MCSymbol *End = nullptr;
- unsigned ParamsSize = 0;
- SmallVector<FPOInstruction, 5> Instructions;
- };
- /// Implements Windows x86-only directives for object emission.
- class X86WinCOFFTargetStreamer : public X86TargetStreamer {
- /// Map from function symbol to its FPO data.
- DenseMap<const MCSymbol *, std::unique_ptr<FPOData>> AllFPOData;
- /// Current FPO data created by .cv_fpo_proc.
- std::unique_ptr<FPOData> CurFPOData;
- bool haveOpenFPOData() { return !!CurFPOData; }
- /// Diagnoses an error at L if we are not in an FPO prologue. Return true on
- /// error.
- bool checkInFPOPrologue(SMLoc L);
- MCSymbol *emitFPOLabel();
- MCContext &getContext() { return getStreamer().getContext(); }
- public:
- X86WinCOFFTargetStreamer(MCStreamer &S) : X86TargetStreamer(S) {}
- bool emitFPOProc(const MCSymbol *ProcSym, unsigned ParamsSize,
- SMLoc L) override;
- bool emitFPOEndPrologue(SMLoc L) override;
- bool emitFPOEndProc(SMLoc L) override;
- bool emitFPOData(const MCSymbol *ProcSym, SMLoc L) override;
- bool emitFPOPushReg(unsigned Reg, SMLoc L) override;
- bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) override;
- bool emitFPOStackAlign(unsigned Align, SMLoc L) override;
- bool emitFPOSetFrame(unsigned Reg, SMLoc L) override;
- };
- } // end namespace
- bool X86WinCOFFAsmTargetStreamer::emitFPOProc(const MCSymbol *ProcSym,
- unsigned ParamsSize, SMLoc L) {
- OS << "\t.cv_fpo_proc\t";
- ProcSym->print(OS, getStreamer().getContext().getAsmInfo());
- OS << ' ' << ParamsSize << '\n';
- return false;
- }
- bool X86WinCOFFAsmTargetStreamer::emitFPOEndPrologue(SMLoc L) {
- OS << "\t.cv_fpo_endprologue\n";
- return false;
- }
- bool X86WinCOFFAsmTargetStreamer::emitFPOEndProc(SMLoc L) {
- OS << "\t.cv_fpo_endproc\n";
- return false;
- }
- bool X86WinCOFFAsmTargetStreamer::emitFPOData(const MCSymbol *ProcSym,
- SMLoc L) {
- OS << "\t.cv_fpo_data\t";
- ProcSym->print(OS, getStreamer().getContext().getAsmInfo());
- OS << '\n';
- return false;
- }
- bool X86WinCOFFAsmTargetStreamer::emitFPOPushReg(unsigned Reg, SMLoc L) {
- OS << "\t.cv_fpo_pushreg\t";
- InstPrinter.printRegName(OS, Reg);
- OS << '\n';
- return false;
- }
- bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc,
- SMLoc L) {
- OS << "\t.cv_fpo_stackalloc\t" << StackAlloc << '\n';
- return false;
- }
- bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlign(unsigned Align, SMLoc L) {
- OS << "\t.cv_fpo_stackalign\t" << Align << '\n';
- return false;
- }
- bool X86WinCOFFAsmTargetStreamer::emitFPOSetFrame(unsigned Reg, SMLoc L) {
- OS << "\t.cv_fpo_setframe\t";
- InstPrinter.printRegName(OS, Reg);
- OS << '\n';
- return false;
- }
- bool X86WinCOFFTargetStreamer::checkInFPOPrologue(SMLoc L) {
- if (!haveOpenFPOData() || CurFPOData->PrologueEnd) {
- getContext().reportError(
- L,
- "directive must appear between .cv_fpo_proc and .cv_fpo_endprologue");
- return true;
- }
- return false;
- }
- MCSymbol *X86WinCOFFTargetStreamer::emitFPOLabel() {
- MCSymbol *Label = getContext().createTempSymbol("cfi", true);
- getStreamer().emitLabel(Label);
- return Label;
- }
- bool X86WinCOFFTargetStreamer::emitFPOProc(const MCSymbol *ProcSym,
- unsigned ParamsSize, SMLoc L) {
- if (haveOpenFPOData()) {
- getContext().reportError(
- L, "opening new .cv_fpo_proc before closing previous frame");
- return true;
- }
- CurFPOData = std::make_unique<FPOData>();
- CurFPOData->Function = ProcSym;
- CurFPOData->Begin = emitFPOLabel();
- CurFPOData->ParamsSize = ParamsSize;
- return false;
- }
- bool X86WinCOFFTargetStreamer::emitFPOEndProc(SMLoc L) {
- if (!haveOpenFPOData()) {
- getContext().reportError(L, ".cv_fpo_endproc must appear after .cv_proc");
- return true;
- }
- if (!CurFPOData->PrologueEnd) {
- // Complain if there were prologue setup instructions but no end prologue.
- if (!CurFPOData->Instructions.empty()) {
- getContext().reportError(L, "missing .cv_fpo_endprologue");
- CurFPOData->Instructions.clear();
- }
- // Claim there is a zero-length prologue to make the label math work out
- // later.
- CurFPOData->PrologueEnd = CurFPOData->Begin;
- }
- CurFPOData->End = emitFPOLabel();
- const MCSymbol *Fn = CurFPOData->Function;
- AllFPOData.insert({Fn, std::move(CurFPOData)});
- return false;
- }
- bool X86WinCOFFTargetStreamer::emitFPOSetFrame(unsigned Reg, SMLoc L) {
- if (checkInFPOPrologue(L))
- return true;
- FPOInstruction Inst;
- Inst.Label = emitFPOLabel();
- Inst.Op = FPOInstruction::SetFrame;
- Inst.RegOrOffset = Reg;
- CurFPOData->Instructions.push_back(Inst);
- return false;
- }
- bool X86WinCOFFTargetStreamer::emitFPOPushReg(unsigned Reg, SMLoc L) {
- if (checkInFPOPrologue(L))
- return true;
- FPOInstruction Inst;
- Inst.Label = emitFPOLabel();
- Inst.Op = FPOInstruction::PushReg;
- Inst.RegOrOffset = Reg;
- CurFPOData->Instructions.push_back(Inst);
- return false;
- }
- bool X86WinCOFFTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) {
- if (checkInFPOPrologue(L))
- return true;
- FPOInstruction Inst;
- Inst.Label = emitFPOLabel();
- Inst.Op = FPOInstruction::StackAlloc;
- Inst.RegOrOffset = StackAlloc;
- CurFPOData->Instructions.push_back(Inst);
- return false;
- }
- bool X86WinCOFFTargetStreamer::emitFPOStackAlign(unsigned Align, SMLoc L) {
- if (checkInFPOPrologue(L))
- return true;
- if (llvm::none_of(CurFPOData->Instructions, [](const FPOInstruction &Inst) {
- return Inst.Op == FPOInstruction::SetFrame;
- })) {
- getContext().reportError(
- L, "a frame register must be established before aligning the stack");
- return true;
- }
- FPOInstruction Inst;
- Inst.Label = emitFPOLabel();
- Inst.Op = FPOInstruction::StackAlign;
- Inst.RegOrOffset = Align;
- CurFPOData->Instructions.push_back(Inst);
- return false;
- }
- bool X86WinCOFFTargetStreamer::emitFPOEndPrologue(SMLoc L) {
- if (checkInFPOPrologue(L))
- return true;
- CurFPOData->PrologueEnd = emitFPOLabel();
- return false;
- }
- namespace {
- struct RegSaveOffset {
- RegSaveOffset(unsigned Reg, unsigned Offset) : Reg(Reg), Offset(Offset) {}
- unsigned Reg = 0;
- unsigned Offset = 0;
- };
- struct FPOStateMachine {
- explicit FPOStateMachine(const FPOData *FPO) : FPO(FPO) {}
- const FPOData *FPO = nullptr;
- unsigned FrameReg = 0;
- unsigned FrameRegOff = 0;
- unsigned CurOffset = 0;
- unsigned LocalSize = 0;
- unsigned SavedRegSize = 0;
- unsigned StackOffsetBeforeAlign = 0;
- unsigned StackAlign = 0;
- unsigned Flags = 0; // FIXME: Set HasSEH / HasEH.
- SmallString<128> FrameFunc;
- SmallVector<RegSaveOffset, 4> RegSaveOffsets;
- void emitFrameDataRecord(MCStreamer &OS, MCSymbol *Label);
- };
- } // end namespace
- static Printable printFPOReg(const MCRegisterInfo *MRI, unsigned LLVMReg) {
- return Printable([MRI, LLVMReg](raw_ostream &OS) {
- switch (LLVMReg) {
- // MSVC only seems to emit symbolic register names for EIP, EBP, and ESP,
- // but the format seems to support more than that, so we emit them.
- case X86::EAX: OS << "$eax"; break;
- case X86::EBX: OS << "$ebx"; break;
- case X86::ECX: OS << "$ecx"; break;
- case X86::EDX: OS << "$edx"; break;
- case X86::EDI: OS << "$edi"; break;
- case X86::ESI: OS << "$esi"; break;
- case X86::ESP: OS << "$esp"; break;
- case X86::EBP: OS << "$ebp"; break;
- case X86::EIP: OS << "$eip"; break;
- // Otherwise, get the codeview register number and print $N.
- default:
- OS << '$' << MRI->getCodeViewRegNum(LLVMReg);
- break;
- }
- });
- }
- void FPOStateMachine::emitFrameDataRecord(MCStreamer &OS, MCSymbol *Label) {
- unsigned CurFlags = Flags;
- if (Label == FPO->Begin)
- CurFlags |= FrameData::IsFunctionStart;
- // Compute the new FrameFunc string.
- FrameFunc.clear();
- raw_svector_ostream FuncOS(FrameFunc);
- const MCRegisterInfo *MRI = OS.getContext().getRegisterInfo();
- assert((StackAlign == 0 || FrameReg != 0) &&
- "cannot align stack without frame reg");
- StringRef CFAVar = StackAlign == 0 ? "$T0" : "$T1";
- if (FrameReg) {
- // CFA is FrameReg + FrameRegOff.
- FuncOS << CFAVar << ' ' << printFPOReg(MRI, FrameReg) << ' ' << FrameRegOff
- << " + = ";
- // Assign $T0, the VFRAME register, the value of ESP after it is aligned.
- // Starting from the CFA, we subtract the size of all pushed registers, and
- // align the result. While we don't store any CSRs in this area, $T0 is used
- // by S_DEFRANGE_FRAMEPOINTER_REL records to find local variables.
- if (StackAlign) {
- FuncOS << "$T0 " << CFAVar << ' ' << StackOffsetBeforeAlign << " - "
- << StackAlign << " @ = ";
- }
- } else {
- // The address of return address is ESP + CurOffset, but we use .raSearch to
- // match MSVC. This seems to ask the debugger to subtract some combination
- // of LocalSize and SavedRegSize from ESP and grovel around in that memory
- // to find the address of a plausible return address.
- FuncOS << CFAVar << " .raSearch = ";
- }
- // Caller's $eip should be dereferenced CFA, and $esp should be CFA plus 4.
- FuncOS << "$eip " << CFAVar << " ^ = ";
- FuncOS << "$esp " << CFAVar << " 4 + = ";
- // Each saved register is stored at an unchanging negative CFA offset.
- for (RegSaveOffset RO : RegSaveOffsets)
- FuncOS << printFPOReg(MRI, RO.Reg) << ' ' << CFAVar << ' ' << RO.Offset
- << " - ^ = ";
- // Add it to the CV string table.
- CodeViewContext &CVCtx = OS.getContext().getCVContext();
- unsigned FrameFuncStrTabOff = CVCtx.addToStringTable(FuncOS.str()).second;
- // MSVC has only ever been observed to emit a MaxStackSize of zero.
- unsigned MaxStackSize = 0;
- // The FrameData record format is:
- // ulittle32_t RvaStart;
- // ulittle32_t CodeSize;
- // ulittle32_t LocalSize;
- // ulittle32_t ParamsSize;
- // ulittle32_t MaxStackSize;
- // ulittle32_t FrameFunc; // String table offset
- // ulittle16_t PrologSize;
- // ulittle16_t SavedRegsSize;
- // ulittle32_t Flags;
- OS.emitAbsoluteSymbolDiff(Label, FPO->Begin, 4); // RvaStart
- OS.emitAbsoluteSymbolDiff(FPO->End, Label, 4); // CodeSize
- OS.emitInt32(LocalSize);
- OS.emitInt32(FPO->ParamsSize);
- OS.emitInt32(MaxStackSize);
- OS.emitInt32(FrameFuncStrTabOff); // FrameFunc
- OS.emitAbsoluteSymbolDiff(FPO->PrologueEnd, Label, 2);
- OS.emitInt16(SavedRegSize);
- OS.emitInt32(CurFlags);
- }
- /// Compute and emit the real CodeView FrameData subsection.
- bool X86WinCOFFTargetStreamer::emitFPOData(const MCSymbol *ProcSym, SMLoc L) {
- MCStreamer &OS = getStreamer();
- MCContext &Ctx = OS.getContext();
- auto I = AllFPOData.find(ProcSym);
- if (I == AllFPOData.end()) {
- Ctx.reportError(L, Twine("no FPO data found for symbol ") +
- ProcSym->getName());
- return true;
- }
- const FPOData *FPO = I->second.get();
- assert(FPO->Begin && FPO->End && FPO->PrologueEnd && "missing FPO label");
- MCSymbol *FrameBegin = Ctx.createTempSymbol(),
- *FrameEnd = Ctx.createTempSymbol();
- OS.emitInt32(unsigned(DebugSubsectionKind::FrameData));
- OS.emitAbsoluteSymbolDiff(FrameEnd, FrameBegin, 4);
- OS.emitLabel(FrameBegin);
- // Start with the RVA of the function in question.
- OS.emitValue(MCSymbolRefExpr::create(FPO->Function,
- MCSymbolRefExpr::VK_COFF_IMGREL32, Ctx),
- 4);
- // Emit a sequence of FrameData records.
- FPOStateMachine FSM(FPO);
- FSM.emitFrameDataRecord(OS, FPO->Begin);
- for (const FPOInstruction &Inst : FPO->Instructions) {
- switch (Inst.Op) {
- case FPOInstruction::PushReg:
- FSM.CurOffset += 4;
- FSM.SavedRegSize += 4;
- FSM.RegSaveOffsets.push_back({Inst.RegOrOffset, FSM.CurOffset});
- break;
- case FPOInstruction::SetFrame:
- FSM.FrameReg = Inst.RegOrOffset;
- FSM.FrameRegOff = FSM.CurOffset;
- break;
- case FPOInstruction::StackAlign:
- FSM.StackOffsetBeforeAlign = FSM.CurOffset;
- FSM.StackAlign = Inst.RegOrOffset;
- break;
- case FPOInstruction::StackAlloc:
- FSM.CurOffset += Inst.RegOrOffset;
- FSM.LocalSize += Inst.RegOrOffset;
- // No need to emit FrameData for stack allocations with a frame pointer.
- if (FSM.FrameReg)
- continue;
- break;
- }
- FSM.emitFrameDataRecord(OS, Inst.Label);
- }
- OS.emitValueToAlignment(4, 0);
- OS.emitLabel(FrameEnd);
- return false;
- }
- MCTargetStreamer *llvm::createX86AsmTargetStreamer(MCStreamer &S,
- formatted_raw_ostream &OS,
- MCInstPrinter *InstPrinter,
- bool IsVerboseAsm) {
- // FIXME: This makes it so we textually assemble COFF directives on ELF.
- // That's kind of nonsensical.
- return new X86WinCOFFAsmTargetStreamer(S, OS, *InstPrinter);
- }
- MCTargetStreamer *
- llvm::createX86ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) {
- // No need to register a target streamer.
- if (!STI.getTargetTriple().isOSBinFormatCOFF())
- return nullptr;
- // Registers itself to the MCStreamer.
- return new X86WinCOFFTargetStreamer(S);
- }
|