Skip to content

Commit 95b00fe

Browse files
committed
disasm: print invalid opcodes as #bytes
1 parent c0f9261 commit 95b00fe

File tree

2 files changed

+113
-11
lines changed

2 files changed

+113
-11
lines changed

disasm/disassembler.go

+19-11
Original file line numberDiff line numberDiff line change
@@ -90,16 +90,11 @@ func (d *Disassembler) Disassemble(bytecode []byte, outW io.Writer) error {
9090
d.printInvalid(out, bytecode[pc])
9191
} else {
9292
d.printPrefix(out, pc)
93-
d.printOp(out, op)
94-
if op.Push && op.Name != "PUSH0" {
95-
size := op.PushSize()
96-
if len(bytecode)-1-pc < size {
97-
d.newline(out, op, nil)
98-
return fmt.Errorf("bytecode truncated, ends within %s", op.Name)
99-
}
100-
data := bytecode[pc+1 : pc+size+1]
101-
d.printPushData(out, data)
93+
if op.Push {
94+
size := d.printPush(out, op, bytecode[pc:])
10295
pc += size
96+
} else {
97+
d.printOp(out, op)
10398
}
10499
}
105100

@@ -120,7 +115,7 @@ func (d *Disassembler) printPrefix(out io.Writer, pc int) {
120115
}
121116

122117
func (d *Disassembler) printInvalid(out io.Writer, b byte) {
123-
fmt.Fprintf(out, "<invalid %x>\n", b)
118+
fmt.Fprintf(out, "#bytes %#x\n", b)
124119
}
125120

126121
func (d *Disassembler) printOp(out io.Writer, op *evm.Op) {
@@ -131,8 +126,21 @@ func (d *Disassembler) printOp(out io.Writer, op *evm.Op) {
131126
fmt.Fprint(out, name)
132127
}
133128

134-
func (d *Disassembler) printPushData(out io.Writer, data []byte) {
129+
func (d *Disassembler) printPush(out io.Writer, op *evm.Op, code []byte) (dataSize int) {
130+
size := op.PushSize()
131+
if size == 0 {
132+
d.printOp(out, op)
133+
return 0
134+
}
135+
if size > len(code)-1 {
136+
// Handle truncated PUSH at end of code.
137+
fmt.Fprintf(out, "#bytes %#x", code)
138+
return len(code) - 1
139+
}
140+
d.printOp(out, op)
141+
data := code[1 : size+1]
135142
fmt.Fprintf(out, " %#x", data)
143+
return len(data)
136144
}
137145

138146
func (d *Disassembler) newline(out io.Writer, prevOp *evm.Op, nextOp *evm.Op) {

disasm/disassembler_test.go

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright 2025 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package disasm
18+
19+
import (
20+
"bytes"
21+
"encoding/hex"
22+
"strings"
23+
"testing"
24+
25+
"github.com/fjl/geas/asm"
26+
)
27+
28+
func TestIncompletePush(t *testing.T) {
29+
bytecode, _ := hex.DecodeString("6080604052348015600e575f80fd5b50603e80601a5f395ff3fe60806040525f80fdfea2646970667358221220ba4339602dd535d09d71fae3164f7aa7f6e098ec879fc9e8f36bd912d4877c5264736f6c63430008190033")
30+
expectedOutput := strings.TrimSpace(`
31+
push1 0x80
32+
push1 0x40
33+
mstore
34+
callvalue
35+
dup1
36+
iszero
37+
push1 0x0e
38+
jumpi
39+
push0
40+
dup1
41+
revert
42+
jumpdest
43+
pop
44+
push1 0x3e
45+
dup1
46+
push1 0x1a
47+
push0
48+
codecopy
49+
push0
50+
return
51+
#bytes 0xfe
52+
push1 0x80
53+
push1 0x40
54+
mstore
55+
push0
56+
dup1
57+
revert
58+
#bytes 0xfe
59+
log2
60+
push5 0x6970667358
61+
#bytes 0x22
62+
slt
63+
keccak256
64+
#bytes 0xba
65+
number
66+
codecopy
67+
push1 0x2d
68+
#bytes 0xd5
69+
calldataload
70+
#bytes 0xd0
71+
swap14
72+
push18 0xfae3164f7aa7f6e098ec879fc9e8f36bd912
73+
#bytes 0xd4
74+
dup8
75+
#bytes 0x7c5264736f6c63430008190033
76+
`)
77+
78+
var buf strings.Builder
79+
d := New()
80+
d.SetShowBlocks(false)
81+
d.SetTarget("cancun")
82+
d.Disassemble(bytecode, &buf)
83+
output := strings.TrimSpace(buf.String())
84+
if output != expectedOutput {
85+
t.Fatal("wrong output:", output)
86+
}
87+
88+
// try round trip
89+
a := asm.New(nil)
90+
rtcode := a.CompileString(output)
91+
if !bytes.Equal(rtcode, bytecode) {
92+
t.Error("disassembly did not round-trip")
93+
}
94+
}

0 commit comments

Comments
 (0)