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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 87 additions & 12 deletions lld/ELF/Arch/Cheri.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,19 @@ enum class CapRelocType {
CODE,
};

bool isCapRelocTypeExec(CapRelocType type) {
switch (type) {
case CapRelocType::DATA:
case CapRelocType::RODATA:
return false;
case CapRelocType::FUNC:
case CapRelocType::IFUNC:
case CapRelocType::CODE:
return true;
}
llvm_unreachable("unknown CapRelocType");
}

static CapRelocType getTargetType(const SymbolAndOffset &target) {
bool isFunc, isGnuIFunc, isTls;
OutputSection *os;
Expand Down Expand Up @@ -429,6 +442,13 @@ void CheriCapRelocsSection::writeToImpl(uint8_t *buf) {
}
uint64_t permissions = CapRelocPermission<ELFT>::encodeType(targetType);

// Use PCC bounds from the PT_CHERI_PCC segment.
if (PhdrEntry *ph = in.cheriBounds; ph && isCapRelocTypeExec(targetType)) {
targetOffset += targetVA - ph->p_vaddr;
targetVA = ph->p_vaddr;
targetSize = ph->p_memsz;
}

// TODO: should we warn about symbols that are out-of-bounds?
// mandoc seems to do it so I guess we need it
// if (TargetOffset < 0 || TargetOffset > TargetSize) warn(...);
Expand Down Expand Up @@ -910,12 +930,30 @@ static void getMipsCheriAbiVariant(std::optional<unsigned> &abi,
abi = static_cast<MipsAbiFlagsSection<ELFT> &>(sec).getCheriAbiVariant();
}

static bool isCheriMipsTrampolineAbi() {
if (config->emachine != EM_MIPS)
return false;

if (!in.mipsAbiFlags)
return false;

std::optional<unsigned> abi;
invokeELFT(getMipsCheriAbiVariant, abi, *in.mipsAbiFlags);
if (!abi)
return false;

if (*abi != DF_MIPS_CHERI_ABI_PLT && *abi != DF_MIPS_CHERI_ABI_FNDESC)
return false;

return true;
}

static bool needsCheriMipsTrampoline(RelType type, const Symbol &sym) {
// In the PLT ABI (and fndesc?) we have to use an elf relocation for function
// pointers to ensure that the runtime linker adds the required trampolines
// that sets $cgp:

if (config->emachine != EM_MIPS)
if (!isCheriMipsTrampolineAbi())
return false;

if (!sym.isFunc() || type == *target->symbolicCapCallRel)
Expand All @@ -928,17 +966,6 @@ static bool needsCheriMipsTrampoline(RelType type, const Symbol &sym) {
if (!hasDynamicLinker())
return false;

if (!in.mipsAbiFlags)
return false;

std::optional<unsigned> abi;
invokeELFT(getMipsCheriAbiVariant, abi, *in.mipsAbiFlags);
if (!abi)
return false;

if (*abi != DF_MIPS_CHERI_ABI_PLT && *abi != DF_MIPS_CHERI_ABI_FNDESC)
return false;

return true;
}

Expand Down Expand Up @@ -981,5 +1008,53 @@ void addRelativeCapabilityRelocation(
addend);
}

// CHERI-MIPS using the PLT and fndesc ABIs uses a different mechanism for
// determining the bounds of PCC.
bool needsCheriPccSegment() { return !isCheriMipsTrampolineAbi(); }

// Determine the required alignment for a single PT_CHERI_PCC segment. Apply
// the alignment to the first OutputSection and adjust the length of the padding
// section to align the end of the segment. Returns true if the alignment of
// the first OutputSection changed or the size of the padding section changed.
static bool alignPCCBounds(PhdrEntry *p, CheriPccPaddingSection &psec) {
OutputSection *first = p->firstSec;
OutputSection *last = p->lastSec;

if (!first)
return false;

assert(psec.getParent() == last && "padding section is not last");
assert(psec.isNeeded() && "padding section is not enabled");

// Ignore existing padding.
uint64_t size = last->getVA() - first->getVA();
uint64_t align = target->getCheriRequiredAlignment(size);
if (align == 0)
align = 1;
bool changed = false;
if (first->addralign < align) {
first->addralign = align;
if (first->ptLoad)
first->ptLoad->p_align =
std::max(first->ptLoad->p_align, first->addralign);
p->p_align = std::max(p->p_align, first->addralign);
changed = true;
}
uint64_t padSize = alignTo(size, align) - size;
if (psec.getSize() != padSize) {
psec.setSize(padSize);
changed = true;
}
return changed;
}

bool cheriCapabilityBoundsAlign() {
// Align the PT_CHERI_PCC segment.
bool changed = false;
if (in.cheriBounds)
changed |= alignPCCBounds(in.cheriBounds, *in.pccPadding);
return changed;
}

} // namespace elf
} // namespace lld
8 changes: 8 additions & 0 deletions lld/ELF/Arch/Cheri.h
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,14 @@ void addRelativeCapabilityRelocation(
InputSectionBase &isec, uint64_t offsetInSec,
llvm::PointerUnion<Symbol *, InputSectionBase *> symOrSec, int64_t addend,
RelExpr expr, RelType type);

bool needsCheriPccSegment();

// Align OutputSections as needed to ensure the bounds of capabilities
// such as PCC do not permit undesired access to portions of other
// OutputSections. Return true if the alignment of any OutputSection
// was modified.
bool cheriCapabilityBoundsAlign();
} // namespace elf
} // namespace lld

Expand Down
9 changes: 9 additions & 0 deletions lld/ELF/Arch/Mips.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "Target.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/CHERI/cheri-compressed-cap/cheri_compressed_cap.h"

using namespace llvm;
using namespace llvm::object;
Expand All @@ -26,6 +27,7 @@ template <class ELFT> class MIPS final : public TargetInfo {
MIPS();
uint32_t calcEFlags() const override;
int getCapabilitySize() const override;
uint64_t getCheriRequiredAlignment(uint64_t len) const override;
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
Expand Down Expand Up @@ -89,6 +91,13 @@ template <class ELFT> int MIPS<ELFT>::getCapabilitySize() const {
return 0;
}

template <class ELFT>
uint64_t MIPS<ELFT>::getCheriRequiredAlignment(uint64_t len) const {
if ((config->eflags & EF_MIPS_MACH) == EF_MIPS_MACH_CHERI128)
return cc128_get_required_alignment(len);
return 1;
}

template <class ELFT>
RelExpr MIPS<ELFT>::getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const {
Expand Down
9 changes: 9 additions & 0 deletions lld/ELF/Arch/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "llvm/CHERI/cheri-compressed-cap/cheri_compressed_cap.h"
#include "llvm/Support/ELFAttributes.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/RISCVAttributeParser.h"
Expand All @@ -32,6 +33,7 @@ class RISCV final : public TargetInfo {
RISCV();
uint32_t calcEFlags() const override;
int getCapabilitySize() const override;
uint64_t getCheriRequiredAlignment(uint64_t len) const override;
int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
void writeGotHeader(uint8_t *buf) const override;
void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
Expand Down Expand Up @@ -157,6 +159,13 @@ int RISCV::getCapabilitySize() const {
return config->is64 ? 16 : 8;
}

uint64_t RISCV::getCheriRequiredAlignment(uint64_t len) const {
if (config->is64)
return cc128_get_required_alignment(len);
else
return cc64_get_required_alignment(len);
}

uint32_t RISCV::calcEFlags() const {
// If there are only binary input files (from -b binary), use a
// value of 0 for the ELF header flags.
Expand Down
3 changes: 3 additions & 0 deletions lld/ELF/OutputSections.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ class OutputSection final : public SectionBase {
uint64_t addr = 0;
uint32_t shName = 0;

// Sections accessed using PCC on CHERI architectures.
std::atomic<bool> cheriPcc{false};

void recordSection(InputSectionBase *isec);
void commitSection(InputSection *isec);
void finalizeInputSections();
Expand Down
16 changes: 16 additions & 0 deletions lld/ELF/Relocations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1136,6 +1136,22 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset,
}
}

if (config->isCheriAbi && sym.isDefined() && (sec->flags & SHF_EXECINSTR) &&
oneof<R_PC, R_AARCH64_PAGE_PC>(expr)) {
OutputSection *osec = sym.getOutputSection();
if (osec == nullptr)
llvm_unreachable(
"PCC-accessed symbol defined in unsupported section type");
// TODO: Make this an error in future? Would need special relocation to
// allow bypassing for specific use cases (e.g. kernel startup code).
if ((osec->flags & SHF_WRITE) && !isRelroSection(osec))
warn("relocation " + toString(type) + " against symbol '" +
toString(sym) + "' in non-PCC section" +
getLocation(*sec, sym, offset));
else
osec->cheriPcc.store(true, std::memory_order_relaxed);
}

// We were asked not to generate PLT entries for ifuncs. Instead, pass the
// direct relocation on through.
if (LLVM_UNLIKELY(isIfunc) && config->zIfuncNoplt) {
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4069,6 +4069,7 @@ void InStruct::reset() {
strTab.reset();
symTab.reset();
symTabShndx.reset();
cheriBounds = nullptr;
}

constexpr char kMemtagAndroidNoteName[] = "Android";
Expand Down
20 changes: 20 additions & 0 deletions lld/ELF/SyntheticSections.h
Original file line number Diff line number Diff line change
Expand Up @@ -1275,6 +1275,23 @@ class PackageMetadataNote final : public SyntheticSection {
size_t getSize() const override;
};

class CheriPccPaddingSection final : public SyntheticSection {
public:
CheriPccPaddingSection()
: SyntheticSection(llvm::ELF::SHF_ALLOC, llvm::ELF::SHT_PROGBITS,
/*alignment=*/1, ".pad.cheri.pcc") {}

void writeTo(uint8_t *buf) override {}
void markNeeded() { needed = true; }
bool isNeeded() const override { return needed; }
size_t getSize() const override { return size; }
void setSize(uint64_t len) { size = len; }

private:
uint64_t size = 0;
bool needed = false;
};

InputSection *createInterpSection();
MergeInputSection *createCommentSection();
template <class ELFT> void splitSections();
Expand Down Expand Up @@ -1336,6 +1353,7 @@ struct InStruct {
std::unique_ptr<IgotPltSection> igotPlt;
std::unique_ptr<TgotSection> tgot;
std::unique_ptr<MipsCheriCapTableSection> mipsCheriCapTable;
std::unique_ptr<CheriPccPaddingSection> pccPadding;
std::unique_ptr<CheriCapRelocsSection> capRelocs;
std::unique_ptr<CheriCapRelocsSection> tgotCapRelocs;
// For per-file/per-function tables:
Expand All @@ -1361,6 +1379,8 @@ struct InStruct {
std::unique_ptr<SymbolTableBaseSection> symTab;
std::unique_ptr<SymtabShndxSection> symTabShndx;

PhdrEntry *cheriBounds;

void reset();
};

Expand Down
4 changes: 4 additions & 0 deletions lld/ELF/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ ErrorPlace elf::getErrorPlace(const uint8_t *loc) {

TargetInfo::~TargetInfo() {}

uint64_t TargetInfo::getCheriRequiredAlignment(uint64_t len) const {
llvm_unreachable("Target does not provide required Cheri alignment");
}

int64_t TargetInfo::getImplicitAddend(const uint8_t *buf, RelType type) const {
internalLinkerError(getErrorLocation(buf),
"cannot read addend for relocation " + toString(type));
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class TargetInfo {
public:
virtual uint32_t calcEFlags() const { return 0; }
virtual int getCapabilitySize() const { return 0; }
virtual uint64_t getCheriRequiredAlignment(uint64_t len) const;
virtual RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const = 0;
virtual RelType getDynRel(RelType type) const { return 0; }
Expand Down
Loading