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
13 changes: 13 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -2892,6 +2892,19 @@ def CHERILibCall : DeclOrTypeAttr {
let Subjects = SubjectList<[Function], ErrorDiag>;
}

def CHERIOTMMIODevice : DeclOrTypeAttr {
let Spellings = [GNU<"cheriot_mmio">];
let Documentation = [CHERIOTMMIODeviceDocs];
let Args = [StringArgument<"DeviceName">, StringArgument<"EncodedPermissions", 1>];
let Subjects = SubjectList<[GlobalVar], ErrorDiag>;
}

def CHERIOTSharedObject : DeclOrTypeAttr {
let Spellings = [GNU<"cheriot_shared_object">];
let Documentation = [CHERIOTSharedObjectDocs];
let Args = [StringArgument<"ObjectName">, StringArgument<"EncodedPermissions", 1>];
let Subjects = SubjectList<[GlobalVar], ErrorDiag>;
}

def CHERINoSubobjectBounds : DeclOrTypeAttr {
let Spellings = [GNU<"cheri_no_subobject_bounds">,
Expand Down
67 changes: 67 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -1473,6 +1473,73 @@ Further reading for other annotations:
}];
}

def CHERIOTMMIODeviceDocs : Documentation {
let Category = DocCatVariable;
let Content = [{
Indicates that the global it refers to must be treated as a cross-compartment
object. In particular, this attribute indicates that the object is related to
an MMIO-bound device. The target global must have the `extern` and `volatile`
qualifiers.

**Usage**: ``__attribute((cheriot_mmio("<device>",
"<permissions_encoding>")))``, where ``"<device>"`` is the (quoted) identifier
of the MMIO-bound device. An example of this is ``"uart"``. The
``"<permissions_encoding>"`` indicates the permissions of the capability
resulting from the import. The permissions encoding is a string of variable
length. The symbols allowed in the string are ``R``, ``W``, ``c`` and ``m``,
whose meaning is explained in the table below.


.. csv-table:: Supported Syntaxes
:header: "Symbol", "Description", "Meaning", "Depends on"

"``R``","Load (Read)", "May be used to read.",""
"``W``","Store (Write)", "May be used to write.",""
"``c``","Load / Store Capability", "May be used to load or store capabilities as well as non-capability data.","``R`` or ``W``"
"``m``","Load Mutable", "May be used to load capabilities with write permission.","``R``"

Examples of valid encodings are: ``"RWcm"`` (all permissions), ``"R"`` (read
only), etc. Note that the order in which symbols appear is not relevant: for
example, `"RWcm"` and `"mcWR"` are both valid and entail the same permissions.

**Warning**: The ``<permissions_encoding>`` parameter is optional, and if no encoding is given ``"RWcm"`` is assumed.
}];
}


def CHERIOTSharedObjectDocs : Documentation {
let Category = DocCatVariable;
let Content = [{
Indicates that the global it refers to must be treated as a cross-compartment
object. In particular, this attribute indicates that the object is a
cross-compartment shared object. The target global must have the `extern` and
`volatile` qualifiers.


**Usage**: ``__attribute((cheriot_shared_object("<object>",
"<permissions_encoding>")))``, where ``"<object>"`` is the (quoted) identifier
of the shared object. The ``"<permissions_encoding>"`` indicates the permissions
of the capability resulting from the import. The permissions encoding is a
string of variable length. The symbols allowed in the string are ``R``, ``W``,
``c`` and ``m``, whose meaning is explained in the table below.


.. csv-table:: Supported Syntaxes
:header: "Symbol", "Description", "Meaning", "Depends on"

"``R``","Load (Read)", "May be used to read.",""
"``W``","Store (Write)", "May be used to write.",""
"``c``","Load / Store Capability", "May be used to load or store capabilities as well as non-capability data.","``R`` or ``W``"
"``m``","Load Mutable", "May be used to load capabilities with write permission.","``R``"

Examples of valid encodings are: ``"RWcm"`` (all permissions), ``"R"`` (read
only), etc. Note that the order in which symbols appear is not relevant: for
example, `"RWcm"` and `"mcWR"` are both valid and entail the same permissions.

**Warning**: The ``<permissions_encoding>`` parameter is optional, and if no encoding is given ``"RWcm"`` is assumed.
}];
}

def ObjCMethodFamilyDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,16 @@ def err_half_const_requires_fp16 : Error<
"half precision constant requires cl_khr_fp16">;
def err_cheriot_minstack_without_annotation : Error<
"__cheriot_minimum_stack__ can only be used in a function with a minimum stack annotation">;
def err_cheriot_malformed_cap_permissions_unknown_sym : Error<
"the permissions in %0 contain unknown permission symbols: '%1' (value: '%2') ">;
def err_cheriot_malformed_cap_permissions_invalid_dep : Error<
"the permissions in %0 contain ill-formed dependencies: %1 (value: '%2') ">;
def warn_cheriot_cap_permissions_duplicate_sym : Warning<
"the permissions in %0 contain a duplicate permission symbol: %1 (value: '%2') ">;
def warn_cheriot_global_cap_import_non_volatile : Warning<
"global variable definition '%0' has attribute %1 but is not qualified as `volatile`">;
def err_cheriot_global_cap_import_initialized: Error<
"global variable definition '%0' with attribute %1 cannot have an initializer">;

// C99 variable-length arrays
def ext_vla : Extension<"variable length arrays are a C99 feature">,
Expand Down
29 changes: 28 additions & 1 deletion clang/lib/CodeGen/Targets/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
//===----------------------------------------------------------------------===//

#include "ABIInfoImpl.h"
#include "TargetInfo.h"
#include "CommonCheriTargetCodeGenInfo.h"
#include "TargetInfo.h"

using namespace clang;
using namespace clang::CodeGen;
Expand Down Expand Up @@ -632,6 +632,33 @@ class RISCVTargetCodeGenInfo : public CommonCheriTargetCodeGenInfo {

void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
CodeGen::CodeGenModule &CGM) const override {
if (const auto *VD = dyn_cast_or_null<VarDecl>(D)) {
if (auto *GVar = llvm::dyn_cast<llvm::GlobalVariable>(GV)) {

// If the clang declaration was annotated with `cheriot_mmio(...)`, we
// need to propagate the attribute by translating it to a matching
// `cheriot_cap_import` attribute in the generated LLVM IR, so that the
// back-end can generate the proper import entries and correctly
// translate references to this global.
if (VD->hasAttr<CHERIOTMMIODeviceAttr>()) {
auto *Attr = D->getAttr<CHERIOTMMIODeviceAttr>();
llvm::CHERIoTGlobalCapabilityImportAttr CapImportAttr(
llvm::CHERIoTGlobalCapabilityImportAttr::MMIO,
Attr->getDeviceName(), Attr->getEncodedPermissions());
GVar->addAttribute(CapImportAttr.getAttrName(), CapImportAttr.str());
}

// Same for the `cheriot_shared_object(...)` attribute.
if (VD->hasAttr<CHERIOTSharedObjectAttr>()) {
auto *Attr = D->getAttr<CHERIOTSharedObjectAttr>();
llvm::CHERIoTGlobalCapabilityImportAttr CapImportAttr(
llvm::CHERIoTGlobalCapabilityImportAttr::SharedObject,
Attr->getObjectName(), Attr->getEncodedPermissions());
GVar->addAttribute(CapImportAttr.getAttrName(), CapImportAttr.str());
}
}
}

const auto *FD = dyn_cast_or_null<FunctionDecl>(D);
if (!FD) return;

Expand Down
20 changes: 20 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13501,6 +13501,26 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
return;
}

// CHERIoT-specific check: if the decl has the `cheriot_mmio` or
// `cheriot_shared_object` attributes and also has an explicit definition, an
// error must be generated.
if (RealDecl->hasAttr<CHERIOTSharedObjectAttr>() ||
RealDecl->hasAttr<CHERIOTMMIODeviceAttr>()) {
const IdentifierInfo *AttrName;

if (auto *Attr = RealDecl->getAttr<CHERIOTSharedObjectAttr>())
AttrName = Attr->getAttrName();
else {
AttrName = RealDecl->getAttr<CHERIOTMMIODeviceAttr>()->getAttrName();
}

Diag(RealDecl->getLocation(),
diag::err_cheriot_global_cap_import_initialized)
<< VDecl->getName() << AttrName;
VDecl->setInvalidDecl();
return;
}

// WebAssembly tables can't be used to initialise a variable.
if (!Init->getType().isNull() && Init->getType()->isWebAssemblyTableType()) {
Diag(Init->getExprLoc(), diag::err_wasm_table_art) << 0;
Expand Down
130 changes: 130 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@
#include "clang/Basic/DarwinSDKInfo.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/Linkage.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Attr.h"
Expand Down Expand Up @@ -61,6 +63,7 @@
#include "llvm/ADT/STLForwardCompat.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/MC/MCSectionMachO.h"
#include "llvm/Support/Error.h"
Expand Down Expand Up @@ -2103,6 +2106,127 @@ static void handleCHERIMethodSuffix(Sema &S, Decl *D, const ParsedAttr &Attr) {
D->addAttr(::new (S.Context) CHERIMethodSuffixAttr(S.Context, Attr, Str));
}

static void handleCHERIOTMMIODevice(Sema &S, Decl *D, const ParsedAttr &Attr,
Sema::DeclAttributeLocation DAL) {
auto *VD = dyn_cast<VarDecl>(D);
if (!VD || !VD->hasGlobalStorage())
return;

if (!VD->hasExternalStorage()) {
VD->setStorageClass(clang::SC_Extern);
}

QualType QT = VD->getType();
if (!QT.isVolatileQualified()) {
S.Diag(Attr.getLoc(), diag::warn_cheriot_global_cap_import_non_volatile)
<< VD->getName() << Attr.getAttrName();
}

StringRef DeviceName;
SourceLocation DeviceNameLiteralLoc;
if (!S.checkStringLiteralArgumentAttr(Attr, 0, DeviceName,
&DeviceNameLiteralLoc))
return;
StringRef Permissions;
SourceLocation PermissionsLiteralLoc;

if (Attr.getNumArgs() > 1) {
S.checkStringLiteralArgumentAttr(Attr, 1, Permissions,
&PermissionsLiteralLoc);
}

std::string OwnedPermissions = Permissions.str();

auto DuplicateSymbolCallback = [&Attr, &S,
Permissions](char DuplicateSymbol) {
S.Diag(Attr.getLoc(), diag::warn_cheriot_cap_permissions_duplicate_sym)
<< Attr.getAttrName() << "'" + std::string({DuplicateSymbol}) + "'"
<< Permissions;
};

auto ExtraneousSymbolCallback = [&Attr, &S,
Permissions](StringRef ExtraneousSymbols) {
S.Diag(Attr.getLoc(),
diag::err_cheriot_malformed_cap_permissions_unknown_sym)
<< Attr.getAttrName() << ExtraneousSymbols << Permissions;
};

auto FailedSemanticCheckCallback = [&Attr, &S,
Permissions](StringRef Reason) {
S.Diag(Attr.getLoc(),
diag::err_cheriot_malformed_cap_permissions_invalid_dep)
<< Attr.getAttrName() << Reason << Permissions;
};

if (!llvm::CHERIoTGlobalCapabilityImportAttr::checkPermissions(
OwnedPermissions, DuplicateSymbolCallback, ExtraneousSymbolCallback,
FailedSemanticCheckCallback))
return;

return D->addAttr(::new (S.Context) CHERIOTMMIODeviceAttr(
S.Context, Attr, DeviceName, OwnedPermissions));
}

static void handleCHERIOTSharedObject(Sema &S, Decl *D, const ParsedAttr &Attr,
Sema::DeclAttributeLocation DAL) {
auto *VD = dyn_cast<VarDecl>(D);
if (!VD || !VD->hasGlobalStorage())
return;

if (VD->hasInit()) {
S.Diag(Attr.getLoc(), diag::err_cheriot_global_cap_import_initialized)
<< VD->getName() << Attr.getAttrName();
}

if (!VD->hasExternalStorage()) {
VD->setStorageClass(clang::SC_Extern);
}

StringRef ObjectName;
SourceLocation ObjectNameLiteralLoc;
if (!S.checkStringLiteralArgumentAttr(Attr, 0, ObjectName,
&ObjectNameLiteralLoc))
return;
StringRef Permissions;
SourceLocation PermissionsLiteralLoc;

if (Attr.getNumArgs() > 1) {
S.checkStringLiteralArgumentAttr(Attr, 1, Permissions,
&PermissionsLiteralLoc);
}

std::string OwnedPermissions = Permissions.str();

auto DuplicateSymbolCallback = [&Attr, &S,
Permissions](char DuplicateSymbol) {
S.Diag(Attr.getLoc(), diag::warn_cheriot_cap_permissions_duplicate_sym)
<< Attr.getAttrName() << "'" + std::string({DuplicateSymbol}) + "'"
<< Permissions;
};

auto ExtraneousSymbolCallback = [&Attr, &S,
Permissions](StringRef ExtraneousSymbols) {
S.Diag(Attr.getLoc(),
diag::err_cheriot_malformed_cap_permissions_unknown_sym)
<< Attr.getAttrName() << ExtraneousSymbols << Permissions;
};

auto FailedSemanticCheckCallback = [&Attr, &S,
Permissions](StringRef Reason) {
S.Diag(Attr.getLoc(),
diag::err_cheriot_malformed_cap_permissions_invalid_dep)
<< Attr.getAttrName() << Reason << Permissions;
};

if (!llvm::CHERIoTGlobalCapabilityImportAttr::checkPermissions(
OwnedPermissions, DuplicateSymbolCallback, ExtraneousSymbolCallback,
FailedSemanticCheckCallback))
return;

D->addAttr(::new (S.Context) CHERIOTSharedObjectAttr(
S.Context, Attr, ObjectName, OwnedPermissions));
}

static void handleCHERICompartmentName(Sema &S, Decl *D, const ParsedAttr &Attr,
Sema::DeclAttributeLocation DAL) {
// cheri_compartment is both:
Expand Down Expand Up @@ -7350,6 +7474,12 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_CHERICompartmentName:
handleCHERICompartmentName(S, D, AL, DAL);
break;
case ParsedAttr::AT_CHERIOTMMIODevice:
handleCHERIOTMMIODevice(S, D, AL, DAL);
break;
case ParsedAttr::AT_CHERIOTSharedObject:
handleCHERIOTSharedObject(S, D, AL, DAL);
break;
case ParsedAttr::AT_InterruptState:
handleInterruptState(S, D, AL);
break;
Expand Down
Loading