moon_bpflab

    User-space BPF interpreter, disassembler and debugger in MoonBit.

    Download zip
    Author
    Version
    0.2.0
    License
    Apache-2.0
    Last updated
    8 days ago
    Downloads
    36

    No dependencies

    #MoonBpfLab

    用户态 BPF 执行与调试工具:加载原始字节码或 ELF .text,反汇编、单步、设断点、查看寄存器与内存,并执行受限指令集。

    • 仓库:https://github.com/btlqql/moon-bpflab
    • 版本:0.2.0
    • 许可证:Apache-2.0

    #为什么单独做这一层

    MoonCakes 上已有 hevienz/moonbit_libbpf,它把 libbpf 绑定给 MoonBit,调用的是内核/宿主能力。MoonBpfLab 的方向相反:在 MoonBit 内解释并调试指令,不把执行转发给 C 库或内核。

    2026-10-24 在 MoonCakes 搜索 bpf 只找到 libbpf 绑定和沙箱规划器,未发现用户态 BPF 虚拟机/调试器。搜索未命中不等于绝对不存在,私有实现和不同命名仍是盲区。

    #功能

    • 指令执行:ALU32/ALU64、JMP/JMP32、LDX(含符号扩展 MEMSX)、STX/ST、END/BSWAP、LD_IMM64、CALL、EXIT。
    • 64 位语义:位模式运算、32 位结果零扩展、算术/逻辑右移、有符号与无符号比较;除零返回 0、模零保留被除数,有符号 div/mod 按 RFC 9669。
    • 内存模型:只读输入区、可写栈区、map 值暂存区;越界访问拒绝。
    • 调试器:Machine::step()、断点、条件断点(寄存器等于指定值)、run_until_break()、寄存器快照、指令覆盖计数;CLI 暴露 --break 与 --when。
    • 子程序调用:call 带返回栈、调用深度上限,并保存/恢复 r6–r9;超限拒绝。
    • Array map:lookup/update/delete helper,键值大小与容量检查。
    • ELF64 子集:读取 little-endian ELF、.text、.symtab/.strtab,只接受 EM_BPF。
    • 反汇编:文本输出 ALU、内存、跳转、helper 调用和 64 位立即数,并标注跳转/调用目标。

    #标准兼容终点

    目标是覆盖 eBPF ISA v1.0 的核心指令语义(ALU/ALU64、JMP/JMP32、LDX 含 MEMSX、STX/ST、END/BSWAP、LDDW、CALL、EXIT),参照 RFC 9669 与 uBPF 解释器。矩阵、输入输出案例和明确排除项见 docs/isa-compatibility.md。

    不在终点内:atomics、legacy packet load、map fd 伪指令、verifier、BTF、CO-RE、重定位、JIT。

    #快速开始

    要求 MoonBit 工具链(本版本在 moon 0.1.20260904 验证)。CLI 以 JS 为目标,文件读取只使用 Node 内置模块。

    moon test moon run cmd/main --target js -- disasm examples/minimal.elf moon run cmd/main --target js -- run examples/minimal.elf moon run cmd/main --target js -- run examples/minimal.elf --trace

    输出:

    0: mov64 r0, 42 1: exit r0=0x2a (42)

    #CLI

    bpflab disasm <file> disassemble with jump/call targets bpflab run <file> [input-hex] [--trace] execute and print r0 bpflab elf <file> print entry, sections and symbols

    run 的第二个参数是可选的十六进制输入,作为只读输入区;--trace 每步打印 pc、反汇编和 r0;--break 在指定 pc 停止;--when 在“pc 处且寄存器等于指定值”时停止;--coverage 输出每条指令的执行次数。

    #支持的指令

    类别指令
    ALU64add, sub, mul, div, or, and, lsh, rsh, neg, mod, xor, mov, arsh
    ALU32同上,结果零扩展到 64 位
    跳转ja, jeq, jgt, jge, jset, jne, jsgt, jsge, jlt, jle, jslt, jsle
    内存ldxb/h/w/dw, stxb/h/w/dw, stb/h/w/dw
    其他lddw, call (helper 与本地子程序), exit

    helper 1 对输入/栈逐字节求和;helper 2/3/4 是 array map 的 lookup/update/delete。

    #测试

    moon test 共 38 项:

    • r0 返回、64 位溢出、32 位零扩展/回绕、有符号与无符号跳转分歧。
    • 小端加载、输入只读、栈往返、越界拒绝。
    • 除零/模零与有符号 div/mod、JMP32 有符号/无符号比较、END/BSWAP、符号扩展加载、r6–r9 调用保存。
    • helper 求和。
    • 反汇编 ALU/内存/跳转/helper;跳转与本地调用带目标地址。
    • 单步、快照、断点、条件断点、指令覆盖计数、有界运行。
    • 本地子程序调用、调用深度上限与 callee-saved 寄存器。
    • array map update/lookup/delete。
    • 最小 ELF 加载、原始字节码解码、坏 ELF 拒绝。
    • uBPF 差分向量:arsh、early-exit、ldxb、factorial、div/mod 零、sdiv/smod、sdiv32、ldxsw 等一致;见 docs/ubpf-conformance.md。

    GitHub Actions 在 wasm、wasm-gc、js、native 四个后端执行 check/build/test。

    #外部对照

    #明确不做

    • 不是内核 verifier:不实现完整类型跟踪、有界循环证明、map 类型兼容矩阵或内核 helper 全集。
    • 不实现 CO-RE、BTF、重定位、JIT、atomics、legacy packet load、map fd 伪指令,也不做完整内核栈帧校验。
    • 只加载 ELF64 little-endian 的 .text 子集,不处理程序头、动态段、多段代码或伪 map fd。
    • 不提供生产级隔离;执行器只用于离线工具和测试,不应用于运行不可信生产代码。
    • array map 是固定容量模型,不含 hash map、per-CPU map、ring buffer 或并发语义。

    #后续

    优先补:更多 ELF 段与重定位、BTF 子集、hash map、更多 helper、条件断点表达式、指令级 trace 导出、atomics/legacy 指令的明确拒绝测试。每项都要有真实编译产物和失败样例,再谈发布。

    BpfError

    pub(all) suberror BpfError {
    UnknownOpcode(Int)
    DivisionByZero
    InvalidRegister(Int)
    InvalidProgram(Int)
    MemoryAccess(UInt64, Int)
    WriteToReadOnly(UInt64)
    StepBudgetExceeded
    UnknownHelper(Int)
    CallDepthExceeded
    UnsupportedCallMode(Int)
    UnsupportedMapFd
    UnknownMap(Int)
    MapFull(Int)
    MapKeyError(Int)
    }

    ElfError

    pub(all) suberror ElfError {
    NotElf
    UnsupportedClass
    UnsupportedEndian
    UnsupportedMachine(Int)
    Truncated
    BadSection
    NoText
    }

    ArrayMap

    pub(all) struct ArrayMap {
    key_size : Int
    value_size : Int
    max_entries : Int
    keys : Array[Array[Int]]
    values : Array[Array[Int]]
    }

    ConditionalBreak

    pub(all) struct ConditionalBreak {
    pc : Int
    reg : Int
    value : UInt64
    }

    ElfImage

    pub(all) struct ElfImage {
    entry : Int
    text : Array[Insn]
    symbols : Array[ElfSymbol]
    sections : Array[ElfSection]
    }

    ElfSection

    pub(all) struct ElfSection {
    name : String
    typ : Int
    flags : Int
    offset : Int
    size : Int
    }

    ElfSymbol

    pub(all) struct ElfSymbol {
    name : String
    value : Int
    size : Int
    info : Int
    }

    Insn

    pub(all) struct Insn {
    op : Int
    dst : Int
    src : Int
    off : Int
    imm : Int
    }

    Machine

    pub(all) struct Machine {
    program : Array[Insn]
    vm : Vm
    pc : Int
    steps : Int
    max_steps : Int
    halted : Bool
    retval : UInt64
    breakpoints : Array[Int]
    conditions : Array[ConditionalBreak]
    call_stack : Array[Int]
    call_saved : Array[UInt64]
    executed : Array[Int]
    max_call_depth : Int
    }

    Machine::add_array_map

    fn Machine::add_array_map(self : Machine, key_size : Int, value_size : Int, max_entries : Int) -> Int raise BpfError

    Machine::clear_breakpoints

    fn Machine::clear_breakpoints(self : Machine) -> Unit

    Machine::coverage

    fn Machine::coverage(self : Machine) -> Array[Int]

    Machine::execution_count

    fn Machine::execution_count(self : Machine, pc : Int) -> Int

    Machine::new

    fn Machine::new(program : Array[Insn], input : Array[Int], max_steps : Int) -> Machine

    Machine::new_default

    fn Machine::new_default(program : Array[Insn], input : Array[Int]) -> Machine

    Machine::run

    fn Machine::run(self : Machine) -> UInt64 raise BpfError

    Machine::run_n

    fn Machine::run_n(self : Machine, limit : Int) -> StepResult raise BpfError

    Machine::run_until_break

    fn Machine::run_until_break(self : Machine) -> StepResult raise BpfError

    Machine::set_breakpoint

    fn Machine::set_breakpoint(self : Machine, pc : Int) -> Unit

    Machine::set_conditional_breakpoint

    fn Machine::set_conditional_breakpoint(self : Machine, pc : Int, reg : Int, value : UInt64) -> Unit raise BpfError

    Machine::snapshot

    fn Machine::snapshot(self : Machine) -> Snapshot

    Machine::step

    fn Machine::step(self : Machine) -> StepResult raise BpfError

    Section

    type Section

    Snapshot

    pub(all) struct Snapshot {
    pc : Int
    steps : Int
    halted : Bool
    retval : UInt64
    regs : Array[UInt64]
    }

    StepResult

    pub(all) enum StepResult {
    Continue
    Halted(UInt64)
    BreakpointHit(Int)
    }

    pub(all) struct Vm {
    regs : Array[UInt64]
    input : Array[Int]
    stack : Array[Int]
    maps : Array[ArrayMap]
    map_scratch : Array[Int]
    steps : Int
    max_steps : Int
    }

    Vm::add_array_map

    fn Vm::add_array_map(self : Vm, key_size : Int, value_size : Int, max_entries : Int) -> Int raise BpfError

    Vm::load

    fn Vm::load(self : Vm, addr : UInt64, size : Int) -> UInt64 raise BpfError

    Vm::new

    fn Vm::new(input : Array[Int], max_steps : Int) -> Vm

    Vm::store

    fn Vm::store(self : Vm, addr : UInt64, size : Int, value : UInt64) -> Unit raise BpfError

    decode_instructions

    fn decode_instructions(bytes : Array[Int]) -> Array[Insn] raise ElfError

    disassemble

    fn disassemble(program : Array[Insn]) -> Array[String]

    disassemble_one

    fn disassemble_one(ins : Insn) -> String

    disassemble_with_targets

    fn disassemble_with_targets(program : Array[Insn]) -> Array[String]

    execute

    fn execute(program : Array[Insn], input : Array[Int], max_steps : Int) -> UInt64 raise BpfError

    insn

    fn insn(op : Int, dst : Int, src : Int, off : Int, imm : Int) -> Insn

    is_elf

    fn is_elf(bytes : Array[Int]) -> Bool

    load_elf

    fn load_elf(bytes : Array[Int]) -> ElfImage raise ElfError

    run_default

    fn run_default(program : Array[Insn], input : Array[Int]) -> UInt64 raise BpfError