Commit 33980b5c authored by chriseth's avatar chriseth

Simple disassembler to Solidity inline assembly.

parent 27d00358
...@@ -26,6 +26,30 @@ module.exports = { ...@@ -26,6 +26,30 @@ module.exports = {
return [ code, codeMap ] return [ code, codeMap ]
}, },
/**
* Parses code as a list of integers into a list of objects containing
* information about the opcode.
*/
parseCode: function (raw) {
var code = []
for (var i = 0; i < raw.length; i++) {
var opcode = opcodes(raw[i], true)
if (opcode.name.slice(0, 4) === 'PUSH') {
var length = raw[i] - 0x5f
opcode.pushData = raw.slice(i + 1, i + length + 1)
// in case pushdata extends beyond code
if (i + 1 + length > raw.length) {
for (var j = opcode.pushData.length; j < length; j++) {
opcode.pushData.push(0)
}
}
i += length
}
code.push(opcode)
}
return code
},
pad: function (num, size) { pad: function (num, size) {
var s = num + '' var s = num + ''
while (s.length < size) s = '0' + s while (s.length < size) s = '0' + s
......
'use strict'
var parseCode = require('./codeUtils').parseCode
var util = require('../helpers/util')
var createExpressions = function (instructions) {
var expressions = []
var labels = 0
for (var i = 0; i < instructions.length; i++) {
let expr = instructions[i]
expr.functional = false
if (expr.name === 'JUMPDEST') {
expr.label = 'label' + (++labels)
} else if (expr.name.slice(0, 3) === 'DUP') {
} else if (expr.name.slice(0, 4) === 'SWAP') {
} else if (expr.out <= 1 && expr.in <= expressions.length) {
var error = false
for (var j = 0; j < expr.in && !error; j++) {
var arg = expressions[expressions.length - j - 1]
if (!arg.functional || arg.out !== 1) {
error = true
break
}
}
if (!error) {
expr.args = expressions.splice(expressions.length - expr.in)
expr.functional = true
}
}
expressions.push(expr)
}
return expressions
}
var toString = function (expr) {
if (expr.name.slice(0, 4) === 'PUSH') {
return util.hexConvert(expr.pushData)
} else if (expr.name === 'JUMPDEST') {
return expr.label + ':'
} else if (expr.args) {
return expr.name.toLowerCase() + '(' + expr.args.reverse().map(toString).join(', ') + ')'
} else {
return expr.name.toLowerCase()
}
}
var disassemble = function (input) {
var code = parseCode(util.hexToIntArray(input))
return createExpressions(code).map(toString).join('\n')
}
module.exports = {
/**
* Disassembler that turns bytecode (as a hex string) into Solidity inline assembly.
*/
disassemble: disassemble
}
...@@ -7,16 +7,25 @@ module.exports = { ...@@ -7,16 +7,25 @@ module.exports = {
var ret = '0x' var ret = '0x'
for (var i = 0; i < ints.length; i++) { for (var i = 0; i < ints.length; i++) {
var h = ints[i] var h = ints[i]
if (h) { ret += (h <= 0xf ? '0' : '') + h.toString(16)
h = h.toString(16)
ret += ('0x' + h) < 0x10 ? '0' + h : h
} else {
ret += '00'
}
} }
return ret return ret
}, },
/**
* Converts a hex string to an array of integers.
*/
hexToIntArray: function (hexString) {
if (hexString.slice(0, 2) === '0x') {
hexString = hexString.slice(2)
}
var integers = []
for (var i = 0; i < hexString.length; i += 2) {
integers.push(parseInt(hexString.slice(i, i + 2), 16))
}
return integers
},
/* /*
ints: list of IntArrays ints: list of IntArrays
*/ */
......
'use strict'
var tape = require('tape')
var disassemble = require('../src/code/disassembler.js').disassemble
tape('Disassembler', function (t) {
t.test('empty', function (st) {
st.plan(1)
st.equal(disassemble(''), '')
})
t.test('add', function (st) {
st.plan(1)
st.equal(disassemble('0x01'), 'add')
})
t.test('push', function (st) {
st.plan(1)
st.equal(disassemble('0x640203'), '0x0203000000')
})
t.test('complexcode', function (st) {
st.plan(1)
var code = '60606040526009600060005055607e8060186000396000f360606040526000357c0100000000000000000000000000000000000000000000000000000000900480630dbe671f146039576035565b6002565b3460025760486004805050604a565b005b6000600090505b600a811015607a5760006000818150548092919060010191905055505b80806001019150506051565b5b5056'
var asm = `mstore(0x40, 0x60)
0x09
0x00
pop(0x00)
sstore
0x7e
dup1
0x18
0x00
codecopy
0x00
return
mstore(0x40, 0x60)
calldataload(0x00)
0x0100000000000000000000000000000000000000000000000000000000
swap1
div
dup1
0x0dbe671f
eq
0x39
jumpi
jump(0x35)
label1:
jump(0x02)
label2:
jumpi(0x02, callvalue())
0x48
0x04
dup1
pop
pop
jump(0x4a)
label3:
stop()
label4:
0x00
0x00
swap1
pop
label5:
0x0a
dup2
lt
iszero
0x7a
jumpi
0x00
0x00
dup2
dup2
pop
sload
dup1
swap3
swap2
swap1
0x01
add
swap2
swap1
pop
sstore
pop
label6:
dup1
dup1
0x01
add
swap2
pop
pop
jump(0x51)
label7:
label8:
pop
jump`
st.equal(disassemble(code), asm)
})
})
...@@ -4,4 +4,4 @@ require('./traceManager.js') ...@@ -4,4 +4,4 @@ require('./traceManager.js')
require('./codeManager.js') require('./codeManager.js')
require('./util.js') require('./util.js')
require('./astwalker.js') require('./astwalker.js')
require('./disassembler.js')
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment