mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-07-24 22:24:54 +00:00
[RuntimeDyldELF] Improve GOT support
Summary: This is the first in a series of patches to eventually add support for TLS relocations to RuntimeDyld. This patch resolves an issue in the current GOT handling, where GOT entries would be reused between object files, which leads to the same situation that necessitates the GOT in the first place, i.e. that the 32-bit offset can not cover all of the address space. Thus this patch makes the GOT object-file-local. Unfortunately, this still isn't quite enough, because the MemoryManager does not yet guarantee that sections are allocated sufficiently close to each other, even if they belong to the same object file. To address this concern, this patch also adds a small API abstraction on top of the GOT allocation mechanism that will allow (temporarily, until the MemoryManager is improved) using the stub mechanism instead of allocating a different section. The actual switch from separate section to stub mechanism will be part of a follow-on commit, so that it can be easily reverted independently at the appropriate time. Test Plan: Includes a test case where the GOT of two object files is artificially forced to be apart by several GB. Reviewers: lhames Reviewed By: lhames Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D8813 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@234839 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "RuntimeDyldELF.h"
|
||||
#include "RuntimeDyldCheckerImpl.h"
|
||||
#include "llvm/ADT/IntervalMap.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
@@ -185,7 +186,7 @@ namespace llvm {
|
||||
|
||||
RuntimeDyldELF::RuntimeDyldELF(RuntimeDyld::MemoryManager &MemMgr,
|
||||
RuntimeDyld::SymbolResolver &Resolver)
|
||||
: RuntimeDyldImpl(MemMgr, Resolver) {}
|
||||
: RuntimeDyldImpl(MemMgr, Resolver), GOTSectionID(0), CurrentGOTIndex(0) {}
|
||||
RuntimeDyldELF::~RuntimeDyldELF() {}
|
||||
|
||||
void RuntimeDyldELF::registerEHFrames() {
|
||||
@@ -245,27 +246,16 @@ void RuntimeDyldELF::resolveX86_64Relocation(const SectionEntry &Section,
|
||||
<< format("%p\n", Section.Address + Offset));
|
||||
break;
|
||||
}
|
||||
case ELF::R_X86_64_GOTPCREL: {
|
||||
// findGOTEntry returns the 'G + GOT' part of the relocation calculation
|
||||
// based on the load/target address of the GOT (not the current/local addr).
|
||||
uint64_t GOTAddr = findGOTEntry(Value, SymOffset);
|
||||
uint64_t FinalAddress = Section.LoadAddress + Offset;
|
||||
// The processRelocationRef method combines the symbol offset and the addend
|
||||
// and in most cases that's what we want. For this relocation type, we need
|
||||
// the raw addend, so we subtract the symbol offset to get it.
|
||||
int64_t RealOffset = GOTAddr + Addend - SymOffset - FinalAddress;
|
||||
assert(RealOffset <= INT32_MAX && RealOffset >= INT32_MIN);
|
||||
int32_t TruncOffset = (RealOffset & 0xFFFFFFFF);
|
||||
support::ulittle32_t::ref(Section.Address + Offset) = TruncOffset;
|
||||
break;
|
||||
}
|
||||
case ELF::R_X86_64_PC32: {
|
||||
// Get the placeholder value from the generated object since
|
||||
// a previous relocation attempt may have overwritten the loaded version
|
||||
support::ulittle32_t::ref Placeholder(
|
||||
(void *)(Section.ObjAddress + Offset));
|
||||
uint64_t FinalAddress = Section.LoadAddress + Offset;
|
||||
int64_t RealOffset = Placeholder + Value + Addend - FinalAddress;
|
||||
int64_t RealOffset = Value + Addend - FinalAddress;
|
||||
// Don't add the placeholder if this is a stub
|
||||
if (Offset < Section.Size)
|
||||
RealOffset += Placeholder;
|
||||
assert(RealOffset <= INT32_MAX && RealOffset >= INT32_MIN);
|
||||
int32_t TruncOffset = (RealOffset & 0xFFFFFFFF);
|
||||
support::ulittle32_t::ref(Section.Address + Offset) = TruncOffset;
|
||||
@@ -277,8 +267,10 @@ void RuntimeDyldELF::resolveX86_64Relocation(const SectionEntry &Section,
|
||||
support::ulittle64_t::ref Placeholder(
|
||||
(void *)(Section.ObjAddress + Offset));
|
||||
uint64_t FinalAddress = Section.LoadAddress + Offset;
|
||||
support::ulittle64_t::ref(Section.Address + Offset) =
|
||||
Placeholder + Value + Addend - FinalAddress;
|
||||
int64_t RealOffset = Value + Addend - FinalAddress;
|
||||
if (Offset < Section.Size)
|
||||
RealOffset += Placeholder;
|
||||
support::ulittle64_t::ref(Section.Address + Offset) = RealOffset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1323,16 +1315,18 @@ relocation_iterator RuntimeDyldELF::processRelocationRef(
|
||||
Stubs[Value] = StubOffset;
|
||||
createStubFunction((uint8_t *)StubAddress);
|
||||
|
||||
// Create a GOT entry for the external function.
|
||||
GOTEntries.push_back(Value);
|
||||
|
||||
// Make our stub function a relative call to the GOT entry.
|
||||
RelocationEntry RE(SectionID, StubOffset + 2, ELF::R_X86_64_GOTPCREL,
|
||||
-4);
|
||||
addRelocationForSymbol(RE, Value.SymbolName);
|
||||
|
||||
// Bump our stub offset counter
|
||||
Section.StubOffset = StubOffset + getMaxStubSize();
|
||||
|
||||
// Allocate a GOT Entry
|
||||
uint64_t GOTOffset = allocateGOTEntries(SectionID, 1);
|
||||
|
||||
// The load of the GOT address has an addend of -4
|
||||
resolveGOTOffsetRelocation(SectionID, StubOffset + 2, GOTOffset - 4);
|
||||
|
||||
// Fill in the value of the symbol we're targeting into the GOT
|
||||
addRelocationForSymbol(computeGOTOffsetRE(SectionID,GOTOffset,0,ELF::R_X86_64_64),
|
||||
Value.SymbolName);
|
||||
}
|
||||
|
||||
// Make the target call a call into the stub table.
|
||||
@@ -1343,10 +1337,17 @@ relocation_iterator RuntimeDyldELF::processRelocationRef(
|
||||
Value.Offset);
|
||||
addRelocationForSection(RE, Value.SectionID);
|
||||
}
|
||||
} else if (Arch == Triple::x86_64 && RelType == ELF::R_X86_64_GOTPCREL) {
|
||||
uint64_t GOTOffset = allocateGOTEntries(SectionID, 1);
|
||||
resolveGOTOffsetRelocation(SectionID, Offset, GOTOffset + Addend);
|
||||
|
||||
// Fill in the value of the symbol we're targeting into the GOT
|
||||
RelocationEntry RE = computeGOTOffsetRE(SectionID, GOTOffset, Value.Offset, ELF::R_X86_64_64);
|
||||
if (Value.SymbolName)
|
||||
addRelocationForSymbol(RE, Value.SymbolName);
|
||||
else
|
||||
addRelocationForSection(RE, Value.SectionID);
|
||||
} else {
|
||||
if (Arch == Triple::x86_64 && RelType == ELF::R_X86_64_GOTPCREL) {
|
||||
GOTEntries.push_back(Value);
|
||||
}
|
||||
RelocationEntry RE(SectionID, Offset, RelType, Value.Addend, Value.Offset);
|
||||
if (Value.SymbolName)
|
||||
addRelocationForSymbol(RE, Value.SymbolName);
|
||||
@@ -1356,22 +1357,6 @@ relocation_iterator RuntimeDyldELF::processRelocationRef(
|
||||
return ++RelI;
|
||||
}
|
||||
|
||||
void RuntimeDyldELF::updateGOTEntries(StringRef Name, uint64_t Addr) {
|
||||
|
||||
SmallVectorImpl<std::pair<SID, GOTRelocations>>::iterator it;
|
||||
SmallVectorImpl<std::pair<SID, GOTRelocations>>::iterator end = GOTs.end();
|
||||
|
||||
for (it = GOTs.begin(); it != end; ++it) {
|
||||
GOTRelocations &GOTEntries = it->second;
|
||||
for (int i = 0, e = GOTEntries.size(); i != e; ++i) {
|
||||
if (GOTEntries[i].SymbolName != nullptr &&
|
||||
GOTEntries[i].SymbolName == Name) {
|
||||
GOTEntries[i].Offset = Addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t RuntimeDyldELF::getGOTEntrySize() {
|
||||
// We don't use the GOT in all of these cases, but it's essentially free
|
||||
// to put them all here.
|
||||
@@ -1398,75 +1383,50 @@ size_t RuntimeDyldELF::getGOTEntrySize() {
|
||||
return Result;
|
||||
}
|
||||
|
||||
uint64_t RuntimeDyldELF::findGOTEntry(uint64_t LoadAddress, uint64_t Offset) {
|
||||
|
||||
const size_t GOTEntrySize = getGOTEntrySize();
|
||||
|
||||
SmallVectorImpl<std::pair<SID, GOTRelocations>>::const_iterator it;
|
||||
SmallVectorImpl<std::pair<SID, GOTRelocations>>::const_iterator end =
|
||||
GOTs.end();
|
||||
|
||||
int GOTIndex = -1;
|
||||
for (it = GOTs.begin(); it != end; ++it) {
|
||||
SID GOTSectionID = it->first;
|
||||
const GOTRelocations &GOTEntries = it->second;
|
||||
|
||||
// Find the matching entry in our vector.
|
||||
uint64_t SymbolOffset = 0;
|
||||
for (int i = 0, e = GOTEntries.size(); i != e; ++i) {
|
||||
if (!GOTEntries[i].SymbolName) {
|
||||
if (getSectionLoadAddress(GOTEntries[i].SectionID) == LoadAddress &&
|
||||
GOTEntries[i].Offset == Offset) {
|
||||
GOTIndex = i;
|
||||
SymbolOffset = GOTEntries[i].Offset;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// GOT entries for external symbols use the addend as the address when
|
||||
// the external symbol has been resolved.
|
||||
if (GOTEntries[i].Offset == LoadAddress) {
|
||||
GOTIndex = i;
|
||||
// Don't use the Addend here. The relocation handler will use it.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (GOTIndex != -1) {
|
||||
if (GOTEntrySize == sizeof(uint64_t)) {
|
||||
uint64_t *LocalGOTAddr = (uint64_t *)getSectionAddress(GOTSectionID);
|
||||
// Fill in this entry with the address of the symbol being referenced.
|
||||
LocalGOTAddr[GOTIndex] = LoadAddress + SymbolOffset;
|
||||
} else {
|
||||
uint32_t *LocalGOTAddr = (uint32_t *)getSectionAddress(GOTSectionID);
|
||||
// Fill in this entry with the address of the symbol being referenced.
|
||||
LocalGOTAddr[GOTIndex] = (uint32_t)(LoadAddress + SymbolOffset);
|
||||
}
|
||||
|
||||
// Calculate the load address of this entry
|
||||
return getSectionLoadAddress(GOTSectionID) + (GOTIndex * GOTEntrySize);
|
||||
}
|
||||
uint64_t RuntimeDyldELF::allocateGOTEntries(unsigned SectionID, unsigned no)
|
||||
{
|
||||
(void)SectionID; // The GOT Section is the same for all section in the object file
|
||||
if (GOTSectionID == 0) {
|
||||
GOTSectionID = Sections.size();
|
||||
// Reserve a section id. We'll allocate the section later
|
||||
// once we know the total size
|
||||
Sections.push_back(SectionEntry(".got", 0, 0, 0));
|
||||
}
|
||||
uint64_t StartOffset = CurrentGOTIndex * getGOTEntrySize();
|
||||
CurrentGOTIndex += no;
|
||||
return StartOffset;
|
||||
}
|
||||
|
||||
assert(GOTIndex != -1 && "Unable to find requested GOT entry.");
|
||||
return 0;
|
||||
void RuntimeDyldELF::resolveGOTOffsetRelocation(unsigned SectionID, uint64_t Offset, uint64_t GOTOffset)
|
||||
{
|
||||
// Fill in the relative address of the GOT Entry into the stub
|
||||
RelocationEntry GOTRE(SectionID, Offset, ELF::R_X86_64_PC32, GOTOffset);
|
||||
addRelocationForSection(GOTRE, GOTSectionID);
|
||||
}
|
||||
|
||||
RelocationEntry RuntimeDyldELF::computeGOTOffsetRE(unsigned SectionID, uint64_t GOTOffset, uint64_t SymbolOffset,
|
||||
uint32_t Type)
|
||||
{
|
||||
(void)SectionID; // The GOT Section is the same for all section in the object file
|
||||
return RelocationEntry(GOTSectionID, GOTOffset, Type, SymbolOffset);
|
||||
}
|
||||
|
||||
void RuntimeDyldELF::finalizeLoad(const ObjectFile &Obj,
|
||||
ObjSectionToIDMap &SectionMap) {
|
||||
// If necessary, allocate the global offset table
|
||||
size_t numGOTEntries = GOTEntries.size();
|
||||
if (numGOTEntries != 0) {
|
||||
if (GOTSectionID != 0) {
|
||||
// Allocate memory for the section
|
||||
unsigned SectionID = Sections.size();
|
||||
size_t TotalSize = numGOTEntries * getGOTEntrySize();
|
||||
size_t TotalSize = CurrentGOTIndex * getGOTEntrySize();
|
||||
uint8_t *Addr = MemMgr.allocateDataSection(TotalSize, getGOTEntrySize(),
|
||||
SectionID, ".got", false);
|
||||
GOTSectionID, ".got", false);
|
||||
if (!Addr)
|
||||
report_fatal_error("Unable to allocate memory for GOT!");
|
||||
|
||||
GOTs.push_back(std::make_pair(SectionID, GOTEntries));
|
||||
Sections.push_back(SectionEntry(".got", Addr, TotalSize, 0));
|
||||
Sections[GOTSectionID] = SectionEntry(".got", Addr, TotalSize, 0);
|
||||
|
||||
if (Checker)
|
||||
Checker->registerSection(Obj.getFileName(), GOTSectionID);
|
||||
|
||||
// For now, initialize all GOT entries to zero. We'll fill them in as
|
||||
// needed when GOT-based relocations are applied.
|
||||
memset(Addr, 0, TotalSize);
|
||||
@@ -1483,6 +1443,9 @@ void RuntimeDyldELF::finalizeLoad(const ObjectFile &Obj,
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GOTSectionID = 0;
|
||||
CurrentGOTIndex = 0;
|
||||
}
|
||||
|
||||
bool RuntimeDyldELF::isCompatibleFile(const object::ObjectFile &Obj) const {
|
||||
|
Reference in New Issue
Block a user