lua vm 二: 查看字节码、看懂字节码

news2025/2/22 21:00:38

本文讲一讲如何查看 lua 的字节码(bytecode),以及如何看懂字节码。

以下分析基于 lua-5.4.6,下载地址:https://lua.org/ftp/ 。


1. 查看字节码

1.1 方法一:使用 luac

luac 是 lua 自带的编译程序,用法是:luac -l -p <文件名> ,如果要完整展示,则用 -l -l,比如 luac -l -l -p <文件名>

有 lua 脚本 a.lua:

print("hello, world")

luac -l -l -p a.lua 生成出来是这样的:

main <a.lua:0,0> (5 instructions at 0x55a732d64cc0)
0+ params, 2 slots, 1 upvalue, 0 locals, 2 constants, 0 functions
        1       [1]     VARARGPREP      0
        2       [1]     GETTABUP        0 0 0   ; _ENV "print"
        3       [1]     LOADK           1 1     ; "hello, world"
        4       [1]     CALL            0 2 1   ; 1 in 0 out
        5       [1]     RETURN          0 1 1   ; 0 out
constants (2) for 0x55a732d64cc0:
        0       S       "print"
        1       S       "hello, world"
locals (0) for 0x55a732d64cc0:
upvalues (1) for 0x55a732d64cc0:
        0       _ENV    1       0

如果是一份由 luac 编译生成出来的字节码文件,则直接 luac -l <文件名>luac -l -l <文件名> 就行了。比如这样:

luac -o a.lua.out a.lua

luac -l -l a.lua.out

1.2 方法二:使用在线工具:luac.nl - Lua Bytecode Explorer

Lua Bytecode Explorer 的完整网址是: https://www.luac.nl/。

它支持从 4.0 到 5.4.6 的所有版本(截至 2024.4)。它还支持生成代码分享链接,在页面底下有 Generate Link 按钮,比如我写的 hello world 脚本: https://luac.nl/s/f79aff49f5fd7746b4e3ae2a65 。

大部分情况下,用这个在线网站是最方便的,推荐使用。

效果是这样的:


图1:lua bytecode explorer 生成字节码


2. 看懂字节码

2.1 指令集的说明文档

方法一:lua 源码

最直接的方式是看源码,所有指令的定义在这个代码文件:lopcodes.h ,不同指令的具体实现在 lvm.c 的 luaV_execute 函数里。


方法二:一些文档或文章

lua5.2 : http://files.catwell.info/misc/mirror/lua-5.2-bytecode-vm-dirk-laurie/lua52vm.html

lua5.3 : https://the-ravi-programming-language.readthedocs.io/en/latest/lua_bytecode_reference.html

lua5.4 : https://zhuanlan.zhihu.com/p/277452733



2.2 指令的基本格式

lua5.4 指令的基本信息:

  • 使用一个 32 位的无符整数来表示一个指令,包含两大部分:操作码和操作数

  • 其中前 7 位用于表示操作码,其余的是操作数,操作数要根据操作码使用不同的编码格式去解析

  • 有 5 种格式编码格式:iABC, iABx, iAsBx, iAx, isJ,每个指令只属于其中之一

下图取自 lua 源码中的 lopcode.h,它精确的标明了不同编码格式用到了哪些 bit 位。


图2:lua5.4 opcode 格式


2.3 指令编码格式

有 5 种指令编码格式,比如 iABC,i 表示 instruction,ABC 分别是三个操作数。

举个具体的例子说明一下吧,对于这样一个脚本:

local function my_add(x, y)
	local z = x + y
    return z
end

用 lua bytecode explorer 生成的,代码片段的 link 是 https://luac.nl/s/fac2949499991b83b0e3ae2a65 ,编译结果如下图:


图3:简单加法例子

local z = x + y 这个语句被编译成这两句:

1	ADD	2 0 1	
2	MMBIN	0 1 6	; __add

MMBIN 这句可以不用管,是元表相关的,这里不会用到,只关注 ADD 这一句就好了。

加法指令 OP_ADD 就是 iABC 格式的:

OP_ADD,/*	A B C	R[A] := R[B] + R[C]				*/

ADD 2 0 1 中 A 对应 2,B 对应 0,C 对应 1。R[0] 存的是形参 x 的值,R[1] 存的是形参 y 的值,R[2] 存的是局部变量 z 的值。

所以 local z = x + y 就翻译成 R[2] = R[0] + R[1]

看到这里可能会有点晕,R[0] 是什么东西?这就是 lua 的 “寄存器”,但它是虚拟出来的,代表的实际上是 lua 数据栈的一个位置,而 lua 数据栈是一个数组结构,每个函数都会在这个数组上占据一段空间。

可以理解为,每个函数都有一个自己的栈,它是一个数组。数组的最前面放的是参数,之后放的是局部变量。在我们的例子中,数据是这样放的:


图4:简单加法的数据结构

实际上,编译结果的 locals 项已经清楚表明了各个变量在 lua 数据栈中的位置了,index 列就表示在数据栈数组中的索引。


图5:locals 信息


2.4 为什么一个函数生成了两个 return 的 opcode

上图中,可以看到 my_add 函数,我们只写了一个 return 语句,但却有两个 return opcdoe,而 main 函数,我们没有 return,也有一个 return opcdoe。下面具体解释一下。

my_add 的两个分别对应 OP_RETURN1 和 OP_RETURN0。第一个 return1 对应的是 return c 这个语句。第二个 return0 是 lua 编译器自动生成的,每个函数的末尾都会补充一个 “final return”。

具体源码可以在 lparser.c 找到,里面分别处理 main 函数和普通函数,lua 会把一个 script 脚本处理成一个函数,就叫 main 函数,如果上图中的 function main

我们显式写的 return 语句,是在 lparser.c 的 retstat 函数处理的,它内部调用 luaK_ret 生成一个 return 的 opcode。

main 函数,是在 lparser.c 的 mainfunc 函数处理的,末尾调用 close_func 处理收尾工作,close_func 内部会调用 luaK_ret 生成一个 return 的 opcode。

普通函数,是在 lparser.c 的 body 函数处理的,末尾调用 close_func 处理收尾工作,close_func 内部会调用 luaK_ret 生成一个 return 的 opcode。


正文完。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1795261.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

无线和移动网络

背景 两个重要的挑战 无线&#xff1a;通过无线链路通信移动&#xff1a;需要网络处理移动&#xff08;不同变换所接入的网络&#xff09;用户 无线网络中的组件 无线主机&#xff08;无线并不总是意味着移动的&#xff09;基站&#xff08;base station 或者叫AP&#xff0…

芝麻IP好用吗?来测试了!

作为老牌代理IP服务厂商&#xff0c;芝麻IP和青果网络代理IP都做的不错&#xff0c;市场上几乎可以是有口皆碑了&#xff0c;上次测试了青果网络的代理IP&#xff0c;效果表现得还挺不错&#xff0c;和他们自己宣传的以及客户对他们的评价大差不差。 总的来说&#xff0c;他们家…

纷享销客安全体系:物理与环境安全

纷享销客的物理设备托管在经过严格准入制度授权的TIER3级别以上的专业数据中心&#xff0c;这些数据中心均通过了等保三级与IS027001安全认证&#xff0c;确保电力、制冷等基础设施提供相应级别的冗余&#xff0c;以增强IDC环境的安全性。 业务操作系统平台采用当前广泛使用的…

解决 iOS 端小程序「saveVideoToPhotosAlbum:fail invalid video」问题

场景复现&#xff1a; const url https://mobvoi-digitalhuman-video-public.weta365.com/1788148372310446080.mp4uni.downloadFile({url,success: (res) > {uni.saveVideoToPhotosAlbum({filePath: res.tempFilePath,success: (res) > {console.log("res > &…

NocoDB开源的智能表格详解-腾讯文档本地替代品

文章目录 一、介绍二、docker-compose部署三、登录NocoDB四、NocoDB手册1. 创建项目2. 收集统计表2.1 添加字段2.2 编辑字段2.3 字段类型2.4 发布表格 3.创建表单3.1 创建表单3.2 分享表单3.3 填写检测单 4.创建看板5.创建画廊 一、介绍 可作为腾讯文档的本地电子表格替代品&a…

VS2019 QT无法打开 源 文件 “QTcpSocket“

VS2019 QT无法打开 源 文件 "QTcpSocket" QT5.15.2_msvc2019_64 严重性 代码 说明 项目 文件 行 禁止显示状态 错误(活动) E1696 无法打开 源 文件 "QTcpSocket" auto_pack_line_demo D:\vs_qt_project\auto_pack_line_de…

OZON快蜗牛数据工具,OZON数据分析工具

在当今的电商时代&#xff0c;数据已经成为了商家们最宝贵的资产之一。无论是产品选品、市场定位&#xff0c;还是营销策略的制定&#xff0c;都离不开对数据的深入分析和精准把握。而在众多电商平台中&#xff0c;OZON以其独特的商业模式和庞大的用户群体&#xff0c;吸引了众…

Vue3项目准备:utils工具插件文件夹中封装request.js配置axios请求基地址及超时时间、请求拦截器、响应拦截器

token介绍 概念&#xff1a;访问权限的令牌&#xff0c;本质上是一串字符串 创建&#xff1a;正确登录后&#xff0c;由后端签发并返回 作用&#xff1a;判断是否有登录状态等&#xff0c;控制访问权限 注意&#xff1a;前端只能判断token有无&#xff0c;而后端才能判断tok…

【vue3+pinia+uniapp项目问题:使用pinia状态管理时store的数据更新,模板渲染视图不能实时更新】

在这里选择不同的学校后&#xff0c;发现store里面的数据打印出来能更新&#xff0c;但是使用store的数据打印出来并未实时更新且渲染在模板上&#xff0c;必须手动刷新视图才能更新。 原因是因为使用了解构赋值传入参数 解决方法 1.使用computed 现在视图能进行实时更新…

GA/T 1400视频汇聚平台EasyCVR级联后,平台显示无通道是什么原因?

国标GB28181安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台部署轻快&#xff0c;可支持的主流标准协议有GA/T 1400、国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。 有用户反馈&#xff…

Servlet实现会话追踪登录功能(结合JDBC)

目录 1.技术要求 2.关于会话Session 生活例子 代码体现 1.存储到session 2.从session中拿出 3.完成登录功能 4.利用Session作追踪 关于jstl应用 关于EL表达式应用 注意 5.测试 1.技术要求 核心&#xff1a; &#xff08;1&#xff09;Servlet项目的搭建 &#x…

正邦科技(第10天)

这里写目录标题 任务一任务二任务三 任务一 下位机报上来的十进制数据进行解析&#xff1a; 360170 固定报文&#xff0c;一个F对应一个字节&#xff0c;温度值&#xff0c;湿度值&#xff0c;烟雾浓度值是十进制转16进制&#xff0c;告警状态需要高低位移位&#xff0c;然后再…

Apple开发者Identifier唯一身份标识创建

1. 选中Identifiers然后点击加号进入创建页面 2.选择要注册的标识类型 选择类型为App然后点击继续 输入应用描述与BundleID并勾选要开启的功能后点击继续 点击注册标识 注册成功后,会在标识列表中看到

Apple开发者macOS设备与描述文件Profile创建完整过程

安装并打开Apple Configurator 新建描述文件 输入macOS平台的描述文件的相关信息,然后选择证书 选择一个可用证书 存储描述文件 存储成功如下: 使用文本编辑器打开刚才保存的描述文件,找到设备名与UDID

MySQL8.3日志文件占用硬盘空间过大,释放方法

执行 reset master; 即可解决问题&#xff0c;我写了个任务&#xff0c;定期执行 执行后目录效果

Vue3学习记录(第一天)

Vue3学习记录_第一天 背景说明记录Vue3实现响应式前端的反射前端对象的属性赋值Vue3响应式实现过程稿前端移除对象的属性 背景 本次学习主要是看视频学习, 没有跟练, 但是很多知识点感觉又容易忘记. 所以通过笔记的方式输出一下. 说明 估计只能自己看懂, 如果能提供一些其他…

ROS学习记录:自定义消息类型

前言 当我们需要传输一些特殊的数据时&#xff0c;且官方的消息包无法满足需求&#xff0c;我们便可以自己定义一个消息类型。 实验步骤 一、在终端输入cd ~/catkin_ws1/src进入工作空间中src目录 二、输入catkin_create_pkg qq_msgs roscpp rospy std_msgs message_generati…

Docker 基础使用(3) 存储卷

文章目录 存储卷的含义存储卷的分类存储卷的作用存储卷的使用存储卷实际使用案例 ---- MySQL灾难恢复存储卷的局限 Docker 基础使用&#xff08;0&#xff09;基础认识 Docker 基础使用 (1) 使用流程概览 Docker 基础使用&#xff08;2&#xff09; 镜像与容器 Docker 基础使用…

Windows系统电脑本地部署AI音乐创作工具并实现无公网IP远程使用

文章目录 前言1. 本地部署2. 使用方法介绍3. 内网穿透工具下载安装4. 配置公网地址5. 配置固定公网地址 前言 本文主要介绍如何在Windows系统电脑上快速本地部署一个文字生成音乐的AI创作工具MusicGPT&#xff0c;并结合cpolar内网穿透工具实现随时随地远程访问使用。 MusicG…

Python 可变长参数的魔法:灵活函数设计的秘密

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 什么是可变长参数&#xff1f; 在 Python 中&#xff0c;可变长参数允许你向函数传入任意数量的参数&#xff0c;而无需预先定义它们的个数。这为编写更加灵活和通用的函数提供了可能。可变长参数主要有两种形式&am…