1 /*
2 * Copyright (c) 2017 VMware, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <memory>
18 #include <string>
19
20 #include <clang/AST/ASTContext.h>
21 #include <clang/AST/RecordLayout.h>
22 #include <clang/AST/RecursiveASTVisitor.h>
23 #include <llvm/ADT/StringExtras.h>
24 #include "common.h"
25 #include "table_desc.h"
26
27 namespace ebpf {
28
29 using std::string;
30 using std::to_string;
31 using std::unique_ptr;
32 using namespace clang;
33
34 // Helper visitor for constructing a string representation of a key/leaf decl
35 class BMapDeclVisitor : public clang::RecursiveASTVisitor<BMapDeclVisitor> {
36 public:
37 explicit BMapDeclVisitor(clang::ASTContext &C, std::string &result);
38 bool TraverseRecordDecl(clang::RecordDecl *Decl);
39 bool VisitRecordDecl(clang::RecordDecl *Decl);
40 bool VisitFieldDecl(clang::FieldDecl *Decl);
41 bool VisitBuiltinType(const clang::BuiltinType *T);
42 bool VisitTypedefType(const clang::TypedefType *T);
43 bool VisitTagType(const clang::TagType *T);
44 bool VisitPointerType(const clang::PointerType *T);
45 bool VisitEnumDecl(clang::EnumDecl *D);
46 bool VisitAttr(clang::Attr *A);
47
48 private:
49 bool shouldSkipPadding(const RecordDecl *D);
50 void genJSONForField(FieldDecl *F);
51
52 private:
53 clang::ASTContext &C;
54 std::string &result_;
55 };
56
57 // Encode the struct layout as a json description
BMapDeclVisitor(ASTContext & C,string & result)58 BMapDeclVisitor::BMapDeclVisitor(ASTContext &C, string &result) : C(C), result_(result) {}
59
shouldSkipPadding(const RecordDecl * D)60 bool BMapDeclVisitor::shouldSkipPadding(const RecordDecl *D) {
61 if (D->isUnion() || D->field_empty())
62 return true;
63 for (auto F : D->getDefinition()->fields()) {
64 if (F->isBitField())
65 return true;
66 QualType Ty = F->getType();
67 if (Ty->isIncompleteArrayType())
68 return true;
69 }
70 return false;
71 }
72
genJSONForField(FieldDecl * F)73 void BMapDeclVisitor::genJSONForField(FieldDecl *F) {
74 if (F->isAnonymousStructOrUnion()) {
75 if (const RecordType *R = dyn_cast<RecordType>(F->getType()))
76 TraverseDecl(R->getDecl());
77 result_ += ", ";
78 return;
79 }
80 result_ += "[";
81 TraverseDecl(F);
82 if (const ConstantArrayType *T = dyn_cast<ConstantArrayType>(F->getType()))
83 #if LLVM_MAJOR_VERSION >= 13
84 result_ += ", [" + toString(T->getSize(), 10, false) + "]";
85 #else
86 result_ += ", [" + T->getSize().toString(10, false) + "]";
87 #endif
88 if (F->isBitField())
89 result_ += ", " + to_string(F->getBitWidthValue(C));
90 result_ += "], ";
91 }
92
VisitFieldDecl(FieldDecl * D)93 bool BMapDeclVisitor::VisitFieldDecl(FieldDecl *D) {
94 result_ += "\"";
95 result_ += D->getName();
96 result_ += "\",";
97 return true;
98 }
99
VisitEnumDecl(EnumDecl * D)100 bool BMapDeclVisitor::VisitEnumDecl(EnumDecl *D) {
101 TraverseType(D->getIntegerType());
102 return false;
103 }
104
TraverseRecordDecl(RecordDecl * D)105 bool BMapDeclVisitor::TraverseRecordDecl(RecordDecl *D) {
106 // skip children, handled in Visit...
107 if (!WalkUpFromRecordDecl(D))
108 return false;
109 return true;
110 }
111
VisitRecordDecl(RecordDecl * D)112 bool BMapDeclVisitor::VisitRecordDecl(RecordDecl *D) {
113 result_ += "[\"";
114 result_ += D->getName();
115 result_ += "\", [";
116
117 bool SkipPadding = shouldSkipPadding(D);
118 if (SkipPadding) {
119 for (auto F : D->getDefinition()->fields()) {
120 genJSONForField(F);
121 }
122 } else {
123 const ASTRecordLayout &Layout = C.getASTRecordLayout(D);
124 CharUnits Offset = C.toCharUnitsFromBits(Layout.getFieldOffset(0));
125 for (auto F : D->getDefinition()->fields()) {
126 CharUnits FieldSize = C.getTypeSizeInChars(F->getType());
127 auto FieldOffsetBits = Layout.getFieldOffset(F->getFieldIndex());
128 CharUnits FieldOffset = C.toCharUnitsFromBits(FieldOffsetBits);
129
130 uint64_t Padding = (FieldOffset - Offset).getQuantity();
131 if (Padding) {
132 /* Padding before this field with "char __pad_<FieldIndex>[Padding]". */
133 result_ += "[\"__pad_" + to_string(F->getFieldIndex()) + "\",\"char\",["
134 + to_string(Padding) + "]], ";
135 }
136 Offset = FieldOffset + FieldSize;
137 genJSONForField(F);
138 }
139
140 /* Additional Padding after the last field so that the Record Size matches */
141 CharUnits RecordSize = Layout.getSize();
142 if (RecordSize > Offset) {
143 result_ += "[\"__pad_end\",\"char\",["
144 + to_string((RecordSize - Offset).getQuantity()) + "]], ";
145 }
146 }
147
148 if (!D->getDefinition()->field_empty())
149 result_.erase(result_.end() - 2);
150 result_ += "]";
151 if (D->isUnion())
152 result_ += ", \"union\"";
153 else if (D->isStruct()) {
154 if (SkipPadding)
155 result_ += ", \"struct\"";
156 else
157 result_ += ", \"struct_packed\"";
158 }
159 result_ += "]";
160 return true;
161 }
162 // pointer to anything should be treated as terminal, don't recurse further
VisitPointerType(const PointerType * T)163 bool BMapDeclVisitor::VisitPointerType(const PointerType *T) {
164 result_ += "\"unsigned long long\"";
165 return false;
166 }
VisitTagType(const TagType * T)167 bool BMapDeclVisitor::VisitTagType(const TagType *T) {
168 return TraverseDecl(T->getDecl()->getDefinition());
169 }
VisitTypedefType(const TypedefType * T)170 bool BMapDeclVisitor::VisitTypedefType(const TypedefType *T) { return TraverseDecl(T->getDecl()); }
VisitBuiltinType(const BuiltinType * T)171 bool BMapDeclVisitor::VisitBuiltinType(const BuiltinType *T) {
172 result_ += "\"";
173 result_ += T->getName(C.getPrintingPolicy());
174 result_ += "\"";
175 return true;
176 }
VisitAttr(clang::Attr * A)177 bool BMapDeclVisitor::VisitAttr(clang::Attr *A) { return false; }
178
179 class JsonMapTypesVisitor : public virtual MapTypesVisitor {
180 public:
Visit(TableDesc & desc,clang::ASTContext & C,clang::QualType key_type,clang::QualType leaf_type)181 virtual void Visit(TableDesc &desc, clang::ASTContext &C, clang::QualType key_type,
182 clang::QualType leaf_type) {
183 BMapDeclVisitor v1(C, desc.key_desc), v2(C, desc.leaf_desc);
184 v1.TraverseType(key_type);
185 v2.TraverseType(leaf_type);
186 }
187 };
188
createJsonMapTypesVisitor()189 unique_ptr<MapTypesVisitor> createJsonMapTypesVisitor() {
190 return make_unique<JsonMapTypesVisitor>();
191 }
192
193 } // namespace ebpf
194