diff options
Diffstat (limited to 'lld/ELF/Relocations.cpp')
-rw-r--r-- | lld/ELF/Relocations.cpp | 235 |
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>(); |