Skip to content

Commit 19e86b0

Browse files
committed
Add ubpf support via UBPF_jll
1 parent 54de5b2 commit 19e86b0

File tree

5 files changed

+131
-171
lines changed

5 files changed

+131
-171
lines changed

Manifest.toml

Lines changed: 0 additions & 169 deletions
This file was deleted.

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ GPUCompiler = "61eb1bfa-7361-4325-ad38-22787b887f55"
88
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
99
LLVM = "929cbde3-209d-540e-8aea-75f648917ca0"
1010
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
11+
UBPF_jll = "502467ad-4a4a-57e4-9860-6b433130b33f"
1112

1213
[compat]
13-
GPUCompiler = "0.9"
14+
GPUCompiler = "0.9.1"
1415
LLVM = "3.4"
16+
UBF_jll = "0.0.2"
1517
julia = "1.6"
1618

1719
[extras]

src/BPFnative.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ using InteractiveUtils
55
using Libdl
66
using GPUCompiler
77

8-
# Host API
8+
# ubpf VM
9+
using UBPF_jll
10+
include("ubpf.jl")
11+
12+
# Common API
913
module API
1014
include("common.jl")
1115
include("libbpf.jl")

src/ubpf.jl

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
export ubpfcall, ubpfverify, ubpfloadelf
2+
3+
struct ebpf_inst
4+
opcode::UInt8
5+
dst_src::UInt8
6+
offset::Int16
7+
imm::Int32
8+
end
9+
const ubpf_jit_fn = Ptr{Cvoid}
10+
const ext_func = Ptr{Cvoid}
11+
Base.@kwdef struct ubpf_vm
12+
insts::Ptr{ebpf_inst}
13+
num_insts::UInt16
14+
jitted::ubpf_jit_fn = C_NULL
15+
jitted_size::Csize_t = 0
16+
ext_funcs::Ptr{ext_func} = C_NULL
17+
ext_func_names::Ptr{Cstring} = C_NULL
18+
bounds_check_enabled::Bool = true
19+
end
20+
ubpf_vm(exe::Vector{ebpf_inst}; kwargs...) =
21+
ubpf_vm(pointer(exe), length(exe); kwargs...)
22+
ubpf_vm(exe::Vector{UInt8}; kwargs...) =
23+
ubpf_vm(reinterpret(Ptr{ebpf_inst}, pointer(exe)),
24+
div(length(exe),sizeof(ebpf_inst));
25+
kwargs...)
26+
27+
ubpfcall(exe::Vector{UInt8}, mem::Vector{UInt8}; kwargs...) =
28+
ubpfcall(collect(reinterpret(ebpf_inst, exe)), mem; kwargs...)
29+
function ubpfcall(exe::Vector{ebpf_inst}, mem::Vector{UInt8}; kwargs...)
30+
GC.@preserve exe mem begin
31+
vm = ubpf_vm(exe; kwargs...)
32+
if ubpfverify(vm) == 0
33+
unsafe_ubpfcall(vm, mem)
34+
else
35+
error("BPF verification failed")
36+
end
37+
end
38+
end
39+
function ubpfverify(vm::ubpf_vm)
40+
vm_ref = Ref(vm)
41+
GC.@preserve vm_ref begin
42+
ccall((:ubpf_verify, libubpf), Cint, (Ptr{ubpf_vm},), vm_ref)
43+
end
44+
end
45+
function unsafe_ubpfcall(vm::ubpf_vm, mem::Union{<:Ptr,<:Integer}, mem_len::Integer)
46+
vm_ref = Ref(vm)
47+
mem = sizeof(mem) < sizeof(Ptr{Cvoid}) ? UInt(mem) : mem
48+
GC.@preserve vm_ref mem begin
49+
ccall((:ubpf_exec, libubpf), UInt64,
50+
(Ptr{ubpf_vm}, Ptr{Cvoid}, Csize_t),
51+
vm_ref, reinterpret(Ptr{Cvoid}, mem), mem_len)
52+
end
53+
end
54+
function unsafe_ubpfcall(vm::ubpf_vm, mem::Vector{UInt8})
55+
vm_ref = Ref(vm)
56+
GC.@preserve vm_ref mem begin
57+
ccall((:ubpf_exec, libubpf), UInt64,
58+
(Ptr{ubpf_vm}, Ptr{Cvoid}, Csize_t),
59+
vm_ref, mem, length(mem))
60+
end
61+
end
62+
function ubpfloadelf(vm::ubpf_vm, exe::Vector{UInt8})
63+
vm_ref = Ref(vm)
64+
errmsg = Ref{Cstring}()
65+
ret = GC.@preserve vm_ref errmsg begin
66+
ccall((:ubpf_load_elf, BPFnative.libubpf), Cint,
67+
(Ptr{BPFnative.ubpf_vm}, Ptr{UInt8}, Csize_t, Ptr{Cstring}),
68+
vm_ref, pointer(exe), length(exe), errmsg)
69+
end
70+
if ret != 0
71+
error(unsafe_string(errmsg[]))
72+
end
73+
vm_ref[]
74+
end
75+
ubpfloadelf(path::String) = ubpfloadelf(read(path))

test/runtests.jl

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,33 @@ end
2121

2222
using InteractiveUtils
2323

24+
if !isdefined(BPFnative, :libubpf)
25+
@warn "ubpf unavailable; skipping interpreter tests"
26+
end
27+
function ubpftest(fn, Targs, arg)
28+
exe = bpffunction(fn, Targs; btf=false, name="kernel")
29+
vm = BPFnative.ubpf_vm(;insts=C_NULL, num_insts=0)
30+
vm = ubpfloadelf(vm, exe)
31+
if ubpfverify(vm) != 0
32+
error("eBPF verifier error during test")
33+
end
34+
if arg === nothing
35+
BPFnative.unsafe_ubpfcall(vm, C_NULL, 0)
36+
else
37+
BPFnative.unsafe_ubpfcall(vm, arg, sizeof(arg))
38+
end
39+
end
40+
macro ubpftest(call)
41+
if isdefined(BPFnative, :libubpf)
42+
@assert Meta.isexpr(call, :call) "@ubpftest expects a call"
43+
@assert length(call.args) <= 2 "Too many call arguments, 1 expected"
44+
fn = call.args[1]
45+
arg = get(call.args, 2, nothing)
46+
Targ = arg !== nothing ? Base.to_tuple_type(typeof.((arg,))) : Tuple{}
47+
:(ubpftest($(esc(fn)), $Targ, $arg))
48+
end
49+
end
50+
2451
@testset "codegen" begin
2552
map_types = (UInt8, UInt16, UInt32, UInt64,
2653
Int8, Int16, Int32, Int64)
@@ -32,6 +59,27 @@ using InteractiveUtils
3259
@test occursin(".section\tlicense,", asm)
3360
@test occursin("_license,@object", asm)
3461
@test occursin(".asciz\t\"abc\"", asm)
62+
@test @ubpftest(kernel(0)) == 0
63+
end
64+
@testset "verifier errors" begin
65+
@testset "loop" begin
66+
function kernel(x)
67+
while true end
68+
0
69+
end
70+
@test_throws ErrorException @ubpftest(kernel(0))
71+
end
72+
end
73+
@info "uBPF error messages during testing are OK"
74+
@testset "runtime errors" begin
75+
@testset "out-of-bounds load" begin
76+
kernel(x) = unsafe_load(Ptr{UInt8}(1))
77+
@test @ubpftest(kernel(0)) == typemax(UInt)
78+
end
79+
end
80+
@testset "argument usage" begin
81+
kernel(x) = x+1
82+
@test @ubpftest(kernel(1)) == 2
3583
end
3684
@testset "map helpers" begin
3785
function kernel(x)

0 commit comments

Comments
 (0)