Add EH support to the MCJIT.

This gets exception handling working on ELF and Macho (x86-64 at least).
Other than the EH frame registration, this patch also implements support
for GOT relocations which are used to locate the personality function on
MachO.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@181167 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Rafael Espindola 2013-05-05 20:43:10 +00:00
parent 9f2bebbc28
commit a2e40fbd62
11 changed files with 188 additions and 14 deletions

View File

@ -66,6 +66,11 @@ public:
/// ///
/// Returns true if an error occurred, false otherwise. /// Returns true if an error occurred, false otherwise.
virtual bool applyPermissions(std::string *ErrMsg = 0) = 0; virtual bool applyPermissions(std::string *ErrMsg = 0) = 0;
/// Register the EH frames with the runtime so that c++ exceptions work. The
/// default implementation does nothing. Look at SectionMemoryManager for one
/// that uses __register_frame.
virtual void registerEHFrames(StringRef SectionData);
}; };
class RuntimeDyld { class RuntimeDyld {
@ -109,6 +114,8 @@ public:
void mapSectionAddress(const void *LocalAddress, uint64_t TargetAddress); void mapSectionAddress(const void *LocalAddress, uint64_t TargetAddress);
StringRef getErrorString(); StringRef getErrorString();
StringRef getEHFrameSection();
}; };
} // end namespace llvm } // end namespace llvm

View File

@ -72,6 +72,8 @@ public:
/// \returns true if an error occurred, false otherwise. /// \returns true if an error occurred, false otherwise.
virtual bool applyPermissions(std::string *ErrMsg = 0); virtual bool applyPermissions(std::string *ErrMsg = 0);
void registerEHFrames(StringRef SectionData);
/// This method returns the address of the specified function. As such it is /// This method returns the address of the specified function. As such it is
/// only useful for resolving library symbols, not code generated symbols. /// only useful for resolving library symbols, not code generated symbols.
/// ///

View File

@ -168,15 +168,14 @@ void MCJIT::finalizeObject() {
// If the call to Dyld.resolveRelocations() is removed from loadObject() // If the call to Dyld.resolveRelocations() is removed from loadObject()
// we'll need to do that here. // we'll need to do that here.
loadObject(M); loadObject(M);
} else {
// Set page permissions. // Resolve any relocations.
MemMgr->applyPermissions(); Dyld.resolveRelocations();
return;
} }
// Resolve any relocations. StringRef EHData = Dyld.getEHFrameSection();
Dyld.resolveRelocations(); if (!EHData.empty())
MemMgr->registerEHFrames(EHData);
// Set page permissions. // Set page permissions.
MemMgr->applyPermissions(); MemMgr->applyPermissions();

View File

@ -146,6 +146,38 @@ bool SectionMemoryManager::applyPermissions(std::string *ErrMsg)
return false; return false;
} }
// Determine whether we can register EH tables.
#if (defined(__GNUC__) && !defined(__ARM_EABI__) && \
!defined(__USING_SJLJ_EXCEPTIONS__))
#define HAVE_EHTABLE_SUPPORT 1
#else
#define HAVE_EHTABLE_SUPPORT 0
#endif
#if HAVE_EHTABLE_SUPPORT
extern "C" void __register_frame(void*);
static const char *processFDE(const char *Entry) {
const char *P = Entry;
uint32_t Length = *((uint32_t*)P);
P += 4;
uint32_t Offset = *((uint32_t*)P);
if (Offset != 0)
__register_frame((void*)Entry);
return P + Length;
}
#endif
void SectionMemoryManager::registerEHFrames(StringRef SectionData) {
#if HAVE_EHTABLE_SUPPORT
const char *P = SectionData.data();
const char *End = SectionData.data() + SectionData.size();
do {
P = processFDE(P);
} while(P != End);
#endif
}
error_code SectionMemoryManager::applyMemoryGroupPermissions(MemoryGroup &MemGroup, error_code SectionMemoryManager::applyMemoryGroupPermissions(MemoryGroup &MemGroup,
unsigned Permissions) { unsigned Permissions) {

View File

@ -25,10 +25,15 @@ using namespace llvm::object;
// Empty out-of-line virtual destructor as the key function. // Empty out-of-line virtual destructor as the key function.
RTDyldMemoryManager::~RTDyldMemoryManager() {} RTDyldMemoryManager::~RTDyldMemoryManager() {}
void RTDyldMemoryManager::registerEHFrames(StringRef SectionData) {}
RuntimeDyldImpl::~RuntimeDyldImpl() {} RuntimeDyldImpl::~RuntimeDyldImpl() {}
namespace llvm { namespace llvm {
StringRef RuntimeDyldImpl::getEHFrameSection() {
return StringRef();
}
// Resolve the relocations for all symbols we currently know about. // Resolve the relocations for all symbols we currently know about.
void RuntimeDyldImpl::resolveRelocations() { void RuntimeDyldImpl::resolveRelocations() {
// First, resolve relocations associated with external symbols. // First, resolve relocations associated with external symbols.
@ -174,7 +179,7 @@ void RuntimeDyldImpl::emitCommonSymbols(ObjectImage &Obj,
if (!Addr) if (!Addr)
report_fatal_error("Unable to allocate memory for common symbols!"); report_fatal_error("Unable to allocate memory for common symbols!");
uint64_t Offset = 0; uint64_t Offset = 0;
Sections.push_back(SectionEntry(StringRef(), Addr, TotalSize, TotalSize, 0)); Sections.push_back(SectionEntry(StringRef(), Addr, TotalSize, 0));
memset(Addr, 0, TotalSize); memset(Addr, 0, TotalSize);
DEBUG(dbgs() << "emitCommonSection SectionID: " << SectionID DEBUG(dbgs() << "emitCommonSection SectionID: " << SectionID
@ -292,8 +297,7 @@ unsigned RuntimeDyldImpl::emitSection(ObjectImage &Obj,
<< "\n"); << "\n");
} }
Sections.push_back(SectionEntry(Name, Addr, Allocate, DataSize, Sections.push_back(SectionEntry(Name, Addr, DataSize, (uintptr_t)pData));
(uintptr_t)pData));
return SectionID; return SectionID;
} }
@ -544,4 +548,8 @@ StringRef RuntimeDyld::getErrorString() {
return Dyld->getErrorString(); return Dyld->getErrorString();
} }
StringRef RuntimeDyld::getEHFrameSection() {
return Dyld->getEHFrameSection();
}
} // end namespace llvm } // end namespace llvm

View File

@ -151,6 +151,14 @@ void DyldELFObject<ELFT>::updateSymbolAddress(const SymbolRef &SymRef,
namespace llvm { namespace llvm {
StringRef RuntimeDyldELF::getEHFrameSection() {
for (int i = 0, e = Sections.size(); i != e; ++i) {
if (Sections[i].Name == ".eh_frame")
return StringRef((const char*)Sections[i].Address, Sections[i].Size);
}
return StringRef();
}
ObjectImage *RuntimeDyldELF::createObjectImage(ObjectBuffer *Buffer) { ObjectImage *RuntimeDyldELF::createObjectImage(ObjectBuffer *Buffer) {
if (Buffer->getBufferSize() < ELF::EI_NIDENT) if (Buffer->getBufferSize() < ELF::EI_NIDENT)
llvm_unreachable("Unexpected ELF object size"); llvm_unreachable("Unexpected ELF object size");

View File

@ -96,6 +96,7 @@ public:
StubMap &Stubs); StubMap &Stubs);
virtual bool isCompatibleFormat(const ObjectBuffer *Buffer) const; virtual bool isCompatibleFormat(const ObjectBuffer *Buffer) const;
virtual ObjectImage *createObjectImage(ObjectBuffer *InputBuffer); virtual ObjectImage *createObjectImage(ObjectBuffer *InputBuffer);
virtual StringRef getEHFrameSection();
virtual ~RuntimeDyldELF(); virtual ~RuntimeDyldELF();
}; };

View File

@ -49,7 +49,7 @@ public:
/// Address - address in the linker's memory where the section resides. /// Address - address in the linker's memory where the section resides.
uint8_t *Address; uint8_t *Address;
/// Size - section size. /// Size - section size. Doesn't include the stubs.
size_t Size; size_t Size;
/// LoadAddress - the address of the section in the target process's memory. /// LoadAddress - the address of the section in the target process's memory.
@ -67,9 +67,9 @@ public:
uintptr_t ObjAddress; uintptr_t ObjAddress;
SectionEntry(StringRef name, uint8_t *address, size_t size, SectionEntry(StringRef name, uint8_t *address, size_t size,
uintptr_t stubOffset, uintptr_t objAddress) uintptr_t objAddress)
: Name(name), Address(address), Size(size), LoadAddress((uintptr_t)address), : Name(name), Address(address), Size(size), LoadAddress((uintptr_t)address),
StubOffset(stubOffset), ObjAddress(objAddress) {} StubOffset(size), ObjAddress(objAddress) {}
}; };
/// RelocationEntry - used to represent relocations internally in the dynamic /// RelocationEntry - used to represent relocations internally in the dynamic
@ -174,6 +174,8 @@ protected:
return 16; return 16;
else if (Arch == Triple::ppc64) else if (Arch == Triple::ppc64)
return 44; return 44;
else if (Arch == Triple::x86_64)
return 8; // GOT
else if (Arch == Triple::systemz) else if (Arch == Triple::systemz)
return 16; return 16;
else else
@ -332,6 +334,8 @@ public:
StringRef getErrorString() { return ErrorStr; } StringRef getErrorString() { return ErrorStr; }
virtual bool isCompatibleFormat(const ObjectBuffer *Buffer) const = 0; virtual bool isCompatibleFormat(const ObjectBuffer *Buffer) const = 0;
virtual StringRef getEHFrameSection();
}; };
} // end namespace llvm } // end namespace llvm

View File

@ -21,6 +21,69 @@ using namespace llvm::object;
namespace llvm { namespace llvm {
static unsigned char *processFDE(unsigned char *P, intptr_t DeltaForText, intptr_t DeltaForEH) {
uint32_t Length = *((uint32_t*)P);
P += 4;
unsigned char *Ret = P + Length;
uint32_t Offset = *((uint32_t*)P);
if (Offset == 0) // is a CIE
return Ret;
P += 4;
intptr_t FDELocation = *((intptr_t*)P);
intptr_t NewLocation = FDELocation - DeltaForText;
*((intptr_t*)P) = NewLocation;
P += sizeof(intptr_t);
// Skip the FDE address range
P += sizeof(intptr_t);
uint8_t Augmentationsize = *P;
P += 1;
if (Augmentationsize != 0) {
intptr_t LSDA = *((intptr_t*)P);
intptr_t NewLSDA = LSDA - DeltaForEH;
*((intptr_t*)P) = NewLSDA;
}
return Ret;
}
static intptr_t computeDelta(SectionEntry *A, SectionEntry *B) {
intptr_t ObjDistance = A->ObjAddress - B->ObjAddress;
intptr_t MemDistance = A->LoadAddress - B->LoadAddress;
return ObjDistance - MemDistance;
}
StringRef RuntimeDyldMachO::getEHFrameSection() {
SectionEntry *Text = NULL;
SectionEntry *EHFrame = NULL;
SectionEntry *ExceptTab = NULL;
for (int i = 0, e = Sections.size(); i != e; ++i) {
if (Sections[i].Name == "__eh_frame")
EHFrame = &Sections[i];
else if (Sections[i].Name == "__text")
Text = &Sections[i];
else if (Sections[i].Name == "__gcc_except_tab")
ExceptTab = &Sections[i];
}
if (Text == NULL || EHFrame == NULL)
return StringRef();
intptr_t DeltaForText = computeDelta(Text, EHFrame);
intptr_t DeltaForEH = 0;
if (ExceptTab)
DeltaForEH = computeDelta(ExceptTab, EHFrame);
unsigned char *P = EHFrame->Address;
unsigned char *End = P + EHFrame->Size;
do {
P = processFDE(P, DeltaForText, DeltaForEH);
} while(P != End);
return StringRef((char*)EHFrame->Address, EHFrame->Size);
}
void RuntimeDyldMachO::resolveRelocation(const RelocationEntry &RE, void RuntimeDyldMachO::resolveRelocation(const RelocationEntry &RE,
uint64_t Value) { uint64_t Value) {
const SectionEntry &Section = Sections[RE.SectionID]; const SectionEntry &Section = Sections[RE.SectionID];
@ -267,7 +330,30 @@ void RuntimeDyldMachO::processRelocationRef(unsigned SectionID,
Value.Addend = Addend - Addr; Value.Addend = Addend - Addr;
} }
if (Arch == Triple::arm && (RelType & 0xf) == macho::RIT_ARM_Branch24Bit) { if (Arch == Triple::x86_64 && RelType == macho::RIT_X86_64_GOT) {
assert(IsPCRel);
assert(Size == 2);
StubMap::const_iterator i = Stubs.find(Value);
uint8_t *Addr;
if (i != Stubs.end()) {
Addr = Section.Address + i->second;
} else {
Stubs[Value] = Section.StubOffset;
uint8_t *GOTEntry = Section.Address + Section.StubOffset;
RelocationEntry RE(SectionID, Section.StubOffset,
macho::RIT_X86_64_Unsigned, Value.Addend - 4, false,
3);
if (Value.SymbolName)
addRelocationForSymbol(RE, Value.SymbolName);
else
addRelocationForSection(RE, Value.SectionID);
Section.StubOffset += 8;
Addr = GOTEntry;
}
resolveRelocation(Section, Offset, (uint64_t)Addr,
macho::RIT_X86_64_Unsigned, 4, true, 2);
} else if (Arch == Triple::arm &&
(RelType & 0xf) == macho::RIT_ARM_Branch24Bit) {
// This is an ARM branch relocation, need to use a stub function. // This is an ARM branch relocation, need to use a stub function.
// Look up for existing stub. // Look up for existing stub.

View File

@ -65,6 +65,7 @@ public:
const SymbolTableMap &Symbols, const SymbolTableMap &Symbols,
StubMap &Stubs); StubMap &Stubs);
virtual bool isCompatibleFormat(const ObjectBuffer *Buffer) const; virtual bool isCompatibleFormat(const ObjectBuffer *Buffer) const;
virtual StringRef getEHFrameSection();
}; };
} // end namespace llvm } // end namespace llvm

View File

@ -0,0 +1,26 @@
; RUN: %lli_mcjit %s
declare i8* @__cxa_allocate_exception(i64)
declare void @__cxa_throw(i8*, i8*, i8*)
declare i32 @__gxx_personality_v0(...)
@_ZTIi = external constant i8*
define void @throwException() {
%exception = tail call i8* @__cxa_allocate_exception(i64 4)
call void @__cxa_throw(i8* %exception, i8* bitcast (i8** @_ZTIi to i8*), i8* null)
unreachable
}
define i32 @main() {
entry:
invoke void @throwException()
to label %try.cont unwind label %lpad
lpad:
%p = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*)
catch i8* bitcast (i8** @_ZTIi to i8*)
br label %try.cont
try.cont:
ret i32 0
}