Wasm规范定义了12种段(从0到12)
由魔数(可用于文件类型的识别)和次版本号开头及其自定义段(ID:0)
wasm-objdump 还原函数结构
xxd 以二进制方式查看
1、类型段(ID:1)
列出Wasm模块使用到的所有函数的参数类型(又叫作函数签名或函数原型)
例如:
const PI float32 = 3.14
func F1(int a){}
func F2(int a){}
func F3(int a){}
func main(){fmt.Println(“hello ,world!”)}
F1、F2、F3为同样的参数类型所以签名数量为1、main为空、fmt.Println参数为string类型 则类型段包含3种函数签名假设排序顺序和源码顺序一致,包含的数据为 (int)->()->(string)
不同的clang版本或编译器对同一源码编译产出体积及其结构都会有所不同。
2、导入段和导出段(ID:2、7)
上面的代码只用了fmt.Println()函数,因此导入段中只有1个项目,有1个全局变量和3个函数被导出(首字母大写)所以导出段包含4个项目
3、函数段和代码段(ID:3、10)
内部函数信息被分开存在这两个段中,其中函数段是一个索引表,列出内部函数对应的类型段的签名索引,函数段和代码段的数量必须一致且一一对应
4、表段和元素段(ID:4、9)
表段列出模块内定义的所有表,用于存储对象引用,目前对象只能是函数,因此可以通过表段实现函数指针的功能。元素段列出表初始化数据。Wasm规范规定模块最多只能导入或定义一张表,所以即使模块有标段也只能有一个项目,表主要和间接调用函数相关。
5、内存段和数据段(ID:5、11)
内存段列出模块内定义的所有内存,数据段列出内存的初始化数据,Wasm规范规定模块最多只能导入或定义一块内存,所以即使模块有内存段也只能有一个项目。
6、全局段(ID:6)
该段列出模块内定义的所有全局变量信息、包括值类型、可变性、和初始值。上面的代码只定了一个全局变量(不可变常量),因此全局段中只有1个项目
7、起始段(ID:8)
该段给出模块内的起始函数索引。起始段只能有一个项目。主要有两个作用,一是在模块加载后进行一些初始化工作;另一个是把模块变成可执行程序。如果模块有起始段,那么Wasm会在加载模块后自动执行起始函数,上面代码有入口函数,因此编译后有起始段。
8、自定义段(ID:0)
该段是给编译器等工具使用,里面可以存放函数名等调试信息,或其他任何附加信息。自定义段不参与Wasm语义,即便是完全忽略自定义段也不影响模块的执行。
除了自定义段,其他段必须按照ID递增的顺序出现,因为很多段之间存在信息的依赖关系。
Wasm设计原则之一是可以一遍完成模块的解析、验证和编译(AOT或JIT编译),可以在下载模块的同时进行解码、验证和编译。可流式处理是wasm的特点之一,二进制模块中各个段的排列方式一定程度上是为了满足这以特点。