EVM字节码学习
EVM指令集
参考:https://ethbook.abyteahead.com/ch7/instructions.html
https://gitee.com/silent_light/mythril/blob/develop/mythril/laser/ethereum/instruction_data.py
谨记evm虚拟机的word(字)是256位32字节
EVM数据管理

可以看到code和storage里存储的数据是非易失的(non-volatile),而stack,args,memory里存储的数据是易失的(volatile),其中code的数据是智能合约的二进制源码,是非易失的很好理解,部署合约的时候data字段也就是合约内容会存储在EVM的code中。
如果要操作这些存储结构里的数据,就需要用到EVM指令,由于EVM的操作码被限制在一个字节以内,所以EVM最多容纳256条指令,目前EVM已经定义了约142条指令,还有100多条用于以后的扩展。这142条指令包括了算法运算,密码学计算,栈操作,memory,storage操作等。
接下来看一下各个存储位置的含义
Stack(栈)
stack可以免费使用,没有gas消耗,用来保存函数的局部变量,数量被限制在了16个,当在合约里中声明的局部变量超过16个时,再编译合约就会遇到Stack too deep, try removing local variables错误。
介绍几个EVM操作栈的指令,在后面分析合约的时候会用到:
- Pop指令(操作码0x50):用来从栈顶弹出一个元素;
- PushX指令:用来把紧跟在后面的1-32字节元素推入栈顶
Push指令一共32条,从Push1(0x60)到Push32(0x7f),因为栈中每个元素是一个字为256bit,一个字节8bit,所以Push指令最多可以把其后32字节的元素放入栈中而不溢出。 - DupX指令:用来复制从栈顶开始的第1-16个元素,复制后把元素在推入栈顶
Dup指令一共16条,从Dup1(0x80)到Dup16(0x8f)。 - SwapX指令:把栈顶元素和从栈顶开始数的第1-16个元素进行交换,Swap指令一共16条,从Swap1(0x01)一直到Swap16(0x9f)。
Args(参数)
args也叫calldata,是一段只读的可寻址的保存函数调用参数的空间,与栈不同的地方的是,如果要使用calldata里面的数据,必须手动指定偏移量和读取的字节数。
EVM提供的用于操作calldata的指令有三个:
calldatasize:返回calldata的大小。calldataload:从calldata中加载32bytes到stack中。calldatacopy:拷贝一些字节到内存中
Memory(内存)
Memory是一个易失性的可以读写修改的空间,主要是在运行期间存储数据,将参数传递给内部函数。内存可以在字节级别寻址,一次可以读取32字节。
EVM提供的用于操作memory的指令有三个:
- mload 加载一个字从内存到stack;
- mstore存储一个值到指定的内存地址,格式mstore(p,v),存储v到地址p;
- mstore8存储一个byte到指定内存地址 ;
当我们操作内存的时候,总是需要加载0x40,因为这个地址保存了空闲内存的指针,避免了覆盖已有的数据。
Storage(存储)
Storage是一个可以读写修改的持久存储的空间,也是每个合约持久化存储数据的地方。Storage是一个巨大的map,一共2^256个插槽,一个插糟有32byte。
EVM提供的用于操作storage的指令有两个:
- sload用于加载一个字从storage到stack中;
- sstore用于存储一个字到storage中;
solidity将定义的状态变量,映射到插糟内,对于静态大小的变量从0开始连续布局,对于动态数组和map则采用了其他方法

浙公网安备 33010602011771号