Skip to content

Commit 8331f61

Browse files
committedFeb 13, 2019
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
1 parent 7761790 commit 8331f61

16 files changed

+345
-117
lines changed
 

‎lld/ELF/InputSection.cpp

-6
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,6 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
611611
case R_ARM_SBREL:
612612
return Sym.getVA(A) - getARMStaticBase(Sym);
613613
case R_GOT:
614-
case R_GOT_PLT:
615614
case R_RELAX_TLS_GD_TO_IE_ABS:
616615
return Sym.getGotVA() + A;
617616
case R_GOTONLY_PC:
@@ -630,7 +629,6 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
630629
case R_RELAX_TLS_GD_TO_IE_GOT_OFF:
631630
return Sym.getGotOffset() + A;
632631
case R_AARCH64_GOT_PAGE_PC:
633-
case R_AARCH64_GOT_PAGE_PC_PLT:
634632
case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC:
635633
return getAArch64Page(Sym.getGotVA() + A) - getAArch64Page(P);
636634
case R_GOT_PC:
@@ -680,10 +678,6 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
680678
uint64_t Val = Sym.isUndefWeak() ? P + A : Sym.getVA(A);
681679
return getAArch64Page(Val) - getAArch64Page(P);
682680
}
683-
case R_AARCH64_PLT_PAGE_PC: {
684-
uint64_t Val = Sym.isUndefWeak() ? P + A : Sym.getPltVA() + A;
685-
return getAArch64Page(Val) - getAArch64Page(P);
686-
}
687681
case R_RISCV_PC_INDIRECT: {
688682
if (const Relocation *HiRel = getRISCVPCRelHi20(&Sym, A))
689683
return getRelocTargetVA(File, HiRel->Type, HiRel->Addend, Sym.getVA(),

‎lld/ELF/Relocations.cpp

+181-67
Original file line numberDiff line numberDiff line change
@@ -336,8 +336,7 @@ static bool isAbsoluteValue(const Symbol &Sym) {
336336

337337
// Returns true if Expr refers a PLT entry.
338338
static bool needsPlt(RelExpr Expr) {
339-
return isRelExprOneOf<R_PLT_PC, R_PPC_CALL_PLT, R_PLT, R_AARCH64_PLT_PAGE_PC,
340-
R_GOT_PLT, R_AARCH64_GOT_PAGE_PC_PLT>(Expr);
339+
return isRelExprOneOf<R_PLT_PC, R_PPC_CALL_PLT, R_PLT>(Expr);
341340
}
342341

343342
// Returns true if Expr refers a GOT entry. Note that this function
@@ -346,16 +345,15 @@ static bool needsPlt(RelExpr Expr) {
346345
static bool needsGot(RelExpr Expr) {
347346
return isRelExprOneOf<R_GOT, R_GOT_OFF, R_HEXAGON_GOT, R_MIPS_GOT_LOCAL_PAGE,
348347
R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_AARCH64_GOT_PAGE_PC,
349-
R_AARCH64_GOT_PAGE_PC_PLT, R_GOT_PC, R_GOT_FROM_END,
350-
R_GOT_PLT>(Expr);
348+
R_GOT_PC, R_GOT_FROM_END>(Expr);
351349
}
352350

353351
// True if this expression is of the form Sym - X, where X is a position in the
354352
// file (PC, or GOT for example).
355353
static bool isRelExpr(RelExpr Expr) {
356354
return isRelExprOneOf<R_PC, R_GOTREL, R_GOTREL_FROM_END, R_MIPS_GOTREL,
357355
R_PPC_CALL, R_PPC_CALL_PLT, R_AARCH64_PAGE_PC,
358-
R_AARCH64_PLT_PAGE_PC, R_RELAX_GOT_PC>(Expr);
356+
R_RELAX_GOT_PC>(Expr);
359357
}
360358

361359
// Returns true if a given relocation can be computed at link-time.
@@ -373,16 +371,16 @@ static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym,
373371
if (isRelExprOneOf<R_GOT_FROM_END, R_GOT_OFF, R_HEXAGON_GOT, R_TLSLD_GOT_OFF,
374372
R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOTREL, R_MIPS_GOT_OFF,
375373
R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, R_MIPS_TLSGD,
376-
R_AARCH64_GOT_PAGE_PC, R_AARCH64_GOT_PAGE_PC_PLT, R_GOT_PC,
377-
R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_GOT,
374+
R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC,
375+
R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_GOT,
378376
R_TLSGD_GOT_FROM_END, R_TLSGD_PC, R_PPC_CALL_PLT,
379377
R_TLSDESC_CALL, R_AARCH64_TLSDESC_PAGE, R_HINT,
380378
R_TLSLD_HINT, R_TLSIE_HINT>(E))
381379
return true;
382380

383381
// These never do, except if the entire file is position dependent or if
384382
// only the low bits are used.
385-
if (E == R_GOT || E == R_GOT_PLT || E == R_PLT || E == R_TLSDESC)
383+
if (E == R_GOT || E == R_PLT || E == R_TLSDESC)
386384
return Target->usesOnlyLowPageBits(Type) || !Config->Pic;
387385

388386
if (Sym.IsPreemptible)
@@ -428,14 +426,8 @@ static RelExpr toPlt(RelExpr Expr) {
428426
return R_PPC_CALL_PLT;
429427
case R_PC:
430428
return R_PLT_PC;
431-
case R_AARCH64_PAGE_PC:
432-
return R_AARCH64_PLT_PAGE_PC;
433-
case R_AARCH64_GOT_PAGE_PC:
434-
return R_AARCH64_GOT_PAGE_PC_PLT;
435429
case R_ABS:
436430
return R_PLT;
437-
case R_GOT:
438-
return R_GOT_PLT;
439431
default:
440432
return Expr;
441433
}
@@ -767,14 +759,7 @@ static void addPltEntry(PltSection *Plt, GotPltSection *GotPlt,
767759
template <class ELFT> static void addGotEntry(Symbol &Sym) {
768760
In.Got->addEntry(Sym);
769761

770-
RelExpr Expr;
771-
if (Sym.isTls())
772-
Expr = R_TLS;
773-
else if (Sym.isGnuIFunc())
774-
Expr = R_PLT;
775-
else
776-
Expr = R_ABS;
777-
762+
RelExpr Expr = Sym.isTls() ? R_TLS : R_ABS;
778763
uint64_t Off = Sym.getGotOffset();
779764

780765
// 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,
969954
getLocation(Sec, Sym, Offset));
970955
}
971956

957+
struct IRelativeReloc {
958+
RelType Type;
959+
InputSectionBase *Sec;
960+
uint64_t Offset;
961+
Symbol *Sym;
962+
};
963+
964+
static std::vector<IRelativeReloc> IRelativeRelocs;
965+
972966
template <class ELFT, class RelTy>
973967
static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I,
974968
RelTy *End) {
@@ -1011,32 +1005,29 @@ static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I,
10111005
if (Config->EMachine == EM_PPC64 && isPPC64SmallCodeModelTocReloc(Type))
10121006
Sec.File->PPC64SmallCodeModelTocRelocs = true;
10131007

1014-
// Strengthen or relax relocations.
1015-
//
1016-
// GNU ifunc symbols must be accessed via PLT because their addresses
1017-
// are determined by runtime.
1008+
if (Sym.isGnuIFunc() && !Config->ZText && Config->WarnIfuncTextrel) {
1009+
warn("using ifunc symbols when text relocations are allowed may produce "
1010+
"a binary that will segfault, if the object file is linked with "
1011+
"old version of glibc (glibc 2.28 and earlier). If this applies to "
1012+
"you, consider recompiling the object files without -fPIC and "
1013+
"without -Wl,-z,notext option. Use -no-warn-ifunc-textrel to "
1014+
"turn off this warning." +
1015+
getLocation(Sec, Sym, Offset));
1016+
}
1017+
1018+
// Relax relocations.
10181019
//
1019-
// On the other hand, if we know that a PLT entry will be resolved within
1020-
// the same ELF module, we can skip PLT access and directly jump to the
1021-
// destination function. For example, if we are linking a main exectuable,
1022-
// all dynamic symbols that can be resolved within the executable will
1023-
// actually be resolved that way at runtime, because the main exectuable
1024-
// is always at the beginning of a search list. We can leverage that fact.
1025-
if (Sym.isGnuIFunc()) {
1026-
if (!Config->ZText && Config->WarnIfuncTextrel) {
1027-
warn("using ifunc symbols when text relocations are allowed may produce "
1028-
"a binary that will segfault, if the object file is linked with "
1029-
"old version of glibc (glibc 2.28 and earlier). If this applies to "
1030-
"you, consider recompiling the object files without -fPIC and "
1031-
"without -Wl,-z,notext option. Use -no-warn-ifunc-textrel to "
1032-
"turn off this warning." +
1033-
getLocation(Sec, Sym, Offset));
1034-
}
1035-
Expr = toPlt(Expr);
1036-
} else if (!Sym.IsPreemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym)) {
1037-
Expr = Target->adjustRelaxExpr(Type, RelocatedAddr, Expr);
1038-
} else if (!Sym.IsPreemptible) {
1039-
Expr = fromPlt(Expr);
1020+
// If we know that a PLT entry will be resolved within the same ELF module, we
1021+
// can skip PLT access and directly jump to the destination function. For
1022+
// example, if we are linking a main exectuable, all dynamic symbols that can
1023+
// be resolved within the executable will actually be resolved that way at
1024+
// runtime, because the main exectuable is always at the beginning of a search
1025+
// list. We can leverage that fact.
1026+
if (!Sym.IsPreemptible && !Sym.isGnuIFunc()) {
1027+
if (Expr == R_GOT_PC && !isAbsoluteValue(Sym))
1028+
Expr = Target->adjustRelaxExpr(Type, RelocatedAddr, Expr);
1029+
else
1030+
Expr = fromPlt(Expr);
10401031
}
10411032

10421033
// 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,
10561047
return;
10571048
}
10581049

1059-
// If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol.
1060-
if (needsPlt(Expr) && !Sym.isInPlt()) {
1061-
if (Sym.isGnuIFunc() && !Sym.IsPreemptible)
1062-
addPltEntry<ELFT>(In.Iplt, In.IgotPlt, In.RelaIplt, Target->IRelativeRel,
1063-
Sym);
1064-
else
1050+
// Non-preemptible ifuncs require special handling. First, handle the usual
1051+
// case where the symbol isn't one of these.
1052+
if (!Sym.isGnuIFunc() || Sym.IsPreemptible) {
1053+
// If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol.
1054+
if (needsPlt(Expr) && !Sym.isInPlt())
10651055
addPltEntry<ELFT>(In.Plt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym);
1066-
}
10671056

1068-
// Create a GOT slot if a relocation needs GOT.
1069-
if (needsGot(Expr)) {
1070-
if (Config->EMachine == EM_MIPS) {
1071-
// MIPS ABI has special rules to process GOT entries and doesn't
1072-
// require relocation entries for them. A special case is TLS
1073-
// relocations. In that case dynamic loader applies dynamic
1074-
// relocations to initialize TLS GOT entries.
1075-
// See "Global Offset Table" in Chapter 5 in the following document
1076-
// for detailed description:
1077-
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
1078-
In.MipsGot->addEntry(*Sec.File, Sym, Addend, Expr);
1079-
} else if (!Sym.isInGot()) {
1080-
addGotEntry<ELFT>(Sym);
1057+
// Create a GOT slot if a relocation needs GOT.
1058+
if (needsGot(Expr)) {
1059+
if (Config->EMachine == EM_MIPS) {
1060+
// MIPS ABI has special rules to process GOT entries and doesn't
1061+
// require relocation entries for them. A special case is TLS
1062+
// relocations. In that case dynamic loader applies dynamic
1063+
// relocations to initialize TLS GOT entries.
1064+
// See "Global Offset Table" in Chapter 5 in the following document
1065+
// for detailed description:
1066+
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
1067+
In.MipsGot->addEntry(*Sec.File, Sym, Addend, Expr);
1068+
} else if (!Sym.isInGot()) {
1069+
addGotEntry<ELFT>(Sym);
1070+
}
1071+
}
1072+
} else {
1073+
// Handle a reference to a non-preemptible ifunc. These are special in a
1074+
// few ways:
1075+
//
1076+
// - Unlike most non-preemptible symbols, non-preemptible ifuncs do not have
1077+
// a fixed value. But assuming that all references to the ifunc are
1078+
// GOT-generating or PLT-generating, the handling of an ifunc is
1079+
// relatively straightforward. We create a PLT entry in Iplt, which is
1080+
// usually at the end of .plt, which makes an indirect call using a
1081+
// matching GOT entry in IgotPlt, which is usually at the end of .got.plt.
1082+
// The GOT entry is relocated using an IRELATIVE relocation in RelaIplt,
1083+
// which is usually at the end of .rela.plt. Unlike most relocations in
1084+
// .rela.plt, which may be evaluated lazily without -z now, dynamic
1085+
// loaders evaluate IRELATIVE relocs eagerly, which means that for
1086+
// IRELATIVE relocs only, GOT-generating relocations can point directly to
1087+
// .got.plt without requiring a separate GOT entry.
1088+
//
1089+
// - Despite the fact that an ifunc does not have a fixed value, compilers
1090+
// that are not passed -fPIC will assume that they do, and will emit
1091+
// direct (non-GOT-generating, non-PLT-generating) relocations to the
1092+
// symbol. This means that if a direct relocation to the symbol is
1093+
// seen, the linker must set a value for the symbol, and this value must
1094+
// be consistent no matter what type of reference is made to the symbol.
1095+
// This can be done by creating a PLT entry for the symbol in the way
1096+
// described above and making it canonical, that is, making all references
1097+
// point to the PLT entry instead of the resolver. In lld we also store
1098+
// the address of the PLT entry in the dynamic symbol table, which means
1099+
// that the symbol will also have the same value in other modules.
1100+
// Because the value loaded from the GOT needs to be consistent with
1101+
// the value computed using a direct relocation, a non-preemptible ifunc
1102+
// may end up with two GOT entries, one in .got.plt that points to the
1103+
// address returned by the resolver and is used only by the PLT entry,
1104+
// and another in .got that points to the PLT entry and is used by
1105+
// GOT-generating relocations.
1106+
//
1107+
// - The fact that these symbols do not have a fixed value makes them an
1108+
// exception to the general rule that a statically linked executable does
1109+
// not require any form of dynamic relocation. To handle these relocations
1110+
// correctly, the IRELATIVE relocations are stored in an array which a
1111+
// statically linked executable's startup code must enumerate using the
1112+
// linker-defined symbols __rela?_iplt_{start,end}.
1113+
//
1114+
// - An absolute relocation to a non-preemptible ifunc (such as a global
1115+
// variable containing a pointer to the ifunc) needs to be relocated in
1116+
// the exact same way as a GOT entry, so we can avoid needing to make the
1117+
// PLT entry canonical by translating such relocations into IRELATIVE
1118+
// relocations in the RelaIplt.
1119+
if (!Sym.isInPlt()) {
1120+
// Create PLT and GOTPLT slots for the symbol.
1121+
Sym.IsInIplt = true;
1122+
1123+
// Create a copy of the symbol to use as the target of the IRELATIVE
1124+
// relocation in the IgotPlt. This is in case we make the PLT canonical
1125+
// later, which would overwrite the original symbol.
1126+
//
1127+
// FIXME: Creating a copy of the symbol here is a bit of a hack. All
1128+
// that's really needed to create the IRELATIVE is the section and value,
1129+
// so ideally we should just need to copy those.
1130+
auto *DirectSym = make<Defined>(cast<Defined>(Sym));
1131+
addPltEntry<ELFT>(In.Iplt, In.IgotPlt, In.RelaIplt, Target->IRelativeRel,
1132+
*DirectSym);
1133+
Sym.PltIndex = DirectSym->PltIndex;
1134+
}
1135+
if (Expr == R_ABS && Addend == 0 && (Sec.Flags & SHF_WRITE)) {
1136+
// We might be able to represent this as an IRELATIVE. But we don't know
1137+
// yet whether some later relocation will make the symbol point to a
1138+
// canonical PLT, which would make this either a dynamic RELATIVE (PIC) or
1139+
// static (non-PIC) relocation. So we keep a record of the information
1140+
// required to process the relocation, and after scanRelocs() has been
1141+
// called on all relocations, the relocation is resolved by
1142+
// addIRelativeRelocs().
1143+
IRelativeRelocs.push_back({Type, &Sec, Offset, &Sym});
1144+
return;
1145+
}
1146+
if (needsGot(Expr)) {
1147+
// Redirect GOT accesses to point to the Igot.
1148+
//
1149+
// This field is also used to keep track of whether we ever needed a GOT
1150+
// entry. If we did and we make the PLT canonical later, we'll need to
1151+
// create a GOT entry pointing to the PLT entry for Sym.
1152+
Sym.GotInIgot = true;
1153+
} else if (!needsPlt(Expr)) {
1154+
// Make the ifunc's PLT entry canonical by changing the value of its
1155+
// symbol to redirect all references to point to it.
1156+
unsigned EntryOffset = Sym.PltIndex * Target->PltEntrySize;
1157+
if (Config->ZRetpolineplt)
1158+
EntryOffset += Target->PltHeaderSize;
1159+
1160+
auto &D = cast<Defined>(Sym);
1161+
D.Section = In.Iplt;
1162+
D.Value = EntryOffset;
1163+
D.Size = 0;
1164+
// It's important to set the symbol type here so that dynamic loaders
1165+
// don't try to call the PLT as if it were an ifunc resolver.
1166+
D.Type = STT_FUNC;
1167+
1168+
if (Sym.GotInIgot) {
1169+
// We previously encountered a GOT generating reference that we
1170+
// redirected to the Igot. Now that the PLT entry is canonical we must
1171+
// clear the redirection to the Igot and add a GOT entry. As we've
1172+
// changed the symbol type to STT_FUNC future GOT generating references
1173+
// will naturally use this GOT entry.
1174+
//
1175+
// We don't need to worry about creating a MIPS GOT here because ifuncs
1176+
// aren't a thing on MIPS.
1177+
Sym.GotInIgot = false;
1178+
addGotEntry<ELFT>(Sym);
1179+
}
10811180
}
10821181
}
10831182

@@ -1107,6 +1206,21 @@ template <class ELFT> void elf::scanRelocations(InputSectionBase &S) {
11071206
scanRelocs<ELFT>(S, S.rels<ELFT>());
11081207
}
11091208

1209+
// Figure out which representation to use for any absolute relocs to
1210+
// non-preemptible ifuncs that we visited during scanRelocs().
1211+
void elf::addIRelativeRelocs() {
1212+
for (IRelativeReloc &R : IRelativeRelocs) {
1213+
if (R.Sym->Type == STT_GNU_IFUNC)
1214+
In.RelaIplt->addReloc(
1215+
{Target->IRelativeRel, R.Sec, R.Offset, true, R.Sym, 0});
1216+
else if (Config->Pic)
1217+
addRelativeReloc(R.Sec, R.Offset, R.Sym, 0, R_ABS, R.Type);
1218+
else
1219+
R.Sec->Relocations.push_back({R_ABS, R.Type, R.Offset, 0, R.Sym});
1220+
}
1221+
IRelativeRelocs.clear();
1222+
}
1223+
11101224
static bool mergeCmp(const InputSection *A, const InputSection *B) {
11111225
// std::merge requires a strict weak ordering.
11121226
if (A->OutSecOff < B->OutSecOff)

‎lld/ELF/Relocations.h

+2-8
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,11 @@ enum RelExpr {
3333
R_ABS,
3434
R_ADDEND,
3535
R_AARCH64_GOT_PAGE_PC,
36-
// The expression is used for IFUNC support. Describes PC-relative
37-
// address of the memory page of GOT entry. This entry is used for
38-
// a redirection to IPLT.
39-
R_AARCH64_GOT_PAGE_PC_PLT,
4036
R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC,
4137
R_AARCH64_PAGE_PC,
42-
R_AARCH64_PLT_PAGE_PC,
4338
R_AARCH64_TLSDESC_PAGE,
4439
R_ARM_SBREL,
4540
R_GOT,
46-
// The expression is used for IFUNC support. Evaluates to GOT entry,
47-
// containing redirection to the IPLT.
48-
R_GOT_PLT,
4941
R_GOTONLY_PC,
5042
R_GOTONLY_PC_FROM_END,
5143
R_GOTREL,
@@ -154,6 +146,8 @@ struct RelocationOffsetComparator {
154146

155147
template <class ELFT> void scanRelocations(InputSectionBase &);
156148

149+
void addIRelativeRelocs();
150+
157151
class ThunkSection;
158152
class Thunk;
159153
struct InputSectionDescription;

0 commit comments

Comments
 (0)
Please sign in to comment.