}
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);
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";
});
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");
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': {
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 " +
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:
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)
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;
" 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();
<< 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;
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 {
return std::move(AugInfo);
}
-Expected<JITTargetAddress>
-EHFrameEdgeFixer::readAbsolutePointer(LinkGraph &G,
- BinaryStreamReader &RecordReader) {
+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,
//===----------------------------------------------------------------------===//
#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;
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:
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)
*(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();
}
std::unique_ptr<JITLinkContext> Ctx) {
PassConfiguration Config;
- // Construct a JITLinker and run the link function.
- // Add a mark-live pass.
- if (auto MarkLive = Ctx->getMarkLivePass(G->getTargetTriple()))
- Config.PrePrunePasses.push_back(std::move(MarkLive));
- else
- Config.PrePrunePasses.push_back(markAllSymbolsLive);
+ if (Ctx->shouldAddDefaultTargetPasses(G->getTargetTriple())) {
- // Add an in-place GOT/Stubs pass.
- Config.PostPrunePasses.push_back([](LinkGraph &G) -> Error {
- ELF_x86_64_GOTAndStubsBuilder(G).run();
- return Error::success();
- });
+ Config.PrePrunePasses.push_back(EHFrameSplitter(".eh_frame"));
+ Config.PrePrunePasses.push_back(EHFrameEdgeFixer(
+ ".eh_frame", G->getPointerSize(), Delta64, Delta32, NegDelta32));
- // Add GOT/Stubs optimizer pass.
- Config.PreFixupPasses.push_back(optimizeELF_x86_64_GOTAndStubs);
+ // Construct a JITLinker and run the link function.
+ // Add a mark-live pass.
+ if (auto MarkLive = Ctx->getMarkLivePass(G->getTargetTriple()))
+ Config.PrePrunePasses.push_back(std::move(MarkLive));
+ else
+ Config.PrePrunePasses.push_back(markAllSymbolsLive);
+
+ // Add an in-place GOT/Stubs pass.
+ Config.PostPrunePasses.push_back([](LinkGraph &G) -> Error {
+ ELF_x86_64_GOTAndStubsBuilder(G).run();
+ return Error::success();
+ });
+
+ // 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));
--- /dev/null
+# REQUIRES: asserts
+# RUN: llvm-mc -triple=x86_64-unknown-linux -position-independent \
+# RUN: -filetype=obj -o %t %s
+# RUN: llvm-jitlink -debug-only=jitlink -define-abs bar=0x01 \
+# RUN: -define-abs _ZTIi=0x02 -noexec %t 2>&1 | FileCheck %s
+#
+# Check that a basic .eh-frame section is recognized and parsed. We
+# Expect to see two FDEs with corresponding keep-alive edges.
+#
+# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at
+# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at
+
+ .text
+ .file "exceptions.cpp"
+ .globl foo
+ .p2align 4, 0x90
+ .type foo,@function
+foo:
+ .cfi_startproc
+
+ pushq %rax
+ .cfi_def_cfa_offset 16
+ movl $4, %edi
+ callq __cxa_allocate_exception@PLT
+ movl $1, (%rax)
+ movq _ZTIi@GOTPCREL(%rip), %rsi
+ movq %rax, %rdi
+ xorl %edx, %edx
+ callq __cxa_throw@PLT
+.Lfunc_end0:
+ .size foo, .Lfunc_end0-foo
+ .cfi_endproc
+
+ .globl main
+ .p2align 4, 0x90
+ .type main,@function
+main:
+.Lfunc_begin0:
+ .cfi_startproc
+ .cfi_personality 155, DW.ref.__gxx_personality_v0
+ .cfi_lsda 27, .Lexception0
+
+ pushq %rbx
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbx, -16
+ xorl %ebx, %ebx
+.Ltmp0:
+ callq bar@PLT
+.Ltmp1:
+
+ movl %ebx, %eax
+ popq %rbx
+ .cfi_def_cfa_offset 8
+ retq
+.LBB1_1:
+ .cfi_def_cfa_offset 16
+.Ltmp2:
+ movq %rax, %rdi
+ callq __cxa_begin_catch@PLT
+ callq __cxa_end_catch@PLT
+ movl $1, %ebx
+ movl %ebx, %eax
+ popq %rbx
+ .cfi_def_cfa_offset 8
+ retq
+.Lfunc_end1:
+ .size main, .Lfunc_end1-main
+ .cfi_endproc
+ .section .gcc_except_table,"a",@progbits
+ .p2align 2
+GCC_except_table1:
+.Lexception0:
+ .byte 255
+ .byte 156
+ .uleb128 .Lttbase0-.Lttbaseref0
+.Lttbaseref0:
+ .byte 1
+ .uleb128 .Lcst_end0-.Lcst_begin0
+.Lcst_begin0:
+ .uleb128 .Ltmp0-.Lfunc_begin0
+ .uleb128 .Ltmp1-.Ltmp0
+ .uleb128 .Ltmp2-.Lfunc_begin0
+ .byte 1
+ .uleb128 .Ltmp1-.Lfunc_begin0
+ .uleb128 .Lfunc_end1-.Ltmp1
+ .byte 0
+ .byte 0
+.Lcst_end0:
+ .byte 1
+
+ .byte 0
+ .p2align 2
+
+.Ltmp3:
+ .quad .L_ZTIi.DW.stub-.Ltmp3
+.Lttbase0:
+ .p2align 2
+
+ .data
+ .p2align 3
+.L_ZTIi.DW.stub:
+ .quad _ZTIi
+ .hidden DW.ref.__gxx_personality_v0
+ .weak DW.ref.__gxx_personality_v0
+ .section .data.DW.ref.__gxx_personality_v0,"aGw",@progbits,DW.ref.__gxx_personality_v0,comdat
+ .p2align 3
+ .type DW.ref.__gxx_personality_v0,@object
+ .size DW.ref.__gxx_personality_v0, 8
+DW.ref.__gxx_personality_v0:
+ .quad __gxx_personality_v0
+ .ident "clang version 12.0.0 (git@github.com:llvm/llvm-project.git afd483e57d166418e94a65bd9716e7dc4c114eed)"
+ .section ".note.GNU-stack","",@progbits
+ .addrsig
+ .addrsig_sym __gxx_personality_v0
+ .addrsig_sym _ZTIi