位元格式
大家也許會很好奇,用 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
請見下面"記憶體額外參數"
  • 記憶體額外參數
    • 對齊 (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