Brenden Blanco | 7009b55 | 2015-05-26 11:48:17 -0700 | [diff] [blame] | 1 | #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 | |
| 11 | extern "C" |
| 12 | int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, |
| 13 | int max_entries); |
| 14 | |
| 15 | namespace ebpf { |
| 16 | using std::map; |
| 17 | using std::string; |
Brenden Blanco | 2833386 | 2015-05-28 18:19:38 -0700 | [diff] [blame] | 18 | using std::to_string; |
Brenden Blanco | 7009b55 | 2015-05-26 11:48:17 -0700 | [diff] [blame] | 19 | using std::unique_ptr; |
| 20 | using namespace clang; |
| 21 | |
| 22 | BTypeVisitor::BTypeVisitor(ASTContext &C, Rewriter &rewriter, map<string, BPFTable> &tables) |
| 23 | : C(C), rewriter_(rewriter), out_(llvm::errs()), tables_(tables) { |
| 24 | } |
| 25 | |
| 26 | bool 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]) |
| 34 | bool 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 Blanco | 2833386 | 2015-05-28 18:19:38 -0700 | [diff] [blame] | 43 | |
| 44 | SourceRange argRange(Call->getArg(0)->getLocStart(), |
| 45 | Call->getArg(Call->getNumArgs()-1)->getLocEnd()); |
| 46 | string args = rewriter_.getRewrittenText(argRange); |
| 47 | |
Brenden Blanco | 7009b55 | 2015-05-26 11:48:17 -0700 | [diff] [blame] | 48 | // 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 Blanco | 2833386 | 2015-05-28 18:19:38 -0700 | [diff] [blame] | 55 | string fd = to_string(table_it->second.fd); |
Brenden Blanco | 7009b55 | 2015-05-26 11:48:17 -0700 | [diff] [blame] | 56 | string prefix, suffix; |
| 57 | string map_update_policy = "BPF_ANY"; |
Brenden Blanco | 2833386 | 2015-05-28 18:19:38 -0700 | [diff] [blame] | 58 | 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 Blanco | d23d699 | 2015-05-28 18:40:09 -0700 | [diff] [blame] | 72 | txt += " if (!leaf) return 0;"; |
Brenden Blanco | 2833386 | 2015-05-28 18:19:38 -0700 | [diff] [blame] | 73 | txt += "}"; |
| 74 | txt += "leaf;})"; |
Brenden Blanco | 7009b55 | 2015-05-26 11:48:17 -0700 | [diff] [blame] | 75 | } else { |
Brenden Blanco | 2833386 | 2015-05-28 18:19:38 -0700 | [diff] [blame] | 76 | 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 Blanco | 7009b55 | 2015-05-26 11:48:17 -0700 | [diff] [blame] | 93 | |
Brenden Blanco | 2833386 | 2015-05-28 18:19:38 -0700 | [diff] [blame] | 94 | txt = prefix + args + suffix; |
| 95 | } |
| 96 | rewriter_.ReplaceText(SourceRange(Call->getLocStart(), Call->getLocEnd()), txt); |
Brenden Blanco | 7009b55 | 2015-05-26 11:48:17 -0700 | [diff] [blame] | 97 | 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 | // } |
| 115 | bool 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 Blanco | 2833386 | 2015-05-28 18:19:38 -0700 | [diff] [blame] | 128 | string fd = to_string(table_it->second.fd); |
Brenden Blanco | 7009b55 | 2015-05-26 11:48:17 -0700 | [diff] [blame] | 129 | 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 Blanco | d23d699 | 2015-05-28 18:40:09 -0700 | [diff] [blame] | 140 | txt += " if (!leaf) return 0;"; |
Brenden Blanco | 7009b55 | 2015-05-26 11:48:17 -0700 | [diff] [blame] | 141 | txt += "}"; |
| 142 | txt += "leaf;}))"; |
| 143 | rewriter_.ReplaceText(SourceRange(Arr->getLocStart(), Arr->getLocEnd()), txt); |
| 144 | } |
| 145 | } |
| 146 | } |
| 147 | } |
| 148 | return true; |
| 149 | } |
| 150 | |
Brenden Blanco | 22a419b | 2015-05-29 11:14:20 -0700 | [diff] [blame] | 151 | bool 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 | } |
| 175 | bool 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 Blanco | 2833386 | 2015-05-28 18:19:38 -0700 | [diff] [blame] | 183 | if (DeclRefExpr *Ref = dyn_cast<DeclRefExpr>(Base)) { |
| 184 | if (DeprecatedAttr *A = Ref->getDecl()->getAttr<DeprecatedAttr>()) { |
| 185 | if (A->getMessage() == "packet") { |
Brenden Blanco | 22a419b | 2015-05-29 11:14:20 -0700 | [diff] [blame] | 186 | if (FieldDecl *F = dyn_cast<FieldDecl>(Memb->getMemberDecl())) { |
Brenden Blanco | 2833386 | 2015-05-28 18:19:38 -0700 | [diff] [blame] | 187 | 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 Blanco | bb7200c | 2015-06-04 18:01:42 -0700 | [diff] [blame] | 190 | string text = "bpf_dext_pkt(skb, _parse_base + (u64)" + base + "+" + to_string(ofs >> 3) |
Brenden Blanco | 2833386 | 2015-05-28 18:19:38 -0700 | [diff] [blame] | 191 | + ", " + 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 Blanco | 7009b55 | 2015-05-26 11:48:17 -0700 | [diff] [blame] | 200 | // Open table FDs when bpf tables (as denoted by section("maps*") attribute) |
| 201 | // are declared. |
| 202 | bool 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 Blanco | 083a31d | 2015-05-27 15:41:35 -0700 | [diff] [blame] | 231 | else if (A->getName() == "maps/prog") |
| 232 | map_type = BPF_MAP_TYPE_PROG_ARRAY; |
Brenden Blanco | 7009b55 | 2015-05-26 11:48:17 -0700 | [diff] [blame] | 233 | 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 Blanco | 7009b55 | 2015-05-26 11:48:17 -0700 | [diff] [blame] | 242 | |
| 243 | BTypeConsumer::BTypeConsumer(ASTContext &C, Rewriter &rewriter, map<string, BPFTable> &tables) |
| 244 | : visitor_(C, rewriter, tables) { |
| 245 | } |
| 246 | |
| 247 | bool BTypeConsumer::HandleTopLevelDecl(DeclGroupRef D) { |
| 248 | for (auto it : D) |
| 249 | visitor_.TraverseDecl(it); |
| 250 | return true; |
| 251 | } |
| 252 | |
| 253 | BFrontendAction::BFrontendAction(llvm::raw_ostream &os) |
| 254 | : rewriter_(new Rewriter), os_(os), tables_(new map<string, BPFTable>) { |
| 255 | } |
| 256 | |
| 257 | void 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 | |
| 264 | unique_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 | } |