Skip to content

Commit 903c951

Browse files
authored
tools: add support for AOT cli (eunomia-bpf#257)
* fix * update examples for aot cli * fix uprobe example * add run native ELF
1 parent c828a48 commit 903c951

File tree

17 files changed

+682
-10
lines changed

17 files changed

+682
-10
lines changed

example/minimal/uprobe.bpf.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ int do_uprobe_trace(struct pt_regs *ctx)
1010
return 0;
1111
}
1212

13-
char LICENSE[] SEC("license") = "GPL";
13+
char LICENSE[] SEC("license") = "GPL";

runtime/include/bpftime_prog.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class bpftime_prog {
3939
{
4040
return insns;
4141
}
42-
42+
const struct ebpf_vm *get_vm() const { return vm; }
4343
private:
4444
int bpftime_prog_set_insn(struct ebpf_inst *insn, size_t insn_cnt);
4545
std::string name;

tools/CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
add_subdirectory(bpftimetool)
2-
add_subdirectory(cli-cpp)
2+
add_subdirectory(cli)
3+
if(BPFTIME_LLVM_JIT)
4+
message(STATUS "Using llvm-jit")
5+
add_subdirectory(aot)
6+
endif()

tools/aot/CMakeLists.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
add_executable(
2+
bpftime-aot-cli
3+
main.cpp
4+
)
5+
6+
set_target_properties(bpftime-aot-cli PROPERTIES OUTPUT_NAME "bpftime-aot")
7+
8+
target_include_directories(bpftime-aot-cli PRIVATE
9+
${SPDLOG_INCLUDE}
10+
${argparse_INCLUDE}
11+
${CMAKE_CURRENT_SOURCE_DIR}/../../vm/llvm-jit/src
12+
${CMAKE_CURRENT_SOURCE_DIR}/../vm/llvm-jit/include
13+
../../runtime/include/
14+
../../runtime/src/
15+
${LIBBPF_INCLUDE_DIRS})
16+
target_link_libraries(bpftime-aot-cli PRIVATE spdlog::spdlog argparse vm-bpf runtime ${LIBBPF_LIBRARIES} elf z)
17+
set_property(TARGET bpftime-aot-cli PROPERTY CXX_STANDARD 20)
18+
19+
target_compile_definitions(bpftime-aot-cli PRIVATE _GNU_SOURCE)
20+
21+
add_dependencies(bpftime-aot-cli spdlog::spdlog argparse vm-bpf libbpf)
22+
23+
install(TARGETS bpftime-aot-cli CONFIGURATIONS Release Debug RelWithDebInfo DESTINATION ~/.bpftime)

tools/aot/README.md

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
# bpftime-aot cli
2+
3+
An cli for help to compile eBPF to native ELF.
4+
5+
It can be used to compile eBPF insns to native insns with helpers, maps define, or load native ELF to run.
6+
7+
## Usage
8+
9+
```console
10+
# bpftime-aot help
11+
Usage: /home/yunwei/ebpf-xdp-dpdk/build-bpftime/bpftime/tools/aot/bpftime-aot [--help] [--version] {build,compile,run}
12+
13+
Optional arguments:
14+
-h, --help shows help message and exits
15+
-v, --version prints version information and exits
16+
17+
Subcommands:
18+
build Build native ELF(s) from eBPF ELF. Each program in the eBPF ELF will be built into a single native ELF
19+
compile Compile the eBPF program loaded in shared memory
20+
run Run an native eBPF program
21+
```
22+
23+
## Build ELF from shared mnemory and use it with helpers and maps
24+
25+
load the eBPF programs and maps to shared memory:
26+
27+
```sh
28+
LD_PRELOAD=build/runtime/syscall-server/libbpftime-syscall-server.so example/malloc/malloc
29+
```
30+
31+
The eBPF code here is:
32+
33+
```c
34+
#define BPF_NO_GLOBAL_DATA
35+
#include <vmlinux.h>
36+
#include <bpf/bpf_helpers.h>
37+
#include <bpf/bpf_tracing.h>
38+
39+
struct {
40+
__uint(type, BPF_MAP_TYPE_HASH);
41+
__uint(max_entries, 1024);
42+
__type(key, u32);
43+
__type(value, u64);
44+
} libc_malloc_calls_total SEC(".maps");
45+
46+
static int increment_map(void *map, void *key, u64 increment)
47+
{
48+
u64 zero = 0, *count = bpf_map_lookup_elem(map, key);
49+
if (!count) {
50+
bpf_map_update_elem(map, key, &zero, BPF_NOEXIST);
51+
count = bpf_map_lookup_elem(map, key);
52+
if (!count) {
53+
return 0;
54+
}
55+
}
56+
u64 res = *count + increment;
57+
bpf_map_update_elem(map, key, &res, BPF_EXIST);
58+
59+
return *count;
60+
}
61+
62+
SEC("uprobe/libc.so.6:malloc")
63+
int do_count(struct pt_regs *ctx)
64+
{
65+
u32 pid = bpf_get_current_pid_tgid() >> 32;
66+
67+
bpf_printk("malloc called from pid %d\n", pid);
68+
69+
increment_map(&libc_malloc_calls_total, &pid, 1);
70+
71+
return 0;
72+
}
73+
74+
char LICENSE[] SEC("license") = "GPL";
75+
```
76+
77+
then build the native ELF from shared memory:
78+
79+
```sh
80+
bpftime-aot compile
81+
```
82+
83+
You will get a native ELF file named `do_count.o`.
84+
85+
You can link it with your program and execute it:
86+
87+
```sh
88+
cd example
89+
clang -O2 main.c do_count.o -o malloc
90+
```
91+
92+
The drive program is like:
93+
94+
```c
95+
#include <stdio.h>
96+
#include <stdint.h>
97+
#include <unistd.h>
98+
#include <stdlib.h>
99+
100+
int bpf_main(void* ctx, uint64_t size);
101+
102+
// bpf_printk
103+
uint64_t _bpf_helper_ext_0006(uint64_t fmt, uint64_t fmt_size, ...)
104+
{
105+
const char *fmt_str = (const char *)fmt;
106+
va_list args;
107+
#pragma GCC diagnostic push
108+
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
109+
#pragma GCC diagnostic ignored "-Wvarargs"
110+
va_start(args, fmt_str);
111+
long ret = vprintf(fmt_str, args);
112+
#pragma GCC diagnostic pop
113+
va_end(args);
114+
return 0;
115+
}
116+
117+
// bpf_get_current_pid_tgid
118+
uint64_t _bpf_helper_ext_0014(void)
119+
{
120+
static int tgid = -1;
121+
static int tid = -1;
122+
if (tid == -1)
123+
tid = gettid();
124+
if (tgid == -1)
125+
tgid = getpid();
126+
return ((uint64_t)tgid << 32) | tid;
127+
}
128+
129+
// here we use an var to mock the map.
130+
uint64_t counter_map = 0;
131+
132+
// bpf_map_lookup_elem
133+
void * _bpf_helper_ext_0001(void *map, const void *key)
134+
{
135+
printf("bpf_map_lookup_elem\n");
136+
return &counter_map;
137+
}
138+
139+
// bpf_map_update_elem
140+
long _bpf_helper_ext_0002(void *map, const void *key, const void *value, uint64_t flags)
141+
{
142+
printf("bpf_map_update_elem\n");
143+
if (value == NULL) {
144+
printf("value is NULL\n");
145+
return -1;
146+
}
147+
uint64_t* value_ptr = (uint64_t*)value_ptr;
148+
counter_map = *value_ptr;
149+
printf("counter_map: %lu\n", counter_map);
150+
return 0;
151+
}
152+
153+
uint64_t __lddw_helper_map_by_fd(uint32_t id) {
154+
printf("map_by_fd\n");
155+
return 0;
156+
}
157+
158+
int main() {
159+
printf("Hello, World!\n");
160+
bpf_main(NULL, 0);
161+
return 0;
162+
}
163+
```
164+
165+
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.
166+
167+
You can refer to `example/malloc.json` for details about how the maps are relocated.
168+
169+
## Compile from eBPF bytecode ELF
170+
171+
You can also compile the eBPF bytecode ELF to native ELF:
172+
173+
```sh
174+
bpftime-aot build bpftime/example/minimal/.output/uprobe.bpf.o -e
175+
```
176+
177+
In this way, the relocation of maps, global variables, and helpers will not be done. The helpers is still works.
178+
179+
## run native ELF
180+
181+
Given a eBPF code:
182+
183+
```c
184+
#define BPF_NO_GLOBAL_DATA
185+
#include <vmlinux.h>
186+
#include <bpf/bpf_helpers.h>
187+
#include <bpf/bpf_tracing.h>
188+
189+
SEC("uprobe/./victim:target_func")
190+
int do_uprobe_trace(struct pt_regs *ctx)
191+
{
192+
bpf_printk("target_func called.\n");
193+
return 0;
194+
}
195+
196+
char LICENSE[] SEC("license") = "GPL";
197+
```
198+
199+
The native C code after relocation is like:
200+
201+
```c
202+
int _bpf_helper_ext_0006(char* arg0);
203+
204+
int bpf_main(void *ctx)
205+
{
206+
_bpf_helper_ext_0006("target_func called.\n");
207+
return 0;
208+
}
209+
```
210+
211+
Compile it with `clang -O3 -c -o do_uprobe_trace.o do_uprobe_trace.c`, and you can load it with AOT runtime.
212+
213+
You can simply run the native ELF:
214+
215+
```console
216+
# bpftime-aot run do_uprobe_trace.o
217+
[2024-03-24 21:57:53.446] [info] [llvm_jit_context.cpp:81] Initializing llvm
218+
[2024-03-24 21:57:53.446] [info] [llvm_jit_context.cpp:204] LLVM-JIT: Loading aot object
219+
target_func called.
220+
[2024-03-24 21:57:53.449] [info] [main.cpp:190] Output: 0
221+
```
222+
223+
## emit llvm ir
224+
225+
```sh
226+
bpftime-aot compile -e
227+
```
228+
229+
or:
230+
231+
```sh
232+
bpftime-aot build -e minimal.bpf.o
233+
```

tools/aot/example/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
malloc

tools/aot/example/Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
malloc: main.c
2+
clang -O2 -flto main.c do_count.o -o malloc
3+
4+
.PHONY: clean
5+
clean:
6+
rm -f malloc

tools/aot/example/do_count.o

1.59 KB
Binary file not shown.

tools/aot/example/do_uprobe_trace.o

1000 Bytes
Binary file not shown.

tools/aot/example/main.c

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#include <stdio.h>
2+
#include <stdint.h>
3+
#include <unistd.h>
4+
#include <stdlib.h>
5+
6+
int bpf_main(void* ctx, uint64_t size);
7+
8+
// bpf_printk
9+
uint64_t _bpf_helper_ext_0006(uint64_t fmt, uint64_t fmt_size, ...)
10+
{
11+
const char *fmt_str = (const char *)fmt;
12+
va_list args;
13+
#pragma GCC diagnostic push
14+
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
15+
#pragma GCC diagnostic ignored "-Wvarargs"
16+
va_start(args, fmt_str);
17+
long ret = vprintf(fmt_str, args);
18+
#pragma GCC diagnostic pop
19+
va_end(args);
20+
return 0;
21+
}
22+
23+
// bpf_get_current_pid_tgid
24+
uint64_t _bpf_helper_ext_0014(void)
25+
{
26+
static int tgid = -1;
27+
static int tid = -1;
28+
if (tid == -1)
29+
tid = gettid();
30+
if (tgid == -1)
31+
tgid = getpid();
32+
return ((uint64_t)tgid << 32) | tid;
33+
}
34+
35+
// here we use an var to mock the map.
36+
uint64_t counter_map = 0;
37+
38+
// bpf_map_lookup_elem
39+
void * _bpf_helper_ext_0001(void *map, const void *key)
40+
{
41+
printf("bpf_map_lookup_elem\n");
42+
return &counter_map;
43+
}
44+
45+
// bpf_map_update_elem
46+
long _bpf_helper_ext_0002(void *map, const void *key, const void *value, uint64_t flags)
47+
{
48+
printf("bpf_map_update_elem\n");
49+
if (value == NULL) {
50+
printf("value is NULL\n");
51+
return -1;
52+
}
53+
uint64_t* value_ptr = (uint64_t*)value_ptr;
54+
counter_map = *value_ptr;
55+
printf("counter_map: %lu\n", counter_map);
56+
return 0;
57+
}
58+
59+
uint64_t __lddw_helper_map_by_fd(uint32_t id) {
60+
printf("map_by_fd\n");
61+
return 0;
62+
}
63+
64+
int main() {
65+
printf("Hello, World!\n");
66+
bpf_main(NULL, 0);
67+
return 0;
68+
}

0 commit comments

Comments
 (0)