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
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -4642,6 +4642,12 @@ def CheriConditionalSeal : Builtin {
let Prototype = "void* __capability(void const* __capability,void const* __capability)";
}

def CHERIoTEmitSealingType : Builtin {
let Spellings = ["__builtin_cheriot_sealing_type"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Prototype = "void*(char const*)";
}

// Safestack builtins.
def GetUnsafeStackStart : Builtin {
let Spellings = ["__builtin___get_unsafe_stack_start"];
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ def err_cheriot_non_addr_of_expr_on_sealed
"the only valid operation on a sealed value is to take its address">;
def err_cheriot_invalid_sealed_declaration
: Error<"cannot declare a sealed variable as %0">;
def err_cheriot_invalid_sealing_key_type_name
: Error<"the sealing key type name '%0' is not a valid identifier">;
def err_cheriot_use_of_builtin_sealing_key_type_no_compartment
: Error<"%0 used, but no compartment name given">;

// C99 variable-length arrays
def ext_vla : Extension<"variable length arrays are a C99 feature">,
Expand Down
47 changes: 47 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6154,6 +6154,53 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
return RValue::get(Builder.CreateIntrinsic(
llvm::Intrinsic::cheri_cap_perms_check, {SizeTy}, {Cap, Perms}));
}
case Builtin::BI__builtin_cheriot_sealing_type: {
const auto *ArgLit = dyn_cast<StringLiteral>(E->getArg(0));
assert(ArgLit && "Argument to built-in should be a string literal!");

auto SealingTypeName = ArgLit->getString().str();
auto CompartmentName = getLangOpts().CheriCompartmentName;
auto PrefixedImportName =
CHERIoTSealingKeyTypeAttr::getSealingTypeSymbolName(CompartmentName,
SealingTypeName);
auto &Mod = CGM.getModule();
auto MangledImportName = "__import." + PrefixedImportName;
auto *GV = Mod.getGlobalVariable(MangledImportName);

if (!GV) {
// This global exists only so that we have a pointer to it and is more
// akin to a GOT entry than a "real" global value: it just
// represents a pointer that we have a way of getting.

auto *OpaqueTypeName = "struct.__CHERIoT__OpaqueSealingKeyType";
llvm::StructType *OpaqueType =
llvm::StructType::getTypeByName(CGM.getLLVMContext(), OpaqueTypeName);

if (!OpaqueType)
OpaqueType = llvm::StructType::create(CGM.getModule().getContext(),
OpaqueTypeName);
else if (!OpaqueType->isOpaque())
CGM.Error(E->getBeginLoc(),
std::string("type '") + OpaqueTypeName +
"' already exists, but is not an opaque type!");

GV = new GlobalVariable(CGM.getModule(), OpaqueType, true,
GlobalValue::ExternalLinkage, nullptr,
MangledImportName);
GV->addAttribute(CHERIoTSealingKeyTypeAttr::getAttrName(),
PrefixedImportName);
GV->addAttribute("cheri-compartment", CompartmentName);
} else {
auto *GVType = dyn_cast<StructType>(GV->getValueType());

if (!GVType || !GVType->isOpaque())
CGM.Error(E->getBeginLoc(),
std::string("global variable '") + MangledImportName +
"' already exists, but its type is not opaque!");
}

return RValue::get(GV);
}
// Round to capability precision:
// TODO: should we handle targets that don't have any precision constraints
// here or in the backend?
Expand Down
24 changes: 24 additions & 0 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2920,6 +2920,30 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
return ExprError();
break;

case Builtin::BI__builtin_cheriot_sealing_type: {
if (checkArgCount(TheCall, 1))
return ExprError();
auto *Arg = dyn_cast<StringLiteral>(TheCall->getArg(0));
if (!Arg)
return ExprError();
auto ArgLit = Arg->getString();
if (!isValidAsciiIdentifier(ArgLit)) {
std::string Escaped;
llvm::raw_string_ostream OS(Escaped);
llvm::printEscapedString(ArgLit, OS);
OS.flush();
Diag(Arg->getExprLoc(), diag::err_cheriot_invalid_sealing_key_type_name)
<< Escaped;
return ExprError();
}
if (getLangOpts().CheriCompartmentName.empty()) {
Diag(Arg->getExprLoc(),
diag::err_cheriot_use_of_builtin_sealing_key_type_no_compartment)
<< Context.BuiltinInfo.getName(
Builtin::BI__builtin_cheriot_sealing_type);
}
break;
}
// OpenCL v2.0, s6.13.16 - Pipe functions
case Builtin::BIread_pipe:
case Builtin::BIwrite_pipe:
Expand Down
51 changes: 51 additions & 0 deletions clang/test/CodeGen/cheri/riscv/cheriot-sealing-type-builtin.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// RUN: %clang_cc1 %s -o - "-triple" "riscv32cheriot-unknown-unknown-cheriotrtos" "-emit-llvm" "-cheri-compartment=static_sealing_test" "-mframe-pointer=none" "-mcmodel=small" "-target-abi" "cheriot" "-O0" "-Werror" -std=c2x | FileCheck %s


struct StructSealingKey { };
enum EnumSealingKey { SEALING_KEY_KIND1, SEALING_KEY_KIND2 };
typedef enum EnumSealingKey TypeDefSealingKey;

// CHECK: %struct.__CHERIoT__OpaqueSealingKeyType = type opaque
// CHECK: @__import.sealing_type.static_sealing_test.StructSealingKey = external addrspace(200) constant %struct.__CHERIoT__OpaqueSealingKeyType #0
// CHECK: @__import.sealing_type.static_sealing_test.EnumSealingKey = external addrspace(200) constant %struct.__CHERIoT__OpaqueSealingKeyType #1
// CHECK: @__import.sealing_type.static_sealing_test.TypeDefSealingKey = external addrspace(200) constant %struct.__CHERIoT__OpaqueSealingKeyType #2
// CHECK: @__import.sealing_type.static_sealing_test.int = external addrspace(200) constant %struct.__CHERIoT__OpaqueSealingKeyType #3

// CHECK: define dso_local void @func() addrspace(200) #4 {
void func() {

// CHECK: entry:
// CHECK: %SealingKey1 = alloca ptr addrspace(200), align 8, addrspace(200)
// CHECK: %SealingKey2 = alloca ptr addrspace(200), align 8, addrspace(200)
// CHECK: %SealingKey3 = alloca ptr addrspace(200), align 8, addrspace(200)
// CHECK: %SealingKey4 = alloca ptr addrspace(200), align 8, addrspace(200)
// CHECK: %SealingKey5 = alloca ptr addrspace(200), align 8, addrspace(200)
// CHECK: %SealingKey6 = alloca ptr addrspace(200), align 8, addrspace(200)
// CHECK: %SealingKey7 = alloca ptr addrspace(200), align 8, addrspace(200)

// CHECK: store ptr addrspace(200) @__import.sealing_type.static_sealing_test.StructSealingKey, ptr addrspace(200) %SealingKey1, align 8
struct StructSealingKey *SealingKey1 = __builtin_cheriot_sealing_type("StructSealingKey");

// CHECK: store ptr addrspace(200) @__import.sealing_type.static_sealing_test.EnumSealingKey, ptr addrspace(200) %SealingKey2, align 8
enum EnumSealingKey *SealingKey2 = __builtin_cheriot_sealing_type("EnumSealingKey");

// CHECK: store ptr addrspace(200) @__import.sealing_type.static_sealing_test.TypeDefSealingKey, ptr addrspace(200) %SealingKey3, align 8
TypeDefSealingKey *SealingKey3 = __builtin_cheriot_sealing_type("TypeDefSealingKey");

// CHECK: store ptr addrspace(200) @__import.sealing_type.static_sealing_test.int, ptr addrspace(200) %SealingKey4, align 8
int *SealingKey4 = __builtin_cheriot_sealing_type("int");

// CHECK: store ptr addrspace(200) @__import.sealing_type.static_sealing_test.int, ptr addrspace(200) %SealingKey5, align 8
int *SealingKey5 = __builtin_cheriot_sealing_type("int");
// CHECK: store ptr addrspace(200) @__import.sealing_type.static_sealing_test.int, ptr addrspace(200) %SealingKey6, align 8
int *SealingKey6 = __builtin_cheriot_sealing_type("int");
// CHECK: store ptr addrspace(200) @__import.sealing_type.static_sealing_test.int, ptr addrspace(200) %SealingKey7, align 8
int *SealingKey7 = __builtin_cheriot_sealing_type("int");

// CHECK: ret void
}

// CHECK: attributes #0 = { "cheri-compartment"="static_sealing_test" "cheriot_sealing_key"="sealing_type.static_sealing_test.StructSealingKey" }
// CHECK: attributes #1 = { "cheri-compartment"="static_sealing_test" "cheriot_sealing_key"="sealing_type.static_sealing_test.EnumSealingKey" }
// CHECK: attributes #2 = { "cheri-compartment"="static_sealing_test" "cheriot_sealing_key"="sealing_type.static_sealing_test.TypeDefSealingKey" }
// CHECK: attributes #3 = { "cheri-compartment"="static_sealing_test" "cheriot_sealing_key"="sealing_type.static_sealing_test.int" }
12 changes: 12 additions & 0 deletions clang/test/Sema/cheri/cheriot-sealing-type-builtin.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// RUN: %riscv32_cheri_cc1 "-triple" "riscv32cheriot-unknown-unknown" "-target-abi" "cheriot" -verify %s

void func() {
int *k1 = __builtin_cheriot_sealing_type(MySealingType); // expected-error{{use of undeclared identifier 'MySealingType'}}
int *k2 = __builtin_cheriot_sealing_type("This is my key"); // expected-error{{the sealing key type name 'This is my key' is not a valid identifier}}
int *k3 = __builtin_cheriot_sealing_type("αβγδ"); // expected-error{{the sealing key type name '\CE\B1\CE\B2\CE\B3\CE\B4' is not a valid identifier}}
int *k4 = __builtin_cheriot_sealing_type("a\0b"); // expected-error{{the sealing key type name 'a\00b' is not a valid identifier}}
int *k5 = __builtin_cheriot_sealing_type("a\"b"); // expected-error{{the sealing key type name 'a\22b' is not a valid identifier}}
int *k6 = __builtin_cheriot_sealing_type("a\nb"); // expected-error{{the sealing key type name 'a\0Ab' is not a valid identifier}}
int *k7 = __builtin_cheriot_sealing_type("a\bb"); // expected-error{{the sealing key type name 'a\08b' is not a valid identifier}}
int *k8 = __builtin_cheriot_sealing_type("ab"); // expected-error{{__builtin_cheriot_sealing_type used, but no compartment name given}}
}
17 changes: 17 additions & 0 deletions llvm/include/llvm/IR/Attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1614,6 +1614,23 @@ class CHERIoTSealedValueAttr {
/// Get the name of the attribute.
static const std::string getAttrName() { return "cheriot_sealed_value"; }
};

/// Represents the LLVM-level attribute that is used to signal
/// that a global (variable) represents a sealing key type.
class CHERIoTSealingKeyTypeAttr {
public:
/// Get the name of the attribute.
static const std::string getAttrName() { return "cheriot_sealing_key"; }

/// Get the mangled name of the symbol representing the sealing key.
static const std::string
getSealingTypeSymbolName(StringRef CompartmentName,
StringRef SealingKeyTypeName) {
return "sealing_type." + CompartmentName.str() + "." +
SealingKeyTypeName.str();
}
};

} // end namespace llvm

#endif // LLVM_IR_ATTRIBUTES_H
97 changes: 76 additions & 21 deletions llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,10 @@ class RISCVAsmPrinter : public AsmPrinter {

private:
/**
* Struct describing compartment exports that must be emitted for this
* compilation unit.
* Struct describing function-like compartment export objects.
*/
struct CompartmentExport
{
/// The compartment name for the function.
struct FunctionCompartmentExport {
/// The compartment name for the object.
std::string CompartmentName;
/// The IR function corresponding to the function.
const Function &Fn;
Expand All @@ -132,9 +130,26 @@ class RISCVAsmPrinter : public AsmPrinter {
/// The size in bytes of the stack frame, 0 if not used.
uint32_t stackSize = 0;
};
SmallVector<CompartmentExport, 1> CompartmentEntries;

/**
* Struct describing sealing key-like compartment export objects.
*/
struct SealingKeyCompartmentExport {
/// The compartment name for the object.
std::string CompartmentName;
/// The name of the sealing key type.
std::string SealingKeyTypeName;

bool operator==(const SealingKeyCompartmentExport &Other) {
return this->CompartmentName == Other.CompartmentName &&
this->SealingKeyTypeName == Other.SealingKeyTypeName;
}
};

SmallVector<FunctionCompartmentExport, 1> FNCompartmentEntries;
SmallVector<SealingKeyCompartmentExport, 1> SKCompartmentEntries;
SmallDenseMap<const Function *, SmallVector<const GlobalAlias *, 1>, 1>
CompartmentEntryAliases;
FnCompartmentEntryAliases;

void emitAttributes(const MCSubtargetInfo &SubtargetInfo);

Expand Down Expand Up @@ -312,6 +327,20 @@ void RISCVAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) {
return;
}

auto CheriotSealingKeyTypeAttrName =
llvm::CHERIoTSealingKeyTypeAttr::getAttrName();

if (GV->hasAttribute(CheriotSealingKeyTypeAttrName)) {
auto Attr = GV->getAttribute(CheriotSealingKeyTypeAttrName);
auto Entry = SealingKeyCompartmentExport{
std::string(GV->getAttribute("cheri-compartment").getValueAsString()),
Attr.getValueAsString().str()};
if (std::find(SKCompartmentEntries.begin(), SKCompartmentEntries.end(),
Entry) == SKCompartmentEntries.end()) {
SKCompartmentEntries.push_back(Entry);
}
}

// Continue through the normal path to emit the global.
return AsmPrinter::emitGlobalVariable(GV);
}
Expand All @@ -320,9 +349,12 @@ void RISCVAsmPrinter::emitGlobalAlias(const Module &M, const GlobalAlias &GA) {
AsmPrinter::emitGlobalAlias(M, GA);

const MCSubtargetInfo &SubtargetInfo = *TM.getMCSubtargetInfo();
auto CheriotSealingKeyTypeAttrName =
llvm::CHERIoTSealingKeyTypeAttr::getAttrName();

if (SubtargetInfo.hasFeature(RISCV::FeatureVendorXCheriot)) {
if (auto *Fn = dyn_cast<Function>(GA.getAliasee()->stripPointerCasts())) {
CompartmentEntryAliases[Fn].push_back(&GA);
FnCompartmentEntryAliases[Fn].push_back(&GA);
}
}
}
Expand Down Expand Up @@ -582,17 +614,17 @@ bool RISCVAsmPrinter::runOnMachineFunction(MachineFunction &MF) {
} else
stackSize = MF.getFrameInfo().getStackSize();
// FIXME: Get stack size as function attribute if specified
CompartmentEntries.push_back(
FNCompartmentEntries.push_back(
{std::string(Fn.getFnAttribute("cheri-compartment").getValueAsString()),
Fn, OutStreamer->getContext().getOrCreateSymbol(MF.getName()),
countUsedArgRegisters(MF) + interruptFlag, false, stackSize});
} else if (Fn.getCallingConv() == CallingConv::CHERI_LibCall)
CompartmentEntries.push_back(
FNCompartmentEntries.push_back(
{"libcalls", Fn,
OutStreamer->getContext().getOrCreateSymbol(MF.getName()),
countUsedArgRegisters(MF) + interruptFlag});
else if (interruptFlag != 0)
CompartmentEntries.push_back(
FNCompartmentEntries.push_back(
{std::string(Fn.getFnAttribute("cheri-compartment").getValueAsString()),
Fn, OutStreamer->getContext().getOrCreateSymbol(MF.getName()),
countUsedArgRegisters(MF) + interruptFlag, true});
Expand Down Expand Up @@ -698,21 +730,21 @@ void RISCVAsmPrinter::emitEndOfAsmFile(Module &M) {
RISCVTargetStreamer &RTS =
static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer());

if (!CompartmentEntries.empty()) {
if (!FNCompartmentEntries.empty()) {
auto &C = OutStreamer->getContext();
auto *Exports = C.getELFSection(".compartment_exports", ELF::SHT_PROGBITS,
ELF::SHF_ALLOC | ELF::SHF_GNU_RETAIN);
OutStreamer->switchSection(Exports);
auto CompartmentStartSym = C.getOrCreateSymbol("__compartment_pcc_start");
for (auto &Entry : CompartmentEntries) {
for (auto &Entry : FNCompartmentEntries) {
auto *Exports = C.getELFSection(".compartment_exports", ELF::SHT_PROGBITS,
ELF::SHF_ALLOC | ELF::SHF_GNU_RETAIN);
OutStreamer->switchSection(Exports);
std::string ExportName = getImportExportTableName(
Entry.CompartmentName, Entry.Fn.getName(), Entry.Fn.getCallingConv(),
/*IsImport*/ false);
auto Sym = C.getOrCreateSymbol(ExportName);
OutStreamer->emitSymbolAttribute(Sym, MCSA_ELF_TypeObject);
// If the function isn't global, don't make its export table entry global
// either. Two different compilation units in the same compartment may
// export different static things.
// If the function isn't global, don't make its export table entry
// global either. Two different compilation units in the same
// compartment may export different static things.
if (Entry.Fn.hasExternalLinkage() && !Entry.forceLocal)
OutStreamer->emitSymbolAttribute(Sym, MCSA_Global);
OutStreamer->emitValueToAlignment(Align(4));
Expand All @@ -727,8 +759,8 @@ void RISCVAsmPrinter::emitEndOfAsmFile(Module &M) {
OutStreamer->emitELFSize(Sym, MCConstantExpr::create(4, C));

// Emit aliases for this export symbol entry.
auto I = CompartmentEntryAliases.find(&Entry.Fn);
if (I == CompartmentEntryAliases.end())
auto I = FnCompartmentEntryAliases.find(&Entry.Fn);
if (I == FnCompartmentEntryAliases.end())
continue;
for (const GlobalAlias *GA : I->second) {
std::string AliasExportName = getImportExportTableName(
Expand All @@ -747,6 +779,29 @@ void RISCVAsmPrinter::emitEndOfAsmFile(Module &M) {
}
}
}

if (!SKCompartmentEntries.empty()) {
auto &C = OutStreamer->getContext();
for (auto &Entry : SKCompartmentEntries) {
auto ExportName = Entry.SealingKeyTypeName;
auto MangledExportName = "__export." + ExportName;
auto *Sym = C.getOrCreateSymbol(MangledExportName);
auto *Exports = C.getELFSection(
".compartment_exports." + ExportName, ELF::SHT_PROGBITS,
ELF::SHF_ALLOC | ELF::SHF_WRITE | ELF::SHF_GROUP, 0, ExportName,
true);
OutStreamer->switchSection(Exports);
OutStreamer->emitSymbolAttribute(Sym, MCSA_ELF_TypeObject);
OutStreamer->emitSymbolAttribute(Sym, MCSA_Global);
OutStreamer->emitValueToAlignment(Align(4));
OutStreamer->emitLabel(Sym);
OutStreamer->emitIntValue(0, 2);
OutStreamer->emitIntValue(0, 1);
OutStreamer->emitIntValue(0b100000, 1);
OutStreamer->emitELFSize(Sym, MCConstantExpr::create(4, C));
}
}

// Generate CHERIoT imports if there are any.
auto &CHERIoTCompartmentImports =
static_cast<RISCVTargetMachine &>(TM).ImportedObjects;
Expand Down
Loading