Skip to content

Commit

Permalink
Add ubpf support via UBPF_jll
Browse files Browse the repository at this point in the history
  • Loading branch information
jpsamaroo committed Dec 17, 2020
1 parent 54de5b2 commit 19e86b0
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 171 deletions.
169 changes: 0 additions & 169 deletions Manifest.toml

This file was deleted.

4 changes: 3 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ GPUCompiler = "61eb1bfa-7361-4325-ad38-22787b887f55"
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
LLVM = "929cbde3-209d-540e-8aea-75f648917ca0"
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
UBPF_jll = "502467ad-4a4a-57e4-9860-6b433130b33f"

[compat]
GPUCompiler = "0.9"
GPUCompiler = "0.9.1"
LLVM = "3.4"
UBF_jll = "0.0.2"
julia = "1.6"

[extras]
Expand Down
6 changes: 5 additions & 1 deletion src/BPFnative.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ using InteractiveUtils
using Libdl
using GPUCompiler

# Host API
# ubpf VM
using UBPF_jll
include("ubpf.jl")

# Common API
module API
include("common.jl")
include("libbpf.jl")
Expand Down
75 changes: 75 additions & 0 deletions src/ubpf.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
export ubpfcall, ubpfverify, ubpfloadelf

struct ebpf_inst
opcode::UInt8
dst_src::UInt8
offset::Int16
imm::Int32
end
const ubpf_jit_fn = Ptr{Cvoid}
const ext_func = Ptr{Cvoid}
Base.@kwdef struct ubpf_vm
insts::Ptr{ebpf_inst}
num_insts::UInt16
jitted::ubpf_jit_fn = C_NULL
jitted_size::Csize_t = 0
ext_funcs::Ptr{ext_func} = C_NULL
ext_func_names::Ptr{Cstring} = C_NULL
bounds_check_enabled::Bool = true
end
ubpf_vm(exe::Vector{ebpf_inst}; kwargs...) =
ubpf_vm(pointer(exe), length(exe); kwargs...)
ubpf_vm(exe::Vector{UInt8}; kwargs...) =
ubpf_vm(reinterpret(Ptr{ebpf_inst}, pointer(exe)),
div(length(exe),sizeof(ebpf_inst));
kwargs...)

ubpfcall(exe::Vector{UInt8}, mem::Vector{UInt8}; kwargs...) =
ubpfcall(collect(reinterpret(ebpf_inst, exe)), mem; kwargs...)
function ubpfcall(exe::Vector{ebpf_inst}, mem::Vector{UInt8}; kwargs...)
GC.@preserve exe mem begin
vm = ubpf_vm(exe; kwargs...)
if ubpfverify(vm) == 0
unsafe_ubpfcall(vm, mem)
else
error("BPF verification failed")
end
end
end
function ubpfverify(vm::ubpf_vm)
vm_ref = Ref(vm)
GC.@preserve vm_ref begin
ccall((:ubpf_verify, libubpf), Cint, (Ptr{ubpf_vm},), vm_ref)
end
end
function unsafe_ubpfcall(vm::ubpf_vm, mem::Union{<:Ptr,<:Integer}, mem_len::Integer)
vm_ref = Ref(vm)
mem = sizeof(mem) < sizeof(Ptr{Cvoid}) ? UInt(mem) : mem
GC.@preserve vm_ref mem begin
ccall((:ubpf_exec, libubpf), UInt64,
(Ptr{ubpf_vm}, Ptr{Cvoid}, Csize_t),
vm_ref, reinterpret(Ptr{Cvoid}, mem), mem_len)
end
end
function unsafe_ubpfcall(vm::ubpf_vm, mem::Vector{UInt8})
vm_ref = Ref(vm)
GC.@preserve vm_ref mem begin
ccall((:ubpf_exec, libubpf), UInt64,
(Ptr{ubpf_vm}, Ptr{Cvoid}, Csize_t),
vm_ref, mem, length(mem))
end
end
function ubpfloadelf(vm::ubpf_vm, exe::Vector{UInt8})
vm_ref = Ref(vm)
errmsg = Ref{Cstring}()
ret = GC.@preserve vm_ref errmsg begin
ccall((:ubpf_load_elf, BPFnative.libubpf), Cint,
(Ptr{BPFnative.ubpf_vm}, Ptr{UInt8}, Csize_t, Ptr{Cstring}),
vm_ref, pointer(exe), length(exe), errmsg)
end
if ret != 0
error(unsafe_string(errmsg[]))
end
vm_ref[]
end
ubpfloadelf(path::String) = ubpfloadelf(read(path))
48 changes: 48 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,33 @@ end

using InteractiveUtils

if !isdefined(BPFnative, :libubpf)
@warn "ubpf unavailable; skipping interpreter tests"
end
function ubpftest(fn, Targs, arg)
exe = bpffunction(fn, Targs; btf=false, name="kernel")
vm = BPFnative.ubpf_vm(;insts=C_NULL, num_insts=0)
vm = ubpfloadelf(vm, exe)
if ubpfverify(vm) != 0
error("eBPF verifier error during test")
end
if arg === nothing
BPFnative.unsafe_ubpfcall(vm, C_NULL, 0)
else
BPFnative.unsafe_ubpfcall(vm, arg, sizeof(arg))
end
end
macro ubpftest(call)
if isdefined(BPFnative, :libubpf)
@assert Meta.isexpr(call, :call) "@ubpftest expects a call"
@assert length(call.args) <= 2 "Too many call arguments, 1 expected"
fn = call.args[1]
arg = get(call.args, 2, nothing)
Targ = arg !== nothing ? Base.to_tuple_type(typeof.((arg,))) : Tuple{}
:(ubpftest($(esc(fn)), $Targ, $arg))
end
end

@testset "codegen" begin
map_types = (UInt8, UInt16, UInt32, UInt64,
Int8, Int16, Int32, Int64)
Expand All @@ -32,6 +59,27 @@ using InteractiveUtils
@test occursin(".section\tlicense,", asm)
@test occursin("_license,@object", asm)
@test occursin(".asciz\t\"abc\"", asm)
@test @ubpftest(kernel(0)) == 0
end
@testset "verifier errors" begin
@testset "loop" begin
function kernel(x)
while true end
0
end
@test_throws ErrorException @ubpftest(kernel(0))
end
end
@info "uBPF error messages during testing are OK"
@testset "runtime errors" begin
@testset "out-of-bounds load" begin
kernel(x) = unsafe_load(Ptr{UInt8}(1))
@test @ubpftest(kernel(0)) == typemax(UInt)
end
end
@testset "argument usage" begin
kernel(x) = x+1
@test @ubpftest(kernel(1)) == 2
end
@testset "map helpers" begin
function kernel(x)
Expand Down

0 comments on commit 19e86b0

Please sign in to comment.