Adding PIC support for ELF on x86_64 platforms

llvm-svn: 188726
This commit is contained in:
Andrew Kaylor 2013-08-19 23:27:43 +00:00
parent f708c87078
commit 4612fed911
4 changed files with 244 additions and 16 deletions

View File

@ -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");

View File

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

View File

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

View File

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