Skip to content

Commit 22bf331

Browse files
authored
[ELF] Refactor RelocScan::scan to be target-specific (llvm#163138)
- Extract RelocScan to RelocScan.h. The file includes Target.h, and cannot be merged with Relocations.h - Add MIPS and PPC64 specific relocation scanners, removing runtime checks for other targets. This refactoring prepares the codebase for better target-specific optimizations and easier addition of target-specific behavior.
1 parent 5a560b3 commit 22bf331

File tree

7 files changed

+446
-354
lines changed

7 files changed

+446
-354
lines changed

lld/ELF/Arch/Mips.cpp

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "InputFiles.h"
10+
#include "RelocScan.h"
1011
#include "Symbols.h"
1112
#include "SyntheticSections.h"
1213
#include "Target.h"
@@ -31,6 +32,9 @@ template <class ELFT> class MIPS final : public TargetInfo {
3132
void writePltHeader(uint8_t *buf) const override;
3233
void writePlt(uint8_t *buf, const Symbol &sym,
3334
uint64_t pltEntryAddr) const override;
35+
template <class RelTy>
36+
void scanSectionImpl(InputSectionBase &, Relocs<RelTy>);
37+
void scanSection(InputSectionBase &) override;
3438
bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
3539
uint64_t branchAddr, const Symbol &s,
3640
int64_t a) const override;
@@ -570,6 +574,123 @@ static uint64_t fixupCrossModeJump(Ctx &ctx, uint8_t *loc, RelType type,
570574
return val;
571575
}
572576

577+
template <class RelTy>
578+
static RelType getMipsN32RelType(Ctx &ctx, RelTy *&rel, RelTy *end) {
579+
uint32_t type = 0;
580+
uint64_t offset = rel->r_offset;
581+
int n = 0;
582+
while (rel != end && rel->r_offset == offset)
583+
type |= (rel++)->getType(ctx.arg.isMips64EL) << (8 * n++);
584+
return type;
585+
}
586+
587+
static RelType getMipsPairType(RelType type, bool isLocal) {
588+
switch (type) {
589+
case R_MIPS_HI16:
590+
return R_MIPS_LO16;
591+
case R_MIPS_GOT16:
592+
// In case of global symbol, the R_MIPS_GOT16 relocation does not
593+
// have a pair. Each global symbol has a unique entry in the GOT
594+
// and a corresponding instruction with help of the R_MIPS_GOT16
595+
// relocation loads an address of the symbol. In case of local
596+
// symbol, the R_MIPS_GOT16 relocation creates a GOT entry to hold
597+
// the high 16 bits of the symbol's value. A paired R_MIPS_LO16
598+
// relocations handle low 16 bits of the address. That allows
599+
// to allocate only one GOT entry for every 64 KiB of local data.
600+
return isLocal ? R_MIPS_LO16 : R_MIPS_NONE;
601+
case R_MICROMIPS_GOT16:
602+
return isLocal ? R_MICROMIPS_LO16 : R_MIPS_NONE;
603+
case R_MIPS_PCHI16:
604+
return R_MIPS_PCLO16;
605+
case R_MICROMIPS_HI16:
606+
return R_MICROMIPS_LO16;
607+
default:
608+
return R_MIPS_NONE;
609+
}
610+
}
611+
612+
template <class ELFT>
613+
template <class RelTy>
614+
void MIPS<ELFT>::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
615+
RelocScan rs(ctx, &sec);
616+
sec.relocations.reserve(rels.size());
617+
RelType type;
618+
for (auto it = rels.begin(); it != rels.end();) {
619+
const RelTy &rel = *it;
620+
uint64_t offset = rel.r_offset;
621+
if constexpr (ELFT::Is64Bits) {
622+
type = it->getType(ctx.arg.isMips64EL);
623+
++it;
624+
} else {
625+
if (ctx.arg.mipsN32Abi) {
626+
type = getMipsN32RelType(ctx, it, rels.end());
627+
} else {
628+
type = it->getType(ctx.arg.isMips64EL);
629+
++it;
630+
}
631+
}
632+
633+
uint32_t symIdx = rel.getSymbol(ctx.arg.isMips64EL);
634+
Symbol &sym = sec.getFile<ELFT>()->getSymbol(symIdx);
635+
RelExpr expr =
636+
ctx.target->getRelExpr(type, sym, sec.content().data() + rel.r_offset);
637+
if (expr == R_NONE)
638+
continue;
639+
if (sym.isUndefined() && symIdx != 0 &&
640+
rs.maybeReportUndefined(cast<Undefined>(sym), offset))
641+
continue;
642+
643+
auto addend = rs.getAddend<ELFT>(rel, type);
644+
if (expr == RE_MIPS_GOTREL && sym.isLocal()) {
645+
addend += sec.getFile<ELFT>()->mipsGp0;
646+
} else if (!RelTy::HasAddend) {
647+
// MIPS has an odd notion of "paired" relocations to calculate addends.
648+
// For example, if a relocation is of R_MIPS_HI16, there must be a
649+
// R_MIPS_LO16 relocation after that, and an addend is calculated using
650+
// the two relocations.
651+
RelType pairTy = getMipsPairType(type, sym.isLocal());
652+
if (pairTy != R_MIPS_NONE) {
653+
const uint8_t *buf = sec.content().data();
654+
// To make things worse, paired relocations might not be contiguous in
655+
// the relocation table, so we need to do linear search. *sigh*
656+
bool found = false;
657+
for (auto *ri = &rel; ri != rels.end(); ++ri) {
658+
if (ri->getType(ctx.arg.isMips64EL) == pairTy &&
659+
ri->getSymbol(ctx.arg.isMips64EL) == symIdx) {
660+
addend += ctx.target->getImplicitAddend(buf + ri->r_offset, pairTy);
661+
found = true;
662+
break;
663+
}
664+
}
665+
666+
if (!found)
667+
Warn(ctx) << "can't find matching " << pairTy << " relocation for "
668+
<< type;
669+
}
670+
}
671+
672+
if (expr == RE_MIPS_TLSLD) {
673+
ctx.in.mipsGot->addTlsIndex(*sec.file);
674+
sec.addReloc({expr, type, offset, addend, &sym});
675+
} else if (expr == RE_MIPS_TLSGD) {
676+
ctx.in.mipsGot->addDynTlsEntry(*sec.file, sym);
677+
sec.addReloc({expr, type, offset, addend, &sym});
678+
} else {
679+
if (expr == R_TPREL && rs.checkTlsLe(offset, sym, type))
680+
continue;
681+
rs.process(expr, type, offset, sym, addend);
682+
}
683+
}
684+
}
685+
686+
template <class ELFT> void MIPS<ELFT>::scanSection(InputSectionBase &sec) {
687+
auto relocs = sec.template relsOrRelas<ELFT>();
688+
if (relocs.areRelocsRel())
689+
scanSectionImpl(sec, relocs.rels);
690+
else
691+
scanSectionImpl(sec, relocs.relas);
692+
}
693+
573694
template <class ELFT>
574695
void MIPS<ELFT>::relocate(uint8_t *loc, const Relocation &rel,
575696
uint64_t val) const {

lld/ELF/Arch/PPC64.cpp

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "InputFiles.h"
1010
#include "OutputSections.h"
11+
#include "RelocScan.h"
1112
#include "SymbolTable.h"
1213
#include "Symbols.h"
1314
#include "SyntheticSections.h"
@@ -178,6 +179,10 @@ class PPC64 final : public TargetInfo {
178179
uint64_t pltEntryAddr) const override;
179180
void writeIplt(uint8_t *buf, const Symbol &sym,
180181
uint64_t pltEntryAddr) const override;
182+
template <class ELFT, class RelTy>
183+
void scanSectionImpl(InputSectionBase &, Relocs<RelTy>);
184+
template <class ELFT> void scanSection1(InputSectionBase &);
185+
void scanSection(InputSectionBase &) override;
181186
void relocate(uint8_t *loc, const Relocation &rel,
182187
uint64_t val) const override;
183188
void writeGotHeader(uint8_t *buf) const override;
@@ -1257,6 +1262,132 @@ static bool isTocOptType(RelType type) {
12571262
}
12581263
}
12591264

1265+
// R_PPC64_TLSGD/R_PPC64_TLSLD is required to mark `bl __tls_get_addr` for
1266+
// General Dynamic/Local Dynamic code sequences. If a GD/LD GOT relocation is
1267+
// found but no R_PPC64_TLSGD/R_PPC64_TLSLD is seen, we assume that the
1268+
// instructions are generated by very old IBM XL compilers. Work around the
1269+
// issue by disabling GD/LD to IE/LE relaxation.
1270+
template <class RelTy>
1271+
static void checkPPC64TLSRelax(InputSectionBase &sec, Relocs<RelTy> rels) {
1272+
// Skip if sec is synthetic (sec.file is null) or if sec has been marked.
1273+
if (!sec.file || sec.file->ppc64DisableTLSRelax)
1274+
return;
1275+
bool hasGDLD = false;
1276+
for (const RelTy &rel : rels) {
1277+
RelType type = rel.getType(false);
1278+
switch (type) {
1279+
case R_PPC64_TLSGD:
1280+
case R_PPC64_TLSLD:
1281+
return; // Found a marker
1282+
case R_PPC64_GOT_TLSGD16:
1283+
case R_PPC64_GOT_TLSGD16_HA:
1284+
case R_PPC64_GOT_TLSGD16_HI:
1285+
case R_PPC64_GOT_TLSGD16_LO:
1286+
case R_PPC64_GOT_TLSLD16:
1287+
case R_PPC64_GOT_TLSLD16_HA:
1288+
case R_PPC64_GOT_TLSLD16_HI:
1289+
case R_PPC64_GOT_TLSLD16_LO:
1290+
hasGDLD = true;
1291+
break;
1292+
}
1293+
}
1294+
if (hasGDLD) {
1295+
sec.file->ppc64DisableTLSRelax = true;
1296+
Warn(sec.file->ctx)
1297+
<< sec.file
1298+
<< ": disable TLS relaxation due to R_PPC64_GOT_TLS* relocations "
1299+
"without "
1300+
"R_PPC64_TLSGD/R_PPC64_TLSLD relocations";
1301+
}
1302+
}
1303+
1304+
template <class ELFT, class RelTy>
1305+
void PPC64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels) {
1306+
RelocScan rs(ctx, &sec);
1307+
sec.relocations.reserve(rels.size());
1308+
checkPPC64TLSRelax<RelTy>(sec, rels);
1309+
for (auto it = rels.begin(); it != rels.end(); ++it) {
1310+
const RelTy &rel = *it;
1311+
uint64_t offset = rel.r_offset;
1312+
uint32_t symIdx = rel.getSymbol(false);
1313+
Symbol &sym = sec.getFile<ELFT>()->getSymbol(symIdx);
1314+
RelType type = rel.getType(false);
1315+
RelExpr expr =
1316+
ctx.target->getRelExpr(type, sym, sec.content().data() + offset);
1317+
if (expr == R_NONE)
1318+
continue;
1319+
if (sym.isUndefined() && symIdx != 0 &&
1320+
rs.maybeReportUndefined(cast<Undefined>(sym), offset))
1321+
continue;
1322+
1323+
auto addend = getAddend<ELFT>(rel);
1324+
if (ctx.arg.isPic && type == R_PPC64_TOC)
1325+
addend += getPPC64TocBase(ctx);
1326+
1327+
// We can separate the small code model relocations into 2 categories:
1328+
// 1) Those that access the compiler generated .toc sections.
1329+
// 2) Those that access the linker allocated got entries.
1330+
// lld allocates got entries to symbols on demand. Since we don't try to
1331+
// sort the got entries in any way, we don't have to track which objects
1332+
// have got-based small code model relocs. The .toc sections get placed
1333+
// after the end of the linker allocated .got section and we do sort those
1334+
// so sections addressed with small code model relocations come first.
1335+
if (type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS)
1336+
sec.file->ppc64SmallCodeModelTocRelocs = true;
1337+
1338+
// Record the TOC entry (.toc + addend) as not relaxable. See the comment in
1339+
// PPC64::relocateAlloc().
1340+
if (type == R_PPC64_TOC16_LO && sym.isSection() && isa<Defined>(sym) &&
1341+
cast<Defined>(sym).section->name == ".toc")
1342+
ctx.ppc64noTocRelax.insert({&sym, addend});
1343+
1344+
if ((type == R_PPC64_TLSGD && expr == R_TLSDESC_CALL) ||
1345+
(type == R_PPC64_TLSLD && expr == R_TLSLD_HINT)) {
1346+
auto it1 = it;
1347+
++it1;
1348+
if (it1 == rels.end()) {
1349+
auto diag = Err(ctx);
1350+
diag << "R_PPC64_TLSGD/R_PPC64_TLSLD may not be the last "
1351+
"relocation";
1352+
printLocation(diag, sec, sym, offset);
1353+
continue;
1354+
}
1355+
1356+
// Offset the 4-byte aligned R_PPC64_TLSGD by one byte in the NOTOC
1357+
// case, so we can discern it later from the toc-case.
1358+
if (it1->getType(/*isMips64EL=*/false) == R_PPC64_REL24_NOTOC)
1359+
++offset;
1360+
}
1361+
1362+
if (oneof<R_GOTREL, RE_PPC64_TOCBASE, RE_PPC64_RELAX_TOC>(expr))
1363+
ctx.in.got->hasGotOffRel.store(true, std::memory_order_relaxed);
1364+
1365+
if (sym.isTls()) {
1366+
if (unsigned processed =
1367+
rs.handleTlsRelocation(expr, type, offset, sym, addend)) {
1368+
it += processed - 1;
1369+
continue;
1370+
}
1371+
}
1372+
rs.process(expr, type, offset, sym, addend);
1373+
}
1374+
}
1375+
1376+
template <class ELFT> void PPC64::scanSection1(InputSectionBase &sec) {
1377+
auto relocs = sec.template relsOrRelas<ELFT>();
1378+
if (relocs.areRelocsCrel())
1379+
scanSectionImpl<ELFT>(sec, relocs.crels);
1380+
else
1381+
scanSectionImpl<ELFT>(sec, relocs.relas);
1382+
}
1383+
1384+
void PPC64::scanSection(InputSectionBase &sec) {
1385+
if (ctx.arg.isLE)
1386+
scanSection1<ELF64LE>(sec);
1387+
else
1388+
scanSection1<ELF64BE>(sec);
1389+
}
1390+
12601391
void PPC64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
12611392
RelType type = rel.type;
12621393
bool shouldTocOptimize = isTocOptType(type);

0 commit comments

Comments
 (0)