ewasm虚拟机开发

准备

解析wasm字节码

wasm概览 wasm指令集 wasm类型 wasm格式

wasm包含12种section: undefined 每个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二进制的解析:

1
2
3
int main() { 
  return 42;
}

对应的wast格式:

1
2
3
4
5
6
7
8
9
(module
 (table 0 anyfunc)
 (memory $0 1)
 (export "memory" (memory $0))
 (export "main" (func $main))
 (func $main (; 0 ;) (result i32)
  (i32.const 42)
 )
)

对应的二进制: 411600756416_.pic.jpg

二进制码:

1
['0x0', '0x61', '0x73', '0x6d', '0x1', '0x0', '0x0', '0x0', '0x1', '0x85', '0x80', '0x80', '0x80', '0x0', '0x1', '0x60', '0x0', '0x1', '0x7f', '0x3', '0x82', '0x80', '0x80', '0x80', '0x0', '0x1', '0x0', '0x4', '0x84', '0x80', '0x80', '0x80', '0x0', '0x1', '0x70', '0x0', '0x0', '0x5', '0x83', '0x80', '0x80', '0x80', '0x0', '0x1', '0x0', '0x1', '0x6', '0x81', '0x80', '0x80', '0x80', '0x0', '0x0', '0x7', '0x91', '0x80', '0x80', '0x80', '0x0', '0x2', '0x6', '0x6d', '0x65', '0x6d', '0x6f', '0x72', '0x79', '0x2', '0x0', '0x4', '0x6d', '0x61', '0x69', '0x6e', '0x0', '0x0', '0xa', '0x8a', '0x80', '0x80', '0x80', '0x0', '0x1', '0x84', '0x80', '0x80', '0x80', '0x0', '0x0', '0x41', '0x2a', '0xb']

一步步分析: 前八个字节表示的东西是固定的。'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解析器

wasmi

可运行wasm的虚拟机

vm

生成可被wasmi执行的合约

pwasm-abi

使用过程宏生成类型solidity合约代码结构的rust合约,并最终编译为wasm-unkonwn-unkonwn(wasm).

最终生成的合约:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#![no_std]
#![allow(non_snake_case)]
#![feature(proc_macro_hygiene)]

extern crate pwasm_std;
extern crate pwasm_ethereum;
extern crate pwasm_abi;
extern crate pwasm_abi_derive;
extern crate alloc;




pub mod array {
    use pwasm_ethereum;
    use pwasm_abi::types::*;
    use pwasm_abi_derive::eth_abi;
    use alloc::collections::BTreeMap;
    use alloc::format;



    #[eth_abi(TokenEndpoint, TokenClient)]
    pub trait ArrayInterface {
        fn constructor(&mut self);
        fn double_array(&mut self,x:U256,y:U256)-> U256;
    }
    
    pub struct ArrayContract;

    impl ArrayInterface for ArrayContract{
        fn double_array(&mut self,x:U256,y:U256) -> U256{
            U256::from(x) + U256::from(x)
        }
        fn constructor(&mut self) {
        
        }        
    }

}

use pwasm_abi::eth::EndpointInterface;

#[no_mangle]
pub fn call() {
    let mut endpoint = array::TokenEndpoint::new(array::ArrayContract{});
    // Read http://solidity.readthedocs.io/en/develop/abi-spec.html#formal-specification-of-the-encoding for details
    pwasm_ethereum::ret(&endpoint.dispatch(&pwasm_ethereum::input()));
}

#[no_mangle]
pub fn deploy() {
    let mut endpoint = array::TokenEndpoint::new(array::ArrayContract{});
    endpoint.dispatch_ctor(&pwasm_ethereum::input());
}

上面的合约通过eth_abi这个过程宏生成了wasm合约. 宏展开的部分代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
fn dispatch(&mut self, payload: &[u8]) -> Vec<u8> {
    let inner = &mut self.inner;
    if payload.len() < 4 {
        ::core::panicking::panic("Invalid abi invoke");
    }
    let method_id = ((payload[0] as u32) << 24)
        + ((payload[1] as u32) << 16)
        + ((payload[2] as u32) << 8)
        + (payload[3] as u32);
    let method_payload = &payload[4..];
    match method_id {
        870935635u32 => {
            if pwasm_ethereum::value() > 0.into() {
                ::core::panicking::panic(
                    "Unable to accept value in non-payable constructor call",
                );
            }
            let mut stream = pwasm_abi::eth::Stream::new(method_payload);
            let result = inner.double_array(
                stream.pop::<U256>().expect("argument decoding failed"),
                stream.pop::<U256>().expect("argument decoding failed"),
            );
            let mut sink = pwasm_abi::eth::Sink::new(1usize);
            sink.push(result);
            sink.finalize_panicking()
        }
        _ => ::core::panicking::panic("Invalid method signature"),
    }
}

fn double_array(&mut self, x: U256, y: U256) -> U256 {
    #![allow(unused_mut)]
    #![allow(unused_variables)]
    let mut payload = Vec::with_capacity(4 + 2usize * 32);
    payload.push((870935635u32 >> 24) as u8);
    payload.push((870935635u32 >> 16) as u8);
    payload.push((870935635u32 >> 8) as u8);
    payload.push(870935635u32 as u8);
    let mut sink = pwasm_abi::eth::Sink::new(2usize);
    sink.push(x);
    sink.push(y);
    sink.drain_to(&mut payload);
    let mut result = [0u8; 32];
    pwasm_ethereum::call(
        self.gas.unwrap_or(200000),
        &self.address,
        self.value.clone().unwrap_or(U256::zero()),
        &payload,
        &mut result[..],
    )
    .expect("Call failed; todo: allow handling inside contracts");
    let mut stream = pwasm_abi::eth::Stream::new(&result);
    stream.pop().expect("failed decode call output")
}

在合约编译的内部实现中通过需要通过eth_abi接口将函数重命名. 这样我们在调用合约时,通过eth_abi编码的数据才能被合约识别运行.

web3eth_abi编码

wasm-build

已生成的wasm合约不能直接被vm运行,wasm-build将由pwasm写的合约再次处理转化为可被vm执行的wasm合约文件.

wasm-build的工作: 1.解析wasm module提取以下section:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
while let Some(section) = sections.pop() {
  match section {
    elements::Section::Type(sect) => { types = Some(sect); }
    elements::Section::Import(sect) => { import = Some(sect); }
    elements::Section::Function(sect) => { funcs = Some(sect); }
    elements::Section::Table(sect) => { table = Some(sect); }
    elements::Section::Memory(sect) => { memory = Some(sect); }
    elements::Section::Global(sect) => { global = Some(sect); }
    elements::Section::Export(sect) => { export = Some(sect); }
    elements::Section::Start(index) => { start = Some(index); }
    elements::Section::Element(sect) => { element = Some(sect); }
    elements::Section::Code(sect) => { code = Some(sect); }
    elements::Section::Data(sect) => { data = Some(sect); }
    _ => {}
  }
}

将wasm的module优化为:

  • Type
  • Import
  • Function
  • Table
  • Memory
  • Global
  • Export
  • Start
  • Code
  • Data
  • other

2.优化函数名及构造函数deploy,call,end等模块.

Licensed under CC BY-NC-SA 4.0
Built with Hugo
主题 StackJimmy 设计