Assembly
理解為什麼這個彙編程式碼會添加 0x20 和 0x1f
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))))
為什麼他們增加
0x20
了size
,並增加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 字節的倍數”