节点、表达式与IR的是什么样的关系?如何根据代码生如何提取出IR指令?在遍历节点时,有哪些对象经常用到。
SlithIR 是 Slither 中用来表示 Solidity 代码的混合中间表示。控制流图的每个节点最多可以包含一个 Solidity 表达式
,该表达式被转换为一组 SlithIR 指令
。这种表示使得实施分析更容易,而不会丢失 Solidity 源代码中包含的关键语义信息。
直观理解下节点、表达式和IR的关系。
对于源代码为
abstract contract WhiteList is Ownable {
constructor() {setWhiteList(_msgSender(),false);}
}
setWhiteList(_msgSender(),false)
,在slither处理时,处理到constructor函数时,会生成一个函数对象(假设该函数对象命名为f),该函数对象有nodes属性,可以取得函数对象所有的节点node,这里有2个node节点:
以第2个node节点为例,节点对象有个expression属性
,对应的就是该节点的expression表达式为EXPRESSION setWhiteList(_msgSender(),false)。(为什么不用第1个node节点做为例子,因为第1个node节点为ENTRY_POINT,它对应的expression为空)。
节点对象还有个属性为irs
,表示该节点中对应的IR指令。第2个node节点中的irs中包含两个ir,为
上面的关系,在调试过程中的展示如下图
SlithIR 使用的指令一共不到 40 条。它没有内部控制流表示,依赖于 Slither 的控制流图结构(SlithIR 指令与图中的每个节点相关联)。下面给出了一些重要指令的高级描述:
LV和RV分别表示一个被赋值的变量(left-value)和一个被读取的变量(rightvalue)。变量可以是 Solidity 变量或由中间表示创建的临时变量。
表示为二进制或一元运算符:
Solidity 允许操作通过间接引用(dereferencing)
访问的映射和数组。 SlithIR 使用一种特定的变量类型,称为 REF (ReferenceVariable
) 来存储间接引用的结果。
index运算符允许间接引用一个变量:
对结构的访问是通过成员运算符完成的:
slither共有9种调用指令。
一些调用可以有额外的参数,例如,H_CALL、L_CALL、Send 和 Transfer 可以存在value
值,代表交易的以太币数量。
包括用于数组操作的 PUSH
、用于类型转换的 CONVERT
以及用于操作元组的运算符
PUSH
PUSH LVALUE RVALUE
PUSH LVALUE Function
(for dynamic function)
Conversion
CONVERT LVALUE RVALUE TYPE
Unpack
LVALUE = UNPACK TUPLEVARIABLE INDEX(:int)
在slither/examples/scripts/slithIR.py
文件中提供了转换功能。
转换功能的源代码:
import sys
from slither import Slither
if len(sys.argv) != 2:
print("python slithIR.py contract.sol")
sys.exit(-1)
# Init slither
slither = Slither(sys.argv[1])
# Iterate over all the contracts
for contract in slither.contracts:
print(f"Contract: {contract.name}")
# Iterate over all the functions
for function in contract.functions:
# Dont explore inherited functions
if function.contract_declarer == contract:
print(f"Function: {function.name}")
# Iterate over the nodes of the function
for node in function.nodes:
# Print the Solidity expression of the nodes
# And the SlithIR operations
if node.expression:
print(f"\tSolidity expression: {node.expression}")
print("\tSlithIR:")
for ir in node.irs:
print(f"\t\t\t{ir}")
我们以下面的solidity源代码为例,看转换出来的IR代码是什么样子的。
using SafeMath for uint;
mapping(address => uint) balances;
function transfer(address to, uint val) public{
balances[msg.sender] = balances[msg.sender].min(
val);
balances[to] = balances[to].add(val);
}
下面为上面的源代码转换出来的对应的IR
Function transfer(address,uint256)
Solidity: balances[msg.sender]=balances[msg.sender].sub(val)
SlithIR:
REF0(uint256)−>balances[msg.sender]
REF1(uint256)−>balances[msg.sender]
TMP1(uint256)=LIBCALLSafeMat...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!