Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -611,7 +611,6 @@ 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 @@ 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 @@ 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(), Index: ELF/Relocations.h =================================================================== --- ELF/Relocations.h +++ ELF/Relocations.h @@ -33,19 +33,11 @@ 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 @@ template void scanRelocations(InputSectionBase &); +void addIRelativeRelocs(); + class ThunkSection; class Thunk; struct InputSectionDescription; Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -336,8 +336,7 @@ // Returns true if Expr refers a PLT entry. static bool needsPlt(RelExpr Expr) { - return isRelExprOneOf(Expr); + return isRelExprOneOf(Expr); } // Returns true if Expr refers a GOT entry. Note that this function @@ -346,8 +345,7 @@ static bool needsGot(RelExpr Expr) { return isRelExprOneOf(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 isRelExpr(RelExpr Expr) { return isRelExprOneOf(Expr); + R_RELAX_GOT_PC>(Expr); } // Returns true if a given relocation can be computed at link-time. @@ -373,8 +371,8 @@ if (isRelExprOneOf(E)) @@ -382,7 +380,7 @@ // 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 @@ 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 @@ template 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 @@ getLocation(Sec, Sym, Offset)); } +struct IRelativeReloc { + RelType Type; + InputSectionBase *Sec; + uint64_t Offset; + Symbol *Sym; +}; + +static std::vector IRelativeRelocs; + template static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I, RelTy *End) { @@ -1011,32 +1005,29 @@ if (Config->EMachine == EM_PPC64 && isPPC64SmallCodeModelTocReloc(Type)) Sec.File->PPC64SmallCodeModelTocRelocs = true; - // Strengthen or relax relocations. - // - // GNU ifunc symbols must be accessed via PLT because their addresses - // are determined by runtime. + 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. // - // 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 @@ 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(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(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(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(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(cast(Sym)); + addPltEntry(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(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(Sym); + } } } @@ -1107,6 +1206,21 @@ scanRelocs(S, S.rels()); } +// 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) Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -181,7 +181,7 @@ 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 @@ // 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; Index: ELF/Symbols.cpp =================================================================== --- ELF/Symbols.cpp +++ ELF/Symbols.cpp @@ -120,20 +120,24 @@ 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; } Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -1142,7 +1142,6 @@ Target->GotPltEntrySize, getIgotPltName()) {} void IgotPltSection::addEntry(Symbol &Sym) { - Sym.IsInIgot = true; assert(Sym.PltIndex == Entries.size()); Entries.push_back(&Sym); } @@ -2344,10 +2343,8 @@ template 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 *>(PltRelocSection)->getRelocOffset(); Entries.push_back(std::make_pair(&Sym, RelOff)); Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -1677,6 +1677,8 @@ if (!Config->Relocatable) forEachRelSec(scanRelocations); + addIRelativeRelocs(); + if (In.Plt && !In.Plt->empty()) In.Plt->addSymbols(); if (In.Iplt && !In.Iplt->empty()) Index: test/ELF/Inputs/gnu-ifunc-canon-ro-abs.s =================================================================== --- test/ELF/Inputs/gnu-ifunc-canon-ro-abs.s +++ test/ELF/Inputs/gnu-ifunc-canon-ro-abs.s @@ -0,0 +1,2 @@ +.rodata +.8byte ifunc Index: test/ELF/Inputs/gnu-ifunc-canon-ro-pcrel.s =================================================================== --- test/ELF/Inputs/gnu-ifunc-canon-ro-pcrel.s +++ test/ELF/Inputs/gnu-ifunc-canon-ro-pcrel.s @@ -0,0 +1,2 @@ +.rodata +.4byte ifunc - . Index: test/ELF/Inputs/gnu-ifunc-canon-rw-addend.s =================================================================== --- test/ELF/Inputs/gnu-ifunc-canon-rw-addend.s +++ test/ELF/Inputs/gnu-ifunc-canon-rw-addend.s @@ -0,0 +1,2 @@ +.data +.8byte ifunc + 1 Index: test/ELF/aarch64-gnu-ifunc-address-pie.s =================================================================== --- test/ELF/aarch64-gnu-ifunc-address-pie.s +++ test/ELF/aarch64-gnu-ifunc-address-pie.s @@ -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 @@ 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 @@ # 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 Index: test/ELF/aarch64-gnu-ifunc-address.s =================================================================== --- test/ELF/aarch64-gnu-ifunc-address.s +++ test/ELF/aarch64-gnu-ifunc-address.s @@ -21,20 +21,17 @@ 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: ] Index: test/ELF/aarch64-gnu-ifunc2.s =================================================================== --- test/ELF/aarch64-gnu-ifunc2.s +++ test/ELF/aarch64-gnu-ifunc2.s @@ -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 Index: test/ELF/aarch64-gnu-ifunc3.s =================================================================== --- test/ELF/aarch64-gnu-ifunc3.s +++ test/ELF/aarch64-gnu-ifunc3.s @@ -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 @@ 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 @@ # 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] Index: test/ELF/gnu-ifunc-canon.s =================================================================== --- test/ELF/gnu-ifunc-canon.s +++ test/ELF/gnu-ifunc-canon.s @@ -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 Index: test/ELF/gnu-ifunc-i386.s =================================================================== --- test/ELF/gnu-ifunc-i386.s +++ test/ELF/gnu-ifunc-i386.s @@ -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