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