Skip to content

Commit ebeca8d

Browse files
committed
[CHERIoT] Add cheriot_mmio, cheriot_shared_object and cheriot_cap_import attributes
The first two are used from the front-end to indicate that an extern global declaration refers to a specific compartment import. The third is the attribute consumed by the back-end to generate the necessary compartment import entry and the correct sequence of instructions when taking the address of the global. Add documentation for newly introduced attributes `cheriot_mmio` is documented in `CHERIMMIODeviceDocs`, and similarly for `cheriot_shared_object`. Add documentation for the piece of code that translates clang-level attributes to LLVM IR ones. Add semantic checks to permissions encoding Change `CapabilityImportAttr` to `GlobalCapabilityImportAttr`, to reflect that it must be used on global (variables) not functions. Move the "global must be extern check" to verifier, replace with assertion in previous location Pull permission symbols out as symbolic values (`EncodedPermissions`->`Permissions`, +`encodePermissions()`) Add a test with multiple references to the same imported global Use enum to show the kind of import, remove "export_" from the string, minor changes to reduce flow nesting Reduce flow nesting Change mtriple Add more comments to `GlobalCapabilityImportAttr` Remove extraneous change Repurpose `ImportedFunctions` to be used with global variables as well, use `emitEndOfAsm` to generate relevant entries fix formatting Fix typo in docs for attributes. Rename `CHERIMMIODevice` and `CHERISharedObject` to `CHERIOT...` Specify, in the docs, that the global must have the `extern` and `volatile` qualifiers Minor fixes to formatting in RISCV.cpp Factor `getElfSection` for `IsGlobal` together with `IsPublic` Minor changes to respect idiomatic LLVM style Refactor syntactic and semantic checks of permissions strings out of the attribute handlers Remove spurious import Reduce nesting in syntax check of permissions string. Fix tests concerning attributes and warnings fix tests concerning import section entries for functions Reduce nesting in `ExpandPseudoInsts` Move `GlobalCapabilityImportAttr` to `llvm/include/llvm/IR/Attributes.h` Remove `namespace cheri` in Attributes.h, rename `GlobalCapabilityImportAttr` to `CHERIoTGlobalCapabilityImportAttr` simplify import objects Add warnings and errors for ill constructed global variables with the `cheriot_mmio` or `cheriot_shared_object` attributes Namely, an ad-hoc error is now generated if the variable with the attribute has an explicit initializer, and a warning is now emitted if a `cheriot_mmio` is not marked as volatile. chore: fix formatting Remove unwanted import Simplify `AUIPCC` expansion. remove unused import remove unused import remove unused imports remove unused imports/replace with `Attributes.h` where needed
1 parent 1153d07 commit ebeca8d

23 files changed

+1300
-65
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2892,6 +2892,19 @@ def CHERILibCall : DeclOrTypeAttr {
28922892
let Subjects = SubjectList<[Function], ErrorDiag>;
28932893
}
28942894

2895+
def CHERIOTMMIODevice : DeclOrTypeAttr {
2896+
let Spellings = [GNU<"cheriot_mmio">];
2897+
let Documentation = [CHERIOTMMIODeviceDocs];
2898+
let Args = [StringArgument<"DeviceName">, StringArgument<"EncodedPermissions", 1>];
2899+
let Subjects = SubjectList<[GlobalVar], ErrorDiag>;
2900+
}
2901+
2902+
def CHERIOTSharedObject : DeclOrTypeAttr {
2903+
let Spellings = [GNU<"cheriot_shared_object">];
2904+
let Documentation = [CHERIOTSharedObjectDocs];
2905+
let Args = [StringArgument<"ObjectName">, StringArgument<"EncodedPermissions", 1>];
2906+
let Subjects = SubjectList<[GlobalVar], ErrorDiag>;
2907+
}
28952908

28962909
def CHERINoSubobjectBounds : DeclOrTypeAttr {
28972910
let Spellings = [GNU<"cheri_no_subobject_bounds">,

clang/include/clang/Basic/AttrDocs.td

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,6 +1473,73 @@ Further reading for other annotations:
14731473
}];
14741474
}
14751475

1476+
def CHERIOTMMIODeviceDocs : Documentation {
1477+
let Category = DocCatVariable;
1478+
let Content = [{
1479+
Indicates that the global it refers to must be treated as a cross-compartment
1480+
object. In particular, this attribute indicates that the object is related to
1481+
an MMIO-bound device. The target global must have the `extern` and `volatile`
1482+
qualifiers.
1483+
1484+
**Usage**: ``__attribute((cheriot_mmio("<device>",
1485+
"<permissions_encoding>")))``, where ``"<device>"`` is the (quoted) identifier
1486+
of the MMIO-bound device. An example of this is ``"uart"``. The
1487+
``"<permissions_encoding>"`` indicates the permissions of the capability
1488+
resulting from the import. The permissions encoding is a string of variable
1489+
length. The symbols allowed in the string are ``R``, ``W``, ``c`` and ``m``,
1490+
whose meaning is explained in the table below.
1491+
1492+
1493+
.. csv-table:: Supported Syntaxes
1494+
:header: "Symbol", "Description", "Meaning", "Depends on"
1495+
1496+
"``R``","Load (Read)", "May be used to read.",""
1497+
"``W``","Store (Write)", "May be used to write.",""
1498+
"``c``","Load / Store Capability", "May be used to load or store capabilities as well as non-capability data.","``R`` or ``W``"
1499+
"``m``","Load Mutable", "May be used to load capabilities with write permission.","``R``"
1500+
1501+
Examples of valid encodings are: ``"RWcm"`` (all permissions), ``"R"`` (read
1502+
only), etc. Note that the order in which symbols appear is not relevant: for
1503+
example, `"RWcm"` and `"mcWR"` are both valid and entail the same permissions.
1504+
1505+
**Warning**: The ``<permissions_encoding>`` parameter is optional, and if no encoding is given ``"RWcm"`` is assumed.
1506+
}];
1507+
}
1508+
1509+
1510+
def CHERIOTSharedObjectDocs : Documentation {
1511+
let Category = DocCatVariable;
1512+
let Content = [{
1513+
Indicates that the global it refers to must be treated as a cross-compartment
1514+
object. In particular, this attribute indicates that the object is a
1515+
cross-compartment shared object. The target global must have the `extern` and
1516+
`volatile` qualifiers.
1517+
1518+
1519+
**Usage**: ``__attribute((cheriot_shared_object("<object>",
1520+
"<permissions_encoding>")))``, where ``"<object>"`` is the (quoted) identifier
1521+
of the shared object. The ``"<permissions_encoding>"`` indicates the permissions
1522+
of the capability resulting from the import. The permissions encoding is a
1523+
string of variable length. The symbols allowed in the string are ``R``, ``W``,
1524+
``c`` and ``m``, whose meaning is explained in the table below.
1525+
1526+
1527+
.. csv-table:: Supported Syntaxes
1528+
:header: "Symbol", "Description", "Meaning", "Depends on"
1529+
1530+
"``R``","Load (Read)", "May be used to read.",""
1531+
"``W``","Store (Write)", "May be used to write.",""
1532+
"``c``","Load / Store Capability", "May be used to load or store capabilities as well as non-capability data.","``R`` or ``W``"
1533+
"``m``","Load Mutable", "May be used to load capabilities with write permission.","``R``"
1534+
1535+
Examples of valid encodings are: ``"RWcm"`` (all permissions), ``"R"`` (read
1536+
only), etc. Note that the order in which symbols appear is not relevant: for
1537+
example, `"RWcm"` and `"mcWR"` are both valid and entail the same permissions.
1538+
1539+
**Warning**: The ``<permissions_encoding>`` parameter is optional, and if no encoding is given ``"RWcm"`` is assumed.
1540+
}];
1541+
}
1542+
14761543
def ObjCMethodFamilyDocs : Documentation {
14771544
let Category = DocCatFunction;
14781545
let Content = [{

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,16 @@ def err_half_const_requires_fp16 : Error<
140140
"half precision constant requires cl_khr_fp16">;
141141
def err_cheriot_minstack_without_annotation : Error<
142142
"__cheriot_minimum_stack__ can only be used in a function with a minimum stack annotation">;
143+
def err_cheriot_malformed_cap_permissions_unknown_sym : Error<
144+
"the permissions in %0 contain unknown permission symbols: '%1' (value: '%2') ">;
145+
def err_cheriot_malformed_cap_permissions_invalid_dep : Error<
146+
"the permissions in %0 contain ill-formed dependencies: %1 (value: '%2') ">;
147+
def warn_cheriot_cap_permissions_duplicate_sym : Warning<
148+
"the permissions in %0 contain a duplicate permission symbol: %1 (value: '%2') ">;
149+
def warn_cheriot_global_cap_import_non_volatile : Warning<
150+
"global variable definition '%0' has attribute %1 but is not qualified as `volatile`">;
151+
def err_cheriot_global_cap_import_initialized: Error<
152+
"global variable definition '%0' with attribute %1 cannot have an initializer">;
143153

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

clang/lib/CodeGen/Targets/RISCV.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "ABIInfoImpl.h"
10-
#include "TargetInfo.h"
1110
#include "CommonCheriTargetCodeGenInfo.h"
11+
#include "TargetInfo.h"
1212

1313
using namespace clang;
1414
using namespace clang::CodeGen;
@@ -632,6 +632,33 @@ class RISCVTargetCodeGenInfo : public CommonCheriTargetCodeGenInfo {
632632

633633
void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
634634
CodeGen::CodeGenModule &CGM) const override {
635+
if (const auto *VD = dyn_cast_or_null<VarDecl>(D)) {
636+
if (auto *GVar = llvm::dyn_cast<llvm::GlobalVariable>(GV)) {
637+
638+
// If the clang declaration was annotated with `cheriot_mmio(...)`, we
639+
// need to propagate the attribute by translating it to a matching
640+
// `cheriot_cap_import` attribute in the generated LLVM IR, so that the
641+
// back-end can generate the proper import entries and correctly
642+
// translate references to this global.
643+
if (VD->hasAttr<CHERIOTMMIODeviceAttr>()) {
644+
auto *Attr = D->getAttr<CHERIOTMMIODeviceAttr>();
645+
llvm::CHERIoTGlobalCapabilityImportAttr CapImportAttr(
646+
llvm::CHERIoTGlobalCapabilityImportAttr::MMIO,
647+
Attr->getDeviceName(), Attr->getEncodedPermissions());
648+
GVar->addAttribute(CapImportAttr.getAttrName(), CapImportAttr.str());
649+
}
650+
651+
// Same for the `cheriot_shared_object(...)` attribute.
652+
if (VD->hasAttr<CHERIOTSharedObjectAttr>()) {
653+
auto *Attr = D->getAttr<CHERIOTSharedObjectAttr>();
654+
llvm::CHERIoTGlobalCapabilityImportAttr CapImportAttr(
655+
llvm::CHERIoTGlobalCapabilityImportAttr::SharedObject,
656+
Attr->getObjectName(), Attr->getEncodedPermissions());
657+
GVar->addAttribute(CapImportAttr.getAttrName(), CapImportAttr.str());
658+
}
659+
}
660+
}
661+
635662
const auto *FD = dyn_cast_or_null<FunctionDecl>(D);
636663
if (!FD) return;
637664

clang/lib/Sema/SemaDecl.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13501,6 +13501,26 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
1350113501
return;
1350213502
}
1350313503

13504+
// CHERIoT-specific check: if the decl has the `cheriot_mmio` or
13505+
// `cheriot_shared_object` attributes and also has an explicit definition, an
13506+
// error must be generated.
13507+
if (RealDecl->hasAttr<CHERIOTSharedObjectAttr>() ||
13508+
RealDecl->hasAttr<CHERIOTMMIODeviceAttr>()) {
13509+
const IdentifierInfo *AttrName;
13510+
13511+
if (auto *Attr = RealDecl->getAttr<CHERIOTSharedObjectAttr>())
13512+
AttrName = Attr->getAttrName();
13513+
else {
13514+
AttrName = RealDecl->getAttr<CHERIOTMMIODeviceAttr>()->getAttrName();
13515+
}
13516+
13517+
Diag(RealDecl->getLocation(),
13518+
diag::err_cheriot_global_cap_import_initialized)
13519+
<< VDecl->getName() << AttrName;
13520+
VDecl->setInvalidDecl();
13521+
return;
13522+
}
13523+
1350413524
// WebAssembly tables can't be used to initialise a variable.
1350513525
if (!Init->getType().isNull() && Init->getType()->isWebAssemblyTableType()) {
1350613526
Diag(Init->getExprLoc(), diag::err_wasm_table_art) << 0;

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
#include "clang/Basic/DarwinSDKInfo.h"
2929
#include "clang/Basic/IdentifierTable.h"
3030
#include "clang/Basic/LangOptions.h"
31+
#include "clang/Basic/Linkage.h"
3132
#include "clang/Basic/SourceLocation.h"
3233
#include "clang/Basic/SourceManager.h"
34+
#include "clang/Basic/Specifiers.h"
3335
#include "clang/Basic/TargetInfo.h"
3436
#include "clang/Lex/Preprocessor.h"
3537
#include "clang/Sema/Attr.h"
@@ -61,6 +63,7 @@
6163
#include "llvm/ADT/STLForwardCompat.h"
6264
#include "llvm/ADT/StringExtras.h"
6365
#include "llvm/Demangle/Demangle.h"
66+
#include "llvm/IR/Attributes.h"
6467
#include "llvm/IR/DerivedTypes.h"
6568
#include "llvm/MC/MCSectionMachO.h"
6669
#include "llvm/Support/Error.h"
@@ -2103,6 +2106,127 @@ static void handleCHERIMethodSuffix(Sema &S, Decl *D, const ParsedAttr &Attr) {
21032106
D->addAttr(::new (S.Context) CHERIMethodSuffixAttr(S.Context, Attr, Str));
21042107
}
21052108

2109+
static void handleCHERIOTMMIODevice(Sema &S, Decl *D, const ParsedAttr &Attr,
2110+
Sema::DeclAttributeLocation DAL) {
2111+
auto *VD = dyn_cast<VarDecl>(D);
2112+
if (!VD || !VD->hasGlobalStorage())
2113+
return;
2114+
2115+
if (!VD->hasExternalStorage()) {
2116+
VD->setStorageClass(clang::SC_Extern);
2117+
}
2118+
2119+
QualType QT = VD->getType();
2120+
if (!QT.isVolatileQualified()) {
2121+
S.Diag(Attr.getLoc(), diag::warn_cheriot_global_cap_import_non_volatile)
2122+
<< VD->getName() << Attr.getAttrName();
2123+
}
2124+
2125+
StringRef DeviceName;
2126+
SourceLocation DeviceNameLiteralLoc;
2127+
if (!S.checkStringLiteralArgumentAttr(Attr, 0, DeviceName,
2128+
&DeviceNameLiteralLoc))
2129+
return;
2130+
StringRef Permissions;
2131+
SourceLocation PermissionsLiteralLoc;
2132+
2133+
if (Attr.getNumArgs() > 1) {
2134+
S.checkStringLiteralArgumentAttr(Attr, 1, Permissions,
2135+
&PermissionsLiteralLoc);
2136+
}
2137+
2138+
std::string OwnedPermissions = Permissions.str();
2139+
2140+
auto DuplicateSymbolCallback = [&Attr, &S,
2141+
Permissions](char DuplicateSymbol) {
2142+
S.Diag(Attr.getLoc(), diag::warn_cheriot_cap_permissions_duplicate_sym)
2143+
<< Attr.getAttrName() << "'" + std::string({DuplicateSymbol}) + "'"
2144+
<< Permissions;
2145+
};
2146+
2147+
auto ExtraneousSymbolCallback = [&Attr, &S,
2148+
Permissions](StringRef ExtraneousSymbols) {
2149+
S.Diag(Attr.getLoc(),
2150+
diag::err_cheriot_malformed_cap_permissions_unknown_sym)
2151+
<< Attr.getAttrName() << ExtraneousSymbols << Permissions;
2152+
};
2153+
2154+
auto FailedSemanticCheckCallback = [&Attr, &S,
2155+
Permissions](StringRef Reason) {
2156+
S.Diag(Attr.getLoc(),
2157+
diag::err_cheriot_malformed_cap_permissions_invalid_dep)
2158+
<< Attr.getAttrName() << Reason << Permissions;
2159+
};
2160+
2161+
if (!llvm::CHERIoTGlobalCapabilityImportAttr::checkPermissions(
2162+
OwnedPermissions, DuplicateSymbolCallback, ExtraneousSymbolCallback,
2163+
FailedSemanticCheckCallback))
2164+
return;
2165+
2166+
return D->addAttr(::new (S.Context) CHERIOTMMIODeviceAttr(
2167+
S.Context, Attr, DeviceName, OwnedPermissions));
2168+
}
2169+
2170+
static void handleCHERIOTSharedObject(Sema &S, Decl *D, const ParsedAttr &Attr,
2171+
Sema::DeclAttributeLocation DAL) {
2172+
auto *VD = dyn_cast<VarDecl>(D);
2173+
if (!VD || !VD->hasGlobalStorage())
2174+
return;
2175+
2176+
if (VD->hasInit()) {
2177+
S.Diag(Attr.getLoc(), diag::err_cheriot_global_cap_import_initialized)
2178+
<< VD->getName() << Attr.getAttrName();
2179+
}
2180+
2181+
if (!VD->hasExternalStorage()) {
2182+
VD->setStorageClass(clang::SC_Extern);
2183+
}
2184+
2185+
StringRef ObjectName;
2186+
SourceLocation ObjectNameLiteralLoc;
2187+
if (!S.checkStringLiteralArgumentAttr(Attr, 0, ObjectName,
2188+
&ObjectNameLiteralLoc))
2189+
return;
2190+
StringRef Permissions;
2191+
SourceLocation PermissionsLiteralLoc;
2192+
2193+
if (Attr.getNumArgs() > 1) {
2194+
S.checkStringLiteralArgumentAttr(Attr, 1, Permissions,
2195+
&PermissionsLiteralLoc);
2196+
}
2197+
2198+
std::string OwnedPermissions = Permissions.str();
2199+
2200+
auto DuplicateSymbolCallback = [&Attr, &S,
2201+
Permissions](char DuplicateSymbol) {
2202+
S.Diag(Attr.getLoc(), diag::warn_cheriot_cap_permissions_duplicate_sym)
2203+
<< Attr.getAttrName() << "'" + std::string({DuplicateSymbol}) + "'"
2204+
<< Permissions;
2205+
};
2206+
2207+
auto ExtraneousSymbolCallback = [&Attr, &S,
2208+
Permissions](StringRef ExtraneousSymbols) {
2209+
S.Diag(Attr.getLoc(),
2210+
diag::err_cheriot_malformed_cap_permissions_unknown_sym)
2211+
<< Attr.getAttrName() << ExtraneousSymbols << Permissions;
2212+
};
2213+
2214+
auto FailedSemanticCheckCallback = [&Attr, &S,
2215+
Permissions](StringRef Reason) {
2216+
S.Diag(Attr.getLoc(),
2217+
diag::err_cheriot_malformed_cap_permissions_invalid_dep)
2218+
<< Attr.getAttrName() << Reason << Permissions;
2219+
};
2220+
2221+
if (!llvm::CHERIoTGlobalCapabilityImportAttr::checkPermissions(
2222+
OwnedPermissions, DuplicateSymbolCallback, ExtraneousSymbolCallback,
2223+
FailedSemanticCheckCallback))
2224+
return;
2225+
2226+
D->addAttr(::new (S.Context) CHERIOTSharedObjectAttr(
2227+
S.Context, Attr, ObjectName, OwnedPermissions));
2228+
}
2229+
21062230
static void handleCHERICompartmentName(Sema &S, Decl *D, const ParsedAttr &Attr,
21072231
Sema::DeclAttributeLocation DAL) {
21082232
// cheri_compartment is both:
@@ -7350,6 +7474,12 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
73507474
case ParsedAttr::AT_CHERICompartmentName:
73517475
handleCHERICompartmentName(S, D, AL, DAL);
73527476
break;
7477+
case ParsedAttr::AT_CHERIOTMMIODevice:
7478+
handleCHERIOTMMIODevice(S, D, AL, DAL);
7479+
break;
7480+
case ParsedAttr::AT_CHERIOTSharedObject:
7481+
handleCHERIOTSharedObject(S, D, AL, DAL);
7482+
break;
73537483
case ParsedAttr::AT_InterruptState:
73547484
handleInterruptState(S, D, AL);
73557485
break;

0 commit comments

Comments
 (0)