《自己动手实现Lua:虚拟机、编译器和标准库》读书笔记
2021-08-12 20:07:27 0 举报
AI智能生成
读书笔记
作者其他创作
大纲/内容
第三章 指令集
它对应 2.3.4.6 每条指令占用4个字节
共32比特
操作码
6比特
操作数
26比特
高级语言的虚拟机是对真实计算的模拟和抽象
可按照实现方式分类
基于栈
例如:Java虚拟机,.netCLR,python虚拟机,ruby的YARV
使用PUSH类的指令往栈顶推入值
使用POP类的指令从栈顶弹出值
其他的指令都是对栈顶值操作的指令
指令集相对比较大,但是指令平均长度比较短
基于寄存器
lua虚拟机,安卓早起使用的虚拟机Dalvik
lua5.0版本以前的虚拟机是基于栈的
可以对寄存器进行寻址
需要把寄存器地址编辑入指令里
所以指令比较长
指令集
如同真实的计算机有一套指令集一样
可按照指令长度分类
定长指令
变长指令集
例如:Java虚拟机
可按照高26个比特分配方式分类
iABC
三个操作数
A 8比特
B--- 9比特
C 9比特
以它居多
iABx
两个操作数
A 8比特
Bx 18比特
iAxBx
两个操作数
A 8比特
sBx 18比特
只有它会被解析成带符号的整数
iAx
一个操作数
占全部的26个比特
重点:理解高比特位,低比特位
指令
分类
常量加载指令
运算符相关指令
循环和跳转指令
函数相关指令
表操作指令
Upvalue操作指令
5.3版本时 它有47条指令
操作码
用于识别指令
操作数
它就是 指令的参数
操作数 A
表示目标寄存器 索引
其他操作数
可按照表示的信息分类
OpArgN
不表示任何信息,也就就不会被使用
OpArgU
iABC
表示寄存器索引
iAsBx
表示跳转的偏移
布尔值
整数值
upvalue索引
子函数索引
OpArgR
表示源寄存器地址
OpArgK
常量表的索引
寄存器索引
9个比特中,最高位==1时表示常量索引否则是寄存器索引
子主题
指令的编码格式
编码
可以用GO语言中的uint32l类型表示
编码模式
指令解码
5个方法
Opcode方法
从指令中提取操作码
ABC()
从iABC模式中提取参数
ABx()
从iABx模式中提取参数
AsBx()
从iAsBx模式中提取参数
Ax()
从iAx模式中提取参数
提取参数的方法
指令表
第二章 二进制chunk
2.3 二进制chunk的格式
数据类型
对于需要使用超过一个字节表示的数据,要考虑大小端的问题
数字
字节
C语言整形叫作cint
C语言size_t,叫作size_t
Lua整形
Lua浮点型
固定长度储存
除字节类型外,都会占用对个字节
字符串
实质,是一个字节数组
字节数组的长度也记录在二进制chunk中
短字符串
长字符串
nulll
列表
cint记录列表的长度
紧接着储存N个列表元素
总体结构
总体而言,分为头部,主函数原型
但还有一个sizeUpvalues字段
头部
把主函数编译成函数原型后,会给它添加一个header,然后dump成一个luac.out文件
签名
很多二进制格式都会以固定魔数开始
四个字节
ESC
L U A 的ASCII码
魔数主要起到快速识别文件格式的作用
可以用 xxd命令观察
如果签名不对,就会拒绝加载该文件
版本号
签名之后的一个字节
大版本号
小版本号
发布号
发布号的增加仅仅意味着bug的修复
如果版本号与虚拟机本身的版本号不一致,就拒绝加载该文件
5.3.4这个版本号如何记录在chunk文件中
5x16+3
格式号
版本号之后的一个字节
如果和虚拟机本身的格式号不匹配,就决绝加载该文件
lua 官方现在使用的格式号是 0
LUAC_DATA
格式号之后的6个字节
Lua 1.0 发布的年份
回车符
换行符
替换符
另一个换行符
各种整数类型占用的字节数
LUAC_DATA 之后的5个字节
也记录了Lua指令占用的字节数
大小端识别信息
LUAC_INT
子主题
如果大小端方式与本机不匹配,则拒绝加载
浮点数识别信息
LUAC_NUM
子主题
如果浮点数格式与本机不匹配,则拒绝加载
2.3.4 函数原型
函数基本信息
源文件名
第一个字段
只有在main 函数的原型中有这个字段
不是必要的信息 -s选项编译就不存在于二进制chunk文件里了
起止行号
固定参数个数
是否是vararg函数
必要的寄存器数量
指令表
行号表
常量表
upvalue表
子函数原形表
局部变量表
Undump()函数
调试信息
行号表
局部变量表
upvalue名列表
2.4 解析二进制chunk
读取基本数据类型
检查头部
读取函数原型
2.2 luac命令介绍
编译lua源文件
-o选项,对输出文件进行明确指定。
-s选项告诉luac去掉调试信息
-p选项仅仅检查语法是否正确
以函数为单位进行编译
每个函数都会编译成一个内部结构
Lua编译器会自动为脚本添加一个main函数
再以它为起点进行编译
可以没有子函数,因此反编译输出只有主函数信息
第一行以main开头
以function开头的说明是个普通函数
查看二进制chunk
-l 选项可以把luac切换到反编译模式
但是精简模式
-l -l 详细模式
会把常量表,局部变量表,upvalue表信息也打印出来
第一行以main开头
以function开头的说明是个普通函数
第二行以此是
1 固定参数数量
如果有加号,表示这是个vararg函数
可变参数函数
2 必要的寄存器数量
3 upvalue 数量
4 局部变量数量
5 常量数量
6 子函数数量
指令列表
指令序号
对应的行号
操作码
操作数
注释信息
2.1 什么是二进制chunk
Lua并不是直接解释执行chunk,而是先由编译器编译成内部结构
内部结构 叫做Prototype
函数的基本信息
参数数量
局部变量数量
字节码
常量表
Upvalue表
调试信息
子函数原型列表
它本质是也是一个字节流
class文件的格式设计的相当紧凑
什么是chunk
lua字节码的一个载体
chunk可以很小,小到只有一两句语句
一段可以被Lua解释器执行的代码
常见问题
lua版本问题
大小端问题
按照本机的大小端方式生产二级制chunk文件
探测被加载的文件的大小端方式,若与本机不一致则决绝加载
0 条评论
下一页