wasm二进制格式简析
二进制格式
参考:《WebAssembly原理与核心技术》
总体示意图
wasm的二进制格式也是以魔数和版本号开头,
之后是模块的主体内容,主要分为12种段,
并给每种段分配了ID(0-11),除自定义段外,
其他段只能出现一次,且必须按照段ID递增顺序出现;
1 | package main |
- 类型段(ID=1)
该段列出wasm模块用到的所有函数类型(函数签名)
上面代码共有三种函数类型
前两个是(int32,int32)->(int32)和()->() - 导入段和导出段(ID=2|ID=7)
该段分别列出模块所有的导入项和导出项,
上面代码使用了fmt.Println(),所以导入段只有一个项目
有一个全局变量和四个函数被导出,所有导出段有五个项目 - 函数段和代码段(ID=3|ID=10)
函数信息被分别存储在两个段中,函数段实际是一个索引表,
列出内部函数所对应的函数签名索引;
代码段存储内部函数的局部变量和字节码;
函数段和代码段中的项目数量必须一致且一一对应; - 表段和元素段(ID=4|ID=9)
表段列出模块内定义的所有表,元素段列出表初始化数据.
wasm规范规定模块最多只能导入或定义一张表,
所以即使模块有表段,里面也只能有一个项目 - 内存段和数据段(ID=5|ID=11)
内存段列出模块内定义的所有内存,数据段列出内存初始化数据;
wasm规范规定模块最多只能导入或定义一块内存,
所以即使模块内有内存段,里面也只能有一个项目 - 全局段(ID=6)
该段列出模块内定义的所有全局变量信息,包括值类型、可变性和初始值
- 起始段(ID=8)
该段给出模块的起始函数索引,起始段只能有一个项目;
起始函数主要有两个作用,一个是模块加载后进行一些初始化操作,
另一个是吧模块变成可执行程序 - 自定义段(ID=0)
该段是给编译器等工具使用的,里面可以存放函数名等调试信息,
或者其他任何附加信息.自定义段不参与wasm语义,所以完全忽略也不影响模块的执行
所有段必须按照ID递增顺序出现,因为很多段之间存在依赖关系,
比如导入段、函数段、代码段等都需要知道函数类型,
所以类型段要在这三个段之前出现.
wasm二进制格式的设计原则之一是可以一遍完成模块的解析、验证和编译(JIT\AOT),
换句话说,wasm实现可以在下载模块的同时进行解码、验证和编译,可以流式处理
索引空间
函数签名、函数、表、内存、全局变量在模块内有各自的索引空间,
局部变量和跳转标签在函数内有各自的索引空间.
- 类型索引
不管是外部导入的函数还是内部定义的函数,
其签名全都存储在类型段中,因此类型段的有效索引范围就是类型索引空间. - 函数索引
函数索引空间由外部函数和内部函数共同构成
- 全局变量索引
由外部全局变量和内部全局变量共同构成.
- 表和内存索引
跟函数索引空间类似,不过只能有一个表和内存,所以有效索引只能为0.
- 局部变量索引
函数的局部变量索引空间由函数的参数和局部变量构成.
- 跳转标签索引
每个函数有自己的跳转索引空间.
实体类型
wasm规范共定义了8种实体类型
- 值类型
4种基本类型: 32位整数(简称i32)、64位整数(简称i64)、32位浮点数(简称f32)和64位浮点数(简称f64)
- 函数类型
就是函数的签名或原型,描述函数的参数数量和类型,以及返回值数量和类型
- 限制类型
描述表的元素数量或者内存页数的上下限.
- 内存类型
描述内存的页数限制,可以定义成限制类型的别名
- 表类型
描述表的元素类型以及元素数量的限制.
- 全局变量类型
描述全局变量的类型和可变性.
- 结果类型
表示函数或表达式的执行结果,在函数类型里已经表示了
- 外部类型
是函数类型、表类型、内存类型和全局变量类型的集合