Commit 61c063ae authored by torgil's avatar torgil Committed by yonghong-song

Make dependency on LLVM native target optional (#2080)

* Make dependency on LLVM native target optional

Adds an option ENABLE_LLVM_NATIVECODEGEN with default value ON.
If set to off the "nativecodegen" llvm will not be enabled, thus
reducing dependencies on needed libraries (reduced text size when
building with statically linked libraries).

Code that uses native target will not be compiled reducing text size.
Currently this affects the rw_engine which needs the native target.

BPF api "rw_engine_enabled" will have default value "true" if
ENABLE_LLVM_NATIVECODEGEN="ON" and "false" if
ENABLE_LLVM_NATIVECODEGEN="OFF"

Not needed for BCC to work. It somehow brought in the interpreter and
executionengine which is needed. Those features are added instead.

* Remove garbage in code making it compile again

* Remove interpreter and executionengine LLVM dependencies

These doesn't seem to be needed on a Ubuntu 18.04 system (although
executionengine is heavily used).

Interpreter was added due to runtime dependency on ARM64. It brings in
a dependency on ffi library.

(.text._ZL10ffiTypeForPN4llvm4TypeE+0x3a): undefined reference to `ffi_type_float'
(.text._ZL10ffiTypeForPN4llvm4TypeE+0x43): undefined reference to `ffi_type_void'
(.text._ZL10ffiTypeForPN4llvm4TypeE+0x53): undefined reference to `ffi_type_pointer'
(.text._ZL10ffiTypeForPN4llvm4TypeE+0x63): undefined reference to `ffi_type_double'
(.text._ZL10ffiTypeForPN4llvm4TypeE+0x78): undefined reference to `ffi_type_sint8'
(.text._ZL10ffiTypeForPN4llvm4TypeE+0x83): undefined reference to `ffi_type_sint16'
(.text._ZL10ffiTypeForPN4llvm4TypeE+0x93): undefined reference to `ffi_type_sint64'
(.text._ZL10ffiTypeForPN4llvm4TypeE+0xb3): undefined reference to `ffi_type_sint32'
/usr/lib/llvm-6.0/lib/libLLVMInterpreter.a
parent 6dc8ec89
......@@ -16,6 +16,7 @@ include(GNUInstallDirs)
include(CheckCXXCompilerFlag)
include(cmake/FindCompilerFlag.cmake)
option(ENABLE_LLVM_NATIVECODEGEN "Enable use of llvm nativecodegen module (needed by rw-engine)" ON)
option(ENABLE_RTTI "Enable compiling with real time type information" OFF)
option(ENABLE_LLVM_SHARED "Enable linking LLVM as a shared library" OFF)
option(ENABLE_CLANG_JIT "Enable Loading BPF through Clang Frontend" ON)
......
......@@ -2,7 +2,10 @@ if(ENABLE_LLVM_SHARED)
set(llvm_libs "LLVM")
else()
set(llvm_raw_libs bitwriter bpfcodegen debuginfodwarf irreader linker
mcjit objcarcopts option passes nativecodegen lto)
mcjit objcarcopts option passes lto)
if(ENABLE_LLVM_NATIVECODEGEN)
set(llvm_raw_libs ${llvm_raw_libs} nativecodegen)
endif()
list(FIND LLVM_AVAILABLE_LIBS "LLVMCoverage" _llvm_coverage)
if (${_llvm_coverage} GREATER -1)
list(APPEND llvm_raw_libs coverage)
......
......@@ -34,6 +34,12 @@ if (${LLVM_PACKAGE_VERSION} VERSION_EQUAL 6 OR ${LLVM_PACKAGE_VERSION} VERSION_G
set(bcc_common_sources ${bcc_common_sources} bcc_debug.cc)
endif()
if(ENABLE_LLVM_NATIVECODEGEN)
set(bcc_common_sources ${bcc_common_sources} bpf_module_rw_engine.cc)
else()
set(bcc_common_sources ${bcc_common_sources} bpf_module_rw_engine_disabled.cc)
endif()
set(bcc_table_sources table_storage.cc shared_table.cc bpffs_table.cc json_map_decl_visitor.cc)
set(bcc_util_sources ns_guard.cc common.cc)
set(bcc_sym_sources bcc_syms.cc bcc_elf.c bcc_perf_map.c bcc_proc.c)
......
......@@ -47,7 +47,7 @@ class BPF {
static const int BPF_MAX_STACK_DEPTH = 127;
explicit BPF(unsigned int flag = 0, TableStorage* ts = nullptr,
bool rw_engine_enabled = true, const std::string &maps_ns = "")
bool rw_engine_enabled = bpf_module_rw_engine_enabled(), const std::string &maps_ns = "")
: flag_(flag),
bpf_module_(new BPFModule(flag, ts, rw_engine_enabled, maps_ns)) {}
StatusTuple init(const std::string& bpf_program,
......
......@@ -13,32 +13,21 @@
* 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 <llvm/ADT/STLExtras.h>
#include <llvm/ExecutionEngine/MCJIT.h>
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
#include <llvm/IRReader/IRReader.h>
#include <llvm/IR/IRBuilder.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/Support/TargetSelect.h>
#include <llvm/Transforms/IPO.h>
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
......@@ -46,13 +35,11 @@
#include "common.h"
#include "bcc_debug.h"
#include "bcc_exception.h"
#include "frontends/b/loader.h"
#include "frontends/clang/loader.h"
#include "frontends/clang/b_frontend_action.h"
#include "bpf_module.h"
#include "exported_files.h"
#include "kbuild_helper.h"
#include "libbpf.h"
namespace ebpf {
......@@ -108,8 +95,7 @@ BPFModule::BPFModule(unsigned flags, TableStorage *ts, bool rw_engine_enabled,
id_(std::to_string((uintptr_t)this)),
maps_ns_(maps_ns),
ts_(ts) {
InitializeNativeTarget();
InitializeNativeTargetAsmPrinter();
initialize_rw_engine();
LLVMInitializeBPFTarget();
LLVMInitializeBPFTargetMC();
LLVMInitializeBPFTargetInfo();
......@@ -148,329 +134,13 @@ BPFModule::~BPFModule() {
}
engine_.reset();
rw_engine_.reset();
cleanup_rw_engine();
ctx_.reset();
func_src_.reset();
ts_->DeletePrefix(Path({id_}));
}
static void debug_printf(Module *mod, IRBuilder<> &B, const string &fmt, vector<Value *> args) {
GlobalVariable *fmt_gvar = B.CreateGlobalString(fmt, "fmt");
args.insert(args.begin(), B.CreateInBoundsGEP(fmt_gvar, vector<Value *>({B.getInt64(0), B.getInt64(0)})));
args.insert(args.begin(), B.getInt64((uintptr_t)stderr));
Function *fprintf_fn = mod->getFunction("fprintf");
if (!fprintf_fn) {
vector<Type *> fprintf_fn_args({B.getInt64Ty(), B.getInt8PtrTy()});
FunctionType *fprintf_fn_type = FunctionType::get(B.getInt32Ty(), fprintf_fn_args, /*isvarArg=*/true);
fprintf_fn = Function::Create(fprintf_fn_type, GlobalValue::ExternalLinkage, "fprintf", mod);
fprintf_fn->setCallingConv(CallingConv::C);
fprintf_fn->addFnAttr(Attribute::NoUnwind);
}
B.CreateCall(fprintf_fn, args);
}
static void finish_sscanf(IRBuilder<> &B, vector<Value *> *args, string *fmt,
const map<string, Value *> &locals, bool exact_args) {
// fmt += "%n";
// int nread = 0;
// int n = sscanf(s, fmt, args..., &nread);
// if (n < 0) return -1;
// s = &s[nread];
Value *sptr = locals.at("sptr");
Value *nread = locals.at("nread");
Function *cur_fn = B.GetInsertBlock()->getParent();
Function *sscanf_fn = B.GetInsertBlock()->getModule()->getFunction("sscanf");
*fmt += "%n";
B.CreateStore(B.getInt32(0), nread);
GlobalVariable *fmt_gvar = B.CreateGlobalString(*fmt, "fmt");
(*args)[1] = B.CreateInBoundsGEP(fmt_gvar, {B.getInt64(0), B.getInt64(0)});
(*args)[0] = B.CreateLoad(sptr);
args->push_back(nread);
CallInst *call = B.CreateCall(sscanf_fn, *args);
call->setTailCall(true);
BasicBlock *label_true = BasicBlock::Create(B.getContext(), "", cur_fn);
BasicBlock *label_false = BasicBlock::Create(B.getContext(), "", cur_fn);
// exact_args means fail if don't consume exact number of "%" inputs
// exact_args is disabled for string parsing (empty case)
Value *cond = exact_args ? B.CreateICmpNE(call, B.getInt32(args->size() - 3))
: B.CreateICmpSLT(call, B.getInt32(0));
B.CreateCondBr(cond, label_true, label_false);
B.SetInsertPoint(label_true);
B.CreateRet(B.getInt32(-1));
B.SetInsertPoint(label_false);
// s = &s[nread];
B.CreateStore(
B.CreateInBoundsGEP(B.CreateLoad(sptr), B.CreateLoad(nread, true)), sptr);
args->resize(2);
fmt->clear();
}
// recursive helper to capture the arguments
static void parse_type(IRBuilder<> &B, vector<Value *> *args, string *fmt,
Type *type, Value *out,
const map<string, Value *> &locals, bool is_writer) {
if (StructType *st = dyn_cast<StructType>(type)) {
*fmt += "{ ";
unsigned idx = 0;
for (auto field : st->elements()) {
parse_type(B, args, fmt, field, B.CreateStructGEP(type, out, idx++),
locals, is_writer);
*fmt += " ";
}
*fmt += "}";
} else if (ArrayType *at = dyn_cast<ArrayType>(type)) {
if (at->getElementType() == B.getInt8Ty()) {
// treat i8[] as a char string instead of as an array of u8's
if (is_writer) {
*fmt += "\"%s\"";
args->push_back(out);
} else {
// When reading strings, scanf doesn't support empty "", so we need to
// break this up into multiple scanf calls. To understand it, let's take
// an example:
// struct Event {
// u32 a;
// struct {
// char x[64];
// int y;
// } b[2];
// u32 c;
// };
// The writer string would look like:
// "{ 0x%x [ { \"%s\" 0x%x } { \"%s\" 0x%x } ] 0x%x }"
// But the reader string needs to restart at each \"\".
// reader0(const char *s, struct Event *val) {
// int nread, rc;
// nread = 0;
// rc = sscanf(s, "{ %i [ { \"%n", &val->a, &nread);
// if (rc != 1) return -1;
// s += nread; nread = 0;
// rc = sscanf(s, "%[^\"]%n", &val->b[0].x, &nread);
// if (rc < 0) return -1;
// s += nread; nread = 0;
// rc = sscanf(s, "\" %i } { \"%n", &val->b[0].y, &nread);
// if (rc != 1) return -1;
// s += nread; nread = 0;
// rc = sscanf(s, "%[^\"]%n", &val->b[1].x, &nread);
// if (rc < 0) return -1;
// s += nread; nread = 0;
// rc = sscanf(s, "\" %i } ] %i }%n", &val->b[1].y, &val->c, &nread);
// if (rc != 2) return -1;
// s += nread; nread = 0;
// return 0;
// }
*fmt += "\"";
finish_sscanf(B, args, fmt, locals, true);
*fmt = "%[^\"]";
args->push_back(out);
finish_sscanf(B, args, fmt, locals, false);
*fmt = "\"";
}
} else {
*fmt += "[ ";
for (size_t i = 0; i < at->getNumElements(); ++i) {
parse_type(B, args, fmt, at->getElementType(),
B.CreateStructGEP(type, out, i), locals, is_writer);
*fmt += " ";
}
*fmt += "]";
}
} else if (isa<PointerType>(type)) {
*fmt += "0xl";
if (is_writer)
*fmt += "x";
else
*fmt += "i";
} else if (IntegerType *it = dyn_cast<IntegerType>(type)) {
if (is_writer)
*fmt += "0x";
if (it->getBitWidth() <= 8)
*fmt += "%hh";
else if (it->getBitWidth() <= 16)
*fmt += "%h";
else if (it->getBitWidth() <= 32)
*fmt += "%";
else
*fmt += "%l";
if (is_writer)
*fmt += "x";
else
*fmt += "i";
args->push_back(is_writer ? B.CreateLoad(out) : out);
}
}
// make_reader generates a dynamic function in the instruction set of the host
// (not bpf) that is able to convert c-strings in the pretty-print format of
// make_writer back into binary representations. The encoding of the string
// takes the llvm ir structure format, which closely maps the c structure but
// not exactly (no support for unions for instance).
// The general algorithm is:
// pod types (u8..u64) <= %i
// array types
// u8[] no nested quotes :( <= "..."
// !u8[] <= [ %i %i ... ]
// struct types
// struct { u8 a; u64 b; } <= { %i %i }
// nesting is supported
// struct { struct { u8 a[]; }; } <= { "" }
// struct { struct { u64 a[]; }; } <= { [ %i %i .. ] }
string BPFModule::make_reader(Module *mod, Type *type) {
auto fn_it = readers_.find(type);
if (fn_it != readers_.end())
return fn_it->second;
// int read(const char *in, Type *out) {
// int n = sscanf(in, "{ %i ... }", &out->field1, ...);
// if (n != num_fields) return -1;
// return 0;
// }
IRBuilder<> B(*ctx_);
FunctionType *sscanf_fn_type = FunctionType::get(
B.getInt32Ty(), {B.getInt8PtrTy(), B.getInt8PtrTy()}, /*isVarArg=*/true);
Function *sscanf_fn = mod->getFunction("sscanf");
if (!sscanf_fn) {
sscanf_fn = Function::Create(sscanf_fn_type, GlobalValue::ExternalLinkage,
"sscanf", mod);
sscanf_fn->setCallingConv(CallingConv::C);
sscanf_fn->addFnAttr(Attribute::NoUnwind);
}
string name = "reader" + std::to_string(readers_.size());
vector<Type *> fn_args({B.getInt8PtrTy(), PointerType::getUnqual(type)});
FunctionType *fn_type = FunctionType::get(B.getInt32Ty(), fn_args, /*isVarArg=*/false);
Function *fn =
Function::Create(fn_type, GlobalValue::ExternalLinkage, name, mod);
auto arg_it = fn->arg_begin();
Argument *arg_in = &*arg_it;
++arg_it;
arg_in->setName("in");
Argument *arg_out = &*arg_it;
++arg_it;
arg_out->setName("out");
BasicBlock *label_entry = BasicBlock::Create(*ctx_, "entry", fn);
B.SetInsertPoint(label_entry);
Value *nread = B.CreateAlloca(B.getInt32Ty());
Value *sptr = B.CreateAlloca(B.getInt8PtrTy());
map<string, Value *> locals{{"nread", nread}, {"sptr", sptr}};
B.CreateStore(arg_in, sptr);
vector<Value *> args({nullptr, nullptr});
string fmt;
parse_type(B, &args, &fmt, type, arg_out, locals, false);
if (0)
debug_printf(mod, B, "%p %p\n", vector<Value *>({arg_in, arg_out}));
finish_sscanf(B, &args, &fmt, locals, true);
B.CreateRet(B.getInt32(0));
readers_[type] = name;
return name;
}
// make_writer generates a dynamic function in the instruction set of the host
// (not bpf) that is able to pretty-print key/leaf entries as a c-string. The
// encoding of the string takes the llvm ir structure format, which closely maps
// the c structure but not exactly (no support for unions for instance).
// The general algorithm is:
// pod types (u8..u64) => 0x%x
// array types
// u8[] => "..."
// !u8[] => [ 0x%x 0x%x ... ]
// struct types
// struct { u8 a; u64 b; } => { 0x%x 0x%x }
// nesting is supported
// struct { struct { u8 a[]; }; } => { "" }
// struct { struct { u64 a[]; }; } => { [ 0x%x 0x%x .. ] }
string BPFModule::make_writer(Module *mod, Type *type) {
auto fn_it = writers_.find(type);
if (fn_it != writers_.end())
return fn_it->second;
// int write(int len, char *out, Type *in) {
// return snprintf(out, len, "{ %i ... }", out->field1, ...);
// }
IRBuilder<> B(*ctx_);
string name = "writer" + std::to_string(writers_.size());
vector<Type *> fn_args({B.getInt8PtrTy(), B.getInt64Ty(), PointerType::getUnqual(type)});
FunctionType *fn_type = FunctionType::get(B.getInt32Ty(), fn_args, /*isVarArg=*/false);
Function *fn =
Function::Create(fn_type, GlobalValue::ExternalLinkage, name, mod);
auto arg_it = fn->arg_begin();
Argument *arg_out = &*arg_it;
++arg_it;
arg_out->setName("out");
Argument *arg_len = &*arg_it;
++arg_it;
arg_len->setName("len");
Argument *arg_in = &*arg_it;
++arg_it;
arg_in->setName("in");
BasicBlock *label_entry = BasicBlock::Create(*ctx_, "entry", fn);
B.SetInsertPoint(label_entry);
map<string, Value *> locals{
{"nread", B.CreateAlloca(B.getInt64Ty())},
};
vector<Value *> args({arg_out, B.CreateZExt(arg_len, B.getInt64Ty()), nullptr});
string fmt;
parse_type(B, &args, &fmt, type, arg_in, locals, true);
GlobalVariable *fmt_gvar = B.CreateGlobalString(fmt, "fmt");
args[2] = B.CreateInBoundsGEP(fmt_gvar, vector<Value *>({B.getInt64(0), B.getInt64(0)}));
if (0)
debug_printf(mod, B, "%d %p %p\n", vector<Value *>({arg_len, arg_out, arg_in}));
vector<Type *> snprintf_fn_args({B.getInt8PtrTy(), B.getInt64Ty(), B.getInt8PtrTy()});
FunctionType *snprintf_fn_type = FunctionType::get(B.getInt32Ty(), snprintf_fn_args, /*isVarArg=*/true);
Function *snprintf_fn = mod->getFunction("snprintf");
if (!snprintf_fn)
snprintf_fn = Function::Create(snprintf_fn_type, GlobalValue::ExternalLinkage, "snprintf", mod);
snprintf_fn->setCallingConv(CallingConv::C);
snprintf_fn->addFnAttr(Attribute::NoUnwind);
CallInst *call = B.CreateCall(snprintf_fn, args);
call->setTailCall(true);
B.CreateRet(call);
writers_[type] = name;
return name;
}
unique_ptr<ExecutionEngine> BPFModule::finalize_rw(unique_ptr<Module> m) {
Module *mod = &*m;
run_pass_manager(*mod);
string err;
EngineBuilder builder(move(m));
builder.setErrorStr(&err);
builder.setUseOrcMCJITReplacement(false);
auto engine = unique_ptr<ExecutionEngine>(builder.create());
if (!engine)
fprintf(stderr, "Could not create ExecutionEngine: %s\n", err.c_str());
return engine;
}
// load an entire c file as a module
int BPFModule::load_cfile(const string &file, bool in_memory, const char *cflags[], int ncflags) {
ClangLoader clang_loader(&*ctx_, flags_);
......@@ -507,79 +177,6 @@ void BPFModule::annotate_light() {
}
}
int BPFModule::annotate() {
for (auto fn = mod_->getFunctionList().begin(); fn != mod_->getFunctionList().end(); ++fn)
if (!fn->hasFnAttribute(Attribute::NoInline))
fn->addFnAttr(Attribute::AlwaysInline);
// separate module to hold the reader functions
auto m = ebpf::make_unique<Module>("sscanf", *ctx_);
size_t id = 0;
Path path({id_});
for (auto it = ts_->lower_bound(path), up = ts_->upper_bound(path); it != up; ++it) {
TableDesc &table = it->second;
tables_.push_back(&it->second);
table_names_[table.name] = id++;
GlobalValue *gvar = mod_->getNamedValue(table.name);
if (!gvar) continue;
if (PointerType *pt = dyn_cast<PointerType>(gvar->getType())) {
if (StructType *st = dyn_cast<StructType>(pt->getElementType())) {
if (st->getNumElements() < 2) continue;
Type *key_type = st->elements()[0];
Type *leaf_type = st->elements()[1];
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
table.key_sscanf = std::bind(&BPFModule::sscanf, this,
make_reader(&*m, key_type), _1, _2);
table.leaf_sscanf = std::bind(&BPFModule::sscanf, this,
make_reader(&*m, leaf_type), _1, _2);
table.key_snprintf = std::bind(&BPFModule::snprintf, this,
make_writer(&*m, key_type), _1, _2, _3);
table.leaf_snprintf =
std::bind(&BPFModule::snprintf, this, make_writer(&*m, leaf_type),
_1, _2, _3);
}
}
}
rw_engine_ = finalize_rw(move(m));
if (!rw_engine_)
return -1;
return 0;
}
StatusTuple BPFModule::sscanf(string fn_name, const char *str, void *val) {
if (!rw_engine_enabled_)
return StatusTuple(-1, "rw_engine not enabled");
auto fn =
(int (*)(const char *, void *))rw_engine_->getFunctionAddress(fn_name);
if (!fn)
return StatusTuple(-1, "sscanf not available");
int rc = fn(str, val);
if (rc < 0)
return StatusTuple(rc, "error in sscanf: %s", std::strerror(errno));
return StatusTuple(rc);
}
StatusTuple BPFModule::snprintf(string fn_name, char *str, size_t sz,
const void *val) {
if (!rw_engine_enabled_)
return StatusTuple(-1, "rw_engine not enabled");
auto fn = (int (*)(char *, size_t,
const void *))rw_engine_->getFunctionAddress(fn_name);
if (!fn)
return StatusTuple(-1, "snprintf not available");
int rc = fn(str, sz, val);
if (rc < 0)
return StatusTuple(rc, "error in snprintf: %s", std::strerror(errno));
if ((size_t)rc == sz)
return StatusTuple(-1, "buffer of size %zd too small", sz);
return StatusTuple(0);
}
void BPFModule::dump_ir(Module &mod) {
legacy::PassManager PM;
PM.add(createPrintModulePass(errs()));
......
......@@ -54,10 +54,14 @@ class BLoader;
class ClangLoader;
class FuncSource;
bool bpf_module_rw_engine_enabled(void);
class BPFModule {
private:
static const std::string FN_PREFIX;
int init_engine();
void initialize_rw_engine();
void cleanup_rw_engine();
int parse(llvm::Module *mod);
int finalize();
int annotate();
......
/*
* 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 <string>
#include <vector>
#include <llvm/ExecutionEngine/MCJIT.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/Support/TargetSelect.h>
#include "common.h"
#include "bpf_module.h"
#include "table_storage.h"
namespace ebpf {
using std::map;
using std::move;
using std::string;
using std::unique_ptr;
using std::vector;
using namespace llvm;
bool bpf_module_rw_engine_enabled(void) {
return true;
}
void BPFModule::initialize_rw_engine() {
InitializeNativeTarget();
InitializeNativeTargetAsmPrinter();
}
void BPFModule::cleanup_rw_engine() {
rw_engine_.reset();
}
static void debug_printf(Module *mod, IRBuilder<> &B, const string &fmt, vector<Value *> args) {
GlobalVariable *fmt_gvar = B.CreateGlobalString(fmt, "fmt");
args.insert(args.begin(), B.CreateInBoundsGEP(fmt_gvar, vector<Value *>({B.getInt64(0), B.getInt64(0)})));
args.insert(args.begin(), B.getInt64((uintptr_t)stderr));
Function *fprintf_fn = mod->getFunction("fprintf");
if (!fprintf_fn) {
vector<Type *> fprintf_fn_args({B.getInt64Ty(), B.getInt8PtrTy()});
FunctionType *fprintf_fn_type = FunctionType::get(B.getInt32Ty(), fprintf_fn_args, /*isvarArg=*/true);
fprintf_fn = Function::Create(fprintf_fn_type, GlobalValue::ExternalLinkage, "fprintf", mod);
fprintf_fn->setCallingConv(CallingConv::C);
fprintf_fn->addFnAttr(Attribute::NoUnwind);
}
B.CreateCall(fprintf_fn, args);
}
static void finish_sscanf(IRBuilder<> &B, vector<Value *> *args, string *fmt,
const map<string, Value *> &locals, bool exact_args) {
// fmt += "%n";
// int nread = 0;
// int n = sscanf(s, fmt, args..., &nread);
// if (n < 0) return -1;
// s = &s[nread];
Value *sptr = locals.at("sptr");
Value *nread = locals.at("nread");
Function *cur_fn = B.GetInsertBlock()->getParent();
Function *sscanf_fn = B.GetInsertBlock()->getModule()->getFunction("sscanf");
*fmt += "%n";
B.CreateStore(B.getInt32(0), nread);
GlobalVariable *fmt_gvar = B.CreateGlobalString(*fmt, "fmt");
(*args)[1] = B.CreateInBoundsGEP(fmt_gvar, {B.getInt64(0), B.getInt64(0)});
(*args)[0] = B.CreateLoad(sptr);
args->push_back(nread);
CallInst *call = B.CreateCall(sscanf_fn, *args);
call->setTailCall(true);
BasicBlock *label_true = BasicBlock::Create(B.getContext(), "", cur_fn);
BasicBlock *label_false = BasicBlock::Create(B.getContext(), "", cur_fn);
// exact_args means fail if don't consume exact number of "%" inputs
// exact_args is disabled for string parsing (empty case)
Value *cond = exact_args ? B.CreateICmpNE(call, B.getInt32(args->size() - 3))
: B.CreateICmpSLT(call, B.getInt32(0));
B.CreateCondBr(cond, label_true, label_false);
B.SetInsertPoint(label_true);
B.CreateRet(B.getInt32(-1));
B.SetInsertPoint(label_false);
// s = &s[nread];
B.CreateStore(
B.CreateInBoundsGEP(B.CreateLoad(sptr), B.CreateLoad(nread, true)), sptr);
args->resize(2);
fmt->clear();
}
// recursive helper to capture the arguments
static void parse_type(IRBuilder<> &B, vector<Value *> *args, string *fmt,
Type *type, Value *out,
const map<string, Value *> &locals, bool is_writer) {
if (StructType *st = dyn_cast<StructType>(type)) {
*fmt += "{ ";
unsigned idx = 0;
for (auto field : st->elements()) {
parse_type(B, args, fmt, field, B.CreateStructGEP(type, out, idx++),
locals, is_writer);
*fmt += " ";
}
*fmt += "}";
} else if (ArrayType *at = dyn_cast<ArrayType>(type)) {
if (at->getElementType() == B.getInt8Ty()) {
// treat i8[] as a char string instead of as an array of u8's
if (is_writer) {
*fmt += "\"%s\"";
args->push_back(out);
} else {
// When reading strings, scanf doesn't support empty "", so we need to
// break this up into multiple scanf calls. To understand it, let's take
// an example:
// struct Event {
// u32 a;
// struct {
// char x[64];
// int y;
// } b[2];
// u32 c;
// };
// The writer string would look like:
// "{ 0x%x [ { \"%s\" 0x%x } { \"%s\" 0x%x } ] 0x%x }"
// But the reader string needs to restart at each \"\".
// reader0(const char *s, struct Event *val) {
// int nread, rc;
// nread = 0;
// rc = sscanf(s, "{ %i [ { \"%n", &val->a, &nread);
// if (rc != 1) return -1;
// s += nread; nread = 0;
// rc = sscanf(s, "%[^\"]%n", &val->b[0].x, &nread);
// if (rc < 0) return -1;
// s += nread; nread = 0;
// rc = sscanf(s, "\" %i } { \"%n", &val->b[0].y, &nread);
// if (rc != 1) return -1;
// s += nread; nread = 0;
// rc = sscanf(s, "%[^\"]%n", &val->b[1].x, &nread);
// if (rc < 0) return -1;
// s += nread; nread = 0;
// rc = sscanf(s, "\" %i } ] %i }%n", &val->b[1].y, &val->c, &nread);
// if (rc != 2) return -1;
// s += nread; nread = 0;
// return 0;
// }
*fmt += "\"";
finish_sscanf(B, args, fmt, locals, true);
*fmt = "%[^\"]";
args->push_back(out);
finish_sscanf(B, args, fmt, locals, false);
*fmt = "\"";
}
} else {
*fmt += "[ ";
for (size_t i = 0; i < at->getNumElements(); ++i) {
parse_type(B, args, fmt, at->getElementType(),
B.CreateStructGEP(type, out, i), locals, is_writer);
*fmt += " ";
}
*fmt += "]";
}
} else if (isa<PointerType>(type)) {
*fmt += "0xl";
if (is_writer)
*fmt += "x";
else
*fmt += "i";
} else if (IntegerType *it = dyn_cast<IntegerType>(type)) {
if (is_writer)
*fmt += "0x";
if (it->getBitWidth() <= 8)
*fmt += "%hh";
else if (it->getBitWidth() <= 16)
*fmt += "%h";
else if (it->getBitWidth() <= 32)
*fmt += "%";
else
*fmt += "%l";
if (is_writer)
*fmt += "x";
else
*fmt += "i";
args->push_back(is_writer ? B.CreateLoad(out) : out);
}
}
// make_reader generates a dynamic function in the instruction set of the host
// (not bpf) that is able to convert c-strings in the pretty-print format of
// make_writer back into binary representations. The encoding of the string
// takes the llvm ir structure format, which closely maps the c structure but
// not exactly (no support for unions for instance).
// The general algorithm is:
// pod types (u8..u64) <= %i
// array types
// u8[] no nested quotes :( <= "..."
// !u8[] <= [ %i %i ... ]
// struct types
// struct { u8 a; u64 b; } <= { %i %i }
// nesting is supported
// struct { struct { u8 a[]; }; } <= { "" }
// struct { struct { u64 a[]; }; } <= { [ %i %i .. ] }
string BPFModule::make_reader(Module *mod, Type *type) {
auto fn_it = readers_.find(type);
if (fn_it != readers_.end())
return fn_it->second;
// int read(const char *in, Type *out) {
// int n = sscanf(in, "{ %i ... }", &out->field1, ...);
// if (n != num_fields) return -1;
// return 0;
// }
IRBuilder<> B(*ctx_);
FunctionType *sscanf_fn_type = FunctionType::get(
B.getInt32Ty(), {B.getInt8PtrTy(), B.getInt8PtrTy()}, /*isVarArg=*/true);
Function *sscanf_fn = mod->getFunction("sscanf");
if (!sscanf_fn) {
sscanf_fn = Function::Create(sscanf_fn_type, GlobalValue::ExternalLinkage,
"sscanf", mod);
sscanf_fn->setCallingConv(CallingConv::C);
sscanf_fn->addFnAttr(Attribute::NoUnwind);
}
string name = "reader" + std::to_string(readers_.size());
vector<Type *> fn_args({B.getInt8PtrTy(), PointerType::getUnqual(type)});
FunctionType *fn_type = FunctionType::get(B.getInt32Ty(), fn_args, /*isVarArg=*/false);
Function *fn =
Function::Create(fn_type, GlobalValue::ExternalLinkage, name, mod);
auto arg_it = fn->arg_begin();
Argument *arg_in = &*arg_it;
++arg_it;
arg_in->setName("in");
Argument *arg_out = &*arg_it;
++arg_it;
arg_out->setName("out");
BasicBlock *label_entry = BasicBlock::Create(*ctx_, "entry", fn);
B.SetInsertPoint(label_entry);
Value *nread = B.CreateAlloca(B.getInt32Ty());
Value *sptr = B.CreateAlloca(B.getInt8PtrTy());
map<string, Value *> locals{{"nread", nread}, {"sptr", sptr}};
B.CreateStore(arg_in, sptr);
vector<Value *> args({nullptr, nullptr});
string fmt;
parse_type(B, &args, &fmt, type, arg_out, locals, false);
if (0)
debug_printf(mod, B, "%p %p\n", vector<Value *>({arg_in, arg_out}));
finish_sscanf(B, &args, &fmt, locals, true);
B.CreateRet(B.getInt32(0));
readers_[type] = name;
return name;
}
// make_writer generates a dynamic function in the instruction set of the host
// (not bpf) that is able to pretty-print key/leaf entries as a c-string. The
// encoding of the string takes the llvm ir structure format, which closely maps
// the c structure but not exactly (no support for unions for instance).
// The general algorithm is:
// pod types (u8..u64) => 0x%x
// array types
// u8[] => "..."
// !u8[] => [ 0x%x 0x%x ... ]
// struct types
// struct { u8 a; u64 b; } => { 0x%x 0x%x }
// nesting is supported
// struct { struct { u8 a[]; }; } => { "" }
// struct { struct { u64 a[]; }; } => { [ 0x%x 0x%x .. ] }
string BPFModule::make_writer(Module *mod, Type *type) {
auto fn_it = writers_.find(type);
if (fn_it != writers_.end())
return fn_it->second;
// int write(int len, char *out, Type *in) {
// return snprintf(out, len, "{ %i ... }", out->field1, ...);
// }
IRBuilder<> B(*ctx_);
string name = "writer" + std::to_string(writers_.size());
vector<Type *> fn_args({B.getInt8PtrTy(), B.getInt64Ty(), PointerType::getUnqual(type)});
FunctionType *fn_type = FunctionType::get(B.getInt32Ty(), fn_args, /*isVarArg=*/false);
Function *fn =
Function::Create(fn_type, GlobalValue::ExternalLinkage, name, mod);
auto arg_it = fn->arg_begin();
Argument *arg_out = &*arg_it;
++arg_it;
arg_out->setName("out");
Argument *arg_len = &*arg_it;
++arg_it;
arg_len->setName("len");
Argument *arg_in = &*arg_it;
++arg_it;
arg_in->setName("in");
BasicBlock *label_entry = BasicBlock::Create(*ctx_, "entry", fn);
B.SetInsertPoint(label_entry);
map<string, Value *> locals{
{"nread", B.CreateAlloca(B.getInt64Ty())},
};
vector<Value *> args({arg_out, B.CreateZExt(arg_len, B.getInt64Ty()), nullptr});
string fmt;
parse_type(B, &args, &fmt, type, arg_in, locals, true);
GlobalVariable *fmt_gvar = B.CreateGlobalString(fmt, "fmt");
args[2] = B.CreateInBoundsGEP(fmt_gvar, vector<Value *>({B.getInt64(0), B.getInt64(0)}));
if (0)
debug_printf(mod, B, "%d %p %p\n", vector<Value *>({arg_len, arg_out, arg_in}));
vector<Type *> snprintf_fn_args({B.getInt8PtrTy(), B.getInt64Ty(), B.getInt8PtrTy()});
FunctionType *snprintf_fn_type = FunctionType::get(B.getInt32Ty(), snprintf_fn_args, /*isVarArg=*/true);
Function *snprintf_fn = mod->getFunction("snprintf");
if (!snprintf_fn)
snprintf_fn = Function::Create(snprintf_fn_type, GlobalValue::ExternalLinkage, "snprintf", mod);
snprintf_fn->setCallingConv(CallingConv::C);
snprintf_fn->addFnAttr(Attribute::NoUnwind);
CallInst *call = B.CreateCall(snprintf_fn, args);
call->setTailCall(true);
B.CreateRet(call);
writers_[type] = name;
return name;
}
unique_ptr<ExecutionEngine> BPFModule::finalize_rw(unique_ptr<Module> m) {
Module *mod = &*m;
run_pass_manager(*mod);
string err;
EngineBuilder builder(move(m));
builder.setErrorStr(&err);
builder.setUseOrcMCJITReplacement(false);
auto engine = unique_ptr<ExecutionEngine>(builder.create());
if (!engine)
fprintf(stderr, "Could not create ExecutionEngine: %s\n", err.c_str());
return engine;
}
int BPFModule::annotate() {
for (auto fn = mod_->getFunctionList().begin(); fn != mod_->getFunctionList().end(); ++fn)
if (!fn->hasFnAttribute(Attribute::NoInline))
fn->addFnAttr(Attribute::AlwaysInline);
// separate module to hold the reader functions
auto m = ebpf::make_unique<Module>("sscanf", *ctx_);
size_t id = 0;
Path path({id_});
for (auto it = ts_->lower_bound(path), up = ts_->upper_bound(path); it != up; ++it) {
TableDesc &table = it->second;
tables_.push_back(&it->second);
table_names_[table.name] = id++;
GlobalValue *gvar = mod_->getNamedValue(table.name);
if (!gvar) continue;
if (PointerType *pt = dyn_cast<PointerType>(gvar->getType())) {
if (StructType *st = dyn_cast<StructType>(pt->getElementType())) {
if (st->getNumElements() < 2) continue;
Type *key_type = st->elements()[0];
Type *leaf_type = st->elements()[1];
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
table.key_sscanf = std::bind(&BPFModule::sscanf, this,
make_reader(&*m, key_type), _1, _2);
table.leaf_sscanf = std::bind(&BPFModule::sscanf, this,
make_reader(&*m, leaf_type), _1, _2);
table.key_snprintf = std::bind(&BPFModule::snprintf, this,
make_writer(&*m, key_type), _1, _2, _3);
table.leaf_snprintf =
std::bind(&BPFModule::snprintf, this, make_writer(&*m, leaf_type),
_1, _2, _3);
}
}
}
rw_engine_ = finalize_rw(move(m));
if (!rw_engine_)
return -1;
return 0;
}
StatusTuple BPFModule::sscanf(string fn_name, const char *str, void *val) {
if (!rw_engine_enabled_)
return StatusTuple(-1, "rw_engine not enabled");
auto fn =
(int (*)(const char *, void *))rw_engine_->getFunctionAddress(fn_name);
if (!fn)
return StatusTuple(-1, "sscanf not available");
int rc = fn(str, val);
if (rc < 0)
return StatusTuple(rc, "error in sscanf: %s", std::strerror(errno));
return StatusTuple(rc);
}
StatusTuple BPFModule::snprintf(string fn_name, char *str, size_t sz,
const void *val) {
if (!rw_engine_enabled_)
return StatusTuple(-1, "rw_engine not enabled");
auto fn = (int (*)(char *, size_t,
const void *))rw_engine_->getFunctionAddress(fn_name);
if (!fn)
return StatusTuple(-1, "snprintf not available");
int rc = fn(str, sz, val);
if (rc < 0)
return StatusTuple(rc, "error in snprintf: %s", std::strerror(errno));
if ((size_t)rc == sz)
return StatusTuple(-1, "buffer of size %zd too small", sz);
return StatusTuple(0);
}
} // namespace ebpf
/*
* 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 "bpf_module.h"
namespace ebpf {
bool bpf_module_rw_engine_enabled(void) {
return false;
}
void BPFModule::initialize_rw_engine() {
}
void BPFModule::cleanup_rw_engine() {
}
int BPFModule::annotate() {
return -1;
}
} // namespace ebpf
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment