015e6b0873
TEST=cross-build Bug: https://github.com/dart-lang/sdk/issues/62081 Change-Id: I39b54f121920dac7d2f2eda54d8cb29edbdccc30 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/464243 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Alexander Aprelev <aam@google.com>
976 lines
35 KiB
C++
976 lines
35 KiB
C++
// 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);
|
|
}
|
|
|
|
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,
|
|
char** error) {
|
|
#if defined(DART_INCLUDE_SIMULATOR)
|
|
*error = Utils::StrDup("running on a simulated architecture");
|
|
return nullptr;
|
|
#else
|
|
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_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'.
|
|
const size_t kPathBufSize = PATH_MAX + 1;
|
|
char canon_path[kPathBufSize];
|
|
script_name =
|
|
File::GetCanonicalPath(nullptr, script_name, canon_path, kPathBufSize);
|
|
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 = Utils::StrDup("not an ELF shared object");
|
|
}
|
|
#elif defined(NATIVE_SHARED_OBJECT_FORMAT_MACHO)
|
|
if (*error == nullptr &&
|
|
magic_number != DartUtils::kAotMachO32MagicNumber &&
|
|
magic_number != DartUtils::kAotMachO64MagicNumber) {
|
|
*error = Utils::StrDup("not a Mach-O shared object");
|
|
}
|
|
#endif
|
|
if (*error == nullptr) {
|
|
*error = Utils::StrDup(
|
|
"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)
|
|
}
|
|
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
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) {
|
|
#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.
|
|
char* error = nullptr;
|
|
if (auto* const snapshot = TryReadAppSnapshotDynamicLibrary(
|
|
DartUtils::kAotELFMagicNumber, script_name, &error)) {
|
|
return snapshot;
|
|
}
|
|
Syslog::PrintErr("Loading dynamic library failed: %s\n", error);
|
|
free(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;
|
|
const char* error = 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) {
|
|
#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.
|
|
char* error = nullptr;
|
|
if (auto* const snapshot = TryReadAppSnapshotDynamicLibrary(
|
|
magic_number, script_name, &error)) {
|
|
return snapshot;
|
|
}
|
|
Syslog::PrintErr("Loading dynamic library failed: %s\n", error);
|
|
free(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;
|
|
const char* error = 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.
|
|
char* error = nullptr;
|
|
if (auto* const snapshot = TryReadAppSnapshotDynamicLibrary(
|
|
magic_number, script_name, &error)) {
|
|
return snapshot;
|
|
}
|
|
Syslog::PrintErr("Loading dynamic library failed: %s\n", error);
|
|
free(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);
|
|
}
|
|
if (magic_number == DartUtils::kAotMachO64MagicNumber) {
|
|
// Read module snapshot.
|
|
char* error = nullptr;
|
|
if (auto* const snapshot = TryReadAppSnapshotDynamicLibrary(
|
|
magic_number, script_name, &error)) {
|
|
return snapshot;
|
|
}
|
|
Syslog::PrintErr("Loading dynamic library failed: %s\n", error);
|
|
free(error);
|
|
return nullptr;
|
|
}
|
|
// 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
|