aboutsummaryrefslogtreecommitdiff
path: root/src/cc/frontends/clang/b_frontend_action.h
blob: 530d322a6e561c67fb90a10cd1859d1ea5ab495a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/*
 * Copyright (c) 2015 PLUMgrid, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>

#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Frontend/FrontendAction.h>
#include <clang/Rewrite/Core/Rewriter.h>

#include "table_storage.h"

namespace clang {
class ASTConsumer;
class ASTContext;
class CompilerInstance;
}

namespace llvm {
class raw_ostream;
class StringRef;
}

namespace ebpf {

class BFrontendAction;
class FuncSource;

// Traces maps with external pointers as values.
class MapVisitor : public clang::RecursiveASTVisitor<MapVisitor> {
 public:
  explicit MapVisitor(std::set<clang::Decl *> &m);
  bool VisitCallExpr(clang::CallExpr *Call);
  void set_ptreg(std::tuple<clang::Decl *, int> &pt) { ptregs_.insert(pt); }
 private:
  std::set<clang::Decl *> &m_;
  std::set<std::tuple<clang::Decl *, int>> ptregs_;
};

// Type visitor and rewriter for B programs.
// It will look for B-specific features and rewrite them into a valid
// C program. As part of the processing, open the necessary BPF tables
// and store the open handles in a map of table-to-fd's.
class BTypeVisitor : public clang::RecursiveASTVisitor<BTypeVisitor> {
 public:
  explicit BTypeVisitor(clang::ASTContext &C, BFrontendAction &fe);
  bool TraverseCallExpr(clang::CallExpr *Call);
  bool VisitFunctionDecl(clang::FunctionDecl *D);
  bool VisitCallExpr(clang::CallExpr *Call);
  bool VisitVarDecl(clang::VarDecl *Decl);
  bool VisitBinaryOperator(clang::BinaryOperator *E);
  bool VisitImplicitCastExpr(clang::ImplicitCastExpr *E);

 private:
  clang::SourceRange expansionRange(clang::SourceRange range);
  bool checkFormatSpecifiers(const std::string& fmt, clang::SourceLocation loc);
  void genParamDirectAssign(clang::FunctionDecl *D, std::string& preamble,
                            const char **calling_conv_regs);
  void genParamIndirectAssign(clang::FunctionDecl *D, std::string& preamble,
                              const char **calling_conv_regs);
  void rewriteFuncParam(clang::FunctionDecl *D);
  int64_t getFieldValue(clang::VarDecl *Decl, clang::FieldDecl *FDecl,
                        int64_t OrigFValue);
  template <unsigned N>
  clang::DiagnosticBuilder error(clang::SourceLocation loc, const char (&fmt)[N]);
  template <unsigned N>
  clang::DiagnosticBuilder warning(clang::SourceLocation loc, const char (&fmt)[N]);

  clang::ASTContext &C;
  clang::DiagnosticsEngine &diag_;
  BFrontendAction &fe_;
  clang::Rewriter &rewriter_;  /// modifications to the source go into this class
  llvm::raw_ostream &out_;  /// for debugging
  std::vector<clang::ParmVarDecl *> fn_args_;
  std::set<clang::Expr *> visited_;
  std::string current_fn_;
  bool has_overlap_kuaddr_;
};

// Do a depth-first search to rewrite all pointers that need to be probed
class ProbeVisitor : public clang::RecursiveASTVisitor<ProbeVisitor> {
 public:
  explicit ProbeVisitor(clang::ASTContext &C, clang::Rewriter &rewriter,
                        std::set<clang::Decl *> &m, bool track_helpers);
  bool VisitVarDecl(clang::VarDecl *Decl);
  bool TraverseStmt(clang::Stmt *S);
  bool VisitCallExpr(clang::CallExpr *Call);
  bool VisitReturnStmt(clang::ReturnStmt *R);
  bool VisitBinaryOperator(clang::BinaryOperator *E);
  bool VisitUnaryOperator(clang::UnaryOperator *E);
  bool VisitMemberExpr(clang::MemberExpr *E);
  bool VisitArraySubscriptExpr(clang::ArraySubscriptExpr *E);
  void set_ptreg(std::tuple<clang::Decl *, int> &pt) { ptregs_.insert(pt); }
  void set_ctx(clang::Decl *D) { ctx_ = D; }
  std::set<std::tuple<clang::Decl *, int>> get_ptregs() { return ptregs_; }
 private:
  bool assignsExtPtr(clang::Expr *E, int *nbAddrOf);
  bool isMemberDereference(clang::Expr *E);
  bool IsContextMemberExpr(clang::Expr *E);
  clang::SourceRange expansionRange(clang::SourceRange range);
  clang::SourceLocation expansionLoc(clang::SourceLocation loc);
  template <unsigned N>
  clang::DiagnosticBuilder error(clang::SourceLocation loc, const char (&fmt)[N]);

  clang::ASTContext &C;
  clang::Rewriter &rewriter_;
  std::set<clang::Decl *> fn_visited_;
  std::set<clang::Expr *> memb_visited_;
  std::set<const clang::Stmt *> whitelist_;
  std::set<std::tuple<clang::Decl *, int>> ptregs_;
  std::set<clang::Decl *> &m_;
  clang::Decl *ctx_;
  bool track_helpers_;
  std::list<int> ptregs_returned_;
  const clang::Stmt *addrof_stmt_;
  bool is_addrof_;
  bool has_overlap_kuaddr_;
};

// A helper class to the frontend action, walks the decls
class BTypeConsumer : public clang::ASTConsumer {
 public:
  explicit BTypeConsumer(clang::ASTContext &C, BFrontendAction &fe,
                         clang::Rewriter &rewriter, std::set<clang::Decl *> &m);
  void HandleTranslationUnit(clang::ASTContext &Context) override;
 private:
  BFrontendAction &fe_;
  MapVisitor map_visitor_;
  BTypeVisitor btype_visitor_;
  ProbeVisitor probe_visitor1_;
  ProbeVisitor probe_visitor2_;
};

// Create a B program in 2 phases (everything else is normal C frontend):
// 1. Catch the map declarations and open the fd's
// 2. Capture the IR
class BFrontendAction : public clang::ASTFrontendAction {
 public:
  // Initialize with the output stream where the new source file contents
  // should be written.
  BFrontendAction(llvm::raw_ostream &os, unsigned flags, TableStorage &ts,
                  const std::string &id, const std::string &main_path,
                  FuncSource &func_src, std::string &mod_src,
                  const std::string &maps_ns,
                  fake_fd_map_def &fake_fd_map,
                  std::map<std::string, std::vector<std::string>> &perf_events);

  // Called by clang when the AST has been completed, here the output stream
  // will be flushed.
  void EndSourceFileAction() override;

  std::unique_ptr<clang::ASTConsumer>
      CreateASTConsumer(clang::CompilerInstance &Compiler, llvm::StringRef InFile) override;

  clang::Rewriter &rewriter() const { return *rewriter_; }
  TableStorage &table_storage() const { return ts_; }
  std::string id() const { return id_; }
  std::string maps_ns() const { return maps_ns_; }
  bool is_rewritable_ext_func(clang::FunctionDecl *D);
  void DoMiscWorkAround();
  // negative fake_fd to be different from real fd in bpf_pseudo_fd.
  int get_next_fake_fd() { return next_fake_fd_--; }
  void add_map_def(int fd,
    std::tuple<int, std::string, int, int, int, int, int, std::string,
               std::string> map_def) {
    fake_fd_map_[fd] = move(map_def);
  }

 private:
  llvm::raw_ostream &os_;
  unsigned flags_;
  TableStorage &ts_;
  std::string id_;
  std::string maps_ns_;
  std::unique_ptr<clang::Rewriter> rewriter_;
  friend class BTypeVisitor;
  std::map<std::string, clang::SourceRange> func_range_;
  const std::string &main_path_;
  FuncSource &func_src_;
  std::string &mod_src_;
  std::set<clang::Decl *> m_;
  int next_fake_fd_;
  fake_fd_map_def &fake_fd_map_;
  std::map<std::string, std::vector<std::string>> &perf_events_;
};

}  // namespace visitor