位元格式
Last updated
Last updated
大家也許會很好奇,用 wat2wasm 把文字轉成 wasm 檔的時候,跑出一大堆數字的部份是代表什麼意思?其實那就是轉換之後檔案裡實際的內容,也就是我們接下來要介紹的位元格式
雖然整數通常會有固定長度,像是 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 的輸出結果做印證
這部份固定佔 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 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)的位置上
數量 (num types)
用一個 LEB128 32 位元無號整數,表示總共有多少函式型別
函式型別
欄位
數值或格式
補充說明
格式
0x60 (func)
表示這是函數
參數總數
LEB128 32 位元無號整數
參數型別
數值型別格式
可能沒有,也可能有多個
回傳值總數
LEB128 32 位元無號整數
目前只會是 0 或 1
回傳值型別
數值型別格式
0 ~ 1 個
數量 (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:可變動
數量 (num functions)
用一個 LEB128 32 位元無號整數,表示總共有多少函式
編號
一個或多個 LEB128 32 位元無號整數,表示該函式使用的函式型別編號
數量 (num tables)
用一個 LEB128 32 位元無號整數,表示總共有多少函式表 (目前只會是 1)
函式表
欄位
數值或格式
補充說明
元素型別
0x70 (anyfunc)
目前函式表只支援這個型別
標記
0 或 1
0:沒有最大值, 1:有最大值
起始大小
LEB128 32 位元無號整數
最大值
LEB128 32 位元無號整數
有最大值才有
數量 (num memories)
用一個 LEB128 32 位元無號整數,表示總共有多少記憶體 (目前只會是 1)
記憶體
欄位
數值或格式
補充說明
標記
0 或 1
0:沒有最大值, 1:有最大值
起始大小
LEB128 32 位元無號整數
以 page 為單位
最大值
LEB128 32 位元無號整數
有最大值才有
數量 (num globals)
用一個 LEB128 32 位元無號整數,表示總共有多少全域變數
全域變數
欄位
數值或格式
補充說明
數值型別
數值型別格式
可變動性
0 或 1
0:不可變動, 1:可變動
起始值指令
指令
請參考下面"指令"小節,只能是常數指令
結束
byte
用一個 0x0b (end) 指令表示結束
數量 (num exports)
用一個 LEB128 32 位元無號整數,表示總共有多少全域變數
輸出物件
欄位
數值或格式
補充說明
長度
LEB128 32 位元無號整數
輸出名稱長度
輸出名稱
字串
以字碼表示
輸出種類
8 位元無號整數
0: 函式, 1:函式表, 2:記憶體, 3:全域變數
位址
LEB128 32 位元無號整數
在儲存空間中輸出物件的位址 (Ex: 種類是函式,表示在儲存空間中的第幾號函式)
索引值 (index)
一個 LEB128 32 位元無號整數,表示起始函式在模組中的編號
數量 (num elements)
用一個 LEB128 32 位元無號整數,表示總共有多少函式表元素
函式表元素
會根據這邊的資料,指定函式表的值
欄位
數值或格式
補充說明
索引值
LEB128 32 位元無號整數
函式表的編號(目前只會是 0)
位移
指令
請參考下面"指令"小節,只能是常數指令
元素數量
LEB128 32 位元無號整數
表示有幾個元素要指定
元素
LEB128 32 位元無號整數
程式碼部份表示函式的內容
數量 (num functions)
用一個 LEB128 32 位元無號整數,表示總共有多少函式內容
函式內容
欄位
數值或格式
補充說明
長度
LEB128 32 位元無號整數
函式內容的總長度
區域變數數量
LEB128 32 位元無號整數
區域變數的總數
區域變數
區域變數
函式內容
byte
結束
byte
一個 0x0b (end) 指令表示結束
區域變數 (在函式內容裏面)
相同種類且接連在一起宣告的區域變數,會合併成一個區域變數格式,所以才會有"數量"欄位
欄位
數值或格式
補充說明
數量
LEB128 32 位元無號整數
表示要宣告幾個區域變數
數值型別
數值型別格式
資料部份表示記憶體的初始資料
數量 (num data)
用一個 LEB128 32 位元無號整數,表示總共有多少資料指定物件
資料指定物件
欄位
數值或格式
補充說明
索引值
LEB128 32 位元無號整數
記憶體的編號(目前只會是 0)
位移
指令
請參考下面"指令"小節,只能是常數指令
長度
LEB128 32 位元無號整數
表示要指定的資料總長度
資料
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 位元無號整數
跳回層數 : 個 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
個模組中函式的編號
個區域變數,請參考下面"區域變數"格式
個指令,請參考下面"指令"小節
個資料內容的 byte