Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/Target/GNULDBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3938,6 +3938,12 @@ bool GNULDBackend::relax() {
if (hasError)
m_Module.setFailure(true);
}
if (LinkerConfig::Object != config().codeGenType()) {
if (!setupProgramHdrs()) {
m_Module.setFailure(true);
return false;
}
}
}

if (!config().getDiagEngine()->diagnose()) {
Expand Down
6 changes: 6 additions & 0 deletions lib/Target/RISCV/RISCVGOT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,9 @@ RISCVGOT *RISCVGOT::CreateIE(ELFSection *O, ResolveInfo *R, bool is32bit) {
return make<RISCVIEGOT<uint32_t, 4, 4>>(O, R);
return make<RISCVIEGOT<uint64_t, 8, 8>>(O, R);
}

RISCVGOT *RISCVGOT::CreateTLSDESC(ELFSection *O, ResolveInfo *R, bool is32bit) {
if (is32bit)
return make<RISCVTLSDESCGOT<uint32_t, 4, 4>>(O, R);
return make<RISCVTLSDESCGOT<uint64_t, 8, 8>>(O, R);
}
20 changes: 20 additions & 0 deletions lib/Target/RISCV/RISCVGOT.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class RISCVGOT : public GOT {
static RISCVGOT *CreateGD(ELFSection *O, ResolveInfo *R, bool is32bit);
static RISCVGOT *CreateLD(ELFSection *O, ResolveInfo *R, bool is32bit);
static RISCVGOT *CreateIE(ELFSection *O, ResolveInfo *R, bool is32bit);
static RISCVGOT *CreateTLSDESC(ELFSection *O, ResolveInfo *R, bool is32bit);
};

template <typename T, uint32_t Align, uint32_t Size>
Expand Down Expand Up @@ -150,6 +151,25 @@ class RISCVLDGOT : public RISCVTGOT<T, Align, Size> {
RISCVGOT *Other = nullptr;
};

template <typename T, uint32_t Align, uint32_t Size>
class RISCVTLSDESCGOT : public RISCVTGOT<T, Align, Size> {
public:
RISCVTLSDESCGOT(ELFSection *O, ResolveInfo *R)
: RISCVTGOT<T, Align, Size>(GOT::TLS_DESC, O, R),
Other(make<RISCVTGOT<T, Align, Size>>(GOT::TLS_DESC, O, R)) {}

RISCVGOT *getFirst() override { return this; }

RISCVGOT *getNext() override { return Other; }

static RISCVGOT *Create(ELFSection *O, ResolveInfo *R) {
return (make<RISCVTLSDESCGOT>(O, R));
}

private:
RISCVGOT *Other;
};

template <typename T, uint32_t Align, uint32_t Size>
class RISCVIEGOT : public RISCVTGOT<T, Align, Size> {
public:
Expand Down
157 changes: 148 additions & 9 deletions lib/Target/RISCV/RISCVLDBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "RISCVLLVMExtern.h"
#include "RISCVPLT.h"
#include "RISCVRelaxationStats.h"
#include "RISCVRelocationHelper.h"
#include "RISCVRelocationInternal.h"
#include "RISCVRelocator.h"
#include "RISCVStandaloneInfo.h"
Expand Down Expand Up @@ -674,6 +675,104 @@ bool RISCVLDBackend::doRelaxationQCLi(Relocation *reloc, Relocator::DWord G) {
return false;
}

bool RISCVLDBackend::doRelaxationTLSDESC(Relocation &R, bool Relax) {

Fragment *frag = R.targetRef()->frag();
RegionFragmentEx *region = llvm::dyn_cast<RegionFragmentEx>(frag);
if (!region)
return false;

const StringRef RelaxType = "RISCV_TLSDESC";
const ResolveInfo &Sym = *R.symInfo();
size_t offset = R.targetRef()->offset();

// In executables, TLSDESC relocations are either removed, when the
// instruction is replaced with a NOP, or replaced with one of a
// different type. The conditions and the instruction substitution rules are
// the same whether or not relaxation is enabled.
auto attempt = [&]() -> bool {
bool Relaxed = Relax && config().options().getRISCVRelax();
if (Relaxed)
relaxDeleteBytes(RelaxType, *region, offset, 4, Sym.name());
else {
// Otherwise, the instruction is replaced with a NOP.
reportMissedRelaxation(RelaxType, *region, offset, 4, Sym.name());
region->replaceInstruction(offset, &R, NOP, 4);
}
R.setType(llvm::ELF::R_RISCV_NONE);
return Relaxed;
};

// The first two instructions are always deleted.
if (R.type() == llvm::ELF::R_RISCV_TLSDESC_HI20 ||
R.type() == llvm::ELF::R_RISCV_TLSDESC_LOAD_LO12)
return attempt();

// We need the base relocation to get the symbol and the original addend.
const Relocation *BaseReloc = getBaseReloc(R);
if (!BaseReloc)
return false;

bool isLocalExec = !isSymbolPreemptible(*BaseReloc->symInfo());
if (isLocalExec) {
// We only need the symbol value to determine if it fits in 12 bits so we
// can use the short form. Hopefully, it will not increase later.
int64_t S = getRelocator()->getSymValue(BaseReloc);
int64_t A = BaseReloc->addend();
int64_t Value = S + A;
bool isShortForm = hi20(Value) == 0;
if (isShortForm) {
// LE short form is one ADDI instruction.
switch (R.type()) {
case llvm::ELF::R_RISCV_TLSDESC_ADD_LO12:
return attempt();
case llvm::ELF::R_RISCV_TLSDESC_CALL:
// addi a0, zero, %lo(S)
R.setTargetData(itype(ADDI, X_A0, X_ZERO, 0));
R.setType(llvm::ELF::R_RISCV_LO12_I);
break;
}
} else {
// LE long form is LUI + ADDI.
switch (R.type()) {
case llvm::ELF::R_RISCV_TLSDESC_ADD_LO12:
// lui a0, %hi(S)
R.setTargetData(utype(LUI, X_A0, 0));
R.setType(llvm::ELF::R_RISCV_HI20);
// Inherit the original addend from the instruction we have deleted.
R.setAddend(BaseReloc->addend());
break;
case llvm::ELF::R_RISCV_TLSDESC_CALL:
// addi a0, a0, %lo(S)
R.setTargetData(itype(ADDI, X_A0, X_A0, 0));
R.setType(llvm::ELF::R_RISCV_LO12_I);
break;
}
}
} else {
// Initial executable: AUIPC + GOT LW/LD:
switch (R.type()) {
case llvm::ELF::R_RISCV_TLSDESC_ADD_LO12:
// auipc a0, %got_pcrel_hi(S)
R.setTargetData(utype(AUIPC, X_A0, 0));
R.setType(llvm::ELF::R_RISCV_GOT_HI20);
// Inherit the original addend from the instruction we have deleted.
R.setAddend(BaseReloc->addend());
break;
case llvm::ELF::R_RISCV_TLSDESC_CALL:
// lw/ld a0, a0+%pcrel_lo(S)
// The correspondence between this relocation and the corresponding HI20
// stays the same even its type changes.
R.setTargetData(
itype(config().targets().is32Bits() ? LW : LD, X_A0, X_A0, 0));
R.setType(llvm::ELF::R_RISCV_PCREL_LO12_I);
break;
}
}

return true;
}

bool RISCVLDBackend::doRelaxationAlign(Relocation *pReloc) {
FragmentRef *Ref = pReloc->targetRef();
Fragment *frag = Ref->frag();
Expand Down Expand Up @@ -913,12 +1012,18 @@ enum RelaxationPass {
RELAXATION_CALL, // Must start at zero
RELAXATION_PC,
RELAXATION_LUI,
RELAXATION_TLSDESC,
RELAXATION_ALIGN,
RELAXATION_PASS_COUNT, // Number of passes
};

void RISCVLDBackend::mayBeRelax(int relaxation_pass, bool &pFinished) {
pFinished = true;

// TLSDESC relaxations only apply to executables.
if (relaxation_pass == RELAXATION_TLSDESC && !config().isBuildingExecutable())
return;

// RELAXATION_ALIGN pass, which is the last pass, will set pFinished to
// false if it has made changes. It is needed to call createProgramHdrs()
// again in the outer loop. Therefore, this function may be entered once more,
Expand Down Expand Up @@ -980,6 +1085,26 @@ void RISCVLDBackend::mayBeRelax(int relaxation_pass, bool &pFinished) {
doRelaxationLui(relocation, GP);
break;
}
case llvm::ELF::R_RISCV_TLSDESC_HI20:
case llvm::ELF::R_RISCV_TLSDESC_LOAD_LO12:
case llvm::ELF::R_RISCV_TLSDESC_ADD_LO12:
case llvm::ELF::R_RISCV_TLSDESC_CALL:
if (relaxation_pass == RELAXATION_TLSDESC) {
// In the TLSDESC relaxation sequence, only the instruction with
// R_RISCV_TLSDESC_HI20 can be marked with R_RISCV_RELAX to indicate
// that the whole sequence is relaxable. So the other three
// relocation types will inherit this knowledge from the
// R_RISCV_TLSDESC_HI20 relocation.
if (type != llvm::ELF::R_RISCV_TLSDESC_HI20)
if (const Relocation *HIReloc = getBaseReloc(*relocation))
nextRelax = rs->getLink()->findRelocation(
HIReloc->targetRef()->offset(), llvm::ELF::R_RISCV_RELAX);
// Note that doRelaxationTLSDESC is used for both optimizations and
// relaxations, therefore this function should be called regardless
// of whether relaxations are enabled.
doRelaxationTLSDESC(*relocation, nextRelax);
}
break;
case llvm::ELF::R_RISCV_ALIGN: {
if (relaxation_pass == RELAXATION_ALIGN)
if (doRelaxationAlign(relocation))
Expand Down Expand Up @@ -1145,12 +1270,20 @@ bool RISCVLDBackend::handleRelocation(ELFSection *pSection,
m_GroupRelocs.insert(std::make_pair(reloc, offsetToReloc->second));
return true;
}
// R_RISCV_PCREL_LO* relocations have the corresponding HI reloc as the
// syminfo, we need to find out the actual target by inspecting this reloc
// and set the appropriate relocation.
// R_RISCV_PCREL_LO* and TLSDESC relocations have the corresponding HI reloc
// as the syminfo, we need to find out the actual target by inspecting this
// reloc and set the appropriate relocation.
case llvm::ELF::R_RISCV_PCREL_LO12_I:
case llvm::ELF::R_RISCV_PCREL_LO12_S: {
Relocation *hi_reloc = findHIRelocation(pSection, pSym.value());
case llvm::ELF::R_RISCV_PCREL_LO12_S:
case llvm::ELF::R_RISCV_TLSDESC_LOAD_LO12:
case llvm::ELF::R_RISCV_TLSDESC_ADD_LO12:
case llvm::ELF::R_RISCV_TLSDESC_CALL: {
bool pcrel = pType == llvm::ELF::R_RISCV_PCREL_LO12_I ||
pType == llvm::ELF::R_RISCV_PCREL_LO12_S;
Relocation *hi_reloc =
pcrel ? findHIRelocation(pSection, pSym.value())
: pSection->findRelocation(pSym.value(),
llvm::ELF::R_RISCV_TLSDESC_HI20);
if (!hi_reloc && pLastVisit) {
config().raise(Diag::rv_hi20_not_found)
<< pSym.name() << getRISCVRelocName(pType)
Expand Down Expand Up @@ -1180,7 +1313,7 @@ bool RISCVLDBackend::handleRelocation(ELFSection *pSection,
reloc->setSymInfo(hi_reloc->symInfo());
pSection->addRelocation(reloc);
}
if (pLastVisit) {
if (pcrel && pLastVisit) {
// Disable GP Relaxation for this pair to mimic GNU
m_DisableGPRelocs.insert(reloc);
m_DisableGPRelocs.insert(hi_reloc);
Expand Down Expand Up @@ -1295,16 +1428,18 @@ bool RISCVLDBackend::handlePendingRelocations(ELFSection *section) {
return false;
}

if (m_PendingRelocations.empty())
return true;

for (auto &r : m_PendingRelocations)
if (!handleRelocation(std::get<0>(r), std::get<1>(r), *(std::get<2>(r)),
std::get<3>(r), std::get<4>(r), /*pLastVisit*/ true))
return false;

// Sort the relocation table, in offset order, since the pending relocations
// that got added at end of the relocation table may not be in offset order.
// Another reason to sort relocations is to make sure R_RISCV_RELAX are
// adjacent to the relocation they affect. The psABI is not clear if
// R_RISCV_RELAX must be adjacent, but using `.reloc` in assembly may generate
// those that are not. Note that because of this, this function must not have
// earlier non-error exists.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit : exits

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

std::stable_sort(section->getRelocations().begin(),
section->getRelocations().end(),
[](Relocation *A, Relocation *B) {
Expand Down Expand Up @@ -1482,6 +1617,10 @@ RISCVGOT *RISCVLDBackend::createGOT(GOT::GOTType T, ELFObjectFile *Obj,
case GOT::TLS_IE:
G = RISCVGOT::CreateIE(Obj->getGOT(), R, config().targets().is32Bits());
break;
case GOT::TLS_DESC:
G = RISCVGOT::CreateTLSDESC(Obj->getGOTPLT(), R,
config().targets().is32Bits());
break;
default:
assert(0);
break;
Expand Down
24 changes: 23 additions & 1 deletion lib/Target/RISCV/RISCVLDBackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@ class RISCVLDBackend : public GNULDBackend {
return DynRelocType::TPREL_LOCAL;
return DynRelocType::TPREL_GLOBAL;
}
if (X->type() == llvm::ELF::R_RISCV_TLSDESC) {
if (X->symInfo() && X->symInfo()->binding() == ResolveInfo::Local)
return DynRelocType::TLSDESC_LOCAL;
return DynRelocType::TLSDESC_GLOBAL;
}
return DynRelocType::DEFAULT;
}

Expand Down Expand Up @@ -185,6 +190,19 @@ class RISCVLDBackend : public GNULDBackend {
return reloc->second;
}

const Relocation *
getNewBaseForTLSDESCRelaxation(const Relocation &BaseReloc) const {
auto It = m_HiToIELoadBase.find(&BaseReloc);
if (It != m_HiToIELoadBase.end())
return It->second;
return nullptr;
}

void setNewBaseForTLSDESCRelaxation(const Relocation &R) {
const Relocation *HIReloc = getBaseReloc(R);
m_HiToIELoadBase[HIReloc] = &R;
}

// Get the value of the symbol, using the PLT slot if one exists.
Relocation::Address getSymbolValuePLT(const Relocation &R);

Expand Down Expand Up @@ -212,7 +230,7 @@ class RISCVLDBackend : public GNULDBackend {

bool doRelaxationLui(Relocation *R, Relocation::DWord G);
bool doRelaxationQCLi(Relocation *R, Relocation::DWord G);

bool doRelaxationTLSDESC(Relocation &reloc, bool Relax);
bool doRelaxationAlign(Relocation *R);

bool doRelaxationPC(Relocation *R, Relocation::DWord G);
Expand Down Expand Up @@ -290,6 +308,10 @@ class RISCVLDBackend : public GNULDBackend {
RISCVRelaxationStats *m_ModuleStats = nullptr;
std::unordered_map<ELFSection *, std::unordered_map<uint32_t, Relocation *>>
SectionRelocMap;

// A map from HI relocations to the relocations that should be used as a base
// address for the load instruction during TLSDESC to IE optimization.
std::unordered_map<const Relocation *, const Relocation *> m_HiToIELoadBase;
};
} // namespace eld

Expand Down
13 changes: 12 additions & 1 deletion lib/Target/RISCV/RISCVRelocationHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,34 @@
namespace {

enum Op {
ADDI = 0x13,
// Full instructions.
NOP = 0x13,

// U-type opcodes.
AUIPC = 0x17,
LUI = 0x37,

// I-type opcodes.
ADDI = 0x13,
JALR = 0x67,
LD = 0x3003,
LW = 0x2003,
SRLI = 0x5013,

// R-type opcodes.
SUB = 0x40000033,
};

enum Reg {
X_ZERO = 0,
X_RA = 1,
X_SP = 2,
X_GP = 3,
X_TP = 4,
X_T0 = 5,
X_T1 = 6,
X_T2 = 7,
X_A0 = 10,
X_T3 = 28
};

Expand Down
Loading
Loading