| /* | |
| * 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 <fcntl.h> | |
| #include <map> | |
| #include <string> | |
| #include <sys/stat.h> | |
| #include <unistd.h> | |
| #include <vector> | |
| #include <set> | |
| #include <linux/bpf.h> | |
| #include <net/if.h> | |
| #include <llvm/ExecutionEngine/MCJIT.h> | |
| #include <llvm/ExecutionEngine/SectionMemoryManager.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/Support/TargetSelect.h> | |
| #include <llvm/Transforms/IPO.h> | |
| #include <llvm/Transforms/IPO/PassManagerBuilder.h> | |
| #include <llvm-c/Transforms/IPO.h> | |
| #include "common.h" | |
| #include "bcc_debug.h" | |
| #include "bcc_elf.h" | |
| #include "frontends/clang/loader.h" | |
| #include "frontends/clang/b_frontend_action.h" | |
| #include "bpf_module.h" | |
| #include "exported_files.h" | |
| #include "libbpf.h" | |
| #include "bcc_btf.h" | |
| #include "bcc_libbpf_inc.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; | |
| const string BPFModule::FN_PREFIX = BPF_FN_PREFIX; | |
| // Snooping class to remember the sections as the JIT creates them | |
| class MyMemoryManager : public SectionMemoryManager { | |
| public: | |
| explicit MyMemoryManager(sec_map_def *sections) | |
| : sections_(sections) { | |
| } | |
| 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); | |
| //printf("allocateDataSection: %s Addr %p Size %ld Alignment %d SectionID %d\n", | |
| // SectionName.str().c_str(), (void *)Addr, Size, Alignment, SectionID); | |
| (*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); | |
| //printf("allocateDataSection: %s Addr %p Size %ld Alignment %d SectionID %d\n", | |
| // SectionName.str().c_str(), (void *)Addr, Size, Alignment, SectionID); | |
| (*sections_)[SectionName.str()] = make_tuple(Addr, Size, SectionID); | |
| return Addr; | |
| } | |
| sec_map_def *sections_; | |
| }; | |
| 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_MAJOR_VERSION >= 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_; | |
| } | |
| func_src_ = ebpf::make_unique<FuncSource>(); | |
| } | |
| 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_) { | |
| for (auto section : sections_) | |
| delete[] get<0>(section.second); | |
| } | |
| engine_.reset(); | |
| cleanup_rw_engine(); | |
| ctx_.reset(); | |
| func_src_.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_, | |
| *func_src_, 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, "", *func_src_, | |
| 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) { | |
| legacy::PassManager PM; | |
| PM.add(createPrintModulePass(errs())); | |
| PM.run(mod); | |
| } | |
| int BPFModule::run_pass_manager(Module &mod) { | |
| if (verifyModule(mod, &errs())) { | |
| if (flags_ & DEBUG_LLVM_IR) | |
| dump_ir(mod); | |
| return -1; | |
| } | |
| 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<LLVMPassManagerRef>(&PM)); | |
| PMB.populateModulePassManager(PM); | |
| if (flags_ & DEBUG_LLVM_IR) | |
| PM.add(createPrintModulePass(outs())); | |
| PM.run(mod); | |
| 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<std::string, std::string> 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<std::string, std::pair<int, int>> &map_tids, | |
| std::map<int, int> &map_fds, | |
| std::map<std::string, int> &inner_map_fds, | |
| bool for_inner_map) { | |
| std::set<std::string> 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 bpf_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.<table_name> sections and retrieve all map key/value type id's | |
| std::map<std::string, std::pair<int, int>> 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<std::string, int> inner_map_fds; | |
| std::map<int, int> 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 | |
| for (auto section : sections) { | |
| auto sec_name = section.first; | |
| if (strncmp(".bpf.fn.", sec_name.c_str(), 8) == 0) { | |
| uint8_t *addr = get<0>(section.second); | |
| uintptr_t size = get<1>(section.second); | |
| struct bpf_insn *insns = (struct bpf_insn *)addr; | |
| int i, num_insns; | |
| num_insns = 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_MAJOR_VERSION >= 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<MyMemoryManager>(sections_p)); | |
| builder.setMArch("bpf"); | |
| #if LLVM_MAJOR_VERSION <= 11 | |
| builder.setUseOrcMCJITReplacement(false); | |
| #endif | |
| engine_ = unique_ptr<ExecutionEngine>(builder.create()); | |
| if (!engine_) { | |
| fprintf(stderr, "Could not create ExecutionEngine: %s\n", err.c_str()); | |
| return -1; | |
| } | |
| #if LLVM_MAJOR_VERSION >= 9 | |
| engine_->setProcessAllSections(true); | |
| #else | |
| if (flags_ & DEBUG_SOURCE) | |
| engine_->setProcessAllSections(true); | |
| #endif | |
| if (int rc = run_pass_manager(*mod)) | |
| return rc; | |
| engine_->finalizeObject(); | |
| if (flags_ & DEBUG_SOURCE) { | |
| SourceDebugger src_debugger(mod, *sections_p, FN_PREFIX, 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)); | |
| } | |
| engine_.reset(); | |
| ctx_.reset(); | |
| } | |
| // give functions an id | |
| for (auto section : sections_) | |
| if (!strncmp(FN_PREFIX.c_str(), section.first.c_str(), FN_PREFIX.size())) | |
| function_names_.push_back(section.first); | |
| return 0; | |
| } | |
| size_t BPFModule::num_functions() const { | |
| return function_names_.size(); | |
| } | |
| const char * BPFModule::function_name(size_t id) const { | |
| if (id >= function_names_.size()) | |
| return nullptr; | |
| return function_names_[id].c_str() + FN_PREFIX.size(); | |
| } | |
| uint8_t * BPFModule::function_start(size_t id) const { | |
| if (id >= function_names_.size()) | |
| return nullptr; | |
| auto section = sections_.find(function_names_[id]); | |
| if (section == sections_.end()) | |
| return nullptr; | |
| return get<0>(section->second); | |
| } | |
| uint8_t * BPFModule::function_start(const string &name) const { | |
| auto section = sections_.find(FN_PREFIX + name); | |
| if (section == sections_.end()) | |
| return nullptr; | |
| return get<0>(section->second); | |
| } | |
| const char * BPFModule::function_source(const string &name) const { | |
| return func_src_->src(name); | |
| } | |
| const char * BPFModule::function_source_rewritten(const string &name) const { | |
| return func_src_->src_rewritten(name); | |
| } | |
| 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 { | |
| if (id >= function_names_.size()) | |
| return 0; | |
| auto section = sections_.find(function_names_[id]); | |
| if (section == sections_.end()) | |
| return 0; | |
| return get<1>(section->second); | |
| } | |
| size_t BPFModule::function_size(const string &name) const { | |
| auto section = sections_.find(FN_PREFIX + name); | |
| if (section == sections_.end()) | |
| return 0; | |
| return get<1>(section->second); | |
| } | |
| 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<uint8_t[]> key; | |
| unique_ptr<uint8_t[]> 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) { | |
| struct bpf_load_program_attr attr = {}; | |
| unsigned func_info_cnt, line_info_cnt, finfo_rec_size, linfo_rec_size; | |
| void *func_info = NULL, *line_info = NULL; | |
| int ret; | |
| attr.prog_type = (enum bpf_prog_type)prog_type; | |
| attr.name = name; | |
| attr.insns = insns; | |
| attr.license = license; | |
| if (attr.prog_type != BPF_PROG_TYPE_TRACING && | |
| attr.prog_type != BPF_PROG_TYPE_EXT) { | |
| attr.kern_version = kern_version; | |
| } | |
| attr.prog_flags = flags; | |
| attr.log_level = log_level; | |
| if (dev_name) | |
| attr.prog_ifindex = if_nametoindex(dev_name); | |
| if (btf_) { | |
| int btf_fd = btf_->get_fd(); | |
| char secname[256]; | |
| ::snprintf(secname, sizeof(secname), ".bpf.fn.%s", 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) { | |
| attr.prog_btf_fd = btf_fd; | |
| attr.func_info = func_info; | |
| attr.func_info_cnt = func_info_cnt; | |
| attr.func_info_rec_size = finfo_rec_size; | |
| attr.line_info = line_info; | |
| attr.line_info_cnt = line_info_cnt; | |
| attr.line_info_rec_size = linfo_rec_size; | |
| } | |
| } | |
| ret = bcc_prog_load_xattr(&attr, 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 |