Skip to content
Draft
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
5 changes: 5 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,11 @@ def cheri_large_cap_table : Flag<["-"], "mxcaptable">, Flags<[NoXarchOption]>, G
def no_cheri_large_cap_table : Flag<["-"], "no-mxcaptable">, Flags<[NoXarchOption]>, Group<cheri_Group>,
HelpText<"Do not use large immediates to index the cap table">;

def cheri_tgot_tls : Flag<["-"], "cheri-tgot-tls">, Group<cheri_Group>,
HelpText<"Use TGOT for CHERI TLS">;
def no_cheri_tgot_tls : Flag<["-"], "no-cheri-tgot-tls">, Group<cheri_Group>,
HelpText<"Use traditional CHERI TLS">;

def cheri_uintcap_EQ : Joined<["-"], "cheri-uintcap=">, Flags<[NoXarchOption,CC1Option,HelpHidden]>, Group<cheri_Group>,
HelpText<"Whether to use the capability address or offset when performing arithmetic on capabilities."
"By default 'address' is used, 'offset' could theoretically be useful for copying GC but is usually "
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Basic/Targets/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ void RISCVTargetInfo::getTargetDefines(const LangOptions &Opts,
auto CapTableABI = llvm::MCTargetOptions::cheriCapabilityTableABI();
Builder.defineMacro("__CHERI_CAPABILITY_TABLE__",
Twine(((int)CapTableABI) + 1));

if (llvm::MCTargetOptions::cheriTLSUseTGOT())
Builder.defineMacro("__CHERI_TGOT_TLS__");
}

// Macros for use with the set and get permissions builtins.
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2166,6 +2166,12 @@ void Clang::AddRISCVTargetArgs(const ArgList &Args,
CmdArgs.push_back("-target-abi");
CmdArgs.push_back(ABIName.data());

if (Args.hasFlag(options::OPT_cheri_tgot_tls, options::OPT_no_cheri_tgot_tls,
false)) {
CmdArgs.push_back("-mllvm");
CmdArgs.push_back("-cheri-tgot-tls");
}

SetRISCVSmallDataLimit(getToolChain(), Args, CmdArgs);

if (!Args.hasFlag(options::OPT_mimplicit_float,
Expand Down Expand Up @@ -8118,6 +8124,12 @@ void ClangAs::AddRISCVTargetArgs(const ArgList &Args,
CmdArgs.push_back("-target-abi");
CmdArgs.push_back(ABIName.data());

if (Args.hasFlag(options::OPT_cheri_tgot_tls, options::OPT_no_cheri_tgot_tls,
false)) {
CmdArgs.push_back("-mllvm");
CmdArgs.push_back("-cheri-tgot-tls");
}

if (Args.hasFlag(options::OPT_mdefault_build_attributes,
options::OPT_mno_default_build_attributes, true)) {
CmdArgs.push_back("-mllvm");
Expand Down
12 changes: 8 additions & 4 deletions lld/ELF/Arch/Cheri.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,13 @@ static uint64_t getTargetSize(const CheriCapRelocLocation &location,
// For negative offsets use 0 instead (we want the range of the full symbol in that case)
int64_t offset = std::max((int64_t)0, target.offset);
uint64_t targetVA = targetSym->getVA(offset);
assert(targetVA >= os->addr);
uint64_t offsetInOS = targetVA - os->addr;
uint64_t osVA = os->addr;
// TLS symbol addresses are relative to the TLS segment. See getSymVA.
// Note that Out::tlsPhdr->firstSec must be valid since getVA succeeded.
if (def->isTls() && !config->relocatable)
osVA -= Out::tlsPhdr->firstSec->addr;
assert(targetVA >= osVA);
uint64_t offsetInOS = targetVA - osVA;
// Check this isn't a symbol defined outside a section in a linker script.
// Use less-or-equal here to account for __end_foo symbols which point 1 past the section
if (offsetInOS <= os->size) {
Expand Down Expand Up @@ -372,9 +377,8 @@ void CheriCapRelocsSection::writeToImpl(uint8_t *buf) {
if (isFunc) {
permissions |= CaptablePermissions<ELFT>::function;
} else if (os) {
assert(!isTls);
// if ((OS->getPhdrFlags() & PF_W) == 0) {
if (((os->flags & SHF_WRITE) == 0) || isRelroSection(os)) {
if ((os->flags & SHF_WRITE) == 0 || (!isTls && isRelroSection(os))) {
permissions |= CaptablePermissions<ELFT>::readOnly;
} else if (os->flags & SHF_EXECINSTR) {
warn("Non-function __cap_reloc against symbol in section with "
Expand Down
21 changes: 21 additions & 0 deletions lld/ELF/Arch/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ RISCV::RISCV() {
relativeRel = R_RISCV_RELATIVE;
iRelativeRel = R_RISCV_IRELATIVE;
cheriCapRel = R_RISCV_CHERI_CAPABILITY;
tgotRel = R_RISCV_CHERI_TLS_TGOT_SLOT;
tgotGotRel = R_RISCV_CHERI_TLS_TGOTREL;
if (config->is64) {
symbolicRel = R_RISCV_64;
tlsModuleIndexRel = R_RISCV_TLS_DTPMOD64;
Expand Down Expand Up @@ -350,10 +352,18 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s,
return R_RELAX_HINT;
case R_RISCV_TPREL_ADD:
case R_RISCV_CHERI_TPREL_CINCOFFSET:
case R_RISCV_CHERI_TLS_TGOT_ADD:
case R_RISCV_RELAX:
return config->relax ? R_RELAX_HINT : R_NONE;
case R_RISCV_CHERI_CAPABILITY:
return R_ABS_CAP;
case R_RISCV_CHERI_TLS_TGOT_HI20:
case R_RISCV_CHERI_TLS_TGOT_LO12_I:
return R_TGOT_TP;
case R_RISCV_CHERI_TLS_TGOT_GOT_HI20:
return R_TGOT_GOT_PC;
case R_RISCV_CHERI_TLS_TGOT_GD_HI20:
return R_TGOT_TLSGD_PC;
// TODO: Deprecate and eventually remove these
case R_RISCV_CHERI_CAPTAB_PCREL_HI20:
return R_GOT_PC;
Expand Down Expand Up @@ -473,6 +483,9 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
case R_RISCV_CHERI_CAPTAB_PCREL_HI20:
case R_RISCV_CHERI_TLS_IE_CAPTAB_PCREL_HI20:
case R_RISCV_CHERI_TLS_GD_CAPTAB_PCREL_HI20:
case R_RISCV_CHERI_TLS_TGOT_GOT_HI20:
case R_RISCV_CHERI_TLS_TGOT_GD_HI20:
case R_RISCV_CHERI_TLS_TGOT_HI20:
case R_RISCV_GOT_HI20:
case R_RISCV_PCREL_HI20:
case R_RISCV_TLS_GD_HI20:
Expand All @@ -485,6 +498,7 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
return;
}

case R_RISCV_CHERI_TLS_TGOT_LO12_I:
case R_RISCV_PCREL_LO12_I:
case R_RISCV_TPREL_LO12_I:
case R_RISCV_LO12_I: {
Expand Down Expand Up @@ -572,6 +586,13 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
write64le(loc, val - dtpOffset);
break;

case R_RISCV_CHERI_TLS_TGOTREL:
if (config->is64)
write64le(loc, val);
else
write32le(loc, val);
break;

case R_RISCV_RELAX:
return; // Ignored (for now)

Expand Down
2 changes: 2 additions & 0 deletions lld/ELF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3019,6 +3019,8 @@ void LinkerDriver::link(opt::InputArgList &args) {
combineEhSections();
if (in.capRelocs)
ctx.inputSections.push_back(in.capRelocs.get());
if (in.tgotCapRelocs)
ctx.inputSections.push_back(in.tgotCapRelocs.get());
}

// Merge .riscv.attributes sections.
Expand Down
22 changes: 21 additions & 1 deletion lld/ELF/InputSection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,9 @@ static Relocation *getRISCVPCRelHi20(const Symbol *sym, uint64_t addend) {
it->type == R_RISCV_TLS_GD_HI20 || it->type == R_RISCV_TLS_GOT_HI20 ||
it->type == R_RISCV_CHERI_CAPTAB_PCREL_HI20 ||
it->type == R_RISCV_CHERI_TLS_GD_CAPTAB_PCREL_HI20 ||
it->type == R_RISCV_CHERI_TLS_IE_CAPTAB_PCREL_HI20)
it->type == R_RISCV_CHERI_TLS_IE_CAPTAB_PCREL_HI20 ||
it->type == R_RISCV_CHERI_TLS_TGOT_GOT_HI20 ||
it->type == R_RISCV_CHERI_TLS_TGOT_GD_HI20)
return &*it;

errorOrWarn("R_RISCV_PCREL_LO12 relocation points to " +
Expand Down Expand Up @@ -651,6 +653,8 @@ static int64_t getTlsTpOffset(const Symbol &s) {
}
}

static int64_t getTlsTgotOffset(const Symbol &s) { return s.getTgotOffset(); }

uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
int64_t a, uint64_t p,
const Symbol &sym, RelExpr expr,
Expand Down Expand Up @@ -861,6 +865,22 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
return in.got->getTlsIndexOff() + a;
case R_TLSLD_PC:
return in.got->getTlsIndexVA() + a - p;
case R_TGOT:
return sym.getTgotOffset() + a;
case R_TGOT_TP:
case R_RELAX_TGOT_TLS_GD_TO_LE:
case R_RELAX_TGOT_TLS_IE_TO_LE:
return getTlsTgotOffset(sym) + a;
case R_TGOT_GOT:
case R_RELAX_TGOT_TLS_GD_TO_IE_ABS:
return in.got->getTgotAddr(sym) + a;
case R_TGOT_GOT_PC:
case R_RELAX_TGOT_TLS_GD_TO_IE:
return in.got->getTgotAddr(sym) + a - p;
case R_TGOT_TLSDESC:
return in.got->getTgotTlsDescAddr(sym) + a;
case R_TGOT_TLSGD_PC:
return in.got->getTgotGlobalDynAddr(sym) + a - p;
case R_ABS_CAP:
llvm_unreachable("R_ABS_CAP should not be handled here!");
case R_MIPS_CHERI_CAPTAB_INDEX:
Expand Down
139 changes: 124 additions & 15 deletions lld/ELF/Relocations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,35 @@ static void addTpOffsetGotEntry(Symbol &sym) {
target->tlsGotRel, *in.got, off, sym, target->symbolicRel);
}

static void addTgotEntry(Symbol &sym) {
in.tgot->addEntry(sym);
uint64_t off = sym.getTgotOffset();

// If preemptible, emit a TGOT_SLOT relocation with a symbol.
if (sym.isPreemptible) {
in.relaTgot->addReloc({target->tgotRel, in.tgot.get(), off,
DynamicReloc::AgainstSymbol, sym, 0, R_ADDEND});
return;
}

RelExpr expr = config->isCheriAbi ? R_ABS_CAP : R_ABS;
RelType type =
config->isCheriAbi ? *target->cheriCapRel : target->symbolicRel;

// Otherwise, the value is either an undef weak link-time constant or
// relative to this module's TLS block.
if (sym.isUndefWeak()) {
if (config->isCheriAbi)
addNullDerivedCapability(sym, *in.got, off, 0);
else
in.tgot->addConstant({expr, type, off, 0, &sym});
} else if (config->isCheriAbi && !config->useRelativeElfCheriRelocs)
in.tgotCapRelocs->addCapReloc({in.tgot.get(), off}, {&sym, 0}, 0);
else
in.relaTgot->addReloc(DynamicReloc::AddendOnlyWithTargetVA, target->tgotRel,
*in.tgot, off, sym, 0, expr, type);
}

// Return true if we can define a symbol in the executable that
// contains the value/function of a symbol defined in a shared
// library.
Expand Down Expand Up @@ -1016,6 +1045,10 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type,
if (e == R_GOT || e == R_PLT)
return target->usesOnlyLowPageBits(type) || !config->isPic;

// These never do, except if the output is an executable.
if (e == R_TGOT || e == R_TGOT_TP)
return !config->shared;

if (sym.isPreemptible)
return false;
if (!config->isPic)
Expand Down Expand Up @@ -1324,7 +1357,14 @@ static unsigned handleMipsTlsRelocation(RelType type, Symbol &sym,
static unsigned handleTlsRelocation(RelType type, Symbol &sym,
InputSectionBase &c, uint64_t offset,
int64_t addend, RelExpr expr) {
if (expr == R_TPREL || expr == R_TPREL_NEG) {
bool isTgot =
oneof<R_TGOT, R_TGOT_TP, R_TGOT_GOT, R_TGOT_GOT_PC, R_TGOT_TLSDESC,
R_TGOT_TLSDESC_CALL, R_TGOT_TLSGD_PC>(expr);

if (oneof<R_TPREL, R_TPREL_NEG, R_TGOT, R_TGOT_TP>(expr)) {
if (isTgot)
sym.setFlags(NEEDS_TGOT);

if (config->shared) {
errorOrWarn("relocation " + toString(type) + " against " + toString(sym) +
" cannot be used with -shared" + getLocation(c, sym, offset));
Expand All @@ -1336,11 +1376,17 @@ static unsigned handleTlsRelocation(RelType type, Symbol &sym,
if (config->emachine == EM_MIPS)
return handleMipsTlsRelocation(type, sym, c, offset, addend, expr);

if (isTgot)
sym.setFlags(NEEDS_TGOT);

if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC,
R_TLSDESC_GOTPLT>(expr) &&
R_TLSDESC_GOTPLT, R_TGOT_TLSDESC, R_TGOT_TLSDESC_CALL>(expr) &&
config->shared) {
if (expr != R_TLSDESC_CALL) {
sym.setFlags(NEEDS_TLSDESC);
if (!oneof<R_TLSDESC_CALL, R_TGOT_TLSDESC_CALL>(expr)) {
if (isTgot)
sym.setFlags(NEEDS_TGOT_TLSDESC);
else
sym.setFlags(NEEDS_TLSDESC);
c.addReloc({expr, type, offset, addend, &sym});
}
return 1;
Expand Down Expand Up @@ -1401,35 +1447,62 @@ static unsigned handleTlsRelocation(RelType type, Symbol &sym,

if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC,
R_TLSDESC_GOTPLT, R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC,
R_TGOT_TLSDESC, R_TGOT_TLSDESC_CALL, R_TGOT_TLSGD_PC,
R_LOONGARCH_TLSGD_PAGE_PC>(expr)) {
if (!toExecRelax) {
sym.setFlags(NEEDS_TLSGD);
if (isTgot)
sym.setFlags(NEEDS_TGOT_TLSGD);
else
sym.setFlags(NEEDS_TLSGD);
c.addReloc({expr, type, offset, addend, &sym});
return 1;
}

// Global-Dynamic relocs can be relaxed to Initial-Exec or Local-Exec
// depending on the symbol being locally defined or not.
if (sym.isPreemptible) {
sym.setFlags(NEEDS_TLSGD_TO_IE);
c.addReloc({target->adjustTlsExpr(type, R_RELAX_TLS_GD_TO_IE), type,
offset, addend, &sym});
// TGOT can always relax to Local-Exec for executables.
if (sym.isPreemptible && !isTgot) {
RelExpr relaxExpr;
if (isTgot) {
sym.setFlags(NEEDS_TGOT_GOT);
relaxExpr = R_RELAX_TGOT_TLS_GD_TO_IE;
} else {
sym.setFlags(NEEDS_TLSGD_TO_IE);
relaxExpr = R_RELAX_TLS_GD_TO_IE;
}
c.addReloc(
{target->adjustTlsExpr(type, relaxExpr), type, offset, addend, &sym});
} else {
c.addReloc({target->adjustTlsExpr(type, R_RELAX_TLS_GD_TO_LE), type,
offset, addend, &sym});
RelExpr relaxExpr;
if (isTgot)
relaxExpr = R_RELAX_TGOT_TLS_GD_TO_LE;
else
relaxExpr = R_RELAX_TLS_GD_TO_LE;
c.addReloc(
{target->adjustTlsExpr(type, relaxExpr), type, offset, addend, &sym});
}
return target->getTlsGdRelaxSkip(type);
}

if (oneof<R_GOT, R_GOTPLT, R_GOT_PC, R_AARCH64_GOT_PAGE_PC,
R_LOONGARCH_GOT_PAGE_PC, R_GOT_OFF, R_TLSIE_HINT>(expr)) {
R_LOONGARCH_GOT_PAGE_PC, R_GOT_OFF, R_TLSIE_HINT, R_TGOT_GOT,
R_TGOT_GOT_PC>(expr)) {
ctx.hasTlsIe.store(true, std::memory_order_relaxed);
// Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally
// defined.
if (toExecRelax && isLocalInExecutable) {
c.addReloc({R_RELAX_TLS_IE_TO_LE, type, offset, addend, &sym});
// TGOT can always relax Initial-Exec to Local-Exec for executables.
if (toExecRelax && (isLocalInExecutable || isTgot)) {
RelExpr relaxExpr;
if (isTgot)
relaxExpr = R_RELAX_TGOT_TLS_IE_TO_LE;
else
relaxExpr = R_RELAX_TLS_IE_TO_LE;
c.addReloc({relaxExpr, type, offset, addend, &sym});
} else if (expr != R_TLSIE_HINT) {
sym.setFlags(NEEDS_TLSIE);
if (isTgot)
sym.setFlags(NEEDS_TGOT_GOT);
else
sym.setFlags(NEEDS_TLSIE);
// R_GOT needs a relative relocation for PIC on i386 and Hexagon.
if (expr == R_GOT && config->isPic && !target->usesOnlyLowPageBits(type))
addRelativeReloc<true>(c, offset, sym, addend, expr, type);
Expand Down Expand Up @@ -1809,6 +1882,42 @@ void elf::postScanRelocations() {

if ((flags & NEEDS_TLSIE) && !(flags & NEEDS_TLSGD_TO_IE))
addTpOffsetGotEntry(sym);

if (flags & NEEDS_TGOT)
addTgotEntry(sym);

if (flags & NEEDS_TGOT_GOT) {
got->addTgotEntry(sym);
uint64_t off = got->getTgotOffset(sym);
if (!config->shared)
got->relocations.push_back(
{R_TGOT_TP, target->tgotGotRel, off, 0, &sym});
else
mainPart->relaDyn->addReloc(DynamicReloc::AddendOnlyWithTargetVA,
target->tgotGotRel, *got, off, sym, 0,
R_TGOT, target->tgotGotRel);
}

if (flags & NEEDS_TGOT_TLSDESC) {
got->addTgotTlsDescEntry(sym);
uint64_t off = got->getTgotTlsDescOffset(sym);
mainPart->relaDyn->addReloc(DynamicReloc::AddendOnlyWithTargetVA,
target->tgotTlsDescRel, *got, off, sym, 0,
R_TGOT, target->tgotTlsDescRel);
}

if (flags & NEEDS_TGOT_TLSGD) {
got->addTgotDynTlsEntry(sym);
uint64_t off = got->getTgotGlobalDynOffset(sym);
if (!config->shared)
// Write one to the GOT slot.
got->addConstant({R_ADDEND, target->symbolicRel, off, 1, &sym});
else
mainPart->relaDyn->addReloc({target->tlsModuleIndexRel, got, off});

uint64_t offsetOff = off + config->wordsize;
got->addConstant({R_TGOT, target->symbolicRel, offsetOff, 0, &sym});
}
};

GotSection *got = in.got.get();
Expand Down
Loading