From 17787b3129805889c1cf530d18ba2905767b466a Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Thu, 22 May 2025 21:37:50 +0100 Subject: [PATCH 01/12] [RISCV][MC] Keep .Lpcrel_hi symbols for CHERI AUIPCC pseudos in assembly Commit 1e3f4875f52c ("Fix build after merge") removed the CanBeUnnamed parameter since it no longer existed, but this is because users that set it to false were supposed to migrate to createNamedTempSymbol, as was done upstream in d9a0c40bce5f ("[MC] Split MCContext::createTempSymbol, default AlwaysAddSuffix to true, and add comments") for AUIPC pseudos. --- llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp index 9241d93b5a506..cd019691d4ff1 100644 --- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -4175,8 +4175,7 @@ void RISCVAsmParser::emitAuipccInstPair(MCOperand DestReg, MCOperand TmpReg, // OP DestReg, TmpReg, %pcrel_lo(TmpLabel) MCContext &Ctx = getContext(); - MCSymbol *TmpLabel = - Ctx.createTempSymbol("pcrel_hi", /* AlwaysAddSuffix=*/true); + MCSymbol *TmpLabel = Ctx.createNamedTempSymbol("pcrel_hi"); Out.emitLabel(TmpLabel); const MCExpr *SymbolHi = MCSpecifierExpr::create(Symbol, VKHi, Ctx); From a80d663ed73031f7f5f5ce22c55b68b719b456f5 Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Fri, 23 May 2025 01:34:22 +0100 Subject: [PATCH 02/12] [ELF][NFC] Fix mismerge in CheriCapTableSection This comment should have been dropped when rebasing the LLVM 17 merge to avoid this hack. Fixes: fb02ad76cea8 ("[ELF] Add InputSectionBase::{addRelocs,relocs} and GotSection::addConstant to add/access relocations") --- lld/ELF/Arch/Cheri.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lld/ELF/Arch/Cheri.cpp b/lld/ELF/Arch/Cheri.cpp index 72a862f9008c5..6f8d973684d8f 100644 --- a/lld/ELF/Arch/Cheri.cpp +++ b/lld/ELF/Arch/Cheri.cpp @@ -961,7 +961,6 @@ void CheriCapTableSection::assignValuesAndAddCapTableSymbols() { this->relocations.push_back( {R_TPREL, ctx.target->symbolicRel, offset, 0, s}); else - // FIXME: casting to GotSection here is a massive hack!! ctx.mainPart->relaDyn->addAddendOnlyRelocIfNonPreemptible( ctx.target->tlsGotRel, *this, offset, *s, ctx.target->symbolicRel); } From 7064460240ae8b94d22dc19982b337afb1ef0f9d Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Fri, 23 May 2025 22:12:12 +0100 Subject: [PATCH 03/12] [ELF][NFC] Add name constructor argument for CheriCapRelocsSection This mirrors RelocationBaseSection (and deriving classes) and will allow additional caprelocs sections to be added. --- lld/ELF/Arch/Cheri.cpp | 4 ++-- lld/ELF/Arch/Cheri.h | 2 +- lld/ELF/SyntheticSections.cpp | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lld/ELF/Arch/Cheri.cpp b/lld/ELF/Arch/Cheri.cpp index 6f8d973684d8f..f98b65519ee2a 100644 --- a/lld/ELF/Arch/Cheri.cpp +++ b/lld/ELF/Arch/Cheri.cpp @@ -43,8 +43,8 @@ struct InMemoryCapRelocEntry { CapRelocUint permissions; }; -CheriCapRelocsSection::CheriCapRelocsSection(Ctx &ctx) - : SyntheticSection(ctx, "__cap_relocs", SHT_PROGBITS, +CheriCapRelocsSection::CheriCapRelocsSection(Ctx &ctx, StringRef name) + : SyntheticSection(ctx, name, SHT_PROGBITS, (ctx.arg.isPic && !ctx.arg.relativeCapRelocsOnly) ? SHF_ALLOC | SHF_WRITE /* XXX: actually RELRO */ : SHF_ALLOC, diff --git a/lld/ELF/Arch/Cheri.h b/lld/ELF/Arch/Cheri.h index 20f31d54e1661..036f02e01f615 100644 --- a/lld/ELF/Arch/Cheri.h +++ b/lld/ELF/Arch/Cheri.h @@ -77,7 +77,7 @@ struct CheriCapReloc { // FIXME: should de-template this class properly class CheriCapRelocsSection : public SyntheticSection { public: - CheriCapRelocsSection(Ctx &ctx); + CheriCapRelocsSection(Ctx &ctx, StringRef name); // Add a __cap_relocs section from in input object file template void addSection(InputSectionBase *s); diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index 8ca05717afc5b..f2cf815fefe00 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -4867,7 +4867,8 @@ template void elf::createSyntheticSections(Ctx &ctx) { add(*ctx.in.bssRelRo); if (ctx.arg.capabilitySize > 0) { - ctx.in.capRelocs = std::make_unique(ctx); + ctx.in.capRelocs = + std::make_unique(ctx, "__cap_relocs"); ctx.in.cheriCapTable = std::make_unique(ctx); add(*ctx.in.cheriCapTable); if (ctx.arg.capTableScope != CapTableScopePolicy::All) { From b6463e982417dc2c733c147b01cec0dcc602f5e6 Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Fri, 23 May 2025 23:10:42 +0100 Subject: [PATCH 04/12] [ELF] Calculate config->isCheriAbi prior to constructing TargetInfo This allows the constructor to check config->isCheriAbi just as it can check config->is64. This also centralises the file compatibility checks, making them follow upstream's. --- lld/ELF/Arch/Cheri.cpp | 11 ++++++ lld/ELF/Arch/Mips.cpp | 13 ------- lld/ELF/Arch/MipsArchTree.cpp | 43 +++++++++--------------- lld/ELF/Arch/RISCV.cpp | 11 ------ lld/ELF/Config.h | 2 ++ lld/ELF/Driver.cpp | 36 +++++++++++++++++--- lld/ELF/InputFiles.cpp | 15 ++++++--- lld/ELF/Target.cpp | 7 ---- lld/ELF/Target.h | 1 - lld/ELF/Writer.h | 1 + lld/test/ELF/cheri/cheri-elf-flags-err.s | 14 ++++++-- lld/test/ELF/cheri/mix-abis-shlib.s | 4 +-- 12 files changed, 85 insertions(+), 73 deletions(-) diff --git a/lld/ELF/Arch/Cheri.cpp b/lld/ELF/Arch/Cheri.cpp index f98b65519ee2a..6bea63484e918 100644 --- a/lld/ELF/Arch/Cheri.cpp +++ b/lld/ELF/Arch/Cheri.cpp @@ -17,6 +17,17 @@ using namespace llvm::ELF; namespace lld { namespace elf { +bool isCheriAbi(const InputFile *f) { + switch (f->emachine) { + case EM_MIPS: + return (f->eflags & EF_MIPS_ABI) == EF_MIPS_ABI_CHERIABI; + case EM_RISCV: + return f->eflags & EF_RISCV_CHERIABI; + default: + return false; + } +} + bool hasDynamicLinker(Ctx &ctx) { return ctx.arg.shared || ctx.arg.pie || !ctx.sharedFiles.empty(); } diff --git a/lld/ELF/Arch/Mips.cpp b/lld/ELF/Arch/Mips.cpp index 090767de495a2..c99d104aea56e 100644 --- a/lld/ELF/Arch/Mips.cpp +++ b/lld/ELF/Arch/Mips.cpp @@ -23,7 +23,6 @@ template class MIPS final : public TargetInfo { public: MIPS(Ctx &); uint32_t calcEFlags() const override; - bool calcIsCheriAbi() const override; int getCapabilitySize() const override; RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; @@ -79,18 +78,6 @@ template uint32_t MIPS::calcEFlags() const { return calcMipsEFlags(ctx); } -template bool MIPS::calcIsCheriAbi() const { - bool isCheriAbi = (ctx.arg.eflags & EF_MIPS_ABI) == EF_MIPS_ABI_CHERIABI; - - if (ctx.arg.isCheriAbi && !ctx.objectFiles.empty() && !isCheriAbi) { - auto diag = Err(ctx); - diag << ctx.objectFiles.front() - << ": object file is non-CheriABI but emulation forces it"; - } - - return isCheriAbi; -} - template int MIPS::getCapabilitySize() const { // Compute the size of a CHERI capability based on the MIPS ABI flags: if ((ctx.arg.eflags & EF_MIPS_MACH) == EF_MIPS_MACH_CHERI128) diff --git a/lld/ELF/Arch/MipsArchTree.cpp b/lld/ELF/Arch/MipsArchTree.cpp index c6919380cb93a..4f648b093e8be 100644 --- a/lld/ELF/Arch/MipsArchTree.cpp +++ b/lld/ELF/Arch/MipsArchTree.cpp @@ -491,35 +491,24 @@ static Mips::AFL_EXT cheriFlagsToAFL_EXT(uint64_t cheriFlags) { return Mips::AFL_EXT::AFL_EXT_NONE; } -void elf::checkMipsShlibCompatible(Ctx &ctx, InputFile *f, uint64_t inputCheriFlags, +void elf::checkMipsShlibCompatible(Ctx &ctx, InputFile *f, + uint64_t inputCheriFlags, uint64_t targetCheriFlags) { - const uint32_t targetABI = ctx.arg.eflags & (EF_MIPS_ABI | EF_MIPS_ABI2); assert(f->emachine == ctx.arg.emachine); - uint32_t abi = f->eflags & (EF_MIPS_ABI | EF_MIPS_ABI2); - // Mips can't link against cheriabi and the other way around - if ((ctx.arg.isCheriAbi && abi != EF_MIPS_ABI_CHERIABI) || - (!ctx.arg.isCheriAbi && abi == EF_MIPS_ABI_CHERIABI)) { - // assert(errorCount() && "Should have already caused an errors"); - // llvm_unreachable("Should have been checked earlier!"); - if (!errorCount()) - error(toStr(ctx, f) + ": ABI '" + getAbiName(abi) + - "' is incompatible with target ABI: " + getAbiName(targetABI)); - } else { - uint64_t inputCheriAbi = inputCheriFlags & DF_MIPS_CHERI_ABI_MASK; - uint64_t targetCheriAbi = targetCheriFlags & DF_MIPS_CHERI_ABI_MASK; - if (inputCheriAbi != targetCheriAbi) { - std::string msg = "target pure-capability ABI " + - getMipsIsaExtName(cheriFlagsToAFL_EXT(targetCheriAbi)) + - " is incompatible with linked shared library\n>>> " + - toStr(ctx, f) + " uses " + - getMipsIsaExtName(cheriFlagsToAFL_EXT(inputCheriAbi)); - // mixing legacy/non-legacy is an error, anything a warning - if (inputCheriAbi == DF_MIPS_CHERI_ABI_LEGACY || - targetCheriAbi == DF_MIPS_CHERI_ABI_LEGACY) - error(msg); - else - warn(msg); - } + uint64_t inputCheriAbi = inputCheriFlags & DF_MIPS_CHERI_ABI_MASK; + uint64_t targetCheriAbi = targetCheriFlags & DF_MIPS_CHERI_ABI_MASK; + if (inputCheriAbi != targetCheriAbi) { + std::string msg = "target pure-capability ABI " + + getMipsIsaExtName(cheriFlagsToAFL_EXT(targetCheriAbi)) + + " is incompatible with linked shared library\n>>> " + + toStr(ctx, f) + " uses " + + getMipsIsaExtName(cheriFlagsToAFL_EXT(inputCheriAbi)); + // mixing legacy/non-legacy is an error, anything a warning + if (inputCheriAbi == DF_MIPS_CHERI_ABI_LEGACY || + targetCheriAbi == DF_MIPS_CHERI_ABI_LEGACY) + error(msg); + else + warn(msg); } } diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp index c5ee6a3f4e1f9..d66ca309bca30 100644 --- a/lld/ELF/Arch/RISCV.cpp +++ b/lld/ELF/Arch/RISCV.cpp @@ -34,7 +34,6 @@ class RISCV final : public TargetInfo { public: RISCV(Ctx &); uint32_t calcEFlags() const override; - bool calcIsCheriAbi() const override; int getCapabilitySize() const override; int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override; void writeGotHeader(uint8_t *buf) const override; @@ -205,16 +204,6 @@ uint32_t RISCV::calcEFlags() const { return target; } -bool RISCV::calcIsCheriAbi() const { - bool isCheriAbi = ctx.arg.eflags & EF_RISCV_CHERIABI; - - if (ctx.arg.isCheriAbi && !ctx.objectFiles.empty() && !isCheriAbi) - error(toStr(ctx, ctx.objectFiles.front()) + - ": object file is non-CheriABI but emulation forces it"); - - return isCheriAbi; -} - int64_t RISCV::getImplicitAddend(const uint8_t *buf, RelType type) const { switch (type) { default: diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 6a9de427b8597..41a661706038d 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -200,6 +200,8 @@ class LinkerDriver { Ctx &ctx; void createFiles(llvm::opt::InputArgList &args); void inferMachineType(); + void inferIsCheriAbi(); + void inferIsCheriot(); template void link(llvm::opt::InputArgList &args); template void compileBitcodeFiles(bool skipLinkedOutput); bool tryAddFatLTOFile(MemoryBufferRef mb, StringRef archiveName, diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index ec59278a7c400..d4fd12cd581b1 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -741,6 +741,8 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { return; inferMachineType(); + inferIsCheriAbi(); + inferIsCheriot(); setConfigs(ctx, args); checkOptions(ctx); if (errCount(ctx)) @@ -2330,6 +2332,36 @@ static uint64_t getMaxPageSize(Ctx &ctx, opt::InputArgList &args) { return val; } +// If -m did not force a CheriABI emulation, infer it from +// object files. +void LinkerDriver::inferIsCheriAbi() { + if (ctx.arg.isCheriAbi) + return; + + for (const auto &f : files) { + if (f->ekind == ELFNoneKind) + continue; + ctx.arg.isCheriAbi = isCheriAbi(f.get()); + return; + } +} + +// If -m did not force CHERIoT, infer it from +// object files. +void LinkerDriver::inferIsCheriot() { + if (ctx.arg.isCheriot) + return; + if (!ctx.arg.isCheriAbi) + return; + + for (const auto &f : files) { + if (f->ekind == ELFNoneKind) + continue; + ctx.arg.isCheriot = f->eflags & EF_RISCV_CHERIOT; + return; + } +} + // Parse -z common-page-size=. The default value is defined by // each target. static uint64_t getCommonPageSize(Ctx &ctx, opt::InputArgList &args) { @@ -3495,10 +3527,6 @@ template void LinkerDriver::link(opt::InputArgList &args) { setTarget(ctx); ctx.arg.eflags = ctx.target->calcEFlags(); - ctx.arg.isCheriAbi = ctx.target->calcIsCheriAbi(); - if (ctx.arg.isCheriAbi) - ctx.arg.isCheriot = ctx.arg.eflags & EF_RISCV_CHERIOT; - // maxPageSize (sometimes called abi page size) is the maximum page size that // the output can be run on. For example if the OS can use 4k or 64k page // sizes then maxPageSize must be 64k for the output to be useable on both. diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 69834959e72ac..32dd4151bf7f1 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -272,16 +272,21 @@ static bool isCompatible(Ctx &ctx, InputFile *file) { if (!file->isElf() && !isa(file)) return true; + bool onlyCheriAbi = false; if (file->ekind == ctx.arg.ekind && file->emachine == ctx.arg.emachine) { - if (ctx.arg.emachine != EM_MIPS) - return true; - if (isMipsN32Abi(ctx, *file) == ctx.arg.mipsN32Abi) - return true; + if (ctx.arg.emachine != EM_MIPS || + isMipsN32Abi(ctx, *file) == ctx.arg.mipsN32Abi) { + if (isCheriAbi(file) == ctx.arg.isCheriAbi) + return true; + onlyCheriAbi = true; + } } StringRef target = !ctx.arg.bfdname.empty() ? ctx.arg.bfdname : ctx.arg.emulation; - if (!target.empty()) { + // NB: Don't print the target for isCheriABI mismatches if it doesn't force + // it, since it's a valid target for both. + if (!target.empty() && (!onlyCheriAbi || target.contains("_cheri"))) { Err(ctx) << file << " is incompatible with " << target; return false; } diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp index 2d3ea59bd3854..ad7d57d30668d 100644 --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -117,13 +117,6 @@ ErrorPlace elf::getErrorPlace(Ctx &ctx, const uint8_t *loc) { TargetInfo::~TargetInfo() {} -bool TargetInfo::calcIsCheriAbi() const { - if (ctx.arg.isCheriAbi) - error("emulation forces CheriABI but not supported for the current target"); - - return false; -} - int64_t TargetInfo::getImplicitAddend(const uint8_t *buf, RelType type) const { InternalErr(ctx, buf) << "cannot read addend for relocation " << type; return 0; diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index 68c851294877f..45239d320f866 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -32,7 +32,6 @@ class TargetInfo { TargetInfo(Ctx &ctx) : ctx(ctx) {} virtual uint32_t calcEFlags() const { return 0; } virtual int getCapabilitySize() const { return 0; } - virtual bool calcIsCheriAbi() const; virtual RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const = 0; virtual RelType getDynRel(RelType type) const { return 0; } diff --git a/lld/ELF/Writer.h b/lld/ELF/Writer.h index a3695387722d3..c6f926b3f25dc 100644 --- a/lld/ELF/Writer.h +++ b/lld/ELF/Writer.h @@ -24,6 +24,7 @@ uint8_t getMipsIsaExt(uint64_t oldExt, llvm::StringRef oldFile, uint64_t newExt, llvm::StringRef newFile); void checkMipsShlibCompatible(Ctx &ctx, InputFile *f, uint64_t shlibCheriFlags, uint64_t targetCheriFlags); +bool isCheriAbi(const InputFile *f); bool isRelroSection(Ctx &ctx, const OutputSection *sec); } // namespace lld::elf diff --git a/lld/test/ELF/cheri/cheri-elf-flags-err.s b/lld/test/ELF/cheri/cheri-elf-flags-err.s index 01f5d2583855f..9d96081c033d5 100644 --- a/lld/test/ELF/cheri/cheri-elf-flags-err.s +++ b/lld/test/ELF/cheri/cheri-elf-flags-err.s @@ -12,7 +12,7 @@ # RUN: ld.lld %t-cheri128-hybrid-lib.o %t-cheri128-hybrid-main.o -o /dev/null # But with -melf64btsmip_cheri_fbsd the target ABI should be purecap (and therefore linking should fail)! # RUN: not ld.lld -melf64btsmip_cheri_fbsd %t-cheri128-hybrid-lib.o %t-cheri128-hybrid-main.o -o %t.exe 2>&1 | FileCheck -DCHERI_TYPE=cheri128 -check-prefix HYBRID-input-with-explicit-PURECAP %s -# HYBRID-input-with-explicit-PURECAP: error: {{.*}}-cheri128-hybrid-lib.o: object file is non-CheriABI but emulation forces it +# HYBRID-input-with-explicit-PURECAP: error: {{.*}}-cheri128-hybrid-lib.o is incompatible with elf64btsmip_cheri_fbsd # Similarly purecap input should work fine without a -m flag: # RUN: ld.lld %t-cheri128-lib.o %t-cheri128-main.o -o /dev/null @@ -94,8 +94,16 @@ __start: # CHERI128-HYBRID-FLAGS-NEXT: EF_MIPS_MACH_CHERI128 (0xC10000) # CHERI128-HYBRID-FLAGS-NEXT: ] -# CHERI128-vs-MIPS: {{.*}}/cheri-elf-flags-err.s.tmp-mips64.o: ABI 'n64' is incompatible with target ABI 'purecap' -# CHERI128-vs-CHERI128-HYBRID: {{.*}}/cheri-elf-flags-err.s.tmp-cheri128-hybrid-lib.o: ABI 'n64' is incompatible with target ABI 'purecap' +# CHERI256-vs-MIPS: {{.*}}/cheri-elf-flags-err.s.tmp-mips64.o is incompatible with {{.*}}/cheri-elf-flags-err.s.tmp-cheri256-main.o +# CHERI128-vs-MIPS: {{.*}}/cheri-elf-flags-err.s.tmp-mips64.o is incompatible with {{.*}}/cheri-elf-flags-err.s.tmp-cheri128-main.o +# CHERI256-vs-CHERI128: incompatible target ISA: +# CHERI256-vs-CHERI128-NEXT: {{.+}}-cheri256-main.o: mips4 (cheri256) +# CHERI256-vs-CHERI128-NEXT: {{.+}}-cheri128-lib.o: mips4 (cheri128) +# CHERI128-vs-CHERI256: incompatible target ISA: +# CHERI128-vs-CHERI256-NEXT: {{.+}}-cheri128-main.o: mips4 (cheri128) +# CHERI128-vs-CHERI256-NEXT: {{.+}}-cheri256-lib.o: mips4 (cheri256) +# CHERI256-vs-CHERI256-HYBRID: {{.*}}/cheri-elf-flags-err.s.tmp-cheri256-hybrid-lib.o is incompatible with {{.*}}/cheri-elf-flags-err.s.tmp-cheri256-main.o +# CHERI128-vs-CHERI128-HYBRID: {{.*}}/cheri-elf-flags-err.s.tmp-cheri128-hybrid-lib.o is incompatible with {{.*}}/cheri-elf-flags-err.s.tmp-cheri128-main.o # TODO: should those files actually link? # CHERI128-HYBRID-vs-MIPS: target ISA 'cheri128' is incompatible with 'mips64': {{.*}}/cheri-elf-flags-err.s.tmp-mips64.o diff --git a/lld/test/ELF/cheri/mix-abis-shlib.s b/lld/test/ELF/cheri/mix-abis-shlib.s index ee10a0cdd06e5..cc14ddc36de8a 100644 --- a/lld/test/ELF/cheri/mix-abis-shlib.s +++ b/lld/test/ELF/cheri/mix-abis-shlib.s @@ -50,9 +50,9 @@ # FNDESC: 0x000000007000C002 MIPS_CHERI_FLAGS ABI_FNDESC RELATIVE_CAPRELOCS # MIPS-NOT: MIPS_CHERI_FLAGS -# ERR-CHERIABI-LINKING-MIPS: ld.lld: error: {{.+}}mix-abis-shlib.s.tmp-lib-mips.so: ABI 'n64' is incompatible with target ABI: purecap +# ERR-CHERIABI-LINKING-MIPS: ld.lld: error: {{.+}}mix-abis-shlib.s.tmp-lib-mips.so is incompatible with {{.+}}mix-abis-shlib.s.tmp-{{.+}}.o # ERR-CHERIABI-LINKING-MIPS-EMPTY: -# ERR-MIPS-LINKING-CHERIABI: ld.lld: error: {{.+}}mix-abis-shlib.s.tmp-lib-{{.+}}.so: ABI 'purecap' is incompatible with target ABI: n64 +# ERR-MIPS-LINKING-CHERIABI: ld.lld: error: {{.+}}mix-abis-shlib.s.tmp-lib-{{.+}}.so is incompatible with {{.+}}mix-abis-shlib.s.tmp-mips.o # ERR-MIPS-LINKING-CHERIABI-EMPTY: # ERR-SHARED: ld.lld: error: target pure-capability ABI EXT_CHERI_ABI_[[IN_ABI]] is incompatible with linked shared library From 796014669769d3d2aad9d30f221a602ffdf17c1a Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Fri, 23 May 2025 23:34:39 +0100 Subject: [PATCH 05/12] [ELF][RISCV] Set correct gotEntrySize for purecap objects This is currently not used but will be in future commits. --- lld/ELF/Arch/RISCV.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp index d66ca309bca30..e04e9b39db475 100644 --- a/lld/ELF/Arch/RISCV.cpp +++ b/lld/ELF/Arch/RISCV.cpp @@ -148,6 +148,8 @@ RISCV::RISCV(Ctx &ctx) : TargetInfo(ctx) { // .got[0] = _DYNAMIC gotHeaderEntriesNum = 1; + if (ctx.arg.isCheriAbi) + gotEntrySize = getCapabilitySize(); // .got.plt[0] = _dl_runtime_resolve, .got.plt[1] = link_map gotPltHeaderEntriesNum = 2; From 1f70b587db9869a175a71e9507c688033f42107a Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Mon, 17 Apr 2023 13:14:04 -0700 Subject: [PATCH 06/12] [InstSimplify] Add a test for current get/set intrinsic folding --- .../InstSimplify/cheri-intrinsics-get-set.ll | 296 ++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll diff --git a/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll b/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll new file mode 100644 index 0000000000000..d3c55c2c61d74 --- /dev/null +++ b/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll @@ -0,0 +1,296 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes --force-update +;; Check folds for CHERI intrinsics that have both get and set variants +; RUN: sed 's/FIELD/address/' %s | opt -S -passes=instsimplify | FileCheck %s --check-prefixes=ADDRESS +; RUN: sed 's/FIELD/flags/' %s | opt -S -passes=instsimplify | FileCheck %s --check-prefixes=FLAGS +; RUN: sed 's/FIELD/high/' %s | opt -S -passes=instsimplify | FileCheck %s --check-prefixes=HIGH +; RUN: sed 's/FIELD/offset/' %s | opt -S -passes=instsimplify | FileCheck %s --check-prefixes=OFFSET +target datalayout = "pf200:128:128:128:64-A200-P200-G200" + +declare i64 @check_fold(i64) nounwind +declare i64 @llvm.cheri.cap.FIELD.get.i64(ptr addrspace(200)) nounwind +declare ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200), i64) nounwind + + +;; This is a no-op and should be folded to ret %arg +define ptr addrspace(200) @fold_set_of_get(ptr addrspace(200) %arg) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_set_of_get +; ADDRESS-SAME: (ptr addrspace(200) [[ARG:%.*]]) addrspace(200) #[[ATTR0:[0-9]+]] { +; ADDRESS-NEXT: ret ptr addrspace(200) [[ARG]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_set_of_get +; FLAGS-SAME: (ptr addrspace(200) [[ARG:%.*]]) addrspace(200) #[[ATTR0:[0-9]+]] { +; FLAGS-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.flags.get.i64(ptr addrspace(200) [[ARG]]) +; FLAGS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.flags.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; FLAGS-NEXT: ret ptr addrspace(200) [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@fold_set_of_get +; HIGH-SAME: (ptr addrspace(200) [[ARG:%.*]]) addrspace(200) #[[ATTR0:[0-9]+]] { +; HIGH-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(ptr addrspace(200) [[ARG]]) +; HIGH-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.high.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; HIGH-NEXT: ret ptr addrspace(200) [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_set_of_get +; OFFSET-SAME: (ptr addrspace(200) [[ARG:%.*]]) addrspace(200) #[[ATTR0:[0-9]+]] { +; OFFSET-NEXT: ret ptr addrspace(200) [[ARG]] +; + %value = tail call i64 @llvm.cheri.cap.FIELD.get.i64(ptr addrspace(200) %arg) + %ret = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %arg, i64 %value) + ret ptr addrspace(200) %ret +} + +;; negative test - get of a different value should not be folded to ret %arg +define ptr addrspace(200) @fold_set_of_get_bad(ptr addrspace(200) %arg, ptr addrspace(200) %other) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_set_of_get_bad +; ADDRESS-SAME: (ptr addrspace(200) [[ARG:%.*]], ptr addrspace(200) [[OTHER:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.address.get.i64(ptr addrspace(200) [[OTHER]]) +; ADDRESS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.address.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; ADDRESS-NEXT: ret ptr addrspace(200) [[RET]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_set_of_get_bad +; FLAGS-SAME: (ptr addrspace(200) [[ARG:%.*]], ptr addrspace(200) [[OTHER:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.flags.get.i64(ptr addrspace(200) [[OTHER]]) +; FLAGS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.flags.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; FLAGS-NEXT: ret ptr addrspace(200) [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@fold_set_of_get_bad +; HIGH-SAME: (ptr addrspace(200) [[ARG:%.*]], ptr addrspace(200) [[OTHER:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(ptr addrspace(200) [[OTHER]]) +; HIGH-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.high.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; HIGH-NEXT: ret ptr addrspace(200) [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_set_of_get_bad +; OFFSET-SAME: (ptr addrspace(200) [[ARG:%.*]], ptr addrspace(200) [[OTHER:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[VALUE:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[OTHER]]) +; OFFSET-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; OFFSET-NEXT: ret ptr addrspace(200) [[RET]] +; + %value = tail call i64 @llvm.cheri.cap.FIELD.get.i64(ptr addrspace(200) %other) + %ret = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %arg, i64 %value) + ret ptr addrspace(200) %ret +} + +;; For almost all intrinsics this get-of-set pair can be elided +;; NB: This is not true for flags where the value written by set could be truncated to another value +;; FIXME: high.get should be folded. +define i64 @fold_get_of_set(ptr addrspace(200) %arg, i64 %value) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_get_of_set +; ADDRESS-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: ret i64 [[VALUE]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_get_of_set +; FLAGS-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.flags.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; FLAGS-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.flags.get.i64(ptr addrspace(200) [[MODIFIED]]) +; FLAGS-NEXT: ret i64 [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@fold_get_of_set +; HIGH-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.high.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; HIGH-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(ptr addrspace(200) [[MODIFIED]]) +; HIGH-NEXT: ret i64 [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_get_of_set +; OFFSET-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: ret i64 [[VALUE]] +; + %modified = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %arg, i64 %value) + %ret = tail call i64 @llvm.cheri.cap.FIELD.get.i64(ptr addrspace(200) %modified) + ret i64 %ret +} + +;; Negative test -- get is used on other intrinsic +define i64 @fold_get_of_set_bad(ptr addrspace(200) %arg, ptr addrspace(200) %other, i64 %value) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_get_of_set_bad +; ADDRESS-SAME: (ptr addrspace(200) [[ARG:%.*]], ptr addrspace(200) [[OTHER:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.address.get.i64(ptr addrspace(200) [[ARG]]) +; ADDRESS-NEXT: ret i64 [[RET]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_get_of_set_bad +; FLAGS-SAME: (ptr addrspace(200) [[ARG:%.*]], ptr addrspace(200) [[OTHER:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.flags.get.i64(ptr addrspace(200) [[ARG]]) +; FLAGS-NEXT: ret i64 [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@fold_get_of_set_bad +; HIGH-SAME: (ptr addrspace(200) [[ARG:%.*]], ptr addrspace(200) [[OTHER:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(ptr addrspace(200) [[ARG]]) +; HIGH-NEXT: ret i64 [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_get_of_set_bad +; OFFSET-SAME: (ptr addrspace(200) [[ARG:%.*]], ptr addrspace(200) [[OTHER:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[ARG]]) +; OFFSET-NEXT: ret i64 [[RET]] +; + %modified = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %other, i64 %value) + %ret = tail call i64 @llvm.cheri.cap.FIELD.get.i64(ptr addrspace(200) %arg) + ret i64 %ret +} + +define i64 @fold_get_on_null() nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_get_on_null +; ADDRESS-SAME: () addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: ret i64 0 +; +; FLAGS-LABEL: define {{[^@]+}}@fold_get_on_null +; FLAGS-SAME: () addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: ret i64 0 +; +; HIGH-LABEL: define {{[^@]+}}@fold_get_on_null +; HIGH-SAME: () addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(ptr addrspace(200) null) +; HIGH-NEXT: ret i64 [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_get_on_null +; OFFSET-SAME: () addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: ret i64 0 +; + %ret = tail call i64 @llvm.cheri.cap.FIELD.get.i64(ptr addrspace(200) null) + ret i64 %ret +} + +define i64 @fold_get_on_null_with_gep(i64 %value, i64 %gepoff) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_get_on_null_with_gep +; ADDRESS-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: ret i64 [[GEPOFF]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_get_on_null_with_gep +; FLAGS-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: ret i64 0 +; +; HIGH-LABEL: define {{[^@]+}}@fold_get_on_null_with_gep +; HIGH-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[TMP:%.*]] = getelementptr i8, ptr addrspace(200) null, i64 [[GEPOFF]] +; HIGH-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(ptr addrspace(200) [[TMP]]) +; HIGH-NEXT: ret i64 [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_get_on_null_with_gep +; OFFSET-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: ret i64 [[GEPOFF]] +; + %tmp = getelementptr i8, ptr addrspace(200) null, i64 %gepoff + %ret = tail call i64 @llvm.cheri.cap.FIELD.get.i64(ptr addrspace(200) %tmp) + ret i64 %ret +} + +;; TODO: Setting is idempotent, so the first call should be elided +define ptr addrspace(200) @set_idempotent(ptr addrspace(200) %arg, i64 %value) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@set_idempotent +; ADDRESS-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.address.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; ADDRESS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.address.set.i64(ptr addrspace(200) [[MODIFIED]], i64 [[VALUE]]) +; ADDRESS-NEXT: ret ptr addrspace(200) [[RET]] +; +; FLAGS-LABEL: define {{[^@]+}}@set_idempotent +; FLAGS-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.flags.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; FLAGS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.flags.set.i64(ptr addrspace(200) [[MODIFIED]], i64 [[VALUE]]) +; FLAGS-NEXT: ret ptr addrspace(200) [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@set_idempotent +; HIGH-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.high.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; HIGH-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.high.set.i64(ptr addrspace(200) [[MODIFIED]], i64 [[VALUE]]) +; HIGH-NEXT: ret ptr addrspace(200) [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@set_idempotent +; OFFSET-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; OFFSET-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[MODIFIED]], i64 [[VALUE]]) +; OFFSET-NEXT: ret ptr addrspace(200) [[RET]] +; + %modified = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %arg, i64 %value) + %ret = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %modified, i64 %value) + ret ptr addrspace(200) %ret +} + + +;; Negative test - calls with the same base argument are not yet elided if the +;; second argument is different since they could have side-effects such as +;; clearing the tag. For example `setoffset(setoffset(x, ), )` +;; would result in an untagged value if the first setoffset went outside the +;; representable range but that detagging would not happen if we elide it. +;; TODO: Maybe we should do this fold since people should not be relying on +;; representability to clear tags of capabilities. +define ptr addrspace(200) @set_different_values(ptr addrspace(200) %arg, i64 %value, i64 %value2) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@set_different_values +; ADDRESS-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]], i64 [[VALUE2:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.address.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; ADDRESS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.address.set.i64(ptr addrspace(200) [[MODIFIED]], i64 [[VALUE]]) +; ADDRESS-NEXT: ret ptr addrspace(200) [[RET]] +; +; FLAGS-LABEL: define {{[^@]+}}@set_different_values +; FLAGS-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]], i64 [[VALUE2:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.flags.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; FLAGS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.flags.set.i64(ptr addrspace(200) [[MODIFIED]], i64 [[VALUE]]) +; FLAGS-NEXT: ret ptr addrspace(200) [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@set_different_values +; HIGH-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]], i64 [[VALUE2:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.high.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; HIGH-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.high.set.i64(ptr addrspace(200) [[MODIFIED]], i64 [[VALUE]]) +; HIGH-NEXT: ret ptr addrspace(200) [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@set_different_values +; OFFSET-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]], i64 [[VALUE2:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; OFFSET-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[MODIFIED]], i64 [[VALUE]]) +; OFFSET-NEXT: ret ptr addrspace(200) [[RET]] +; + %modified = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %arg, i64 %value) + %ret = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %modified, i64 %value) + ret ptr addrspace(200) %ret +} + +define ptr addrspace(200) @fold_set_on_null(i64 %value) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_set_on_null +; ADDRESS-SAME: (i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.address.set.i64(ptr addrspace(200) null, i64 [[VALUE]]) +; ADDRESS-NEXT: ret ptr addrspace(200) [[RET]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_set_on_null +; FLAGS-SAME: (i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.flags.set.i64(ptr addrspace(200) null, i64 [[VALUE]]) +; FLAGS-NEXT: ret ptr addrspace(200) [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@fold_set_on_null +; HIGH-SAME: (i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.high.set.i64(ptr addrspace(200) null, i64 [[VALUE]]) +; HIGH-NEXT: ret ptr addrspace(200) [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_set_on_null +; OFFSET-SAME: (i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) null, i64 [[VALUE]]) +; OFFSET-NEXT: ret ptr addrspace(200) [[RET]] +; + %ret = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) null, i64 %value) + ret ptr addrspace(200) %ret +} + +define ptr addrspace(200) @fold_set_on_null_with_gep(i64 %value, i64 %gepoff) nounwind { +; ADDRESS-LABEL: define {{[^@]+}}@fold_set_on_null_with_gep +; ADDRESS-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; ADDRESS-NEXT: [[TMP:%.*]] = getelementptr i8, ptr addrspace(200) null, i64 [[GEPOFF]] +; ADDRESS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.address.set.i64(ptr addrspace(200) [[TMP]], i64 [[VALUE]]) +; ADDRESS-NEXT: ret ptr addrspace(200) [[RET]] +; +; FLAGS-LABEL: define {{[^@]+}}@fold_set_on_null_with_gep +; FLAGS-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; FLAGS-NEXT: [[TMP:%.*]] = getelementptr i8, ptr addrspace(200) null, i64 [[GEPOFF]] +; FLAGS-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.flags.set.i64(ptr addrspace(200) [[TMP]], i64 [[VALUE]]) +; FLAGS-NEXT: ret ptr addrspace(200) [[RET]] +; +; HIGH-LABEL: define {{[^@]+}}@fold_set_on_null_with_gep +; HIGH-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; HIGH-NEXT: [[TMP:%.*]] = getelementptr i8, ptr addrspace(200) null, i64 [[GEPOFF]] +; HIGH-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.high.set.i64(ptr addrspace(200) [[TMP]], i64 [[VALUE]]) +; HIGH-NEXT: ret ptr addrspace(200) [[RET]] +; +; OFFSET-LABEL: define {{[^@]+}}@fold_set_on_null_with_gep +; OFFSET-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { +; OFFSET-NEXT: [[TMP:%.*]] = getelementptr i8, ptr addrspace(200) null, i64 [[GEPOFF]] +; OFFSET-NEXT: [[RET:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[TMP]], i64 [[VALUE]]) +; OFFSET-NEXT: ret ptr addrspace(200) [[RET]] +; + %tmp = getelementptr i8, ptr addrspace(200) null, i64 %gepoff + %ret = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %tmp, i64 %value) + ret ptr addrspace(200) %ret +} From 9982278ab943c893d518adfc78fd903c3692fa05 Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Fri, 12 May 2023 11:01:13 -0700 Subject: [PATCH 07/12] [InstSimplify] Simplify getoffset/getaddr folding. NFC Now that we depend on C++14, we can use switches in constexpr functions and no longer need to pass two template parameters. --- llvm/include/llvm/IR/CheriIntrinsics.h | 6 ++++-- llvm/lib/Analysis/InstructionSimplify.cpp | 23 ++++++----------------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/llvm/include/llvm/IR/CheriIntrinsics.h b/llvm/include/llvm/IR/CheriIntrinsics.h index caa292981a72c..7edd61df9d0e1 100644 --- a/llvm/include/llvm/IR/CheriIntrinsics.h +++ b/llvm/include/llvm/IR/CheriIntrinsics.h @@ -21,7 +21,8 @@ namespace llvm { namespace cheri { -inline Intrinsic::ID correspondingSetIntrinsic(Intrinsic::ID GetIntrin) { +inline constexpr Intrinsic::ID +correspondingSetIntrinsic(Intrinsic::ID GetIntrin) { switch (GetIntrin) { case Intrinsic::cheri_cap_offset_get: return Intrinsic::cheri_cap_offset_set; @@ -33,7 +34,8 @@ inline Intrinsic::ID correspondingSetIntrinsic(Intrinsic::ID GetIntrin) { llvm_unreachable("No matching set intrinsic"); } } -inline Intrinsic::ID correspondingGetIntrinsic(Intrinsic::ID SetIntrin) { +inline constexpr Intrinsic::ID +correspondingGetIntrinsic(Intrinsic::ID SetIntrin) { switch (SetIntrin) { case Intrinsic::cheri_cap_offset_set: return Intrinsic::cheri_cap_offset_get; diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp index c1e7dd98fa5f3..f356798999516 100644 --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -6366,9 +6366,10 @@ stripAndAccumulateGEPsAndPointerCastsSameRepr(Value *V, const DataLayout &DL, return Result; } -template +template static Value *inferCapabilityOffsetOrAddr(Value *V, Type *ResultTy, const DataLayout &DL) { + constexpr Intrinsic::ID SetIntrin = cheri::correspondingSetIntrinsic(Intrin); // Try to infer the offset/address from a prior setoffset/setaddr value Value *IntrinArg = nullptr; // getaddr(setaddr(A, B)) -> B and getoffset(setoffset(A, B)) -> B @@ -6427,20 +6428,6 @@ static Value *inferCapabilityOffsetOrAddr(Value *V, Type *ResultTy, return nullptr; } -Value *simplifyCapabilityGetOffset(Value *V, Type *ResultTy, - const DataLayout &DL) { - return inferCapabilityOffsetOrAddr( - V, ResultTy, DL); -} - -Value *simplifyCapabilityGetAddress(Value *V, Type *ResultTy, - const DataLayout &DL) { - return inferCapabilityOffsetOrAddr( - V, ResultTy, DL); -} - static Value *simplifyUnaryIntrinsic(Function *F, Value *Op0, const SimplifyQuery &Q, const CallBase *Call) { @@ -6519,11 +6506,13 @@ static Value *simplifyUnaryIntrinsic(Function *F, Value *Op0, case Intrinsic::cheri_cap_length_get: break; case Intrinsic::cheri_cap_address_get: - if (Value *V = simplifyCapabilityGetAddress(Op0, F->getReturnType(), Q.DL)) + if (auto *V = inferCapabilityOffsetOrAddr( + Op0, F->getReturnType(), Q.DL)) return V; break; case Intrinsic::cheri_cap_offset_get: - if (Value *V = simplifyCapabilityGetOffset(Op0, F->getReturnType(), Q.DL)) + if (auto *V = inferCapabilityOffsetOrAddr( + Op0, F->getReturnType(), Q.DL)) return V; break; From a1583d6b07e31ee4a4b985034d600c4be2365bcd Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Wed, 28 May 2025 13:06:17 -0700 Subject: [PATCH 08/12] [InstSimplify] Simplify @llvm.cheri.cap.high.get As part of this refactor some of the CHERI get intrinsics folding code. This refactoring introduces a minor change to getoffset folding as well: we no longer fold arbitrary getoffset(setoffset(A, B)) -> B since the setoffset could have changed the bounds interpretation and therefore also the result. This optimization is unlikely to make a difference in practice and is somewhat incorrect so I believe dropping it makes sense. --- clang/test/CodeGen/cheri/vaddr-mode.c | 12 +++- llvm/include/llvm/IR/CheriIntrinsics.h | 4 ++ llvm/lib/Analysis/InstructionSimplify.cpp | 61 ++++++++++++++----- ...insics-folding-broken-module-regression.ll | 2 - ...insics-folding-broken-module-regression.ll | 7 +-- ...insics-folding-broken-module-regression.ll | 7 +-- ...insics-folding-broken-module-regression.ll | 7 +-- .../InstSimplify/cheri-intrinsics-get-set.ll | 15 ++--- 8 files changed, 68 insertions(+), 47 deletions(-) diff --git a/clang/test/CodeGen/cheri/vaddr-mode.c b/clang/test/CodeGen/cheri/vaddr-mode.c index 9293f60a2e209..a1f61e8236f02 100644 --- a/clang/test/CodeGen/cheri/vaddr-mode.c +++ b/clang/test/CodeGen/cheri/vaddr-mode.c @@ -48,7 +48,9 @@ long write_uintcap(__uintcap_t *cap) { // OFFSET-NEXT: entry: // OFFSET-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[CAP:%.*]]) // OFFSET-NEXT: [[AND:%.*]] = and i64 [[TMP0]], 3 -// OFFSET-NEXT: ret i64 [[AND]] +// OFFSET-NEXT: [[TMP1:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[CAP]], i64 [[AND]]) +// OFFSET-NEXT: [[TMP2:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[TMP1]]) +// OFFSET-NEXT: ret i64 [[TMP2]] // long get_low_bits(__uintcap_t cap) { return cap & 3; @@ -126,7 +128,9 @@ __uintcap_t xor_uintcap(__uintcap_t cap, __uintcap_t cap2) { // OFFSET-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[CAP:%.*]]) // OFFSET-NEXT: [[TMP1:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[CAP2:%.*]]) // OFFSET-NEXT: [[XOR:%.*]] = xor i64 [[TMP1]], [[TMP0]] -// OFFSET-NEXT: ret i64 [[XOR]] +// OFFSET-NEXT: [[TMP2:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[CAP]], i64 [[XOR]]) +// OFFSET-NEXT: [[TMP3:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[TMP2]]) +// OFFSET-NEXT: ret i64 [[TMP3]] // long xor_uintcap_return_long(__uintcap_t cap, __uintcap_t cap2) { return cap ^ cap2; @@ -160,7 +164,9 @@ __uintcap_t modulo_return_uintcap(__uintcap_t cap) { // OFFSET-NEXT: entry: // OFFSET-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[CAP:%.*]]) // OFFSET-NEXT: [[REM:%.*]] = and i64 [[TMP0]], 31 -// OFFSET-NEXT: ret i64 [[REM]] +// OFFSET-NEXT: [[TMP1:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[CAP]], i64 [[REM]]) +// OFFSET-NEXT: [[TMP2:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[TMP1]]) +// OFFSET-NEXT: ret i64 [[TMP2]] // long modulo_return_long(__uintcap_t cap) { return cap % 32; diff --git a/llvm/include/llvm/IR/CheriIntrinsics.h b/llvm/include/llvm/IR/CheriIntrinsics.h index 7edd61df9d0e1..06f776f1af26b 100644 --- a/llvm/include/llvm/IR/CheriIntrinsics.h +++ b/llvm/include/llvm/IR/CheriIntrinsics.h @@ -30,6 +30,8 @@ correspondingSetIntrinsic(Intrinsic::ID GetIntrin) { return Intrinsic::cheri_cap_address_set; case Intrinsic::cheri_cap_flags_get: return Intrinsic::cheri_cap_flags_set; + case Intrinsic::cheri_cap_high_get: + return Intrinsic::cheri_cap_high_set; default: llvm_unreachable("No matching set intrinsic"); } @@ -43,6 +45,8 @@ correspondingGetIntrinsic(Intrinsic::ID SetIntrin) { return Intrinsic::cheri_cap_address_get; case Intrinsic::cheri_cap_flags_set: return Intrinsic::cheri_cap_flags_get; + case Intrinsic::cheri_cap_high_set: + return Intrinsic::cheri_cap_high_get; default: llvm_unreachable("No matching get intrinsic"); } diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp index f356798999516..3462c01337cd6 100644 --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -6366,26 +6366,45 @@ stripAndAccumulateGEPsAndPointerCastsSameRepr(Value *V, const DataLayout &DL, return Result; } +// Common helper to fold llvm.cheri.get.foo intrinsics template -static Value *inferCapabilityOffsetOrAddr(Value *V, Type *ResultTy, - const DataLayout &DL) { +static Value *foldCheriGetSetPair(Value *V, Type *ResultTy, + const DataLayout &DL) { constexpr Intrinsic::ID SetIntrin = cheri::correspondingSetIntrinsic(Intrin); - // Try to infer the offset/address from a prior setoffset/setaddr value + // getFoo(setFoo(A, B)) -> B + // This is true for address and high, but not necessarily true for flags and + // offset since setflags truncates the value and setoffset may change the + // bounds representation (and thus the offset) if it is significantly OOB. + constexpr bool SetUpdatesAllGetBits = + SetIntrin != Intrinsic::cheri_cap_offset_set && + SetIntrin != Intrinsic::cheri_cap_flags_set; Value *IntrinArg = nullptr; - // getaddr(setaddr(A, B)) -> B and getoffset(setoffset(A, B)) -> B - if (match(V, m_Intrinsic(m_Value(), m_Value(IntrinArg)))) { - return IntrinArg; - } - // get{addr,offset}(setaddr(NULL, B)) -> B - if (match(V, m_Intrinsic( - m_Zero(), m_Value(IntrinArg)))) { + if (SetUpdatesAllGetBits && + match(V, m_Intrinsic(m_Value(), m_Value(IntrinArg)))) { return IntrinArg; } - // get{addr,offset}(setoffset(NULL, B)) -> B - if (match(V, m_Intrinsic( - m_Zero(), m_Value(IntrinArg)))) { - return IntrinArg; + // All intrinsics we call this function for return zero for NULL inputs. + // In fact, we can extend this to any input derived from NULL where the only + // operations have been address manipulations (unless we are retrieving a + // result depending on the address), since those cannot affect non-bounds + // capability metadata. + constexpr bool CanIgnoreAddressManipulation = + Intrin != Intrinsic::cheri_cap_address_get && + Intrin != Intrinsic::cheri_cap_offset_get; + Value *Base = CanIgnoreAddressManipulation + ? getBasePtrIgnoringCapabilityAddressManipulation(V, DL) + : V->stripPointerCastsSameRepresentation(); + if (isa(Base)) { + return Constant::getNullValue(ResultTy); } + return nullptr; +} + +template +static Value *inferCapabilityOffsetOrAddr(Value *V, Type *ResultTy, + const DataLayout &DL) { + if (Value *Ret = foldCheriGetSetPair(V, ResultTy, DL)) + return Ret; APInt OffsetAPInt(DL.getIndexTypeSizeInBits(V->getType()), 0); // Look through pointer casts and accumulate constant GEPs: @@ -6419,6 +6438,7 @@ static Value *inferCapabilityOffsetOrAddr(Value *V, Type *ResultTy, // We can also fold chains of constant GEPS: // For example: getoffset(GEP(setoffset(A, Const1), 100) -> Const1 + 100 ConstantInt *ConstSetArg; + constexpr Intrinsic::ID SetIntrin = cheri::correspondingSetIntrinsic(Intrin); if (match(BasePtr, m_Intrinsic(m_Value(), m_ConstantInt(ConstSetArg)))) { return ConstantInt::get(ResultTy, ConstSetArg->getValue() + OffsetAPInt); @@ -6472,7 +6492,6 @@ static Value *simplifyUnaryIntrinsic(Function *F, Value *Op0, return Constant::getNullValue(F->getReturnType()); break; case Intrinsic::cheri_cap_sealed_get: - case Intrinsic::cheri_cap_flags_get: case Intrinsic::cheri_cap_base_get: // Values derived from NULL and where the only operations that have been // applied are address manipulations, always have the following properties: @@ -6510,6 +6529,18 @@ static Value *simplifyUnaryIntrinsic(Function *F, Value *Op0, Op0, F->getReturnType(), Q.DL)) return V; break; + case Intrinsic::cheri_cap_flags_get: { + if (auto *Ret = foldCheriGetSetPair( + Op0, F->getReturnType(), Q.DL)) + return Ret; + break; + } + case Intrinsic::cheri_cap_high_get: { + if (auto *Ret = foldCheriGetSetPair( + Op0, F->getReturnType(), Q.DL)) + return Ret; + break; + } case Intrinsic::cheri_cap_offset_get: if (auto *V = inferCapabilityOffsetOrAddr( Op0, F->getReturnType(), Q.DL)) diff --git a/llvm/test/CodeGen/CHERI-Generic/Inputs/cheri-intrinsics-folding-broken-module-regression.ll b/llvm/test/CodeGen/CHERI-Generic/Inputs/cheri-intrinsics-folding-broken-module-regression.ll index 5951969b570a7..ba8cf4c9fa6b3 100644 --- a/llvm/test/CodeGen/CHERI-Generic/Inputs/cheri-intrinsics-folding-broken-module-regression.ll +++ b/llvm/test/CodeGen/CHERI-Generic/Inputs/cheri-intrinsics-folding-broken-module-regression.ll @@ -1,6 +1,4 @@ ; This used to create a broken function. -; FIXME: the getoffset+add sequence should be folded to an increment -; REQUIRES: mips-registered-target ; RUN: opt @PURECAP_HARDFLOAT_ARGS@ -S -passes=instcombine %s -o - | FileCheck %s ; RUN: opt @PURECAP_HARDFLOAT_ARGS@ -S '-passes=default' %s | llc @PURECAP_HARDFLOAT_ARGS@ -O3 -o - | FileCheck %s --check-prefix ASM target datalayout = "@PURECAP_DATALAYOUT@" diff --git a/llvm/test/CodeGen/CHERI-Generic/MIPS/cheri-intrinsics-folding-broken-module-regression.ll b/llvm/test/CodeGen/CHERI-Generic/MIPS/cheri-intrinsics-folding-broken-module-regression.ll index 0fb65051c24d6..81b1f55b35d05 100644 --- a/llvm/test/CodeGen/CHERI-Generic/MIPS/cheri-intrinsics-folding-broken-module-regression.ll +++ b/llvm/test/CodeGen/CHERI-Generic/MIPS/cheri-intrinsics-folding-broken-module-regression.ll @@ -1,8 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --scrub-attributes --version 2 ; DO NOT EDIT -- This file was generated from test/CodeGen/CHERI-Generic/Inputs/cheri-intrinsics-folding-broken-module-regression.ll ; This used to create a broken function. -; FIXME: the getoffset+add sequence should be folded to an increment -; REQUIRES: mips-registered-target ; RUN: opt -mtriple=mips64 -mcpu=cheri128 -mattr=+cheri128 --relocation-model=pic -target-abi purecap -S -passes=instcombine %s -o - | FileCheck %s ; RUN: opt -mtriple=mips64 -mcpu=cheri128 -mattr=+cheri128 --relocation-model=pic -target-abi purecap -S '-passes=default' %s | llc -mtriple=mips64 -mcpu=cheri128 -mattr=+cheri128 --relocation-model=pic -target-abi purecap -O3 -o - | FileCheck %s --check-prefix ASM target datalayout = "E-m:e-pf200:128:128:128:64-i8:8:32-i16:16:32-i64:64-n32:64-S128-A200-P200-G200" @@ -23,13 +21,10 @@ define void @g(i64 %x, i64 %y) addrspace(200) nounwind { ; ASM-NEXT: lui $1, %pcrel_hi(_CHERI_CAPABILITY_TABLE_-8) ; ASM-NEXT: daddiu $1, $1, %pcrel_lo(_CHERI_CAPABILITY_TABLE_-4) ; ASM-NEXT: cgetpccincoffset $c1, $1 -; ASM-NEXT: daddu $2, $5, $4 ; ASM-NEXT: clcbi $c2, %captab20(d)($c1) ; ASM-NEXT: clcbi $c1, %captab20(e)($c1) -; ASM-NEXT: cgetoffset $1, $c2 ; ASM-NEXT: cincoffset $c2, $c2, $4 -; ASM-NEXT: daddu $1, $2, $1 -; ASM-NEXT: csetoffset $c2, $c2, $1 +; ASM-NEXT: cincoffset $c2, $c2, $5 ; ASM-NEXT: cjr $c17 ; ASM-NEXT: csc $c2, $zero, 0($c1) ; CHECK-LABEL: define void @g diff --git a/llvm/test/CodeGen/CHERI-Generic/RISCV32/cheri-intrinsics-folding-broken-module-regression.ll b/llvm/test/CodeGen/CHERI-Generic/RISCV32/cheri-intrinsics-folding-broken-module-regression.ll index cbaa2aab56786..5af50f984d024 100644 --- a/llvm/test/CodeGen/CHERI-Generic/RISCV32/cheri-intrinsics-folding-broken-module-regression.ll +++ b/llvm/test/CodeGen/CHERI-Generic/RISCV32/cheri-intrinsics-folding-broken-module-regression.ll @@ -1,8 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --scrub-attributes --version 2 ; DO NOT EDIT -- This file was generated from test/CodeGen/CHERI-Generic/Inputs/cheri-intrinsics-folding-broken-module-regression.ll ; This used to create a broken function. -; FIXME: the getoffset+add sequence should be folded to an increment -; REQUIRES: mips-registered-target ; RUN: opt -mtriple=riscv32 --relocation-model=pic -target-abi il32pc64f -mattr=+xcheri,+xcheripurecap,+f -S -passes=instcombine %s -o - | FileCheck %s ; RUN: opt -mtriple=riscv32 --relocation-model=pic -target-abi il32pc64f -mattr=+xcheri,+xcheripurecap,+f -S '-passes=default' %s | llc -mtriple=riscv32 --relocation-model=pic -target-abi il32pc64f -mattr=+xcheri,+xcheripurecap,+f -O3 -o - | FileCheck %s --check-prefix ASM target datalayout = "e-m:e-pf200:64:64:64:32-p:32:32-i64:64-n32-S128-A200-P200-G200" @@ -23,14 +21,11 @@ define void @g(i32 %x, i32 %y) addrspace(200) nounwind { ; ASM-NEXT: .LBB0_1: # Label of block must be emitted ; ASM-NEXT: auipcc a2, %captab_pcrel_hi(d) ; ASM-NEXT: clc a2, %pcrel_lo(.LBB0_1)(a2) -; ASM-NEXT: add a1, a1, a0 ; ASM-NEXT: .LBB0_2: # Label of block must be emitted ; ASM-NEXT: auipcc a3, %captab_pcrel_hi(e) ; ASM-NEXT: clc a3, %pcrel_lo(.LBB0_2)(a3) ; ASM-NEXT: cincoffset a0, a2, a0 -; ASM-NEXT: cgetoffset a2, a2 -; ASM-NEXT: add a1, a1, a2 -; ASM-NEXT: csetoffset a0, a0, a1 +; ASM-NEXT: cincoffset a0, a0, a1 ; ASM-NEXT: csc a0, 0(a3) ; ASM-NEXT: cret ; CHECK-LABEL: define void @g diff --git a/llvm/test/CodeGen/CHERI-Generic/RISCV64/cheri-intrinsics-folding-broken-module-regression.ll b/llvm/test/CodeGen/CHERI-Generic/RISCV64/cheri-intrinsics-folding-broken-module-regression.ll index 25f0998e2ab39..e714263a9d82d 100644 --- a/llvm/test/CodeGen/CHERI-Generic/RISCV64/cheri-intrinsics-folding-broken-module-regression.ll +++ b/llvm/test/CodeGen/CHERI-Generic/RISCV64/cheri-intrinsics-folding-broken-module-regression.ll @@ -1,8 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --scrub-attributes --version 2 ; DO NOT EDIT -- This file was generated from test/CodeGen/CHERI-Generic/Inputs/cheri-intrinsics-folding-broken-module-regression.ll ; This used to create a broken function. -; FIXME: the getoffset+add sequence should be folded to an increment -; REQUIRES: mips-registered-target ; RUN: opt -mtriple=riscv64 --relocation-model=pic -target-abi l64pc128d -mattr=+xcheri,+xcheripurecap,+f,+d -S -passes=instcombine %s -o - | FileCheck %s ; RUN: opt -mtriple=riscv64 --relocation-model=pic -target-abi l64pc128d -mattr=+xcheri,+xcheripurecap,+f,+d -S '-passes=default' %s | llc -mtriple=riscv64 --relocation-model=pic -target-abi l64pc128d -mattr=+xcheri,+xcheripurecap,+f,+d -O3 -o - | FileCheck %s --check-prefix ASM target datalayout = "e-m:e-pf200:128:128:128:64-p:64:64-i64:64-i128:128-n64-S128-A200-P200-G200" @@ -23,14 +21,11 @@ define void @g(i64 %x, i64 %y) addrspace(200) nounwind { ; ASM-NEXT: .LBB0_1: # Label of block must be emitted ; ASM-NEXT: auipcc a2, %captab_pcrel_hi(d) ; ASM-NEXT: clc a2, %pcrel_lo(.LBB0_1)(a2) -; ASM-NEXT: add a1, a1, a0 ; ASM-NEXT: .LBB0_2: # Label of block must be emitted ; ASM-NEXT: auipcc a3, %captab_pcrel_hi(e) ; ASM-NEXT: clc a3, %pcrel_lo(.LBB0_2)(a3) ; ASM-NEXT: cincoffset a0, a2, a0 -; ASM-NEXT: cgetoffset a2, a2 -; ASM-NEXT: add a1, a1, a2 -; ASM-NEXT: csetoffset a0, a0, a1 +; ASM-NEXT: cincoffset a0, a0, a1 ; ASM-NEXT: csc a0, 0(a3) ; ASM-NEXT: cret ; CHECK-LABEL: define void @g diff --git a/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll b/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll index d3c55c2c61d74..9708890081aab 100644 --- a/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll +++ b/llvm/test/Transforms/InstSimplify/cheri-intrinsics-get-set.ll @@ -85,13 +85,13 @@ define i64 @fold_get_of_set(ptr addrspace(200) %arg, i64 %value) nounwind { ; ; HIGH-LABEL: define {{[^@]+}}@fold_get_of_set ; HIGH-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { -; HIGH-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.high.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) -; HIGH-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(ptr addrspace(200) [[MODIFIED]]) -; HIGH-NEXT: ret i64 [[RET]] +; HIGH-NEXT: ret i64 [[VALUE]] ; ; OFFSET-LABEL: define {{[^@]+}}@fold_get_of_set ; OFFSET-SAME: (ptr addrspace(200) [[ARG:%.*]], i64 [[VALUE:%.*]]) addrspace(200) #[[ATTR0]] { -; OFFSET-NEXT: ret i64 [[VALUE]] +; OFFSET-NEXT: [[MODIFIED:%.*]] = tail call ptr addrspace(200) @llvm.cheri.cap.offset.set.i64(ptr addrspace(200) [[ARG]], i64 [[VALUE]]) +; OFFSET-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.offset.get.i64(ptr addrspace(200) [[MODIFIED]]) +; OFFSET-NEXT: ret i64 [[RET]] ; %modified = tail call ptr addrspace(200) @llvm.cheri.cap.FIELD.set.i64(ptr addrspace(200) %arg, i64 %value) %ret = tail call i64 @llvm.cheri.cap.FIELD.get.i64(ptr addrspace(200) %modified) @@ -136,8 +136,7 @@ define i64 @fold_get_on_null() nounwind { ; ; HIGH-LABEL: define {{[^@]+}}@fold_get_on_null ; HIGH-SAME: () addrspace(200) #[[ATTR0]] { -; HIGH-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(ptr addrspace(200) null) -; HIGH-NEXT: ret i64 [[RET]] +; HIGH-NEXT: ret i64 0 ; ; OFFSET-LABEL: define {{[^@]+}}@fold_get_on_null ; OFFSET-SAME: () addrspace(200) #[[ATTR0]] { @@ -158,9 +157,7 @@ define i64 @fold_get_on_null_with_gep(i64 %value, i64 %gepoff) nounwind { ; ; HIGH-LABEL: define {{[^@]+}}@fold_get_on_null_with_gep ; HIGH-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { -; HIGH-NEXT: [[TMP:%.*]] = getelementptr i8, ptr addrspace(200) null, i64 [[GEPOFF]] -; HIGH-NEXT: [[RET:%.*]] = tail call i64 @llvm.cheri.cap.high.get.i64(ptr addrspace(200) [[TMP]]) -; HIGH-NEXT: ret i64 [[RET]] +; HIGH-NEXT: ret i64 0 ; ; OFFSET-LABEL: define {{[^@]+}}@fold_get_on_null_with_gep ; OFFSET-SAME: (i64 [[VALUE:%.*]], i64 [[GEPOFF:%.*]]) addrspace(200) #[[ATTR0]] { From 2c292599ad82fc60c02085d07927aa4edc7402e0 Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Fri, 30 May 2025 23:46:26 +0100 Subject: [PATCH 09/12] [RISCV] Enable a test that works these days --- llvm/test/CodeGen/RISCV/cheri/double-imm.ll | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/llvm/test/CodeGen/RISCV/cheri/double-imm.ll b/llvm/test/CodeGen/RISCV/cheri/double-imm.ll index a191d9581989f..380e4f7aa273a 100644 --- a/llvm/test/CodeGen/RISCV/cheri/double-imm.ll +++ b/llvm/test/CodeGen/RISCV/cheri/double-imm.ll @@ -1,10 +1,9 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; Check that .LCPI symbols have a size associated with them. -; TODO: Enable once BuildPairF64 works for purecap -; RUNNOT: %riscv32_cheri_purecap_llc -mattr=+d -verify-machineinstrs < %s \ -; RUNNOT: | FileCheck --check-prefix=CHECK,IL32PC64 %s +; RUN: %riscv32_cheri_purecap_llc -mattr=+d -verify-machineinstrs < %s \ +; RUN: | FileCheck %s ; RUN: %riscv64_cheri_purecap_llc -mattr=+d -verify-machineinstrs < %s \ -; RUN: | FileCheck --check-prefixes=CHECK,L64PC128 %s --allow-unused-prefixes +; RUN: | FileCheck %s ; CHECK-LABEL: .rodata.cst8,"aM",@progbits,8 ; CHECK-NEXT: .p2align 3 From 24947515598f27018edc4bdd0a531eb8994eb535 Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Sat, 31 May 2025 00:08:50 +0100 Subject: [PATCH 10/12] [RISCV] Fix a RUN line causing the test not to be run Fixes: 1949a199a433 ("[MachineOutliner][CHERI-RISC-V] Use capability jumps for purecap") --- llvm/test/CodeGen/RISCV/cheri/machine-outliner.mir | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/llvm/test/CodeGen/RISCV/cheri/machine-outliner.mir b/llvm/test/CodeGen/RISCV/cheri/machine-outliner.mir index bf249467e8a81..fae1b58f1b22b 100644 --- a/llvm/test/CodeGen/RISCV/cheri/machine-outliner.mir +++ b/llvm/test/CodeGen/RISCV/cheri/machine-outliner.mir @@ -1,6 +1,6 @@ # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py ## The MachineOutliner was creating non-purecap instructions (found while compiling PCRE) -# RUNs: %riscv64_cheri_purecap_llc --run-pass=machine-outliner %s -o - | FileCheck %s +# RUN: %riscv64_cheri_purecap_llc --run-pass=machine-outliner %s -o - | FileCheck %s # RUN: %riscv64_cheri_purecap_llc --start-before=machine-outliner %s -o - | FileCheck %s --check-prefix=ASM --- | target datalayout = "e-m:e-pf200:128:128:128:64-p:64:64-i64:64-i128:128-n64-S128-A200-P200-G200" @@ -49,7 +49,7 @@ body: | ; CHECK-NEXT: successors: %bb.2(0x40000000), %bb.3(0x40000000) ; CHECK-NEXT: liveins: $x10_y, $x12_y, $x13_y, $x11 ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: $x5_y = PseudoCCALLReg target-flags(riscv-ccall) @OUTLINED_FUNCTION_0, implicit-def $x5_y, implicit-def $x15_y, implicit-def $x14, implicit-def $x15, implicit $x10_y, implicit $x12_y + ; CHECK-NEXT: PseudoCCALLReg target-flags(riscv-ccall) @OUTLINED_FUNCTION_0, implicit-def $x14, implicit-def $x15, implicit-def $x5_y, implicit-def $x15_y, implicit $x10_y, implicit $x12_y ; CHECK-NEXT: renamable $x14 = ANDI killed renamable $x14, 1 ; CHECK-NEXT: BEQ killed renamable $x14, $x0, %bb.3 ; CHECK-NEXT: {{ $}} @@ -57,7 +57,7 @@ body: | ; CHECK-NEXT: successors: %bb.3(0x80000000) ; CHECK-NEXT: liveins: $x10_y, $x12_y, $x13_y, $x11 ; CHECK-NEXT: {{ $}} - ; CHECK-NEXT: $x5_y = PseudoCCALLReg target-flags(riscv-ccall) @OUTLINED_FUNCTION_0, implicit-def $x5_y, implicit-def $x15_y, implicit-def $x14, implicit-def $x15, implicit $x10_y, implicit $x12_y + ; CHECK-NEXT: PseudoCCALLReg target-flags(riscv-ccall) @OUTLINED_FUNCTION_0, implicit-def $x14, implicit-def $x15, implicit-def $x5_y, implicit-def $x15_y, implicit $x10_y, implicit $x12_y ; CHECK-NEXT: renamable $x15 = ANDI killed renamable $x14, 1 ; CHECK-NEXT: $x14_y = CMove $x0_y ; CHECK-NEXT: {{ $}} @@ -122,7 +122,7 @@ body: | # CHECK: liveins: [] # CHECK: body: | # CHECK-NEXT: bb.0: -# CHECK-NEXT: liveins: $x10_y, $x12_y, $x11, $x13_y, $x5_y +# CHECK-NEXT: liveins: $x10_y, $x12_y, $x13_y, $x11, $x5_y # CHECK-NEXT: {{ $}} # CHECK-NEXT: renamable $x14 = CLHU renamable $x10_y, 0 # CHECK-NEXT: renamable $x15 = SRLI renamable $x14, 3 @@ -132,7 +132,6 @@ body: | # CHECK-NEXT: renamable $x14 = SRLW killed renamable $x15, killed renamable $x14 # FIXME: This should not be a JALR: # CHECK-NEXT: $x0_y = CJALR $x5_y, 0 -# CHECK-EMPTY: # CHECK-NEXT: ... # ASM-LABEL: pcre2_match_16: From 30a07f12c2a2e752c223b0234a1130ebcafd3120 Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Fri, 30 May 2025 21:22:20 +0100 Subject: [PATCH 11/12] [ELF][RISCV] Use .got rather than .captable for CHERI-RISC-V --- lld/ELF/Arch/RISCV.cpp | 50 +++++---- lld/ELF/InputSection.cpp | 14 --- lld/ELF/Relocations.cpp | 115 +++++++------------ lld/ELF/Relocations.h | 3 - lld/ELF/SyntheticSections.cpp | 11 +- lld/test/ELF/cheri/riscv/plt.s | 38 ++++--- lld/test/ELF/cheri/riscv/reloc-got.s | 77 +++++++++++++ lld/test/ELF/cheri/riscv/tls.s | 158 ++++++++++++++------------- 8 files changed, 256 insertions(+), 210 deletions(-) create mode 100644 lld/test/ELF/cheri/riscv/reloc-got.s diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp index e04e9b39db475..f4e04222a2e62 100644 --- a/lld/ELF/Arch/RISCV.cpp +++ b/lld/ELF/Arch/RISCV.cpp @@ -143,7 +143,10 @@ RISCV::RISCV(Ctx &ctx) : TargetInfo(ctx) { tlsOffsetRel = R_RISCV_TLS_DTPREL32; tlsGotRel = R_RISCV_TLS_TPREL32; } - gotRel = symbolicRel; + if (ctx.arg.isCheriAbi) + gotRel = *cheriCapRel; + else + gotRel = symbolicRel; tlsDescRel = R_RISCV_TLSDESC; // .got[0] = _DYNAMIC @@ -307,9 +310,8 @@ void RISCV::writePlt(uint8_t *buf, const Symbol &sym, // nop uint32_t ptrload = ctx.arg.isCheriAbi ? ctx.arg.is64 ? CLC_128 : CLC_64 : ctx.arg.is64 ? LD : LW; - uint32_t entryva = ctx.arg.isCheriAbi - ? sym.getCapTableVA(ctx, ctx.in.plt.get(), 0) - : sym.getGotPltVA(ctx); + uint32_t entryva = + ctx.arg.isCheriAbi ? sym.getGotVA(ctx) : sym.getGotPltVA(ctx); uint32_t offset = entryva - pltEntryAddr; write32le(buf + 0, utype(AUIPC, X_T3, hi20(offset))); write32le(buf + 4, itype(ptrload, X_T3, X_T3, lo12(offset))); @@ -393,25 +395,27 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s, return RE_RISCV_LEB128; case R_RISCV_CHERI_CAPABILITY: return R_CHERI_CAPABILITY; - case R_RISCV_CHERI_CAPTAB_PCREL_HI20: - return R_CHERI_CAPABILITY_TABLE_ENTRY_PC; - case R_RISCV_CHERI_TLS_IE_CAPTAB_PCREL_HI20: - return R_CHERI_CAPABILITY_TABLE_TLSIE_ENTRY_PC; - case R_RISCV_CHERI_TLS_GD_CAPTAB_PCREL_HI20: - return R_CHERI_CAPABILITY_TABLE_TLSGD_ENTRY_PC; - case R_RISCV_CHERIOT_COMPARTMENT_HI: - return isPCCRelative(ctx, loc, &s) ? R_PC : R_CHERIOT_COMPARTMENT_CGPREL_HI; - case R_RISCV_CHERIOT_COMPARTMENT_LO_I: - return R_CHERIOT_COMPARTMENT_CGPREL_LO_I; - case R_RISCV_CHERIOT_COMPARTMENT_LO_S: - return R_CHERIOT_COMPARTMENT_CGPREL_LO_S; - case R_RISCV_CHERIOT_COMPARTMENT_SIZE: - return R_CHERIOT_COMPARTMENT_SIZE; - default: - Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << type.v - << ") against symbol " << &s; - return R_NONE; - } + // TODO: Deprecate and eventually remove these + case R_RISCV_CHERI_CAPTAB_PCREL_HI20: + return R_GOT_PC; + case R_RISCV_CHERI_TLS_IE_CAPTAB_PCREL_HI20: + return R_GOT_PC; + case R_RISCV_CHERI_TLS_GD_CAPTAB_PCREL_HI20: + return R_TLSGD_PC; + case R_RISCV_CHERIOT_COMPARTMENT_HI: + return isPCCRelative(ctx, loc, &s) ? R_PC + : R_CHERIOT_COMPARTMENT_CGPREL_HI; + case R_RISCV_CHERIOT_COMPARTMENT_LO_I: + return R_CHERIOT_COMPARTMENT_CGPREL_LO_I; + case R_RISCV_CHERIOT_COMPARTMENT_LO_S: + return R_CHERIOT_COMPARTMENT_CGPREL_LO_S; + case R_RISCV_CHERIOT_COMPARTMENT_SIZE: + return R_CHERIOT_COMPARTMENT_SIZE; + default: + Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << type.v + << ") against symbol " << &s; + return R_NONE; + } } void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index ffe2d7408eae7..4a8f39a800443 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -1020,20 +1020,6 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r, case R_CHERI_CAPABILITY_TABLE_INDEX_CALL_SMALL_IMMEDIATE: assert(a == 0 && "capability table index relocs should not have addends"); return r.sym->getCapTableOffset(ctx, this, r.offset); - case R_CHERI_CAPABILITY_TABLE_ENTRY_PC: { - assert(a == 0 && "capability table entry relocs should not have addends"); - return r.sym->getCapTableVA(ctx, this, r.offset) - p; - } - case R_CHERI_CAPABILITY_TABLE_TLSGD_ENTRY_PC: { - assert(a == 0 && "capability table index relocs should not have addends"); - uint64_t capTableOffset = ctx.in.cheriCapTable->getDynTlsOffset(*r.sym); - return ctx.sym.cheriCapabilityTable->getVA(ctx) + capTableOffset - p; - } - case R_CHERI_CAPABILITY_TABLE_TLSIE_ENTRY_PC: { - assert(a == 0 && "capability table index relocs should not have addends"); - uint64_t capTableOffset = ctx.in.cheriCapTable->getTlsOffset(*r.sym); - return ctx.sym.cheriCapabilityTable->getVA(ctx) + capTableOffset - p; - } case R_CHERI_CAPABILITY_TABLE_REL: if (!ctx.sym.cheriCapabilityTable) { error("cannot compute difference between non-existent " diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index d0683f904caef..003387454e325 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -923,20 +923,21 @@ template static void addPltEntry(Ctx &ctx, PltSection &plt, GotPltSection &gotPlt, RelocationBaseSection &rel, RelType type, Symbol &sym) { plt.addEntry(sym); - if (ctx.arg.isCheriAbi) { - // TODO: More normal .got.plt rather than piggy-backing on .captable. We - // pass R_CHERI_CAPABILITY_TABLE_INDEX rather than the more obvious - // R_CHERI_CAPABILITY_TABLE_INDEX_CALL to force dynamic relocations into - // .rela.dyn rather than .rela.plt so no rtld changes are needed, as the - // latter doesn't really achieve anything without lazy binding. - ctx.in.cheriCapTable->addEntry(sym, R_CHERI_CAPABILITY_TABLE_INDEX, &plt, 0); - } else { - gotPlt.addEntry(sym); - rel.addReloc({type, &gotPlt, sym.getGotPltOffset(ctx), - sym.isPreemptible ? DynamicReloc::AgainstSymbol - : DynamicReloc::AddendOnlyWithTargetVA, - sym, 0, R_ABS}); - } + + // For CHERI-RISC-V we mark the symbol NEEDS_GOT so it will end up in .got as + // a function pointer, and uses .rela.dyn rather than .rela.plt, so no rtld + // changes are needed. + // + // TODO: More normal .got.plt with lazy-binding rather than piggy-backing on + // .got once rtld supports it. + if (ctx.arg.emachine == EM_RISCV && ctx.arg.isCheriAbi) + return; + + gotPlt.addEntry(sym); + rel.addReloc({type, &gotPlt, sym.getGotPltOffset(ctx), + sym.isPreemptible ? DynamicReloc::AgainstSymbol + : DynamicReloc::AddendOnlyWithTargetVA, + sym, 0, R_ABS}); } void elf::addGotEntry(Ctx &ctx, Symbol &sym) { @@ -952,8 +953,12 @@ void elf::addGotEntry(Ctx &ctx, Symbol &sym) { } // Otherwise, the value is either a link-time constant or the load base - // plus a constant. - if (!ctx.arg.isPic || isAbsolute(sym)) + // plus a constant. For CHERI it always requires run-time initialisation. + if (ctx.arg.isCheriAbi) { + invokeELFT(addCapabilityRelocation, ctx, &sym, *ctx.target->cheriCapRel, + ctx.in.got.get(), off, R_CHERI_CAPABILITY, 0, false, + [] { return ""; }); + } else if (!ctx.arg.isPic || isAbsolute(sym)) ctx.in.got->addConstant({R_ABS, ctx.target->symbolicRel, off, 0, &sym}); else addRelativeReloc(ctx, *ctx.in.got, off, sym, 0, R_ABS, @@ -1023,25 +1028,22 @@ 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_RELAX_HINT, RE_MIPS_GOT_LOCAL_PAGE, - RE_MIPS_GOTREL, RE_MIPS_GOT_OFF, RE_MIPS_GOT_OFF32, RE_MIPS_GOT_GP_PC, - RE_AARCH64_GOT_PAGE_PC, RE_AARCH64_AUTH_GOT_PAGE_PC, R_GOT_PC, - R_GOTONLY_PC, R_GOTPLTONLY_PC, R_PLT_PC, R_PLT_GOTREL, R_PLT_GOTPLT, - R_GOTPLT_GOTREL, R_GOTPLT_PC, RE_PPC32_PLTREL, RE_PPC64_CALL_PLT, - RE_PPC64_RELAX_TOC, RE_RISCV_ADD, RE_AARCH64_GOT_PAGE, - RE_AARCH64_AUTH_GOT, RE_AARCH64_AUTH_GOT_PC, RE_LOONGARCH_PLT_PAGE_PC, - RE_LOONGARCH_GOT, RE_LOONGARCH_GOT_PAGE_PC, - R_CHERI_CAPABILITY_TABLE_INDEX, + if (oneof(e)) + R_CHERIOT_COMPARTMENT_CGPREL_LO_S, R_CHERIOT_COMPARTMENT_SIZE>(e)) return true; // Cheri capability relocations are never static link time constants since @@ -1161,20 +1163,7 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset, if (oneof(expr)) { - std::lock_guard lock(ctx.relocMutex); - ctx.in.cheriCapTable->addEntry(sym, expr, sec, offset); - // Write out the index into the instruction - sec->relocations.push_back({expr, type, offset, addend, &sym}); - return; - } - - if (oneof(expr)) { + R_CHERI_CAPABILITY_TABLE_INDEX_CALL_SMALL_IMMEDIATE>(expr)) { std::lock_guard lock(ctx.relocMutex); ctx.in.cheriCapTable->addEntry(sym, expr, sec, offset); // Write out the index into the instruction @@ -1203,6 +1192,9 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset, } } else if (needsPlt(expr)) { sym.setFlags(NEEDS_PLT); + // See addPltEntry + if (ctx.arg.emachine == EM_RISCV && ctx.arg.isCheriAbi) + sym.setFlags(NEEDS_GOT); } else if (LLVM_UNLIKELY(isIfunc)) { sym.setFlags(HAS_DIRECT_RELOC); } @@ -1359,6 +1351,9 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset, printLocation(diag, *sec, sym, offset); } sym.setFlags(NEEDS_COPY | NEEDS_PLT); + // See addPltEntry + if (ctx.arg.emachine == EM_RISCV && ctx.arg.isCheriAbi) + sym.setFlags(NEEDS_GOT); sec->addReloc({expr, type, offset, addend, &sym}); return; } @@ -1501,36 +1496,6 @@ unsigned RelocationScanner::handleTlsRelocation(RelExpr expr, RelType type, !(isRISCV && expr != R_TLSDESC_PC && expr != R_TLSDESC_CALL) && !sec->file->ppc64DisableTLSRelax; - // No targets currently support TLS relaxation, so we can avoid duplicating - // much of the logic below for the captable. - if (expr == R_CHERI_CAPABILITY_TABLE_TLSGD_ENTRY_PC) { - std::lock_guard lock(ctx.relocMutex); - ctx.in.cheriCapTable->addDynTlsEntry(sym); - sec->relocations.push_back({expr, type, offset, addend, &sym}); - return 1; - } - if (expr == R_CHERI_CAPABILITY_TABLE_TLSIE_ENTRY_PC) { - std::lock_guard lock(ctx.relocMutex); - ctx.in.cheriCapTable->addTlsEntry(sym); - sec->relocations.push_back({expr, type, offset, addend, &sym}); - return 1; - } - - // No targets currently support TLS relaxation, so we can avoid duplicating - // much of the logic below for the captable. - if (expr == R_CHERI_CAPABILITY_TABLE_TLSGD_ENTRY_PC) { - std::lock_guard lock(ctx.relocMutex); - ctx.in.cheriCapTable->addDynTlsEntry(sym); - sec->relocations.push_back({expr, type, offset, addend, &sym}); - return 1; - } - if (expr == R_CHERI_CAPABILITY_TABLE_TLSIE_ENTRY_PC) { - std::lock_guard lock(ctx.relocMutex); - ctx.in.cheriCapTable->addTlsEntry(sym); - sec->relocations.push_back({expr, type, offset, addend, &sym}); - return 1; - } - // If we are producing an executable and the symbol is non-preemptable, it // must be defined and the code sequence can be optimized to use // Local-Exesec-> diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h index e1016758556f9..bde63e051edba 100644 --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -126,9 +126,6 @@ enum RelExpr { R_CHERI_CAPABILITY_TABLE_INDEX_SMALL_IMMEDIATE, R_CHERI_CAPABILITY_TABLE_INDEX_CALL, R_CHERI_CAPABILITY_TABLE_INDEX_CALL_SMALL_IMMEDIATE, - R_CHERI_CAPABILITY_TABLE_ENTRY_PC, - R_CHERI_CAPABILITY_TABLE_TLSGD_ENTRY_PC, - R_CHERI_CAPABILITY_TABLE_TLSIE_ENTRY_PC, R_CHERI_CAPABILITY_TABLE_REL, // relative offset to _CHERI_CAPABILITY_TABLE_ R_MIPS_CHERI_CAPTAB_TLSGD, R_MIPS_CHERI_CAPTAB_TLSLD, diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index f2cf815fefe00..64580f73b86dd 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -686,6 +686,9 @@ GotSection::GotSection(Ctx &ctx) void GotSection::addConstant(const Relocation &r) { relocations.push_back(r); } void GotSection::addEntry(const Symbol &sym) { + // TODO: Separate out TLS IE entries for CHERI so we can pack them more + // efficiently rather than consuming a whole capability-sized slot for an + // integer. assert(sym.auxIdx == ctx.symAux.size() - 1); ctx.symAux.back().gotIdx = numEntries++; } @@ -710,8 +713,12 @@ void GotSection::addTlsDescAuthEntry() { bool GotSection::addDynTlsEntry(const Symbol &sym) { assert(sym.auxIdx == ctx.symAux.size() - 1); ctx.symAux.back().tlsGdIdx = numEntries; - // Global Dynamic TLS entries take two GOT slots. - numEntries += 2; + // Global Dynamic TLS entries take two GOT slots, except on CHERI where they + // can be packed into one GOT slot. + if (ctx.arg.isCheriAbi) + ++numEntries; + else + numEntries += 2; return true; } diff --git a/lld/test/ELF/cheri/riscv/plt.s b/lld/test/ELF/cheri/riscv/plt.s index 1a0e1dc7cf69f..29b52f282dacf 100644 --- a/lld/test/ELF/cheri/riscv/plt.s +++ b/lld/test/ELF/cheri/riscv/plt.s @@ -7,7 +7,7 @@ # RUN: ld.lld %t.32.o %t1.32.so -z separate-code -o %t.32 # RUN: llvm-readelf -S -s %t.32 | FileCheck --check-prefixes=SEC,NM %s # RUN: llvm-readobj -r %t.32 | FileCheck --check-prefix=RELOC32 %s -# RUN: llvm-readelf -x .captable %t.32 | FileCheck --check-prefix=CAPTAB32 %s +# RUN: llvm-readelf -x .got %t.32 | FileCheck --check-prefix=GOT32 %s # RUN: llvm-objdump -d --no-show-raw-insn --print-imm-hex=false %t.32 | FileCheck --check-prefixes=DIS,DIS32 %s # RUN: %riscv64_cheri_purecap_llvm-mc -filetype=obj %t1.s -o %t1.64.o @@ -16,7 +16,7 @@ # RUN: ld.lld %t.64.o %t1.64.so -z separate-code -o %t.64 # RUN: llvm-readelf -S -s %t.64 | FileCheck --check-prefixes=SEC,NM %s # RUN: llvm-readobj -r %t.64 | FileCheck --check-prefix=RELOC64 %s -# RUN: llvm-readelf -x .captable %t.64 | FileCheck --check-prefix=CAPTAB64 %s +# RUN: llvm-readelf -x .got %t.64 | FileCheck --check-prefix=GOT64 %s # RUN: llvm-objdump -d --no-show-raw-insn --print-imm-hex=false %t.64 | FileCheck --check-prefixes=DIS,DIS64 %s # SEC: .plt PROGBITS {{0*}}00011030 @@ -27,19 +27,21 @@ # NM: {{0*}}00000000 0 FUNC WEAK DEFAULT UND weak # RELOC32: .rela.dyn { -# RELOC32-NEXT: 0x12000 R_RISCV_CHERI_CAPABILITY bar 0x0 -# RELOC32-NEXT: 0x12008 R_RISCV_CHERI_CAPABILITY weak 0x0 +# RELOC32-NEXT: 0x12068 R_RISCV_CHERI_CAPABILITY bar 0x0 +# RELOC32-NEXT: 0x12070 R_RISCV_CHERI_CAPABILITY weak 0x0 # RELOC32-NEXT: } -# CAPTAB32: section '.captable' -# CAPTAB32-NEXT: 0x00012000 00000000 00000000 00000000 00000000 +# GOT32: section '.got' +# GOT32-NEXT: 0x00012060 00200100 00000000 00000000 00000000 +# GOT32-NEXT: 0x00012070 00000000 00000000 # RELOC64: .rela.dyn { -# RELOC64-NEXT: 0x12000 R_RISCV_CHERI_CAPABILITY bar 0x0 -# RELOC64-NEXT: 0x12010 R_RISCV_CHERI_CAPABILITY weak 0x0 +# RELOC64-NEXT: 0x120D0 R_RISCV_CHERI_CAPABILITY bar 0x0 +# RELOC64-NEXT: 0x120E0 R_RISCV_CHERI_CAPABILITY weak 0x0 # RELOC64-NEXT: } -# CAPTAB64: section '.captable' -# CAPTAB64-NEXT: 0x00012000 00000000 00000000 00000000 00000000 -# CAPTAB64-NEXT: 0x00012010 00000000 00000000 00000000 00000000 +# GOT64: section '.got' +# GOT64-NEXT: 0x000120c0 00200100 00000000 00000000 00000000 +# GOT64-NEXT: 0x000120d0 00000000 00000000 00000000 00000000 +# GOT64-NEXT: 0x000120e0 00000000 00000000 00000000 00000000 # DIS: <_start>: ## Direct call @@ -62,17 +64,19 @@ # DIS: <.plt>: # DIS-NEXT: ... -## 32-bit: &.captable[bar]-. = 0x12000-0x11050 = 4096*1-80 +## 32-bit: &.got[bar]-. = 0x12068-0x11050 = 4096*1+24 +## 64-bit: &.got[bar]-. = 0x120d0-0x11050 = 4096*1+128 # DIS: 11050: auipcc t3, 1 -# DIS32-NEXT: clc t3, -80(t3) -# DIS64-NEXT: clc t3, -80(t3) +# DIS32-NEXT: clc t3, 24(t3) +# DIS64-NEXT: clc t3, 128(t3) # DIS-NEXT: cjalr t1, t3 # DIS-NEXT: nop -## 32-bit: &.captable[weak]-. = 0x12008-0x11060 = 4096*1-88 +## 32-bit: &.got[weak]-. = 0x12070-0x11060 = 4096*1+16 +## 64-bit: &.got[weak]-. = 0x120e0-0x11060 = 4096*1+128 # DIS: 11060: auipcc t3, 1 -# DIS32-NEXT: clc t3, -88(t3) -# DIS64-NEXT: clc t3, -80(t3) +# DIS32-NEXT: clc t3, 16(t3) +# DIS64-NEXT: clc t3, 128(t3) # DIS-NEXT: cjalr t1, t3 # DIS-NEXT: nop diff --git a/lld/test/ELF/cheri/riscv/reloc-got.s b/lld/test/ELF/cheri/riscv/reloc-got.s new file mode 100644 index 0000000000000..0afec1fd772e0 --- /dev/null +++ b/lld/test/ELF/cheri/riscv/reloc-got.s @@ -0,0 +1,77 @@ +# REQUIRES: riscv +# RUN: echo '.globl b; b:' | %riscv32_cheri_purecap_llvm-mc -filetype=obj - -o %t1.o +# RUN: ld.lld -shared %t1.o -soname=t1.so -o %t1.so + +# RUN: %riscv32_cheri_purecap_llvm-mc -filetype=obj -position-independent %s -o %t.o +# RUN: ld.lld %t.o %t1.so -o %t +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SEC32 %s +# RUN: llvm-readobj -r --cap-relocs %t | FileCheck --check-prefix=RELOC32 %s +# RUN: llvm-nm %t | FileCheck --check-prefix=NM32 %s +# RUN: llvm-readobj -x .got %t | FileCheck --check-prefix=HEX32 %s +# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=DIS32 %s + +# RUN: echo '.globl b; b:' | %riscv64_cheri_purecap_llvm-mc -filetype=obj - -o %t1.o +# RUN: ld.lld -shared %t1.o -soname=t1.so -o %t1.so + +# RUN: %riscv64_cheri_purecap_llvm-mc -filetype=obj -position-independent %s -o %t.o +# RUN: ld.lld %t.o %t1.so -o %t +# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SEC64 %s +# RUN: llvm-readobj -r --cap-relocs %t | FileCheck --check-prefix=RELOC64 %s +# RUN: llvm-nm %t | FileCheck --check-prefix=NM64 %s +# RUN: llvm-readobj -x .got %t | FileCheck --check-prefix=HEX64 %s +# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=DIS64 %s + +# SEC32: .got PROGBITS 00012230 000230 000018 +# SEC64: .got PROGBITS 00000000000123a0 0003a0 000030 + +# RELOC32: .rela.dyn { +# RELOC32-NEXT: 0x12238 R_RISCV_CHERI_CAPABILITY b 0x0 +# RELOC32-NEXT: } +# RELOC32: CHERI __cap_relocs [ +# RELOC32-NEXT: 0x012240 Base: 0x13248 (a+0) Length: 4 Perms: Object +# RELOC32-NEXT: ] + +# RELOC64: .rela.dyn { +# RELOC64-NEXT: 0x123B0 R_RISCV_CHERI_CAPABILITY b 0x0 +# RELOC64-NEXT: } +# RELOC64: CHERI __cap_relocs [ +# RELOC64-NEXT: 0x0123c0 Base: 0x133d0 (a+0) Length: 4 Perms: Object +# RELOC64-NEXT: ] + +# NM32: 00013248 d a +# NM64: 00000000000133d0 d a + +## .got[0] = _DYNAMIC +## .got[1] = 0 (relocated by R_RISCV_CHERI_CAPABILITY at run time) +## .got[2] = 0 (relocated by __cap_relocs at run time) +# HEX32: section '.got': +# HEX32: 0x00012230 c0210100 00000000 00000000 00000000 +# HEX32: 0x00012240 00000000 00000000 + +# HEX64: section '.got': +# HEX64: 0x000123a0 c0220100 00000000 00000000 00000000 +# HEX64: 0x000123b0 00000000 00000000 00000000 00000000 +# HEX64: 0x000123c0 00000000 00000000 00000000 00000000 + +## &.got[2]-. = 0x12240-0x111b0 = 4096*1+144 +# DIS32: 111b0: auipcc a0, 0x1 +# DIS32-NEXT: clc a0, 0x90(a0) +## &.got[1]-. = 0x12238-0x111b8 = 4096*1+128 +# DIS32: 111b8: auipcc a0, 0x1 +# DIS32-NEXT: clc a0, 0x80(a0) + +## &.got[2]-. = 0x123c0-0x112b0 = 4096*1+272 +# DIS64: 112b0: auipcc a0, 0x1 +# DIS64-NEXT: clc a0, 0x110(a0) +## &.got[1]-. = 0x123b0-0x112b8 = 4096*1+248 +# DIS64: 112b8: auipcc a0, 0x1 +# DIS64-NEXT: clc a0, 0xf8(a0) + +clgc a0, a +clgc a0, b + +.data +a: +## An undefined reference of _GLOBAL_OFFSET_TABLE_ causes .got[0] to be +## allocated to store _DYNAMIC. +.long _GLOBAL_OFFSET_TABLE_ - . diff --git a/lld/test/ELF/cheri/riscv/tls.s b/lld/test/ELF/cheri/riscv/tls.s index fbf5931f32fd4..f10bb3b149657 100644 --- a/lld/test/ELF/cheri/riscv/tls.s +++ b/lld/test/ELF/cheri/riscv/tls.s @@ -7,13 +7,13 @@ # RUN: %riscv32_cheri_purecap_llvm-mc --defsym PIC=0 -filetype=obj %s -o %t.32.o # RUN: ld.lld %t.32.o %t1.32.so -o %t.32 # RUN: llvm-readobj -r %t.32 | FileCheck --check-prefix=RV32-REL %s -# RUN: llvm-readelf -x .captable %t.32 | FileCheck --check-prefix=RV32-CAP %s +# RUN: llvm-readelf -x .got %t.32 | FileCheck --check-prefix=RV32-GOT %s # RUN: llvm-objdump -d --no-show-raw-insn --print-imm-hex=false %t.32 | FileCheck --check-prefix=RV32-DIS %s # RUN: %riscv32_cheri_purecap_llvm-mc --defsym PIC=1 -filetype=obj %s -o %t.32.pico # RUN: ld.lld -shared %t.32.pico %t1.32.so -o %t.32.so # RUN: llvm-readobj -r %t.32.so | FileCheck --check-prefix=RV32-SO-REL %s -# RUN: llvm-readelf -x .captable %t.32.so | FileCheck --check-prefix=RV32-SO-CAP %s +# RUN: llvm-readelf -x .got %t.32.so | FileCheck --check-prefix=RV32-SO-GOT %s # RUN: llvm-objdump -d --no-show-raw-insn --print-imm-hex=false %t.32.so | FileCheck --check-prefix=RV32-SO-DIS %s # RUN: %riscv64_cheri_purecap_llvm-mc -filetype=obj %t.s -o %t1.64.o @@ -22,132 +22,138 @@ # RUN: %riscv64_cheri_purecap_llvm-mc --defsym PIC=0 -filetype=obj %s -o %t.64.o # RUN: ld.lld %t.64.o %t1.64.so -o %t.64 # RUN: llvm-readobj -r %t.64 | FileCheck --check-prefix=RV64-REL %s -# RUN: llvm-readelf -x .captable %t.64 | FileCheck --check-prefix=RV64-CAP %s +# RUN: llvm-readelf -x .got %t.64 | FileCheck --check-prefix=RV64-GOT %s # RUN: llvm-objdump -d --no-show-raw-insn --print-imm-hex=false %t.64 | FileCheck --check-prefix=RV64-DIS %s # RUN: %riscv64_cheri_purecap_llvm-mc --defsym PIC=1 -filetype=obj %s -o %t.64.pico # RUN: ld.lld -shared %t.64.pico %t1.64.so -o %t.64.so # RUN: llvm-readobj -r %t.64.so | FileCheck --check-prefix=RV64-SO-REL %s -# RUN: llvm-readelf -x .captable %t.64.so | FileCheck --check-prefix=RV64-SO-CAP %s +# RUN: llvm-readelf -x .got %t.64.so | FileCheck --check-prefix=RV64-SO-GOT %s # RUN: llvm-objdump -d --no-show-raw-insn --print-imm-hex=false %t.64.so | FileCheck --check-prefix=RV64-SO-DIS %s # RV32-REL: .rela.dyn { -# RV32-REL-NEXT: 0x121E0 R_RISCV_TLS_DTPMOD32 evar 0x0 -# RV32-REL-NEXT: 0x121E4 R_RISCV_TLS_DTPREL32 evar 0x0 -# RV32-REL-NEXT: 0x121F0 R_RISCV_TLS_TPREL32 evar 0x0 +# RV32-REL-NEXT: 0x12248 R_RISCV_TLS_DTPMOD32 evar 0x0 +# RV32-REL-NEXT: 0x1224C R_RISCV_TLS_DTPREL32 evar 0x0 +# RV32-REL-NEXT: 0x12250 R_RISCV_TLS_TPREL32 evar 0x0 # RV32-REL-NEXT: } # RV32-SO-REL: .rela.dyn { -# RV32-SO-REL-NEXT: 0x3290 R_RISCV_TLS_DTPMOD32 - 0x0 -# RV32-SO-REL-NEXT: 0x329C R_RISCV_TLS_TPREL32 - 0x4 -# RV32-SO-REL-NEXT: 0x3288 R_RISCV_TLS_DTPMOD32 evar 0x0 -# RV32-SO-REL-NEXT: 0x328C R_RISCV_TLS_DTPREL32 evar 0x0 -# RV32-SO-REL-NEXT: 0x3298 R_RISCV_TLS_TPREL32 evar 0x0 +# RV32-SO-REL-NEXT: 0x2288 R_RISCV_TLS_DTPMOD32 - 0x0 +# RV32-SO-REL-NEXT: 0x2290 R_RISCV_TLS_TPREL32 - 0x4 +# RV32-SO-REL-NEXT: 0x2278 R_RISCV_TLS_DTPMOD32 evar 0x0 +# RV32-SO-REL-NEXT: 0x227C R_RISCV_TLS_DTPREL32 evar 0x0 +# RV32-SO-REL-NEXT: 0x2280 R_RISCV_TLS_TPREL32 evar 0x0 # RV32-SO-REL-NEXT: } -# RV32-CAP: section '.captable': -# RV32-CAP-NEXT: 0x000121e0 00000000 00000000 01000000 04000000 -# RV32-CAP-NEXT: 0x000121f0 00000000 04000000 +# RV32-GOT: section '.got': +# RV32-GOT-NEXT: 0x00012240 e0210100 00000000 00000000 00000000 +# RV32-GOT-NEXT: 0x00012250 00000000 00000000 01000000 04000000 +# RV32-GOT-NEXT: 0x00012260 04000000 00000000 -# RV32-SO-CAP: section '.captable': -# RV32-SO-CAP-NEXT: 0x00003288 00000000 00000000 00000000 04000000 -# RV32-SO-CAP-NEXT: 0x00003298 00000000 00000000 +# RV32-SO-GOT: section '.got': +# RV32-SO-GOT-NEXT: 0x00002270 10220000 00000000 00000000 00000000 +# RV32-SO-GOT-NEXT: 0x00002280 00000000 00000000 00000000 04000000 +# RV32-SO-GOT-NEXT: 0x00002290 00000000 00000000 -# 0x121e0 - 0x111b4 = 0x0102c (GD evar) +# 0x12248 - 0x111b4 = 0x1094 (GD evar) # RV32-DIS: 111b4: auipcc a0, 1 -# RV32-DIS-NEXT: cincoffset a0, a0, 44 +# RV32-DIS-NEXT: cincoffset a0, a0, 148 -# 0x121f0 - 0x111bc = 0x01034 (IE evar) +# 0x12250 - 0x111bc = 0x1094 (IE evar) # RV32-DIS: 111bc: auipcc a0, 1 -# RV32-DIS-NEXT: clw a0, 52(a0) +# RV32-DIS-NEXT: clw a0, 148(a0) -# 0x121e8 - 0x111c4 = 0x01024 (GD lvar) +# 0x12258 - 0x111c4 = 0x1094 (GD lvar) # RV32-DIS: 111c4: auipcc a0, 1 -# RV32-DIS-NEXT: cincoffset a0, a0, 36 +# RV32-DIS-NEXT: cincoffset a0, a0, 148 -# 0x121f4 - 0x111cc = 0x01028 (IE lvar) +# 0x12260 - 0x111cc = 0x1094 (IE lvar) # RV32-DIS: 111cc: auipcc a0, 1 -# RV32-DIS-NEXT: clw a0, 40(a0) +# RV32-DIS-NEXT: clw a0, 148(a0) # RV32-DIS: 111d4: lui a0, 0 # RV32-DIS-NEXT: cincoffset a0, tp, a0 # RV32-DIS-NEXT: cincoffset a0, a0, 4 -# 0x3288 - 0x1210 = 0x2078 (GD evar) -# RV32-SO-DIS: 1210: auipcc a0, 2 -# RV32-SO-DIS-NEXT: cincoffset a0, a0, 120 +# 0x2278 - 0x11f0 = 0x1088 (GD evar) +# RV32-SO-DIS: 11f0: auipcc a0, 1 +# RV32-SO-DIS-NEXT: cincoffset a0, a0, 136 -# 0x3298 - 0x1218 = 0x2080 (IE evar) -# RV32-SO-DIS: 1218: auipcc a0, 2 -# RV32-SO-DIS-NEXT: clw a0, 128(a0) +# 0x2280 - 0x11f8 = 0x1088 (IE evar) +# RV32-SO-DIS: 11f8: auipcc a0, 1 +# RV32-SO-DIS-NEXT: clw a0, 136(a0) -# 0x3290 - 0x1220 = 0x2070 (GD lvar) -# RV32-SO-DIS: 1220: auipcc a0, 2 -# RV32-SO-DIS-NEXT: cincoffset a0, a0, 112 +# 0x2288 - 0x1200 = 0x1088 (GD lvar) +# RV32-SO-DIS: 1200: auipcc a0, 1 +# RV32-SO-DIS-NEXT: cincoffset a0, a0, 136 -# 0x329c - 0x1228 = 0x2074 (IE lvar) -# RV32-SO-DIS: 1228: auipcc a0, 2 -# RV32-SO-DIS-NEXT: clw a0, 116(a0) +# 0x2290 - 0x1208 = 0x1088 (IE lvar) +# RV32-SO-DIS: 1208: auipcc a0, 1 +# RV32-SO-DIS-NEXT: clw a0, 136(a0) # RV64-REL: .rela.dyn { -# RV64-REL-NEXT: 0x122F0 R_RISCV_TLS_DTPMOD64 evar 0x0 -# RV64-REL-NEXT: 0x122F8 R_RISCV_TLS_DTPREL64 evar 0x0 -# RV64-REL-NEXT: 0x12310 R_RISCV_TLS_TPREL64 evar 0x0 +# RV64-REL-NEXT: 0x123C0 R_RISCV_TLS_DTPMOD64 evar 0x0 +# RV64-REL-NEXT: 0x123C8 R_RISCV_TLS_DTPREL64 evar 0x0 +# RV64-REL-NEXT: 0x123D0 R_RISCV_TLS_TPREL64 evar 0x0 # RV64-REL-NEXT: } # RV64-SO-REL: .rela.dyn { -# RV64-SO-REL-NEXT: 0x3430 R_RISCV_TLS_DTPMOD64 - 0x0 -# RV64-SO-REL-NEXT: 0x3448 R_RISCV_TLS_TPREL64 - 0x4 -# RV64-SO-REL-NEXT: 0x3420 R_RISCV_TLS_DTPMOD64 evar 0x0 -# RV64-SO-REL-NEXT: 0x3428 R_RISCV_TLS_DTPREL64 evar 0x0 -# RV64-SO-REL-NEXT: 0x3440 R_RISCV_TLS_TPREL64 evar 0x0 +# RV64-SO-REL-NEXT: 0x2430 R_RISCV_TLS_DTPMOD64 - 0x0 +# RV64-SO-REL-NEXT: 0x2440 R_RISCV_TLS_TPREL64 - 0x4 +# RV64-SO-REL-NEXT: 0x2410 R_RISCV_TLS_DTPMOD64 evar 0x0 +# RV64-SO-REL-NEXT: 0x2418 R_RISCV_TLS_DTPREL64 evar 0x0 +# RV64-SO-REL-NEXT: 0x2420 R_RISCV_TLS_TPREL64 evar 0x0 # RV64-SO-REL-NEXT: } -# RV64-CAP: section '.captable': -# RV64-CAP-NEXT: 0x000122f0 00000000 00000000 00000000 00000000 -# RV64-CAP-NEXT: 0x00012300 01000000 00000000 04000000 00000000 -# RV64-CAP-NEXT: 0x00012310 00000000 00000000 04000000 00000000 - -# RV64-SO-CAP: section '.captable': -# RV64-SO-CAP-NEXT: 0x00003420 00000000 00000000 00000000 00000000 -# RV64-SO-CAP-NEXT: 0x00003430 00000000 00000000 04000000 00000000 -# RV64-SO-CAP-NEXT: 0x00003440 00000000 00000000 00000000 00000000 - -# 0x122f0 - 0x112b8 = 0x01038 (GD evar) +# RV64-GOT: section '.got': +# RV64-GOT-NEXT: 0x000123b0 e8220100 00000000 00000000 00000000 +# RV64-GOT-NEXT: 0x000123c0 00000000 00000000 00000000 00000000 +# RV64-GOT-NEXT: 0x000123d0 00000000 00000000 00000000 00000000 +# RV64-GOT-NEXT: 0x000123e0 01000000 00000000 04000000 00000000 +# RV64-GOT-NEXT: 0x000123f0 04000000 00000000 00000000 00000000 + +# RV64-SO-GOT: section '.got': +# RV64-SO-GOT-NEXT: 0x00002400 38230000 00000000 00000000 00000000 +# RV64-SO-GOT-NEXT: 0x00002410 00000000 00000000 00000000 00000000 +# RV64-SO-GOT-NEXT: 0x00002420 00000000 00000000 00000000 00000000 +# RV64-SO-GOT-NEXT: 0x00002430 00000000 00000000 04000000 00000000 +# RV64-SO-GOT-NEXT: 0x00002440 00000000 00000000 00000000 00000000 + +# 0x123c0 - 0x112b8 = 0x1108 (GD evar) # RV64-DIS: 112b8: auipcc a0, 1 -# RV64-DIS-NEXT: cincoffset a0, a0, 56 +# RV64-DIS-NEXT: cincoffset a0, a0, 264 -# 0x12310 - 0x112c0 = 0x01050 (IE evar) +# 0x123d0 - 0x112c0 = 0x1110 (IE evar) # RV64-DIS: 112c0: auipcc a0, 1 -# RV64-DIS-NEXT: cld a0, 80(a0) +# RV64-DIS-NEXT: cld a0, 272(a0) -# 0x12300 - 0x112c8 = 0x01038 (GD lvar) +# 0x123e0 - 0x112c8 = 0x1118 (GD lvar) # RV64-DIS: 112c8: auipcc a0, 1 -# RV64-DIS-NEXT: cincoffset a0, a0, 56 +# RV64-DIS-NEXT: cincoffset a0, a0, 280 -# 0x12318 - 0x112d0 = 0x01048 (IE lvar) +# 0x123f0 - 0x112d0 = 0x1120 (IE lvar) # RV64-DIS: 112d0: auipcc a0, 1 -# RV64-DIS-NEXT: cld a0, 72(a0) +# RV64-DIS-NEXT: cld a0, 288(a0) # RV64-DIS: 112d8: lui a0, 0 # RV64-DIS-NEXT: cincoffset a0, tp, a0 # RV64-DIS-NEXT: cincoffset a0, a0, 4 -# 0x3420 - 0x1350 = 0x20d0 (GD evar) -# RV64-SO-DIS: 1350: auipcc a0, 2 -# RV64-SO-DIS-NEXT: cincoffset a0, a0, 208 +# 0x2410 - 0x1318 = 0x10f8 (GD evar) +# RV64-SO-DIS: 1318: auipcc a0, 1 +# RV64-SO-DIS-NEXT: cincoffset a0, a0, 248 -# 0x3440 - 0x1358 = 0x20e8 (IE evar) -# RV64-SO-DIS: 1358: auipcc a0, 2 -# RV64-SO-DIS-NEXT: cld a0, 232(a0) +# 0x2420 - 0x1320 = 0x1100 (IE evar) +# RV64-SO-DIS: 1320: auipcc a0, 1 +# RV64-SO-DIS-NEXT: cld a0, 256(a0) -# 0x3430 - 0x1360 = 0x20d0 (GD lvar) -# RV64-SO-DIS: 1360: auipcc a0, 2 -# RV64-SO-DIS-NEXT: cincoffset a0, a0, 208 +# 0x2430 - 0x1328 = 0x1108 (GD lvar) +# RV64-SO-DIS: 1328: auipcc a0, 1 +# RV64-SO-DIS-NEXT: cincoffset a0, a0, 264 -# 0x3448 - 0x1368 = 0x20e0 (IE lvar) -# RV64-SO-DIS: 1368: auipcc a0, 2 -# RV64-SO-DIS-NEXT: cld a0, 224(a0) +# 0x2440 - 0x1330 = 0x1110 (IE lvar) +# RV64-SO-DIS: 1330: auipcc a0, 1 +# RV64-SO-DIS-NEXT: cld a0, 272(a0) .global _start _start: From f9ec9dc60ced4fe3eb379045c43052662356b891 Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Fri, 30 May 2025 21:36:56 +0100 Subject: [PATCH 12/12] [ELF][NFC] Drop unwarranted XXX comments RELRO sections are SHF_WRITE, so there's nothing wrong with the code here. This is how it's meant to work, and how things like GotSection are constructed. --- lld/ELF/Arch/Cheri.cpp | 128 +++++++++++++++++----------------- lld/ELF/Arch/Cheri.h | 12 ++-- lld/ELF/Arch/Mips.cpp | 10 +-- lld/ELF/Config.h | 10 +-- lld/ELF/InputSection.cpp | 22 +++--- lld/ELF/Relocations.cpp | 49 +++++++------ lld/ELF/Relocations.h | 10 +-- lld/ELF/Symbols.cpp | 15 ++-- lld/ELF/Symbols.h | 9 +-- lld/ELF/SyntheticSections.cpp | 38 ++++++---- lld/ELF/SyntheticSections.h | 4 +- lld/ELF/Writer.cpp | 30 ++++---- 12 files changed, 174 insertions(+), 163 deletions(-) diff --git a/lld/ELF/Arch/Cheri.cpp b/lld/ELF/Arch/Cheri.cpp index 6bea63484e918..3bd18741918dd 100644 --- a/lld/ELF/Arch/Cheri.cpp +++ b/lld/ELF/Arch/Cheri.cpp @@ -57,7 +57,7 @@ struct InMemoryCapRelocEntry { CheriCapRelocsSection::CheriCapRelocsSection(Ctx &ctx, StringRef name) : SyntheticSection(ctx, name, SHT_PROGBITS, (ctx.arg.isPic && !ctx.arg.relativeCapRelocsOnly) - ? SHF_ALLOC | SHF_WRITE /* XXX: actually RELRO */ + ? SHF_ALLOC | SHF_WRITE : SHF_ALLOC, ctx.arg.wordsize) { this->entsize = ctx.arg.wordsize * 5; @@ -603,16 +603,14 @@ void CheriCapRelocsSection::writeTo(uint8_t *buf) { invokeELFT(writeToImpl, buf); } -CheriCapTableSection::CheriCapTableSection(Ctx &ctx) - : SyntheticSection( - ctx, ".captable", SHT_PROGBITS, - SHF_ALLOC | SHF_WRITE, /* XXX: actually RELRO for BIND_NOW*/ - ctx.arg.capabilitySize) { +MipsCheriCapTableSection::MipsCheriCapTableSection(Ctx &ctx) + : SyntheticSection(ctx, ".captable", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE, + ctx.arg.capabilitySize) { assert(ctx.arg.capabilitySize > 0); this->entsize = ctx.arg.capabilitySize; } -void CheriCapTableSection::writeTo(uint8_t *buf) { +void MipsCheriCapTableSection::writeTo(uint8_t *buf) { // Capability part should be filled with all zeros and crt_init_globals fills // it in. For the TLS part, assignValuesAndAddCapTableSymbols adds any static // relocations needed, and should be procesed by relocateAlloc. @@ -625,8 +623,8 @@ static Defined *findMatchingFunction(const InputSectionBase *isec, return isec->getEnclosingFunction(symOffset); } -CheriCapTableSection::CaptableMap & -CheriCapTableSection::getCaptableMapForFileAndOffset( +MipsCheriCapTableSection::CaptableMap & +MipsCheriCapTableSection::getCaptableMapForFileAndOffset( const InputSectionBase *isec, uint64_t offset) { if (LLVM_LIKELY(ctx.arg.capTableScope == CapTableScopePolicy::All)) return globalEntries; @@ -648,8 +646,9 @@ CheriCapTableSection::getCaptableMapForFileAndOffset( return globalEntries; } -void CheriCapTableSection::addEntry(Symbol &sym, RelExpr expr, - InputSectionBase *isec, uint64_t offset) { +void MipsCheriCapTableSection::addEntry(Symbol &sym, RelExpr expr, + InputSectionBase *isec, + uint64_t offset) { // FIXME: can this be called from multiple threads? CapTableIndex idx; idx.needsSmallImm = false; @@ -657,12 +656,12 @@ void CheriCapTableSection::addEntry(Symbol &sym, RelExpr expr, idx.firstUse = SymbolAndOffset(isec, offset); assert(!idx.firstUse->symOrSec.isNull()); switch (expr) { - case R_CHERI_CAPABILITY_TABLE_INDEX_SMALL_IMMEDIATE: - case R_CHERI_CAPABILITY_TABLE_INDEX_CALL_SMALL_IMMEDIATE: - idx.needsSmallImm = true; - break; - default: - break; + case R_MIPS_CHERI_CAPTAB_INDEX_SMALL_IMMEDIATE: + case R_MIPS_CHERI_CAPTAB_INDEX_CALL_SMALL_IMMEDIATE: + idx.needsSmallImm = true; + break; + default: + break; } // If the symbol is only ever referenced by the captable call relocations we // can emit a capability call relocation instead of a normal capability @@ -670,25 +669,25 @@ void CheriCapTableSection::addEntry(Symbol &sym, RelExpr expr, // not used as a function pointer and therefore does not need a unique // address (plt stub) across all DSOs. switch (expr) { - case R_CHERI_CAPABILITY_TABLE_INDEX_CALL: - case R_CHERI_CAPABILITY_TABLE_INDEX_CALL_SMALL_IMMEDIATE: - if (!sym.isFunc() && !sym.isUndefWeak()) { - CheriCapRelocLocation loc{isec, offset}; - std::string msg = "call relocation against non-function symbol " + - verboseToString(ctx, &sym, 0) + "\n>>> referenced by " + - loc.toString(ctx); - if (sym.isUndefined() && - ctx.arg.unresolvedSymbolsInShlib == UnresolvedPolicy::Ignore) { - // Don't fail the build for shared libraries unless - nonFatalWarning(msg); - } else { - warn(msg); - } + case R_MIPS_CHERI_CAPTAB_INDEX_CALL: + case R_MIPS_CHERI_CAPTAB_INDEX_CALL_SMALL_IMMEDIATE: + if (!sym.isFunc() && !sym.isUndefWeak()) { + CheriCapRelocLocation loc{isec, offset}; + std::string msg = "call relocation against non-function symbol " + + verboseToString(ctx, &sym, 0) + "\n>>> referenced by " + + loc.toString(ctx); + if (sym.isUndefined() && + ctx.arg.unresolvedSymbolsInShlib == UnresolvedPolicy::Ignore) { + // Don't fail the build for shared libraries unless + nonFatalWarning(msg); + } else { + warn(msg); } - idx.usedInCallExpr = true; - break; - default: - break; + } + idx.usedInCallExpr = true; + break; + default: + break; } CaptableMap &entries = getCaptableMapForFileAndOffset(isec, offset); if (ctx.arg.zCapTableDebug) { @@ -711,25 +710,25 @@ void CheriCapTableSection::addEntry(Symbol &sym, RelExpr expr, } } -void CheriCapTableSection::addDynTlsEntry(Symbol &sym) { +void MipsCheriCapTableSection::addDynTlsEntry(Symbol &sym) { dynTlsEntries.map.insert(std::make_pair(&sym, CapTableIndex())); } -void CheriCapTableSection::addTlsIndex() { +void MipsCheriCapTableSection::addTlsIndex() { dynTlsEntries.map.insert(std::make_pair(nullptr, CapTableIndex())); } -void CheriCapTableSection::addTlsEntry(Symbol &sym) { +void MipsCheriCapTableSection::addTlsEntry(Symbol &sym) { tlsEntries.map.insert(std::make_pair(&sym, CapTableIndex())); } -uint32_t CheriCapTableSection::getIndex(const Symbol &sym, - const InputSectionBase *isec, - uint64_t offset) const { +uint32_t MipsCheriCapTableSection::getIndex(const Symbol &sym, + const InputSectionBase *isec, + uint64_t offset) const { assert(valuesAssigned && "getIndex called before index assignment"); const CaptableMap &entries = - const_cast(this)->getCaptableMapForFileAndOffset( - isec, offset); + const_cast(this) + ->getCaptableMapForFileAndOffset(isec, offset); auto it = entries.map.find(const_cast(&sym)); assert(entries.firstIndex != std::numeric_limits::max() && "First index not set yet?"); @@ -741,21 +740,21 @@ uint32_t CheriCapTableSection::getIndex(const Symbol &sym, return *it->second.index - entries.firstIndex; } -uint32_t CheriCapTableSection::getDynTlsOffset(const Symbol &sym) const { +uint32_t MipsCheriCapTableSection::getDynTlsOffset(const Symbol &sym) const { assert(valuesAssigned && "getDynTlsOffset called before index assignment"); auto it = dynTlsEntries.map.find(const_cast(&sym)); assert(it != dynTlsEntries.map.end()); return *it->second.index * ctx.arg.wordsize; } -uint32_t CheriCapTableSection::getTlsIndexOffset() const { +uint32_t MipsCheriCapTableSection::getTlsIndexOffset() const { assert(valuesAssigned && "getTlsIndexOffset called before index assignment"); auto it = dynTlsEntries.map.find(nullptr); assert(it != dynTlsEntries.map.end()); return *it->second.index * ctx.arg.wordsize; } -uint32_t CheriCapTableSection::getTlsOffset(const Symbol &sym) const { +uint32_t MipsCheriCapTableSection::getTlsOffset(const Symbol &sym) const { assert(valuesAssigned && "getTlsOffset called before index assignment"); auto it = tlsEntries.map.find(const_cast(&sym)); assert(it != tlsEntries.map.end()); @@ -763,9 +762,9 @@ uint32_t CheriCapTableSection::getTlsOffset(const Symbol &sym) const { } template -uint64_t CheriCapTableSection::assignIndices(uint64_t startIndex, - CaptableMap &entries, - const Twine &symContext) { +uint64_t MipsCheriCapTableSection::assignIndices(uint64_t startIndex, + CaptableMap &entries, + const Twine &symContext) { // Usually StartIndex will be zero (one global captable) but if we are // compiling with per-file/per-function uint64_t smallEntryCount = 0; @@ -875,7 +874,7 @@ uint64_t CheriCapTableSection::assignIndices(uint64_t startIndex, ? ctx.in.relaPlt.get() : ctx.mainPart->relaDyn.get(); addCapabilityRelocation( - ctx, targetSym, elfCapabilityReloc, ctx.in.cheriCapTable.get(), off, + ctx, targetSym, elfCapabilityReloc, ctx.in.mipsCheriCapTable.get(), off, R_CHERI_CAPABILITY, 0, it.second.usedInCallExpr, [&]() { return ("\n>>> referenced by " + refName + "\n>>> first used in " + @@ -889,7 +888,7 @@ uint64_t CheriCapTableSection::assignIndices(uint64_t startIndex, } template -void CheriCapTableSection::assignValuesAndAddCapTableSymbols() { +void MipsCheriCapTableSection::assignValuesAndAddCapTableSymbols() { // First assign the global indices (which will usually be the only ones) uint64_t assignedEntries = assignIndices(0, globalEntries, ""); if (LLVM_UNLIKELY(ctx.arg.capTableScope != CapTableScopePolicy::All)) { @@ -980,22 +979,22 @@ void CheriCapTableSection::assignValuesAndAddCapTableSymbols() { } template void -CheriCapTableSection::assignValuesAndAddCapTableSymbols(); +MipsCheriCapTableSection::assignValuesAndAddCapTableSymbols(); template void -CheriCapTableSection::assignValuesAndAddCapTableSymbols(); +MipsCheriCapTableSection::assignValuesAndAddCapTableSymbols(); template void -CheriCapTableSection::assignValuesAndAddCapTableSymbols(); +MipsCheriCapTableSection::assignValuesAndAddCapTableSymbols(); template void -CheriCapTableSection::assignValuesAndAddCapTableSymbols(); +MipsCheriCapTableSection::assignValuesAndAddCapTableSymbols(); -CheriCapTableMappingSection::CheriCapTableMappingSection(Ctx &ctx) +MipsCheriCapTableMappingSection::MipsCheriCapTableMappingSection(Ctx &ctx) : SyntheticSection(ctx, ".captable_mapping", SHT_PROGBITS, SHF_ALLOC, 8) { assert(ctx.arg.capabilitySize > 0); this->entsize = sizeof(CaptableMappingEntry); static_assert(sizeof(CaptableMappingEntry) == 24, ""); } -size_t CheriCapTableMappingSection::getSize() const { +size_t MipsCheriCapTableMappingSection::getSize() const { assert(ctx.arg.capTableScope != CapTableScopePolicy::All); if (!isNeeded()) return 0; size_t count = 0; @@ -1010,9 +1009,10 @@ size_t CheriCapTableMappingSection::getSize() const { return count * sizeof(CaptableMappingEntry); } -void CheriCapTableMappingSection::writeTo(uint8_t *buf) { +void MipsCheriCapTableMappingSection::writeTo(uint8_t *buf) { assert(ctx.arg.capTableScope != CapTableScopePolicy::All); - if (!ctx.in.cheriCapTable) return; + if (!ctx.in.mipsCheriCapTable) + return; if (!ctx.in.symTab) { error("Cannot write " + this->name + " without .symtab section!"); return; @@ -1025,14 +1025,14 @@ void CheriCapTableMappingSection::writeTo(uint8_t *buf) { for (const SymbolTableEntry &ste : ctx.in.symTab->getSymbols()) { Symbol *sym = ste.sym; if (!sym->isDefined() || !sym->isFunc()) continue; - const CheriCapTableSection::CaptableMap *capTableMap = nullptr; + const MipsCheriCapTableSection::CaptableMap *capTableMap = nullptr; if (ctx.arg.capTableScope == CapTableScopePolicy::Function) { - auto it = ctx.in.cheriCapTable->perFunctionEntries.find(sym); - if (it != ctx.in.cheriCapTable->perFunctionEntries.end()) + auto it = ctx.in.mipsCheriCapTable->perFunctionEntries.find(sym); + if (it != ctx.in.mipsCheriCapTable->perFunctionEntries.end()) capTableMap = &it->second; } else if (ctx.arg.capTableScope == CapTableScopePolicy::File) { - auto it = ctx.in.cheriCapTable->perFileEntries.find(sym->file); - if (it != ctx.in.cheriCapTable->perFileEntries.end()) + auto it = ctx.in.mipsCheriCapTable->perFileEntries.find(sym->file); + if (it != ctx.in.mipsCheriCapTable->perFileEntries.end()) capTableMap = &it->second; } else { llvm_unreachable("Invalid mode!"); diff --git a/lld/ELF/Arch/Cheri.h b/lld/ELF/Arch/Cheri.h index 036f02e01f615..a846273cd2e61 100644 --- a/lld/ELF/Arch/Cheri.h +++ b/lld/ELF/Arch/Cheri.h @@ -186,9 +186,9 @@ class CheriCapRelocsSection : public SyntheticSection { // +---------------------------------------+ // TODO: TLS caps also need to be per file/function -class CheriCapTableSection : public SyntheticSection { +class MipsCheriCapTableSection : public SyntheticSection { public: - CheriCapTableSection(Ctx &ctx); + MipsCheriCapTableSection(Ctx &ctx); // InputFile and Offset is needed in order to implement per-file/per-function // tables void addEntry(Symbol &sym, RelExpr expr, InputSectionBase *isec, @@ -266,7 +266,7 @@ class CheriCapTableSection : public SyntheticSection { CaptableMap dynTlsEntries; CaptableMap tlsEntries; bool valuesAssigned = false; - friend class CheriCapTableMappingSection; + friend class MipsCheriCapTableMappingSection; }; // TODO: could shrink these to reduce size overhead but this is experimental @@ -280,13 +280,13 @@ struct CaptableMappingEntry { // Map from symbol vaddr -> captable subset so that RTLD can setup the correct // trampolines to initialize $cgp to the correct subset -class CheriCapTableMappingSection : public SyntheticSection { +class MipsCheriCapTableMappingSection : public SyntheticSection { public: - CheriCapTableMappingSection(Ctx &ctx); + MipsCheriCapTableMappingSection(Ctx &ctx); bool isNeeded() const override { if (ctx.arg.capTableScope == CapTableScopePolicy::All) return false; - return ctx.in.cheriCapTable && ctx.in.cheriCapTable->isNeeded(); + return ctx.in.mipsCheriCapTable && ctx.in.mipsCheriCapTable->isNeeded(); } void writeTo(uint8_t *buf) override; size_t getSize() const override; diff --git a/lld/ELF/Arch/Mips.cpp b/lld/ELF/Arch/Mips.cpp index c99d104aea56e..93d6eb9ed904d 100644 --- a/lld/ELF/Arch/Mips.cpp +++ b/lld/ELF/Arch/Mips.cpp @@ -121,7 +121,7 @@ RelExpr MIPS::getRelExpr(RelType type, const Symbol &s, case R_MICROMIPS_GPREL7_S2: return RE_MIPS_GOTREL; case R_MIPS_CHERI_CAPTABLEREL16: - return R_CHERI_CAPABILITY_TABLE_REL; + return R_MIPS_CHERI_CAPTAB_REL; case R_MIPS_26: case R_MICROMIPS_26_S1: return R_PLT; @@ -210,16 +210,16 @@ RelExpr MIPS::getRelExpr(RelType type, const Symbol &s, return R_CHERI_CAPABILITY; case R_MIPS_CHERI_CAPTAB_LO16: case R_MIPS_CHERI_CAPTAB_HI16: - return R_CHERI_CAPABILITY_TABLE_INDEX; + return R_MIPS_CHERI_CAPTAB_INDEX; case R_MIPS_CHERI_CAPCALL_LO16: case R_MIPS_CHERI_CAPCALL_HI16: - return R_CHERI_CAPABILITY_TABLE_INDEX_CALL; + return R_MIPS_CHERI_CAPTAB_INDEX_CALL; case R_MIPS_CHERI_CAPTAB_CLC11: case R_MIPS_CHERI_CAPTAB20: - return R_CHERI_CAPABILITY_TABLE_INDEX_SMALL_IMMEDIATE; + return R_MIPS_CHERI_CAPTAB_INDEX_SMALL_IMMEDIATE; case R_MIPS_CHERI_CAPCALL_CLC11: case R_MIPS_CHERI_CAPCALL20: - return R_CHERI_CAPABILITY_TABLE_INDEX_CALL_SMALL_IMMEDIATE; + return R_MIPS_CHERI_CAPTAB_INDEX_CALL_SMALL_IMMEDIATE; case R_MIPS_CHERI_CAPTAB_TLS_GD_LO16: case R_MIPS_CHERI_CAPTAB_TLS_GD_HI16: return R_MIPS_CHERI_CAPTAB_TLSGD; diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 41a661706038d..49b9414ff0a53 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -78,9 +78,9 @@ class SymbolTableBaseSection; class SymtabShndxSection; class SyntheticSection; -class CheriCapTableSection; +class MipsCheriCapTableSection; class CheriCapRelocsSection; -class CheriCapTableMappingSection; +class MipsCheriCapTableMappingSection; enum ELFKind : uint8_t { ELFNoneKind, @@ -624,9 +624,9 @@ struct InStruct { std::unique_ptr gotPlt; std::unique_ptr igotPlt; std::unique_ptr relroPadding; - std::unique_ptr cheriCapTable; + std::unique_ptr mipsCheriCapTable; std::unique_ptr capRelocs; - std::unique_ptr cheriCapTableMapping; + std::unique_ptr mipsCheriCapTableMapping; std::unique_ptr armCmseSGSection; std::unique_ptr ppc64LongBranchTarget; std::unique_ptr mipsAbiFlags; @@ -713,7 +713,7 @@ struct Ctx : CommonLinkerContext { // _TLS_MODULE_BASE_ on targets that support TLSDESC. Defined *tlsModuleBase; - Defined *cheriCapabilityTable; + Defined *mipsCheriCapabilityTable; }; ElfSym sym{}; std::unique_ptr symtab; diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index 4a8f39a800443..7fc673eb3ba43 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -1014,28 +1014,28 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r, return ctx.in.got->getTlsIndexVA() + a - p; case R_CHERI_CAPABILITY: llvm_unreachable("R_CHERI_CAPABILITY should not be handled here!"); - case R_CHERI_CAPABILITY_TABLE_INDEX: - case R_CHERI_CAPABILITY_TABLE_INDEX_SMALL_IMMEDIATE: - case R_CHERI_CAPABILITY_TABLE_INDEX_CALL: - case R_CHERI_CAPABILITY_TABLE_INDEX_CALL_SMALL_IMMEDIATE: + case R_MIPS_CHERI_CAPTAB_INDEX: + case R_MIPS_CHERI_CAPTAB_INDEX_SMALL_IMMEDIATE: + case R_MIPS_CHERI_CAPTAB_INDEX_CALL: + case R_MIPS_CHERI_CAPTAB_INDEX_CALL_SMALL_IMMEDIATE: assert(a == 0 && "capability table index relocs should not have addends"); - return r.sym->getCapTableOffset(ctx, this, r.offset); - case R_CHERI_CAPABILITY_TABLE_REL: - if (!ctx.sym.cheriCapabilityTable) { + return r.sym->getMipsCheriCapTableOffset(ctx, this, r.offset); + case R_MIPS_CHERI_CAPTAB_REL: + if (!ctx.sym.mipsCheriCapabilityTable) { error("cannot compute difference between non-existent " "CheriCapabilityTable and symbol " + toStr(ctx, *r.sym)); return r.sym->getVA(ctx, a); } - return r.sym->getVA(ctx, a) - ctx.sym.cheriCapabilityTable->getVA(ctx); + return r.sym->getVA(ctx, a) - ctx.sym.mipsCheriCapabilityTable->getVA(ctx); case R_MIPS_CHERI_CAPTAB_TLSGD: assert(a == 0 && "capability table index relocs should not have addends"); - return ctx.in.cheriCapTable->getDynTlsOffset(*r.sym); + return ctx.in.mipsCheriCapTable->getDynTlsOffset(*r.sym); case R_MIPS_CHERI_CAPTAB_TLSLD: assert(a == 0 && "capability table index relocs should not have addends"); - return ctx.in.cheriCapTable->getTlsIndexOffset(); + return ctx.in.mipsCheriCapTable->getTlsIndexOffset(); case R_MIPS_CHERI_CAPTAB_TPREL: assert(a == 0 && "capability table index relocs should not have addends"); - return ctx.in.cheriCapTable->getTlsOffset(*r.sym); + return ctx.in.mipsCheriCapTable->getTlsOffset(*r.sym); // LO_I is used for both PCC and CGP-relative addresses. For backwards // compatibility, the symbol may be a CGP-relative symbol. In newer code, it // will always be the symbol containing the accompanying HI relocation. diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index 003387454e325..267b4091c9b99 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -217,7 +217,7 @@ static bool isRelExpr(RelExpr expr) { return oneof(expr); + RE_LOONGARCH_PAGE_PC, R_MIPS_CHERI_CAPTAB_REL>(expr); } static RelExpr toPlt(RelExpr expr) { @@ -1028,22 +1028,21 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym, uint64_t relOff) const { // These expressions always compute a constant - if (oneof(e)) + if (oneof< + R_GOTPLT, R_GOT_OFF, R_RELAX_HINT, RE_MIPS_GOT_LOCAL_PAGE, + RE_MIPS_GOTREL, RE_MIPS_GOT_OFF, RE_MIPS_GOT_OFF32, RE_MIPS_GOT_GP_PC, + RE_AARCH64_GOT_PAGE_PC, RE_AARCH64_AUTH_GOT_PAGE_PC, R_GOT_PC, + R_GOTONLY_PC, R_GOTPLTONLY_PC, R_PLT_PC, R_PLT_GOTREL, R_PLT_GOTPLT, + R_GOTPLT_GOTREL, R_GOTPLT_PC, RE_PPC32_PLTREL, RE_PPC64_CALL_PLT, + RE_PPC64_RELAX_TOC, RE_RISCV_ADD, RE_AARCH64_GOT_PAGE, + RE_AARCH64_AUTH_GOT, RE_AARCH64_AUTH_GOT_PC, RE_LOONGARCH_PLT_PAGE_PC, + RE_LOONGARCH_GOT, RE_LOONGARCH_GOT_PAGE_PC, R_MIPS_CHERI_CAPTAB_INDEX, + R_MIPS_CHERI_CAPTAB_INDEX_SMALL_IMMEDIATE, + R_MIPS_CHERI_CAPTAB_INDEX_CALL, + R_MIPS_CHERI_CAPTAB_INDEX_CALL_SMALL_IMMEDIATE, + R_MIPS_CHERI_CAPTAB_REL, R_CHERIOT_COMPARTMENT_CGPREL_HI, + R_CHERIOT_COMPARTMENT_CGPREL_LO_I, R_CHERIOT_COMPARTMENT_CGPREL_LO_S, + R_CHERIOT_COMPARTMENT_SIZE>(e)) return true; // Cheri capability relocations are never static link time constants since @@ -1160,12 +1159,12 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset, return; } - if (oneof(expr)) { + if (oneof(expr)) { std::lock_guard lock(ctx.relocMutex); - ctx.in.cheriCapTable->addEntry(sym, expr, sec, offset); + ctx.in.mipsCheriCapTable->addEntry(sym, expr, sec, offset); // Write out the index into the instruction sec->relocations.push_back({expr, type, offset, addend, &sym}); return; @@ -1389,17 +1388,17 @@ static unsigned handleMipsTlsRelocation(Ctx &ctx, RelType type, Symbol &sym, return 1; } if (expr == R_MIPS_CHERI_CAPTAB_TLSLD) { - ctx.in.cheriCapTable->addTlsIndex(); + ctx.in.mipsCheriCapTable->addTlsIndex(); c.relocations.push_back({expr, type, offset, addend, &sym}); return 1; } if (expr == R_MIPS_CHERI_CAPTAB_TLSGD) { - ctx.in.cheriCapTable->addDynTlsEntry(sym); + ctx.in.mipsCheriCapTable->addDynTlsEntry(sym); c.relocations.push_back({expr, type, offset, addend, &sym}); return 1; } if (expr == R_MIPS_CHERI_CAPTAB_TPREL) { - ctx.in.cheriCapTable->addTlsEntry(sym); + ctx.in.mipsCheriCapTable->addTlsEntry(sym); c.relocations.push_back({expr, type, offset, addend, &sym}); return 1; } diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h index bde63e051edba..d64d2e235f817 100644 --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -122,11 +122,11 @@ enum RelExpr { RE_RISCV_ADD, RE_RISCV_LEB128, RE_RISCV_PC_INDIRECT, - R_CHERI_CAPABILITY_TABLE_INDEX, - R_CHERI_CAPABILITY_TABLE_INDEX_SMALL_IMMEDIATE, - R_CHERI_CAPABILITY_TABLE_INDEX_CALL, - R_CHERI_CAPABILITY_TABLE_INDEX_CALL_SMALL_IMMEDIATE, - R_CHERI_CAPABILITY_TABLE_REL, // relative offset to _CHERI_CAPABILITY_TABLE_ + R_MIPS_CHERI_CAPTAB_INDEX, + R_MIPS_CHERI_CAPTAB_INDEX_SMALL_IMMEDIATE, + R_MIPS_CHERI_CAPTAB_INDEX_CALL, + R_MIPS_CHERI_CAPTAB_INDEX_CALL_SMALL_IMMEDIATE, + R_MIPS_CHERI_CAPTAB_REL, // relative offset to _CHERI_CAPABILITY_TABLE_ R_MIPS_CHERI_CAPTAB_TLSGD, R_MIPS_CHERI_CAPTAB_TLSLD, R_MIPS_CHERI_CAPTAB_TPREL, diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp index 9fa643dcbe14d..8271d1a5f2905 100644 --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -255,16 +255,17 @@ uint64_t Symbol::getPltVA(Ctx &ctx) const { return outVA; } -uint64_t Symbol::getCapTableVA(Ctx &ctx, const InputSectionBase *isec, - uint64_t offset) const { - return ctx.sym.cheriCapabilityTable->getVA(ctx) + - getCapTableOffset(ctx, isec, offset); +uint64_t Symbol::getMipsCheriCapTableVA(Ctx &ctx, const InputSectionBase *isec, + uint64_t offset) const { + return ctx.sym.mipsCheriCapabilityTable->getVA(ctx) + + getMipsCheriCapTableOffset(ctx, isec, offset); } -uint64_t Symbol::getCapTableOffset(Ctx &ctx, const InputSectionBase *isec, - uint64_t offset) const { +uint64_t Symbol::getMipsCheriCapTableOffset(Ctx &ctx, + const InputSectionBase *isec, + uint64_t offset) const { return ctx.arg.capabilitySize * - ctx.in.cheriCapTable->getIndex(*this, isec, offset); + ctx.in.mipsCheriCapTable->getIndex(*this, isec, offset); } uint64_t Defined::getSize(Ctx &ctx) const { diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index 843472b4d2cf1..ebf4325a0daf8 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -215,10 +215,11 @@ class Symbol { uint64_t getGotPltOffset(Ctx &) const; uint64_t getGotPltVA(Ctx &) const; uint64_t getPltVA(Ctx &) const; - uint64_t getCapTableVA(Ctx &ctx, const InputSectionBase *isec, - uint64_t offset) const; - uint64_t getCapTableOffset(Ctx &ctx, const InputSectionBase *isec, - uint64_t offset) const; + uint64_t getMipsCheriCapTableVA(Ctx &ctx, const InputSectionBase *isec, + + uint64_t offset) const; + uint64_t getMipsCheriCapTableOffset(Ctx &ctx, const InputSectionBase *isec, + uint64_t offset) const; uint64_t getSize(Ctx &ctx) const; OutputSection *getOutputSection() const; diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index 64580f73b86dd..319dea0683a68 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -1642,14 +1642,17 @@ DynamicSection::computeContents() { if (f->isNeeded) checkMipsShlibCompatible(ctx, f, f->cheriFlags, targetCheriFlags); } - if (ctx.in.cheriCapTable && ctx.in.cheriCapTable->isNeeded()) { - addInSec(DT_MIPS_CHERI_CAPTABLE, *ctx.in.cheriCapTable); - addInt(DT_MIPS_CHERI_CAPTABLESZ, ctx.in.cheriCapTable->getParent()->size); + if (ctx.in.mipsCheriCapTable && ctx.in.mipsCheriCapTable->isNeeded()) { + addInSec(DT_MIPS_CHERI_CAPTABLE, *ctx.in.mipsCheriCapTable); + addInt(DT_MIPS_CHERI_CAPTABLESZ, + ctx.in.mipsCheriCapTable->getParent()->size); } - if (ctx.in.cheriCapTableMapping && ctx.in.cheriCapTableMapping->isNeeded()) { - addInSec(DT_MIPS_CHERI_CAPTABLE_MAPPING, *ctx.in.cheriCapTableMapping); + if (ctx.in.mipsCheriCapTableMapping && + ctx.in.mipsCheriCapTableMapping->isNeeded()) { + addInSec(DT_MIPS_CHERI_CAPTABLE_MAPPING, + *ctx.in.mipsCheriCapTableMapping); addInt(DT_MIPS_CHERI_CAPTABLE_MAPPINGSZ, - ctx.in.cheriCapTableMapping->getParent()->size); + ctx.in.mipsCheriCapTableMapping->getParent()->size); } if (ctx.in.capRelocs && ctx.in.capRelocs->isNeeded()) { addInSec(DT_MIPS_CHERI___CAPRELOCS, *ctx.in.capRelocs); @@ -1798,10 +1801,10 @@ void RelocationBaseSection::finalizeContents() { if (ctx.in.relaPlt.get() == this && ctx.in.gotPlt->getParent()) { getParent()->flags |= ELF::SHF_INFO_LINK; // For CheriABI we use the captable as the sh_info value - if (ctx.arg.isCheriAbi && ctx.in.cheriCapTable && - ctx.in.cheriCapTable->isNeeded()) { - assert(ctx.in.cheriCapTable->getParent()->sectionIndex != UINT32_MAX); - getParent()->info = ctx.in.cheriCapTable->getParent()->sectionIndex; + if (ctx.arg.isCheriAbi && ctx.in.mipsCheriCapTable && + ctx.in.mipsCheriCapTable->isNeeded()) { + assert(ctx.in.mipsCheriCapTable->getParent()->sectionIndex != UINT32_MAX); + getParent()->info = ctx.in.mipsCheriCapTable->getParent()->sectionIndex; } else { getParent()->info = ctx.in.gotPlt->getParent()->sectionIndex; } @@ -4876,11 +4879,16 @@ template void elf::createSyntheticSections(Ctx &ctx) { if (ctx.arg.capabilitySize > 0) { ctx.in.capRelocs = std::make_unique(ctx, "__cap_relocs"); - ctx.in.cheriCapTable = std::make_unique(ctx); - add(*ctx.in.cheriCapTable); - if (ctx.arg.capTableScope != CapTableScopePolicy::All) { - ctx.in.cheriCapTableMapping = std::make_unique(ctx); - add(*ctx.in.cheriCapTableMapping); + + if (ctx.arg.emachine == EM_MIPS) { + ctx.in.mipsCheriCapTable = + std::make_unique(ctx); + add(*ctx.in.mipsCheriCapTable); + if (ctx.arg.capTableScope != CapTableScopePolicy::All) { + ctx.in.mipsCheriCapTableMapping = + std::make_unique(ctx); + add(*ctx.in.mipsCheriCapTableMapping); + } } } diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h index 5bad061a5ec59..f0a24973ec239 100644 --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -1426,8 +1426,8 @@ class PPC64LongBranchTargetSection final : public SyntheticSection { // Can only be forward declared here since it depends on SyntheticSection class CheriCapRelocsSection; -class CheriCapTableSection; -class CheriCapTableMappingSection; +class MipsCheriCapTableSection; +class MipsCheriCapTableMappingSection; template class PartitionElfHeaderSection final : public SyntheticSection { diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 0f7af3d8f03f4..f7fd98efda124 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -642,7 +642,8 @@ bool elf::isRelroSection(Ctx &ctx, const OutputSection *sec) { // Similarly the CHERI capability table is also relro since the capabilities // in the table need to be initialized at runtime to set the tag bits - if (ctx.in.cheriCapTable && sec == ctx.in.cheriCapTable->getParent()) { + if (ctx.in.mipsCheriCapTable && + sec == ctx.in.mipsCheriCapTable->getParent()) { // Without -z now, the PLT stubs can update the captable entries so we // can't mark it as relro. It can also be relro for static binaries: return ctx.arg.zNow || !ctx.arg.isPic; @@ -1910,13 +1911,13 @@ template void Writer::finalizeSections() { } } - if (ctx.in.cheriCapTable) { + if (ctx.arg.emachine == EM_MIPS && ctx.in.mipsCheriCapTable) { // When creating relocatable output we should not define the // _CHERI_CAPABILITY_TABLE_ symbol because otherwise we get duplicate // symbol errors when linking that into a final executable if (!ctx.arg.relocatable) - ctx.sym.cheriCapabilityTable = - addOptionalRegular(ctx, captableSym, ctx.in.cheriCapTable.get(), 0); + ctx.sym.mipsCheriCapabilityTable = addOptionalRegular( + ctx, captableSym, ctx.in.mipsCheriCapTable.get(), 0); } // This responsible for splitting up .eh_frame section into @@ -1962,18 +1963,19 @@ template void Writer::finalizeSections() { // Do the cap table index assignment // Must come before CapRelocs->finalizeContents() because it can add // __cap_relocs - if (ctx.in.cheriCapTable) { + if (ctx.in.mipsCheriCapTable) { // Ensure that we always have a _CHERI_CAPABILITY_TABLE_ symbol if the // cap table exists. This makes llvm-objdump more useful since it can now // print the target of a cap table load - if (!ctx.sym.cheriCapabilityTable && ctx.in.cheriCapTable->isNeeded()) { - ctx.sym.cheriCapabilityTable = cast( - ctx.symtab->addSymbol(Defined{ctx, nullptr, captableSym, STB_LOCAL, - STV_HIDDEN, STT_NOTYPE, 0, 0, ctx.in.cheriCapTable.get()})); - ctx.sym.cheriCapabilityTable->isSectionStartSymbol = true; - assert(!ctx.sym.cheriCapabilityTable->isPreemptible); + if (!ctx.sym.mipsCheriCapabilityTable && + ctx.in.mipsCheriCapTable->isNeeded()) { + ctx.sym.mipsCheriCapabilityTable = cast(ctx.symtab->addSymbol( + Defined{ctx, nullptr, captableSym, STB_LOCAL, STV_HIDDEN, + STT_NOTYPE, 0, 0, ctx.in.mipsCheriCapTable.get()})); + ctx.sym.mipsCheriCapabilityTable->isSectionStartSymbol = true; + assert(!ctx.sym.mipsCheriCapabilityTable->isPreemptible); } - ctx.in.cheriCapTable->assignValuesAndAddCapTableSymbols(); + ctx.in.mipsCheriCapTable->assignValuesAndAddCapTableSymbols(); } // Now handle __cap_relocs (must be before RelaDyn because it might @@ -2366,9 +2368,9 @@ template void Writer::addStartEndSymbols() { define("__fini_array_start", "__fini_array_end", ctx.out.finiArray); define("__ctors_start", "__ctors_end", findSection(ctx, ".ctors")); define("__dtors_start", "__dtors_end", findSection(ctx, ".dtors")); - if (ctx.in.cheriCapTable) + if (ctx.in.mipsCheriCapTable) define("__cap_table_start", "__cap_table_end", - ctx.in.cheriCapTable->getOutputSection()); + ctx.in.mipsCheriCapTable->getOutputSection()); // As a special case, don't unnecessarily retain .ARM.exidx, which would // create an empty PT_ARM_EXIDX.