Commit 6884fbc2 authored by Lang Hames's avatar Lang Hames
Browse files

[JITLink] Enable exception handling for ELF.

Adds the EHFrameSplitter and EHFrameEdgeFixer passes to the default JITLink
pass pipeline for ELF/x86-64, and teaches EHFrameEdgeFixer to handle some
new pointer encodings.

Together these changes enable exception handling (at least for the basic
cases that I've tested so far) for ELF/x86-64 objects loaded via JITLink.
parent ffc3e800
Loading
Loading
Loading
Loading
+142 −41
Original line number Diff line number Diff line
@@ -119,9 +119,10 @@ Error EHFrameSplitter::processBlock(LinkGraph &G, Block &B,
}

EHFrameEdgeFixer::EHFrameEdgeFixer(StringRef EHFrameSectionName,
                                   Edge::Kind Delta64, Edge::Kind NegDelta32)
    : EHFrameSectionName(EHFrameSectionName), Delta64(Delta64),
      NegDelta32(NegDelta32) {}
                                   unsigned PointerSize, Edge::Kind Delta64,
                                   Edge::Kind Delta32, Edge::Kind NegDelta32)
    : EHFrameSectionName(EHFrameSectionName), PointerSize(PointerSize),
      Delta64(Delta64), Delta32(Delta32), NegDelta32(NegDelta32) {}

Error EHFrameEdgeFixer::operator()(LinkGraph &G) {
  auto *EHFrame = G.findSectionByName(EHFrameSectionName);
@@ -134,6 +135,11 @@ Error EHFrameEdgeFixer::operator()(LinkGraph &G) {
    return Error::success();
  }

  // Check that we support the graph's pointer size.
  if (G.getPointerSize() != 4 && G.getPointerSize() != 8)
    return make_error<JITLinkError>(
        "EHFrameEdgeFixer only supports 32 and 64 bit targets");

  LLVM_DEBUG({
    dbgs() << "EHFrameEdgeFixer: Processing " << EHFrameSectionName << "...\n";
  });
@@ -258,7 +264,6 @@ Error EHFrameEdgeFixer::processBlock(ParseContext &PC, Block &B) {
Error EHFrameEdgeFixer::processCIE(ParseContext &PC, Block &B,
                                   size_t RecordOffset, size_t RecordLength,
                                   size_t CIEDeltaFieldOffset) {
  using namespace dwarf;

  LLVM_DEBUG(dbgs() << "      Record is CIE\n");

@@ -329,11 +334,12 @@ Error EHFrameEdgeFixer::processCIE(ParseContext &PC, Block &B,
      uint8_t LSDAPointerEncoding;
      if (auto Err = RecordReader.readInteger(LSDAPointerEncoding))
        return Err;
      if (LSDAPointerEncoding != (DW_EH_PE_pcrel | DW_EH_PE_absptr))
      if (!isSupportedPointerEncoding(LSDAPointerEncoding))
        return make_error<JITLinkError>(
            "Unsupported LSDA pointer encoding " +
            formatv("{0:x2}", LSDAPointerEncoding) + " in CIE at " +
            formatv("{0:x16}", CIESymbol.getAddress()));
      CIEInfo.LSDAPointerEncoding = LSDAPointerEncoding;
      break;
    }
    case 'P': {
@@ -341,7 +347,8 @@ Error EHFrameEdgeFixer::processCIE(ParseContext &PC, Block &B,
      if (auto Err = RecordReader.readInteger(PersonalityPointerEncoding))
        return Err;
      if (PersonalityPointerEncoding !=
          (DW_EH_PE_indirect | DW_EH_PE_pcrel | DW_EH_PE_sdata4))
          (dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel |
           dwarf::DW_EH_PE_sdata4))
        return make_error<JITLinkError>(
            "Unspported personality pointer "
            "encoding " +
@@ -356,12 +363,12 @@ Error EHFrameEdgeFixer::processCIE(ParseContext &PC, Block &B,
      uint8_t FDEPointerEncoding;
      if (auto Err = RecordReader.readInteger(FDEPointerEncoding))
        return Err;
      if (FDEPointerEncoding != (DW_EH_PE_pcrel | DW_EH_PE_absptr))
      if (!isSupportedPointerEncoding(FDEPointerEncoding))
        return make_error<JITLinkError>(
            "Unsupported FDE address pointer "
            "encoding " +
            "Unsupported FDE pointer encoding " +
            formatv("{0:x2}", FDEPointerEncoding) + " in CIE at " +
            formatv("{0:x16}", CIESymbol.getAddress()));
      CIEInfo.FDEPointerEncoding = FDEPointerEncoding;
      break;
    }
    default:
@@ -445,11 +452,13 @@ Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
    JITTargetAddress PCBeginFieldOffset = RecordReader.getOffset();
    auto PCEdgeItr = BlockEdges.find(RecordOffset + PCBeginFieldOffset);
    if (PCEdgeItr == BlockEdges.end()) {
      auto PCBeginDelta = readAbsolutePointer(PC.G, RecordReader);
      if (!PCBeginDelta)
        return PCBeginDelta.takeError();
      JITTargetAddress PCBegin =
          RecordAddress + PCBeginFieldOffset + *PCBeginDelta;
      auto PCBeginPtrInfo =
          readEncodedPointer(CIEInfo->FDEPointerEncoding,
                             RecordAddress + PCBeginFieldOffset, RecordReader);
      if (!PCBeginPtrInfo)
        return PCBeginPtrInfo.takeError();
      JITTargetAddress PCBegin = PCBeginPtrInfo->first;
      Edge::Kind PCBeginEdgeKind = PCBeginPtrInfo->second;
      LLVM_DEBUG({
        dbgs() << "        Adding edge at "
               << formatv("{0:x16}", RecordAddress + PCBeginFieldOffset)
@@ -458,7 +467,8 @@ Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
      auto PCBeginSym = getOrCreateSymbol(PC, PCBegin);
      if (!PCBeginSym)
        return PCBeginSym.takeError();
      B.addEdge(Delta64, RecordOffset + PCBeginFieldOffset, *PCBeginSym, 0);
      B.addEdge(PCBeginEdgeKind, RecordOffset + PCBeginFieldOffset, *PCBeginSym,
                0);
      PCBeginBlock = &PCBeginSym->getBlock();
    } else {
      auto &EI = PCEdgeItr->second;
@@ -479,38 +489,42 @@ Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
                                        " points at external block");
      }
      PCBeginBlock = &EI.Target->getBlock();
      if (auto Err = RecordReader.skip(PC.G.getPointerSize()))
      if (auto Err = RecordReader.skip(
              getPointerEncodingDataSize(CIEInfo->FDEPointerEncoding)))
        return Err;
    }

    // Add a keep-alive edge from the FDE target to the FDE to ensure that the
    // FDE is kept alive if its target is.
    assert(PCBeginBlock && "PC-begin block not recorded");
    LLVM_DEBUG({
      dbgs() << "        Adding keep-alive edge from target at "
             << formatv("{0:x16}", PCBeginBlock->getAddress()) << " to FDE at "
             << formatv("{0:x16}", RecordAddress) << "\n";
    });
    PCBeginBlock->addEdge(Edge::KeepAlive, 0, FDESymbol, 0);
  }

  // Skip over the PC range size field.
  if (auto Err = RecordReader.skip(PC.G.getPointerSize()))
  if (auto Err = RecordReader.skip(
          getPointerEncodingDataSize(CIEInfo->FDEPointerEncoding)))
    return Err;

  if (CIEInfo->FDEsHaveLSDAField) {
    uint64_t AugmentationDataSize;
    if (auto Err = RecordReader.readULEB128(AugmentationDataSize))
      return Err;
    if (AugmentationDataSize != PC.G.getPointerSize())
      return make_error<JITLinkError>(
          "Unexpected FDE augmentation data size (expected " +
          Twine(PC.G.getPointerSize()) + ", got " +
          Twine(AugmentationDataSize) + ") for FDE at " +
          formatv("{0:x16}", RecordAddress));

    JITTargetAddress LSDAFieldOffset = RecordReader.getOffset();
    auto LSDAEdgeItr = BlockEdges.find(RecordOffset + LSDAFieldOffset);
    if (LSDAEdgeItr == BlockEdges.end()) {
      auto LSDADelta = readAbsolutePointer(PC.G, RecordReader);
      if (!LSDADelta)
        return LSDADelta.takeError();
      JITTargetAddress LSDA = RecordAddress + LSDAFieldOffset + *LSDADelta;
      auto LSDAPointerInfo =
          readEncodedPointer(CIEInfo->LSDAPointerEncoding,
                             RecordAddress + LSDAFieldOffset, RecordReader);
      if (!LSDAPointerInfo)
        return LSDAPointerInfo.takeError();
      JITTargetAddress LSDA = LSDAPointerInfo->first;
      Edge::Kind LSDAEdgeKind = LSDAPointerInfo->second;
      auto LSDASym = getOrCreateSymbol(PC, LSDA);
      if (!LSDASym)
        return LSDASym.takeError();
@@ -519,7 +533,7 @@ Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
               << formatv("{0:x16}", RecordAddress + LSDAFieldOffset)
               << " to LSDA at " << formatv("{0:x16}", LSDA) << "\n";
      });
      B.addEdge(Delta64, RecordOffset + LSDAFieldOffset, *LSDASym, 0);
      B.addEdge(LSDAEdgeKind, RecordOffset + LSDAFieldOffset, *LSDASym, 0);
    } else {
      LLVM_DEBUG({
        auto &EI = LSDAEdgeItr->second;
@@ -530,7 +544,7 @@ Error EHFrameEdgeFixer::processFDE(ParseContext &PC, Block &B,
          dbgs() << " + " << formatv("{0:x16}", EI.Addend);
        dbgs() << "\n";
      });
      if (auto Err = RecordReader.skip(PC.G.getPointerSize()))
      if (auto Err = RecordReader.skip(AugmentationDataSize))
        return Err;
    }
  } else {
@@ -581,23 +595,110 @@ EHFrameEdgeFixer::parseAugmentationString(BinaryStreamReader &RecordReader) {
  return std::move(AugInfo);
}

Expected<JITTargetAddress>
EHFrameEdgeFixer::readAbsolutePointer(LinkGraph &G,
bool EHFrameEdgeFixer::isSupportedPointerEncoding(uint8_t PointerEncoding) {
  using namespace dwarf;

  // We only support PC-rel for now.
  if ((PointerEncoding & 0x70) != DW_EH_PE_pcrel)
    return false;

  // readEncodedPointer does not handle indirect.
  if (PointerEncoding & DW_EH_PE_indirect)
    return false;

  // Supported datatypes.
  switch (PointerEncoding & 0xf) {
  case DW_EH_PE_absptr:
  case DW_EH_PE_udata4:
  case DW_EH_PE_udata8:
  case DW_EH_PE_sdata4:
  case DW_EH_PE_sdata8:
    return true;
  }

  return false;
}

unsigned EHFrameEdgeFixer::getPointerEncodingDataSize(uint8_t PointerEncoding) {
  using namespace dwarf;

  assert(isSupportedPointerEncoding(PointerEncoding) &&
         "Unsupported pointer encoding");
  switch (PointerEncoding & 0xf) {
  case DW_EH_PE_absptr:
    return PointerSize;
  case DW_EH_PE_udata4:
  case DW_EH_PE_sdata4:
    return 4;
  case DW_EH_PE_udata8:
  case DW_EH_PE_sdata8:
    return 8;
  default:
    llvm_unreachable("Unsupported encoding");
  }
}

Expected<std::pair<JITTargetAddress, Edge::Kind>>
EHFrameEdgeFixer::readEncodedPointer(uint8_t PointerEncoding,
                                     JITTargetAddress PointerFieldAddress,
                                     BinaryStreamReader &RecordReader) {
  static_assert(sizeof(JITTargetAddress) == sizeof(uint64_t),
                "Result must be able to hold a uint64_t");
  assert(isSupportedPointerEncoding(PointerEncoding) &&
         "Unsupported pointer encoding");

  using namespace dwarf;

  // Isolate data type, remap absptr to udata4 or udata8. This relies on us
  // having verified that the graph uses 32-bit or 64-bit pointers only at the
  // start of this pass.
  uint8_t EffectiveType = PointerEncoding & 0xf;
  if (EffectiveType == DW_EH_PE_absptr)
    EffectiveType = (PointerSize == 8) ? DW_EH_PE_udata8 : DW_EH_PE_udata4;

  JITTargetAddress Addr;
  if (G.getPointerSize() == 8) {
    if (auto Err = RecordReader.readInteger(Addr))
  Edge::Kind PointerEdgeKind;
  switch (EffectiveType) {
  case DW_EH_PE_udata4: {
    uint32_t Val;
    if (auto Err = RecordReader.readInteger(Val))
      return std::move(Err);
  } else if (G.getPointerSize() == 4) {
    uint32_t Addr32;
    if (auto Err = RecordReader.readInteger(Addr32))
    Addr = PointerFieldAddress + Val;
    PointerEdgeKind = Delta32;
    break;
  }
  case DW_EH_PE_udata8: {
    uint64_t Val;
    if (auto Err = RecordReader.readInteger(Val))
      return std::move(Err);
    Addr = Addr32;
  } else
    llvm_unreachable("Pointer size is not 32-bit or 64-bit");
  return Addr;
    Addr = PointerFieldAddress + Val;
    PointerEdgeKind = Delta64;
    break;
  }
  case DW_EH_PE_sdata4: {
    int32_t Val;
    if (auto Err = RecordReader.readInteger(Val))
      return std::move(Err);
    Addr = PointerFieldAddress + Val;
    PointerEdgeKind = Delta32;
    break;
  }
  case DW_EH_PE_sdata8: {
    int64_t Val;
    if (auto Err = RecordReader.readInteger(Val))
      return std::move(Err);
    Addr = PointerFieldAddress + Val;
    PointerEdgeKind = Delta64;
    break;
  }
  }

  if (PointerEdgeKind == Edge::Invalid)
    return make_error<JITLinkError>(
        "Unspported edge kind for encoded pointer at " +
        formatv("{0:x}", PointerFieldAddress));

  return std::make_pair(Addr, Delta64);
}

Expected<Symbol &> EHFrameEdgeFixer::getOrCreateSymbol(ParseContext &PC,
+14 −3
Original line number Diff line number Diff line
@@ -40,7 +40,8 @@ private:
/// edges.
class EHFrameEdgeFixer {
public:
  EHFrameEdgeFixer(StringRef EHFrameSectionName, Edge::Kind Delta64,
  EHFrameEdgeFixer(StringRef EHFrameSectionName, unsigned PointerSize,
                   Edge::Kind Delta64, Edge::Kind Delta32,
                   Edge::Kind NegDelta32);
  Error operator()(LinkGraph &G);

@@ -57,6 +58,8 @@ private:
    CIEInformation(Symbol &CIESymbol) : CIESymbol(&CIESymbol) {}
    Symbol *CIESymbol = nullptr;
    bool FDEsHaveLSDAField = false;
    uint8_t FDEPointerEncoding = 0;
    uint8_t LSDAPointerEncoding = 0;
  };

  struct EdgeTarget {
@@ -96,12 +99,20 @@ private:

  Expected<AugmentationInfo>
  parseAugmentationString(BinaryStreamReader &RecordReader);
  Expected<JITTargetAddress>
  readAbsolutePointer(LinkGraph &G, BinaryStreamReader &RecordReader);

  static bool isSupportedPointerEncoding(uint8_t PointerEncoding);
  unsigned getPointerEncodingDataSize(uint8_t PointerEncoding);
  Expected<std::pair<JITTargetAddress, Edge::Kind>>
  readEncodedPointer(uint8_t PointerEncoding,
                     JITTargetAddress PointerFieldAddress,
                     BinaryStreamReader &RecordReader);

  Expected<Symbol &> getOrCreateSymbol(ParseContext &PC, JITTargetAddress Addr);

  StringRef EHFrameSectionName;
  unsigned PointerSize;
  Edge::Kind Delta64;
  Edge::Kind Delta32;
  Edge::Kind NegDelta32;
};

+31 −18
Original line number Diff line number Diff line
@@ -11,12 +11,14 @@
//===----------------------------------------------------------------------===//

#include "llvm/ExecutionEngine/JITLink/ELF_x86_64.h"
#include "BasicGOTAndStubsBuilder.h"
#include "JITLinkGeneric.h"
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Support/Endian.h"

#include "BasicGOTAndStubsBuilder.h"
#include "EHFrameSupportImpl.h"
#include "JITLinkGeneric.h"

#define DEBUG_TYPE "jitlink"

using namespace llvm;
@@ -238,6 +240,8 @@ private:
    switch (Type) {
    case ELF::R_X86_64_PC32:
      return ELF_x86_64_Edges::ELFX86RelocationKind::PCRel32;
    case ELF::R_X86_64_PC64:
      return ELF_x86_64_Edges::ELFX86RelocationKind::Delta64;
    case ELF::R_X86_64_64:
      return ELF_x86_64_Edges::ELFX86RelocationKind::Pointer64;
    case ELF::R_X86_64_GOTPCREL:
@@ -404,9 +408,6 @@ private:
      LLVM_DEBUG({
        dbgs() << "Adding relocations from section " << *RelSectName << "\n";
      });
      // Deal with .eh_frame later
      if (*RelSectName == StringRef(".rela.eh_frame"))
        continue;

      auto UpdateSection = Obj.getSection(SecRef.sh_info);
      if (!UpdateSection)
@@ -734,6 +735,11 @@ private:
      *(ulittle64_t *)FixupPtr = Value;
      break;
    }
    case ELFX86RelocationKind::Delta64: {
      int64_t Value = E.getTarget().getAddress() + E.getAddend() - FixupAddress;
      *(little64_t *)FixupPtr = Value;
      break;
    }
    }
    return Error::success();
  }
@@ -760,6 +766,12 @@ void link_ELF_x86_64(std::unique_ptr<LinkGraph> G,
                     std::unique_ptr<JITLinkContext> Ctx) {
  PassConfiguration Config;

  if (Ctx->shouldAddDefaultTargetPasses(G->getTargetTriple())) {

    Config.PrePrunePasses.push_back(EHFrameSplitter(".eh_frame"));
    Config.PrePrunePasses.push_back(EHFrameEdgeFixer(
        ".eh_frame", G->getPointerSize(), Delta64, Delta32, NegDelta32));

    // Construct a JITLinker and run the link function.
    // Add a mark-live pass.
    if (auto MarkLive = Ctx->getMarkLivePass(G->getTargetTriple()))
@@ -775,6 +787,7 @@ void link_ELF_x86_64(std::unique_ptr<LinkGraph> G,

    // Add GOT/Stubs optimizer pass.
    Config.PreFixupPasses.push_back(optimizeELF_x86_64_GOTAndStubs);
  }

  if (auto Err = Ctx->modifyPassConfig(G->getTargetTriple(), Config))
    return Ctx->notifyFailed(std::move(Err));
+1 −1
Original line number Diff line number Diff line
@@ -16,10 +16,10 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
#include "llvm/Object/MachO.h"

#include "EHFrameSupportImpl.h"
#include "JITLinkGeneric.h"
#include "llvm/Object/MachO.h"

#include <list>

+2 −2
Original line number Diff line number Diff line
@@ -669,8 +669,8 @@ void link_MachO_x86_64(std::unique_ptr<LinkGraph> G,
  if (Ctx->shouldAddDefaultTargetPasses(G->getTargetTriple())) {
    // Add eh-frame passses.
    Config.PrePrunePasses.push_back(EHFrameSplitter("__eh_frame"));
    Config.PrePrunePasses.push_back(
        EHFrameEdgeFixer("__eh_frame", Delta64, NegDelta32));
    Config.PrePrunePasses.push_back(EHFrameEdgeFixer(
        "__eh_frame", G->getPointerSize(), Delta64, Delta32, NegDelta32));

    // Add a mark-live pass.
    if (auto MarkLive = Ctx->getMarkLivePass(G->getTargetTriple()))
Loading