rbpf虚拟机-汇编和反汇编器

简介: 该篇文章是rbpf汇编器和反汇编器代码块功能的整理。(学习该虚拟机的目的是为了搞懂solana合约的执行方式,solana使用的rbpf是在该虚拟机上进行扩展。)

Welcome to Code Block's blog

本篇文章主要介绍了
[rbpf虚拟机-汇编和反汇编器]
❤博主广交技术好友,喜欢我的文章的可以关注一下❤

一、概述

该篇文章是rbpf汇编器和反汇编器代码块功能的整理。

(学习该虚拟机的目的是为了搞懂solana合约的执行方式,solana使用的rbpf是在该虚拟机上进行扩展。)

汇编器的作用是将机器指令转换为二进制操作码,反汇编器将二进制操作码数据转换为机器指令的形式进行显示。

二、主要功能

汇编和反汇编器主要实现以下功能:

  1. 机器指令转二进制
    • 机器指令到初始指令结构体(Instruction)
    • 初始指令类到指令结构体(Insn)
    • 指令列表通过切片转换为二进制
  2. 二进制转机器指令
    • 循环解析到机器指令

三、关键函数解析

3.1 汇编器

3.1.1 parse -转换为Instruction列表

pub fn parse(input: &str) -> Result<Vec<Instruction>, String> {
     
    //1.跳过零个或多个空格
    //2.这里的with 不关心((), ["mov", "add", "sub"]) 里的()
    let mut with = spaces().with(many(instruction()).skip(eof()));

    #[cfg(feature = "std")]
    {
     
        //根据with解析器去解析指令
        match with.easy_parse(position::Stream::new(input)) {
     
            Ok((insts, _)) => Ok(insts),
            Err(err) => Err(err.to_string()),
        }
    }
    #[cfg(not(feature = "std"))]
    {
     
        match with.parse(position::Stream::new(input)) {
     
            Ok((insts, _)) => Ok(insts),
            Err(err) => Err(err.to_string()),
        }
    }
}

在parse方法中,会将机器指令如:"move r1, 0"格式转换为:

[Instruction {
      name: "mov", operands: [Register(1), Integer(0)] }]

主要使用的是combine 转换库,在这里是根据空格和换行分割后,使用instruction()进行封装,instruction()方法内容如下:

n instruction<I>() -> impl Parser<I, Output = Instruction>
where
    I: Stream<Token = char>,
    I::Error: ParseError<I::Token, I::Range, I::Position>,
{
     
    //解析以特定分隔符分隔的多个元素。
    let operands = sep_by(operand(), char(',').skip(spaces()));

    (ident().skip(spaces()), operands, spaces()).map(|t| Instruction {
     
        name: t.0,
        operands: t.1,
    })
}

什么是combine?
combine 是一个基于解析器组合子(parser combinators)的库,它允许开发者通过组合简单的解析器来构建复杂的解析器。解析器组合子是一种函数式编程技术,通过将小的解析器组合起来,形成更大、更复杂的解析器。

combine文档

3.1.2 assemble_internal-转换为Insn

fn assemble_internal(parsed: &[Instruction]) -> Result<Vec<Insn>, String> {
     
    let instruction_map = make_instruction_map();
    let mut result: Vec<Insn> = vec![];

    //循环parsed列表 (parsed来自parse方法返回值)
    for instruction in parsed {
     
        //获取命令名称
        let name = instruction.name.as_str();
        //匹配类型和opcode
        match instruction_map.get(name) {
     
            Some(&(inst_type, opc)) => {
     
                match encode(inst_type, opc, &instruction.operands) {
     
                    //匹配成功后返在结果内添加命令
                    Ok(insn) => result.push(insn),
                    //发生错误时,返回错误
                    Err(msg) => return Err(format!("Failed to encode {name}: {msg}")),
                }
                // Special case for lddw.
                // 当类型是imm类型时,对命令取高32位进行拼接
                if let LoadImm = inst_type {
     
                    if let Integer(imm) = instruction.operands[1] {
     
                        result.push(insn(0, 0, 0, 0, imm >> 32).unwrap());
                    }
                }
            }
            None => return Err(format!("Invalid instruction {name:?}")),
        }
    }
    Ok(result)
}

assemble_internal()作用是将parse()处理完成后的数据进行进一步的处理,处理成以下数据格式:

[Insn {
      opc: 183, dst: 1, src: 0, off: 0, imm: 0 }]

Insn数据为ebpf的指令格式.

  31      24 23     16 15      8 7       0
+--------+--------+--------+--------+
|    dst  |    src  |  offset  |  opcode |
+--------+--------+--------+--------+
|           immediate / address          |
+----------------------------------------+

然后通过切片extend_from_slice的方式将其转换为二进制格式。完整的转换代码如下:

pub fn assemble(src: &str) -> Result<Vec<u8>, String> {
     
    let parsed = (parse(src))?;
    //Ok([Instruction { name: "mov", operands: [Register(0), Integer(0)] }, Instruction { name: "add", operands: [Register(1), Integer(2)] }])
    let insns = (assemble_internal(&parsed))?;

    let mut result: Vec<u8> = vec![];
    for insn in insns {
     
        result.extend_from_slice(&insn.to_array());
    }
    Ok(result)
}

3.2 反汇编器

3.2.1 to_insn_vec-转换为机器指令

pub fn to_insn_vec(prog: &[u8]) -> Vec<HLInsn> {
     
    //验证程序是规定的8位
    if prog.len() % ebpf::INSN_SIZE != 0 {
     
        panic!(
            "[Disassembler] Error: eBPF program length must be a multiple of {:?} octets",
            ebpf::INSN_SIZE
        );
    }
    //验证是否为空
    if prog.is_empty() {
     
        return vec![];
    }
    let mut res = vec![];
    let mut insn_ptr: usize = 0;

    //循环遍历指令
    while insn_ptr * ebpf::INSN_SIZE < prog.len() {
     
        let insn = ebpf::get_insn(prog, insn_ptr);
        let name;
        let desc;
        let mut imm = insn.imm as i64;

        match insn.opc {
     
          ebpf::LD_ABS_B => {
     
                name = "ldabsb";
                desc = ldabs_str(name, &insn);
            },
          ......
    }
    res
}

to_insn_vec方法代码中使用循环的方式对每个二进制指令(8位),根据每条指令的opcode码进行转换,转换为字符串格式的机器指令(desc),其中包括对指令长度、是否为空进行检查。

四、总结

通过上述对源码进行解读,可以看到汇编器和反汇编器在虚拟机中起着重要的作用,允许开发者直接编写与 eBPF 架构兼容的指令。通过汇编器,这些指令被翻译成内核能够理解和执行的二进制形式。

代码来源:rbpf虚拟机
鸣谢: qmonnet 提供的开源代码.

当然,我也会将带有中文注释和自己理解的一些代码上传的我的github页面,感兴趣的朋友可以进行clone查看.

我的GitHub:forked


感谢您的点赞、关注、收藏!

目录
相关文章
|
2月前
|
存储 弹性计算 数据库
阿里云2026年第一波活动有哪些?云服务器抢购、满减券、免费试用都有
2026年阿里云推出多项优惠活动:轻量应用服务器2核2G峰值200M带宽38元抢购,云服务器“99计划”99元与199元新购续费同价;学生享300元无门槛券,教师享5折优惠;初创企业最高得100万上云抵扣金,企业迁云/出海可申5亿算力补贴;140+款云产品免费试用最长12个月,配合165元满减券包(满50减15、满180减50、满350减100),满足个人、企业全场景上云需求,助力个人、企业和学生用户优惠上云。
610 2
|
存储 人工智能 开发框架
Eliza:TypeScript 版开源 AI Agent 开发框架,快速搭建智能、个性的 Agents 系统
Eliza 是一个开源的多代理模拟框架,支持多平台连接、多模型集成,能够快速构建智能、高效的AI系统。
1666 8
Eliza:TypeScript 版开源 AI Agent 开发框架,快速搭建智能、个性的 Agents 系统
|
7月前
|
新能源
大盘择时:慎用固定均线!12年回测A股数据揭示择时策略的3大适应性缺陷
动量策略加入50日与200日均线择时后收益下降,主要因均线滞后、逻辑冲突及市场变化。解决方案包括动态调整择时参数、结合多指标验证、优化动量执行细节,提升策略适应性与收益表现。
|
9月前
|
存储 NoSQL 区块链
开源:LMDB 操作工具:lmcmd
本文介绍了 LMDB(一种高效的键值存储数据库)和基于 Python 开发的命令行工具 `lmcmd`。由于 LMDB 使用二进制文件存储,管理和调试不便,因此开发了 `lmcmd`,提供了类似 Redis 的命令行操作界面,支持数据库操作、数据导入导出和查找等功能。文章涵盖了 `lmcmd` 的安装、连接数据库和常用命令(如 `set`、`get`、`export` 等)示例。最后强调了开源工具的价值,鼓励用户反馈和改进。
407 0
|
计算机视觉
【CV大模型SAM(Segment-Anything)】如何保存分割后的对象mask?并提取mask对应的图片区域?
【CV大模型SAM(Segment-Anything)】如何保存分割后的对象mask?并提取mask对应的图片区域?
【CV大模型SAM(Segment-Anything)】如何保存分割后的对象mask?并提取mask对应的图片区域?
阿里云地域节点测试IP(国内+海外)Ping值延迟测试
阿里云地域节点遍布全球19个地区节点(国内+海外),共有56个可用区,哪个快网分享阿里云国内及海外节点测试IP,阿里云节点Ping值延迟测试: 阿里云节点全球无缝覆盖,提供CN2高速网络,BGP接入支持。
41642 0
|
缓存 监控 Java
Elasticsearch集群JVM调优
Elasticsearch集群JVM调优
533 5
|
安全 PHP 开发者
PHP中的面向对象编程:深入理解与应用
在PHP的世界里,面向对象编程(OOP)是一块基石,它不仅为开发者提供了一种组织代码的清晰方式,而且通过类和对象的概念,增强了代码的重用性和可维护性。本文旨在深入探讨PHP中OOP的核心概念,包括类、对象、继承、封装、多态等,并通过实例展示如何在实际开发中有效利用这些概念。我们将从基础开始,逐步过渡到高级主题,并探索PHP 7及以上版本中OOP的新特性,以及它们如何影响现代PHP开发。
130 33
|
芯片 存储 算法
芯片验证 | Formal验证技术总结
芯片验证 | Formal验证技术总结
1067 0
芯片验证 | Formal验证技术总结