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

本文讲一讲如何查看 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 functions1       [1]     VARARGPREP      02       [1]     GETTABUP        0 0 0   ; _ENV "print"3       [1]     LOADK           1 1     ; "hello, world"4       [1]     CALL            0 2 1   ; 1 in 0 out5       [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.lualuac -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 + yreturn 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.rhkb.cn/news/339887.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SaaS 电商设计 (十一) 那些高并发电商系统的限流方案设计

目录 一.什么是限流二.怎么做限流呢2.1 有哪些常见的系统限流算法2.1.1 固定窗口2.1.1 滑动窗口2.1.2 令牌桶2.1.3 漏桶算法 2.2 常见的限流方式2.2.1 单机限流&集群限流2.2.2 前置限流&后置限流 2.3 实际落地是怎么做的2.3.1 流量链路2.3.2 各链路限流2.3.2.1 网关层2…

Maven 中的 classifier 属性用过没?

最近训练营有小伙伴问到松哥一个关于 Maven 依赖的问题&#xff0c;涉及到 classifier 属性&#xff0c;随机问了几个小伙伴&#xff0c;都说工作中没用到过&#xff0c;因此简单整篇文章和小伙伴们分享下。 Maven 大家日常开发应该都有使用&#xff0c;Maven 中有一个比较好玩…

图论(四)—最短路问题(Dijkstra)

一、最短路 概念&#xff1a;从某个点 A 到另一个点B的最短距离&#xff08;或路径&#xff09;。从点 A 到 B 可能有多条路线&#xff0c;多种距离&#xff0c;求其中最短的距离和相应路径。 最短路径分类&#xff1a; 单源最短路&#xff1a;图中的一个点到其余各点的最短路径…

【图像处理与机器视觉】频率域滤波

知识铺垫 复数 CRjI 可以看作复平面上的点&#xff0c;则该复数的坐标为&#xff08;R&#xff0c;I&#xff09; 欧拉公式 e j θ c o s θ j s i n θ e^{j\theta} cos \theta j sin \theta ejθcosθjsinθ 极坐标系中复数可以表示为&#xff1a; C ∣ C ∣ ( c o s…

15、matlab绘图汇总(图例、标题、坐标轴、线条格式、颜色和散点格式设置)

1、plot()函数默认格式画图 代码: x=0:0.1:20;%绘图默认格式 y=sin(x); plot(x,y) 2、X轴和Y轴显示范围/axis()函数 代码: x=0:0.1:20;%绘图默认格式 y=sin(x); plot(x,y) axis([0 21 -1.1 1.1])%设置范围 3、网格显示/grid on函数 代码: x=0:0.1:20;%绘图默认格式 …

拓展虚拟世界边界,云手机可以做到吗

虚拟世界&#xff0c;AI&#xff0c;VR等词汇是21世纪最为流行的词汇&#xff0c;在科技背后&#xff0c;这些词汇的影响变得越来越大&#xff0c;已经走进了人们的世界&#xff0c;比如之前APPLE发布的vision pro&#xff0c;使人们能够更加身临其境的体验到原生os系统&#x…

从MLP到卷积

1.从MLP到卷积层 最近要做多通道的实验&#xff0c;所以重新将处理图像的基础模型回顾一下&#xff0c;什么是卷积&#xff1f;卷积本质是是一种特殊的全连接层。 1.1怎么w的权重从一个值变成了4维呢?可以这样理解&#xff0c;在此举一个例子&#xff1a; 其实本质可以看成&…

安防综合管理系统EasyCVR平台GA/T1400视图库:基于XML的消息体格式

GA/T 1400标准的应用范围广泛&#xff0c;涵盖了公安系统的视频图像信息应用系统&#xff0c;如警务综合平台、治安防控系统、交通管理系统等。在视频监控系统中&#xff0c;GA/T 1400公安视图库的对接是实现视频图像信息传输、处理和管理的重要环节。 以视频汇聚EasyCVR视频监…

Flink中因java的泛型擦除导致的报错及解决

【报错】 Exception in thread "main" org.apache.flink.api.common.functions.InvalidTypesException: The return type of function Custom Source could not be determined automatically, due to type erasure. You can give type information hints by using th…

使用Xshell一键在多个会话中执行多个命令

背景 平时在工作中经常通过ssh远程操作Linux&#xff0c;由于我们负责的服务部署在超过5台服务器&#xff08;相同的代码及路径&#xff09;&#xff0c;每次发布后执行重启都得重复操作5次关闭、检查、启动、查看日志&#xff0c;特别繁琐。 后来发现Xshell 7可以录制脚本&am…

【MyBatis】MyBatis操作数据库(二):动态SQL、#{}与${}的区别

目录 一、 动态SQL1.1 \<if>标签1.2 \<trim>标签1.3 \<where>标签1.4 \<set>标签1.5 \<foreach>标签1.6 \<include>标签 二、 #{}与${}的区别2.1 #{}是预编译sql&#xff0c;${}是即时sql2.2 SQL注入2.3 #{}性能高于${}2.4 ${}用于排序功能…

洛谷 P10566 「Daily OI Round 4」Analysis 题解

先弄个 ASCII 码表&#xff1a; 分析 很明显&#xff0c;想要节省时间&#xff0c;就要把这些字符转换成和它们的 ASCII 值最接近的大写字母。 通过 ASCII 码表&#xff0c;很容易就可以发现&#xff1a; ASCII 值与数字最接近的大写字母是 A \texttt A A。ASCII 值与小写…

JVM学习-监控工具(二)

jmap&#xff1a;导出内存映像文件&内存使用情况 基本情况 jmap(JVM Memory Map)&#xff1a;一方法获取dump文件(堆转储快照文件&#xff0c;二进制文件)&#xff0c;还可以获取目标Java进程的内存相关信息&#xff0c;包括Java堆各区域的使用情况、堆中对象的统计信息、…

多线程新手村5--线程池

1.1 线程池是什么 线程诞生的意义是因为进程的创建/销毁开销太大&#xff0c;所以使用线程提高代码的执行效率&#xff1b;那如果想要进一步提升执行效率&#xff0c;该怎么办呢&#xff1f;有一个方法是使用线程池。 首先&#xff0c;什么是线程池&#xff1a;池就是池子&am…

奶茶店、女装店、餐饮店是高危创业方向,原因如下:

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 现在的俊男靓女们&#xff0c;心中都有一个执念&#xff1a; (1)想证明自己了&#xff0c;开个奶茶去…… (2)想多赚点钱了&#xff0c;加盟餐饮店去…… (3)工作不顺心了&#xff0c;搞个女装店去…… 但凡抱着…

102.网络游戏逆向分析与漏洞攻防-ui界面的设计-反隐身功能的界面设计与实现(有不使用MFC生成,自己手写代码创建复选框与事件的例子)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果&#xff0c;代码看不懂是正常的&#xff0c;只要会抄就行&#xff0c;抄着抄着就能懂了 内容…

独立游戏开发的 6 个步骤

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

discuz论坛怎么修改备案信息

大家好&#xff0c;今天给大家分享下discuz如何填写备案信息并且展示在网站首页。大家都知道国内网站都需要备案&#xff0c;不通过备案的网站上是没办法通过域名打开的。大家也可以通过搜索网创有方&#xff0c;或者直接点击网创有方 查看悬挂备案号后的效果。 首先大家可以看…

MySQL表的增删改查初阶(上篇)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人…

成人本科毕业论文怎么写?分享自己的经验

撰写成人本科毕业论文是一个系统而深入的过程&#xff0c;以下是我个人的经验分享&#xff0c;希望能帮助你更好地完成这一任务&#xff1a; 1. 明确论文选题 兴趣与专长&#xff1a;选择自己感兴趣且有一定专长的领域&#xff0c;这样更容易深入研究。可行性&#xff1a;确保…