Skip to content

Commit

Permalink
[ELF] Implement infrastructure for thunk code creation
Browse files Browse the repository at this point in the history
Some targets might require creation of thunks. For example, MIPS targets
require stubs to call PIC code from non-PIC one. The patch implements
infrastructure for thunk code creation and provides support for MIPS
LA25 stubs. Any MIPS PIC code function is invoked with its address
in register $t9. So if we have a branch instruction from non-PIC code
to the PIC one we cannot make the jump directly and need to create a small
stub to save the target function address.
See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf

- In relocation scanning phase we ask target about thunk creation necessity
by calling `TagetInfo::needsThunk` method. The `InputSection` class
maintains list of Symbols requires thunk creation.

- Reassigning offsets performed for each input sections after relocation
scanning complete because position of each section might change due
thunk creation.

- The patch introduces new dedicated value for DefinedSynthetic symbols
DefinedSynthetic::SectionEnd. Synthetic symbol with that value always
points to the end of the corresponding output section. That allows to
escape updating synthetic symbols if output sections sizes changes after
relocation scanning due thunk creation.

- In the `InputSection::writeTo` method we write thunks after corresponding
input section. Each thunk is written by calling `TargetInfo::writeThunk` method.

- The patch supports the only type of thunk code for each target. For now,
it is enough.

Differential Revision: http://reviews.llvm.org/D17934

llvm-svn: 265059
  • Loading branch information
atanasyan committed Mar 31, 2016
1 parent 098c3fc commit 13f6da1
Show file tree
Hide file tree
Showing 11 changed files with 241 additions and 14 deletions.
36 changes: 36 additions & 0 deletions lld/ELF/InputSection.cpp
Expand Up @@ -38,6 +38,13 @@ InputSectionBase<ELFT>::InputSectionBase(elf::ObjectFile<ELFT> *File,
Align = std::max<uintX_t>(Header->sh_addralign, 1);
}

template <class ELFT> size_t InputSectionBase<ELFT>::getSize() const {
if (auto *D = dyn_cast<InputSection<ELFT>>(this))
if (D->getThunksSize() > 0)
return D->getThunkOff() + D->getThunksSize();
return Header->sh_size;
}

template <class ELFT> StringRef InputSectionBase<ELFT>::getSectionName() const {
return check(File->getObj().getSectionName(this->Header));
}
Expand Down Expand Up @@ -105,6 +112,19 @@ InputSectionBase<ELFT> *InputSection<ELFT>::getRelocatedSection() {
return Sections[this->Header->sh_info];
}

template <class ELFT> void InputSection<ELFT>::addThunk(SymbolBody &Body) {
Body.ThunkIndex = Thunks.size();
Thunks.push_back(&Body);
}

template <class ELFT> uint64_t InputSection<ELFT>::getThunkOff() const {
return this->Header->sh_size;
}

template <class ELFT> uint64_t InputSection<ELFT>::getThunksSize() const {
return Thunks.size() * Target->ThunkSize;
}

// This is used for -r. We can't use memcpy to copy relocations because we need
// to update symbol table offset and section index for each relocation. So we
// copy relocations one by one.
Expand Down Expand Up @@ -293,6 +313,9 @@ void InputSectionBase<ELFT>::relocate(uint8_t *Buf, uint8_t *BufEnd,
// If that's the case, we leave the field alone rather than filling it
// with a possibly incorrect value.
continue;
} else if (Target->needsThunk(Type, *this->getFile(), Body)) {
// Get address of a thunk code related to the symbol.
SymVA = Body.getThunkVA<ELFT>();
} else if (Config->EMachine == EM_MIPS) {
SymVA = adjustMipsSymVA<ELFT>(Type, *File, Body, AddrLoc, SymVA);
} else if (!Target->needsCopyRel<ELFT>(Type, Body) &&
Expand Down Expand Up @@ -333,6 +356,19 @@ template <class ELFT> void InputSection<ELFT>::writeTo(uint8_t *Buf) {
else
this->relocate(Buf, BufEnd, EObj.rels(RelSec));
}

// The section might have a data/code generated by the linker and need
// to be written after the section. Usually these are thunks - small piece
// of code used to jump between "incompatible" functions like PIC and non-PIC
// or if the jump target too far and its address does not fit to the short
// jump istruction.
if (!Thunks.empty()) {
Buf += OutSecOff + getThunkOff();
for (const SymbolBody *S : Thunks) {
Target->writeThunk(Buf, S->getVA<ELFT>());
Buf += Target->ThunkSize;
}
}
}

template <class ELFT>
Expand Down
15 changes: 14 additions & 1 deletion lld/ELF/InputSection.h
Expand Up @@ -56,7 +56,7 @@ template <class ELFT> class InputSectionBase {
InputSectionBase<ELFT> *Repl;

// Returns the size of this section (even if this is a common or BSS.)
size_t getSize() const { return Header->sh_size; }
size_t getSize() const;

static InputSectionBase<ELFT> *Discarded;

Expand Down Expand Up @@ -167,6 +167,17 @@ template <class ELFT> class InputSection : public InputSectionBase<ELFT> {

InputSectionBase<ELFT> *getRelocatedSection();

// Register thunk related to the symbol. When the section is written
// to a mmap'ed file, target is requested to write an actual thunk code.
// Now thunks is supported for MIPS target only.
void addThunk(SymbolBody &Body);

// The offset of synthetic thunk code from beginning of this section.
uint64_t getThunkOff() const;

// Size of chunk with thunks code.
uint64_t getThunksSize() const;

private:
template <class RelTy>
void copyRelocations(uint8_t *Buf, llvm::iterator_range<const RelTy *> Rels);
Expand All @@ -176,6 +187,8 @@ template <class ELFT> class InputSection : public InputSectionBase<ELFT> {

// Used by ICF.
uint64_t GroupId = 0;

llvm::TinyPtrVector<const SymbolBody *> Thunks;
};

// MIPS .reginfo section provides information on the registers used by the code
Expand Down
12 changes: 2 additions & 10 deletions lld/ELF/OutputSections.cpp
Expand Up @@ -821,12 +821,6 @@ void OutputSection<ELFT>::addSection(InputSectionBase<ELFT> *C) {
Sections.push_back(S);
S->OutSec = this;
this->updateAlign(S->Align);

uintX_t Off = this->Header.sh_size;
Off = alignTo(Off, S->Align);
S->OutSecOff = Off;
Off += S->getSize();
this->Header.sh_size = Off;
}

// If an input string is in the form of "foo.N" where N is a number,
Expand All @@ -843,8 +837,8 @@ static int getPriority(StringRef S) {
}

// This function is called after we sort input sections
// to update their offsets.
template <class ELFT> void OutputSection<ELFT>::reassignOffsets() {
// and scan relocations to setup sections' offsets.
template <class ELFT> void OutputSection<ELFT>::assignOffsets() {
uintX_t Off = 0;
for (InputSection<ELFT> *S : Sections) {
Off = alignTo(Off, S->Align);
Expand Down Expand Up @@ -872,7 +866,6 @@ template <class ELFT> void OutputSection<ELFT>::sortInitFini() {
Sections.clear();
for (Pair &P : V)
Sections.push_back(P.second);
reassignOffsets();
}

// Returns true if S matches /Filename.?\.o$/.
Expand Down Expand Up @@ -933,7 +926,6 @@ static bool compCtors(const InputSection<ELFT> *A,
// Read the comment above.
template <class ELFT> void OutputSection<ELFT>::sortCtorsDtors() {
std::stable_sort(Sections.begin(), Sections.end(), compCtors<ELFT>);
reassignOffsets();
}

static void fill(uint8_t *Buf, size_t Size, ArrayRef<uint8_t> A) {
Expand Down
3 changes: 2 additions & 1 deletion lld/ELF/OutputSections.h
Expand Up @@ -87,6 +87,7 @@ template <class ELFT> class OutputSectionBase {
// Typically the first section of each PT_LOAD segment has this flag.
bool PageAlign = false;

virtual void assignOffsets() {}
virtual void finalize() {}
virtual void writeTo(uint8_t *Buf) {}
virtual ~OutputSectionBase() = default;
Expand Down Expand Up @@ -271,10 +272,10 @@ class OutputSection final : public OutputSectionBase<ELFT> {
void sortInitFini();
void sortCtorsDtors();
void writeTo(uint8_t *Buf) override;
void assignOffsets() override;
void finalize() override;

private:
void reassignOffsets();
std::vector<InputSection<ELFT> *> Sections;
};

Expand Down
14 changes: 14 additions & 0 deletions lld/ELF/Symbols.cpp
Expand Up @@ -37,6 +37,8 @@ static typename ELFT::uint getSymVA(const SymbolBody &Body,
switch (Body.kind()) {
case SymbolBody::DefinedSyntheticKind: {
auto &D = cast<DefinedSynthetic<ELFT>>(Body);
if (D.Value == DefinedSynthetic<ELFT>::SectionEnd)
return D.Section.getVA() + D.Section.getSize();
return D.Section.getVA() + D.Value;
}
case SymbolBody::DefinedRegularKind: {
Expand Down Expand Up @@ -133,6 +135,13 @@ template <class ELFT> typename ELFT::uint SymbolBody::getPltVA() const {
PltIndex * Target->PltEntrySize;
}

template <class ELFT> typename ELFT::uint SymbolBody::getThunkVA() const {
auto *D = cast<DefinedRegular<ELFT>>(this);
auto *S = cast<InputSection<ELFT>>(D->Section);
return S->OutSec->getVA() + S->OutSecOff + S->getThunkOff() +
ThunkIndex * Target->ThunkSize;
}

template <class ELFT> typename ELFT::uint SymbolBody::getSize() const {
if (auto *B = dyn_cast<DefinedElf<ELFT>>(this))
return B->Sym.st_size;
Expand Down Expand Up @@ -298,6 +307,11 @@ template uint32_t SymbolBody::template getSize<ELF32BE>() const;
template uint64_t SymbolBody::template getSize<ELF64LE>() const;
template uint64_t SymbolBody::template getSize<ELF64BE>() const;

template uint32_t SymbolBody::template getThunkVA<ELF32LE>() const;
template uint32_t SymbolBody::template getThunkVA<ELF32BE>() const;
template uint64_t SymbolBody::template getThunkVA<ELF64LE>() const;
template uint64_t SymbolBody::template getThunkVA<ELF64BE>() const;

template int SymbolBody::compare<ELF32LE>(SymbolBody *Other);
template int SymbolBody::compare<ELF32BE>(SymbolBody *Other);
template int SymbolBody::compare<ELF64LE>(SymbolBody *Other);
Expand Down
7 changes: 7 additions & 0 deletions lld/ELF/Symbols.h
Expand Up @@ -85,9 +85,11 @@ class SymbolBody {
uint32_t GotIndex = -1;
uint32_t GotPltIndex = -1;
uint32_t PltIndex = -1;
uint32_t ThunkIndex = -1;
bool hasGlobalDynIndex() { return GlobalDynIndex != uint32_t(-1); }
bool isInGot() const { return GotIndex != -1U; }
bool isInPlt() const { return PltIndex != -1U; }
bool hasThunk() const { return ThunkIndex != -1U; }

void setUsedInRegularObj() { IsUsedInRegularObj = true; }

Expand All @@ -97,6 +99,7 @@ class SymbolBody {
template <class ELFT> typename ELFT::uint getGotVA() const;
template <class ELFT> typename ELFT::uint getGotPltVA() const;
template <class ELFT> typename ELFT::uint getPltVA() const;
template <class ELFT> typename ELFT::uint getThunkVA() const;
template <class ELFT> typename ELFT::uint getSize() const;

// A SymbolBody has a backreference to a Symbol. Originally they are
Expand Down Expand Up @@ -249,6 +252,10 @@ template <class ELFT> class DefinedSynthetic : public Defined {
return S->kind() == SymbolBody::DefinedSyntheticKind;
}

// Special value designates that the symbol 'points'
// to the end of the section.
static const uintX_t SectionEnd = uintX_t(-1);

uintX_t Value;
const OutputSectionBase<ELFT> &Section;
};
Expand Down
49 changes: 49 additions & 0 deletions lld/ELF/Target.cpp
Expand Up @@ -17,6 +17,7 @@

#include "Target.h"
#include "Error.h"
#include "InputFiles.h"
#include "OutputSections.h"
#include "Symbols.h"

Expand Down Expand Up @@ -198,9 +199,12 @@ template <class ELFT> class MipsTargetInfo final : public TargetInfo {
void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
void writeGotHeader(uint8_t *Buf) const override;
void writeThunk(uint8_t *Buf, uint64_t S) const override;
bool needsCopyRelImpl(uint32_t Type) const override;
bool needsGot(uint32_t Type, const SymbolBody &S) const override;
bool needsPltImpl(uint32_t Type) const override;
bool needsThunk(uint32_t Type, const InputFile &File,
const SymbolBody &S) const override;
void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P,
uint64_t SA) const override;
bool isHintRel(uint32_t Type) const override;
Expand Down Expand Up @@ -331,6 +335,11 @@ TargetInfo::PltNeed TargetInfo::needsPlt(uint32_t Type,
return Plt_No;
}

bool TargetInfo::needsThunk(uint32_t Type, const InputFile &File,
const SymbolBody &S) const {
return false;
}

bool TargetInfo::isTlsInitialExecRel(uint32_t Type) const { return false; }

bool TargetInfo::pointsToLocalDynamicGotEntry(uint32_t Type) const {
Expand Down Expand Up @@ -1581,6 +1590,7 @@ template <class ELFT> MipsTargetInfo<ELFT>::MipsTargetInfo() {
PageSize = 65536;
PltEntrySize = 16;
PltZeroSize = 32;
ThunkSize = 16;
UseLazyBinding = true;
CopyRel = R_MIPS_COPY;
PltRel = R_MIPS_JUMP_SLOT;
Expand Down Expand Up @@ -1694,6 +1704,20 @@ void MipsTargetInfo<ELFT>::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
writeMipsLo16<E>(Buf + 12, GotEntryAddr);
}

template <class ELFT>
void MipsTargetInfo<ELFT>::writeThunk(uint8_t *Buf, uint64_t S) const {
// Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
// See MipsTargetInfo::writeThunk for details.
const endianness E = ELFT::TargetEndianness;
write32<E>(Buf, 0x3c190000); // lui $25, %hi(func)
write32<E>(Buf + 4, 0x08000000); // j func
write32<E>(Buf + 8, 0x27390000); // addiu $25, $25, %lo(func)
write32<E>(Buf + 12, 0x00000000); // nop
writeMipsHi16<E>(Buf, S);
write32<E>(Buf + 4, 0x08000000 | (S >> 2));
writeMipsLo16<E>(Buf + 8, S);
}

template <class ELFT>
bool MipsTargetInfo<ELFT>::needsCopyRelImpl(uint32_t Type) const {
return !isRelRelative(Type);
Expand All @@ -1714,6 +1738,31 @@ bool MipsTargetInfo<ELFT>::needsPltImpl(uint32_t Type) const {
return Type == R_MIPS_26;
}

template <class ELFT>
bool MipsTargetInfo<ELFT>::needsThunk(uint32_t Type, const InputFile &File,
const SymbolBody &S) const {
// Any MIPS PIC code function is invoked with its address in register $t9.
// So if we have a branch instruction from non-PIC code to the PIC one
// we cannot make the jump directly and need to create a small stubs
// to save the target function address.
// See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
if (Type != R_MIPS_26)
return false;
auto *F = dyn_cast<ELFFileBase<ELFT>>(&File);
if (!F)
return false;
// If current file has PIC code, LA25 stub is not required.
if (F->getObj().getHeader()->e_flags & EF_MIPS_PIC)
return false;
auto *D = dyn_cast<DefinedRegular<ELFT>>(&S);
if (!D || !D->Section)
return false;
// LA25 is required if target file has PIC code
// or target symbol is a PIC symbol.
return (D->Section->getFile()->getObj().getHeader()->e_flags & EF_MIPS_PIC) ||
(D->Sym.st_other & STO_MIPS_MIPS16) == STO_MIPS_PIC;
}

template <class ELFT>
uint64_t MipsTargetInfo<ELFT>::getImplicitAddend(uint8_t *Buf,
uint32_t Type) const {
Expand Down
7 changes: 7 additions & 0 deletions lld/ELF/Target.h
Expand Up @@ -17,6 +17,7 @@

namespace lld {
namespace elf {
class InputFile;
class SymbolBody;

class TargetInfo {
Expand Down Expand Up @@ -62,6 +63,11 @@ class TargetInfo {
enum PltNeed { Plt_No, Plt_Explicit, Plt_Implicit };
PltNeed needsPlt(uint32_t Type, const SymbolBody &S) const;

virtual bool needsThunk(uint32_t Type, const InputFile &File,
const SymbolBody &S) const;

virtual void writeThunk(uint8_t *Buf, uint64_t S) const {}

virtual void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type,
uint64_t P, uint64_t SA) const = 0;
virtual bool isGotRelative(uint32_t Type) const;
Expand Down Expand Up @@ -94,6 +100,7 @@ class TargetInfo {
unsigned PltZeroSize = 0;
unsigned GotHeaderEntriesNum = 0;
unsigned GotPltHeaderEntriesNum = 3;
uint32_t ThunkSize = 0;
bool UseLazyBinding = false;

private:
Expand Down

0 comments on commit 13f6da1

Please sign in to comment.