From: monojenkins Date: Tue, 10 Mar 2020 11:20:11 +0000 (-0400) Subject: Convert PLT table and call site to execute only on AMD64. (#33120) X-Git-Tag: submit/tizen/20210909.063632~9251 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=170c05ea7adb773138c36120b903422bafaa0fb7;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Convert PLT table and call site to execute only on AMD64. (#33120) Current implementation embedded critical runtime information directly into PLT slot. It also depends on finding call site in generic trampoline reading call target from instruction stream in order to locate PLT slot in use and then read GOT offset as well as PLT info offset from PLT slot instruction stream. This is problematic on platforms where code is execute only. Fix changes how the metadata needed in order to correctly patch PLT is discovered. Instead of depending on reading instruction stream, it is loaded into RAX (free to be used when calling through PLT on AMD64) in PLT slot before jmp takes place that moves control over to generic trampoline. The PLT slot is the only place where we have access to both GOT index (used in jmp), PLT info offset (currently embedded after jmp instruction) and emitted PLT slot index. Since PLT slot index can be used to recover GOT offset used by PLT slot as well as PLT info offset at runtime, PLT slot index will be emitted into instruction stream and loaded into RAX (prepared to be configurable to other reg if needed) before doing the jump over to generic trampoline. Size of emitted imm constant is optimized based on number of total PLT slots used in image, meaning that 1, 2, or 4 bytes could be used to store PLT slot index as an imm constant in instruction stream. The additional jmp should have minimal to no performance overhead since it should complete within 1 cycle, reading imm constant from instruction stream that shouldn’t incur additional cache misses and sine there is no data dependency between mov and jmp, there should be options to pipeline both instructions. Since mov has smaller latency than indirect jmp, mov should be complete once control gets into the generic tramp (if pipelined). In order to resolve plt info offset at runtime, needed information is now emitted as part of got_info_offsets, increase table with 4 bytes/plt slot, same size currently emitted into the PLT slot instruction stream. Code size of PLT slot is currently 10 bytes (6 bytes jmp and 4 byte PLT info offset). Since PLT info offset has been moved into got_info_offset table, size of PLT slot will be 2, 4, 5 byte mov (depending on needed imm size) and 6 bytes jmp instruction. As an example, mscorlib can emit all its PLT slots using 2 byte imm constant, meaning that the PLT slot will still be 10 bytes, but since PLT info offset of 4 bytes is moved into got_info_offset, total image increase will be 4 bytes/PLT slot. This is however still cheaper than alternatives that would burn 1 trampoline/PLT slot (at least 10 additional bytes/slot) or setup lookup tables in image or calculate more info at runtime, all-consuming more memory in total. Note, using this approach is optional and runtime needs to be built using MONO_ARCH_CODE_EXEC_ONLY in order to enable it since it’s only an opt in features on platforms that can't read from instructions stream. Current implementation is AMD64 only, but same pattern could be applied to other architectures when needed. Co-authored-by: lateralusX --- diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index f6b7e45..318ef23 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -216,7 +216,6 @@ typedef struct MonoAotOptions { gboolean try_llvm; gboolean llvm; gboolean llvm_only; - gboolean method_table_as_data; int nthreads; int ntrampolines; int nrgctx_trampolines; @@ -318,7 +317,7 @@ typedef struct MonoAotCompile { GHashTable *gsharedvt_in_signatures; GHashTable *gsharedvt_out_signatures; guint32 *plt_got_info_offsets; - guint32 got_offset, llvm_got_offset, plt_offset, plt_got_offset_base, nshared_got_entries; + guint32 got_offset, llvm_got_offset, plt_offset, plt_got_offset_base, plt_got_info_offset_base, nshared_got_entries; /* Number of GOT entries reserved for trampolines */ guint32 num_trampoline_got_entries; guint32 tramp_page_size; @@ -1122,6 +1121,9 @@ arch_init (MonoAotCompile *acfg) g_string_append_printf (acfg->llc_args, " -march=x86-64 %s", has_custom_args ? "" : "-mcpu=generic"); /* NOP */ acfg->align_pad_value = 0x90; +#ifdef MONO_ARCH_CODE_EXEC_ONLY + acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_CODE_EXEC_ONLY); +#endif #endif g_string_append (acfg->llc_args, " -enable-implicit-null-checks -disable-fault-maps"); @@ -1796,16 +1798,26 @@ arch_emit_objc_selector_ref (MonoAotCompile *acfg, guint8 *code, int index, int } #endif +#ifdef MONO_ARCH_CODE_EXEC_ONLY +#if defined(TARGET_AMD64) +/* Keep in sync with tramp-amd64.c, aot_arch_get_plt_entry_index. */ +#define PLT_ENTRY_OFFSET_REG AMD64_RAX +#endif +#endif + /* * arch_emit_plt_entry: * * Emit code for the PLT entry. - * The plt entry should look like this: + * The plt entry should look like this on architectures where code is read/execute: * * + * The plt entry should look like this on architectures where code is execute only: + * mov RAX, PLT entry offset + * */ static void -arch_emit_plt_entry (MonoAotCompile *acfg, const char *got_symbol, int offset, int info_offset) +arch_emit_plt_entry (MonoAotCompile *acfg, const char *got_symbol, guint32 plt_index, int offset, int info_offset) { #if defined(TARGET_X86) /* jmp *(%ebx) */ @@ -1815,11 +1827,35 @@ arch_emit_plt_entry (MonoAotCompile *acfg, const char *got_symbol, int offset, i /* Used by mono_aot_get_plt_info_offset */ emit_int32 (acfg, info_offset); #elif defined(TARGET_AMD64) +#ifdef MONO_ARCH_CODE_EXEC_ONLY + guint8 buf [16]; + guint8 *code = buf; + + /* Emit smallest possible imm size 1, 2 or 4 bytes based on total number of PLT entries. */ + if (acfg->plt_offset <= (guint32)0xFF) { + amd64_emit_rex(code, sizeof (guint8), 0, 0, PLT_ENTRY_OFFSET_REG); + *(code)++ = (unsigned char)0xb0 + (PLT_ENTRY_OFFSET_REG & 0x7); + x86_imm_emit8 (code, (guint8)(plt_index)); + } else if (acfg->plt_offset <= (guint32)0xFFFF) { + x86_prefix(code, X86_OPERAND_PREFIX); + amd64_emit_rex(code, sizeof (guint16), 0, 0, PLT_ENTRY_OFFSET_REG); + *(code)++ = (unsigned char)0xb8 + (PLT_ENTRY_OFFSET_REG & 0x7); + x86_imm_emit16 (code, (guint16)(plt_index)); + } else { + amd64_mov_reg_imm_size (code, PLT_ENTRY_OFFSET_REG, plt_index, sizeof(plt_index)); + } + emit_bytes (acfg, buf, code - buf); + acfg->stats.plt_size += code - buf; + emit_unset_mode (acfg); fprintf (acfg->fp, "jmp *%s+%d(%%rip)\n", got_symbol, offset); + acfg->stats.plt_size += 6; +#else + fprintf (acfg->fp, "jmp *%s+%d(%%rip)\n", got_symbol, offset); /* Used by mono_aot_get_plt_info_offset */ emit_int32 (acfg, info_offset); acfg->stats.plt_size += 10; +#endif #elif defined(TARGET_ARM) guint8 buf [256]; guint8 *code; @@ -1859,7 +1895,7 @@ arch_emit_plt_entry (MonoAotCompile *acfg, const char *got_symbol, int offset, i * This is only needed on arm to handle thumb interop. */ static void -arch_emit_llvm_plt_entry (MonoAotCompile *acfg, const char *got_symbol, int offset, int info_offset) +arch_emit_llvm_plt_entry (MonoAotCompile *acfg, const char *got_symbol, int plt_index, int offset, int info_offset) { #if defined(TARGET_ARM) /* LLVM calls the PLT entries using bl, so these have to be thumb2 */ @@ -7360,7 +7396,7 @@ emit_plt (MonoAotCompile *acfg) emit_label (acfg, plt_entry->symbol); - arch_emit_plt_entry (acfg, acfg->got_symbol, (acfg->plt_got_offset_base + i) * sizeof (target_mgreg_t), acfg->plt_got_info_offsets [i]); + arch_emit_plt_entry (acfg, acfg->got_symbol, i, (acfg->plt_got_offset_base + i) * sizeof (target_mgreg_t), acfg->plt_got_info_offsets [i]); if (debug_sym) emit_symbol_size (acfg, debug_sym, "."); @@ -7407,7 +7443,7 @@ emit_plt (MonoAotCompile *acfg) if (acfg->llvm) emit_global_inner (acfg, plt_entry->llvm_symbol, TRUE); - arch_emit_llvm_plt_entry (acfg, acfg->got_symbol, (acfg->plt_got_offset_base + i) * sizeof (target_mgreg_t), acfg->plt_got_info_offsets [i]); + arch_emit_llvm_plt_entry (acfg, acfg->got_symbol, i, (acfg->plt_got_offset_base + i) * sizeof (target_mgreg_t), acfg->plt_got_info_offsets [i]); if (debug_sym) { emit_symbol_size (acfg, debug_sym, "."); @@ -10210,10 +10246,9 @@ emit_code (MonoAotCompile *acfg) #endif sprintf (symbol, "method_addresses"); - if (acfg->aot_opts.method_table_as_data) { + if (acfg->flags & MONO_AOT_FILE_FLAG_CODE_EXEC_ONLY) { /* Emit the method address table as a table of pointers */ emit_section_change (acfg, ".data", 0); - acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_METHOD_TABLE_AS_DATA); } else { emit_section_change (acfg, RODATA_REL_SECT, !!is_func); } @@ -10227,7 +10262,7 @@ emit_code (MonoAotCompile *acfg) for (i = 0; i < acfg->nmethods; ++i) { #ifdef MONO_ARCH_AOT_SUPPORTED - if (acfg->aot_opts.method_table_as_data) { + if (acfg->flags & MONO_AOT_FILE_FLAG_CODE_EXEC_ONLY) { if (!ignore_cfg (acfg->cfgs [i])) emit_pointer (acfg, acfg->cfgs [i]->asm_symbol); else @@ -10971,7 +11006,8 @@ emit_got_info (MonoAotCompile *acfg, gboolean llvm) /* Add the patches needed by the PLT to the GOT */ if (!llvm) { acfg->plt_got_offset_base = acfg->got_offset; - first_plt_got_patch = info->got_patches->len; + acfg->plt_got_info_offset_base = info->got_patches->len; + first_plt_got_patch = acfg->plt_got_info_offset_base; for (i = 1; i < acfg->plt_offset; ++i) { MonoPltEntry *plt_entry = (MonoPltEntry *)g_hash_table_lookup (acfg->plt_offset_to_entry, GUINT_TO_POINTER (i)); @@ -11023,9 +11059,13 @@ emit_got_info (MonoAotCompile *acfg, gboolean llvm) } /* Emit got_info_offsets table */ - +#ifdef MONO_ARCH_CODE_EXEC_ONLY + int got_info_offsets_to_emit = info->got_patches->len; +#else /* No need to emit offsets for the got plt entries, the plt embeds them directly */ - acfg->stats.offsets_size += emit_offset_table (acfg, llvm ? "llvm_got_info_offsets" : "got_info_offsets", llvm ? MONO_AOT_TABLE_LLVM_GOT_INFO_OFFSETS : MONO_AOT_TABLE_GOT_INFO_OFFSETS, llvm ? acfg->llvm_got_offset : first_plt_got_patch, 10, (gint32*)got_info_offsets); + int got_info_offsets_to_emit = first_plt_got_patch; +#endif + acfg->stats.offsets_size += emit_offset_table (acfg, llvm ? "llvm_got_info_offsets" : "got_info_offsets", llvm ? MONO_AOT_TABLE_LLVM_GOT_INFO_OFFSETS : MONO_AOT_TABLE_GOT_INFO_OFFSETS, llvm ? acfg->llvm_got_offset : got_info_offsets_to_emit, 10, (gint32*)got_info_offsets); } static void @@ -11249,6 +11289,7 @@ init_aot_file_info (MonoAotCompile *acfg, MonoAotFileInfo *info) info->version = MONO_AOT_FILE_VERSION; info->plt_got_offset_base = acfg->plt_got_offset_base; + info->plt_got_info_offset_base = acfg->plt_got_info_offset_base; info->got_size = acfg->got_offset * sizeof (target_mgreg_t); info->plt_size = acfg->plt_offset; info->nmethods = acfg->nmethods; @@ -11405,6 +11446,7 @@ emit_aot_file_info (MonoAotCompile *acfg, MonoAotFileInfo *info) emit_pointer (acfg, symbols [i]); emit_int32 (acfg, info->plt_got_offset_base); + emit_int32 (acfg, info->plt_got_info_offset_base); emit_int32 (acfg, info->got_size); emit_int32 (acfg, info->plt_size); emit_int32 (acfg, info->nmethods); diff --git a/src/mono/mono/mini/aot-runtime.c b/src/mono/mono/mini/aot-runtime.c index 42476c2..4a44ad9 100644 --- a/src/mono/mono/mini/aot-runtime.c +++ b/src/mono/mono/mini/aot-runtime.c @@ -79,6 +79,12 @@ #define ENABLE_AOT_CACHE #endif +#ifdef MONO_ARCH_CODE_EXEC_ONLY +extern guint8* mono_aot_arch_get_plt_entry_exec_only (gpointer amodule_info, host_mgreg_t *regs, guint8 *code, guint8 *plt); +extern guint32 mono_arch_get_plt_info_offset_exec_only (gpointer amodule_info, guint8 *plt_entry, host_mgreg_t *regs, guint8 *code, MonoAotResolvePltInfoOffset resolver, gpointer amodule); +extern void mono_arch_patch_plt_entry_exec_only (gpointer amodule_info, guint8 *code, gpointer *got, host_mgreg_t *regs, guint8 *addr); +#endif + #define ROUND_DOWN(VALUE,SIZE) ((VALUE) & ~((SIZE) - 1)) typedef struct { @@ -1861,7 +1867,7 @@ check_usable (MonoAssembly *assembly, MonoAotFileInfo *info, guint8 *blob, char guint32 excluded_cpu_optimizations; if (strcmp (assembly->image->guid, (const char*)info->assembly_guid)) { - msg = g_strdup_printf ("doesn't match assembly"); + msg = g_strdup ("doesn't match assembly"); usable = FALSE; } @@ -1877,41 +1883,41 @@ check_usable (MonoAssembly *assembly, MonoAotFileInfo *info, guint8 *blob, char if (mono_aot_only && !full_aot) { if (!interp) { - msg = g_strdup_printf ("not compiled with --aot=full"); + msg = g_strdup ("not compiled with --aot=full"); usable = FALSE; } } if (!mono_aot_only && full_aot) { - msg = g_strdup_printf ("compiled with --aot=full"); + msg = g_strdup ("compiled with --aot=full"); usable = FALSE; } if (mono_use_interpreter && !interp && !strcmp (assembly->aname.name, "mscorlib")) { /* mscorlib contains necessary interpreter trampolines */ - msg = g_strdup_printf ("not compiled with --aot=interp"); + msg = g_strdup ("not compiled with --aot=interp"); usable = FALSE; } if (mono_llvm_only && !(info->flags & MONO_AOT_FILE_FLAG_LLVM_ONLY)) { - msg = g_strdup_printf ("not compiled with --aot=llvmonly"); + msg = g_strdup ("not compiled with --aot=llvmonly"); usable = FALSE; } if (mono_use_llvm && !(info->flags & MONO_AOT_FILE_FLAG_WITH_LLVM)) { /* Prefer LLVM JITted code when using --llvm */ - msg = g_strdup_printf ("not compiled with --aot=llvm"); + msg = g_strdup ("not compiled with --aot=llvm"); usable = FALSE; } if (mini_debug_options.mdb_optimizations && !(info->flags & MONO_AOT_FILE_FLAG_DEBUG) && !full_aot && !interp) { - msg = g_strdup_printf ("not compiled for debugging"); + msg = g_strdup ("not compiled for debugging"); usable = FALSE; } mono_arch_cpu_optimizations (&excluded_cpu_optimizations); if (info->opts & excluded_cpu_optimizations) { - msg = g_strdup_printf ("compiled with unsupported CPU optimizations"); + msg = g_strdup ("compiled with unsupported CPU optimizations"); usable = FALSE; } if (!mono_aot_only && (info->simd_opts & ~mono_arch_cpu_enumerate_simd_versions ())) { - msg = g_strdup_printf ("compiled with unsupported SIMD extensions"); + msg = g_strdup ("compiled with unsupported SIMD extensions"); usable = FALSE; } @@ -1928,10 +1934,22 @@ check_usable (MonoAssembly *assembly, MonoAotFileInfo *info, guint8 *blob, char safepoints = info->flags & MONO_AOT_FILE_FLAG_SAFEPOINTS; if (!safepoints && mono_threads_are_safepoints_enabled ()) { - msg = g_strdup_printf ("not compiled with safepoints"); + msg = g_strdup ("not compiled with safepoints"); usable = FALSE; } +#ifdef MONO_ARCH_CODE_EXEC_ONLY + if (!(info->flags & MONO_AOT_FILE_FLAG_CODE_EXEC_ONLY)) { + msg = g_strdup ("not compiled targeting a runtime configured as CODE_EXEC_ONLY"); + usable = FALSE; + } +#else + if (info->flags & MONO_AOT_FILE_FLAG_CODE_EXEC_ONLY) { + msg = g_strdup ("compiled targeting a runtime configured as CODE_EXEC_ONLY"); + usable = FALSE; + } +#endif + *out_msg = msg; return usable; } @@ -2392,7 +2410,7 @@ load_aot_module (MonoAssemblyLoadContext *alc, MonoAssembly *assembly, gpointer addr = get_method (i); } - if (amodule->info.flags & MONO_AOT_FILE_FLAG_METHOD_TABLE_AS_DATA) { + if (amodule->info.flags & MONO_AOT_FILE_FLAG_CODE_EXEC_ONLY) { addr = ((gpointer*)amodule->info.method_addresses) [i]; } else { /* method_addresses () contains a table of branches, since the ios linker can update those correctly */ @@ -5065,10 +5083,19 @@ find_aot_module (guint8 *code) return user_data.module; } +#ifdef MONO_ARCH_CODE_EXEC_ONLY +static guint32 +aot_resolve_plt_info_offset (gpointer amodule, guint32 plt_entry_index) +{ + MonoAotModule *module = (MonoAotModule*)amodule; + return mono_aot_get_offset (module->got_info_offsets, module->info.plt_got_info_offset_base + plt_entry_index); +} +#endif + void -mono_aot_patch_plt_entry (guint8 *code, guint8 *plt_entry, gpointer *got, host_mgreg_t *regs, guint8 *addr) +mono_aot_patch_plt_entry (gpointer aot_module, guint8 *code, guint8 *plt_entry, gpointer *got, host_mgreg_t *regs, guint8 *addr) { - MonoAotModule *amodule; + MonoAotModule *amodule = (MonoAotModule *)aot_module; /* * Since AOT code is only used in the root domain, @@ -5077,12 +5104,14 @@ mono_aot_patch_plt_entry (guint8 *code, guint8 *plt_entry, gpointer *got, host_m * mono_method_same_domain () but without loading the metadata for the method. */ if (mono_domain_get () == mono_get_root_domain ()) { - if (!got) { + if (!amodule) { amodule = find_aot_module (code); - if (amodule) - got = amodule->got; } - mono_arch_patch_plt_entry (plt_entry, got, regs, addr); +#ifdef MONO_ARCH_CODE_EXEC_ONLY + mono_arch_patch_plt_entry_exec_only (&amodule->info, plt_entry, amodule->got, regs, addr); +#else + mono_arch_patch_plt_entry (plt_entry, amodule->got, regs, addr); +#endif } } @@ -5094,10 +5123,11 @@ mono_aot_patch_plt_entry (guint8 *code, guint8 *plt_entry, gpointer *got, host_m * Returns NULL if the something cannot be loaded. */ gpointer -mono_aot_plt_resolve (gpointer aot_module, guint32 plt_info_offset, guint8 *code, MonoError *error) +mono_aot_plt_resolve (gpointer aot_module, host_mgreg_t *regs, guint8 *code, MonoError *error) { #ifdef MONO_ARCH_AOT_SUPPORTED guint8 *p, *target, *plt_entry; + guint32 plt_info_offset; MonoJumpInfo ji; MonoAotModule *module = (MonoAotModule*)aot_module; gboolean res, no_ftnptr = FALSE; @@ -5106,6 +5136,11 @@ mono_aot_plt_resolve (gpointer aot_module, guint32 plt_info_offset, guint8 *code error_init (error); + plt_entry = mono_aot_get_plt_entry (regs, code); + g_assert (plt_entry); + + plt_info_offset = mono_aot_get_plt_info_offset (aot_module, plt_entry, regs, code); + //printf ("DYN: %p %d\n", aot_module, plt_info_offset); p = &module->blob [plt_info_offset]; @@ -5178,9 +5213,7 @@ mono_aot_plt_resolve (gpointer aot_module, guint32 plt_info_offset, guint8 *code mono_mempool_destroy (mp); /* Patch the PLT entry with target which might be the actual method not a trampoline */ - plt_entry = mono_aot_get_plt_entry (code); - g_assert (plt_entry); - mono_aot_patch_plt_entry (code, plt_entry, module->got, NULL, target); + mono_aot_patch_plt_entry (aot_module, code, plt_entry, module->got, regs, target); return target; #else @@ -5240,7 +5273,7 @@ init_plt (MonoAotModule *amodule) * Return the address of the PLT entry called by the code at CODE if exists. */ guint8* -mono_aot_get_plt_entry (guint8 *code) +mono_aot_get_plt_entry (host_mgreg_t *regs, guint8 *code) { MonoAotModule *amodule = find_aot_module (code); guint8 *target = NULL; @@ -5254,7 +5287,11 @@ mono_aot_get_plt_entry (guint8 *code) #endif #ifdef MONO_ARCH_AOT_SUPPORTED +#ifdef MONO_ARCH_CODE_EXEC_ONLY + target = mono_aot_arch_get_plt_entry_exec_only (&amodule->info, regs, code, amodule->plt); +#else target = mono_arch_get_call_target (code); +#endif #else g_assert_not_reached (); #endif @@ -5284,15 +5321,20 @@ mono_aot_get_plt_entry (guint8 *code) * Return the PLT info offset belonging to the plt entry called by CODE. */ guint32 -mono_aot_get_plt_info_offset (host_mgreg_t *regs, guint8 *code) +mono_aot_get_plt_info_offset (gpointer aot_module, guint8 *plt_entry, host_mgreg_t *regs, guint8 *code) { - guint8 *plt_entry = mono_aot_get_plt_entry (code); - - g_assert (plt_entry); + if (!plt_entry) { + plt_entry = mono_aot_get_plt_entry (regs, code); + g_assert (plt_entry); + } /* The offset is embedded inside the code after the plt entry */ #ifdef MONO_ARCH_AOT_SUPPORTED +#ifdef MONO_ARCH_CODE_EXEC_ONLY + return mono_arch_get_plt_info_offset_exec_only (&((MonoAotModule*)aot_module)->info, plt_entry, regs, code, aot_resolve_plt_info_offset, aot_module); +#else return mono_arch_get_plt_info_offset (plt_entry, regs, code); +#endif #else g_assert_not_reached (); return 0; @@ -6362,13 +6404,13 @@ mono_aot_get_plt_entry (guint8 *code) } gpointer -mono_aot_plt_resolve (gpointer aot_module, guint32 plt_info_offset, guint8 *code, MonoError *error) +mono_aot_plt_resolve (gpointer aot_module, host_mgreg_t *regs, guint8 *code, MonoError *error) { return NULL; } void -mono_aot_patch_plt_entry (guint8 *code, guint8 *plt_entry, gpointer *got, host_mgreg_t *regs, guint8 *addr) +mono_aot_patch_plt_entry (gpointer aot_module, guint8 *code, guint8 *plt_entry, gpointer *got, host_mgreg_t *regs, guint8 *addr) { } diff --git a/src/mono/mono/mini/aot-runtime.h b/src/mono/mono/mini/aot-runtime.h index 0b6a0a2..9d37c77 100644 --- a/src/mono/mono/mini/aot-runtime.h +++ b/src/mono/mono/mini/aot-runtime.h @@ -11,7 +11,7 @@ #include "mini.h" /* Version number of the AOT file format */ -#define MONO_AOT_FILE_VERSION 173 +#define MONO_AOT_FILE_VERSION 174 #define MONO_AOT_TRAMP_PAGE_SIZE 16384 @@ -74,7 +74,7 @@ typedef enum { MONO_AOT_FILE_FLAG_SEPARATE_DATA = 64, MONO_AOT_FILE_FLAG_EAGER_LOAD = 128, MONO_AOT_FILE_FLAG_INTERP = 256, - MONO_AOT_FILE_FLAG_METHOD_TABLE_AS_DATA = 512 + MONO_AOT_FILE_FLAG_CODE_EXEC_ONLY = 512 } MonoAotFileFlags; typedef enum { @@ -180,6 +180,8 @@ typedef struct MonoAotFileInfo /* Scalars */ /* The index of the first GOT slot used by the PLT */ guint32 plt_got_offset_base; + /* The index of the first GOT info slot used by the PLT */ + guint32 plt_got_info_offset_base; /* Number of entries in the GOT */ guint32 got_size; /* Number of entries in the PLT */ @@ -240,13 +242,13 @@ gpointer mono_aot_get_method (MonoDomain *domain, MonoMethod *method, MonoError *error); gpointer mono_aot_get_method_from_token (MonoDomain *domain, MonoImage *image, guint32 token, MonoError *error); gboolean mono_aot_is_got_entry (guint8 *code, guint8 *addr); -guint8* mono_aot_get_plt_entry (guint8 *code); -guint32 mono_aot_get_plt_info_offset (host_mgreg_t *regs, guint8 *code); +guint8* mono_aot_get_plt_entry (host_mgreg_t *regs, guint8 *code); +guint32 mono_aot_get_plt_info_offset (gpointer aot_module, guint8 *plt_entry, host_mgreg_t *regs, guint8 *code); gboolean mono_aot_get_cached_class_info (MonoClass *klass, MonoCachedClassInfo *res); gboolean mono_aot_get_class_from_name (MonoImage *image, const char *name_space, const char *name, MonoClass **klass); MonoJitInfo* mono_aot_find_jit_info (MonoDomain *domain, MonoImage *image, gpointer addr); -gpointer mono_aot_plt_resolve (gpointer aot_module, guint32 plt_info_offset, guint8 *code, MonoError *error); -void mono_aot_patch_plt_entry (guint8 *code, guint8 *plt_entry, gpointer *got, host_mgreg_t *regs, guint8 *addr); +gpointer mono_aot_plt_resolve (gpointer aot_module, host_mgreg_t *regs, guint8 *code, MonoError *error); +void mono_aot_patch_plt_entry (gpointer aot_module, guint8 *code, guint8 *plt_entry, gpointer *got, host_mgreg_t *regs, guint8 *addr); gpointer mono_aot_get_method_from_vt_slot (MonoDomain *domain, MonoVTable *vtable, int slot, MonoError *error); gpointer mono_aot_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len); gpointer mono_aot_get_trampoline (const char *name); @@ -281,4 +283,8 @@ typedef unsigned char* (*MonoLoadAotDataFunc) (MonoAssembly *assembly, typedef void (*MonoFreeAotDataFunc) (MonoAssembly *assembly, int size, gpointer user_data, void *handle); MONO_API void mono_install_load_aot_data_hook (MonoLoadAotDataFunc load_func, MonoFreeAotDataFunc free_func, gpointer user_data); +#ifdef MONO_ARCH_CODE_EXEC_ONLY +typedef guint32 (*MonoAotResolvePltInfoOffset)(gpointer amodule, guint32 plt_entry_index); +#endif + #endif /* __MONO_AOT_RUNTIME_H__ */ diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index b9ea744..f4bb201 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -10007,7 +10007,11 @@ AddJitGlobal (MonoLLVMModule *module, LLVMTypeRef type, const char *name) g_free (s); return v; } -#define FILE_INFO_NFIELDS (2 + MONO_AOT_FILE_INFO_NUM_SYMBOLS + 22 + 5) +#define FILE_INFO_NUM_HEADER_FIELDS 2 +#define FILE_INFO_NUM_SCALAR_FIELDS 22 +#define FILE_INFO_NUM_ARRAY_FIELDS 5 +#define FILE_INFO_NUM_AOTID_FIELDS 1 +#define FILE_INFO_NFIELDS (FILE_INFO_NUM_HEADER_FIELDS + MONO_AOT_FILE_INFO_NUM_SYMBOLS + FILE_INFO_NUM_SCALAR_FIELDS + FILE_INFO_NUM_ARRAY_FIELDS + FILE_INFO_NUM_AOTID_FIELDS) static void create_aot_info_var (MonoLLVMModule *module) @@ -10028,11 +10032,11 @@ create_aot_info_var (MonoLLVMModule *module) for (i = 0; i < MONO_AOT_FILE_INFO_NUM_SYMBOLS; ++i) eltypes [tindex ++] = LLVMPointerType (LLVMInt8Type (), 0); /* Scalars */ - for (i = 0; i < 21; ++i) + for (i = 0; i < FILE_INFO_NUM_SCALAR_FIELDS; ++i) eltypes [tindex ++] = LLVMInt32Type (); /* Arrays */ eltypes [tindex ++] = LLVMArrayType (LLVMInt32Type (), MONO_AOT_TABLE_NUM); - for (i = 0; i < 4; ++i) + for (i = 0; i < FILE_INFO_NUM_ARRAY_FIELDS - 1; ++i) eltypes [tindex ++] = LLVMArrayType (LLVMInt32Type (), MONO_AOT_TRAMP_NUM); eltypes [tindex ++] = LLVMArrayType (LLVMInt8Type (), 16); g_assert (tindex == nfields); @@ -10172,12 +10176,13 @@ emit_aot_file_info (MonoLLVMModule *module) } for (i = 0; i < MONO_AOT_FILE_INFO_NUM_SYMBOLS; ++i) { - g_assert (fields [2 + i]); - fields [2 + i] = LLVMConstBitCast (fields [2 + i], eltype); + g_assert (fields [FILE_INFO_NUM_HEADER_FIELDS + i]); + fields [FILE_INFO_NUM_HEADER_FIELDS + i] = LLVMConstBitCast (fields [FILE_INFO_NUM_HEADER_FIELDS + i], eltype); } /* Scalars */ fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->plt_got_offset_base, FALSE); + fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->plt_got_info_offset_base, FALSE); fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->got_size, FALSE); fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->plt_size, FALSE); fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->nmethods, FALSE); diff --git a/src/mono/mono/mini/mini-trampolines.c b/src/mono/mono/mini/mini-trampolines.c index 55834cd..bf0886e 100644 --- a/src/mono/mono/mini/mini-trampolines.c +++ b/src/mono/mono/mini/mini-trampolines.c @@ -697,7 +697,7 @@ common_call_trampoline (host_mgreg_t *regs, guint8 *code, MonoMethod *m, MonoVTa *vtable_slot_to_patch = mono_get_addr_from_ftnptr (addr); } } else { - guint8 *plt_entry = mono_aot_get_plt_entry (code); + guint8 *plt_entry = mono_aot_get_plt_entry (regs, code); gboolean no_patch = FALSE; MonoJitInfo *target_ji; @@ -718,7 +718,7 @@ common_call_trampoline (host_mgreg_t *regs, guint8 *code, MonoMethod *m, MonoVTa } } if (!no_patch) - mono_aot_patch_plt_entry (code, plt_entry, NULL, regs, (guint8 *)addr); + mono_aot_patch_plt_entry (NULL, code, plt_entry, NULL, regs, (guint8 *)addr); } else { if (generic_shared) { if (m->wrapper_type != MONO_WRAPPER_NONE) @@ -951,10 +951,10 @@ mono_aot_trampoline (host_mgreg_t *regs, guint8 *code, guint8 *token_info, addr = mono_create_ftnptr (mono_domain_get (), addr); /* This is a normal call through a PLT entry */ - plt_entry = mono_aot_get_plt_entry (code); + plt_entry = mono_aot_get_plt_entry (regs, code); g_assert (plt_entry); - mono_aot_patch_plt_entry (code, plt_entry, NULL, regs, (guint8 *)addr); + mono_aot_patch_plt_entry (NULL, code, plt_entry, NULL, regs, (guint8 *)addr); return addr; } @@ -970,13 +970,12 @@ mono_aot_plt_trampoline (host_mgreg_t *regs, guint8 *code, guint8 *aot_module, { MONO_REQ_GC_UNSAFE_MODE; - guint32 plt_info_offset = mono_aot_get_plt_info_offset (regs, code); gpointer res; ERROR_DECL (error); UnlockedIncrement (&trampoline_calls); - res = mono_aot_plt_resolve (aot_module, plt_info_offset, code, error); + res = mono_aot_plt_resolve (aot_module, regs, code, error); if (!res) { if (!is_ok (error)) { mono_error_set_pending_exception (error); diff --git a/src/mono/mono/mini/tramp-amd64.c b/src/mono/mono/mini/tramp-amd64.c index 4b74ac4..80df122 100644 --- a/src/mono/mono/mini/tramp-amd64.c +++ b/src/mono/mono/mini/tramp-amd64.c @@ -36,6 +36,13 @@ #endif #include "mono/utils/mono-tls-inline.h" +#ifdef MONO_ARCH_CODE_EXEC_ONLY +#include "aot-runtime.h" +guint8* mono_aot_arch_get_plt_entry_exec_only (gpointer amodule_info, host_mgreg_t *regs, guint8 *code, guint8 *plt); +guint32 mono_arch_get_plt_info_offset_exec_only (gpointer amodule_info, guint8 *plt_entry, host_mgreg_t *regs, guint8 *code, MonoAotResolvePltInfoOffset resolver, gpointer amodule); +void mono_arch_patch_plt_entry_exec_only (gpointer amodule_info, guint8 *code, gpointer *got, host_mgreg_t *regs, guint8 *addr); +#endif + #define IS_REX(inst) (((inst) >= 0x40) && ((inst) <= 0x4f)) #ifndef DISABLE_JIT @@ -207,26 +214,7 @@ mono_arch_create_llvm_native_thunk (MonoDomain *domain, guint8 *addr) MONO_PROFILER_RAISE (jit_code_buffer, (thunk_start, thunk_code - thunk_start, MONO_PROFILER_CODE_BUFFER_HELPER, NULL)); return addr; } -#endif /* !DISABLE_JIT */ - -void -mono_arch_patch_plt_entry (guint8 *code, gpointer *got, host_mgreg_t *regs, guint8 *addr) -{ - gint32 disp; - gpointer *plt_jump_table_entry; - - /* A PLT entry: jmp *(%rip) */ - g_assert (code [0] == 0xff); - g_assert (code [1] == 0x25); - - disp = *(gint32*)(code + 2); - - plt_jump_table_entry = (gpointer*)(code + 6 + disp); - - mono_atomic_xchg_ptr (plt_jump_table_entry, addr); -} -#ifndef DISABLE_JIT static void stack_unaligned (MonoTrampolineType tramp_type) { @@ -360,7 +348,7 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf g_assert (r11_save_code == after_r11_save_code); /* Copy from the save slot into the register array slot */ - amd64_mov_reg_membase (code, i, AMD64_RSP, r11_save_offset + orig_rsp_to_rbp_offset, sizeof (target_mgreg_t)); + amd64_mov_reg_membase (code, i, AMD64_RSP, r11_save_offset + orig_rsp_to_rbp_offset + framesize, sizeof (target_mgreg_t)); amd64_mov_membase_reg (code, AMD64_RBP, saved_regs_offset + (i * sizeof (target_mgreg_t)), i, sizeof (target_mgreg_t)); } /* cfa = rbp + cfa_offset */ @@ -552,7 +540,7 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf /* Restore argument registers, r10 (imt method/rgxtx) and rax (needed for direct calls to C vararg functions). */ for (i = 0; i < AMD64_NREG; ++i) - if (AMD64_IS_ARGUMENT_REG (i) || i == AMD64_R10 || i == AMD64_RAX) + if (AMD64_IS_ARGUMENT_REG (i) || i == AMD64_R10 || i == AMD64_RAX || i == AMD64_R11) amd64_mov_reg_membase (code, i, AMD64_RBP, saved_regs_offset + (i * sizeof (target_mgreg_t)), sizeof (target_mgreg_t)); for (i = 0; i < AMD64_XMM_NREG; ++i) if (AMD64_IS_ARGUMENT_XREG (i)) @@ -818,6 +806,70 @@ mono_arch_get_call_target (guint8 *code) } } +#ifdef MONO_ARCH_CODE_EXEC_ONLY +/* Keep in sync with aot-compiler.c, arch_emit_plt_entry. */ +#define PLT_ENTRY_OFFSET_REG AMD64_RAX + +/* If PLT_ENTRY_OFFSET_REG is R8 - R15, increase mov instruction size by 1 due to use of REX. */ +#define PLT_MOV_REG_IMM8_SIZE (1 + sizeof (guint8)) +#define PLT_MOV_REG_IMM16_SIZE (2 + sizeof (guint16)) +#define PLT_MOV_REG_IMM32_SIZE (1 + sizeof (guint32)) +#define PLT_JMP_INST_SIZE 6 + +static guchar +aot_arch_get_plt_entry_size (MonoAotFileInfo *info, host_mgreg_t *regs, guint8 *code, guint8 *plt) +{ + if (info->plt_size <= 0xFF) + return PLT_MOV_REG_IMM8_SIZE + PLT_JMP_INST_SIZE; + else if (info->plt_size <= 0xFFFF) + return PLT_MOV_REG_IMM16_SIZE + PLT_JMP_INST_SIZE; + else + return PLT_MOV_REG_IMM32_SIZE + PLT_JMP_INST_SIZE; +} + +static guint32 +aot_arch_get_plt_entry_index (MonoAotFileInfo *info, host_mgreg_t *regs, guint8 *code, guint8 *plt) +{ + if (info->plt_size <= 0xFF) + return regs[PLT_ENTRY_OFFSET_REG] & 0xFF; + else if (info->plt_size <= 0xFFFF) + return regs[PLT_ENTRY_OFFSET_REG] & 0xFFFF; + else + return regs[PLT_ENTRY_OFFSET_REG] & 0xFFFFFFFF; +} + +guint8* +mono_aot_arch_get_plt_entry_exec_only (gpointer amodule_info, host_mgreg_t *regs, guint8 *code, guint8 *plt) +{ + guint32 plt_entry_index = aot_arch_get_plt_entry_index ((MonoAotFileInfo *)amodule_info, regs, code, plt); + guchar plt_entry_size = aot_arch_get_plt_entry_size ((MonoAotFileInfo *)amodule_info, regs, code, plt); + + /* First PLT slot is never emitted into table, take that into account */ + /* when calculating corresponding PLT entry. */ + plt_entry_index--; + return plt + ((gsize)plt_entry_index * (gsize)plt_entry_size); +} + +guint32 +mono_arch_get_plt_info_offset_exec_only (gpointer amodule_info, guint8 *plt_entry, host_mgreg_t *regs, guint8 *code, MonoAotResolvePltInfoOffset resolver, gpointer amodule) +{ + guint32 plt_entry_index = aot_arch_get_plt_entry_index ((MonoAotFileInfo *)amodule_info, regs, code, NULL); + + /* First PLT slot is never emitted into table, take that into account */ + /* when calculating offset. */ + plt_entry_index--; + return resolver (amodule, plt_entry_index); +} + +void +mono_arch_patch_plt_entry_exec_only (gpointer amodule_info, guint8 *code, gpointer *got, host_mgreg_t *regs, guint8 *addr) +{ + /* Same calculation of GOT offset as done in aot-compiler.c, emit_plt and used as jmp DISP. */ + guint32 plt_entry_index = aot_arch_get_plt_entry_index ((MonoAotFileInfo *)amodule_info, regs, code, NULL); + gpointer *plt_jump_table_entry = ((gpointer *)(got + ((MonoAotFileInfo *)amodule_info)->plt_got_offset_base) + plt_entry_index); + mono_atomic_xchg_ptr (plt_jump_table_entry, addr); +} +#else /* * mono_arch_get_plt_info_offset: * @@ -829,6 +881,24 @@ mono_arch_get_plt_info_offset (guint8 *plt_entry, host_mgreg_t *regs, guint8 *co return *(guint32*)(plt_entry + 6); } +void +mono_arch_patch_plt_entry (guint8 *code, gpointer *got, host_mgreg_t *regs, guint8 *addr) +{ + gint32 disp; + gpointer *plt_jump_table_entry; + + /* A PLT entry: jmp *(%rip) */ + g_assert (code [0] == 0xff); + g_assert (code [1] == 0x25); + + disp = *(gint32*)(code + 2); + + plt_jump_table_entry = (gpointer*)(code + 6 + disp); + + mono_atomic_xchg_ptr (plt_jump_table_entry, addr); +} +#endif + #ifndef DISABLE_JIT /* * mono_arch_create_sdb_trampoline: