1// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4#if !defined(DART_PRECOMPILED_RUNTIME)
5
6#include "vm/kernel_binary.h"
7
8#include <memory>
9
10#include "platform/globals.h"
11#include "vm/compiler/frontend/kernel_to_il.h"
12#include "vm/dart_api_impl.h"
13#include "vm/flags.h"
14#include "vm/growable_array.h"
15#include "vm/kernel.h"
16#include "vm/object.h"
17#include "vm/os.h"
18#include "vm/version.h"
19
20namespace dart {
21
22namespace kernel {
23
24const char* Reader::TagName(Tag tag) {
25 switch (tag) {
26#define CASE(Name, value) \
27 case k##Name: \
28 return #Name;
29 KERNEL_TAG_LIST(CASE)
30#undef CASE
31 default:
32 break;
33 }
34 return "Unknown";
35}
36
37TypedDataPtr Reader::ReadLineStartsData(intptr_t line_start_count) {
38 TypedData& line_starts_data = TypedData::Handle(
39 TypedData::New(kTypedDataInt8ArrayCid, line_start_count, Heap::kOld));
40
41 const intptr_t start_offset = offset();
42 intptr_t i = 0;
43 for (; i < line_start_count; ++i) {
44 const intptr_t delta = ReadUInt();
45 if (delta > kMaxInt8) {
46 break;
47 }
48 line_starts_data.SetInt8(i, static_cast<int8_t>(delta));
49 }
50
51 if (i < line_start_count) {
52 // Slow path: choose representation between Int16 and Int32 typed data.
53 set_offset(start_offset);
54 intptr_t max_delta = 0;
55 for (intptr_t i = 0; i < line_start_count; ++i) {
56 const intptr_t delta = ReadUInt();
57 if (delta > max_delta) {
58 max_delta = delta;
59 }
60 }
61
62 ASSERT(max_delta > kMaxInt8);
63 const intptr_t cid = (max_delta <= kMaxInt16) ? kTypedDataInt16ArrayCid
64 : kTypedDataInt32ArrayCid;
65 line_starts_data = TypedData::New(cid, line_start_count, Heap::kOld);
66
67 set_offset(start_offset);
68 for (intptr_t i = 0; i < line_start_count; ++i) {
69 const intptr_t delta = ReadUInt();
70 if (cid == kTypedDataInt16ArrayCid) {
71 line_starts_data.SetInt16(i << 1, static_cast<int16_t>(delta));
72 } else {
73 line_starts_data.SetInt32(i << 2, delta);
74 }
75 }
76 }
77
78 return line_starts_data.raw();
79}
80
81const char* kKernelInvalidFilesize =
82 "File size is too small to be a valid kernel file";
83const char* kKernelInvalidMagicIdentifier = "Invalid magic identifier";
84const char* kKernelInvalidBinaryFormatVersion =
85 "Invalid kernel binary format version";
86const char* kKernelInvalidSizeIndicated =
87 "Invalid kernel binary: Indicated size is invalid";
88const char* kKernelInvalidSdkHash = "Invalid SDK hash";
89
90const int kSdkHashSizeInBytes = 10;
91const char* kSdkHashNull = "0000000000";
92
93std::unique_ptr<Program> Program::ReadFrom(Reader* reader, const char** error) {
94 if (reader->size() < 70) {
95 // A kernel file (v43) currently contains at least the following:
96 // * Magic number (32)
97 // * Kernel version (32)
98 // * SDK Hash (10 * 8)
99 // * List of problems (8)
100 // * Length of source map (32)
101 // * Length of canonical name table (8)
102 // * Metadata length (32)
103 // * Length of string table (8)
104 // * Length of constant table (8)
105 // * Component index (11 * 32)
106 //
107 // so is at least 74 bytes.
108 // (Technically it will also contain an empty entry in both source map and
109 // string table, taking up another 8 bytes.)
110 if (error != nullptr) {
111 *error = kKernelInvalidFilesize;
112 }
113 return nullptr;
114 }
115
116 uint32_t magic = reader->ReadUInt32();
117 if (magic != kMagicProgramFile) {
118 if (error != nullptr) {
119 *error = kKernelInvalidMagicIdentifier;
120 }
121 return nullptr;
122 }
123
124 uint32_t formatVersion = reader->ReadUInt32();
125 if ((formatVersion < kMinSupportedKernelFormatVersion) ||
126 (formatVersion > kMaxSupportedKernelFormatVersion)) {
127 if (error != nullptr) {
128 *error = kKernelInvalidBinaryFormatVersion;
129 }
130 return nullptr;
131 }
132
133 uint8_t sdkHash[kSdkHashSizeInBytes + 1];
134 reader->ReadBytes(sdkHash, kSdkHashSizeInBytes);
135 sdkHash[kSdkHashSizeInBytes] = 0; // Null terminate.
136 if (strcmp(Version::SdkHash(), kSdkHashNull) != 0 &&
137 strcmp((const char*)sdkHash, kSdkHashNull) != 0 &&
138 strcmp((const char*)sdkHash, Version::SdkHash()) != 0) {
139 if (error != nullptr) {
140 *error = kKernelInvalidSdkHash;
141 }
142 return nullptr;
143 }
144
145 std::unique_ptr<Program> program(new Program());
146 program->binary_version_ = formatVersion;
147 program->typed_data_ = reader->typed_data();
148 program->kernel_data_ = reader->buffer();
149 program->kernel_data_size_ = reader->size();
150
151 // Dill files can be concatenated (e.g. cat a.dill b.dill > c.dill). Find out
152 // if this dill contains more than one program.
153 int subprogram_count = 0;
154 reader->set_offset(reader->size() - 4);
155 while (reader->offset() > 0) {
156 intptr_t size = reader->ReadUInt32();
157 intptr_t start = reader->offset() - size;
158 if (start < 0 || size <= 0) {
159 if (error != nullptr) {
160 *error = kKernelInvalidSizeIndicated;
161 }
162 return nullptr;
163 }
164 ++subprogram_count;
165 if (subprogram_count > 1) break;
166 reader->set_offset(start - 4);
167 }
168 program->single_program_ = subprogram_count == 1;
169
170 // Read backwards at the end.
171 program->library_count_ = reader->ReadFromIndexNoReset(
172 reader->size_, LibraryCountFieldCountFromEnd, 1, 0);
173 intptr_t count_from_first_library_offset =
174 SourceTableFieldCountFromFirstLibraryOffset41Plus;
175 program->source_table_offset_ = reader->ReadFromIndexNoReset(
176 reader->size_,
177 LibraryCountFieldCountFromEnd + 1 + program->library_count_ + 1 +
178 count_from_first_library_offset,
179 1, 0);
180 program->name_table_offset_ = reader->ReadUInt32();
181 program->metadata_payloads_offset_ = reader->ReadUInt32();
182 program->metadata_mappings_offset_ = reader->ReadUInt32();
183 program->string_table_offset_ = reader->ReadUInt32();
184 program->constant_table_offset_ = reader->ReadUInt32();
185
186 program->main_method_reference_ = NameIndex(reader->ReadUInt32() - 1);
187 NNBDCompiledMode compilation_mode =
188 static_cast<NNBDCompiledMode>(reader->ReadUInt32());
189 program->compilation_mode_ = compilation_mode;
190
191 return program;
192}
193
194std::unique_ptr<Program> Program::ReadFromFile(
195 const char* script_uri, const char** error /* = nullptr */) {
196 Thread* thread = Thread::Current();
197 Isolate* isolate = thread->isolate();
198 if (script_uri == NULL) {
199 return nullptr;
200 }
201 if (!isolate->HasTagHandler()) {
202 return nullptr;
203 }
204 std::unique_ptr<kernel::Program> kernel_program;
205
206 const String& uri = String::Handle(String::New(script_uri));
207 const Object& ret = Object::Handle(
208 isolate->CallTagHandler(Dart_kKernelTag, Object::null_object(), uri));
209 if (ret.IsExternalTypedData()) {
210 const auto& typed_data = ExternalTypedData::Handle(
211 thread->zone(), ExternalTypedData::RawCast(ret.raw()));
212 kernel_program = kernel::Program::ReadFromTypedData(typed_data);
213 return kernel_program;
214 } else if (error != nullptr) {
215 Api::Scope api_scope(thread);
216 Dart_Handle retval = Api::NewHandle(thread, ret.raw());
217 {
218 TransitionVMToNative transition(thread);
219 *error = Dart_GetError(retval);
220 }
221 }
222 return kernel_program;
223}
224
225std::unique_ptr<Program> Program::ReadFromBuffer(const uint8_t* buffer,
226 intptr_t buffer_length,
227 const char** error) {
228 kernel::Reader reader(buffer, buffer_length);
229 return kernel::Program::ReadFrom(&reader, error);
230}
231
232std::unique_ptr<Program> Program::ReadFromTypedData(
233 const ExternalTypedData& typed_data, const char** error) {
234 kernel::Reader reader(typed_data);
235 return kernel::Program::ReadFrom(&reader, error);
236}
237
238} // namespace kernel
239} // namespace dart
240#endif // !defined(DART_PRECOMPILED_RUNTIME)
241