From e860a9b0fa6dfdedd1b00b8c1caf72547c8dfe29 Mon Sep 17 00:00:00 2001 From: Ryan Macnak Date: Mon, 20 Apr 2026 11:31:19 -0700 Subject: [PATCH] [vm, ffi] Emit the FFI callback stub as a raw blob and place it in the Fuchsia package. Any blob in the package can be loaded as an executable VMO. TEST=child CL Bug: https://dartbug.com/52579 Change-Id: I61f7a84453da4da4733c5a70d4d19f0a6652581b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/496661 Commit-Queue: Ryan Macnak Reviewed-by: Alexander Aprelev --- BUILD.gn | 8 +++++++ build/rbe/rewrapper_dart.py | 1 + runtime/bin/BUILD.gn | 14 +++++++++++ runtime/bin/dart_api_win.c | 10 ++++++++ runtime/bin/gen_snapshot.cc | 24 +++++++++++++++++-- runtime/include/dart_api.h | 3 +++ .../tests/vm/dart/exported_symbols_test.dart | 1 + runtime/vm/dart_api_impl.cc | 22 +++++++++++++++++ 8 files changed, 81 insertions(+), 2 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index 50a09ec8ede..7f1ef0ad73b 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -444,6 +444,7 @@ if (is_fuchsia) { manifest = "runtime/bin/${binary}_test_component.cml" data_deps = [ "runtime/bin:${binary}", + "runtime/bin:ffi_callback_stub_bin", "runtime/bin:ffi_test_dynamic_library", "runtime/bin:ffi_test_functions", ] @@ -464,6 +465,13 @@ if (is_fuchsia) { }, ] } + resources += [ + { + path = get_label_info("runtime/bin:ffi_callback_stub_bin", + "target_gen_dir") + "/ffi_callback_stub.bin" + dest = "lib/ffi_callback_stub.bin" + }, + ] foreach(file, resource_files) { resources += [ { diff --git a/build/rbe/rewrapper_dart.py b/build/rbe/rewrapper_dart.py index 012e990c309..eb59621ba8b 100644 --- a/build/rbe/rewrapper_dart.py +++ b/build/rbe/rewrapper_dart.py @@ -708,6 +708,7 @@ trace to find the place to insert the appropriate support. '--isolate_snapshot_data', '--isolate_snapshot_instructions', '--elf', + '--ffi_callback_stub', ]): self.outputs.append(self.rebase(self.optarg)) elif self.get_option([ diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn index 0eedc4279fc..9465b23ea4d 100644 --- a/runtime/bin/BUILD.gn +++ b/runtime/bin/BUILD.gn @@ -650,6 +650,20 @@ gen_snapshot_action("generate_snapshot_bin") { ] } +gen_snapshot_action("ffi_callback_stub_bin") { + deps = [ "../vm:vm_platform_stripped" ] + platform_dill = "$root_out_dir/vm_platform_stripped.dill" + inputs = [ platform_dill ] + outputs = [ "$target_gen_dir/ffi_callback_stub.bin" ] + args = [ + "--deterministic", + "--snapshot_kind=ffi-callback-stub", + "--ffi_callback_stub=" + + rebase_path("$target_gen_dir/ffi_callback_stub.bin", root_build_dir), + rebase_path(platform_dill, root_build_dir), + ] +} + # Generates an assembly file defining a given symbol with the bytes from a # binary file. Places the symbol in a text section if 'executable' is true, # otherwise places the symbol in a read-only data section. diff --git a/runtime/bin/dart_api_win.c b/runtime/bin/dart_api_win.c index ef9709577a0..9115ea160fd 100644 --- a/runtime/bin/dart_api_win.c +++ b/runtime/bin/dart_api_win.c @@ -459,6 +459,8 @@ typedef Dart_Handle (*Dart_CreateAppAOTSnapshotAndRelocatableObjectType)( typedef Dart_Handle (*Dart_CreateVMAOTSnapshotAsAssemblyType)( Dart_StreamingWriteCallback, void*); +typedef Dart_Handle (*Dart_WriteCallbackStubType)(Dart_StreamingWriteCallback, + void*); typedef Dart_Handle (*Dart_SortClassesType)(); typedef Dart_Handle (*Dart_CreateAppJITSnapshotAsBlobsType)(uint8_t**, intptr_t*, @@ -772,6 +774,7 @@ static Dart_CreateAppAOTSnapshotAndRelocatableObjectType Dart_CreateAppAOTSnapshotAndRelocatableObjectFn = NULL; static Dart_CreateVMAOTSnapshotAsAssemblyType Dart_CreateVMAOTSnapshotAsAssemblyFn = NULL; +static Dart_WriteCallbackStubType Dart_WriteCallbackStubFn = NULL; static Dart_SortClassesType Dart_SortClassesFn = NULL; static Dart_CreateAppJITSnapshotAsBlobsType Dart_CreateAppJITSnapshotAsBlobsFn = NULL; @@ -1368,6 +1371,8 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { Dart_CreateVMAOTSnapshotAsAssemblyFn = (Dart_CreateVMAOTSnapshotAsAssemblyType)GetProcAddress( process, "Dart_CreateVMAOTSnapshotAsAssembly"); + Dart_WriteCallbackStubFn = (Dart_WriteCallbackStubType)GetProcAddress( + process, "Dart_WriteCallbackStub"); Dart_SortClassesFn = (Dart_SortClassesType)GetProcAddress(process, "Dart_SortClasses"); Dart_CreateAppJITSnapshotAsBlobsFn = @@ -2701,6 +2706,11 @@ Dart_Handle Dart_CreateVMAOTSnapshotAsAssembly( return Dart_CreateVMAOTSnapshotAsAssemblyFn(callback, callback_data); } +Dart_Handle Dart_WriteCallbackStub(Dart_StreamingWriteCallback callback, + void* callback_data) { + return Dart_WriteCallbackStubFn(callback, callback_data); +} + Dart_Handle Dart_SortClasses() { return Dart_SortClassesFn(); } diff --git a/runtime/bin/gen_snapshot.cc b/runtime/bin/gen_snapshot.cc index ab04917dc5f..ac706fae749 100644 --- a/runtime/bin/gen_snapshot.cc +++ b/runtime/bin/gen_snapshot.cc @@ -80,6 +80,7 @@ enum SnapshotKind { kAppAOTElf, kAppAOTMachODylib, kVMAOTAssembly, + kFfiCallbackStub, }; static SnapshotKind snapshot_kind = kCore; @@ -93,6 +94,7 @@ static const char* const kSnapshotKindNames[] = { "app-aot-elf", "app-aot-macho-dylib", "vm-aot-assembly", + "ffi-callback-stub", nullptr, // clang-format on }; @@ -114,7 +116,8 @@ static const char* const kSnapshotKindNames[] = { V(macho_object, macho_object_filename) \ V(loading_unit_manifest, loading_unit_manifest_filename) \ V(save_debugging_info, debugging_info_filename) \ - V(save_obfuscation_map, obfuscation_map_filename) + V(save_obfuscation_map, obfuscation_map_filename) \ + V(ffi_callback_stub, ffi_callback_stub_filename) #define BOOL_OPTIONS_LIST(V) \ V(compile_all, compile_all) \ @@ -142,7 +145,8 @@ DEFINE_CB_OPTION(ProcessEnvironmentOption); static bool IsSnapshottingForPrecompilation() { return (snapshot_kind == kAppAOTAssembly) || (snapshot_kind == kAppAOTElf) || (snapshot_kind == kAppAOTMachODylib) || - (snapshot_kind == kVMAOTAssembly); + (snapshot_kind == kVMAOTAssembly) || + (snapshot_kind == kFfiCallbackStub); } // clang-format off @@ -308,6 +312,14 @@ static int ParseArguments(int argc, #endif break; } + case kFfiCallbackStub: + if (ffi_callback_stub_filename == nullptr) { + Syslog::PrintErr( + "Building the FFI callback stub blob requires specifying " + "an output file for --ffi_callback_stub.\n\n"); + return -1; + } + break; } if (!obfuscate && obfuscation_map_filename != nullptr) { @@ -637,6 +649,13 @@ static void CreateAndWritePrecompiledSnapshot() { CHECK_RESULT(result); return; } + if (snapshot_kind == kFfiCallbackStub) { + File* file = OpenFile(ffi_callback_stub_filename); + RefCntReleaseScope rs(file); + Dart_Handle result = Dart_WriteCallbackStub(StreamingWriteCallback, file); + CHECK_RESULT(result); + return; + } Dart_AotBinaryFormat format; const char* kind_str = nullptr; @@ -834,6 +853,7 @@ static int CreateIsolateAndSnapshot(const CommandLineOptions& inputs) { case kAppAOTElf: case kAppAOTMachODylib: case kVMAOTAssembly: + case kFfiCallbackStub: CreateAndWritePrecompiledSnapshot(); break; default: diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h index 46a01c3632a..1de2f7238d4 100644 --- a/runtime/include/dart_api.h +++ b/runtime/include/dart_api.h @@ -4239,6 +4239,9 @@ Dart_CreateAppAOTSnapshotAndRelocatableObject( DART_EXPORT DART_API_WARN_UNUSED_RESULT Dart_Handle Dart_CreateVMAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback, void* callback_data); +DART_EXPORT DART_API_WARN_UNUSED_RESULT Dart_Handle +Dart_WriteCallbackStub(Dart_StreamingWriteCallback callback, + void* callback_data); /** * Sorts the class-ids in depth first traversal order of the inheritance diff --git a/runtime/tests/vm/dart/exported_symbols_test.dart b/runtime/tests/vm/dart/exported_symbols_test.dart index bfe08019536..7bb92832815 100644 --- a/runtime/tests/vm/dart/exported_symbols_test.dart +++ b/runtime/tests/vm/dart/exported_symbols_test.dart @@ -366,6 +366,7 @@ main() { "Dart_TypeToNullableType", "Dart_TypeVoid", "Dart_VersionString", + "Dart_WriteCallbackStub", "Dart_WriteHeapSnapshot", "Dart_WriteProfileToTimeline", if (isAOTRuntime) ...[ diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc index 5e50b1a9d3e..fec5d63f9ce 100644 --- a/runtime/vm/dart_api_impl.cc +++ b/runtime/vm/dart_api_impl.cc @@ -6911,6 +6911,28 @@ Dart_CreateVMAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback, #endif } +DART_EXPORT Dart_Handle +Dart_WriteCallbackStub(Dart_StreamingWriteCallback callback, + void* callback_data) { +#if defined(TARGET_ARCH_IA32) + return Api::NewError("AOT compilation is not supported on IA32."); +#elif !defined(DART_PRECOMPILER) + return Api::NewError( + "This VM was built without support for AOT compilation."); +#else + DARTSCOPE(Thread::Current()); + API_TIMELINE_DURATION(T); + CHECK_NULL(callback); + + callback(callback_data, + reinterpret_cast( + StubCode::FfiCallbackTrampoline().EntryPoint()), + StubCode::FfiCallbackTrampoline().Size()); + + return Api::Success(); +#endif +} + DART_EXPORT Dart_Handle Dart_CreateAppAOTSnapshotAsElf(Dart_StreamingWriteCallback callback, void* callback_data,