Start adding support for creating the GOT.

With this a program can call into a shared library with

  jmp *foo@GOTPCREL(%rip)

llvm-svn: 247992
This commit is contained in:
Rafael Espindola 2015-09-18 14:40:19 +00:00
parent ded4e756b2
commit 5c2310c30c
4 changed files with 133 additions and 18 deletions

View File

@ -134,7 +134,7 @@ public:
ArrayRef<SectionChunk<ELFT> *> getChunks() { return Chunks; }
const SymbolBody *getSymbolBody(uint32_t SymbolIndex) const {
SymbolBody *getSymbolBody(uint32_t SymbolIndex) const {
uint32_t FirstNonLocal = this->Symtab->sh_info;
if (SymbolIndex < FirstNonLocal)
return nullptr;

View File

@ -70,6 +70,10 @@ public:
}
void setDynamicSymbolTableIndex(unsigned V) { DynamicSymbolTableIndex = V; }
unsigned getGotIndex() const { return GotIndex; }
bool isInGot() const { return GotIndex != -1U; }
void setGotIndex(unsigned I) { GotIndex = I; }
// A SymbolBody has a backreference to a Symbol. Originally they are
// doubly-linked. A backreference will never change. But the pointer
// in the Symbol may be mutated by the resolver. If you have a
@ -96,6 +100,7 @@ protected:
unsigned MostConstrainingVisibility : 2;
unsigned IsUsedInRegularObj : 1;
unsigned DynamicSymbolTableIndex = 0;
unsigned GotIndex = -1;
StringRef Name;
Symbol *Backref = nullptr;
};

View File

@ -81,6 +81,8 @@ public:
}
uint32_t getType() { return Header.sh_type; }
static unsigned getAddrSize() { return Is64Bits ? 8 : 4; }
virtual void finalize() {}
virtual void writeTo(uint8_t *Buf) = 0;
@ -98,17 +100,55 @@ template <class ELFT> struct DynamicReloc {
const Elf_Rel &RI;
};
static bool relocNeedsGOT(uint32_t Type) {
switch (Type) {
default:
return false;
case R_X86_64_GOTPCREL:
return true;
}
}
template <class ELFT>
class GotSection final : public OutputSectionBase<ELFT::Is64Bits> {
typedef OutputSectionBase<ELFT::Is64Bits> Base;
typedef typename Base::uintX_t uintX_t;
public:
GotSection()
: OutputSectionBase<ELFT::Is64Bits>(".got", SHT_PROGBITS,
SHF_ALLOC | SHF_WRITE) {
this->Header.sh_addralign = this->getAddrSize();
}
void finalize() override {
this->Header.sh_size = Entries.size() * this->getAddrSize();
}
void writeTo(uint8_t *Buf) override {}
void addEntry(SymbolBody *Sym) {
Sym->setGotIndex(Entries.size());
Entries.push_back(Sym);
}
bool empty() const { return Entries.empty(); }
uintX_t getEntryAddr(const SymbolBody &B) const {
return this->getVA() + B.getGotIndex() * this->getAddrSize();
}
private:
std::vector<const SymbolBody *> Entries;
};
template <class ELFT>
class RelocationSection final : public OutputSectionBase<ELFT::Is64Bits> {
typedef typename ELFFile<ELFT>::Elf_Rel Elf_Rel;
typedef typename ELFFile<ELFT>::Elf_Rela Elf_Rela;
public:
RelocationSection(SymbolTableSection<ELFT> &DynSymSec, bool IsRela)
RelocationSection(SymbolTableSection<ELFT> &DynSymSec,
const GotSection<ELFT> &GotSec, bool IsRela)
: OutputSectionBase<ELFT::Is64Bits>(IsRela ? ".rela.dyn" : ".rel.dyn",
IsRela ? SHT_RELA : SHT_REL,
SHF_ALLOC),
DynSymSec(DynSymSec), IsRela(IsRela) {
DynSymSec(DynSymSec), GotSec(GotSec), IsRela(IsRela) {
this->Header.sh_entsize = IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
this->Header.sh_addralign = ELFT::Is64Bits ? 8 : 4;
}
@ -127,12 +167,18 @@ public:
OutputSection<ELFT> *Out = C.getOutputSection();
uint32_t SymIndex = RI.getSymbol(IsMips64EL);
const SymbolBody *Body = C.getFile()->getSymbolBody(SymIndex);
P->r_offset = RI.r_offset + C.getOutputSectionOff() + Out->getVA();
P->setSymbolAndType(Body->getDynamicSymbolTableIndex(),
RI.getType(IsMips64EL), IsMips64EL);
if (IsRela)
P->r_addend = static_cast<const Elf_Rela &>(RI).r_addend;
uint32_t Type = RI.getType(IsMips64EL);
if (relocNeedsGOT(Type)) {
P->r_offset = GotSec.getEntryAddr(*Body);
P->setSymbolAndType(Body->getDynamicSymbolTableIndex(),
R_X86_64_GLOB_DAT, IsMips64EL);
} else {
P->r_offset = RI.r_offset + C.getOutputSectionOff() + Out->getVA();
P->setSymbolAndType(Body->getDynamicSymbolTableIndex(), Type,
IsMips64EL);
if (IsRela)
P->r_addend = static_cast<const Elf_Rela &>(RI).r_addend;
}
++P;
}
@ -143,6 +189,7 @@ public:
private:
std::vector<DynamicReloc<ELFT>> Relocs;
SymbolTableSection<ELFT> &DynSymSec;
const GotSection<ELFT> &GotSec;
const bool IsRela;
};
}
@ -155,8 +202,10 @@ public:
typedef typename ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
typedef typename ELFFile<ELFT>::Elf_Rel Elf_Rel;
typedef typename ELFFile<ELFT>::Elf_Rela Elf_Rela;
OutputSection(StringRef Name, uint32_t sh_type, uintX_t sh_flags)
: OutputSectionBase<ELFT::Is64Bits>(Name, sh_type, sh_flags) {}
OutputSection(const GotSection<ELFT> &GotSec, StringRef Name,
uint32_t sh_type, uintX_t sh_flags)
: OutputSectionBase<ELFT::Is64Bits>(Name, sh_type, sh_flags),
GotSec(GotSec) {}
void addChunk(SectionChunk<ELFT> *C);
void writeTo(uint8_t *Buf) override;
@ -173,6 +222,7 @@ public:
private:
std::vector<SectionChunk<ELFT> *> Chunks;
const GotSection<ELFT> &GotSec;
};
namespace {
@ -507,7 +557,7 @@ public:
typedef typename ELFFile<ELFT>::Elf_Rela Elf_Rela;
Writer(SymbolTable *T)
: SymTabSec(*this, *T, StrTabSec), DynSymSec(*this, *T, DynStrSec),
RelaDynSec(DynSymSec, T->shouldUseRela()), HashSec(DynSymSec),
RelaDynSec(DynSymSec, GotSec, T->shouldUseRela()), HashSec(DynSymSec),
DynamicSec(*T, HashSec, RelaDynSec) {}
void run();
@ -559,6 +609,8 @@ private:
RelocationSection<ELFT> RelaDynSec;
GotSection<ELFT> GotSec;
HashTableSection<ELFT> HashSec;
DynamicSection<ELFT> DynamicSec;
@ -676,16 +728,22 @@ void OutputSection<ELFT>::relocate(
if (!Body)
continue;
uint32_t Type = RI.getType(IsMips64EL);
uintX_t SymVA;
if (auto *DR = dyn_cast<DefinedRegular<ELFT>>(Body))
if (auto *DR = dyn_cast<DefinedRegular<ELFT>>(Body)) {
SymVA = getSymVA<ELFT>(DR);
else if (auto *DA = dyn_cast<DefinedAbsolute<ELFT>>(Body))
} else if (auto *DA = dyn_cast<DefinedAbsolute<ELFT>>(Body)) {
SymVA = DA->Sym.st_value;
else
} else if (auto *S = dyn_cast<SharedSymbol<ELFT>>(Body)) {
if (!relocNeedsGOT(Type))
continue;
SymVA = GotSec.getEntryAddr(*S);
Type = R_X86_64_PC32;
} else {
// Skip unsupported for now.
continue;
}
uint32_t Type = RI.getType(IsMips64EL);
relocateOne(Buf, RI, Type, BaseAddr, SymVA);
}
}
@ -899,12 +957,17 @@ void Writer<ELFT>::scanRelocs(
bool IsMips64EL = File.getObj()->isMips64EL();
for (const RelType &RI : Rels) {
uint32_t SymIndex = RI.getSymbol(IsMips64EL);
const SymbolBody *Body = File.getSymbolBody(SymIndex);
SymbolBody *Body = File.getSymbolBody(SymIndex);
if (!Body)
continue;
auto *S = dyn_cast<SharedSymbol<ELFT>>(Body);
if (!S)
continue;
if (relocNeedsGOT(RI.getType(IsMips64EL))) {
if (Body->isInGot())
continue;
GotSec.addEntry(Body);
}
RelaDynSec.addReloc({C, RI});
}
}
@ -934,7 +997,7 @@ template <class ELFT> void Writer<ELFT>::createSections() {
OutputSection<ELFT> *&Sec = Map[Key];
if (!Sec) {
Sec = new (CAlloc.Allocate())
OutputSection<ELFT>(Key.Name, Key.sh_type, Key.sh_flags);
OutputSection<ELFT>(GotSec, Key.Name, Key.sh_type, Key.sh_flags);
OutputSections.push_back(Sec);
}
return Sec;
@ -1009,6 +1072,8 @@ template <class ELFT> void Writer<ELFT>::createSections() {
OutputSections.push_back(&DynStrSec);
if (RelaDynSec.hasRelocs())
OutputSections.push_back(&RelaDynSec);
if (!GotSec.empty())
OutputSections.push_back(&GotSec);
}
std::stable_sort(OutputSections.begin(), OutputSections.end(),

45
lld/test/elf2/got.s Normal file
View File

@ -0,0 +1,45 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t2.o
// RUN: lld -flavor gnu2 -shared %t2.o -o %t2.so
// RUN: lld -flavor gnu2 %t.o %t2.so -o %t
// RUN: llvm-readobj -s -r %t | FileCheck %s
// RUN: llvm-objdump -d %t | FileCheck --check-prefix=DISASM %s
// REQUIRES: x86
// CHECK: Name: .got
// CHECK-NEXT: Type: SHT_PROGBITS
// CHECK-NEXT: Flags [
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ]
// CHECK-NEXT: Address: 0x15000
// CHECK-NEXT: Offset:
// CHECK-NEXT: Size: 16
// CHECK-NEXT: Link: 0
// CHECK-NEXT: Info: 0
// CHECK-NEXT: AddressAlignment: 8
// CHECK: Relocations [
// CHECK-NEXT: Section ({{.*}}) .rela.dyn {
// CHECK-NEXT: 0x15000 R_X86_64_GLOB_DAT bar 0x0
// CHECK-NEXT: 0x15008 R_X86_64_GLOB_DAT zed 0x0
// CHECK-NEXT: }
// CHECK-NEXT: ]
// Unfortunately FileCheck can't do math, so we have to check for explicit
// values:
// 0x15000 - (0x11000 + 2) - 4 = 16378
// 0x15000 - (0x11006 + 2) - 4 = 16372
// 0x15008 - (0x1100c + 2) - 4 = 16374
// DISASM: _start:
// DISASM-NEXT: 11000: ff 25 fa 3f 00 00 jmpq *16378(%rip)
// DISASM-NEXT: 11006: ff 25 f4 3f 00 00 jmpq *16372(%rip)
// DISASM-NEXT: 1100c: ff 25 f6 3f 00 00 jmpq *16374(%rip)
.global _start
_start:
jmp *bar@GOTPCREL(%rip)
jmp *bar@GOTPCREL(%rip)
jmp *zed@GOTPCREL(%rip)