/* * 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" #include #include #include #if LLVM_VERSION_MAJOR <= 16 #include #endif #include #include #if LLVM_VERSION_MAJOR >= 16 #include #else #include #endif #include #include #if LLVM_VERSION_MAJOR >= 15 #include #include #include #include #else #include #endif #include #include #include #include #include #include #if LLVM_VERSION_MAJOR <= 16 #include #endif #include #include #include #include #include #include #include #include #include "bcc_btf.h" #include "bcc_debug.h" #include "bcc_elf.h" #include "bcc_libbpf_inc.h" #include "common.h" #include "exported_files.h" #include "frontends/clang/b_frontend_action.h" #include "frontends/clang/loader.h" #include "libbpf.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(sec_map_def *sections, ProgFuncInfo *prog_func_info) : sections_(sections), prog_func_info_(prog_func_info) {} virtual ~MyMemoryManager() {} uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName) override { // The programs need to change from fake fd to real map fd, so not allocate ReadOnly regions. uint8_t *Addr = SectionMemoryManager::allocateDataSection(Size, Alignment, SectionID, SectionName, false); (*sections_)[SectionName.str()] = make_tuple(Addr, Size, SectionID); return Addr; } uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName, bool isReadOnly) override { // The lines in .BTF.ext line_info, if corresponding to remapped files, will have empty source line. // The line_info will be fixed in place, so not allocate ReadOnly regions. uint8_t *Addr = SectionMemoryManager::allocateDataSection(Size, Alignment, SectionID, SectionName, false); (*sections_)[SectionName.str()] = make_tuple(Addr, Size, SectionID); return Addr; } void notifyObjectLoaded(ExecutionEngine *EE, const object::ObjectFile &o) override { auto sizes = llvm::object::computeSymbolSizes(o); for (auto ss : sizes) { auto maybe_name = ss.first.getName(); if (!maybe_name) continue; std::string name = maybe_name->str(); auto info = prog_func_info_->get_func(name); if (!info) continue; auto section = ss.first.getSection(); if (!section) continue; #if LLVM_VERSION_MAJOR >= 10 auto sec_name = section.get()->getName(); if (!sec_name) continue; #else llvm::StringRef sec_name_obj; if (!section.get()->getName(sec_name_obj)) continue; auto sec_name = &sec_name_obj; #endif info->section_ = sec_name->str(); info->size_ = ss.second; } } sec_map_def *sections_; ProgFuncInfo *prog_func_info_; }; BPFModule::BPFModule(unsigned flags, TableStorage *ts, bool rw_engine_enabled, const std::string &maps_ns, bool allow_rlimit, const char *dev_name) : flags_(flags), rw_engine_enabled_(rw_engine_enabled && bpf_module_rw_engine_enabled()), used_b_loader_(false), allow_rlimit_(allow_rlimit), ctx_(new LLVMContext), id_(std::to_string((uintptr_t)this)), maps_ns_(maps_ns), ts_(ts), btf_(nullptr) { ifindex_ = dev_name ? if_nametoindex(dev_name) : 0; initialize_rw_engine(); LLVMInitializeBPFTarget(); LLVMInitializeBPFTargetMC(); LLVMInitializeBPFTargetInfo(); LLVMInitializeBPFAsmPrinter(); #if LLVM_VERSION_MAJOR >= 6 LLVMInitializeBPFAsmParser(); if (flags & DEBUG_SOURCE) LLVMInitializeBPFDisassembler(); #endif LLVMLinkInMCJIT(); /* call empty function to force linking of MCJIT */ if (!ts_) { local_ts_ = createSharedTableStorage(); ts_ = &*local_ts_; } prog_func_info_ = ebpf::make_unique(); } static StatusTuple unimplemented_sscanf(const char *, void *) { return StatusTuple(-1, "sscanf unimplemented"); } static StatusTuple unimplemented_snprintf(char *, size_t, const void *) { return StatusTuple(-1, "snprintf unimplemented"); } BPFModule::~BPFModule() { for (auto &v : tables_) { v->key_sscanf = unimplemented_sscanf; v->leaf_sscanf = unimplemented_sscanf; v->key_snprintf = unimplemented_snprintf; v->leaf_snprintf = unimplemented_snprintf; } if (!rw_engine_enabled_) { prog_func_info_->for_each_func( [&](std::string name, FuncInfo &info) { if (!info.start_) return; delete[] info.start_; }); for (auto §ion : sections_) { delete[] std::get<0>(section.second); } } engine_.reset(); cleanup_rw_engine(); ctx_.reset(); prog_func_info_.reset(); if (btf_) delete btf_; ts_->DeletePrefix(Path({id_})); } int BPFModule::free_bcc_memory() { return bcc_free_memory(); } // 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_); if (clang_loader.parse(&mod_, *ts_, file, in_memory, cflags, ncflags, id_, *prog_func_info_, mod_src_, maps_ns_, fake_fd_map_, perf_events_)) 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 &text) { ClangLoader clang_loader(&*ctx_, flags_); const char *cflags[] = {"-DB_WORKAROUND"}; if (clang_loader.parse(&mod_, *ts_, text, true, cflags, 1, "", *prog_func_info_, mod_src_, "", fake_fd_map_, perf_events_)) return -1; return 0; } void BPFModule::annotate_light() { for (auto fn = mod_->getFunctionList().begin(); fn != mod_->getFunctionList().end(); ++fn) if (!fn->hasFnAttribute(Attribute::NoInline)) fn->addFnAttr(Attribute::AlwaysInline); 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++; } } void BPFModule::dump_ir(Module &mod) { #if LLVM_VERSION_MAJOR >= 15 // Create the analysis managers LoopAnalysisManager LAM; FunctionAnalysisManager FAM; CGSCCAnalysisManager CGAM; ModuleAnalysisManager MAM; // Create the pass manager PassBuilder PB; PB.registerModuleAnalyses(MAM); PB.registerCGSCCAnalyses(CGAM); PB.registerFunctionAnalyses(FAM); PB.registerLoopAnalyses(LAM); PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); auto MPM = PB.buildPerModuleDefaultPipeline(OptimizationLevel::O2); // Add passes and run MPM.addPass(PrintModulePass(errs())); MPM.run(mod, MAM); #else legacy::PassManager PM; PM.add(createPrintModulePass(errs())); PM.run(mod); #endif } int BPFModule::run_pass_manager(Module &mod) { if (verifyModule(mod, &errs())) { if (flags_ & DEBUG_LLVM_IR) dump_ir(mod); return -1; } #if LLVM_VERSION_MAJOR >= 15 // Create the analysis managers LoopAnalysisManager LAM; FunctionAnalysisManager FAM; CGSCCAnalysisManager CGAM; ModuleAnalysisManager MAM; // Create the pass manager PassBuilder PB; PB.registerModuleAnalyses(MAM); PB.registerCGSCCAnalyses(CGAM); PB.registerFunctionAnalyses(FAM); PB.registerLoopAnalyses(LAM); PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); auto MPM = PB.buildPerModuleDefaultPipeline(OptimizationLevel::O3); // Add passes and run MPM.addPass(AlwaysInlinerPass()); if (flags_ & DEBUG_LLVM_IR) MPM.addPass(PrintModulePass(outs())); MPM.run(mod, MAM); #else legacy::PassManager PM; PassManagerBuilder PMB; PMB.OptLevel = 3; PM.add(createFunctionInliningPass()); /* * llvm < 4.0 needs * PM.add(createAlwaysInlinerPass()); * llvm >= 4.0 needs * PM.add(createAlwaysInlinerLegacyPass()); * use below 'stable' workaround */ LLVMAddAlwaysInlinerPass(reinterpret_cast(&PM)); PMB.populateModulePassManager(PM); if (flags_ & DEBUG_LLVM_IR) PM.add(createPrintModulePass(outs())); PM.run(mod); #endif return 0; } void BPFModule::load_btf(sec_map_def §ions) { uint8_t *btf_sec = nullptr, *btf_ext_sec = nullptr; uintptr_t btf_sec_size = 0, btf_ext_sec_size = 0; for (auto section: sections) { auto sname = section.first; uint8_t *addr = get<0>(section.second); uintptr_t size = get<1>(section.second); if (strcmp(".BTF", sname.c_str()) == 0) { btf_sec = addr; btf_sec_size = size; } if (strcmp(".BTF.ext", sname.c_str()) == 0) { btf_ext_sec = addr; btf_ext_sec_size = size; } } if (btf_sec == nullptr || btf_ext_sec == nullptr) return; // Both .BTF and .BTF.ext ELF sections are present. // The remapped files (the main file and /virtual/include/bcc/helpers.h) // will provide missing source codes in the .BTF.ext line_info table. auto helpers_h = ExportedFiles::headers().find("/virtual/include/bcc/helpers.h"); if (helpers_h == ExportedFiles::headers().end()) { fprintf(stderr, "Internal error: missing bcc/helpers.h"); return; } std::map remapped_sources; remapped_sources["/virtual/main.c"] = mod_src_; remapped_sources["/virtual/include/bcc/helpers.h"] = helpers_h->second; BTF *btf = new BTF(flags_ & DEBUG_BTF, sections); int ret = btf->load(btf_sec, btf_sec_size, btf_ext_sec, btf_ext_sec_size, remapped_sources); if (ret) { delete btf; return; } btf_ = btf; } int BPFModule::create_maps(std::map> &map_tids, std::map &map_fds, std::map &inner_map_fds, bool for_inner_map) { std::set inner_maps; if (for_inner_map) { for (auto map : fake_fd_map_) { std::string inner_map_name = get<7>(map.second); if (inner_map_name.size()) inner_maps.insert(inner_map_name); } } for (auto map : fake_fd_map_) { int fd, fake_fd, map_type, key_size, value_size, max_entries, map_flags; int pinned_id; const char *map_name; const char *pinned; std::string inner_map_name; int inner_map_fd = 0; fake_fd = map.first; map_type = get<0>(map.second); map_name = get<1>(map.second).c_str(); key_size = get<2>(map.second); value_size = get<3>(map.second); max_entries = get<4>(map.second); map_flags = get<5>(map.second); pinned_id = get<6>(map.second); inner_map_name = get<7>(map.second); if (for_inner_map) { if (inner_maps.find(map_name) == inner_maps.end()) continue; if (inner_map_name.size()) { fprintf(stderr, "inner map %s has inner map %s\n", map_name, inner_map_name.c_str()); return -1; } } else { if (inner_map_fds.find(map_name) != inner_map_fds.end()) continue; if (inner_map_name.size()) inner_map_fd = inner_map_fds[inner_map_name]; } if (pinned_id <= 0) { struct bcc_create_map_attr attr = {}; attr.map_type = (enum bpf_map_type)map_type; attr.name = map_name; attr.key_size = key_size; attr.value_size = value_size; attr.max_entries = max_entries; attr.map_flags = map_flags; attr.map_ifindex = ifindex_; attr.inner_map_fd = inner_map_fd; if (map_tids.find(map_name) != map_tids.end()) { attr.btf_fd = btf_->get_fd(); attr.btf_key_type_id = map_tids[map_name].first; attr.btf_value_type_id = map_tids[map_name].second; } fd = bcc_create_map_xattr(&attr, allow_rlimit_); } else { fd = bpf_map_get_fd_by_id(pinned_id); } if (fd < 0) { fprintf(stderr, "could not open bpf map: %s, error: %s\n", map_name, strerror(errno)); return -1; } if (pinned_id == -1) { pinned = get<8>(map.second).c_str(); if (bpf_obj_pin(fd, pinned)) { fprintf(stderr, "failed to pin map: %s, error: %s\n", pinned, strerror(errno)); return -1; } } if (for_inner_map) inner_map_fds[map_name] = fd; map_fds[fake_fd] = fd; } return 0; } int BPFModule::load_maps(sec_map_def §ions) { // find .maps. sections and retrieve all map key/value type id's std::map> map_tids; if (btf_) { for (auto section : sections) { auto sec_name = section.first; if (strncmp(".maps.", sec_name.c_str(), 6) == 0) { std::string map_name = sec_name.substr(6); unsigned key_tid = 0, value_tid = 0; unsigned expected_ksize = 0, expected_vsize = 0; // skip extern maps, which won't be in fake_fd_map_ as they do not // require explicit bpf_create_map. bool is_extern = false; for (auto &t : tables_) { if (t->name == map_name) { is_extern = t->is_extern; break; } } if (is_extern) continue; for (auto map : fake_fd_map_) { std::string name; name = get<1>(map.second); if (map_name == name) { expected_ksize = get<2>(map.second); expected_vsize = get<3>(map.second); break; } } int ret = btf_->get_map_tids(map_name, expected_ksize, expected_vsize, &key_tid, &value_tid); if (ret) continue; map_tids[map_name] = std::make_pair(key_tid, value_tid); } } } // create maps std::map inner_map_fds; std::map map_fds; if (create_maps(map_tids, map_fds, inner_map_fds, true) < 0) return -1; if (create_maps(map_tids, map_fds, inner_map_fds, false) < 0) return -1; // update map table fd's for (auto it = ts_->begin(), up = ts_->end(); it != up; ++it) { TableDesc &table = it->second; if (map_fds.find(table.fake_fd) != map_fds.end()) { table.fd = map_fds[table.fake_fd]; table.fake_fd = 0; } } // update instructions prog_func_info_->for_each_func([&](std::string name, FuncInfo &info) { struct bpf_insn *insns = (struct bpf_insn *)info.start_; uint32_t i, num_insns = info.size_ / sizeof(struct bpf_insn); for (i = 0; i < num_insns; i++) { if (insns[i].code == (BPF_LD | BPF_DW | BPF_IMM)) { // change map_fd is it is a ld_pseudo if (insns[i].src_reg == BPF_PSEUDO_MAP_FD && map_fds.find(insns[i].imm) != map_fds.end()) insns[i].imm = map_fds[insns[i].imm]; i++; } } }); return 0; } int BPFModule::finalize() { Module *mod = &*mod_; sec_map_def tmp_sections, *sections_p; mod->setTargetTriple("bpf-pc-linux"); #if LLVM_VERSION_MAJOR >= 11 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ mod->setDataLayout("e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"); #else mod->setDataLayout("E-m:e-p:64:64-i64:64-i128:128-n32:64-S128"); #endif #else #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ mod->setDataLayout("e-m:e-p:64:64-i64:64-n32:64-S128"); #else mod->setDataLayout("E-m:e-p:64:64-i64:64-n32:64-S128"); #endif #endif sections_p = rw_engine_enabled_ ? §ions_ : &tmp_sections; string err; EngineBuilder builder(move(mod_)); builder.setErrorStr(&err); builder.setMCJITMemoryManager( ebpf::make_unique(sections_p, &*prog_func_info_)); builder.setMArch("bpf"); #if LLVM_VERSION_MAJOR <= 11 builder.setUseOrcMCJITReplacement(false); #endif engine_ = unique_ptr(builder.create()); if (!engine_) { fprintf(stderr, "Could not create ExecutionEngine: %s\n", err.c_str()); return -1; } engine_->setProcessAllSections(true); if (int rc = run_pass_manager(*mod)) return rc; engine_->finalizeObject(); prog_func_info_->for_each_func([&](std::string name, FuncInfo &info) { info.start_ = (uint8_t *)engine_->getFunctionAddress(name); }); finalize_prog_func_info(); if (flags_ & DEBUG_SOURCE) { SourceDebugger src_debugger(mod, *sections_p, *prog_func_info_, mod_src_, src_dbg_fmap_); src_debugger.dump(); } load_btf(*sections_p); if (load_maps(*sections_p)) return -1; if (!rw_engine_enabled_) { // Setup sections_ correctly and then free llvm internal memory for (auto section : tmp_sections) { auto fname = section.first; uintptr_t size = get<1>(section.second); uint8_t *tmp_p = NULL; // Only copy data for non-map sections if (strncmp("maps/", section.first.c_str(), 5)) { uint8_t *addr = get<0>(section.second); tmp_p = new uint8_t[size]; memcpy(tmp_p, addr, size); } sections_[fname] = make_tuple(tmp_p, size, get<2>(section.second)); } prog_func_info_->for_each_func([](std::string name, FuncInfo &info) { uint8_t *tmp_p = new uint8_t[info.size_]; memcpy(tmp_p, info.start_, info.size_); info.start_ = tmp_p; }); engine_.reset(); ctx_.reset(); } return 0; } void BPFModule::finalize_prog_func_info() { // prog_func_info_'s FuncInfo data is gradually populated (first in frontend // action, then bpf_module). It's possible for a FuncInfo to have been // created by FrontendAction but no corresponding start location found in // bpf_module - filter out these functions // // The numeric function ids in the new prog_func_info_ are considered // canonical std::unique_ptr finalized = ebpf::make_unique(); prog_func_info_->for_each_func([&](std::string name, FuncInfo &info) { if(info.start_) { auto i = finalized->add_func(name); if (i) { // should always be true *i = info; } } }); prog_func_info_.swap(finalized); } size_t BPFModule::num_functions() const { return prog_func_info_->num_funcs(); } const char * BPFModule::function_name(size_t id) const { auto name = prog_func_info_->func_name(id); if (name) return name->c_str(); return nullptr; } uint8_t * BPFModule::function_start(size_t id) const { auto fn = prog_func_info_->get_func(id); if (fn) return fn->start_; return nullptr; } uint8_t * BPFModule::function_start(const string &name) const { auto fn = prog_func_info_->get_func(name); if (fn) return fn->start_; return nullptr; } const char * BPFModule::function_source(const string &name) const { auto fn = prog_func_info_->get_func(name); if (fn) return fn->src_.c_str(); return ""; } const char * BPFModule::function_source_rewritten(const string &name) const { auto fn = prog_func_info_->get_func(name); if (fn) return fn->src_rewritten_.c_str(); return ""; } int BPFModule::annotate_prog_tag(const string &name, int prog_fd, struct bpf_insn *insns, int prog_len) { unsigned long long tag1, tag2; int err; err = bpf_prog_compute_tag(insns, prog_len, &tag1); if (err) return err; err = bpf_prog_get_tag(prog_fd, &tag2); if (err) return err; if (tag1 != tag2) { fprintf(stderr, "prog tag mismatch %llx %llx\n", tag1, tag2); return -1; } err = mkdir(BCC_PROG_TAG_DIR, 0777); if (err && errno != EEXIST) { fprintf(stderr, "cannot create " BCC_PROG_TAG_DIR "\n"); return -1; } char buf[128]; ::snprintf(buf, sizeof(buf), BCC_PROG_TAG_DIR "/bpf_prog_%llx", tag1); err = mkdir(buf, 0777); if (err && errno != EEXIST) { fprintf(stderr, "cannot create %s\n", buf); return -1; } ::snprintf(buf, sizeof(buf), BCC_PROG_TAG_DIR "/bpf_prog_%llx/%s.c", tag1, name.data()); FileDesc fd(open(buf, O_CREAT | O_WRONLY | O_TRUNC, 0644)); if (fd < 0) { fprintf(stderr, "cannot create %s\n", buf); return -1; } const char *src = function_source(name); write(fd, src, strlen(src)); ::snprintf(buf, sizeof(buf), BCC_PROG_TAG_DIR "/bpf_prog_%llx/%s.rewritten.c", tag1, name.data()); fd = open(buf, O_CREAT | O_WRONLY | O_TRUNC, 0644); if (fd < 0) { fprintf(stderr, "cannot create %s\n", buf); return -1; } src = function_source_rewritten(name); write(fd, src, strlen(src)); if (!src_dbg_fmap_[name].empty()) { ::snprintf(buf, sizeof(buf), BCC_PROG_TAG_DIR "/bpf_prog_%llx/%s.dis.txt", tag1, name.data()); fd = open(buf, O_CREAT | O_WRONLY | O_TRUNC, 0644); if (fd < 0) { fprintf(stderr, "cannot create %s\n", buf); return -1; } const char *src = src_dbg_fmap_[name].c_str(); write(fd, src, strlen(src)); } return 0; } size_t BPFModule::function_size(size_t id) const { auto fn = prog_func_info_->get_func(id); if (fn) return fn->size_; return 0; } size_t BPFModule::function_size(const string &name) const { auto fn = prog_func_info_->get_func(name); if (fn) return fn->size_; return 0; } 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); } size_t BPFModule::num_tables() const { return tables_.size(); } size_t BPFModule::perf_event_fields(const char *event) const { auto it = perf_events_.find(event); if (it == perf_events_.end()) return 0; return it->second.size(); } const char * BPFModule::perf_event_field(const char *event, size_t i) const { auto it = perf_events_.find(event); if (it == perf_events_.end() || i >= it->second.size()) return nullptr; return it->second[i].c_str(); } size_t BPFModule::table_id(const string &name) const { auto it = table_names_.find(name); if (it == table_names_.end()) return ~0ull; return it->second; } int BPFModule::table_fd(const string &name) const { return table_fd(table_id(name)); } int BPFModule::table_fd(size_t id) const { if (id >= tables_.size()) return -1; return tables_[id]->fd; } int BPFModule::table_type(const string &name) const { return table_type(table_id(name)); } int BPFModule::table_type(size_t id) const { if (id >= tables_.size()) return -1; return tables_[id]->type; } size_t BPFModule::table_max_entries(const string &name) const { return table_max_entries(table_id(name)); } size_t BPFModule::table_max_entries(size_t id) const { if (id >= tables_.size()) return 0; return tables_[id]->max_entries; } int BPFModule::table_flags(const string &name) const { return table_flags(table_id(name)); } int BPFModule::table_flags(size_t id) const { if (id >= tables_.size()) return -1; return tables_[id]->flags; } const char * BPFModule::table_name(size_t id) const { if (id >= tables_.size()) return nullptr; return tables_[id]->name.c_str(); } const char * BPFModule::table_key_desc(size_t id) const { if (used_b_loader_) return nullptr; if (id >= tables_.size()) return nullptr; return tables_[id]->key_desc.c_str(); } const char * BPFModule::table_key_desc(const string &name) const { return table_key_desc(table_id(name)); } const char * BPFModule::table_leaf_desc(size_t id) const { if (used_b_loader_) return nullptr; if (id >= tables_.size()) return nullptr; return tables_[id]->leaf_desc.c_str(); } const char * BPFModule::table_leaf_desc(const string &name) const { return table_leaf_desc(table_id(name)); } size_t BPFModule::table_key_size(size_t id) const { if (id >= tables_.size()) return 0; return tables_[id]->key_size; } size_t BPFModule::table_key_size(const string &name) const { return table_key_size(table_id(name)); } size_t BPFModule::table_leaf_size(size_t id) const { if (id >= tables_.size()) return 0; return tables_[id]->leaf_size; } size_t BPFModule::table_leaf_size(const string &name) const { return table_leaf_size(table_id(name)); } struct TableIterator { TableIterator(size_t key_size, size_t leaf_size) : key(new uint8_t[key_size]), leaf(new uint8_t[leaf_size]) { } unique_ptr key; unique_ptr leaf; uint8_t keyb[512]; }; int BPFModule::table_key_printf(size_t id, char *buf, size_t buflen, const void *key) { if (id >= tables_.size()) return -1; const TableDesc &desc = *tables_[id]; StatusTuple rc = desc.key_snprintf(buf, buflen, key); if (rc.code() < 0) { fprintf(stderr, "%s\n", rc.msg().c_str()); return -1; } return 0; } int BPFModule::table_leaf_printf(size_t id, char *buf, size_t buflen, const void *leaf) { if (id >= tables_.size()) return -1; const TableDesc &desc = *tables_[id]; StatusTuple rc = desc.leaf_snprintf(buf, buflen, leaf); if (rc.code() < 0) { fprintf(stderr, "%s\n", rc.msg().c_str()); return -1; } return 0; } int BPFModule::table_key_scanf(size_t id, const char *key_str, void *key) { if (id >= tables_.size()) return -1; const TableDesc &desc = *tables_[id]; StatusTuple rc = desc.key_sscanf(key_str, key); if (rc.code() < 0) { fprintf(stderr, "%s\n", rc.msg().c_str()); return -1; } return 0; } int BPFModule::table_leaf_scanf(size_t id, const char *leaf_str, void *leaf) { if (id >= tables_.size()) return -1; const TableDesc &desc = *tables_[id]; StatusTuple rc = desc.leaf_sscanf(leaf_str, leaf); if (rc.code() < 0) { fprintf(stderr, "%s\n", rc.msg().c_str()); return -1; } return 0; } // load a C file int BPFModule::load_c(const string &filename, const char *cflags[], int ncflags) { if (!sections_.empty()) { fprintf(stderr, "Program already initialized\n"); return -1; } if (filename.empty()) { fprintf(stderr, "Invalid filename\n"); return -1; } if (int rc = load_cfile(filename, false, cflags, ncflags)) return rc; if (rw_engine_enabled_) { if (int rc = annotate()) return rc; } else { annotate_light(); } if (int rc = finalize()) return rc; return 0; } // load a C text string int BPFModule::load_string(const string &text, const char *cflags[], int ncflags) { if (!sections_.empty()) { fprintf(stderr, "Program already initialized\n"); return -1; } if (int rc = load_cfile(text, true, cflags, ncflags)) return rc; if (rw_engine_enabled_) { if (int rc = annotate()) return rc; } else { annotate_light(); } if (int rc = finalize()) return rc; return 0; } int BPFModule::bcc_func_load(int prog_type, const char *name, const struct bpf_insn *insns, int prog_len, const char *license, unsigned kern_version, int log_level, char *log_buf, unsigned log_buf_size, const char *dev_name, unsigned flags, int expected_attach_type) { struct bpf_prog_load_opts opts = {}; unsigned func_info_cnt, line_info_cnt, finfo_rec_size, linfo_rec_size; void *func_info = NULL, *line_info = NULL; int ret; if (expected_attach_type != -1) { opts.expected_attach_type = (enum bpf_attach_type)expected_attach_type; } if (prog_type != BPF_PROG_TYPE_TRACING && prog_type != BPF_PROG_TYPE_EXT) { opts.kern_version = kern_version; } opts.prog_flags = flags; opts.log_level = log_level; if (dev_name) opts.prog_ifindex = if_nametoindex(dev_name); if (btf_) { int btf_fd = btf_->get_fd(); char secname[256]; ::snprintf(secname, sizeof(secname), "%s%s", BPF_FN_PREFIX, name); ret = btf_->get_btf_info(secname, &func_info, &func_info_cnt, &finfo_rec_size, &line_info, &line_info_cnt, &linfo_rec_size); if (!ret) { opts.prog_btf_fd = btf_fd; opts.func_info = func_info; opts.func_info_cnt = func_info_cnt; opts.func_info_rec_size = finfo_rec_size; opts.line_info = line_info; opts.line_info_cnt = line_info_cnt; opts.line_info_rec_size = linfo_rec_size; } } ret = bcc_prog_load_xattr((enum bpf_prog_type)prog_type, name, license, insns, &opts, prog_len, log_buf, log_buf_size, allow_rlimit_); if (btf_) { free(func_info); free(line_info); } return ret; } int BPFModule::bcc_func_attach(int prog_fd, int attachable_fd, int attach_type, unsigned int flags) { return bpf_prog_attach(prog_fd, attachable_fd, (enum bpf_attach_type)attach_type, flags); } int BPFModule::bcc_func_detach(int prog_fd, int attachable_fd, int attach_type) { return bpf_prog_detach2(prog_fd, attachable_fd, (enum bpf_attach_type)attach_type); } } // namespace ebpf