forked from holiman/goevmlab
-
Notifications
You must be signed in to change notification settings - Fork 0
/
disassembly.go
124 lines (112 loc) · 2.91 KB
/
disassembly.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package ops
import "fmt"
// Iterator for disassembled EVM instructions
type instructionIterator struct {
code []byte
pc uint64
arg []byte
op OpCode
error error
started bool
stackBalance int
}
// Create a new instruction iterator.
func NewInstructionIterator(code []byte) *instructionIterator {
it := new(instructionIterator)
it.code = code
return it
}
// InstructionCount counts the number of instructions
func InstructionCount(code []byte) int {
it := NewInstructionIterator(code)
c := 0
for it.Next() {
c++
}
return c
}
// Skips num instructions.
func (it *instructionIterator) Skip(num int) {
c := 0
for it.Next() && c < num {
c++
}
}
// Returns true if there is a next instruction and moves on.
func (it *instructionIterator) Next() bool {
if it.error != nil || uint64(len(it.code)) <= it.pc {
// We previously reached an error or the end.
return false
}
if it.started {
// Since the iteration has been already started we move to the next instruction.
if it.arg != nil {
it.pc += uint64(len(it.arg))
}
it.pc++
} else {
// We start the iteration from the first instruction.
it.started = true
}
if uint64(len(it.code)) <= it.pc {
// We reached the end.
return false
}
it.op = OpCode(it.code[it.pc])
if it.op.HasImmediate() {
switch {
case it.op >= PUSH1 && it.op <= PUSH32:
a := uint64(it.op) - uint64(PUSH1) + 1
u := it.pc + 1 + a
if uint64(len(it.code)) < u {
it.error = fmt.Errorf("incomplete push instruction at %v", it.pc)
return false
}
it.arg = it.code[it.pc+1 : u]
//case it.op == RJUMP || it.op == RJUMPI:
// u := it.pc + 1 + 2
// if uint64(len(it.code)) < u {
// it.error = fmt.Errorf("incomplete RJUMP/RJUMPI instruction at %v", it.pc)
// return false
// }
// it.arg = it.code[it.pc+1 : u]
//case it.op == RJUMPV:
// // First we need to peek at the next byte, to see the length
// if uint64(len(it.code)) <= it.pc+1 {
// it.error = fmt.Errorf("incomplete RJUMPV instruction at %v", it.pc)
// return false
// }
// count := uint64(it.code[it.pc+1])
// // The rumpv table is count x uint16 bytes large
// a := 1 + 2*count
// u := it.pc + 1 + a
// if uint64(len(it.code)) < u {
// it.error = fmt.Errorf("incomplete RJUMPV instruction at %v", it.pc)
// return false
// }
// it.arg = it.code[it.pc+1 : u]
default:
panic("Unkown op")
}
} else {
it.arg = nil
}
it.stackBalance += it.op.Stackdelta()
return true
}
// Returns any error that may have been encountered.
func (it *instructionIterator) Error() error {
return it.error
}
// Returns the PC of the current instruction.
func (it *instructionIterator) PC() uint64 {
return it.pc
}
// Returns the opcode of the current instruction.
func (it *instructionIterator) Op() OpCode {
return it.op
}
// Returns the argument of the current instruction.
func (it *instructionIterator) Arg() []byte {
return it.arg
}