Skip to content

Commit

Permalink
RuntimeDyldELF/AArch64: Implement basic GOT support
Browse files Browse the repository at this point in the history
This patch implements two GOT relocations:
R_AARCH64_ADR_GOT_PAGE and R_AARCH64_LD64_GOT_LO12_NC

Differential revision: https://reviews.llvm.org/D28571


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@294191 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
eleviant committed Feb 6, 2017
1 parent 6f12c47 commit 3523e86
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 73 deletions.
26 changes: 26 additions & 0 deletions lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,14 @@ Error RuntimeDyldImpl::computeTotalAllocSize(const ObjectFile &Obj,
}
}

// Compute Global Offset Table size. If it is not zero we
// also update alignment, which is equal to a size of a
// single GOT entry.
if (unsigned GotSize = computeGOTSize(Obj)) {
RWSectionSizes.push_back(GotSize);
RWDataAlign = std::max<uint32_t>(RWDataAlign, getGOTEntrySize());
}

// Compute the size of all common symbols
uint64_t CommonSize = 0;
uint32_t CommonAlign = 1;
Expand Down Expand Up @@ -518,6 +526,24 @@ Error RuntimeDyldImpl::computeTotalAllocSize(const ObjectFile &Obj,
return Error::success();
}

// compute GOT size
unsigned RuntimeDyldImpl::computeGOTSize(const ObjectFile &Obj) {
size_t GotEntrySize = getGOTEntrySize();
if (!GotEntrySize)
return 0;

size_t GotSize = 0;
for (section_iterator SI = Obj.section_begin(), SE = Obj.section_end();
SI != SE; ++SI) {

for (const RelocationRef &Reloc : SI->relocations())
if (relocationNeedsGot(Reloc))
GotSize += GotEntrySize;
}

return GotSize;
}

// compute stub buffer size for the given section
unsigned RuntimeDyldImpl::computeSectionStubBufSize(const ObjectFile &Obj,
const SectionRef &Section) {
Expand Down
189 changes: 123 additions & 66 deletions lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,61 @@ bool RuntimeDyldELF::resolveAArch64ShortBranch(
return true;
}

void RuntimeDyldELF::resolveAArch64Branch(unsigned SectionID,
const RelocationValueRef &Value,
relocation_iterator RelI,
StubMap &Stubs) {

DEBUG(dbgs() << "\t\tThis is an AArch64 branch relocation.");
SectionEntry &Section = Sections[SectionID];

uint64_t Offset = RelI->getOffset();
unsigned RelType = RelI->getType();
// Look for an existing stub.
StubMap::const_iterator i = Stubs.find(Value);
if (i != Stubs.end()) {
resolveRelocation(Section, Offset,
(uint64_t)Section.getAddressWithOffset(i->second),
RelType, 0);
DEBUG(dbgs() << " Stub function found\n");
} else if (!resolveAArch64ShortBranch(SectionID, RelI, Value)) {
// Create a new stub function.
DEBUG(dbgs() << " Create a new stub function\n");
Stubs[Value] = Section.getStubOffset();
uint8_t *StubTargetAddr = createStubFunction(
Section.getAddressWithOffset(Section.getStubOffset()));

RelocationEntry REmovz_g3(SectionID, StubTargetAddr - Section.getAddress(),
ELF::R_AARCH64_MOVW_UABS_G3, Value.Addend);
RelocationEntry REmovk_g2(SectionID,
StubTargetAddr - Section.getAddress() + 4,
ELF::R_AARCH64_MOVW_UABS_G2_NC, Value.Addend);
RelocationEntry REmovk_g1(SectionID,
StubTargetAddr - Section.getAddress() + 8,
ELF::R_AARCH64_MOVW_UABS_G1_NC, Value.Addend);
RelocationEntry REmovk_g0(SectionID,
StubTargetAddr - Section.getAddress() + 12,
ELF::R_AARCH64_MOVW_UABS_G0_NC, Value.Addend);

if (Value.SymbolName) {
addRelocationForSymbol(REmovz_g3, Value.SymbolName);
addRelocationForSymbol(REmovk_g2, Value.SymbolName);
addRelocationForSymbol(REmovk_g1, Value.SymbolName);
addRelocationForSymbol(REmovk_g0, Value.SymbolName);
} else {
addRelocationForSection(REmovz_g3, Value.SectionID);
addRelocationForSection(REmovk_g2, Value.SectionID);
addRelocationForSection(REmovk_g1, Value.SectionID);
addRelocationForSection(REmovk_g0, Value.SectionID);
}
resolveRelocation(Section, Offset,
reinterpret_cast<uint64_t>(Section.getAddressWithOffset(
Section.getStubOffset())),
RelType, 0);
Section.advanceStubOffset(getMaxStubSize());
}
}

Expected<relocation_iterator>
RuntimeDyldELF::processRelocationRef(
unsigned SectionID, relocation_iterator RelI, const ObjectFile &O,
Expand Down Expand Up @@ -1055,55 +1110,22 @@ RuntimeDyldELF::processRelocationRef(

DEBUG(dbgs() << "\t\tSectionID: " << SectionID << " Offset: " << Offset
<< "\n");
if ((Arch == Triple::aarch64 || Arch == Triple::aarch64_be) &&
(RelType == ELF::R_AARCH64_CALL26 || RelType == ELF::R_AARCH64_JUMP26)) {
// This is an AArch64 branch relocation, need to use a stub function.
DEBUG(dbgs() << "\t\tThis is an AArch64 branch relocation.");
SectionEntry &Section = Sections[SectionID];

// Look for an existing stub.
StubMap::const_iterator i = Stubs.find(Value);
if (i != Stubs.end()) {
resolveRelocation(Section, Offset,
(uint64_t)Section.getAddressWithOffset(i->second),
RelType, 0);
DEBUG(dbgs() << " Stub function found\n");
} else if (!resolveAArch64ShortBranch(SectionID, RelI, Value)) {
// Create a new stub function.
DEBUG(dbgs() << " Create a new stub function\n");
Stubs[Value] = Section.getStubOffset();
uint8_t *StubTargetAddr = createStubFunction(
Section.getAddressWithOffset(Section.getStubOffset()));

RelocationEntry REmovz_g3(SectionID,
StubTargetAddr - Section.getAddress(),
ELF::R_AARCH64_MOVW_UABS_G3, Value.Addend);
RelocationEntry REmovk_g2(SectionID, StubTargetAddr -
Section.getAddress() + 4,
ELF::R_AARCH64_MOVW_UABS_G2_NC, Value.Addend);
RelocationEntry REmovk_g1(SectionID, StubTargetAddr -
Section.getAddress() + 8,
ELF::R_AARCH64_MOVW_UABS_G1_NC, Value.Addend);
RelocationEntry REmovk_g0(SectionID, StubTargetAddr -
Section.getAddress() + 12,
ELF::R_AARCH64_MOVW_UABS_G0_NC, Value.Addend);

if (Value.SymbolName) {
addRelocationForSymbol(REmovz_g3, Value.SymbolName);
addRelocationForSymbol(REmovk_g2, Value.SymbolName);
addRelocationForSymbol(REmovk_g1, Value.SymbolName);
addRelocationForSymbol(REmovk_g0, Value.SymbolName);
} else {
addRelocationForSection(REmovz_g3, Value.SectionID);
addRelocationForSection(REmovk_g2, Value.SectionID);
addRelocationForSection(REmovk_g1, Value.SectionID);
addRelocationForSection(REmovk_g0, Value.SectionID);
}
resolveRelocation(Section, Offset,
reinterpret_cast<uint64_t>(Section.getAddressWithOffset(
Section.getStubOffset())),
RelType, 0);
Section.advanceStubOffset(getMaxStubSize());
if ((Arch == Triple::aarch64 || Arch == Triple::aarch64_be)) {
if (RelType == ELF::R_AARCH64_CALL26 || RelType == ELF::R_AARCH64_JUMP26) {
resolveAArch64Branch(SectionID, Value, RelI, Stubs);
} else if (RelType == ELF::R_AARCH64_ADR_GOT_PAGE) {
// Craete new GOT entry or find existing one. If GOT entry is
// to be created, then we also emit ABS64 relocation for it.
uint64_t GOTOffset = findOrAllocGOTEntry(Value, ELF::R_AARCH64_ABS64);
resolveGOTOffsetRelocation(SectionID, Offset, GOTOffset + Addend,
ELF::R_AARCH64_ADR_PREL_PG_HI21);

} else if (RelType == ELF::R_AARCH64_LD64_GOT_LO12_NC) {
uint64_t GOTOffset = findOrAllocGOTEntry(Value, ELF::R_AARCH64_ABS64);
resolveGOTOffsetRelocation(SectionID, Offset, GOTOffset + Addend,
ELF::R_AARCH64_LDST64_ABS_LO12_NC);
} else {
processSimpleRelocation(SectionID, Offset, RelType, Value);
}
} else if (Arch == Triple::arm) {
if (RelType == ELF::R_ARM_PC24 || RelType == ELF::R_ARM_CALL ||
Expand Down Expand Up @@ -1252,7 +1274,7 @@ RuntimeDyldELF::processRelocationRef(
if (i != GOTSymbolOffsets.end())
RE.SymOffset = i->second;
else {
RE.SymOffset = allocateGOTEntries(SectionID, 1);
RE.SymOffset = allocateGOTEntries(1);
GOTSymbolOffsets[TargetName] = RE.SymOffset;
}
}
Expand Down Expand Up @@ -1509,14 +1531,15 @@ RuntimeDyldELF::processRelocationRef(
Section.advanceStubOffset(getMaxStubSize());

// Allocate a GOT Entry
uint64_t GOTOffset = allocateGOTEntries(SectionID, 1);
uint64_t GOTOffset = allocateGOTEntries(1);

// The load of the GOT address has an addend of -4
resolveGOTOffsetRelocation(SectionID, StubOffset + 2, GOTOffset - 4);
resolveGOTOffsetRelocation(SectionID, StubOffset + 2, GOTOffset - 4,
ELF::R_X86_64_PC32);

// Fill in the value of the symbol we're targeting into the GOT
addRelocationForSymbol(
computeGOTOffsetRE(SectionID, GOTOffset, 0, ELF::R_X86_64_64),
computeGOTOffsetRE(GOTOffset, 0, ELF::R_X86_64_64),
Value.SymbolName);
}

Expand All @@ -1531,11 +1554,13 @@ RuntimeDyldELF::processRelocationRef(
} else if (RelType == ELF::R_X86_64_GOTPCREL ||
RelType == ELF::R_X86_64_GOTPCRELX ||
RelType == ELF::R_X86_64_REX_GOTPCRELX) {
uint64_t GOTOffset = allocateGOTEntries(SectionID, 1);
resolveGOTOffsetRelocation(SectionID, Offset, GOTOffset + Addend);
uint64_t GOTOffset = allocateGOTEntries(1);
resolveGOTOffsetRelocation(SectionID, Offset, GOTOffset + Addend,
ELF::R_X86_64_PC32);

// 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);
RelocationEntry RE =
computeGOTOffsetRE(GOTOffset, Value.Offset, ELF::R_X86_64_64);
if (Value.SymbolName)
addRelocationForSymbol(RE, Value.SymbolName);
else
Expand Down Expand Up @@ -1593,9 +1618,7 @@ size_t RuntimeDyldELF::getGOTEntrySize() {
return Result;
}

uint64_t RuntimeDyldELF::allocateGOTEntries(unsigned SectionID, unsigned no)
{
(void)SectionID; // The GOT Section is the same for all section in the object file
uint64_t RuntimeDyldELF::allocateGOTEntries(unsigned no) {
if (GOTSectionID == 0) {
GOTSectionID = Sections.size();
// Reserve a section id. We'll allocate the section later
Expand All @@ -1607,17 +1630,38 @@ uint64_t RuntimeDyldELF::allocateGOTEntries(unsigned SectionID, unsigned no)
return StartOffset;
}

void RuntimeDyldELF::resolveGOTOffsetRelocation(unsigned SectionID, uint64_t Offset, uint64_t GOTOffset)
{
uint64_t RuntimeDyldELF::findOrAllocGOTEntry(const RelocationValueRef &Value,
unsigned GOTRelType) {
auto E = GOTOffsetMap.insert({Value, 0});
if (E.second) {
uint64_t GOTOffset = allocateGOTEntries(1);

// Create relocation for newly created GOT entry
RelocationEntry RE =
computeGOTOffsetRE(GOTOffset, Value.Offset, GOTRelType);
if (Value.SymbolName)
addRelocationForSymbol(RE, Value.SymbolName);
else
addRelocationForSection(RE, Value.SectionID);

E.first->second = GOTOffset;
}

return E.first->second;
}

void RuntimeDyldELF::resolveGOTOffsetRelocation(unsigned SectionID,
uint64_t Offset,
uint64_t GOTOffset,
uint32_t Type) {
// Fill in the relative address of the GOT Entry into the stub
RelocationEntry GOTRE(SectionID, Offset, ELF::R_X86_64_PC32, GOTOffset);
RelocationEntry GOTRE(SectionID, Offset, Type, 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
RelocationEntry RuntimeDyldELF::computeGOTOffsetRE(uint64_t GOTOffset,
uint64_t SymbolOffset,
uint32_t Type) {
return RelocationEntry(GOTSectionID, GOTOffset, Type, SymbolOffset);
}

Expand Down Expand Up @@ -1683,6 +1727,19 @@ bool RuntimeDyldELF::isCompatibleFile(const object::ObjectFile &Obj) const {
return Obj.isELF();
}

bool RuntimeDyldELF::relocationNeedsGot(const RelocationRef &R) const {
unsigned RelTy = R.getType();
if (Arch == Triple::aarch64 || Arch == Triple::aarch64_be)
return RelTy == ELF::R_AARCH64_ADR_GOT_PAGE ||
RelTy == ELF::R_AARCH64_LD64_GOT_LO12_NC;

if (Arch == Triple::x86_64)
return RelTy == ELF::R_X86_64_GOTPCREL ||
RelTy == ELF::R_X86_64_GOTPCRELX ||
RelTy == ELF::R_X86_64_REX_GOTPCRELX;
return false;
}

bool RuntimeDyldELF::relocationNeedsStub(const RelocationRef &R) const {
if (Arch != Triple::x86_64)
return true; // Conservative answer
Expand Down
21 changes: 15 additions & 6 deletions lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ class RuntimeDyldELF : public RuntimeDyldImpl {
bool resolveAArch64ShortBranch(unsigned SectionID, relocation_iterator RelI,
const RelocationValueRef &Value);

void resolveAArch64Branch(unsigned SectionID, const RelocationValueRef &Value,
relocation_iterator RelI, StubMap &Stubs);

void resolveARMRelocation(const SectionEntry &Section, uint64_t Offset,
uint32_t Value, uint32_t Type, int32_t Addend);

Expand Down Expand Up @@ -88,24 +91,26 @@ class RuntimeDyldELF : public RuntimeDyldImpl {
ObjSectionToIDMap &LocalSections,
RelocationValueRef &Rel);
protected:
size_t getGOTEntrySize();
size_t getGOTEntrySize() override;

private:
SectionEntry &getSection(unsigned SectionID) { return Sections[SectionID]; }

// Allocate no GOT entries for use in the given section.
uint64_t allocateGOTEntries(unsigned SectionID, unsigned no);
uint64_t allocateGOTEntries(unsigned no);

// Find GOT entry corresponding to relocation or create new one.
uint64_t findOrAllocGOTEntry(const RelocationValueRef &Value,
unsigned GOTRelType);

// Resolve the relvative address of GOTOffset in Section ID and place
// it at the given Offset
void resolveGOTOffsetRelocation(unsigned SectionID, uint64_t Offset,
uint64_t GOTOffset);
uint64_t GOTOffset, uint32_t Type);

// For a GOT entry referenced from SectionID, compute a relocation entry
// that will place the final resolved value in the GOT slot
RelocationEntry computeGOTOffsetRE(unsigned SectionID,
uint64_t GOTOffset,
uint64_t SymbolOffset,
RelocationEntry computeGOTOffsetRE(uint64_t GOTOffset, uint64_t SymbolOffset,
unsigned Type);

// Compute the address in memory where we can find the placeholder
Expand Down Expand Up @@ -146,6 +151,10 @@ class RuntimeDyldELF : public RuntimeDyldImpl {
SmallVector<SID, 2> UnregisteredEHFrameSections;
SmallVector<SID, 2> RegisteredEHFrameSections;

// Map between GOT relocation value and corresponding GOT offset
std::map<RelocationValueRef, uint64_t> GOTOffsetMap;

bool relocationNeedsGot(const RelocationRef &R) const override;
bool relocationNeedsStub(const RelocationRef &R) const override;

public:
Expand Down
13 changes: 12 additions & 1 deletion lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ class RelocationValueRef {
}
};

/// @brief Symbol info for RuntimeDyld.
/// @brief Symbol info for RuntimeDyld.
class SymbolTableEntry {
public:
SymbolTableEntry()
Expand Down Expand Up @@ -426,13 +426,24 @@ class RuntimeDyldImpl {
uint64_t &RODataSize, uint32_t &RODataAlign,
uint64_t &RWDataSize, uint32_t &RWDataAlign);

// \brief Compute GOT size
unsigned computeGOTSize(const ObjectFile &Obj);

// \brief Compute the stub buffer size required for a section
unsigned computeSectionStubBufSize(const ObjectFile &Obj,
const SectionRef &Section);

// \brief Implementation of the generic part of the loadObject algorithm.
Expected<ObjSectionToIDMap> loadObjectImpl(const object::ObjectFile &Obj);

// \brief Return size of Global Offset Table (GOT) entry
virtual size_t getGOTEntrySize() { return 0; }

// \brief Return true if the relocation R may require allocating a GOT entry.
virtual bool relocationNeedsGot(const RelocationRef &R) const {
return false;
}

// \brief Return true if the relocation R may require allocating a stub.
virtual bool relocationNeedsStub(const RelocationRef &R) const {
return true; // Conservative answer
Expand Down
Loading

0 comments on commit 3523e86

Please sign in to comment.