diff --git a/.gitignore b/.gitignore index cb2d2affd..182ecedc2 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,6 @@ product-mini/platforms/linux-sgx/enclave-sample/iwasm build_out tests/wamr-test-suites/workspace -!/test-tools/wamr-ide/VSCode-Extension/.vscode \ No newline at end of file +!/test-tools/wamr-ide/VSCode-Extension/.vscode + +samples/socket-api/wasm-src/inc/pthread.h diff --git a/build-scripts/lldb-wasm.patch b/build-scripts/lldb-wasm.patch index 0d056210a..74dd4dfe1 100644 --- a/build-scripts/lldb-wasm.patch +++ b/build-scripts/lldb-wasm.patch @@ -5800,6 +5800,22 @@ index c878a2ac4..ad5945b0a 100644 // Don't allow the caching that lldb_private::Process::ReadMemory does since // we have it all cached in the trace files. return DoReadMemory(addr, buf, size, error); +diff --git a/lldb/source/Target/ThreadPlanStepRange.cpp b/lldb/source/Target/ThreadPlanStepRange.cpp +index 896e647bb..f76307016 100644 +--- a/lldb/source/Target/ThreadPlanStepRange.cpp ++++ b/lldb/source/Target/ThreadPlanStepRange.cpp +@@ -334,7 +334,10 @@ bool ThreadPlanStepRange::SetNextBranchBreakpoint() { + // If we didn't find a branch, run to the end of the range. + if (branch_index == UINT32_MAX) { + uint32_t last_index = instructions->GetSize() - 1; +- if (last_index - pc_index > 1) { ++ /* This line causes the "step over was treated as step in" issue, we ++ * modify it as a workaround */ ++ /* The origin line is: if (last_index - pc_index > 1) { */ ++ if (last_index - pc_index >= 1) { + InstructionSP last_inst = + instructions->GetInstructionAtIndex(last_index); + size_t last_inst_size = last_inst->GetOpcode().GetByteSize(); diff --git a/lldb/source/Target/UnixSignals.cpp b/lldb/source/Target/UnixSignals.cpp index 4ec2e25c7..24c88fe9a 100644 --- a/lldb/source/Target/UnixSignals.cpp diff --git a/core/config.h b/core/config.h index 31c4e2bcd..ac11affdb 100644 --- a/core/config.h +++ b/core/config.h @@ -169,6 +169,15 @@ #define WASM_ENABLE_DEBUG_INTERP 0 #endif +#if WASM_ENABLE_DEBUG_INTERP != 0 +#ifndef DEBUG_EXECUTION_MEMORY_SIZE +/* 0x85000 is the size required by lldb, if this is changed to a smaller value, + * then the debugger will not be able to evaluate user expressions, other + * functionality such as breakpoint and stepping are not influenced by this */ +#define DEBUG_EXECUTION_MEMORY_SIZE 0x85000 +#endif +#endif /* end of WASM_ENABLE_DEBUG_INTERP != 0 */ + #ifndef WASM_ENABLE_DEBUG_AOT #define WASM_ENABLE_DEBUG_AOT 0 #endif diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c index 6638e4543..24ae38dfe 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -1773,6 +1773,33 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, HANDLE_OP_END(); } + /* constant instructions */ + HANDLE_OP(WASM_OP_F64_CONST) + HANDLE_OP(WASM_OP_I64_CONST) + { + uint8 *orig_ip = frame_ip; + + frame_ip += sizeof(uint64); + addr_ret = GET_OFFSET(); + + bh_memcpy_s(frame_lp + addr_ret, sizeof(uint64), orig_ip, + sizeof(uint64)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_CONST) + HANDLE_OP(WASM_OP_I32_CONST) + { + uint8 *orig_ip = frame_ip; + + frame_ip += sizeof(uint32); + addr_ret = GET_OFFSET(); + + bh_memcpy_s(frame_lp + addr_ret, sizeof(uint32), orig_ip, + sizeof(uint32)); + HANDLE_OP_END(); + } + /* comparison instructions of i32 */ HANDLE_OP(WASM_OP_I32_EQZ) { @@ -3496,10 +3523,6 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, HANDLE_OP(WASM_OP_F64_LOAD) HANDLE_OP(EXT_OP_GET_LOCAL_FAST) HANDLE_OP(WASM_OP_GET_LOCAL) - HANDLE_OP(WASM_OP_F64_CONST) - HANDLE_OP(WASM_OP_I64_CONST) - HANDLE_OP(WASM_OP_F32_CONST) - HANDLE_OP(WASM_OP_I32_CONST) HANDLE_OP(WASM_OP_DROP) HANDLE_OP(WASM_OP_DROP_64) HANDLE_OP(WASM_OP_BLOCK) diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index ad0ce81b4..d54f63a02 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -1828,7 +1828,7 @@ load_function_section(const uint8 *buf, const uint8 *buf_end, uint32 func_count; uint64 total_size; uint32 code_count = 0, code_size, type_index, i, j, k, local_type_index; - uint32 local_count, local_set_count, sub_local_count; + uint32 local_count, local_set_count, sub_local_count, local_cell_num; uint8 type; WASMFunction *func; @@ -1954,9 +1954,17 @@ load_function_section(const uint8 *buf, const uint8 *buf_end, func->param_cell_num = func->func_type->param_cell_num; func->ret_cell_num = func->func_type->ret_cell_num; - func->local_cell_num = + local_cell_num = wasm_get_cell_num(func->local_types, func->local_count); + if (local_cell_num > UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "local count too large"); + return false; + } + + func->local_cell_num = (uint16)local_cell_num; + if (!init_function_local_offsets(func, error_buf, error_buf_size)) return false; @@ -4408,8 +4416,8 @@ typedef struct WASMLoaderContext { /* const buffer */ uint8 *const_buf; uint16 num_const; - uint16 const_buf_size; uint16 const_cell_num; + uint32 const_buf_size; /* processed code */ uint8 *p_code_compiled; @@ -4619,38 +4627,45 @@ wasm_loader_ctx_destroy(WASMLoaderContext *ctx) } static WASMLoaderContext * -wasm_loader_ctx_init(WASMFunction *func) +wasm_loader_ctx_init(WASMFunction *func, char *error_buf, uint32 error_buf_size) { WASMLoaderContext *loader_ctx = - loader_malloc(sizeof(WASMLoaderContext), NULL, 0); + loader_malloc(sizeof(WASMLoaderContext), error_buf, error_buf_size); if (!loader_ctx) return NULL; loader_ctx->frame_ref_size = 32; - if (!(loader_ctx->frame_ref_bottom = loader_ctx->frame_ref = - loader_malloc(loader_ctx->frame_ref_size, NULL, 0))) + if (!(loader_ctx->frame_ref_bottom = loader_ctx->frame_ref = loader_malloc( + loader_ctx->frame_ref_size, error_buf, error_buf_size))) goto fail; loader_ctx->frame_ref_boundary = loader_ctx->frame_ref_bottom + 32; loader_ctx->frame_csp_size = sizeof(BranchBlock) * 8; - if (!(loader_ctx->frame_csp_bottom = loader_ctx->frame_csp = - loader_malloc(loader_ctx->frame_csp_size, NULL, 0))) + if (!(loader_ctx->frame_csp_bottom = loader_ctx->frame_csp = loader_malloc( + loader_ctx->frame_csp_size, error_buf, error_buf_size))) goto fail; loader_ctx->frame_csp_boundary = loader_ctx->frame_csp_bottom + 8; #if WASM_ENABLE_FAST_INTERP != 0 loader_ctx->frame_offset_size = sizeof(int16) * 32; if (!(loader_ctx->frame_offset_bottom = loader_ctx->frame_offset = - loader_malloc(loader_ctx->frame_offset_size, NULL, 0))) + loader_malloc(loader_ctx->frame_offset_size, error_buf, + error_buf_size))) goto fail; loader_ctx->frame_offset_boundary = loader_ctx->frame_offset_bottom + 32; loader_ctx->num_const = 0; loader_ctx->const_buf_size = sizeof(Const) * 8; - if (!(loader_ctx->const_buf = - loader_malloc(loader_ctx->const_buf_size, NULL, 0))) + if (!(loader_ctx->const_buf = loader_malloc(loader_ctx->const_buf_size, + error_buf, error_buf_size))) goto fail; + if (func->param_cell_num >= (int32)INT16_MAX - func->local_cell_num) { + set_error_buf(error_buf, error_buf_size, + "fast interpreter offset overflow"); + goto fail; + } + loader_ctx->start_dynamic_offset = loader_ctx->dynamic_offset = loader_ctx->max_dynamic_offset = func->param_cell_num + func->local_cell_num; @@ -4930,6 +4945,24 @@ fail: LOG_OP("%d\t", value); \ } while (0) +#define emit_uint64(ctx, value) \ + do { \ + wasm_loader_emit_const(ctx, &value, false); \ + LOG_OP("%lld\t", value); \ + } while (0) + +#define emit_float32(ctx, value) \ + do { \ + wasm_loader_emit_const(ctx, &value, true); \ + LOG_OP("%f\t", value); \ + } while (0) + +#define emit_float64(ctx, value) \ + do { \ + wasm_loader_emit_const(ctx, &value, false); \ + LOG_OP("%f\t", value); \ + } while (0) + static bool wasm_loader_ctx_reinit(WASMLoaderContext *ctx) { @@ -4961,6 +4994,28 @@ wasm_loader_ctx_reinit(WASMLoaderContext *ctx) return true; } +static void +wasm_loader_emit_const(WASMLoaderContext *ctx, void *value, bool is_32_bit) +{ + uint32 size = is_32_bit ? sizeof(uint32) : sizeof(uint64); + + if (ctx->p_code_compiled) { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert(((uintptr_t)ctx->p_code_compiled & 1) == 0); +#endif + bh_memcpy_s(ctx->p_code_compiled, + ctx->p_code_compiled_end - ctx->p_code_compiled, value, + size); + ctx->p_code_compiled += size; + } + else { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert((ctx->code_compiled_size & 1) == 0); +#endif + ctx->code_compiled_size += size; + } +} + static void wasm_loader_emit_uint32(WASMLoaderContext *ctx, uint32 value) { @@ -5290,8 +5345,12 @@ wasm_loader_push_frame_offset(WASMLoaderContext *ctx, uint8 type, emit_operand(ctx, ctx->dynamic_offset); *(ctx->frame_offset)++ = ctx->dynamic_offset; ctx->dynamic_offset++; - if (ctx->dynamic_offset > ctx->max_dynamic_offset) + if (ctx->dynamic_offset > ctx->max_dynamic_offset) { ctx->max_dynamic_offset = ctx->dynamic_offset; + if (ctx->max_dynamic_offset >= INT16_MAX) { + goto fail; + } + } } if (is_32bit_type(type)) @@ -5305,10 +5364,19 @@ wasm_loader_push_frame_offset(WASMLoaderContext *ctx, uint8 type, ctx->frame_offset++; if (!disable_emit) { ctx->dynamic_offset++; - if (ctx->dynamic_offset > ctx->max_dynamic_offset) + if (ctx->dynamic_offset > ctx->max_dynamic_offset) { ctx->max_dynamic_offset = ctx->dynamic_offset; + if (ctx->max_dynamic_offset >= INT16_MAX) { + goto fail; + } + } } return true; + +fail: + set_error_buf(error_buf, error_buf_size, + "fast interpreter offset overflow"); + return false; } /* This function should be in front of wasm_loader_pop_frame_ref @@ -5429,8 +5497,11 @@ wasm_loader_get_const_offset(WASMLoaderContext *ctx, uint8 type, void *value, int16 *offset, char *error_buf, uint32 error_buf_size) { + int8 bytes_to_increase; int16 operand_offset = 0; Const *c; + + /* Search existing constant */ for (c = (Const *)ctx->const_buf; (uint8 *)c < ctx->const_buf + ctx->num_const * sizeof(Const); c++) { /* TODO: handle v128 type? */ @@ -5456,7 +5527,24 @@ wasm_loader_get_const_offset(WASMLoaderContext *ctx, uint8 type, void *value, else operand_offset += 2; } + if ((uint8 *)c == ctx->const_buf + ctx->num_const * sizeof(Const)) { + /* New constant, append to the const buffer */ + if ((type == VALUE_TYPE_F64) || (type == VALUE_TYPE_I64)) { + bytes_to_increase = 2; + } + else { + bytes_to_increase = 1; + } + + /* The max cell num of const buffer is 32768 since the valid index range + * is -32768 ~ -1. Return an invalid index 0 to indicate the buffer is + * full */ + if (ctx->const_cell_num > INT16_MAX - bytes_to_increase + 1) { + *offset = 0; + return true; + } + if ((uint8 *)c == ctx->const_buf + ctx->const_buf_size) { MEM_REALLOC(ctx->const_buf, ctx->const_buf_size, ctx->const_buf_size + 4 * sizeof(Const)); @@ -6386,8 +6474,7 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, local_types = func->local_types; local_offsets = func->local_offsets; - if (!(loader_ctx = wasm_loader_ctx_init(func))) { - set_error_buf(error_buf, error_buf_size, "allocate memory failed"); + if (!(loader_ctx = wasm_loader_ctx_init(func, error_buf, error_buf_size))) { goto fail; } @@ -7726,6 +7813,12 @@ re_scan: skip_label(); disable_emit = true; GET_CONST_OFFSET(VALUE_TYPE_I32, i32_const); + + if (operand_offset == 0) { + disable_emit = false; + emit_label(WASM_OP_I32_CONST); + emit_uint32(loader_ctx, i32_const); + } #else (void)i32_const; #endif @@ -7738,6 +7831,12 @@ re_scan: skip_label(); disable_emit = true; GET_CONST_OFFSET(VALUE_TYPE_I64, i64_const); + + if (operand_offset == 0) { + disable_emit = false; + emit_label(WASM_OP_I64_CONST); + emit_uint64(loader_ctx, i64_const); + } #endif PUSH_I64(); break; @@ -7750,6 +7849,12 @@ re_scan: bh_memcpy_s((uint8 *)&f32_const, sizeof(float32), p_org, sizeof(float32)); GET_CONST_F32_OFFSET(VALUE_TYPE_F32, f32_const); + + if (operand_offset == 0) { + disable_emit = false; + emit_label(WASM_OP_F32_CONST); + emit_float32(loader_ctx, f32_const); + } #endif PUSH_F32(); break; @@ -7763,6 +7868,12 @@ re_scan: bh_memcpy_s((uint8 *)&f64_const, sizeof(float64), p_org, sizeof(float64)); GET_CONST_F64_OFFSET(VALUE_TYPE_F64, f64_const); + + if (operand_offset == 0) { + disable_emit = false; + emit_label(WASM_OP_F64_CONST); + emit_float64(loader_ctx, f64_const); + } #endif PUSH_F64(); break; diff --git a/core/iwasm/interpreter/wasm_mini_loader.c b/core/iwasm/interpreter/wasm_mini_loader.c index b92e499a1..500bf6215 100644 --- a/core/iwasm/interpreter/wasm_mini_loader.c +++ b/core/iwasm/interpreter/wasm_mini_loader.c @@ -914,7 +914,7 @@ load_function_section(const uint8 *buf, const uint8 *buf_end, uint32 func_count; uint64 total_size; uint32 code_count = 0, code_size, type_index, i, j, k, local_type_index; - uint32 local_count, local_set_count, sub_local_count; + uint32 local_count, local_set_count, sub_local_count, local_cell_num; uint8 type; WASMFunction *func; @@ -1005,8 +1005,11 @@ load_function_section(const uint8 *buf, const uint8 *buf_end, func->param_cell_num = func->func_type->param_cell_num; func->ret_cell_num = func->func_type->ret_cell_num; - func->local_cell_num = + local_cell_num = wasm_get_cell_num(func->local_types, func->local_count); + bh_assert(local_cell_num <= UINT16_MAX); + + func->local_cell_num = (uint16)local_cell_num; if (!init_function_local_offsets(func, error_buf, error_buf_size)) return false; @@ -2993,8 +2996,8 @@ typedef struct WASMLoaderContext { /* const buffer */ uint8 *const_buf; uint16 num_const; - uint16 const_buf_size; uint16 const_cell_num; + uint32 const_buf_size; /* processed code */ uint8 *p_code_compiled; @@ -3179,38 +3182,45 @@ wasm_loader_ctx_destroy(WASMLoaderContext *ctx) } static WASMLoaderContext * -wasm_loader_ctx_init(WASMFunction *func) +wasm_loader_ctx_init(WASMFunction *func, char *error_buf, uint32 error_buf_size) { WASMLoaderContext *loader_ctx = - loader_malloc(sizeof(WASMLoaderContext), NULL, 0); + loader_malloc(sizeof(WASMLoaderContext), error_buf, error_buf_size); if (!loader_ctx) - return false; + return NULL; loader_ctx->frame_ref_size = 32; - if (!(loader_ctx->frame_ref_bottom = loader_ctx->frame_ref = - loader_malloc(loader_ctx->frame_ref_size, NULL, 0))) + if (!(loader_ctx->frame_ref_bottom = loader_ctx->frame_ref = loader_malloc( + loader_ctx->frame_ref_size, error_buf, error_buf_size))) goto fail; loader_ctx->frame_ref_boundary = loader_ctx->frame_ref_bottom + 32; loader_ctx->frame_csp_size = sizeof(BranchBlock) * 8; - if (!(loader_ctx->frame_csp_bottom = loader_ctx->frame_csp = - loader_malloc(loader_ctx->frame_csp_size, NULL, 0))) + if (!(loader_ctx->frame_csp_bottom = loader_ctx->frame_csp = loader_malloc( + loader_ctx->frame_csp_size, error_buf, error_buf_size))) goto fail; loader_ctx->frame_csp_boundary = loader_ctx->frame_csp_bottom + 8; #if WASM_ENABLE_FAST_INTERP != 0 loader_ctx->frame_offset_size = sizeof(int16) * 32; if (!(loader_ctx->frame_offset_bottom = loader_ctx->frame_offset = - loader_malloc(loader_ctx->frame_offset_size, NULL, 0))) + loader_malloc(loader_ctx->frame_offset_size, error_buf, + error_buf_size))) goto fail; loader_ctx->frame_offset_boundary = loader_ctx->frame_offset_bottom + 32; loader_ctx->num_const = 0; loader_ctx->const_buf_size = sizeof(Const) * 8; - if (!(loader_ctx->const_buf = - loader_malloc(loader_ctx->const_buf_size, NULL, 0))) + if (!(loader_ctx->const_buf = loader_malloc(loader_ctx->const_buf_size, + error_buf, error_buf_size))) goto fail; + if (func->param_cell_num >= (int32)INT16_MAX - func->local_cell_num) { + set_error_buf(error_buf, error_buf_size, + "fast interpreter offset overflow"); + goto fail; + } + loader_ctx->start_dynamic_offset = loader_ctx->dynamic_offset = loader_ctx->max_dynamic_offset = func->param_cell_num + func->local_cell_num; @@ -3463,6 +3473,24 @@ wasm_loader_pop_frame_csp(WASMLoaderContext *ctx, char *error_buf, LOG_OP("%d\t", value); \ } while (0) +#define emit_uint64(ctx, value) \ + do { \ + wasm_loader_emit_const(ctx, &value, false); \ + LOG_OP("%lld\t", value); \ + } while (0) + +#define emit_float32(ctx, value) \ + do { \ + wasm_loader_emit_const(ctx, &value, true); \ + LOG_OP("%f\t", value); \ + } while (0) + +#define emit_float64(ctx, value) \ + do { \ + wasm_loader_emit_const(ctx, &value, false); \ + LOG_OP("%f\t", value); \ + } while (0) + static bool wasm_loader_ctx_reinit(WASMLoaderContext *ctx) { @@ -3494,6 +3522,28 @@ wasm_loader_ctx_reinit(WASMLoaderContext *ctx) return true; } +static void +wasm_loader_emit_const(WASMLoaderContext *ctx, void *value, bool is_32_bit) +{ + uint32 size = is_32_bit ? sizeof(uint32) : sizeof(uint64); + + if (ctx->p_code_compiled) { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert(((uintptr_t)ctx->p_code_compiled & 1) == 0); +#endif + bh_memcpy_s(ctx->p_code_compiled, + ctx->p_code_compiled_end - ctx->p_code_compiled, value, + size); + ctx->p_code_compiled += size; + } + else { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert((ctx->code_compiled_size & 1) == 0); +#endif + ctx->code_compiled_size += size; + } +} + static void wasm_loader_emit_uint32(WASMLoaderContext *ctx, uint32 value) { @@ -3825,8 +3875,10 @@ wasm_loader_push_frame_offset(WASMLoaderContext *ctx, uint8 type, emit_operand(ctx, ctx->dynamic_offset); *(ctx->frame_offset)++ = ctx->dynamic_offset; ctx->dynamic_offset++; - if (ctx->dynamic_offset > ctx->max_dynamic_offset) + if (ctx->dynamic_offset > ctx->max_dynamic_offset) { ctx->max_dynamic_offset = ctx->dynamic_offset; + bh_assert(ctx->max_dynamic_offset < INT16_MAX); + } } if (is_32bit_type(type)) @@ -3840,8 +3892,10 @@ wasm_loader_push_frame_offset(WASMLoaderContext *ctx, uint8 type, ctx->frame_offset++; if (!disable_emit) { ctx->dynamic_offset++; - if (ctx->dynamic_offset > ctx->max_dynamic_offset) + if (ctx->dynamic_offset > ctx->max_dynamic_offset) { ctx->max_dynamic_offset = ctx->dynamic_offset; + bh_assert(ctx->max_dynamic_offset < INT16_MAX); + } } return true; } @@ -3962,8 +4016,11 @@ wasm_loader_get_const_offset(WASMLoaderContext *ctx, uint8 type, void *value, int16 *offset, char *error_buf, uint32 error_buf_size) { + int8 bytes_to_increase; int16 operand_offset = 0; Const *c; + + /* Search existing constant */ for (c = (Const *)ctx->const_buf; (uint8 *)c < ctx->const_buf + ctx->num_const * sizeof(Const); c++) { if ((type == c->value_type) @@ -3988,7 +4045,24 @@ wasm_loader_get_const_offset(WASMLoaderContext *ctx, uint8 type, void *value, else operand_offset += 1; } + if ((uint8 *)c == ctx->const_buf + ctx->num_const * sizeof(Const)) { + /* New constant, append to the const buffer */ + if ((type == VALUE_TYPE_F64) || (type == VALUE_TYPE_I64)) { + bytes_to_increase = 2; + } + else { + bytes_to_increase = 1; + } + + /* The max cell num of const buffer is 32768 since the valid index range + * is -32768 ~ -1. Return an invalid index 0 to indicate the buffer is + * full */ + if (ctx->const_cell_num > INT16_MAX - bytes_to_increase + 1) { + *offset = 0; + return true; + } + if ((uint8 *)c == ctx->const_buf + ctx->const_buf_size) { MEM_REALLOC(ctx->const_buf, ctx->const_buf_size, ctx->const_buf_size + 4 * sizeof(Const)); @@ -4788,8 +4862,7 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, local_types = func->local_types; local_offsets = func->local_offsets; - if (!(loader_ctx = wasm_loader_ctx_init(func))) { - set_error_buf(error_buf, error_buf_size, "allocate memory failed"); + if (!(loader_ctx = wasm_loader_ctx_init(func, error_buf, error_buf_size))) { goto fail; } @@ -5977,6 +6050,12 @@ re_scan: skip_label(); disable_emit = true; GET_CONST_OFFSET(VALUE_TYPE_I32, i32_const); + + if (operand_offset == 0) { + disable_emit = false; + emit_label(WASM_OP_I32_CONST); + emit_uint32(loader_ctx, i32_const); + } #else (void)i32_const; #endif @@ -5989,6 +6068,12 @@ re_scan: skip_label(); disable_emit = true; GET_CONST_OFFSET(VALUE_TYPE_I64, i64_const); + + if (operand_offset == 0) { + disable_emit = false; + emit_label(WASM_OP_I64_CONST); + emit_uint64(loader_ctx, i64_const); + } #endif PUSH_I64(); break; @@ -6001,6 +6086,12 @@ re_scan: bh_memcpy_s((uint8 *)&f32_const, sizeof(float32), p_org, sizeof(float32)); GET_CONST_F32_OFFSET(VALUE_TYPE_F32, f32_const); + + if (operand_offset == 0) { + disable_emit = false; + emit_label(WASM_OP_F32_CONST); + emit_float32(loader_ctx, f32_const); + } #endif PUSH_F32(); break; @@ -6014,6 +6105,12 @@ re_scan: bh_memcpy_s((uint8 *)&f64_const, sizeof(float64), p_org, sizeof(float64)); GET_CONST_F64_OFFSET(VALUE_TYPE_F64, f64_const); + + if (operand_offset == 0) { + disable_emit = false; + emit_label(WASM_OP_F64_CONST); + emit_float64(loader_ctx, f64_const); + } #endif PUSH_F64(); break; diff --git a/core/iwasm/libraries/debug-engine/debug_engine.c b/core/iwasm/libraries/debug-engine/debug_engine.c index 6fc0cd37b..a20d19669 100644 --- a/core/iwasm/libraries/debug-engine/debug_engine.c +++ b/core/iwasm/libraries/debug-engine/debug_engine.c @@ -345,7 +345,8 @@ WASMDebugInstance * wasm_debug_instance_create(WASMCluster *cluster) { WASMDebugInstance *instance; - WASMExecEnv *exec_env; + WASMExecEnv *exec_env = NULL; + wasm_module_inst_t module_inst = NULL; if (!g_debug_engine || !g_debug_engine->active) { return NULL; @@ -373,6 +374,24 @@ wasm_debug_instance_create(WASMCluster *cluster) instance->current_tid = exec_env->handle; + module_inst = wasm_runtime_get_module_inst(exec_env); + bh_assert(module_inst); + + /* Allocate linear memory for evaluating expressions during debugging. If + * the allocation failed, the debugger will not be able to evaluate + * expressions */ + instance->exec_mem_info.size = DEBUG_EXECUTION_MEMORY_SIZE; + instance->exec_mem_info.start_offset = wasm_runtime_module_malloc( + module_inst, instance->exec_mem_info.size, NULL); + if (instance->exec_mem_info.start_offset == 0) { + LOG_WARNING( + "WASM Debug Engine warning: failed to allocate linear memory for " + "execution. \n" + "Will not be able to evaluate expressions during " + "debugging"); + } + instance->exec_mem_info.current_pos = instance->exec_mem_info.start_offset; + if (!wasm_debug_control_thread_create(instance)) { LOG_ERROR("WASM Debug Engine error: failed to create control thread"); goto fail3; @@ -1182,9 +1201,7 @@ wasm_debug_instance_mmap(WASMDebugInstance *instance, uint32 size, int32 map_port) { WASMExecEnv *exec_env; - WASMModuleInstance *module_inst; - uint32 offset; - void *native_addr; + uint32 offset = 0; (void)map_port; if (!instance) @@ -1194,15 +1211,23 @@ wasm_debug_instance_mmap(WASMDebugInstance *instance, uint32 size, if (!exec_env) return 0; - module_inst = (WASMModuleInstance *)exec_env->module_inst; + if (instance->exec_mem_info.start_offset == 0) { + return 0; + } - /* TODO: malloc in wasi libc maybe not be thread safe, we hope LLDB will - always ask for memory when threads stopped */ - offset = wasm_runtime_module_malloc((wasm_module_inst_t)module_inst, size, - &native_addr); - if (!offset) + if ((uint64)instance->exec_mem_info.current_pos + - instance->exec_mem_info.start_offset + size + <= (uint64)instance->exec_mem_info.size) { + offset = instance->exec_mem_info.current_pos; + instance->exec_mem_info.current_pos += size; + } + + if (offset == 0) { LOG_WARNING("the memory may be not enough for debug, try use larger " "--heap-size"); + return 0; + } + return WASM_ADDR(WasmMemory, 0, offset); } @@ -1210,8 +1235,6 @@ bool wasm_debug_instance_ummap(WASMDebugInstance *instance, uint64 addr) { WASMExecEnv *exec_env; - WASMModuleInstance *module_inst; - uint32 offset; if (!instance) return false; @@ -1220,11 +1243,13 @@ wasm_debug_instance_ummap(WASMDebugInstance *instance, uint64 addr) if (!exec_env) return false; - module_inst = (WASMModuleInstance *)exec_env->module_inst; - if (WASM_ADDR_TYPE(addr) == WasmMemory) { - offset = WASM_ADDR_OFFSET(addr); - wasm_runtime_module_free((wasm_module_inst_t)module_inst, offset); - return true; + if (instance->exec_mem_info.start_offset == 0) { + return false; } - return false; + + (void)addr; + + /* Currently we don't support to free the execution memory, simply return + * true here */ + return true; } diff --git a/core/iwasm/libraries/debug-engine/debug_engine.h b/core/iwasm/libraries/debug-engine/debug_engine.h index 5dd8dca8e..6057b0ce3 100644 --- a/core/iwasm/libraries/debug-engine/debug_engine.h +++ b/core/iwasm/libraries/debug-engine/debug_engine.h @@ -44,6 +44,12 @@ typedef enum debug_state_t { APP_STOPPED } debug_state_t; +typedef struct WASMDebugExecutionMemory { + uint32 start_offset; + uint32 size; + uint32 current_pos; +} WASMDebugExecutionMemory; + typedef struct WASMDebugInstance { struct WASMDebugInstance *next; WASMDebugControlThread *control_thread; @@ -60,6 +66,11 @@ typedef struct WASMDebugInstance { * RUNNING when receiving STEP/CONTINUE commands, and set to * STOPPED when any thread stopped */ volatile debug_state_t current_state; + /* Execution memory info. During debugging, the debug client may request to + * malloc a memory space to evaluate user expressions. We preserve a buffer + * during creating debug instance, and use a simple bump pointer allocator + * to serve lldb's memory request */ + WASMDebugExecutionMemory exec_mem_info; } WASMDebugInstance; typedef enum WASMDebugEventKind { diff --git a/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h b/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h index 6f4d5d421..15ec0f519 100644 --- a/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h +++ b/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h @@ -80,6 +80,12 @@ connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); int listen(int sockfd, int backlog); +ssize_t +recvmsg(int sockfd, struct msghdr *msg, int flags); + +ssize_t +sendmsg(int sockfd, const struct msghdr *msg, int flags); + int socket(int domain, int type, int protocol); #endif diff --git a/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c b/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c index 9f6bae93e..f5236fcf6 100644 --- a/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c +++ b/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -126,6 +127,10 @@ connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) __wasi_addr_t wasi_addr = { 0 }; __wasi_errno_t error; + if (NULL == addr) { + HANDLE_ERROR(__WASI_ERRNO_INVAL) + } + error = sockaddr_to_wasi_addr(addr, addrlen, &wasi_addr); HANDLE_ERROR(error) @@ -143,6 +148,79 @@ listen(int sockfd, int backlog) return __WASI_ERRNO_SUCCESS; } +ssize_t +recvmsg(int sockfd, struct msghdr *msg, int flags) +{ + // Prepare input parameters. + __wasi_iovec_t *ri_data = NULL; + size_t i = 0; + size_t ro_datalen; + __wasi_roflags_t ro_flags; + + if (NULL == msg) { + HANDLE_ERROR(__WASI_ERRNO_INVAL) + } + + // Validate flags. + if (flags != 0) { + HANDLE_ERROR(__WASI_ERRNO_NOPROTOOPT) + } + + // __wasi_ciovec_t -> struct iovec + if (!(ri_data = malloc(sizeof(__wasi_iovec_t) * msg->msg_iovlen))) { + HANDLE_ERROR(__WASI_ERRNO_NOMEM) + } + + for (i = 0; i < msg->msg_iovlen; i++) { + ri_data[i].buf = msg->msg_iov[i].iov_base; + ri_data[i].buf_len = msg->msg_iov[i].iov_len; + } + + // Perform system call. + __wasi_errno_t error = __wasi_sock_recv(sockfd, ri_data, msg->msg_iovlen, 0, + &ro_datalen, &ro_flags); + free(ri_data); + HANDLE_ERROR(error) + + return ro_datalen; +} + +ssize_t +sendmsg(int sockfd, const struct msghdr *msg, int flags) +{ + // Prepare input parameters. + __wasi_ciovec_t *si_data = NULL; + size_t so_datalen = 0; + size_t i = 0; + + if (NULL == msg) { + HANDLE_ERROR(__WASI_ERRNO_INVAL) + } + + // This implementation does not support any flags. + if (flags != 0) { + HANDLE_ERROR(__WASI_ERRNO_NOPROTOOPT) + } + + // struct iovec -> __wasi_ciovec_t + if (!(si_data = malloc(sizeof(__wasi_ciovec_t) * msg->msg_iovlen))) { + HANDLE_ERROR(__WASI_ERRNO_NOMEM) + } + + for (i = 0; i < msg->msg_iovlen; i++) { + si_data[i].buf = msg->msg_iov[i].iov_base; + si_data[i].buf_len = msg->msg_iov[i].iov_len; + } + + // Perform system call. + __wasi_errno_t error = + __wasi_sock_send(sockfd, si_data, msg->msg_iovlen, 0, &so_datalen); + free(si_data); + HANDLE_ERROR(error) + + return so_datalen; +} + int socket(int domain, int type, int protocol) { diff --git a/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c b/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c index a6e8693e5..e4893239d 100644 --- a/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c +++ b/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c @@ -60,119 +60,54 @@ wasm_runtime_module_realloc(wasm_module_inst_t module, uint32 ptr, uint32 size, typedef int (*out_func_t)(int c, void *ctx); -enum pad_type { - PAD_NONE, - PAD_ZERO_BEFORE, - PAD_SPACE_BEFORE, - PAD_SPACE_AFTER, -}; - typedef char *_va_list; #define _INTSIZEOF(n) (((uint32)sizeof(n) + 3) & (uint32)~3) #define _va_arg(ap, t) (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))) -#define CHECK_VA_ARG(ap, t) \ - do { \ - if ((uint8 *)ap + _INTSIZEOF(t) > native_end_addr) \ - goto fail; \ +#define CHECK_VA_ARG(ap, t) \ + do { \ + if ((uint8 *)ap + _INTSIZEOF(t) > native_end_addr) { \ + if (fmt_buf != temp_fmt) { \ + wasm_runtime_free(fmt_buf); \ + } \ + goto fail; \ + } \ } while (0) -/** - * @brief Output an unsigned int in hex format - * - * Output an unsigned int on output installed by platform at init time. Should - * be able to handle an unsigned int of any size, 32 or 64 bit. - * @param num Number to output - * - * @return N/A - */ -static void -_printf_hex_uint(out_func_t out, void *ctx, const uint64 num, bool is_u64, - enum pad_type padding, int min_width) -{ - int shift = sizeof(num) * 8; - int found_largest_digit = 0; - int remaining = 16; /* 16 digits max */ - int digits = 0; - char nibble; +/* clang-format off */ +#define PREPARE_TEMP_FORMAT() \ + char temp_fmt[32], *s, *fmt_buf = temp_fmt; \ + uint32 fmt_buf_len = (uint32)sizeof(temp_fmt); \ + int32 n; \ + \ + /* additional 2 bytes: one is the format char, \ + * the other is `\0` */ \ + if (fmt - fmt_start_addr + 2 >= fmt_buf_len) { \ + bh_assert(fmt - fmt_start_addr <= UINT32_MAX - 2); \ + fmt_buf_len = fmt - fmt_start_addr + 2; \ + if (!(fmt_buf = wasm_runtime_malloc(fmt_buf_len))) { \ + print_err(out, ctx); \ + break; \ + } \ + } \ + \ + memset(fmt_buf, 0, fmt_buf_len); \ + bh_memcpy_s(fmt_buf, fmt_buf_len, \ + fmt_start_addr, fmt - fmt_start_addr + 1); +/* clang-format on */ - while (shift >= 4) { - shift -= 4; - nibble = (num >> shift) & 0xf; - - if (nibble || found_largest_digit || shift == 0) { - found_largest_digit = 1; - nibble = (char)(nibble + (nibble > 9 ? 87 : 48)); - out((int)nibble, ctx); - digits++; - continue; - } - - if (remaining-- <= min_width) { - if (padding == PAD_ZERO_BEFORE) { - out('0', ctx); - } - else if (padding == PAD_SPACE_BEFORE) { - out(' ', ctx); - } - } - } - - if (padding == PAD_SPACE_AFTER) { - remaining = min_width * 2 - digits; - while (remaining-- > 0) { - out(' ', ctx); - } - } -} - -/** - * @brief Output an unsigned int in decimal format - * - * Output an unsigned int on output installed by platform at init time. Only - * works with 32-bit values. - * @param num Number to output - * - * @return N/A - */ -static void -_printf_dec_uint(out_func_t out, void *ctx, const uint32 num, - enum pad_type padding, int min_width) -{ - uint32 pos = 999999999; - uint32 remainder = num; - int found_largest_digit = 0; - int remaining = 10; /* 10 digits max */ - int digits = 1; - - /* make sure we don't skip if value is zero */ - if (min_width <= 0) { - min_width = 1; - } - - while (pos >= 9) { - if (found_largest_digit || remainder > pos) { - found_largest_digit = 1; - out((int)((remainder / (pos + 1)) + 48), ctx); - digits++; - } - else if (remaining <= min_width && padding < PAD_SPACE_AFTER) { - out((int)(padding == PAD_ZERO_BEFORE ? '0' : ' '), ctx); - digits++; - } - remaining--; - remainder %= (pos + 1); - pos /= 10; - } - out((int)(remainder + 48), ctx); - - if (padding == PAD_SPACE_AFTER) { - remaining = min_width - digits; - while (remaining-- > 0) { - out(' ', ctx); - } - } -} +#define OUTPUT_TEMP_FORMAT() \ + do { \ + if (n > 0) { \ + s = buf; \ + while (*s) \ + out((int)(*s++), ctx); \ + } \ + \ + if (fmt_buf != temp_fmt) { \ + wasm_runtime_free(fmt_buf); \ + } \ + } while (0) static void print_err(out_func_t out, void *ctx) @@ -187,10 +122,10 @@ _vprintf_wa(out_func_t out, void *ctx, const char *fmt, _va_list ap, wasm_module_inst_t module_inst) { int might_format = 0; /* 1 if encountered a '%' */ - enum pad_type padding = PAD_NONE; - int min_width = -1; int long_ctr = 0; uint8 *native_end_addr; + const char *fmt_start_addr = NULL; + bool is_signed; if (!wasm_runtime_get_native_addr_range(module_inst, (uint8 *)ap, NULL, &native_end_addr)) @@ -205,23 +140,19 @@ _vprintf_wa(out_func_t out, void *ctx, const char *fmt, _va_list ap, } else { might_format = 1; - min_width = -1; - padding = PAD_NONE; long_ctr = 0; + fmt_start_addr = fmt; + is_signed = false; } } else { switch (*fmt) { + case '.': + case '+': case '-': - padding = PAD_SPACE_AFTER; - goto still_might_format; - + case ' ': + case '#': case '0': - if (min_width < 0 && padding == PAD_NONE) { - padding = PAD_ZERO_BEFORE; - goto still_might_format; - } - goto handle_1_to_9; case '1': case '2': case '3': @@ -231,17 +162,12 @@ _vprintf_wa(out_func_t out, void *ctx, const char *fmt, _va_list ap, case '7': case '8': case '9': - handle_1_to_9: - if (min_width < 0) { - min_width = *fmt - '0'; - } - else { - min_width = 10 * min_width + *fmt - '0'; - } + case 't': /* ptrdiff_t */ + goto still_might_format; - if (padding == PAD_NONE) { - padding = PAD_SPACE_BEFORE; - } + case 'j': + /* intmax_t/uintmax_t */ + long_ctr = 2; goto still_might_format; case 'l': @@ -252,113 +178,108 @@ _vprintf_wa(out_func_t out, void *ctx, const char *fmt, _va_list ap, /* FIXME: do nothing for these modifiers */ goto still_might_format; + case 'o': case 'd': case 'i': - { - int32 d; - - if (long_ctr < 2) { - CHECK_VA_ARG(ap, int32); - d = _va_arg(ap, int32); - } - else { - int64 lld; - CHECK_VA_ARG(ap, int64); - lld = _va_arg(ap, int64); - if (lld > INT32_MAX || lld < INT32_MIN) { - print_err(out, ctx); - break; - } - d = (int32)lld; - } - - if (d < 0) { - out((int)'-', ctx); - d = -d; - min_width--; - } - _printf_dec_uint(out, ctx, (uint32)d, padding, min_width); - break; - } - case 'u': - { - uint32 u; - - if (long_ctr < 2) { - CHECK_VA_ARG(ap, uint32); - u = _va_arg(ap, uint32); - } - else { - uint64 llu; - CHECK_VA_ARG(ap, uint64); - llu = _va_arg(ap, uint64); - if (llu > INT32_MAX) { - print_err(out, ctx); - break; - } - u = (uint32)llu; - } - _printf_dec_uint(out, ctx, u, padding, min_width); - break; - } - case 'p': - out('0', ctx); - out('x', ctx); - /* left-pad pointers with zeros */ - padding = PAD_ZERO_BEFORE; - min_width = 8; + is_signed = true; /* Fall through */ + case 'u': + case 'p': case 'x': case 'X': + case 'c': { - uint64 x; - bool is_ptr = (*fmt == 'p') ? true : false; + char buf[64]; + PREPARE_TEMP_FORMAT(); if (long_ctr < 2) { CHECK_VA_ARG(ap, uint32); - x = _va_arg(ap, uint32); + + if (is_signed) { + int32 d; + d = _va_arg(ap, int32); + n = snprintf(buf, sizeof(buf), fmt_buf, d); + } + else { + uint32 u; + u = _va_arg(ap, uint32); + n = snprintf(buf, sizeof(buf), fmt_buf, u); + } } else { + /* Make 8-byte aligned */ + ap = (_va_list)(((uintptr_t)ap + 7) & ~(uintptr_t)7); CHECK_VA_ARG(ap, uint64); - x = _va_arg(ap, uint64); + + if (is_signed) { + int64 lld; + lld = _va_arg(ap, int64); + n = snprintf(buf, sizeof(buf), fmt_buf, lld); + } + else { + uint64 llu; + llu = _va_arg(ap, uint64); + n = snprintf(buf, sizeof(buf), fmt_buf, llu); + } } - _printf_hex_uint(out, ctx, x, !is_ptr, padding, min_width); + + OUTPUT_TEMP_FORMAT(); break; } case 's': { - char *s; + char buf_tmp[128], *buf = buf_tmp; char *start; - uint32 s_offset; + uint32 s_offset, str_len, buf_len; + + PREPARE_TEMP_FORMAT(); CHECK_VA_ARG(ap, int32); s_offset = _va_arg(ap, uint32); if (!validate_app_str_addr(s_offset)) { + if (fmt_buf != temp_fmt) { + wasm_runtime_free(fmt_buf); + } return false; } s = start = addr_app_to_native(s_offset); - while (*s) - out((int)(*s++), ctx); + str_len = strlen(start); + if (str_len >= UINT32_MAX - 64) { + print_err(out, ctx); + if (fmt_buf != temp_fmt) { + wasm_runtime_free(fmt_buf); + } + break; + } - if (padding == PAD_SPACE_AFTER) { - int remaining = min_width - (int32)(s - start); - while (remaining-- > 0) { - out(' ', ctx); + /* reserve 64 more bytes as there may be width description + * in the fmt */ + buf_len = str_len + 64; + + if (buf_len > (uint32)sizeof(buf_tmp)) { + if (!(buf = wasm_runtime_malloc(buf_len))) { + print_err(out, ctx); + if (fmt_buf != temp_fmt) { + wasm_runtime_free(fmt_buf); + } + break; } } - break; - } - case 'c': - { - int c; - CHECK_VA_ARG(ap, int); - c = _va_arg(ap, int); - out(c, ctx); + n = snprintf(buf, buf_len, fmt_buf, + (s_offset == 0 && str_len == 0) ? NULL + : start); + + OUTPUT_TEMP_FORMAT(); + + if (buf != buf_tmp) { + wasm_runtime_free(buf); + } + break; } @@ -368,22 +289,31 @@ _vprintf_wa(out_func_t out, void *ctx, const char *fmt, _va_list ap, break; } + case 'e': + case 'E': + case 'g': + case 'G': case 'f': + case 'F': { float64 f64; - char buf[16], *s; + char buf[64]; + PREPARE_TEMP_FORMAT(); /* Make 8-byte aligned */ ap = (_va_list)(((uintptr_t)ap + 7) & ~(uintptr_t)7); CHECK_VA_ARG(ap, float64); f64 = _va_arg(ap, float64); - snprintf(buf, sizeof(buf), "%f", f64); - s = buf; - while (*s) - out((int)(*s++), ctx); + n = snprintf(buf, sizeof(buf), fmt_buf, f64); + + OUTPUT_TEMP_FORMAT(); break; } + case 'n': + /* print nothing */ + break; + default: out((int)'%', ctx); out((int)*fmt, ctx); diff --git a/core/iwasm/libraries/libc-uvwasi/libc_uvwasi_wrapper.c b/core/iwasm/libraries/libc-uvwasi/libc_uvwasi_wrapper.c index e292984e7..5f2a55215 100644 --- a/core/iwasm/libraries/libc-uvwasi/libc_uvwasi_wrapper.c +++ b/core/iwasm/libraries/libc-uvwasi/libc_uvwasi_wrapper.c @@ -112,7 +112,6 @@ wasi_args_get(wasm_exec_env_t exec_env, uint32 *argv_offsets, char *argv_buf) for (i = 0; i < argc; i++) argv_offsets[i] = addr_native_to_app(argv[i]); - argv_offsets[argc] = 0; wasm_runtime_free(argv); return 0; @@ -208,7 +207,6 @@ wasi_environ_get(wasm_exec_env_t exec_env, uint32 *environ_offsets, for (i = 0; i < environ_count; i++) environ_offsets[i] = addr_native_to_app(environs[i]); - environ_offsets[environ_count] = 0; wasm_runtime_free(environs); return 0; diff --git a/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c b/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c index 733cd4374..c819c6d91 100644 --- a/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c +++ b/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c @@ -131,7 +131,6 @@ wasi_args_get(wasm_exec_env_t exec_env, uint32 *argv_offsets, char *argv_buf) for (i = 0; i < argc; i++) argv_offsets[i] = addr_native_to_app(argv[i]); - argv_offsets[argc] = 0; wasm_runtime_free(argv); return 0; @@ -234,7 +233,6 @@ wasi_environ_get(wasm_exec_env_t exec_env, uint32 *environ_offsets, for (i = 0; i < environ_count; i++) environ_offsets[i] = addr_native_to_app(environs[i]); - environ_offsets[environ_count] = 0; wasm_runtime_free(environs); return 0; diff --git a/core/shared/platform/linux-sgx/sgx_socket.c b/core/shared/platform/linux-sgx/sgx_socket.c index 9ed92f53e..156d79ef8 100644 --- a/core/shared/platform/linux-sgx/sgx_socket.c +++ b/core/shared/platform/linux-sgx/sgx_socket.c @@ -9,21 +9,194 @@ #define TRACE_OCALL_FAIL() os_printf("ocall %s failed!\n", __FUNCTION__) +/** OCALLs prototypes **/ int -ocall_socket(int *p_ret, int domain, int type, int protocol); +ocall_accept(int *p_ret, int sockfd, void *addr, uint32_t *addrlen, + uint32_t addr_size); + +int +ocall_bind(int *p_ret, int sockfd, const void *addr, uint32_t addrlen); + +int +ocall_close(int *p_ret, int fd); + +int +ocall_connect(int *p_ret, int sockfd, void *addr, uint32_t addrlen); + +int +ocall_fcntl_long(int *p_ret, int fd, int cmd, long arg); + +int +ocall_getsockname(int *p_ret, int sockfd, void *addr, uint32_t *addrlen, + uint32_t addr_size); + int ocall_getsockopt(int *p_ret, int sockfd, int level, int optname, void *val_buf, unsigned int val_buf_size, void *len_buf); int -ocall_sendmsg(ssize_t *p_ret, int sockfd, void *msg_buf, - unsigned int msg_buf_size, int flags); +ocall_listen(int *p_ret, int sockfd, int backlog); + +int +ocall_recv(int *p_ret, int sockfd, void *buf, size_t len, int flags); + int ocall_recvmsg(ssize_t *p_ret, int sockfd, void *msg_buf, unsigned int msg_buf_size, int flags); + +int +ocall_send(int *p_ret, int sockfd, const void *buf, size_t len, int flags); + +int +ocall_sendmsg(ssize_t *p_ret, int sockfd, void *msg_buf, + unsigned int msg_buf_size, int flags); + +int +ocall_setsockopt(int *p_ret, int sockfd, int level, int optname, void *optval, + unsigned int optlen); + int ocall_shutdown(int *p_ret, int sockfd, int how); +int +ocall_socket(int *p_ret, int domain, int type, int protocol); +/** OCALLs prototypes end **/ + +/** In-enclave implementation of POSIX functions **/ +static bool +is_little_endian() +{ + long i = 0x01020304; + unsigned char *c = (unsigned char *)&i; + return (*c == 0x04) ? true : false; +} + +static void +swap32(uint8 *pData) +{ + uint8 value = *pData; + *pData = *(pData + 3); + *(pData + 3) = value; + + value = *(pData + 1); + *(pData + 1) = *(pData + 2); + *(pData + 2) = value; +} + +static void +swap16(uint8 *pData) +{ + uint8 value = *pData; + *(pData) = *(pData + 1); + *(pData + 1) = value; +} + +static uint32 +htonl(uint32 value) +{ + uint32 ret; + if (is_little_endian()) { + ret = value; + swap32((uint8 *)&ret); + return ret; + } + + return value; +} + +static uint32 +ntohl(uint32 value) +{ + return htonl(value); +} + +static uint16 +htons(uint16 value) +{ + uint16 ret; + if (is_little_endian()) { + ret = value; + swap16((uint8 *)&ret); + return ret; + } + + return value; +} + +static uint16 +ntohs(uint16 value) +{ + return htons(value); +} + +/* Coming from musl, under MIT license */ +static int +__inet_aton(const char *s0, struct in_addr *dest) +{ + const char *s = s0; + unsigned char *d = (void *)dest; + unsigned long a[4] = { 0 }; + char *z; + int i; + + for (i = 0; i < 4; i++) { + a[i] = strtoul(s, &z, 0); + if (z == s || (*z && *z != '.') || !isdigit(*s)) + return 0; + if (!*z) + break; + s = z + 1; + } + if (i == 4) + return 0; + switch (i) { + case 0: + a[1] = a[0] & 0xffffff; + a[0] >>= 24; + case 1: + a[2] = a[1] & 0xffff; + a[1] >>= 16; + case 2: + a[3] = a[2] & 0xff; + a[2] >>= 8; + } + for (i = 0; i < 4; i++) { + if (a[i] > 255) + return 0; + d[i] = a[i]; + } + return 1; +} + +/* Coming from musl, under MIT license */ +static int +inet_addr(const char *p) +{ + struct in_addr a; + if (!__inet_aton(p, &a)) + return -1; + return a.s_addr; +} + +static int +inet_network(const char *p) +{ + return ntohl(inet_addr(p)); +} +/** In-enclave implementation of POSIX functions end **/ + +static int +textual_addr_to_sockaddr(const char *textual, int port, struct sockaddr_in *out) +{ + assert(textual); + + out->sin_family = AF_INET; + out->sin_port = htons(port); + out->sin_addr.s_addr = inet_addr(textual); + + return BHT_OK; +} + int socket(int domain, int type, int protocol) { @@ -232,67 +405,219 @@ os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr, unsigned int *addrlen) { - errno = ENOSYS; - return -1; + struct sockaddr addr_tmp; + unsigned int len = sizeof(struct sockaddr); + + if (ocall_accept(sock, server_sock, &addr_tmp, &len, len) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (*sock < 0) { + errno = get_errno(); + return BHT_ERROR; + } + + return BHT_OK; } int os_socket_bind(bh_socket_t socket, const char *host, int *port) { - errno = ENOSYS; - return -1; + struct sockaddr_in addr; + struct linger ling; + unsigned int socklen; + int ret; + + assert(host); + assert(port); + + ling.l_onoff = 1; + ling.l_linger = 0; + + if (ocall_fcntl_long(&ret, socket, F_SETFD, FD_CLOEXEC) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret < 0) { + goto fail; + } + + if (ocall_setsockopt(&ret, socket, SOL_SOCKET, SO_LINGER, &ling, + sizeof(ling)) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret < 0) { + goto fail; + } + + addr.sin_addr.s_addr = inet_addr(host); + addr.sin_port = htons(*port); + addr.sin_family = AF_INET; + + if (ocall_bind(&ret, socket, &addr, sizeof(addr)) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret < 0) { + goto fail; + } + + socklen = sizeof(addr); + + if (ocall_getsockname(&ret, socket, (void *)&addr, &socklen, socklen) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) { + goto fail; + } + + *port = ntohs(addr.sin_port); + + return BHT_OK; + +fail: + errno = get_errno(); + return BHT_ERROR; } int os_socket_close(bh_socket_t socket) { - errno = ENOSYS; - return -1; + int ret; + + if (ocall_close(&ret, socket) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; } int os_socket_connect(bh_socket_t socket, const char *addr, int port) -{} +{ + struct sockaddr_in addr_in = { 0 }; + socklen_t addr_len = sizeof(struct sockaddr_in); + int ret = 0; + + if ((ret = textual_addr_to_sockaddr(addr, port, &addr_in)) < 0) { + return ret; + } + + if (ocall_connect(&ret, socket, &addr_in, addr_len) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} int os_socket_create(bh_socket_t *sock, int tcp_or_udp) { - errno = ENOSYS; - return -1; + if (!sock) { + return BHT_ERROR; + } + + if (1 == tcp_or_udp) { + if (ocall_socket(sock, AF_INET, SOCK_STREAM, IPPROTO_TCP) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + } + else if (0 == tcp_or_udp) { + if (ocall_socket(sock, AF_INET, SOCK_DGRAM, 0) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + } + + if (*sock == -1) { + errno = get_errno(); + return BHT_ERROR; + } + + return BHT_OK; } int os_socket_inet_network(const char *cp, uint32 *out) { - errno = ENOSYS; - return -1; + if (!cp) + return BHT_ERROR; + + *out = inet_network(cp); + + return BHT_OK; } int os_socket_listen(bh_socket_t socket, int max_client) { - errno = ENOSYS; - return -1; + int ret; + + if (ocall_listen(&ret, socket, max_client) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; } int os_socket_recv(bh_socket_t socket, void *buf, unsigned int len) { - errno = ENOSYS; - return -1; + int ret; + + if (ocall_recv(&ret, socket, buf, len, 0) != SGX_SUCCESS) { + errno = ENOSYS; + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; } int os_socket_send(bh_socket_t socket, const void *buf, unsigned int len) { - errno = ENOSYS; - return -1; + int ret; + + if (ocall_send(&ret, socket, buf, len, 0) != SGX_SUCCESS) { + errno = ENOSYS; + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; } int os_socket_shutdown(bh_socket_t socket) { - errno = ENOSYS; - return -1; + return shutdown(socket, O_RDWR); } #endif diff --git a/core/shared/platform/linux-sgx/sgx_socket.h b/core/shared/platform/linux-sgx/sgx_socket.h index 6e5b0438d..7dc05fcee 100644 --- a/core/shared/platform/linux-sgx/sgx_socket.h +++ b/core/shared/platform/linux-sgx/sgx_socket.h @@ -14,11 +14,12 @@ extern "C" { #define SOL_SOCKET 1 +#define SO_TYPE 3 +#define SO_LINGER 13 + #define SOCK_STREAM 1 #define SOCK_DGRAM 2 -#define SO_TYPE 3 - #define MSG_OOB 0x0001 #define MSG_PEEK 0x0002 #define MSG_DONTROUTE 0x0004 @@ -44,6 +45,16 @@ extern "C" { #define SHUT_WR 1 #define SHUT_RDWR 2 +/* Address families. */ +#define AF_INET 2 /* IP protocol family. */ + +/* Standard well-defined IP protocols. */ +#define IPPROTO_TCP 6 /* Transmission Control Protocol. */ + +/* Types of sockets. */ +#define SOCK_DGRAM \ + 2 /* Connectionless, unreliable datagrams of fixed maximum length. */ + struct msghdr { void *msg_name; socklen_t msg_namelen; @@ -54,6 +65,36 @@ struct msghdr { int msg_flags; }; +/* Internet address. */ +struct in_addr { + uint32_t s_addr; +}; +typedef struct in_addr in_addr_t; + +/* Structure describing an Internet socket address. */ +#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */ +struct sockaddr_in { + uint16_t sin_family; + uint16_t sin_port; /* Port number. */ + struct in_addr sin_addr; /* Internet address. */ + + /* Pad to size of `struct sockaddr'. */ + unsigned char__pad[__SOCK_SIZE__ - sizeof(uint16_t) - sizeof(uint16_t) + - sizeof(struct in_addr)]; +}; + +/* Structure used to manipulate the SO_LINGER option. */ +struct linger { + int l_onoff; /* Nonzero to linger on close. */ + int l_linger; /* Time to linger. */ +}; + +/* Structure describing a generic socket address. */ +struct sockaddr { + unsigned short int sa_family; /* Common data: address family and length. */ + char sa_data[14]; /* Address data. */ +}; + int socket(int domain, int type, int protocol); diff --git a/core/shared/platform/linux-sgx/sgx_wamr.edl b/core/shared/platform/linux-sgx/sgx_wamr.edl index 0a47fcbb2..a5ce1897d 100644 --- a/core/shared/platform/linux-sgx/sgx_wamr.edl +++ b/core/shared/platform/linux-sgx/sgx_wamr.edl @@ -118,19 +118,34 @@ enclave { int ocall_pthread_rwlock_unlock([user_check]void *rwlock); int ocall_get_errno(); - int ocall_socket(int domain, int type, int protocol); + + /* sockets */ + int ocall_accept(int sockfd, [in, size=addr_size]void *addr, + [in, size=4] uint32_t *addrlen, uint32_t addr_size); + int ocall_bind(int sockfd, [in, size=addrlen]const void *addr, + uint32_t addrlen); + int ocall_connect(int sockfd, [in, size=addrlen]void *addr, uint32_t addrlen); + int ocall_getsockname(int sockfd, [in, size=addr_size]void *addr, + [in, out, size=4]uint32_t *addrlen, uint32_t addr_size); int ocall_getsockopt(int sockfd, int level, int optname, [out, size=val_buf_size]void *val_buf, unsigned int val_buf_size, [in, out, size=4]void *len_buf); - ssize_t ocall_sendmsg(int sockfd, - [in, size=msg_buf_size]void *msg_buf, - unsigned int msg_buf_size, - int flags); + int ocall_listen(int sockfd, int backlog); + int ocall_recv(int sockfd, [out, size=len]void *buf, size_t len, int flags); ssize_t ocall_recvmsg(int sockfd, [in, out, size=msg_buf_size]void *msg_buf, unsigned int msg_buf_size, int flags); + int ocall_send(int sockfd, [in, size=len]const void *buf, size_t len, int flags); + ssize_t ocall_sendmsg(int sockfd, + [in, size=msg_buf_size]void *msg_buf, + unsigned int msg_buf_size, + int flags); + int ocall_setsockopt(int sockfd, int level, int optname, + [in, size=optlen]void *optval, + unsigned int optlen); int ocall_shutdown(int sockfd, int how); + int ocall_socket(int domain, int type, int protocol); }; }; diff --git a/core/shared/platform/linux-sgx/untrusted/socket.c b/core/shared/platform/linux-sgx/untrusted/socket.c index 08d884bb5..b9f3b2a5a 100644 --- a/core/shared/platform/linux-sgx/untrusted/socket.c +++ b/core/shared/platform/linux-sgx/untrusted/socket.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include int ocall_socket(int domain, int type, int protocol) @@ -72,4 +74,53 @@ int ocall_shutdown(int sockfd, int how) { return shutdown(sockfd, how); +} + +int +ocall_setsockopt(int sockfd, int level, int optname, void *optval, + unsigned int optlen) +{ + return setsockopt(sockfd, level, optname, optval, optlen); +} + +int +ocall_bind(int sockfd, const void *addr, uint32_t addrlen) +{ + return bind(sockfd, (const struct sockaddr *)addr, addrlen); +} + +int +ocall_getsockname(int sockfd, void *addr, uint32_t *addrlen, uint32_t addr_size) +{ + return getsockname(sockfd, (struct sockaddr *)addr, addrlen); +} + +int +ocall_listen(int sockfd, int backlog) +{ + return listen(sockfd, backlog); +} + +int +ocall_accept(int sockfd, void *addr, uint32_t *addrlen, uint32_t addr_size) +{ + return accept(sockfd, (struct sockaddr *)addr, addrlen); +} + +int +ocall_recv(int sockfd, void *buf, size_t len, int flags) +{ + return recv(sockfd, buf, len, flags); +} + +int +ocall_send(int sockfd, const void *buf, size_t len, int flags) +{ + return send(sockfd, buf, len, flags); +} + +int +ocall_connect(int sockfd, void *addr, uint32_t addrlen) +{ + return connect(sockfd, (const struct sockaddr *)addr, addrlen); } \ No newline at end of file diff --git a/doc/build_wamr.md b/doc/build_wamr.md index 1db2f3d70..d3fe5a683 100644 --- a/doc/build_wamr.md +++ b/doc/build_wamr.md @@ -66,6 +66,9 @@ cmake -DWAMR_BUILD_PLATFORM=linux -DWAMR_BUILD_TARGET=ARM #### **Enable shared memory feature** - **WAMR_BUILD_SHARED_MEMORY**=1/0, default to disable if not set +#### **Enable bulk memory feature** +- **WAMR_BUILD_BULK_MEMORY**=1/0, default to disable if not set + #### **Enable thread manager** - **WAMR_BUILD_THREAD_MGR**=1/0, default to disable if not set diff --git a/doc/socket_api.md b/doc/socket_api.md index 91f668e55..203a0e645 100644 --- a/doc/socket_api.md +++ b/doc/socket_api.md @@ -10,7 +10,7 @@ and `socket()`. Users can call those functions in WebAssembly code directly. Those WebAssembly socket calls will be dispatched to the imported functions and eventually will be implemented by host socket APIs. -This document introduces a way to support _Berkeley/Posix Socket APIs_ in +This document introduces a way to support the _Berkeley/POSIX Socket API_ in WebAssembly code. ## Patch the native code @@ -24,7 +24,7 @@ native source code. #endif ``` -`__wasi__` is a Marco defined by WASI. The host compiler will not enable it. +`__wasi__` is a macro defined by WASI. The host compiler will not enable it. ## CMake files @@ -57,10 +57,25 @@ The _iwasm_ should be compiled with `WAMR_BUILD_LIBC_WASI=1`. By default, it is enabled. _iwasm_ accepts address ranges via an option, `--addr-pool`, to implement -the capability control. All IP address the WebAssebmly application may need to `bind()` or `connect()` should be announced first. Every IP address should be in CIRD notation. +the capability control. All IP address the WebAssembly application may need to `bind()` or `connect()` +should be announced first. Every IP address should be in CIRD notation. ```bash $ iwasm --addr-pool=1.2.3.4/15,2.3.4.6/16 socket_example.wasm ``` Refer to [socket api sample](../samples/socket-api) for more details. + +## Intel SGX support + +WAMR also supports the socket API within Intel SGX enclaves. + +The _iwasm_ should be compiled with `WAMR_BUILD_LIBC_WASI=1` and `WAMR_BUILD_LIB_PTHREAD=1`, which are enabled by default. + +Similarly to running _iwasm_ outside of an enclave, the allowed address ranges are given via the option `--addr-pool`. + +```bash +$ iwasm --addr-pool=1.2.3.4/15,2.3.4.6/16 socket_example.wasm +``` + +Refer to [socket api sample](../samples/socket-api) for the compilation of the Wasm applications and [_iwasm_ for Intel SGX](../product-mini/platforms/linux-sgx) for the Wasm runtime. \ No newline at end of file diff --git a/product-mini/platforms/linux-sgx/enclave-sample/App/App.cpp b/product-mini/platforms/linux-sgx/enclave-sample/App/App.cpp index bd6b0e367..ba269d34f 100644 --- a/product-mini/platforms/linux-sgx/enclave-sample/App/App.cpp +++ b/product-mini/platforms/linux-sgx/enclave-sample/App/App.cpp @@ -227,6 +227,10 @@ print_help() printf(" --dir= Grant wasi access to the given host directories\n"); printf(" to the program, for example:\n"); printf(" --dir= --dir=\n"); + printf(" --addr-pool= Grant wasi access to the given network addresses in\n"); + printf(" CIRD notation to the program, seperated with ',',\n"); + printf(" for example:\n"); + printf(" --addr-pool=1.2.3.4/15,2.3.4.5/16\n"); printf(" --max-threads=n Set maximum thread number per cluster, default is 4\n"); return 1; } @@ -550,9 +554,10 @@ app_instance_func(void *wasm_module_inst, const char *func_name, int app_argc, static bool set_wasi_args(void *wasm_module, const char **dir_list, uint32_t dir_list_size, const char **env_list, uint32_t env_list_size, int stdinfd, - int stdoutfd, int stderrfd, char **argv, uint32_t argc) + int stdoutfd, int stderrfd, char **argv, uint32_t argc, + const char **addr_pool, uint32_t addr_pool_size) { - uint64_t ecall_args[10]; + uint64_t ecall_args[12]; ecall_args[0] = (uint64_t)(uintptr_t)wasm_module; ecall_args[1] = (uint64_t)(uintptr_t)dir_list; @@ -564,9 +569,11 @@ set_wasi_args(void *wasm_module, const char **dir_list, uint32_t dir_list_size, ecall_args[7] = stderrfd; ecall_args[8] = (uint64_t)(uintptr_t)argv; ecall_args[9] = argc; + ecall_args[10] = (uint64_t)(uintptr_t)addr_pool; + ecall_args[11] = addr_pool_size; if (SGX_SUCCESS != ecall_handle_command(g_eid, CMD_SET_WASI_ARGS, (uint8_t *)ecall_args, - sizeof(uint64_t) * 10)) { + sizeof(uint64_t) * 12)) { printf("Call ecall_handle_command() failed.\n"); } @@ -590,6 +597,8 @@ main(int argc, char *argv[]) uint32_t dir_list_size = 0; const char *env_list[8] = { NULL }; uint32_t env_list_size = 0; + const char *addr_pool[8] = { NULL }; + uint32_t addr_pool_size = 0; uint32_t max_thread_num = 4; if (enclave_init(&g_eid) < 0) { @@ -666,6 +675,26 @@ main(int argc, char *argv[]) return print_help(); } } + /* TODO: parse the configuration file via --addr-pool-file */ + else if (!strncmp(argv[0], "--addr-pool=", strlen("--addr-pool="))) { + /* like: --addr-pool=100.200.244.255/30 */ + char *token = NULL; + + if ('\0' == argv[0][12]) + return print_help(); + + token = strtok(argv[0] + strlen("--addr-pool="), ","); + while (token) { + if (addr_pool_size >= sizeof(addr_pool) / sizeof(char *)) { + printf("Only allow max address number %d\n", + (int)(sizeof(addr_pool) / sizeof(char *))); + return -1; + } + + addr_pool[addr_pool_size++] = token; + token = strtok(NULL, ";"); + } + } else if (!strncmp(argv[0], "--max-threads=", 14)) { if (argv[0][14] == '\0') return print_help(); @@ -705,7 +734,8 @@ main(int argc, char *argv[]) /* Set wasi arguments */ if (!set_wasi_args(wasm_module, dir_list, dir_list_size, env_list, - env_list_size, 0, 1, 2, argv, argc)) { + env_list_size, 0, 1, 2, argv, argc, addr_pool, + addr_pool_size)) { printf("%s\n", "set wasi arguments failed.\n"); goto fail3; } @@ -771,6 +801,8 @@ wamr_pal_create_process(struct wamr_pal_create_process_args *args) uint32_t dir_list_size = 0; const char *env_list[8] = { NULL }; uint32_t env_list_size = 0; + const char *addr_pool[8] = { NULL }; + uint32_t addr_pool_size = 0; uint32_t max_thread_num = 4; char *wasm_files[16]; void *wasm_module_inst[16]; @@ -845,7 +877,7 @@ wamr_pal_create_process(struct wamr_pal_create_process_args *args) /* Set wasi arguments */ if (!set_wasi_args(wasm_module, dir_list, dir_list_size, env_list, env_list_size, stdinfd, stdoutfd, stderrfd, argv, - argc)) { + argc, addr_pool, addr_pool_size)) { printf("%s\n", "set wasi arguments failed.\n"); unload_module(wasm_module); free(wasm_file_buf); diff --git a/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.cpp b/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.cpp index c20abdc14..35367e08a 100644 --- a/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.cpp +++ b/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.cpp @@ -50,6 +50,8 @@ typedef struct EnclaveModule { uint32 wasi_dir_list_size; char **wasi_env_list; uint32 wasi_env_list_size; + char **wasi_addr_pool_list; + uint32 wasi_addr_pool_list_size; char **wasi_argv; uint32 wasi_argc; bool is_xip_file; @@ -407,6 +409,8 @@ handle_cmd_set_wasi_args(uint64 *args, int32 argc) char **wasi_argv = *(char ***)args++; char *p, *p1; uint32 wasi_argc = *(uint32 *)args++; + char **addr_pool_list = *(char ***)args++; + uint32 addr_pool_list_size = *(uint32 *)args++; uint64 total_size = 0; int32 i, str_len; @@ -414,6 +418,7 @@ handle_cmd_set_wasi_args(uint64 *args, int32 argc) total_size += sizeof(char *) * (uint64)dir_list_size + sizeof(char *) * (uint64)env_list_size + + sizeof(char *) * (uint64)addr_pool_list_size + sizeof(char *) * (uint64)wasi_argc; for (i = 0; i < dir_list_size; i++) { @@ -424,6 +429,10 @@ handle_cmd_set_wasi_args(uint64 *args, int32 argc) total_size += strlen(env_list[i]) + 1; } + for (i = 0; i < addr_pool_list_size; i++) { + total_size += strlen(addr_pool_list[i]) + 1; + } + for (i = 0; i < wasi_argc; i++) { total_size += strlen(wasi_argv[i]) + 1; } @@ -436,7 +445,7 @@ handle_cmd_set_wasi_args(uint64 *args, int32 argc) } p1 = p + sizeof(char *) * dir_list_size + sizeof(char *) * env_list_size - + sizeof(char *) * wasi_argc; + + sizeof(char *) * addr_pool_list_size + sizeof(char *) * wasi_argc; if (dir_list_size > 0) { enclave_module->wasi_dir_list = (char **)p; @@ -462,6 +471,18 @@ handle_cmd_set_wasi_args(uint64 *args, int32 argc) p += sizeof(char *) * env_list_size; } + if (addr_pool_list_size > 0) { + enclave_module->wasi_addr_pool_list = (char **)p; + enclave_module->wasi_addr_pool_list_size = addr_pool_list_size; + for (i = 0; i < addr_pool_list_size; i++) { + enclave_module->wasi_addr_pool_list[i] = p1; + str_len = strlen(addr_pool_list[i]); + bh_memcpy_s(p1, str_len + 1, addr_pool_list[i], str_len + 1); + p1 += str_len + 1; + } + p += sizeof(char *) * addr_pool_list_size; + } + if (wasi_argc > 0) { enclave_module->wasi_argv = (char **)p; enclave_module->wasi_argc = wasi_argc; @@ -481,6 +502,11 @@ handle_cmd_set_wasi_args(uint64 *args, int32 argc) (stdinfd != -1) ? stdinfd : 0, (stdoutfd != -1) ? stdoutfd : 1, (stderrfd != -1) ? stderrfd : 2); + wasm_runtime_set_wasi_addr_pool( + enclave_module->module, + (const char **)enclave_module->wasi_addr_pool_list, + addr_pool_list_size); + *args_org = true; } #else diff --git a/samples/socket-api/CMakeLists.txt b/samples/socket-api/CMakeLists.txt index 16cafdb45..12b979517 100644 --- a/samples/socket-api/CMakeLists.txt +++ b/samples/socket-api/CMakeLists.txt @@ -89,14 +89,17 @@ ExternalProject_Add(wasm-app INSTALL_COMMAND ${CMAKE_COMMAND} -E copy tcp_client.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build tcp_server.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build - tcp_client.wasm.dump ${CMAKE_CURRENT_SOURCE_DIR}/build - tcp_server.wasm.dump ${CMAKE_CURRENT_SOURCE_DIR}/build + send_recv.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build ) add_executable(tcp_server ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/tcp_server.c) target_link_libraries(tcp_server pthread) + add_executable(tcp_client ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/tcp_client.c) +add_executable(send_recv ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/send_recv.c) +target_link_libraries(send_recv pthread) + ############################################ ## Build iwasm with wasi and pthread support ############################################ diff --git a/samples/socket-api/README.md b/samples/socket-api/README.md index 320f4a6ad..71f62ca82 100644 --- a/samples/socket-api/README.md +++ b/samples/socket-api/README.md @@ -1,5 +1,4 @@ -"socket-api" sample introduction -================================ +# "socket-api" sample introduction This sample demonstrates how to use WAMR socket-api to develop wasm network applications. Two wasm applications are provided: tcp-server and tcp-client, and this sample demonstrates @@ -19,26 +18,30 @@ cmake .. make ``` -The file `tcp_server.wasm`, `tcp_client.wasm` and `iwasm` will be created. -And also file `tcp_server` and `tcp_client` of native version are created. +`iwasm` and three Wasm modules, `tcp_server.wasm`, `tcp_client.wasm`, `send_recv.wasm` +will be generated. And their corresponding native version, `tcp_server`, +`tcp_client`, `send_recv` are generated too. -Note that iwasm is built with libc-wasi and lib-pthread enabled. +> Note that iwasm is built with libc-wasi and lib-pthread enabled. ## Run workload Start the tcp server, which opens port 1234 and waits for clients to connect. + ```bash cd build ./iwasm --addr-pool=0.0.0.0/15 tcp_server.wasm ``` Start the tcp client, which connects the server and receives message. + ```bash cd build ./iwasm --addr-pool=127.0.0.1/15 tcp_client.wasm ``` The output of client is like: + ```bash [Client] Create socket [Client] Connect socket @@ -54,4 +57,29 @@ Say Hi from the Server [Client] BYE ``` +`send_recv.wasm` contains a thread as a server and a thread as a client. They +send and receive data via 127.0.0.1:1234. + +```bash +$ ./iwasm --addr-pool=127.0.0.1/0 ./send_recv.wasm +``` + +The output is: + +```bash +Server is online ... +Client is running... +Start receiving. +Start sending. +Send 106 bytes successfully! +Receive 106 bytes successlly! +Data: + The stars shine down + It brings us light + Light comes down + To make us paths + It watches us + And mourns for us +``` + Refer to [socket api document](../../doc/socket_api.md) for more details. diff --git a/samples/socket-api/wasm-src/CMakeLists.txt b/samples/socket-api/wasm-src/CMakeLists.txt index b83c8f88f..5c0ed7e24 100644 --- a/samples/socket-api/wasm-src/CMakeLists.txt +++ b/samples/socket-api/wasm-src/CMakeLists.txt @@ -78,3 +78,4 @@ endfunction() compile_with_clang(tcp_server.c) compile_with_clang(tcp_client.c) +compile_with_clang(send_recv.c) diff --git a/samples/socket-api/wasm-src/send_recv.c b/samples/socket-api/wasm-src/send_recv.c new file mode 100644 index 000000000..9ac0aad31 --- /dev/null +++ b/samples/socket-api/wasm-src/send_recv.c @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __wasi__ +#include +#endif + +static pthread_mutex_t lock = { 0 }; +static pthread_cond_t cond = { 0 }; +static bool server_is_ready = false; + +void * +run_as_server(void *arg) +{ + int sock = -1, on = 1; + struct sockaddr_in addr = { 0 }; + int addrlen = 0; + int new_sock = -1; + char *buf[] = { + "The stars shine down", "It brings us light", "Light comes down", + "To make us paths", "It watches us", "And mourns for us", + }; + struct iovec iov[] = { + { .iov_base = buf[0], .iov_len = strlen(buf[0]) + 1 }, + { .iov_base = buf[1], .iov_len = strlen(buf[1]) + 1 }, + { .iov_base = buf[2], .iov_len = strlen(buf[2]) + 1 }, + { .iov_base = buf[3], .iov_len = strlen(buf[3]) + 1 }, + { .iov_base = buf[4], .iov_len = strlen(buf[4]) + 1 }, + { .iov_base = buf[5], .iov_len = strlen(buf[5]) + 1 }, + }; + struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 6 }; + ssize_t send_len = 0; + + pthread_mutex_lock(&lock); + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + perror("Create a socket failed"); + goto RETURN; + } + +#ifndef __wasi__ + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))) { + perror("Setsockopt failed"); + goto RETURN; + } +#endif + + /* 0.0.0.0:1234 */ + addr.sin_family = AF_INET; + addr.sin_port = htons(1234); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + addrlen = sizeof(addr); + if (bind(sock, (struct sockaddr *)&addr, addrlen) < 0) { + perror("Bind failed"); + goto UNLOCK_SHUTDOWN; + } + + if (listen(sock, 0) < 0) { + perror("Listen failed"); + goto UNLOCK_SHUTDOWN; + } + + server_is_ready = true; + pthread_cond_signal(&cond); + pthread_mutex_unlock(&lock); + + printf("Server is online ... \n"); + + new_sock = accept(sock, (struct sockaddr *)&addr, (socklen_t *)&addrlen); + if (new_sock < 0) { + perror("Accept failed"); + goto SHUTDOWN; + } + + printf("Start sending. \n"); + send_len = sendmsg(new_sock, &msg, 0); + if (send_len < 0) { + perror("Sendmsg failed"); + goto SHUTDOWN; + } + printf("Send %ld bytes successfully!\n", send_len); + +SHUTDOWN: + shutdown(sock, SHUT_RD); + return NULL; + +UNLOCK_SHUTDOWN: + shutdown(sock, SHUT_RD); +RETURN: + pthread_mutex_unlock(&lock); + return NULL; +} + +void * +run_as_client(void *arg) +{ + int sock = -1; + struct sockaddr_in addr = { 0 }; + char buf[256] = { 0 }; + struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) }; + struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 }; + ssize_t recv_len = 0; + + pthread_mutex_lock(&lock); + while (false == server_is_ready) { + pthread_cond_wait(&cond, &lock); + } + pthread_mutex_unlock(&lock); + + printf("Client is running...\n"); + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + perror("Create a socket failed"); + goto RETURN; + } + + /* 127.0.0.1:1234 */ + addr.sin_family = AF_INET; + addr.sin_port = htons(1234); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("Connect failed"); + goto UNLOCK_SHUTDOWN; + } + + printf("Start receiving. \n"); + recv_len = recvmsg(sock, &msg, 0); + if (recv_len < 0) { + perror("Recvmsg failed"); + goto SHUTDOWN; + } + + printf("Receive %ld bytes successlly!\n", recv_len); + printf("Data:\n"); + + uint8_t i = 0; + char *s = msg.msg_iov->iov_base; + for (i = 0; i < 6; i++) { + printf(" %s\n", s); + s += strlen(s) + 1; + } + +SHUTDOWN: + shutdown(sock, SHUT_RD); + return NULL; + +UNLOCK_SHUTDOWN: + shutdown(sock, SHUT_RD); +RETURN: + pthread_mutex_unlock(&lock); + return NULL; +} + +int +main(int argc, char *argv[]) +{ + pthread_t cs[2] = { 0 }; + uint8_t i = 0; + int ret = EXIT_SUCCESS; + + if (pthread_mutex_init(&lock, NULL)) { + perror("Initialize mutex failed"); + ret = EXIT_FAILURE; + goto RETURN; + } + + if (pthread_cond_init(&cond, NULL)) { + perror("Initialize condition failed"); + ret = EXIT_FAILURE; + goto DESTROY_MUTEX; + } + + if (pthread_create(&cs[0], NULL, run_as_server, NULL)) { + perror("Create a server thread failed"); + ret = EXIT_FAILURE; + goto DESTROY_COND; + } + + if (pthread_create(&cs[1], NULL, run_as_client, NULL)) { + perror("Create a client thread failed"); + ret = EXIT_FAILURE; + goto DESTROY_COND; + } + + for (i = 0; i < 2; i++) { + pthread_join(cs[i], NULL); + } + +DESTROY_COND: + pthread_cond_destroy(&cond); +DESTROY_MUTEX: + pthread_mutex_destroy(&lock); +RETURN: + return ret; +}