Home | History | Annotate | Line # | Download | only in MCTargetDesc
      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.emitInt32(LocalSize);
    376   OS.emitInt32(FPO->ParamsSize);
    377   OS.emitInt32(MaxStackSize);
    378   OS.emitInt32(FrameFuncStrTabOff); // FrameFunc
    379   OS.emitAbsoluteSymbolDiff(FPO->PrologueEnd, Label, 2);
    380   OS.emitInt16(SavedRegSize);
    381   OS.emitInt32(CurFlags);
    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.emitInt32(unsigned(DebugSubsectionKind::FrameData));
    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