f7699740cc
Adds a Mach-O loader, similar to the existing ELF loader, that
can be used on platforms that do not support loading Mach-O dynamic
libraries via dlopen().
Other changes:
* Renames the --force-load-elf-from-memory command line argument
to --force-load-from-memory.
* Use CPU_TYPE_ANY and CPU_SUBTYPE_ANY for architectures that do
not have specific constants in <mach/machine.h> (e.g., RISCV),
as the snapshot header check after loading also catches architecture
mismatches.
* Emit the unwinding information at the end of the text segment
for Windows Mach-O snapshots as is done for ELF ones.
TEST=vm/dart/unobfuscated_static_symbols_test
vm/dart/use_dwarf_stack_traces_flag_test
Issue: https://github.com/dart-lang/sdk/issues/60307
Change-Id: I34a2a334f47d18d5c4f4a712956e71fd0ac94024
Cq-Include-Trybots: luci.dart.try:vm-aot-linux-debug-x64-try,vm-mac-release-arm64-try,vm-aot-mac-release-arm64-try,vm-aot-mac-release-x64-try,vm-aot-dwarf-linux-product-x64-try,vm-linux-debug-x64-try,vm-mac-debug-arm64-try,vm-fuchsia-release-x64-try,vm-fuchsia-release-arm64-try,vm-linux-debug-ia32-try,vm-aot-linux-debug-simarm_x64-try,vm-aot-linux-debug-simriscv32-try,vm-aot-linux-debug-simriscv64-try,vm-aot-linux-release-simarm_x64-try,vm-gcc-linux-try,vm-ubsan-linux-release-arm64-try,vm-aot-win-release-arm64-try,vm-aot-win-release-x64-try,vm-win-release-x64-try,vm-win-release-arm64-try,vm-aot-win-debug-arm64-try,vm-win-debug-arm64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/430100
Reviewed-by: Slava Egorov <vegorov@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
557 lines
20 KiB
C++
557 lines
20 KiB
C++
// Copyright (c) 2025, 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/macho_loader.h"
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "platform/globals.h"
|
|
|
|
#if defined(DART_HOST_OS_FUCHSIA)
|
|
#include <sys/mman.h>
|
|
#endif
|
|
|
|
#include "platform/mach_o.h"
|
|
#include "platform/unwinding_records.h"
|
|
|
|
#include "bin/file.h"
|
|
#include "bin/mappable.h"
|
|
#include "bin/virtual_memory.h"
|
|
|
|
namespace dart {
|
|
namespace bin {
|
|
|
|
namespace mach_o {
|
|
|
|
class LoadCommandIterator {
|
|
public:
|
|
LoadCommandIterator(const void* start, size_t size)
|
|
: end_(reinterpret_cast<const void*>(reinterpret_cast<uword>(start) +
|
|
size)),
|
|
current_(start) {}
|
|
|
|
void Advance() {
|
|
ASSERT(!Done());
|
|
const uint32_t size = current()->cmdsize;
|
|
current_ =
|
|
reinterpret_cast<const void*>(reinterpret_cast<uword>(current_) + size);
|
|
}
|
|
|
|
bool Done() { return current_ >= end_; }
|
|
|
|
const dart::mach_o::load_command* current() const {
|
|
return reinterpret_cast<const dart::mach_o::load_command*>(current_);
|
|
}
|
|
|
|
private:
|
|
const void* end_;
|
|
const void* current_;
|
|
};
|
|
|
|
/// A loader for a subset of Mach-O which may be used to load objects produced
|
|
/// by Dart_CreateAppAOTSnapshotAsMachO.
|
|
class LoadedMachODylib {
|
|
public:
|
|
LoadedMachODylib(std::unique_ptr<Mappable> mappable,
|
|
uint64_t macho_data_offset)
|
|
: mappable_(std::move(mappable)), macho_data_offset_(macho_data_offset) {}
|
|
|
|
~LoadedMachODylib();
|
|
|
|
/// Loads the Mach-O dynamic library object into memory. Returns whether the
|
|
/// load was successful. On failure, the error may be retrieved by 'error()'.
|
|
bool Load();
|
|
|
|
/// Reads Dart-specific symbols from the loaded Mach-O dynamic library.
|
|
///
|
|
/// Stores the address of the corresponding symbol in each non-null output
|
|
/// parameter.
|
|
///
|
|
/// Fails if any output parameter is non-null but points to null and the
|
|
/// corresponding symbol was not found, or if the dynamic symbol table could
|
|
/// not be decoded.
|
|
///
|
|
/// Has the side effect of initializing the relocated addresses for the text
|
|
/// sections corresponding to non-null output parameters in the BSS segment.
|
|
///
|
|
/// On failure, the error may be retrieved by 'error()'.
|
|
bool ResolveSymbols(const uint8_t** vm_data,
|
|
const uint8_t** vm_instrs,
|
|
const uint8_t** isolate_data,
|
|
const uint8_t** isolate_instrs);
|
|
|
|
const char* error() { return error_; }
|
|
|
|
private:
|
|
bool ReadHeader();
|
|
bool LoadSegments();
|
|
bool ReadDynamicSymbolTable();
|
|
|
|
static uword PageSize() { return VirtualMemory::PageSize(); }
|
|
|
|
// Unlike File::Map, allows non-aligned 'start' and 'length'.
|
|
MappedMemory* MapFilePiece(uword start,
|
|
uword length,
|
|
const void** mapping_start);
|
|
|
|
// Initialized on a successful Load().
|
|
std::unique_ptr<Mappable> mappable_;
|
|
const uint64_t macho_data_offset_;
|
|
|
|
// Initialized on error.
|
|
const char* error_ = nullptr;
|
|
|
|
// Initialized by ReadHeader().
|
|
dart::mach_o::mach_header header_;
|
|
std::unique_ptr<MappedMemory> load_commands_mapping_;
|
|
const void* load_commands_ = nullptr;
|
|
|
|
// Initialized by LoadSegments().
|
|
std::unique_ptr<VirtualMemory> base_;
|
|
|
|
// Initialized by ReadDynamicSymbolTable().
|
|
const char* string_table_ = nullptr;
|
|
std::unique_ptr<MappedMemory> string_table_mapping_;
|
|
const dart::mach_o::nlist* external_symbols_ = nullptr;
|
|
uword external_symbol_count_ = 0;
|
|
std::unique_ptr<MappedMemory> external_symbols_mapping_;
|
|
|
|
#if defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT)
|
|
// Dynamic table for looking up unwinding exceptions info.
|
|
// Initialized by LoadSegments as we load executable segment.
|
|
MallocGrowableArray<void*> dynamic_runtime_function_tables_;
|
|
#endif
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(LoadedMachODylib);
|
|
};
|
|
|
|
#define CHECK(value) \
|
|
if (!(value)) { \
|
|
ASSERT(error_ != nullptr); \
|
|
return false; \
|
|
}
|
|
|
|
#define ERROR(message) \
|
|
{ \
|
|
error_ = (message); \
|
|
return false; \
|
|
}
|
|
|
|
#define CHECK_ERROR(value, message) \
|
|
if (!(value)) { \
|
|
error_ = (message); \
|
|
return false; \
|
|
}
|
|
|
|
bool LoadedMachODylib::Load() {
|
|
VirtualMemory::Init();
|
|
|
|
if (error_ != nullptr) {
|
|
return false;
|
|
}
|
|
|
|
CHECK_ERROR(Utils::IsAligned(macho_data_offset_, PageSize()),
|
|
"File offset must be page-aligned.");
|
|
|
|
ASSERT(mappable_ != nullptr);
|
|
CHECK_ERROR(mappable_->SetPosition(macho_data_offset_),
|
|
"Invalid file offset.");
|
|
|
|
CHECK(ReadHeader());
|
|
CHECK(LoadSegments());
|
|
CHECK(ReadDynamicSymbolTable());
|
|
|
|
mappable_.reset();
|
|
|
|
return true;
|
|
}
|
|
|
|
LoadedMachODylib::~LoadedMachODylib() {
|
|
#if defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT)
|
|
for (intptr_t i = 0; i < dynamic_runtime_function_tables_.length(); i++) {
|
|
UnwindingRecordsPlatform::UnregisterDynamicTable(
|
|
dynamic_runtime_function_tables_[i]);
|
|
}
|
|
#endif
|
|
|
|
// Unmap the image.
|
|
base_.reset();
|
|
|
|
// Explicitly destroy all the mappings before closing the file.
|
|
load_commands_mapping_.reset();
|
|
string_table_mapping_.reset();
|
|
external_symbols_mapping_.reset();
|
|
}
|
|
|
|
bool LoadedMachODylib::ReadHeader() {
|
|
CHECK_ERROR(mappable_->ReadFully(&header_, sizeof(dart::mach_o::mach_header)),
|
|
"Could not read Mach-O file.");
|
|
|
|
CHECK_ERROR(header_.magic == dart::mach_o::MH_MAGIC ||
|
|
header_.magic == dart::mach_o::MH_MAGIC_64,
|
|
"Expected a host-endian Mach-O object.");
|
|
|
|
CHECK_ERROR(header_.filetype == dart::mach_o::MH_DYLIB,
|
|
"Can only load Mach-O dynamic libraries.");
|
|
|
|
#if defined(TARGET_ARCH_IA32)
|
|
CHECK_ERROR(header_.cputype == dart::mach_o::CPU_TYPE_I386,
|
|
"Architecture mismatch.");
|
|
CHECK_ERROR(header_.cpusubtype == dart::mach_o::CPU_SUBTYPE_I386_ALL,
|
|
"Unexpected subtype of X86 specified");
|
|
#elif defined(TARGET_ARCH_X64)
|
|
CHECK_ERROR(header_.cputype == dart::mach_o::CPU_TYPE_X86_64,
|
|
"Architecture mismatch.");
|
|
CHECK_ERROR(header_.cpusubtype == dart::mach_o::CPU_SUBTYPE_X86_64_ALL,
|
|
"Unexpected subtype of X86_64 specified");
|
|
#elif defined(TARGET_ARCH_ARM)
|
|
CHECK_ERROR(header_.cputype == dart::mach_o::CPU_TYPE_ARM,
|
|
"Architecture mismatch.");
|
|
CHECK_ERROR(header_.cpusubtype == dart::mach_o::CPU_SUBTYPE_ARM_ALL,
|
|
"Unexpected subtype of ARM specified");
|
|
#elif defined(TARGET_ARCH_ARM64)
|
|
CHECK_ERROR(header_.cputype == dart::mach_o::CPU_TYPE_ARM64,
|
|
"Architecture mismatch.");
|
|
CHECK_ERROR(header_.cpusubtype == dart::mach_o::CPU_SUBTYPE_ARM64_ALL,
|
|
"Unexpected subtype of ARM64 specified");
|
|
#else
|
|
// Not an architecture with appropriate constants defined in <mach/machine.h>,
|
|
// which means we set the cpu type and subtype to ANY as the snapshot header
|
|
// check after loading also catches any architecture mismatches.
|
|
CHECK_ERROR(header_.cputype == dart::mach_o::CPU_TYPE_ANY,
|
|
"Architecture mismatch.");
|
|
CHECK_ERROR(header_.cpusubtype == dart::mach_o::CPU_SUBTYPE_ANY,
|
|
"Unexpected subtype specified");
|
|
#endif
|
|
|
|
const uword file_start = header_.magic == dart::mach_o::MH_MAGIC_64
|
|
? sizeof(dart::mach_o::mach_header_64)
|
|
: sizeof(dart::mach_o::mach_header);
|
|
const uword file_length = header_.sizeofcmds;
|
|
load_commands_mapping_.reset(
|
|
MapFilePiece(file_start, file_length,
|
|
reinterpret_cast<const void**>(&load_commands_)));
|
|
CHECK_ERROR(load_commands_mapping_ != nullptr,
|
|
"Could not mmap the load commands.");
|
|
return true;
|
|
}
|
|
|
|
bool LoadedMachODylib::LoadSegments() {
|
|
// Calculate the total amount of virtual memory needed.
|
|
uint64_t total_memory = 0;
|
|
{
|
|
LoadCommandIterator it(load_commands_, header_.sizeofcmds);
|
|
while (!it.Done()) {
|
|
auto* const current = it.current();
|
|
if (current->cmd == dart::mach_o::LC_SEGMENT) {
|
|
auto* const segment =
|
|
reinterpret_cast<const dart::mach_o::segment_command*>(current);
|
|
total_memory = Utils::Maximum<uint64_t>(
|
|
segment->vmaddr + segment->vmsize, total_memory);
|
|
} else if (current->cmd == dart::mach_o::LC_SEGMENT_64) {
|
|
auto* const segment_64 =
|
|
reinterpret_cast<const dart::mach_o::segment_command_64*>(current);
|
|
total_memory = Utils::Maximum<uint64_t>(
|
|
segment_64->vmaddr + segment_64->vmsize, total_memory);
|
|
}
|
|
it.Advance();
|
|
}
|
|
}
|
|
total_memory = Utils::RoundUp(total_memory, PageSize());
|
|
|
|
base_.reset(VirtualMemory::Allocate(total_memory,
|
|
/*is_executable=*/false,
|
|
"dart-compiled-image"));
|
|
CHECK_ERROR(base_ != nullptr, "Could not reserve virtual memory.");
|
|
|
|
{
|
|
LoadCommandIterator it(load_commands_, header_.sizeofcmds);
|
|
while (!it.Done()) {
|
|
auto* const current = it.current();
|
|
uint64_t memory_offset, memory_size, file_offset, file_size;
|
|
dart::mach_o::vm_prot_t initprot;
|
|
if (current->cmd == dart::mach_o::LC_SEGMENT) {
|
|
auto* const segment =
|
|
reinterpret_cast<const dart::mach_o::segment_command*>(current);
|
|
memory_offset = segment->vmaddr;
|
|
memory_size = segment->vmsize;
|
|
file_offset = segment->fileoff;
|
|
file_size = segment->filesize;
|
|
initprot = segment->initprot;
|
|
} else if (current->cmd == dart::mach_o::LC_SEGMENT_64) {
|
|
auto* const segment_64 =
|
|
reinterpret_cast<const dart::mach_o::segment_command_64*>(current);
|
|
memory_offset = segment_64->vmaddr;
|
|
memory_size = segment_64->vmsize;
|
|
file_offset = segment_64->fileoff;
|
|
file_size = segment_64->filesize;
|
|
initprot = segment_64->initprot;
|
|
} else {
|
|
it.Advance();
|
|
continue;
|
|
}
|
|
|
|
const uint64_t adjustment = memory_offset % PageSize();
|
|
CHECK_ERROR(
|
|
adjustment == (file_offset % PageSize()),
|
|
"Difference between file and memory offset must be page-aligned.");
|
|
|
|
void* const memory_start =
|
|
static_cast<char*>(base_->address()) + memory_offset - adjustment;
|
|
const uword file_start = macho_data_offset_ + file_offset - adjustment;
|
|
const uword length = memory_size + adjustment;
|
|
|
|
File::MapType map_type = File::kReadOnly;
|
|
if (initprot ==
|
|
(dart::mach_o::VM_PROT_READ | dart::mach_o::VM_PROT_WRITE)) {
|
|
map_type = File::kReadWrite;
|
|
} else if (initprot ==
|
|
(dart::mach_o::VM_PROT_READ | dart::mach_o::VM_PROT_EXECUTE)) {
|
|
map_type = File::kReadExecute;
|
|
} else if (initprot == dart::mach_o::VM_PROT_READ) {
|
|
map_type = File::kReadOnly;
|
|
} else {
|
|
Syslog::PrintErr("VM protection flags were: 0x%x\n", initprot);
|
|
ERROR("Unsupported VM protection flags set.");
|
|
}
|
|
|
|
#if defined(DART_HOST_OS_FUCHSIA)
|
|
// mmap is less flexible on Fuchsia than on Linux and Darwin, in
|
|
// (at least) two important ways:
|
|
//
|
|
// 1. We cannot map a file opened as RX into an RW mapping, even if the
|
|
// mode is MAP_PRIVATE (which implies copy-on-write).
|
|
// 2. We cannot atomically replace an existing anonymous mapping with a
|
|
// file mapping: we must first unmap the existing mapping.
|
|
|
|
if (map_type == File::kReadWrite) {
|
|
CHECK_ERROR(mappable_->SetPosition(file_start),
|
|
"Could not advance file position.");
|
|
CHECK_ERROR(mappable_->ReadFully(memory_start, length),
|
|
"Could not read file.");
|
|
it.Advance();
|
|
continue;
|
|
}
|
|
|
|
CHECK_ERROR(munmap(memory_start, length) == 0,
|
|
"Could not unmap reservation.");
|
|
#endif
|
|
|
|
std::unique_ptr<MappedMemory> memory(
|
|
mappable_->Map(map_type, file_start, length, memory_start));
|
|
CHECK_ERROR(memory != nullptr, "Could not map segment.");
|
|
CHECK_ERROR(memory->address() == memory_start,
|
|
"Mapping not at requested address.");
|
|
#if defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT)
|
|
// For executable pages register unwinding information that should be
|
|
// present on the page.
|
|
if (map_type == File::kReadExecute) {
|
|
// RegisterExecutableMemory checks the end of the memory space, so
|
|
// if there are zerofill sections or the like in this segment, then
|
|
// the offset of the unwinding records is incorrectly calculated.
|
|
CHECK_ERROR(memory_size == file_size,
|
|
"Executable segment contains zerofill sections.");
|
|
void* ptable = nullptr;
|
|
UnwindingRecordsPlatform::RegisterExecutableMemory(memory->address(),
|
|
length, &ptable);
|
|
dynamic_runtime_function_tables_.Add(ptable);
|
|
}
|
|
#else
|
|
USE(file_size);
|
|
#endif
|
|
it.Advance();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool LoadedMachODylib::ReadDynamicSymbolTable() {
|
|
const dart::mach_o::symtab_command* symtab = nullptr;
|
|
const dart::mach_o::dysymtab_command* dysymtab = nullptr;
|
|
LoadCommandIterator it(load_commands_, header_.sizeofcmds);
|
|
while (!it.Done()) {
|
|
auto* const c = it.current();
|
|
if (c->cmd == dart::mach_o::LC_SYMTAB) {
|
|
symtab = reinterpret_cast<const dart::mach_o::symtab_command*>(c);
|
|
} else if (c->cmd == dart::mach_o::LC_DYSYMTAB) {
|
|
dysymtab = reinterpret_cast<const dart::mach_o::dysymtab_command*>(c);
|
|
}
|
|
it.Advance();
|
|
}
|
|
CHECK_ERROR(symtab != nullptr, "Could not locate symbol table.");
|
|
CHECK_ERROR(dysymtab != nullptr, "Could not locate dynamic symbol table.");
|
|
CHECK_ERROR(dysymtab->iextdefsym + dysymtab->nextdefsym <= symtab->nsyms,
|
|
"Dynamic symbol table offsets are out of range.");
|
|
|
|
{
|
|
const uword file_start = symtab->stroff;
|
|
const uword file_length = symtab->strsize;
|
|
string_table_mapping_.reset(
|
|
MapFilePiece(file_start, file_length,
|
|
reinterpret_cast<const void**>(&string_table_)));
|
|
CHECK_ERROR(string_table_mapping_ != nullptr,
|
|
"Could not mmap the string table.");
|
|
}
|
|
external_symbol_count_ = dysymtab->nextdefsym;
|
|
{
|
|
// Note that the offset iextdefsym is the offset into the symbol table in
|
|
// terms of symbols, not in terms of raw bytes.
|
|
const intptr_t symbol_size = sizeof(dart::mach_o::nlist);
|
|
const uword file_start =
|
|
symtab->symoff + dysymtab->iextdefsym * symbol_size;
|
|
const uword file_length = dysymtab->nextdefsym * symbol_size;
|
|
external_symbols_mapping_.reset(
|
|
MapFilePiece(file_start, file_length,
|
|
reinterpret_cast<const void**>(&external_symbols_)));
|
|
CHECK_ERROR(external_symbols_mapping_ != nullptr,
|
|
"Could not mmap the external symbols.");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool LoadedMachODylib::ResolveSymbols(const uint8_t** vm_data,
|
|
const uint8_t** vm_instrs,
|
|
const uint8_t** isolate_data,
|
|
const uint8_t** isolate_instrs) {
|
|
if (error_ != nullptr) {
|
|
return false;
|
|
}
|
|
|
|
for (uword i = 0; i < external_symbol_count_; ++i) {
|
|
const auto& sym = external_symbols_[i];
|
|
const char* name = string_table_ + sym.n_idx;
|
|
const uint8_t** output = nullptr;
|
|
|
|
if (strcmp(name, kVmSnapshotDataAsmSymbol) == 0) {
|
|
output = vm_data;
|
|
} else if (strcmp(name, kVmSnapshotInstructionsAsmSymbol) == 0) {
|
|
output = vm_instrs;
|
|
} else if (strcmp(name, kIsolateSnapshotDataAsmSymbol) == 0) {
|
|
output = isolate_data;
|
|
} else if (strcmp(name, kIsolateSnapshotInstructionsAsmSymbol) == 0) {
|
|
output = isolate_instrs;
|
|
}
|
|
|
|
if (output != nullptr) {
|
|
*output = reinterpret_cast<const uint8_t*>(base_->start() + sym.n_value);
|
|
}
|
|
}
|
|
|
|
CHECK_ERROR(isolate_data == nullptr || *isolate_data != nullptr,
|
|
"Could not find isolate snapshot data.");
|
|
CHECK_ERROR(isolate_instrs == nullptr || *isolate_instrs != nullptr,
|
|
"Could not find isolate instructions.");
|
|
return true;
|
|
}
|
|
|
|
MappedMemory* LoadedMachODylib::MapFilePiece(uword file_start,
|
|
uword file_length,
|
|
const void** mem_start) {
|
|
const uword adjustment = (macho_data_offset_ + file_start) % PageSize();
|
|
const uword mapping_offset = macho_data_offset_ + file_start - adjustment;
|
|
const uword mapping_length =
|
|
Utils::RoundUp(macho_data_offset_ + file_start + file_length,
|
|
PageSize()) -
|
|
mapping_offset;
|
|
MappedMemory* const mapping =
|
|
mappable_->Map(bin::File::kReadOnly, mapping_offset, mapping_length);
|
|
|
|
if (mapping != nullptr) {
|
|
*mem_start = reinterpret_cast<uint8_t*>(mapping->start() +
|
|
(file_start % PageSize()));
|
|
}
|
|
|
|
return mapping;
|
|
}
|
|
|
|
} // namespace mach_o
|
|
} // namespace bin
|
|
} // namespace dart
|
|
|
|
using namespace dart::bin::mach_o; // NOLINT
|
|
using Mappable = dart::bin::Mappable;
|
|
|
|
#if defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_LINUX)
|
|
DART_EXPORT Dart_LoadedMachODylib* Dart_LoadMachODylib_Fd(
|
|
int fd,
|
|
uint64_t file_offset,
|
|
const char** error,
|
|
const uint8_t** vm_snapshot_data,
|
|
const uint8_t** vm_snapshot_instrs,
|
|
const uint8_t** vm_isolate_data,
|
|
const uint8_t** vm_isolate_instrs) {
|
|
std::unique_ptr<Mappable> mappable(Mappable::FromFD(fd));
|
|
std::unique_ptr<LoadedMachODylib> macho(
|
|
new LoadedMachODylib(std::move(mappable), file_offset));
|
|
|
|
if (!macho->Load() ||
|
|
!macho->ResolveSymbols(vm_snapshot_data, vm_snapshot_instrs,
|
|
vm_isolate_data, vm_isolate_instrs)) {
|
|
*error = macho->error();
|
|
return nullptr;
|
|
}
|
|
|
|
return reinterpret_cast<Dart_LoadedMachODylib*>(macho.release());
|
|
}
|
|
#endif
|
|
|
|
DART_EXPORT Dart_LoadedMachODylib* Dart_LoadMachODylib(
|
|
const char* filename,
|
|
uint64_t file_offset,
|
|
const char** error,
|
|
const uint8_t** vm_snapshot_data,
|
|
const uint8_t** vm_snapshot_instrs,
|
|
const uint8_t** vm_isolate_data,
|
|
const uint8_t** vm_isolate_instrs) {
|
|
std::unique_ptr<Mappable> mappable(Mappable::FromPath(filename));
|
|
if (mappable == nullptr) {
|
|
*error = "Couldn't open file.";
|
|
return nullptr;
|
|
}
|
|
std::unique_ptr<LoadedMachODylib> macho(
|
|
new LoadedMachODylib(std::move(mappable), file_offset));
|
|
|
|
if (!macho->Load() ||
|
|
!macho->ResolveSymbols(vm_snapshot_data, vm_snapshot_instrs,
|
|
vm_isolate_data, vm_isolate_instrs)) {
|
|
*error = macho->error();
|
|
return nullptr;
|
|
}
|
|
|
|
return reinterpret_cast<Dart_LoadedMachODylib*>(macho.release());
|
|
}
|
|
|
|
DART_EXPORT Dart_LoadedMachODylib* Dart_LoadMachODylib_Memory(
|
|
const uint8_t* snapshot,
|
|
uint64_t snapshot_size,
|
|
const char** error,
|
|
const uint8_t** vm_snapshot_data,
|
|
const uint8_t** vm_snapshot_instrs,
|
|
const uint8_t** vm_isolate_data,
|
|
const uint8_t** vm_isolate_instrs) {
|
|
std::unique_ptr<Mappable> mappable(
|
|
Mappable::FromMemory(snapshot, snapshot_size));
|
|
if (mappable == nullptr) {
|
|
*error = "Couldn't open file.";
|
|
return nullptr;
|
|
}
|
|
std::unique_ptr<LoadedMachODylib> macho(
|
|
new LoadedMachODylib(std::move(mappable), /*file_offset=*/0));
|
|
|
|
if (!macho->Load() ||
|
|
!macho->ResolveSymbols(vm_snapshot_data, vm_snapshot_instrs,
|
|
vm_isolate_data, vm_isolate_instrs)) {
|
|
*error = macho->error();
|
|
return nullptr;
|
|
}
|
|
|
|
return reinterpret_cast<Dart_LoadedMachODylib*>(macho.release());
|
|
}
|
|
|
|
DART_EXPORT void Dart_UnloadMachODylib(Dart_LoadedMachODylib* loaded) {
|
|
delete reinterpret_cast<LoadedMachODylib*>(loaded);
|
|
}
|