理解EVM程序 - 从 Solidity 到 EVM的结构
在这篇文章中,我们将会带你理解 Solidity 程序在编译成 EVM 程序后是如何工作的。我们下面以 WETH 这一简单的智能合约为例讲解
从反汇编理解 EVM 程序
初始化
大部分智能合约都会以 0x6060 6040 52...
开头,这一初始化过程为:
1 | PUSH1 0x60 # Part 1 |
- 在 0x40-0x60 的内存空间中存储空白地址指针 - 0x40 以下的地址被 EVM 保留用于内部Hash
- 检查
CALLDATASIZE
是否小于 4(没有函数签名),如果是则跳转到 0x00af,否则继续执行 - 从
CALLDATALOAD
中读取函数签名,这一步是通过除以0x0100000000000000000000000000000000000000000000000000000000
实现
在提取完函数签名后,接下来是函数跳转表部分。
跳转表
反汇编结果的17-70行是函数跳转表,这一部分的作用是根据函数签名跳转到对应的函数。这一部分的代码格式如下:
1 | PUSH4 [signature] |
其中 signature 是函数签名,function 是函数的地址。这一部分的代码是由编译器自动生成的,我们可以通过 solc --hashes
命令查看函数签名和函数地址的对应关系。在 WETH 中,我们可以得到如下结果:
1 | 0x06fdde03 name() |
EVM 会用上一步获得的函数签名与跳转表按顺序匹配,最后会有一项来兜底失配的函数,这一部分的内容为跳转到 fallback 函数。
函数体和 fallback
在 匹配表匹配到对应函数签名后,EVM 会跳转到对应的函数体。函数体内部可以分为以下两个部分:参数解析和执行部分,函数进入后会检查参数长度是否正确,如果不正确则 revert
。检查过参数长度后会进入执行和返回部分。