blob: 512da65e47c6ee8df86a9868045b56e7ef4f8836 [file] [log] [blame]
Brenden Blanco7009b552015-05-26 11:48:17 -07001#include <linux/bpf.h>
2
3#include <clang/AST/ASTConsumer.h>
4#include <clang/AST/ASTContext.h>
5#include <clang/AST/RecordLayout.h>
6#include <clang/Frontend/CompilerInstance.h>
7#include <clang/Rewrite/Core/Rewriter.h>
8
9#include "b_frontend_action.h"
10
11extern "C"
12int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
13 int max_entries);
14
15namespace ebpf {
16using std::map;
17using std::string;
Brenden Blanco28333862015-05-28 18:19:38 -070018using std::to_string;
Brenden Blanco7009b552015-05-26 11:48:17 -070019using std::unique_ptr;
20using namespace clang;
21
22BTypeVisitor::BTypeVisitor(ASTContext &C, Rewriter &rewriter, map<string, BPFTable> &tables)
23 : C(C), rewriter_(rewriter), out_(llvm::errs()), tables_(tables) {
24}
25
26bool BTypeVisitor::VisitFunctionDecl(FunctionDecl *D) {
27 return true;
28}
29
30// convert calls of the type:
31// table.foo(&key)
32// to:
33// bpf_table_foo_elem(bpf_pseudo_fd(table), &key [,&leaf])
34bool BTypeVisitor::VisitCallExpr(CallExpr *Call) {
35 // make sure node is a reference to a bpf table, which is assured by the
36 // presence of the section("maps/<typename>") GNU __attribute__
37 if (MemberExpr *Memb = dyn_cast<MemberExpr>(Call->getCallee()->IgnoreImplicit())) {
38 StringRef memb_name = Memb->getMemberDecl()->getName();
39 if (DeclRefExpr *Ref = dyn_cast<DeclRefExpr>(Memb->getBase())) {
40 if (SectionAttr *A = Ref->getDecl()->getAttr<SectionAttr>()) {
41 if (!A->getName().startswith("maps"))
42 return true;
Brenden Blanco28333862015-05-28 18:19:38 -070043
44 SourceRange argRange(Call->getArg(0)->getLocStart(),
45 Call->getArg(Call->getNumArgs()-1)->getLocEnd());
46 string args = rewriter_.getRewrittenText(argRange);
47
Brenden Blanco7009b552015-05-26 11:48:17 -070048 // find the table fd, which was opened at declaration time
49 auto table_it = tables_.find(Ref->getDecl()->getName());
50 if (table_it == tables_.end()) {
51 C.getDiagnostics().Report(Ref->getLocEnd(), diag::err_expected)
52 << "initialized handle for bpf_table";
53 return false;
54 }
Brenden Blanco28333862015-05-28 18:19:38 -070055 string fd = to_string(table_it->second.fd);
Brenden Blanco7009b552015-05-26 11:48:17 -070056 string prefix, suffix;
57 string map_update_policy = "BPF_ANY";
Brenden Blanco28333862015-05-28 18:19:38 -070058 string txt;
59 if (memb_name == "lookup_or_init") {
60 string map_update_policy = "BPF_NOEXIST";
61 string name = Ref->getDecl()->getName();
62 string arg0 = rewriter_.getRewrittenText(SourceRange(Call->getArg(0)->getLocStart(),
63 Call->getArg(0)->getLocEnd()));
64 string arg1 = rewriter_.getRewrittenText(SourceRange(Call->getArg(1)->getLocStart(),
65 Call->getArg(1)->getLocEnd()));
66 string lookup = "bpf_map_lookup_elem_(bpf_pseudo_fd(1, " + fd + ")";
67 string update = "bpf_map_update_elem_(bpf_pseudo_fd(1, " + fd + ")";
68 txt = "({typeof(" + name + ".leaf) *leaf = " + lookup + ", " + arg0 + "); ";
69 txt += "if (!leaf) {";
70 txt += " " + update + ", " + arg0 + ", " + arg1 + ", " + map_update_policy + ");";
71 txt += " leaf = " + lookup + ", " + arg0 + ");";
Brenden Blancod23d6992015-05-28 18:40:09 -070072 txt += " if (!leaf) return 0;";
Brenden Blanco28333862015-05-28 18:19:38 -070073 txt += "}";
74 txt += "leaf;})";
Brenden Blanco7009b552015-05-26 11:48:17 -070075 } else {
Brenden Blanco28333862015-05-28 18:19:38 -070076 if (memb_name == "lookup") {
77 prefix = "bpf_map_lookup_elem";
78 suffix = ")";
79 } else if (memb_name == "update") {
80 prefix = "bpf_map_update_elem";
81 suffix = ", " + map_update_policy + ")";
82 } else if (memb_name == "delete") {
83 prefix = "bpf_map_delete_elem";
84 suffix = ")";
85 } else if (memb_name == "call") {
86 prefix = "bpf_tail_call_";
87 suffix = ")";
88 } else {
89 llvm::errs() << "error: unknown bpf_table operation " << memb_name << "\n";
90 return false;
91 }
92 prefix += "((void *)bpf_pseudo_fd(1, " + fd + "), ";
Brenden Blanco7009b552015-05-26 11:48:17 -070093
Brenden Blanco28333862015-05-28 18:19:38 -070094 txt = prefix + args + suffix;
95 }
96 rewriter_.ReplaceText(SourceRange(Call->getLocStart(), Call->getLocEnd()), txt);
Brenden Blanco7009b552015-05-26 11:48:17 -070097 return true;
98 }
99 }
100 }
101 return true;
102}
103
104// look for table subscript references, and turn them into auto table entries:
105// table.data[key]
106// becomes:
107// struct Key key = {123};
108// struct Leaf *leaf = table.get(&key);
109// if (!leaf) {
110// struct Leaf zleaf = {0};
111// table.put(&key, &zleaf, BPF_NOEXIST);
112// leaf = table.get(&key);
113// if (!leaf) return -1;
114// }
115bool BTypeVisitor::VisitArraySubscriptExpr(ArraySubscriptExpr *Arr) {
116 Expr *LHS = Arr->getLHS()->IgnoreImplicit();
117 Expr *RHS = Arr->getRHS()->IgnoreImplicit();
118 if (MemberExpr *Memb = dyn_cast<MemberExpr>(LHS)) {
119 if (DeclRefExpr *Ref = dyn_cast<DeclRefExpr>(Memb->getBase())) {
120 if (SectionAttr *A = Ref->getDecl()->getAttr<SectionAttr>()) {
121 if (A->getName().startswith("maps")) {
122 auto table_it = tables_.find(Ref->getDecl()->getName());
123 if (table_it == tables_.end()) {
124 C.getDiagnostics().Report(Ref->getLocEnd(), diag::err_expected)
125 << "initialized handle for bpf_table";
126 return false;
127 }
Brenden Blanco28333862015-05-28 18:19:38 -0700128 string fd = to_string(table_it->second.fd);
Brenden Blanco7009b552015-05-26 11:48:17 -0700129 string map_update_policy = "BPF_NOEXIST";
130 string name = Ref->getDecl()->getName();
131 SourceRange argRange(RHS->getLocStart(), RHS->getLocEnd());
132 string args = rewriter_.getRewrittenText(argRange);
133 string lookup = "bpf_map_lookup_elem_(bpf_pseudo_fd(1, " + fd + ")";
134 string update = "bpf_map_update_elem_(bpf_pseudo_fd(1, " + fd + ")";
135 string txt = "(*({typeof(" + name + ".leaf) *leaf = " + lookup + ", " + args + "); ";
136 txt += "if (!leaf) {";
137 txt += " typeof(" + name + ".leaf) zleaf = {0};";
138 txt += " " + update + ", " + args + ", &zleaf, " + map_update_policy + ");";
139 txt += " leaf = " + lookup + ", " + args + ");";
Brenden Blancod23d6992015-05-28 18:40:09 -0700140 txt += " if (!leaf) return 0;";
Brenden Blanco7009b552015-05-26 11:48:17 -0700141 txt += "}";
142 txt += "leaf;}))";
143 rewriter_.ReplaceText(SourceRange(Arr->getLocStart(), Arr->getLocEnd()), txt);
144 }
145 }
146 }
147 }
148 return true;
149}
150
Brenden Blanco22a419b2015-05-29 11:14:20 -0700151bool BTypeVisitor::VisitBinaryOperator(BinaryOperator *E) {
152 if (!E->isAssignmentOp())
153 return true;
154 Expr *LHS = E->getLHS()->IgnoreImplicit();
155 Expr *RHS = E->getRHS()->IgnoreImplicit();
156 if (MemberExpr *Memb = dyn_cast<MemberExpr>(LHS)) {
157 if (DeclRefExpr *Base = dyn_cast<DeclRefExpr>(Memb->getBase()->IgnoreImplicit())) {
158 if (DeprecatedAttr *A = Base->getDecl()->getAttr<DeprecatedAttr>()) {
159 if (A->getMessage() == "packet") {
160 if (FieldDecl *F = dyn_cast<FieldDecl>(Memb->getMemberDecl())) {
161 uint64_t ofs = C.getFieldOffset(F);
162 uint64_t sz = F->isBitField() ? F->getBitWidthValue(C) : C.getTypeSize(F->getType());
163 string base = rewriter_.getRewrittenText(SourceRange(Base->getLocStart(), Base->getLocEnd()));
164 string rhs = rewriter_.getRewrittenText(SourceRange(RHS->getLocStart(), RHS->getLocEnd()));
165 string text = "bpf_dins_pkt(skb, (u64)" + base + "+" + to_string(ofs >> 3)
166 + ", " + to_string(ofs & 0x7) + ", " + to_string(sz) + ", " + rhs + ")";
167 rewriter_.ReplaceText(SourceRange(E->getLocStart(), E->getLocEnd()), text);
168 }
169 }
170 }
171 }
172 }
173 return true;
174}
175bool BTypeVisitor::VisitImplicitCastExpr(ImplicitCastExpr *E) {
176 // use dext only for RValues
177 if (E->getCastKind() != CK_LValueToRValue)
178 return true;
179 MemberExpr *Memb = dyn_cast<MemberExpr>(E->IgnoreImplicit());
180 if (!Memb)
181 return true;
182 Expr *Base = Memb->getBase()->IgnoreImplicit();
Brenden Blanco28333862015-05-28 18:19:38 -0700183 if (DeclRefExpr *Ref = dyn_cast<DeclRefExpr>(Base)) {
184 if (DeprecatedAttr *A = Ref->getDecl()->getAttr<DeprecatedAttr>()) {
185 if (A->getMessage() == "packet") {
Brenden Blanco22a419b2015-05-29 11:14:20 -0700186 if (FieldDecl *F = dyn_cast<FieldDecl>(Memb->getMemberDecl())) {
Brenden Blanco28333862015-05-28 18:19:38 -0700187 uint64_t ofs = C.getFieldOffset(F);
188 uint64_t sz = F->isBitField() ? F->getBitWidthValue(C) : C.getTypeSize(F->getType());
189 string base = rewriter_.getRewrittenText(SourceRange(Base->getLocStart(), Base->getLocEnd()));
Brenden Blancobb7200c2015-06-04 18:01:42 -0700190 string text = "bpf_dext_pkt(skb, _parse_base + (u64)" + base + "+" + to_string(ofs >> 3)
Brenden Blanco28333862015-05-28 18:19:38 -0700191 + ", " + to_string(ofs & 0x7) + ", " + to_string(sz) + ")";
192 rewriter_.ReplaceText(SourceRange(E->getLocStart(), E->getLocEnd()), text);
193 }
194 }
195 }
196 }
197 return true;
198}
199
Brenden Blanco7009b552015-05-26 11:48:17 -0700200// Open table FDs when bpf tables (as denoted by section("maps*") attribute)
201// are declared.
202bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) {
203 const RecordType *R = Decl->getType()->getAs<RecordType>();
204 if (SectionAttr *A = Decl->getAttr<SectionAttr>()) {
205 if (!A->getName().startswith("maps"))
206 return true;
207 if (!R) {
208 C.getDiagnostics().Report(Decl->getLocEnd(), diag::err_expected)
209 << "struct type for bpf_table";
210 return false;
211 }
212 const RecordDecl *RD = R->getDecl()->getDefinition();
213 BPFTable table;
214 unsigned i = 0;
215 for (auto F : RD->fields()) {
216 size_t sz = C.getTypeSize(F->getType()) >> 3;
217 if (F->getName() == "key") {
218 table.key_size = sz;
219 } else if (F->getName() == "leaf") {
220 table.leaf_size = sz;
221 } else if (F->getName() == "data") {
222 table.max_entries = sz / table.leaf_size;
223 }
224 ++i;
225 }
226 bpf_map_type map_type = BPF_MAP_TYPE_UNSPEC;
227 if (A->getName() == "maps/hash")
228 map_type = BPF_MAP_TYPE_HASH;
229 else if (A->getName() == "maps/array")
230 map_type = BPF_MAP_TYPE_ARRAY;
Brenden Blanco083a31d2015-05-27 15:41:35 -0700231 else if (A->getName() == "maps/prog")
232 map_type = BPF_MAP_TYPE_PROG_ARRAY;
Brenden Blanco7009b552015-05-26 11:48:17 -0700233 table.fd = bpf_create_map(map_type, table.key_size, table.leaf_size, table.max_entries);
234 if (table.fd < 0) {
235 llvm::errs() << "error: could not open bpf fd\n";
236 return false;
237 }
238 tables_[Decl->getName()] = table;
239 }
240 return true;
241}
Brenden Blanco7009b552015-05-26 11:48:17 -0700242
243BTypeConsumer::BTypeConsumer(ASTContext &C, Rewriter &rewriter, map<string, BPFTable> &tables)
244 : visitor_(C, rewriter, tables) {
245}
246
247bool BTypeConsumer::HandleTopLevelDecl(DeclGroupRef D) {
248 for (auto it : D)
249 visitor_.TraverseDecl(it);
250 return true;
251}
252
253BFrontendAction::BFrontendAction(llvm::raw_ostream &os)
254 : rewriter_(new Rewriter), os_(os), tables_(new map<string, BPFTable>) {
255}
256
257void BFrontendAction::EndSourceFileAction() {
258 // uncomment to see rewritten source
259 //rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()).write(llvm::errs());
260 rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()).write(os_);
261 os_.flush();
262}
263
264unique_ptr<ASTConsumer> BFrontendAction::CreateASTConsumer(CompilerInstance &Compiler, llvm::StringRef InFile) {
265 rewriter_->setSourceMgr(Compiler.getSourceManager(), Compiler.getLangOpts());
266 return unique_ptr<ASTConsumer>(new BTypeConsumer(Compiler.getASTContext(), *rewriter_, *tables_));
267}
268
269}