1// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4#ifndef RUNTIME_VM_FFI_CALLBACK_TRAMPOLINES_H_
5#define RUNTIME_VM_FFI_CALLBACK_TRAMPOLINES_H_
6
7#include "platform/allocation.h"
8#include "platform/growable_array.h"
9#include "vm/flag_list.h"
10#include "vm/virtual_memory.h"
11
12#if !defined(DART_PRECOMPILED_RUNTIME)
13#include "vm/compiler/stub_code_compiler.h"
14#endif // !defined(DART_PRECOMPILED_RUNTIME)
15
16namespace dart {
17
18#if !defined(DART_PRECOMPILED_RUNTIME)
19// In JIT mode, when write-protection is enabled without dual-mapping, we cannot
20// rely on Instructions generated in the Isolate's heap to be executable while
21// native code is running in a safepoint. This means that native code cannot
22// directly invoke FFI callback trampolines.
23//
24// To solve this, we create trampolines tied to consecutive sequences of
25// callback IDs which leave the safepoint before invoking the FFI callback,
26// and re-enter the safepoint on return from the callback.
27//
28// Since we can never map these trampolines RX -> RW, we eagerly generate as
29// many as will fit on a single page, since pages are the smallest granularity
30// of memory protection.
31//
32// See also:
33// - StubCodeCompiler::GenerateJITCallbackTrampolines
34// - {NativeEntryInstr, NativeReturnInstr}::EmitNativeCode
35DECLARE_FLAG(bool, write_protect_code);
36
37class NativeCallbackTrampolines : public ValueObject {
38 public:
39 static bool Enabled() { return !FLAG_precompiled_mode; }
40
41 static intptr_t NumCallbackTrampolinesPerPage() {
42 return (VirtualMemory::PageSize() -
43 compiler::StubCodeCompiler::kNativeCallbackSharedStubSize) /
44 compiler::StubCodeCompiler::kNativeCallbackTrampolineSize;
45 }
46
47 NativeCallbackTrampolines() {}
48 ~NativeCallbackTrampolines() {
49 // Unmap all the trampoline pages. 'VirtualMemory's are new-allocated.
50 for (intptr_t i = 0; i < trampoline_pages_.length(); ++i) {
51 delete trampoline_pages_[i];
52 }
53 }
54
55 // For each callback ID, we have an entry in Thread::ffi_callback_code_ and
56 // a trampoline here. These arrays must be kept in sync and this method is
57 // exposed to assert that.
58 intptr_t next_callback_id() const { return next_callback_id_; }
59
60 // Allocates a callback trampoline corresponding to the callback id
61 // 'next_callback_id()'. Returns an entrypoint to the trampoline.
62 void AllocateTrampoline();
63
64 // Get the entrypoint for a previously allocated callback ID.
65 uword TrampolineForId(int32_t callback_id);
66
67 private:
68 MallocGrowableArray<VirtualMemory*> trampoline_pages_;
69 intptr_t trampolines_left_on_page_ = 0;
70 intptr_t next_callback_id_ = 0;
71
72 DISALLOW_COPY_AND_ASSIGN(NativeCallbackTrampolines);
73};
74#endif // !defined(DART_PRECOMPILED_RUNTIME)
75
76} // namespace dart
77
78#endif // RUNTIME_VM_FFI_CALLBACK_TRAMPOLINES_H_
79