blob: ff70b31aa52ea410d0fdc5db013fd525775f99fd [file] [log] [blame]
/*
* 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);
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_;
};
// 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);
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 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_;
};
// 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);
// 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_; }
bool is_rewritable_ext_func(clang::FunctionDecl *D);
void DoMiscWorkAround();
private:
llvm::raw_ostream &os_;
unsigned flags_;
TableStorage &ts_;
std::string id_;
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_;
};
} // namespace visitor