Skip to content

Commit dd810be

Browse files
committed
Add study notes for 2025-08-18
1 parent 4a027df commit dd810be

1 file changed

Lines changed: 136 additions & 0 deletions

File tree

brucexu-eth.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,142 @@ E 卫兵
1515
## Notes
1616

1717
<!-- Content_START -->
18+
# 2025-08-18
19+
20+
# Solidity Opcode
21+
22+
## MSTORE
23+
24+
MSTORE 是内存存储操作码,用于将 32 字节数据写入内存。
25+
26+
MSTORE 功能
27+
28+
- 操作码: 0x52
29+
- Gas消耗: 3 + 内存扩展成本
30+
- 栈输入: [offset, value]
31+
- 栈输出: 无
32+
- 功能: 将 32 字节的 value 存储到内存 offset 位置
33+
34+
MSTORE 详细解释
35+
36+
1. 核心特性
37+
38+
- 总是写入 32 字节:即使 value 小于 32 字节,也会用 0 填充左侧
39+
- 可以覆盖:新值会完全覆盖指定位置的 32 字节
40+
- 内存扩展:访问新内存区域会触发内存扩展,增加 gas 成本
41+
42+
2. 内存布局
43+
44+
0x00-0x3F: Scratch space (临时空间)
45+
0x40-0x5F: Free memory pointer (自由内存指针)
46+
0x60-0x7F: Zero slot (零槽)
47+
0x80+: 可用内存
48+
49+
3. Gas 成本计算
50+
51+
- 基础成本:3 gas
52+
- 内存扩展:memory_cost = (memory_size^2) / 512 + (3 * memory_size)
53+
- 首次访问高地址会很贵
54+
55+
4. 常见用途
56+
57+
- 返回数据:配合 RETURN 使用
58+
- 构建数组/字符串:动态数据结构
59+
- 函数参数传递:ABI 编码
60+
- 临时计算:使用 scratch space
61+
62+
5. 与 MSTORE8 的区别
63+
64+
- MSTORE: 存储 32 字节
65+
- MSTORE8: 只存储 1 字节(最右边的字节)
66+
67+
6. 注意事项
68+
69+
- 跨 32 字节边界存储会覆盖两个槽
70+
- 内存不会自动清零,可能包含脏数据
71+
- 总是要更新自由内存指针(0x40)
72+
73+
Example:
74+
75+
```
76+
mstore(0x40, 0x4444)
77+
```
78+
79+
->
80+
81+
```
82+
PUSH2(0x4444)
83+
PUSH1(0x40)
84+
MSTORE
85+
86+
memory:
87+
040| 00...00 44 44
88+
```
89+
90+
## EVM 内存布局详解
91+
92+
0x00-0x3F (0-63字节): Scratch Space 临时空间
93+
94+
- 用途: 编译器的临时工作区
95+
- 特点: 可以被随时覆盖,不保证持久
96+
- 常见操作:
97+
- keccak256 哈希计算的输入准备
98+
- 函数返回值的临时存放
99+
- ABI 编码的中间步骤
100+
101+
0x40-0x5F (64-95字节): Free Memory Pointer 自由内存指针
102+
103+
- 用途: 追踪下一个可用内存位置
104+
- 初始值: 0x80(跳过所有保留区域)
105+
- 重要性: 防止内存覆盖,确保内存分配安全
106+
- 使用方式:
107+
let ptr := mload(0x40) // 获取当前可用位置
108+
// ... 使用内存 ...
109+
mstore(0x40, add(ptr, size)) // 更新指针
110+
111+
0x60-0x7F (96-127字节): Zero Slot 零槽
112+
113+
- 用途: 提供一个永远为 0 的位置
114+
- 优化: 避免使用 PUSH1 0x00 指令,节省 gas
115+
- 保证: EVM 确保这个位置始终为 0
116+
117+
0x80+ (128字节起): 可用内存
118+
119+
- 用途: 实际数据存储区域
120+
- 分配: 通过自由内存指针动态分配
121+
- 内容: 数组、字符串、结构体、返回数据等
122+
123+
内存管理原则
124+
125+
1. 永远不要硬编码内存地址(0x80 以下)
126+
2. 总是使用自由内存指针分配新内存
127+
3. 分配后更新指针防止覆盖
128+
4. 内存只会增长,不会缩小或释放
129+
130+
## 创建变量和 MLOAD 的流程
131+
132+
```
133+
uint256 val0; uint256 val32; uint256 val64; uint256 val96; uint256 val128;
134+
assembly {
135+
val0 := mload(0x00) // Read from byte 0
136+
val32 := mload(0x20) // Read from byte 32
137+
val64 := mload(0x40) // Read from byte 64
138+
val96 := mload(0x60) // Read from byte 96
139+
val128 := mload(0x80) // Read from byte 128
140+
}
141+
```
142+
143+
uint256 val0 -> PUSH0 to Stack
144+
uint256 val32 -> DUP1 ??TODO 为什么不使用 PUSH0?
145+
146+
val64 := mload(0x40) -> PUSH1(0x40) set address -> MLOAD get value -> SWAP3 set to the stack position -> POP remove the tempory value
147+
148+
## 几个之前的问题
149+
150+
### opcode 和源代码的关系
151+
152+
源代码实际上会翻译成 opcode 进行实际的执行,所以多个 opcode 对应一行具体的代码。通过 source map 进行映射
153+
18154
# 2025-08-15
19155

20156
这个 forge debug 无法找到 source map 这个问题还是比较严重的,只能在 test case 里面 debug 有什么用呢?

0 commit comments

Comments
 (0)