asm.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // Copyright 2017 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. // Provides support for dealing with EVM assembly instructions (e.g., disassembling them).
  17. package asm
  18. import (
  19. "encoding/hex"
  20. "fmt"
  21. "github.com/ethereum/go-ethereum/core/vm"
  22. )
  23. // Iterator for disassembled EVM instructions
  24. type instructionIterator struct {
  25. code []byte
  26. pc uint64
  27. arg []byte
  28. op vm.OpCode
  29. error error
  30. started bool
  31. }
  32. // Create a new instruction iterator.
  33. func NewInstructionIterator(code []byte) *instructionIterator {
  34. it := new(instructionIterator)
  35. it.code = code
  36. return it
  37. }
  38. // Returns true if there is a next instruction and moves on.
  39. func (it *instructionIterator) Next() bool {
  40. if it.error != nil || uint64(len(it.code)) <= it.pc {
  41. // We previously reached an error or the end.
  42. return false
  43. }
  44. if it.started {
  45. // Since the iteration has been already started we move to the next instruction.
  46. if it.arg != nil {
  47. it.pc += uint64(len(it.arg))
  48. }
  49. it.pc++
  50. } else {
  51. // We start the iteration from the first instruction.
  52. it.started = true
  53. }
  54. if uint64(len(it.code)) <= it.pc {
  55. // We reached the end.
  56. return false
  57. }
  58. it.op = vm.OpCode(it.code[it.pc])
  59. if it.op.IsPush() {
  60. a := uint64(it.op) - uint64(vm.PUSH1) + 1
  61. u := it.pc + 1 + a
  62. if uint64(len(it.code)) <= it.pc || uint64(len(it.code)) < u {
  63. it.error = fmt.Errorf("incomplete push instruction at %v", it.pc)
  64. return false
  65. }
  66. it.arg = it.code[it.pc+1 : u]
  67. } else {
  68. it.arg = nil
  69. }
  70. return true
  71. }
  72. // Returns any error that may have been encountered.
  73. func (it *instructionIterator) Error() error {
  74. return it.error
  75. }
  76. // Returns the PC of the current instruction.
  77. func (it *instructionIterator) PC() uint64 {
  78. return it.pc
  79. }
  80. // Returns the opcode of the current instruction.
  81. func (it *instructionIterator) Op() vm.OpCode {
  82. return it.op
  83. }
  84. // Returns the argument of the current instruction.
  85. func (it *instructionIterator) Arg() []byte {
  86. return it.arg
  87. }
  88. // Pretty-print all disassembled EVM instructions to stdout.
  89. func PrintDisassembled(code string) error {
  90. script, err := hex.DecodeString(code)
  91. if err != nil {
  92. return err
  93. }
  94. it := NewInstructionIterator(script)
  95. for it.Next() {
  96. if it.Arg() != nil && 0 < len(it.Arg()) {
  97. fmt.Printf("%05x: %v 0x%x\n", it.PC(), it.Op(), it.Arg())
  98. } else {
  99. fmt.Printf("%05x: %v\n", it.PC(), it.Op())
  100. }
  101. }
  102. return it.Error()
  103. }
  104. // Return all disassembled EVM instructions in human-readable format.
  105. func Disassemble(script []byte) ([]string, error) {
  106. instrs := make([]string, 0)
  107. it := NewInstructionIterator(script)
  108. for it.Next() {
  109. if it.Arg() != nil && 0 < len(it.Arg()) {
  110. instrs = append(instrs, fmt.Sprintf("%05x: %v 0x%x\n", it.PC(), it.Op(), it.Arg()))
  111. } else {
  112. instrs = append(instrs, fmt.Sprintf("%05x: %v\n", it.PC(), it.Op()))
  113. }
  114. }
  115. if err := it.Error(); err != nil {
  116. return nil, err
  117. }
  118. return instrs, nil
  119. }