// 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 #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(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; } NNBDCompiledMode Program::DetectNullSafety(const uint8_t* buffer, intptr_t buffer_length) { Reader reader(buffer, buffer_length); std::unique_ptr program = Program::ReadFrom(&reader, nullptr); if (program == nullptr) return NNBDCompiledMode::kInvalid; return program->compilation_mode_; } std::unique_ptr 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(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); NNBDCompiledMode compilation_mode = static_cast(reader->ReadUInt32()); program->compilation_mode_ = compilation_mode; return program; } std::unique_ptr 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; 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::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(buffer), buffer_length, Heap::kNew)); kernel::Reader reader(binary); return kernel::Program::ReadFrom(&reader, error); } std::unique_ptr 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)