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