blob: 93029cdc4e7d7c7a35025049b8fe3e4fb35ed483 [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 <algorithm>
#include <fcntl.h>
#include <ftw.h>
#include <map>
#include <stdio.h>
#include <string>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <vector>
#include <linux/bpf.h>
#include <clang/Basic/FileManager.h>
#include <clang/Basic/TargetInfo.h>
#include <clang/CodeGen/BackendUtil.h>
#include <clang/CodeGen/CodeGenAction.h>
#include <clang/Driver/Compilation.h>
#include <clang/Driver/Driver.h>
#include <clang/Driver/Job.h>
#include <clang/Driver/Tool.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/CompilerInvocation.h>
#include <clang/Frontend/FrontendActions.h>
#include <clang/Frontend/FrontendDiagnostic.h>
#include <clang/Frontend/TextDiagnosticPrinter.h>
#include <clang/FrontendTool/Utils.h>
#include <llvm/ADT/STLExtras.h>
#include <llvm/ExecutionEngine/MCJIT.h>
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
#include <llvm/IRReader/IRReader.h>
#include <llvm/IR/IRPrintingPasses.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Verifier.h>
#include <llvm/Object/ObjectFile.h>
#include <llvm/Support/FormattedStream.h>
#include <llvm/Support/Host.h>
#include <llvm/Support/SourceMgr.h>
#include <llvm/Transforms/IPO.h>
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
#include "exception.h"
#include "parser.h"
#include "type_check.h"
#include "codegen_llvm.h"
#include "b_frontend_action.h"
#include "bpf_module.h"
#include "kbuild_helper.h"
namespace ebpf {
using std::get;
using std::make_tuple;
using std::map;
using std::move;
using std::string;
using std::tuple;
using std::unique_ptr;
using std::vector;
using namespace llvm;
// Snooping class to remember the sections as the JIT creates them
class MyMemoryManager : public SectionMemoryManager {
public:
explicit MyMemoryManager(map<string, tuple<uint8_t *, uintptr_t>> *sections)
: sections_(sections) {
}
virtual ~MyMemoryManager() {}
uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,
unsigned SectionID,
StringRef SectionName) override {
uint8_t *Addr = SectionMemoryManager::allocateCodeSection(Size, Alignment, SectionID, SectionName);
//printf("allocateCodeSection: %s Addr %p Size %ld Alignment %d SectionID %d\n",
// SectionName.str().c_str(), (void *)Addr, Size, Alignment, SectionID);
(*sections_)[SectionName.str()] = make_tuple(Addr, Size);
return Addr;
}
uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment,
unsigned SectionID, StringRef SectionName,
bool isReadOnly) override {
uint8_t *Addr = SectionMemoryManager::allocateDataSection(Size, Alignment, SectionID, SectionName, isReadOnly);
//printf("allocateDataSection: %s Addr %p Size %ld Alignment %d SectionID %d RO %d\n",
// SectionName.str().c_str(), (void *)Addr, Size, Alignment, SectionID, isReadOnly);
(*sections_)[SectionName.str()] = make_tuple(Addr, Size);
return Addr;
}
map<string, tuple<uint8_t *, uintptr_t>> *sections_;
};
BPFModule::BPFModule(unsigned flags)
: flags_(flags), ctx_(new LLVMContext) {
LLVMInitializeBPFTarget();
LLVMInitializeBPFTargetMC();
LLVMInitializeBPFTargetInfo();
LLVMInitializeBPFAsmPrinter();
LLVMLinkInMCJIT(); /* call empty function to force linking of MCJIT */
}
BPFModule::~BPFModule() {
engine_.reset();
ctx_.reset();
}
int BPFModule::load_file_module(unique_ptr<llvm::Module> *mod, const string &file, bool in_memory) {
using namespace clang;
struct utsname un;
uname(&un);
char kdir[256];
snprintf(kdir, sizeof(kdir), "%s/%s/build", KERNEL_MODULES_DIR, un.release);
// clang needs to run inside the kernel dir
DirStack dstack(kdir);
if (!dstack.ok())
return -1;
string abs_file;
if (in_memory) {
abs_file = "<bcc-memory-buffer>";
} else {
if (file.substr(0, 1) == "/")
abs_file = file;
else
abs_file = string(dstack.cwd()) + "/" + file;
}
vector<const char *> flags_cstr({"-O0", "-emit-llvm", "-I", dstack.cwd(),
"-Wno-deprecated-declarations",
"-x", "c", "-c", abs_file.c_str()});
KBuildHelper kbuild_helper;
vector<string> kflags;
if (kbuild_helper.get_flags(un.release, &kflags))
return -1;
for (auto it = kflags.begin(); it != kflags.end(); ++it)
flags_cstr.push_back(it->c_str());
// set up the error reporting class
IntrusiveRefCntPtr<DiagnosticOptions> diag_opts(new DiagnosticOptions());
auto diag_client = new TextDiagnosticPrinter(llvm::errs(), &*diag_opts);
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
DiagnosticsEngine diags(DiagID, &*diag_opts, diag_client);
// set up the command line argument wrapper
driver::Driver drv("", "x86_64-unknown-linux-gnu", diags);
drv.setTitle("bcc-clang-driver");
drv.setCheckInputsExist(false);
unique_ptr<driver::Compilation> compilation(drv.BuildCompilation(flags_cstr));
if (!compilation)
return -1;
// expect exactly 1 job, otherwise error
const driver::JobList &jobs = compilation->getJobs();
if (jobs.size() != 1 || !isa<driver::Command>(*jobs.begin())) {
SmallString<256> msg;
llvm::raw_svector_ostream os(msg);
jobs.Print(os, "; ", true);
diags.Report(diag::err_fe_expected_compiler_job) << os.str();
return -1;
}
const driver::Command &cmd = cast<driver::Command>(*jobs.begin());
if (llvm::StringRef(cmd.getCreator().getName()) != "clang") {
diags.Report(diag::err_fe_expected_clang_command);
return -1;
}
// Initialize a compiler invocation object from the clang (-cc1) arguments.
const driver::ArgStringList &ccargs = cmd.getArguments();
// first pass
auto invocation1 = make_unique<CompilerInvocation>();
if (!CompilerInvocation::CreateFromArgs(*invocation1, const_cast<const char **>(ccargs.data()),
const_cast<const char **>(ccargs.data()) + ccargs.size(), diags))
return -1;
if (in_memory) {
invocation1->getPreprocessorOpts().addRemappedFile("<bcc-memory-buffer>",
llvm::MemoryBuffer::getMemBuffer(file).release());
invocation1->getFrontendOpts().Inputs.clear();
invocation1->getFrontendOpts().Inputs.push_back(FrontendInputFile("<bcc-memory-buffer>", IK_C));
}
CompilerInstance compiler1;
compiler1.setInvocation(invocation1.release());
compiler1.createDiagnostics();
// capture the rewritten c file
string out_str;
llvm::raw_string_ostream os(out_str);
BFrontendAction bact(os);
if (!compiler1.ExecuteAction(bact))
return -1;
// this contains the open FDs
tables_ = bact.take_tables();
// second pass, clear input and take rewrite buffer
auto invocation2 = make_unique<CompilerInvocation>();
if (!CompilerInvocation::CreateFromArgs(*invocation2, const_cast<const char **>(ccargs.data()),
const_cast<const char **>(ccargs.data()) + ccargs.size(), diags))
return -1;
CompilerInstance compiler2;
invocation2->getPreprocessorOpts().addRemappedFile("<bcc-memory-buffer>",
llvm::MemoryBuffer::getMemBuffer(out_str).release());
invocation2->getFrontendOpts().Inputs.clear();
invocation2->getFrontendOpts().Inputs.push_back(FrontendInputFile("<bcc-memory-buffer>", IK_C));
// suppress warnings in the 2nd pass, but bail out on errors (our fault)
invocation2->getDiagnosticOpts().IgnoreWarnings = true;
compiler2.setInvocation(invocation2.release());
compiler2.createDiagnostics();
EmitLLVMOnlyAction ir_act(&*ctx_);
if (!compiler2.ExecuteAction(ir_act))
return -1;
*mod = ir_act.takeModule();
return 0;
}
// load an entire c file as a module
int BPFModule::load_cfile(const string &file, bool in_memory) {
unique_ptr<Module> mod;
if (load_file_module(&mod, file, in_memory))
return -1;
mod_ = &*mod;
mod_->setDataLayout("e-m:e-i64:64-f80:128-n8:16:32:64-S128");
mod_->setTargetTriple("bpf-pc-linux");
for (auto fn = mod_->getFunctionList().begin(); fn != mod_->getFunctionList().end(); ++fn)
fn->addFnAttr(Attribute::AlwaysInline);
string err;
engine_ = unique_ptr<ExecutionEngine>(EngineBuilder(move(mod))
.setErrorStr(&err)
.setMCJITMemoryManager(make_unique<MyMemoryManager>(&sections_))
.setMArch("bpf")
.create());
if (!engine_) {
fprintf(stderr, "Could not create ExecutionEngine: %s\n", err.c_str());
return -1;
}
return 0;
}
// NOTE: this is a duplicate of the above, but planning to deprecate if we
// settle on clang as the frontend
// Load in a pre-built list of functions into the initial Module object, then
// build an ExecutionEngine.
int BPFModule::load_includes(const string &tmpfile) {
unique_ptr<Module> mod;
if (load_file_module(&mod, tmpfile, false))
return -1;
mod_ = &*mod;
mod_->setDataLayout("e-m:e-i64:64-f80:128-n8:16:32:64-S128");
mod_->setTargetTriple("bpf-pc-linux");
for (auto fn = mod_->getFunctionList().begin(); fn != mod_->getFunctionList().end(); ++fn)
fn->addFnAttr(Attribute::AlwaysInline);
string err;
engine_ = unique_ptr<ExecutionEngine>(EngineBuilder(move(mod))
.setErrorStr(&err)
.setMCJITMemoryManager(make_unique<MyMemoryManager>(&sections_))
.setMArch("bpf")
.create());
if (!engine_) {
fprintf(stderr, "Could not create ExecutionEngine: %s\n", err.c_str());
return -1;
}
return 0;
}
void BPFModule::dump_ir() {
legacy::PassManager PM;
PM.add(createPrintModulePass(outs()));
PM.run(*mod_);
}
int BPFModule::parse() {
int rc;
proto_parser_ = make_unique<ebpf::cc::Parser>(proto_filename_);
rc = proto_parser_->parse();
if (rc) {
fprintf(stderr, "In file: %s\n", filename_.c_str());
return rc;
}
parser_ = make_unique<ebpf::cc::Parser>(filename_);
rc = parser_->parse();
if (rc) {
fprintf(stderr, "In file: %s\n", filename_.c_str());
return rc;
}
//ebpf::cc::Printer printer(stderr);
//printer.visit(parser_->root_node_);
ebpf::cc::TypeCheck type_check(parser_->scopes_.get(), proto_parser_->scopes_.get(), parser_->pragmas_);
auto ret = type_check.visit(parser_->root_node_);
if (get<0>(ret) != 0 || get<1>(ret).size()) {
fprintf(stderr, "Type error @line=%d: %s\n", get<0>(ret), get<1>(ret).c_str());
return -1;
}
// TODO: clean this
if (load_includes("../../src/cc/bpf_helpers.h") < 0)
return -1;
codegen_ = ebpf::make_unique<ebpf::cc::CodegenLLVM>(mod_, parser_->scopes_.get(), proto_parser_->scopes_.get());
ret = codegen_->visit(parser_->root_node_);
if (get<0>(ret) != 0 || get<1>(ret).size()) {
fprintf(stderr, "Codegen error @line=%d: %s\n", get<0>(ret), get<1>(ret).c_str());
return get<0>(ret);
}
return 0;
}
int BPFModule::finalize() {
if (verifyModule(*mod_, &errs())) {
if (flags_ & 1)
dump_ir();
return -1;
}
legacy::PassManager PM;
PassManagerBuilder PMB;
PMB.OptLevel = 3;
PM.add(createFunctionInliningPass());
PM.add(createAlwaysInlinerPass());
PMB.populateModulePassManager(PM);
if (flags_ & 1)
PM.add(createPrintModulePass(outs()));
PM.run(*mod_);
engine_->finalizeObject();
return 0;
}
uint8_t * BPFModule::start(const string &name) const {
auto section = sections_.find("." + name);
if (section == sections_.end())
return nullptr;
return get<0>(section->second);
}
size_t BPFModule::size(const string &name) const {
auto section = sections_.find("." + name);
if (section == sections_.end())
return 0;
return get<1>(section->second);
}
char * BPFModule::license() const {
auto section = sections_.find("license");
if (section == sections_.end())
return nullptr;
return (char *)get<0>(section->second);
}
unsigned BPFModule::kern_version() const {
auto section = sections_.find("version");
if (section == sections_.end())
return 0;
return *(unsigned *)get<0>(section->second);
}
int BPFModule::table_fd(const string &name) const {
int fd = codegen_ ? codegen_->get_table_fd(name) : -1;
if (fd >= 0) return fd;
auto table_it = tables_->find(name);
if (table_it == tables_->end()) return -1;
return table_it->second.fd;
}
int BPFModule::load(const string &filename, const string &proto_filename) {
if (!sections_.empty()) {
fprintf(stderr, "Program already initialized\n");
return -1;
}
filename_ = filename;
proto_filename_ = proto_filename;
if (proto_filename_.empty()) {
// direct load of .b file
if (int rc = load_cfile(filename_, false))
return rc;
} else {
// old lex .b file
if (int rc = parse())
return rc;
}
if (int rc = finalize())
return rc;
return 0;
}
int BPFModule::load_string(const string &text) {
if (!sections_.empty()) {
fprintf(stderr, "Program already initialized\n");
return -1;
}
filename_ = "<memory>";
if (int rc = load_cfile(text, true))
return rc;
if (int rc = finalize())
return rc;
return 0;
}
} // namespace ebpf