[Clang][Attr] Support btf_type_tag attribute
authorYonghong Song <yhs@fb.com>
Thu, 4 Nov 2021 20:23:19 +0000 (13:23 -0700)
committerYonghong Song <yhs@fb.com>
Thu, 4 Nov 2021 20:59:18 +0000 (13:59 -0700)
This patch introduced btf_type_tag attribute. The attribute
is a type attribute and intends to address the below
linux use cases.
    typedef int __user *__intp;
    int foo(int __user *arg, ...)
    static int do_execve(struct filename *filename,
        const char __user *const __user *__argv,
        const char __user *const __user *__envp)

Here __user in the kernel defined as
    __attribute__((noderef, address_space(__user)))
for sparse ([1]) type checking mode.

For normal clang compilation, we intend to replace it with
    __attribute__((btf_type_tag("user")))
and record such informaiton in dwarf and BTF so such
information later can be used in kernel for bpf verification
or for other tracing functionalities.

  [1] https://www.kernel.org/doc/html/v4.11/dev-tools/sparse.html

Differential Revision: https://reviews.llvm.org/D111199

clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/AttrDocs.td
clang/lib/AST/TypePrinter.cpp
clang/lib/Sema/SemaType.cpp
clang/test/Sema/attr-btf_type_tag.c [new file with mode: 0644]

index 31ca7d21b9022be0de9ce41497dad0eef82804f8..a5443c967bf66c1283f4a07c5e4650fbf25eddd6 100644 (file)
@@ -1845,6 +1845,13 @@ def BTFDeclTag : InheritableAttr {
   let LangOpts = [COnly];
 }
 
+def BTFTypeTag : TypeAttr {
+  let Spellings = [Clang<"btf_type_tag">];
+  let Args = [StringArgument<"BTFTypeTag">];
+  let Documentation = [BTFTypeTagDocs];
+  let LangOpts = [COnly];
+}
+
 def WebAssemblyExportName : InheritableAttr,
                             TargetSpecificAttr<TargetWebAssembly> {
   let Spellings = [Clang<"export_name">];
index 09477d6c4423da7039646060339a9149717f25c9..e7afb3699eb17a87a57c64d12c2c69b821adcebd 100644 (file)
@@ -2023,6 +2023,23 @@ section too.
   }];
 }
 
+def BTFTypeTagDocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+Clang supports the ``__attribute__((btf_type_tag("ARGUMENT")))`` attribute for
+all targets. It only has effect when ``-g`` is specified on the command line and
+is currently silently ignored when not applied to a pointer type (note: this
+scenario may be diagnosed in the future).
+
+The ``ARGUMENT`` string will be preserved in IR and emitted to DWARF for the
+types used in variable declarations, function declarations, or typedef
+declarations.
+
+For BPF targets, the ``ARGUMENT`` string will also be emitted to .BTF ELF
+section.
+  }];
+}
+
 def MipsInterruptDocs : Documentation {
   let Category = DocCatFunction;
   let Heading = "interrupt (MIPS)";
index 40127f18ce460d378efac90fba3344f4d4e73572..eca9af3e5f3663d3be708eadd54b336f7b535253 100644 (file)
@@ -1746,6 +1746,9 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
   case attr::ArmMveStrictPolymorphism:
     OS << "__clang_arm_mve_strict_polymorphism";
     break;
+  case attr::BTFTypeTag:
+    OS << "btf_type_tag";
+    break;
   }
   OS << "))";
 }
index 5918876c02bf0dc81e44de1629366c3a40b64171..3512165ffdbf0ea3232f994fb539b695d1d77c3d 100644 (file)
@@ -5900,6 +5900,9 @@ namespace {
     void VisitQualifiedTypeLoc(QualifiedTypeLoc TL) {
       Visit(TL.getUnqualifiedLoc());
     }
+    // Allow to fill pointee's type locations, e.g.,
+    //   int __attr * __attr * __attr *p;
+    void VisitPointerTypeLoc(PointerTypeLoc TL) { Visit(TL.getNextTypeLoc()); }
     void VisitTypedefTypeLoc(TypedefTypeLoc TL) {
       TL.setNameLoc(DS.getTypeSpecTypeLoc());
     }
@@ -6500,6 +6503,34 @@ QualType Sema::BuildAddressSpaceAttr(QualType &T, Expr *AddrSpace,
   return BuildAddressSpaceAttr(T, ASIdx, AddrSpace, AttrLoc);
 }
 
+static void HandleBTFTypeTagAttribute(QualType &Type, const ParsedAttr &Attr,
+                                      TypeProcessingState &State) {
+  Sema &S = State.getSema();
+
+  // Check the number of attribute arguments.
+  if (Attr.getNumArgs() != 1) {
+    S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments)
+        << Attr << 1;
+    Attr.setInvalid();
+    return;
+  }
+
+  // Ensure the argument is a string.
+  auto *StrLiteral = dyn_cast<StringLiteral>(Attr.getArgAsExpr(0));
+  if (!StrLiteral) {
+    S.Diag(Attr.getLoc(), diag::err_attribute_argument_type)
+        << Attr << AANT_ArgumentString;
+    Attr.setInvalid();
+    return;
+  }
+
+  ASTContext &Ctx = S.Context;
+  StringRef BTFTypeTag = StrLiteral->getString();
+  Type = State.getAttributedType(
+      ::new (Ctx) BTFTypeTagAttr(Ctx, Attr, BTFTypeTag), Type, Type);
+  return;
+}
+
 /// HandleAddressSpaceTypeAttribute - Process an address_space attribute on the
 /// specified type.  The attribute contains 1 argument, the id of the address
 /// space for the type.
@@ -8129,6 +8160,11 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
     case ParsedAttr::IgnoredAttribute:
       break;
 
+    case ParsedAttr::AT_BTFTypeTag:
+      HandleBTFTypeTagAttribute(type, attr, state);
+      attr.setUsedAsTypeAttr();
+      break;
+
     case ParsedAttr::AT_MayAlias:
       // FIXME: This attribute needs to actually be handled, but if we ignore
       // it it breaks large amounts of Linux software.
diff --git a/clang/test/Sema/attr-btf_type_tag.c b/clang/test/Sema/attr-btf_type_tag.c
new file mode 100644 (file)
index 0000000..b40d0e4
--- /dev/null
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -x c -triple x86_64-pc-linux-gnu -dwarf-version=4 -fsyntax-only -verify %s
+
+#define __tag1 __attribute__((btf_type_tag("tag1")))
+#define __tag2 __attribute__((btf_type_tag("tag2")))
+#define __tag3 __attribute__((btf_type_tag("tag3")))
+#define __tag4 __attribute__((btf_type_tag("tag4")))
+#define __tag5 __attribute__((btf_type_tag("tag5")))
+#define __tag6 __attribute__((btf_type_tag("tag6")))
+
+int __attribute__((btf_type_tag("tag1", "tag2"))) *invalid1; // expected-error {{'btf_type_tag' attribute takes one argument}}
+int __attribute__((btf_type_tag(2))) *invalid2; // expected-error {{'btf_type_tag' attribute requires a string}}
+
+int * __tag1 __tag2 * __tag3 __tag4 * __tag5 __tag6 *g;
+
+typedef void __fn_t(int);
+typedef __fn_t __tag1 __tag2 * __tag3 __tag4 *__fn2_t;
+struct t {
+  int __tag1 * __tag2 * __tag3 *a;
+  int __tag1 __tag2 __tag3 *b;
+  __fn2_t c;
+  long d;
+};
+int __tag4 * __tag5 * __tag6 *foo1(struct t __tag1 * __tag2 * __tag3 *a1) {
+  return (int __tag4 * __tag5 * __tag6 *)a1[0][0]->d;
+}