summaryrefslogtreecommitdiffstats
path: root/lld/ELF
diff options
context:
space:
mode:
Diffstat (limited to 'lld/ELF')
-rw-r--r--lld/ELF/Arch/PPC64.cpp26
-rw-r--r--lld/ELF/Relocations.cpp1
-rw-r--r--lld/ELF/Symbols.cpp11
-rw-r--r--lld/ELF/Symbols.h7
-rw-r--r--lld/ELF/SyntheticSections.cpp47
-rw-r--r--lld/ELF/SyntheticSections.h20
-rw-r--r--lld/ELF/Thunks.cpp83
-rw-r--r--lld/ELF/Writer.cpp6
8 files changed, 186 insertions, 15 deletions
diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp
index 4613179df16..884f1764e6f 100644
--- a/lld/ELF/Arch/PPC64.cpp
+++ b/lld/ELF/Arch/PPC64.cpp
@@ -113,6 +113,7 @@ public:
void writeGotHeader(uint8_t *Buf) const override;
bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
uint64_t BranchAddr, const Symbol &S) const override;
+ bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override;
RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
RelExpr Expr) const override;
void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
@@ -709,9 +710,28 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
bool PPC64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
uint64_t BranchAddr, const Symbol &S) const {
- // If a function is in the plt it needs to be called through
- // a call stub.
- return Type == R_PPC64_REL24 && S.isInPlt();
+ // The only call relocation we currently support is the REL24 type.
+ if (Type != R_PPC64_REL24)
+ return false;
+
+ // If a function is in the Plt it needs to be called with a call-stub.
+ if (S.isInPlt())
+ return true;
+
+ // If a symbol is a weak undefined and we are compiling an executable
+ // it doesn't need a range-extending thunk since it can't be called.
+ if (S.isUndefWeak() && !Config->Shared)
+ return false;
+
+ // If the offset exceeds the range of the branch type then it will need
+ // a range-extending thunk.
+ return !inBranchRange(Type, BranchAddr, S.getVA());
+}
+
+bool PPC64::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
+ assert(Type == R_PPC64_REL24 && "Unexpected relocation type used in branch");
+ int64_t Offset = Dst - Src;
+ return isInt<26>(Offset);
}
RelExpr PPC64::adjustRelaxExpr(RelType Type, const uint8_t *Data,
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index d2c4bbba71c..b4ed71f4d6c 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -489,6 +489,7 @@ static void replaceWithDefined(Symbol &Sym, SectionBase *Sec, uint64_t Value,
Sym.PltIndex = Old.PltIndex;
Sym.GotIndex = Old.GotIndex;
Sym.VerdefIndex = Old.VerdefIndex;
+ Sym.PPC64BranchltIndex = Old.PPC64BranchltIndex;
Sym.IsPreemptible = true;
Sym.ExportDynamic = true;
Sym.IsUsedInRegularObj = true;
diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index 5f7d95381b4..2ff2edf6740 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -138,6 +138,11 @@ uint64_t Symbol::getGotPltOffset() const {
return (PltIndex + Target->GotPltHeaderEntriesNum) * Target->GotPltEntrySize;
}
+uint64_t Symbol::getPPC64LongBranchOffset() const {
+ assert(PPC64BranchltIndex != 0xffff);
+ return PPC64BranchltIndex * Target->GotPltEntrySize;
+}
+
uint64_t Symbol::getPltVA() const {
if (this->IsInIplt)
return In.Iplt->getVA() + PltIndex * Target->PltEntrySize;
@@ -149,6 +154,12 @@ uint64_t Symbol::getPltOffset() const {
return Target->getPltEntryOffset(PltIndex);
}
+uint64_t Symbol::getPPC64LongBranchTableVA() const {
+ assert(PPC64BranchltIndex != 0xffff);
+ return In.PPC64LongBranchTarget->getVA() +
+ PPC64BranchltIndex * Target->GotPltEntrySize;
+}
+
uint64_t Symbol::getSize() const {
if (const auto *DR = dyn_cast<Defined>(this))
return DR->Size;
diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h
index a6c5697a697..26a80ace74f 100644
--- a/lld/ELF/Symbols.h
+++ b/lld/ELF/Symbols.h
@@ -79,6 +79,7 @@ public:
uint32_t DynsymIndex = 0;
uint32_t GotIndex = -1;
uint32_t PltIndex = -1;
+
uint32_t GlobalDynIndex = -1;
// This field is a index to the symbol's version definition.
@@ -87,6 +88,9 @@ public:
// Version definition index.
uint16_t VersionId;
+ // An index into the .branch_lt section on PPC64.
+ uint16_t PPC64BranchltIndex = -1;
+
// Symbol binding. This is not overwritten by replaceSymbol to track
// changes during resolution. In particular:
// - An undefined weak is still weak when it resolves to a shared library.
@@ -159,6 +163,7 @@ public:
bool isInGot() const { return GotIndex != -1U; }
bool isInPlt() const { return PltIndex != -1U; }
+ bool isInPPC64Branchlt() const { return PPC64BranchltIndex != 0xffff; }
uint64_t getVA(int64_t Addend = 0) const;
@@ -168,6 +173,8 @@ public:
uint64_t getGotPltVA() const;
uint64_t getPltVA() const;
uint64_t getPltOffset() const;
+ uint64_t getPPC64LongBranchTableVA() const;
+ uint64_t getPPC64LongBranchOffset() const;
uint64_t getSize() const;
OutputSection *getOutputSection() const;
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 2ceec55e84e..97fd8395f85 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -3070,6 +3070,53 @@ bool ThunkSection::assignOffsets() {
return Changed;
}
+// If linking position-dependent code then the table will store the addresses
+// directly in the binary so the section has type SHT_PROGBITS. If linking
+// position-independent code the section has type SHT_NOBITS since it will be
+// allocated and filled in by the dynamic linker.
+PPC64LongBranchTargetSection::PPC64LongBranchTargetSection()
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE,
+ Config->Pic ? SHT_NOBITS : SHT_PROGBITS, 8,
+ ".branch_lt") {}
+
+void PPC64LongBranchTargetSection::addEntry(Symbol &Sym) {
+ assert(Sym.PPC64BranchltIndex == 0xffff);
+ Sym.PPC64BranchltIndex = Entries.size();
+ Entries.push_back(&Sym);
+}
+
+size_t PPC64LongBranchTargetSection::getSize() const {
+ return Entries.size() * 8;
+}
+
+void PPC64LongBranchTargetSection::writeTo(uint8_t *Buf) {
+ assert(Target->GotPltEntrySize == 8);
+ // If linking non-pic we have the final addresses of the targets and they get
+ // written to the table directly. For pic the dynamic linker will allocate
+ // the section and fill it it.
+ if (Config->Pic)
+ return;
+
+ for (const Symbol *Sym : Entries) {
+ assert(Sym->getVA());
+ // Need calls to branch to the local entry-point since a long-branch
+ // must be a local-call.
+ write64(Buf,
+ Sym->getVA() + getPPC64GlobalEntryToLocalEntryOffset(Sym->StOther));
+ Buf += Target->GotPltEntrySize;
+ }
+}
+
+bool PPC64LongBranchTargetSection::empty() const {
+ // `removeUnusedSyntheticSections()` is called before thunk allocation which
+ // is too early to determine if this section will be empty or not. We need
+ // Finalized to keep the section alive until after thunk creation. Finalized
+ // only gets set to true once `finalizeSections()` is called after thunk
+ // creation. Becuase of this, if we don't create any long-branch thunks we end
+ // up with an empty .branch_lt section in the binary.
+ return Finalized && Entries.empty();
+}
+
InStruct elf::In;
template GdbIndexSection *GdbIndexSection::create<ELF32LE>();
diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index 3ddb4083ed5..f35a8bba8df 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -964,6 +964,25 @@ private:
size_t Size = 0;
};
+// This section is used to store the addresses of functions that are called
+// in range-extending thunks on PowerPC64. When producing position dependant
+// code the addresses are link-time constants and the table is written out to
+// the binary. When producing position-dependant code the table is allocated and
+// filled in by the dynamic linker.
+class PPC64LongBranchTargetSection final : public SyntheticSection {
+public:
+ PPC64LongBranchTargetSection();
+ void addEntry(Symbol &Sym);
+ size_t getSize() const override;
+ void writeTo(uint8_t *Buf) override;
+ bool empty() const override;
+ void finalizeContents() override { Finalized = true; }
+
+private:
+ std::vector<const Symbol *> Entries;
+ bool Finalized = false;
+};
+
InputSection *createInterpSection();
MergeInputSection *createCommentSection();
template <class ELFT> void splitSections();
@@ -990,6 +1009,7 @@ struct InStruct {
GotSection *Got;
GotPltSection *GotPlt;
IgotPltSection *IgotPlt;
+ PPC64LongBranchTargetSection *PPC64LongBranchTarget;
MipsGotSection *MipsGot;
MipsRldMapSection *MipsRldMap;
PltSection *Plt;
diff --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp
index 901de370253..c8e7dca8b25 100644
--- a/lld/ELF/Thunks.cpp
+++ b/lld/ELF/Thunks.cpp
@@ -234,6 +234,46 @@ public:
void addSymbols(ThunkSection &IS) override;
};
+// A bl instruction uses a signed 24 bit offset, with an implicit 4 byte
+// alignment. This gives a possible 26 bits of 'reach'. If the call offset is
+// larger then that we need to emit a long-branch thunk. The target address
+// of the callee is stored in a table to be accessed TOC-relative. Since the
+// call must be local (a non-local call will have a PltCallStub instead) the
+// table stores the address of the callee's local entry point. For
+// position-independent code a corresponding relative dynamic relocation is
+// used.
+class PPC64LongBranchThunk : public Thunk {
+public:
+ uint32_t size() override { return 16; }
+ void writeTo(uint8_t *Buf) override;
+ void addSymbols(ThunkSection &IS) override;
+
+protected:
+ PPC64LongBranchThunk(Symbol &Dest) : Thunk(Dest) {}
+};
+
+class PPC64PILongBranchThunk final : public PPC64LongBranchThunk {
+public:
+ PPC64PILongBranchThunk(Symbol &Dest) : PPC64LongBranchThunk(Dest) {
+ assert(!Dest.IsPreemptible);
+ if (Dest.isInPPC64Branchlt())
+ return;
+
+ In.PPC64LongBranchTarget->addEntry(Dest);
+ In.RelaDyn->addReloc({Target->RelativeRel, In.PPC64LongBranchTarget,
+ Dest.getPPC64LongBranchOffset(), true, &Dest,
+ getPPC64GlobalEntryToLocalEntryOffset(Dest.StOther)});
+ }
+};
+
+class PPC64PDLongBranchThunk final : public PPC64LongBranchThunk {
+public:
+ PPC64PDLongBranchThunk(Symbol &Dest) : PPC64LongBranchThunk(Dest) {
+ if (!Dest.isInPPC64Branchlt())
+ In.PPC64LongBranchTarget->addEntry(Dest);
+ }
+};
+
} // end anonymous namespace
Defined *Thunk::addSymbol(StringRef Name, uint8_t Type, uint64_t Value,
@@ -573,17 +613,21 @@ InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
return dyn_cast<InputSection>(DR.Section);
}
-void PPC64PltCallStub::writeTo(uint8_t *Buf) {
- int64_t Off = Destination.getGotPltVA() - getPPC64TocBase();
- // Need to add 0x8000 to offset to account for the low bits being signed.
- uint16_t OffHa = (Off + 0x8000) >> 16;
- uint16_t OffLo = Off;
+static void writePPCLoadAndBranch(uint8_t *Buf, int64_t Offset) {
+ uint16_t OffHa = (Offset + 0x8000) >> 16;
+ uint16_t OffLo = Offset & 0xffff;
- write32(Buf + 0, 0xf8410018); // std r2,24(r1)
- write32(Buf + 4, 0x3d820000 | OffHa); // addis r12,r2, X@plt@to@ha
- write32(Buf + 8, 0xe98c0000 | OffLo); // ld r12,X@plt@toc@l(r12)
- write32(Buf + 12, 0x7d8903a6); // mtctr r12
- write32(Buf + 16, 0x4e800420); // bctr
+ write32(Buf + 0, 0x3d820000 | OffHa); // addis r12, r2, OffHa
+ write32(Buf + 4, 0xe98c0000 | OffLo); // ld r12, OffLo(r12)
+ write32(Buf + 8, 0x7d8903a6); // mtctr r12
+ write32(Buf + 12, 0x4e800420); // bctr
+}
+
+void PPC64PltCallStub::writeTo(uint8_t *Buf) {
+ int64_t Offset = Destination.getGotPltVA() - getPPC64TocBase();
+ // Save the TOC pointer to the save-slot reserved in the call frame.
+ write32(Buf + 0, 0xf8410018); // std r2,24(r1)
+ writePPCLoadAndBranch(Buf + 4, Offset);
}
void PPC64PltCallStub::addSymbols(ThunkSection &IS) {
@@ -592,6 +636,16 @@ void PPC64PltCallStub::addSymbols(ThunkSection &IS) {
S->NeedsTocRestore = true;
}
+void PPC64LongBranchThunk::writeTo(uint8_t *Buf) {
+ int64_t Offset = Destination.getPPC64LongBranchTableVA() - getPPC64TocBase();
+ writePPCLoadAndBranch(Buf, Offset);
+}
+
+void PPC64LongBranchThunk::addSymbols(ThunkSection &IS) {
+ addSymbol(Saver.save("__long_branch_" + Destination.getName()), STT_FUNC, 0,
+ IS);
+}
+
Thunk::Thunk(Symbol &D) : Destination(D), Offset(0) {}
Thunk::~Thunk() = default;
@@ -675,9 +729,14 @@ static Thunk *addThunkMips(RelType Type, Symbol &S) {
}
static Thunk *addThunkPPC64(RelType Type, Symbol &S) {
- if (Type == R_PPC64_REL24)
+ assert(Type == R_PPC64_REL24 && "unexpected relocation type for thunk");
+ if (S.isInPlt())
return make<PPC64PltCallStub>(S);
- fatal("unexpected relocation type");
+
+ if (Config->Pic)
+ return make<PPC64PILongBranchThunk>(S);
+
+ return make<PPC64PDLongBranchThunk>(S);
}
Thunk *addThunk(RelType Type, Symbol &S) {
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index e486a77e7c5..0eb117a6228 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -365,6 +365,11 @@ template <class ELFT> static void createSyntheticSections() {
Add(In.Got);
}
+ if (Config->EMachine == EM_PPC64) {
+ In.PPC64LongBranchTarget = make<PPC64LongBranchTargetSection>();
+ Add(In.PPC64LongBranchTarget);
+ }
+
In.GotPlt = make<GotPltSection>();
Add(In.GotPlt);
In.IgotPlt = make<IgotPltSection>();
@@ -1756,6 +1761,7 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
// maybeAddThunks may have added local symbols to the static symbol table.
finalizeSynthetic(In.SymTab);
+ finalizeSynthetic(In.PPC64LongBranchTarget);
// Fill other section headers. The dynamic table is finalized
// at the end because some tags like RELSZ depend on result
OpenPOWER on IntegriCloud