From 362ef8e67c96578d769d9fab0df97d327554a46d Mon Sep 17 00:00:00 2001 From: Samir Jindel Date: Tue, 24 Sep 2019 15:03:19 +0000 Subject: [PATCH] [vm/aot] Add a custom ELF loader in dart_precompiled_runtime. Also switch some CQ bots using blobs to ELF. Once all embedders have migrated, we will remove blobs support entirely. Change-Id: Ie5e8c1187ad6c1af362b5715daafd3641bc8cc0e Cq-Include-Trybots:luci.dart.try:vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-release-simarm-try,vm-kernel-precomp-linux-release-simarm64-try,vm-kernel-precomp-bare-linux-release-simarm-try,vm-kernel-precomp-mac-debug-simarm_x64-try,vm-kernel-precomp-mac-release-simarm64-try,vm-kernel-precomp-win-release-x64-try,vm-kernel-precomp-android-release-arm-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/116620 Commit-Queue: Samir Jindel Reviewed-by: Martin Kustermann --- runtime/bin/BUILD.gn | 22 + runtime/bin/elf_loader.cc | 394 ++++++++++++++++++ runtime/bin/elf_loader.h | 30 ++ runtime/bin/extensions_android.cc | 6 - runtime/bin/extensions_fuchsia.cc | 6 - runtime/bin/extensions_linux.cc | 6 - runtime/bin/extensions_macos.cc | 6 - runtime/bin/extensions_win.cc | 6 - runtime/bin/file.h | 38 +- runtime/bin/file_android.cc | 15 +- runtime/bin/file_fuchsia.cc | 15 +- runtime/bin/file_linux.cc | 15 +- runtime/bin/file_macos.cc | 63 ++- runtime/bin/file_win.cc | 46 +- runtime/bin/snapshot_utils.cc | 76 +++- runtime/include/dart_api.h | 33 +- runtime/platform/elf.h | 170 ++++++++ runtime/vm/dart_api_impl.cc | 4 - runtime/vm/elf.cc | 188 ++++----- runtime/vm/virtual_memory.h | 5 +- runtime/vm/virtual_memory_fuchsia.cc | 9 +- runtime/vm/virtual_memory_posix.cc | 17 +- runtime/vm/virtual_memory_win.cc | 17 +- tests/ffi/ffi.status | 9 - .../function_callbacks_unsupported_test.dart | 19 - .../standalone_2/dwarf_stack_trace_test.dart | 2 +- tools/bots/test_matrix.json | 12 +- 27 files changed, 962 insertions(+), 267 deletions(-) create mode 100644 runtime/bin/elf_loader.cc create mode 100644 runtime/bin/elf_loader.h create mode 100644 runtime/platform/elf.h delete mode 100644 tests/ffi/function_callbacks_unsupported_test.dart diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn index 4f60c402eb9..6570f8be475 100644 --- a/runtime/bin/BUILD.gn +++ b/runtime/bin/BUILD.gn @@ -117,6 +117,23 @@ static_library("crashpad") { } } +static_library("elf_loader") { + configs += [ + "..:dart_arch_config", + "..:dart_config", + "..:dart_product_config", + "..:dart_os_fuchsia_config", + ] + include_dirs = [ ".." ] + sources = [ + "elf_loader.cc", + "elf_loader.h", + ] + deps = [ + ":libdart_builtin", + ] +} + template("build_gen_snapshot") { extra_configs = [] if (defined(invoker.extra_configs)) { @@ -841,6 +858,9 @@ dart_executable("dart_precompiled_runtime") { "main.cc", "snapshot_empty.cc", ] + + extra_deps += [ ":elf_loader" ] + if (dart_runtime_mode == "release") { extra_sources += [ "observatory_assets_empty.cc" ] } @@ -862,6 +882,8 @@ dart_executable("dartaotruntime") { "observatory_assets_empty.cc", "snapshot_empty.cc", ] + + extra_deps += [ ":elf_loader" ] } executable("process_test") { diff --git a/runtime/bin/elf_loader.cc b/runtime/bin/elf_loader.cc new file mode 100644 index 00000000000..54eed8c8fe4 --- /dev/null +++ b/runtime/bin/elf_loader.cc @@ -0,0 +1,394 @@ +// Copyright (c) 2019, 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 +#include +#include +#include +#include +#include + +#include + +namespace dart { +namespace bin { + +namespace elf { + +/// A loader for a subset of ELF which may be used to load objects produced by +/// Dart_CreateAppAOTSnapshotAsElf. +class LoadedElf { + public: + explicit LoadedElf(const char* filename) + : filename_(strdup(filename), std::free) {} + ~LoadedElf(); + + /// Loads the ELF 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 ELF. + /// + /// 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. + /// + /// 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 ReadProgramTable(); + bool LoadSegments(); + bool ReadSectionTable(); + bool ReadSectionStringTable(); + bool ReadSections(); + + static uword PageSize(); + + // Unlike File::Map, allows non-aligned 'start' and 'length'. + MappedMemory* MapFilePiece(uword start, + uword length, + const void** mapping_start); + + std::unique_ptr filename_; + + // Initialized on a successful Load(). + File* file_; + + // Initialized on error. + const char* error_ = nullptr; + + // Initialized by ReadHeader(). + dart::elf::ElfHeader header_; + + // Initialized by ReadProgramTable(). + std::unique_ptr program_table_mapping_; + const dart::elf::ProgramHeader* program_table_ = nullptr; + + // Initialized by LoadSegments(). + std::unique_ptr base_; + + // Initialized by ReadSectionTable(). + std::unique_ptr section_table_mapping_; + const dart::elf::SectionHeader* section_table_ = nullptr; + + // Initialized by ReadSectionStringTable(). + std::unique_ptr section_string_table_mapping_; + const char* section_string_table_ = nullptr; + + // Initialized by ReadSections(). + const char* dynamic_string_table_ = nullptr; + const dart::elf::Symbol* dynamic_symbol_table_ = nullptr; + uword dynamic_symbol_count_ = 0; + + DISALLOW_COPY_AND_ASSIGN(LoadedElf); +}; + +#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; \ + } + +uword LoadedElf::PageSize() { + static uword page_size_ = VirtualMemory::CalculatePageSize(); + return page_size_; +} + +bool LoadedElf::Load() { + if (error_ != nullptr) { + return false; + } + + file_ = File::Open(/*namespc=*/nullptr, filename_.get(), + bin::File::FileOpenMode::kRead); + CHECK_ERROR(file_ != nullptr, "Cannot open ELF object file."); + + CHECK(ReadHeader()); + CHECK(ReadProgramTable()); + CHECK(LoadSegments()); + CHECK(ReadSectionTable()); + CHECK(ReadSectionStringTable()); + CHECK(ReadSections()); + + return true; +} + +LoadedElf::~LoadedElf() { + // Unmap the image. + base_.reset(); + + // Explicitly destroy all the mappings before closing the file. + program_table_mapping_.reset(); + section_table_mapping_.reset(); + section_string_table_mapping_.reset(); + + if (file_ != nullptr) { + file_->Close(); + file_->Release(); + } +} + +bool LoadedElf::ReadHeader() { + CHECK_ERROR(file_->ReadFully(&header_, sizeof(dart::elf::ElfHeader)), + "Could not read ELF file."); + + CHECK_ERROR(header_.ident[dart::elf::EI_DATA] == dart::elf::ELFDATA2LSB, + "Expected little-endian ELF object."); + + CHECK_ERROR(header_.type == dart::elf::ET_DYN, + "Can only load dynamic libraries."); + +#if defined(TARGET_ARCH_IA32) + CHECK_ERROR(header_.machine == dart::elf::EM_386, "Architecture mismatch."); +#elif defined(TARGET_ARCH_X64) + CHECK_ERROR(header_.machine == dart::elf::EM_X86_64, + "Architecture mismatch."); +#elif defined(TARGET_ARCH_ARM) + CHECK_ERROR(header_.machine == dart::elf::EM_ARM, "Architecture mismatch."); +#elif defined(TARGET_ARCH_ARM64) + CHECK_ERROR(header_.machine == dart::elf::EM_AARCH64, + "Architecture mismatch."); +#else +#error Unsupported architecture architecture. +#endif + + CHECK_ERROR(header_.version == dart::elf::EV_CURRENT, + "Unexpected ELF version."); + CHECK_ERROR(header_.header_size == sizeof(dart::elf::ElfHeader), + "Unexpected header size."); + CHECK_ERROR( + header_.program_table_entry_size == sizeof(dart::elf::ProgramHeader), + "Unexpected program header size."); + CHECK_ERROR( + header_.section_table_entry_size == sizeof(dart::elf::SectionHeader), + "Unexpected section header size."); + + return true; +} + +bool LoadedElf::ReadProgramTable() { + const uword file_start = header_.program_table_offset; + const uword file_length = + header_.num_program_headers * sizeof(dart::elf::ProgramHeader); + program_table_mapping_.reset( + MapFilePiece(file_start, file_length, + reinterpret_cast(&program_table_))); + CHECK_ERROR(program_table_mapping_ != nullptr, + "Could not mmap the program table."); + return true; +} + +bool LoadedElf::ReadSectionTable() { + const uword file_start = header_.section_table_offset; + const uword file_length = + header_.num_section_headers * sizeof(dart::elf::SectionHeader); + section_table_mapping_.reset( + MapFilePiece(file_start, file_length, + reinterpret_cast(§ion_table_))); + CHECK_ERROR(section_table_mapping_ != nullptr, + "Could not mmap the section table."); + return true; +} + +bool LoadedElf::ReadSectionStringTable() { + const dart::elf::SectionHeader header = + section_table_[header_.shstrtab_section_index]; + section_string_table_mapping_.reset( + MapFilePiece(header.file_offset, header.file_size, + reinterpret_cast(§ion_string_table_))); + CHECK_ERROR(section_string_table_mapping_ != nullptr, + "Could not mmap the section string table."); + return true; +} + +bool LoadedElf::LoadSegments() { + // Calculate the total amount of virtual memory needed. + uword total_memory = 0; + for (uword i = 0; i < header_.num_program_headers; ++i) { + const dart::elf::ProgramHeader header = program_table_[i]; + + // Only PT_LOAD segments need to be loaded. + if (header.type != dart::elf::ProgramHeaderType::PT_LOAD) continue; + + total_memory = Utils::Maximum( + static_cast(header.memory_offset + header.memory_size), + total_memory); + CHECK_ERROR(Utils::IsPowerOfTwo(header.alignment), + "Alignment must be a power of two."); + CHECK_ERROR(header.alignment <= PageSize(), + "Cannot align greater than page size.") + } + total_memory = Utils::RoundUp(total_memory, PageSize()); + + base_.reset(VirtualMemory::AllocateAligned( + total_memory, /*alignment=*/PageSize(), + /*is_executable=*/false, /*mapping name=*/filename_.get())); + CHECK_ERROR(base_ != nullptr, "Could not reserve virtual memory."); + + for (uword i = 0; i < header_.num_program_headers; ++i) { + const dart::elf::ProgramHeader header = program_table_[i]; + + // Only PT_LOAD segments need to be loaded. + if (header.type != dart::elf::ProgramHeaderType::PT_LOAD) continue; + + const uword memory_offset = header.memory_offset, + file_offset = header.file_offset; + CHECK_ERROR( + (memory_offset % PageSize()) == (file_offset % PageSize()), + "Difference between file and memory offset must be page-aligned."); + + const intptr_t adjustment = header.memory_offset % PageSize(); + + void* const memory_start = + static_cast(base_->address()) + memory_offset - adjustment; + const uword file_start = file_offset - adjustment; + const uword length = header.memory_size + adjustment; + + File::MapType map_type = File::kReadOnly; + if (header.flags == (dart::elf::PF_R | dart::elf::PF_W)) { + map_type = File::kReadWrite; + } else if (header.flags == (dart::elf::PF_R | dart::elf::PF_X)) { + map_type = File::kReadExecute; + } else if (header.flags == dart::elf::PF_R) { + map_type = File::kReadOnly; + } else { + ERROR("Unsupported segment flag set."); + } + + std::unique_ptr memory( + file_->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."); + } + + return true; +} + +bool LoadedElf::ReadSections() { + for (uword i = 0; i < header_.num_section_headers; ++i) { + const dart::elf::SectionHeader header = section_table_[i]; + const char* const name = section_string_table_ + header.name; + if (strcmp(name, ".dynstr") == 0) { + CHECK_ERROR(header.memory_offset != 0, ".dynstr must be loaded."); + dynamic_string_table_ = + static_cast(base_->address()) + header.memory_offset; + } else if (strcmp(name, ".dynsym") == 0) { + CHECK_ERROR(header.memory_offset != 0, ".dynsym must be loaded."); + dynamic_symbol_table_ = reinterpret_cast( + base_->start() + header.memory_offset); + dynamic_symbol_count_ = header.file_size / sizeof(dart::elf::Symbol); + } + } + + CHECK_ERROR(dynamic_string_table_ != nullptr, "Couldn't find .dynstr."); + CHECK_ERROR(dynamic_symbol_table_ != nullptr, "Couldn't find .dynsym."); + return true; +} + +bool LoadedElf::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; + } + + // The first entry of the symbol table is reserved. + for (uword i = 1; i < dynamic_symbol_count_; ++i) { + const dart::elf::Symbol sym = dynamic_symbol_table_[i]; + const char* name = dynamic_string_table_ + sym.name; + const uint8_t** output = nullptr; + + if (strcmp(name, kVmSnapshotDataSymbolName) == 0) { + output = vm_data; + } else if (strcmp(name, kVmSnapshotInstructionsSymbolName) == 0) { + output = vm_instrs; + } else if (strcmp(name, kIsolateSnapshotDataSymbolName) == 0) { + output = isolate_data; + } else if (strcmp(name, kIsolateSnapshotInstructionsSymbolName) == 0) { + output = isolate_instrs; + } + + if (output != nullptr) { + *output = reinterpret_cast(base_->start() + sym.value); + } + } + + CHECK_ERROR(vm_data == nullptr || *vm_data != nullptr, + "Could not find VM snapshot data."); + CHECK_ERROR(vm_instrs == nullptr || *vm_instrs != nullptr, + "Could not find VM snapshot instructions."); + 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* LoadedElf::MapFilePiece(uword file_start, + uword file_length, + const void** mem_start) { + const uword mapping_offset = Utils::RoundDown(file_start, PageSize()); + const uword mapping_length = + Utils::RoundUp(file_length + file_start % PageSize(), PageSize()); + MappedMemory* const mapping = + file_->Map(bin::File::kReadOnly, mapping_offset, mapping_length); + + if (mapping != nullptr) { + *mem_start = reinterpret_cast(mapping->start() + + (file_start % PageSize())); + } + + return mapping; +} + +} // namespace elf +} // namespace bin +} // namespace dart + +DART_EXPORT void* Dart_LoadELF(const char* filename, + 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 elf( + new dart::bin::elf::LoadedElf(filename)); + + if (!elf->Load() || + !elf->ResolveSymbols(vm_snapshot_data, vm_snapshot_instrs, + vm_isolate_data, vm_isolate_instrs)) { + *error = elf->error(); + return nullptr; + } + + return elf.release(); +} + +DART_EXPORT void Dart_UnloadELF(void* loaded) { + delete reinterpret_cast(loaded); +} diff --git a/runtime/bin/elf_loader.h b/runtime/bin/elf_loader.h new file mode 100644 index 00000000000..b604ee63d42 --- /dev/null +++ b/runtime/bin/elf_loader.h @@ -0,0 +1,30 @@ +// Copyright (c) 2019, 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. + +#ifndef RUNTIME_BIN_ELF_LOADER_H_ +#define RUNTIME_BIN_ELF_LOADER_H_ + +#include + +typedef void* LoadedElfLibrary; + +/// Loads an ELF object in 'filename'. +/// +/// On success, returns a handle to the library which may be used to close it +/// in Dart_UnloadELF. On error, returns 'nullptr' and sets 'error'. The error +/// string should not be 'free'-d. +/// +/// Looks up the Dart snapshot symbols "_kVmSnapshotData", +/// "_kVmSnapshotInstructions", "_kVmIsoalteData" and "_kVmIsolateInstructions" +/// into the respectively named out-parameters. +DART_EXPORT LoadedElfLibrary Dart_LoadELF(const char* filename, + 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); + +DART_EXPORT void Dart_UnloadELF(LoadedElfLibrary loaded); + +#endif // RUNTIME_BIN_ELF_LOADER_H_ diff --git a/runtime/bin/extensions_android.cc b/runtime/bin/extensions_android.cc index 1b2f36385e1..32c32a7d026 100644 --- a/runtime/bin/extensions_android.cc +++ b/runtime/bin/extensions_android.cc @@ -13,12 +13,6 @@ namespace dart { namespace bin { -const char* kVmSnapshotDataSymbolName = "_kDartVmSnapshotData"; -const char* kVmSnapshotInstructionsSymbolName = "_kDartVmSnapshotInstructions"; -const char* kIsolateSnapshotDataSymbolName = "_kDartIsolateSnapshotData"; -const char* kIsolateSnapshotInstructionsSymbolName = - "_kDartIsolateSnapshotInstructions"; - void* Extensions::LoadExtensionLibrary(const char* library_file) { return dlopen(library_file, RTLD_LAZY); } diff --git a/runtime/bin/extensions_fuchsia.cc b/runtime/bin/extensions_fuchsia.cc index 64df6b693a4..de71763bc11 100644 --- a/runtime/bin/extensions_fuchsia.cc +++ b/runtime/bin/extensions_fuchsia.cc @@ -17,12 +17,6 @@ namespace dart { namespace bin { -const char* kVmSnapshotDataSymbolName = "_kDartVmSnapshotData"; -const char* kVmSnapshotInstructionsSymbolName = "_kDartVmSnapshotInstructions"; -const char* kIsolateSnapshotDataSymbolName = "_kDartIsolateSnapshotData"; -const char* kIsolateSnapshotInstructionsSymbolName = - "_kDartIsolateSnapshotInstructions"; - void* Extensions::LoadExtensionLibrary(const char* library_file) { return dlopen(library_file, RTLD_LAZY); } diff --git a/runtime/bin/extensions_linux.cc b/runtime/bin/extensions_linux.cc index d8972a768f9..e2359d02dbc 100644 --- a/runtime/bin/extensions_linux.cc +++ b/runtime/bin/extensions_linux.cc @@ -13,12 +13,6 @@ namespace dart { namespace bin { -const char* kVmSnapshotDataSymbolName = "_kDartVmSnapshotData"; -const char* kVmSnapshotInstructionsSymbolName = "_kDartVmSnapshotInstructions"; -const char* kIsolateSnapshotDataSymbolName = "_kDartIsolateSnapshotData"; -const char* kIsolateSnapshotInstructionsSymbolName = - "_kDartIsolateSnapshotInstructions"; - void* Extensions::LoadExtensionLibrary(const char* library_file) { return dlopen(library_file, RTLD_LAZY); } diff --git a/runtime/bin/extensions_macos.cc b/runtime/bin/extensions_macos.cc index 7007f4a8f1f..f5436a0c2df 100644 --- a/runtime/bin/extensions_macos.cc +++ b/runtime/bin/extensions_macos.cc @@ -13,12 +13,6 @@ namespace dart { namespace bin { -const char* kVmSnapshotDataSymbolName = "kDartVmSnapshotData"; -const char* kVmSnapshotInstructionsSymbolName = "kDartVmSnapshotInstructions"; -const char* kIsolateSnapshotDataSymbolName = "kDartIsolateSnapshotData"; -const char* kIsolateSnapshotInstructionsSymbolName = - "kDartIsolateSnapshotInstructions"; - void* Extensions::LoadExtensionLibrary(const char* library_file) { return dlopen(library_file, RTLD_LAZY); } diff --git a/runtime/bin/extensions_win.cc b/runtime/bin/extensions_win.cc index 8baf714f5b8..d9fecd5ad22 100644 --- a/runtime/bin/extensions_win.cc +++ b/runtime/bin/extensions_win.cc @@ -13,12 +13,6 @@ namespace dart { namespace bin { -const char* kVmSnapshotDataSymbolName = "_kDartVmSnapshotData"; -const char* kVmSnapshotInstructionsSymbolName = "_kDartVmSnapshotInstructions"; -const char* kIsolateSnapshotDataSymbolName = "_kDartIsolateSnapshotData"; -const char* kIsolateSnapshotInstructionsSymbolName = - "_kDartIsolateSnapshotInstructions"; - void* Extensions::LoadExtensionLibrary(const char* library_file) { SetLastError(0); diff --git a/runtime/bin/file.h b/runtime/bin/file.h index 928fbddb28a..eacd0ca57e5 100644 --- a/runtime/bin/file.h +++ b/runtime/bin/file.h @@ -24,15 +24,23 @@ class FileHandle; class MappedMemory { public: - MappedMemory(void* address, intptr_t size) : address_(address), size_(size) {} - ~MappedMemory() { Unmap(); } + MappedMemory(void* address, intptr_t size, bool should_unmap = true) + : should_unmap_(should_unmap), address_(address), size_(size) {} + ~MappedMemory() { + if (should_unmap_) Unmap(); + } void* address() const { return address_; } intptr_t size() const { return size_; } + uword start() const { return reinterpret_cast(address()); } private: void Unmap(); + // False for mappings which reside inside another, and will be removed when + // the outer mapping is removed. + bool should_unmap_; + void* address_; intptr_t size_; @@ -100,8 +108,30 @@ class File : public ReferenceCounted { enum MapType { kReadOnly = 0, kReadExecute = 1, + kReadWrite = 2, }; - MappedMemory* Map(MapType type, int64_t position, int64_t length); + + /// Maps or copies the file into memory. + /// + /// 'position' and 'length' should be page-aligned. + /// + /// If 'start' is zero, allocates virtual memory for the mapping. When the + /// returned 'MappedMemory' is destroyed, the mapping is removed. + /// + /// If 'start' is non-zero, it must point within a suitably sized existing + /// mapping. The returned 'MappedMemory' will not remove the mapping when it + /// is destroyed; rather, the mapping will be removed when the enclosing + /// mapping is removed. This mode is not supported on Fuchsia. + /// + /// If 'type' is 'kReadWrite', writes to the mapping are *not* copied back to + /// the file. + /// + /// 'position' + 'length' may be larger than the file size. In this case, the + /// extra memory is zero-filled. + MappedMemory* Map(MapType type, + int64_t position, + int64_t length, + void* start = nullptr); // Read/Write attempt to transfer num_bytes to/from buffer. It returns // the number of bytes read/written. @@ -172,7 +202,7 @@ class File : public ReferenceCounted { // reading. If mode contains kWrite the file is opened for both // reading and writing. If mode contains kWrite and the file does // not exist the file is created. The file is truncated to length 0 if - // mode contains kTruncate. Assumes we are in an API scope. + // mode contains kTruncate. static File* Open(Namespace* namespc, const char* path, FileOpenMode mode); // Same as [File::Open], but attempts to convert uri to path before opening diff --git a/runtime/bin/file_android.cc b/runtime/bin/file_android.cc index c634b0d83e4..6fa50ada7fd 100644 --- a/runtime/bin/file_android.cc +++ b/runtime/bin/file_android.cc @@ -76,7 +76,10 @@ bool File::IsClosed() { return handle_->fd() == kClosedFd; } -MappedMemory* File::Map(MapType type, int64_t position, int64_t length) { +MappedMemory* File::Map(MapType type, + int64_t position, + int64_t length, + void* start) { ASSERT(handle_->fd() >= 0); ASSERT(length > 0); int prot = PROT_NONE; @@ -87,14 +90,16 @@ MappedMemory* File::Map(MapType type, int64_t position, int64_t length) { case kReadExecute: prot = PROT_READ | PROT_EXEC; break; - default: - return NULL; + case kReadWrite: + prot = PROT_READ | PROT_WRITE; + break; } - void* addr = mmap(NULL, length, prot, MAP_PRIVATE, handle_->fd(), position); + const int flags = MAP_PRIVATE | (start != nullptr ? MAP_FIXED : 0); + void* addr = mmap(start, length, prot, flags, handle_->fd(), position); if (addr == MAP_FAILED) { return NULL; } - return new MappedMemory(addr, length); + return new MappedMemory(addr, length, /*should_unmap=*/start == nullptr); } void MappedMemory::Unmap() { diff --git a/runtime/bin/file_fuchsia.cc b/runtime/bin/file_fuchsia.cc index 74e3c50c039..64cc848c4c1 100644 --- a/runtime/bin/file_fuchsia.cc +++ b/runtime/bin/file_fuchsia.cc @@ -75,7 +75,10 @@ bool File::IsClosed() { return handle_->fd() == kClosedFd; } -MappedMemory* File::Map(MapType type, int64_t position, int64_t length) { +MappedMemory* File::Map(MapType type, + int64_t position, + int64_t length, + void* start) { ASSERT(handle_->fd() >= 0); ASSERT(length > 0); int prot = PROT_NONE; @@ -86,14 +89,16 @@ MappedMemory* File::Map(MapType type, int64_t position, int64_t length) { case kReadExecute: prot = PROT_READ | PROT_EXEC; break; - default: - return NULL; + case kReadWrite: + prot = PROT_READ | PROT_WRITE; + break; } - void* addr = mmap(NULL, length, prot, MAP_PRIVATE, handle_->fd(), position); + const int flags = MAP_PRIVATE | (start != nullptr ? MAP_FIXED : 0); + void* addr = mmap(start, length, prot, flags, handle_->fd(), position); if (addr == MAP_FAILED) { return NULL; } - return new MappedMemory(addr, length); + return new MappedMemory(addr, length, /*should_unmap=*/start == nullptr); } void MappedMemory::Unmap() { diff --git a/runtime/bin/file_linux.cc b/runtime/bin/file_linux.cc index e6db3ade163..190b195a754 100644 --- a/runtime/bin/file_linux.cc +++ b/runtime/bin/file_linux.cc @@ -75,7 +75,10 @@ bool File::IsClosed() { return handle_->fd() == kClosedFd; } -MappedMemory* File::Map(MapType type, int64_t position, int64_t length) { +MappedMemory* File::Map(MapType type, + int64_t position, + int64_t length, + void* start) { ASSERT(handle_->fd() >= 0); ASSERT(length > 0); int prot = PROT_NONE; @@ -86,14 +89,16 @@ MappedMemory* File::Map(MapType type, int64_t position, int64_t length) { case kReadExecute: prot = PROT_READ | PROT_EXEC; break; - default: - return NULL; + case kReadWrite: + prot = PROT_READ | PROT_WRITE; + break; } - void* addr = mmap(NULL, length, prot, MAP_PRIVATE, handle_->fd(), position); + const int flags = MAP_PRIVATE | (start != nullptr ? MAP_FIXED : 0); + void* addr = mmap(start, length, prot, flags, handle_->fd(), position); if (addr == MAP_FAILED) { return NULL; } - return new MappedMemory(addr, length); + return new MappedMemory(addr, length, /*should_unmap=*/start == nullptr); } void MappedMemory::Unmap() { diff --git a/runtime/bin/file_macos.cc b/runtime/bin/file_macos.cc index 7315cfe3a18..2e132827c2d 100644 --- a/runtime/bin/file_macos.cc +++ b/runtime/bin/file_macos.cc @@ -76,7 +76,10 @@ bool File::IsClosed() { return handle_->fd() == kClosedFd; } -MappedMemory* File::Map(MapType type, int64_t position, int64_t length) { +MappedMemory* File::Map(MapType type, + int64_t position, + int64_t length, + void* start) { ASSERT(handle_->fd() >= 0); ASSERT(length > 0); int prot = PROT_NONE; @@ -91,34 +94,58 @@ MappedMemory* File::Map(MapType type, int64_t position, int64_t length) { map_flags |= (MAP_JIT | MAP_ANONYMOUS); } break; - default: - return NULL; + case kReadWrite: + prot = PROT_READ | PROT_WRITE; + break; } - void* addr = NULL; + if (start != nullptr) { + map_flags |= MAP_FIXED; + } + void* addr = start; if ((type == kReadExecute) && IsAtLeastOS10_14()) { - addr = mmap(NULL, length, (PROT_READ | PROT_WRITE), map_flags, -1, 0); - if (addr == MAP_FAILED) { - Syslog::PrintErr("mmap failed %s\n", strerror(errno)); - return NULL; + // Due to codesigning restrictions, we cannot map the file as executable + // directly. We must first copy it into an anonymous mapping and then mark + // the mapping as executable. + if (addr == nullptr) { + addr = mmap(nullptr, length, (PROT_READ | PROT_WRITE), map_flags, -1, 0); + if (addr == MAP_FAILED) { + Syslog::PrintErr("mmap failed %s\n", strerror(errno)); + return nullptr; + } } + + const int64_t remaining_length = Length() - position; SetPosition(position); - if (!ReadFully(addr, length)) { + if (!ReadFully(addr, Utils::Minimum(length, remaining_length))) { Syslog::PrintErr("ReadFully failed\n"); - munmap(addr, length); - return NULL; + if (start == nullptr) { + munmap(addr, length); + } + return nullptr; } + + // If the requested mapping is larger than the file size, we should fill the + // extra memory with zeros. + if (length > remaining_length) { + memset(reinterpret_cast(addr) + remaining_length, 0, + length - remaining_length); + } + if (mprotect(addr, length, prot) != 0) { Syslog::PrintErr("mprotect failed %s\n", strerror(errno)); - munmap(addr, length); - return NULL; + if (start == nullptr) { + munmap(addr, length); + } + return nullptr; } } else { - addr = mmap(NULL, length, prot, map_flags, handle_->fd(), position); + addr = mmap(addr, length, prot, map_flags, handle_->fd(), position); + if (addr == MAP_FAILED) { + Syslog::PrintErr("mmap failed %s\n", strerror(errno)); + return nullptr; + } } - if (addr == MAP_FAILED) { - return NULL; - } - return new MappedMemory(addr, length); + return new MappedMemory(addr, length, /*should_unmap=*/start == nullptr); } void MappedMemory::Unmap() { diff --git a/runtime/bin/file_win.cc b/runtime/bin/file_win.cc index d0011beb2d7..f5c6010f9cd 100644 --- a/runtime/bin/file_win.cc +++ b/runtime/bin/file_win.cc @@ -73,7 +73,10 @@ bool File::IsClosed() { return handle_->fd() == kClosedFd; } -MappedMemory* File::Map(File::MapType type, int64_t position, int64_t length) { +MappedMemory* File::Map(File::MapType type, + int64_t position, + int64_t length, + void* start) { DWORD prot_alloc; DWORD prot_final; switch (type) { @@ -85,31 +88,48 @@ MappedMemory* File::Map(File::MapType type, int64_t position, int64_t length) { prot_alloc = PAGE_EXECUTE_READWRITE; prot_final = PAGE_EXECUTE_READ; break; - default: - return NULL; + case File::kReadWrite: + prot_alloc = PAGE_READWRITE; + prot_final = PAGE_READWRITE; + break; } - void* addr = VirtualAlloc(NULL, length, MEM_COMMIT | MEM_RESERVE, prot_alloc); - if (addr == NULL) { - Syslog::PrintErr("VirtualAlloc failed %d\n", GetLastError()); - return NULL; + void* addr = start; + if (addr == nullptr) { + addr = VirtualAlloc(nullptr, length, MEM_COMMIT | MEM_RESERVE, prot_alloc); + if (addr == nullptr) { + Syslog::PrintErr("VirtualAlloc failed %d\n", GetLastError()); + return nullptr; + } } + const int64_t remaining_length = Length() - position; SetPosition(position); - if (!ReadFully(addr, length)) { + if (!ReadFully(addr, Utils::Minimum(length, remaining_length))) { Syslog::PrintErr("ReadFully failed %d\n", GetLastError()); - VirtualFree(addr, 0, MEM_RELEASE); - return NULL; + if (start == nullptr) { + VirtualFree(addr, 0, MEM_RELEASE); + } + return nullptr; + } + + // If the requested mapping is larger than the file size, we should fill the + // extra memory with zeros. + if (length > remaining_length) { + memset(reinterpret_cast(addr) + remaining_length, 0, + length - remaining_length); } DWORD old_prot; bool result = VirtualProtect(addr, length, prot_final, &old_prot); if (!result) { Syslog::PrintErr("VirtualProtect failed %d\n", GetLastError()); - VirtualFree(addr, 0, MEM_RELEASE); - return NULL; + if (start == nullptr) { + VirtualFree(addr, 0, MEM_RELEASE); + } + return nullptr; } - return new MappedMemory(addr, length); + return new MappedMemory(addr, length, /*should_unmap=*/start == nullptr); } void MappedMemory::Unmap() { diff --git a/runtime/bin/snapshot_utils.cc b/runtime/bin/snapshot_utils.cc index b550741f995..aceba6c9aa1 100644 --- a/runtime/bin/snapshot_utils.cc +++ b/runtime/bin/snapshot_utils.cc @@ -8,6 +8,7 @@ #include "bin/dartutils.h" #include "bin/dfe.h" +#include "bin/elf_loader.h" #include "bin/error_exit.h" #include "bin/extensions.h" #include "bin/file.h" @@ -20,11 +21,6 @@ namespace dart { namespace bin { -extern const char* kVmSnapshotDataSymbolName; -extern const char* kVmSnapshotInstructionsSymbolName; -extern const char* kIsolateSnapshotDataSymbolName; -extern const char* kIsolateSnapshotInstructionsSymbolName; - static const int64_t kAppSnapshotHeaderSize = 5 * kInt64Size; static const int64_t kAppSnapshotPageSize = 4 * KB; @@ -192,6 +188,42 @@ AppSnapshot* Snapshot::TryReadAppendedAppSnapshotBlobs( } #if defined(DART_PRECOMPILED_RUNTIME) + +#if defined(TARGET_OS_WINDOWS) || defined(USING_SIMULATOR) +class ElfAppSnapshot : public AppSnapshot { + public: + ElfAppSnapshot(LoadedElfLibrary 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) + : 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: + LoadedElfLibrary 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_; +}; +#endif // defined(TARGET_OS_WINDOWS) || defined(USING_SIMULATOR) + class DylibAppSnapshot : public AppSnapshot { public: DylibAppSnapshot(void* library, @@ -261,17 +293,37 @@ static AppSnapshot* TryReadAppSnapshotDynamicLibrary(const char* script_name) { return new DylibAppSnapshot(library, vm_data_buffer, vm_instructions_buffer, isolate_data_buffer, isolate_instructions_buffer); } + +static AppSnapshot* TryReadAppSnapshotElf(const char* script_name) { +#if defined(TARGET_OS_WINDOWS) || defined(USING_SIMULATOR) + const char* error = nullptr; + const uint8_t *vm_data_buffer = nullptr, *vm_instructions_buffer = nullptr, + *isolate_data_buffer = nullptr, + *isolate_instructions_buffer = nullptr; + void* handle = Dart_LoadELF(script_name, &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); +#else + return nullptr; +#endif +} #endif // defined(DART_PRECOMPILED_RUNTIME) AppSnapshot* Snapshot::TryReadAppSnapshot(const char* script_name) { - if (File::GetType(NULL, script_name, true) != File::kIsFile) { + 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 NULL; + return nullptr; } AppSnapshot* snapshot = TryReadAppSnapshotBlobs(script_name); - if (snapshot != NULL) { + if (snapshot != nullptr) { return snapshot; } #if defined(DART_PRECOMPILED_RUNTIME) @@ -287,12 +339,16 @@ AppSnapshot* Snapshot::TryReadAppSnapshot(const char* script_name) { #endif snapshot = TryReadAppSnapshotDynamicLibrary(script_name); + if (snapshot != nullptr) { + return snapshot; + } - if (snapshot != NULL) { + snapshot = TryReadAppSnapshotElf(script_name); + if (snapshot != nullptr) { return snapshot; } #endif // defined(DART_PRECOMPILED_RUNTIME) - return NULL; + return nullptr; } #if !defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) && !defined(TESTING) diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h index 3d060bef867..cf831e42981 100644 --- a/runtime/include/dart_api.h +++ b/runtime/include/dart_api.h @@ -3283,8 +3283,7 @@ DART_EXPORT Dart_Port Dart_ServiceWaitForLoadPort(); * \return Returns true if the profile is successfully written and false * otherwise. */ -DART_EXPORT bool Dart_WriteProfileToTimeline(Dart_Port main_port, - char** error); +DART_EXPORT bool Dart_WriteProfileToTimeline(Dart_Port main_port, char** error); /* * ==================== @@ -3360,16 +3359,30 @@ typedef void (*Dart_StreamingWriteCallback)(void* callback_data, const uint8_t* buffer, intptr_t size); +#if defined(__APPLE__) +#define kVmSnapshotDataSymbolName "kDartVmSnapshotData" +#define kVmSnapshotInstructionsSymbolName "kDartVmSnapshotInstructions" +#define kIsolateSnapshotDataSymbolName "kDartIsolateSnapshotData" +#define kIsolateSnapshotInstructionsSymbolName \ + "kDartIsolateSnapshotInstructions" +#else +#define kVmSnapshotDataSymbolName "_kDartVmSnapshotData" +#define kVmSnapshotInstructionsSymbolName "_kDartVmSnapshotInstructions" +#define kIsolateSnapshotDataSymbolName "_kDartIsolateSnapshotData" +#define kIsolateSnapshotInstructionsSymbolName \ + "_kDartIsolateSnapshotInstructions" +#endif + /** * Creates a precompiled snapshot. * - A root library must have been loaded. * - Dart_Precompile must have been called. * * Outputs an assembly file defining the symbols - * - kDartVmSnapshotData - * - kDartVmSnapshotInstructions - * - kDartIsolateSnapshotData - * - kDartIsolateSnapshotInstructions + * - _kDartVmSnapshotData + * - _kDartVmSnapshotInstructions + * - _kDartIsolateSnapshotData + * - _kDartIsolateSnapshotInstructions * * The assembly should be compiled as a static or shared library and linked or * loaded by the embedder. @@ -3392,10 +3405,10 @@ Dart_CreateAppAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback, * - Dart_Precompile must have been called. * * Outputs an ELF shared library defining the symbols - * - kDartVmSnapshotData - * - kDartVmSnapshotInstructions - * - kDartIsolateSnapshotData - * - kDartIsolateSnapshotInstructions + * - _kDartVmSnapshotData + * - _kDartVmSnapshotInstructions + * - _kDartIsolateSnapshotData + * - _kDartIsolateSnapshotInstructions * * The shared library should be dynamically loaded by the embedder. * Running this snapshot requires a VM compiled with DART_PRECOMPILED_SNAPSHOT. diff --git a/runtime/platform/elf.h b/runtime/platform/elf.h new file mode 100644 index 00000000000..729ac7dcf56 --- /dev/null +++ b/runtime/platform/elf.h @@ -0,0 +1,170 @@ +// Copyright (c) 2019, 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. + +#ifndef RUNTIME_PLATFORM_ELF_H_ +#define RUNTIME_PLATFORM_ELF_H_ + +#include "platform/globals.h" + +namespace dart { +namespace elf { + +#pragma pack(push, 1) + +struct ElfHeader { + uint8_t ident[16]; + uint16_t type; + uint16_t machine; + uint32_t version; +#if defined(TARGET_ARCH_IS_32_BIT) + uint32_t entry_point; + uint32_t program_table_offset; + uint32_t section_table_offset; +#else + uint64_t entry_point; + uint64_t program_table_offset; + uint64_t section_table_offset; +#endif + uint32_t flags; + uint16_t header_size; + uint16_t program_table_entry_size; + uint16_t num_program_headers; + uint16_t section_table_entry_size; + uint16_t num_section_headers; + uint16_t shstrtab_section_index; +}; + +enum class ProgramHeaderType : uint32_t { + PT_LOAD = 1, + PT_DYNAMIC = 2, + PT_PHDR = 6, +}; + +struct ProgramHeader { +#if defined(TARGET_ARCH_IS_32_BIT) + ProgramHeaderType type; + uint32_t file_offset; + uint32_t memory_offset; + uint32_t physical_memory_offset; + uint32_t file_size; + uint32_t memory_size; + uint32_t flags; + uint32_t alignment; +#else + ProgramHeaderType type; + uint32_t flags; + uint64_t file_offset; + uint64_t memory_offset; + uint64_t physical_memory_offset; + uint64_t file_size; + uint64_t memory_size; + uint64_t alignment; +#endif +}; + +struct SectionHeader { +#if defined(TARGET_ARCH_IS_32_BIT) + uint32_t name; + uint32_t type; + uint32_t flags; + uint32_t memory_offset; + uint32_t file_offset; + uint32_t file_size; + uint32_t link; + uint32_t info; + uint32_t alignment; + uint32_t entry_size; +#else + uint32_t name; + uint32_t type; + uint64_t flags; + uint64_t memory_offset; + uint64_t file_offset; + uint64_t file_size; + uint32_t link; + uint32_t info; + uint64_t alignment; + uint64_t entry_size; +#endif +}; + +struct Symbol { +#if defined(TARGET_ARCH_IS_32_BIT) + uint32_t name; + uint32_t value; + uint32_t size; + uint8_t info; + uint8_t other; // Reserved by ELF. + uint16_t section; +#else + uint32_t name; + uint8_t info; + uint8_t other; // Reserved by ELF. + uint16_t section; + uint64_t value; + uint64_t size; +#endif +}; + +#pragma pack(pop) + +static constexpr intptr_t ELFCLASS32 = 1; +static constexpr intptr_t ELFCLASS64 = 2; + +static const intptr_t EI_DATA = 5; +static const intptr_t ELFDATA2LSB = 1; + +static const intptr_t ELFOSABI_SYSV = 0; + +static const intptr_t ET_DYN = 3; + +static constexpr intptr_t EF_ARM_ABI_FLOAT_HARD = 0x00000400; +static constexpr intptr_t EF_ARM_ABI_FLOAT_SOFT = 0x00000200; +static constexpr intptr_t EF_ARM_ABI = 0x05000000; + +static constexpr intptr_t EM_386 = 3; +static constexpr intptr_t EM_ARM = 40; +static constexpr intptr_t EM_X86_64 = 62; +static constexpr intptr_t EM_AARCH64 = 183; + +static const intptr_t EV_CURRENT = 1; + +static const intptr_t PF_X = 1; +static const intptr_t PF_W = 2; +static const intptr_t PF_R = 4; + +static const intptr_t SHT_PROGBITS = 1; +static const intptr_t SHT_STRTAB = 3; +static const intptr_t SHT_HASH = 5; +static const intptr_t SHT_DYNSYM = 11; +static const intptr_t SHT_DYNAMIC = 6; + +static const intptr_t SHF_WRITE = 0x1; +static const intptr_t SHF_ALLOC = 0x2; +static const intptr_t SHF_EXECINSTR = 0x4; + +static const intptr_t SHN_UNDEF = 0; + +static const intptr_t STN_UNDEF = 0; + +static const intptr_t PT_LOAD = 1; +static const intptr_t PT_DYNAMIC = 2; +static const intptr_t PT_PHDR = 6; + +static const intptr_t STB_GLOBAL = 1; + +static const intptr_t STT_OBJECT = 1; // I.e., data. +static const intptr_t STT_FUNC = 2; + +static const intptr_t DT_NULL = 0; +static const intptr_t DT_HASH = 4; +static const intptr_t DT_STRTAB = 5; +static const intptr_t DT_SYMTAB = 6; +static const intptr_t DT_STRSZ = 10; +static const intptr_t DT_SYMENT = 11; + +} // namespace elf +} // namespace dart + +#endif // RUNTIME_PLATFORM_ELF_H_ diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc index 4c62313318d..bb04dd6dcac 100644 --- a/runtime/vm/dart_api_impl.cc +++ b/runtime/vm/dart_api_impl.cc @@ -6182,10 +6182,6 @@ Dart_CreateAppAOTSnapshotAsElf(Dart_StreamingWriteCallback callback, return Api::NewError("AOT compilation is not supported on IA32."); #elif defined(TARGET_ARCH_DBC) return Api::NewError("AOT compilation is not supported on DBC."); -#elif defined(TARGET_OS_WINDOWS) - return Api::NewError("Windows cannot load ELF."); -#elif defined(TARGET_OS_MACOS) - return Api::NewError("macOS/iOS cannot load ELF."); #elif !defined(DART_PRECOMPILER) return Api::NewError( "This VM was built without support for AOT compilation."); diff --git a/runtime/vm/elf.cc b/runtime/vm/elf.cc index 63060295ed8..b4778d0fbc2 100644 --- a/runtime/vm/elf.cc +++ b/runtime/vm/elf.cc @@ -4,66 +4,13 @@ #include "vm/elf.h" +#include "platform/elf.h" #include "platform/text_buffer.h" #include "vm/cpu.h" #include "vm/thread.h" namespace dart { -#define ELFCLASS32 1 -#define ELFCLASS64 2 - -static const intptr_t ELFDATA2LSB = 1; - -static const intptr_t ELFOSABI_SYSV = 0; - -#define EF_ARM_ABI_FLOAT_HARD 0x00000400 -#define EF_ARM_ABI_FLOAT_SOFT 0x00000200 -#define EF_ARM_ABI 0x05000000 - -static const intptr_t ET_DYN = 3; - -#define EM_386 3 -#define EM_ARM 40 -#define EM_X86_64 62 -#define EM_AARCH64 183 - -static const intptr_t EV_CURRENT = 1; - -static const intptr_t SHT_PROGBITS = 1; -static const intptr_t SHT_STRTAB = 3; -static const intptr_t SHT_HASH = 5; -static const intptr_t SHT_DYNSYM = 11; -static const intptr_t SHT_DYNAMIC = 6; - -static const intptr_t SHF_WRITE = 0x1; -static const intptr_t SHF_ALLOC = 0x2; -static const intptr_t SHF_EXECINSTR = 0x4; - -static const intptr_t SHN_UNDEF = 0; - -static const intptr_t STN_UNDEF = 0; - -static const intptr_t PT_LOAD = 1; -static const intptr_t PT_DYNAMIC = 2; -static const intptr_t PT_PHDR = 6; - -static const intptr_t PF_X = 1; -static const intptr_t PF_W = 2; -static const intptr_t PF_R = 4; - -static const intptr_t STB_GLOBAL = 1; - -static const intptr_t STT_OBJECT = 1; // I.e., data. -static const intptr_t STT_FUNC = 2; - -static const intptr_t DT_NULL = 0; -static const intptr_t DT_HASH = 4; -static const intptr_t DT_STRTAB = 5; -static const intptr_t DT_SYMTAB = 6; -static const intptr_t DT_STRSZ = 10; -static const intptr_t DT_SYMENT = 11; - #if defined(TARGET_ARCH_IS_32_BIT) static const intptr_t kElfHeaderSize = 52; static const intptr_t kElfSectionTableAlignment = 4; @@ -94,7 +41,7 @@ class Section : public ZoneAllocated { intptr_t section_type = 0; intptr_t section_flags = 0; intptr_t section_index = -1; - intptr_t section_link = SHN_UNDEF; + intptr_t section_link = elf::SHN_UNDEF; intptr_t section_info = 0; intptr_t section_entry_size = 0; intptr_t file_size = 0; @@ -119,16 +66,16 @@ class ProgramBits : public Section { intptr_t memsz = -1) { if (memsz == -1) memsz = filesz; - section_type = SHT_PROGBITS; + section_type = elf::SHT_PROGBITS; if (allocate) { - section_flags = SHF_ALLOC; - if (executable) section_flags |= SHF_EXECINSTR; - if (writable) section_flags |= SHF_WRITE; + section_flags = elf::SHF_ALLOC; + if (executable) section_flags |= elf::SHF_EXECINSTR; + if (writable) section_flags |= elf::SHF_WRITE; - segment_type = PT_LOAD; - segment_flags = PF_R; - if (executable) segment_flags |= PF_X; - if (writable) segment_flags |= PF_W; + segment_type = elf::PT_LOAD; + segment_flags = elf::PF_R; + if (executable) segment_flags |= elf::PF_X; + if (writable) segment_flags |= elf::PF_W; } bytes_ = bytes; @@ -148,10 +95,10 @@ class ProgramBits : public Section { class StringTable : public Section { public: explicit StringTable(bool allocate) : text_(128) { - section_type = SHT_STRTAB; - section_flags = allocate ? SHF_ALLOC : 0; - segment_type = PT_LOAD; - segment_flags = PF_R; + section_type = elf::SHT_STRTAB; + section_flags = allocate ? elf::SHF_ALLOC : 0; + segment_type = elf::PT_LOAD; + segment_flags = elf::PF_R; text_.AddChar('\0'); memory_size = file_size = text_.length(); @@ -186,10 +133,10 @@ class Symbol : public ZoneAllocated { class SymbolTable : public Section { public: SymbolTable() { - section_type = SHT_DYNSYM; - section_flags = SHF_ALLOC; - segment_type = PT_LOAD; - segment_flags = PF_R; + section_type = elf::SHT_DYNSYM; + section_flags = elf::SHF_ALLOC; + segment_type = elf::PT_LOAD; + segment_flags = elf::PF_R; section_entry_size = kElfSymbolTableEntrySize; AddSymbol(NULL); @@ -268,24 +215,24 @@ static uint32_t ElfHash(const unsigned char* name) { class SymbolHashTable : public Section { public: SymbolHashTable(StringTable* strtab, SymbolTable* symtab) { - section_type = SHT_HASH; - section_flags = SHF_ALLOC; + section_type = elf::SHT_HASH; + section_flags = elf::SHF_ALLOC; section_link = symtab->section_index; section_entry_size = kElfSymbolHashTableEntrySize; - segment_type = PT_LOAD; - segment_flags = PF_R; + segment_type = elf::PT_LOAD; + segment_flags = elf::PF_R; nchain_ = symtab->length(); nbucket_ = symtab->length(); bucket_ = Thread::Current()->zone()->Alloc(nbucket_); for (intptr_t i = 0; i < nbucket_; i++) { - bucket_[i] = STN_UNDEF; + bucket_[i] = elf::STN_UNDEF; } chain_ = Thread::Current()->zone()->Alloc(nchain_); for (intptr_t i = 0; i < nchain_; i++) { - chain_[i] = STN_UNDEF; + chain_[i] = elf::STN_UNDEF; } for (intptr_t i = 1; i < symtab->length(); i++) { @@ -322,20 +269,20 @@ class DynamicTable : public Section { DynamicTable(StringTable* strtab, SymbolTable* symtab, SymbolHashTable* hash) { - section_type = SHT_DYNAMIC; + section_type = elf::SHT_DYNAMIC; section_link = strtab->section_index; - section_flags = SHF_ALLOC | SHF_WRITE; + section_flags = elf::SHF_ALLOC | elf::SHF_WRITE; section_entry_size = kElfDynamicTableEntrySize; - segment_type = PT_LOAD; - segment_flags = PF_R | PF_W; + segment_type = elf::PT_LOAD; + segment_flags = elf::PF_R | elf::PF_W; - AddEntry(DT_HASH, hash->memory_offset); - AddEntry(DT_STRTAB, strtab->memory_offset); - AddEntry(DT_STRSZ, strtab->memory_size); - AddEntry(DT_SYMTAB, symtab->memory_offset); - AddEntry(DT_SYMENT, kElfSymbolTableEntrySize); - AddEntry(DT_NULL, 0); + AddEntry(elf::DT_HASH, hash->memory_offset); + AddEntry(elf::DT_STRTAB, strtab->memory_offset); + AddEntry(elf::DT_STRSZ, strtab->memory_size); + AddEntry(elf::DT_SYMTAB, symtab->memory_offset); + AddEntry(elf::DT_SYMENT, kElfSymbolTableEntrySize); + AddEntry(elf::DT_NULL, 0); } void Write(Elf* stream) { @@ -432,7 +379,7 @@ intptr_t Elf::AddText(const char* name, const uint8_t* bytes, intptr_t size) { Symbol* symbol = new (zone_) Symbol(); symbol->cstr = name; symbol->name = symstrtab_->AddString(name); - symbol->info = (STB_GLOBAL << 4) | STT_FUNC; + symbol->info = (elf::STB_GLOBAL << 4) | elf::STT_FUNC; symbol->section = image->section_index; // For shared libraries, this is the offset from the DSO base. For static // libraries, this is section relative. @@ -453,7 +400,7 @@ intptr_t Elf::AddBSSData(const char* name, intptr_t size) { Symbol* symbol = new (zone_) Symbol(); symbol->cstr = name; symbol->name = symstrtab_->AddString(name); - symbol->info = (STB_GLOBAL << 4) | STT_OBJECT; + symbol->info = (elf::STB_GLOBAL << 4) | elf::STT_OBJECT; symbol->section = image->section_index; // For shared libraries, this is the offset from the DSO base. For static // libraries, this is section relative. @@ -473,7 +420,7 @@ intptr_t Elf::AddROData(const char* name, const uint8_t* bytes, intptr_t size) { Symbol* symbol = new (zone_) Symbol(); symbol->cstr = name; symbol->name = symstrtab_->AddString(name); - symbol->info = (STB_GLOBAL << 4) | STT_OBJECT; + symbol->info = (elf::STB_GLOBAL << 4) | elf::STT_OBJECT; symbol->section = image->section_index; // For shared libraries, this is the offset from the DSO base. For static // libraries, this is section relative. @@ -550,39 +497,52 @@ void Elf::ComputeFileOffsets() { void Elf::WriteHeader() { #if defined(TARGET_ARCH_IS_32_BIT) - uint8_t size = ELFCLASS32; + uint8_t size = elf::ELFCLASS32; #else - uint8_t size = ELFCLASS64; + uint8_t size = elf::ELFCLASS64; #endif - uint8_t e_ident[16] = { - 0x7f, 'E', 'L', 'F', size, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV, - 0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t e_ident[16] = {0x7f, + 'E', + 'L', + 'F', + size, + elf::ELFDATA2LSB, + elf::EV_CURRENT, + elf::ELFOSABI_SYSV, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0}; stream_->WriteBytes(e_ident, 16); - WriteHalf(ET_DYN); // Shared library. + WriteHalf(elf::ET_DYN); // Shared library. #if defined(TARGET_ARCH_IA32) - WriteHalf(EM_386); + WriteHalf(elf::EM_386); #elif defined(TARGET_ARCH_X64) - WriteHalf(EM_X86_64); + WriteHalf(elf::EM_X86_64); #elif defined(TARGET_ARCH_ARM) - WriteHalf(EM_ARM); + WriteHalf(elf::EM_ARM); #elif defined(TARGET_ARCH_ARM64) - WriteHalf(EM_AARCH64); + WriteHalf(elf::EM_AARCH64); #else // E.g., DBC. FATAL("Unknown ELF architecture"); #endif - WriteWord(EV_CURRENT); // Version + WriteWord(elf::EV_CURRENT); // Version WriteAddr(0); // "Entry point" WriteOff(program_table_file_offset_); WriteOff(section_table_file_offset_); #if defined(TARGET_ARCH_ARM) - uword flags = EF_ARM_ABI | - (TargetCPUFeatures::hardfp_supported() ? EF_ARM_ABI_FLOAT_HARD - : EF_ARM_ABI_FLOAT_SOFT); + uword flags = elf::EF_ARM_ABI | (TargetCPUFeatures::hardfp_supported() + ? elf::EF_ARM_ABI_FLOAT_HARD + : elf::EF_ARM_ABI_FLOAT_SOFT); #else uword flags = 0; #endif @@ -607,17 +567,17 @@ void Elf::WriteProgramTable() { ASSERT(kNumImplicitSegments == 3); const intptr_t start = stream_->position(); #if defined(TARGET_ARCH_IS_32_BIT) - WriteWord(PT_PHDR); + WriteWord(elf::PT_PHDR); WriteOff(program_table_file_offset_); // File offset. WriteAddr(program_table_file_offset_); // Virtual address. WriteAddr(program_table_file_offset_); // Physical address, not used. WriteWord(program_table_file_size_); WriteWord(program_table_file_size_); - WriteWord(PF_R); + WriteWord(elf::PF_R); WriteWord(kPageSize); #else - WriteWord(PT_PHDR); - WriteWord(PF_R); + WriteWord(elf::PT_PHDR); + WriteWord(elf::PF_R); WriteOff(program_table_file_offset_); // File offset. WriteAddr(program_table_file_offset_); // Virtual address. WriteAddr(program_table_file_offset_); // Physical address, not used. @@ -642,17 +602,17 @@ void Elf::WriteProgramTable() { ASSERT(kNumImplicitSegments == 3); const intptr_t start = stream_->position(); #if defined(TARGET_ARCH_IS_32_BIT) - WriteWord(PT_LOAD); + WriteWord(elf::PT_LOAD); WriteOff(0); // File offset. WriteAddr(0); // Virtual address. WriteAddr(0); // Physical address, not used. WriteWord(program_table_file_offset_ + program_table_file_size_); WriteWord(program_table_file_offset_ + program_table_file_size_); - WriteWord(PF_R); + WriteWord(elf::PF_R); WriteWord(kPageSize); #else - WriteWord(PT_LOAD); - WriteWord(PF_R); + WriteWord(elf::PT_LOAD); + WriteWord(elf::PF_R); WriteOff(0); // File offset. WriteAddr(0); // Virtual address. WriteAddr(0); // Physical address, not used. @@ -696,7 +656,7 @@ void Elf::WriteProgramTable() { ASSERT(kNumImplicitSegments == 3); const intptr_t start = stream_->position(); #if defined(TARGET_ARCH_IS_32_BIT) - WriteWord(PT_DYNAMIC); + WriteWord(elf::PT_DYNAMIC); WriteOff(dynamic_->file_offset); WriteAddr(dynamic_->memory_offset); // Virtual address. WriteAddr(dynamic_->memory_offset); // Physical address, not used. @@ -705,7 +665,7 @@ void Elf::WriteProgramTable() { WriteWord(dynamic_->segment_flags); WriteWord(dynamic_->alignment); #else - WriteWord(PT_DYNAMIC); + WriteWord(elf::PT_DYNAMIC); WriteWord(dynamic_->segment_flags); WriteOff(dynamic_->file_offset); WriteAddr(dynamic_->memory_offset); // Virtual address. diff --git a/runtime/vm/virtual_memory.h b/runtime/vm/virtual_memory.h index 2eb43728081..42142157441 100644 --- a/runtime/vm/virtual_memory.h +++ b/runtime/vm/virtual_memory.h @@ -57,12 +57,15 @@ class VirtualMemory { bool is_executable, const char* name); + // Returns the cached page size. Use only if Init() has been called. static intptr_t PageSize() { ASSERT(page_size_ != 0); - ASSERT(Utils::IsPowerOfTwo(page_size_)); return page_size_; } + // Use only if Init() might not have been called. + static intptr_t CalculatePageSize(); + static bool InSamePage(uword address0, uword address1); // Truncate this virtual memory segment. diff --git a/runtime/vm/virtual_memory_fuchsia.cc b/runtime/vm/virtual_memory_fuchsia.cc index 09e946dff36..4f2aa7845d2 100644 --- a/runtime/vm/virtual_memory_fuchsia.cc +++ b/runtime/vm/virtual_memory_fuchsia.cc @@ -40,8 +40,15 @@ DECLARE_FLAG(bool, write_protect_code); uword VirtualMemory::page_size_ = 0; +intptr_t VirtualMemory::CalculatePageSize() { + const intptr_t page_size = getpagesize(); + ASSERT(page_size != 0); + ASSERT(Utils::IsPowerOfTwo(page_size)); + return page_size; +} + void VirtualMemory::Init() { - page_size_ = getpagesize(); + page_size_ = CalculatePageSize(); } static void Unmap(zx_handle_t vmar, uword start, uword end) { diff --git a/runtime/vm/virtual_memory_posix.cc b/runtime/vm/virtual_memory_posix.cc index 87cbc0405a7..5c4ad94028d 100644 --- a/runtime/vm/virtual_memory_posix.cc +++ b/runtime/vm/virtual_memory_posix.cc @@ -42,8 +42,15 @@ DECLARE_FLAG(bool, generate_perf_jitdump); uword VirtualMemory::page_size_ = 0; +intptr_t VirtualMemory::CalculatePageSize() { + const intptr_t page_size = getpagesize(); + ASSERT(page_size != 0); + ASSERT(Utils::IsPowerOfTwo(page_size)); + return page_size; +} + void VirtualMemory::Init() { - page_size_ = getpagesize(); + page_size_ = CalculatePageSize(); #if defined(DUAL_MAPPING_SUPPORTED) // Perf is Linux-specific and the flags aren't defined in Product. @@ -62,7 +69,7 @@ void VirtualMemory::Init() { // such as on docker containers, and disable dual mapping in this case. // Also detect for missing support of memfd_create syscall. if (FLAG_dual_map_code) { - intptr_t size = page_size_; + intptr_t size = PageSize(); intptr_t alignment = 256 * 1024; // e.g. heap page size. VirtualMemory* vm = AllocateAligned(size, alignment, true, NULL); if (vm == NULL) { @@ -167,10 +174,10 @@ VirtualMemory* VirtualMemory::AllocateAligned(intptr_t size, // // If FLAG_dual_map_code is active, the executable mapping will be mapped RX // immediately and never changes protection until it is eventually unmapped. - ASSERT(Utils::IsAligned(size, page_size_)); + ASSERT(Utils::IsAligned(size, PageSize())); ASSERT(Utils::IsPowerOfTwo(alignment)); - ASSERT(Utils::IsAligned(alignment, page_size_)); - const intptr_t allocated_size = size + alignment - page_size_; + ASSERT(Utils::IsAligned(alignment, PageSize())); + const intptr_t allocated_size = size + alignment - PageSize(); #if defined(DUAL_MAPPING_SUPPORTED) int fd = -1; const bool dual_mapping = diff --git a/runtime/vm/virtual_memory_win.cc b/runtime/vm/virtual_memory_win.cc index 33d4edbc261..d52ea07c7f8 100644 --- a/runtime/vm/virtual_memory_win.cc +++ b/runtime/vm/virtual_memory_win.cc @@ -18,10 +18,17 @@ DECLARE_FLAG(bool, write_protect_code); uword VirtualMemory::page_size_ = 0; -void VirtualMemory::Init() { +intptr_t VirtualMemory::CalculatePageSize() { SYSTEM_INFO info; GetSystemInfo(&info); - page_size_ = info.dwPageSize; + const intptr_t page_size = info.dwPageSize; + ASSERT(page_size != 0); + ASSERT(Utils::IsPowerOfTwo(page_size)); + return page_size; +} + +void VirtualMemory::Init() { + page_size_ = CalculatePageSize(); } bool VirtualMemory::DualMappingEnabled() { @@ -35,10 +42,10 @@ VirtualMemory* VirtualMemory::AllocateAligned(intptr_t size, // When FLAG_write_protect_code is active, code memory (indicated by // is_executable = true) is allocated as non-executable and later // changed to executable via VirtualMemory::Protect. - ASSERT(Utils::IsAligned(size, page_size_)); + ASSERT(Utils::IsAligned(size, PageSize())); ASSERT(Utils::IsPowerOfTwo(alignment)); - ASSERT(Utils::IsAligned(alignment, page_size_)); - intptr_t reserved_size = size + alignment - page_size_; + ASSERT(Utils::IsAligned(alignment, PageSize())); + intptr_t reserved_size = size + alignment - PageSize(); int prot = (is_executable && !FLAG_write_protect_code) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; diff --git a/tests/ffi/ffi.status b/tests/ffi/ffi.status index a6caf81a205..868f0980b8d 100644 --- a/tests/ffi/ffi.status +++ b/tests/ffi/ffi.status @@ -5,15 +5,6 @@ [ $arch == simdbc || $arch == simdbc64 ] *: Skip # SIMDBC will be deleted soon. -# Issue 37295, not yet supported in blobs snapshots at present. -[ $compiler == dartkp && $system == windows ] -function_callbacks_test: Skip -regress_37511_callbacks_test: Skip -stacktrace_regress_37910_test: Skip - -[ $compiler != dartkp || $system != windows ] -function_callbacks_unsupported_test: SkipByDesign # See above - [ $builder_tag == asan ] data_not_asan_test: SkipByDesign # This test tries to allocate too much memory on purpose. diff --git a/tests/ffi/function_callbacks_unsupported_test.dart b/tests/ffi/function_callbacks_unsupported_test.dart deleted file mode 100644 index 5ac1b5d93ed..00000000000 --- a/tests/ffi/function_callbacks_unsupported_test.dart +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2019, 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. -// -// Dart test program for testing that FFI callbacks report an appropriate -// runtime error for unsupported snapshot formats. - -import 'dart:ffi'; - -import 'package:expect/expect.dart'; - -bool checkError(UnsupportedError err) { - return "$err".contains("callbacks are not yet supported in blobs"); -} - -void main() { - Expect.throws( - () => Pointer.fromFunction(main), checkError); -} diff --git a/tests/standalone_2/dwarf_stack_trace_test.dart b/tests/standalone_2/dwarf_stack_trace_test.dart index 23a35ef2445..32a80bb31fc 100644 --- a/tests/standalone_2/dwarf_stack_trace_test.dart +++ b/tests/standalone_2/dwarf_stack_trace_test.dart @@ -66,7 +66,7 @@ main() { throw "'file' failed"; return; } - if (!result.stdout.contains("shared object")) { + if (!result.stdout.contains("Mach-O")) { print("Skipping test because we are not running from a dylib"); return; } diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json index f7b6d576b5f..1d3da928673 100644 --- a/tools/bots/test_matrix.json +++ b/tools/bots/test_matrix.json @@ -341,11 +341,7 @@ "use-elf": true }}, "dartk-android-(debug|product|release)-(arm|arm64)": {}, - "dartkp-linux-(debug|product|release)-(simarm|simarm64)": { - "options": { - "use-blobs": true - }}, - "dartkp-(win|mac)-(debug|product|release)-(simarm|simarm64)": { + "dartkp-(linux|win|mac)-(debug|product|release)-(simarm|simarm64)": { "options": { "use-blobs": true }}, @@ -356,11 +352,11 @@ }}, "dartkp-win-(product|release)-x64": { "options": { - "use-blobs": true + "use-elf": true }}, "dartkp-win-debug-x64": { "options": { - "use-blobs": true, + "use-elf": true, "vm-options": ["--no-enable-malloc-hooks"] }}, "dartkp-(linux|mac)-(product|release)-x64": { }, @@ -380,7 +376,7 @@ "dartkp-no-bare-(linux|mac|win)-(debug|product|release)-(simarm|simarm64)": { "options": { "vm-options": ["--no-enable-malloc-hooks", "--no-use-bare-instructions"], - "use-blobs": true + "use-elf": true }}, "dartk-(linux|mac|win)-(debug|product|release)-(ia32|x64)": { }, "dartk-checked-(linux|mac|win)-(debug|product|release)-(ia32|x64)": {