brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.1 KiB · b722964 Raw
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