aboutsummaryrefslogtreecommitdiffstats
path: root/core/asm
diff options
context:
space:
mode:
authorValentin Wüstholz <wuestholz@users.noreply.github.com>2017-02-27 19:21:19 +0800
committerFelix Lange <fjl@users.noreply.github.com>2017-02-27 19:21:19 +0800
commit37511ec5207b46829226b94e7c0da6a74609dd2a (patch)
tree4688a87378dd16cd9785ee66150380d4fdd66cb3 /core/asm
parent5c8fe28b725bd9b128edceae3215132ea741641b (diff)
downloaddexon-37511ec5207b46829226b94e7c0da6a74609dd2a.tar
dexon-37511ec5207b46829226b94e7c0da6a74609dd2a.tar.gz
dexon-37511ec5207b46829226b94e7c0da6a74609dd2a.tar.bz2
dexon-37511ec5207b46829226b94e7c0da6a74609dd2a.tar.lz
dexon-37511ec5207b46829226b94e7c0da6a74609dd2a.tar.xz
dexon-37511ec5207b46829226b94e7c0da6a74609dd2a.tar.zst
dexon-37511ec5207b46829226b94e7c0da6a74609dd2a.zip
core, core/vm, cmd/disasm: unify procedures for disassembling evm code (#3530)
Diffstat (limited to 'core/asm')
-rw-r--r--core/asm/asm.go139
-rw-r--r--core/asm/asm_test.go74
2 files changed, 213 insertions, 0 deletions
diff --git a/core/asm/asm.go b/core/asm/asm.go
new file mode 100644
index 000000000..5fe827e7c
--- /dev/null
+++ b/core/asm/asm.go
@@ -0,0 +1,139 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Provides support for dealing with EVM assembly instructions (e.g., disassembling them).
+package asm
+
+import (
+ "encoding/hex"
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/core/vm"
+)
+
+// Iterator for disassembled EVM instructions
+type instructionIterator struct {
+ code []byte
+ pc uint64
+ arg []byte
+ op vm.OpCode
+ error error
+ started bool
+}
+
+// Create a new instruction iterator.
+func NewInstructionIterator(code []byte) *instructionIterator {
+ it := new(instructionIterator)
+ it.code = code
+ return it
+}
+
+// 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 = vm.OpCode(it.code[it.pc])
+ if it.op.IsPush() {
+ a := uint64(it.op) - uint64(vm.PUSH1) + 1
+ u := it.pc + 1 + a
+ if uint64(len(it.code)) <= it.pc || 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]
+ } else {
+ it.arg = nil
+ }
+ 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() vm.OpCode {
+ return it.op
+}
+
+// Returns the argument of the current instruction.
+func (it *instructionIterator) Arg() []byte {
+ return it.arg
+}
+
+// Pretty-print all disassembled EVM instructions to stdout.
+func PrintDisassembled(code string) error {
+ script, err := hex.DecodeString(code)
+ if err != nil {
+ return err
+ }
+
+ it := NewInstructionIterator(script)
+ for it.Next() {
+ if it.Arg() != nil && 0 < len(it.Arg()) {
+ fmt.Printf("%06v: %v 0x%x\n", it.PC(), it.Op(), it.Arg())
+ } else {
+ fmt.Printf("%06v: %v\n", it.PC(), it.Op())
+ }
+ }
+ if err := it.Error(); err != nil {
+ return err
+ }
+ return nil
+}
+
+// Return all disassembled EVM instructions in human-readable format.
+func Disassemble(script []byte) ([]string, error) {
+ instrs := make([]string, 0)
+
+ it := NewInstructionIterator(script)
+ for it.Next() {
+ if it.Arg() != nil && 0 < len(it.Arg()) {
+ instrs = append(instrs, fmt.Sprintf("%06v: %v 0x%x\n", it.PC(), it.Op(), it.Arg()))
+ } else {
+ instrs = append(instrs, fmt.Sprintf("%06v: %v\n", it.PC(), it.Op()))
+ }
+ }
+ if err := it.Error(); err != nil {
+ return nil, err
+ }
+ return instrs, nil
+}
diff --git a/core/asm/asm_test.go b/core/asm/asm_test.go
new file mode 100644
index 000000000..92b26b67a
--- /dev/null
+++ b/core/asm/asm_test.go
@@ -0,0 +1,74 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package asm
+
+import (
+ "testing"
+
+ "encoding/hex"
+)
+
+// Tests disassembling the instructions for valid evm code
+func TestInstructionIteratorValid(t *testing.T) {
+ cnt := 0
+ script, _ := hex.DecodeString("61000000")
+
+ it := NewInstructionIterator(script)
+ for it.Next() {
+ cnt++
+ }
+
+ if err := it.Error(); err != nil {
+ t.Errorf("Expected 2, but encountered error %v instead.", err)
+ }
+ if cnt != 2 {
+ t.Errorf("Expected 2, but got %v instead.", cnt)
+ }
+}
+
+// Tests disassembling the instructions for invalid evm code
+func TestInstructionIteratorInvalid(t *testing.T) {
+ cnt := 0
+ script, _ := hex.DecodeString("6100")
+
+ it := NewInstructionIterator(script)
+ for it.Next() {
+ cnt++
+ }
+
+ if it.Error() == nil {
+ t.Errorf("Expected an error, but got %v instead.", cnt)
+ }
+}
+
+// Tests disassembling the instructions for empty evm code
+func TestInstructionIteratorEmpty(t *testing.T) {
+ cnt := 0
+ script, _ := hex.DecodeString("")
+
+ it := NewInstructionIterator(script)
+ for it.Next() {
+ cnt++
+ }
+
+ if err := it.Error(); err != nil {
+ t.Errorf("Expected 0, but encountered error %v instead.", err)
+ }
+ if cnt != 0 {
+ t.Errorf("Expected 0, but got %v instead.", cnt)
+ }
+}