diff options
author | Valentin Wüstholz <wuestholz@users.noreply.github.com> | 2017-02-27 19:21:19 +0800 |
---|---|---|
committer | Felix Lange <fjl@users.noreply.github.com> | 2017-02-27 19:21:19 +0800 |
commit | 37511ec5207b46829226b94e7c0da6a74609dd2a (patch) | |
tree | 4688a87378dd16cd9785ee66150380d4fdd66cb3 /core/asm | |
parent | 5c8fe28b725bd9b128edceae3215132ea741641b (diff) | |
download | go-tangerine-37511ec5207b46829226b94e7c0da6a74609dd2a.tar go-tangerine-37511ec5207b46829226b94e7c0da6a74609dd2a.tar.gz go-tangerine-37511ec5207b46829226b94e7c0da6a74609dd2a.tar.bz2 go-tangerine-37511ec5207b46829226b94e7c0da6a74609dd2a.tar.lz go-tangerine-37511ec5207b46829226b94e7c0da6a74609dd2a.tar.xz go-tangerine-37511ec5207b46829226b94e7c0da6a74609dd2a.tar.zst go-tangerine-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.go | 139 | ||||
-rw-r--r-- | core/asm/asm_test.go | 74 |
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) + } +} |