• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <map>
17 #include <string>
18 #include <vector>
19 
20 #include <llvm/ExecutionEngine/MCJIT.h>
21 #include <llvm/IR/IRBuilder.h>
22 #include <llvm/Support/TargetSelect.h>
23 
24 #include "common.h"
25 #include "bpf_module.h"
26 #include "table_storage.h"
27 
28 namespace ebpf {
29 
30 using std::map;
31 using std::move;
32 using std::string;
33 using std::unique_ptr;
34 using std::vector;
35 using namespace llvm;
36 
bpf_module_rw_engine_enabled(void)37 bool bpf_module_rw_engine_enabled(void) {
38   return true;
39 }
40 
initialize_rw_engine()41 void BPFModule::initialize_rw_engine() {
42   InitializeNativeTarget();
43   InitializeNativeTargetAsmPrinter();
44 }
45 
cleanup_rw_engine()46 void BPFModule::cleanup_rw_engine() {
47   rw_engine_.reset();
48 }
49 
debug_printf(Module * mod,IRBuilder<> & B,const string & fmt,vector<Value * > args)50 static void debug_printf(Module *mod, IRBuilder<> &B, const string &fmt, vector<Value *> args) {
51   GlobalVariable *fmt_gvar = B.CreateGlobalString(fmt, "fmt");
52   args.insert(args.begin(), B.CreateInBoundsGEP(fmt_gvar, vector<Value *>({B.getInt64(0), B.getInt64(0)})));
53   args.insert(args.begin(), B.getInt64((uintptr_t)stderr));
54   Function *fprintf_fn = mod->getFunction("fprintf");
55   if (!fprintf_fn) {
56     vector<Type *> fprintf_fn_args({B.getInt64Ty(), B.getInt8PtrTy()});
57     FunctionType *fprintf_fn_type = FunctionType::get(B.getInt32Ty(), fprintf_fn_args, /*isvarArg=*/true);
58     fprintf_fn = Function::Create(fprintf_fn_type, GlobalValue::ExternalLinkage, "fprintf", mod);
59     fprintf_fn->setCallingConv(CallingConv::C);
60     fprintf_fn->addFnAttr(Attribute::NoUnwind);
61   }
62   B.CreateCall(fprintf_fn, args);
63 }
64 
finish_sscanf(IRBuilder<> & B,vector<Value * > * args,string * fmt,const map<string,Value * > & locals,bool exact_args)65 static void finish_sscanf(IRBuilder<> &B, vector<Value *> *args, string *fmt,
66                           const map<string, Value *> &locals, bool exact_args) {
67   // fmt += "%n";
68   // int nread = 0;
69   // int n = sscanf(s, fmt, args..., &nread);
70   // if (n < 0) return -1;
71   // s = &s[nread];
72   Value *sptr = locals.at("sptr");
73   Value *nread = locals.at("nread");
74   Function *cur_fn = B.GetInsertBlock()->getParent();
75   Function *sscanf_fn = B.GetInsertBlock()->getModule()->getFunction("sscanf");
76   *fmt += "%n";
77   B.CreateStore(B.getInt32(0), nread);
78   GlobalVariable *fmt_gvar = B.CreateGlobalString(*fmt, "fmt");
79   (*args)[1] = B.CreateInBoundsGEP(fmt_gvar, {B.getInt64(0), B.getInt64(0)});
80   (*args)[0] = B.CreateLoad(sptr);
81   args->push_back(nread);
82   CallInst *call = B.CreateCall(sscanf_fn, *args);
83   call->setTailCall(true);
84 
85   BasicBlock *label_true = BasicBlock::Create(B.getContext(), "", cur_fn);
86   BasicBlock *label_false = BasicBlock::Create(B.getContext(), "", cur_fn);
87 
88   // exact_args means fail if don't consume exact number of "%" inputs
89   // exact_args is disabled for string parsing (empty case)
90   Value *cond = exact_args ? B.CreateICmpNE(call, B.getInt32(args->size() - 3))
91                            : B.CreateICmpSLT(call, B.getInt32(0));
92   B.CreateCondBr(cond, label_true, label_false);
93 
94   B.SetInsertPoint(label_true);
95   B.CreateRet(B.getInt32(-1));
96 
97   B.SetInsertPoint(label_false);
98   // s = &s[nread];
99   B.CreateStore(
100       B.CreateInBoundsGEP(B.CreateLoad(sptr), B.CreateLoad(nread, true)), sptr);
101 
102   args->resize(2);
103   fmt->clear();
104 }
105 
106 // recursive helper to capture the arguments
parse_type(IRBuilder<> & B,vector<Value * > * args,string * fmt,Type * type,Value * out,const map<string,Value * > & locals,bool is_writer)107 static void parse_type(IRBuilder<> &B, vector<Value *> *args, string *fmt,
108                        Type *type, Value *out,
109                        const map<string, Value *> &locals, bool is_writer) {
110   if (StructType *st = dyn_cast<StructType>(type)) {
111     *fmt += "{ ";
112     unsigned idx = 0;
113     for (auto field : st->elements()) {
114       parse_type(B, args, fmt, field, B.CreateStructGEP(type, out, idx++),
115                  locals, is_writer);
116       *fmt += " ";
117     }
118     *fmt += "}";
119   } else if (ArrayType *at = dyn_cast<ArrayType>(type)) {
120     if (at->getElementType() == B.getInt8Ty()) {
121       // treat i8[] as a char string instead of as an array of u8's
122       if (is_writer) {
123         *fmt += "\"%s\"";
124         args->push_back(out);
125       } else {
126         // When reading strings, scanf doesn't support empty "", so we need to
127         // break this up into multiple scanf calls. To understand it, let's take
128         // an example:
129         // struct Event {
130         //   u32 a;
131         //   struct {
132         //     char x[64];
133         //     int y;
134         //   } b[2];
135         //   u32 c;
136         // };
137         // The writer string would look like:
138         //  "{ 0x%x [ { \"%s\" 0x%x } { \"%s\" 0x%x } ] 0x%x }"
139         // But the reader string needs to restart at each \"\".
140         //  reader0(const char *s, struct Event *val) {
141         //    int nread, rc;
142         //    nread = 0;
143         //    rc = sscanf(s, "{ %i [ { \"%n", &val->a, &nread);
144         //    if (rc != 1) return -1;
145         //    s += nread; nread = 0;
146         //    rc = sscanf(s, "%[^\"]%n", &val->b[0].x, &nread);
147         //    if (rc < 0) return -1;
148         //    s += nread; nread = 0;
149         //    rc = sscanf(s, "\" %i } { \"%n", &val->b[0].y, &nread);
150         //    if (rc != 1) return -1;
151         //    s += nread; nread = 0;
152         //    rc = sscanf(s, "%[^\"]%n", &val->b[1].x, &nread);
153         //    if (rc < 0) return -1;
154         //    s += nread; nread = 0;
155         //    rc = sscanf(s, "\" %i } ] %i }%n", &val->b[1].y, &val->c, &nread);
156         //    if (rc != 2) return -1;
157         //    s += nread; nread = 0;
158         //    return 0;
159         //  }
160         *fmt += "\"";
161         finish_sscanf(B, args, fmt, locals, true);
162 
163         *fmt = "%[^\"]";
164         args->push_back(out);
165         finish_sscanf(B, args, fmt, locals, false);
166 
167         *fmt = "\"";
168       }
169     } else {
170       *fmt += "[ ";
171       for (size_t i = 0; i < at->getNumElements(); ++i) {
172         parse_type(B, args, fmt, at->getElementType(),
173                    B.CreateStructGEP(type, out, i), locals, is_writer);
174         *fmt += " ";
175       }
176       *fmt += "]";
177     }
178   } else if (isa<PointerType>(type)) {
179     *fmt += "0xl";
180     if (is_writer)
181       *fmt += "x";
182     else
183       *fmt += "i";
184   } else if (IntegerType *it = dyn_cast<IntegerType>(type)) {
185     if (is_writer)
186       *fmt += "0x";
187     if (it->getBitWidth() <= 8)
188       *fmt += "%hh";
189     else if (it->getBitWidth() <= 16)
190       *fmt += "%h";
191     else if (it->getBitWidth() <= 32)
192       *fmt += "%";
193     else
194       *fmt += "%l";
195     if (is_writer)
196       *fmt += "x";
197     else
198       *fmt += "i";
199     args->push_back(is_writer ? B.CreateLoad(out) : out);
200   }
201 }
202 
203 // make_reader generates a dynamic function in the instruction set of the host
204 // (not bpf) that is able to convert c-strings in the pretty-print format of
205 // make_writer back into binary representations. The encoding of the string
206 // takes the llvm ir structure format, which closely maps the c structure but
207 // not exactly (no support for unions for instance).
208 // The general algorithm is:
209 //  pod types (u8..u64)                <= %i
210 //  array types
211 //   u8[]  no nested quotes :(         <= "..."
212 //   !u8[]                             <= [ %i %i ... ]
213 //  struct types
214 //   struct { u8 a; u64 b; }           <= { %i %i }
215 //  nesting is supported
216 //   struct { struct { u8 a[]; }; }    <= { "" }
217 //   struct { struct { u64 a[]; }; }   <= { [ %i %i .. ] }
make_reader(Module * mod,Type * type)218 string BPFModule::make_reader(Module *mod, Type *type) {
219   auto fn_it = readers_.find(type);
220   if (fn_it != readers_.end())
221     return fn_it->second;
222 
223   // int read(const char *in, Type *out) {
224   //   int n = sscanf(in, "{ %i ... }", &out->field1, ...);
225   //   if (n != num_fields) return -1;
226   //   return 0;
227   // }
228 
229   IRBuilder<> B(*ctx_);
230 
231   FunctionType *sscanf_fn_type = FunctionType::get(
232       B.getInt32Ty(), {B.getInt8PtrTy(), B.getInt8PtrTy()}, /*isVarArg=*/true);
233   Function *sscanf_fn = mod->getFunction("sscanf");
234   if (!sscanf_fn) {
235     sscanf_fn = Function::Create(sscanf_fn_type, GlobalValue::ExternalLinkage,
236                                  "sscanf", mod);
237     sscanf_fn->setCallingConv(CallingConv::C);
238     sscanf_fn->addFnAttr(Attribute::NoUnwind);
239   }
240 
241   string name = "reader" + std::to_string(readers_.size());
242   vector<Type *> fn_args({B.getInt8PtrTy(), PointerType::getUnqual(type)});
243   FunctionType *fn_type = FunctionType::get(B.getInt32Ty(), fn_args, /*isVarArg=*/false);
244   Function *fn =
245       Function::Create(fn_type, GlobalValue::ExternalLinkage, name, mod);
246   auto arg_it = fn->arg_begin();
247   Argument *arg_in = &*arg_it;
248   ++arg_it;
249   arg_in->setName("in");
250   Argument *arg_out = &*arg_it;
251   ++arg_it;
252   arg_out->setName("out");
253 
254   BasicBlock *label_entry = BasicBlock::Create(*ctx_, "entry", fn);
255   B.SetInsertPoint(label_entry);
256 
257   Value *nread = B.CreateAlloca(B.getInt32Ty());
258   Value *sptr = B.CreateAlloca(B.getInt8PtrTy());
259   map<string, Value *> locals{{"nread", nread}, {"sptr", sptr}};
260   B.CreateStore(arg_in, sptr);
261   vector<Value *> args({nullptr, nullptr});
262   string fmt;
263   parse_type(B, &args, &fmt, type, arg_out, locals, false);
264 
265   if (0)
266     debug_printf(mod, B, "%p %p\n", vector<Value *>({arg_in, arg_out}));
267 
268   finish_sscanf(B, &args, &fmt, locals, true);
269 
270   B.CreateRet(B.getInt32(0));
271 
272   readers_[type] = name;
273   return name;
274 }
275 
276 // make_writer generates a dynamic function in the instruction set of the host
277 // (not bpf) that is able to pretty-print key/leaf entries as a c-string. The
278 // encoding of the string takes the llvm ir structure format, which closely maps
279 // the c structure but not exactly (no support for unions for instance).
280 // The general algorithm is:
281 //  pod types (u8..u64)                => 0x%x
282 //  array types
283 //   u8[]                              => "..."
284 //   !u8[]                             => [ 0x%x 0x%x ... ]
285 //  struct types
286 //   struct { u8 a; u64 b; }           => { 0x%x 0x%x }
287 //  nesting is supported
288 //   struct { struct { u8 a[]; }; }    => { "" }
289 //   struct { struct { u64 a[]; }; }   => { [ 0x%x 0x%x .. ] }
make_writer(Module * mod,Type * type)290 string BPFModule::make_writer(Module *mod, Type *type) {
291   auto fn_it = writers_.find(type);
292   if (fn_it != writers_.end())
293     return fn_it->second;
294 
295   // int write(int len, char *out, Type *in) {
296   //   return snprintf(out, len, "{ %i ... }", out->field1, ...);
297   // }
298 
299   IRBuilder<> B(*ctx_);
300 
301   string name = "writer" + std::to_string(writers_.size());
302   vector<Type *> fn_args({B.getInt8PtrTy(), B.getInt64Ty(), PointerType::getUnqual(type)});
303   FunctionType *fn_type = FunctionType::get(B.getInt32Ty(), fn_args, /*isVarArg=*/false);
304   Function *fn =
305       Function::Create(fn_type, GlobalValue::ExternalLinkage, name, mod);
306   auto arg_it = fn->arg_begin();
307   Argument *arg_out = &*arg_it;
308   ++arg_it;
309   arg_out->setName("out");
310   Argument *arg_len = &*arg_it;
311   ++arg_it;
312   arg_len->setName("len");
313   Argument *arg_in = &*arg_it;
314   ++arg_it;
315   arg_in->setName("in");
316 
317   BasicBlock *label_entry = BasicBlock::Create(*ctx_, "entry", fn);
318   B.SetInsertPoint(label_entry);
319 
320   map<string, Value *> locals{
321       {"nread", B.CreateAlloca(B.getInt64Ty())},
322   };
323   vector<Value *> args({arg_out, B.CreateZExt(arg_len, B.getInt64Ty()), nullptr});
324   string fmt;
325   parse_type(B, &args, &fmt, type, arg_in, locals, true);
326 
327   GlobalVariable *fmt_gvar = B.CreateGlobalString(fmt, "fmt");
328 
329   args[2] = B.CreateInBoundsGEP(fmt_gvar, vector<Value *>({B.getInt64(0), B.getInt64(0)}));
330 
331   if (0)
332     debug_printf(mod, B, "%d %p %p\n", vector<Value *>({arg_len, arg_out, arg_in}));
333 
334   vector<Type *> snprintf_fn_args({B.getInt8PtrTy(), B.getInt64Ty(), B.getInt8PtrTy()});
335   FunctionType *snprintf_fn_type = FunctionType::get(B.getInt32Ty(), snprintf_fn_args, /*isVarArg=*/true);
336   Function *snprintf_fn = mod->getFunction("snprintf");
337   if (!snprintf_fn)
338     snprintf_fn = Function::Create(snprintf_fn_type, GlobalValue::ExternalLinkage, "snprintf", mod);
339   snprintf_fn->setCallingConv(CallingConv::C);
340   snprintf_fn->addFnAttr(Attribute::NoUnwind);
341 
342   CallInst *call = B.CreateCall(snprintf_fn, args);
343   call->setTailCall(true);
344 
345   B.CreateRet(call);
346 
347   writers_[type] = name;
348   return name;
349 }
350 
finalize_rw(unique_ptr<Module> m)351 unique_ptr<ExecutionEngine> BPFModule::finalize_rw(unique_ptr<Module> m) {
352   Module *mod = &*m;
353 
354   run_pass_manager(*mod);
355 
356   string err;
357   EngineBuilder builder(move(m));
358   builder.setErrorStr(&err);
359   builder.setUseOrcMCJITReplacement(false);
360   auto engine = unique_ptr<ExecutionEngine>(builder.create());
361   if (!engine)
362     fprintf(stderr, "Could not create ExecutionEngine: %s\n", err.c_str());
363   return engine;
364 }
365 
annotate()366 int BPFModule::annotate() {
367   for (auto fn = mod_->getFunctionList().begin(); fn != mod_->getFunctionList().end(); ++fn)
368     if (!fn->hasFnAttribute(Attribute::NoInline))
369       fn->addFnAttr(Attribute::AlwaysInline);
370 
371   // separate module to hold the reader functions
372   auto m = ebpf::make_unique<Module>("sscanf", *ctx_);
373 
374   size_t id = 0;
375   Path path({id_});
376   for (auto it = ts_->lower_bound(path), up = ts_->upper_bound(path); it != up; ++it) {
377     TableDesc &table = it->second;
378     tables_.push_back(&it->second);
379     table_names_[table.name] = id++;
380     GlobalValue *gvar = mod_->getNamedValue(table.name);
381     if (!gvar) continue;
382     if (PointerType *pt = dyn_cast<PointerType>(gvar->getType())) {
383       if (StructType *st = dyn_cast<StructType>(pt->getElementType())) {
384         if (st->getNumElements() < 2) continue;
385         Type *key_type = st->elements()[0];
386         Type *leaf_type = st->elements()[1];
387 
388         using std::placeholders::_1;
389         using std::placeholders::_2;
390         using std::placeholders::_3;
391         table.key_sscanf = std::bind(&BPFModule::sscanf, this,
392                                      make_reader(&*m, key_type), _1, _2);
393         table.leaf_sscanf = std::bind(&BPFModule::sscanf, this,
394                                       make_reader(&*m, leaf_type), _1, _2);
395         table.key_snprintf = std::bind(&BPFModule::snprintf, this,
396                                        make_writer(&*m, key_type), _1, _2, _3);
397         table.leaf_snprintf =
398             std::bind(&BPFModule::snprintf, this, make_writer(&*m, leaf_type),
399                       _1, _2, _3);
400       }
401     }
402   }
403 
404   rw_engine_ = finalize_rw(move(m));
405   if (!rw_engine_)
406     return -1;
407   return 0;
408 }
409 
sscanf(string fn_name,const char * str,void * val)410 StatusTuple BPFModule::sscanf(string fn_name, const char *str, void *val) {
411   if (!rw_engine_enabled_)
412     return StatusTuple(-1, "rw_engine not enabled");
413   auto fn =
414       (int (*)(const char *, void *))rw_engine_->getFunctionAddress(fn_name);
415   if (!fn)
416     return StatusTuple(-1, "sscanf not available");
417   int rc = fn(str, val);
418   if (rc < 0)
419     return StatusTuple(rc, "error in sscanf: %s", std::strerror(errno));
420   return StatusTuple(rc);
421 }
422 
snprintf(string fn_name,char * str,size_t sz,const void * val)423 StatusTuple BPFModule::snprintf(string fn_name, char *str, size_t sz,
424                                 const void *val) {
425   if (!rw_engine_enabled_)
426     return StatusTuple(-1, "rw_engine not enabled");
427   auto fn = (int (*)(char *, size_t,
428                      const void *))rw_engine_->getFunctionAddress(fn_name);
429   if (!fn)
430     return StatusTuple(-1, "snprintf not available");
431   int rc = fn(str, sz, val);
432   if (rc < 0)
433     return StatusTuple(rc, "error in snprintf: %s", std::strerror(errno));
434   if ((size_t)rc == sz)
435     return StatusTuple(-1, "buffer of size %zd too small", sz);
436   return StatusTuple(0);
437 }
438 
439 } // namespace ebpf
440