diff --git a/CMakeLists.txt b/CMakeLists.txt index dafa4be87e12e8eaaa460f934d5d5efb9a54b9e8..09f886ee351af6313f322beb297f1d6c2ab77143 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,7 +119,6 @@ else( WIN32 ) # Apple AND Linux #if(NOT READLINE_INCLUDE_DIR OR NOT READLINE_LIBRARIES) # MESSAGE(FATAL_ERROR "Could not find lib readline.") #endif() - if( APPLE ) # Apple Specific Options Here message( STATUS "Configuring Eos on OS X" ) @@ -128,7 +127,7 @@ else( WIN32 ) # Apple AND Linux else( APPLE ) # Linux Specific Options Here message( STATUS "Configuring Eos on Linux" ) - set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -Wall" ) + set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++1z -Wc++1z-extensions -Wall" ) if ( FULL_STATIC_BUILD ) set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -static-libgcc") endif ( FULL_STATIC_BUILD ) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 8865e09d7cad3b45cc9a4d591e74288a6e579370..16971d65abbbe9608562d8da1bf28cf4d74b4e1e 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -11,7 +11,6 @@ add_library( eosio_chain wasm_interface.cpp wasm_eosio_constraints.cpp wasm_eosio_injection.cpp - wasm_eosio_binary_ops.cpp wasm_module_walker.cpp apply_context.cpp rate_limiting.cpp diff --git a/libraries/chain/include/eosio/chain/wasm_eosio_binary_ops.hpp b/libraries/chain/include/eosio/chain/wasm_eosio_binary_ops.hpp index 49eb57a36689f1fb69af962d86d9c5820651c8ab..0488dc57727bd6baa8a099a67e5ab58556e80ef9 100644 --- a/libraries/chain/include/eosio/chain/wasm_eosio_binary_ops.hpp +++ b/libraries/chain/include/eosio/chain/wasm_eosio_binary_ops.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include "IR/Operators.h" #include "IR/Module.h" @@ -31,13 +33,6 @@ using code_vector = std::vector; using code_iterator = std::vector::iterator; using wasm_op_generator = std::function, size_t)>; -enum valtype { - i32 = 0x7F, - i64 = 0x7E, - f32 = 0x7D, - f64 = 0x7C -}; - #pragma pack (push) struct memarg { uint32_t a; // align @@ -48,434 +43,472 @@ struct blocktype { uint8_t result = 0x40; // either null (0x40) or valtype }; -template -struct functype { - uint8_t code = 0x60; - std::array params; - std::array results; -}; - -struct mut { - uint8_t code = 0x00; // either 0x00 const or 0x01 var -}; - -struct globaltype { - valtype t; - mut m; -}; - -struct callindirecttype { - uint32_t funcidx; - uint8_t end = 0x00; -}; - struct memoryoptype { uint8_t end = 0x00; }; +struct branchtabletype { + uint64_t target_depth; + uint64_t table_index; +}; +// using for instructions with no fields +struct voidtype {}; -struct no_param_op { - static constexpr bool has_param = false; -}; -struct block_param_op { -}; +inline std::string to_string( uint32_t field ) { + return std::string("i32 : ")+std::to_string(field); +} +inline std::string to_string( uint64_t field ) { + return std::string("i64 : ")+std::to_string(field); +} +inline std::string to_string( blocktype field ) { + return std::string("blocktype : ")+std::to_string((uint32_t)field.result); +} +inline std::string to_string( memoryoptype field ) { + return std::string("memoryoptype : ")+std::to_string((uint32_t)field.end); +} +inline std::string to_string( memarg field ) { + return std::string("memarg : ")+std::to_string(field.a)+std::string(", ")+std::to_string(field.o); +} +inline std::string to_string( branchtabletype field ) { + return std::string("branchtabletype : ")+std::to_string(field.target_depth)+std::string(", ")+std::to_string(field.table_index); +} + +inline std::vector pack( uint32_t field ) { + return { U8(field), U8(field >> 8), U8(field >> 16), U8(field >> 24) }; +} +inline std::vector pack( uint64_t field ) { + return { U8(field), U8(field >> 8), U8(field >> 16), U8(field >> 24), + U8(field >> 32), U8(field >> 40), U8(field >> 48), U8(field >> 56) + }; +} +inline std::vector pack( blocktype field ) { + return { U8(field.result) }; +} +inline std::vector pack( memoryoptype field ) { + return { U8(field.end) }; +} +inline std::vector pack( memarg field ) { + return { U8(field.a), U8(field.a >> 8), U8(field.a >> 16), U8(field.a >> 24), + U8(field.o), U8(field.o >> 8), U8(field.o >> 16), U8(field.o >> 24), + }; +} +inline std::vector pack( branchtabletype field ) { + return { U8(field.target_depth), U8(field.target_depth >> 8), U8(field.target_depth >> 16), U8(field.target_depth >> 24), + U8(field.target_depth >> 32), U8(field.target_depth >> 40), U8(field.target_depth >> 48), U8(field.target_depth >> 56), + U8(field.table_index), U8(field.table_index >> 8), U8(field.table_index >> 16), U8(field.table_index >> 24), + U8(field.table_index >> 32), U8(field.table_index >> 40), U8(field.table_index >> 48), U8(field.table_index >> 56) -std::string to_string( uint32_t field ); -std::string to_string( uint64_t field ); -std::string to_string( blocktype field ); -std::string to_string( memoryoptype field ); -std::string to_string( memarg field ); -std::string to_string( callindirecttype field ); - -std::vector pack( uint32_t field ); -std::vector pack( uint64_t field ); -std::vector pack( blocktype field ); -std::vector pack( memoryoptype field ); -std::vector pack( memarg field ); -std::vector pack( callindirecttype field ); - -#define CONSTRUCT_OP( r, DATA, OP ) \ -template \ -struct OP : instr_base { \ - uint8_t code = BOOST_PP_CAT(OP,_code); \ - uint8_t get_code() override { return BOOST_PP_CAT(OP,_code); } \ - int skip_ahead() override { return sizeof(code); } \ - void unpack( char* opcode ) override { \ - } \ - std::vector pack() override { return { U8(code) }; } \ - std::string to_string() override { return BOOST_PP_STRINGIZE(OP); } \ + }; +} +/* +inline std::string to_string( uint32_t field ); +inline std::string to_string( uint64_t field ); +inline std::string to_string( blocktype field ); +inline std::string to_string( memoryoptype field ); +inline std::string to_string( memarg field ); +inline std::string to_string( branchtabletype field ); + +inline std::vector pack( uint32_t field ); +inline std::vector pack( uint64_t field ); +inline std::vector pack( blocktype field ); +inline std::vector pack( memoryoptype field ); +inline std::vector pack( memarg field ); +inline std::vector pack( branchtabletype field ); +*/ + +template +struct field_specific_params { + static constexpr int skip_ahead = sizeof(uint16_t) + sizeof(Field); + static auto unpack( char* opcode, Field& f ) { f = *reinterpret_cast(opcode); } + static auto pack(Field& f) { return eosio::chain::wasm_ops::pack(f); } + static auto to_string(Field& f) { return std::string(" ")+ + eosio::chain::wasm_ops::to_string(f); } }; -// TODO use fields described above. WASM-JIT serializer is doing something weird in it's binary representation -#define CONSTRUCT_OP_HAS_DATA( r, DATA, OP ) \ -template \ -struct OP : instr_base { \ - uint8_t code = BOOST_PP_CAT(OP,_code); \ - DATA field; \ - uint8_t get_code() override { return BOOST_PP_CAT(OP,_code); } \ - int skip_ahead() override { return 1+sizeof(code) + sizeof(field); } \ - void unpack( char* opcode ) override { \ - field = *reinterpret_cast((opcode)); \ - } \ - std::vector pack() override { \ - std::vector output = { U8(code) }; \ - std::vector fields = eosio::chain::wasm_ops::pack(field); \ - output.insert( output.end(), fields.begin(), fields.end() ); \ - return output; \ - } \ - std::string to_string() override { return std::string(BOOST_PP_STRINGIZE(OP))+ \ - std::string(" ")+eosio::chain::wasm_ops::to_string(field); } \ +template <> +struct field_specific_params { + static constexpr int skip_ahead = sizeof(uint16_t); + static auto unpack( char* opcode, voidtype& f ) {} + static auto pack(voidtype& f) { return std::vector{}; } + static auto to_string(voidtype& f) { return ""; } +}; + +#define CONSTRUCT_OP_HAS_DATA( r, DATA, OP ) \ +template \ +struct OP : instr_base { \ + uint16_t code = BOOST_PP_CAT(OP,_code); \ + DATA field; \ + uint16_t get_code() override { return BOOST_PP_CAT(OP,_code); } \ + int skip_ahead() override { return field_specific_params::skip_ahead; } \ + void unpack( char* opcode ) override { \ + field_specific_params::unpack( opcode, field ); \ + } \ + std::vector pack() override { \ + std::vector output = { U8(code), 0 }; \ + std::vector field_pack = field_specific_params::pack( field ); \ + output.insert( output.end(), field_pack.begin(), field_pack.end() ); \ + return output; \ + } \ + std::string to_string() override { \ + return std::string(BOOST_PP_STRINGIZE(OP))+field_specific_params::to_string( field ); \ + } \ }; -#define WASM_OP_SEQ (error) \ - (end) \ - (unreachable) \ - (nop) \ - (else_) \ - (return_) \ - (drop) \ - (select) \ - (i32_eqz) \ - (i32_eq) \ - (i32_ne) \ - (i32_lt_s) \ - (i32_lt_u) \ - (i32_gt_s) \ - (i32_gt_u) \ - (i32_le_s) \ - (i32_le_u) \ - (i32_ge_s) \ - (i32_ge_u) \ - (i64_eqz) \ - (i64_eq) \ - (i64_ne) \ - (i64_lt_s) \ - (i64_lt_u) \ - (i64_gt_s) \ - (i64_gt_u) \ - (i64_le_s) \ - (i64_le_u) \ - (i64_ge_s) \ - (i64_ge_u) \ - (f32_eq) \ - (f32_ne) \ - (f32_lt) \ - (f32_gt) \ - (f32_le) \ - (f32_ge) \ - (f64_eq) \ - (f64_ne) \ - (f64_lt) \ - (f64_gt) \ - (f64_le) \ - (f64_ge) \ - (i32_clz) \ - (i32_ctz) \ - (i32_popcnt) \ - (i32_add) \ - (i32_sub) \ - (i32_mul) \ - (i32_div_s) \ - (i32_div_u) \ - (i32_rem_s) \ - (i32_rem_u) \ - (i32_and) \ - (i32_or) \ - (i32_xor) \ - (i32_shl) \ - (i32_shr_s) \ - (i32_shr_u) \ - (i32_rotl) \ - (i32_rotr) \ - (i64_clz) \ - (i64_ctz) \ - (i64_popcnt) \ - (i64_add) \ - (i64_sub) \ - (i64_mul) \ - (i64_div_s) \ - (i64_div_u) \ - (i64_rem_s) \ - (i64_rem_u) \ - (i64_and) \ - (i64_or) \ - (i64_xor) \ - (i64_shl) \ - (i64_shr_s) \ - (i64_shr_u) \ - (i64_rotl) \ - (i64_rotr) \ - (f32_abs) \ - (f32_neg) \ - (f32_ceil) \ - (f32_floor) \ - (f32_trunc) \ - (f32_nearest) \ - (f32_sqrt) \ - (f32_add) \ - (f32_sub) \ - (f32_mul) \ - (f32_div) \ - (f32_min) \ - (f32_max) \ - (f32_copysign) \ - (f64_abs) \ - (f64_neg) \ - (f64_ceil) \ - (f64_floor) \ - (f64_trunc) \ - (f64_nearest) \ - (f64_sqrt) \ - (f64_add) \ - (f64_sub) \ - (f64_mul) \ - (f64_div) \ - (f64_min) \ - (f64_max) \ - (f64_copysign) \ - (i32_wrap_i64) \ - (i32_trunc_s_f32) \ - (i32_trunc_u_f32) \ - (i32_trunc_s_f64) \ - (i32_trunc_u_f64) \ - (i64_extend_s_i32) \ - (i64_extend_u_i32) \ - (i64_trunc_s_f32) \ - (i64_trunc_u_f32) \ - (i64_trunc_s_f64) \ - (i64_trunc_u_f64) \ - (f32_convert_s_i32) \ - (f32_convert_u_i32) \ - (f32_convert_s_i64) \ - (f32_convert_u_i64) \ - (f32_demote_f64) \ - (f64_convert_s_i32) \ - (f64_convert_u_i32) \ - (f64_convert_s_i64) \ - (f64_convert_u_i64) \ - (f64_promote_f32) \ - (i32_reinterpret_f32) \ - (i64_reinterpret_f64) \ - (f32_reinterpret_i32) \ - (f64_reinterpret_i64) \ - (br_table) \ - (block) \ - (loop) \ - (if_) \ - (br) \ - (br_if) \ - (call) \ - (get_local) \ - (set_local) \ - (tee_local) \ - (get_global) \ - (set_global) \ - (i32_const) \ - (f32_const) \ - (i32_load) \ - (i64_load) \ - (f32_load) \ - (f64_load) \ - (i32_load8_s) \ - (i32_load8_u) \ - (i32_load16_s) \ - (i32_load16_u) \ - (i64_load8_s) \ - (i64_load8_u) \ - (i64_load16_s) \ - (i64_load16_u) \ - (i64_load32_s) \ - (i64_load32_u) \ - (i32_store) \ - (i64_store) \ - (f32_store) \ - (f64_store) \ - (i32_store8) \ - (i32_store16) \ - (i64_store8) \ - (i64_store16) \ - (i64_store32) \ - (i64_const) \ - (f64_const) \ - (call_indirect) \ - (grow_memory) \ - (current_memory) +#define WASM_OP_SEQ (error) \ + (end) \ + (unreachable) \ + (nop) \ + (else_) \ + (return_) \ + (drop) \ + (select) \ + (i32_eqz) \ + (i32_eq) \ + (i32_ne) \ + (i32_lt_s) \ + (i32_lt_u) \ + (i32_gt_s) \ + (i32_gt_u) \ + (i32_le_s) \ + (i32_le_u) \ + (i32_ge_s) \ + (i32_ge_u) \ + (i64_eqz) \ + (i64_eq) \ + (i64_ne) \ + (i64_lt_s) \ + (i64_lt_u) \ + (i64_gt_s) \ + (i64_gt_u) \ + (i64_le_s) \ + (i64_le_u) \ + (i64_ge_s) \ + (i64_ge_u) \ + (f32_eq) \ + (f32_ne) \ + (f32_lt) \ + (f32_gt) \ + (f32_le) \ + (f32_ge) \ + (f64_eq) \ + (f64_ne) \ + (f64_lt) \ + (f64_gt) \ + (f64_le) \ + (f64_ge) \ + (i32_clz) \ + (i32_ctz) \ + (i32_popcnt) \ + (i32_add) \ + (i32_sub) \ + (i32_mul) \ + (i32_div_s) \ + (i32_div_u) \ + (i32_rem_s) \ + (i32_rem_u) \ + (i32_and) \ + (i32_or) \ + (i32_xor) \ + (i32_shl) \ + (i32_shr_s) \ + (i32_shr_u) \ + (i32_rotl) \ + (i32_rotr) \ + (i64_clz) \ + (i64_ctz) \ + (i64_popcnt) \ + (i64_add) \ + (i64_sub) \ + (i64_mul) \ + (i64_div_s) \ + (i64_div_u) \ + (i64_rem_s) \ + (i64_rem_u) \ + (i64_and) \ + (i64_or) \ + (i64_xor) \ + (i64_shl) \ + (i64_shr_s) \ + (i64_shr_u) \ + (i64_rotl) \ + (i64_rotr) \ + (f32_abs) \ + (f32_neg) \ + (f32_ceil) \ + (f32_floor) \ + (f32_trunc) \ + (f32_nearest) \ + (f32_sqrt) \ + (f32_add) \ + (f32_sub) \ + (f32_mul) \ + (f32_div) \ + (f32_min) \ + (f32_max) \ + (f32_copysign) \ + (f64_abs) \ + (f64_neg) \ + (f64_ceil) \ + (f64_floor) \ + (f64_trunc) \ + (f64_nearest) \ + (f64_sqrt) \ + (f64_add) \ + (f64_sub) \ + (f64_mul) \ + (f64_div) \ + (f64_min) \ + (f64_max) \ + (f64_copysign) \ + (i32_wrap_i64) \ + (i32_trunc_s_f32) \ + (i32_trunc_u_f32) \ + (i32_trunc_s_f64) \ + (i32_trunc_u_f64) \ + (i64_extend_s_i32) \ + (i64_extend_u_i32) \ + (i64_trunc_s_f32) \ + (i64_trunc_u_f32) \ + (i64_trunc_s_f64) \ + (i64_trunc_u_f64) \ + (f32_convert_s_i32) \ + (f32_convert_u_i32) \ + (f32_convert_s_i64) \ + (f32_convert_u_i64) \ + (f32_demote_f64) \ + (f64_convert_s_i32) \ + (f64_convert_u_i32) \ + (f64_convert_s_i64) \ + (f64_convert_u_i64) \ + (f64_promote_f32) \ + (i32_reinterpret_f32) \ + (i64_reinterpret_f64) \ + (f32_reinterpret_i32) \ + (f64_reinterpret_i64) \ +/* BLOCK TYPE OPS */ \ + (block) \ + (loop) \ + (if_) \ +/* 32bit OPS */ \ + (br) \ + (br_if) \ + (call) \ + (call_indirect) \ + (get_local) \ + (set_local) \ + (tee_local) \ + (get_global) \ + (set_global) \ + (i32_const) \ + (f32_const) \ +/* memarg OPS */ \ + (i32_load) \ + (i64_load) \ + (f32_load) \ + (f64_load) \ + (i32_load8_s) \ + (i32_load8_u) \ + (i32_load16_s) \ + (i32_load16_u) \ + (i64_load8_s) \ + (i64_load8_u) \ + (i64_load16_s) \ + (i64_load16_u) \ + (i64_load32_s) \ + (i64_load32_u) \ + (i32_store) \ + (i64_store) \ + (f32_store) \ + (f64_store) \ + (i32_store8) \ + (i32_store16) \ + (i64_store8) \ + (i64_store16) \ + (i64_store32) \ +/* 64bit OPS */ \ + (i64_const) \ + (f64_const) \ +/* memtype OPS */ \ + (grow_memory) \ + (current_memory) \ +/* branchtable op */ \ + (br_table) \ enum code { - unreachable_code = 0x00, - nop_code = 0x01, - block_code = 0x02, - loop_code = 0x03, - if__code = 0x04, - else__code = 0x05, - end_code = 0x0B, - br_code = 0x0C, - br_if_code = 0x0D, - br_table_code = 0x0E, - return__code = 0x0F, - call_code = 0x10, - call_indirect_code = 0x11, - drop_code = 0x1A, - select_code = 0x1B, - get_local_code = 0x20, - set_local_code = 0x21, - tee_local_code = 0x22, - get_global_code = 0x23, - set_global_code = 0x24, - i32_load_code = 0x28, - i64_load_code = 0x29, - f32_load_code = 0x2A, - f64_load_code = 0x2B, - i32_load8_s_code = 0x2C, - i32_load8_u_code = 0x2D, - i32_load16_s_code = 0x2E, - i32_load16_u_code = 0x2F, - i64_load8_s_code = 0x30, - i64_load8_u_code = 0x31, - i64_load16_s_code = 0x32, - i64_load16_u_code = 0x33, - i64_load32_s_code = 0x34, - i64_load32_u_code = 0x35, - i32_store_code = 0x36, - i64_store_code = 0x37, - f32_store_code = 0x38, - f64_store_code = 0x39, - i32_store8_code = 0x3A, - i32_store16_code = 0x3B, - i64_store8_code = 0x3C, - i64_store16_code = 0x3D, - i64_store32_code = 0x3E, - current_memory_code = 0x3F, - grow_memory_code = 0x40, - i32_const_code = 0x41, - i64_const_code = 0x42, - f32_const_code = 0x43, - f64_const_code = 0x44, - i32_eqz_code = 0x45, - i32_eq_code = 0x46, - i32_ne_code = 0x47, - i32_lt_s_code = 0x48, - i32_lt_u_code = 0x49, - i32_gt_s_code = 0x4A, - i32_gt_u_code = 0x4B, - i32_le_s_code = 0x4C, - i32_le_u_code = 0x4D, - i32_ge_s_code = 0x4E, - i32_ge_u_code = 0x4F, - i64_eqz_code = 0x50, - i64_eq_code = 0x51, - i64_ne_code = 0x52, - i64_lt_s_code = 0x53, - i64_lt_u_code = 0x54, - i64_gt_s_code = 0x55, - i64_gt_u_code = 0x56, - i64_le_s_code = 0x57, - i64_le_u_code = 0x58, - i64_ge_s_code = 0x59, - i64_ge_u_code = 0x5A, - f32_eq_code = 0x5B, - f32_ne_code = 0x5C, - f32_lt_code = 0x5D, - f32_gt_code = 0x5E, - f32_le_code = 0x5F, - f32_ge_code = 0x60, - f64_eq_code = 0x61, - f64_ne_code = 0x62, - f64_lt_code = 0x63, - f64_gt_code = 0x64, - f64_le_code = 0x65, - f64_ge_code = 0x66, - i32_clz_code = 0x67, - i32_ctz_code = 0x68, - i32_popcnt_code = 0x69, - i32_add_code = 0x6A, - i32_sub_code = 0x6B, - i32_mul_code = 0x6C, - i32_div_s_code = 0x6D, - i32_div_u_code = 0x6E, - i32_rem_s_code = 0x6F, - i32_rem_u_code = 0x70, - i32_and_code = 0x71, - i32_or_code = 0x72, - i32_xor_code = 0x73, - i32_shl_code = 0x74, - i32_shr_s_code = 0x75, - i32_shr_u_code = 0x76, - i32_rotl_code = 0x77, - i32_rotr_code = 0x78, - i64_clz_code = 0x79, - i64_ctz_code = 0x7A, - i64_popcnt_code = 0x7B, - i64_add_code = 0x7C, - i64_sub_code = 0x7D, - i64_mul_code = 0x7E, - i64_div_s_code = 0x7F, - i64_div_u_code = 0x80, - i64_rem_s_code = 0x81, - i64_rem_u_code = 0x82, - i64_and_code = 0x83, - i64_or_code = 0x84, - i64_xor_code = 0x85, - i64_shl_code = 0x86, - i64_shr_s_code = 0x87, - i64_shr_u_code = 0x88, - i64_rotl_code = 0x89, - i64_rotr_code = 0x8A, - f32_abs_code = 0x8B, - f32_neg_code = 0x8C, - f32_ceil_code = 0x8D, - f32_floor_code = 0x8E, - f32_trunc_code = 0x8F, - f32_nearest_code = 0x90, - f32_sqrt_code = 0x91, - f32_add_code = 0x92, - f32_sub_code = 0x93, - f32_mul_code = 0x94, - f32_div_code = 0x95, - f32_min_code = 0x96, - f32_max_code = 0x97, - f32_copysign_code = 0x98, - f64_abs_code = 0x99, - f64_neg_code = 0x9A, - f64_ceil_code = 0x9B, - f64_floor_code = 0x9C, - f64_trunc_code = 0x9D, - f64_nearest_code = 0x9E, - f64_sqrt_code = 0x9F, - f64_add_code = 0xA0, - f64_sub_code = 0xA1, - f64_mul_code = 0xA2, - f64_div_code = 0xA3, - f64_min_code = 0xA4, - f64_max_code = 0xA5, - f64_copysign_code = 0xA6, - i32_wrap_i64_code = 0xA7, - i32_trunc_s_f32_code = 0xA8, - i32_trunc_u_f32_code = 0xA9, - i32_trunc_s_f64_code = 0xAA, - i32_trunc_u_f64_code = 0xAB, - i64_extend_s_i32_code = 0xAC, - i64_extend_u_i32_code = 0xAD, - i64_trunc_s_f32_code = 0xAE, - i64_trunc_u_f32_code = 0xAF, - i64_trunc_s_f64_code = 0xB0, - i64_trunc_u_f64_code = 0xB1, - f32_convert_s_i32_code = 0xB2, - f32_convert_u_i32_code = 0xB3, - f32_convert_s_i64_code = 0xB4, - f32_convert_u_i64_code = 0xB5, - f32_demote_f64_code = 0xB6, - f64_convert_s_i32_code = 0xB7, - f64_convert_u_i32_code = 0xB8, - f64_convert_s_i64_code = 0xB9, - f64_convert_u_i64_code = 0xBA, - f64_promote_f32_code = 0xBB, - i32_reinterpret_f32_code = 0xBC, - i64_reinterpret_f64_code = 0xBD, - f32_reinterpret_i32_code = 0xBE, - f64_reinterpret_i64_code = 0xBF, - error_code = 0xFF, + unreachable_code = 0x00, + nop_code = 0x01, + block_code = 0x02, + loop_code = 0x03, + if__code = 0x04, + else__code = 0x05, + end_code = 0x0B, + br_code = 0x0C, + br_if_code = 0x0D, + br_table_code = 0x0E, + return__code = 0x0F, + call_code = 0x10, + call_indirect_code = 0x11, + drop_code = 0x1A, + select_code = 0x1B, + get_local_code = 0x20, + set_local_code = 0x21, + tee_local_code = 0x22, + get_global_code = 0x23, + set_global_code = 0x24, + i32_load_code = 0x28, + i64_load_code = 0x29, + f32_load_code = 0x2A, + f64_load_code = 0x2B, + i32_load8_s_code = 0x2C, + i32_load8_u_code = 0x2D, + i32_load16_s_code = 0x2E, + i32_load16_u_code = 0x2F, + i64_load8_s_code = 0x30, + i64_load8_u_code = 0x31, + i64_load16_s_code = 0x32, + i64_load16_u_code = 0x33, + i64_load32_s_code = 0x34, + i64_load32_u_code = 0x35, + i32_store_code = 0x36, + i64_store_code = 0x37, + f32_store_code = 0x38, + f64_store_code = 0x39, + i32_store8_code = 0x3A, + i32_store16_code = 0x3B, + i64_store8_code = 0x3C, + i64_store16_code = 0x3D, + i64_store32_code = 0x3E, + current_memory_code = 0x3F, + grow_memory_code = 0x40, + i32_const_code = 0x41, + i64_const_code = 0x42, + f32_const_code = 0x43, + f64_const_code = 0x44, + i32_eqz_code = 0x45, + i32_eq_code = 0x46, + i32_ne_code = 0x47, + i32_lt_s_code = 0x48, + i32_lt_u_code = 0x49, + i32_gt_s_code = 0x4A, + i32_gt_u_code = 0x4B, + i32_le_s_code = 0x4C, + i32_le_u_code = 0x4D, + i32_ge_s_code = 0x4E, + i32_ge_u_code = 0x4F, + i64_eqz_code = 0x50, + i64_eq_code = 0x51, + i64_ne_code = 0x52, + i64_lt_s_code = 0x53, + i64_lt_u_code = 0x54, + i64_gt_s_code = 0x55, + i64_gt_u_code = 0x56, + i64_le_s_code = 0x57, + i64_le_u_code = 0x58, + i64_ge_s_code = 0x59, + i64_ge_u_code = 0x5A, + f32_eq_code = 0x5B, + f32_ne_code = 0x5C, + f32_lt_code = 0x5D, + f32_gt_code = 0x5E, + f32_le_code = 0x5F, + f32_ge_code = 0x60, + f64_eq_code = 0x61, + f64_ne_code = 0x62, + f64_lt_code = 0x63, + f64_gt_code = 0x64, + f64_le_code = 0x65, + f64_ge_code = 0x66, + i32_clz_code = 0x67, + i32_ctz_code = 0x68, + i32_popcnt_code = 0x69, + i32_add_code = 0x6A, + i32_sub_code = 0x6B, + i32_mul_code = 0x6C, + i32_div_s_code = 0x6D, + i32_div_u_code = 0x6E, + i32_rem_s_code = 0x6F, + i32_rem_u_code = 0x70, + i32_and_code = 0x71, + i32_or_code = 0x72, + i32_xor_code = 0x73, + i32_shl_code = 0x74, + i32_shr_s_code = 0x75, + i32_shr_u_code = 0x76, + i32_rotl_code = 0x77, + i32_rotr_code = 0x78, + i64_clz_code = 0x79, + i64_ctz_code = 0x7A, + i64_popcnt_code = 0x7B, + i64_add_code = 0x7C, + i64_sub_code = 0x7D, + i64_mul_code = 0x7E, + i64_div_s_code = 0x7F, + i64_div_u_code = 0x80, + i64_rem_s_code = 0x81, + i64_rem_u_code = 0x82, + i64_and_code = 0x83, + i64_or_code = 0x84, + i64_xor_code = 0x85, + i64_shl_code = 0x86, + i64_shr_s_code = 0x87, + i64_shr_u_code = 0x88, + i64_rotl_code = 0x89, + i64_rotr_code = 0x8A, + f32_abs_code = 0x8B, + f32_neg_code = 0x8C, + f32_ceil_code = 0x8D, + f32_floor_code = 0x8E, + f32_trunc_code = 0x8F, + f32_nearest_code = 0x90, + f32_sqrt_code = 0x91, + f32_add_code = 0x92, + f32_sub_code = 0x93, + f32_mul_code = 0x94, + f32_div_code = 0x95, + f32_min_code = 0x96, + f32_max_code = 0x97, + f32_copysign_code = 0x98, + f64_abs_code = 0x99, + f64_neg_code = 0x9A, + f64_ceil_code = 0x9B, + f64_floor_code = 0x9C, + f64_trunc_code = 0x9D, + f64_nearest_code = 0x9E, + f64_sqrt_code = 0x9F, + f64_add_code = 0xA0, + f64_sub_code = 0xA1, + f64_mul_code = 0xA2, + f64_div_code = 0xA3, + f64_min_code = 0xA4, + f64_max_code = 0xA5, + f64_copysign_code = 0xA6, + i32_wrap_i64_code = 0xA7, + i32_trunc_s_f32_code = 0xA8, + i32_trunc_u_f32_code = 0xA9, + i32_trunc_s_f64_code = 0xAA, + i32_trunc_u_f64_code = 0xAB, + i64_extend_s_i32_code = 0xAC, + i64_extend_u_i32_code = 0xAD, + i64_trunc_s_f32_code = 0xAE, + i64_trunc_u_f32_code = 0xAF, + i64_trunc_s_f64_code = 0xB0, + i64_trunc_u_f64_code = 0xB1, + f32_convert_s_i32_code = 0xB2, + f32_convert_u_i32_code = 0xB3, + f32_convert_s_i64_code = 0xB4, + f32_convert_u_i64_code = 0xB5, + f32_demote_f64_code = 0xB6, + f64_convert_s_i32_code = 0xB7, + f64_convert_u_i32_code = 0xB8, + f64_convert_s_i64_code = 0xB9, + f64_convert_u_i64_code = 0xBA, + f64_promote_f32_code = 0xBB, + i32_reinterpret_f32_code = 0xBC, + i64_reinterpret_f64_code = 0xBD, + f32_reinterpret_i32_code = 0xBE, + f64_reinterpret_i64_code = 0xBF, + error_code = 0xFF, }; // code struct visitor_arg { @@ -487,7 +520,7 @@ struct visitor_arg { struct instr { virtual std::string to_string() { return "instr"; } - virtual uint8_t get_code() = 0; + virtual uint16_t get_code() = 0; virtual void visit( visitor_arg&& arg ) = 0; virtual int skip_ahead() = 0; virtual void unpack( char* opcode ) = 0; @@ -505,14 +538,14 @@ struct propagate_should_kill { }; template -struct base_injector { +struct base_mutator { static void accept( wasm_ops::instr* inst, wasm_ops::visitor_arg& arg ) { std::vector self_code = inst->pack(); arg.new_code->insert( arg.new_code->end(), self_code.begin(), self_code.end() ); } }; template <> -struct base_injector { +struct base_mutator { static void accept( wasm_ops::instr* inst, wasm_ops::visitor_arg& arg ) { } }; @@ -523,42 +556,43 @@ struct instr_base : instr { for ( auto m : { Mutators::accept... } ) { m(this, arg); } - base_injector::value>::accept( this, arg ); + base_mutator::value>::accept( this, arg ); } }; - +/* // construct the ops // special case for br_table template struct br_table : instr_base { br_table() = default; br_table( char* block ) {} - uint8_t code = br_table_code; + uint16_t code = br_table_code; std::vector targets; uint32_t else_target; - uint8_t get_code() override { return br_table_code; } + uint16_t get_code() override { return br_table_code; } int skip_ahead() override { return sizeof(code); } // TODO fix this void unpack( char* opcode ) override { //uint8_t* code = (uint8_t*)(block.data() + sizeof(code)); // skip the opcode } std::vector pack() override { - return { U8(code) }; + return { U8(code), U8(code >> 8) }; } std::string to_string() override { return "br_table"; } - }; - -BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP, no_param_op , BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 0, 131 ) ) // NOTE, this skips past br_table as it is defined above -BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, blocktype, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 132, 3 ) ) -BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, uint32_t, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 135, 10 ) ) -BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, memarg, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 145, 23 ) ) -BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, uint64_t, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 168, 2 ) ) -BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, callindirecttype, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 170, 1 ) ) -BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, memoryoptype, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 171, 2 ) ) - -#undef CONSTRUCT_OP +*/ + +// construct the instructions +BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, voidtype, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 0, 131 ) ) +BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, blocktype, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 131, 3 ) ) +BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, uint32_t, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 134, 11 ) ) +BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, memarg, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 145, 23 ) ) +BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, uint64_t, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 168, 2 ) ) +BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, memoryoptype, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 170, 2 ) ) +BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, branchtabletype, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 172, 1 ) ) +// NOTE BRANCH TABLE IS OMITTED #undef CONSTRUCT_OP_HAS_DATA + #pragma pack (pop) struct nop_mutator { @@ -688,12 +722,4 @@ const std::vector* EOSIO_OperatorDecoderStream::_cached_ops = FC_REFLECT_TEMPLATE( (typename T), eosio::chain::wasm_ops::block< T >, (code)(rt) ) FC_REFLECT( eosio::chain::wasm_ops::memarg, (a)(o) ) FC_REFLECT( eosio::chain::wasm_ops::blocktype, (result) ) -//FC_REFLECT( eosio::chain::wasm_ops::functype, (code)(params)(results) ) -FC_REFLECT( eosio::chain::wasm_ops::limits, (code)(min)(max) ) -FC_REFLECT( eosio::chain::wasm_ops::memtype, (lim) ) -FC_REFLECT( eosio::chain::wasm_ops::elemtype, (code) ) -FC_REFLECT( eosio::chain::wasm_ops::tabletype, (et)(lim) ) -FC_REFLECT( eosio::chain::wasm_ops::mut, (code) ) -FC_REFLECT( eosio::chain::wasm_ops::globaltype, (t)(m) ) -FC_REFLECT( eosio::chain::wasm_ops::callindirecttype, (funcidx)(end) ) FC_REFLECT( eosio::chain::wasm_ops::memoryoptype, (end) ) diff --git a/libraries/chain/include/eosio/chain/wasm_eosio_injection.hpp b/libraries/chain/include/eosio/chain/wasm_eosio_injection.hpp index 2878a8c7ec5839a56d7ad5e5c1b295e9fdf0fb8e..bc15c2008217e1cf14405d40c4c5ab910577f7f2 100644 --- a/libraries/chain/include/eosio/chain/wasm_eosio_injection.hpp +++ b/libraries/chain/include/eosio/chain/wasm_eosio_injection.hpp @@ -5,95 +5,51 @@ #include #include #include +#include #include "IR/Module.h" #include "IR/Operators.h" #include "WASM/WASM.h" -namespace eosio { namespace chain { namespace wasm_injections { - // module validators - // effectively do nothing and pass - struct noop_injection_visitor { - static void inject( IR::Module& m ); - static void initializer(); - }; - /* - static U32 checktimeIndex(const Module& module) - { - return module.functions.imports.size() - 1; - } - - void setTypeSlot(const Module& module, ResultType returnType, const std::vector& parameterTypes) - { - if (returnType == ResultType::none && parameterTypes.size() == 1 && parameterTypes[0] == ValueType::i32 ) - typeSlot = module.types.size() - 1; - } - - void addTypeSlot(Module& module) - { - if (typeSlot < 0) - { - // add a type for void func(void) - typeSlot = module.types.size(); - module.types.push_back(FunctionType::get(ResultType::none, std::vector(1, ValueType::i32))); +namespace eosio { namespace chain { namespace wasm_injections { + using namespace IR; + // helper functions for injection + + struct injector_utils { + //static std::unordered_map type_slots; + static std::map, uint32_t> type_slots; + static void init() { type_slots.clear(); } + + template + static void add_type_slot( Module& mod ) { + if ( type_slots.find({FromResultType::value, FromValueType::value...}) == type_slots.end() ) { + type_slots.emplace( std::vector{FromResultType::value, FromValueType::value...}, mod.types.size() ); + mod.types.push_back( FunctionType::get( Result, { Params... } ) ); + } } - } - void addImport(Module& module) - { - if (module.functions.imports.size() == 0 || module.functions.imports.back().exportName.compare(u8"checktime") != 0) { - if (typeSlot < 0) { - addTypeSlot(module); + template + static void add_import(Module& module, const char* scope, const char* func_name, int32_t& index ) { + if (module.functions.imports.size() == 0 || module.functions.imports.back().exportName.compare(func_name) != 0) { + add_type_slot( module ); + + const uint32_t func_type_index = type_slots[{ FromResultType::value, FromValueType::value... }]; + module.functions.imports.push_back({{func_type_index}, std::move(scope), std::move(func_name)}); + index = module.functions.imports.size()-1; + // shift all exported functions by 1 + for ( int i=0; i < module.exports.size(); i++ ) { + if ( module.exports[i].kind == IR::ObjectKind::function ) + module.exports[i].index += 1; + } } - - const U32 functionTypeIndex = typeSlot; - module.functions.imports.push_back({{functionTypeIndex}, std::move(u8"env"), std::move(u8"checktime")}); } - } - - void conditionallyAddCall(Opcode opcode, const ControlStructureImm& imm, Module& module, OperatorEncoderStream& operatorEncoderStream, CodeValidationStream& codeValidationStream) - { - switch(opcode) - { - case Opcode::loop: - case Opcode::block: - addCall(module, operatorEncoderStream, codeValidationStream); - default: - break; - }; - } - - template - void conditionallyAddCall(Opcode , const Imm& , Module& , OperatorEncoderStream& , CodeValidationStream& ) - { - } - - void adjustIfFunctionIndex(Uptr& index, ObjectKind kind) - { - if (kind == ObjectKind::function) - ++index; - } - - void adjustExportIndex(Module& module) - { - // all function exports need to have their index increased to account for inserted definition - for (auto& exportDef : module.exports) - { - adjustIfFunctionIndex(exportDef.index, exportDef.kind); - } - } - - void adjustCallIndex(const Module& module, CallImm& imm) - { - if (imm.functionIndex >= checktimeIndex(module)) - ++imm.functionIndex; - } + }; - template - void adjustCallIndex(const Module& , Imm& ) - { - } -*/ + struct noop_injection_visitor { + static void inject( IR::Module& m ); + static void initializer(); + }; + struct memories_injection_visitor { static void inject( IR::Module& m ); @@ -114,7 +70,7 @@ namespace eosio { namespace chain { namespace wasm_injections { static void inject( IR::Module& m ); static void initializer(); }; - + struct blacklist_injection_visitor { static void inject( IR::Module& m ); static void initializer(); @@ -138,6 +94,7 @@ namespace eosio { namespace chain { namespace wasm_injections { struct debug_printer { static constexpr bool kills = false; + static void init() {} static void accept( wasm_ops::instr* inst, wasm_ops::visitor_arg& arg ) { std::cout << "INSTRUCTION : " << inst->to_string() << "\n"; } @@ -145,6 +102,7 @@ namespace eosio { namespace chain { namespace wasm_injections { struct instruction_counter { static constexpr bool kills = false; + static void init() { icnt = 0; } static void accept( wasm_ops::instr* inst, wasm_ops::visitor_arg& arg ) { // maybe change this later to variable weighting for different instruction types icnt++; @@ -154,22 +112,23 @@ namespace eosio { namespace chain { namespace wasm_injections { struct checktime_injector { static constexpr bool kills = false; + static void init() { checktime_idx = -1; } static void accept( wasm_ops::instr* inst, wasm_ops::visitor_arg& arg ) { -// if (checktime_idx == -1) -// FC_THROW("Error, this should be run after module injector"); - //std::cout << "HIT A BLOCK " << " " << index << "\n"; + // first add the import for checktime + injector_utils::add_import( *(arg.module), u8"env", u8"checktime", checktime_idx ); + wasm_ops::op_types<>::i32_const_t cnt; cnt.field = instruction_counter::icnt; + wasm_ops::op_types<>::call_t chktm; chktm.field = checktime_idx; instruction_counter::icnt = 0; + // pack the ops for insertion std::vector injected = cnt.pack(); std::vector tmp = chktm.pack(); injected.insert( injected.end(), tmp.begin(), tmp.end() ); - //arg.new_code->insert( arg.new_code->end(), injected.begin(), injected.end() ); - //arg.function_def->code.insert( arg.function_def->code.begin()+arg.code_index, - // injected.begin(), injected.end() ); + arg.new_code->insert( arg.new_code->end(), injected.begin(), injected.end() ); } static int32_t checktime_idx; }; @@ -327,8 +286,9 @@ namespace eosio { namespace chain { namespace wasm_injections { wasm_binary_injection() { _module_injectors.init(); // initialize static fields of injectors - instruction_counter::icnt = 0; - checktime_injector::checktime_idx = -1; + //injector_utils::init(); + instruction_counter::init(); + checktime_injector::init(); } static void inject( IR::Module& mod ) { @@ -338,17 +298,9 @@ namespace eosio { namespace chain { namespace wasm_injections { std::vector new_code; while ( decoder ) { auto op = decoder.decodeOp(); - std::vector::iterator decoded_index = (fd.code.begin() + decoder.index()); op->visit( { &mod, &new_code, &fd, decoder.index() } ); } - for ( int i=0; i < new_code.size(); i++ ) - std::cout < +#include #include #include #include @@ -48,6 +49,8 @@ namespace eosio { namespace chain { namespace wasm_constraints { // instruction validators // simple mutator that doesn't actually mutate anything // used to verify that a given instruction is valid for execution on our platform + // for validators set kills to true, this eliminates the extraneous building + // of new code that is going to get thrown away any way struct whitelist_validator { static constexpr bool kills = true; static void accept( wasm_ops::instr* inst, wasm_ops::visitor_arg& arg ) { @@ -55,6 +58,17 @@ namespace eosio { namespace chain { namespace wasm_constraints { } }; + struct large_offset_validator { + static constexpr bool kills = true; + static void accept( wasm_ops::instr* inst, wasm_ops::visitor_arg& arg ) { + // cast to a type that has a memarg field + wasm_ops::op_types<>::i32_load_t* memarg_instr = reinterpret_cast::i32_load_t*>(inst); + if(memarg_instr->field.o >= wasm_constraints::maximum_linear_memory) + FC_THROW_EXCEPTION(wasm_execution_error, "Smart contract used an invalid large memory store/load offset"); + } + + }; + struct wasm_opcode_no_disposition_exception { std::string opcode_name; }; @@ -62,7 +76,8 @@ namespace eosio { namespace chain { namespace wasm_constraints { struct blacklist_validator { static constexpr bool kills = true; static void accept( wasm_ops::instr* inst, wasm_ops::visitor_arg& arg ) { - throw wasm_opcode_no_disposition_exception { inst->to_string() }; + FC_THROW_EXCEPTION(wasm_execution_error, "Error, blacklisted opcode ${op} ", + ("op", inst->to_string())); } }; @@ -92,29 +107,29 @@ namespace eosio { namespace chain { namespace wasm_constraints { using set_global_t = wasm_ops::set_global ; using nop_t = wasm_ops::nop ; - using i32_load_t = wasm_ops::i32_load ; - using i64_load_t = wasm_ops::i64_load ; - using f32_load_t = wasm_ops::f32_load ; - using f64_load_t = wasm_ops::f64_load ; - using i32_load8_s_t = wasm_ops::i32_load8_s ; - using i32_load8_u_t = wasm_ops::i32_load8_u ; - using i32_load16_s_t = wasm_ops::i32_load16_s ; - using i32_load16_u_t = wasm_ops::i32_load16_u ; - using i64_load8_s_t = wasm_ops::i64_load8_s ; - using i64_load8_u_t = wasm_ops::i64_load8_u ; - using i64_load16_s_t = wasm_ops::i64_load16_s ; - using i64_load16_u_t = wasm_ops::i64_load16_u ; - using i64_load32_s_t = wasm_ops::i64_load32_s ; - using i64_load32_u_t = wasm_ops::i64_load32_u ; - using i32_store_t = wasm_ops::i32_store ; - using i64_store_t = wasm_ops::i64_store ; - using f32_store_t = wasm_ops::f32_store ; - using f64_store_t = wasm_ops::f64_store ; - using i32_store8_t = wasm_ops::i32_store8 ; - using i32_store16_t = wasm_ops::i32_store16 ; - using i64_store8_t = wasm_ops::i64_store8 ; - using i64_store16_t = wasm_ops::i64_store16 ; - using i64_store32_t = wasm_ops::i64_store32 ; + using i32_load_t = wasm_ops::i32_load ; + using i64_load_t = wasm_ops::i64_load ; + using f32_load_t = wasm_ops::f32_load ; + using f64_load_t = wasm_ops::f64_load ; + using i32_load8_s_t = wasm_ops::i32_load8_s ; + using i32_load8_u_t = wasm_ops::i32_load8_u ; + using i32_load16_s_t = wasm_ops::i32_load16_s ; + using i32_load16_u_t = wasm_ops::i32_load16_u ; + using i64_load8_s_t = wasm_ops::i64_load8_s ; + using i64_load8_u_t = wasm_ops::i64_load8_u ; + using i64_load16_s_t = wasm_ops::i64_load16_s ; + using i64_load16_u_t = wasm_ops::i64_load16_u ; + using i64_load32_s_t = wasm_ops::i64_load32_s ; + using i64_load32_u_t = wasm_ops::i64_load32_u ; + using i32_store_t = wasm_ops::i32_store ; + using i64_store_t = wasm_ops::i64_store ; + using f32_store_t = wasm_ops::f32_store ; + using f64_store_t = wasm_ops::f64_store ; + using i32_store8_t = wasm_ops::i32_store8 ; + using i32_store16_t = wasm_ops::i32_store16 ; + using i64_store8_t = wasm_ops::i64_store8 ; + using i64_store16_t = wasm_ops::i64_store16 ; + using i64_store32_t = wasm_ops::i64_store32 ; using i32_const_t = wasm_ops::i32_const ; using i64_const_t = wasm_ops::i64_const ; diff --git a/libraries/chain/wasm_eosio_binary_ops.cpp b/libraries/chain/wasm_eosio_binary_ops.cpp index b521e061caa1d63fdbdaa8d6fa8fb13ced07ad76..a6251a0559c87b0f5f6d3eceaa6d10fe2021f7d1 100644 --- a/libraries/chain/wasm_eosio_binary_ops.cpp +++ b/libraries/chain/wasm_eosio_binary_ops.cpp @@ -9,57 +9,63 @@ namespace eosio { namespace chain { namespace wasm_ops { using namespace IR; -std::string to_string( NoImm imm ) { +inline std::string to_string( NoImm imm ) { return "no imm"; } -std::string to_string( MemoryImm imm ) { +inline std::string to_string( MemoryImm imm ) { return "memory imm"; } -std::string to_string( CallImm imm ) { +inline std::string to_string( CallImm imm ) { return "call index : "+std::to_string(uint32_t(imm.functionIndex)); } -std::string to_string( CallIndirectImm imm ) { +inline std::string to_string( CallIndirectImm imm ) { return "call indirect type : "+std::to_string(uint32_t(imm.type.index)); } -std::string to_string( uint32_t field ) { +inline std::string to_string( uint32_t field ) { return std::string("i32 : ")+std::to_string(field); } -std::string to_string( uint64_t field ) { +inline std::string to_string( uint64_t field ) { return std::string("i64 : ")+std::to_string(field); } -std::string to_string( blocktype field ) { +inline std::string to_string( blocktype field ) { return std::string("blocktype : ")+std::to_string((uint32_t)field.result); } -std::string to_string( memoryoptype field ) { +inline std::string to_string( memoryoptype field ) { return std::string("memoryoptype : ")+std::to_string((uint32_t)field.end); } -std::string to_string( memarg field ) { +inline std::string to_string( memarg field ) { return std::string("memarg : ")+std::to_string(field.a)+std::string(", ")+std::to_string(field.o); } -std::string to_string( callindirecttype field ) { - return std::string("callindirecttype : ")+ - std::to_string(field.funcidx)+std::string(", ")+std::to_string((uint32_t)field.end); +inline std::string to_string( branchtabletype field ) { + return std::string("branchtabletype : ")+std::to_string(field.target_depth)+std::string(", ")+std::to_string(field.table_index); } -std::vector pack( uint32_t field ) { - return { U8(field >> 24), U8(field >> 16), U8(field >> 8), U8(field) }; +inline std::vector pack( uint32_t field ) { + return { U8(field), U8(field >> 8), U8(field >> 16), U8(field >> 24) }; } -std::vector pack( uint64_t field ) { - return { U8(field >> 56), U8(field >> 48), U8(field >> 40), U8(field >> 32), - U8(field >> 24), U8(field >> 16), U8(field >> 8), U8(field) }; +inline std::vector pack( uint64_t field ) { + return { U8(field), U8(field >> 8), U8(field >> 16), U8(field >> 24), + U8(field >> 32), U8(field >> 40), U8(field >> 48), U8(field >> 56) + }; } -std::vector pack( blocktype field ) { +inline std::vector pack( blocktype field ) { return { U8(field.result) }; } -std::vector pack( memoryoptype field ) { +inline std::vector pack( memoryoptype field ) { return { U8(field.end) }; } -std::vector pack( memarg field ) { - return { U8(field.a >> 24), U8(field.a >> 16), U8(field.a >> 8), U8(field.a), - U8(field.o >> 24), U8(field.o >> 16), U8(field.o >> 8), U8(field.o) }; +inline std::vector pack( memarg field ) { + return { U8(field.a), U8(field.a >> 8), U8(field.a >> 16), U8(field.a >> 24), + U8(field.o), U8(field.o >> 8), U8(field.o >> 16), U8(field.o >> 24), + }; + } -std::vector pack( callindirecttype field ) { - return { U8(field.funcidx >> 24), U8(field.funcidx >> 16), U8(field.funcidx >> 8), U8(field.funcidx), - U8(field.end) }; +inline std::vector pack( branchtabletype field ) { + return { U8(field.target_depth), U8(field.target_depth >> 8), U8(field.target_depth >> 16), U8(field.target_depth >> 24), + U8(field.target_depth >> 32), U8(field.target_depth >> 40), U8(field.target_depth >> 48), U8(field.target_depth >> 56), + U8(field.table_index), U8(field.table_index >> 8), U8(field.table_index >> 16), U8(field.table_index >> 24), + U8(field.table_index >> 32), U8(field.table_index >> 40), U8(field.table_index >> 48), U8(field.table_index >> 56) + + }; } }}} // namespace eosio, chain, wasm_ops diff --git a/libraries/chain/wasm_eosio_injection.cpp b/libraries/chain/wasm_eosio_injection.cpp index d0916d6980e929d4aa6eb1fef55a544326254c74..bb5f7472e677ec930be053ec3c7d5a7f26768f46 100644 --- a/libraries/chain/wasm_eosio_injection.cpp +++ b/libraries/chain/wasm_eosio_injection.cpp @@ -9,14 +9,9 @@ namespace eosio { namespace chain { namespace wasm_injections { using namespace IR; -/* -std::string to_string( ControlStructureImm imm ) { - return "result type : "+std::to_string(uint32_t(imm.resultType)); -} -std::string to_string( BranchImm imm ) { - return "branch target : "+std::to_string(uint32_t(imm.targetDepth)); -} -*/ + +//std::unordered_map injector_utils::type_slots; +std::map, uint32_t> injector_utils::type_slots; void noop_injection_visitor::inject( Module& m ) { /* just pass */ } void noop_injection_visitor::initializer() { /* just pass */ } diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 1a632d3d82d7e7b40d0fd3d99830e4a3d3b938f5..5783cb4425b2106ae902ce4de9566329cd75b87b 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -185,128 +185,110 @@ namespace eosio { namespace chain { fc::optional binaryen_info; try { -#if 0 + + IR::Module* module = new IR::Module(); + IR::Module* module2 = new IR::Module(); + Serialization::MemoryInputStream stream((const U8 *) wasm_binary, wasm_binary_size); - WASM::serializeWithInjection(stream, *module); - validate_eosio_wasm_constraints(*module); - wasm_module_walker< - wasm_constraints::constraints_validators, - wasm_constraints::constraints_validators> - wmw(*module); - - wasm_ops::wasm_instr_ptr nopi = std::make_shared< wasm_ops::nop >(); - wasm_ops::wasm_instr_ptr blocki = std::make_shared< wasm_ops::block >(); - wasm_ops::op_types::block_t b; - wasm_ops::op_types::i64_popcount_t ipc; - wasm_ops::nop n; - wasm_ops::call br; - br.field = 23; - wasm_rewriter::op_constrainers::f32_add_t f32; - b.visit(); - ipc.visit(); - wmw.pre_validate(); - for (auto def : module->functions.defs) { - char* code = (char*)(def.code.data()); - char* code_end = code+def.code.size(); - std::cout << "FUNC " << "\n"; - uint32_t it = 0; - /* - while ( it < def.code.size() ) { - std::cout << "OPCODE " << std::hex << (uint32_t)code[it] << "\n"; - it++; - } - */ - it = 0; - for (FunctionDef& fd : module->functions.defs ) { - OperatorDecoderStream decoder(fd.code); - while (decoder) { - std::cout << "OP " << std::hex << (uint32_t)decoder.decodeOp() << "\n"; - wasm_ops::instr* op = wasm_ops::get_instr_from_op>(*(decoder.index)); - std::cout << "OP2 " << op->to_string() << " " << (uint32_t)((wasm_ops::i32_const*)op)->field << "\n"; - /* - if ( op->get_code() == 0x41 ) - else - std::cout << "OP2 " << std::hex << (uint32_t)op->get_code() << " " << op->to_string() << "\n"; - */ - } + Serialization::MemoryInputStream stream2((const U8 *) wasm_binary, wasm_binary_size); + WASM::serialize(stream, *module); + WASM::serializeWithInjection(stream2, *module2); + + wasm_constraints::wasm_binary_validation::validate( *module ); + wasm_injections::wasm_binary_injection::inject( *module ); + /* + std::cout << "INJECTED\n"; + wasm_injections::wasm_binary_injection::inject( *module ); + std::cout << "INJECTEDSS\n"; + wasm_injections::wasm_binary_injection::inject( *module2 ); + + auto restype = [](IR::ResultType f){ + if (f == IR::ResultType::none ) return "none"; + if (f == IR::ResultType::i32 ) return "i32"; + if (f == IR::ResultType::i64 ) return "i64"; + if (f == IR::ResultType::f32 ) return "f32"; + if (f == IR::ResultType::f64 ) return "f64"; + }; + auto valtype = [](IR::ValueType f){ + if (f == IR::ValueType::any ) return "any"; + if (f == IR::ValueType::i32 ) return "i32"; + if (f == IR::ValueType::i64 ) return "i64"; + if (f == IR::ValueType::f32 ) return "f32"; + if (f == IR::ValueType::f64 ) return "f64"; + }; + + + std::cout << "FUNCTION_Type A \n"; + for (auto e : module->types) { + std::cout << "Function ("<< restype(e->ret) << ") ("; + for (auto ee : e->parameters) { + std::cout << valtype(ee) << ", "; } - /* - while ( it < def.code.size() ) { - std::cout << "OPCODE " << std::hex << (uint32_t)code[it] << "\n"; - wasm_ops::instr* op = wasm_ops::get_instr_from_op>(code[it]); - it = op->unpack( def.code, it ); - if ( op->get_code() == 0x41 ) - std::cout << "OP " << op->to_string() << " " << (uint32_t)((wasm_ops::i32_const*)op)->field << "\n"; - else if ( op->get_code() == 0x42 ) - std::cout << "OP " << op->to_string() << " " << (uint32_t)((wasm_ops::i32_const*)op)->field << "\n"; - else - std::cout << "OP " << op->to_string() << "\n"; + } + std::cout << "\n"; + std::cout << "FUNCTION_Type B \n"; + for (auto e : module->types) { + std::cout << "Function ("<< restype(e->ret) << ") ("; + for (auto ee : e->parameters) { + std::cout << valtype(ee) << ", "; } - */ - //std::cout << (int)n << " "; } - /* std::cout << "\n"; - uint8_t* buff = new uint8_t[3]; - buff[0] = wasm_ops::block_code; - buff[1] = 120; - //buff[2] = wasm_ops::nop_code; - buff[2] = 34; - std::vector code = {wasm_ops::block_code, 13, wasm_ops::nop_code}; -// wasm_ops::instr* block_ = new wasm_ops::wasm_op_table::which_pointer, wasm_ops::block_code>::instr_t((char*)buff); -// wasm_ops::instr* nop_ = new wasm_ops::wasm_op_table::which_pointer, wasm_ops::nop_code>::instr_t(block_->skip_ahead((char*)buff)); - wasm_ops::instr* block_ = wasm_ops::get_instr_from_op>(wasm_ops::block_code); - wasm_ops::instr* nop_ = wasm_ops::get_instr_from_op>(wasm_ops::nop_code); - - datastream ds( code.data(), code.size() ); - - //fc::raw::unpack(ds, *nop_); - block_->visit(); - - std::cout << "FIELD : " << (uint64_t)((wasm_ops::block*)block_)->field.result << "\n"; - std::cout << "CODE : " << (int)block_->get_code() << "NAME : " << block_->to_string() << "\n"; - std::cout << "CODE : " << (int)nop_->get_code() << "NAME : " << nop_->to_string() << "\n"; + + std::cout << "FUNCTION_Import A \n"; + for (auto e : module->functions.imports) + std::cout << e.exportName << "\n"; + std::cout << "\n"; + std::cout << "FUNCTION_Import B \n"; + for (auto e : module2->functions.imports) + std::cout << e.exportName << "\n"; + + std::cout << "\n"; + std::cout << "TABLES_Import A \n"; + for (auto e : module->tables.imports) + std::cout << e.exportName << "\n"; + + std::cout << "\n"; + std::cout << "TABLES_Import B \n"; + for (auto e : module2->tables.imports) + std::cout << e.exportName << "\n"; + std::cout << "\n"; + + std::cout << "MEMORIES_Import A \n"; + for (auto e : module->memories.imports) + std::cout << e.exportName << "\n"; + + std::cout << "\n"; + std::cout << "MEMORIES_Import B \n"; + for (auto e : module2->memories.imports) + std::cout << e.exportName << "\n"; + std::cout << "\n"; + + std::cout << "GLOBALS_Import A \n"; + for (auto e : module->globals.imports) + std::cout << e.exportName << "\n"; + + std::cout << "\n"; + std::cout << "GLOBALS_Import B \n"; + for (auto e : module2->globals.imports) + std::cout << e.exportName << "\n"; + std::cout << "\n"; + + std::cout << "EXPORT A \n"; + for (auto e : module->exports) + std::cout << e.name << " " << uint32_t(e.index) << "\n"; + + std::cout << "\n"; + std::cout << "EXPORT B \n"; + for (auto e : module2->exports) + std::cout << e.name << " " << uint32_t(e.index) << "\n"; + std::cout << "\n"; */ - root_resolver resolver; - LinkResult link_result = linkModule(*module, resolver); - instance = instantiateModule(*module, std::move(link_result.resolvedImports)); - FC_ASSERT(instance != nullptr); - - //populate the module's data segments in to a vector so the initial state can be - // restored on each invocation - //Be Warned, this may need to be revisited when module imports make sense. The - // code won't handle data segments that initalize an imported memory which I think - // is valid. - for(const DataSegment& data_segment : module->dataSegments) { - FC_ASSERT(data_segment.baseOffset.type == InitializerExpression::Type::i32_const); - FC_ASSERT(module->memories.defs.size()); - const U32 base_offset = data_segment.baseOffset.i32; - const Uptr memory_size = (module->memories.defs[0].type.size.min << IR::numBytesPerPageLog2); - if(base_offset >= memory_size || base_offset + data_segment.data.size() > memory_size) - FC_THROW_EXCEPTION(wasm_execution_error, "WASM data segment outside of valid memory range"); - if(base_offset + data_segment.data.size() > mem_image.size()) - mem_image.resize(base_offset + data_segment.data.size(), 0x00); - memcpy(mem_image.data() + base_offset, data_segment.data.data(), data_segment.data.size()); - } -#endif - /// TODO: make validation generic - IR::Module* module = new IR::Module(); - Serialization::MemoryInputStream stream((const U8 *) wasm_binary, wasm_binary_size); - wasm_constraints::wasm_binary_validation::validate( *module ); - WASM::serialize(stream, *module); - Serialization::ArrayOutputStream outstream; - wasm_constraints::wasm_binary_validation::validate( *module ); - wasm_injections::wasm_binary_injection::inject( *module ); + Serialization::ArrayOutputStream outstream; WASM::serialize(outstream, *module); std::vector bytes = outstream.getBytes(); - + wavm = wavm::entry::build((char*)bytes.data(), bytes.size()); wavm_info.emplace(*wavm); @@ -613,6 +595,7 @@ public: using context_aware_api::context_aware_api; void checktime(uint32_t instruction_count) { + std::cout << "CHECKTIME\n"; context.checktime(instruction_count); } }; diff --git a/libraries/chain/webassembly/binaryen.cpp b/libraries/chain/webassembly/binaryen.cpp index efe699dfb572043ab8ff77a179571242a93137ac..710b52a5bcd2153fb3beb144e72b642f1ddec551 100644 --- a/libraries/chain/webassembly/binaryen.cpp +++ b/libraries/chain/webassembly/binaryen.cpp @@ -159,7 +159,7 @@ entry entry::build(const char* wasm_binary, size_t wasm_binary_size) { WasmBinaryBuilder builder(*module, code, false); builder.read(); - inject_checktime(*module.get()); + //inject_checktime(*module.get()); FC_ASSERT(module->memory.initial * Memory::kPageSize <= wasm_constraints::maximum_linear_memory); @@ -226,4 +226,4 @@ entry::entry(unique_ptr&& module, linear_memory_type&& memory, call_indi } -}}}} \ No newline at end of file +}}}} diff --git a/libraries/chain/webassembly/wavm.cpp b/libraries/chain/webassembly/wavm.cpp index e33d2dd707e09247de2f62053ecab7b9d0d22bac..dadf79bbf7247fbd94ae9c9f8bbd5a0b826f0360 100644 --- a/libraries/chain/webassembly/wavm.cpp +++ b/libraries/chain/webassembly/wavm.cpp @@ -120,9 +120,7 @@ void entry::prepare( const info& base_info ) { entry entry::build(const char* wasm_binary, size_t wasm_binary_size) { Module* module = new Module(); Serialization::MemoryInputStream stream((const U8 *) wasm_binary, wasm_binary_size); - WASM::serializeWithInjection(stream, *module); - // TODO clean this up - //validate_eosio_wasm_constraints(*module); + WASM::serialize(stream, *module); root_resolver resolver; LinkResult link_result = linkModule(*module, resolver); diff --git a/libraries/wasm-jit/Include/IR/Operators.h b/libraries/wasm-jit/Include/IR/Operators.h index 45143889e6788881bc00804c0a86d20c0dc71030..794f0edb5e47a6ad1cc19de94111c012b31e5a9b 100644 --- a/libraries/wasm-jit/Include/IR/Operators.h +++ b/libraries/wasm-jit/Include/IR/Operators.h @@ -25,6 +25,7 @@ namespace IR struct BranchTableImm { + static_assert(sizeof(Uptr) == 8, "SIZEE"); Uptr defaultTargetDepth; Uptr branchTableIndex; // An index into the FunctionDef's branchTables array. }; diff --git a/libraries/wasm-jit/Include/IR/Types.h b/libraries/wasm-jit/Include/IR/Types.h index 607e2530bc9600bc63656e7d2859371652f9859e..e9c01d66fb36aff9c4604ef3d3dfc441ac85454d 100644 --- a/libraries/wasm-jit/Include/IR/Types.h +++ b/libraries/wasm-jit/Include/IR/Types.h @@ -26,6 +26,21 @@ namespace IR max = num-1 }; + template struct FromValueType; + template<> struct FromValueType { static constexpr uint16_t value = 0; }; + template<> struct FromValueType { static constexpr uint16_t value = 1; }; + template<> struct FromValueType { static constexpr uint16_t value = 2; }; + template<> struct FromValueType { static constexpr uint16_t value = 3; }; + template<> struct FromValueType { static constexpr uint16_t value = 4; }; + + + template struct ToValueType; + template<> struct ToValueType<0> { static constexpr ValueType value = ValueType::any; }; + template<> struct ToValueType<1> { static constexpr ValueType value = ValueType::i32; }; + template<> struct ToValueType<2> { static constexpr ValueType value = ValueType::i64; }; + template<> struct ToValueType<3> { static constexpr ValueType value = ValueType::f32; }; + template<> struct ToValueType<4> { static constexpr ValueType value = ValueType::f64; }; + template struct ValueTypeInfo; template<> struct ValueTypeInfo { typedef I32 Value; }; template<> struct ValueTypeInfo { typedef I64 Value; }; @@ -128,7 +143,22 @@ namespace IR num, max = num-1, }; - + + template struct ToResultType; + template<> struct ToResultType<0> { static constexpr ResultType value = ResultType::none; }; + template<> struct ToResultType<1> { static constexpr ResultType value = ResultType::i32; }; + template<> struct ToResultType<2> { static constexpr ResultType value = ResultType::i64; }; + template<> struct ToResultType<3> { static constexpr ResultType value = ResultType::f32; }; + template<> struct ToResultType<4> { static constexpr ResultType value = ResultType::f64; }; + + template struct FromResultType; + template<> struct FromResultType { static constexpr uint16_t value = 0; }; + template<> struct FromResultType { static constexpr uint16_t value = 1; }; + template<> struct FromResultType { static constexpr uint16_t value = 2; }; + template<> struct FromResultType { static constexpr uint16_t value = 3; }; + template<> struct FromResultType { static constexpr uint16_t value = 4; }; + + inline Uptr getArity(ResultType returnType) { return returnType == ResultType::none ? 0 : 1; } inline const char* asString(ResultType type) @@ -337,4 +367,4 @@ namespace IR default: Errors::unreachable(); }; } -} \ No newline at end of file +}