1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-18 18:42:46 +02:00

[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.
This commit is contained in:
Lang Hames 2021-01-25 15:14:22 +11:00
parent d5b70bbb38
commit f7963e4e27
6 changed files with 305 additions and 65 deletions

View File

@ -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,
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,

View File

@ -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;
};

View File

@ -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,21 +766,28 @@ void link_ELF_x86_64(std::unique_ptr<LinkGraph> G,
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));

View File

@ -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>

View File

@ -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()))

View File

@ -0,0 +1,115 @@
# 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 -noexec %t 2>&1 | \
# RUN: 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