| // Copyright (c) 2017, 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. |
| |
| #include "bin/snapshot_utils.h" |
| |
| #include <cerrno> |
| #include <memory> |
| |
| #include "bin/dartutils.h" |
| #include "bin/dfe.h" |
| #include "bin/elf_loader.h" |
| #include "bin/error_exit.h" |
| #include "bin/file.h" |
| #include "bin/macho_loader.h" |
| #include "bin/platform.h" |
| #include "include/dart_api.h" |
| #if defined(DART_TARGET_OS_MACOS) |
| #include <platform/mach_o.h> |
| #endif |
| #if defined(DART_TARGET_OS_WINDOWS) |
| #include <platform/pe.h> |
| #endif |
| #include "platform/utils.h" |
| |
| #define LOG_SECTION_BOUNDARIES false |
| |
| #if !defined(DART_INCLUDE_SIMULATOR) |
| #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || \ |
| defined(DART_HOST_OS_FUCHSIA) |
| #define NATIVE_SHARED_OBJECT_FORMAT_ELF 1 |
| #elif defined(DART_HOST_OS_MACOS) |
| #define NATIVE_SHARED_OBJECT_FORMAT_MACHO 1 |
| #endif |
| #endif // !defined(DART_INCLUDE_SIMULATOR) |
| |
| namespace dart { |
| namespace bin { |
| |
| static constexpr int64_t kAppSnapshotHeaderSize = 2 * kInt64Size; |
| // The largest possible page size among the platforms we support (Linux ARM64). |
| static constexpr int64_t kAppSnapshotPageSize = 64 * KB; |
| |
| static const char kMachOAppSnapshotNoteName[] DART_UNUSED = "__dart_app_snap"; |
| |
| #if !defined(DART_PRECOMPILED_RUNTIME) |
| class DummySnapshot : public AppSnapshot { |
| public: |
| explicit DummySnapshot(DartUtils::MagicNumber num) : AppSnapshot(num) {} |
| |
| ~DummySnapshot() {} |
| |
| void SetBuffers(const uint8_t** vm_data_buffer, |
| const uint8_t** vm_instructions_buffer, |
| const uint8_t** isolate_data_buffer, |
| const uint8_t** isolate_instructions_buffer) { |
| UNREACHABLE(); |
| } |
| |
| private: |
| }; |
| |
| class MappedAppSnapshot : public AppSnapshot { |
| public: |
| MappedAppSnapshot(MappedMemory* vm_snapshot_data, |
| MappedMemory* vm_snapshot_instructions, |
| MappedMemory* isolate_snapshot_data, |
| MappedMemory* isolate_snapshot_instructions) |
| : AppSnapshot(DartUtils::kAppJITMagicNumber), |
| vm_data_mapping_(vm_snapshot_data), |
| vm_instructions_mapping_(vm_snapshot_instructions), |
| isolate_data_mapping_(isolate_snapshot_data), |
| isolate_instructions_mapping_(isolate_snapshot_instructions) {} |
| |
| ~MappedAppSnapshot() { |
| delete vm_data_mapping_; |
| delete vm_instructions_mapping_; |
| delete isolate_data_mapping_; |
| delete isolate_instructions_mapping_; |
| } |
| |
| void SetBuffers(const uint8_t** vm_data_buffer, |
| const uint8_t** vm_instructions_buffer, |
| const uint8_t** isolate_data_buffer, |
| const uint8_t** isolate_instructions_buffer) { |
| if (vm_data_mapping_ != nullptr) { |
| *vm_data_buffer = |
| reinterpret_cast<const uint8_t*>(vm_data_mapping_->address()); |
| } |
| if (vm_instructions_mapping_ != nullptr) { |
| *vm_instructions_buffer = |
| reinterpret_cast<const uint8_t*>(vm_instructions_mapping_->address()); |
| } |
| if (isolate_data_mapping_ != nullptr) { |
| *isolate_data_buffer = |
| reinterpret_cast<const uint8_t*>(isolate_data_mapping_->address()); |
| } |
| if (isolate_instructions_mapping_ != nullptr) { |
| *isolate_instructions_buffer = reinterpret_cast<const uint8_t*>( |
| isolate_instructions_mapping_->address()); |
| } |
| } |
| |
| private: |
| MappedMemory* vm_data_mapping_; |
| MappedMemory* vm_instructions_mapping_; |
| MappedMemory* isolate_data_mapping_; |
| MappedMemory* isolate_instructions_mapping_; |
| }; |
| |
| static AppSnapshot* TryReadAppSnapshotBlobs(const char* script_name, |
| File* file) { |
| if ((file->Length() - file->Position()) < kAppSnapshotHeaderSize) { |
| return nullptr; |
| } |
| |
| int64_t header[2]; |
| ASSERT(sizeof(header) == kAppSnapshotHeaderSize); |
| if (!file->ReadFully(&header, kAppSnapshotHeaderSize)) { |
| return nullptr; |
| } |
| int64_t isolate_data_size = header[0]; |
| int64_t isolate_data_position = |
| Utils::RoundUp(file->Position(), kAppSnapshotPageSize); |
| int64_t isolate_instructions_size = header[1]; |
| int64_t isolate_instructions_position = |
| isolate_data_position + isolate_data_size; |
| if (isolate_instructions_size != 0) { |
| isolate_instructions_position = |
| Utils::RoundUp(isolate_instructions_position, kAppSnapshotPageSize); |
| } |
| |
| MappedMemory* isolate_data_mapping = nullptr; |
| if (isolate_data_size != 0) { |
| isolate_data_mapping = |
| file->Map(File::kReadOnly, isolate_data_position, isolate_data_size); |
| if (isolate_data_mapping == nullptr) { |
| FATAL("Failed to memory map snapshot: %s\n", script_name); |
| } |
| } |
| |
| MappedMemory* isolate_instr_mapping = nullptr; |
| if (isolate_instructions_size != 0) { |
| isolate_instr_mapping = |
| file->Map(File::kReadExecute, isolate_instructions_position, |
| isolate_instructions_size); |
| if (isolate_instr_mapping == nullptr) { |
| FATAL("Failed to memory map snapshot: %s\n", script_name); |
| } |
| } |
| |
| auto app_snapshot = new MappedAppSnapshot( |
| nullptr, nullptr, isolate_data_mapping, isolate_instr_mapping); |
| return app_snapshot; |
| } |
| #endif // !defined(DART_PRECOMPILED_RUNTIME) |
| |
| static DartUtils::MagicNumber ReadMagicNumberAt(File& file, int64_t offset) { |
| // Attempt to read a magic number from the specified offset, even if there |
| // are less than kMaxMagicNumberSize bytes available. |
| const int64_t remaining = file.Length() - offset; |
| if (remaining <= 0) { |
| Syslog::PrintErr("File truncated before or at offset 0x%" Px64 ".\n", |
| offset); |
| return DartUtils::kUnknownMagicNumber; |
| } |
| if (!file.SetPosition(offset)) { |
| return DartUtils::kUnknownMagicNumber; |
| } |
| uint8_t header[DartUtils::kMaxMagicNumberSize]; |
| auto const read_size = Utils::Minimum<int64_t>(remaining, sizeof(header)); |
| if (!file.ReadFully(&header, read_size)) { |
| return DartUtils::kUnknownMagicNumber; |
| } |
| return DartUtils::SniffForMagicNumber(header, read_size); |
| } |
| |
| #if defined(DART_PRECOMPILED_RUNTIME) |
| class DylibAppSnapshot : public AppSnapshot { |
| public: |
| DylibAppSnapshot(DartUtils::MagicNumber magic_number, |
| void* library, |
| const uint8_t* vm_snapshot_data, |
| const uint8_t* vm_snapshot_instructions, |
| const uint8_t* isolate_snapshot_data, |
| const uint8_t* isolate_snapshot_instructions) |
| : AppSnapshot(magic_number), |
| library_(library), |
| vm_snapshot_data_(vm_snapshot_data), |
| vm_snapshot_instructions_(vm_snapshot_instructions), |
| isolate_snapshot_data_(isolate_snapshot_data), |
| isolate_snapshot_instructions_(isolate_snapshot_instructions) {} |
| |
| ~DylibAppSnapshot() { Utils::UnloadDynamicLibrary(library_); } |
| |
| void SetBuffers(const uint8_t** vm_data_buffer, |
| const uint8_t** vm_instructions_buffer, |
| const uint8_t** isolate_data_buffer, |
| const uint8_t** isolate_instructions_buffer) { |
| *vm_data_buffer = vm_snapshot_data_; |
| *vm_instructions_buffer = vm_snapshot_instructions_; |
| *isolate_data_buffer = isolate_snapshot_data_; |
| *isolate_instructions_buffer = isolate_snapshot_instructions_; |
| } |
| |
| private: |
| void* library_; |
| const uint8_t* vm_snapshot_data_; |
| const uint8_t* vm_snapshot_instructions_; |
| const uint8_t* isolate_snapshot_data_; |
| const uint8_t* isolate_snapshot_instructions_; |
| }; |
| |
| static AppSnapshot* TryReadAppSnapshotDynamicLibrary( |
| DartUtils::MagicNumber magic_number, |
| const char* script_name, |
| const char** error) { |
| #if defined(DART_INCLUDE_SIMULATOR) |
| *error = "running on a simulated architecture"; |
| return nullptr; |
| #else |
| #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_MACOS) |
| // On Linux and OSX, resolve the script path before passing into dlopen() |
| // since dlopen will not search the filesystem for paths like 'libtest.so'. |
| CStringUniquePtr absolute_path(realpath(script_name, nullptr)); |
| script_name = absolute_path.get(); |
| if (script_name == nullptr) { |
| const intptr_t err = errno; |
| const int kBufferSize = 1024; |
| char error_buf[kBufferSize]; |
| Utils::StrError(err, error_buf, kBufferSize); |
| *error = Utils::SCreate("could not resolve path: %s", error_buf); |
| return nullptr; |
| } |
| #endif |
| void* library = Utils::LoadDynamicLibrary(script_name, error); |
| if (library == nullptr) { |
| #if defined(NATIVE_SHARED_OBJECT_FORMAT_ELF) |
| if (*error == nullptr && magic_number != DartUtils::kAotELFMagicNumber) { |
| *error = "not an ELF shared object"; |
| } |
| #elif defined(NATIVE_SHARED_OBJECT_FORMAT_MACHO) |
| if (*error == nullptr && |
| magic_number != DartUtils::kAotMachO32MagicNumber && |
| magic_number != DartUtils::kAotMachO64MagicNumber) { |
| *error = "not a Mach-O shared object"; |
| } |
| #endif |
| if (*error == nullptr) { |
| *error = "unknown failure loading dynamic library (wrong format?)"; |
| } |
| return nullptr; |
| } |
| |
| const uint8_t* vm_data_buffer = reinterpret_cast<const uint8_t*>( |
| Utils::ResolveSymbolInDynamicLibrary(library, kVmSnapshotDataCSymbol)); |
| |
| const uint8_t* vm_instructions_buffer = |
| reinterpret_cast<const uint8_t*>(Utils::ResolveSymbolInDynamicLibrary( |
| library, kVmSnapshotInstructionsCSymbol)); |
| |
| const uint8_t* isolate_data_buffer = |
| reinterpret_cast<const uint8_t*>(Utils::ResolveSymbolInDynamicLibrary( |
| library, kIsolateSnapshotDataCSymbol)); |
| if (isolate_data_buffer == nullptr) { |
| FATAL("Failed to resolve symbol '%s'\n", kIsolateSnapshotDataCSymbol); |
| } |
| |
| const uint8_t* isolate_instructions_buffer = |
| reinterpret_cast<const uint8_t*>(Utils::ResolveSymbolInDynamicLibrary( |
| library, kIsolateSnapshotInstructionsCSymbol)); |
| if (isolate_instructions_buffer == nullptr) { |
| FATAL("Failed to resolve symbol '%s'\n", |
| kIsolateSnapshotInstructionsCSymbol); |
| } |
| |
| return new DylibAppSnapshot(magic_number, library, vm_data_buffer, |
| vm_instructions_buffer, isolate_data_buffer, |
| isolate_instructions_buffer); |
| #endif // defined(DART_INCLUDE_SIMULATOR) |
| } |
| |
| class ElfAppSnapshot : public AppSnapshot { |
| public: |
| ElfAppSnapshot(Dart_LoadedElf* elf, |
| const uint8_t* vm_snapshot_data, |
| const uint8_t* vm_snapshot_instructions, |
| const uint8_t* isolate_snapshot_data, |
| const uint8_t* isolate_snapshot_instructions) |
| : AppSnapshot{DartUtils::kAotELFMagicNumber}, |
| elf_(elf), |
| vm_snapshot_data_(vm_snapshot_data), |
| vm_snapshot_instructions_(vm_snapshot_instructions), |
| isolate_snapshot_data_(isolate_snapshot_data), |
| isolate_snapshot_instructions_(isolate_snapshot_instructions) {} |
| |
| virtual ~ElfAppSnapshot() { Dart_UnloadELF(elf_); } |
| |
| void SetBuffers(const uint8_t** vm_data_buffer, |
| const uint8_t** vm_instructions_buffer, |
| const uint8_t** isolate_data_buffer, |
| const uint8_t** isolate_instructions_buffer) { |
| *vm_data_buffer = vm_snapshot_data_; |
| *vm_instructions_buffer = vm_snapshot_instructions_; |
| *isolate_data_buffer = isolate_snapshot_data_; |
| *isolate_instructions_buffer = isolate_snapshot_instructions_; |
| } |
| |
| private: |
| Dart_LoadedElf* elf_; |
| const uint8_t* vm_snapshot_data_; |
| const uint8_t* vm_snapshot_instructions_; |
| const uint8_t* isolate_snapshot_data_; |
| const uint8_t* isolate_snapshot_instructions_; |
| }; |
| |
| static AppSnapshot* TryReadAppSnapshotElf(const char* script_name, |
| uint64_t file_offset, |
| bool force_load_from_memory) { |
| const char* error = nullptr; |
| #if defined(NATIVE_SHARED_OBJECT_FORMAT_ELF) |
| if (file_offset == 0 && !force_load_from_memory) { |
| // The load as a dynamic library should succeed, since this is a platform |
| // that natively understands ELF. |
| if (auto* const snapshot = TryReadAppSnapshotDynamicLibrary( |
| DartUtils::kAotELFMagicNumber, script_name, &error)) { |
| return snapshot; |
| } |
| Syslog::PrintErr("Loading dynamic library failed: %s\n", error); |
| return nullptr; |
| } |
| #endif |
| const uint8_t *vm_data_buffer = nullptr, *vm_instructions_buffer = nullptr, |
| *isolate_data_buffer = nullptr, |
| *isolate_instructions_buffer = nullptr; |
| Dart_LoadedElf* handle = nullptr; |
| if (force_load_from_memory) { |
| File* const file = |
| File::Open(/*namespc=*/nullptr, script_name, File::kRead); |
| if (file == nullptr) return nullptr; |
| MappedMemory* memory = file->Map(File::kReadOnly, /*position=*/0, |
| /*length=*/file->Length()); |
| if (memory == nullptr) return nullptr; |
| const uint8_t* address = |
| reinterpret_cast<const uint8_t*>(memory->address()); |
| handle = |
| Dart_LoadELF_Memory(address + file_offset, file->Length(), &error, |
| &vm_data_buffer, &vm_instructions_buffer, |
| &isolate_data_buffer, &isolate_instructions_buffer); |
| delete memory; |
| file->Release(); |
| } else { |
| handle = Dart_LoadELF(script_name, file_offset, &error, &vm_data_buffer, |
| &vm_instructions_buffer, &isolate_data_buffer, |
| &isolate_instructions_buffer); |
| } |
| if (handle == nullptr) { |
| Syslog::PrintErr("Loading failed: %s\n", error); |
| return nullptr; |
| } |
| return new ElfAppSnapshot(handle, vm_data_buffer, vm_instructions_buffer, |
| isolate_data_buffer, isolate_instructions_buffer); |
| } |
| |
| class MachODylibAppSnapshot : public AppSnapshot { |
| public: |
| MachODylibAppSnapshot(DartUtils::MagicNumber magic_number, |
| Dart_LoadedMachODylib* macho, |
| const uint8_t* vm_snapshot_data, |
| const uint8_t* vm_snapshot_instructions, |
| const uint8_t* isolate_snapshot_data, |
| const uint8_t* isolate_snapshot_instructions) |
| : AppSnapshot{magic_number}, |
| macho_(macho), |
| vm_snapshot_data_(vm_snapshot_data), |
| vm_snapshot_instructions_(vm_snapshot_instructions), |
| isolate_snapshot_data_(isolate_snapshot_data), |
| isolate_snapshot_instructions_(isolate_snapshot_instructions) {} |
| |
| virtual ~MachODylibAppSnapshot() { Dart_UnloadMachODylib(macho_); } |
| |
| void SetBuffers(const uint8_t** vm_data_buffer, |
| const uint8_t** vm_instructions_buffer, |
| const uint8_t** isolate_data_buffer, |
| const uint8_t** isolate_instructions_buffer) { |
| *vm_data_buffer = vm_snapshot_data_; |
| *vm_instructions_buffer = vm_snapshot_instructions_; |
| *isolate_data_buffer = isolate_snapshot_data_; |
| *isolate_instructions_buffer = isolate_snapshot_instructions_; |
| } |
| |
| private: |
| Dart_LoadedMachODylib* macho_; |
| const uint8_t* vm_snapshot_data_; |
| const uint8_t* vm_snapshot_instructions_; |
| const uint8_t* isolate_snapshot_data_; |
| const uint8_t* isolate_snapshot_instructions_; |
| }; |
| |
| static AppSnapshot* TryReadAppSnapshotMachODylib( |
| DartUtils::MagicNumber magic_number, |
| const char* script_name, |
| uint64_t file_offset, |
| bool force_load_from_memory) { |
| const char* error = nullptr; |
| #if defined(NATIVE_SHARED_OBJECT_FORMAT_MACHO) |
| if (file_offset == 0 && !force_load_from_memory) { |
| // The load as a dynamic library should succeed, since this is a platform |
| // that natively understands Mach-O. |
| if (auto* const snapshot = TryReadAppSnapshotDynamicLibrary( |
| magic_number, script_name, &error)) { |
| return snapshot; |
| } |
| Syslog::PrintErr("Loading dynamic library failed: %s\n", error); |
| return nullptr; |
| } |
| #endif |
| const uint8_t *vm_data_buffer = nullptr, *vm_instructions_buffer = nullptr, |
| *isolate_data_buffer = nullptr, |
| *isolate_instructions_buffer = nullptr; |
| Dart_LoadedMachODylib* handle = nullptr; |
| if (force_load_from_memory) { |
| File* const file = |
| File::Open(/*namespc=*/nullptr, script_name, File::kRead); |
| if (file == nullptr) return nullptr; |
| MappedMemory* memory = file->Map(File::kReadOnly, /*position=*/0, |
| /*length=*/file->Length()); |
| if (memory == nullptr) { |
| Syslog::PrintErr("File mapping failed\n"); |
| return nullptr; |
| } |
| const uint8_t* address = |
| reinterpret_cast<const uint8_t*>(memory->address()); |
| handle = Dart_LoadMachODylib_Memory( |
| address + file_offset, file->Length(), &error, &vm_data_buffer, |
| &vm_instructions_buffer, &isolate_data_buffer, |
| &isolate_instructions_buffer); |
| delete memory; |
| file->Release(); |
| } else { |
| handle = |
| Dart_LoadMachODylib(script_name, file_offset, &error, &vm_data_buffer, |
| &vm_instructions_buffer, &isolate_data_buffer, |
| &isolate_instructions_buffer); |
| } |
| if (handle == nullptr) { |
| Syslog::PrintErr("Loading failed: %s\n", error); |
| return nullptr; |
| } |
| return new MachODylibAppSnapshot(magic_number, handle, vm_data_buffer, |
| vm_instructions_buffer, isolate_data_buffer, |
| isolate_instructions_buffer); |
| } |
| |
| static AppSnapshot* TryReadAppSnapshotAt(const char* script_name, |
| File& file, |
| int64_t file_offset, |
| bool force_load_from_memory = false) { |
| auto const magic_number = ReadMagicNumberAt(file, file_offset); |
| if (magic_number == DartUtils::kAotELFMagicNumber) { |
| return TryReadAppSnapshotElf(script_name, file_offset, |
| force_load_from_memory); |
| } |
| |
| if (magic_number == DartUtils::kAotMachO32MagicNumber || |
| magic_number == DartUtils::kAotMachO64MagicNumber) { |
| return TryReadAppSnapshotMachODylib(magic_number, script_name, file_offset, |
| force_load_from_memory); |
| } |
| |
| if (file_offset == 0) { |
| // This is a non-appended snapshot which is not handled by any of the |
| // non-native loaders, so attempt to load it as a native dynamic library. |
| const char* error = nullptr; |
| if (auto* const snapshot = TryReadAppSnapshotDynamicLibrary( |
| magic_number, script_name, &error)) { |
| return snapshot; |
| } |
| Syslog::PrintErr("Loading dynamic library failed: %s\n", error); |
| } |
| |
| return nullptr; |
| } |
| |
| #if defined(DART_TARGET_OS_MACOS) |
| AppSnapshot* Snapshot::TryReadAppendedAppSnapshotFromMachO( |
| const char* container_path) { |
| // Ensure file is actually MachO-formatted. |
| DartUtils::MagicNumber magic_number; |
| if (!IsMachOFormattedBinary(container_path, &magic_number)) { |
| Syslog::PrintErr("Expected a Mach-O binary.\n"); |
| return nullptr; |
| } |
| |
| File* file = File::Open(nullptr, container_path, File::kRead); |
| if (file == nullptr) { |
| return nullptr; |
| } |
| RefCntReleaseScope<File> rs(file); |
| |
| // Read in the Mach-O header. Note that the 64-bit header is the same layout |
| // as the 32-bit header, just with an extra field for alignment, so we can |
| // safely load a 32-bit header to get all the information we need. |
| mach_o::mach_header header; |
| if (!file->ReadFully(&header, sizeof(header))) { |
| Syslog::PrintErr("Could not read a complete Mach-O header.\n"); |
| return nullptr; |
| } |
| |
| auto const bitsize = DartUtils::MagicNumberBitSize(magic_number); |
| if (bitsize == 64) { |
| // The load commands start immediately after the full header. |
| if (!file->SetPosition(sizeof(mach_o::mach_header_64))) { |
| Syslog::PrintErr("Could not read a complete Mach-O 64-bit header.\n"); |
| } |
| } else { |
| ASSERT_EQUAL(bitsize, 32); |
| } |
| |
| // Now we search through the load commands to find our snapshot note, which |
| // has a data_owner field of kMachOAppSnapshotNoteName. |
| for (uint32_t i = 0; i < header.ncmds; ++i) { |
| mach_o::load_command command; |
| file->ReadFully(&command, sizeof(mach_o::load_command)); |
| |
| file->SetPosition(file->Position() - sizeof(command)); |
| if (command.cmd != mach_o::LC_NOTE) { |
| file->SetPosition(file->Position() + command.cmdsize); |
| continue; |
| } |
| |
| mach_o::note_command note; |
| file->ReadFully(¬e, sizeof(note)); |
| |
| if (strcmp(note.data_owner, kMachOAppSnapshotNoteName) != 0) { |
| file->SetPosition(file->Position() + command.cmdsize); |
| continue; |
| } |
| |
| return TryReadAppSnapshotAt(container_path, *file, note.offset); |
| } |
| |
| return nullptr; |
| } |
| #endif // defined(DART_TARGET_OS_MACOS) |
| |
| #if defined(DART_TARGET_OS_WINDOWS) |
| // Keep in sync with CoffSectionTable._snapshotSectionName from |
| // pkg/dart2native/lib/dart2native_pe.dart. |
| static const char kSnapshotSectionName[] = "snapshot"; |
| // Ignore the null terminator, as it won't be present if the string length is |
| // exactly pe::kCoffSectionNameSize. |
| static_assert(sizeof(kSnapshotSectionName) - 1 <= pe::kCoffSectionNameSize, |
| "Section name of snapshot too large"); |
| |
| AppSnapshot* Snapshot::TryReadAppendedAppSnapshotFromPE( |
| const char* container_path) { |
| File* const file = File::Open(nullptr, container_path, File::kRead); |
| if (file == nullptr) { |
| return nullptr; |
| } |
| RefCntReleaseScope<File> rs(file); |
| |
| // Ensure file is actually PE-formatted. |
| if (!IsPEFormattedBinary(container_path)) { |
| Syslog::PrintErr( |
| "Attempted load target was not formatted as expected: " |
| "expected PE32 or PE32+ image file.\n"); |
| return nullptr; |
| } |
| |
| // Parse the offset into the PE contents (i.e., skipping the MS-DOS stub). |
| uint32_t pe_offset; |
| file->SetPosition(pe::kPEOffsetOffset); |
| file->ReadFully(&pe_offset, sizeof(pe_offset)); |
| |
| // Skip past the magic bytes to the COFF file header and COFF optional header. |
| const intptr_t coff_offset = pe_offset + sizeof(pe::kPEMagic); |
| file->SetPosition(coff_offset); |
| pe::coff_file_header file_header; |
| file->ReadFully(&file_header, sizeof(file_header)); |
| // The optional header follows directly after the file header. |
| pe::coff_optional_header opt_header; |
| file->ReadFully(&opt_header, sizeof(opt_header)); |
| |
| // Skip to the section table. |
| const intptr_t coff_symbol_table_offset = |
| coff_offset + sizeof(file_header) + file_header.optional_header_size; |
| file->SetPosition(coff_symbol_table_offset); |
| for (intptr_t i = 0; i < file_header.num_sections; i++) { |
| pe::coff_section_header section_header; |
| file->ReadFully(§ion_header, sizeof(section_header)); |
| if (strncmp(section_header.name, kSnapshotSectionName, |
| pe::kCoffSectionNameSize) == 0) { |
| // We have to do the loading manually even though currently the snapshot |
| // data is at the end of the file because the file alignment for |
| // PE sections can be less than the page size, and TryReadAppSnapshotElf |
| // won't work if the file offset isn't page-aligned. |
| const char* error = nullptr; |
| const uint8_t* vm_data_buffer = nullptr; |
| const uint8_t* vm_instructions_buffer = nullptr; |
| const uint8_t* isolate_data_buffer = nullptr; |
| const uint8_t* isolate_instructions_buffer = nullptr; |
| |
| const intptr_t offset = section_header.file_offset; |
| const intptr_t size = section_header.file_size; |
| |
| auto const magic_number = ReadMagicNumberAt(*file, offset); |
| |
| std::unique_ptr<uint8_t[]> snapshot(new uint8_t[size]); |
| file->SetPosition(offset); |
| file->ReadFully(snapshot.get(), sizeof(uint8_t) * size); |
| |
| if (magic_number == DartUtils::kAotELFMagicNumber) { |
| Dart_LoadedElf* const handle = |
| Dart_LoadELF_Memory(snapshot.get(), size, &error, &vm_data_buffer, |
| &vm_instructions_buffer, &isolate_data_buffer, |
| &isolate_instructions_buffer); |
| |
| if (handle == nullptr) { |
| Syslog::PrintErr("Loading failed: %s\n", error); |
| return nullptr; |
| } |
| |
| return new ElfAppSnapshot(handle, vm_data_buffer, |
| vm_instructions_buffer, isolate_data_buffer, |
| isolate_instructions_buffer); |
| } |
| |
| if (magic_number == DartUtils::kAotMachO32MagicNumber || |
| magic_number == DartUtils::kAotMachO64MagicNumber) { |
| Dart_LoadedMachODylib* const handle = Dart_LoadMachODylib_Memory( |
| snapshot.get(), size, &error, &vm_data_buffer, |
| &vm_instructions_buffer, &isolate_data_buffer, |
| &isolate_instructions_buffer); |
| |
| if (handle == nullptr) { |
| Syslog::PrintErr("Loading failed: %s\n", error); |
| return nullptr; |
| } |
| |
| return new MachODylibAppSnapshot( |
| magic_number, handle, vm_data_buffer, vm_instructions_buffer, |
| isolate_data_buffer, isolate_instructions_buffer); |
| } |
| |
| return nullptr; |
| } |
| } |
| |
| return nullptr; |
| } |
| #endif // defined(DART_TARGET_OS_WINDOWS) |
| |
| AppSnapshot* Snapshot::TryReadAppendedAppSnapshot(const char* container_path) { |
| #if defined(DART_TARGET_OS_MACOS) |
| if (IsMachOFormattedBinary(container_path)) { |
| return TryReadAppendedAppSnapshotFromMachO(container_path); |
| } |
| #elif defined(DART_TARGET_OS_WINDOWS) |
| if (IsPEFormattedBinary(container_path)) { |
| return TryReadAppendedAppSnapshotFromPE(container_path); |
| } |
| #endif |
| |
| File* file = File::Open(nullptr, container_path, File::kRead); |
| if (file == nullptr) { |
| return nullptr; |
| } |
| RefCntReleaseScope<File> rs(file); |
| |
| // For other appended snapshots, the header for the appended snapshot |
| // information are two 64-bit integers at the end of the file: |
| // ... |
| // snapshot offset (length of snapshot is to appended header) |
| // DartUtils::kAppJITMagicNumber |
| const int64_t magic_number_offset = file->Length() - kInt64Size; |
| auto const magic_number = ReadMagicNumberAt(*file, magic_number_offset); |
| if (magic_number != DartUtils::kAppJITMagicNumber) { |
| return nullptr; |
| } |
| |
| const int64_t snapshot_offset_offset = magic_number_offset - kInt64Size; |
| int64_t snapshot_offset; |
| if (!file->SetPosition(snapshot_offset_offset)) { |
| return nullptr; |
| } |
| if (!file->ReadFully(&snapshot_offset, sizeof(snapshot_offset))) { |
| return nullptr; |
| } |
| // The offset is always encoded as Little Endian. |
| snapshot_offset = Utils::LittleEndianToHost64(snapshot_offset); |
| if (snapshot_offset <= 0) { |
| return nullptr; |
| } |
| |
| return TryReadAppSnapshotAt(container_path, *file, snapshot_offset); |
| } |
| #endif // defined(DART_PRECOMPILED_RUNTIME) |
| |
| bool Snapshot::IsMachOFormattedBinary(const char* filename, |
| DartUtils::MagicNumber* out) { |
| File* file = File::Open(nullptr, filename, File::kRead); |
| if (file == nullptr) { |
| return false; |
| } |
| RefCntReleaseScope<File> rs(file); |
| |
| auto const magic_number = ReadMagicNumberAt(*file, /*offset=*/0); |
| if (out != nullptr) { |
| *out = magic_number; |
| } |
| return magic_number == DartUtils::kAotMachO32MagicNumber || |
| magic_number == DartUtils::kAotMachO64MagicNumber; |
| } |
| |
| #if defined(DART_TARGET_OS_WINDOWS) |
| bool Snapshot::IsPEFormattedBinary(const char* filename) { |
| File* file = File::Open(nullptr, filename, File::kRead); |
| if (file == nullptr) { |
| return false; |
| } |
| RefCntReleaseScope<File> rs(file); |
| |
| // Parse the PE offset. |
| uint32_t pe_offset; |
| // Ensure the file is long enough to contain the PE offset. |
| if (file->Length() < |
| static_cast<intptr_t>(pe::kPEOffsetOffset + sizeof(pe_offset))) { |
| return false; |
| } |
| file->SetPosition(pe::kPEOffsetOffset); |
| file->Read(&pe_offset, sizeof(pe_offset)); |
| |
| // Ensure the file is long enough to contain the PE magic bytes. |
| if (file->Length() < |
| static_cast<intptr_t>(pe_offset + sizeof(pe::kPEMagic))) { |
| return false; |
| } |
| // Check the magic bytes. |
| file->SetPosition(pe_offset); |
| for (size_t i = 0; i < sizeof(pe::kPEMagic); i++) { |
| char c; |
| file->Read(&c, sizeof(c)); |
| if (c != pe::kPEMagic[i]) { |
| return false; |
| } |
| } |
| |
| // Check that there is a coff optional header. |
| pe::coff_file_header file_header; |
| pe::coff_optional_header opt_header; |
| file->Read(&file_header, sizeof(file_header)); |
| if (file_header.optional_header_size < sizeof(opt_header)) { |
| return false; |
| } |
| file->Read(&opt_header, sizeof(opt_header)); |
| // Check the magic bytes in the coff optional header. |
| if (opt_header.magic != pe::kPE32Magic && |
| opt_header.magic != pe::kPE32PlusMagic) { |
| return false; |
| } |
| |
| return true; |
| } |
| #endif // defined(DART_TARGET_OS_WINDOWS) |
| |
| AppSnapshot* Snapshot::TryReadAppSnapshot(const char* script_uri, |
| bool force_load_from_memory, |
| bool decode_uri) { |
| CStringUniquePtr decoded_path(nullptr); |
| const char* script_name = nullptr; |
| if (decode_uri) { |
| decoded_path = File::UriToPath(script_uri); |
| if (decoded_path == nullptr) { |
| return nullptr; |
| } |
| script_name = decoded_path.get(); |
| } else { |
| script_name = script_uri; |
| } |
| if (File::GetType(nullptr, script_name, true) != File::kIsFile) { |
| // If 'script_name' refers to a pipe, don't read to check for an app |
| // snapshot since we cannot rewind if it isn't (and couldn't mmap it in |
| // anyway if it was). |
| return nullptr; |
| } |
| File* file = File::Open(nullptr, script_name, File::kRead); |
| if (file == nullptr) { |
| return nullptr; |
| } |
| RefCntReleaseScope<File> rs(file); |
| |
| const intptr_t offset = 0; |
| #if defined(DART_PRECOMPILED_RUNTIME) |
| return TryReadAppSnapshotAt(script_name, *file, offset, |
| force_load_from_memory); |
| #else |
| auto const magic_number = ReadMagicNumberAt(*file, offset); |
| if (magic_number == DartUtils::kAppJITMagicNumber) { |
| // Return the JIT snapshot. |
| return TryReadAppSnapshotBlobs(script_name, file); |
| } |
| // We create a dummy snapshot object just to remember the type which |
| // has already been identified by sniffing the magic number. |
| return new DummySnapshot(magic_number); |
| #endif // defined(DART_PRECOMPILED_RUNTIME) |
| |
| return nullptr; |
| } |
| |
| #if !defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) && !defined(TESTING) |
| static void WriteSnapshotFile(const char* filename, |
| const uint8_t* buffer, |
| const intptr_t size) { |
| File* file = File::Open(nullptr, filename, File::kWriteTruncate); |
| if (file == nullptr) { |
| ErrorExit(kErrorExitCode, "Unable to open file %s for writing snapshot\n", |
| filename); |
| } |
| |
| if (!file->WriteFully(buffer, size)) { |
| ErrorExit(kErrorExitCode, "Unable to write file %s for writing snapshot\n", |
| filename); |
| } |
| file->Release(); |
| } |
| #endif |
| |
| static bool WriteInt64(File* file, int64_t size) { |
| return file->WriteFully(&size, sizeof(size)); |
| } |
| |
| void Snapshot::WriteAppSnapshot(const char* filename, |
| uint8_t* isolate_data_buffer, |
| intptr_t isolate_data_size, |
| uint8_t* isolate_instructions_buffer, |
| intptr_t isolate_instructions_size) { |
| File* file = File::Open(nullptr, filename, File::kWriteTruncate); |
| if (file == nullptr) { |
| ErrorExit(kErrorExitCode, "Unable to write snapshot file '%s'\n", filename); |
| } |
| |
| file->WriteFully(appjit_magic_number.bytes, appjit_magic_number.length); |
| WriteInt64(file, isolate_data_size); |
| WriteInt64(file, isolate_instructions_size); |
| ASSERT(file->Position() == |
| (kAppSnapshotHeaderSize + DartUtils::kMaxMagicNumberSize)); |
| |
| file->SetPosition(Utils::RoundUp(file->Position(), kAppSnapshotPageSize)); |
| if (LOG_SECTION_BOUNDARIES) { |
| Syslog::PrintErr("%" Px64 ": Isolate Data\n", file->Position()); |
| } |
| if (!file->WriteFully(isolate_data_buffer, isolate_data_size)) { |
| ErrorExit(kErrorExitCode, "Unable to write snapshot file '%s'\n", filename); |
| } |
| |
| if (isolate_instructions_size != 0) { |
| file->SetPosition(Utils::RoundUp(file->Position(), kAppSnapshotPageSize)); |
| if (LOG_SECTION_BOUNDARIES) { |
| Syslog::PrintErr("%" Px64 ": Isolate Instructions\n", file->Position()); |
| } |
| if (!file->WriteFully(isolate_instructions_buffer, |
| isolate_instructions_size)) { |
| ErrorExit(kErrorExitCode, "Unable to write snapshot file '%s'\n", |
| filename); |
| } |
| } |
| |
| file->Flush(); |
| file->Release(); |
| } |
| |
| void Snapshot::GenerateKernel(const char* snapshot_filename, |
| const char* script_name, |
| const char* package_config) { |
| #if !defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) && !defined(TESTING) |
| ASSERT(Dart_CurrentIsolate() == nullptr); |
| |
| uint8_t* kernel_buffer = nullptr; |
| intptr_t kernel_buffer_size = 0; |
| dfe.ReadScript(script_name, nullptr, &kernel_buffer, &kernel_buffer_size); |
| if (kernel_buffer != nullptr) { |
| WriteSnapshotFile(snapshot_filename, kernel_buffer, kernel_buffer_size); |
| free(kernel_buffer); |
| } else { |
| Dart_KernelCompilationResult result = |
| dfe.CompileScript(script_name, /*incremental*/ false, package_config, |
| /*snapshot=*/true, /*embedd_sources=*/true); |
| if (result.status != Dart_KernelCompilationStatus_Ok) { |
| Syslog::PrintErr("%s\n", result.error); |
| Platform::Exit(kCompilationErrorExitCode); |
| } |
| WriteSnapshotFile(snapshot_filename, result.kernel, result.kernel_size); |
| free(result.kernel); |
| } |
| #else |
| UNREACHABLE(); |
| #endif // !defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) && !defined(TESTING) |
| } |
| |
| void Snapshot::GenerateAppJIT(const char* snapshot_filename) { |
| #if defined(TARGET_ARCH_IA32) |
| // Snapshots with code are not supported on IA32. |
| uint8_t* isolate_buffer = nullptr; |
| intptr_t isolate_size = 0; |
| |
| Dart_Handle result = Dart_CreateSnapshot(nullptr, nullptr, &isolate_buffer, |
| &isolate_size, /*is_core=*/false); |
| if (Dart_IsError(result)) { |
| ErrorExit(kErrorExitCode, "%s\n", Dart_GetError(result)); |
| } |
| |
| WriteAppSnapshot(snapshot_filename, isolate_buffer, isolate_size, nullptr, 0); |
| #else |
| uint8_t* isolate_data_buffer = nullptr; |
| intptr_t isolate_data_size = 0; |
| uint8_t* isolate_instructions_buffer = nullptr; |
| intptr_t isolate_instructions_size = 0; |
| Dart_Handle result = Dart_CreateAppJITSnapshotAsBlobs( |
| &isolate_data_buffer, &isolate_data_size, &isolate_instructions_buffer, |
| &isolate_instructions_size); |
| if (Dart_IsError(result)) { |
| ErrorExit(kErrorExitCode, "%s\n", Dart_GetError(result)); |
| } |
| WriteAppSnapshot(snapshot_filename, isolate_data_buffer, isolate_data_size, |
| isolate_instructions_buffer, isolate_instructions_size); |
| #endif |
| } |
| |
| static void StreamingWriteCallback(void* callback_data, |
| const uint8_t* buffer, |
| intptr_t size) { |
| File* file = reinterpret_cast<File*>(callback_data); |
| if (!file->WriteFully(buffer, size)) { |
| ErrorExit(kErrorExitCode, "Unable to write snapshot file\n"); |
| } |
| } |
| |
| void Snapshot::GenerateAppAOTAsAssembly(const char* snapshot_filename) { |
| File* file = File::Open(nullptr, snapshot_filename, File::kWriteTruncate); |
| RefCntReleaseScope<File> rs(file); |
| if (file == nullptr) { |
| ErrorExit(kErrorExitCode, "Unable to open file %s for writing snapshot\n", |
| snapshot_filename); |
| } |
| Dart_Handle result = Dart_CreateAppAOTSnapshotAsAssembly( |
| StreamingWriteCallback, file, /*stripped=*/false, |
| /*debug_callback_data=*/nullptr); |
| if (Dart_IsError(result)) { |
| ErrorExit(kErrorExitCode, "%s\n", Dart_GetError(result)); |
| } |
| } |
| |
| } // namespace bin |
| } // namespace dart |