forked from OSchip/llvm-project
Adding PIC support for ELF on x86_64 platforms
llvm-svn: 188726
This commit is contained in:
parent
f708c87078
commit
4612fed911
|
@ -169,6 +169,9 @@ ObjectImage *RuntimeDyldImpl::loadObject(ObjectBuffer *InputBuffer) {
|
|||
}
|
||||
}
|
||||
|
||||
// Give the subclasses a chance to tie-up any loose ends.
|
||||
finalizeLoad();
|
||||
|
||||
return obj.take();
|
||||
}
|
||||
|
||||
|
@ -424,6 +427,10 @@ uint8_t *RuntimeDyldImpl::createStubFunction(uint8_t *Addr) {
|
|||
writeInt16BE(Addr+6, 0x07F1); // brc 15,%r1
|
||||
// 8-byte address stored at Addr + 8
|
||||
return Addr;
|
||||
} else if (Arch == Triple::x86_64) {
|
||||
*Addr = 0xFF; // jmp
|
||||
*(Addr+1) = 0x25; // rip
|
||||
// 32-bit PC-relative address of the GOT entry will be stored at Addr+2
|
||||
}
|
||||
return Addr;
|
||||
}
|
||||
|
@ -473,6 +480,7 @@ void RuntimeDyldImpl::resolveExternalSymbols() {
|
|||
// MemoryManager.
|
||||
uint8_t *Addr = (uint8_t*) MemMgr->getPointerToNamedFunction(Name.data(),
|
||||
true);
|
||||
updateGOTEntries(Name, (uint64_t)Addr);
|
||||
DEBUG(dbgs() << "Resolving relocations Name: " << Name
|
||||
<< "\t" << format("%p", Addr)
|
||||
<< "\n");
|
||||
|
|
|
@ -202,7 +202,8 @@ void RuntimeDyldELF::resolveX86_64Relocation(const SectionEntry &Section,
|
|||
uint64_t Offset,
|
||||
uint64_t Value,
|
||||
uint32_t Type,
|
||||
int64_t Addend) {
|
||||
int64_t Addend,
|
||||
uint64_t SymOffset) {
|
||||
switch (Type) {
|
||||
default:
|
||||
llvm_unreachable("Relocation type not implemented yet!");
|
||||
|
@ -227,6 +228,21 @@ void RuntimeDyldELF::resolveX86_64Relocation(const SectionEntry &Section,
|
|||
<< " at " << format("%p\n",Target));
|
||||
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);
|
||||
uint32_t *Target = reinterpret_cast<uint32_t*>(Section.Address + Offset);
|
||||
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);
|
||||
*Target = 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
|
||||
|
@ -240,6 +256,16 @@ void RuntimeDyldELF::resolveX86_64Relocation(const SectionEntry &Section,
|
|||
*Target = TruncOffset;
|
||||
break;
|
||||
}
|
||||
case ELF::R_X86_64_PC64: {
|
||||
// Get the placeholder value from the generated object since
|
||||
// a previous relocation attempt may have overwritten the loaded version
|
||||
uint64_t *Placeholder = reinterpret_cast<uint64_t*>(Section.ObjAddress
|
||||
+ Offset);
|
||||
uint64_t *Target = reinterpret_cast<uint64_t*>(Section.Address + Offset);
|
||||
uint64_t FinalAddress = Section.LoadAddress + Offset;
|
||||
*Target = *Placeholder + Value + Addend - FinalAddress;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -584,7 +610,7 @@ void RuntimeDyldELF::findOPDEntrySection(ObjectImage &Obj,
|
|||
// Finally compares the Symbol value and the target symbol offset
|
||||
// to check if this .opd entry refers to the symbol the relocation
|
||||
// points to.
|
||||
if (Rel.Addend != (intptr_t)TargetSymbolOffset)
|
||||
if (Rel.Addend != (int64_t)TargetSymbolOffset)
|
||||
continue;
|
||||
|
||||
section_iterator tsi(Obj.end_sections());
|
||||
|
@ -757,17 +783,19 @@ void RuntimeDyldELF::resolveSystemZRelocation(const SectionEntry &Section,
|
|||
void RuntimeDyldELF::resolveRelocation(const RelocationEntry &RE,
|
||||
uint64_t Value) {
|
||||
const SectionEntry &Section = Sections[RE.SectionID];
|
||||
return resolveRelocation(Section, RE.Offset, Value, RE.RelType, RE.Addend);
|
||||
return resolveRelocation(Section, RE.Offset, Value, RE.RelType, RE.Addend,
|
||||
RE.SymOffset);
|
||||
}
|
||||
|
||||
void RuntimeDyldELF::resolveRelocation(const SectionEntry &Section,
|
||||
uint64_t Offset,
|
||||
uint64_t Value,
|
||||
uint32_t Type,
|
||||
int64_t Addend) {
|
||||
int64_t Addend,
|
||||
uint64_t SymOffset) {
|
||||
switch (Arch) {
|
||||
case Triple::x86_64:
|
||||
resolveX86_64Relocation(Section, Offset, Value, Type, Addend);
|
||||
resolveX86_64Relocation(Section, Offset, Value, Type, Addend, SymOffset);
|
||||
break;
|
||||
case Triple::x86:
|
||||
resolveX86Relocation(Section, Offset,
|
||||
|
@ -830,6 +858,7 @@ void RuntimeDyldELF::processRelocationRef(unsigned SectionID,
|
|||
}
|
||||
if (lsi != Symbols.end()) {
|
||||
Value.SectionID = lsi->second.first;
|
||||
Value.Offset = lsi->second.second;
|
||||
Value.Addend = lsi->second.second + Addend;
|
||||
} else {
|
||||
// Search for the symbol in the global symbol table
|
||||
|
@ -838,6 +867,7 @@ void RuntimeDyldELF::processRelocationRef(unsigned SectionID,
|
|||
gsi = GlobalSymbolTable.find(TargetName.data());
|
||||
if (gsi != GlobalSymbolTable.end()) {
|
||||
Value.SectionID = gsi->second.first;
|
||||
Value.Offset = gsi->second.second;
|
||||
Value.Addend = gsi->second.second + Addend;
|
||||
} else {
|
||||
switch (SymType) {
|
||||
|
@ -860,6 +890,7 @@ void RuntimeDyldELF::processRelocationRef(unsigned SectionID,
|
|||
Value.Addend = Addend;
|
||||
break;
|
||||
}
|
||||
case SymbolRef::ST_Data:
|
||||
case SymbolRef::ST_Unknown: {
|
||||
Value.SymbolName = TargetName.data();
|
||||
Value.Addend = Addend;
|
||||
|
@ -1150,8 +1181,67 @@ void RuntimeDyldELF::processRelocationRef(unsigned SectionID,
|
|||
ELF::R_390_PC32DBL, Addend);
|
||||
else
|
||||
resolveRelocation(Section, Offset, StubAddress, RelType, Addend);
|
||||
} else if (Arch == Triple::x86_64 && RelType == ELF::R_X86_64_PLT32) {
|
||||
// The way the PLT relocations normally work is that the linker allocates the
|
||||
// PLT and this relocation makes a PC-relative call into the PLT. The PLT
|
||||
// entry will then jump to an address provided by the GOT. On first call, the
|
||||
// GOT address will point back into PLT code that resolves the symbol. After
|
||||
// the first call, the GOT entry points to the actual function.
|
||||
//
|
||||
// For local functions we're ignoring all of that here and just replacing
|
||||
// the PLT32 relocation type with PC32, which will translate the relocation
|
||||
// into a PC-relative call directly to the function. For external symbols we
|
||||
// can't be sure the function will be within 2^32 bytes of the call site, so
|
||||
// we need to create a stub, which calls into the GOT. This case is
|
||||
// equivalent to the usual PLT implementation except that we use the stub
|
||||
// mechanism in RuntimeDyld (which puts stubs at the end of the section)
|
||||
// rather than allocating a PLT section.
|
||||
if (Value.SymbolName) {
|
||||
// This is a call to an external function.
|
||||
// Look for an existing stub.
|
||||
SectionEntry &Section = Sections[SectionID];
|
||||
StubMap::const_iterator i = Stubs.find(Value);
|
||||
uintptr_t StubAddress;
|
||||
if (i != Stubs.end()) {
|
||||
StubAddress = uintptr_t(Section.Address) + i->second;
|
||||
DEBUG(dbgs() << " Stub function found\n");
|
||||
} else {
|
||||
// Create a new stub function (equivalent to a PLT entry).
|
||||
DEBUG(dbgs() << " Create a new stub function\n");
|
||||
|
||||
uintptr_t BaseAddress = uintptr_t(Section.Address);
|
||||
uintptr_t StubAlignment = getStubAlignment();
|
||||
StubAddress = (BaseAddress + Section.StubOffset +
|
||||
StubAlignment - 1) & -StubAlignment;
|
||||
unsigned StubOffset = StubAddress - BaseAddress;
|
||||
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();
|
||||
}
|
||||
|
||||
// Make the target call a call into the stub table.
|
||||
resolveRelocation(Section, Offset, StubAddress,
|
||||
ELF::R_X86_64_PC32, Addend);
|
||||
} else {
|
||||
RelocationEntry RE(SectionID, Offset, ELF::R_X86_64_PC32, Value.Addend,
|
||||
Value.Offset);
|
||||
addRelocationForSection(RE, Value.SectionID);
|
||||
}
|
||||
} else {
|
||||
RelocationEntry RE(SectionID, Offset, RelType, Value.Addend);
|
||||
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);
|
||||
else
|
||||
|
@ -1159,6 +1249,106 @@ void RuntimeDyldELF::processRelocationRef(unsigned SectionID,
|
|||
}
|
||||
}
|
||||
|
||||
void RuntimeDyldELF::updateGOTEntries(StringRef Name, uint64_t Addr) {
|
||||
for (int i = 0, e = GOTEntries.size(); i != e; ++i) {
|
||||
if (GOTEntries[i].SymbolName != 0 && 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.
|
||||
size_t Result = 0;
|
||||
switch (Arch) {
|
||||
case Triple::x86_64:
|
||||
case Triple::aarch64:
|
||||
case Triple::ppc64:
|
||||
case Triple::ppc64le:
|
||||
case Triple::systemz:
|
||||
Result = sizeof(uint64_t);
|
||||
break;
|
||||
case Triple::x86:
|
||||
case Triple::arm:
|
||||
case Triple::thumb:
|
||||
case Triple::mips:
|
||||
case Triple::mipsel:
|
||||
Result = sizeof(uint32_t);
|
||||
break;
|
||||
default: llvm_unreachable("Unsupported CPU type!");
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
uint64_t RuntimeDyldELF::findGOTEntry(uint64_t LoadAddress,
|
||||
uint64_t Offset) {
|
||||
assert(GOTSectionID != 0
|
||||
&& "Attempting to lookup GOT entry but the GOT was never allocated.");
|
||||
if (GOTSectionID == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t GOTEntrySize = getGOTEntrySize();
|
||||
|
||||
// Find the matching entry in our vector.
|
||||
int GOTIndex = -1;
|
||||
uint64_t SymbolOffset = 0;
|
||||
for (int i = 0, e = GOTEntries.size(); i != e; ++i) {
|
||||
if (GOTEntries[i].SymbolName == 0) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(GOTIndex != -1 && "Unable to find requested GOT entry.");
|
||||
if (GOTIndex == -1)
|
||||
return 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void RuntimeDyldELF::finalizeLoad() {
|
||||
// Allocate the GOT if necessary
|
||||
size_t numGOTEntries = GOTEntries.size();
|
||||
if (numGOTEntries != 0) {
|
||||
// Allocate memory for the section
|
||||
unsigned SectionID = Sections.size();
|
||||
size_t TotalSize = numGOTEntries * getGOTEntrySize();
|
||||
uint8_t *Addr = MemMgr->allocateDataSection(TotalSize, getGOTEntrySize(),
|
||||
SectionID, false);
|
||||
if (!Addr)
|
||||
report_fatal_error("Unable to allocate memory for GOT!");
|
||||
Sections.push_back(SectionEntry(".got", Addr, TotalSize, 0));
|
||||
// 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);
|
||||
GOTSectionID = SectionID;
|
||||
}
|
||||
}
|
||||
|
||||
bool RuntimeDyldELF::isCompatibleFormat(const ObjectBuffer *Buffer) const {
|
||||
if (Buffer->getBufferSize() < strlen(ELF::ElfMagic))
|
||||
return false;
|
||||
|
|
|
@ -35,13 +35,15 @@ class RuntimeDyldELF : public RuntimeDyldImpl {
|
|||
uint64_t Offset,
|
||||
uint64_t Value,
|
||||
uint32_t Type,
|
||||
int64_t Addend);
|
||||
int64_t Addend,
|
||||
uint64_t SymOffset=0);
|
||||
|
||||
void resolveX86_64Relocation(const SectionEntry &Section,
|
||||
uint64_t Offset,
|
||||
uint64_t Value,
|
||||
uint32_t Type,
|
||||
int64_t Addend);
|
||||
int64_t Addend,
|
||||
uint64_t SymOffset);
|
||||
|
||||
void resolveX86Relocation(const SectionEntry &Section,
|
||||
uint64_t Offset,
|
||||
|
@ -84,8 +86,18 @@ class RuntimeDyldELF : public RuntimeDyldImpl {
|
|||
ObjSectionToIDMap &LocalSections,
|
||||
RelocationValueRef &Rel);
|
||||
|
||||
uint64_t findGOTEntry(uint64_t LoadAddr, uint64_t Offset);
|
||||
size_t getGOTEntrySize();
|
||||
|
||||
virtual void updateGOTEntries(StringRef Name, uint64_t Addr);
|
||||
|
||||
SmallVector<RelocationValueRef, 2> GOTEntries;
|
||||
unsigned GOTSectionID;
|
||||
|
||||
public:
|
||||
RuntimeDyldELF(RTDyldMemoryManager *mm) : RuntimeDyldImpl(mm) {}
|
||||
RuntimeDyldELF(RTDyldMemoryManager *mm) : RuntimeDyldImpl(mm),
|
||||
GOTSectionID(0)
|
||||
{}
|
||||
|
||||
virtual void resolveRelocation(const RelocationEntry &RE, uint64_t Value);
|
||||
virtual void processRelocationRef(unsigned SectionID,
|
||||
|
@ -97,6 +109,7 @@ public:
|
|||
virtual bool isCompatibleFormat(const ObjectBuffer *Buffer) const;
|
||||
virtual ObjectImage *createObjectImage(ObjectBuffer *InputBuffer);
|
||||
virtual StringRef getEHFrameSection();
|
||||
virtual void finalizeLoad();
|
||||
virtual ~RuntimeDyldELF();
|
||||
};
|
||||
|
||||
|
|
|
@ -80,14 +80,18 @@ public:
|
|||
unsigned SectionID;
|
||||
|
||||
/// Offset - offset into the section.
|
||||
uintptr_t Offset;
|
||||
uint64_t Offset;
|
||||
|
||||
/// RelType - relocation type.
|
||||
uint32_t RelType;
|
||||
|
||||
/// Addend - the relocation addend encoded in the instruction itself. Also
|
||||
/// used to make a relocation section relative instead of symbol relative.
|
||||
intptr_t Addend;
|
||||
int64_t Addend;
|
||||
|
||||
/// SymOffset - Section offset of the relocation entry's symbol (used for GOT
|
||||
/// lookup).
|
||||
uint64_t SymOffset;
|
||||
|
||||
/// True if this is a PCRel relocation (MachO specific).
|
||||
bool IsPCRel;
|
||||
|
@ -97,20 +101,26 @@ public:
|
|||
|
||||
RelocationEntry(unsigned id, uint64_t offset, uint32_t type, int64_t addend)
|
||||
: SectionID(id), Offset(offset), RelType(type), Addend(addend),
|
||||
IsPCRel(false), Size(0) {}
|
||||
SymOffset(0), IsPCRel(false), Size(0) {}
|
||||
|
||||
RelocationEntry(unsigned id, uint64_t offset, uint32_t type, int64_t addend,
|
||||
uint64_t symoffset)
|
||||
: SectionID(id), Offset(offset), RelType(type), Addend(addend),
|
||||
SymOffset(symoffset), IsPCRel(false), Size(0) {}
|
||||
|
||||
RelocationEntry(unsigned id, uint64_t offset, uint32_t type, int64_t addend,
|
||||
bool IsPCRel, unsigned Size)
|
||||
: SectionID(id), Offset(offset), RelType(type), Addend(addend),
|
||||
IsPCRel(IsPCRel), Size(Size) {}
|
||||
SymOffset(0), IsPCRel(IsPCRel), Size(Size) {}
|
||||
};
|
||||
|
||||
class RelocationValueRef {
|
||||
public:
|
||||
unsigned SectionID;
|
||||
intptr_t Addend;
|
||||
uint64_t Offset;
|
||||
int64_t Addend;
|
||||
const char *SymbolName;
|
||||
RelocationValueRef(): SectionID(0), Addend(0), SymbolName(0) {}
|
||||
RelocationValueRef(): SectionID(0), Offset(0), Addend(0), SymbolName(0) {}
|
||||
|
||||
inline bool operator==(const RelocationValueRef &Other) const {
|
||||
return std::memcmp(this, &Other, sizeof(RelocationValueRef)) == 0;
|
||||
|
@ -175,7 +185,7 @@ protected:
|
|||
else if (Arch == Triple::ppc64 || Arch == Triple::ppc64le)
|
||||
return 44;
|
||||
else if (Arch == Triple::x86_64)
|
||||
return 8; // GOT
|
||||
return 6; // 2-byte jmp instruction + 32-bit relative address
|
||||
else if (Arch == Triple::systemz)
|
||||
return 16;
|
||||
else
|
||||
|
@ -292,6 +302,11 @@ protected:
|
|||
|
||||
/// \brief Resolve relocations to external symbols.
|
||||
void resolveExternalSymbols();
|
||||
|
||||
/// \brief Update GOT entries for external symbols.
|
||||
// The base class does nothing. ELF overrides this.
|
||||
virtual void updateGOTEntries(StringRef Name, uint64_t Addr) {}
|
||||
|
||||
virtual ObjectImage *createObjectImage(ObjectBuffer *InputBuffer);
|
||||
public:
|
||||
RuntimeDyldImpl(RTDyldMemoryManager *mm) : MemMgr(mm), HasError(false) {}
|
||||
|
@ -336,6 +351,8 @@ public:
|
|||
virtual bool isCompatibleFormat(const ObjectBuffer *Buffer) const = 0;
|
||||
|
||||
virtual StringRef getEHFrameSection();
|
||||
|
||||
virtual void finalizeLoad() {}
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
|
Loading…
Reference in New Issue