解析wasm二进制文件

[TOC]

文件内容

hello的源文件如下:

int main()


{



  Println("hello world\n");



  return 0;



}

wat文件如下:

(module


 (type $FUNCSIG$i (func (result i32)))



 (type $FUNCSIG$ii (func (param i32) (result i32)))



 (import "env" "Println" (func $Println (param i32) (result i32)))



 (table 0 anyfunc)



 (memory $0 1)



 (data (i32.const 16) "hello world\n\00")



 (export "memory" (memory $0))



 (export "main" (func $main))



 (func $main (; 1 ;) (result i32)



  (drop



   (call $Println



    (i32.const 16)



   )



  )



  (i32.const 0)



 )



)

hello的wasm文件如下:

var wasmCode = new Uint8Array([0,97,115,109,1,0,0,0,1,138,128,128,128,0,2,96,0,1,127,96,1,127,1,127,2,143,128,128,128,0,1,3,101,110,118,7,80,114,105,110,116,108,110,0,1,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,1,10,143,128,128,128,0,1,137,128,128,128,0,0,65,16,16,0,26,65,0,11,11,147,128,128,128,0,1,0,65,16,11,13,104,101,108,108,111,32,119,111,114,108,100,10,0]);

16进制读取结果为:

pct@Chandler:~/go/src/github.com/go-interpreter/wagon/cmd/wasm-run$ hexdump -C hello.wasm
00000000  00 61 73 6d 01 00 00 00  01 8a 80 80 80 00 02 60  |.asm...........`|
00000010  00 01 7f 60 01 7f 01 7f  02 8f 80 80 80 00 01 03  |...`............|
00000020  65 6e 76 07 50 72 69 6e  74 6c 6e 00 01 03 82 80  |env.Println.....|
00000030  80 80 00 01 00 04 84 80  80 80 00 01 70 00 00 05  |............p...|
00000040  83 80 80 80 00 01 00 01  06 81 80 80 80 00 00 07  |................|
00000050  91 80 80 80 00 02 06 6d  65 6d 6f 72 79 02 00 04  |.......memory...|
00000060  6d 61 69 6e 00 01 0a 8f  80 80 80 00 01 89 80 80  |main............|
00000070  80 00 00 41 10 10 00 1a  41 00 0b 0b 92 80 80 80  |...A....A.......|
00000080  00 01 00 41 10 0b 0c 68  65 6c 6c 6f 20 77 6f 72  |...A...hello wor|
00000090  6c 64 00                                          |ld.|
00000093

wasm-dump解析结果如下:

pct@Chandler:~/go/src/github.com/go-interpreter/wagon/cmd/wasm-run$ wasm-dump -s hello.wasm
hello.wasm: module version: 0x1


contents of section type:
0000000e  02 60 00 01 7f 60 01 7f  01 7f                    |.`...`....|


contents of section import:
0000001e  01 03 65 6e 76 07 50 72  69 6e 74 6c 6e 00 01     |..env.Println..|


contents of section function:
00000033  01 00                                             |..|


contents of section table:
0000003b  01 70 00 00                                       |.p..|


contents of section memory:
00000045  01 00 01                                          |...|


contents of section global:
0000004e  00                                                |.|


contents of section export:
00000055  02 06 6d 65 6d 6f 72 79  02 00 04 6d 61 69 6e 00  |..memory...main.|
00000065  01                                                |.|


contents of section code:
0000006c  01 89 80 80 80 00 00 41  10 10 00 1a 41 00 0b     |.......A....A..|


contents of section data:
00000081  01 00 41 10 0b 0c 68 65  6c 6c 6f 20 77 6f 72 6c  |..A...hello worl|
00000091  64 00                                             |d.|

二进制解析

魔数和版本号

下面表格是wasm规格书中定义的code: 首先因为大小端问题,这里的存放和源文件是不同的,wasm的魔数为0x6d736100,版本号为0x01,所以前四个为魔数00 61 73 6d,后四个是版本号01 00 00 00,大小端颠倒了。

Type Section

接下来的数字是01 8a 80 80 80 00,01是type,表示函数签名的Type Section,后面的8a 80 80 80 00是LEB128编码方式表示长度为0a。 也就是:

02 60  00 01 7f 60 01 7f 01 7f

02表示entries数量为2,60表示是个函数的签名,00表示参数为0,那么也就省略了param_types,01表示有一个返回值,7f表示返回值为i32类型,这个是main的函数声明; 同理,60 01 7f 01 7f表示一个带有i32类型并返回i32类型的函数声明,也就是Println的函数声明。

Import Section

再往下是02,表示Import Section, 长度为15, 02 8f 80 80 80 00,内容为:

01 03 65 6e 76 07 50 72  69 6e 74 6c 6e 00 01

01则只有一个引用,module长度为3,名称为env,field长度为7,名称为Println,最后的kind表示引入的类型,00表示引入的是个函数,01则表示函数的索引。

Function Section

然后是03,Function Section, 长度为82 80 80 80 00,内容为:

01 00

01表示签名索引数量为1,00表示在type区中的索引值为0,即main函数的签名。

Table Section

接下来是04, Table Section, 长度为84 80 80 80 00 ,内容为:

01 70 00 00

01表示数量为1,类型为70,目前只能是70,表示anyfunc。

Memory Section

05-Memory Section, 长度为83 80 80 80 00,内容为:

01 00 01

Global Section

06-Global Section, 长度为81 80 80 80 00,内容为:

00

Export Section

07-Export Section, 长度为91 80 80 80 00,内容为:

02 06 6d  65 6d 6f 72 79 02 00 04 6d 61 69 6e 00 01

02表示export数量为2,06表示第一个entry长度,名称为memory,02表示类型为memory类型,index为00,表示在对应索引空间的索引值; 04表示第二个entry长度为4,名称为main,类型为00,function类型,索引为01

Code Section

下面就是Code Section了,中间的Start Section, Element Section在这个模块中没有体现: 0a-Code Section,长度为8f 80 80 80 00,内容为:

01 89 80 80 80 00 00 41  10 10 00 1a 41 00 0b

01表示数量为1,89 80 80 80 00表示size为9,00是locals数量,为0则locals就没有,41 10 10 00 1a 41 00是code,0b是end。

Data Section

0b-Data Section, 长度为92 80 80 80 00,内容为:

 01 00 41 10 0b 0c 68 65  6c 6c 6f 20 77 6f 72 6c  64 00

01表示数量为1,00表示在线性内存中的索引为0,41表示iConst.32, 10表示call操作码, 0b表示offset,0c表示参数长度为12,68 65 6c 6c 6f 20 77 6f 72 6c 64 00表示我们打印的内容为hello world 至此,文件解析完毕。

最后更新于