summaryrefslogtreecommitdiff
path: root/erts/emulator/beam/jit/beam_jit_main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/beam/jit/beam_jit_main.cpp')
-rw-r--r--erts/emulator/beam/jit/beam_jit_main.cpp142
1 files changed, 94 insertions, 48 deletions
diff --git a/erts/emulator/beam/jit/beam_jit_main.cpp b/erts/emulator/beam/jit/beam_jit_main.cpp
index ef0ad6943a..0cd732039f 100644
--- a/erts/emulator/beam/jit/beam_jit_main.cpp
+++ b/erts/emulator/beam/jit/beam_jit_main.cpp
@@ -43,6 +43,8 @@ ErtsFrameLayout ERTS_WRITE_UNLIKELY(erts_frame_layout);
#ifdef HAVE_LINUX_PERF_SUPPORT
enum beamasm_perf_flags erts_jit_perf_support;
#endif
+/* Force use of single-mapped RWX memory for JIT code */
+int erts_jit_single_map = 0;
/*
* Special Beam instructions.
@@ -54,7 +56,8 @@ ErtsCodePtr beam_exit;
ErtsCodePtr beam_export_trampoline;
ErtsCodePtr beam_bif_export_trap;
ErtsCodePtr beam_continue_exit;
-ErtsCodePtr beam_save_calls;
+ErtsCodePtr beam_save_calls_export;
+ErtsCodePtr beam_save_calls_fun;
ErtsCodePtr beam_unloaded_fun;
/* NOTE These should be the only variables containing trace instructions.
@@ -65,7 +68,7 @@ ErtsCodePtr beam_unloaded_fun;
ErtsCodePtr beam_return_to_trace; /* OpCode(i_return_to_trace) */
ErtsCodePtr beam_return_trace; /* OpCode(i_return_trace) */
ErtsCodePtr beam_exception_trace; /* UGLY also OpCode(i_return_trace) */
-ErtsCodePtr beam_return_time_trace; /* OpCode(i_return_time_trace) */
+ErtsCodePtr beam_call_trace_return; /* OpCode(i_call_trace_return) */
static JitAllocator *jit_allocator;
@@ -86,7 +89,7 @@ static void install_bifs(void) {
int i;
ASSERT(beam_export_trampoline != NULL);
- ASSERT(beam_save_calls != NULL);
+ ASSERT(beam_save_calls_export != NULL);
for (i = 0; i < BIF_SIZE; i++) {
BifEntry *entry;
@@ -117,42 +120,56 @@ static void install_bifs(void) {
}
}
-static JitAllocator *create_allocator(JitAllocator::CreateParams *params) {
+static auto create_allocator(const JitAllocator::CreateParams &params) {
void *test_ro, *test_rw;
+ bool single_mapped;
Error err;
- auto *allocator = new JitAllocator(params);
+ auto *allocator = new JitAllocator(&params);
err = allocator->alloc(&test_ro, &test_rw, 1);
+
+ if (err == ErrorCode::kErrorOk) {
+ /* We can get dual-mapped memory when asking for single-mapped memory
+ * if the latter is not possible: return whether that happened. */
+ single_mapped = (test_ro == test_rw);
+ }
+
allocator->release(test_ro);
if (err == ErrorCode::kErrorOk) {
- return allocator;
+ return std::make_pair(allocator, single_mapped);
}
delete allocator;
- return nullptr;
+ return std::make_pair((JitAllocator *)nullptr, false);
}
static JitAllocator *pick_allocator() {
- JitAllocator::CreateParams single_params;
- single_params.reset();
+ JitAllocator::CreateParams params;
+ JitAllocator *allocator;
+ bool single_mapped;
+
+#if defined(VALGRIND)
+ erts_jit_single_map = 1;
+#elif defined(__APPLE__) && defined(__aarch64__)
+ /* Allocating dual-mapped executable memory on this platform is horribly
+ * slow, and provides little security benefits over the MAP_JIT per-thread
+ * permission scheme. Force single-mapped memory.
+ *
+ * 64-bit x86 still uses dual-mapped memory as it lacks support for per-
+ * thread permissions and thus gets unprotected RWX pages with MAP_JIT. */
+ erts_jit_single_map = 1;
+#endif
#if defined(HAVE_LINUX_PERF_SUPPORT)
/* `perf` has a hard time showing symbols for dual-mapped memory, so we'll
* use single-mapped memory when enabled. */
if (erts_jit_perf_support & BEAMASM_PERF_ENABLED) {
- if (auto *alloc = create_allocator(&single_params)) {
- return alloc;
- }
-
- ERTS_INTERNAL_ERROR("jit: Failed to allocate executable+writable "
- "memory. Either allow this or disable the "
- "'+JPperf' option.");
+ erts_jit_single_map = 1;
}
#endif
-#if !defined(VALGRIND)
/* Default to dual-mapped memory with separate executable and writable
* regions of the same code. This is required for platforms that enforce
* W^X, and we prefer it when available to catch errors sooner.
@@ -162,28 +179,34 @@ static JitAllocator *pick_allocator() {
* file descriptor per block on most platforms. The block sizes do grow
* over time, but we don't want to waste half a dozen fds just to get to
* the shell on platforms that are very fd-constrained. */
- JitAllocator::CreateParams dual_params;
+ params.reset();
+ params.blockSize = 32 << 20;
- dual_params.reset();
- dual_params.options = JitAllocatorOptions::kUseDualMapping,
- dual_params.blockSize = 4 << 20;
+ allocator = nullptr;
+ single_mapped = false;
- if (auto *alloc = create_allocator(&dual_params)) {
- return alloc;
- } else if (auto *alloc = create_allocator(&single_params)) {
- return alloc;
+ if (!erts_jit_single_map) {
+ params.options = JitAllocatorOptions::kUseDualMapping;
+ std::tie(allocator, single_mapped) = create_allocator(params);
}
- ERTS_INTERNAL_ERROR("jit: Cannot allocate executable memory. Use the "
- "interpreter instead.");
-#elif defined(VALGRIND)
- if (auto *alloc = create_allocator(&single_params)) {
- return alloc;
+ if (allocator == nullptr) {
+ params.options &= ~JitAllocatorOptions::kUseDualMapping;
+ std::tie(allocator, single_mapped) = create_allocator(params);
}
- ERTS_INTERNAL_ERROR("jit: the valgrind emulator requires the ability to "
- "allocate executable+writable memory.");
-#endif
+ if (erts_jit_single_map && !single_mapped) {
+ ERTS_INTERNAL_ERROR("jit: Failed to allocate executable+writable "
+ "memory. Either allow this or disable both the "
+ "'+JPperf' and '+JMsingle' options.");
+ }
+
+ if (allocator == nullptr) {
+ ERTS_INTERNAL_ERROR("jit: Cannot allocate executable memory. Use the "
+ "interpreter instead.");
+ }
+
+ return allocator;
}
void beamasm_init() {
@@ -208,10 +231,10 @@ void beamasm_init() {
0,
op_i_return_to_trace,
&beam_return_to_trace},
- {am_return_time_trace,
+ {am_call_trace_return,
0,
- op_i_return_time_trace,
- &beam_return_time_trace}};
+ op_i_call_trace_return,
+ &beam_call_trace_return}};
Eterm mod_name;
ERTS_DECL_AM(erts_beamasm);
@@ -325,7 +348,8 @@ void beamasm_init() {
/* These instructions rely on register contents, and can only be reached
* from a `call_ext_*`-instruction or trapping from the emulator, hence the
* lack of wrapper functions. */
- beam_save_calls = (ErtsCodePtr)bga->get_dispatch_save_calls();
+ beam_save_calls_export = (ErtsCodePtr)bga->get_dispatch_save_calls_export();
+ beam_save_calls_fun = (ErtsCodePtr)bga->get_dispatch_save_calls_fun();
beam_export_trampoline = (ErtsCodePtr)bga->get_export_trampoline();
/* Used when trappping to Erlang code from the emulator, setting up
@@ -340,7 +364,7 @@ void beamasm_init() {
beamasm_metadata_late_init();
}
-bool BeamAssembler::hasCpuFeature(uint32_t featureId) {
+bool BeamAssemblerCommon::hasCpuFeature(uint32_t featureId) {
return cpuinfo.hasFeature(featureId);
}
@@ -365,6 +389,26 @@ extern "C"
#endif
}
+ void beamasm_unseal_module(const void *executable_region,
+ void *writable_region,
+ size_t size) {
+ (void)executable_region;
+ (void)writable_region;
+ (void)size;
+
+ VirtMem::protectJitMemory(VirtMem::ProtectJitAccess::kReadWrite);
+ }
+
+ void beamasm_seal_module(const void *executable_region,
+ void *writable_region,
+ size_t size) {
+ (void)executable_region;
+ (void)writable_region;
+ (void)size;
+
+ VirtMem::protectJitMemory(VirtMem::ProtectJitAccess::kReadExecute);
+ }
+
void beamasm_flush_icache(const void *address, size_t size) {
#ifdef DEBUG
erts_debug_require_code_barrier();
@@ -389,7 +433,7 @@ extern "C"
ETHR_COMPILER_BARRIER;
- for (size_t i = start & ~ERTS_CACHE_LINE_MASK; i < end;
+ for (UWord i = start & ~ERTS_CACHE_LINE_MASK; i < end;
i += ERTS_CACHE_LINE_SIZE) {
__asm__ __volatile__("dc cvau, %0\n"
"ic ivau, %0\n" ::"r"(i)
@@ -473,9 +517,11 @@ extern "C"
delete ba;
}
- void beamasm_purge_module(const void *native_module_exec,
- void *native_module_rw) {
- jit_allocator->release(const_cast<void *>(native_module_exec));
+ void beamasm_purge_module(const void *executable_region,
+ void *writable_region,
+ size_t size) {
+ (void)size;
+ jit_allocator->release(const_cast<void *>(executable_region));
}
ErtsCodePtr beamasm_get_code(void *instance, int label) {
@@ -511,16 +557,16 @@ extern "C"
}
void beamasm_codegen(void *instance,
- const void **native_module_exec,
- void **native_module_rw,
+ const void **executable_region,
+ void **writable_region,
const BeamCodeHeader *in_hdr,
const BeamCodeHeader **out_exec_hdr,
BeamCodeHeader **out_rw_hdr) {
BeamModuleAssembler *ba = static_cast<BeamModuleAssembler *>(instance);
ba->codegen(jit_allocator,
- native_module_exec,
- native_module_rw,
+ executable_region,
+ writable_region,
in_hdr,
out_exec_hdr,
out_rw_hdr);
@@ -562,7 +608,7 @@ extern "C"
void beamasm_patch_import(void *instance,
char *rw_base,
int index,
- BeamInstr import) {
+ const Export *import) {
BeamModuleAssembler *ba = static_cast<BeamModuleAssembler *>(instance);
ba->patchImport(rw_base, index, import);
}
@@ -578,7 +624,7 @@ extern "C"
void beamasm_patch_lambda(void *instance,
char *rw_base,
int index,
- BeamInstr fe) {
+ const ErlFunEntry *fe) {
BeamModuleAssembler *ba = static_cast<BeamModuleAssembler *>(instance);
ba->patchLambda(rw_base, index, fe);
}