Skip to content

Commit cf783be

Browse files
committed
Reland D114783/D115603 [ELF] Split scanRelocations into scanRelocations/postScanRelocations
(Fixed an issue about GOT on a copy relocated alias.) (Fixed an issue about not creating r_addend=0 IRELATIVE for unreferenced non-preemptible ifunc.) The idea is to make scanRelocations mark some actions are needed (GOT/PLT/etc) and postpone the real work to postScanRelocations. It gives some flexibility: * Make it feasible to support .plt.got (PR32938): we need to know whether GLOB_DAT and JUMP_SLOT are both needed. * Make non-preemptible IFUNC handling slightly cleaner: avoid setting/clearing sym.gotInIgot * -z nocopyrel: report all copy relocation places for one symbol * Make GOT deduplication feasible * Make parallel relocation scanning feasible (if we can avoid all stateful operations and make Symbol attributes atomic), but parallelism may not be the appealing choice Since this patch moves a large chunk of code out of ELFT templates. My x86-64 executable is actually a few hundred bytes smaller. For ppc32-ifunc-nonpreemptible-pic.s: I remove absolute relocation references to non-preemptible ifunc because absolute relocation references are incorrect in -fpie mode. Reviewed By: peter.smith, ikudrin Differential Revision: https://reviews.llvm.org/D114783
1 parent 04cf411 commit cf783be

28 files changed

+307
-266
lines changed

lld/ELF/Arch/AArch64.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -690,11 +690,11 @@ void AArch64BtiPac::writePlt(uint8_t *buf, const Symbol &sym,
690690
};
691691
const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop
692692

693-
// needsPltAddr indicates a non-ifunc canonical PLT entry whose address may
693+
// needsCopy indicates a non-ifunc canonical PLT entry whose address may
694694
// escape to shared objects. isInIplt indicates a non-preemptible ifunc. Its
695695
// address may escape if referenced by a direct relocation. The condition is
696696
// conservative.
697-
bool hasBti = btiHeader && (sym.needsPltAddr || sym.isInIplt);
697+
bool hasBti = btiHeader && (sym.needsCopy || sym.isInIplt);
698698
if (hasBti) {
699699
memcpy(buf, btiData, sizeof(btiData));
700700
buf += sizeof(btiData);

lld/ELF/MapFile.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ static std::vector<Defined *> getSymbols() {
5858
for (Symbol *b : file->getSymbols())
5959
if (auto *dr = dyn_cast<Defined>(b))
6060
if (!dr->isSection() && dr->section && dr->section->isLive() &&
61-
(dr->file == file || dr->needsPltAddr || dr->section->bss))
61+
(dr->file == file || dr->needsCopy || dr->section->bss))
6262
v.push_back(dr);
6363
return v;
6464
}

lld/ELF/Relocations.cpp

Lines changed: 159 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,8 @@ static void replaceWithDefined(Symbol &sym, SectionBase *sec, uint64_t value,
307307
sym.verdefIndex = old.verdefIndex;
308308
sym.exportDynamic = true;
309309
sym.isUsedInRegularObj = true;
310+
// A copy relocated alias may need a GOT entry.
311+
sym.needsGot = old.needsGot;
310312
}
311313

312314
// Reserve space in .bss or .bss.rel.ro for copy relocation.
@@ -351,7 +353,7 @@ static void replaceWithDefined(Symbol &sym, SectionBase *sec, uint64_t value,
351353
// to the variable in .bss. This kind of issue is sometimes very hard to
352354
// debug. What's a solution? Instead of exporting a variable V from a DSO,
353355
// define an accessor getV().
354-
template <class ELFT> static void addCopyRelSymbol(SharedSymbol &ss) {
356+
template <class ELFT> static void addCopyRelSymbolImpl(SharedSymbol &ss) {
355357
// Copy relocation against zero-sized symbol doesn't make sense.
356358
uint64_t symSize = ss.getSize();
357359
if (symSize == 0 || ss.alignment == 0)
@@ -382,6 +384,26 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol &ss) {
382384
mainPart->relaDyn->addSymbolReloc(target->copyRel, sec, 0, ss);
383385
}
384386

387+
static void addCopyRelSymbol(SharedSymbol &ss) {
388+
const SharedFile &file = ss.getFile();
389+
switch (file.ekind) {
390+
case ELF32LEKind:
391+
addCopyRelSymbolImpl<ELF32LE>(ss);
392+
break;
393+
case ELF32BEKind:
394+
addCopyRelSymbolImpl<ELF32BE>(ss);
395+
break;
396+
case ELF64LEKind:
397+
addCopyRelSymbolImpl<ELF64LE>(ss);
398+
break;
399+
case ELF64BEKind:
400+
addCopyRelSymbolImpl<ELF64BE>(ss);
401+
break;
402+
default:
403+
llvm_unreachable("");
404+
}
405+
}
406+
385407
// MIPS has an odd notion of "paired" relocations to calculate addends.
386408
// For example, if a relocation is of R_MIPS_HI16, there must be a
387409
// R_MIPS_LO16 relocation after that, and an addend is calculated using
@@ -1045,7 +1067,7 @@ static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
10451067
" against symbol '" + toString(*ss) +
10461068
"'; recompile with -fPIC or remove '-z nocopyreloc'" +
10471069
getLocation(sec, sym, offset));
1048-
addCopyRelSymbol<ELFT>(*ss);
1070+
sym.needsCopy = true;
10491071
}
10501072
sec.relocations.push_back({expr, type, offset, addend, &sym});
10511073
return;
@@ -1083,20 +1105,8 @@ static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
10831105
errorOrWarn("symbol '" + toString(sym) +
10841106
"' cannot be preempted; recompile with -fPIE" +
10851107
getLocation(sec, sym, offset));
1086-
if (!sym.isInPlt())
1087-
addPltEntry(in.plt, in.gotPlt, in.relaPlt, target->pltRel, sym);
1088-
if (!sym.isDefined()) {
1089-
replaceWithDefined(
1090-
sym, in.plt,
1091-
target->pltHeaderSize + target->pltEntrySize * sym.pltIndex, 0);
1092-
if (config->emachine == EM_PPC) {
1093-
// PPC32 canonical PLT entries are at the beginning of .glink
1094-
cast<Defined>(sym).value = in.plt->headerSize;
1095-
in.plt->headerSize += 16;
1096-
cast<PPC32GlinkSection>(in.plt)->canonical_plts.push_back(&sym);
1097-
}
1098-
}
1099-
sym.needsPltAddr = true;
1108+
sym.needsCopy = true;
1109+
sym.needsPlt = true;
11001110
sec.relocations.push_back({expr, type, offset, addend, &sym});
11011111
return;
11021112
}
@@ -1425,116 +1435,23 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
14251435
return;
14261436
}
14271437

1428-
// Non-preemptible ifuncs require special handling. First, handle the usual
1429-
// case where the symbol isn't one of these.
1430-
if (!sym.isGnuIFunc() || sym.isPreemptible) {
1431-
// If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol.
1432-
if (needsPlt(expr) && !sym.isInPlt())
1433-
addPltEntry(in.plt, in.gotPlt, in.relaPlt, target->pltRel, sym);
1434-
1435-
// Create a GOT slot if a relocation needs GOT.
1436-
if (needsGot(expr)) {
1437-
if (config->emachine == EM_MIPS) {
1438-
// MIPS ABI has special rules to process GOT entries and doesn't
1439-
// require relocation entries for them. A special case is TLS
1440-
// relocations. In that case dynamic loader applies dynamic
1441-
// relocations to initialize TLS GOT entries.
1442-
// See "Global Offset Table" in Chapter 5 in the following document
1443-
// for detailed description:
1444-
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
1445-
in.mipsGot->addEntry(*sec.file, sym, addend, expr);
1446-
} else if (!sym.isInGot()) {
1447-
addGotEntry(sym);
1448-
}
1438+
if (needsGot(expr)) {
1439+
if (config->emachine == EM_MIPS) {
1440+
// MIPS ABI has special rules to process GOT entries and doesn't
1441+
// require relocation entries for them. A special case is TLS
1442+
// relocations. In that case dynamic loader applies dynamic
1443+
// relocations to initialize TLS GOT entries.
1444+
// See "Global Offset Table" in Chapter 5 in the following document
1445+
// for detailed description:
1446+
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
1447+
in.mipsGot->addEntry(*sec.file, sym, addend, expr);
1448+
} else {
1449+
sym.needsGot = true;
14491450
}
1451+
} else if (needsPlt(expr)) {
1452+
sym.needsPlt = true;
14501453
} else {
1451-
// Handle a reference to a non-preemptible ifunc. These are special in a
1452-
// few ways:
1453-
//
1454-
// - Unlike most non-preemptible symbols, non-preemptible ifuncs do not have
1455-
// a fixed value. But assuming that all references to the ifunc are
1456-
// GOT-generating or PLT-generating, the handling of an ifunc is
1457-
// relatively straightforward. We create a PLT entry in Iplt, which is
1458-
// usually at the end of .plt, which makes an indirect call using a
1459-
// matching GOT entry in igotPlt, which is usually at the end of .got.plt.
1460-
// The GOT entry is relocated using an IRELATIVE relocation in relaIplt,
1461-
// which is usually at the end of .rela.plt. Unlike most relocations in
1462-
// .rela.plt, which may be evaluated lazily without -z now, dynamic
1463-
// loaders evaluate IRELATIVE relocs eagerly, which means that for
1464-
// IRELATIVE relocs only, GOT-generating relocations can point directly to
1465-
// .got.plt without requiring a separate GOT entry.
1466-
//
1467-
// - Despite the fact that an ifunc does not have a fixed value, compilers
1468-
// that are not passed -fPIC will assume that they do, and will emit
1469-
// direct (non-GOT-generating, non-PLT-generating) relocations to the
1470-
// symbol. This means that if a direct relocation to the symbol is
1471-
// seen, the linker must set a value for the symbol, and this value must
1472-
// be consistent no matter what type of reference is made to the symbol.
1473-
// This can be done by creating a PLT entry for the symbol in the way
1474-
// described above and making it canonical, that is, making all references
1475-
// point to the PLT entry instead of the resolver. In lld we also store
1476-
// the address of the PLT entry in the dynamic symbol table, which means
1477-
// that the symbol will also have the same value in other modules.
1478-
// Because the value loaded from the GOT needs to be consistent with
1479-
// the value computed using a direct relocation, a non-preemptible ifunc
1480-
// may end up with two GOT entries, one in .got.plt that points to the
1481-
// address returned by the resolver and is used only by the PLT entry,
1482-
// and another in .got that points to the PLT entry and is used by
1483-
// GOT-generating relocations.
1484-
//
1485-
// - The fact that these symbols do not have a fixed value makes them an
1486-
// exception to the general rule that a statically linked executable does
1487-
// not require any form of dynamic relocation. To handle these relocations
1488-
// correctly, the IRELATIVE relocations are stored in an array which a
1489-
// statically linked executable's startup code must enumerate using the
1490-
// linker-defined symbols __rela?_iplt_{start,end}.
1491-
if (!sym.isInPlt()) {
1492-
// Create PLT and GOTPLT slots for the symbol.
1493-
sym.isInIplt = true;
1494-
1495-
// Create a copy of the symbol to use as the target of the IRELATIVE
1496-
// relocation in the igotPlt. This is in case we make the PLT canonical
1497-
// later, which would overwrite the original symbol.
1498-
//
1499-
// FIXME: Creating a copy of the symbol here is a bit of a hack. All
1500-
// that's really needed to create the IRELATIVE is the section and value,
1501-
// so ideally we should just need to copy those.
1502-
auto *directSym = make<Defined>(cast<Defined>(sym));
1503-
addPltEntry(in.iplt, in.igotPlt, in.relaIplt, target->iRelativeRel,
1504-
*directSym);
1505-
sym.pltIndex = directSym->pltIndex;
1506-
}
1507-
if (needsGot(expr)) {
1508-
// Redirect GOT accesses to point to the Igot.
1509-
//
1510-
// This field is also used to keep track of whether we ever needed a GOT
1511-
// entry. If we did and we make the PLT canonical later, we'll need to
1512-
// create a GOT entry pointing to the PLT entry for Sym.
1513-
sym.gotInIgot = true;
1514-
} else if (!needsPlt(expr)) {
1515-
// Make the ifunc's PLT entry canonical by changing the value of its
1516-
// symbol to redirect all references to point to it.
1517-
auto &d = cast<Defined>(sym);
1518-
d.section = in.iplt;
1519-
d.value = sym.pltIndex * target->ipltEntrySize;
1520-
d.size = 0;
1521-
// It's important to set the symbol type here so that dynamic loaders
1522-
// don't try to call the PLT as if it were an ifunc resolver.
1523-
d.type = STT_FUNC;
1524-
1525-
if (sym.gotInIgot) {
1526-
// We previously encountered a GOT generating reference that we
1527-
// redirected to the Igot. Now that the PLT entry is canonical we must
1528-
// clear the redirection to the Igot and add a GOT entry. As we've
1529-
// changed the symbol type to STT_FUNC future GOT generating references
1530-
// will naturally use this GOT entry.
1531-
//
1532-
// We don't need to worry about creating a MIPS GOT here because ifuncs
1533-
// aren't a thing on MIPS.
1534-
sym.gotInIgot = false;
1535-
addGotEntry(sym);
1536-
}
1537-
}
1454+
sym.hasDirectReloc = true;
15381455
}
15391456

15401457
processRelocAux<ELFT>(sec, expr, type, offset, sym, addend);
@@ -1615,6 +1532,124 @@ template <class ELFT> void elf::scanRelocations(InputSectionBase &s) {
16151532
scanRelocs<ELFT>(s, rels.relas);
16161533
}
16171534

1535+
static bool handleNonPreemptibleIfunc(Symbol &sym) {
1536+
// Handle a reference to a non-preemptible ifunc. These are special in a
1537+
// few ways:
1538+
//
1539+
// - Unlike most non-preemptible symbols, non-preemptible ifuncs do not have
1540+
// a fixed value. But assuming that all references to the ifunc are
1541+
// GOT-generating or PLT-generating, the handling of an ifunc is
1542+
// relatively straightforward. We create a PLT entry in Iplt, which is
1543+
// usually at the end of .plt, which makes an indirect call using a
1544+
// matching GOT entry in igotPlt, which is usually at the end of .got.plt.
1545+
// The GOT entry is relocated using an IRELATIVE relocation in relaIplt,
1546+
// which is usually at the end of .rela.plt. Unlike most relocations in
1547+
// .rela.plt, which may be evaluated lazily without -z now, dynamic
1548+
// loaders evaluate IRELATIVE relocs eagerly, which means that for
1549+
// IRELATIVE relocs only, GOT-generating relocations can point directly to
1550+
// .got.plt without requiring a separate GOT entry.
1551+
//
1552+
// - Despite the fact that an ifunc does not have a fixed value, compilers
1553+
// that are not passed -fPIC will assume that they do, and will emit
1554+
// direct (non-GOT-generating, non-PLT-generating) relocations to the
1555+
// symbol. This means that if a direct relocation to the symbol is
1556+
// seen, the linker must set a value for the symbol, and this value must
1557+
// be consistent no matter what type of reference is made to the symbol.
1558+
// This can be done by creating a PLT entry for the symbol in the way
1559+
// described above and making it canonical, that is, making all references
1560+
// point to the PLT entry instead of the resolver. In lld we also store
1561+
// the address of the PLT entry in the dynamic symbol table, which means
1562+
// that the symbol will also have the same value in other modules.
1563+
// Because the value loaded from the GOT needs to be consistent with
1564+
// the value computed using a direct relocation, a non-preemptible ifunc
1565+
// may end up with two GOT entries, one in .got.plt that points to the
1566+
// address returned by the resolver and is used only by the PLT entry,
1567+
// and another in .got that points to the PLT entry and is used by
1568+
// GOT-generating relocations.
1569+
//
1570+
// - The fact that these symbols do not have a fixed value makes them an
1571+
// exception to the general rule that a statically linked executable does
1572+
// not require any form of dynamic relocation. To handle these relocations
1573+
// correctly, the IRELATIVE relocations are stored in an array which a
1574+
// statically linked executable's startup code must enumerate using the
1575+
// linker-defined symbols __rela?_iplt_{start,end}.
1576+
if (!sym.isGnuIFunc() || sym.isPreemptible || config->zIfuncNoplt)
1577+
return false;
1578+
// Skip unreferenced non-preemptible ifunc.
1579+
if (!(sym.needsGot || sym.needsPlt || sym.hasDirectReloc))
1580+
return true;
1581+
1582+
sym.isInIplt = true;
1583+
1584+
// Create an Iplt and the associated IRELATIVE relocation pointing to the
1585+
// original section/value pairs. For non-GOT non-PLT relocation case below, we
1586+
// may alter section/value, so create a copy of the symbol to make
1587+
// section/value fixed.
1588+
auto *directSym = make<Defined>(cast<Defined>(sym));
1589+
addPltEntry(in.iplt, in.igotPlt, in.relaIplt, target->iRelativeRel,
1590+
*directSym);
1591+
sym.pltIndex = directSym->pltIndex;
1592+
1593+
if (sym.hasDirectReloc) {
1594+
// Change the value to the IPLT and redirect all references to it.
1595+
auto &d = cast<Defined>(sym);
1596+
d.section = in.iplt;
1597+
d.value = sym.pltIndex * target->ipltEntrySize;
1598+
d.size = 0;
1599+
// It's important to set the symbol type here so that dynamic loaders
1600+
// don't try to call the PLT as if it were an ifunc resolver.
1601+
d.type = STT_FUNC;
1602+
1603+
if (sym.needsGot)
1604+
addGotEntry(sym);
1605+
} else if (sym.needsGot) {
1606+
// Redirect GOT accesses to point to the Igot.
1607+
sym.gotInIgot = true;
1608+
}
1609+
return true;
1610+
}
1611+
1612+
void elf::postScanRelocations() {
1613+
auto fn = [](Symbol &sym) {
1614+
if (handleNonPreemptibleIfunc(sym))
1615+
return;
1616+
if (sym.needsGot)
1617+
addGotEntry(sym);
1618+
if (sym.needsPlt)
1619+
addPltEntry(in.plt, in.gotPlt, in.relaPlt, target->pltRel, sym);
1620+
if (sym.needsCopy) {
1621+
if (sym.isObject()) {
1622+
addCopyRelSymbol(cast<SharedSymbol>(sym));
1623+
// needsCopy is cleared for sym and its aliases so that in later
1624+
// iterations aliases won't cause redundant copies.
1625+
assert(!sym.needsCopy);
1626+
} else {
1627+
assert(sym.isFunc() && sym.needsPlt);
1628+
if (!sym.isDefined()) {
1629+
replaceWithDefined(
1630+
sym, in.plt,
1631+
target->pltHeaderSize + target->pltEntrySize * sym.pltIndex, 0);
1632+
sym.needsCopy = true;
1633+
if (config->emachine == EM_PPC) {
1634+
// PPC32 canonical PLT entries are at the beginning of .glink
1635+
cast<Defined>(sym).value = in.plt->headerSize;
1636+
in.plt->headerSize += 16;
1637+
cast<PPC32GlinkSection>(in.plt)->canonical_plts.push_back(&sym);
1638+
}
1639+
}
1640+
}
1641+
}
1642+
};
1643+
for (Symbol *sym : symtab->symbols())
1644+
fn(*sym);
1645+
1646+
// Local symbols may need the aforementioned non-preemptible ifunc and GOT
1647+
// handling. They don't need regular PLT.
1648+
for (InputFile *file : objectFiles)
1649+
for (Symbol *sym : cast<ELFFileBase>(file)->getLocalSymbols())
1650+
fn(*sym);
1651+
}
1652+
16181653
static bool mergeCmp(const InputSection *a, const InputSection *b) {
16191654
// std::merge requires a strict weak ordering.
16201655
if (a->outSecOff < b->outSecOff)

lld/ELF/Relocations.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ struct JumpInstrMod {
126126
// Call reportUndefinedSymbols() after calling scanRelocations() to emit
127127
// the diagnostics.
128128
template <class ELFT> void scanRelocations(InputSectionBase &);
129+
void postScanRelocations();
129130

130131
template <class ELFT> void reportUndefinedSymbols();
131132

lld/ELF/Symbols.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ static uint64_t getSymVA(const Symbol &sym, int64_t addend) {
120120
// field etc) do the same trick as compiler uses to mark microMIPS
121121
// for CPU - set the less-significant bit.
122122
if (config->emachine == EM_MIPS && isMicroMips() &&
123-
((sym.stOther & STO_MIPS_MICROMIPS) || sym.needsPltAddr))
123+
((sym.stOther & STO_MIPS_MICROMIPS) || sym.needsCopy))
124124
va |= 1;
125125

126126
if (d.isTls() && !config->relocatable) {

0 commit comments

Comments
 (0)