// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. // Generate a snapshot file after loading all the scripts specified on the // command line. #include #include #include #include #include #include "bin/builtin.h" #include "bin/console.h" #include "bin/dartutils.h" #include "bin/error_exit.h" #include "bin/eventhandler.h" #include "bin/exe_utils.h" #include "bin/file.h" #include "bin/loader.h" #include "bin/options.h" #include "bin/platform.h" #include "bin/snapshot_utils.h" #include "bin/thread.h" #include "bin/utils.h" #include "bin/vmservice_impl.h" #include "include/dart_api.h" #include "include/dart_tools_api.h" #include "platform/globals.h" #include "platform/growable_array.h" #include "platform/hashmap.h" #include "platform/syslog.h" #include "platform/text_buffer.h" namespace dart { namespace bin { #define CHECK_RESULT(result) \ if (Dart_IsError(result)) { \ intptr_t exit_code = 0; \ Syslog::PrintErr("Error: %s\n", Dart_GetError(result)); \ if (Dart_IsCompilationError(result)) { \ exit_code = kCompilationErrorExitCode; \ } else if (Dart_IsApiError(result)) { \ exit_code = kApiErrorExitCode; \ } else { \ exit_code = kErrorExitCode; \ } \ Dart_ExitScope(); \ Dart_ShutdownIsolate(); \ exit(exit_code); \ } // The environment provided through the command line using -D options. static dart::SimpleHashMap* environment = nullptr; static bool ProcessEnvironmentOption(const char* arg, CommandLineOptions* vm_options) { return OptionProcessor::ProcessEnvironmentOption(arg, vm_options, &environment); } // The core snapshot to use when creating isolates. Normally nullptr, but loaded // from a file when creating AppJIT snapshots. const uint8_t* isolate_snapshot_data = nullptr; const uint8_t* isolate_snapshot_text = nullptr; // Global state that indicates whether a snapshot is to be created and // if so which file to write the snapshot into. The ordering of this list must // match kSnapshotKindNames below. enum SnapshotKind { kCore, kApp, kAppJIT, kAppAOTAssembly, kAppAOTElf, kAppAOTMachODylib, kFfiCallbackStub, }; static SnapshotKind snapshot_kind = kCore; // The ordering of this list must match the SnapshotKind enum above. static const char* const kSnapshotKindNames[] = { // clang-format off "core", "app", "app-jit", "app-aot-assembly", "app-aot-elf", "app-aot-macho-dylib", "ffi-callback-stub", nullptr, // clang-format on }; #define STRING_OPTIONS_LIST(V) \ V(load_vm_snapshot_data, load_vm_snapshot_data_filename) \ V(load_vm_snapshot_instructions, load_vm_snapshot_instructions_filename) \ V(load_isolate_snapshot_data, load_isolate_snapshot_data_filename) \ V(load_isolate_snapshot_instructions, \ load_isolate_snapshot_instructions_filename) \ V(snapshot_data, snapshot_data_filename) \ V(snapshot_text, snapshot_text_filename) \ V(assembly, assembly_filename) \ V(elf, elf_filename) \ V(macho, macho_filename) \ 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(load_obfuscation_map, load_obfuscation_map_filename) \ V(ffi_callback_stub, ffi_callback_stub_filename) #define BOOL_OPTIONS_LIST(V) \ V(compile_all, compile_all) \ V(help, help) \ V(obfuscate, obfuscate) \ V(strip, strip) \ V(verbose, verbose) \ V(version, version) #define STRING_OPTION_DEFINITION(flag, variable) \ static const char* variable = nullptr; \ DEFINE_STRING_OPTION(flag, variable) STRING_OPTIONS_LIST(STRING_OPTION_DEFINITION) #undef STRING_OPTION_DEFINITION #define BOOL_OPTION_DEFINITION(flag, variable) \ static bool variable = false; \ DEFINE_BOOL_OPTION(flag, variable) BOOL_OPTIONS_LIST(BOOL_OPTION_DEFINITION) #undef BOOL_OPTION_DEFINITION DEFINE_ENUM_OPTION(snapshot_kind, SnapshotKind, snapshot_kind); DEFINE_CB_OPTION(ProcessEnvironmentOption); static bool IsSnapshottingForPrecompilation() { return (snapshot_kind == kAppAOTAssembly) || (snapshot_kind == kAppAOTElf) || (snapshot_kind == kAppAOTMachODylib) || (snapshot_kind == kFfiCallbackStub); } // clang-format off static void PrintUsage() { Syslog::PrintErr( "Usage: gen_snapshot [] [] \n" " \n" "Common options: \n" "--help \n" " Display this message (add --verbose for information about all VM options).\n" "--version \n" " Print the SDK version. \n" " \n" "To create a core snapshot: \n" "--snapshot_kind=core \n" "--snapshot_data= \n" "--snapshot_text= \n" " \n" " \n" "To create an AOT application snapshot as assembly suitable for compilation \n" "as a static or dynamic library: \n" "--snapshot_kind=app-aot-assembly \n" "--assembly= \n" "[--strip] \n" "[--obfuscate] \n" "[--save-debugging-info=] \n" "[--save-obfuscation-map=] \n" "[--load-obfuscation-map=] \n" " \n" " \n" "To create an AOT application snapshot as an ELF shared library: \n" "--snapshot_kind=app-aot-elf \n" "--elf= \n" "[--strip] \n" "[--obfuscate] \n" "[--save-debugging-info=] \n" "[--save-obfuscation-map=] \n" "[--load-obfuscation-map=] \n" " \n" " \n" "To create an AOT application snapshot as an Mach-O dynamic library (dylib): \n" "--snapshot_kind=app-aot-macho-dylib \n" "--macho= \n" "[--strip] \n" "[--obfuscate] \n" "[--save-debugging-info=] \n" "[--save-obfuscation-map=] \n" "[--load-obfuscation-map=] \n" " \n" " \n" "AOT snapshots can be obfuscated: that is all identifiers will be renamed \n" "during compilation. This mode is enabled with --obfuscate flag. Mapping \n" "between original and obfuscated names can be serialized as a JSON array \n" "using --save-obfuscation-map= option. See dartbug.com/30524 \n" "for implementation details and limitations of the obfuscation pass. \n" " \n" "\n"); if (verbose) { Syslog::PrintErr( "The following options are only used for VM development and may\n" "be changed in any future version:\n"); const char* print_flags = "--print_flags"; char* error = Dart_SetVMFlags(1, &print_flags); ASSERT(error == nullptr); } } // clang-format on // Parse out the command line arguments. Returns -1 if the arguments // are incorrect, 0 otherwise. static int ParseArguments(int argc, char** argv, CommandLineOptions* vm_options, CommandLineOptions* inputs) { // Skip the binary name. int i = 1; // Parse out the vm options. while ((i < argc) && OptionProcessor::IsValidShortFlag(argv[i])) { if (OptionProcessor::TryProcess(argv[i], vm_options)) { i += 1; continue; } vm_options->AddArgument(argv[i]); i += 1; } // Parse out the kernel inputs. while (i < argc) { inputs->AddArgument(argv[i]); i++; } if (help) { PrintUsage(); Platform::Exit(0); } else if (version) { Syslog::PrintErr("Dart SDK version: %s\n", Dart_VersionString()); Platform::Exit(0); } // Verify consistency of arguments. if (inputs->count() < 1) { Syslog::PrintErr("At least one input is required\n"); return -1; } switch (snapshot_kind) { case kCore: { if ((snapshot_data_filename == nullptr) || (snapshot_text_filename == nullptr)) { Syslog::PrintErr( "Building a core snapshot requires specifying output files for " "--snapshot_data and --snapshot_text.\n\n"); return -1; } break; } case kApp: case kAppJIT: { if ((snapshot_data_filename == nullptr) || (snapshot_text_filename == nullptr)) { Syslog::PrintErr( "Building an app JIT snapshot requires specifying input files for " "--load_vm_snapshot_data and --load_vm_snapshot_instructions, an " " output file for --snapshot_data, and an output " "file for --snapshot_text.\n\n"); return -1; } break; } case kAppAOTElf: { if (elf_filename == nullptr) { Syslog::PrintErr( "Building an AOT snapshot as ELF requires specifying " "an output file for --elf.\n\n"); return -1; } break; } case kAppAOTMachODylib: { if (macho_filename == nullptr) { Syslog::PrintErr( "Building an AOT snapshot as a Mach-O dynamic library requires " " specifying an output file for --macho.\n\n"); return -1; } break; } case kAppAOTAssembly: { if (assembly_filename == nullptr) { Syslog::PrintErr( "Building an AOT snapshot as assembly requires specifying " "an output file for --assembly.\n\n"); return -1; } #if defined(DART_TARGET_OS_WINDOWS) if (debugging_info_filename != nullptr) { // TODO(https://github.com/dart-lang/sdk/issues/60812): Support PDB. Syslog::PrintErr( "warning: ignoring --save-debugging-info when " "generating assembly for Windows.\n\n"); } #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) { Syslog::PrintErr( "--save-obfuscation_map=<...> should only be specified when " "obfuscation is enabled by the --obfuscate flag.\n\n"); return -1; } if (!obfuscate && load_obfuscation_map_filename != nullptr) { Syslog::PrintErr( "--load-obfuscation_map=<...> should only be specified when " "obfuscation is enabled by the --obfuscate flag.\n\n"); return -1; } if (!IsSnapshottingForPrecompilation()) { if (obfuscate) { Syslog::PrintErr( "Obfuscation can only be enabled when building an AOT snapshot.\n\n"); return -1; } if (debugging_info_filename != nullptr) { Syslog::PrintErr( "--save-debugging-info=<...> can only be enabled when building an " "AOT snapshot.\n\n"); return -1; } if (load_obfuscation_map_filename != nullptr) { Syslog::PrintErr( "--load-obfuscation-map=<...> can only be enabled when building an " "AOT snapshot.\n\n"); return -1; } if (strip) { Syslog::PrintErr( "Stripping can only be enabled when building an AOT snapshot.\n\n"); return -1; } } return 0; } PRINTF_ATTRIBUTE(1, 2) static void PrintErrAndExit(const char* format, ...) { va_list args; va_start(args, format); Syslog::VPrintErr(format, args); va_end(args); // ExitScope and ShutdownIsolate will abort() if there is no current isolate. if (Dart_CurrentIsolate() != nullptr) { Dart_ExitScope(); Dart_ShutdownIsolate(); } exit(kErrorExitCode); } static File* OpenFile(const char* filename) { File* file = File::Open(nullptr, filename, File::kWriteTruncate); if (file == nullptr) { PrintErrAndExit("Error: Unable to write file: %s\n\n", filename); } return file; } static void WriteFile(const char* filename, const uint8_t* buffer, const intptr_t size) { File* file = OpenFile(filename); RefCntReleaseScope rs(file); if (!file->WriteFully(buffer, size)) { PrintErrAndExit("Error: Unable to write file: %s\n\n", filename); } } static void ReadFile(const char* filename, uint8_t** buffer, intptr_t* size) { File* file = File::Open(nullptr, filename, File::kRead); if (file == nullptr) { PrintErrAndExit("Error: Unable to read file: %s\n", filename); } RefCntReleaseScope rs(file); *size = file->Length(); *buffer = reinterpret_cast(malloc(*size)); if (!file->ReadFully(*buffer, *size)) { PrintErrAndExit("Error: Unable to read file: %s\n", filename); } } static void MallocFinalizer(void* isolate_callback_data, void* peer) { free(peer); } static bool IsJsonWhitespace(uint8_t c) { return c == ' ' || c == '\n' || c == '\r' || c == '\t'; } static int HexDigit(uint8_t c) { if (c >= '0' && c <= '9') { return c - '0'; } if (c >= 'a' && c <= 'f') { return 10 + (c - 'a'); } if (c >= 'A' && c <= 'F') { return 10 + (c - 'A'); } return -1; } static void AppendUtf8(char* buffer, intptr_t* length, uint32_t code_point) { if (code_point <= 0x7F) { buffer[(*length)++] = static_cast(code_point); } else if (code_point <= 0x7FF) { buffer[(*length)++] = static_cast(0xC0 | (code_point >> 6)); buffer[(*length)++] = static_cast(0x80 | (code_point & 0x3F)); } else if (code_point <= 0xFFFF) { buffer[(*length)++] = static_cast(0xE0 | (code_point >> 12)); buffer[(*length)++] = static_cast(0x80 | ((code_point >> 6) & 0x3F)); buffer[(*length)++] = static_cast(0x80 | (code_point & 0x3F)); } else { buffer[(*length)++] = static_cast(0xF0 | (code_point >> 18)); buffer[(*length)++] = static_cast(0x80 | ((code_point >> 12) & 0x3F)); buffer[(*length)++] = static_cast(0x80 | ((code_point >> 6) & 0x3F)); buffer[(*length)++] = static_cast(0x80 | (code_point & 0x3F)); } } static bool ParseJsonStringArray(const uint8_t* buffer, intptr_t size, MallocGrowableArray* strings, const char** error) { intptr_t cursor = 0; auto skip_whitespace = [&]() { while (cursor < size && IsJsonWhitespace(buffer[cursor])) { cursor++; } }; skip_whitespace(); if (cursor >= size || buffer[cursor++] != '[') { *error = "expected '['"; return false; } skip_whitespace(); if (cursor < size && buffer[cursor] == ']') { cursor++; skip_whitespace(); if (cursor != size) { *error = "unexpected characters after ']'"; return false; } return true; } while (cursor < size) { if (buffer[cursor++] != '"') { *error = "expected string"; return false; } char* value = reinterpret_cast(malloc(size + 1)); if (value == nullptr) { *error = "out of memory"; return false; } intptr_t length = 0; while (cursor < size) { uint8_t c = buffer[cursor++]; if (c == '"') { value[length] = '\0'; strings->Add(value); value = nullptr; break; } if (c != '\\') { value[length++] = static_cast(c); continue; } if (cursor >= size) { free(value); *error = "unterminated escape"; return false; } c = buffer[cursor++]; switch (c) { case '"': case '\\': case '/': value[length++] = static_cast(c); break; case 'b': value[length++] = '\b'; break; case 'f': value[length++] = '\f'; break; case 'n': value[length++] = '\n'; break; case 'r': value[length++] = '\r'; break; case 't': value[length++] = '\t'; break; case 'u': { if (cursor + 4 > size) { free(value); *error = "incomplete unicode escape"; return false; } uint32_t code_point = 0; for (intptr_t i = 0; i < 4; i++) { const int digit = HexDigit(buffer[cursor++]); if (digit < 0) { free(value); *error = "invalid unicode escape"; return false; } code_point = (code_point << 4) | digit; } AppendUtf8(value, &length, code_point); break; } default: free(value); *error = "invalid escape"; return false; } } if (value != nullptr) { free(value); *error = "unterminated string"; return false; } skip_whitespace(); if (cursor < size && buffer[cursor] == ',') { cursor++; skip_whitespace(); continue; } if (cursor < size && buffer[cursor] == ']') { cursor++; skip_whitespace(); if (cursor != size) { *error = "unexpected characters after ']'"; return false; } return true; } *error = "expected ',' or ']'"; return false; } *error = "unterminated array"; return false; } static void MaybeLoadObfuscationMap() { if (load_obfuscation_map_filename == nullptr) { return; } uint8_t* buffer = nullptr; intptr_t size = 0; ReadFile(load_obfuscation_map_filename, &buffer, &size); MallocGrowableArray strings; const char* parse_error = nullptr; if (!ParseJsonStringArray(buffer, size, &strings, &parse_error)) { free(buffer); for (intptr_t i = 0; i < strings.length(); i++) { free(const_cast(strings[i])); } PrintErrAndExit("Error: Invalid obfuscation map %s: %s\n", load_obfuscation_map_filename, parse_error); } if ((strings.length() % 2) != 0) { free(buffer); for (intptr_t i = 0; i < strings.length(); i++) { free(const_cast(strings[i])); } PrintErrAndExit( "Error: Invalid obfuscation map %s: expected string pairs\n", load_obfuscation_map_filename); } Dart_Handle result = Dart_SetObfuscationMap(strings.data(), strings.length()); free(buffer); for (intptr_t i = 0; i < strings.length(); i++) { free(const_cast(strings[i])); } CHECK_RESULT(result); } static void MaybeLoadExtraInputs(const CommandLineOptions& inputs) { for (intptr_t i = 1; i < inputs.count(); i++) { uint8_t* buffer = nullptr; intptr_t size = 0; ReadFile(inputs.GetArgument(i), &buffer, &size); Dart_Handle td = Dart_NewExternalTypedDataWithFinalizer( Dart_TypedData_kUint8, buffer, size, buffer, size, MallocFinalizer); CHECK_RESULT(td); Dart_Handle result = Dart_LoadLibrary(td); CHECK_RESULT(result); } } static void MaybeLoadCode() { if (compile_all && (snapshot_kind == kAppJIT)) { Dart_Handle result = Dart_CompileAll(); CHECK_RESULT(result); } } static void CreateAndWriteCoreSnapshot() { ASSERT(snapshot_kind == kCore); ASSERT(snapshot_data_filename != nullptr); ASSERT(snapshot_text_filename != nullptr); Dart_Handle result; uint8_t* snapshot_data_buffer = nullptr; intptr_t snapshot_data_size = 0; result = Dart_CreateSnapshot(&snapshot_data_buffer, &snapshot_data_size); CHECK_RESULT(result); WriteFile(snapshot_data_filename, snapshot_data_buffer, snapshot_data_size); // Create empty file for the convenience of build systems. WriteFile(snapshot_text_filename, nullptr, 0); } static std::unique_ptr MapFile(const char* filename, File::MapType type, const uint8_t** buffer) { File* file = File::Open(nullptr, filename, File::kRead); if (file == nullptr) { Syslog::PrintErr("Failed to open: %s\n", filename); exit(kErrorExitCode); } RefCntReleaseScope rs(file); intptr_t length = file->Length(); if (length == 0) { // Can't map an empty file. *buffer = nullptr; return nullptr; } MappedMemory* mapping = file->Map(type, 0, length); if (mapping == nullptr) { Syslog::PrintErr("Failed to read: %s\n", filename); exit(kErrorExitCode); } *buffer = reinterpret_cast(mapping->address()); return std::unique_ptr(mapping); } static void CreateAndWriteAppSnapshot() { ASSERT(snapshot_kind == kApp); ASSERT(snapshot_data_filename != nullptr); Dart_Handle result; uint8_t* snapshot_data_buffer = nullptr; intptr_t snapshot_data_size = 0; result = Dart_CreateSnapshot(&snapshot_data_buffer, &snapshot_data_size); CHECK_RESULT(result); WriteFile(snapshot_data_filename, snapshot_data_buffer, snapshot_data_size); if (snapshot_text_filename != nullptr) { // Create empty file for the convenience of build systems. WriteFile(snapshot_text_filename, nullptr, 0); } } static void CreateAndWriteAppJITSnapshot() { ASSERT(snapshot_kind == kAppJIT); ASSERT(snapshot_data_filename != nullptr); ASSERT(snapshot_text_filename != nullptr); Dart_Handle result; uint8_t* snapshot_data_buffer = nullptr; intptr_t snapshot_data_size = 0; uint8_t* snapshot_text_buffer = nullptr; intptr_t snapshot_text_size = 0; result = Dart_CreateAppJITSnapshotAsBlobs( &snapshot_data_buffer, &snapshot_data_size, &snapshot_text_buffer, &snapshot_text_size); CHECK_RESULT(result); WriteFile(snapshot_data_filename, snapshot_data_buffer, snapshot_data_size); WriteFile(snapshot_text_filename, snapshot_text_buffer, snapshot_text_size); } static void StreamingWriteCallback(void* callback_data, const uint8_t* buffer, intptr_t size) { File* file = reinterpret_cast(callback_data); if ((file != nullptr) && !file->WriteFully(buffer, size)) { PrintErrAndExit("Error: Unable to write snapshot file\n\n"); } } static void StreamingCloseCallback(void* callback_data) { File* file = reinterpret_cast(callback_data); file->Release(); } static File* OpenLoadingUnitManifest() { File* manifest_file = OpenFile(loading_unit_manifest_filename); if (!manifest_file->Print("{ \"loadingUnits\": [\n ")) { PrintErrAndExit("Error: Unable to write file: %s\n\n", loading_unit_manifest_filename); } return manifest_file; } static void WriteLoadingUnitManifest(File* manifest_file, intptr_t id, const char* path, const char* debug_path = nullptr) { TextBuffer line(128); if (id != 1) { line.AddString(",\n "); } line.Printf("{\n \"id\": %" Pd ",\n \"path\": \"", id); line.AddEscapedString(path); if (debug_path != nullptr) { line.Printf("\",\n \"debugPath\": \""); line.AddEscapedString(debug_path); } line.AddString("\",\n \"libraries\": [\n "); Dart_Handle uris = Dart_LoadingUnitLibraryUris(id); CHECK_RESULT(uris); intptr_t length; CHECK_RESULT(Dart_ListLength(uris, &length)); for (intptr_t i = 0; i < length; i++) { const char* uri; CHECK_RESULT(Dart_StringToCString(Dart_ListGetAt(uris, i), &uri)); if (i != 0) { line.AddString(",\n "); } line.AddString("\""); line.AddEscapedString(uri); line.AddString("\""); } line.AddString("\n ]}"); if (!manifest_file->Print("%s", line.buffer())) { PrintErrAndExit("Error: Unable to write file: %s\n\n", loading_unit_manifest_filename); } } static void CloseLoadingUnitManifest(File* manifest_file) { if (!manifest_file->Print("]}\n")) { PrintErrAndExit("Error: Unable to write file: %s\n\n", loading_unit_manifest_filename); } manifest_file->Release(); } static void NextLoadingUnit(void* callback_data, intptr_t loading_unit_id, void** write_callback_data, void** write_debug_callback_data, const char* main_filename, const char* suffix) { char* filename = loading_unit_id == 1 ? Utils::StrDup(main_filename) : Utils::SCreate("%s-%" Pd ".part.%s", main_filename, loading_unit_id, suffix); File* file = OpenFile(filename); *write_callback_data = file; char* debug_filename = nullptr; if (debugging_info_filename != nullptr) { debug_filename = loading_unit_id == 1 ? Utils::StrDup(debugging_info_filename) : Utils::SCreate("%s-%" Pd ".part.so", debugging_info_filename, loading_unit_id); File* debug_file = OpenFile(debug_filename); *write_debug_callback_data = debug_file; } WriteLoadingUnitManifest(reinterpret_cast(callback_data), loading_unit_id, filename, debug_filename); free(debug_filename); free(filename); } static void NextAsmCallback(void* callback_data, intptr_t loading_unit_id, void** write_callback_data, void** write_debug_callback_data) { NextLoadingUnit(callback_data, loading_unit_id, write_callback_data, write_debug_callback_data, assembly_filename, "S"); } static void NextElfCallback(void* callback_data, intptr_t loading_unit_id, void** write_callback_data, void** write_debug_callback_data) { NextLoadingUnit(callback_data, loading_unit_id, write_callback_data, write_debug_callback_data, elf_filename, "so"); } static void CreateAndWritePrecompiledSnapshot() { ASSERT(IsSnapshottingForPrecompilation()); 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; const char* filename = nullptr; const char* object_filename = nullptr; // Default to the assembly ones just to avoid having to type-specify here. auto* next_callback = NextAsmCallback; auto* create_multiple_callback = Dart_CreateAppAOTSnapshotAsAssemblies; switch (snapshot_kind) { case kAppAOTAssembly: kind_str = "assembly code"; filename = assembly_filename; format = Dart_AotBinaryFormat_Assembly; break; case kAppAOTElf: kind_str = "ELF library"; filename = elf_filename; format = Dart_AotBinaryFormat_Elf; next_callback = NextElfCallback; create_multiple_callback = Dart_CreateAppAOTSnapshotAsElfs; break; case kAppAOTMachODylib: kind_str = "MachO dynamic library"; filename = macho_filename; object_filename = macho_object_filename; format = Dart_AotBinaryFormat_MachO_Dylib; // Not currently implemented. next_callback = nullptr; create_multiple_callback = nullptr; if (loading_unit_manifest_filename != nullptr) { PrintErrAndExit("error: deferred loading not implemented for Mach-O"); } break; default: UNREACHABLE(); } ASSERT(kind_str != nullptr); ASSERT(filename != nullptr); MaybeLoadObfuscationMap(); // Precompile with specified embedder entry points Dart_Handle result = Dart_Precompile(); CHECK_RESULT(result); if (strip && (debugging_info_filename == nullptr)) { Syslog::PrintErr( "Warning: Generating %s without DWARF debugging" " information.\n", kind_str); } char* identifier = Utils::Basename(filename); // Create a precompiled snapshot. if (loading_unit_manifest_filename == nullptr) { File* file = OpenFile(filename); RefCntReleaseScope rs(file); File* debug_file = nullptr; if (debugging_info_filename != nullptr) { debug_file = OpenFile(debugging_info_filename); } if (object_filename != nullptr) { File* object_file = OpenFile(object_filename); result = Dart_CreateAppAOTSnapshotAndRelocatableObject( format, StreamingWriteCallback, file, object_file, strip, debug_file, identifier, object_filename); object_file->Release(); } else { result = Dart_CreateAppAOTSnapshotAsBinary(format, StreamingWriteCallback, file, strip, debug_file, identifier, filename); } if (debug_file != nullptr) debug_file->Release(); if (identifier != nullptr) { free(identifier); identifier = nullptr; } CHECK_RESULT(result); } else { ASSERT(create_multiple_callback != nullptr); ASSERT(next_callback != nullptr); File* manifest_file = OpenLoadingUnitManifest(); result = create_multiple_callback(next_callback, manifest_file, strip, StreamingWriteCallback, StreamingCloseCallback); if (identifier != nullptr) { free(identifier); identifier = nullptr; } CHECK_RESULT(result); CloseLoadingUnitManifest(manifest_file); } if (obfuscate && !strip) { Syslog::PrintErr( "Warning: The generated %s contains unobfuscated DWARF " "debugging information.\n" " To avoid this, use --strip to remove it.\n", kind_str); } // Serialize obfuscation map if requested. if (obfuscation_map_filename != nullptr) { ASSERT(obfuscate); uint8_t* buffer = nullptr; intptr_t size = 0; result = Dart_GetObfuscationMap(&buffer, &size); CHECK_RESULT(result); WriteFile(obfuscation_map_filename, buffer, size); } } static int CreateIsolateAndSnapshot(const CommandLineOptions& inputs) { uint8_t* kernel_buffer = nullptr; intptr_t kernel_buffer_size = 0; ReadFile(inputs.GetArgument(0), &kernel_buffer, &kernel_buffer_size); Dart_IsolateFlags isolate_flags; Dart_IsolateFlagsInitialize(&isolate_flags); if (IsSnapshottingForPrecompilation()) { isolate_flags.obfuscate = obfuscate; } auto isolate_group_data = std::unique_ptr( new IsolateGroupData(nullptr, nullptr, nullptr, nullptr, false)); Dart_Isolate isolate; char* error = nullptr; bool loading_kernel_failed = false; if (isolate_snapshot_data == nullptr) { // We need to capture the vmservice library in the core snapshot, so load it // in the main isolate as well. isolate_flags.load_vmservice_library = true; isolate = Dart_CreateIsolateGroupFromKernel( nullptr, nullptr, kernel_buffer, kernel_buffer_size, &isolate_flags, isolate_group_data.get(), /*isolate_data=*/nullptr, &error); loading_kernel_failed = (isolate == nullptr); } else { isolate = Dart_CreateIsolateGroup(nullptr, nullptr, isolate_snapshot_data, isolate_snapshot_text, &isolate_flags, isolate_group_data.get(), /*isolate_data=*/nullptr, &error); } if (isolate == nullptr) { Syslog::PrintErr("%s\n", error); free(error); free(kernel_buffer); // The only real reason when `gen_snapshot` fails to create an isolate from // a valid kernel file is if loading the kernel results in a "compile-time" // error. // // There are other possible reasons, like memory allocation failures, but // those are very uncommon. // // The Dart API doesn't allow us to distinguish the different error cases, // so we'll use [kCompilationErrorExitCode] for failed kernel loading, since // a compile-time error is the most probable cause. return loading_kernel_failed ? kCompilationErrorExitCode : kErrorExitCode; } Dart_EnterScope(); Dart_Handle result = Dart_SetEnvironmentCallback(DartUtils::EnvironmentCallback); CHECK_RESULT(result); // The root library has to be set to generate AOT snapshots, and sometimes we // set one for the core snapshot too. // If the input dill file has a root library, then Dart_LoadScript will // ignore this dummy uri and set the root library to the one reported in // the dill file. Since dill files are not dart script files, // trying to resolve the root library URI based on the dill file name // would not help. // // If the input dill file does not have a root library, then // Dart_LoadScript will error. // // TODO(kernel): Dart_CreateIsolateGroupFromKernel should respect the root // library in the kernel file, though this requires auditing the other // loading paths in the embedders that had to work around this. result = Dart_SetRootLibrary( Dart_LoadLibraryFromKernel(kernel_buffer, kernel_buffer_size)); CHECK_RESULT(result); MaybeLoadExtraInputs(inputs); MaybeLoadCode(); switch (snapshot_kind) { case kCore: CreateAndWriteCoreSnapshot(); break; case kApp: CreateAndWriteAppSnapshot(); break; case kAppJIT: CreateAndWriteAppJITSnapshot(); break; case kAppAOTAssembly: case kAppAOTElf: case kAppAOTMachODylib: case kFfiCallbackStub: CreateAndWritePrecompiledSnapshot(); break; default: UNREACHABLE(); } Dart_ExitScope(); Dart_ShutdownIsolate(); free(kernel_buffer); return 0; } int main(int argc, char** argv) { #if !defined(DART_HOST_OS_WINDOWS) // Very early so any crashes during startup can also be symbolized. EXEUtils::LoadDartProfilerSymbols(argv[0]); #endif const int EXTRA_VM_ARGUMENTS = 7; CommandLineOptions vm_options(argc + EXTRA_VM_ARGUMENTS); CommandLineOptions inputs(argc); // When running from the command line we assume that we are optimizing for // throughput, and therefore use a larger new gen semi space size and a faster // new gen growth factor unless others have been specified. if (kWordSize <= 4) { vm_options.AddArgument("--new_gen_semi_max_size=16"); } else { vm_options.AddArgument("--new_gen_semi_max_size=32"); } vm_options.AddArgument("--new_gen_growth_factor=4"); vm_options.AddArgument("--deterministic"); // Parse command line arguments. if (ParseArguments(argc, argv, &vm_options, &inputs) < 0) { PrintUsage(); return kErrorExitCode; } DartUtils::SetEnvironment(environment); if (!Platform::Initialize()) { Syslog::PrintErr("Initialization failed\n"); return kErrorExitCode; } Console::SaveConfig(); Loader::InitOnce(); DartUtils::SetOriginalWorkingDirectory(); // Start event handler. TimerUtils::InitOnce(); EventHandler::Start(); if (IsSnapshottingForPrecompilation()) { vm_options.AddArgument("--precompilation"); // AOT snapshot can be deployed to another machine, // so generated code should not depend on the CPU features // of the system where snapshot was generated. vm_options.AddArgument("--target_unknown_cpu"); } else if (snapshot_kind == kAppJIT) { // App-jit snapshot can be deployed to another machine, // so generated code should not depend on the CPU features // of the system where snapshot was generated. vm_options.AddArgument("--target_unknown_cpu"); #if !defined(TARGET_ARCH_IA32) vm_options.AddArgument("--link_natives_lazily"); #endif } char* error = Dart_SetVMFlags(vm_options.count(), vm_options.arguments()); if (error != nullptr) { Syslog::PrintErr("Setting VM flags failed: %s\n", error); free(error); return kErrorExitCode; } Dart_InitializeParams init_params; memset(&init_params, 0, sizeof(init_params)); init_params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION; init_params.file_open = DartUtils::OpenFile; init_params.file_read = DartUtils::ReadFile; init_params.file_write = DartUtils::WriteFile; init_params.file_close = DartUtils::CloseFile; init_params.entropy_source = DartUtils::EntropySource; init_params.start_kernel_isolate = false; std::unique_ptr mapped_isolate_snapshot_data; std::unique_ptr mapped_isolate_snapshot_text; if (load_isolate_snapshot_data_filename != nullptr) { mapped_isolate_snapshot_data = MapFile(load_isolate_snapshot_data_filename, File::kReadOnly, &isolate_snapshot_data); } if (load_isolate_snapshot_instructions_filename != nullptr) { mapped_isolate_snapshot_text = MapFile(load_isolate_snapshot_instructions_filename, File::kReadExecute, &isolate_snapshot_text); } error = Dart_Initialize(&init_params); if (error != nullptr) { Syslog::PrintErr("VM initialization failed: %s\n", error); free(error); return kErrorExitCode; } int result = CreateIsolateAndSnapshot(inputs); if (result != 0) { return result; } error = Dart_Cleanup(); if (error != nullptr) { Syslog::PrintErr("VM cleanup failed: %s\n", error); free(error); } EventHandler::Stop(); return 0; } } // namespace bin } // namespace dart int main(int argc, char** argv) { return dart::bin::main(argc, argv); }