diff options
Diffstat (limited to 'ELF/Relocations.cpp')
| -rw-r--r-- | ELF/Relocations.cpp | 1028 |
1 files changed, 643 insertions, 385 deletions
diff --git a/ELF/Relocations.cpp b/ELF/Relocations.cpp index cecd11e907906..baef0a2f2257b 100644 --- a/ELF/Relocations.cpp +++ b/ELF/Relocations.cpp @@ -43,6 +43,7 @@ #include "Relocations.h" #include "Config.h" +#include "Memory.h" #include "OutputSections.h" #include "Strings.h" #include "SymbolTable.h" @@ -52,20 +53,30 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/raw_ostream.h" +#include <algorithm> using namespace llvm; using namespace llvm::ELF; using namespace llvm::object; using namespace llvm::support::endian; -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; -static bool refersToGotEntry(RelExpr Expr) { - return isRelExprOneOf<R_GOT, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOT_OFF, - R_MIPS_GOT_OFF32, R_MIPS_TLSGD, R_MIPS_TLSLD, - R_GOT_PAGE_PC, R_GOT_PC, R_GOT_FROM_END, R_TLSGD, - R_TLSGD_PC, R_TLSDESC, R_TLSDESC_PAGE>(Expr); +// Construct a message in the following format. +// +// >>> defined in /home/alice/src/foo.o +// >>> referenced by bar.c:12 (/home/alice/src/bar.c:12) +// >>> /home/alice/src/bar.o:(.text+0x1) +template <class ELFT> +static std::string getLocation(InputSectionBase &S, const SymbolBody &Sym, + uint64_t Off) { + std::string Msg = + "\n>>> defined in " + toString(Sym.File) + "\n>>> referenced by "; + std::string Src = S.getSrcMsg<ELFT>(Off); + if (!Src.empty()) + Msg += Src + "\n>>> "; + return Msg + S.getObjMsg<ELFT>(Off); } static bool isPreemptible(const SymbolBody &Body, uint32_t Type) { @@ -84,44 +95,92 @@ static bool isPreemptible(const SymbolBody &Body, uint32_t Type) { return Body.isPreemptible(); } -// This function is similar to the `handleTlsRelocation`. ARM and MIPS do not -// support any relaxations for TLS relocations so by factoring out ARM and MIPS +// This function is similar to the `handleTlsRelocation`. MIPS does not +// support any relaxations for TLS relocations so by factoring out MIPS // handling in to the separate function we can simplify the code and do not -// pollute `handleTlsRelocation` by ARM and MIPS `ifs` statements. -template <class ELFT, class GOT> -static unsigned handleNoRelaxTlsRelocation( - GOT *Got, uint32_t Type, SymbolBody &Body, InputSectionBase<ELFT> &C, - typename ELFT::uint Offset, typename ELFT::uint Addend, RelExpr Expr) { - typedef typename ELFT::uint uintX_t; - auto addModuleReloc = [](SymbolBody &Body, GOT *Got, uintX_t Off, bool LD) { - // The Dynamic TLS Module Index Relocation can be statically resolved to 1 - // if we know that we are linking an executable. For ARM we resolve the - // relocation when writing the Got. MIPS has a custom Got implementation - // that writes the Module index in directly. - if (!Body.isPreemptible() && !Config->Pic && Config->EMachine == EM_ARM) - Got->Relocations.push_back( - {R_ABS, Target->TlsModuleIndexRel, Off, 0, &Body}); - else { - SymbolBody *Dest = LD ? nullptr : &Body; +// pollute other `handleTlsRelocation` by MIPS `ifs` statements. +// Mips has a custom MipsGotSection that handles the writing of GOT entries +// without dynamic relocations. +template <class ELFT> +static unsigned handleMipsTlsRelocation(uint32_t Type, SymbolBody &Body, + InputSectionBase &C, uint64_t Offset, + int64_t Addend, RelExpr Expr) { + if (Expr == R_MIPS_TLSLD) { + if (In<ELFT>::MipsGot->addTlsIndex() && Config->Pic) + In<ELFT>::RelaDyn->addReloc({Target->TlsModuleIndexRel, In<ELFT>::MipsGot, + In<ELFT>::MipsGot->getTlsIndexOff(), false, + nullptr, 0}); + C.Relocations.push_back({Expr, Type, Offset, Addend, &Body}); + return 1; + } + + if (Expr == R_MIPS_TLSGD) { + if (In<ELFT>::MipsGot->addDynTlsEntry(Body) && Body.isPreemptible()) { + uint64_t Off = In<ELFT>::MipsGot->getGlobalDynOffset(Body); In<ELFT>::RelaDyn->addReloc( - {Target->TlsModuleIndexRel, Got, Off, false, Dest, 0}); + {Target->TlsModuleIndexRel, In<ELFT>::MipsGot, Off, false, &Body, 0}); + if (Body.isPreemptible()) + In<ELFT>::RelaDyn->addReloc({Target->TlsOffsetRel, In<ELFT>::MipsGot, + Off + Config->Wordsize, false, &Body, 0}); } + C.Relocations.push_back({Expr, Type, Offset, Addend, &Body}); + return 1; + } + return 0; +} + +// This function is similar to the `handleMipsTlsRelocation`. ARM also does not +// support any relaxations for TLS relocations. ARM is logically similar to Mips +// in how it handles TLS, but Mips uses its own custom GOT which handles some +// of the cases that ARM uses GOT relocations for. +// +// We look for TLS global dynamic and local dynamic relocations, these may +// require the generation of a pair of GOT entries that have associated +// dynamic relocations. When the results of the dynamic relocations can be +// resolved at static link time we do so. This is necessary for static linking +// as there will be no dynamic loader to resolve them at load-time. +// +// The pair of GOT entries created are of the form +// GOT[e0] Module Index (Used to find pointer to TLS block at run-time) +// GOT[e1] Offset of symbol in TLS block +template <class ELFT> +static unsigned handleARMTlsRelocation(uint32_t Type, SymbolBody &Body, + InputSectionBase &C, uint64_t Offset, + int64_t Addend, RelExpr Expr) { + // The Dynamic TLS Module Index Relocation for a symbol defined in an + // executable is always 1. If the target Symbol is not preemtible then + // we know the offset into the TLS block at static link time. + bool NeedDynId = Body.isPreemptible() || Config->Shared; + bool NeedDynOff = Body.isPreemptible(); + + auto AddTlsReloc = [&](uint64_t Off, uint32_t Type, SymbolBody *Dest, + bool Dyn) { + if (Dyn) + In<ELFT>::RelaDyn->addReloc({Type, In<ELFT>::Got, Off, false, Dest, 0}); + else + In<ELFT>::Got->Relocations.push_back({R_ABS, Type, Off, 0, Dest}); }; - if (Expr == R_MIPS_TLSLD || Expr == R_TLSLD_PC) { - if (Got->addTlsIndex() && (Config->Pic || Config->EMachine == EM_ARM)) - addModuleReloc(Body, Got, Got->getTlsIndexOff(), true); + + // Local Dynamic is for access to module local TLS variables, while still + // being suitable for being dynamically loaded via dlopen. + // GOT[e0] is the module index, with a special value of 0 for the current + // module. GOT[e1] is unused. There only needs to be one module index entry. + if (Expr == R_TLSLD_PC && In<ELFT>::Got->addTlsIndex()) { + AddTlsReloc(In<ELFT>::Got->getTlsIndexOff(), Target->TlsModuleIndexRel, + NeedDynId ? nullptr : &Body, NeedDynId); C.Relocations.push_back({Expr, Type, Offset, Addend, &Body}); return 1; } - if (Target->isTlsGlobalDynamicRel(Type)) { - if (Got->addDynTlsEntry(Body) && - (Body.isPreemptible() || Config->EMachine == EM_ARM)) { - uintX_t Off = Got->getGlobalDynOffset(Body); - addModuleReloc(Body, Got, Off, false); - if (Body.isPreemptible()) - In<ELFT>::RelaDyn->addReloc({Target->TlsOffsetRel, Got, - Off + (uintX_t)sizeof(uintX_t), false, - &Body, 0}); + + // Global Dynamic is the most general purpose access model. When we know + // the module index and offset of symbol in TLS block we can fill these in + // using static GOT relocations. + if (Expr == R_TLSGD_PC) { + if (In<ELFT>::Got->addDynTlsEntry(Body)) { + uint64_t Off = In<ELFT>::Got->getGlobalDynOffset(Body); + AddTlsReloc(Off, Target->TlsModuleIndexRel, &Body, NeedDynId); + AddTlsReloc(Off + Config->Wordsize, Target->TlsOffsetRel, &Body, + NeedDynOff); } C.Relocations.push_back({Expr, Type, Offset, Addend, &Body}); return 1; @@ -131,30 +190,25 @@ static unsigned handleNoRelaxTlsRelocation( // Returns the number of relocations processed. template <class ELFT> -static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body, - InputSectionBase<ELFT> &C, - typename ELFT::uint Offset, - typename ELFT::uint Addend, RelExpr Expr) { +static unsigned +handleTlsRelocation(uint32_t Type, SymbolBody &Body, InputSectionBase &C, + typename ELFT::uint Offset, int64_t Addend, RelExpr Expr) { if (!(C.Flags & SHF_ALLOC)) return 0; if (!Body.isTls()) return 0; - typedef typename ELFT::uint uintX_t; - if (Config->EMachine == EM_ARM) - return handleNoRelaxTlsRelocation<ELFT>(In<ELFT>::Got, Type, Body, C, - Offset, Addend, Expr); + return handleARMTlsRelocation<ELFT>(Type, Body, C, Offset, Addend, Expr); if (Config->EMachine == EM_MIPS) - return handleNoRelaxTlsRelocation<ELFT>(In<ELFT>::MipsGot, Type, Body, C, - Offset, Addend, Expr); + return handleMipsTlsRelocation<ELFT>(Type, Body, C, Offset, Addend, Expr); bool IsPreemptible = isPreemptible(Body, Type); - if ((Expr == R_TLSDESC || Expr == R_TLSDESC_PAGE || Expr == R_TLSDESC_CALL) && + if (isRelExprOneOf<R_TLSDESC, R_TLSDESC_PAGE, R_TLSDESC_CALL>(Expr) && Config->Shared) { if (In<ELFT>::Got->addDynTlsEntry(Body)) { - uintX_t Off = In<ELFT>::Got->getGlobalDynOffset(Body); + uint64_t Off = In<ELFT>::Got->getGlobalDynOffset(Body); In<ELFT>::RelaDyn->addReloc({Target->TlsDescRel, In<ELFT>::Got, Off, !IsPreemptible, &Body, 0}); } @@ -163,7 +217,7 @@ static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body, return 1; } - if (Expr == R_TLSLD_PC || Expr == R_TLSLD) { + if (isRelExprOneOf<R_TLSLD_PC, R_TLSLD>(Expr)) { // Local-Dynamic relocs can be relaxed to Local-Exec. if (!Config->Shared) { C.Relocations.push_back( @@ -185,17 +239,17 @@ static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body, return 1; } - if (Expr == R_TLSDESC_PAGE || Expr == R_TLSDESC || Expr == R_TLSDESC_CALL || - Target->isTlsGlobalDynamicRel(Type)) { + if (isRelExprOneOf<R_TLSDESC, R_TLSDESC_PAGE, R_TLSDESC_CALL, R_TLSGD, + R_TLSGD_PC>(Expr)) { if (Config->Shared) { if (In<ELFT>::Got->addDynTlsEntry(Body)) { - uintX_t Off = In<ELFT>::Got->getGlobalDynOffset(Body); + uint64_t Off = In<ELFT>::Got->getGlobalDynOffset(Body); In<ELFT>::RelaDyn->addReloc( {Target->TlsModuleIndexRel, In<ELFT>::Got, Off, false, &Body, 0}); // If the symbol is preemptible we need the dynamic linker to write // the offset too. - uintX_t OffsetOff = Off + (uintX_t)sizeof(uintX_t); + uint64_t OffsetOff = Off + Config->Wordsize; if (IsPreemptible) In<ELFT>::RelaDyn->addReloc({Target->TlsOffsetRel, In<ELFT>::Got, OffsetOff, false, &Body, 0}); @@ -216,14 +270,13 @@ static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body, if (!Body.isInGot()) { In<ELFT>::Got->addEntry(Body); In<ELFT>::RelaDyn->addReloc({Target->TlsGotRel, In<ELFT>::Got, - Body.getGotOffset<ELFT>(), false, &Body, - 0}); + Body.getGotOffset(), false, &Body, 0}); } - return Target->TlsGdRelaxSkip; + } else { + C.Relocations.push_back( + {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_LE), Type, + Offset, Addend, &Body}); } - C.Relocations.push_back( - {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_LE), Type, - Offset, Addend, &Body}); return Target->TlsGdRelaxSkip; } @@ -234,16 +287,14 @@ static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body, {R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Body}); return 1; } - return 0; -} -template <endianness E> static int16_t readSignedLo16(const uint8_t *Loc) { - return read32<E>(Loc) & 0xffff; + if (Expr == R_TLSDESC_CALL) + return 1; + return 0; } -template <class RelTy> -static uint32_t getMipsPairType(const RelTy *Rel, const SymbolBody &Sym) { - switch (Rel->getType(Config->Mips64EL)) { +static uint32_t getMipsPairType(uint32_t Type, const SymbolBody &Sym) { + switch (Type) { case R_MIPS_HI16: return R_MIPS_LO16; case R_MIPS_GOT16: @@ -257,72 +308,60 @@ static uint32_t getMipsPairType(const RelTy *Rel, const SymbolBody &Sym) { } } -template <class ELFT, class RelTy> -static int32_t findMipsPairedAddend(const uint8_t *Buf, const uint8_t *BufLoc, - SymbolBody &Sym, const RelTy *Rel, - const RelTy *End) { - uint32_t SymIndex = Rel->getSymbol(Config->Mips64EL); - uint32_t Type = getMipsPairType(Rel, Sym); - - // Some MIPS relocations use addend calculated from addend of the relocation - // itself and addend of paired relocation. ABI requires to compute such - // combined addend in case of REL relocation record format only. - // See p. 4-17 at ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf - if (RelTy::IsRela || Type == R_MIPS_NONE) - return 0; - - for (const RelTy *RI = Rel; RI != End; ++RI) { - if (RI->getType(Config->Mips64EL) != Type) - continue; - if (RI->getSymbol(Config->Mips64EL) != SymIndex) - continue; - const endianness E = ELFT::TargetEndianness; - return ((read32<E>(BufLoc) & 0xffff) << 16) + - readSignedLo16<E>(Buf + RI->r_offset); - } - warn("can't find matching " + toString(Type) + " relocation for " + - toString(Rel->getType(Config->Mips64EL))); - return 0; -} - // True if non-preemptable symbol always has the same value regardless of where // the DSO is loaded. -template <class ELFT> static bool isAbsolute(const SymbolBody &Body) { +static bool isAbsolute(const SymbolBody &Body) { if (Body.isUndefined()) return !Body.isLocal() && Body.symbol()->isWeak(); - if (const auto *DR = dyn_cast<DefinedRegular<ELFT>>(&Body)) + if (const auto *DR = dyn_cast<DefinedRegular>(&Body)) return DR->Section == nullptr; // Absolute symbol. return false; } -template <class ELFT> static bool isAbsoluteValue(const SymbolBody &Body) { - return isAbsolute<ELFT>(Body) || Body.isTls(); +static bool isAbsoluteValue(const SymbolBody &Body) { + return isAbsolute(Body) || Body.isTls(); } +// Returns true if Expr refers a PLT entry. static bool needsPlt(RelExpr Expr) { - return isRelExprOneOf<R_PLT_PC, R_PPC_PLT_OPD, R_PLT, R_PLT_PAGE_PC, - R_THUNK_PLT_PC>(Expr); + return isRelExprOneOf<R_PLT_PC, R_PPC_PLT_OPD, R_PLT, R_PLT_PAGE_PC>(Expr); +} + +// Returns true if Expr refers a GOT entry. Note that this function +// returns false for TLS variables even though they need GOT, because +// TLS variables uses GOT differently than the regular variables. +static bool needsGot(RelExpr Expr) { + return isRelExprOneOf<R_GOT, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOT_OFF, + R_MIPS_GOT_OFF32, R_GOT_PAGE_PC, R_GOT_PC, + R_GOT_FROM_END>(Expr); } // True if this expression is of the form Sym - X, where X is a position in the // file (PC, or GOT for example). static bool isRelExpr(RelExpr Expr) { return isRelExprOneOf<R_PC, R_GOTREL, R_GOTREL_FROM_END, R_MIPS_GOTREL, - R_PAGE_PC, R_RELAX_GOT_PC, R_THUNK_PC, R_THUNK_PLT_PC>( - Expr); + R_PAGE_PC, R_RELAX_GOT_PC>(Expr); } +// Returns true if a given relocation can be computed at link-time. +// +// For instance, we know the offset from a relocation to its target at +// link-time if the relocation is PC-relative and refers a +// non-interposable function in the same executable. This function +// will return true for such relocation. +// +// If this function returns false, that means we need to emit a +// dynamic relocation so that the relocation will be fixed at load-time. template <class ELFT> static bool isStaticLinkTimeConstant(RelExpr E, uint32_t Type, const SymbolBody &Body, - InputSectionBase<ELFT> &S, - typename ELFT::uint RelOff) { + InputSectionBase &S, uint64_t RelOff) { // These expressions always compute a constant if (isRelExprOneOf<R_SIZE, R_GOT_FROM_END, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE, - R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_MIPS_TLSGD, - R_GOT_PAGE_PC, R_GOT_PC, R_PLT_PC, R_TLSGD_PC, R_TLSGD, - R_PPC_PLT_OPD, R_TLSDESC_CALL, R_TLSDESC_PAGE, R_HINT, - R_THUNK_PC, R_THUNK_PLT_PC>(E)) + R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, + R_MIPS_TLSGD, R_GOT_PAGE_PC, R_GOT_PC, R_PLT_PC, + R_TLSGD_PC, R_TLSGD, R_PPC_PLT_OPD, R_TLSDESC_CALL, + R_TLSDESC_PAGE, R_HINT>(E)) return true; // These never do, except if the entire file is position dependent or if @@ -332,16 +371,19 @@ static bool isStaticLinkTimeConstant(RelExpr E, uint32_t Type, if (isPreemptible(Body, Type)) return false; - if (!Config->Pic) return true; - bool AbsVal = isAbsoluteValue<ELFT>(Body); + // For the target and the relocation, we want to know if they are + // absolute or relative. + bool AbsVal = isAbsoluteValue(Body); bool RelE = isRelExpr(E); if (AbsVal && !RelE) return true; if (!AbsVal && RelE) return true; + if (!AbsVal && !RelE) + return Target->usesOnlyLowPageBits(Type); // Relative relocation to an absolute value. This is normally unrepresentable, // but if the relocation refers to a weak undefined symbol, we allow it to @@ -351,18 +393,13 @@ static bool isStaticLinkTimeConstant(RelExpr E, uint32_t Type, // Another special case is MIPS _gp_disp symbol which represents offset // between start of a function and '_gp' value and defined as absolute just // to simplify the code. - if (AbsVal && RelE) { - if (Body.isUndefined() && !Body.isLocal() && Body.symbol()->isWeak()) - return true; - if (&Body == ElfSym<ELFT>::MipsGpDisp) - return true; - error(S.getLocation(RelOff) + ": relocation " + toString(Type) + - " cannot refer to absolute symbol '" + toString(Body) + - "' defined in " + toString(Body.File)); + assert(AbsVal && RelE); + if (Body.isUndefined() && !Body.isLocal() && Body.symbol()->isWeak()) return true; - } - return Target->usesOnlyLowPageBits(Type); + error("relocation " + toString(Type) + " cannot refer to absolute symbol: " + + toString(Body) + getLocation<ELFT>(S, Body, RelOff)); + return true; } static RelExpr toPlt(RelExpr Expr) { @@ -389,23 +426,14 @@ static RelExpr fromPlt(RelExpr Expr) { return Expr; } -template <class ELFT> static uint32_t getAlignment(SharedSymbol<ELFT> *SS) { - typedef typename ELFT::uint uintX_t; - - uintX_t SecAlign = SS->file()->getSection(SS->Sym)->sh_addralign; - uintX_t SymValue = SS->Sym.st_value; - int TrailingZeros = - std::min(countTrailingZeros(SecAlign), countTrailingZeros(SymValue)); - return 1 << TrailingZeros; -} - -template <class ELFT> static bool isReadOnly(SharedSymbol<ELFT> *SS) { - typedef typename ELFT::uint uintX_t; +// Returns true if a given shared symbol is in a read-only segment in a DSO. +template <class ELFT> static bool isReadOnly(SharedSymbol *SS) { typedef typename ELFT::Phdr Elf_Phdr; + uint64_t Value = SS->getValue<ELFT>(); // Determine if the symbol is read-only by scanning the DSO's program headers. - uintX_t Value = SS->Sym.st_value; - for (const Elf_Phdr &Phdr : check(SS->file()->getObj().program_headers())) + auto *File = cast<SharedFile<ELFT>>(SS->File); + for (const Elf_Phdr &Phdr : check(File->getObj().program_headers())) if ((Phdr.p_type == ELF::PT_LOAD || Phdr.p_type == ELF::PT_GNU_RELRO) && !(Phdr.p_flags & ELF::PF_W) && Value >= Phdr.p_vaddr && Value < Phdr.p_vaddr + Phdr.p_memsz) @@ -413,62 +441,112 @@ template <class ELFT> static bool isReadOnly(SharedSymbol<ELFT> *SS) { return false; } -// Reserve space in .bss or .bss.rel.ro for copy relocation. -template <class ELFT> static void addCopyRelSymbol(SharedSymbol<ELFT> *SS) { - typedef typename ELFT::uint uintX_t; +// Returns symbols at the same offset as a given symbol, including SS itself. +// +// If two or more symbols are at the same offset, and at least one of +// them are copied by a copy relocation, all of them need to be copied. +// Otherwise, they would refer different places at runtime. +template <class ELFT> +static std::vector<SharedSymbol *> getSymbolsAt(SharedSymbol *SS) { typedef typename ELFT::Sym Elf_Sym; + auto *File = cast<SharedFile<ELFT>>(SS->File); + uint64_t Shndx = SS->getShndx<ELFT>(); + uint64_t Value = SS->getValue<ELFT>(); + + std::vector<SharedSymbol *> Ret; + for (const Elf_Sym &S : File->getGlobalSymbols()) { + if (S.st_shndx != Shndx || S.st_value != Value) + continue; + StringRef Name = check(S.getName(File->getStringTable())); + SymbolBody *Sym = Symtab<ELFT>::X->find(Name); + if (auto *Alias = dyn_cast_or_null<SharedSymbol>(Sym)) + Ret.push_back(Alias); + } + return Ret; +} + +// Reserve space in .bss or .bss.rel.ro for copy relocation. +// +// The copy relocation is pretty much a hack. If you use a copy relocation +// in your program, not only the symbol name but the symbol's size, RW/RO +// bit and alignment become part of the ABI. In addition to that, if the +// symbol has aliases, the aliases become part of the ABI. That's subtle, +// but if you violate that implicit ABI, that can cause very counter- +// intuitive consequences. +// +// So, what is the copy relocation? It's for linking non-position +// independent code to DSOs. In an ideal world, all references to data +// exported by DSOs should go indirectly through GOT. But if object files +// are compiled as non-PIC, all data references are direct. There is no +// way for the linker to transform the code to use GOT, as machine +// instructions are already set in stone in object files. This is where +// the copy relocation takes a role. +// +// A copy relocation instructs the dynamic linker to copy data from a DSO +// to a specified address (which is usually in .bss) at load-time. If the +// static linker (that's us) finds a direct data reference to a DSO +// symbol, it creates a copy relocation, so that the symbol can be +// resolved as if it were in .bss rather than in a DSO. +// +// As you can see in this function, we create a copy relocation for the +// dynamic linker, and the relocation contains not only symbol name but +// various other informtion about the symbol. So, such attributes become a +// part of the ABI. +// +// Note for application developers: I can give you a piece of advice if +// you are writing a shared library. You probably should export only +// functions from your library. You shouldn't export variables. +// +// As an example what can happen when you export variables without knowing +// the semantics of copy relocations, assume that you have an exported +// variable of type T. It is an ABI-breaking change to add new members at +// end of T even though doing that doesn't change the layout of the +// existing members. That's because the space for the new members are not +// reserved in .bss unless you recompile the main program. That means they +// are likely to overlap with other data that happens to be laid out next +// to the variable in .bss. This kind of issue is sometimes very hard to +// debug. What's a solution? Instead of exporting a varaible V from a DSO, +// define an accessor getV(). +template <class ELFT> static void addCopyRelSymbol(SharedSymbol *SS) { // Copy relocation against zero-sized symbol doesn't make sense. - uintX_t SymSize = SS->template getSize<ELFT>(); + uint64_t SymSize = SS->template getSize<ELFT>(); if (SymSize == 0) fatal("cannot create a copy relocation for symbol " + toString(*SS)); // See if this symbol is in a read-only segment. If so, preserve the symbol's // memory protection by reserving space in the .bss.rel.ro section. - bool IsReadOnly = isReadOnly(SS); - OutputSection<ELFT> *CopySec = - IsReadOnly ? Out<ELFT>::BssRelRo : Out<ELFT>::Bss; + bool IsReadOnly = isReadOnly<ELFT>(SS); + BssSection *Sec = IsReadOnly ? In<ELFT>::BssRelRo : In<ELFT>::Bss; + uint64_t Off = Sec->reserveSpace(SymSize, SS->getAlignment<ELFT>()); - uintX_t Alignment = getAlignment(SS); - uintX_t Off = alignTo(CopySec->Size, Alignment); - CopySec->Size = Off + SymSize; - CopySec->updateAlignment(Alignment); - uintX_t Shndx = SS->Sym.st_shndx; - uintX_t Value = SS->Sym.st_value; // Look through the DSO's dynamic symbol table for aliases and create a // dynamic symbol for each one. This causes the copy relocation to correctly // interpose any aliases. - for (const Elf_Sym &S : SS->file()->getGlobalSymbols()) { - if (S.st_shndx != Shndx || S.st_value != Value) - continue; - auto *Alias = dyn_cast_or_null<SharedSymbol<ELFT>>( - Symtab<ELFT>::X->find(check(S.getName(SS->file()->getStringTable())))); - if (!Alias) - continue; - Alias->CopyIsInBssRelRo = IsReadOnly; - Alias->CopyOffset = Off; - Alias->NeedsCopyOrPltAddr = true; - Alias->symbol()->IsUsedInRegularObj = true; + for (SharedSymbol *Sym : getSymbolsAt<ELFT>(SS)) { + Sym->NeedsCopy = true; + Sym->CopyRelSec = Sec; + Sym->CopyRelSecOff = Off; + Sym->symbol()->IsUsedInRegularObj = true; } - In<ELFT>::RelaDyn->addReloc({Target->CopyRel, CopySec, Off, false, SS, 0}); + + In<ELFT>::RelaDyn->addReloc({Target->CopyRel, Sec, Off, false, SS, 0}); } template <class ELFT> -static RelExpr adjustExpr(const elf::ObjectFile<ELFT> &File, SymbolBody &Body, - bool IsWrite, RelExpr Expr, uint32_t Type, - const uint8_t *Data, InputSectionBase<ELFT> &S, +static RelExpr adjustExpr(SymbolBody &Body, RelExpr Expr, uint32_t Type, + const uint8_t *Data, InputSectionBase &S, typename ELFT::uint RelOff) { - bool Preemptible = isPreemptible(Body, Type); if (Body.isGnuIFunc()) { Expr = toPlt(Expr); - } else if (!Preemptible) { + } else if (!isPreemptible(Body, Type)) { if (needsPlt(Expr)) Expr = fromPlt(Expr); - if (Expr == R_GOT_PC && !isAbsoluteValue<ELFT>(Body)) + if (Expr == R_GOT_PC && !isAbsoluteValue(Body)) Expr = Target->adjustRelaxExpr(Type, Data, Expr); } - Expr = Target->getThunkExpr(Expr, Type, File, Body); + bool IsWrite = !Config->ZText || (S.Flags & SHF_WRITE); if (IsWrite || isStaticLinkTimeConstant<ELFT>(Expr, Type, Body, S, RelOff)) return Expr; @@ -476,25 +554,34 @@ static RelExpr adjustExpr(const elf::ObjectFile<ELFT> &File, SymbolBody &Body, // only memory. We can hack around it if we are producing an executable and // the refered symbol can be preemepted to refer to the executable. if (Config->Shared || (Config->Pic && !isRelExpr(Expr))) { - error(S.getLocation(RelOff) + ": can't create dynamic relocation " + - toString(Type) + " against " + + error("can't create dynamic relocation " + toString(Type) + " against " + (Body.getName().empty() ? "local symbol in readonly segment" - : "symbol '" + toString(Body) + "'") + - " defined in " + toString(Body.File)); + : "symbol: " + toString(Body)) + + getLocation<ELFT>(S, Body, RelOff)); return Expr; } + if (Body.getVisibility() != STV_DEFAULT) { - error(S.getLocation(RelOff) + ": cannot preempt symbol '" + toString(Body) + - "' defined in " + toString(Body.File)); + error("cannot preempt symbol: " + toString(Body) + + getLocation<ELFT>(S, Body, RelOff)); return Expr; } + if (Body.isObject()) { // Produce a copy relocation. - auto *B = cast<SharedSymbol<ELFT>>(&Body); - if (!B->needsCopy()) - addCopyRelSymbol(B); + auto *B = cast<SharedSymbol>(&Body); + if (!B->NeedsCopy) { + if (Config->ZNocopyreloc) + error("unresolvable relocation " + toString(Type) + + " against symbol '" + toString(*B) + + "'; recompile with -fPIC or remove '-z nocopyreloc'" + + getLocation<ELFT>(S, Body, RelOff)); + + addCopyRelSymbol<ELFT>(B); + } return Expr; } + if (Body.isFunc()) { // This handles a non PIC program call to function in a shared library. In // an ideal world, we could just report an error saying the relocation can @@ -516,61 +603,109 @@ static RelExpr adjustExpr(const elf::ObjectFile<ELFT> &File, SymbolBody &Body, // that points to the real function is a dedicated got entry used by the // plt. That is identified by special relocation types (R_X86_64_JUMP_SLOT, // R_386_JMP_SLOT, etc). - Body.NeedsCopyOrPltAddr = true; + Body.NeedsPltAddr = true; return toPlt(Expr); } - error("symbol '" + toString(Body) + "' defined in " + toString(Body.File) + - " is missing type"); + error("symbol '" + toString(Body) + "' defined in " + toString(Body.File) + + " has no type"); return Expr; } +// Returns an addend of a given relocation. If it is RELA, an addend +// is in a relocation itself. If it is REL, we need to read it from an +// input section. template <class ELFT, class RelTy> -static typename ELFT::uint computeAddend(const elf::ObjectFile<ELFT> &File, - const uint8_t *SectionData, - const RelTy *End, const RelTy &RI, - RelExpr Expr, SymbolBody &Body) { - typedef typename ELFT::uint uintX_t; +static int64_t computeAddend(const RelTy &Rel, const uint8_t *Buf) { + uint32_t Type = Rel.getType(Config->IsMips64EL); + int64_t A = RelTy::IsRela + ? getAddend<ELFT>(Rel) + : Target->getImplicitAddend(Buf + Rel.r_offset, Type); + + if (Config->EMachine == EM_PPC64 && Config->Pic && Type == R_PPC64_TOC) + A += getPPC64TocBase(); + return A; +} + +// MIPS has an odd notion of "paired" relocations to calculate addends. +// For example, if a relocation is of R_MIPS_HI16, there must be a +// R_MIPS_LO16 relocation after that, and an addend is calculated using +// the two relocations. +template <class ELFT, class RelTy> +static int64_t computeMipsAddend(const RelTy &Rel, InputSectionBase &Sec, + RelExpr Expr, SymbolBody &Body, + const RelTy *End) { + if (Expr == R_MIPS_GOTREL && Body.isLocal()) + return Sec.getFile<ELFT>()->MipsGp0; + + // The ABI says that the paired relocation is used only for REL. + // See p. 4-17 at ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf + if (RelTy::IsRela) + return 0; - uint32_t Type = RI.getType(Config->Mips64EL); - uintX_t Addend = getAddend<ELFT>(RI); - const uint8_t *BufLoc = SectionData + RI.r_offset; - if (!RelTy::IsRela) - Addend += Target->getImplicitAddend(BufLoc, Type); - if (Config->EMachine == EM_MIPS) { - Addend += findMipsPairedAddend<ELFT>(SectionData, BufLoc, Body, &RI, End); - if (Type == R_MIPS_LO16 && Expr == R_PC) - // R_MIPS_LO16 expression has R_PC type iif the target is _gp_disp - // symbol. In that case we should use the following formula for - // calculation "AHL + GP - P + 4". Let's add 4 right here. - // For details see p. 4-19 at - // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf - Addend += 4; - if (Expr == R_MIPS_GOTREL && Body.isLocal()) - Addend += File.MipsGp0; + uint32_t Type = Rel.getType(Config->IsMips64EL); + uint32_t PairTy = getMipsPairType(Type, Body); + if (PairTy == R_MIPS_NONE) + return 0; + + const uint8_t *Buf = Sec.Data.data(); + uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL); + + // To make things worse, paired relocations might not be contiguous in + // the relocation table, so we need to do linear search. *sigh* + for (const RelTy *RI = &Rel; RI != End; ++RI) { + if (RI->getType(Config->IsMips64EL) != PairTy) + continue; + if (RI->getSymbol(Config->IsMips64EL) != SymIndex) + continue; + + endianness E = Config->Endianness; + int32_t Hi = (read32(Buf + Rel.r_offset, E) & 0xffff) << 16; + int32_t Lo = SignExtend32<16>(read32(Buf + RI->r_offset, E)); + return Hi + Lo; } - if (Config->Pic && Config->EMachine == EM_PPC64 && Type == R_PPC64_TOC) - Addend += getPPC64TocBase(); - return Addend; + + warn("can't find matching " + toString(PairTy) + " relocation for " + + toString(Type)); + return 0; } template <class ELFT> -static void reportUndefined(SymbolBody &Sym, InputSectionBase<ELFT> &S, - typename ELFT::uint Offset) { - if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore) +static void reportUndefined(SymbolBody &Sym, InputSectionBase &S, + uint64_t Offset) { + if (Config->UnresolvedSymbols == UnresolvedPolicy::IgnoreAll) return; - if (Config->Shared && Sym.symbol()->Visibility == STV_DEFAULT && - Config->UnresolvedSymbols != UnresolvedPolicy::NoUndef) + bool CanBeExternal = Sym.symbol()->computeBinding() != STB_LOCAL && + Sym.getVisibility() == STV_DEFAULT; + if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore && CanBeExternal) return; std::string Msg = - S.getLocation(Offset) + ": undefined symbol '" + toString(Sym) + "'"; + "undefined symbol: " + toString(Sym) + "\n>>> referenced by "; + + std::string Src = S.getSrcMsg<ELFT>(Offset); + if (!Src.empty()) + Msg += Src + "\n>>> "; + Msg += S.getObjMsg<ELFT>(Offset); - if (Config->UnresolvedSymbols == UnresolvedPolicy::Warn) + if (Config->UnresolvedSymbols == UnresolvedPolicy::WarnAll || + (Config->UnresolvedSymbols == UnresolvedPolicy::Warn && CanBeExternal)) { warn(Msg); - else + } else { error(Msg); + + if (Config->ArchiveWithoutSymbolsSeen) { + message("At least one archive listed no symbols in its index." + " This can happen when creating archives with a version" + " of ar that does not understand the object files in" + " the archive. For example, if you are using LLVM" + " bitcode objects (such as created by -flto), you may" + " need to use llvm-ar or GNU ar with a plugin."); + // Reset to false so that we print the message only once. + Config->ArchiveWithoutSymbolsSeen = false; + } + } } template <class RelTy> @@ -584,11 +719,95 @@ mergeMipsN32RelTypes(uint32_t Type, uint32_t Offset, RelTy *I, RelTy *E) { uint32_t Processed = 0; for (; I != E && Offset == I->r_offset; ++I) { ++Processed; - Type |= I->getType(Config->Mips64EL) << (8 * Processed); + Type |= I->getType(Config->IsMips64EL) << (8 * Processed); } return std::make_pair(Type, Processed); } +// .eh_frame sections are mergeable input sections, so their input +// offsets are not linearly mapped to output section. For each input +// offset, we need to find a section piece containing the offset and +// add the piece's base address to the input offset to compute the +// output offset. That isn't cheap. +// +// This class is to speed up the offset computation. When we process +// relocations, we access offsets in the monotonically increasing +// order. So we can optimize for that access pattern. +// +// For sections other than .eh_frame, this class doesn't do anything. +namespace { +class OffsetGetter { +public: + explicit OffsetGetter(InputSectionBase &Sec) { + if (auto *Eh = dyn_cast<EhInputSection>(&Sec)) { + P = Eh->Pieces; + Size = Eh->Pieces.size(); + } + } + + // Translates offsets in input sections to offsets in output sections. + // Given offset must increase monotonically. We assume that P is + // sorted by InputOff. + uint64_t get(uint64_t Off) { + if (P.empty()) + return Off; + + while (I != Size && P[I].InputOff + P[I].size() <= Off) + ++I; + if (I == Size) + return Off; + + // P must be contiguous, so there must be no holes in between. + assert(P[I].InputOff <= Off && "Relocation not in any piece"); + + // Offset -1 means that the piece is dead (i.e. garbage collected). + if (P[I].OutputOff == -1) + return -1; + return P[I].OutputOff + Off - P[I].InputOff; + } + +private: + ArrayRef<EhSectionPiece> P; + size_t I = 0; + size_t Size; +}; +} // namespace + +template <class ELFT, class GotPltSection> +static void addPltEntry(PltSection *Plt, GotPltSection *GotPlt, + RelocationSection<ELFT> *Rel, uint32_t Type, + SymbolBody &Sym, bool UseSymVA) { + Plt->addEntry<ELFT>(Sym); + GotPlt->addEntry(Sym); + Rel->addReloc({Type, GotPlt, Sym.getGotPltOffset(), UseSymVA, &Sym, 0}); +} + +template <class ELFT> +static void addGotEntry(SymbolBody &Sym, bool Preemptible) { + In<ELFT>::Got->addEntry(Sym); + + uint64_t Off = Sym.getGotOffset(); + uint32_t DynType; + RelExpr Expr = R_ABS; + + if (Sym.isTls()) { + DynType = Target->TlsGotRel; + Expr = R_TLS; + } else if (!Preemptible && Config->Pic && !isAbsolute(Sym)) { + DynType = Target->RelativeRel; + } else { + DynType = Target->GotRel; + } + + bool Constant = !Preemptible && !(Config->Pic && !isAbsolute(Sym)); + if (!Constant) + In<ELFT>::RelaDyn->addReloc( + {DynType, In<ELFT>::Got, Off, !Preemptible, &Sym, 0}); + + if (Constant || (!Config->IsRela && !Preemptible)) + In<ELFT>::Got->Relocations.push_back({Expr, DynType, Off, 0, &Sym}); +} + // The reason we have to do this early scan is as follows // * To mmap the output file, we need to know the size // * For that, we need to know how many dynamic relocs we will have. @@ -603,114 +822,104 @@ mergeMipsN32RelTypes(uint32_t Type, uint32_t Offset, RelTy *I, RelTy *E) { // complicates things for the dynamic linker and means we would have to reserve // space for the extra PT_LOAD even if we end up not using it. template <class ELFT, class RelTy> -static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) { - typedef typename ELFT::uint uintX_t; - - bool IsWrite = C.Flags & SHF_WRITE; - - auto AddDyn = [=](const DynamicReloc<ELFT> &Reloc) { - In<ELFT>::RelaDyn->addReloc(Reloc); - }; - - const elf::ObjectFile<ELFT> *File = C.getFile(); - ArrayRef<uint8_t> SectionData = C.Data; - const uint8_t *Buf = SectionData.begin(); +static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) { + OffsetGetter GetOffset(Sec); - ArrayRef<EhSectionPiece> Pieces; - if (auto *Eh = dyn_cast<EhInputSection<ELFT>>(&C)) - Pieces = Eh->Pieces; - - ArrayRef<EhSectionPiece>::iterator PieceI = Pieces.begin(); - ArrayRef<EhSectionPiece>::iterator PieceE = Pieces.end(); - - for (auto I = Rels.begin(), E = Rels.end(); I != E; ++I) { - const RelTy &RI = *I; - SymbolBody &Body = File->getRelocTargetSym(RI); - uint32_t Type = RI.getType(Config->Mips64EL); + for (auto I = Rels.begin(), End = Rels.end(); I != End; ++I) { + const RelTy &Rel = *I; + SymbolBody &Body = Sec.getFile<ELFT>()->getRelocTargetSym(Rel); + uint32_t Type = Rel.getType(Config->IsMips64EL); if (Config->MipsN32Abi) { uint32_t Processed; std::tie(Type, Processed) = - mergeMipsN32RelTypes(Type, RI.r_offset, I + 1, E); + mergeMipsN32RelTypes(Type, Rel.r_offset, I + 1, End); I += Processed; } - // We only report undefined symbols if they are referenced somewhere in the - // code. + // Compute the offset of this section in the output section. + uint64_t Offset = GetOffset.get(Rel.r_offset); + if (Offset == uint64_t(-1)) + continue; + + // Report undefined symbols. The fact that we report undefined + // symbols here means that we report undefined symbols only when + // they have relocations pointing to them. We don't care about + // undefined symbols that are in dead-stripped sections. if (!Body.isLocal() && Body.isUndefined() && !Body.symbol()->isWeak()) - reportUndefined(Body, C, RI.r_offset); + reportUndefined<ELFT>(Body, Sec, Rel.r_offset); + + RelExpr Expr = + Target->getRelExpr(Type, Body, Sec.Data.begin() + Rel.r_offset); + + // Ignore "hint" relocations because they are only markers for relaxation. + if (isRelExprOneOf<R_HINT, R_NONE>(Expr)) + continue; - RelExpr Expr = Target->getRelExpr(Type, Body); bool Preemptible = isPreemptible(Body, Type); - Expr = adjustExpr(*File, Body, IsWrite, Expr, Type, Buf + RI.r_offset, C, - RI.r_offset); + Expr = adjustExpr<ELFT>(Body, Expr, Type, Sec.Data.data() + Rel.r_offset, + Sec, Rel.r_offset); if (ErrorCount) continue; - // Skip a relocation that points to a dead piece - // in a eh_frame section. - while (PieceI != PieceE && - (PieceI->InputOff + PieceI->size() <= RI.r_offset)) - ++PieceI; - - // Compute the offset of this section in the output section. We do it here - // to try to compute it only once. - uintX_t Offset; - if (PieceI != PieceE) { - assert(PieceI->InputOff <= RI.r_offset && "Relocation not in any piece"); - if (PieceI->OutputOff == -1) - continue; - Offset = PieceI->OutputOff + RI.r_offset - PieceI->InputOff; - } else { - Offset = RI.r_offset; - } - // This relocation does not require got entry, but it is relative to got and // needs it to be created. Here we request for that. - if (Expr == R_GOTONLY_PC || Expr == R_GOTONLY_PC_FROM_END || - Expr == R_GOTREL || Expr == R_GOTREL_FROM_END || Expr == R_PPC_TOC) + if (isRelExprOneOf<R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, R_GOTREL, + R_GOTREL_FROM_END, R_PPC_TOC>(Expr)) In<ELFT>::Got->HasGotOffRel = true; - uintX_t Addend = computeAddend(*File, Buf, E, RI, Expr, Body); + // Read an addend. + int64_t Addend = computeAddend<ELFT>(Rel, Sec.Data.data()); + if (Config->EMachine == EM_MIPS) + Addend += computeMipsAddend<ELFT>(Rel, Sec, Expr, Body, End); + // Process some TLS relocations, including relaxing TLS relocations. + // Note that this function does not handle all TLS relocations. if (unsigned Processed = - handleTlsRelocation<ELFT>(Type, Body, C, Offset, Addend, Expr)) { + handleTlsRelocation<ELFT>(Type, Body, Sec, Offset, Addend, Expr)) { I += (Processed - 1); continue; } - // Ignore "hint" and TLS Descriptor call relocation because they are - // only markers for relaxation. - if (isRelExprOneOf<R_HINT, R_TLSDESC_CALL>(Expr)) - continue; - - if (needsPlt(Expr) || - isRelExprOneOf<R_THUNK_ABS, R_THUNK_PC, R_THUNK_PLT_PC>(Expr) || - refersToGotEntry(Expr) || !isPreemptible(Body, Type)) { - // If the relocation points to something in the file, we can process it. - bool Constant = - isStaticLinkTimeConstant<ELFT>(Expr, Type, Body, C, RI.r_offset); + // If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol. + if (needsPlt(Expr) && !Body.isInPlt()) { + if (Body.isGnuIFunc() && !Preemptible) + addPltEntry(InX::Iplt, In<ELFT>::IgotPlt, In<ELFT>::RelaIplt, + Target->IRelativeRel, Body, true); + else + addPltEntry(InX::Plt, In<ELFT>::GotPlt, In<ELFT>::RelaPlt, + Target->PltRel, Body, !Preemptible); + } - // If the output being produced is position independent, the final value - // is still not known. In that case we still need some help from the - // dynamic linker. We can however do better than just copying the incoming - // relocation. We can process some of it and and just ask the dynamic - // linker to add the load address. - if (!Constant) - AddDyn({Target->RelativeRel, &C, Offset, true, &Body, Addend}); + // Create a GOT slot if a relocation needs GOT. + if (needsGot(Expr)) { + if (Config->EMachine == EM_MIPS) { + // MIPS ABI has special rules to process GOT entries and doesn't + // require relocation entries for them. A special case is TLS + // relocations. In that case dynamic loader applies dynamic + // relocations to initialize TLS GOT entries. + // See "Global Offset Table" in Chapter 5 in the following document + // for detailed description: + // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf + In<ELFT>::MipsGot->addEntry(Body, Addend, Expr); + if (Body.isTls() && Body.isPreemptible()) + In<ELFT>::RelaDyn->addReloc({Target->TlsGotRel, In<ELFT>::MipsGot, + Body.getGotOffset(), false, &Body, 0}); + } else if (!Body.isInGot()) { + addGotEntry<ELFT>(Body, Preemptible); + } + } - // If the produced value is a constant, we just remember to write it - // when outputting this section. We also have to do it if the format - // uses Elf_Rel, since in that case the written value is the addend. - if (Constant || !RelTy::IsRela) - C.Relocations.push_back({Expr, Type, Offset, Addend, &Body}); - } else { + if (!needsPlt(Expr) && !needsGot(Expr) && isPreemptible(Body, Type)) { // We don't know anything about the finaly symbol. Just ask the dynamic // linker to handle the relocation for us. if (!Target->isPicRel(Type)) - error(C.getLocation(Offset) + ": relocation " + toString(Type) + - " cannot be used against shared object; recompile with -fPIC."); - AddDyn({Target->getDynRel(Type), &C, Offset, false, &Body, Addend}); + error("relocation " + toString(Type) + + " cannot be used against shared object; recompile with -fPIC" + + getLocation<ELFT>(Sec, Body, Offset)); + + In<ELFT>::RelaDyn->addReloc( + {Target->getDynRel(Type), &Sec, Offset, false, &Body, Addend}); // MIPS ABI turns using of GOT and dynamic relocations inside out. // While regular ABI uses dynamic relocations to fill up GOT entries @@ -732,114 +941,163 @@ static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) { continue; } - // At this point we are done with the relocated position. Some relocations - // also require us to create a got or plt entry. + // If the relocation points to something in the file, we can process it. + bool IsConstant = + isStaticLinkTimeConstant<ELFT>(Expr, Type, Body, Sec, Rel.r_offset); - // If a relocation needs PLT, we create a PLT and a GOT slot for the symbol. - if (needsPlt(Expr)) { - if (Body.isInPlt()) - continue; + // If the output being produced is position independent, the final value + // is still not known. In that case we still need some help from the + // dynamic linker. We can however do better than just copying the incoming + // relocation. We can process some of it and and just ask the dynamic + // linker to add the load address. + if (!IsConstant) + In<ELFT>::RelaDyn->addReloc( + {Target->RelativeRel, &Sec, Offset, true, &Body, Addend}); - if (Body.isGnuIFunc() && !Preemptible) { - In<ELFT>::Iplt->addEntry(Body); - In<ELFT>::IgotPlt->addEntry(Body); - In<ELFT>::RelaIplt->addReloc({Target->IRelativeRel, In<ELFT>::IgotPlt, - Body.getGotPltOffset<ELFT>(), - !Preemptible, &Body, 0}); - } else { - In<ELFT>::Plt->addEntry(Body); - In<ELFT>::GotPlt->addEntry(Body); - In<ELFT>::RelaPlt->addReloc({Target->PltRel, In<ELFT>::GotPlt, - Body.getGotPltOffset<ELFT>(), !Preemptible, - &Body, 0}); - } - continue; - } + // If the produced value is a constant, we just remember to write it + // when outputting this section. We also have to do it if the format + // uses Elf_Rel, since in that case the written value is the addend. + if (IsConstant || !RelTy::IsRela) + Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Body}); + } +} - if (refersToGotEntry(Expr)) { - if (Config->EMachine == EM_MIPS) { - // MIPS ABI has special rules to process GOT entries and doesn't - // require relocation entries for them. A special case is TLS - // relocations. In that case dynamic loader applies dynamic - // relocations to initialize TLS GOT entries. - // See "Global Offset Table" in Chapter 5 in the following document - // for detailed description: - // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf - In<ELFT>::MipsGot->addEntry(Body, Addend, Expr); - if (Body.isTls() && Body.isPreemptible()) - AddDyn({Target->TlsGotRel, In<ELFT>::MipsGot, - Body.getGotOffset<ELFT>(), false, &Body, 0}); - continue; - } +template <class ELFT> void elf::scanRelocations(InputSectionBase &S) { + if (S.AreRelocsRela) + scanRelocs<ELFT>(S, S.relas<ELFT>()); + else + scanRelocs<ELFT>(S, S.rels<ELFT>()); +} - if (Body.isInGot()) - continue; +// Insert the Thunks for OutputSection OS into their designated place +// in the Sections vector, and recalculate the InputSection output section +// offsets. +// This may invalidate any output section offsets stored outside of InputSection +template <class ELFT> +void ThunkCreator<ELFT>::mergeThunks(OutputSection *OS, + std::vector<ThunkSection *> &Thunks) { + // Order Thunks in ascending OutSecOff + auto ThunkCmp = [](const ThunkSection *A, const ThunkSection *B) { + return A->OutSecOff < B->OutSecOff; + }; + std::stable_sort(Thunks.begin(), Thunks.end(), ThunkCmp); - In<ELFT>::Got->addEntry(Body); - uintX_t Off = Body.getGotOffset<ELFT>(); - uint32_t DynType; - RelExpr GotRE = R_ABS; - if (Body.isTls()) { - DynType = Target->TlsGotRel; - GotRE = R_TLS; - } else if (!Preemptible && Config->Pic && !isAbsolute<ELFT>(Body)) - DynType = Target->RelativeRel; - else - DynType = Target->GotRel; + // Merge sorted vectors of Thunks and InputSections by OutSecOff + std::vector<InputSection *> Tmp; + Tmp.reserve(OS->Sections.size() + Thunks.size()); + auto MergeCmp = [](const InputSection *A, const InputSection *B) { + // std::merge requires a strict weak ordering. + if (A->OutSecOff < B->OutSecOff) + return true; + if (A->OutSecOff == B->OutSecOff) + // Check if Thunk is immediately before any specific Target InputSection + // for example Mips LA25 Thunks. + if (auto *TA = dyn_cast<ThunkSection>(A)) + if (TA && TA->getTargetInputSection() == B) + return true; + return false; + }; + std::merge(OS->Sections.begin(), OS->Sections.end(), Thunks.begin(), + Thunks.end(), std::back_inserter(Tmp), MergeCmp); + OS->Sections = std::move(Tmp); + OS->assignOffsets(); +} - // FIXME: this logic is almost duplicated above. - bool Constant = !Preemptible && !(Config->Pic && !isAbsolute<ELFT>(Body)); - if (!Constant) - AddDyn({DynType, In<ELFT>::Got, Off, !Preemptible, &Body, 0}); - if (Constant || (!RelTy::IsRela && !Preemptible)) - In<ELFT>::Got->Relocations.push_back({GotRE, DynType, Off, 0, &Body}); - continue; +template <class ELFT> +ThunkSection *ThunkCreator<ELFT>::getOSThunkSec(ThunkSection *&TS, + OutputSection *OS) { + if (TS == nullptr) { + uint32_t Off = 0; + for (auto *IS : OS->Sections) { + Off = IS->OutSecOff + IS->getSize(); + if ((IS->Flags & SHF_EXECINSTR) == 0) + break; } + TS = make<ThunkSection>(OS, Off); + ThunkSections[OS].push_back(TS); } + return TS; } -template <class ELFT> void scanRelocations(InputSectionBase<ELFT> &S) { - if (S.AreRelocsRela) - scanRelocs(S, S.relas()); - else - scanRelocs(S, S.rels()); +template <class ELFT> +ThunkSection *ThunkCreator<ELFT>::getISThunkSec(InputSection *IS, + OutputSection *OS) { + ThunkSection *TS = ThunkedSections.lookup(IS); + if (TS) + return TS; + auto *TOS = cast<OutputSection>(IS->OutSec); + TS = make<ThunkSection>(TOS, IS->OutSecOff); + ThunkSections[TOS].push_back(TS); + ThunkedSections[IS] = TS; + return TS; } -template <class ELFT, class RelTy> -static void createThunks(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) { - const elf::ObjectFile<ELFT> *File = C.getFile(); - for (const RelTy &Rel : Rels) { - SymbolBody &Body = File->getRelocTargetSym(Rel); - uint32_t Type = Rel.getType(Config->Mips64EL); - RelExpr Expr = Target->getRelExpr(Type, Body); - if (!isPreemptible(Body, Type) && needsPlt(Expr)) - Expr = fromPlt(Expr); - Expr = Target->getThunkExpr(Expr, Type, *File, Body); - // Some targets might require creation of thunks for relocations. - // Now we support only MIPS which requires LA25 thunk to call PIC - // code from non-PIC one, and ARM which requires interworking. - if (Expr == R_THUNK_ABS || Expr == R_THUNK_PC || Expr == R_THUNK_PLT_PC) { - auto *Sec = cast<InputSection<ELFT>>(&C); - addThunk<ELFT>(Type, Body, *Sec); +template <class ELFT> +std::pair<Thunk *, bool> ThunkCreator<ELFT>::getThunk(SymbolBody &Body, + uint32_t Type) { + auto res = ThunkedSymbols.insert({&Body, nullptr}); + if (res.second) + res.first->second = addThunk<ELFT>(Type, Body); + return std::make_pair(res.first->second, res.second); +} + +// Process all relocations from the InputSections that have been assigned +// to OutputSections and redirect through Thunks if needed. +// +// createThunks must be called after scanRelocs has created the Relocations for +// each InputSection. It must be called before the static symbol table is +// finalized. If any Thunks are added to an OutputSection the output section +// offsets of the InputSections will change. +// +// FIXME: All Thunks are assumed to be in range of the relocation. Range +// extension Thunks are not yet supported. +template <class ELFT> +bool ThunkCreator<ELFT>::createThunks( + ArrayRef<OutputSection *> OutputSections) { + // Create all the Thunks and insert them into synthetic ThunkSections. The + // ThunkSections are later inserted back into the OutputSection. + + // We separate the creation of ThunkSections from the insertion of the + // ThunkSections back into the OutputSection as ThunkSections are not always + // inserted into the same OutputSection as the caller. + for (OutputSection *OS : OutputSections) { + ThunkSection *OSTS = nullptr; + for (InputSection *IS : OS->Sections) { + for (Relocation &Rel : IS->Relocations) { + SymbolBody &Body = *Rel.Sym; + if (!Target->needsThunk(Rel.Expr, Rel.Type, IS->File, Body)) + continue; + Thunk *T; + bool IsNew; + std::tie(T, IsNew) = getThunk(Body, Rel.Type); + if (IsNew) { + // Find or create a ThunkSection for the new Thunk + ThunkSection *TS; + if (auto *TIS = T->getTargetInputSection()) + TS = getISThunkSec(TIS, OS); + else + TS = getOSThunkSec(OSTS, OS); + TS->addThunk(T); + } + // Redirect relocation to Thunk, we never go via the PLT to a Thunk + Rel.Sym = T->ThunkSym; + Rel.Expr = fromPlt(Rel.Expr); + } } } -} -template <class ELFT> void createThunks(InputSectionBase<ELFT> &S) { - if (S.AreRelocsRela) - createThunks(S, S.relas()); - else - createThunks(S, S.rels()); + // Merge all created synthetic ThunkSections back into OutputSection + for (auto &KV : ThunkSections) + mergeThunks(KV.first, KV.second); + return !ThunkSections.empty(); } -template void scanRelocations<ELF32LE>(InputSectionBase<ELF32LE> &); -template void scanRelocations<ELF32BE>(InputSectionBase<ELF32BE> &); -template void scanRelocations<ELF64LE>(InputSectionBase<ELF64LE> &); -template void scanRelocations<ELF64BE>(InputSectionBase<ELF64BE> &); +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 createThunks<ELF32LE>(InputSectionBase<ELF32LE> &); -template void createThunks<ELF32BE>(InputSectionBase<ELF32BE> &); -template void createThunks<ELF64LE>(InputSectionBase<ELF64LE> &); -template void createThunks<ELF64BE>(InputSectionBase<ELF64BE> &); -} -} +template class elf::ThunkCreator<ELF32LE>; +template class elf::ThunkCreator<ELF32BE>; +template class elf::ThunkCreator<ELF64LE>; +template class elf::ThunkCreator<ELF64BE>; |
