作者:黑白大彩电,软件工程专业,多年C++开发经验,在Annchain团队负责Websocket、RPC以及WebAssembly方面的研发工作。
WebAssembly按照字面意思就是Web世界(浏览器)里的Assembly(汇编语言),也就是浏览器支持的一种底层的语言,它的指令集确实类似汇编指令,虽然事实上并不完全是,但还是有些接近。严格来说,WebAssembly是一种浏览器支持的二进制文件格式标准,该文件格式定义了基于堆栈运行的图灵完备的指令集所组成的二进制代码段,以及为了运行指定的代码(函数)所需要的相关环境,比如:数据类型、函数签名、数据区、内存区、表区、开始区、代码区等。
WebAssembly文件布局

如图一所示,WebAssembly文件(称之为模块)总体上由三块组成:魔数(固定值:0x6d736100,小端存储)、版本号(当前值:0x00000001,小端存储)、以及若干个区(section)。其中,每个区由三部分组成:类型、长度以及实际数据。图中的varuint32表示使用leb128编码方式。因为WebAssembly是在网络上传输的,所以该格式为了尽量缩小文件体积,在内部广泛使用了数据压缩格式leb128,leb128是变长的整数压缩编码形式。因为在很多情况下,比如一个32位的整数在大部分时间存储的值只需要一个8位的字节存储即可,但是将来又有可能需要32位,因此可以采用采用leb128编码。leb128采用的方式是从低字节开始,每个字节中的最高位不是存储实际的值,而是用来作为标志位,当该位置位时,表示下一字节还有数据,否则表示下一字节没有数据,没有数据的字节直接省略,从而达到了数据压缩的目的。
模块里定义的各个区根据不同的类型,它们的实际数据有着不同的定义结构。目前支持的区类型范围是整数1到11这11种。如图二所示:

在详细介绍每个区结构之前,先来了解下基础知识点:
[if !supportLists]1. [endif]数据类型,WebAssembly只有四种数据类型i32、i64、f32、f64。分别表示32位整数、64位整数、32位浮点数、64位浮点数。
[if !supportLists]2. [endif]函数签名,WebAssembly的函数签名是由各个参数类型外加返回值类型组成,形如:(param1_type, param2_type [,...]) result_type。当前版本的函数只支持一个返回值。
[if !supportLists]3. [endif]索引空间,WebAssembly模块中的函数、内存、表、全局变量都各自有自己的索引空间,用于记录各自的顺序,方便相关指令引用访问。
有了以上三点基础知识之后,下面分别介绍每个区的结构
类型区(type值为1):

如图三所示,类型区主要包含整体结构是一个由多个函数签名组成的数组,以及一个数组大小(在数组之前)组成。在每个函数签名结构里,第一个字段是一个固定值0x60,紧接着是函数的参数类型以及返回值类型。参数类型由参数个数以及对应的多个参数类型组成;返回值类型由返回值个数(当前为1)以及返回值类型组成。
比如一个C函数int add(int a, int b)生成的签名是这样:0x60 2 i32 i32 1 i32,表示有两个参数,依次是i32 i32类型,一个返回值,类型是i32。
导入区(type值为2)

导入区定义的是本模块从其它模块中导入的函数、表、内存、全局变量等,如图四所示。导入区整体也是一个数组外加数组的大小,数组中的每个元素表示的是一个导入信息,包括:导入的模块名称(UTF8字符)、导入的字段名称(UTF8字符)、导入类型(0-3)以及和导入类型相关的描述信息。其中,导入类型0表示函数,后面紧跟的是该函数的函数签名在类型区所在数组中的索引;导入类型1表示表,后面紧跟的是表中元素类型以及表的初始容量和最大容量信息;导入类型2表示导入的是内存,后面紧跟的是内存的初始容量和最大容量,内存的单位是页,每页大小是65536字节;导入类型3表示全局变量,后面紧跟着的是全局标量的类型以及是否只读。
导出区(type值为7)

导出区定义的是本模块中导出的函数、表、内存、全局变量。只有导出的内容才能被其它模块导入,它的整体结构也是一个数组结构以及数组长度。每个数组元素定义的是单独的一个导出信息,包括导出字段名、导出类型以及和导出类型所对应的指向其索引空间中的索引值。导出类型和导入区中的导入类型一致。
全局区(type值为6)

全局区定义了模块中的全局变量,以及属性信息,比如是否只读。它的结构也是一个数组以及数组大小,如图六所示。每个数组元素定义了全局变量的如下信息:类型、是否只读以及用于初始化全局变量的表达式。
开始区(type值为8)

开始区结构很简单,只有一个字段,指向本模块函数索引空间的索引值。当模块加载完成后,如果存在开始区,那么会首先调用开始区指向的函数。
函数区(type值为3)
函数区定义了本模块中所有函数的函数签名的索引数组以及该数组大小,如图八所示。

函数区中每个元素分别和如下介绍的代码区中的每个函数代码一一对应。而本元素中的内容是指向类型区中函数签名数组中某个签名的索引。
代码区(type值为10)
代码区定义了模块中一个或者多个由指令集组成的函数定义,它分别和函数区中的函数签名一一对应。如图九所示:

它由多个函数体组成的数组以及数组大小构成,每个函数体又分别包括:函数体大小(本函数体的字节数)、局部变量、代码。在WebAssembly中,局部变量包括函数的参数以及函数内定义的局部变量,代码区里的局部变量不包括函数参数部分。代码部分是由具体的指令集中的指令组成来完成该函数要实现的功能。
表区(type值为4)
表区是WebAssembly模块里比较令人迷糊的东西,它实际上存放的是本模块中定义的函数的索引的列表,用于在运行时调用其指向的函数。类似于C语言中的函数指针,之所以不把它放到内存区,是基于安全性考虑,它的结构如图十所示。图中的resizable_limits包含初始大小以及最大大小字段。其中每个元素定义了一个表。

每个元素由两部分构成:类型(目前只支持anyfunc,表示任意的函数类型)、大小信息(包括初始大小、最大大小)。
元素区(type值为9)
表区定义了表的类型及大小,而元素区用于填充表区。元素区本身也是一个数组结构,里面的每个元素定义了需要填充的表索引、表中的偏移值、指向的函数索引。如图十一所示:

正如元素区用于填充表区一样,下面介绍的内存区和数据区也是这种关系,数据区用于填充内存区
内存区(type值为5)
内存区定义了模块中运行时所需要的内存空间,在模块初始化的时候由数据区进行填充。该区像表区一样,定义了内存区的大小,但是没有类型,因为类型是字节型。该区每个元素定义了一块内存,如图十二所示:

数据区(type值为11)
数据区像元素区一样,定义了如何填充内存区的结构,它本身也是一个数组,每个元素填充一段内存。如图十三所示:

Annchain是由众安科技、众安-复旦区块链与信息安全联合实验室研发, 支持智能合约的高性能通用区块链协议。作为工信部指导的中国区块链技术和产业发展论坛两大开源项目之一,Annchain立足于中国,致力于助力中国抢占区块链全球话语权。Annchain专注于解决扩展性、去中心化、安全三者平衡问题,做通用的分布式应用计算平台。目前已在数十家生态伙伴的商业场景中落地,场景涵盖农业溯源、珠宝溯源、资产通证化、公益、数据开放计算、广告分发平台和供应链金融等,开发者可基于Annchain高效构建适合自身的区块链应用。
Annchain当前包含两个子项目Annchain.Genesis和Annchain.OG。
Annchain.Genesis作为基于链表式结构的第一代区块链基础协议,其主要特征是模块化、高可靠、易用,支持多种智能合约引擎,开发者可根据具体业务需求选用共识与合约引擎,达到企业级使用需求。
Annchain.OG作为基于DAG账本结构的第二代区块链基础协议,其拥有DAG高效,强扩展特点的同时,又通过基于贡献的多维共识解决其安全可靠问题,同时Annchain.OG支持多种智能合约引擎及计算引擎,具有了更强计算和存储能力。
众安科技:
众安信息技术服务有限公司(简称“众安科技”)成立于2016年11月2日,是由蚂蚁金服、腾讯、中国平安发起设立的众安保险旗下的全资科技子公司,专注于区块链、人工智能、密码学、物联网等前沿技术研究。众安科技汇聚了行业内的顶尖科研人才,其中区块链团队规模近200人,多数来自于各顶尖区块链团队。
众安-复旦区块链与信息安全联合实验室:
众安-复旦区块链与信息安全联合实验室是国内首个高校与企业联合组建的区块链与信息安全实验室,专注于区块链核心技术研究,探索应用落地。
上海市区块链工程技术研究中心:
上海市区块链工程技术研究中心由科委主导,依托复旦大学,联合众安在线财产保险股份有限公司,上海众人信息技术有限公司共同设立。专注分布式账本前沿共性技术与成果转化研究,推动工程落地。
更多关于annchain
www.annchain.io
Github: github.com/annchain
Twitter: https://twitter.com/Annchain007
Facebook: https://www.facebook.com/Annchain-295110641341258
TelegramGroup: t.me/Annchain
TelegramChannel: t.me/AnnchainChannel
扫码备注昵称+研发方向
加入annchain技术社群

网友评论