位元格式

大家也許會很好奇,用 wat2wasm 把文字轉成 wasm 檔的時候,跑出一大堆數字的部份是代表什麼意思?其實那就是轉換之後檔案裡實際的內容,也就是我們接下來要介紹的位元格式

LEB128 (Little-Endian Base 128)

雖然整數通常會有固定長度,像是 8, 16, 32, 64 bits,但是以 32 bits 來說,實際上常常不需要用到 32 bits 來表示。為了精減大小,WebAssembly 在位元格式上常常會使用 LEB128 格式來表示整數。

解碼

每個 byte 只用 7 bits 表示數值,剩下的 1 bit 表示是否結束

如果長度不夠,照有號/無號整數的方式補足長度,得到有號/無號整數的數值

編碼

  • 負數

  • 正數

以 32 位元整數來說,最多用到 5 個 byte,如果全都是 1 開頭的話表示錯誤;64 位元整數最多 10 個,16 位元最多 3 個,也是用這種方法檢查錯誤

雖然在數值接近最大值的時候,是用更多位元表示相同的數,不過大部分的狀況數值不會那麼大,所以用 LEB128 可以有效的降低檔案大小

數值型別的位元格式

型別

數值

i32

0x7f

i64

0x7e

f32

0x7d

f64

0x7c

模組的位元格式

接下來講解模組中各部份的位元格式,大家可以對照 wat2wasm 的輸出結果做印證

前文 (Preamble)

這部份固定佔 8 bytes,大部分的位元格式中幾乎都會有前文,用來區別不同的位元格式

  • 魔術數字 (Magic number) 4 bytes

    • 0x00 0x61 0x73 0x6d,用字碼轉換成文字之後就是 "\0asm",表示這是 wasm 格式

  • 版本 (Version) 4 bytes

    • 0x01 0x00 0x00 0x00,表示這是 0x01 版的 WebAssembly,在之前也有 0x0a, 0x0b, 0x0c, 0x0d的先行版本,之後如果推出新版的 WebAssembly,這邊就會變動

各部份標頭 (Section header)

接下來會分成許多部份 (Section),對照到在 模組 章節一般格式的各個部份。每個部份都不是必須的,沒有用到的話就不會產生

每個部份在依開始都會有個標頭 (header),標示各個部份的代碼還有長度

  • 代碼 (Section code)

    用來區分是哪個部份

英文名稱

代碼

Type

1

Import

2

Function

3

Table

4

Memory

5

Global

6

Export

7

Start

8

Element

9

Code

10

Data

11

  • 長度 (Section size)

    用一個 LEB128 32 位元無號整數,表示整個部份的長度 (不包括標頭)

    在 wat2wasm 的輸出裡會看到這邊是 section size (guess),既然是guess,表示這的值是亂猜的,不用理會,也不會被編進檔案裡

    真正的值在下面 FIXUP section size 的地方,在編進檔案的時候也是這個真正值會被寫到section size (guess)的位置上

函式型別 (Type section)

  • 數量 (num types)

    • 用一個 LEB128 32 位元無號整數,表示總共有多少函式型別

  • 函式型別

欄位

數值或格式

補充說明

格式

0x60 (func)

表示這是函數

參數總數

LEB128 32 位元無號整數

參數型別

數值型別格式

可能沒有,也可能有多個

回傳值總數

LEB128 32 位元無號整數

目前只會是 0 或 1

回傳值型別

數值型別格式

0 ~ 1 個

引入 (Import section)

  • 數量 (num imports)

    • 用一個 LEB128 32 位元無號整數,表示總共有多少引入物件

  • 引入物件:

欄位

數值或格式

補充說明

字串長度

LEB128 32 位元無號整數

模組名稱的字串長度

模組名稱

字串

以字碼表示

字串長度

LEB128 32 位元無號整數

輸出名稱的字串長度

輸出名稱

字串

以字碼表示

引入種類

8 位元無號整數

0: 函式, 1:函式表, 2:記憶體, 3:全域變數

在引入物件中,接下來的部份會依據不同的引入種類,而有不同的格式:

  • 函式

    • 一個 LEB128 32 位元無號整數,表示函式型別的編號

  • 函式表

欄位

數值或格式

補充說明

元素型別

0x70 (anyfunc)

目前函式表只支援這個型別

標記

0 或 1

0:沒有最大值, 1:有最大值

起始大小

LEB128 32 位元無號整數

最大值

LEB128 32 位元無號整數

有最大值才有

  • 記憶體

欄位

數值或格式

補充說明

標記

0 或 1

0:沒有最大值, 1:有最大值

起始大小

LEB128 32 位元無號整數

以 page 為單位

最大值

LEB128 32 位元無號整數

有最大值才有

  • 全域變數

欄位

數值或格式

補充說明

數值型別

數值型別格式

可變動性

0 或 1

0:不可變動, 1:可變動

函式 (Function section)

  • 數量 (num functions)

    • 用一個 LEB128 32 位元無號整數,表示總共有多少函式

  • 編號

    • 一個或多個 LEB128 32 位元無號整數,表示該函式使用的函式型別編號

函式表 (Table section)

  • 數量 (num tables)

    • 用一個 LEB128 32 位元無號整數,表示總共有多少函式表 (目前只會是 1)

  • 函式表

欄位

數值或格式

補充說明

元素型別

0x70 (anyfunc)

目前函式表只支援這個型別

標記

0 或 1

0:沒有最大值, 1:有最大值

起始大小

LEB128 32 位元無號整數

最大值

LEB128 32 位元無號整數

有最大值才有

記憶體 (Memory section)

  • 數量 (num memories)

    • 用一個 LEB128 32 位元無號整數,表示總共有多少記憶體 (目前只會是 1)

  • 記憶體

欄位

數值或格式

補充說明

標記

0 或 1

0:沒有最大值, 1:有最大值

起始大小

LEB128 32 位元無號整數

以 page 為單位

最大值

LEB128 32 位元無號整數

有最大值才有

全域變數 (Global section)

  • 數量 (num globals)

    • 用一個 LEB128 32 位元無號整數,表示總共有多少全域變數

  • 全域變數

欄位

數值或格式

補充說明

數值型別

數值型別格式

可變動性

0 或 1

0:不可變動, 1:可變動

起始值指令

指令

請參考下面"指令"小節,只能是常數指令

結束

byte

用一個 0x0b (end) 指令表示結束

輸出 (Export section)

  • 數量 (num exports)

    • 用一個 LEB128 32 位元無號整數,表示總共有多少全域變數

  • 輸出物件

欄位

數值或格式

補充說明

長度

LEB128 32 位元無號整數

輸出名稱長度

輸出名稱

字串

以字碼表示

輸出種類

8 位元無號整數

0: 函式, 1:函式表, 2:記憶體, 3:全域變數

位址

LEB128 32 位元無號整數

在儲存空間中輸出物件的位址 (Ex: 種類是函式,表示在儲存空間中的第幾號函式)

起始函式 (Start section)

  • 索引值 (index)

    • 一個 LEB128 32 位元無號整數,表示起始函式在模組中的編號

函式表元素 (Element section)

  • 數量 (num elements)

    • 用一個 LEB128 32 位元無號整數,表示總共有多少函式表元素

  • 函式表元素

    • 會根據這邊的資料,指定函式表的值

欄位

數值或格式

補充說明

索引值

LEB128 32 位元無號整數

函式表的編號(目前只會是 0)

位移

指令

請參考下面"指令"小節,只能是常數指令

元素數量

LEB128 32 位元無號整數

表示有幾個元素要指定

元素

LEB128 32 位元無號整數

0\ge 0 個模組中函式的編號

程式碼 (Code section)

程式碼部份表示函式的內容

  • 數量 (num functions)

    • 用一個 LEB128 32 位元無號整數,表示總共有多少函式內容

  • 函式內容

欄位

數值或格式

補充說明

長度

LEB128 32 位元無號整數

函式內容的總長度

區域變數數量

LEB128 32 位元無號整數

區域變數的總數

區域變數

區域變數

0\ge 0 個區域變數,請參考下面"區域變數"格式

函式內容

byte

0\ge 0 個指令,請參考下面"指令"小節

結束

byte

一個 0x0b (end) 指令表示結束

  • 區域變數 (在函式內容裏面)

    相同種類且接連在一起宣告的區域變數,會合併成一個區域變數格式,所以才會有"數量"欄位

欄位

數值或格式

補充說明

數量

LEB128 32 位元無號整數

表示要宣告幾個區域變數

數值型別

數值型別格式

資料 (Data section)

資料部份表示記憶體的初始資料

  • 數量 (num data)

    • 用一個 LEB128 32 位元無號整數,表示總共有多少資料指定物件

  • 資料指定物件

欄位

數值或格式

補充說明

索引值

LEB128 32 位元無號整數

記憶體的編號(目前只會是 0)

位移

指令

請參考下面"指令"小節,只能是常數指令

長度

LEB128 32 位元無號整數

表示要指定的資料總長度

資料

byte

0\ge 0 個資料內容的 byte

指令

指令的部份大部份是一個 byte 編碼,有些指令會加上額外的參數

控制指令

指令

byte 編碼

額外參數

unreachable

0x00

nop

0x01

block

0x02

一個數值型別表示回傳值,或 0x40 表示沒回傳值

loop

0x03

一個數值型別表示回傳值,或 0x40 表示沒回傳值

if

0x04

一個數值型別表示回傳值,或 0x40 表示沒回傳值

else

0x05

end

0x0b

br

0x0c

跳回層數 : LEB128 32 位元無號整數

br_if

0x0d

跳回層數 : LEB128 32 位元無號整數

br_table

0x0e

請見下面 "br_table 額外參數"

return

0x0f

call

0x10

函式編號 : LEB128 32 位元無號整數

call_indirect

0x11

函式型別編號 : LEB128 32 位元無號整數 ;保留值 : 0x00

  • br_table 額外參數

    • 跳回層數數量 : 一個 LEB128 32 位元無號整數

    • 跳回層數 : 1\ge 1 個 LEB128 32 位元無號整數

參數指令

指令

byte 編碼

額外參數

drop

0x1a

select

0x1b

get_local

0x20

區域變數編號 : LEB128 32 位元無號整數

set_local

0x21

區域變數編號 : LEB128 32 位元無號整數

tee_local

0x22

區域變數編號 : LEB128 32 位元無號整數

get_global

0x23

全域變數編號 : LEB128 32 位元無號整數

set_global

0x24

全域變數編號 : LEB128 32 位元無號整數

記憶體指令

指令

byte 編碼

額外參數

i32.load

0x28

請見下面"記憶體額外參數"

i64.load

0x29

請見下面"記憶體額外參數"

f32.load

0x2a

請見下面"記憶體額外參數"

f64.load

0x2b

請見下面"記憶體額外參數"

i32.load8_s

0x2c

請見下面"記憶體額外參數"

i32.load8_u

0x2d

請見下面"記憶體額外參數"

i32.load16_s

0x2e

請見下面"記憶體額外參數"

i32.load16_u

0x2f

請見下面"記憶體額外參數"

i64.load8_s

0x30

請見下面"記憶體額外參數"

i64.load8_u

0x31

請見下面"記憶體額外參數"

i64.load16_s

0x32

請見下面"記憶體額外參數"

i64.load16_u

0x33

請見下面"記憶體額外參數"

i64.load32_s

0x34

請見下面"記憶體額外參數"

i64.load32_u

0x35

請見下面"記憶體額外參數"

i32.store

0x36

請見下面"記憶體額外參數"

i64.store

0x37

請見下面"記憶體額外參數"

f32.store

0x38

請見下面"記憶體額外參數"

f64.store

0x39

請見下面"記憶體額外參數"

i32.store8

0x3a

請見下面"記憶體額外參數"

i32.store16

0x3b

請見下面"記憶體額外參數"

i64.store8

0x3c

請見下面"記憶體額外參數"

i64.store16

0x3d

請見下面"記憶體額外參數"

i64.store32

0x3e

請見下面"記憶體額外參數"

current_memory

0x3f

保留值 : 0x00

grow_memory

0x40

保留值 : 0x00

  • 記憶體額外參數

    • 對齊 (alignment) : 一個 LEB128 32 位元無號整數

      (WasmVM 不會用到這個值)

    • 位移 (offset) : 一個 LEB128 32 位元無號整數

常數指令

指令

byte 編碼

額外參數

i32.const

0x41

數值 : LEB128 32 位元號整數

i64.const

0x42

數值 : LEB128 64 位元號整數

f32.const

0x43

數值 : 4 個 byte 表示單精度浮點數

f64.const

0x44

數值 : 8 個 byte 表示雙精度浮點數

算術指令

指令

byte 編碼

額外參數

i32.eqz

0x45

i32.eq

0x46

i32.ne

0x47

i32.lt_s

0x48

i32.lt_u

0x49

i32.gt_s

0x4a

i32.gt_u

0x4b

i32.le_s

0x4c

i32.le_u

0x4d

i32.ge_s

0x4e

i32.ge_u

0x4f

i64.eqz

0x50

i64.eq

0x51

i64.ne

0x52

i64.lt_s

0x53

i64.lt_u

0x54

i64.gt_s

0x55

i64.gt_u

0x56

i64.le_s

0x57

i64.le_u

0x58

i64.ge_s

0x59

i64.ge_u

0x5a

f32.eq

0x5b

f32.ne

0x5c

f32.lt

0x5d

f32.gt

0x5e

f32.le

0x5f

f32.ge

0x60

f64.eq

0x61

f64.ne

0x62

f64.lt

0x63

f64.gt

0x64

f64.le

0x65

f64.ge

0x66

i32.clz

0x67

i32.ctz

0x68

i32.popcnt

0x69

i32.add

0x6a

i32.sub

0x6b

i32.mul

0x6c

i32.div_s

0x6d

i32.div_u

0x6e

i32.rem_s

0x6f

i32.rem_u

0x70

i32.and

0x71

i32.or

0x72

i32.xor

0x73

i32.shl

0x74

i32.shr_s

0x75

i32.shr_u

0x76

i32.rotl

0x77

i32.rotr

0x78

i64.clz

0x79

i64.ctz

0x7a

i64.popcnt

0x7b

i64.add

0x7c

i64.sub

0x7d

i64.mul

0x7e

i64.div_s

0x7f

i64.div_u

0x80

i64.rem_s

0x81

i64.rem_u

0x82

i64.and

0x83

i64.or

0x84

i64.xor

0x85

i64.shl

0x86

i64.shr_s

0x87

i64.shr_u

0x88

i64.rotl

0x89

i64.rotr

0x8a

f32.abs

0x8b

f32.neg

0x8c

f32.ceil

0x8d

f32.floor

0x8e

f32.trunc

0x8f

f32.nearest

0x90

f32.sqrt

0x91

f32.add

0x92

f32.sub

0x93

f32.mul

0x94

f32.div

0x95

f32.min

0x96

f32.max

0x97

f32.copysign

0x98

f64.abs

0x99

f64.neg

0x9a

f64.ceil

0x9b

f64.floor

0x9c

f64.trunc

0x9d

f64.nearest

0x9e

f64.sqrt

0x9f

f64.add

0xa0

f64.sub

0xa1

f64.mul

0xa2

f64.div

0xa3

f64.min

0xa4

f64.max

0xa5

f64.copysign

0xa6

i32.wrap/i64

0xa7

i32.trunc_s/f32

0xa8

i32.trunc_u/f32

0xa9

i32.trunc_s/f64

0xaa

i32.trunc_u/f64

0xab

i64.extend_s/i32

0xac

i64.extend_u/i32

0xad

i64.trunc_s/f32

0xae

i64.trunc_u/f32

0xaf

i64.trunc_s/f64

0xb0

i64.trunc_u/f64

0xb1

f32.convert_s/i32

0xb2

f32.convert_u/i32

0xb3

f32.convert_s/i64

0xb4

f32.convert_u/i64

0xb5

f32.demote/f64

0xb6

f64.convert_s/i32

0xb7

f64.convert_u/i32

0xb8

f64.convert_s/i64

0xb9

f64.convert_u/i64

0xba

f64.promote/f32

0xbb

i32.reinterpret/f32

0xbc

i64.reinterpret/f64

0xbd

f32.reinterpret/i32

0xbe

f64.reinterpret/i64

0xbf