diff options
-rw-r--r-- | man/mono.1 | 6 | ||||
-rw-r--r-- | mono/metadata/marshal.c | 2 | ||||
-rw-r--r-- | mono/metadata/marshal.h | 1 | ||||
-rw-r--r-- | mono/mini/aot-compiler.c | 2 | ||||
-rw-r--r-- | mono/mini/cpu-x86.md | 2 | ||||
-rw-r--r-- | mono/mini/method-to-ir.c | 36 | ||||
-rw-r--r-- | mono/mini/mini-ops.h | 3 | ||||
-rw-r--r-- | mono/mini/mini-x86.c | 6 | ||||
-rw-r--r-- | mono/mini/mini.c | 5 | ||||
-rw-r--r-- | mono/mini/mini.h | 9 | ||||
-rw-r--r-- | mono/tests/libtest.c | 12 | ||||
-rw-r--r-- | mono/tests/pinvoke2.cs | 20 |
12 files changed, 100 insertions, 4 deletions
diff --git a/man/mono.1 b/man/mono.1 index 74cbb7dbb8b..99be6c4f23b 100644 --- a/man/mono.1 +++ b/man/mono.1 @@ -1534,6 +1534,12 @@ after a SIGSEGV or SIGABRT in unmanaged code. This option will suspend the program when a native SIGSEGV is received. This is useful for debugging crashes which do not happen under gdb, since a live process contains more information than a core file. +.TP +\fBcheck-pinvoke-callconv\fR +This option causes the runtime to check for calling convention +mismatches when using pinvoke, i.e. mixing cdecl/stdcall. It only +works on windows. If a mismatch is detected, an +ExecutionEngineException is thrown. .ne .RE .TP diff --git a/mono/metadata/marshal.c b/mono/metadata/marshal.c index 792a1dcf37e..f7b8f98b5ea 100644 --- a/mono/metadata/marshal.c +++ b/mono/metadata/marshal.c @@ -9044,7 +9044,7 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, mb, csig, csig->param_count + 16); mono_mb_free (mb); - info = mono_wrapper_info_create (res, WRAPPER_SUBTYPE_NONE); + info = mono_wrapper_info_create (res, WRAPPER_SUBTYPE_PINVOKE); info->d.managed_to_native.method = method; mono_marshal_set_wrapper_info (res, info); diff --git a/mono/metadata/marshal.h b/mono/metadata/marshal.h index a5e45ee0d7a..a85bd5d26fb 100644 --- a/mono/metadata/marshal.h +++ b/mono/metadata/marshal.h @@ -104,6 +104,7 @@ typedef enum { /* Subtypes of MONO_WRAPPER_MANAGED_TO_NATIVE */ WRAPPER_SUBTYPE_ICALL_WRAPPER, WRAPPER_SUBTYPE_NATIVE_FUNC_AOT, + WRAPPER_SUBTYPE_PINVOKE, /* Subtypes of MONO_WRAPPER_UNKNOWN */ WRAPPER_SUBTYPE_SYNCHRONIZED_INNER, WRAPPER_SUBTYPE_GSHAREDVT_IN, diff --git a/mono/mini/aot-compiler.c b/mono/mini/aot-compiler.c index 8fb6c47d5a2..9f50e95b128 100644 --- a/mono/mini/aot-compiler.c +++ b/mono/mini/aot-compiler.c @@ -2655,7 +2655,7 @@ encode_method_ref (MonoAotCompile *acfg, MonoMethod *method, guint8 *buf, guint8 } else if (info->subtype == WRAPPER_SUBTYPE_NATIVE_FUNC_AOT) { encode_method_ref (acfg, info->d.managed_to_native.method, p, &p); } else { - g_assert (info->subtype == WRAPPER_SUBTYPE_NONE); + g_assert (info->subtype == WRAPPER_SUBTYPE_NONE || info->subtype == WRAPPER_SUBTYPE_PINVOKE); encode_method_ref (acfg, info->d.managed_to_native.method, p, &p); } break; diff --git a/mono/mini/cpu-x86.md b/mono/mini/cpu-x86.md index 1d7197f7392..b239ed0e765 100644 --- a/mono/mini/cpu-x86.md +++ b/mono/mini/cpu-x86.md @@ -634,3 +634,5 @@ gc_liveness_def: len:0 gc_liveness_use: len:0 gc_spill_slot_liveness_def: len:0 gc_param_slot_liveness_def: len:0 +get_sp: dest:i len:6 +set_sp: src1:i len:6 diff --git a/mono/mini/method-to-ir.c b/mono/mini/method-to-ir.c index a83d01abe95..9024af39d73 100644 --- a/mono/mini/method-to-ir.c +++ b/mono/mini/method-to-ir.c @@ -2571,13 +2571,31 @@ inline static MonoInst* mono_emit_calli (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args, MonoInst *addr, MonoInst *imt_arg, MonoInst *rgctx_arg) { MonoCallInst *call; + MonoInst *ins; int rgctx_reg = -1; + gboolean check_sp = FALSE; + + if (cfg->check_pinvoke_callconv && cfg->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) { + WrapperInfo *info = mono_marshal_get_wrapper_info (cfg->method); + + if (info && info->subtype == WRAPPER_SUBTYPE_PINVOKE) + check_sp = TRUE; + } if (rgctx_arg) { rgctx_reg = mono_alloc_preg (cfg); MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, rgctx_reg, rgctx_arg->dreg); } + if (check_sp) { + if (!cfg->stack_inbalance_var) + cfg->stack_inbalance_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL); + + MONO_INST_NEW (cfg, ins, OP_GET_SP); + ins->dreg = cfg->stack_inbalance_var->dreg; + MONO_ADD_INS (cfg->cbb, ins); + } + call = mono_emit_call_args (cfg, sig, args, TRUE, FALSE, FALSE, rgctx_arg ? TRUE : FALSE, FALSE); call->inst.sreg1 = addr->dreg; @@ -2587,6 +2605,24 @@ mono_emit_calli (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args, Mo MONO_ADD_INS (cfg->cbb, (MonoInst*)call); + if (check_sp) { + int sp_reg; + + sp_reg = mono_alloc_preg (cfg); + + MONO_INST_NEW (cfg, ins, OP_GET_SP); + ins->dreg = sp_reg; + MONO_ADD_INS (cfg->cbb, ins); + + /* Restore the stack so we don't crash when throwing the exception */ + MONO_INST_NEW (cfg, ins, OP_SET_SP); + ins->sreg1 = cfg->stack_inbalance_var->dreg; + MONO_ADD_INS (cfg->cbb, ins); + + MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, cfg->stack_inbalance_var->dreg, sp_reg); + MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "ExecutionEngineException"); + } + if (rgctx_arg) set_rgctx_arg (cfg, call, rgctx_reg, rgctx_arg); diff --git a/mono/mini/mini-ops.h b/mono/mini/mini-ops.h index c9cec9f7cc6..59011ebccd3 100644 --- a/mono/mini/mini-ops.h +++ b/mono/mini/mini-ops.h @@ -1245,3 +1245,6 @@ MINI_OP(OP_LLVM_OUTARG_VT, "llvm_outarg_vt", IREG, VREG, NONE) MINI_OP(OP_OBJC_GET_SELECTOR, "objc_get_selector", IREG, NONE, NONE) +MINI_OP(OP_GET_SP, "get_sp", IREG, NONE, NONE) +MINI_OP(OP_SET_SP, "set_sp", NONE, IREG, NONE) + diff --git a/mono/mini/mini-x86.c b/mono/mini/mini-x86.c index 3d9863e868a..d34fc64190b 100644 --- a/mono/mini/mini-x86.c +++ b/mono/mini/mini-x86.c @@ -5039,6 +5039,12 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) ins->backend.pc_offset = code - cfg->native_code; bb->spill_slot_defs = g_slist_prepend_mempool (cfg->mempool, bb->spill_slot_defs, ins); break; + case OP_GET_SP: + x86_mov_reg_reg (code, ins->dreg, X86_ESP, sizeof (mgreg_t)); + break; + case OP_SET_SP: + x86_mov_reg_reg (code, X86_ESP, ins->sreg1, sizeof (mgreg_t)); + break; default: g_warning ("unknown opcode %s\n", mono_inst_name (ins->opcode)); g_assert_not_reached (); diff --git a/mono/mini/mini.c b/mono/mini/mini.c index 616d696b95b..941ced81111 100644 --- a/mono/mini/mini.c +++ b/mono/mini/mini.c @@ -4990,6 +4990,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, JitFl cfg->gen_seq_points = debug_options.gen_seq_points; cfg->explicit_null_checks = debug_options.explicit_null_checks; cfg->soft_breakpoints = debug_options.soft_breakpoints; + cfg->check_pinvoke_callconv = debug_options.check_pinvoke_callconv; if (try_generic_shared) cfg->generic_sharing_context = (MonoGenericSharingContext*)&cfg->gsctx; cfg->compile_llvm = try_llvm; @@ -6985,9 +6986,11 @@ mini_parse_debug_options (void) debug_options.better_cast_details = TRUE; else if (!strcmp (arg, "soft-breakpoints")) debug_options.soft_breakpoints = TRUE; + else if (!strcmp (arg, "check-pinvoke-callconv")) + debug_options.check_pinvoke_callconv = TRUE; else { fprintf (stderr, "Invalid option for the MONO_DEBUG env variable: %s\n", arg); - fprintf (stderr, "Available options: 'handle-sigint', 'keep-delegates', 'reverse-pinvoke-exceptions', 'collect-pagefault-stats', 'break-on-unverified', 'no-gdb-backtrace', 'dont-free-domains', 'suspend-on-sigsegv', 'suspend-on-exception', 'suspend-on-unhandled', 'dyn-runtime-invoke', 'gdb', 'explicit-null-checks', 'init-stacks'\n"); + fprintf (stderr, "Available options: 'handle-sigint', 'keep-delegates', 'reverse-pinvoke-exceptions', 'collect-pagefault-stats', 'break-on-unverified', 'no-gdb-backtrace', 'dont-free-domains', 'suspend-on-sigsegv', 'suspend-on-exception', 'suspend-on-unhandled', 'dyn-runtime-invoke', 'gdb', 'explicit-null-checks', 'init-stacks', 'check-pinvoke-callconv'\n"); exit (1); } } diff --git a/mono/mini/mini.h b/mono/mini/mini.h index 7069811551f..88adbd8c3c3 100644 --- a/mono/mini/mini.h +++ b/mono/mini/mini.h @@ -122,7 +122,7 @@ #endif /* Version number of the AOT file format */ -#define MONO_AOT_FILE_VERSION 99 +#define MONO_AOT_FILE_VERSION 100 //TODO: This is x86/amd64 specific. #define mono_simd_shuffle_mask(a,b,c,d) ((a) | ((b) << 2) | ((c) << 4) | ((d) << 6)) @@ -1412,6 +1412,8 @@ typedef struct { MonoInst *lmf_var; MonoInst *lmf_addr_var; + MonoInst *stack_inbalance_var; + unsigned char *cil_start; #ifdef __native_client_codegen__ /* this alloc is not aligned, native_code */ @@ -1486,6 +1488,7 @@ typedef struct { guint has_indirection : 1; guint has_atomic_add_new_i4 : 1; guint has_atomic_exchange_i4 : 1; + guint check_pinvoke_callconv : 1; gpointer debug_info; guint32 lmf_offset; guint16 *intvars; @@ -1843,6 +1846,10 @@ typedef struct { * Load AOT JIT info eagerly. */ gboolean load_aot_jit_info_eagerly; + /* + * Check for pinvoke calling convention mismatches. + */ + gboolean check_pinvoke_callconv; } MonoDebugOptions; enum { diff --git a/mono/tests/libtest.c b/mono/tests/libtest.c index f683ee2b66e..e057944bbd7 100644 --- a/mono/tests/libtest.c +++ b/mono/tests/libtest.c @@ -1973,6 +1973,18 @@ mono_test_stdcall_name_mangling (int a, int b, int c) return a + b + c; } +LIBTEST_API int +mono_test_stdcall_mismatch_1 (int a, int b, int c) +{ + return a + b + c; +} + +LIBTEST_API int STDCALL +mono_test_stdcall_mismatch_2 (int a, int b, int c) +{ + return a + b + c; +} + /* * PASSING AND RETURNING SMALL STRUCTURES FROM DELEGATES TESTS */ diff --git a/mono/tests/pinvoke2.cs b/mono/tests/pinvoke2.cs index 22c83573c40..71c51a7d814 100644 --- a/mono/tests/pinvoke2.cs +++ b/mono/tests/pinvoke2.cs @@ -1320,6 +1320,26 @@ public class Tests { return string_marshal_test3 (null); } +#if FALSE + [DllImport ("libtest", EntryPoint="mono_test_stdcall_mismatch_1", CallingConvention=CallingConvention.StdCall)] + public static extern int mono_test_stdcall_mismatch_1 (int a, int b, int c); + + /* Test mismatched called conventions, the native function is cdecl */ + public static int test_0_stdcall_mismatch_1 () { + mono_test_stdcall_mismatch_1 (0, 1, 2); + return 0; + } + + [DllImport ("libtest", EntryPoint="mono_test_stdcall_mismatch_2", CallingConvention=CallingConvention.Cdecl)] + public static extern int mono_test_stdcall_mismatch_2 (int a, int b, int c); + + /* Test mismatched called conventions, the native function is stdcall */ + public static int test_0_stdcall_mismatch_2 () { + mono_test_stdcall_mismatch_2 (0, 1, 2); + return 0; + } +#endif + [DllImport ("libtest", EntryPoint="mono_test_stdcall_name_mangling", CallingConvention=CallingConvention.StdCall)] public static extern int mono_test_stdcall_name_mangling (int a, int b, int c); |