Skip to content

Commit

Permalink
[ELF] Make relocateAlloc target specific. NFC
Browse files Browse the repository at this point in the history
The target-specific code (AArch64, PPC64) does not fit into the generic code and
adds virtual function overhead. Move relocateAlloc into ELF/Arch/ instead. This
removes many virtual functions (relaxTls*). In addition, this helps get rid of
getRelocTargetVA dispatch and many RelExpr members in the future.
  • Loading branch information
MaskRay committed Oct 17, 2022
1 parent 5b9597f commit 685b212
Show file tree
Hide file tree
Showing 12 changed files with 322 additions and 268 deletions.
2 changes: 1 addition & 1 deletion lld/ELF/AArch64ErrataFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ void Patch843419Section::writeTo(uint8_t *buf) {
write32le(buf, read32le(patchee->rawData.begin() + patcheeOffset));

// Apply any relocation transferred from the original patchee section.
relocateAlloc(buf, buf + getSize());
target->relocateAlloc(*this, buf);

// Return address is the next instruction after the one we have just copied.
uint64_t s = getLDSTAddr() + 4;
Expand Down
2 changes: 1 addition & 1 deletion lld/ELF/ARMErrataFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ void Patch657417Section::writeTo(uint8_t *buf) {
write32le(buf, 0x9000f000);
// If we have a relocation then apply it.
if (!relocations.empty()) {
relocateAlloc(buf, buf + getSize());
target->relocateAlloc(*this, buf);
return;
}

Expand Down
80 changes: 64 additions & 16 deletions lld/ELF/Arch/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//

#include "OutputSections.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
Expand Down Expand Up @@ -48,12 +49,18 @@ class AArch64 : public TargetInfo {
void relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override;
void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override;

private:
void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void initRelaxer(ArrayRef<Relocation> relocs) const;
bool tryRelaxAdrpAdd(const Relocation &adrpRel, const Relocation &addRel,
uint64_t secAddr, uint8_t *buf) const;
bool tryRelaxAdrpLdr(const Relocation &adrpRel, const Relocation &ldrRel,
uint64_t secAddr, uint8_t *buf) const;
mutable bool safeToRelaxAdrpLdr = false;
};
} // namespace

Expand Down Expand Up @@ -587,11 +594,9 @@ void AArch64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
llvm_unreachable("invalid relocation for TLS IE to LE relaxation");
}

AArch64Relaxer::AArch64Relaxer(ArrayRef<Relocation> relocs) {
if (!config->relax || config->emachine != EM_AARCH64) {
safeToRelaxAdrpLdr = false;
void AArch64::initRelaxer(ArrayRef<Relocation> relocs) const {
if (!config->relax)
return;
}
// Check if R_AARCH64_ADR_GOT_PAGE and R_AARCH64_LD64_GOT_LO12_NC
// always appear in pairs.
size_t i = 0;
Expand All @@ -610,9 +615,9 @@ AArch64Relaxer::AArch64Relaxer(ArrayRef<Relocation> relocs) {
safeToRelaxAdrpLdr = i == size;
}

bool AArch64Relaxer::tryRelaxAdrpAdd(const Relocation &adrpRel,
const Relocation &addRel, uint64_t secAddr,
uint8_t *buf) const {
bool AArch64::tryRelaxAdrpAdd(const Relocation &adrpRel,
const Relocation &addRel, uint64_t secAddr,
uint8_t *buf) const {
// When the address of sym is within the range of ADR then
// we may relax
// ADRP xn, sym
Expand Down Expand Up @@ -659,9 +664,9 @@ bool AArch64Relaxer::tryRelaxAdrpAdd(const Relocation &adrpRel,
return true;
}

bool AArch64Relaxer::tryRelaxAdrpLdr(const Relocation &adrpRel,
const Relocation &ldrRel, uint64_t secAddr,
uint8_t *buf) const {
bool AArch64::tryRelaxAdrpLdr(const Relocation &adrpRel,
const Relocation &ldrRel, uint64_t secAddr,
uint8_t *buf) const {
if (!safeToRelaxAdrpLdr)
return false;

Expand Down Expand Up @@ -734,6 +739,49 @@ bool AArch64Relaxer::tryRelaxAdrpLdr(const Relocation &adrpRel,
return true;
}

void AArch64::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
uint64_t secAddr = sec.getOutputSection()->addr;
if (auto *s = dyn_cast<InputSection>(&sec))
secAddr += s->outSecOff;
initRelaxer(sec.relocations);
for (size_t i = 0, size = sec.relocations.size(); i != size; ++i) {
const Relocation &rel = sec.relocations[i];
uint8_t *loc = buf + rel.offset;
const uint64_t val =
sec.getRelocTargetVA(sec.file, rel.type, rel.addend,
secAddr + rel.offset, *rel.sym, rel.expr);
switch (rel.expr) {
case R_AARCH64_GOT_PAGE_PC:
if (i + 1 < size &&
tryRelaxAdrpLdr(rel, sec.relocations[i + 1], secAddr, buf)) {
++i;
continue;
}
break;
case R_AARCH64_PAGE_PC:
if (i + 1 < size &&
tryRelaxAdrpAdd(rel, sec.relocations[i + 1], secAddr, buf)) {
++i;
continue;
}
break;
case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC:
case R_RELAX_TLS_GD_TO_IE_ABS:
relaxTlsGdToIe(loc, rel, val);
continue;
case R_RELAX_TLS_GD_TO_LE:
relaxTlsGdToLe(loc, rel, val);
continue;
case R_RELAX_TLS_IE_TO_LE:
relaxTlsIeToLe(loc, rel, val);
continue;
default:
break;
}
relocate(loc, rel, val);
}
}

// AArch64 may use security features in variant PLT sequences. These are:
// Pointer Authentication (PAC), introduced in armv8.3-a and Branch Target
// Indicator (BTI) introduced in armv8.5-a. The additional instructions used
Expand Down
45 changes: 37 additions & 8 deletions lld/ELF/Arch/PPC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,13 @@ class PPC final : public TargetInfo {
uint64_t val) const override;
RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override;
int getTlsGdRelaxSkip(RelType type) const override;
void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override;

private:
void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
};
} // namespace

Expand Down Expand Up @@ -483,6 +482,36 @@ void PPC::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
}
}

void PPC::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
uint64_t secAddr = sec.getOutputSection()->addr;
if (auto *s = dyn_cast<InputSection>(&sec))
secAddr += s->outSecOff;
for (const Relocation &rel : sec.relocations) {
uint8_t *loc = buf + rel.offset;
const uint64_t val = SignExtend64(
sec.getRelocTargetVA(sec.file, rel.type, rel.addend,
secAddr + rel.offset, *rel.sym, rel.expr),
32);
switch (rel.expr) {
case R_RELAX_TLS_GD_TO_IE_GOT_OFF:
relaxTlsGdToIe(loc, rel, val);
break;
case R_RELAX_TLS_GD_TO_LE:
relaxTlsGdToLe(loc, rel, val);
break;
case R_RELAX_TLS_LD_TO_LE_ABS:
relaxTlsLdToLe(loc, rel, val);
break;
case R_RELAX_TLS_IE_TO_LE:
relaxTlsIeToLe(loc, rel, val);
break;
default:
relocate(loc, rel, val);
break;
}
}
}

TargetInfo *elf::getPPCTargetInfo() {
static PPC target;
return &target;
Expand Down
106 changes: 94 additions & 12 deletions lld/ELF/Arch/PPC64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "InputFiles.h"
#include "OutputSections.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
Expand Down Expand Up @@ -174,19 +175,17 @@ class PPC64 final : public TargetInfo {
RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override;
RelExpr adjustGotPcExpr(RelType type, int64_t addend,
const uint8_t *loc) const override;
void relaxGot(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override;

bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
uint8_t stOther) const override;

private:
void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
};
} // namespace

Expand Down Expand Up @@ -362,7 +361,8 @@ getRelaTocSymAndAddend(InputSectionBase *tocSec, uint64_t offset) {
// ld/lwa 3, 0(3) # load the value from the address
//
// Returns true if the relaxation is performed.
bool elf::tryRelaxPPC64TocIndirection(const Relocation &rel, uint8_t *bufLoc) {
static bool tryRelaxPPC64TocIndirection(const Relocation &rel,
uint8_t *bufLoc) {
assert(config->tocOptimize);
if (rel.addend < 0)
return false;
Expand Down Expand Up @@ -393,7 +393,8 @@ bool elf::tryRelaxPPC64TocIndirection(const Relocation &rel, uint8_t *bufLoc) {
return false;

// Add PPC64TocOffset that will be subtracted by PPC64::relocate().
target->relaxGot(bufLoc, rel, tocRelative + ppc64TocOffset);
static_cast<const PPC64 &>(*target).relaxGot(bufLoc, rel,
tocRelative + ppc64TocOffset);
return true;
}

Expand Down Expand Up @@ -1512,6 +1513,87 @@ void PPC64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
}
}

void PPC64::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
uint64_t secAddr = sec.getOutputSection()->addr;
if (auto *s = dyn_cast<InputSection>(&sec))
secAddr += s->outSecOff;
uint64_t lastPPCRelaxedRelocOff = -1;
for (const Relocation &rel : sec.relocations) {
uint8_t *loc = buf + rel.offset;
const uint64_t val =
sec.getRelocTargetVA(sec.file, rel.type, rel.addend,
secAddr + rel.offset, *rel.sym, rel.expr);
switch (rel.expr) {
case R_PPC64_RELAX_GOT_PC: {
// The R_PPC64_PCREL_OPT relocation must appear immediately after
// R_PPC64_GOT_PCREL34 in the relocations table at the same offset.
// We can only relax R_PPC64_PCREL_OPT if we have also relaxed
// the associated R_PPC64_GOT_PCREL34 since only the latter has an
// associated symbol. So save the offset when relaxing R_PPC64_GOT_PCREL34
// and only relax the other if the saved offset matches.
if (rel.type == R_PPC64_GOT_PCREL34)
lastPPCRelaxedRelocOff = rel.offset;
if (rel.type == R_PPC64_PCREL_OPT && rel.offset != lastPPCRelaxedRelocOff)
break;
relaxGot(loc, rel, val);
break;
}
case R_PPC64_RELAX_TOC:
// rel.sym refers to the STT_SECTION symbol associated to the .toc input
// section. If an R_PPC64_TOC16_LO (.toc + addend) references the TOC
// entry, there may be R_PPC64_TOC16_HA not paired with
// R_PPC64_TOC16_LO_DS. Don't relax. This loses some relaxation
// opportunities but is safe.
if (ppc64noTocRelax.count({rel.sym, rel.addend}) ||
!tryRelaxPPC64TocIndirection(rel, loc))
relocate(loc, rel, val);
break;
case R_PPC64_CALL:
// If this is a call to __tls_get_addr, it may be part of a TLS
// sequence that has been relaxed and turned into a nop. In this
// case, we don't want to handle it as a call.
if (read32(loc) == 0x60000000) // nop
break;

// Patch a nop (0x60000000) to a ld.
if (rel.sym->needsTocRestore) {
// gcc/gfortran 5.4, 6.3 and earlier versions do not add nop for
// recursive calls even if the function is preemptible. This is not
// wrong in the common case where the function is not preempted at
// runtime. Just ignore.
if ((rel.offset + 8 > sec.rawData.size() ||
read32(loc + 4) != 0x60000000) &&
rel.sym->file != sec.file) {
// Use substr(6) to remove the "__plt_" prefix.
errorOrWarn(getErrorLocation(loc) + "call to " +
lld::toString(*rel.sym).substr(6) +
" lacks nop, can't restore toc");
break;
}
write32(loc + 4, 0xe8410018); // ld %r2, 24(%r1)
}
relocate(loc, rel, val);
break;
case R_RELAX_TLS_GD_TO_IE:
case R_RELAX_TLS_GD_TO_IE_GOT_OFF:
relaxTlsGdToIe(loc, rel, val);
break;
case R_RELAX_TLS_GD_TO_LE:
relaxTlsGdToLe(loc, rel, val);
break;
case R_RELAX_TLS_LD_TO_LE_ABS:
relaxTlsLdToLe(loc, rel, val);
break;
case R_RELAX_TLS_IE_TO_LE:
relaxTlsIeToLe(loc, rel, val);
break;
default:
relocate(loc, rel, val);
break;
}
}
}

// The prologue for a split-stack function is expected to look roughly
// like this:
// .Lglobal_entry_point:
Expand Down
Loading

0 comments on commit 685b212

Please sign in to comment.