diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2020-07-26 19:36:28 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2020-07-26 19:36:28 +0000 | 
| commit | cfca06d7963fa0909f90483b42a6d7d194d01e08 (patch) | |
| tree | 209fb2a2d68f8f277793fc8df46c753d31bc853b /lld/ELF/Relocations.cpp | |
| parent | 706b4fc47bbc608932d3b491ae19a3b9cde9497b (diff) | |
Notes
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>(); | 
