summaryrefslogtreecommitdiff
path: root/lld/ELF/Relocations.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lld/ELF/Relocations.cpp')
-rw-r--r--lld/ELF/Relocations.cpp235
1 files changed, 174 insertions, 61 deletions
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 4731554e0c0d..751ded397768 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -62,9 +62,9 @@ using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::support::endian;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
static Optional<std::string> getLinkerScriptLocation(const Symbol &sym) {
for (BaseCommand *base : script->sectionCommands)
if (auto *cmd = dyn_cast<SymbolAssignment>(base))
@@ -73,6 +73,15 @@ static Optional<std::string> getLinkerScriptLocation(const Symbol &sym) {
return None;
}
+static std::string getDefinedLocation(const Symbol &sym) {
+ std::string msg = "\n>>> defined in ";
+ if (sym.file)
+ msg += toString(sym.file);
+ else if (Optional<std::string> loc = getLinkerScriptLocation(sym))
+ msg += *loc;
+ return msg;
+}
+
// Construct a message in the following format.
//
// >>> defined in /home/alice/src/foo.o
@@ -80,19 +89,30 @@ static Optional<std::string> getLinkerScriptLocation(const Symbol &sym) {
// >>> /home/alice/src/bar.o:(.text+0x1)
static std::string getLocation(InputSectionBase &s, const Symbol &sym,
uint64_t off) {
- std::string msg = "\n>>> defined in ";
- if (sym.file)
- msg += toString(sym.file);
- else if (Optional<std::string> loc = getLinkerScriptLocation(sym))
- msg += *loc;
-
- msg += "\n>>> referenced by ";
+ std::string msg = getDefinedLocation(sym) + "\n>>> referenced by ";
std::string src = s.getSrcMsg(sym, off);
if (!src.empty())
msg += src + "\n>>> ";
return msg + s.getObjMsg(off);
}
+void elf::reportRangeError(uint8_t *loc, const Relocation &rel, const Twine &v,
+ int64_t min, uint64_t max) {
+ ErrorPlace errPlace = getErrorPlace(loc);
+ std::string hint;
+ if (rel.sym && !rel.sym->isLocal())
+ hint = "; references " + lld::toString(*rel.sym) +
+ getDefinedLocation(*rel.sym);
+
+ if (errPlace.isec && errPlace.isec->name.startswith(".debug"))
+ hint += "; consider recompiling with -fdebug-types-section to reduce size "
+ "of debug sections";
+
+ errorOrWarn(errPlace.loc + "relocation " + lld::toString(rel.type) +
+ " out of range: " + v.str() + " is not in [" + Twine(min).str() +
+ ", " + Twine(max).str() + "]" + hint);
+}
+
namespace {
// Build a bitmask with one bit set for each RelExpr.
//
@@ -177,9 +197,9 @@ handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
return 1;
}
- bool canRelax = config->emachine != EM_ARM &&
- config->emachine != EM_HEXAGON &&
- config->emachine != EM_RISCV;
+ bool toExecRelax = !config->shared && config->emachine != EM_ARM &&
+ config->emachine != EM_HEXAGON &&
+ config->emachine != EM_RISCV;
// If we are producing an executable and the symbol is non-preemptable, it
// must be defined and the code sequence can be relaxed to use Local-Exec.
@@ -197,7 +217,7 @@ handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
if (oneof<R_TLSLD_GOT, R_TLSLD_GOTPLT, R_TLSLD_PC, R_TLSLD_HINT>(
expr)) {
// Local-Dynamic relocs can be relaxed to Local-Exec.
- if (canRelax && !config->shared) {
+ if (toExecRelax) {
c.relocations.push_back(
{target->adjustRelaxExpr(type, nullptr, R_RELAX_TLS_LD_TO_LE), type,
offset, addend, &sym});
@@ -218,7 +238,7 @@ handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
}
// Local-Dynamic relocs can be relaxed to Local-Exec.
- if (expr == R_DTPREL && !config->shared) {
+ if (expr == R_DTPREL && toExecRelax) {
c.relocations.push_back(
{target->adjustRelaxExpr(type, nullptr, R_RELAX_TLS_LD_TO_LE), type,
offset, addend, &sym});
@@ -240,7 +260,7 @@ handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC,
R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC>(expr)) {
- if (!canRelax || config->shared) {
+ if (!toExecRelax) {
if (in.got->addDynTlsEntry(sym)) {
uint64_t off = in.got->getGlobalDynOffset(sym);
@@ -288,7 +308,7 @@ handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
// defined.
if (oneof<R_GOT, R_GOTPLT, R_GOT_PC, R_AARCH64_GOT_PAGE_PC, R_GOT_OFF,
R_TLSIE_HINT>(expr) &&
- canRelax && isLocalInExecutable) {
+ toExecRelax && isLocalInExecutable) {
c.relocations.push_back({R_RELAX_TLS_IE_TO_LE, type, offset, addend, &sym});
return 1;
}
@@ -408,6 +428,14 @@ static bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym,
assert(absVal && relE);
+ // Allow R_PLT_PC (optimized to R_PC here) to a hidden undefined weak symbol
+ // in PIC mode. This is a little strange, but it allows us to link function
+ // calls to such symbols (e.g. glibc/stdlib/exit.c:__run_exit_handlers).
+ // Normally such a call will be guarded with a comparison, which will load a
+ // zero from the GOT.
+ if (sym.isUndefWeak())
+ return true;
+
// We set the final symbols values for linker script defined symbols later.
// They always can be computed as a link time constant.
if (sym.scriptDefined)
@@ -753,7 +781,7 @@ static const Symbol *getAlternativeSpelling(const Undefined &sym,
break;
// Substitute name[i].
- newName = name;
+ newName = std::string(name);
for (char c = '0'; c <= 'z'; ++c) {
newName[i] = c;
if (const Symbol *s = suggest(newName))
@@ -842,7 +870,7 @@ static void reportUndefinedSymbol(const UndefinedDiag &undef,
if (msg.empty())
msg = "undefined " + visibility() + "symbol: " + toString(sym);
- const size_t maxUndefReferences = 10;
+ const size_t maxUndefReferences = 3;
size_t i = 0;
for (UndefinedDiag::Loc l : undef.locs) {
if (i >= maxUndefReferences)
@@ -873,8 +901,9 @@ static void reportUndefinedSymbol(const UndefinedDiag &undef,
}
if (sym.getName().startswith("_ZTV"))
- msg += "\nthe vtable symbol may be undefined because the class is missing "
- "its key function (see https://lld.llvm.org/missingkeyfunction)";
+ msg +=
+ "\n>>> the vtable symbol may be undefined because the class is missing "
+ "its key function (see https://lld.llvm.org/missingkeyfunction)";
if (undef.isWarning)
warn(msg);
@@ -882,7 +911,7 @@ static void reportUndefinedSymbol(const UndefinedDiag &undef,
error(msg);
}
-template <class ELFT> void reportUndefinedSymbols() {
+template <class ELFT> void elf::reportUndefinedSymbols() {
// Find the first "undefined symbol" diagnostic for each diagnostic, and
// collect all "referenced from" lines at the first diagnostic.
DenseMap<Symbol *, UndefinedDiag *> firstRef;
@@ -918,8 +947,12 @@ static bool maybeReportUndefined(Symbol &sym, InputSectionBase &sec,
// .toc and the .rela.toc are incorrectly not placed in the comdat. The ELF
// spec says references from outside the group to a STB_LOCAL symbol are not
// allowed. Work around the bug.
- if (config->emachine == EM_PPC64 &&
- cast<Undefined>(sym).discardedSecIdx != 0 && sec.name == ".toc")
+ //
+ // PPC32 .got2 is similar but cannot be fixed. Multiple .got2 is infeasible
+ // because .LC0-.LTOC is not representable if the two labels are in different
+ // .got2
+ if (cast<Undefined>(sym).discardedSecIdx != 0 &&
+ (sec.name == ".got2" || sec.name == ".toc"))
return false;
bool isWarning =
@@ -1190,10 +1223,17 @@ static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
getLocation(sec, sym, offset));
if (!sym.isInPlt())
addPltEntry(in.plt, in.gotPlt, in.relaPlt, target->pltRel, sym);
- if (!sym.isDefined())
+ if (!sym.isDefined()) {
replaceWithDefined(
sym, in.plt,
target->pltHeaderSize + target->pltEntrySize * sym.pltIndex, 0);
+ if (config->emachine == EM_PPC) {
+ // PPC32 canonical PLT entries are at the beginning of .glink
+ cast<Defined>(sym).value = in.plt->headerSize;
+ in.plt->headerSize += 16;
+ cast<PPC32GlinkSection>(in.plt)->canonical_plts.push_back(&sym);
+ }
+ }
sym.needsPltAddr = true;
sec.relocations.push_back({expr, type, offset, addend, &sym});
return;
@@ -1254,17 +1294,6 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
if (expr == R_NONE)
return;
- // We can separate the small code model relocations into 2 categories:
- // 1) Those that access the compiler generated .toc sections.
- // 2) Those that access the linker allocated got entries.
- // lld allocates got entries to symbols on demand. Since we don't try to sort
- // the got entries in any way, we don't have to track which objects have
- // got-based small code model relocs. The .toc sections get placed after the
- // end of the linker allocated .got section and we do sort those so sections
- // addressed with small code model relocations come first.
- if (config->emachine == EM_PPC64 && isPPC64SmallCodeModelTocReloc(type))
- sec.file->ppc64SmallCodeModelTocRelocs = true;
-
if (sym.isGnuIFunc() && !config->zText && config->warnIfuncTextrel) {
warn("using ifunc symbols when text relocations are allowed may produce "
"a binary that will segfault, if the object file is linked with "
@@ -1278,6 +1307,25 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
// Read an addend.
int64_t addend = computeAddend<ELFT>(rel, end, sec, expr, sym.isLocal());
+ if (config->emachine == EM_PPC64) {
+ // We can separate the small code model relocations into 2 categories:
+ // 1) Those that access the compiler generated .toc sections.
+ // 2) Those that access the linker allocated got entries.
+ // lld allocates got entries to symbols on demand. Since we don't try to
+ // sort the got entries in any way, we don't have to track which objects
+ // have got-based small code model relocs. The .toc sections get placed
+ // after the end of the linker allocated .got section and we do sort those
+ // so sections addressed with small code model relocations come first.
+ if (isPPC64SmallCodeModelTocReloc(type))
+ sec.file->ppc64SmallCodeModelTocRelocs = true;
+
+ // Record the TOC entry (.toc + addend) as not relaxable. See the comment in
+ // InputSectionBase::relocateAlloc().
+ if (type == R_PPC64_TOC16_LO && sym.isSection() && isa<Defined>(sym) &&
+ cast<Defined>(sym).section->name == ".toc")
+ ppc64noTocRelax.insert({&sym, addend});
+ }
+
// Relax relocations.
//
// If we know that a PLT entry will be resolved within the same ELF module, we
@@ -1290,10 +1338,16 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
if (expr == R_GOT_PC && !isAbsoluteValue(sym)) {
expr = target->adjustRelaxExpr(type, relocatedAddr, expr);
} else {
- // Addend of R_PPC_PLTREL24 is used to choose call stub type. It should be
- // ignored if optimized to R_PC.
+ // The 0x8000 bit of r_addend of R_PPC_PLTREL24 is used to choose call
+ // stub type. It should be ignored if optimized to R_PC.
if (config->emachine == EM_PPC && expr == R_PPC32_PLTREL)
- addend = 0;
+ addend &= ~0x8000;
+ // R_HEX_GD_PLT_B22_PCREL (call a@GDPLT) is transformed into
+ // call __tls_get_addr even if the symbol is non-preemptible.
+ if (!(config->emachine == EM_HEXAGON &&
+ (type == R_HEX_GD_PLT_B22_PCREL ||
+ type == R_HEX_GD_PLT_B22_PCREL_X ||
+ type == R_HEX_GD_PLT_B32_PCREL_X)))
expr = fromPlt(expr);
}
}
@@ -1460,7 +1514,7 @@ static void scanRelocs(InputSectionBase &sec, ArrayRef<RelTy> rels) {
});
}
-template <class ELFT> void scanRelocations(InputSectionBase &s) {
+template <class ELFT> void elf::scanRelocations(InputSectionBase &s) {
if (s.areRelocsRela)
scanRelocs<ELFT>(s, s.relas<ELFT>());
else
@@ -1744,6 +1798,37 @@ ThunkSection *ThunkCreator::addThunkSection(OutputSection *os,
uint64_t off) {
auto *ts = make<ThunkSection>(os, off);
ts->partition = os->partition;
+ if ((config->fixCortexA53Errata843419 || config->fixCortexA8) &&
+ !isd->sections.empty()) {
+ // The errata fixes are sensitive to addresses modulo 4 KiB. When we add
+ // thunks we disturb the base addresses of sections placed after the thunks
+ // this makes patches we have generated redundant, and may cause us to
+ // generate more patches as different instructions are now in sensitive
+ // locations. When we generate more patches we may force more branches to
+ // go out of range, causing more thunks to be generated. In pathological
+ // cases this can cause the address dependent content pass not to converge.
+ // We fix this by rounding up the size of the ThunkSection to 4KiB, this
+ // limits the insertion of a ThunkSection on the addresses modulo 4 KiB,
+ // which means that adding Thunks to the section does not invalidate
+ // errata patches for following code.
+ // Rounding up the size to 4KiB has consequences for code-size and can
+ // trip up linker script defined assertions. For example the linux kernel
+ // has an assertion that what LLD represents as an InputSectionDescription
+ // does not exceed 4 KiB even if the overall OutputSection is > 128 Mib.
+ // We use the heuristic of rounding up the size when both of the following
+ // conditions are true:
+ // 1.) The OutputSection is larger than the ThunkSectionSpacing. This
+ // accounts for the case where no single InputSectionDescription is
+ // larger than the OutputSection size. This is conservative but simple.
+ // 2.) The InputSectionDescription is larger than 4 KiB. This will prevent
+ // any assertion failures that an InputSectionDescription is < 4 KiB
+ // in size.
+ uint64_t isdSize = isd->sections.back()->outSecOff +
+ isd->sections.back()->getSize() -
+ isd->sections.front()->outSecOff;
+ if (os->size > target->getThunkSectionSpacing() && isdSize > 4096)
+ ts->roundUpSizeForErrata = true;
+ }
isd->thunkSections.push_back({ts, pass});
return ts;
}
@@ -1812,9 +1897,7 @@ bool ThunkCreator::normalizeExistingThunk(Relocation &rel, uint64_t src) {
rel.sym->getVA(rel.addend) + getPCBias(rel.type)))
return true;
rel.sym = &t->destination;
- // TODO Restore addend on all targets.
- if (config->emachine == EM_AARCH64 || config->emachine == EM_PPC64)
- rel.addend = t->addend;
+ rel.addend = t->addend;
if (rel.sym->isInPlt())
rel.expr = toPlt(rel.expr);
}
@@ -1892,16 +1975,11 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> outputSections) {
rel.sym = t->getThunkTargetSym();
rel.expr = fromPlt(rel.expr);
- // On AArch64 and PPC64, a jump/call relocation may be encoded as
+ // On AArch64 and PPC, a jump/call relocation may be encoded as
// STT_SECTION + non-zero addend, clear the addend after
// redirection.
- //
- // The addend of R_PPC_PLTREL24 should be ignored after changing to
- // R_PC.
- if (config->emachine == EM_AARCH64 ||
- config->emachine == EM_PPC64 ||
- (config->emachine == EM_PPC && rel.type == R_PPC_PLTREL24))
- rel.addend = 0;
+ if (config->emachine != EM_MIPS)
+ rel.addend = -getPCBias(rel.type);
}
for (auto &p : isd->thunkSections)
@@ -1917,14 +1995,49 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> outputSections) {
return addressesChanged;
}
-template void scanRelocations<ELF32LE>(InputSectionBase &);
-template void scanRelocations<ELF32BE>(InputSectionBase &);
-template void scanRelocations<ELF64LE>(InputSectionBase &);
-template void scanRelocations<ELF64BE>(InputSectionBase &);
-template void reportUndefinedSymbols<ELF32LE>();
-template void reportUndefinedSymbols<ELF32BE>();
-template void reportUndefinedSymbols<ELF64LE>();
-template void reportUndefinedSymbols<ELF64BE>();
-
-} // namespace elf
-} // namespace lld
+// The following aid in the conversion of call x@GDPLT to call __tls_get_addr
+// hexagonNeedsTLSSymbol scans for relocations would require a call to
+// __tls_get_addr.
+// hexagonTLSSymbolUpdate rebinds the relocation to __tls_get_addr.
+bool elf::hexagonNeedsTLSSymbol(ArrayRef<OutputSection *> outputSections) {
+ bool needTlsSymbol = false;
+ forEachInputSectionDescription(
+ outputSections, [&](OutputSection *os, InputSectionDescription *isd) {
+ for (InputSection *isec : isd->sections)
+ for (Relocation &rel : isec->relocations)
+ if (rel.sym->type == llvm::ELF::STT_TLS && rel.expr == R_PLT_PC) {
+ needTlsSymbol = true;
+ return;
+ }
+ });
+ return needTlsSymbol;
+}
+
+void elf::hexagonTLSSymbolUpdate(ArrayRef<OutputSection *> outputSections) {
+ Symbol *sym = symtab->find("__tls_get_addr");
+ if (!sym)
+ return;
+ bool needEntry = true;
+ forEachInputSectionDescription(
+ outputSections, [&](OutputSection *os, InputSectionDescription *isd) {
+ for (InputSection *isec : isd->sections)
+ for (Relocation &rel : isec->relocations)
+ if (rel.sym->type == llvm::ELF::STT_TLS && rel.expr == R_PLT_PC) {
+ if (needEntry) {
+ addPltEntry(in.plt, in.gotPlt, in.relaPlt, target->pltRel,
+ *sym);
+ needEntry = false;
+ }
+ rel.sym = sym;
+ }
+ });
+}
+
+template void elf::scanRelocations<ELF32LE>(InputSectionBase &);
+template void elf::scanRelocations<ELF32BE>(InputSectionBase &);
+template void elf::scanRelocations<ELF64LE>(InputSectionBase &);
+template void elf::scanRelocations<ELF64BE>(InputSectionBase &);
+template void elf::reportUndefinedSymbols<ELF32LE>();
+template void elf::reportUndefinedSymbols<ELF32BE>();
+template void elf::reportUndefinedSymbols<ELF64LE>();
+template void elf::reportUndefinedSymbols<ELF64BE>();