准备
解析wasm字节码
wasm包含12种section: 每个section包括:
- section id(大小1字节)
- section size(格式为u32记录section大小)
- data(section中保存的内容,格式取决于section id)
每个 section 都是可选的, 如果某个 wasm 模块省略了某个 section, 则等同于存在一个内容为空的 section.
https://wasdk.github.io/WasmFiddle/?这个网站可以编译c/c++为wasm格式。 用一个简单的例子来说明对wasm二进制的解析:
|
|
对应的wast格式:
|
|
对应的二进制:
二进制码:
|
|
一步步分析:
前八个字节表示的东西是固定的。'0x0', '0x61', '0x73', '0x6d'
前四个字节表示\0asm
.'0x1', '0x0', '0x0', '0x0'
表示为1的版本号。
wasm采用LEB128编码: 谷歌为了省点空间,在安卓中用了这个压缩标准,其原理就是一个4字节32位的数字,一般很少占满,比如数字1,2等,它其实用几位就可以表示。下面看看它的工作原理: 1)忽略高字节的正负代表位。 2)每七位组成一组,待编码。 3)有符号和无符号的分成不同的情况来处理。 4)无符号,最高位为0表示结束,1表示还有后续字节。待编码的七位为数据。 5)有符号的处理,同上,但需要处理高位为0的不同情况。 6)有符号的处理,同上,需要处理高位为1的不同情况。 从上面可以看出无符号比较简单,有符号还需要处理几种情况,这里举一个无符号的例子,简单理解一下,更多请查阅相关资料: 编码: 十六进制数字:0x238 ========> 二进制 0000 0010 0011 1000 分成两个七位的组:0000100(高位), 0111000(低位) 0000100后面没数据了,所以前面加一个0;0111000后面还有一个字节,所以高为为1,则变为: 00000100(即:0x04),10111000(0x70)
则小端存放的数据为:0x70,0x04 解码: 正好相反,把数据拆开,按编码逆向过程即可。
对于rust,我们可以直接用现有的crate:parity-wasm对wasm进行解析。
wasm解析器
可运行wasm的虚拟机
生成可被wasmi执行的合约
pwasm-abi
使用过程宏生成类型solidity合约代码结构的rust合约,并最终编译为wasm-unkonwn-unkonwn(wasm).
最终生成的合约:
|
|
上面的合约通过eth_abi
这个过程宏生成了wasm合约.
宏展开的部分代码如下:
|
|
在合约编译的内部实现中通过需要通过eth_abi
接口将函数重命名.
这样我们在调用合约时,通过eth_abi
编码的数据才能被合约识别运行.
wasm-build
已生成的wasm合约不能直接被vm运行,wasm-build将由pwasm写的合约再次处理转化为可被vm执行的wasm合约文件.
wasm-build的工作: 1.解析wasm module提取以下section:
|
|
将wasm的module优化为:
- Type
- Import
- Function
- Table
- Memory
- Global
- Export
- Start
- Code
- Data
- other
2.优化函数名及构造函数deploy,call,end等模块.