wasm 添加 gas 計算

本篇文章不贅述如何解析 wasm 指令, 如何執(zhí)行 webassembly


在解析 wasm 的過程中, 添加 addgas 指令, 具體由 imported 函數(shù) addgas() 來實現(xiàn)

addgas 指令需要添加在 函數(shù)段(以下用block代替)的最一開始.
block , 指的是函數(shù)分支, 如下:

//block1
int a = 3;
if (a > 5) {
//block2
    a++;
}else{
//block3
    a--;
    return;
}

以上我們給這個函數(shù)分了 3 個block, 運行時 可能會覆蓋 block1, block2; 也可能覆蓋 block1, block3;
所以 以這樣的分支結(jié)構(gòu), 來劃分block, 可以精確的計算代碼執(zhí)行消耗.

難點在于如何對 block 分區(qū). 在嘗試了幾種代碼流程控制的命令后(如 if/else, switch, loop, continue等), 在編譯成 wasm 后, 大多會轉(zhuǎn)變成 br_if loop end 等, 后面有發(fā)現(xiàn)新的再添加. 對這些指令進行處理.

函數(shù)開始時, 向 stack 中放入元素 0. 該元素即代表最外層代碼塊的 gas 消耗. stack 中第 n 個元素,代表該函數(shù)第 n 層代碼塊. 由于 br_if 沒有匹配的 end 指令, 即遇到 下一個 br_if 或者 return 時, 代表該 block 結(jié)束, 并即使添加(計算) gas 消耗數(shù)量.

  • 碰到 (br_if return), pop_stack => top, addgas(top) , push_stack 0
  • 碰到 (block, loop), push_stack 0
  • 碰到 end , pop_stack
  • code 讀取結(jié)束 pop_stack => top, addgas(top)
  • 其余指令 stack[top] = stack[top] + 1

操作實例如下


(module
  (type $type0 (func (param i32)))
  (type $type1 (func (param i32) (result i32)))
  (import "env" "_Z6addgasi" (func $import0 (param i32)))
  (table $table0 0 anyfunc)
  (memory $memory0 1)
  (export "memory" (memory $memory0))
  (export "computer" (func $func1))
  
  
  (func $func1 (param $var0 i32) (result i32)   // 初始化 stack: [0]
    i32.const 3
    call $import0                               [2]
    block $label2                               [2, 0]
      block $label1                             [2, 0, 0]
        block $label0   
>> 添加gas 指令, 改指令為后續(xù)添加, 且不計入指令數(shù)量
          i32.const 3                           [2, 0, 0, 0]
          call $import0
>>                                      
          get_local $var0                       
          i32.const 3
          i32.eq                                [2, 0, 0, 3] 
          br_if $label0                         [2, 0, 0]=> addgas 3 [2, 0, 0, 0]
          
          get_local $var0
          i32.const 2
          i32.eq                                [2, 0, 0, 3] 
          br_if $label1                         => addgas [2, 0, 0, 0]
          get_local $var0               
          i32.const 1
          i32.ne
          br_if $label2
          i32.const 1
          call $import0
          get_local $var0                       [2, 0, 0, 3]
          return                                addgas 3 => [2, 0, 0, 0]
        end $label0                             [2, 0, 0]
        i32.const 3                 
        call $import0
        get_local $var0                         [2, 0, 3]
        return                                  addgas 3 => [2, 0, 0]
      end $label1                               [2, 0]
      i32.const 2                       
      call $import0
      get_local $var0                           [2, 3]
      return                                    addgas 3 => [2, 0]
    end $label2                                 [2]
    i32.const 0
    call $import0
    get_local $var0                             [5]
                                                addgas 5 => []
  )
)

這樣 添加的 addgas 指令就是 wasm 內(nèi)置的操作了, 然后你的指令怎么解析來的,就怎么解析回去. 可以 encodewasm 文件. 給其他 wasm 虛擬機調(diào)用. 不用自己重新定義運行時.

至于每個 op 對應(yīng)的 gas 權(quán)重. 可以定義一個 map 表. 在解析 functionCode 的時候根據(jù) op 計算 gas 值.


添加導(dǎo)入函數(shù)結(jié)構(gòu)

addgas 這里把它定義為一個 imported 的函數(shù), 實際這個函數(shù)是不存在的, 所以我們要構(gòu)造這個函數(shù).
構(gòu)造原則如下:

  1. 由于 types 類型會優(yōu)先解析. type 類型指的是函數(shù)類型. 即函數(shù)的 參數(shù)個數(shù)類型, 返回值類型.
    addgas 定義為 void addgas(int), 需要檢查 types 中是否有 參數(shù)為整型, 無返回值的函數(shù).
    如果沒有, 添加該函數(shù)類型到 types 末尾
  2. 添加 import 函數(shù), 到 importsSection 末尾
  3. 至于為什么要添加到 typesimports 末尾, 是為了不影響原先的流程. function 對應(yīng)的 typeIndex, call 指令對應(yīng)的 importedIndex
  4. 需要修改 typesSection, importsSection, codeSection 對應(yīng)的 payload 長度
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容