Attr


Q&A

  • How to add new attributes and propagate it using LLVM?

  • How about attributes in assembly language?


LLVM

References:

An attribute can be a single “enum” value (the enum being the [Attribute::AttrKind] enum), a string representing a target-dependent attribute, or an attribute-value pair. Some examples:

  • Target-independent: noinline, zext
  • Target-dependent: “no-sse”, “thumb2”
  • Attribute-value pair: “cpu” = “cortex-a8”, align = 4

Note: for an attribute value pair, we expect a target-dependent attribute to have a string for the value.

Attribute

An Attribute object is passed by value.

[Attribute::get] to create a new Attribute object

Attribute::get:

[Attribute::AttrKind] enum. This enumeration lists the attributes that can be associated with parameters, function results, or the function itself.

Attribute::AttrKind

llvm/IR/Attributes.inc

AttributeList

stores a collection of Attribute objects for each kind of object that may have an attribute associated with it:

  • the function as a whole,
  • the return type, or
  • the function’s parameters.

A function’s attributes are at index AttributeList::FunctionIndex;

the return type’s attributes are at index AttributeList::ReturnIndex;

the function’s parameters’ attributes are at indices 1, …, n (where ‘n’ is the number of parameters). Most methods on the AttributeList class take an index parameter.

AttrBuilder

A builder class to create AttributeList object;

add and remove attributes;

An AttrBuilder should be passed by reference.

DO NOT USE AttrBuilder::addRawValue(), or AttrBuilder(uint64_t Val); will be remove in the future.

Clang

Reference 1 2

Parsed representation of an attribute is an ‘PassedAttr’ object.

keywords attribute vs non-keywords attribute.

  • keywords attribute. The parsing of the keyword and creation of the ParseAttr object must be done manually.
  • non-keywords attribute. The parsing of attributes is handled automatically by Clang.

Sema::ProcessDeclAttributeList() is called with Decl and an ParsedAttr, transform a parsed attribute into a semantic attribute. The semantic attribute object is attached to Decl, can be obtained by Decl::getAttr<T>(). The process of the attribute is determined by the attribute definition and semantic requirements of the attribute.

Attribute definition in file include/clang/Basic/Attr.td. This definition is used to

  • automatically generate functionality used for the implementation of the attribute, such as
    • a class derived from clang::Attr,
    • information for the parser to use,
    • automated semantic checking for some attributes, etc.

Add an attribute

To add an attribute:

In include/clang/Basic/Attr.td, add a new attribute definition. The attribute must derive from an Attr (tablegen, not semantic) type, or one of its derivatives:

  • InheritableAttr: most derive from the InheritableAttr type; An InheritableAttr specifies that the attribute can be inherited by later redeclarations of the Decl it is associated with;
  • InheritableParamAttr is similar to InheritableAttr, except that the attribute is written on a parameter instead of a declaration;
  • TypeAttr: the attribute is intended to apply to the type instead of the declarationi; such attribute will generally not be given an AST representation.
  • IgnoredAttr: parsed but will generate an ignored attribute diagnostic when used; may be useful when an attr is supported by another vendor but not supported by clang.

The attr definition contains:

  • semantic name;
  • a spelling list: the spellings the attribute supports;
  • a subject list:
  • a documentation list. can be ‘Undocumented’.
  • the arguments the attribute expects;
  • and more.

Argument

If Args is [StringArgument<"Arg1">, IntArgument<"Arg2">], then __attribute__((myattribute("Hello", 3))) will be a valid use.

Arguments can be flagged optional.

Steps to change Clang/LLVM

Add attributes for functions in both clang/llvm

see 1.

  1. clang/include/clang/Basic/Attr.td: add definition of attribute for clang;
  2. llvm/include/llvm/IR/Attributes.td: add def for llvm;
  3. llvm/lib/IR/Attributes.cpp: change getAsString(), which convert attributes to a string;
  4. llvm/lib/IR/Verifier.cpp: add Attribute::YourAttrbute in isFunctionOnlyAttr;
  5. clang/lib/Sema/SemaDeclAttr.cpp: Process the attribute; in ProcessDeclAttribute(), either by new function or just call handleSimpleAttribute.
  6. clang/lib/CodeGen/CodeGenModule.cpp: attach attribute to function when generate code in CodeGenModule::SetLLVMFunctionAttributesForDefinition().

    // LLM: privilege tracking attributes for functions
    if (D->hasAttr<PrivilegeFunctionAttr>()){
      B.addAttribute(llvm::Attribute::PrivilegeFunction);
    }
    if (D->hasAttr<PrivilegeLevelAttr>()){
      B.addAttribute(llvm::Attribute::PrivilegeLevel);
    }
    
  7. Add LLVM support to write/read bitcode with attributes.

    • llvm/lib/AsmParser/LLToken.h: add kw_yourAttribute to llvm::lltok::Kind.
    • llvm/lib/AsmParser/LLLexer.cpp add KEYWORD(yourAttribute);to lltok::Kind LLLexer::LexIdentifier.
    • llvm/lib/AsmParser/LLParser.cpp: add code to turn keyword into the IR attribute in LLParser::ParseFnAttributeValuePairs. case lltok::kw_yourAttribute: B.addAttribute(Attribute::YourAttribute); break;
    • llvm/include/llvm/Bitcode/LLVMBitCodes.h: add a new entry to end of AttributeKindCodes. ATTR_KIND_YourAttribute = 55,
    • llvm/lib/Bitcode/Writer/BitcodeWriter.cpp:

      case Attribute::YourAttribute:
      return bitc::ATTR_KIND_YourAttribute;
      
    • llvm/include/llvm/Bitcode/BitCodeReader.cpp: in getAttrFromCode:

      case bitc::ATTR_KIND_YourAttribute:
      return Attribute::YourAttribute;
      
Add attributes for global variables in clang/llvm:

little change for step 6, change function SetCommonAttributes() and convert the attribute in clang (attached with clang::GlobalDecal) to llvm (attached to llvm::GlobalVariable)

// file: clang/lib/CodeGen/CodeGenModule.cpp
// add priv attribute to global variables
if (llvm::GlobalVariable *GVar= dyn_cast<llvm::GlobalVariable>(GV)){
      if (auto *PAttr = D->getAttr<PrivilegeDataAttr>()){
        GVar->addAttribute("privilege_data");
      }
      if ( auto *PAttr = D->getAttr<PrivilegeLevelAttr>()){
        GVar->addAttribute("privilege_level", std::to_string(PAttr->getPrivilegeLevel()));
      } 
}
Attributes for function arguments:

clang/lib/CodeGen/CGDecl.cpp: EmitParmDecl(): parse the attribute and add it for bit code code generation.

  if (D.hasAttr<PrivilegeDataAttr>()){
    llvm::Argument *LLVMArg = dyn_cast<llvm::Argument>(Arg.getAnyValue());
    LLVMArg->addAttr(llvm::Attribute::PrivilegeData);
  }

llvm/lib/AsmParser/LLParser.cpp: ParseOptionalParamAttrs(): parse the bit code and convert attribute to LLVM Attribute object. add one line:

case lltok::kw_privilege_data: B.addAttribute(Attribute::PrivilegeData); break;

Attributes for local (non-static) variables:

Clang detected the attribute of local (non-static) variable at clang/lib/CodeGen/CGDecl.cpp: EmitAutoVarAlloca();

Attributes for local static variables:

Clang detected static local variable at clang/lib/CodeGen/CGDecl.cpp: EmitStaticVarDecl();

// LLM: add privilege_data attribute
if (auto *PA = D.getAttr<PrivilegeDataAttr>()){
  llvm::errs() << "LLM: " << __FILE__ << ":"
    << __FUNCTION__ << ": found priv data attr on static var: "
    << D.getName() << ". Added to llvm::GlobalVariable\n";
  
  var->addAttribute(llvm::Attribute::PrivilegeData);
}

Debugging

Warning:

/root/cheri/llvm-project/clang/lib/AST/TypePrinter.cpp:1514:11: warning: enumeration values 'PrivilegeData', 'PrivilegeFunction', and 'PrivilegeLevel' not handled in switch [-Wswitch]
  switch (T->getAttrKind()) {

solution: added switch cases (commit):

  // file: clang/lib/AST/TypePrinter.cpp
  // LLM: added to avoid warning
  case attr::PrivilegeData:
    OS << "privilege_data";
    break;
  case attr::PrivilegeFunction:
    OS << "privilege_function";
    break;
  case attr::PrivilegeLevel:
    OS << "privilege_level(" << "???" << ")";
    break;

Warning:


[537/3555] Building CXX object lib/Bitcode/Reader/CMakeFiles/LLVMBitReader.dir/BitcodeReader.cpp.o
/root/cheri/llvm-project/llvm/lib/Bitcode/Reader/BitcodeReader.cpp:1216:11: warning: enumeration values 'PrivilegeData', 'PrivilegeFunction', and 'PrivilegeLevel' not handled in switch [-Wswitch]
  switch (Val) {
          ^
1 warning generated.
[555/3555] Building CXX object lib/Transforms/Utils/CMakeFiles/LLVMTransformUtils.dir/CodeExtractor.cpp.o
/root/cheri/llvm-project/llvm/lib/Transforms/Utils/CodeExtractor.cpp:789:15: warning: enumeration values 'PrivilegeData', 'PrivilegeFunction', and 'PrivilegeLevel' not handled in switch [-Wswitch]
      switch (Attr.getKindAsEnum()) {
              ^
1 warning generated.

Created Nov 1, 2019 // Last Updated Jul 22, 2020

If you could revise
the fundmental principles of
computer system design
to improve security...

... what would you change?