Assembly

理解為什麼這個彙編程式碼會添加 0x20 和 0x1f

  • August 30, 2021

MakerDAO 的DSProxy中定義的執行函式包含以下彙編程式碼:

assembly {
   let succeeded := delegatecall(sub(gas, 5000), _target, add(_data, 0x20), mload(_data), 0, 0)
   let size := returndatasize

   response := mload(0x40)
   mstore(0x40, add(response, and(add(add(size, 0x20), 0x1f), not(0x1f))))
   mstore(response, size)
   returndatacopy(add(response, 0x20), 0, size)

   switch iszero(succeeded)
   case 1 {
       revert(add(response, 0x20), size)
   }
}

在高層次上,我明白這是做什麼的。_target它在 DSProxy 儲存的上下文中將呼叫委託給合約上的一個函式。它從剩餘的津貼中減去 5000 gas,以確保有足夠的 gas 用於執行其餘的彙編程式碼。它將_data(定義為的變數bytes memory)作為呼叫數據傳遞。然後它將返回的值打包到response變數中(再次定義為bytes memory)。最後它檢查合約呼叫是否失敗,如果失敗,它會恢復。

我很難理解這一行:

mstore(0x40, add(response, and(add(add(size, 0x20), 0x1f), not(0x1f))))

為什麼他們增加0x20size,並增加0x1f了它的價值,最後是 AND 的否定0x1f?我知道這些是指針,但我不知道它們為什麼要從這些特定值中讀取。

資訊: 0x40 是一個特殊地址: https ://docs.soliditylang.org/en/v0.8.7/assembly.html#example

Solidity 通過以下方式管理記憶體。在記憶體的 0x40 位置有一個“空閒記憶體指針”。如果要分配記憶體,請使用從該指針指向的位置開始的記憶體並對其進行更新。

所以程式碼讀作

// how much memory do we need to allocate for the response ?
   let size := returndatasize 

// load the response at the address given by the free memory pointer
   response := mload(0x40)

// save at the free memory pointer address (0x40) the next position of the available memory
   mstore(0x40, add(response, and(add(add(size, 0x20), 0x1f), not(0x1f)))) 

這是為了更新空閒記憶體指針(0x40)

!0x1f 的下一個位置是 32 的倍數:

  • 空閒記憶體指針應該指向一個四捨五入的位置
  • 返回數據的大小不能是 32 的倍數(例如它可能只有 5 個字節)

所以這基本上是

“在我通過添加響應大小(大小)計算的響應起始位置(響應)之後,將可用記憶體指針(0x40)設置為下一個 32 字節的倍數”

引用自:https://ethereum.stackexchange.com/questions/108022