WebAssembly 是基于栈式虚拟机的虚拟二进制指令集(V-ISA),它被设计为高级编程语言的可移植编译目标。长安链使用的是wasm的二进制模块,我们这里着重分析WebAssembly的二进制模块。WebAssembly的各组件含义及关联关系需要一段时间的学习来掌握,需要大家自己不断的研究与琢磨。
WebAssembly的官方介绍: https://www.wasm.com.cn/docs/binary-encoding/
wasm的二进制模块包含11大组件:
官方文档中提供解析wasm二进制的方式:
- magic & version
magic - uint32 - 0x6d736100
version - uint32 - 0x1 -
根据不同的sec type 分别解析
-
各section有详细字段说明
- 在官方文档描述中,字段类型uint32、int32比较好理解4字节。但varuintN、varintN并未见过,这是
LEB128
编码格式,具体解码方式可参考下述代码
func DecodeUint32(r io.Reader) (ret uint32, num uint64, err error) {
const (
uint32Mask uint32 = 1 << 7
uint32Mask2 = ^uint32Mask
)
for shift := 0; shift < 35; shift += 7 {
b, err := readByteAsUint32(r)
if err != nil {
return 0, 0, fmt.Errorf("readByte failed: %w", err)
}
num++
ret |= (b & uint32Mask2) << shift
if b&uint32Mask == 0 {
break
}
}
return
}
- 我们以长安链官方合约(
镜像chainmaker-go-contract:1.1.1
中的合约为例)进行解析。
5.1)执行hexdump main.wasm > main.dump
命令以二进制形式查看。使用sublime等编辑工具打开。
5.2)magic & version
a) 开始00 61 73 6d
四个字节表示magic,二进制使用的是小端方式编码(大小端的含义还需要自行百度学习),实际为:0x6d736100
b) 随后01 00 00 00
四个字节表示version,0x01
5.3)Type Section
该组件定义了函数的签名声明信息,定义函数的入参、返回值个数及类型。解析如下:
a) 随后一个字节01
表示下面的section为Type Section
。
b) 随后varuint32类型
的73
一个字节,表示该section的长度,通常在处理的时候会忽略该字段,除非session的id 为0。
c)随后12
表示后面有18个type要描述(12
是16进制)。
d) 随后60
作为每个type的分割符,随后01
表示一个形参,随后7f
表示该形参的类型为i32。
e)随后00
表示函数返回值为0个。
f)重复d,e流程,直到遍历18个type
5.4)Function Section
该组件包含指向Type Section的Index,Function Section是数组结构,数组下标与Code Section组件一一对应,Index表示执行Type Section的函数签名。
5.5)Code Section
该组件包含函数具体的实现逻辑以及本地变量信息,Code Section是一个数组,大小与Function Section相对应。
5.6)Export Section
该组件描述的是外部可访问的内存、变量、方法等。以方法组件为例,每个Export Section元素会关联一个Index,该Index指向Code Section。当外部调用某method,可以先在export区找到匹配的method,在通过export的index,找到要执行的字节码,然后装载执行。
5.7)Data Section
该组件描述对线性的memory进行数据初始化,组件记录数据信息以及要初始化的位置信息。
5.8)Memory Section
该组件描述程序内存块数量以及最大、最小值等。
wasm二进制执行流程可以参照:https://github.com/mathetake/gasm.git,学习字节码的解析流程。长安链基于该开源代码进行bug修复,描述在 chainmaker-go/module/vm/gasm/README.md
。