forked from OSchip/llvm-project
ELF: Allow GOT relocs pointing to non-preemptable ifunc to resolve to an IRELATIVE where possible.
Non-GOT non-PLT relocations to non-preemptible ifuncs result in the creation of a canonical PLT, which now takes the identity of the IFUNC in the symbol table. This (a) ensures address consistency inside and outside the module, and (b) fixes a bug where some of these relocations end up pointing to the resolver. Fixes (at least) PR40474 and PR40501. Differential Revision: https://reviews.llvm.org/D57371 llvm-svn: 353981
This commit is contained in:
parent
7761790881
commit
8331f61a51
|
|
@ -611,7 +611,6 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
|
|||
case R_ARM_SBREL:
|
||||
return Sym.getVA(A) - getARMStaticBase(Sym);
|
||||
case R_GOT:
|
||||
case R_GOT_PLT:
|
||||
case R_RELAX_TLS_GD_TO_IE_ABS:
|
||||
return Sym.getGotVA() + A;
|
||||
case R_GOTONLY_PC:
|
||||
|
|
@ -630,7 +629,6 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
|
|||
case R_RELAX_TLS_GD_TO_IE_GOT_OFF:
|
||||
return Sym.getGotOffset() + A;
|
||||
case R_AARCH64_GOT_PAGE_PC:
|
||||
case R_AARCH64_GOT_PAGE_PC_PLT:
|
||||
case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC:
|
||||
return getAArch64Page(Sym.getGotVA() + A) - getAArch64Page(P);
|
||||
case R_GOT_PC:
|
||||
|
|
@ -680,10 +678,6 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
|
|||
uint64_t Val = Sym.isUndefWeak() ? P + A : Sym.getVA(A);
|
||||
return getAArch64Page(Val) - getAArch64Page(P);
|
||||
}
|
||||
case R_AARCH64_PLT_PAGE_PC: {
|
||||
uint64_t Val = Sym.isUndefWeak() ? P + A : Sym.getPltVA() + A;
|
||||
return getAArch64Page(Val) - getAArch64Page(P);
|
||||
}
|
||||
case R_RISCV_PC_INDIRECT: {
|
||||
if (const Relocation *HiRel = getRISCVPCRelHi20(&Sym, A))
|
||||
return getRelocTargetVA(File, HiRel->Type, HiRel->Addend, Sym.getVA(),
|
||||
|
|
|
|||
|
|
@ -336,8 +336,7 @@ static bool isAbsoluteValue(const Symbol &Sym) {
|
|||
|
||||
// Returns true if Expr refers a PLT entry.
|
||||
static bool needsPlt(RelExpr Expr) {
|
||||
return isRelExprOneOf<R_PLT_PC, R_PPC_CALL_PLT, R_PLT, R_AARCH64_PLT_PAGE_PC,
|
||||
R_GOT_PLT, R_AARCH64_GOT_PAGE_PC_PLT>(Expr);
|
||||
return isRelExprOneOf<R_PLT_PC, R_PPC_CALL_PLT, R_PLT>(Expr);
|
||||
}
|
||||
|
||||
// Returns true if Expr refers a GOT entry. Note that this function
|
||||
|
|
@ -346,8 +345,7 @@ static bool needsPlt(RelExpr Expr) {
|
|||
static bool needsGot(RelExpr Expr) {
|
||||
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_AARCH64_GOT_PAGE_PC_PLT, R_GOT_PC, R_GOT_FROM_END,
|
||||
R_GOT_PLT>(Expr);
|
||||
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
|
||||
|
|
@ -355,7 +353,7 @@ static bool needsGot(RelExpr Expr) {
|
|||
static bool isRelExpr(RelExpr Expr) {
|
||||
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_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.
|
||||
|
|
@ -373,8 +371,8 @@ static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym,
|
|||
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_OFF32, R_MIPS_GOT_GP_PC, R_MIPS_TLSGD,
|
||||
R_AARCH64_GOT_PAGE_PC, R_AARCH64_GOT_PAGE_PC_PLT, R_GOT_PC,
|
||||
R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_GOT,
|
||||
R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC,
|
||||
R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_GOT,
|
||||
R_TLSGD_GOT_FROM_END, R_TLSGD_PC, R_PPC_CALL_PLT,
|
||||
R_TLSDESC_CALL, R_AARCH64_TLSDESC_PAGE, R_HINT,
|
||||
R_TLSLD_HINT, R_TLSIE_HINT>(E))
|
||||
|
|
@ -382,7 +380,7 @@ static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym,
|
|||
|
||||
// These never do, except if the entire file is position dependent or if
|
||||
// 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;
|
||||
|
||||
if (Sym.IsPreemptible)
|
||||
|
|
@ -428,14 +426,8 @@ static RelExpr toPlt(RelExpr Expr) {
|
|||
return R_PPC_CALL_PLT;
|
||||
case R_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:
|
||||
return R_PLT;
|
||||
case R_GOT:
|
||||
return R_GOT_PLT;
|
||||
default:
|
||||
return Expr;
|
||||
}
|
||||
|
|
@ -767,14 +759,7 @@ static void addPltEntry(PltSection *Plt, GotPltSection *GotPlt,
|
|||
template <class ELFT> static void addGotEntry(Symbol &Sym) {
|
||||
In.Got->addEntry(Sym);
|
||||
|
||||
RelExpr Expr;
|
||||
if (Sym.isTls())
|
||||
Expr = R_TLS;
|
||||
else if (Sym.isGnuIFunc())
|
||||
Expr = R_PLT;
|
||||
else
|
||||
Expr = R_ABS;
|
||||
|
||||
RelExpr Expr = Sym.isTls() ? R_TLS : R_ABS;
|
||||
uint64_t Off = Sym.getGotOffset();
|
||||
|
||||
// If a GOT slot value can be calculated at link-time, which is now,
|
||||
|
|
@ -969,6 +954,15 @@ static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type,
|
|||
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>
|
||||
static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I,
|
||||
RelTy *End) {
|
||||
|
|
@ -1011,32 +1005,29 @@ static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I,
|
|||
if (Config->EMachine == EM_PPC64 && isPPC64SmallCodeModelTocReloc(Type))
|
||||
Sec.File->PPC64SmallCodeModelTocRelocs = true;
|
||||
|
||||
// Strengthen or relax relocations.
|
||||
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 "
|
||||
"old version of glibc (glibc 2.28 and earlier). If this applies to "
|
||||
"you, consider recompiling the object files without -fPIC and "
|
||||
"without -Wl,-z,notext option. Use -no-warn-ifunc-textrel to "
|
||||
"turn off this warning." +
|
||||
getLocation(Sec, Sym, Offset));
|
||||
}
|
||||
|
||||
// Relax relocations.
|
||||
//
|
||||
// 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()) {
|
||||
if (!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 "
|
||||
"old version of glibc (glibc 2.28 and earlier). If this applies to "
|
||||
"you, consider recompiling the object files without -fPIC and "
|
||||
"without -Wl,-z,notext option. Use -no-warn-ifunc-textrel to "
|
||||
"turn off this warning." +
|
||||
getLocation(Sec, Sym, Offset));
|
||||
}
|
||||
Expr = toPlt(Expr);
|
||||
} else if (!Sym.IsPreemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym)) {
|
||||
Expr = Target->adjustRelaxExpr(Type, RelocatedAddr, Expr);
|
||||
} else if (!Sym.IsPreemptible) {
|
||||
Expr = fromPlt(Expr);
|
||||
// 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()) {
|
||||
if (Expr == R_GOT_PC && !isAbsoluteValue(Sym))
|
||||
Expr = Target->adjustRelaxExpr(Type, RelocatedAddr, Expr);
|
||||
else
|
||||
Expr = fromPlt(Expr);
|
||||
}
|
||||
|
||||
// This relocation does not require got entry, but it is relative to got and
|
||||
|
|
@ -1056,28 +1047,136 @@ static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I,
|
|||
return;
|
||||
}
|
||||
|
||||
// If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol.
|
||||
if (needsPlt(Expr) && !Sym.isInPlt()) {
|
||||
if (Sym.isGnuIFunc() && !Sym.IsPreemptible)
|
||||
addPltEntry<ELFT>(In.Iplt, In.IgotPlt, In.RelaIplt, Target->IRelativeRel,
|
||||
Sym);
|
||||
else
|
||||
// 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 (needsPlt(Expr) && !Sym.isInPlt())
|
||||
addPltEntry<ELFT>(In.Plt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym);
|
||||
}
|
||||
|
||||
// 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.MipsGot->addEntry(*Sec.File, Sym, Addend, Expr);
|
||||
} else if (!Sym.isInGot()) {
|
||||
addGotEntry<ELFT>(Sym);
|
||||
// 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.MipsGot->addEntry(*Sec.File, Sym, Addend, Expr);
|
||||
} else if (!Sym.isInGot()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1107,6 +1206,21 @@ template <class ELFT> void elf::scanRelocations(InputSectionBase &S) {
|
|||
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) {
|
||||
// std::merge requires a strict weak ordering.
|
||||
if (A->OutSecOff < B->OutSecOff)
|
||||
|
|
|
|||
|
|
@ -33,19 +33,11 @@ enum RelExpr {
|
|||
R_ABS,
|
||||
R_ADDEND,
|
||||
R_AARCH64_GOT_PAGE_PC,
|
||||
// The expression is used for IFUNC support. Describes PC-relative
|
||||
// address of the memory page of GOT entry. This entry is used for
|
||||
// a redirection to IPLT.
|
||||
R_AARCH64_GOT_PAGE_PC_PLT,
|
||||
R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC,
|
||||
R_AARCH64_PAGE_PC,
|
||||
R_AARCH64_PLT_PAGE_PC,
|
||||
R_AARCH64_TLSDESC_PAGE,
|
||||
R_ARM_SBREL,
|
||||
R_GOT,
|
||||
// The expression is used for IFUNC support. Evaluates to GOT entry,
|
||||
// containing redirection to the IPLT.
|
||||
R_GOT_PLT,
|
||||
R_GOTONLY_PC,
|
||||
R_GOTONLY_PC_FROM_END,
|
||||
R_GOTREL,
|
||||
|
|
@ -154,6 +146,8 @@ struct RelocationOffsetComparator {
|
|||
|
||||
template <class ELFT> void scanRelocations(InputSectionBase &);
|
||||
|
||||
void addIRelativeRelocs();
|
||||
|
||||
class ThunkSection;
|
||||
class Thunk;
|
||||
struct InputSectionDescription;
|
||||
|
|
|
|||
|
|
@ -120,20 +120,24 @@ uint64_t Symbol::getVA(int64_t Addend) const {
|
|||
return OutVA + Addend;
|
||||
}
|
||||
|
||||
uint64_t Symbol::getGotVA() const { return In.Got->getVA() + getGotOffset(); }
|
||||
uint64_t Symbol::getGotVA() const {
|
||||
if (GotInIgot)
|
||||
return In.IgotPlt->getVA() + getGotPltOffset();
|
||||
return In.Got->getVA() + getGotOffset();
|
||||
}
|
||||
|
||||
uint64_t Symbol::getGotOffset() const {
|
||||
return GotIndex * Target->GotEntrySize;
|
||||
}
|
||||
|
||||
uint64_t Symbol::getGotPltVA() const {
|
||||
if (this->IsInIgot)
|
||||
if (IsInIplt)
|
||||
return In.IgotPlt->getVA() + getGotPltOffset();
|
||||
return In.GotPlt->getVA() + getGotPltOffset();
|
||||
}
|
||||
|
||||
uint64_t Symbol::getGotPltOffset() const {
|
||||
if (IsInIgot)
|
||||
if (IsInIplt)
|
||||
return PltIndex * Target->GotPltEntrySize;
|
||||
return (PltIndex + Target->GotPltHeaderEntriesNum) * Target->GotPltEntrySize;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ protected:
|
|||
uint8_t StOther, uint8_t Type)
|
||||
: File(File), NameData(Name.Data), NameSize(Name.Size), Binding(Binding),
|
||||
Type(Type), StOther(StOther), SymbolKind(K), NeedsPltAddr(false),
|
||||
IsInIplt(false), IsInIgot(false), IsPreemptible(false),
|
||||
IsInIplt(false), GotInIgot(false), IsPreemptible(false),
|
||||
Used(!Config->GcSections), NeedsTocRestore(false),
|
||||
ScriptDefined(false) {}
|
||||
|
||||
|
|
@ -190,11 +190,13 @@ public:
|
|||
// For SharedSymbol only.
|
||||
unsigned NeedsPltAddr : 1;
|
||||
|
||||
// True if this symbol is in the Iplt sub-section of the Plt.
|
||||
// True if this symbol is in the Iplt sub-section of the Plt and the Igot
|
||||
// sub-section of the .got.plt or .got.
|
||||
unsigned IsInIplt : 1;
|
||||
|
||||
// True if this symbol is in the Igot sub-section of the .got.plt or .got.
|
||||
unsigned IsInIgot : 1;
|
||||
// True if this symbol needs a GOT entry and its GOT entry is actually in
|
||||
// Igot. This will be true only for certain non-preemptible ifuncs.
|
||||
unsigned GotInIgot : 1;
|
||||
|
||||
// True if this symbol is preemptible at load time.
|
||||
unsigned IsPreemptible : 1;
|
||||
|
|
|
|||
|
|
@ -1142,7 +1142,6 @@ IgotPltSection::IgotPltSection()
|
|||
Target->GotPltEntrySize, getIgotPltName()) {}
|
||||
|
||||
void IgotPltSection::addEntry(Symbol &Sym) {
|
||||
Sym.IsInIgot = true;
|
||||
assert(Sym.PltIndex == Entries.size());
|
||||
Entries.push_back(&Sym);
|
||||
}
|
||||
|
|
@ -2344,10 +2343,8 @@ void PltSection::writeTo(uint8_t *Buf) {
|
|||
template <class ELFT> void PltSection::addEntry(Symbol &Sym) {
|
||||
Sym.PltIndex = Entries.size();
|
||||
RelocationBaseSection *PltRelocSection = In.RelaPlt;
|
||||
if (IsIplt) {
|
||||
if (IsIplt)
|
||||
PltRelocSection = In.RelaIplt;
|
||||
Sym.IsInIplt = true;
|
||||
}
|
||||
unsigned RelOff =
|
||||
static_cast<RelocationSection<ELFT> *>(PltRelocSection)->getRelocOffset();
|
||||
Entries.push_back(std::make_pair(&Sym, RelOff));
|
||||
|
|
|
|||
|
|
@ -1677,6 +1677,8 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
|
|||
if (!Config->Relocatable)
|
||||
forEachRelSec(scanRelocations<ELFT>);
|
||||
|
||||
addIRelativeRelocs();
|
||||
|
||||
if (In.Plt && !In.Plt->empty())
|
||||
In.Plt->addSymbols();
|
||||
if (In.Iplt && !In.Iplt->empty())
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
.rodata
|
||||
.8byte ifunc
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
.rodata
|
||||
.4byte ifunc - .
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
.data
|
||||
.8byte ifunc + 1
|
||||
|
|
@ -12,6 +12,9 @@
|
|||
.globl myfunc
|
||||
.type myfunc,@gnu_indirect_function
|
||||
myfunc:
|
||||
.globl myfunc_resolver
|
||||
.type myfunc_resolver,@function
|
||||
myfunc_resolver:
|
||||
ret
|
||||
|
||||
.text
|
||||
|
|
@ -22,7 +25,7 @@ main:
|
|||
add x8, x8, :lo12: myfunc
|
||||
ret
|
||||
|
||||
# CHECK: 0000000000010000 myfunc:
|
||||
# CHECK: 0000000000010000 myfunc_resolver:
|
||||
# CHECK-NEXT: 10000: c0 03 5f d6 ret
|
||||
# CHECK: 0000000000010004 main:
|
||||
# CHECK-NEXT: 10004: 08 00 00 90 adrp x8, #0
|
||||
|
|
@ -31,7 +34,7 @@ main:
|
|||
# x8 = 0x10010 = .plt for myfunc
|
||||
# CHECK-NEXT: 1000c: c0 03 5f d6 ret
|
||||
# CHECK-NEXT: Disassembly of section .plt:
|
||||
# CHECK-NEXT: 0000000000010010 .plt:
|
||||
# CHECK-NEXT: 0000000000010010 myfunc:
|
||||
# CHECK-NEXT: 10010: 90 00 00 90 adrp x16, #65536
|
||||
# CHECK-NEXT: 10014: 11 02 40 f9 ldr x17, [x16]
|
||||
# CHECK-NEXT: 10018: 10 02 00 91 add x16, x16, #0
|
||||
|
|
|
|||
|
|
@ -21,20 +21,17 @@ main:
|
|||
ldr x8, [x8, :got_lo12:myfunc]
|
||||
ret
|
||||
# CHECK: 0000000000010004 main:
|
||||
# x8 = 0x30000
|
||||
# CHECK-NEXT: 10004: 08 01 00 90 adrp x8, #131072
|
||||
# x8 = 0x300e0 = .got entry for myfunc with R_AARCH64_GLOB_DAT
|
||||
# CHECK-NEXT: 10008: 08 71 40 f9 ldr x8, [x8, #224]
|
||||
# x8 = 0x20000
|
||||
# CHECK-NEXT: 10004: 88 00 00 90 adrp x8, #65536
|
||||
# x8 = 0x200a0 = .got entry for myfunc with R_AARCH64_GLOB_DAT
|
||||
# CHECK-NEXT: 10008: 08 51 40 f9 ldr x8, [x8, #160]
|
||||
# CHECK-NEXT: 1000c: c0 03 5f d6 ret
|
||||
|
||||
# CHECK: Disassembly of section .got:
|
||||
# CHECK-NEXT: 00000000000300e0 .got:
|
||||
# CHECK-NEXT: 00000000000200a0 .got:
|
||||
|
||||
# CHECK-RELOCS: Relocations [
|
||||
# CHECK-RELOCS-NEXT: Section {{.*}} .rela.dyn {
|
||||
# CHECK-RELOCS-NEXT: 0x300E0 R_AARCH64_GLOB_DAT myfunc 0x0
|
||||
# CHECK-RELOCS-NEXT: }
|
||||
# CHECK-RELOCS-NEXT: Section {{.*}} .rela.plt {
|
||||
# CHECK-RELOCS-NEXT: 0x20018 R_AARCH64_JUMP_SLOT myfunc 0x0
|
||||
# CHECK-RELOCS-NEXT: 0x200A0 R_AARCH64_GLOB_DAT myfunc 0x0
|
||||
# CHECK-RELOCS-NEXT: }
|
||||
# CHECK-RELOCS-NEXT: ]
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
# CHECK-NEXT: 210000:
|
||||
|
||||
# CHECK: main:
|
||||
# adrp x8, 0x230000, 0x230000 == address in .got
|
||||
# CHECK-NEXT: 210004: {{.*}} adrp x8, #131072
|
||||
# adrp x8, 0x220000, 0x220000 == address in .got.plt
|
||||
# CHECK-NEXT: 210004: {{.*}} adrp x8, #65536
|
||||
# CHECK-NEXT: 210008: {{.*}} ldr x8, [x8]
|
||||
# CHECK-NEXT: 21000c: {{.*}} ret
|
||||
|
||||
|
|
@ -26,11 +26,6 @@
|
|||
# CHECK-NEXT: .got.plt:
|
||||
# CHECK-NEXT: 220000:
|
||||
|
||||
# CHECK: Disassembly of section .got:
|
||||
# CHECK-NEXT: .got:
|
||||
# 0x210010 == address in .plt
|
||||
# CHECK-NEXT: 230000: 10 00 21 00
|
||||
|
||||
# RELOC: Relocations [
|
||||
# RELOC-NEXT: Section {{.*}} .rela.plt {
|
||||
# RELOC-NEXT: 0x220000 R_AARCH64_IRELATIVE - 0x210000
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@
|
|||
.globl myfunc
|
||||
.type myfunc,@gnu_indirect_function
|
||||
myfunc:
|
||||
.globl myfunc_resolver
|
||||
.type myfunc_resolver,@function
|
||||
myfunc_resolver:
|
||||
ret
|
||||
|
||||
.text
|
||||
|
|
@ -22,7 +25,7 @@ _start:
|
|||
ret
|
||||
|
||||
# CHECK: Disassembly of section .text:
|
||||
# CHECK-NEXT: myfunc:
|
||||
# CHECK-NEXT: myfunc_resolver:
|
||||
# CHECK-NEXT: 210000: c0 03 5f d6 ret
|
||||
# CHECK: _start:
|
||||
# adrp x8, 0x210000 + 0x10 from add == .plt entry
|
||||
|
|
@ -30,7 +33,7 @@ _start:
|
|||
# CHECK-NEXT: 210008: 08 41 00 91 add x8, x8, #16
|
||||
# CHECK-NEXT: 21000c: c0 03 5f d6 ret
|
||||
# CHECK-NEXT: Disassembly of section .plt:
|
||||
# CHECK-NEXT: .plt:
|
||||
# CHECK-NEXT: myfunc:
|
||||
# adrp x16, 0x220000, 0x220000 == address in .got.plt
|
||||
# CHECK-NEXT: 210010: 90 00 00 90 adrp x16, #65536
|
||||
# CHECK-NEXT: 210014: 11 02 40 f9 ldr x17, [x16]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
// REQUIRES: x86
|
||||
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
|
||||
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/gnu-ifunc-canon-ro-pcrel.s -o %t-ro-pcrel.o
|
||||
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/gnu-ifunc-canon-ro-abs.s -o %t-ro-abs.o
|
||||
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/gnu-ifunc-canon-rw-addend.s -o %t-rw-addend.o
|
||||
// RUN: ld.lld %t.o -o %t1
|
||||
// RUN: llvm-readobj -relocs %t1 | FileCheck --check-prefix=IREL2 %s
|
||||
// RUN: ld.lld %t.o %t-ro-pcrel.o -o %t2
|
||||
// RUN: llvm-readobj -relocs %t2 | FileCheck --check-prefix=IREL1 %s
|
||||
// RUN: ld.lld %t.o %t-ro-abs.o -o %t3
|
||||
// RUN: llvm-readobj -relocs %t3 | FileCheck --check-prefix=IREL1 %s
|
||||
// RUN: ld.lld %t.o %t-rw-addend.o -o %t4
|
||||
// RUN: llvm-readobj -relocs %t4 | FileCheck --check-prefix=IREL1 %s
|
||||
// RUN: llvm-objdump -s %t4 | FileCheck --check-prefix=DUMP %s
|
||||
// RUN: ld.lld %t.o %t-rw-addend.o -o %t4a -z retpolineplt
|
||||
// RUN: llvm-readobj -relocs %t4a | FileCheck --check-prefix=IREL1 %s
|
||||
// RUN: llvm-objdump -s %t4a | FileCheck --check-prefix=DUMP2 %s
|
||||
// RUN: ld.lld %t-ro-pcrel.o %t.o -o %t5
|
||||
// RUN: llvm-readobj -relocs %t5 | FileCheck --check-prefix=IREL1 %s
|
||||
// RUN: ld.lld %t-ro-abs.o %t.o -o %t6
|
||||
// RUN: llvm-readobj -relocs %t6 | FileCheck --check-prefix=IREL1 %s
|
||||
// RUN: ld.lld %t-rw-addend.o %t.o -o %t7
|
||||
// RUN: llvm-readobj -relocs %t7 | FileCheck --check-prefix=IREL1 %s
|
||||
// RUN: ld.lld %t.o -o %t8 -pie
|
||||
// RUN: llvm-readobj -relocs %t8 | FileCheck --check-prefix=IREL2 %s
|
||||
// RUN: ld.lld %t.o %t-ro-pcrel.o -o %t9 -pie
|
||||
// RUN: llvm-readobj -relocs %t9 | FileCheck --check-prefix=IREL1-REL2 %s
|
||||
// RUN: ld.lld %t.o %t-rw-addend.o -o %t10 -pie
|
||||
// RUN: llvm-readobj -relocs %t10 | FileCheck --check-prefix=IREL1-REL3 %s
|
||||
// RUN: ld.lld %t-ro-pcrel.o %t.o -o %t11 -pie
|
||||
// RUN: llvm-readobj -relocs %t11 | FileCheck --check-prefix=IREL1-REL2 %s
|
||||
// RUN: ld.lld %t-rw-addend.o %t.o -o %t12 -pie
|
||||
// RUN: llvm-readobj -relocs %t12 | FileCheck --check-prefix=IREL1-REL3 %s
|
||||
|
||||
// Two relocs, one for the GOT and the other for .data.
|
||||
// IREL2-NOT: R_X86_64_
|
||||
// IREL2: .rela.plt
|
||||
// IREL2-NEXT: R_X86_64_IRELATIVE
|
||||
// IREL2-NEXT: R_X86_64_IRELATIVE
|
||||
// IREL2-NOT: R_X86_64_
|
||||
|
||||
// One reloc for the canonical PLT.
|
||||
// IREL1-NOT: R_X86_64_
|
||||
// IREL1: .rela.plt
|
||||
// IREL1-NEXT: R_X86_64_IRELATIVE
|
||||
// IREL1-NOT: R_X86_64_
|
||||
|
||||
// One reloc for the canonical PLT and two RELATIVE relocations pointing to it,
|
||||
// one in the GOT and one in .data.
|
||||
// IREL1-REL2-NOT: R_X86_64_
|
||||
// IREL1-REL2: .rela.dyn
|
||||
// IREL1-REL2-NEXT: R_X86_64_RELATIVE
|
||||
// IREL1-REL2-NEXT: R_X86_64_RELATIVE
|
||||
// IREL1-REL2: .rela.plt
|
||||
// IREL1-REL2-NEXT: R_X86_64_IRELATIVE
|
||||
// IREL1-REL2-NOT: R_X86_64_
|
||||
|
||||
// One reloc for the canonical PLT and three RELATIVE relocations pointing to it,
|
||||
// one in the GOT and two in .data.
|
||||
// IREL1-REL3-NOT: R_X86_64_
|
||||
// IREL1-REL3: .rela.dyn
|
||||
// IREL1-REL3-NEXT: R_X86_64_RELATIVE
|
||||
// IREL1-REL3-NEXT: R_X86_64_RELATIVE
|
||||
// IREL1-REL3-NEXT: R_X86_64_RELATIVE
|
||||
// IREL1-REL3: .rela.plt
|
||||
// IREL1-REL3-NEXT: R_X86_64_IRELATIVE
|
||||
// IREL1-REL3-NOT: R_X86_64_
|
||||
|
||||
// Make sure the static relocations look right, both with and without headers.
|
||||
// DUMP: Contents of section .plt:
|
||||
// DUMP-NEXT: 201010
|
||||
// DUMP: Contents of section .data:
|
||||
// DUMP-NEXT: 202000 10102000 00000000 11102000 00000000
|
||||
// DUMP: Contents of section .got:
|
||||
// DUMP-NEXT: 203000 10102000 00000000
|
||||
|
||||
// DUMP2: Contents of section .plt:
|
||||
// DUMP2-NEXT: 201010
|
||||
// DUMP2: Contents of section .data:
|
||||
// DUMP2-NEXT: 202000 40102000 00000000 41102000 00000000
|
||||
// DUMP2: Contents of section .got:
|
||||
// DUMP2-NEXT: 203000 40102000 00000000
|
||||
|
||||
lea ifunc@gotpcrel(%rip), %rbx
|
||||
|
||||
.type ifunc STT_GNU_IFUNC
|
||||
.globl ifunc
|
||||
ifunc:
|
||||
ret
|
||||
|
||||
.data
|
||||
.8byte ifunc
|
||||
|
|
@ -70,28 +70,46 @@
|
|||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: Symbol {
|
||||
// CHECK-NEXT: Name: bar
|
||||
// CHECK-NEXT: Value: 0x401030
|
||||
// CHECK-NEXT: Size: 0
|
||||
// CHECK-NEXT: Binding: Global
|
||||
// CHECK-NEXT: Type: Function
|
||||
// CHECK-NEXT: Other: 0
|
||||
// CHECK-NEXT: Section: .plt
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: Symbol {
|
||||
// CHECK-NEXT: Name: bar_resolver
|
||||
// CHECK-NEXT: Value: 0x401001
|
||||
// CHECK-NEXT: Size: 0
|
||||
// CHECK-NEXT: Binding: Global
|
||||
// CHECK-NEXT: Type: GNU_IFunc
|
||||
// CHECK-NEXT: Type: Function
|
||||
// CHECK-NEXT: Other: 0
|
||||
// CHECK-NEXT: Section: .text
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: Symbol {
|
||||
// CHECK-NEXT: Name: foo
|
||||
// CHECK-NEXT: Value: 0x401020
|
||||
// CHECK-NEXT: Size: 0
|
||||
// CHECK-NEXT: Binding: Global
|
||||
// CHECK-NEXT: Type: Function
|
||||
// CHECK-NEXT: Other: 0
|
||||
// CHECK-NEXT: Section: .plt
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: Symbol {
|
||||
// CHECK-NEXT: Name: foo_resolver
|
||||
// CHECK-NEXT: Value: 0x401000
|
||||
// CHECK-NEXT: Size: 0
|
||||
// CHECK-NEXT: Binding: Global
|
||||
// CHECK-NEXT: Type: GNU_IFunc
|
||||
// CHECK-NEXT: Type: Function
|
||||
// CHECK-NEXT: Other: 0
|
||||
// CHECK-NEXT: Section: .text
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT:]
|
||||
|
||||
// DISASM: Disassembly of section .text:
|
||||
// DISASM-NEXT: foo:
|
||||
// DISASM-NEXT: foo_resolver:
|
||||
// DISASM-NEXT: 401000: c3 retl
|
||||
// DISASM: bar:
|
||||
// DISASM: bar_resolver:
|
||||
// DISASM-NEXT: 401001: c3 retl
|
||||
// DISASM: _start:
|
||||
// DISASM-NEXT: 401002: e8 19 00 00 00 calll 25
|
||||
|
|
@ -99,10 +117,11 @@
|
|||
// DISASM-NEXT: 40100c: ba d4 00 40 00 movl $4194516, %edx
|
||||
// DISASM-NEXT: 401011: ba e4 00 40 00 movl $4194532, %edx
|
||||
// DISASM-NEXT: Disassembly of section .plt:
|
||||
// DISASM-NEXT: .plt:
|
||||
// DISASM-NEXT: foo:
|
||||
// DISASM-NEXT: 401020: ff 25 00 20 40 00 jmpl *4202496
|
||||
// DISASM-NEXT: 401026: 68 10 00 00 00 pushl $16
|
||||
// DISASM-NEXT: 40102b: e9 e0 ff ff ff jmp -32 <_start+0xe>
|
||||
// DISASM: bar:
|
||||
// DISASM-NEXT: 401030: ff 25 04 20 40 00 jmpl *4202500
|
||||
// DISASM-NEXT: 401036: 68 18 00 00 00 pushl $24
|
||||
// DISASM-NEXT: 40103b: e9 d0 ff ff ff jmp -48 <_start+0xe>
|
||||
|
|
@ -111,11 +130,17 @@
|
|||
.type foo STT_GNU_IFUNC
|
||||
.globl foo
|
||||
foo:
|
||||
.type foo_resolver STT_FUNC
|
||||
.globl foo_resolver
|
||||
foo_resolver:
|
||||
ret
|
||||
|
||||
.type bar STT_GNU_IFUNC
|
||||
.globl bar
|
||||
bar:
|
||||
.type bar_resolver STT_FUNC
|
||||
.globl bar_resolver
|
||||
bar_resolver:
|
||||
ret
|
||||
|
||||
.globl _start
|
||||
|
|
|
|||
Loading…
Reference in New Issue