brintos

brintos / llvm-project-archived public Read only

0
0
Text · 19.7 KiB · 653645f Raw
544 lines · cpp
1//===--------- ELFDebugObjectPlugin.cpp - JITLink debug objects -----------===//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// FIXME: Update Plugin to poke the debug object into a new JITLink section,10//        rather than creating a new allocation.11//12//===----------------------------------------------------------------------===//13 14#include "llvm/ExecutionEngine/Orc/Debugging/ELFDebugObjectPlugin.h"15 16#include "llvm/ADT/ArrayRef.h"17#include "llvm/ADT/StringMap.h"18#include "llvm/ADT/StringRef.h"19#include "llvm/BinaryFormat/ELF.h"20#include "llvm/ExecutionEngine/JITLink/JITLinkDylib.h"21#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"22#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"23#include "llvm/Object/ELFObjectFile.h"24#include "llvm/Support/Errc.h"25#include "llvm/Support/MSVCErrorWorkarounds.h"26#include "llvm/Support/MemoryBuffer.h"27#include "llvm/Support/Process.h"28#include "llvm/Support/raw_ostream.h"29 30#include <set>31 32#define DEBUG_TYPE "orc"33 34using namespace llvm::jitlink;35using namespace llvm::object;36 37namespace llvm {38namespace orc {39 40class DebugObjectSection {41public:42  virtual void setTargetMemoryRange(SectionRange Range) = 0;43  virtual void dump(raw_ostream &OS, StringRef Name) {}44  virtual ~DebugObjectSection() = default;45};46 47template <typename ELFT>48class ELFDebugObjectSection : public DebugObjectSection {49public:50  // BinaryFormat ELF is not meant as a mutable format. We can only make changes51  // that don't invalidate the file structure.52  ELFDebugObjectSection(const typename ELFT::Shdr *Header)53      : Header(const_cast<typename ELFT::Shdr *>(Header)) {}54 55  void setTargetMemoryRange(SectionRange Range) override;56  void dump(raw_ostream &OS, StringRef Name) override;57 58  Error validateInBounds(StringRef Buffer, const char *Name) const;59 60private:61  typename ELFT::Shdr *Header;62};63 64template <typename ELFT>65void ELFDebugObjectSection<ELFT>::setTargetMemoryRange(SectionRange Range) {66  // All recorded sections are candidates for load-address patching.67  Header->sh_addr =68      static_cast<typename ELFT::uint>(Range.getStart().getValue());69}70 71template <typename ELFT>72Error ELFDebugObjectSection<ELFT>::validateInBounds(StringRef Buffer,73                                                    const char *Name) const {74  const uint8_t *Start = Buffer.bytes_begin();75  const uint8_t *End = Buffer.bytes_end();76  const uint8_t *HeaderPtr = reinterpret_cast<uint8_t *>(Header);77  if (HeaderPtr < Start || HeaderPtr + sizeof(typename ELFT::Shdr) > End)78    return make_error<StringError>(79        formatv("{0} section header at {1:x16} not within bounds of the "80                "given debug object buffer [{2:x16} - {3:x16}]",81                Name, &Header->sh_addr, Start, End),82        inconvertibleErrorCode());83  if (Header->sh_offset + Header->sh_size > Buffer.size())84    return make_error<StringError>(85        formatv("{0} section data [{1:x16} - {2:x16}] not within bounds of "86                "the given debug object buffer [{3:x16} - {4:x16}]",87                Name, Start + Header->sh_offset,88                Start + Header->sh_offset + Header->sh_size, Start, End),89        inconvertibleErrorCode());90  return Error::success();91}92 93template <typename ELFT>94void ELFDebugObjectSection<ELFT>::dump(raw_ostream &OS, StringRef Name) {95  if (uint64_t Addr = Header->sh_addr) {96    OS << formatv("  {0:x16} {1}\n", Addr, Name);97  } else {98    OS << formatv("                     {0}\n", Name);99  }100}101 102enum DebugObjectFlags : int {103  // Request final target memory load-addresses for all sections.104  ReportFinalSectionLoadAddresses = 1 << 0,105 106  // We found sections with debug information when processing the input object.107  HasDebugSections = 1 << 1,108};109 110/// The plugin creates a debug object from when JITLink starts processing the111/// corresponding LinkGraph. It provides access to the pass configuration of112/// the LinkGraph and calls the finalization function, once the resulting link113/// artifact was emitted.114///115class DebugObject {116public:117  DebugObject(JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD,118              ExecutionSession &ES)119      : MemMgr(MemMgr), JD(JD), ES(ES), Flags(DebugObjectFlags{}) {120    FinalizeFuture = FinalizePromise.get_future();121  }122 123  bool hasFlags(DebugObjectFlags F) const { return Flags & F; }124  void setFlags(DebugObjectFlags F) {125    Flags = static_cast<DebugObjectFlags>(Flags | F);126  }127  void clearFlags(DebugObjectFlags F) {128    Flags = static_cast<DebugObjectFlags>(Flags & ~F);129  }130 131  using FinalizeContinuation = std::function<void(Expected<ExecutorAddrRange>)>;132  void finalizeAsync(FinalizeContinuation OnAsync);133 134  void failMaterialization(Error Err) {135    FinalizePromise.set_value(std::move(Err));136  }137 138  void reportTargetMem(ExecutorAddrRange TargetMem) {139    FinalizePromise.set_value(TargetMem);140  }141 142  Expected<ExecutorAddrRange> awaitTargetMem() { return FinalizeFuture.get(); }143 144  virtual ~DebugObject() {145    if (Alloc) {146      std::vector<FinalizedAlloc> Allocs;147      Allocs.push_back(std::move(Alloc));148      if (Error Err = MemMgr.deallocate(std::move(Allocs)))149        ES.reportError(std::move(Err));150    }151  }152 153  virtual void reportSectionTargetMemoryRange(StringRef Name,154                                              SectionRange TargetMem) {}155 156protected:157  using InFlightAlloc = JITLinkMemoryManager::InFlightAlloc;158  using FinalizedAlloc = JITLinkMemoryManager::FinalizedAlloc;159 160  virtual Expected<SimpleSegmentAlloc> finalizeWorkingMemory() = 0;161 162  JITLinkMemoryManager &MemMgr;163  const JITLinkDylib *JD = nullptr;164  ExecutionSession &ES;165 166  std::promise<MSVCPExpected<ExecutorAddrRange>> FinalizePromise;167  std::future<MSVCPExpected<ExecutorAddrRange>> FinalizeFuture;168 169private:170  DebugObjectFlags Flags;171  FinalizedAlloc Alloc;172};173 174// Finalize working memory and take ownership of the resulting allocation. Start175// copying memory over to the target and pass on the result once we're done.176// Ownership of the allocation remains with us for the rest of our lifetime.177void DebugObject::finalizeAsync(FinalizeContinuation OnFinalize) {178  assert(!this->Alloc && "Cannot finalize more than once");179  if (auto SimpleSegAlloc = finalizeWorkingMemory()) {180    auto ROSeg = SimpleSegAlloc->getSegInfo(MemProt::Read);181    ExecutorAddrRange DebugObjRange(ROSeg.Addr, ROSeg.WorkingMem.size());182    SimpleSegAlloc->finalize(183        [this, DebugObjRange,184         OnFinalize = std::move(OnFinalize)](Expected<FinalizedAlloc> FA) {185          if (FA) {186            // Note: FA->getAddress() is supposed to be the address of the187            // memory range on the target, but InProcessMemoryManager returns188            // the address of a FinalizedAllocInfo helper instead.189            this->Alloc = std::move(*FA);190            OnFinalize(DebugObjRange);191          } else192            OnFinalize(FA.takeError());193        });194  } else {195    // We could report this error synchronously, but it's easier this way,196    // because the FinalizePromise will be triggered unconditionally.197    OnFinalize(SimpleSegAlloc.takeError());198  }199}200 201/// The current implementation of ELFDebugObject replicates the approach used in202/// RuntimeDyld: It patches executable and data section headers in the given203/// object buffer with load-addresses of their corresponding sections in target204/// memory.205///206class ELFDebugObject : public DebugObject {207public:208  static Expected<std::unique_ptr<DebugObject>>209  Create(MemoryBufferRef Buffer, JITLinkContext &Ctx, ExecutionSession &ES);210 211  void reportSectionTargetMemoryRange(StringRef Name,212                                      SectionRange TargetMem) override;213 214  StringRef getBuffer() const { return Buffer->getMemBufferRef().getBuffer(); }215 216protected:217  Expected<SimpleSegmentAlloc> finalizeWorkingMemory() override;218 219  template <typename ELFT>220  Error recordSection(StringRef Name,221                      std::unique_ptr<ELFDebugObjectSection<ELFT>> Section);222  DebugObjectSection *getSection(StringRef Name);223 224private:225  template <typename ELFT>226  static Expected<std::unique_ptr<ELFDebugObject>>227  CreateArchType(MemoryBufferRef Buffer, JITLinkMemoryManager &MemMgr,228                 const JITLinkDylib *JD, ExecutionSession &ES);229 230  static std::unique_ptr<WritableMemoryBuffer>231  CopyBuffer(MemoryBufferRef Buffer, Error &Err);232 233  ELFDebugObject(std::unique_ptr<WritableMemoryBuffer> Buffer,234                 JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD,235                 ExecutionSession &ES)236      : DebugObject(MemMgr, JD, ES), Buffer(std::move(Buffer)) {237    setFlags(ReportFinalSectionLoadAddresses);238  }239 240  std::unique_ptr<WritableMemoryBuffer> Buffer;241  StringMap<std::unique_ptr<DebugObjectSection>> Sections;242};243 244static const std::set<StringRef> DwarfSectionNames = {245#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION)        \246  ELF_NAME,247#include "llvm/BinaryFormat/Dwarf.def"248#undef HANDLE_DWARF_SECTION249};250 251static bool isDwarfSection(StringRef SectionName) {252  return DwarfSectionNames.count(SectionName) == 1;253}254 255std::unique_ptr<WritableMemoryBuffer>256ELFDebugObject::CopyBuffer(MemoryBufferRef Buffer, Error &Err) {257  ErrorAsOutParameter _(Err);258  size_t Size = Buffer.getBufferSize();259  StringRef Name = Buffer.getBufferIdentifier();260  if (auto Copy = WritableMemoryBuffer::getNewUninitMemBuffer(Size, Name)) {261    memcpy(Copy->getBufferStart(), Buffer.getBufferStart(), Size);262    return Copy;263  }264 265  Err = errorCodeToError(make_error_code(errc::not_enough_memory));266  return nullptr;267}268 269template <typename ELFT>270Expected<std::unique_ptr<ELFDebugObject>>271ELFDebugObject::CreateArchType(MemoryBufferRef Buffer,272                               JITLinkMemoryManager &MemMgr,273                               const JITLinkDylib *JD, ExecutionSession &ES) {274  using SectionHeader = typename ELFT::Shdr;275 276  Error Err = Error::success();277  std::unique_ptr<ELFDebugObject> DebugObj(278      new ELFDebugObject(CopyBuffer(Buffer, Err), MemMgr, JD, ES));279  if (Err)280    return std::move(Err);281 282  Expected<ELFFile<ELFT>> ObjRef = ELFFile<ELFT>::create(DebugObj->getBuffer());283  if (!ObjRef)284    return ObjRef.takeError();285 286  Expected<ArrayRef<SectionHeader>> Sections = ObjRef->sections();287  if (!Sections)288    return Sections.takeError();289 290  for (const SectionHeader &Header : *Sections) {291    Expected<StringRef> Name = ObjRef->getSectionName(Header);292    if (!Name)293      return Name.takeError();294    if (Name->empty())295      continue;296    if (isDwarfSection(*Name))297      DebugObj->setFlags(HasDebugSections);298 299    // Only record text and data sections (i.e. no bss, comments, rel, etc.)300    if (Header.sh_type != ELF::SHT_PROGBITS &&301        Header.sh_type != ELF::SHT_X86_64_UNWIND)302      continue;303    if (!(Header.sh_flags & ELF::SHF_ALLOC))304      continue;305 306    auto Wrapped = std::make_unique<ELFDebugObjectSection<ELFT>>(&Header);307    if (Error Err = DebugObj->recordSection(*Name, std::move(Wrapped)))308      return std::move(Err);309  }310 311  return std::move(DebugObj);312}313 314Expected<std::unique_ptr<DebugObject>>315ELFDebugObject::Create(MemoryBufferRef Buffer, JITLinkContext &Ctx,316                       ExecutionSession &ES) {317  unsigned char Class, Endian;318  std::tie(Class, Endian) = getElfArchType(Buffer.getBuffer());319 320  if (Class == ELF::ELFCLASS32) {321    if (Endian == ELF::ELFDATA2LSB)322      return CreateArchType<ELF32LE>(Buffer, Ctx.getMemoryManager(),323                                     Ctx.getJITLinkDylib(), ES);324    if (Endian == ELF::ELFDATA2MSB)325      return CreateArchType<ELF32BE>(Buffer, Ctx.getMemoryManager(),326                                     Ctx.getJITLinkDylib(), ES);327    return nullptr;328  }329  if (Class == ELF::ELFCLASS64) {330    if (Endian == ELF::ELFDATA2LSB)331      return CreateArchType<ELF64LE>(Buffer, Ctx.getMemoryManager(),332                                     Ctx.getJITLinkDylib(), ES);333    if (Endian == ELF::ELFDATA2MSB)334      return CreateArchType<ELF64BE>(Buffer, Ctx.getMemoryManager(),335                                     Ctx.getJITLinkDylib(), ES);336    return nullptr;337  }338  return nullptr;339}340 341Expected<SimpleSegmentAlloc> ELFDebugObject::finalizeWorkingMemory() {342  LLVM_DEBUG({343    dbgs() << "Section load-addresses in debug object for \""344           << Buffer->getBufferIdentifier() << "\":\n";345    for (const auto &KV : Sections)346      KV.second->dump(dbgs(), KV.first());347  });348 349  // TODO: This works, but what actual alignment requirements do we have?350  unsigned PageSize = sys::Process::getPageSizeEstimate();351  size_t Size = Buffer->getBufferSize();352 353  // Allocate working memory for debug object in read-only segment.354  auto Alloc = SimpleSegmentAlloc::Create(355      MemMgr, ES.getSymbolStringPool(), ES.getTargetTriple(), JD,356      {{MemProt::Read, {Size, Align(PageSize)}}});357  if (!Alloc)358    return Alloc;359 360  // Initialize working memory with a copy of our object buffer.361  auto SegInfo = Alloc->getSegInfo(MemProt::Read);362  memcpy(SegInfo.WorkingMem.data(), Buffer->getBufferStart(), Size);363  Buffer.reset();364 365  return Alloc;366}367 368void ELFDebugObject::reportSectionTargetMemoryRange(StringRef Name,369                                                    SectionRange TargetMem) {370  if (auto *DebugObjSection = getSection(Name))371    DebugObjSection->setTargetMemoryRange(TargetMem);372}373 374template <typename ELFT>375Error ELFDebugObject::recordSection(376    StringRef Name, std::unique_ptr<ELFDebugObjectSection<ELFT>> Section) {377  if (Error Err = Section->validateInBounds(this->getBuffer(), Name.data()))378    return Err;379  bool Inserted = Sections.try_emplace(Name, std::move(Section)).second;380  if (!Inserted)381    LLVM_DEBUG(dbgs() << "Skipping debug registration for section '" << Name382                      << "' in object " << Buffer->getBufferIdentifier()383                      << " (duplicate name)\n");384  return Error::success();385}386 387DebugObjectSection *ELFDebugObject::getSection(StringRef Name) {388  auto It = Sections.find(Name);389  return It == Sections.end() ? nullptr : It->second.get();390}391 392/// Creates a debug object based on the input object file from393/// ObjectLinkingLayerJITLinkContext.394///395static Expected<std::unique_ptr<DebugObject>>396createDebugObjectFromBuffer(ExecutionSession &ES, LinkGraph &G,397                            JITLinkContext &Ctx, MemoryBufferRef ObjBuffer) {398  switch (G.getTargetTriple().getObjectFormat()) {399  case Triple::ELF:400    return ELFDebugObject::Create(ObjBuffer, Ctx, ES);401 402  default:403    // TODO: Once we add support for other formats, we might want to split this404    // into multiple files.405    return nullptr;406  }407}408 409ELFDebugObjectPlugin::ELFDebugObjectPlugin(ExecutionSession &ES,410                                           bool RequireDebugSections,411                                           bool AutoRegisterCode, Error &Err)412    : ES(ES), RequireDebugSections(RequireDebugSections),413      AutoRegisterCode(AutoRegisterCode) {414  // Pass bootstrap symbol for registration function to enable debugging415  ErrorAsOutParameter _(&Err);416  Err = ES.getExecutorProcessControl().getBootstrapSymbols(417      {{RegistrationAction, rt::RegisterJITLoaderGDBAllocActionName}});418}419 420ELFDebugObjectPlugin::~ELFDebugObjectPlugin() = default;421 422void ELFDebugObjectPlugin::notifyMaterializing(423    MaterializationResponsibility &MR, LinkGraph &G, JITLinkContext &Ctx,424    MemoryBufferRef ObjBuffer) {425  std::lock_guard<std::mutex> Lock(PendingObjsLock);426  assert(PendingObjs.count(&MR) == 0 &&427         "Cannot have more than one pending debug object per "428         "MaterializationResponsibility");429 430  if (auto DebugObj = createDebugObjectFromBuffer(ES, G, Ctx, ObjBuffer)) {431    // Not all link artifacts allow debugging.432    if (*DebugObj == nullptr)433      return;434    if (RequireDebugSections && !(**DebugObj).hasFlags(HasDebugSections)) {435      LLVM_DEBUG(dbgs() << "Skipping debug registration for LinkGraph '"436                        << G.getName() << "': no debug info\n");437      return;438    }439    PendingObjs[&MR] = std::move(*DebugObj);440  } else {441    ES.reportError(DebugObj.takeError());442  }443}444 445void ELFDebugObjectPlugin::modifyPassConfig(MaterializationResponsibility &MR,446                                            LinkGraph &G,447                                            PassConfiguration &PassConfig) {448  // Not all link artifacts have associated debug objects.449  std::lock_guard<std::mutex> Lock(PendingObjsLock);450  auto It = PendingObjs.find(&MR);451  if (It == PendingObjs.end())452    return;453 454  DebugObject &DebugObj = *It->second;455  if (DebugObj.hasFlags(ReportFinalSectionLoadAddresses)) {456    PassConfig.PostAllocationPasses.push_back(457        [&DebugObj](LinkGraph &Graph) -> Error {458          for (const Section &GraphSection : Graph.sections())459            DebugObj.reportSectionTargetMemoryRange(GraphSection.getName(),460                                                    SectionRange(GraphSection));461          return Error::success();462        });463 464    PassConfig.PreFixupPasses.push_back(465        [this, &DebugObj, &MR](LinkGraph &G) -> Error {466          DebugObj.finalizeAsync([this, &DebugObj,467                                  &MR](Expected<ExecutorAddrRange> TargetMem) {468            if (!TargetMem) {469              DebugObj.failMaterialization(TargetMem.takeError());470              return;471            }472            // Update tracking info473            Error Err = MR.withResourceKeyDo([&](ResourceKey K) {474              std::lock_guard<std::mutex> LockPending(PendingObjsLock);475              std::lock_guard<std::mutex> LockRegistered(RegisteredObjsLock);476              auto It = PendingObjs.find(&MR);477              RegisteredObjs[K].push_back(std::move(It->second));478              PendingObjs.erase(It);479            });480 481            if (Err)482              DebugObj.failMaterialization(std::move(Err));483 484            // Unblock post-fixup pass485            DebugObj.reportTargetMem(*TargetMem);486          });487          return Error::success();488        });489 490    PassConfig.PostFixupPasses.push_back(491        [this, &DebugObj](LinkGraph &G) -> Error {492          Expected<ExecutorAddrRange> R = DebugObj.awaitTargetMem();493          if (!R)494            return R.takeError();495          if (R->empty())496            return Error::success();497 498          using namespace shared;499          G.allocActions().push_back(500              {cantFail(WrapperFunctionCall::Create<501                        SPSArgList<SPSExecutorAddrRange, bool>>(502                   RegistrationAction, *R, AutoRegisterCode)),503               {/* no deregistration */}});504          return Error::success();505        });506  }507}508 509Error ELFDebugObjectPlugin::notifyFailed(MaterializationResponsibility &MR) {510  std::lock_guard<std::mutex> Lock(PendingObjsLock);511  PendingObjs.erase(&MR);512  return Error::success();513}514 515void ELFDebugObjectPlugin::notifyTransferringResources(JITDylib &JD,516                                                       ResourceKey DstKey,517                                                       ResourceKey SrcKey) {518  // Debug objects are stored by ResourceKey only after registration.519  // Thus, pending objects don't need to be updated here.520  std::lock_guard<std::mutex> Lock(RegisteredObjsLock);521  auto SrcIt = RegisteredObjs.find(SrcKey);522  if (SrcIt != RegisteredObjs.end()) {523    // Resources from distinct MaterializationResponsibilitys can get merged524    // after emission, so we can have multiple debug objects per resource key.525    for (std::unique_ptr<DebugObject> &DebugObj : SrcIt->second)526      RegisteredObjs[DstKey].push_back(std::move(DebugObj));527    RegisteredObjs.erase(SrcIt);528  }529}530 531Error ELFDebugObjectPlugin::notifyRemovingResources(JITDylib &JD,532                                                    ResourceKey Key) {533  // Removing the resource for a pending object fails materialization, so they534  // get cleaned up in the notifyFailed() handler.535  std::lock_guard<std::mutex> Lock(RegisteredObjsLock);536  RegisteredObjs.erase(Key);537 538  // TODO: Implement unregister notifications.539  return Error::success();540}541 542} // namespace orc543} // namespace llvm544