| // Copyright (c) 2016, 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. |
| #if !defined(DART_PRECOMPILED_RUNTIME) |
| |
| #include "vm/kernel_binary.h" |
| |
| #include <memory> |
| |
| #include "platform/globals.h" |
| #include "vm/compiler/frontend/kernel_to_il.h" |
| #include "vm/dart_api_impl.h" |
| #include "vm/flags.h" |
| #include "vm/growable_array.h" |
| #include "vm/kernel.h" |
| #include "vm/object.h" |
| #include "vm/os.h" |
| #include "vm/version.h" |
| |
| namespace dart { |
| |
| namespace kernel { |
| |
| const char* Reader::TagName(Tag tag) { |
| switch (tag) { |
| #define CASE(Name, value) \ |
| case k##Name: \ |
| return #Name; |
| KERNEL_TAG_LIST(CASE) |
| #undef CASE |
| default: |
| break; |
| } |
| return "Unknown"; |
| } |
| |
| TypedDataPtr Reader::ReadLineStartsData(intptr_t line_start_count) { |
| const intptr_t start_offset = offset(); |
| |
| // Choose representation between Uint16 and Uint32 typed data. |
| intptr_t max_start = 0; |
| for (intptr_t i = 0; i < line_start_count; ++i) { |
| const intptr_t delta = ReadUInt(); |
| max_start += delta; |
| } |
| |
| const intptr_t cid = (max_start <= kMaxUint16) ? kTypedDataUint16ArrayCid |
| : kTypedDataUint32ArrayCid; |
| const TypedData& line_starts_data = |
| TypedData::Handle(TypedData::New(cid, line_start_count, Heap::kOld)); |
| |
| set_offset(start_offset); |
| intptr_t current_start = 0; |
| for (intptr_t i = 0; i < line_start_count; ++i) { |
| const intptr_t delta = ReadUInt(); |
| current_start += delta; |
| if (cid == kTypedDataUint16ArrayCid) { |
| line_starts_data.SetUint16(i << 1, static_cast<uint16_t>(current_start)); |
| } else { |
| line_starts_data.SetUint32(i << 2, current_start); |
| } |
| } |
| |
| return line_starts_data.ptr(); |
| } |
| |
| const char* kKernelInvalidFilesize = |
| "File size is too small to be a valid kernel file"; |
| const char* kKernelInvalidMagicIdentifier = "Invalid magic identifier"; |
| const char* kKernelInvalidBinaryFormatVersion = |
| "Invalid kernel binary format version"; |
| const char* kKernelInvalidSizeIndicated = |
| "Invalid kernel binary: Indicated size is invalid"; |
| const char* kKernelInvalidSdkHash = "Invalid SDK hash"; |
| |
| const int kSdkHashSizeInBytes = 10; |
| const char* kSdkHashNull = "0000000000"; |
| |
| bool IsValidSdkHash(const uint8_t* sdk_hash) { |
| if (memcmp(Version::SdkHash(), kSdkHashNull, kSdkHashSizeInBytes) != 0 && |
| memcmp(sdk_hash, kSdkHashNull, kSdkHashSizeInBytes) != 0 && |
| memcmp(sdk_hash, Version::SdkHash(), kSdkHashSizeInBytes) != 0) { |
| return false; |
| } |
| return true; |
| } |
| |
| std::unique_ptr<Program> Program::ReadFrom(Reader* reader, const char** error) { |
| if (reader->size() < 70) { |
| // A kernel file (v43) currently contains at least the following: |
| // * Magic number (32) |
| // * Kernel version (32) |
| // * SDK Hash (10 * 8) |
| // * List of problems (8) |
| // * Length of source map (32) |
| // * Length of canonical name table (8) |
| // * Metadata length (32) |
| // * Length of string table (8) |
| // * Length of constant table (8) |
| // * Component index (11 * 32) |
| // |
| // so is at least 74 bytes. |
| // (Technically it will also contain an empty entry in both source map and |
| // string table, taking up another 8 bytes.) |
| if (error != nullptr) { |
| *error = kKernelInvalidFilesize; |
| } |
| return nullptr; |
| } |
| |
| uint32_t magic = reader->ReadUInt32(); |
| if (magic != kMagicProgramFile) { |
| if (error != nullptr) { |
| *error = kKernelInvalidMagicIdentifier; |
| } |
| return nullptr; |
| } |
| |
| const uint32_t format_version = reader->ReadUInt32(); |
| if (format_version != kSupportedKernelFormatVersion) { |
| if (error != nullptr) { |
| *error = kKernelInvalidBinaryFormatVersion; |
| } |
| return nullptr; |
| } |
| |
| if (!IsValidSdkHash(reader->BufferAt(reader->offset()))) { |
| if (error != nullptr) { |
| *error = kKernelInvalidSdkHash; |
| } |
| return nullptr; |
| } |
| reader->set_offset(reader->offset() + kSdkHashSizeInBytes); |
| |
| std::unique_ptr<Program> program(new Program(reader->typed_data())); |
| |
| // Dill files can be concatenated (e.g. cat a.dill b.dill > c.dill). Find out |
| // if this dill contains more than one program. |
| int subprogram_count = 0; |
| reader->set_offset(reader->size() - 4); |
| while (reader->offset() > 0) { |
| intptr_t size = reader->ReadUInt32(); |
| intptr_t start = reader->offset() - size; |
| if (start < 0 || size <= 0) { |
| if (error != nullptr) { |
| *error = kKernelInvalidSizeIndicated; |
| } |
| return nullptr; |
| } |
| ++subprogram_count; |
| if (subprogram_count > 1) break; |
| reader->set_offset(start - 4); |
| } |
| program->single_program_ = subprogram_count == 1; |
| |
| // Read backwards at the end. |
| program->library_count_ = reader->ReadFromIndexNoReset( |
| reader->size_, LibraryCountFieldCountFromEnd, 1, 0); |
| intptr_t count_from_first_library_offset = |
| SourceTableFieldCountFromFirstLibraryOffset; |
| program->source_table_offset_ = reader->ReadFromIndexNoReset( |
| reader->size_, |
| LibraryCountFieldCountFromEnd + 1 + program->library_count_ + 1 + |
| count_from_first_library_offset, |
| 1, 0); |
| program->constant_table_offset_ = reader->ReadUInt32(); |
| reader->ReadUInt32(); // offset for constant table index. |
| program->name_table_offset_ = reader->ReadUInt32(); |
| program->metadata_payloads_offset_ = reader->ReadUInt32(); |
| program->metadata_mappings_offset_ = reader->ReadUInt32(); |
| program->string_table_offset_ = reader->ReadUInt32(); |
| // The below includes any 8-bit alignment; denotes the end of the previous |
| // block. |
| program->component_index_offset_ = reader->ReadUInt32(); |
| |
| program->main_method_reference_ = NameIndex(reader->ReadUInt32() - 1); |
| reader->ReadUInt32(); // Read and ignore NNBD compilation mode. |
| |
| return program; |
| } |
| |
| std::unique_ptr<Program> Program::ReadFromFile( |
| const char* script_uri, |
| const char** error /* = nullptr */) { |
| Thread* thread = Thread::Current(); |
| auto isolate_group = thread->isolate_group(); |
| if (script_uri == nullptr) { |
| return nullptr; |
| } |
| if (!isolate_group->HasTagHandler()) { |
| return nullptr; |
| } |
| std::unique_ptr<kernel::Program> kernel_program; |
| |
| const String& uri = String::Handle(String::New(script_uri)); |
| const Object& ret = Object::Handle(isolate_group->CallTagHandler( |
| Dart_kKernelTag, Object::null_object(), uri)); |
| if (ret.IsExternalTypedData()) { |
| const auto& typed_data = ExternalTypedData::Cast(ret); |
| kernel_program = kernel::Program::ReadFromTypedData(typed_data); |
| return kernel_program; |
| } else if (error != nullptr) { |
| Api::Scope api_scope(thread); |
| Dart_Handle retval = Api::NewHandle(thread, ret.ptr()); |
| { |
| TransitionVMToNative transition(thread); |
| *error = Dart_GetError(retval); |
| } |
| } |
| return kernel_program; |
| } |
| |
| std::unique_ptr<Program> Program::ReadFromBuffer(const uint8_t* buffer, |
| intptr_t buffer_length, |
| const char** error) { |
| // Whoever called this method (e.g. embedder) has to ensure the buffer stays |
| // alive until the VM is done with the last usage (e.g. isolate shutdown). |
| const auto& binary = ExternalTypedData::Handle(ExternalTypedData::New( |
| kExternalTypedDataUint8ArrayCid, const_cast<uint8_t*>(buffer), |
| buffer_length, Heap::kNew)); |
| kernel::Reader reader(binary); |
| return kernel::Program::ReadFrom(&reader, error); |
| } |
| |
| std::unique_ptr<Program> Program::ReadFromTypedData( |
| const ExternalTypedData& typed_data, |
| const char** error) { |
| kernel::Reader reader(typed_data); |
| return kernel::Program::ReadFrom(&reader, error); |
| } |
| |
| } // namespace kernel |
| } // namespace dart |
| #endif // !defined(DART_PRECOMPILED_RUNTIME) |