From 2269a01e18fb60f9c34495a1545567e818709ba4 Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Fri, 21 Dec 2018 10:27:02 +0100 Subject: [PATCH] Copy respository files and add rebar.config --- .gitignore | 1 + include/aeb_opcodes.hrl | 181 +++++++++++++++ rebar.lock | 1 + src/aeb_asm.erl | 147 ++++++++++++ src/aeb_asm_scan.xrl | 211 +++++++++++++++++ src/aeb_disassemble.erl | 102 ++++++++ src/aeb_opcodes.erl | 448 ++++++++++++++++++++++++++++++++++++ src/aeb_primops.erl | 31 +++ src/aebytecode.app.src | 14 ++ test/aebytecode_SUITE.erl | 19 ++ test/asm_code/identity.aesm | 74 ++++++ 11 files changed, 1229 insertions(+) create mode 100644 include/aeb_opcodes.hrl create mode 100644 rebar.lock create mode 100644 src/aeb_asm.erl create mode 100644 src/aeb_asm_scan.xrl create mode 100644 src/aeb_disassemble.erl create mode 100644 src/aeb_opcodes.erl create mode 100644 src/aeb_primops.erl create mode 100644 src/aebytecode.app.src create mode 100644 test/aebytecode_SUITE.erl create mode 100644 test/asm_code/identity.aesm diff --git a/.gitignore b/.gitignore index 3826c85..db21748 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ ebin/*.beam rel/example_project .concrete/DEV_MODE .rebar +aeb_asm_scan.erl diff --git a/include/aeb_opcodes.hrl b/include/aeb_opcodes.hrl new file mode 100644 index 0000000..0e7d0e0 --- /dev/null +++ b/include/aeb_opcodes.hrl @@ -0,0 +1,181 @@ + +%% AEVM opcodes +-define( 'STOP', 16#00). +-define( 'ADD', 16#01). +-define( 'MUL', 16#02). +-define( 'SUB', 16#03). +-define( 'DIV', 16#04). +-define( 'SDIV', 16#05). +-define( 'MOD', 16#06). +-define( 'SMOD', 16#07). +-define( 'ADDMOD', 16#08). +-define( 'MULMOD', 16#09). +-define( 'EXP', 16#0a). +-define( 'SIGNEXTEND', 16#0b). + +-define( 'LT', 16#10). +-define( 'GT', 16#11). +-define( 'SLT', 16#12). +-define( 'SGT', 16#13). +-define( 'EQ', 16#14). +-define( 'ISZERO', 16#15). +-define( 'AND', 16#16). +-define( 'OR', 16#17). +-define( 'XOR', 16#18). +-define( 'NOT', 16#19). +-define( 'BYTE', 16#1a). + +-define( 'SHA3', 16#20). + +-define( 'ADDRESS', 16#30). +-define( 'BALANCE', 16#31). +-define( 'ORIGIN', 16#32). +-define( 'CALLER', 16#33). +-define( 'CALLVALUE', 16#34). +-define( 'CALLDATALOAD', 16#35). +-define( 'CALLDATASIZE', 16#36). +-define( 'CALLDATACOPY', 16#37). +-define( 'CODESIZE', 16#38). +-define( 'CODECOPY', 16#39). +-define( 'GASPRICE', 16#3a). +-define( 'EXTCODESIZE', 16#3b). +-define( 'EXTCODECOPY', 16#3c). +-define( 'RETURNDATASIZE', 16#3d). +-define( 'RETURNDATACOPY', 16#3e). + +-define( 'BLOCKHASH', 16#40). +-define( 'COINBASE', 16#41). +-define( 'TIMESTAMP', 16#42). +-define( 'NUMBER', 16#43). +-define( 'DIFFICULTY', 16#44). +-define( 'GASLIMIT', 16#45). + +-define( 'POP', 16#50). +-define( 'MLOAD', 16#51). +-define( 'MSTORE', 16#52). +-define( 'MSTORE8', 16#53). +-define( 'SLOAD', 16#54). +-define( 'SSTORE', 16#55). +-define( 'JUMP', 16#56). +-define( 'JUMPI', 16#57). +-define( 'PC', 16#58). +-define( 'MSIZE', 16#59). +-define( 'GAS', 16#5a). +-define( 'JUMPDEST', 16#5b). + +-define( 'PUSH1', 16#60). +-define( 'PUSH2', 16#61). +-define( 'PUSH3', 16#62). +-define( 'PUSH4', 16#63). +-define( 'PUSH5', 16#64). +-define( 'PUSH6', 16#65). +-define( 'PUSH7', 16#66). +-define( 'PUSH8', 16#67). +-define( 'PUSH9', 16#68). +-define( 'PUSH10', 16#69). +-define( 'PUSH11', 16#6a). +-define( 'PUSH12', 16#6b). +-define( 'PUSH13', 16#6c). +-define( 'PUSH14', 16#6d). +-define( 'PUSH15', 16#6e). +-define( 'PUSH16', 16#6f). +-define( 'PUSH17', 16#70). +-define( 'PUSH18', 16#71). +-define( 'PUSH19', 16#72). +-define( 'PUSH20', 16#73). +-define( 'PUSH21', 16#74). +-define( 'PUSH22', 16#75). +-define( 'PUSH23', 16#76). +-define( 'PUSH24', 16#77). +-define( 'PUSH25', 16#78). +-define( 'PUSH26', 16#79). +-define( 'PUSH27', 16#7a). +-define( 'PUSH28', 16#7b). +-define( 'PUSH29', 16#7c). +-define( 'PUSH30', 16#7d). +-define( 'PUSH31', 16#7e). +-define( 'PUSH32', 16#7f). +-define( 'DUP1', 16#80). +-define( 'DUP2', 16#81). +-define( 'DUP3', 16#82). +-define( 'DUP4', 16#83). +-define( 'DUP5', 16#84). +-define( 'DUP6', 16#85). +-define( 'DUP7', 16#86). +-define( 'DUP8', 16#87). +-define( 'DUP9', 16#88). +-define( 'DUP10', 16#89). +-define( 'DUP11', 16#8a). +-define( 'DUP12', 16#8b). +-define( 'DUP13', 16#8c). +-define( 'DUP14', 16#8d). +-define( 'DUP15', 16#8e). +-define( 'DUP16', 16#8f). +-define( 'SWAP1', 16#90). +-define( 'SWAP2', 16#91). +-define( 'SWAP3', 16#92). +-define( 'SWAP4', 16#93). +-define( 'SWAP5', 16#94). +-define( 'SWAP6', 16#95). +-define( 'SWAP7', 16#96). +-define( 'SWAP8', 16#97). +-define( 'SWAP9', 16#98). +-define( 'SWAP10', 16#99). +-define( 'SWAP11', 16#9a). +-define( 'SWAP12', 16#9b). +-define( 'SWAP13', 16#9c). +-define( 'SWAP14', 16#9d). +-define( 'SWAP15', 16#9e). +-define( 'SWAP16', 16#9f). +-define( 'LOG0', 16#a0). +-define( 'LOG1', 16#a1). +-define( 'LOG2', 16#a2). +-define( 'LOG3', 16#a3). +-define( 'LOG4', 16#a4). + +-define( 'CREATE', 16#f0). +-define( 'CALL', 16#f1). +-define( 'CALLCODE', 16#f2). +-define( 'RETURN', 16#f3). +-define( 'DELEGATECALL', 16#f4). + +-define( 'STATICCALL', 16#fa). + +-define( 'REVERT', 16#fd). +-define( 'INVALID', 16#fe). +-define( 'SUICIDE', 16#ff). + +-define( COMMENT(X), {comment, X}). + +%% Transactions are implemented as contract calls to address zero, with the +%% first argument encoding the transaction type according to the below. + +-define(PRIM_CALLS_CONTRACT, 0). + +-define(PRIM_CALL_SPEND, 1). + +-define(PRIM_CALL_IN_ORACLE_RANGE(__TTYPE__), (((__TTYPE__) > 99) andalso ((__TTYPE__) < 200))). +-define(PRIM_CALL_ORACLE_REGISTER, 100). +-define(PRIM_CALL_ORACLE_QUERY, 101). +-define(PRIM_CALL_ORACLE_RESPOND, 102). +-define(PRIM_CALL_ORACLE_EXTEND, 103). +-define(PRIM_CALL_ORACLE_GET_ANSWER, 104). +-define(PRIM_CALL_ORACLE_GET_QUESTION, 105). +-define(PRIM_CALL_ORACLE_QUERY_FEE, 106). + +-define(PRIM_CALL_IN_AENS_RANGE(__TTYPE__), (((__TTYPE__) > 199) andalso ((__TTYPE__) < 300))). +-define(PRIM_CALL_AENS_RESOLVE, 200). +-define(PRIM_CALL_AENS_PRECLAIM, 201). +-define(PRIM_CALL_AENS_CLAIM, 202). +-define(PRIM_CALL_AENS_UPDATE, 203). +-define(PRIM_CALL_AENS_TRANSFER, 204). +-define(PRIM_CALL_AENS_REVOKE, 205). + +-define(PRIM_CALL_IN_MAP_RANGE(__TTYPE__), (((__TTYPE__) > 299) andalso ((__TTYPE__) < 400))). +-define(PRIM_CALL_MAP_EMPTY, 300). +-define(PRIM_CALL_MAP_GET, 301). +-define(PRIM_CALL_MAP_PUT, 302). +-define(PRIM_CALL_MAP_DELETE, 303). +-define(PRIM_CALL_MAP_SIZE, 304). +-define(PRIM_CALL_MAP_TOLIST, 305). + diff --git a/rebar.lock b/rebar.lock new file mode 100644 index 0000000..57afcca --- /dev/null +++ b/rebar.lock @@ -0,0 +1 @@ +[]. diff --git a/src/aeb_asm.erl b/src/aeb_asm.erl new file mode 100644 index 0000000..d463835 --- /dev/null +++ b/src/aeb_asm.erl @@ -0,0 +1,147 @@ +%%%------------------------------------------------------------------- +%%% @copyright (C) 2017, Aeternity Anstalt +%%% @doc Assembler for aevm machine code. +%%% +%%% Assembler code can be read from a file. +%%% The assembler has the following format +%%% Comments start with 2 semicolons and runs till end of line +%%% ;; This is a comment +%%% Opcode mnemonics start with an upper case letter. +%%% DUP1 +%%% Identifiers start with a lower case letter +%%% an_identifier +%%% All identifiers has to be decleard with a label. +%%% A label is an identifier that ends with a colon. +%%% The label is the byte code address of the next instruction. +%%% a_label: +%%% Immediates can be of four types: +%%% 1. integers +%%% 42 +%%% 2. hexadecimal integers starting with 0x +%%% 0x0deadbeef0 +%%% 3. 256-bit hash strings starting with # +%%% followed by up to 64 hex chars +%%% #00000deadbeef +%%% 4. labels as descibed above. +%%% +%%% @end +%%% Created : 21 Dec 2017 +%%%------------------------------------------------------------------- + +-module(aeb_asm). + +-export([ file/2 + , pp/1 + , to_hexstring/1 + ]). + +-include_lib("aebytecode/include/aeb_opcodes.hrl"). + + +pp(Asm) -> + Listing = format(Asm), + io:format("~s~n", [Listing]). + +format(Asm) -> format(Asm, 0). + +format([{comment, Comment} | Rest], Address) -> + ";; " ++ Comment ++ "\n" ++ format(Rest, Address); +format([Mnemonic | Rest], Address) -> + Op = aeb_opcodes:m_to_op(Mnemonic), + case (Op >= ?PUSH1) andalso (Op =< ?PUSH32) of + true -> + Arity = aeb_opcodes:op_size(Op) - 1, + {Args, Code} = get_args(Arity, Rest), + " " ++ atom_to_list(Mnemonic) + ++ " " ++ Args ++ "\n" + ++ format(Code, Address + Arity + 1); + false -> + " " ++ atom_to_list(Mnemonic) ++ "\n" + ++ format(Rest, Address + 1) + end; +format([],_) -> []. + +%% TODO: Are args encoded as one list element or as a number of bytes... +get_args(1, [Arg|Code]) -> + {integer_to_list(Arg), Code}; +get_args(N, [Arg|Code]) -> + {Args, Rest} = get_args(N-1, Code), + {integer_to_list(Arg) ++ ", " ++ Args, Rest}. + + + +file(Filename, Options) -> + {ok, File} = file:read_file(Filename), + {ok, Tokens, _} = aeb_asm_scan:scan(binary_to_list(File)), + + case proplists:lookup(pp_tokens, Options) of + {pp_tokens, true} -> + io:format("Tokens ~p~n",[Tokens]); + none -> + ok + end, + + ByteList = to_bytecode(Tokens, 0, #{}, [], Options), + + case proplists:lookup(pp_hex_string, Options) of + {pp_hex_string, true} -> + io:format("Code: ~s~n",[to_hexstring(ByteList)]); + none -> + ok + end, + + + list_to_binary(ByteList). + +to_hexstring(ByteList) -> + "0x" ++ lists:flatten( + [io_lib:format("~2.16.0b", [X]) + || X <- ByteList]). + + +to_bytecode([{mnemonic,_line, Op}|Rest], Address, Env, Code, Opts) -> + OpCode = aeb_opcodes:m_to_op(Op), + OpSize = aeb_opcodes:op_size(OpCode), + to_bytecode(Rest, Address + OpSize, Env, [OpCode|Code], Opts); +to_bytecode([{int,_line, Int}|Rest], Address, Env, Code, Opts) -> + to_bytecode(Rest, Address, Env, [Int|Code], Opts); +to_bytecode([{hash,_line, Hash}|Rest], Address, Env, Code, Opts) -> + to_bytecode(Rest, Address, Env, [Hash|Code], Opts); +to_bytecode([{id,_line, ID}|Rest], Address, Env, Code, Opts) -> + to_bytecode(Rest, Address, Env, [{ref, ID}|Code], Opts); +to_bytecode([{label,_line, Label}|Rest], Address, Env, Code, Opts) -> + to_bytecode(Rest, Address, Env#{Label => Address}, Code, Opts); +to_bytecode([], _Address, Env, Code, Opts) -> + case proplists:lookup(pp_opcodes, Opts) of + {pp_opcodes, true} -> + io:format("opcodes ~p~n", [lists:reverse(Code)]); + none -> + ok + end, + + PatchedCode = resolve_refs(Code, Env, []), + case proplists:lookup(pp_patched_code, Opts) of + {pp_patched_code, true} -> + io:format("Patched Code: ~p~n", [PatchedCode]); + none -> + ok + end, + + expand_args(PatchedCode). + +%% Also reverses the code (back to unreversed state). +resolve_refs([{ref, ID} | Rest], Env, Code) -> + Address = maps:get(ID, Env), + resolve_refs(Rest, Env, [Address | Code]); +resolve_refs([Op | Rest], Env, Code) -> + resolve_refs(Rest, Env, [Op | Code]); +resolve_refs([],_Env, Code) -> Code. + +expand_args([OP, Arg | Rest]) when OP >= ?PUSH1 andalso OP =< ?PUSH32 -> + BitSize = (aeb_opcodes:op_size(OP) - 1) * 8, + Bin = << << X:BitSize>> || X <- [Arg] >>, + ArgByteList = binary_to_list(Bin), + [OP | ArgByteList] ++ expand_args(Rest); +expand_args([OP | Rest]) -> + [OP | expand_args(Rest)]; +expand_args([]) -> []. diff --git a/src/aeb_asm_scan.xrl b/src/aeb_asm_scan.xrl new file mode 100644 index 0000000..0362d5c --- /dev/null +++ b/src/aeb_asm_scan.xrl @@ -0,0 +1,211 @@ +%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*- +%%%------------------------------------------------------------------- +%%% @copyright (C) 2017, Aeternity Anstalt +%%% @doc Assembler lexer. +%%% +%%% @end +%%%------------------------------------------------------------------- + +Definitions. +DIGIT = [0-9] +HEXDIGIT = [0-9a-fA-F] +LOWER = [a-z_] +UPPER = [A-Z] +INT = {DIGIT}+ +HEX = 0x{HEXDIGIT}+ +HASH = #{HEXDIGIT}+ +WS = [\000-\s] +ID = {LOWER}[a-zA-Z0-9_]* +LABEL = {ID}: + + + +Rules. +{LABEL} : {token, {label, TokenLine, TokenChars -- ":"}}. +STOP : {token, {mnemonic, TokenLine, 'STOP'}}. +ADD : {token, {mnemonic, TokenLine, 'ADD'}}. +MUL : {token, {mnemonic, TokenLine, 'MUL'}}. +SUB : {token, {mnemonic, TokenLine, 'SUB'}}. +DIV : {token, {mnemonic, TokenLine, 'DIV'}}. +SDIV : {token, {mnemonic, TokenLine, 'SDIV'}}. +MOD : {token, {mnemonic, TokenLine, 'MOD'}}. +SMOD : {token, {mnemonic, TokenLine, 'SMOD'}}. +ADDMOD : {token, {mnemonic, TokenLine, 'ADDMOD'}}. +MULMOD : {token, {mnemonic, TokenLine, 'MULMOD'}}. +EXP : {token, {mnemonic, TokenLine, 'EXP'}}. +SIGNEXTEND : {token, {mnemonic, TokenLine, 'SIGNEXTEND'}}. +LT : {token, {mnemonic, TokenLine, 'LT'}}. +GT : {token, {mnemonic, TokenLine, 'GT'}}. +SLT : {token, {mnemonic, TokenLine, 'SLT'}}. +SGT : {token, {mnemonic, TokenLine, 'SGT'}}. +EQ : {token, {mnemonic, TokenLine, 'EQ'}}. +ISZERO : {token, {mnemonic, TokenLine, 'ISZERO'}}. +AND : {token, {mnemonic, TokenLine, 'AND'}}. +OR : {token, {mnemonic, TokenLine, 'OR'}}. +XOR : {token, {mnemonic, TokenLine, 'XOR'}}. +NOT : {token, {mnemonic, TokenLine, 'NOT'}}. +BYTE : {token, {mnemonic, TokenLine, 'BYTE'}}. +SHA3 : {token, {mnemonic, TokenLine, 'SHA3'}}. +ADDRESS : {token, {mnemonic, TokenLine, 'ADDRESS'}}. +BALANCE : {token, {mnemonic, TokenLine, 'BALANCE'}}. +ORIGIN : {token, {mnemonic, TokenLine, 'ORIGIN'}}. +CALLER : {token, {mnemonic, TokenLine, 'CALLER'}}. +CALLVALUE : {token, {mnemonic, TokenLine, 'CALLVALUE'}}. +CALLDATALOAD : {token, {mnemonic, TokenLine, 'CALLDATALOAD'}}. +CALLDATASIZE : {token, {mnemonic, TokenLine, 'CALLDATASIZE'}}. +CALLDATACOPY : {token, {mnemonic, TokenLine, 'CALLDATACOPY'}}. +CODESIZE : {token, {mnemonic, TokenLine, 'CODESIZE'}}. +CODECOPY : {token, {mnemonic, TokenLine, 'CODECOPY'}}. +GASPRICE : {token, {mnemonic, TokenLine, 'GASPRICE'}}. +EXTCODESIZE : {token, {mnemonic, TokenLine, 'EXTCODESIZE'}}. +EXTCODECOPY : {token, {mnemonic, TokenLine, 'EXTCODECOPY'}}. +RETURNDATASIZE : {token, {mnemonic, TokenLine, 'RETURNDATASIZE'}}. +RETURNDATACOPY : {token, {mnemonic, TokenLine, 'RETURNDATACOPY'}}. +BLOCKHASH : {token, {mnemonic, TokenLine, 'BLOCKHASH'}}. +COINBASE : {token, {mnemonic, TokenLine, 'COINBASE'}}. +TIMESTAMP : {token, {mnemonic, TokenLine, 'TIMESTAMP'}}. +NUMBER : {token, {mnemonic, TokenLine, 'NUMBER'}}. +DIFFICULTY : {token, {mnemonic, TokenLine, 'DIFFICULTY'}}. +GASLIMIT : {token, {mnemonic, TokenLine, 'GASLIMIT'}}. +POP : {token, {mnemonic, TokenLine, 'POP'}}. +MLOAD : {token, {mnemonic, TokenLine, 'MLOAD'}}. +MSTORE : {token, {mnemonic, TokenLine, 'MSTORE'}}. +MSTORE8 : {token, {mnemonic, TokenLine, 'MSTORE8'}}. +SLOAD : {token, {mnemonic, TokenLine, 'SLOAD'}}. +SSTORE : {token, {mnemonic, TokenLine, 'SSTORE'}}. +JUMP : {token, {mnemonic, TokenLine, 'JUMP'}}. +JUMPI : {token, {mnemonic, TokenLine, 'JUMPI'}}. +PC : {token, {mnemonic, TokenLine, 'PC'}}. +MSIZE : {token, {mnemonic, TokenLine, 'MSIZE'}}. +GAS : {token, {mnemonic, TokenLine, 'GAS'}}. +JUMPDEST : {token, {mnemonic, TokenLine, 'JUMPDEST'}}. +PUSH1 : {token, {mnemonic, TokenLine, 'PUSH1'}}. +PUSH2 : {token, {mnemonic, TokenLine, 'PUSH2'}}. +PUSH3 : {token, {mnemonic, TokenLine, 'PUSH3'}}. +PUSH4 : {token, {mnemonic, TokenLine, 'PUSH4'}}. +PUSH5 : {token, {mnemonic, TokenLine, 'PUSH5'}}. +PUSH6 : {token, {mnemonic, TokenLine, 'PUSH6'}}. +PUSH7 : {token, {mnemonic, TokenLine, 'PUSH7'}}. +PUSH8 : {token, {mnemonic, TokenLine, 'PUSH8'}}. +PUSH9 : {token, {mnemonic, TokenLine, 'PUSH9'}}. +PUSH10 : {token, {mnemonic, TokenLine, 'PUSH10'}}. +PUSH11 : {token, {mnemonic, TokenLine, 'PUSH11'}}. +PUSH12 : {token, {mnemonic, TokenLine, 'PUSH12'}}. +PUSH13 : {token, {mnemonic, TokenLine, 'PUSH13'}}. +PUSH14 : {token, {mnemonic, TokenLine, 'PUSH14'}}. +PUSH15 : {token, {mnemonic, TokenLine, 'PUSH15'}}. +PUSH16 : {token, {mnemonic, TokenLine, 'PUSH16'}}. +PUSH17 : {token, {mnemonic, TokenLine, 'PUSH17'}}. +PUSH18 : {token, {mnemonic, TokenLine, 'PUSH18'}}. +PUSH19 : {token, {mnemonic, TokenLine, 'PUSH19'}}. +PUSH20 : {token, {mnemonic, TokenLine, 'PUSH20'}}. +PUSH21 : {token, {mnemonic, TokenLine, 'PUSH21'}}. +PUSH22 : {token, {mnemonic, TokenLine, 'PUSH22'}}. +PUSH23 : {token, {mnemonic, TokenLine, 'PUSH23'}}. +PUSH24 : {token, {mnemonic, TokenLine, 'PUSH24'}}. +PUSH25 : {token, {mnemonic, TokenLine, 'PUSH25'}}. +PUSH26 : {token, {mnemonic, TokenLine, 'PUSH26'}}. +PUSH27 : {token, {mnemonic, TokenLine, 'PUSH27'}}. +PUSH28 : {token, {mnemonic, TokenLine, 'PUSH28'}}. +PUSH29 : {token, {mnemonic, TokenLine, 'PUSH29'}}. +PUSH30 : {token, {mnemonic, TokenLine, 'PUSH30'}}. +PUSH31 : {token, {mnemonic, TokenLine, 'PUSH31'}}. +PUSH32 : {token, {mnemonic, TokenLine, 'PUSH32'}}. +DUP1 : {token, {mnemonic, TokenLine, 'DUP1'}}. +DUP2 : {token, {mnemonic, TokenLine, 'DUP2'}}. +DUP3 : {token, {mnemonic, TokenLine, 'DUP3'}}. +DUP4 : {token, {mnemonic, TokenLine, 'DUP4'}}. +DUP5 : {token, {mnemonic, TokenLine, 'DUP5'}}. +DUP6 : {token, {mnemonic, TokenLine, 'DUP6'}}. +DUP7 : {token, {mnemonic, TokenLine, 'DUP7'}}. +DUP8 : {token, {mnemonic, TokenLine, 'DUP8'}}. +DUP9 : {token, {mnemonic, TokenLine, 'DUP9'}}. +DUP10 : {token, {mnemonic, TokenLine, 'DUP10'}}. +DUP11 : {token, {mnemonic, TokenLine, 'DUP11'}}. +DUP12 : {token, {mnemonic, TokenLine, 'DUP12'}}. +DUP13 : {token, {mnemonic, TokenLine, 'DUP13'}}. +DUP14 : {token, {mnemonic, TokenLine, 'DUP14'}}. +DUP15 : {token, {mnemonic, TokenLine, 'DUP15'}}. +DUP16 : {token, {mnemonic, TokenLine, 'DUP16'}}. +SWAP1 : {token, {mnemonic, TokenLine, 'SWAP1'}}. +SWAP2 : {token, {mnemonic, TokenLine, 'SWAP2'}}. +SWAP3 : {token, {mnemonic, TokenLine, 'SWAP3'}}. +SWAP4 : {token, {mnemonic, TokenLine, 'SWAP4'}}. +SWAP5 : {token, {mnemonic, TokenLine, 'SWAP5'}}. +SWAP6 : {token, {mnemonic, TokenLine, 'SWAP6'}}. +SWAP7 : {token, {mnemonic, TokenLine, 'SWAP7'}}. +SWAP8 : {token, {mnemonic, TokenLine, 'SWAP8'}}. +SWAP9 : {token, {mnemonic, TokenLine, 'SWAP9'}}. +SWAP10 : {token, {mnemonic, TokenLine, 'SWAP10'}}. +SWAP11 : {token, {mnemonic, TokenLine, 'SWAP11'}}. +SWAP12 : {token, {mnemonic, TokenLine, 'SWAP12'}}. +SWAP13 : {token, {mnemonic, TokenLine, 'SWAP13'}}. +SWAP14 : {token, {mnemonic, TokenLine, 'SWAP14'}}. +SWAP15 : {token, {mnemonic, TokenLine, 'SWAP15'}}. +SWAP16 : {token, {mnemonic, TokenLine, 'SWAP16'}}. +LOG0 : {token, {mnemonic, TokenLine, 'LOG0'}}. +LOG1 : {token, {mnemonic, TokenLine, 'LOG1'}}. +LOG2 : {token, {mnemonic, TokenLine, 'LOG2'}}. +LOG3 : {token, {mnemonic, TokenLine, 'LOG3'}}. +LOG4 : {token, {mnemonic, TokenLine, 'LOG4'}}. +CREATE : {token, {mnemonic, TokenLine, 'CREATE'}}. +CALL : {token, {mnemonic, TokenLine, 'CALL'}}. +CALLCODE : {token, {mnemonic, TokenLine, 'CALLCODE'}}. +RETURN : {token, {mnemonic, TokenLine, 'RETURN'}}. +DELEGATECALL : {token, {mnemonic, TokenLine, 'DELEGATECALL'}}. +STATICCALL : {token, {mnemonic, TokenLine, 'STATICCALL'}}. +REVERT : {token, {mnemonic, TokenLine, 'REVERT'}}. +INVALID : {token, {mnemonic, TokenLine, 'INVALID'}}. +SUICIDE : {token, {mnemonic, TokenLine, 'SUICIDE'}}. +COMMENT : {token, {mnemonic, TokenLine, 'COMMENT'}}. +{ID} : + {token, {id, TokenLine, TokenChars}}. +{HEX} : + {token, {int, TokenLine, parse_hex(TokenChars)}}. +{INT} : + {token, {int, TokenLine, parse_int(TokenChars)}}. +{HASH} : + {token, {hash, TokenLine, parse_hash(TokenChars)}}. + + +%% Symbols +, : {token, {',', TokenLine}}. +\. : {token, {'.', TokenLine}}. +\( : {token, {'(', TokenLine}}. +\) : {token, {')', TokenLine}}. +\[ : {token, {'[', TokenLine}}. +\] : {token, {']', TokenLine}}. +{ : {token, {'{', TokenLine}}. +} : {token, {'}', TokenLine}}. + + +%% Whitespace ignore +{WS} : skip_token. + +%% Comments (TODO: nested comments) +;;.* : skip_token. + +. : {error, "Unexpected token: " ++ TokenChars}. + +Erlang code. + +-export([scan/1]). + +-dialyzer({nowarn_function, yyrev/2}). + +-ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]). + +-include_lib("aebytecode/include/aeb_opcodes.hrl"). + + +parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16). + +parse_int(Chars) -> list_to_integer(Chars). + +parse_hash("#" ++ Chars) -> + N = list_to_integer(Chars, 16), + <>. + +scan(S) -> + string(S). + diff --git a/src/aeb_disassemble.erl b/src/aeb_disassemble.erl new file mode 100644 index 0000000..621fa7d --- /dev/null +++ b/src/aeb_disassemble.erl @@ -0,0 +1,102 @@ +%%%------------------------------------------------------------------- +%%% @copyright (C) 2017, Aeternity Anstalt +%%% @doc +%%% Prettyprint aevm machine code +%%% @end +%%% Created : 2 Oct 2017 +%%%------------------------------------------------------------------- + +-module(aeb_disassemble). + +-export([ pp/1, + format/2, + format_address/1 + ]). + +-include_lib("aebytecode/include/aeb_opcodes.hrl"). + + +pp(Binary) -> + Listing = format(Binary, fun io:format/2), + io:format("~s~n", [Listing]). + +format(Binary, ErrFormatFun) -> + pp(0, binary:bin_to_list(Binary), [], ErrFormatFun). + +pp(Address, [Op|Ops], Assembly, ErrFormatFun) -> + case Op of + X when (X >= ?STOP) andalso (X =< ?SIGNEXTEND) -> + Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), + next(Address, Ops, Instr, Assembly, ErrFormatFun); + X when (X >= ?LT) andalso (X =< ?BYTE) -> + Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), + next(Address, Ops, Instr, Assembly, ErrFormatFun); + X when (X >= ?SHA3) andalso (X =< ?SHA3) -> + Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), + next(Address, Ops, Instr, Assembly, ErrFormatFun); + X when (X >= ?ADDRESS) andalso (X =< ?EXTCODECOPY) -> + Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), + next(Address, Ops, Instr, Assembly, ErrFormatFun); + X when (X >= ?BLOCKHASH) andalso (X =< ?GASLIMIT) -> + Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), + next(Address, Ops, Instr, Assembly, ErrFormatFun); + X when (X >= ?POP) andalso (X =< ?JUMPDEST) -> + Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), + next(Address, Ops, Instr, Assembly, ErrFormatFun); + X when (X >= ?PUSH1) andalso (X =< ?PUSH32) -> + Bytes = X-?PUSH1+1, + {ArgList, NextOps} = lists:split(Bytes, Ops), + Arg = arglist_to_arg(ArgList), + Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), [{Arg,8*Bytes}]), + next(Address+Bytes, NextOps, Instr, Assembly, ErrFormatFun); + X when (X >= ?DUP1) andalso (X =< ?LOG4) -> + Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), + next(Address, Ops, Instr, Assembly, ErrFormatFun); + X when (X >= ?CREATE) andalso (X =< ?DELEGATECALL) -> + Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), + next(Address, Ops, Instr, Assembly, ErrFormatFun); + X when (X >= ?INVALID) andalso (X =< ?SUICIDE) -> + Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), + next(Address, Ops, Instr, Assembly, ErrFormatFun); + _ -> + ErrFormatFun("unhandled op ~p at ~p",[Op, Address]), + next(Address, Ops, "", Assembly, ErrFormatFun) + end; +pp(_, [], Assembly, _) -> lists:reverse(Assembly). + + +arglist_to_arg([B|Bs]) -> + arglist_to_arg(Bs, B). + +arglist_to_arg([B|Bs], Acc) -> + arglist_to_arg(Bs, Acc*256 + B); +arglist_to_arg([], Acc) -> Acc. + +pp_instruction(Address, Op, Args) -> + [format_address(Address), " ", + pad_op(atom_to_list(Op)), + pp_args(Args), + "\n"]. + +format_address(Address) -> + io_lib:format("0x~8.16.0B",[Address]). + +pad_op(Op) -> + N = length(Op), + Pad = 17 - N, + [Op,lists:duplicate(Pad, 32)]. + +pp_args([]) -> []; +pp_args([{Arg, Size}]) -> + case Size of + 8 -> io_lib:format("0x~2.16.0B",[Arg]); + 160 -> io_lib:format("0x~64.16.0B",[Arg]); + 232 -> io_lib:format("0x~64.16.0B",[Arg]); + 256 -> io_lib:format("0x~64.16.0B",[Arg]); + _ -> io_lib:format("0x~64.16.0B",[Arg]) + end; +pp_args([{Arg, Size}|Args]) -> + [pp_args([{Arg, Size}]), " ", pp_args(Args)]. + +next(Address, Ops, Instr, Assembly, ErrFormatFun) -> + pp(Address+1, Ops, [Instr|Assembly], ErrFormatFun). diff --git a/src/aeb_opcodes.erl b/src/aeb_opcodes.erl new file mode 100644 index 0000000..75aeb1e --- /dev/null +++ b/src/aeb_opcodes.erl @@ -0,0 +1,448 @@ +%%%------------------------------------------------------------------- +%%% @copyright (C) 2017, Aeternity Anstalt +%%% @doc +%%% Opcodes +%%% @end +%%% Created : 2 Oct 2017 +%%%------------------------------------------------------------------- + +-module(aeb_opcodes). + +-export([ dup/1 + , mnemonic/1 + , m_to_op/1 + , opcode/1 + , op_size/1 + , push/1 + , swap/1 + ]). + +-include_lib("aebytecode/include/aeb_opcodes.hrl"). + + +%%==================================================================== +%% API +%%==================================================================== + +opcode(?STOP) -> ?STOP; +opcode(?ADD) -> ?ADD; +opcode(?MUL) -> ?MUL; +opcode(?SUB) -> ?SUB; +opcode(?DIV) -> ?DIV; +opcode(?SDIV) -> ?SDIV; +opcode(?MOD) -> ?MOD; +opcode(?SMOD) -> ?SMOD; +opcode(?ADDMOD) -> ?ADDMOD; +opcode(?MULMOD) -> ?MULMOD; +opcode(?EXP) -> ?EXP; +opcode(?SIGNEXTEND) -> ?SIGNEXTEND; +opcode(?LT) -> ?LT; +opcode(?GT) -> ?GT; +opcode(?SLT) -> ?SLT; +opcode(?SGT) -> ?SGT; +opcode(?EQ) -> ?EQ; +opcode(?ISZERO) -> ?ISZERO; +opcode(?AND) -> ?AND; +opcode(?OR) -> ?OR; +opcode(?XOR) -> ?XOR; +opcode(?NOT) -> ?NOT; +opcode(?BYTE) -> ?BYTE; +opcode(?SHA3) -> ?SHA3; +opcode(?ADDRESS) -> ?ADDRESS; +opcode(?BALANCE) -> ?BALANCE; +opcode(?ORIGIN) -> ?ORIGIN; +opcode(?CALLER) -> ?CALLER; +opcode(?CALLVALUE) -> ?CALLVALUE; +opcode(?CALLDATALOAD) -> ?CALLDATALOAD; +opcode(?CALLDATASIZE) -> ?CALLDATASIZE; +opcode(?CALLDATACOPY) -> ?CALLDATACOPY; +opcode(?CODESIZE) -> ?CODESIZE; +opcode(?CODECOPY) -> ?CODECOPY; +opcode(?GASPRICE) -> ?GASPRICE; +opcode(?EXTCODESIZE) -> ?EXTCODESIZE; +opcode(?EXTCODECOPY) -> ?EXTCODECOPY; +opcode(?RETURNDATASIZE) -> ?RETURNDATASIZE; %% TODO +opcode(?RETURNDATACOPY) -> ?RETURNDATACOPY; %% TODO +opcode(?BLOCKHASH) -> ?BLOCKHASH; +opcode(?COINBASE) -> ?COINBASE; +opcode(?TIMESTAMP) -> ?TIMESTAMP; +opcode(?NUMBER) -> ?NUMBER; +opcode(?DIFFICULTY) -> ?DIFFICULTY; +opcode(?GASLIMIT) -> ?GASLIMIT; +opcode(?POP) -> ?POP; +opcode(?MLOAD) -> ?MLOAD; +opcode(?MSTORE) -> ?MSTORE; +opcode(?MSTORE8) -> ?MSTORE8; +opcode(?SLOAD) -> ?SLOAD; +opcode(?SSTORE) -> ?SSTORE; +opcode(?JUMP) -> ?JUMP; +opcode(?JUMPI) -> ?JUMPI; +opcode(?PC) -> ?PC; +opcode(?MSIZE) -> ?MSIZE; +opcode(?GAS) -> ?GAS; +opcode(?JUMPDEST) -> ?JUMPDEST; +opcode(?PUSH1) -> ?PUSH1; +opcode(?PUSH2) -> ?PUSH2; +opcode(?PUSH3) -> ?PUSH3; +opcode(?PUSH4) -> ?PUSH4; +opcode(?PUSH5) -> ?PUSH5; +opcode(?PUSH6) -> ?PUSH6; +opcode(?PUSH7) -> ?PUSH7; +opcode(?PUSH8) -> ?PUSH8; +opcode(?PUSH9) -> ?PUSH9; +opcode(?PUSH10) -> ?PUSH10; +opcode(?PUSH11) -> ?PUSH11; +opcode(?PUSH12) -> ?PUSH12; +opcode(?PUSH13) -> ?PUSH13; +opcode(?PUSH14) -> ?PUSH14; +opcode(?PUSH15) -> ?PUSH15; +opcode(?PUSH16) -> ?PUSH16; +opcode(?PUSH17) -> ?PUSH17; +opcode(?PUSH18) -> ?PUSH18; +opcode(?PUSH19) -> ?PUSH19; +opcode(?PUSH20) -> ?PUSH20; +opcode(?PUSH21) -> ?PUSH21; +opcode(?PUSH22) -> ?PUSH22; +opcode(?PUSH23) -> ?PUSH23; +opcode(?PUSH24) -> ?PUSH24; +opcode(?PUSH25) -> ?PUSH25; +opcode(?PUSH26) -> ?PUSH26; +opcode(?PUSH27) -> ?PUSH27; +opcode(?PUSH28) -> ?PUSH28; +opcode(?PUSH29) -> ?PUSH29; +opcode(?PUSH30) -> ?PUSH30; +opcode(?PUSH31) -> ?PUSH31; +opcode(?PUSH32) -> ?PUSH32; +opcode(?DUP1) -> ?DUP1; +opcode(?DUP2) -> ?DUP2; +opcode(?DUP3) -> ?DUP3; +opcode(?DUP4) -> ?DUP4; +opcode(?DUP5) -> ?DUP5; +opcode(?DUP6) -> ?DUP6; +opcode(?DUP7) -> ?DUP7; +opcode(?DUP8) -> ?DUP8; +opcode(?DUP9) -> ?DUP9; +opcode(?DUP10) -> ?DUP10; +opcode(?DUP11) -> ?DUP11; +opcode(?DUP12) -> ?DUP12; +opcode(?DUP13) -> ?DUP13; +opcode(?DUP14) -> ?DUP14; +opcode(?DUP15) -> ?DUP15; +opcode(?DUP16) -> ?DUP16; +opcode(?SWAP1) -> ?SWAP1; +opcode(?SWAP2) -> ?SWAP2; +opcode(?SWAP3) -> ?SWAP3; +opcode(?SWAP4) -> ?SWAP4; +opcode(?SWAP5) -> ?SWAP5; +opcode(?SWAP6) -> ?SWAP6; +opcode(?SWAP7) -> ?SWAP7; +opcode(?SWAP8) -> ?SWAP8; +opcode(?SWAP9) -> ?SWAP9; +opcode(?SWAP10) -> ?SWAP10; +opcode(?SWAP11) -> ?SWAP11; +opcode(?SWAP12) -> ?SWAP12; +opcode(?SWAP13) -> ?SWAP13; +opcode(?SWAP14) -> ?SWAP14; +opcode(?SWAP15) -> ?SWAP15; +opcode(?SWAP16) -> ?SWAP16; +opcode(?LOG0) -> ?LOG0; +opcode(?LOG1) -> ?LOG1; +opcode(?LOG2) -> ?LOG2; +opcode(?LOG3) -> ?LOG3; +opcode(?LOG4) -> ?LOG4; +opcode(?CREATE) -> ?CREATE; +opcode(?CALL) -> ?CALL; +opcode(?CALLCODE) -> ?CALLCODE; +opcode(?RETURN) -> ?RETURN; +opcode(?DELEGATECALL) -> ?DELEGATECALL; +opcode(?STATICCALL) -> ?STATICCALL; %% TODO +opcode(?REVERT) -> ?REVERT; +opcode({comment,X}) -> ?COMMENT(X); +opcode(?SUICIDE) -> ?SUICIDE. + + +mnemonic(?STOP) -> 'STOP' ; +mnemonic(?ADD) -> 'ADD' ; +mnemonic(?MUL) -> 'MUL' ; +mnemonic(?SUB) -> 'SUB' ; +mnemonic(?DIV) -> 'DIV' ; +mnemonic(?SDIV) -> 'SDIV' ; +mnemonic(?MOD) -> 'MOD' ; +mnemonic(?SMOD) -> 'SMOD' ; +mnemonic(?ADDMOD) -> 'ADDMOD' ; +mnemonic(?MULMOD) -> 'MULMOD' ; +mnemonic(?EXP) -> 'EXP' ; +mnemonic(?SIGNEXTEND) -> 'SIGNEXTEND' ; +mnemonic(?LT) -> 'LT' ; +mnemonic(?GT) -> 'GT' ; +mnemonic(?SLT) -> 'SLT' ; +mnemonic(?SGT) -> 'SGT' ; +mnemonic(?EQ) -> 'EQ' ; +mnemonic(?ISZERO) -> 'ISZERO' ; +mnemonic(?AND) -> 'AND' ; +mnemonic(?OR) -> 'OR' ; +mnemonic(?XOR) -> 'XOR' ; +mnemonic(?NOT) -> 'NOT' ; +mnemonic(?BYTE) -> 'BYTE' ; +mnemonic(?SHA3) -> 'SHA3' ; +mnemonic(?ADDRESS) -> 'ADDRESS' ; +mnemonic(?BALANCE) -> 'BALANCE' ; +mnemonic(?ORIGIN) -> 'ORIGIN' ; +mnemonic(?CALLER) -> 'CALLER' ; +mnemonic(?CALLVALUE) -> 'CALLVALUE' ; +mnemonic(?CALLDATALOAD) -> 'CALLDATALOAD' ; +mnemonic(?CALLDATASIZE) -> 'CALLDATASIZE' ; +mnemonic(?CALLDATACOPY) -> 'CALLDATACOPY' ; +mnemonic(?CODESIZE) -> 'CODESIZE' ; +mnemonic(?CODECOPY) -> 'CODECOPY' ; +mnemonic(?GASPRICE) -> 'GASPRICE' ; +mnemonic(?EXTCODESIZE) -> 'EXTCODESIZE' ; +mnemonic(?EXTCODECOPY) -> 'EXTCODECOPY' ; +mnemonic(?RETURNDATASIZE) -> 'RETURNDATASIZE' ; +mnemonic(?RETURNDATACOPY) -> 'RETURNDATACOPY' ; +mnemonic(?BLOCKHASH) -> 'BLOCKHASH' ; +mnemonic(?COINBASE) -> 'COINBASE' ; +mnemonic(?TIMESTAMP) -> 'TIMESTAMP' ; +mnemonic(?NUMBER) -> 'NUMBER' ; +mnemonic(?DIFFICULTY) -> 'DIFFICULTY' ; +mnemonic(?GASLIMIT) -> 'GASLIMIT' ; +mnemonic(?POP) -> 'POP' ; +mnemonic(?MLOAD) -> 'MLOAD' ; +mnemonic(?MSTORE) -> 'MSTORE' ; +mnemonic(?MSTORE8) -> 'MSTORE8' ; +mnemonic(?SLOAD) -> 'SLOAD' ; +mnemonic(?SSTORE) -> 'SSTORE' ; +mnemonic(?JUMP) -> 'JUMP' ; +mnemonic(?JUMPI) -> 'JUMPI' ; +mnemonic(?PC) -> 'PC' ; +mnemonic(?MSIZE) -> 'MSIZE' ; +mnemonic(?GAS) -> 'GAS' ; +mnemonic(?JUMPDEST) -> 'JUMPDEST' ; +mnemonic(?PUSH1) -> 'PUSH1' ; +mnemonic(?PUSH2) -> 'PUSH2' ; +mnemonic(?PUSH3) -> 'PUSH3' ; +mnemonic(?PUSH4) -> 'PUSH4' ; +mnemonic(?PUSH5) -> 'PUSH5' ; +mnemonic(?PUSH6) -> 'PUSH6' ; +mnemonic(?PUSH7) -> 'PUSH7' ; +mnemonic(?PUSH8) -> 'PUSH8' ; +mnemonic(?PUSH9) -> 'PUSH9' ; +mnemonic(?PUSH10) -> 'PUSH10' ; +mnemonic(?PUSH11) -> 'PUSH11' ; +mnemonic(?PUSH12) -> 'PUSH12' ; +mnemonic(?PUSH13) -> 'PUSH13' ; +mnemonic(?PUSH14) -> 'PUSH14' ; +mnemonic(?PUSH15) -> 'PUSH15' ; +mnemonic(?PUSH16) -> 'PUSH16' ; +mnemonic(?PUSH17) -> 'PUSH17' ; +mnemonic(?PUSH18) -> 'PUSH18' ; +mnemonic(?PUSH19) -> 'PUSH19' ; +mnemonic(?PUSH20) -> 'PUSH20' ; +mnemonic(?PUSH21) -> 'PUSH21' ; +mnemonic(?PUSH22) -> 'PUSH22' ; +mnemonic(?PUSH23) -> 'PUSH23' ; +mnemonic(?PUSH24) -> 'PUSH24' ; +mnemonic(?PUSH25) -> 'PUSH25' ; +mnemonic(?PUSH26) -> 'PUSH26' ; +mnemonic(?PUSH27) -> 'PUSH27' ; +mnemonic(?PUSH28) -> 'PUSH28' ; +mnemonic(?PUSH29) -> 'PUSH29' ; +mnemonic(?PUSH30) -> 'PUSH30' ; +mnemonic(?PUSH31) -> 'PUSH31' ; +mnemonic(?PUSH32) -> 'PUSH32' ; +mnemonic(?DUP1) -> 'DUP1' ; +mnemonic(?DUP2) -> 'DUP2' ; +mnemonic(?DUP3) -> 'DUP3' ; +mnemonic(?DUP4) -> 'DUP4' ; +mnemonic(?DUP5) -> 'DUP5' ; +mnemonic(?DUP6) -> 'DUP6' ; +mnemonic(?DUP7) -> 'DUP7' ; +mnemonic(?DUP8) -> 'DUP8' ; +mnemonic(?DUP9) -> 'DUP9' ; +mnemonic(?DUP10) -> 'DUP10' ; +mnemonic(?DUP11) -> 'DUP11' ; +mnemonic(?DUP12) -> 'DUP12' ; +mnemonic(?DUP13) -> 'DUP13' ; +mnemonic(?DUP14) -> 'DUP14' ; +mnemonic(?DUP15) -> 'DUP15' ; +mnemonic(?DUP16) -> 'DUP16' ; +mnemonic(?SWAP1) -> 'SWAP1' ; +mnemonic(?SWAP2) -> 'SWAP2' ; +mnemonic(?SWAP3) -> 'SWAP3' ; +mnemonic(?SWAP4) -> 'SWAP4' ; +mnemonic(?SWAP5) -> 'SWAP5' ; +mnemonic(?SWAP6) -> 'SWAP6' ; +mnemonic(?SWAP7) -> 'SWAP7' ; +mnemonic(?SWAP8) -> 'SWAP8' ; +mnemonic(?SWAP9) -> 'SWAP9' ; +mnemonic(?SWAP10) -> 'SWAP10' ; +mnemonic(?SWAP11) -> 'SWAP11' ; +mnemonic(?SWAP12) -> 'SWAP12' ; +mnemonic(?SWAP13) -> 'SWAP13' ; +mnemonic(?SWAP14) -> 'SWAP14' ; +mnemonic(?SWAP15) -> 'SWAP15' ; +mnemonic(?SWAP16) -> 'SWAP16' ; +mnemonic(?LOG0) -> 'LOG0' ; +mnemonic(?LOG1) -> 'LOG1' ; +mnemonic(?LOG2) -> 'LOG2' ; +mnemonic(?LOG3) -> 'LOG3' ; +mnemonic(?LOG4) -> 'LOG4' ; +mnemonic(?CREATE) -> 'CREATE' ; +mnemonic(?CALL) -> 'CALL' ; +mnemonic(?CALLCODE) -> 'CALLCODE' ; +mnemonic(?RETURN) -> 'RETURN' ; +mnemonic(?DELEGATECALL) -> 'DELEGATECALL' ; +mnemonic(?STATICCALL) -> 'STATICCALL' ; +mnemonic(?REVERT) -> 'REVERT' ; +mnemonic({comment,_}) -> 'COMMENT' ; +mnemonic(?SUICIDE) -> 'SUICIDE' . + + + +m_to_op('STOP') -> ?STOP ; +m_to_op('ADD') -> ?ADD ; +m_to_op('MUL') -> ?MUL ; +m_to_op('SUB') -> ?SUB ; +m_to_op('DIV') -> ?DIV ; +m_to_op('SDIV') -> ?SDIV ; +m_to_op('MOD') -> ?MOD ; +m_to_op('SMOD') -> ?SMOD ; +m_to_op('ADDMOD') -> ?ADDMOD ; +m_to_op('MULMOD') -> ?MULMOD ; +m_to_op('EXP') -> ?EXP ; +m_to_op('SIGNEXTEND') -> ?SIGNEXTEND ; +m_to_op('LT') -> ?LT ; +m_to_op('GT') -> ?GT ; +m_to_op('SLT') -> ?SLT ; +m_to_op('SGT') -> ?SGT ; +m_to_op('EQ') -> ?EQ ; +m_to_op('ISZERO') -> ?ISZERO ; +m_to_op('AND') -> ?AND ; +m_to_op('OR') -> ?OR ; +m_to_op('XOR') -> ?XOR ; +m_to_op('NOT') -> ?NOT ; +m_to_op('BYTE') -> ?BYTE ; +m_to_op('SHA3') -> ?SHA3 ; +m_to_op('ADDRESS') -> ?ADDRESS ; +m_to_op('BALANCE') -> ?BALANCE ; +m_to_op('ORIGIN') -> ?ORIGIN ; +m_to_op('CALLER') -> ?CALLER ; +m_to_op('CALLVALUE') -> ?CALLVALUE ; +m_to_op('CALLDATALOAD') -> ?CALLDATALOAD ; +m_to_op('CALLDATASIZE') -> ?CALLDATASIZE ; +m_to_op('CALLDATACOPY') -> ?CALLDATACOPY ; +m_to_op('CODESIZE') -> ?CODESIZE ; +m_to_op('CODECOPY') -> ?CODECOPY ; +m_to_op('GASPRICE') -> ?GASPRICE ; +m_to_op('EXTCODESIZE') -> ?EXTCODESIZE ; +m_to_op('EXTCODECOPY') -> ?EXTCODECOPY ; +m_to_op('RETURNDATASIZE') -> ?RETURNDATASIZE ; +m_to_op('RETURNDATACOPY') -> ?RETURNDATACOPY ; +m_to_op('BLOCKHASH') -> ?BLOCKHASH ; +m_to_op('COINBASE') -> ?COINBASE ; +m_to_op('TIMESTAMP') -> ?TIMESTAMP ; +m_to_op('NUMBER') -> ?NUMBER ; +m_to_op('DIFFICULTY') -> ?DIFFICULTY ; +m_to_op('GASLIMIT') -> ?GASLIMIT ; +m_to_op('POP') -> ?POP ; +m_to_op('MLOAD') -> ?MLOAD ; +m_to_op('MSTORE') -> ?MSTORE ; +m_to_op('MSTORE8') -> ?MSTORE8 ; +m_to_op('SLOAD') -> ?SLOAD ; +m_to_op('SSTORE') -> ?SSTORE ; +m_to_op('JUMP') -> ?JUMP ; +m_to_op('JUMPI') -> ?JUMPI ; +m_to_op('PC') -> ?PC ; +m_to_op('MSIZE') -> ?MSIZE ; +m_to_op('GAS') -> ?GAS ; +m_to_op('JUMPDEST') -> ?JUMPDEST ; +m_to_op('PUSH1') -> ?PUSH1 ; +m_to_op('PUSH2') -> ?PUSH2 ; +m_to_op('PUSH3') -> ?PUSH3 ; +m_to_op('PUSH4') -> ?PUSH4 ; +m_to_op('PUSH5') -> ?PUSH5 ; +m_to_op('PUSH6') -> ?PUSH6 ; +m_to_op('PUSH7') -> ?PUSH7 ; +m_to_op('PUSH8') -> ?PUSH8 ; +m_to_op('PUSH9') -> ?PUSH9 ; +m_to_op('PUSH10') -> ?PUSH10 ; +m_to_op('PUSH11') -> ?PUSH11 ; +m_to_op('PUSH12') -> ?PUSH12 ; +m_to_op('PUSH13') -> ?PUSH13 ; +m_to_op('PUSH14') -> ?PUSH14 ; +m_to_op('PUSH15') -> ?PUSH15 ; +m_to_op('PUSH16') -> ?PUSH16 ; +m_to_op('PUSH17') -> ?PUSH17 ; +m_to_op('PUSH18') -> ?PUSH18 ; +m_to_op('PUSH19') -> ?PUSH19 ; +m_to_op('PUSH20') -> ?PUSH20 ; +m_to_op('PUSH21') -> ?PUSH21 ; +m_to_op('PUSH22') -> ?PUSH22 ; +m_to_op('PUSH23') -> ?PUSH23 ; +m_to_op('PUSH24') -> ?PUSH24 ; +m_to_op('PUSH25') -> ?PUSH25 ; +m_to_op('PUSH26') -> ?PUSH26 ; +m_to_op('PUSH27') -> ?PUSH27 ; +m_to_op('PUSH28') -> ?PUSH28 ; +m_to_op('PUSH29') -> ?PUSH29 ; +m_to_op('PUSH30') -> ?PUSH30 ; +m_to_op('PUSH31') -> ?PUSH31 ; +m_to_op('PUSH32') -> ?PUSH32 ; +m_to_op('DUP1') -> ?DUP1 ; +m_to_op('DUP2') -> ?DUP2 ; +m_to_op('DUP3') -> ?DUP3 ; +m_to_op('DUP4') -> ?DUP4 ; +m_to_op('DUP5') -> ?DUP5 ; +m_to_op('DUP6') -> ?DUP6 ; +m_to_op('DUP7') -> ?DUP7 ; +m_to_op('DUP8') -> ?DUP8 ; +m_to_op('DUP9') -> ?DUP9 ; +m_to_op('DUP10') -> ?DUP10 ; +m_to_op('DUP11') -> ?DUP11 ; +m_to_op('DUP12') -> ?DUP12 ; +m_to_op('DUP13') -> ?DUP13 ; +m_to_op('DUP14') -> ?DUP14 ; +m_to_op('DUP15') -> ?DUP15 ; +m_to_op('DUP16') -> ?DUP16 ; +m_to_op('SWAP1') -> ?SWAP1 ; +m_to_op('SWAP2') -> ?SWAP2 ; +m_to_op('SWAP3') -> ?SWAP3 ; +m_to_op('SWAP4') -> ?SWAP4 ; +m_to_op('SWAP5') -> ?SWAP5 ; +m_to_op('SWAP6') -> ?SWAP6 ; +m_to_op('SWAP7') -> ?SWAP7 ; +m_to_op('SWAP8') -> ?SWAP8 ; +m_to_op('SWAP9') -> ?SWAP9 ; +m_to_op('SWAP10') -> ?SWAP10 ; +m_to_op('SWAP11') -> ?SWAP11 ; +m_to_op('SWAP12') -> ?SWAP12 ; +m_to_op('SWAP13') -> ?SWAP13 ; +m_to_op('SWAP14') -> ?SWAP14 ; +m_to_op('SWAP15') -> ?SWAP15 ; +m_to_op('SWAP16') -> ?SWAP16 ; +m_to_op('LOG0') -> ?LOG0 ; +m_to_op('LOG1') -> ?LOG1 ; +m_to_op('LOG2') -> ?LOG2 ; +m_to_op('LOG3') -> ?LOG3 ; +m_to_op('LOG4') -> ?LOG4 ; +m_to_op('CREATE') -> ?CREATE ; +m_to_op('CALL') -> ?CALL ; +m_to_op('CALLCODE') -> ?CALLCODE ; +m_to_op('RETURN') -> ?RETURN ; +m_to_op('DELEGATECALL') -> ?DELEGATECALL ; +m_to_op('STATICCALL') -> ?STATICCALL ; +m_to_op('REVERT') -> ?REVERT ; +m_to_op('COMMENT') -> ?COMMENT("") ; +m_to_op('SUICIDE') -> ?SUICIDE ; +m_to_op(Data) when 0= Data . + +push(N) when N >= 1, N =< 32 -> ?PUSH1 + N - 1. +dup(N) when N >= 1, N =< 16 -> ?DUP1 + N - 1. +swap(N) when N >= 1, N =< 16 -> ?SWAP1 + N - 1. + +op_size(OP) when OP >= ?PUSH1 andalso OP =< ?PUSH32 -> + (OP - ?PUSH1) + 2; +op_size({comment, _}) -> 0; +op_size(_) -> 1. + diff --git a/src/aeb_primops.erl b/src/aeb_primops.erl new file mode 100644 index 0000000..b7aaeaf --- /dev/null +++ b/src/aeb_primops.erl @@ -0,0 +1,31 @@ +%%%------------------------------------------------------------------- +%%% @copyright (C) 2018, Aeternity Anstalt +%%% @doc +%%% Handle interaction with the aeternity chain +%%% through calls to AEternity primitive operations at address 0. +%%% @end +%%% Created : 18 Dec 2018 +%%%------------------------------------------------------------------- + +-module(aeb_primops). +-export([ is_local_primop_op/1 + , op_needs_type_check/1 + ]). + +-include("aeb_opcodes.hrl"). + +is_local_primop_op(Op) when ?PRIM_CALL_IN_MAP_RANGE(Op) -> true; +is_local_primop_op(Op) when is_integer(Op) -> false. + +op_needs_type_check(Op) -> + (not is_local_primop_op(Op)) andalso op_has_dynamic_type(Op). + +op_has_dynamic_type(?PRIM_CALL_ORACLE_QUERY) -> true; +op_has_dynamic_type(?PRIM_CALL_ORACLE_RESPOND) -> true; +op_has_dynamic_type(?PRIM_CALL_ORACLE_GET_QUESTION) -> true; +op_has_dynamic_type(?PRIM_CALL_ORACLE_GET_ANSWER) -> true; +op_has_dynamic_type(?PRIM_CALL_MAP_GET) -> true; +op_has_dynamic_type(?PRIM_CALL_MAP_PUT) -> true; +op_has_dynamic_type(?PRIM_CALL_MAP_TOLIST) -> true; +op_has_dynamic_type(?PRIM_CALL_AENS_RESOLVE) -> true; +op_has_dynamic_type(_) -> false. diff --git a/src/aebytecode.app.src b/src/aebytecode.app.src new file mode 100644 index 0000000..53b095c --- /dev/null +++ b/src/aebytecode.app.src @@ -0,0 +1,14 @@ +{application, aebytecode, + [{description, "Bytecode definitions for AEthernity VM shared with compiler."}, + {vsn, "1.0.0"}, + {registered, []}, + {applications, + [kernel, + stdlib + ]}, + {env,[]}, + {modules, []}, + {maintainers, []}, + {licenses, []}, + {links, []} + ]}. diff --git a/test/aebytecode_SUITE.erl b/test/aebytecode_SUITE.erl new file mode 100644 index 0000000..2c28b87 --- /dev/null +++ b/test/aebytecode_SUITE.erl @@ -0,0 +1,19 @@ +-module(aebytecode_SUITE). + +%% common_test exports +-export([ all/0 ]). + +%% test case exports +-export([ roundtrip_identy/1 ]). + +-include_lib("common_test/include/ct.hrl"). + +all() -> + [ roundtrip_identy ]. + +roundtrip_identy(_Cfg) -> + CodeDir = code:lib_dir(aebytecode, test), + FileName = filename:join(CodeDir, "asm_code/identity.aesm"), + Code = aeb_asm:file(FileName, []), + ct:log("Code ~p:~n~s~n", [FileName, aeb_disassemble:format(Code, fun io:format/2)]), + ok. diff --git a/test/asm_code/identity.aesm b/test/asm_code/identity.aesm new file mode 100644 index 0000000..37273be --- /dev/null +++ b/test/asm_code/identity.aesm @@ -0,0 +1,74 @@ +;; CONTRACT: Identity + + PUSH1 0 + CALLDATALOAD + + DUP1 + ;; Should be the hash of + ;; the signature of the + ;; first function (use 0 as placeholder) + PUSH32 0x0000000000000000000000000000 + EQ + PUSH32 id_entry + JUMPI + + STOP + +id_entry: JUMPDEST + ;; Skip the function name in the calldata + PUSH1 32 + ;; Load argument on stack + CALLDATALOAD + ;; This function only takes one immidiate as argument. + + ;; Call the local version of the function + ;; Get return address + PC + PUSH1 39 + ADD + ;; Get Argument + SWAP1 + PUSH32 id_local + JUMP + + + ;; return here from local call + ;; Store top of stack at mem[0] + JUMPDEST + PUSH1 0 + MSTORE + ;; Return mem[0]-mem[32] + PUSH1 32 + PUSH1 0 + RETURN + + ;; for local calls + ;; Stack: + ;; | | + ;; | Arg | <- SP + ;; | RetAdr | + ;; ... + +id_local: JUMPDEST + + ;; Just return the argument + SWAP1 + ;; Stack: + ;; | | + ;; | RetAdr | <- SP + ;; | RetVal | (Arg in this case) + ;; ... + + JUMP + +;; Test the code from the shell +;; aevm_eeevm:eval(aevm_eeevm_state:init(#{ exec => #{ code => list_to_binary(aeb_asm:file("apps/aesophia/test/contracts/identity.aesm", [])), address => 0, caller => 0, data => <<0:256, 42:256>>, gas => 1000000, gasPrice => 1, origin => 0, value => 0 }, env => #{currentCoinbase => 0, currentDifficulty => 0, currentGasLimit => 10000, currentNumber => 0, currentTimestamp => 0}, pre => #{}}, #{})). + +;; Test the code from the shell with tracing. +;; aevm_eeevm:eval(aevm_eeevm_state:init(#{ exec => #{ code => aeb_asm:file("apps/aesophia/test/contracts/identity.aesm", []), address => 0, caller => 0, data => <<0:256, 42:256>>, gas => 1000000, gasPrice => 1, origin => 0, value => 0 }, env => #{currentCoinbase => 0, currentDifficulty => 0, currentGasLimit => 10000, currentNumber => 0, currentTimestamp => 0}, pre => #{}}, #{ trace => true})). + + +;; Test the code from the shell with tracing. +;; aevm_eeevm:eval(aevm_eeevm_state:init(#{ exec => #{ code => aeb_asm:file("apps/aesophia/test/contracts/identity.aesm", [pp_tokens, pp_opcodes, pp_patched_code, pp_hex_string]), address => 0, caller => 0, data => <<0:256, 42:256>>, gas => 1000000, gasPrice => 1, origin => 0, value => 0}, env => #{currentCoinbase => 0, currentDifficulty => 0, currentGasLimit => 10000, currentNumber => 0, currentTimestamp => 0}, pre => #{}}, #{ trace => true})). + +;; aec_conductor:stop_mining(). -- 2.30.2