1 /* 2 * Copyright (c) 2015 PLUMgrid, 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 <map> 18 #include <memory> 19 #include <set> 20 #include <string> 21 #include <vector> 22 23 #include <clang/AST/RecursiveASTVisitor.h> 24 #include <clang/Frontend/FrontendAction.h> 25 #include <clang/Rewrite/Core/Rewriter.h> 26 27 #include "table_storage.h" 28 29 namespace clang { 30 class ASTConsumer; 31 class ASTContext; 32 class CompilerInstance; 33 } 34 35 namespace llvm { 36 class raw_ostream; 37 class StringRef; 38 } 39 40 namespace ebpf { 41 42 class BFrontendAction; 43 class ProgFuncInfo; 44 45 // Traces maps with external pointers as values. 46 class MapVisitor : public clang::RecursiveASTVisitor<MapVisitor> { 47 public: 48 explicit MapVisitor(std::set<clang::Decl *> &m); 49 bool VisitCallExpr(clang::CallExpr *Call); set_ptreg(std::tuple<clang::Decl *,int> & pt)50 void set_ptreg(std::tuple<clang::Decl *, int> &pt) { ptregs_.insert(pt); } 51 private: 52 std::set<clang::Decl *> &m_; 53 std::set<std::tuple<clang::Decl *, int>> ptregs_; 54 }; 55 56 // Type visitor and rewriter for B programs. 57 // It will look for B-specific features and rewrite them into a valid 58 // C program. As part of the processing, open the necessary BPF tables 59 // and store the open handles in a map of table-to-fd's. 60 class BTypeVisitor : public clang::RecursiveASTVisitor<BTypeVisitor> { 61 public: 62 explicit BTypeVisitor(clang::ASTContext &C, BFrontendAction &fe); 63 bool TraverseCallExpr(clang::CallExpr *Call); 64 bool VisitFunctionDecl(clang::FunctionDecl *D); 65 bool VisitCallExpr(clang::CallExpr *Call); 66 bool VisitVarDecl(clang::VarDecl *Decl); 67 bool VisitBinaryOperator(clang::BinaryOperator *E); 68 bool VisitImplicitCastExpr(clang::ImplicitCastExpr *E); 69 70 private: 71 clang::SourceRange expansionRange(clang::SourceRange range); 72 bool checkFormatSpecifiers(const std::string& fmt, clang::SourceLocation loc); 73 void genParamDirectAssign(clang::FunctionDecl *D, std::string& preamble, 74 const char **calling_conv_regs); 75 void genParamIndirectAssign(clang::FunctionDecl *D, std::string& preamble, 76 const char **calling_conv_regs); 77 void rewriteFuncParam(clang::FunctionDecl *D); 78 int64_t getFieldValue(clang::VarDecl *Decl, clang::FieldDecl *FDecl, 79 int64_t OrigFValue); 80 template <unsigned N> 81 clang::DiagnosticBuilder error(clang::SourceLocation loc, const char (&fmt)[N]); 82 template <unsigned N> 83 clang::DiagnosticBuilder warning(clang::SourceLocation loc, const char (&fmt)[N]); 84 85 clang::ASTContext &C; 86 clang::DiagnosticsEngine &diag_; 87 BFrontendAction &fe_; 88 clang::Rewriter &rewriter_; /// modifications to the source go into this class 89 llvm::raw_ostream &out_; /// for debugging 90 std::vector<clang::ParmVarDecl *> fn_args_; 91 std::set<clang::Expr *> visited_; 92 std::string current_fn_; 93 bool cannot_fall_back_safely; 94 }; 95 96 // Do a depth-first search to rewrite all pointers that need to be probed 97 class ProbeVisitor : public clang::RecursiveASTVisitor<ProbeVisitor> { 98 public: 99 explicit ProbeVisitor(clang::ASTContext &C, clang::Rewriter &rewriter, 100 std::set<clang::Decl *> &m, bool track_helpers); 101 bool VisitVarDecl(clang::VarDecl *Decl); 102 bool TraverseStmt(clang::Stmt *S); 103 bool VisitCallExpr(clang::CallExpr *Call); 104 bool VisitReturnStmt(clang::ReturnStmt *R); 105 bool VisitBinaryOperator(clang::BinaryOperator *E); 106 bool VisitUnaryOperator(clang::UnaryOperator *E); 107 bool VisitMemberExpr(clang::MemberExpr *E); 108 bool VisitArraySubscriptExpr(clang::ArraySubscriptExpr *E); set_ptreg(std::tuple<clang::Decl *,int> & pt)109 void set_ptreg(std::tuple<clang::Decl *, int> &pt) { ptregs_.insert(pt); } set_ctx(clang::Decl * D)110 void set_ctx(clang::Decl *D) { ctx_ = D; } get_ptregs()111 std::set<std::tuple<clang::Decl *, int>> get_ptregs() { return ptregs_; } 112 private: 113 bool assignsExtPtr(clang::Expr *E, int *nbAddrOf); 114 bool isMemberDereference(clang::Expr *E); 115 bool IsContextMemberExpr(clang::Expr *E); 116 clang::SourceRange expansionRange(clang::SourceRange range); 117 clang::SourceLocation expansionLoc(clang::SourceLocation loc); 118 template <unsigned N> 119 clang::DiagnosticBuilder error(clang::SourceLocation loc, const char (&fmt)[N]); 120 121 clang::ASTContext &C; 122 clang::Rewriter &rewriter_; 123 std::set<clang::Decl *> fn_visited_; 124 std::set<clang::Expr *> memb_visited_; 125 std::set<const clang::Stmt *> whitelist_; 126 std::set<std::tuple<clang::Decl *, int>> ptregs_; 127 std::set<clang::Decl *> &m_; 128 clang::Decl *ctx_; 129 bool track_helpers_; 130 std::list<int> ptregs_returned_; 131 const clang::Stmt *addrof_stmt_; 132 bool is_addrof_; 133 bool cannot_fall_back_safely; 134 }; 135 136 // A helper class to the frontend action, walks the decls 137 class BTypeConsumer : public clang::ASTConsumer { 138 public: 139 explicit BTypeConsumer(clang::ASTContext &C, BFrontendAction &fe, 140 clang::Rewriter &rewriter, std::set<clang::Decl *> &m); 141 void HandleTranslationUnit(clang::ASTContext &Context) override; 142 private: 143 BFrontendAction &fe_; 144 MapVisitor map_visitor_; 145 BTypeVisitor btype_visitor_; 146 ProbeVisitor probe_visitor1_; 147 ProbeVisitor probe_visitor2_; 148 }; 149 150 // Create a B program in 2 phases (everything else is normal C frontend): 151 // 1. Catch the map declarations and open the fd's 152 // 2. Capture the IR 153 class BFrontendAction : public clang::ASTFrontendAction { 154 public: 155 // Initialize with the output stream where the new source file contents 156 // should be written. 157 BFrontendAction(llvm::raw_ostream &os, unsigned flags, TableStorage &ts, 158 const std::string &id, const std::string &main_path, 159 ProgFuncInfo &prog_func_info, std::string &mod_src, 160 const std::string &maps_ns, fake_fd_map_def &fake_fd_map, 161 std::map<std::string, std::vector<std::string>> &perf_events); 162 163 // Called by clang when the AST has been completed, here the output stream 164 // will be flushed. 165 void EndSourceFileAction() override; 166 167 std::unique_ptr<clang::ASTConsumer> 168 CreateASTConsumer(clang::CompilerInstance &Compiler, llvm::StringRef InFile) override; 169 rewriter()170 clang::Rewriter &rewriter() const { return *rewriter_; } table_storage()171 TableStorage &table_storage() const { return ts_; } id()172 std::string id() const { return id_; } maps_ns()173 std::string maps_ns() const { return maps_ns_; } 174 bool is_rewritable_ext_func(clang::FunctionDecl *D); 175 void DoMiscWorkAround(); 176 // negative fake_fd to be different from real fd in bpf_pseudo_fd. get_next_fake_fd()177 int get_next_fake_fd() { return next_fake_fd_--; } add_map_def(int fd,std::tuple<int,std::string,int,int,int,int,int,std::string,std::string> map_def)178 void add_map_def(int fd, 179 std::tuple<int, std::string, int, int, int, int, int, std::string, 180 std::string> map_def) { 181 fake_fd_map_[fd] = move(map_def); 182 } 183 184 private: 185 llvm::raw_ostream &os_; 186 unsigned flags_; 187 TableStorage &ts_; 188 std::string id_; 189 std::string maps_ns_; 190 std::unique_ptr<clang::Rewriter> rewriter_; 191 friend class BTypeVisitor; 192 std::map<std::string, clang::SourceRange> func_range_; 193 const std::string &main_path_; 194 ProgFuncInfo &prog_func_info_; 195 std::string &mod_src_; 196 std::set<clang::Decl *> m_; 197 int next_fake_fd_; 198 fake_fd_map_def &fake_fd_map_; 199 std::map<std::string, std::vector<std::string>> &perf_events_; 200 }; 201 202 } // namespace visitor 203