[ELF] Add RelocationScanner. NFC

Currently the way some relocation-related static functions pass around
states is clumsy. Add a Resolver class to store some states as member
variables.

Advantages:

* Avoid the parameter `InputSectionBase &sec` (this offsets the cost passing around `this` paramemter)
* Avoid the parameter `end` (Mips and PowerPC hacks)
* `config` and `target` can be cached as member variables to reduce global state accesses. (potential speedup because the compiler didn't know `config`/`target` were not changed across function calls)
* If we ever want to reduce if-else costs (e.g. `config->emachine==EM_MIPS` for non-Mips) or introduce parallel relocation scan not handling some tricky arches (PPC/Mips), we can templatize Resolver

`target` isn't used as much as `config`, so I change it to a const reference
during the migration.

There is a minor performance inprovement for elf::scanRelocations.

Reviewed By: ikudrin, peter.smith

Differential Revision: https://reviews.llvm.org/D116881
This commit is contained in:
Fangrui Song 2022-01-11 09:54:53 -08:00
parent b22a93f4fb
commit 37a1291885

View File

@ -447,6 +447,36 @@ private:
ArrayRef<EhSectionPiece> pieces;
size_t i = 0;
};
// This class encapsulates states needed to scan relocations for one
// InputSectionBase.
class RelocationScanner {
public:
explicit RelocationScanner(InputSectionBase &sec)
: sec(sec), getter(sec), config(elf::config.get()), target(*elf::target) {
}
template <class ELFT, class RelTy> void scan(ArrayRef<RelTy> rels);
private:
InputSectionBase &sec;
OffsetGetter getter;
const Configuration *const config;
const TargetInfo &target;
// End of relocations, used by Mips/PPC64.
const void *end = nullptr;
template <class RelTy> RelType getMipsN32RelType(RelTy *&rel) const;
template <class ELFT, class RelTy>
int64_t computeMipsAddend(const RelTy &rel, RelExpr expr, bool isLocal) const;
template <class ELFT, class RelTy>
int64_t computeAddend(const RelTy &rel, RelExpr expr, bool isLocal) const;
bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym,
uint64_t relOff) const;
void processAux(RelExpr expr, RelType type, uint64_t offset, Symbol &sym,
int64_t addend) const;
template <class ELFT, class RelTy> void scanOne(RelTy *&i);
};
} // namespace
// MIPS has an odd notion of "paired" relocations to calculate addends.
@ -454,9 +484,8 @@ private:
// R_MIPS_LO16 relocation after that, and an addend is calculated using
// the two relocations.
template <class ELFT, class RelTy>
static int64_t computeMipsAddend(const RelTy &rel, const RelTy *end,
InputSectionBase &sec, RelExpr expr,
bool isLocal) {
int64_t RelocationScanner::computeMipsAddend(const RelTy &rel, RelExpr expr,
bool isLocal) const {
if (expr == R_MIPS_GOTREL && isLocal)
return sec.getFile<ELFT>()->mipsGp0;
@ -475,10 +504,10 @@ static int64_t computeMipsAddend(const RelTy &rel, const RelTy *end,
// To make things worse, paired relocations might not be contiguous in
// the relocation table, so we need to do linear search. *sigh*
for (const RelTy *ri = &rel; ri != end; ++ri)
for (const RelTy *ri = &rel; ri != static_cast<const RelTy *>(end); ++ri)
if (ri->getType(config->isMips64EL) == pairTy &&
ri->getSymbol(config->isMips64EL) == symIndex)
return target->getImplicitAddend(buf + ri->r_offset, pairTy);
return target.getImplicitAddend(buf + ri->r_offset, pairTy);
warn("can't find matching " + toString(pairTy) + " relocation for " +
toString(type));
@ -489,9 +518,8 @@ static int64_t computeMipsAddend(const RelTy &rel, const RelTy *end,
// is in a relocation itself. If it is REL, we need to read it from an
// input section.
template <class ELFT, class RelTy>
static int64_t computeAddend(const RelTy &rel, const RelTy *end,
InputSectionBase &sec, RelExpr expr,
bool isLocal) {
int64_t RelocationScanner::computeAddend(const RelTy &rel, RelExpr expr,
bool isLocal) const {
int64_t addend;
RelType type = rel.getType(config->isMips64EL);
@ -499,13 +527,13 @@ static int64_t computeAddend(const RelTy &rel, const RelTy *end,
addend = getAddend<ELFT>(rel);
} else {
const uint8_t *buf = sec.data().data();
addend = target->getImplicitAddend(buf + rel.r_offset, type);
addend = target.getImplicitAddend(buf + rel.r_offset, type);
}
if (config->emachine == EM_PPC64 && config->isPic && type == R_PPC64_TOC)
addend += getPPC64TocBase();
if (config->emachine == EM_MIPS)
addend += computeMipsAddend<ELFT>(rel, end, sec, expr, isLocal);
addend += computeMipsAddend<ELFT>(rel, expr, isLocal);
return addend;
}
@ -822,12 +850,13 @@ static bool maybeReportUndefined(Symbol &sym, InputSectionBase &sec,
// packs all relocations into the single relocation record. Here we emulate
// this for the N32 ABI. Iterate over relocation with the same offset and put
// theirs types into the single bit-set.
template <class RelTy> static RelType getMipsN32RelType(RelTy *&rel, RelTy *end) {
template <class RelTy>
RelType RelocationScanner::getMipsN32RelType(RelTy *&rel) const {
RelType type = 0;
uint64_t offset = rel->r_offset;
int n = 0;
while (rel != end && rel->r_offset == offset)
while (rel != static_cast<const RelTy *>(end) && rel->r_offset == offset)
type |= (rel++)->getType(config->isMips64EL) << (8 * n++);
return type;
}
@ -923,8 +952,9 @@ static bool canDefineSymbolInExecutable(Symbol &sym) {
//
// If this function returns false, that means we need to emit a
// dynamic relocation so that the relocation will be fixed at load-time.
static bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym,
InputSectionBase &s, uint64_t relOff) {
bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type,
const Symbol &sym,
uint64_t relOff) const {
// These expressions always compute a constant
if (oneof<R_GOTPLT, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOTREL,
R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC,
@ -936,7 +966,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_PLT)
return target->usesOnlyLowPageBits(type) || !config->isPic;
return target.usesOnlyLowPageBits(type) || !config->isPic;
if (sym.isPreemptible)
return false;
@ -956,7 +986,7 @@ static bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym,
if (!absVal && relE)
return true;
if (!absVal && !relE)
return target->usesOnlyLowPageBits(type);
return target.usesOnlyLowPageBits(type);
assert(absVal && relE);
@ -974,7 +1004,7 @@ static bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym,
return true;
error("relocation " + toString(type) + " cannot refer to absolute symbol: " +
toString(sym) + getLocation(s, sym, relOff));
toString(sym) + getLocation(sec, sym, relOff));
return true;
}
@ -991,9 +1021,8 @@ static bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym,
// sections. Given that it is ro, we will need an extra PT_LOAD. This
// complicates things for the dynamic linker and means we would have to reserve
// space for the extra PT_LOAD even if we end up not using it.
template <class ELFT>
static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
uint64_t offset, Symbol &sym, int64_t addend) {
void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset,
Symbol &sym, int64_t addend) const {
// If the relocation is known to be a link-time constant, we know no dynamic
// relocation will be created, pass the control to relocateAlloc() or
// relocateNonAlloc() to resolve it.
@ -1008,7 +1037,7 @@ static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
// -shared matches the spirit of its -z undefs default. -pie has freedom on
// choices, and we choose dynamic relocations to be consistent with the
// handling of GOT-generating relocations.
if (isStaticLinkTimeConstant(expr, type, sym, sec, offset) ||
if (isStaticLinkTimeConstant(expr, type, sym, offset) ||
(!config->isPic && sym.isUndefWeak())) {
sec.relocations.push_back({expr, type, offset, addend, &sym});
return;
@ -1016,13 +1045,13 @@ static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
bool canWrite = (sec.flags & SHF_WRITE) || !config->zText;
if (canWrite) {
RelType rel = target->getDynRel(type);
if (expr == R_GOT || (rel == target->symbolicRel && !sym.isPreemptible)) {
RelType rel = target.getDynRel(type);
if (expr == R_GOT || (rel == target.symbolicRel && !sym.isPreemptible)) {
addRelativeReloc(sec, offset, sym, addend, expr, type);
return;
} else if (rel != 0) {
if (config->emachine == EM_MIPS && rel == target->symbolicRel)
rel = target->relativeRel;
if (config->emachine == EM_MIPS && rel == target.symbolicRel)
rel = target.relativeRel;
sec.getPartition().relaDyn->addSymbolReloc(rel, sec, offset, sym, addend,
type);
@ -1260,9 +1289,7 @@ static unsigned handleTlsRelocation(RelType type, Symbol &sym,
return 0;
}
template <class ELFT, class RelTy>
static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
RelTy *end) {
template <class ELFT, class RelTy> void RelocationScanner::scanOne(RelTy *&i) {
const RelTy &rel = *i;
uint32_t symIndex = rel.getSymbol(config->isMips64EL);
Symbol &sym = sec.getFile<ELFT>()->getSymbol(symIndex);
@ -1270,14 +1297,14 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
// Deal with MIPS oddity.
if (config->mipsN32Abi) {
type = getMipsN32RelType(i, end);
type = getMipsN32RelType(i);
} else {
type = rel.getType(config->isMips64EL);
++i;
}
// Get an offset in an output section this relocation is applied to.
uint64_t offset = getOffset.get(rel.r_offset);
uint64_t offset = getter.get(rel.r_offset);
if (offset == uint64_t(-1))
return;
@ -1288,14 +1315,14 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
return;
const uint8_t *relocatedAddr = sec.data().begin() + rel.r_offset;
RelExpr expr = target->getRelExpr(type, sym, relocatedAddr);
RelExpr expr = target.getRelExpr(type, sym, relocatedAddr);
// Ignore R_*_NONE and other marker relocations.
if (expr == R_NONE)
return;
// Read an addend.
int64_t addend = computeAddend<ELFT>(rel, end, sec, expr, sym.isLocal());
int64_t addend = computeAddend<ELFT>(rel, expr, sym.isLocal());
if (config->emachine == EM_PPC64) {
// We can separate the small code model relocations into 2 categories:
@ -1344,7 +1371,7 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
}
// Process TLS relocations, including relaxing TLS relocations. Note that
// R_TPREL and R_TPREL_NEG relocations are resolved in processRelocAux.
// R_TPREL and R_TPREL_NEG relocations are resolved in processAux.
if (expr == R_TPREL || expr == R_TPREL_NEG) {
if (config->shared) {
errorOrWarn("relocation " + toString(type) + " against " + toString(sym) +
@ -1380,7 +1407,7 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
type == R_HEX_GD_PLT_B32_PCREL_X)))
expr = fromPlt(expr);
} else if (!isAbsoluteValue(sym)) {
expr = target->adjustGotPcExpr(type, addend, relocatedAddr);
expr = target.adjustGotPcExpr(type, addend, relocatedAddr);
}
}
@ -1411,7 +1438,7 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
sym.hasDirectReloc = true;
}
processRelocAux<ELFT>(sec, expr, type, offset, sym, addend);
processAux(expr, type, offset, sym, addend);
}
// R_PPC64_TLSGD/R_PPC64_TLSLD is required to mark `bl __tls_get_addr` for
@ -1452,9 +1479,7 @@ static void checkPPC64TLSRelax(InputSectionBase &sec, ArrayRef<RelTy> rels) {
}
template <class ELFT, class RelTy>
static void scanRelocs(InputSectionBase &sec, ArrayRef<RelTy> rels) {
OffsetGetter getOffset(sec);
void RelocationScanner::scan(ArrayRef<RelTy> rels) {
// Not all relocations end up in Sec.Relocations, but a lot do.
sec.relocations.reserve(rels.size());
@ -1468,8 +1493,9 @@ static void scanRelocs(InputSectionBase &sec, ArrayRef<RelTy> rels) {
if (isa<EhInputSection>(sec))
rels = sortRels(rels, storage);
for (auto i = rels.begin(), end = rels.end(); i != end;)
scanReloc<ELFT>(sec, getOffset, i, end);
end = static_cast<const void *>(rels.end());
for (auto i = rels.begin(); i != end;)
scanOne<ELFT>(i);
// Sort relocations by offset for more efficient searching for
// R_RISCV_PCREL_HI20 and R_PPC64_ADDR64.
@ -1482,11 +1508,12 @@ static void scanRelocs(InputSectionBase &sec, ArrayRef<RelTy> rels) {
}
template <class ELFT> void elf::scanRelocations(InputSectionBase &s) {
RelocationScanner scanner(s);
const RelsOrRelas<ELFT> rels = s.template relsOrRelas<ELFT>();
if (rels.areRelocsRel())
scanRelocs<ELFT>(s, rels.rels);
scanner.template scan<ELFT>(rels.rels);
else
scanRelocs<ELFT>(s, rels.relas);
scanner.template scan<ELFT>(rels.relas);
}
static bool handleNonPreemptibleIfunc(Symbol &sym) {