Changeset View
Changeset View
Standalone View
Standalone View
head/contrib/llvm/tools/lld/ELF/Relocations.cpp
Show First 20 Lines • Show All 331 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static bool isAbsoluteValue(const Symbol &Sym) { | static bool isAbsoluteValue(const Symbol &Sym) { | ||||
return isAbsolute(Sym) || Sym.isTls(); | return isAbsolute(Sym) || Sym.isTls(); | ||||
} | } | ||||
// Returns true if Expr refers a PLT entry. | // Returns true if Expr refers a PLT entry. | ||||
static bool needsPlt(RelExpr Expr) { | static bool needsPlt(RelExpr Expr) { | ||||
return isRelExprOneOf<R_PLT_PC, R_PPC_CALL_PLT, R_PLT, R_AARCH64_PLT_PAGE_PC, | return isRelExprOneOf<R_PLT_PC, R_PPC_CALL_PLT, R_PLT>(Expr); | ||||
R_GOT_PLT, R_AARCH64_GOT_PAGE_PC_PLT>(Expr); | |||||
} | } | ||||
// Returns true if Expr refers a GOT entry. Note that this function | // Returns true if Expr refers a GOT entry. Note that this function | ||||
// returns false for TLS variables even though they need GOT, because | // returns false for TLS variables even though they need GOT, because | ||||
// TLS variables uses GOT differently than the regular variables. | // TLS variables uses GOT differently than the regular variables. | ||||
static bool needsGot(RelExpr Expr) { | static bool needsGot(RelExpr Expr) { | ||||
return isRelExprOneOf<R_GOT, R_GOT_OFF, R_HEXAGON_GOT, R_MIPS_GOT_LOCAL_PAGE, | return isRelExprOneOf<R_GOT, R_GOT_OFF, R_HEXAGON_GOT, R_MIPS_GOT_LOCAL_PAGE, | ||||
R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_AARCH64_GOT_PAGE_PC, | R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_AARCH64_GOT_PAGE_PC, | ||||
R_AARCH64_GOT_PAGE_PC_PLT, R_GOT_PC, R_GOT_FROM_END, | R_GOT_PC, R_GOT_FROM_END>(Expr); | ||||
R_GOT_PLT>(Expr); | |||||
} | } | ||||
// True if this expression is of the form Sym - X, where X is a position in the | // True if this expression is of the form Sym - X, where X is a position in the | ||||
// file (PC, or GOT for example). | // file (PC, or GOT for example). | ||||
static bool isRelExpr(RelExpr Expr) { | static bool isRelExpr(RelExpr Expr) { | ||||
return isRelExprOneOf<R_PC, R_GOTREL, R_GOTREL_FROM_END, R_MIPS_GOTREL, | return isRelExprOneOf<R_PC, R_GOTREL, R_GOTREL_FROM_END, R_MIPS_GOTREL, | ||||
R_PPC_CALL, R_PPC_CALL_PLT, R_AARCH64_PAGE_PC, | R_PPC_CALL, R_PPC_CALL_PLT, R_AARCH64_PAGE_PC, | ||||
R_AARCH64_PLT_PAGE_PC, R_RELAX_GOT_PC>(Expr); | R_RELAX_GOT_PC>(Expr); | ||||
} | } | ||||
// Returns true if a given relocation can be computed at link-time. | // 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 | // For instance, we know the offset from a relocation to its target at | ||||
// link-time if the relocation is PC-relative and refers a | // link-time if the relocation is PC-relative and refers a | ||||
// non-interposable function in the same executable. This function | // non-interposable function in the same executable. This function | ||||
// will return true for such relocation. | // will return true for such relocation. | ||||
// | // | ||||
// If this function returns false, that means we need to emit a | // If this function returns false, that means we need to emit a | ||||
// dynamic relocation so that the relocation will be fixed at load-time. | // dynamic relocation so that the relocation will be fixed at load-time. | ||||
static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym, | static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym, | ||||
InputSectionBase &S, uint64_t RelOff) { | InputSectionBase &S, uint64_t RelOff) { | ||||
// These expressions always compute a constant | // These expressions always compute a constant | ||||
if (isRelExprOneOf<R_GOT_FROM_END, R_GOT_OFF, R_HEXAGON_GOT, R_TLSLD_GOT_OFF, | if (isRelExprOneOf<R_GOT_FROM_END, R_GOT_OFF, R_HEXAGON_GOT, R_TLSLD_GOT_OFF, | ||||
R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOTREL, R_MIPS_GOT_OFF, | R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOTREL, R_MIPS_GOT_OFF, | ||||
R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, R_MIPS_TLSGD, | R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, R_MIPS_TLSGD, | ||||
R_AARCH64_GOT_PAGE_PC, R_AARCH64_GOT_PAGE_PC_PLT, R_GOT_PC, | R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, | ||||
R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_GOT, | R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_GOT, | ||||
R_TLSGD_GOT_FROM_END, R_TLSGD_PC, R_PPC_CALL_PLT, | R_TLSGD_GOT_FROM_END, R_TLSGD_PC, R_PPC_CALL_PLT, | ||||
R_TLSDESC_CALL, R_AARCH64_TLSDESC_PAGE, R_HINT, | R_TLSDESC_CALL, R_AARCH64_TLSDESC_PAGE, R_HINT, | ||||
R_TLSLD_HINT, R_TLSIE_HINT>(E)) | R_TLSLD_HINT, R_TLSIE_HINT>(E)) | ||||
return true; | return true; | ||||
// The computation involves output from the ifunc resolver. | |||||
if (Sym.isGnuIFunc() && Config->ZIfuncnoplt) | |||||
return false; | |||||
// These never do, except if the entire file is position dependent or if | // These never do, except if the entire file is position dependent or if | ||||
// only the low bits are used. | // only the low bits are used. | ||||
if (E == R_GOT || E == R_GOT_PLT || E == R_PLT || E == R_TLSDESC) | if (E == R_GOT || E == R_PLT || E == R_TLSDESC) | ||||
return Target->usesOnlyLowPageBits(Type) || !Config->Pic; | return Target->usesOnlyLowPageBits(Type) || !Config->Pic; | ||||
if (Sym.IsPreemptible) | if (Sym.IsPreemptible) | ||||
return false; | return false; | ||||
if (!Config->Pic) | if (!Config->Pic) | ||||
return true; | return true; | ||||
// The size of a non preemptible symbol is a constant. | // The size of a non preemptible symbol is a constant. | ||||
Show All 29 Lines | |||||
} | } | ||||
static RelExpr toPlt(RelExpr Expr) { | static RelExpr toPlt(RelExpr Expr) { | ||||
switch (Expr) { | switch (Expr) { | ||||
case R_PPC_CALL: | case R_PPC_CALL: | ||||
return R_PPC_CALL_PLT; | return R_PPC_CALL_PLT; | ||||
case R_PC: | case R_PC: | ||||
return R_PLT_PC; | return R_PLT_PC; | ||||
case R_AARCH64_PAGE_PC: | |||||
return R_AARCH64_PLT_PAGE_PC; | |||||
case R_AARCH64_GOT_PAGE_PC: | |||||
return R_AARCH64_GOT_PAGE_PC_PLT; | |||||
case R_ABS: | case R_ABS: | ||||
return R_PLT; | return R_PLT; | ||||
case R_GOT: | |||||
return R_GOT_PLT; | |||||
default: | default: | ||||
return Expr; | return Expr; | ||||
} | } | ||||
} | } | ||||
static RelExpr fromPlt(RelExpr Expr) { | static RelExpr fromPlt(RelExpr Expr) { | ||||
// We decided not to use a plt. Optimize a reference to the plt to a | // We decided not to use a plt. Optimize a reference to the plt to a | ||||
// reference to the symbol itself. | // reference to the symbol itself. | ||||
▲ Show 20 Lines • Show All 315 Lines • ▼ Show 20 Lines | static void addPltEntry(PltSection *Plt, GotPltSection *GotPlt, | ||||
GotPlt->addEntry(Sym); | GotPlt->addEntry(Sym); | ||||
Rel->addReloc( | Rel->addReloc( | ||||
{Type, GotPlt, Sym.getGotPltOffset(), !Sym.IsPreemptible, &Sym, 0}); | {Type, GotPlt, Sym.getGotPltOffset(), !Sym.IsPreemptible, &Sym, 0}); | ||||
} | } | ||||
template <class ELFT> static void addGotEntry(Symbol &Sym) { | template <class ELFT> static void addGotEntry(Symbol &Sym) { | ||||
In.Got->addEntry(Sym); | In.Got->addEntry(Sym); | ||||
RelExpr Expr; | RelExpr Expr = Sym.isTls() ? R_TLS : R_ABS; | ||||
if (Sym.isTls()) | |||||
Expr = R_TLS; | |||||
else if (Sym.isGnuIFunc()) | |||||
Expr = R_PLT; | |||||
else | |||||
Expr = R_ABS; | |||||
uint64_t Off = Sym.getGotOffset(); | uint64_t Off = Sym.getGotOffset(); | ||||
// If a GOT slot value can be calculated at link-time, which is now, | // If a GOT slot value can be calculated at link-time, which is now, | ||||
// we can just fill that out. | // we can just fill that out. | ||||
// | // | ||||
// (We don't actually write a value to a GOT slot right now, but we | // (We don't actually write a value to a GOT slot right now, but we | ||||
// add a static relocation to a Relocations vector so that | // add a static relocation to a Relocations vector so that | ||||
// InputSection::relocate will do the work for us. We may be able | // InputSection::relocate will do the work for us. We may be able | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | |||||
template <class ELFT, class RelTy> | template <class ELFT, class RelTy> | ||||
static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type, | static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type, | ||||
uint64_t Offset, Symbol &Sym, const RelTy &Rel, | uint64_t Offset, Symbol &Sym, const RelTy &Rel, | ||||
int64_t Addend) { | int64_t Addend) { | ||||
if (isStaticLinkTimeConstant(Expr, Type, Sym, Sec, Offset)) { | if (isStaticLinkTimeConstant(Expr, Type, Sym, Sec, Offset)) { | ||||
Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); | Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); | ||||
return; | return; | ||||
} | } | ||||
if (Sym.isGnuIFunc() && Config->ZIfuncnoplt) { | |||||
In.RelaDyn->addReloc(Type, &Sec, Offset, &Sym, Addend, R_ADDEND, Type); | |||||
return; | |||||
} | |||||
bool CanWrite = (Sec.Flags & SHF_WRITE) || !Config->ZText; | bool CanWrite = (Sec.Flags & SHF_WRITE) || !Config->ZText; | ||||
if (CanWrite) { | if (CanWrite) { | ||||
// R_GOT refers to a position in the got, even if the symbol is preemptible. | // R_GOT refers to a position in the got, even if the symbol is preemptible. | ||||
bool IsPreemptibleValue = Sym.IsPreemptible && Expr != R_GOT; | bool IsPreemptibleValue = Sym.IsPreemptible && Expr != R_GOT; | ||||
if (!IsPreemptibleValue) { | if (!IsPreemptibleValue) { | ||||
addRelativeReloc(&Sec, Offset, &Sym, Addend, Expr, Type); | addRelativeReloc(&Sec, Offset, &Sym, Addend, Expr, Type); | ||||
return; | return; | ||||
▲ Show 20 Lines • Show All 110 Lines • ▼ Show 20 Lines | if (Sym.isFunc()) { | ||||
Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); | Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); | ||||
return; | return; | ||||
} | } | ||||
errorOrWarn("symbol '" + toString(Sym) + "' has no type" + | errorOrWarn("symbol '" + toString(Sym) + "' has no type" + | ||||
getLocation(Sec, Sym, Offset)); | getLocation(Sec, Sym, Offset)); | ||||
} | } | ||||
struct IRelativeReloc { | |||||
RelType Type; | |||||
InputSectionBase *Sec; | |||||
uint64_t Offset; | |||||
Symbol *Sym; | |||||
}; | |||||
static std::vector<IRelativeReloc> IRelativeRelocs; | |||||
template <class ELFT, class RelTy> | template <class ELFT, class RelTy> | ||||
static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I, | static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I, | ||||
RelTy *End) { | RelTy *End) { | ||||
const RelTy &Rel = *I; | const RelTy &Rel = *I; | ||||
Symbol &Sym = Sec.getFile<ELFT>()->getRelocTargetSym(Rel); | Symbol &Sym = Sec.getFile<ELFT>()->getRelocTargetSym(Rel); | ||||
RelType Type; | RelType Type; | ||||
// Deal with MIPS oddity. | // Deal with MIPS oddity. | ||||
Show All 15 Lines | static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I, | ||||
const uint8_t *RelocatedAddr = Sec.data().begin() + Rel.r_offset; | const uint8_t *RelocatedAddr = Sec.data().begin() + Rel.r_offset; | ||||
RelExpr Expr = Target->getRelExpr(Type, Sym, RelocatedAddr); | RelExpr Expr = Target->getRelExpr(Type, Sym, RelocatedAddr); | ||||
// Ignore "hint" relocations because they are only markers for relaxation. | // Ignore "hint" relocations because they are only markers for relaxation. | ||||
if (isRelExprOneOf<R_HINT, R_NONE>(Expr)) | if (isRelExprOneOf<R_HINT, R_NONE>(Expr)) | ||||
return; | return; | ||||
// Strenghten or relax relocations. | if (Sym.isGnuIFunc() && !Config->ZText && Config->WarnIfuncTextrel) { | ||||
// | |||||
// GNU ifunc symbols must be accessed via PLT because their addresses | |||||
// are determined by runtime. | |||||
// | |||||
// On the other hand, if we know that a PLT entry will be resolved within | |||||
// the same ELF module, we can skip PLT access and directly jump to the | |||||
// destination function. For example, if we are linking a main exectuable, | |||||
// all dynamic symbols that can be resolved within the executable will | |||||
// actually be resolved that way at runtime, because the main exectuable | |||||
// is always at the beginning of a search list. We can leverage that fact. | |||||
if (Sym.isGnuIFunc() && !Config->ZIfuncnoplt) { | |||||
if (!Config->ZText && Config->WarnIfuncTextrel) { | |||||
warn("using ifunc symbols when text relocations are allowed may produce " | warn("using ifunc symbols when text relocations are allowed may produce " | ||||
"a binary that will segfault, if the object file is linked with " | "a binary that will segfault, if the object file is linked with " | ||||
"old version of glibc (glibc 2.28 and earlier). If this applies to " | "old version of glibc (glibc 2.28 and earlier). If this applies to " | ||||
"you, consider recompiling the object files without -fPIC and " | "you, consider recompiling the object files without -fPIC and " | ||||
"without -Wl,-z,notext option. Use -no-warn-ifunc-textrel to " | "without -Wl,-z,notext option. Use -no-warn-ifunc-textrel to " | ||||
"turn off this warning." + | "turn off this warning." + | ||||
getLocation(Sec, Sym, Offset)); | getLocation(Sec, Sym, Offset)); | ||||
} | } | ||||
Expr = toPlt(Expr); | |||||
} else if (!Sym.IsPreemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym)) { | // Relax relocations. | ||||
// | |||||
// If we know that a PLT entry will be resolved within the same ELF module, we | |||||
// can skip PLT access and directly jump to the destination function. For | |||||
// example, if we are linking a main exectuable, all dynamic symbols that can | |||||
// be resolved within the executable will actually be resolved that way at | |||||
// runtime, because the main exectuable is always at the beginning of a search | |||||
// list. We can leverage that fact. | |||||
if (!Sym.IsPreemptible && (!Sym.isGnuIFunc() || Config->ZIfuncNoplt)) { | |||||
if (Expr == R_GOT_PC && !isAbsoluteValue(Sym)) | |||||
Expr = Target->adjustRelaxExpr(Type, RelocatedAddr, Expr); | Expr = Target->adjustRelaxExpr(Type, RelocatedAddr, Expr); | ||||
} else if (!Sym.IsPreemptible) { | else | ||||
Expr = fromPlt(Expr); | Expr = fromPlt(Expr); | ||||
} | } | ||||
// This relocation does not require got entry, but it is relative to got and | // This relocation does not require got entry, but it is relative to got and | ||||
// needs it to be created. Here we request for that. | // needs it to be created. Here we request for that. | ||||
if (isRelExprOneOf<R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, R_GOTREL, | if (isRelExprOneOf<R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, R_GOTREL, | ||||
R_GOTREL_FROM_END, R_PPC_TOC>(Expr)) | R_GOTREL_FROM_END, R_PPC_TOC>(Expr)) | ||||
In.Got->HasGotOffRel = true; | In.Got->HasGotOffRel = true; | ||||
// Read an addend. | // Read an addend. | ||||
int64_t Addend = computeAddend<ELFT>(Rel, End, Sec, Expr, Sym.isLocal()); | int64_t Addend = computeAddend<ELFT>(Rel, End, Sec, Expr, Sym.isLocal()); | ||||
// Process some TLS relocations, including relaxing TLS relocations. | // Process some TLS relocations, including relaxing TLS relocations. | ||||
// Note that this function does not handle all TLS relocations. | // Note that this function does not handle all TLS relocations. | ||||
if (unsigned Processed = | if (unsigned Processed = | ||||
handleTlsRelocation<ELFT>(Type, Sym, Sec, Offset, Addend, Expr)) { | handleTlsRelocation<ELFT>(Type, Sym, Sec, Offset, Addend, Expr)) { | ||||
I += (Processed - 1); | I += (Processed - 1); | ||||
return; | return; | ||||
} | } | ||||
// We were asked not to generate PLT entries for ifuncs. Instead, pass the | |||||
// direct relocation on through. | |||||
if (Sym.isGnuIFunc() && Config->ZIfuncNoplt) { | |||||
Sym.ExportDynamic = true; | |||||
In.RelaDyn->addReloc(Type, &Sec, Offset, &Sym, Addend, R_ADDEND, Type); | |||||
return; | |||||
} | |||||
// Non-preemptible ifuncs require special handling. First, handle the usual | |||||
// case where the symbol isn't one of these. | |||||
if (!Sym.isGnuIFunc() || Sym.IsPreemptible) { | |||||
// If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol. | // If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol. | ||||
if (needsPlt(Expr) && !Sym.isInPlt()) { | if (needsPlt(Expr) && !Sym.isInPlt()) | ||||
if (Sym.isGnuIFunc() && !Sym.IsPreemptible) | |||||
addPltEntry<ELFT>(In.Iplt, In.IgotPlt, In.RelaIplt, Target->IRelativeRel, | |||||
Sym); | |||||
else | |||||
addPltEntry<ELFT>(In.Plt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym); | addPltEntry<ELFT>(In.Plt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym); | ||||
} | |||||
// Create a GOT slot if a relocation needs GOT. | // Create a GOT slot if a relocation needs GOT. | ||||
if (needsGot(Expr)) { | if (needsGot(Expr)) { | ||||
if (Config->EMachine == EM_MIPS) { | if (Config->EMachine == EM_MIPS) { | ||||
// MIPS ABI has special rules to process GOT entries and doesn't | // MIPS ABI has special rules to process GOT entries and doesn't | ||||
// require relocation entries for them. A special case is TLS | // require relocation entries for them. A special case is TLS | ||||
// relocations. In that case dynamic loader applies dynamic | // relocations. In that case dynamic loader applies dynamic | ||||
// relocations to initialize TLS GOT entries. | // relocations to initialize TLS GOT entries. | ||||
// See "Global Offset Table" in Chapter 5 in the following document | // See "Global Offset Table" in Chapter 5 in the following document | ||||
// for detailed description: | // for detailed description: | ||||
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf | // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf | ||||
In.MipsGot->addEntry(*Sec.File, Sym, Addend, Expr); | In.MipsGot->addEntry(*Sec.File, Sym, Addend, Expr); | ||||
} else if (!Sym.isInGot()) { | } else if (!Sym.isInGot()) { | ||||
addGotEntry<ELFT>(Sym); | addGotEntry<ELFT>(Sym); | ||||
} | } | ||||
} | } | ||||
} else { | |||||
// Handle a reference to a non-preemptible ifunc. These are special in a | |||||
// few ways: | |||||
// | |||||
// - Unlike most non-preemptible symbols, non-preemptible ifuncs do not have | |||||
// a fixed value. But assuming that all references to the ifunc are | |||||
// GOT-generating or PLT-generating, the handling of an ifunc is | |||||
// relatively straightforward. We create a PLT entry in Iplt, which is | |||||
// usually at the end of .plt, which makes an indirect call using a | |||||
// matching GOT entry in IgotPlt, which is usually at the end of .got.plt. | |||||
// The GOT entry is relocated using an IRELATIVE relocation in RelaIplt, | |||||
// which is usually at the end of .rela.plt. Unlike most relocations in | |||||
// .rela.plt, which may be evaluated lazily without -z now, dynamic | |||||
// loaders evaluate IRELATIVE relocs eagerly, which means that for | |||||
// IRELATIVE relocs only, GOT-generating relocations can point directly to | |||||
// .got.plt without requiring a separate GOT entry. | |||||
// | |||||
// - Despite the fact that an ifunc does not have a fixed value, compilers | |||||
// that are not passed -fPIC will assume that they do, and will emit | |||||
// direct (non-GOT-generating, non-PLT-generating) relocations to the | |||||
// symbol. This means that if a direct relocation to the symbol is | |||||
// seen, the linker must set a value for the symbol, and this value must | |||||
// be consistent no matter what type of reference is made to the symbol. | |||||
// This can be done by creating a PLT entry for the symbol in the way | |||||
// described above and making it canonical, that is, making all references | |||||
// point to the PLT entry instead of the resolver. In lld we also store | |||||
// the address of the PLT entry in the dynamic symbol table, which means | |||||
// that the symbol will also have the same value in other modules. | |||||
// Because the value loaded from the GOT needs to be consistent with | |||||
// the value computed using a direct relocation, a non-preemptible ifunc | |||||
// may end up with two GOT entries, one in .got.plt that points to the | |||||
// address returned by the resolver and is used only by the PLT entry, | |||||
// and another in .got that points to the PLT entry and is used by | |||||
// GOT-generating relocations. | |||||
// | |||||
// - The fact that these symbols do not have a fixed value makes them an | |||||
// exception to the general rule that a statically linked executable does | |||||
// not require any form of dynamic relocation. To handle these relocations | |||||
// correctly, the IRELATIVE relocations are stored in an array which a | |||||
// statically linked executable's startup code must enumerate using the | |||||
// linker-defined symbols __rela?_iplt_{start,end}. | |||||
// | |||||
// - An absolute relocation to a non-preemptible ifunc (such as a global | |||||
// variable containing a pointer to the ifunc) needs to be relocated in | |||||
// the exact same way as a GOT entry, so we can avoid needing to make the | |||||
// PLT entry canonical by translating such relocations into IRELATIVE | |||||
// relocations in the RelaIplt. | |||||
if (!Sym.isInPlt()) { | |||||
// Create PLT and GOTPLT slots for the symbol. | |||||
Sym.IsInIplt = true; | |||||
// Create a copy of the symbol to use as the target of the IRELATIVE | |||||
// relocation in the IgotPlt. This is in case we make the PLT canonical | |||||
// later, which would overwrite the original symbol. | |||||
// | |||||
// FIXME: Creating a copy of the symbol here is a bit of a hack. All | |||||
// that's really needed to create the IRELATIVE is the section and value, | |||||
// so ideally we should just need to copy those. | |||||
auto *DirectSym = make<Defined>(cast<Defined>(Sym)); | |||||
addPltEntry<ELFT>(In.Iplt, In.IgotPlt, In.RelaIplt, Target->IRelativeRel, | |||||
*DirectSym); | |||||
Sym.PltIndex = DirectSym->PltIndex; | |||||
} | |||||
if (Expr == R_ABS && Addend == 0 && (Sec.Flags & SHF_WRITE)) { | |||||
// We might be able to represent this as an IRELATIVE. But we don't know | |||||
// yet whether some later relocation will make the symbol point to a | |||||
// canonical PLT, which would make this either a dynamic RELATIVE (PIC) or | |||||
// static (non-PIC) relocation. So we keep a record of the information | |||||
// required to process the relocation, and after scanRelocs() has been | |||||
// called on all relocations, the relocation is resolved by | |||||
// addIRelativeRelocs(). | |||||
IRelativeRelocs.push_back({Type, &Sec, Offset, &Sym}); | |||||
return; | |||||
} | |||||
if (needsGot(Expr)) { | |||||
// Redirect GOT accesses to point to the Igot. | |||||
// | |||||
// This field is also used to keep track of whether we ever needed a GOT | |||||
// entry. If we did and we make the PLT canonical later, we'll need to | |||||
// create a GOT entry pointing to the PLT entry for Sym. | |||||
Sym.GotInIgot = true; | |||||
} else if (!needsPlt(Expr)) { | |||||
// Make the ifunc's PLT entry canonical by changing the value of its | |||||
// symbol to redirect all references to point to it. | |||||
unsigned EntryOffset = Sym.PltIndex * Target->PltEntrySize; | |||||
if (Config->ZRetpolineplt) | |||||
EntryOffset += Target->PltHeaderSize; | |||||
auto &D = cast<Defined>(Sym); | |||||
D.Section = In.Iplt; | |||||
D.Value = EntryOffset; | |||||
D.Size = 0; | |||||
// It's important to set the symbol type here so that dynamic loaders | |||||
// don't try to call the PLT as if it were an ifunc resolver. | |||||
D.Type = STT_FUNC; | |||||
if (Sym.GotInIgot) { | |||||
// We previously encountered a GOT generating reference that we | |||||
// redirected to the Igot. Now that the PLT entry is canonical we must | |||||
// clear the redirection to the Igot and add a GOT entry. As we've | |||||
// changed the symbol type to STT_FUNC future GOT generating references | |||||
// will naturally use this GOT entry. | |||||
// | |||||
// We don't need to worry about creating a MIPS GOT here because ifuncs | |||||
// aren't a thing on MIPS. | |||||
Sym.GotInIgot = false; | |||||
addGotEntry<ELFT>(Sym); | |||||
} | |||||
} | |||||
} | |||||
processRelocAux<ELFT>(Sec, Expr, Type, Offset, Sym, Rel, Addend); | processRelocAux<ELFT>(Sec, Expr, Type, Offset, Sym, Rel, Addend); | ||||
} | } | ||||
template <class ELFT, class RelTy> | template <class ELFT, class RelTy> | ||||
static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) { | static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) { | ||||
OffsetGetter GetOffset(Sec); | OffsetGetter GetOffset(Sec); | ||||
// Not all relocations end up in Sec.Relocations, but a lot do. | // Not all relocations end up in Sec.Relocations, but a lot do. | ||||
Sec.Relocations.reserve(Rels.size()); | Sec.Relocations.reserve(Rels.size()); | ||||
for (auto I = Rels.begin(), End = Rels.end(); I != End;) | for (auto I = Rels.begin(), End = Rels.end(); I != End;) | ||||
scanReloc<ELFT>(Sec, GetOffset, I, End); | scanReloc<ELFT>(Sec, GetOffset, I, End); | ||||
// Sort relocations by offset to binary search for R_RISCV_PCREL_HI20 | // Sort relocations by offset to binary search for R_RISCV_PCREL_HI20 | ||||
if (Config->EMachine == EM_RISCV) | if (Config->EMachine == EM_RISCV) | ||||
std::stable_sort(Sec.Relocations.begin(), Sec.Relocations.end(), | std::stable_sort(Sec.Relocations.begin(), Sec.Relocations.end(), | ||||
RelocationOffsetComparator{}); | RelocationOffsetComparator{}); | ||||
} | } | ||||
template <class ELFT> void elf::scanRelocations(InputSectionBase &S) { | template <class ELFT> void elf::scanRelocations(InputSectionBase &S) { | ||||
if (S.AreRelocsRela) | if (S.AreRelocsRela) | ||||
scanRelocs<ELFT>(S, S.relas<ELFT>()); | scanRelocs<ELFT>(S, S.relas<ELFT>()); | ||||
else | else | ||||
scanRelocs<ELFT>(S, S.rels<ELFT>()); | scanRelocs<ELFT>(S, S.rels<ELFT>()); | ||||
} | |||||
// Figure out which representation to use for any absolute relocs to | |||||
// non-preemptible ifuncs that we visited during scanRelocs(). | |||||
void elf::addIRelativeRelocs() { | |||||
for (IRelativeReloc &R : IRelativeRelocs) { | |||||
if (R.Sym->Type == STT_GNU_IFUNC) | |||||
In.RelaIplt->addReloc( | |||||
{Target->IRelativeRel, R.Sec, R.Offset, true, R.Sym, 0}); | |||||
else if (Config->Pic) | |||||
addRelativeReloc(R.Sec, R.Offset, R.Sym, 0, R_ABS, R.Type); | |||||
else | |||||
R.Sec->Relocations.push_back({R_ABS, R.Type, R.Offset, 0, R.Sym}); | |||||
} | |||||
IRelativeRelocs.clear(); | |||||
} | } | ||||
static bool mergeCmp(const InputSection *A, const InputSection *B) { | static bool mergeCmp(const InputSection *A, const InputSection *B) { | ||||
// std::merge requires a strict weak ordering. | // std::merge requires a strict weak ordering. | ||||
if (A->OutSecOff < B->OutSecOff) | if (A->OutSecOff < B->OutSecOff) | ||||
return true; | return true; | ||||
if (A->OutSecOff == B->OutSecOff) { | if (A->OutSecOff == B->OutSecOff) { | ||||
▲ Show 20 Lines • Show All 407 Lines • Show Last 20 Lines |