Skip to content

Commit 2aa7cfa

Browse files
committed
[CHERIoT] Add __builtin_cheriot_sealing_type clang built-in
1 parent 6053c45 commit 2aa7cfa

File tree

11 files changed

+404
-24
lines changed

11 files changed

+404
-24
lines changed

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4642,6 +4642,12 @@ def CheriConditionalSeal : Builtin {
46424642
let Prototype = "void* __capability(void const* __capability,void const* __capability)";
46434643
}
46444644

4645+
def CHERIoTEmitSealingType : Builtin {
4646+
let Spellings = ["__builtin_cheriot_sealing_type"];
4647+
let Attributes = [NoThrow, Const, CustomTypeChecking];
4648+
let Prototype = "void*(char const*)";
4649+
}
4650+
46454651
// Safestack builtins.
46464652
def GetUnsafeStackStart : Builtin {
46474653
let Spellings = ["__builtin___get_unsafe_stack_start"];

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ def err_cheriot_non_addr_of_expr_on_sealed
155155
"the only valid operation on a sealed value is to take its address">;
156156
def err_cheriot_invalid_sealed_declaration
157157
: Error<"cannot declare a sealed variable as %0">;
158+
def err_cheriot_invalid_sealing_key_type_name
159+
: Error<"the sealing key type name '%0' is not a valid identifier">;
160+
def err_cheriot_use_of_builtin_sealing_key_type_no_compartment
161+
: Error<"%0 used, but no compartment name given">;
158162

159163
// C99 variable-length arrays
160164
def ext_vla : Extension<"variable length arrays are a C99 feature">,

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6154,6 +6154,53 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
61546154
return RValue::get(Builder.CreateIntrinsic(
61556155
llvm::Intrinsic::cheri_cap_perms_check, {SizeTy}, {Cap, Perms}));
61566156
}
6157+
case Builtin::BI__builtin_cheriot_sealing_type: {
6158+
const auto *ArgLit = dyn_cast<StringLiteral>(E->getArg(0));
6159+
assert(ArgLit && "Argument to built-in should be a string literal!");
6160+
6161+
auto SealingTypeName = ArgLit->getString().str();
6162+
auto CompartmentName = getLangOpts().CheriCompartmentName;
6163+
auto PrefixedImportName =
6164+
CHERIoTSealingKeyTypeAttr::getSealingTypeSymbolName(CompartmentName,
6165+
SealingTypeName);
6166+
auto &Mod = CGM.getModule();
6167+
auto MangledImportName = "__import." + PrefixedImportName;
6168+
auto *GV = Mod.getGlobalVariable(MangledImportName);
6169+
6170+
if (!GV) {
6171+
// This global exists only so that we have a pointer to it and is more
6172+
// akin to a GOT entry than a "real" global value: it just
6173+
// represents a pointer that we have a way of getting.
6174+
6175+
auto *OpaqueTypeName = "struct.__CHERIoT__OpaqueSealingKeyType";
6176+
llvm::StructType *OpaqueType =
6177+
llvm::StructType::getTypeByName(CGM.getLLVMContext(), OpaqueTypeName);
6178+
6179+
if (!OpaqueType)
6180+
OpaqueType = llvm::StructType::create(CGM.getModule().getContext(),
6181+
OpaqueTypeName);
6182+
else if (!OpaqueType->isOpaque())
6183+
CGM.Error(E->getBeginLoc(),
6184+
std::string("type '") + OpaqueTypeName +
6185+
"' already exists, but is not an opaque type!");
6186+
6187+
GV = new GlobalVariable(CGM.getModule(), OpaqueType, true,
6188+
GlobalValue::ExternalLinkage, nullptr,
6189+
MangledImportName);
6190+
GV->addAttribute(CHERIoTSealingKeyTypeAttr::getAttrName(),
6191+
PrefixedImportName);
6192+
GV->addAttribute("cheri-compartment", CompartmentName);
6193+
} else {
6194+
auto *GVType = dyn_cast<StructType>(GV->getValueType());
6195+
6196+
if (!GVType || !GVType->isOpaque())
6197+
CGM.Error(E->getBeginLoc(),
6198+
std::string("global variable '") + MangledImportName +
6199+
"' already exists, but its type is not opaque!");
6200+
}
6201+
6202+
return RValue::get(GV);
6203+
}
61576204
// Round to capability precision:
61586205
// TODO: should we handle targets that don't have any precision constraints
61596206
// here or in the backend?

clang/lib/Sema/SemaChecking.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2920,6 +2920,30 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
29202920
return ExprError();
29212921
break;
29222922

2923+
case Builtin::BI__builtin_cheriot_sealing_type: {
2924+
if (checkArgCount(TheCall, 1))
2925+
return ExprError();
2926+
auto *Arg = dyn_cast<StringLiteral>(TheCall->getArg(0));
2927+
if (!Arg)
2928+
return ExprError();
2929+
auto ArgLit = Arg->getString();
2930+
if (!isValidAsciiIdentifier(ArgLit)) {
2931+
std::string Escaped;
2932+
llvm::raw_string_ostream OS(Escaped);
2933+
llvm::printEscapedString(ArgLit, OS);
2934+
OS.flush();
2935+
Diag(Arg->getExprLoc(), diag::err_cheriot_invalid_sealing_key_type_name)
2936+
<< Escaped;
2937+
return ExprError();
2938+
}
2939+
if (getLangOpts().CheriCompartmentName.empty()) {
2940+
Diag(Arg->getExprLoc(),
2941+
diag::err_cheriot_use_of_builtin_sealing_key_type_no_compartment)
2942+
<< Context.BuiltinInfo.getName(
2943+
Builtin::BI__builtin_cheriot_sealing_type);
2944+
}
2945+
break;
2946+
}
29232947
// OpenCL v2.0, s6.13.16 - Pipe functions
29242948
case Builtin::BIread_pipe:
29252949
case Builtin::BIwrite_pipe:
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// 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
2+
3+
4+
struct StructSealingKey { };
5+
enum EnumSealingKey { SEALING_KEY_KIND1, SEALING_KEY_KIND2 };
6+
typedef enum EnumSealingKey TypeDefSealingKey;
7+
8+
// CHECK: %struct.__CHERIoT__OpaqueSealingKeyType = type opaque
9+
// CHECK: @__import.sealing_type.static_sealing_test.StructSealingKey = external addrspace(200) constant %struct.__CHERIoT__OpaqueSealingKeyType #0
10+
// CHECK: @__import.sealing_type.static_sealing_test.EnumSealingKey = external addrspace(200) constant %struct.__CHERIoT__OpaqueSealingKeyType #1
11+
// CHECK: @__import.sealing_type.static_sealing_test.TypeDefSealingKey = external addrspace(200) constant %struct.__CHERIoT__OpaqueSealingKeyType #2
12+
// CHECK: @__import.sealing_type.static_sealing_test.int = external addrspace(200) constant %struct.__CHERIoT__OpaqueSealingKeyType #3
13+
14+
// CHECK: define dso_local void @func() addrspace(200) #4 {
15+
void func() {
16+
17+
// CHECK: entry:
18+
// CHECK: %SealingKey1 = alloca ptr addrspace(200), align 8, addrspace(200)
19+
// CHECK: %SealingKey2 = alloca ptr addrspace(200), align 8, addrspace(200)
20+
// CHECK: %SealingKey3 = alloca ptr addrspace(200), align 8, addrspace(200)
21+
// CHECK: %SealingKey4 = alloca ptr addrspace(200), align 8, addrspace(200)
22+
// CHECK: %SealingKey5 = alloca ptr addrspace(200), align 8, addrspace(200)
23+
// CHECK: %SealingKey6 = alloca ptr addrspace(200), align 8, addrspace(200)
24+
// CHECK: %SealingKey7 = alloca ptr addrspace(200), align 8, addrspace(200)
25+
26+
// CHECK: store ptr addrspace(200) @__import.sealing_type.static_sealing_test.StructSealingKey, ptr addrspace(200) %SealingKey1, align 8
27+
struct StructSealingKey *SealingKey1 = __builtin_cheriot_sealing_type("StructSealingKey");
28+
29+
// CHECK: store ptr addrspace(200) @__import.sealing_type.static_sealing_test.EnumSealingKey, ptr addrspace(200) %SealingKey2, align 8
30+
enum EnumSealingKey *SealingKey2 = __builtin_cheriot_sealing_type("EnumSealingKey");
31+
32+
// CHECK: store ptr addrspace(200) @__import.sealing_type.static_sealing_test.TypeDefSealingKey, ptr addrspace(200) %SealingKey3, align 8
33+
TypeDefSealingKey *SealingKey3 = __builtin_cheriot_sealing_type("TypeDefSealingKey");
34+
35+
// CHECK: store ptr addrspace(200) @__import.sealing_type.static_sealing_test.int, ptr addrspace(200) %SealingKey4, align 8
36+
int *SealingKey4 = __builtin_cheriot_sealing_type("int");
37+
38+
// CHECK: store ptr addrspace(200) @__import.sealing_type.static_sealing_test.int, ptr addrspace(200) %SealingKey5, align 8
39+
int *SealingKey5 = __builtin_cheriot_sealing_type("int");
40+
// CHECK: store ptr addrspace(200) @__import.sealing_type.static_sealing_test.int, ptr addrspace(200) %SealingKey6, align 8
41+
int *SealingKey6 = __builtin_cheriot_sealing_type("int");
42+
// CHECK: store ptr addrspace(200) @__import.sealing_type.static_sealing_test.int, ptr addrspace(200) %SealingKey7, align 8
43+
int *SealingKey7 = __builtin_cheriot_sealing_type("int");
44+
45+
// CHECK: ret void
46+
}
47+
48+
// CHECK: attributes #0 = { "cheri-compartment"="static_sealing_test" "cheriot_sealing_key"="sealing_type.static_sealing_test.StructSealingKey" }
49+
// CHECK: attributes #1 = { "cheri-compartment"="static_sealing_test" "cheriot_sealing_key"="sealing_type.static_sealing_test.EnumSealingKey" }
50+
// CHECK: attributes #2 = { "cheri-compartment"="static_sealing_test" "cheriot_sealing_key"="sealing_type.static_sealing_test.TypeDefSealingKey" }
51+
// CHECK: attributes #3 = { "cheri-compartment"="static_sealing_test" "cheriot_sealing_key"="sealing_type.static_sealing_test.int" }
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %riscv32_cheri_cc1 "-triple" "riscv32cheriot-unknown-unknown" "-target-abi" "cheriot" -verify %s
2+
3+
void func() {
4+
int *k1 = __builtin_cheriot_sealing_type(MySealingType); // expected-error{{use of undeclared identifier 'MySealingType'}}
5+
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}}
6+
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}}
7+
int *k4 = __builtin_cheriot_sealing_type("a\0b"); // expected-error{{the sealing key type name 'a\00b' is not a valid identifier}}
8+
int *k5 = __builtin_cheriot_sealing_type("a\"b"); // expected-error{{the sealing key type name 'a\22b' is not a valid identifier}}
9+
int *k6 = __builtin_cheriot_sealing_type("a\nb"); // expected-error{{the sealing key type name 'a\0Ab' is not a valid identifier}}
10+
int *k7 = __builtin_cheriot_sealing_type("a\bb"); // expected-error{{the sealing key type name 'a\08b' is not a valid identifier}}
11+
int *k8 = __builtin_cheriot_sealing_type("ab"); // expected-error{{__builtin_cheriot_sealing_type used, but no compartment name given}}
12+
}

llvm/include/llvm/IR/Attributes.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,6 +1614,23 @@ class CHERIoTSealedValueAttr {
16141614
/// Get the name of the attribute.
16151615
static const std::string getAttrName() { return "cheriot_sealed_value"; }
16161616
};
1617+
1618+
/// Represents the LLVM-level attribute that is used to signal
1619+
/// that a global (variable) represents a sealing key type.
1620+
class CHERIoTSealingKeyTypeAttr {
1621+
public:
1622+
/// Get the name of the attribute.
1623+
static const std::string getAttrName() { return "cheriot_sealing_key"; }
1624+
1625+
/// Get the mangled name of the symbol representing the sealing key.
1626+
static const std::string
1627+
getSealingTypeSymbolName(StringRef CompartmentName,
1628+
StringRef SealingKeyTypeName) {
1629+
return "sealing_type." + CompartmentName.str() + "." +
1630+
SealingKeyTypeName.str();
1631+
}
1632+
};
1633+
16171634
} // end namespace llvm
16181635

16191636
#endif // LLVM_IR_ATTRIBUTES_H

llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp

Lines changed: 76 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,10 @@ class RISCVAsmPrinter : public AsmPrinter {
114114

115115
private:
116116
/**
117-
* Struct describing compartment exports that must be emitted for this
118-
* compilation unit.
117+
* Struct describing function-like compartment export objects.
119118
*/
120-
struct CompartmentExport
121-
{
122-
/// The compartment name for the function.
119+
struct FunctionCompartmentExport {
120+
/// The compartment name for the object.
123121
std::string CompartmentName;
124122
/// The IR function corresponding to the function.
125123
const Function &Fn;
@@ -132,9 +130,26 @@ class RISCVAsmPrinter : public AsmPrinter {
132130
/// The size in bytes of the stack frame, 0 if not used.
133131
uint32_t stackSize = 0;
134132
};
135-
SmallVector<CompartmentExport, 1> CompartmentEntries;
133+
134+
/**
135+
* Struct describing sealing key-like compartment export objects.
136+
*/
137+
struct SealingKeyCompartmentExport {
138+
/// The compartment name for the object.
139+
std::string CompartmentName;
140+
/// The name of the sealing key type.
141+
std::string SealingKeyTypeName;
142+
143+
bool operator==(const SealingKeyCompartmentExport &Other) {
144+
return this->CompartmentName == Other.CompartmentName &&
145+
this->SealingKeyTypeName == Other.SealingKeyTypeName;
146+
}
147+
};
148+
149+
SmallVector<FunctionCompartmentExport, 1> FNCompartmentEntries;
150+
SmallVector<SealingKeyCompartmentExport, 1> SKCompartmentEntries;
136151
SmallDenseMap<const Function *, SmallVector<const GlobalAlias *, 1>, 1>
137-
CompartmentEntryAliases;
152+
FnCompartmentEntryAliases;
138153

139154
void emitAttributes(const MCSubtargetInfo &SubtargetInfo);
140155

@@ -312,6 +327,20 @@ void RISCVAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) {
312327
return;
313328
}
314329

330+
auto CheriotSealingKeyTypeAttrName =
331+
llvm::CHERIoTSealingKeyTypeAttr::getAttrName();
332+
333+
if (GV->hasAttribute(CheriotSealingKeyTypeAttrName)) {
334+
auto Attr = GV->getAttribute(CheriotSealingKeyTypeAttrName);
335+
auto Entry = SealingKeyCompartmentExport{
336+
std::string(GV->getAttribute("cheri-compartment").getValueAsString()),
337+
Attr.getValueAsString().str()};
338+
if (std::find(SKCompartmentEntries.begin(), SKCompartmentEntries.end(),
339+
Entry) == SKCompartmentEntries.end()) {
340+
SKCompartmentEntries.push_back(Entry);
341+
}
342+
}
343+
315344
// Continue through the normal path to emit the global.
316345
return AsmPrinter::emitGlobalVariable(GV);
317346
}
@@ -320,9 +349,12 @@ void RISCVAsmPrinter::emitGlobalAlias(const Module &M, const GlobalAlias &GA) {
320349
AsmPrinter::emitGlobalAlias(M, GA);
321350

322351
const MCSubtargetInfo &SubtargetInfo = *TM.getMCSubtargetInfo();
352+
auto CheriotSealingKeyTypeAttrName =
353+
llvm::CHERIoTSealingKeyTypeAttr::getAttrName();
354+
323355
if (SubtargetInfo.hasFeature(RISCV::FeatureVendorXCheriot)) {
324356
if (auto *Fn = dyn_cast<Function>(GA.getAliasee()->stripPointerCasts())) {
325-
CompartmentEntryAliases[Fn].push_back(&GA);
357+
FnCompartmentEntryAliases[Fn].push_back(&GA);
326358
}
327359
}
328360
}
@@ -582,17 +614,17 @@ bool RISCVAsmPrinter::runOnMachineFunction(MachineFunction &MF) {
582614
} else
583615
stackSize = MF.getFrameInfo().getStackSize();
584616
// FIXME: Get stack size as function attribute if specified
585-
CompartmentEntries.push_back(
617+
FNCompartmentEntries.push_back(
586618
{std::string(Fn.getFnAttribute("cheri-compartment").getValueAsString()),
587619
Fn, OutStreamer->getContext().getOrCreateSymbol(MF.getName()),
588620
countUsedArgRegisters(MF) + interruptFlag, false, stackSize});
589621
} else if (Fn.getCallingConv() == CallingConv::CHERI_LibCall)
590-
CompartmentEntries.push_back(
622+
FNCompartmentEntries.push_back(
591623
{"libcalls", Fn,
592624
OutStreamer->getContext().getOrCreateSymbol(MF.getName()),
593625
countUsedArgRegisters(MF) + interruptFlag});
594626
else if (interruptFlag != 0)
595-
CompartmentEntries.push_back(
627+
FNCompartmentEntries.push_back(
596628
{std::string(Fn.getFnAttribute("cheri-compartment").getValueAsString()),
597629
Fn, OutStreamer->getContext().getOrCreateSymbol(MF.getName()),
598630
countUsedArgRegisters(MF) + interruptFlag, true});
@@ -698,21 +730,21 @@ void RISCVAsmPrinter::emitEndOfAsmFile(Module &M) {
698730
RISCVTargetStreamer &RTS =
699731
static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer());
700732

701-
if (!CompartmentEntries.empty()) {
733+
if (!FNCompartmentEntries.empty()) {
702734
auto &C = OutStreamer->getContext();
703-
auto *Exports = C.getELFSection(".compartment_exports", ELF::SHT_PROGBITS,
704-
ELF::SHF_ALLOC | ELF::SHF_GNU_RETAIN);
705-
OutStreamer->switchSection(Exports);
706735
auto CompartmentStartSym = C.getOrCreateSymbol("__compartment_pcc_start");
707-
for (auto &Entry : CompartmentEntries) {
736+
for (auto &Entry : FNCompartmentEntries) {
737+
auto *Exports = C.getELFSection(".compartment_exports", ELF::SHT_PROGBITS,
738+
ELF::SHF_ALLOC | ELF::SHF_GNU_RETAIN);
739+
OutStreamer->switchSection(Exports);
708740
std::string ExportName = getImportExportTableName(
709741
Entry.CompartmentName, Entry.Fn.getName(), Entry.Fn.getCallingConv(),
710742
/*IsImport*/ false);
711743
auto Sym = C.getOrCreateSymbol(ExportName);
712744
OutStreamer->emitSymbolAttribute(Sym, MCSA_ELF_TypeObject);
713-
// If the function isn't global, don't make its export table entry global
714-
// either. Two different compilation units in the same compartment may
715-
// export different static things.
745+
// If the function isn't global, don't make its export table entry
746+
// global either. Two different compilation units in the same
747+
// compartment may export different static things.
716748
if (Entry.Fn.hasExternalLinkage() && !Entry.forceLocal)
717749
OutStreamer->emitSymbolAttribute(Sym, MCSA_Global);
718750
OutStreamer->emitValueToAlignment(Align(4));
@@ -727,8 +759,8 @@ void RISCVAsmPrinter::emitEndOfAsmFile(Module &M) {
727759
OutStreamer->emitELFSize(Sym, MCConstantExpr::create(4, C));
728760

729761
// Emit aliases for this export symbol entry.
730-
auto I = CompartmentEntryAliases.find(&Entry.Fn);
731-
if (I == CompartmentEntryAliases.end())
762+
auto I = FnCompartmentEntryAliases.find(&Entry.Fn);
763+
if (I == FnCompartmentEntryAliases.end())
732764
continue;
733765
for (const GlobalAlias *GA : I->second) {
734766
std::string AliasExportName = getImportExportTableName(
@@ -747,6 +779,29 @@ void RISCVAsmPrinter::emitEndOfAsmFile(Module &M) {
747779
}
748780
}
749781
}
782+
783+
if (!SKCompartmentEntries.empty()) {
784+
auto &C = OutStreamer->getContext();
785+
for (auto &Entry : SKCompartmentEntries) {
786+
auto ExportName = Entry.SealingKeyTypeName;
787+
auto MangledExportName = "__export." + ExportName;
788+
auto *Sym = C.getOrCreateSymbol(MangledExportName);
789+
auto *Exports = C.getELFSection(
790+
".compartment_exports." + ExportName, ELF::SHT_PROGBITS,
791+
ELF::SHF_ALLOC | ELF::SHF_WRITE | ELF::SHF_GROUP, 0, ExportName,
792+
true);
793+
OutStreamer->switchSection(Exports);
794+
OutStreamer->emitSymbolAttribute(Sym, MCSA_ELF_TypeObject);
795+
OutStreamer->emitSymbolAttribute(Sym, MCSA_Global);
796+
OutStreamer->emitValueToAlignment(Align(4));
797+
OutStreamer->emitLabel(Sym);
798+
OutStreamer->emitIntValue(0, 2);
799+
OutStreamer->emitIntValue(0, 1);
800+
OutStreamer->emitIntValue(0b100000, 1);
801+
OutStreamer->emitELFSize(Sym, MCConstantExpr::create(4, C));
802+
}
803+
}
804+
750805
// Generate CHERIoT imports if there are any.
751806
auto &CHERIoTCompartmentImports =
752807
static_cast<RISCVTargetMachine &>(TM).ImportedObjects;

0 commit comments

Comments
 (0)