From 903c95199da424219e9a4042e718c4f252bfdeae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=91=E5=BE=AE?= <1067852565@qq.com> Date: Sun, 24 Mar 2024 22:52:50 +0000 Subject: [PATCH] tools: add support for AOT cli (#257) * fix * update examples for aot cli * fix uprobe example * add run native ELF --- example/minimal/uprobe.bpf.c | 2 +- runtime/include/bpftime_prog.hpp | 2 +- tools/CMakeLists.txt | 6 +- tools/aot/CMakeLists.txt | 23 ++ tools/aot/README.md | 233 +++++++++++++++++++ tools/aot/example/.gitignore | 1 + tools/aot/example/Makefile | 6 + tools/aot/example/do_count.o | Bin 0 -> 1632 bytes tools/aot/example/do_uprobe_trace.o | Bin 0 -> 1000 bytes tools/aot/example/main.c | 68 ++++++ tools/aot/example/malloc.json | 66 ++++++ tools/aot/main.cpp | 266 ++++++++++++++++++++++ tools/{cli-cpp => cli}/CMakeLists.txt | 0 tools/{cli-cpp => cli}/main.cpp | 0 vm/llvm-jit/include/llvm_bpf_jit.h | 2 + vm/llvm-jit/src/llvm/llvm_jit_context.cpp | 9 +- vm/llvm-jit/src/llvm/llvm_jit_context.hpp | 8 +- 17 files changed, 682 insertions(+), 10 deletions(-) create mode 100644 tools/aot/CMakeLists.txt create mode 100644 tools/aot/README.md create mode 100644 tools/aot/example/.gitignore create mode 100644 tools/aot/example/Makefile create mode 100644 tools/aot/example/do_count.o create mode 100644 tools/aot/example/do_uprobe_trace.o create mode 100644 tools/aot/example/main.c create mode 100644 tools/aot/example/malloc.json create mode 100644 tools/aot/main.cpp rename tools/{cli-cpp => cli}/CMakeLists.txt (100%) rename tools/{cli-cpp => cli}/main.cpp (100%) diff --git a/example/minimal/uprobe.bpf.c b/example/minimal/uprobe.bpf.c index dbc94e1f..35f2a8d3 100644 --- a/example/minimal/uprobe.bpf.c +++ b/example/minimal/uprobe.bpf.c @@ -10,4 +10,4 @@ int do_uprobe_trace(struct pt_regs *ctx) return 0; } -char LICENSE[] SEC("license") = "GPL"; +char LICENSE[] SEC("license") = "GPL"; \ No newline at end of file diff --git a/runtime/include/bpftime_prog.hpp b/runtime/include/bpftime_prog.hpp index 3df22f3f..801e93da 100644 --- a/runtime/include/bpftime_prog.hpp +++ b/runtime/include/bpftime_prog.hpp @@ -39,7 +39,7 @@ class bpftime_prog { { return insns; } - + const struct ebpf_vm *get_vm() const { return vm; } private: int bpftime_prog_set_insn(struct ebpf_inst *insn, size_t insn_cnt); std::string name; diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 64ff935e..61b4d6d8 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,2 +1,6 @@ add_subdirectory(bpftimetool) -add_subdirectory(cli-cpp) +add_subdirectory(cli) +if(BPFTIME_LLVM_JIT) + message(STATUS "Using llvm-jit") + add_subdirectory(aot) +endif() diff --git a/tools/aot/CMakeLists.txt b/tools/aot/CMakeLists.txt new file mode 100644 index 00000000..d6f6e34a --- /dev/null +++ b/tools/aot/CMakeLists.txt @@ -0,0 +1,23 @@ +add_executable( + bpftime-aot-cli + main.cpp +) + +set_target_properties(bpftime-aot-cli PROPERTIES OUTPUT_NAME "bpftime-aot") + +target_include_directories(bpftime-aot-cli PRIVATE + ${SPDLOG_INCLUDE} + ${argparse_INCLUDE} + ${CMAKE_CURRENT_SOURCE_DIR}/../../vm/llvm-jit/src + ${CMAKE_CURRENT_SOURCE_DIR}/../vm/llvm-jit/include + ../../runtime/include/ + ../../runtime/src/ + ${LIBBPF_INCLUDE_DIRS}) +target_link_libraries(bpftime-aot-cli PRIVATE spdlog::spdlog argparse vm-bpf runtime ${LIBBPF_LIBRARIES} elf z) +set_property(TARGET bpftime-aot-cli PROPERTY CXX_STANDARD 20) + +target_compile_definitions(bpftime-aot-cli PRIVATE _GNU_SOURCE) + +add_dependencies(bpftime-aot-cli spdlog::spdlog argparse vm-bpf libbpf) + +install(TARGETS bpftime-aot-cli CONFIGURATIONS Release Debug RelWithDebInfo DESTINATION ~/.bpftime) diff --git a/tools/aot/README.md b/tools/aot/README.md new file mode 100644 index 00000000..0b07816b --- /dev/null +++ b/tools/aot/README.md @@ -0,0 +1,233 @@ +# bpftime-aot cli + +An cli for help to compile eBPF to native ELF. + +It can be used to compile eBPF insns to native insns with helpers, maps define, or load native ELF to run. + +## Usage + +```console +# bpftime-aot help +Usage: /home/yunwei/ebpf-xdp-dpdk/build-bpftime/bpftime/tools/aot/bpftime-aot [--help] [--version] {build,compile,run} + +Optional arguments: + -h, --help shows help message and exits + -v, --version prints version information and exits + +Subcommands: + build Build native ELF(s) from eBPF ELF. Each program in the eBPF ELF will be built into a single native ELF + compile Compile the eBPF program loaded in shared memory + run Run an native eBPF program +``` + +## Build ELF from shared mnemory and use it with helpers and maps + +load the eBPF programs and maps to shared memory: + +```sh +LD_PRELOAD=build/runtime/syscall-server/libbpftime-syscall-server.so example/malloc/malloc +``` + +The eBPF code here is: + +```c +#define BPF_NO_GLOBAL_DATA +#include +#include +#include + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, u32); + __type(value, u64); +} libc_malloc_calls_total SEC(".maps"); + +static int increment_map(void *map, void *key, u64 increment) +{ + u64 zero = 0, *count = bpf_map_lookup_elem(map, key); + if (!count) { + bpf_map_update_elem(map, key, &zero, BPF_NOEXIST); + count = bpf_map_lookup_elem(map, key); + if (!count) { + return 0; + } + } + u64 res = *count + increment; + bpf_map_update_elem(map, key, &res, BPF_EXIST); + + return *count; +} + +SEC("uprobe/libc.so.6:malloc") +int do_count(struct pt_regs *ctx) +{ + u32 pid = bpf_get_current_pid_tgid() >> 32; + + bpf_printk("malloc called from pid %d\n", pid); + + increment_map(&libc_malloc_calls_total, &pid, 1); + + return 0; +} + +char LICENSE[] SEC("license") = "GPL"; +``` + +then build the native ELF from shared memory: + +```sh +bpftime-aot compile +``` + +You will get a native ELF file named `do_count.o`. + +You can link it with your program and execute it: + +```sh +cd example +clang -O2 main.c do_count.o -o malloc +``` + +The drive program is like: + +```c +#include +#include +#include +#include + +int bpf_main(void* ctx, uint64_t size); + +// bpf_printk +uint64_t _bpf_helper_ext_0006(uint64_t fmt, uint64_t fmt_size, ...) +{ + const char *fmt_str = (const char *)fmt; + va_list args; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#pragma GCC diagnostic ignored "-Wvarargs" + va_start(args, fmt_str); + long ret = vprintf(fmt_str, args); +#pragma GCC diagnostic pop + va_end(args); + return 0; +} + +// bpf_get_current_pid_tgid +uint64_t _bpf_helper_ext_0014(void) +{ + static int tgid = -1; + static int tid = -1; + if (tid == -1) + tid = gettid(); + if (tgid == -1) + tgid = getpid(); + return ((uint64_t)tgid << 32) | tid; +} + +// here we use an var to mock the map. +uint64_t counter_map = 0; + +// bpf_map_lookup_elem +void * _bpf_helper_ext_0001(void *map, const void *key) +{ + printf("bpf_map_lookup_elem\n"); + return &counter_map; +} + +// bpf_map_update_elem +long _bpf_helper_ext_0002(void *map, const void *key, const void *value, uint64_t flags) +{ + printf("bpf_map_update_elem\n"); + if (value == NULL) { + printf("value is NULL\n"); + return -1; + } + uint64_t* value_ptr = (uint64_t*)value_ptr; + counter_map = *value_ptr; + printf("counter_map: %lu\n", counter_map); + return 0; +} + +uint64_t __lddw_helper_map_by_fd(uint32_t id) { + printf("map_by_fd\n"); + return 0; +} + +int main() { + printf("Hello, World!\n"); + bpf_main(NULL, 0); + return 0; +} +``` + +Note by loading eBPF programs with libbpf and LD_PRELOAD, maps, global variables, and helpers are already relocated in shared memory, so you can use them directly in your program. For example, the input of `__lddw_helper_map_by_fd` function would be the actual map id in shared memory. + +You can refer to `example/malloc.json` for details about how the maps are relocated. + +## Compile from eBPF bytecode ELF + +You can also compile the eBPF bytecode ELF to native ELF: + +```sh +bpftime-aot build bpftime/example/minimal/.output/uprobe.bpf.o -e +``` + +In this way, the relocation of maps, global variables, and helpers will not be done. The helpers is still works. + +## run native ELF + +Given a eBPF code: + +```c +#define BPF_NO_GLOBAL_DATA +#include +#include +#include + +SEC("uprobe/./victim:target_func") +int do_uprobe_trace(struct pt_regs *ctx) +{ + bpf_printk("target_func called.\n"); + return 0; +} + +char LICENSE[] SEC("license") = "GPL"; +``` + +The native C code after relocation is like: + +```c +int _bpf_helper_ext_0006(char* arg0); + +int bpf_main(void *ctx) +{ + _bpf_helper_ext_0006("target_func called.\n"); + return 0; +} +``` + +Compile it with `clang -O3 -c -o do_uprobe_trace.o do_uprobe_trace.c`, and you can load it with AOT runtime. + +You can simply run the native ELF: + +```console +# bpftime-aot run do_uprobe_trace.o +[2024-03-24 21:57:53.446] [info] [llvm_jit_context.cpp:81] Initializing llvm +[2024-03-24 21:57:53.446] [info] [llvm_jit_context.cpp:204] LLVM-JIT: Loading aot object +target_func called. +[2024-03-24 21:57:53.449] [info] [main.cpp:190] Output: 0 +``` + +## emit llvm ir + +```sh +bpftime-aot compile -e +``` + +or: + +```sh +bpftime-aot build -e minimal.bpf.o +``` diff --git a/tools/aot/example/.gitignore b/tools/aot/example/.gitignore new file mode 100644 index 00000000..09ffdda0 --- /dev/null +++ b/tools/aot/example/.gitignore @@ -0,0 +1 @@ +malloc diff --git a/tools/aot/example/Makefile b/tools/aot/example/Makefile new file mode 100644 index 00000000..fb6c3fac --- /dev/null +++ b/tools/aot/example/Makefile @@ -0,0 +1,6 @@ +malloc: main.c + clang -O2 -flto main.c do_count.o -o malloc + +.PHONY: clean +clean: + rm -f malloc diff --git a/tools/aot/example/do_count.o b/tools/aot/example/do_count.o new file mode 100644 index 0000000000000000000000000000000000000000..8d45fb71c73bbcb5b3ad9ae61c9bbd8e6a28cb23 GIT binary patch literal 1632 zcmbW0&1(}u6u@VbXoJ)>h=)KG7F~}CNvZWBHe1lnVUgnJL2=z=H#9bBLb}DoiR2}7iEO}ToBu6~~fxPzS9ZTj|`q&A+*zh(%rHyxGTs>>ZP zjRUkkB@k-uyJOywSGg1aUNZ!}wpP&+}%Q{UPn=(m3-UC9r zE$nI!`>eJ1T)BaMVZL;-MOWY97F}0vDq0)-M_`}`cP|p55)mfG$HE)X(G;+wj)#y_ z*x{@gnH8goD9wqT`$8?W9())Go)APy5u=VT%%c6(IN%WZ(e?8N33fUx*wf-MDhuCBC)<5pz?)nm|gC{d|I`OiZ*W;F1u}HdU zS}W+eB@$mMT4sFi!j;&vrDqmNFV3k+#vN_J%$LkEWR$dYRm&PAzHF5( zJq@~AK&?Sx9tG=Yi9}-B2PUU{U}Dk_CfVQozq$;m_@2G;jKQYI!M3$e@r;n6c1wyG z{C;zv_l{#Oo?VIuF#VVvPKVie=7{)fj52-um26Uf4ooZAKwwr=lljR@7jRK2e$q;8X#z5NpJpNDX$hR literal 0 HcmV?d00001 diff --git a/tools/aot/example/do_uprobe_trace.o b/tools/aot/example/do_uprobe_trace.o new file mode 100644 index 0000000000000000000000000000000000000000..705137218eafa88859a3bf78f0c2d78e0491c157 GIT binary patch literal 1000 zcmb_aze~eV5Wck47Ad8Of}lc&tPRyc97Hpy%}_)Ng1CgVzFIY{C4EA5v4t)Ug0qu< zMh6i=(SO0wv4ewd4nn<4-orGFn+JDa?z^9P?_F#ZHY17xQWRK)Ur(|CeIKd2v#mosI{wvEfK`n3jNT-~W3ggtIK2aKB)iV*YMRb$lcei}_5 z6&4#$D!+Cneb znU>$$$u_xFJ_awBRn3ZH)fi|^?r^Jwcvd4cv4L4-c7r)4_A_(2+>$%=AIu8&h=%lO zi+5oKV!r3%M#j++N9Y$_IPzR&EO@q5OJ}Of1cBRP(2Vfoh;+X48AyH}x!_JzaziFW zg1L*BgIr_i?vT3`waWq}$obOG;z!v2)sX$iFjv;g{nwET&Zge4WKdcjxc^A^6s-RN DfNfa5 literal 0 HcmV?d00001 diff --git a/tools/aot/example/main.c b/tools/aot/example/main.c new file mode 100644 index 00000000..4581a0a7 --- /dev/null +++ b/tools/aot/example/main.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include + +int bpf_main(void* ctx, uint64_t size); + +// bpf_printk +uint64_t _bpf_helper_ext_0006(uint64_t fmt, uint64_t fmt_size, ...) +{ + const char *fmt_str = (const char *)fmt; + va_list args; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#pragma GCC diagnostic ignored "-Wvarargs" + va_start(args, fmt_str); + long ret = vprintf(fmt_str, args); +#pragma GCC diagnostic pop + va_end(args); + return 0; +} + +// bpf_get_current_pid_tgid +uint64_t _bpf_helper_ext_0014(void) +{ + static int tgid = -1; + static int tid = -1; + if (tid == -1) + tid = gettid(); + if (tgid == -1) + tgid = getpid(); + return ((uint64_t)tgid << 32) | tid; +} + +// here we use an var to mock the map. +uint64_t counter_map = 0; + +// bpf_map_lookup_elem +void * _bpf_helper_ext_0001(void *map, const void *key) +{ + printf("bpf_map_lookup_elem\n"); + return &counter_map; +} + +// bpf_map_update_elem +long _bpf_helper_ext_0002(void *map, const void *key, const void *value, uint64_t flags) +{ + printf("bpf_map_update_elem\n"); + if (value == NULL) { + printf("value is NULL\n"); + return -1; + } + uint64_t* value_ptr = (uint64_t*)value_ptr; + counter_map = *value_ptr; + printf("counter_map: %lu\n", counter_map); + return 0; +} + +uint64_t __lddw_helper_map_by_fd(uint32_t id) { + printf("map_by_fd\n"); + return 0; +} + +int main() { + printf("Hello, World!\n"); + bpf_main(NULL, 0); + return 0; +} diff --git a/tools/aot/example/malloc.json b/tools/aot/example/malloc.json new file mode 100644 index 00000000..1cb2d73f --- /dev/null +++ b/tools/aot/example/malloc.json @@ -0,0 +1,66 @@ +{ + "3": { + "attr": { + "btf_id": 1, + "btf_key_type_id": 0, + "btf_value_type_id": 0, + "btf_vmlinux_value_type_id": 0, + "flags": 0, + "ifindex": 0, + "kernel_bpf_map_id": 0, + "key_size": 4, + "map_extra": 0, + "map_type": 1, + "max_entries": 1024, + "value_size": 8 + }, + "name": "libc_malloc_cal", + "type": "bpf_map_handler" + }, + "4": { + "attr": { + "btf_id": 2, + "btf_key_type_id": 0, + "btf_value_type_id": 0, + "btf_vmlinux_value_type_id": 0, + "flags": 128, + "ifindex": 0, + "kernel_bpf_map_id": 0, + "key_size": 4, + "map_extra": 0, + "map_type": 2, + "max_entries": 1, + "value_size": 27 + }, + "name": ".rodata.str1.1", + "type": "bpf_map_handler" + }, + "5": { + "attr": { + "cnt": 55, + "insns": "850000000e0000007700000020000000630af4ff00000000b7010000640a00006b1af0ff00000000180100006f6d207000000000696420257b1ae8ff0000000018010000616c6c6500000000642066727b1ae0ff00000000180100006d616c6c000000006f6320637b1ad8ff00000000b706000000000000736af2ff00000000bfa100000000000007010000d8ffffffb70200001b000000bf0300000000000085000000060000007b6ad8ff00000000bfa200000000000007020000f4ffffff18110000030000000000000000000000850000000100000055000e0000000000bfa600000000000007060000f4ffffffbfa300000000000007030000d8ffffff18110000030000000000000000000000bf62000000000000b704000001000000850000000200000018110000030000000000000000000000bf62000000000000850000000100000015000b0000000000790100000000000007010000010000007b1af8ff00000000bfa200000000000007020000f4ffffffbfa300000000000007030000f8ffffff18110000030000000000000000000000b7040000020000008500000002000000b7000000000000009500000000000000", + "type": 532368092 + }, + "name": "do_count", + "type": "bpf_prog_handler" + }, + "6": { + "attr": { + "_module_name": "/lib/x86_64-linux-gnu/libc.so.6", + "data_type": "uprobe_perf_event_data", + "offset": 676000, + "pid": -1, + "ref_ctr_off": 0, + "type": 6 + }, + "enabled": true, + "type": "bpf_perf_event_handler" + }, + "7": { + "attr": { + "prog_fd": 5, + "target_fd": 6 + }, + "type": "bpf_link_handler" + } +} \ No newline at end of file diff --git a/tools/aot/main.cpp b/tools/aot/main.cpp new file mode 100644 index 00000000..4dca3c7b --- /dev/null +++ b/tools/aot/main.cpp @@ -0,0 +1,266 @@ +#include "ebpf-vm.h" +#include "llvm_bpf_jit.h" +#include "bpftime_helper_group.hpp" +#include "bpftime_prog.hpp" +#include "bpftime_shm.hpp" +#include "bpf_attach_ctx.hpp" +#include "spdlog/spdlog.h" +#include "spdlog/cfg/env.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace llvm; +using namespace llvm::orc; + +extern "C" int _libbpf_print(libbpf_print_level level, const char *fmt, + va_list ap) +{ + char buf[2048]; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + int ret = vsnprintf(buf, sizeof(buf), fmt, ap); +#pragma GCC diagnostic pop + std::string out(buf); + if (out.back() == '\n') + out.pop_back(); + if (level == LIBBPF_WARN) { + SPDLOG_WARN("{}", out); + } else if (level == LIBBPF_INFO) { + SPDLOG_INFO("{}", out); + } else if (level == LIBBPF_DEBUG) { + SPDLOG_INFO("{}", out); + } + return ret; +} + +union bpf_attach_ctx_holder { + bpftime::bpf_attach_ctx ctx; + bpf_attach_ctx_holder() + { + } + ~bpf_attach_ctx_holder() + { + } + void destroy() + { + ctx.~bpf_attach_ctx(); + } + void init() + { + new (&ctx) bpftime::bpf_attach_ctx; + } +}; + +static bpf_attach_ctx_holder ctx_holder; + +bool emit_llvm_ir = false; + +static int build_ebpf_program(const std::string &ebpf_elf, + const std::filesystem::path &output) +{ + bpf_object *obj = bpf_object__open(ebpf_elf.c_str()); + if (!obj) { + SPDLOG_CRITICAL("Unable to open BPF elf: {}", errno); + return 1; + } + std::unique_ptr elf( + obj, bpf_object__close); + bpf_program *prog; + bpf_object__for_each_program(prog, elf.get()) + { + auto name = bpf_program__name(prog); + SPDLOG_INFO("Processing program {}", name); + bpftime::bpftime_prog bpftime_prog((const ebpf_inst *) bpf_program__insns(prog), + bpf_program__insn_cnt(prog), + name); + bpftime::bpftime_helper_group::get_kernel_utils_helper_group() + .add_helper_group_to_prog(&bpftime_prog); + bpftime::bpftime_helper_group::get_shm_maps_helper_group() + .add_helper_group_to_prog(&bpftime_prog); + bpftime_prog.bpftime_prog_load(true); + llvm_bpf_jit_context ctx(bpftime_prog.get_vm()); + auto result = ctx.do_aot_compile(emit_llvm_ir); + auto out_path = output / (std::string(name) + ".o"); + std::ofstream ofs(out_path, std::ios::binary); + ofs.write((const char *)result.data(), result.size()); + SPDLOG_INFO("Program {} written to {}", name, out_path.c_str()); + } + return 0; +} + +static int compile_ebpf_program(const std::filesystem::path &output) +{ + using namespace bpftime; + bpftime_initialize_global_shm(shm_open_type::SHM_OPEN_ONLY); + ctx_holder.init(); + const handler_manager *manager = + shm_holder.global_shared_memory.get_manager(); + size_t handler_size = manager->size(); + // TODO: fix load programs + for (size_t i = 0; i < manager->size(); i++) { + if (std::holds_alternative( + manager->get_handler(i))) { + const auto &prog = std::get( + manager->get_handler(i)); + // temp work around: we need to create new attach points + // in the runtime + // TODO: fix this hard code name + auto new_prog = bpftime_prog(prog.insns.data(), + prog.insns.size(), + prog.name.c_str()); + bpftime::bpftime_helper_group::get_kernel_utils_helper_group() + .add_helper_group_to_prog(&new_prog); + bpftime::bpftime_helper_group::get_shm_maps_helper_group() + .add_helper_group_to_prog(&new_prog); + new_prog.bpftime_prog_load(true); + llvm_bpf_jit_context ctx(new_prog.get_vm()); + auto result = ctx.do_aot_compile(emit_llvm_ir); + auto out_path = output / (std::string(prog.name.c_str()) + ".o"); + std::ofstream ofs(out_path, std::ios::binary); + ofs.write((const char *)result.data(), result.size()); + return 0; + } + } + return 0; +} + +static ExitOnError exit_on_error; +using bpf_func = uint64_t (*)(const void *, uint64_t); + +static int run_ebpf_program(const std::filesystem::path &elf, + std::optional memory) +{ + // read the file + std::ifstream ifs(elf, std::ios::binary | std::ios::ate); + if (!ifs.is_open()) { + SPDLOG_ERROR("Unable to open ELF file"); + return 1; + } + std::streamsize size = ifs.tellg(); + ifs.seekg(0, std::ios::beg); + std::vector file_buf(size); + if (!ifs.read((char *)file_buf.data(), size)) { + SPDLOG_ERROR("Unable to read ELF"); + return 1; + } + std::vector mem; + if (memory.has_value()) { + std::ifstream ifs(*memory, std::ios::binary | std::ios::ate); + if (!ifs.is_open()) { + SPDLOG_ERROR("Unable to open memory file"); + return 1; + } + std::streamsize size = ifs.tellg(); + ifs.seekg(0, std::ios::beg); + mem.resize(size); + if (!ifs.read((char *)mem.data(), size)) { + SPDLOG_ERROR("Unable to read memory"); + return 1; + } + SPDLOG_INFO("Memory size: {}", size); + } + + const ebpf_inst insn[1] = {}; + bpftime::bpftime_prog bpftime_prog(insn, 0, "bpf_main"); + bpftime::bpftime_helper_group::get_kernel_utils_helper_group() + .add_helper_group_to_prog(&bpftime_prog); + bpftime::bpftime_helper_group::get_shm_maps_helper_group() + .add_helper_group_to_prog(&bpftime_prog); + auto vm = bpftime_prog.get_vm(); + vm->jit_context->load_aot_object(file_buf); + int ret = vm->jit_context->get_entry_address()(&mem, sizeof(mem)); + SPDLOG_INFO("Output: {}", ret); + return 0; +} + +int main(int argc, const char **argv) +{ + spdlog::cfg::load_env_levels(); + libbpf_set_print(_libbpf_print); + InitializeNativeTarget(); + InitializeNativeTargetAsmPrinter(); + argparse::ArgumentParser program(argv[0]); + + argparse::ArgumentParser build_command("build"); + build_command.add_description( + "Build native ELF(s) from eBPF ELF. Each program in the eBPF ELF will be built into a single native ELF"); + build_command.add_argument("-o", "--output") + .default_value(".") + .help("Output directory (There might be multiple output files for a single input)"); + build_command.add_argument("EBPF_ELF") + .help("Path to an eBPF ELF executable"); + build_command.add_argument("-e", "--emit_llvm") + .default_value(false) + .implicit_value(true) + .help("Emit LLVM IR for the eBPF program"); + + argparse::ArgumentParser run_command("run"); + run_command.add_description("Run an native eBPF program"); + run_command.add_argument("PATH").help("Path to the ELF file"); + run_command.add_argument("MEMORY") + .help("Path to the memory file") + .nargs(0, 1); + + argparse::ArgumentParser compile_command("compile"); + compile_command.add_description("Compile the eBPF program loaded in shared memory"); + compile_command.add_argument("-o", "--output") + .default_value(".") + .help("Output directory (There might be multiple output files for a single input)"); + compile_command.add_argument("-e", "--emit_llvm") + .default_value(false) + .implicit_value(true) + .help("Emit LLVM IR for the eBPF program"); + + program.add_subparser(build_command); + program.add_subparser(run_command); + program.add_subparser(compile_command); + + try { + program.parse_args(argc, argv); + } catch (const std::exception &err) { + std::cerr << err.what() << std::endl; + std::cerr << program; + std::exit(1); + } + if (!program) { + std::cerr << program; + std::exit(1); + } + if (program.is_subcommand_used(build_command)) { + emit_llvm_ir = build_command.get("emit_llvm"); + return build_ebpf_program( + build_command.get("EBPF_ELF"), + build_command.get("output")); + } else if (program.is_subcommand_used(run_command)) { + if (run_command.is_used("MEMORY")) { + return run_ebpf_program( + run_command.get("PATH"), + run_command.get("MEMORY")); + } else { + return run_ebpf_program( + run_command.get("PATH"), {}); + } + } else if (program.is_subcommand_used(compile_command)) { + emit_llvm_ir = compile_command.get("emit_llvm"); + return compile_ebpf_program(compile_command.get("output")); + } + return 0; +} diff --git a/tools/cli-cpp/CMakeLists.txt b/tools/cli/CMakeLists.txt similarity index 100% rename from tools/cli-cpp/CMakeLists.txt rename to tools/cli/CMakeLists.txt diff --git a/tools/cli-cpp/main.cpp b/tools/cli/main.cpp similarity index 100% rename from tools/cli-cpp/main.cpp rename to tools/cli/main.cpp diff --git a/vm/llvm-jit/include/llvm_bpf_jit.h b/vm/llvm-jit/include/llvm_bpf_jit.h index 5e031547..ad506081 100644 --- a/vm/llvm-jit/include/llvm_bpf_jit.h +++ b/vm/llvm-jit/include/llvm_bpf_jit.h @@ -45,6 +45,8 @@ struct ebpf_vm { uint64_t (*code_addr)(uint32_t); }; +int ebpf_aot_compile(struct ebpf_vm* vm, uint64_t secret); + #ifdef __cplusplus } #endif diff --git a/vm/llvm-jit/src/llvm/llvm_jit_context.cpp b/vm/llvm-jit/src/llvm/llvm_jit_context.cpp index a088877c..e2d6dea7 100644 --- a/vm/llvm-jit/src/llvm/llvm_jit_context.cpp +++ b/vm/llvm-jit/src/llvm/llvm_jit_context.cpp @@ -115,7 +115,7 @@ llvm_bpf_jit_context::~llvm_bpf_jit_context() std::vector llvm_bpf_jit_context::do_aot_compile( const std::vector &extFuncNames, - const std::vector &lddwHelpers) + const std::vector &lddwHelpers, bool print_ir) { SPDLOG_DEBUG("AOT: start"); if (auto module = generateModule(extFuncNames, lddwHelpers); module) { @@ -123,6 +123,9 @@ std::vector llvm_bpf_jit_context::do_aot_compile( SPDLOG_DEBUG("AOT: target triple: {}", defaultTargetTriple); return module->withModuleDo([&](auto &module) -> std::vector { + if (print_ir) { + module.print(errs(), nullptr); + } optimizeModule(module); module.setTargetTriple(defaultTargetTriple); std::string error; @@ -174,7 +177,7 @@ std::vector llvm_bpf_jit_context::do_aot_compile( } } -std::vector llvm_bpf_jit_context::do_aot_compile() +std::vector llvm_bpf_jit_context::do_aot_compile(bool print_ir) { std::vector extNames, lddwNames; for (uint32_t i = 0; i < std::size(vm->ext_funcs); i++) { @@ -193,7 +196,7 @@ std::vector llvm_bpf_jit_context::do_aot_compile() tryDefineLddwHelper(LDDW_HELPER_MAP_VAL, (void *)vm->map_val); tryDefineLddwHelper(LDDW_HELPER_CODE_ADDR, (void *)vm->code_addr); tryDefineLddwHelper(LDDW_HELPER_VAR_ADDR, (void *)vm->var_addr); - return this->do_aot_compile(extNames, lddwNames); + return this->do_aot_compile(extNames, lddwNames, print_ir); } void llvm_bpf_jit_context::load_aot_object(const std::vector &buf) diff --git a/vm/llvm-jit/src/llvm/llvm_jit_context.hpp b/vm/llvm-jit/src/llvm/llvm_jit_context.hpp index dfee18bc..0d879b45 100644 --- a/vm/llvm-jit/src/llvm/llvm_jit_context.hpp +++ b/vm/llvm-jit/src/llvm/llvm_jit_context.hpp @@ -32,28 +32,28 @@ const static char *LDDW_HELPER_CODE_ADDR = "__lddw_helper_code_addr"; #define IS_ALIGNED(x, a) (((uintptr_t)(x) & ((a)-1)) == 0) -struct llvm_bpf_jit_context { +class llvm_bpf_jit_context { const ebpf_vm *vm; std::optional > jit; std::unique_ptr compiling; llvm::Expected generateModule(const std::vector &extFuncNames, const std::vector &lddwHelpers); - void do_jit_compile(); std::vector do_aot_compile(const std::vector &extFuncNames, - const std::vector &lddwHelpers); + const std::vector &lddwHelpers, bool print_ir); // (JIT, extFuncs, lddwHelpers) std::tuple, std::vector, std::vector > create_and_initialize_lljit_instance(); public: + void do_jit_compile(); llvm_bpf_jit_context(const ebpf_vm *m_vm); virtual ~llvm_bpf_jit_context(); ebpf_jit_fn compile(); ebpf_jit_fn get_entry_address(); - std::vector do_aot_compile(); + std::vector do_aot_compile(bool print_ir = false); void load_aot_object(const std::vector &buf); };