1// Copyright (c) 2017, 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
5#include <memory>
6
7#include "bin/snapshot_utils.h"
8
9#include "bin/dartutils.h"
10#include "bin/dfe.h"
11#include "bin/elf_loader.h"
12#include "bin/error_exit.h"
13#include "bin/extensions.h"
14#include "bin/file.h"
15#include "bin/platform.h"
16#include "include/dart_api.h"
17#include "platform/utils.h"
18
19#define LOG_SECTION_BOUNDARIES false
20
21namespace dart {
22namespace bin {
23
24static const int64_t kAppSnapshotHeaderSize = 5 * kInt64Size;
25static const int64_t kAppSnapshotPageSize = 16 * KB;
26
27class MappedAppSnapshot : public AppSnapshot {
28 public:
29 MappedAppSnapshot(MappedMemory* vm_snapshot_data,
30 MappedMemory* vm_snapshot_instructions,
31 MappedMemory* isolate_snapshot_data,
32 MappedMemory* isolate_snapshot_instructions)
33 : vm_data_mapping_(vm_snapshot_data),
34 vm_instructions_mapping_(vm_snapshot_instructions),
35 isolate_data_mapping_(isolate_snapshot_data),
36 isolate_instructions_mapping_(isolate_snapshot_instructions) {}
37
38 ~MappedAppSnapshot() {
39 delete vm_data_mapping_;
40 delete vm_instructions_mapping_;
41 delete isolate_data_mapping_;
42 delete isolate_instructions_mapping_;
43 }
44
45 void SetBuffers(const uint8_t** vm_data_buffer,
46 const uint8_t** vm_instructions_buffer,
47 const uint8_t** isolate_data_buffer,
48 const uint8_t** isolate_instructions_buffer) {
49 if (vm_data_mapping_ != NULL) {
50 *vm_data_buffer =
51 reinterpret_cast<const uint8_t*>(vm_data_mapping_->address());
52 }
53 if (vm_instructions_mapping_ != NULL) {
54 *vm_instructions_buffer =
55 reinterpret_cast<const uint8_t*>(vm_instructions_mapping_->address());
56 }
57 if (isolate_data_mapping_ != NULL) {
58 *isolate_data_buffer =
59 reinterpret_cast<const uint8_t*>(isolate_data_mapping_->address());
60 }
61 if (isolate_instructions_mapping_ != NULL) {
62 *isolate_instructions_buffer = reinterpret_cast<const uint8_t*>(
63 isolate_instructions_mapping_->address());
64 }
65 }
66
67 private:
68 MappedMemory* vm_data_mapping_;
69 MappedMemory* vm_instructions_mapping_;
70 MappedMemory* isolate_data_mapping_;
71 MappedMemory* isolate_instructions_mapping_;
72};
73
74static AppSnapshot* TryReadAppSnapshotBlobs(const char* script_name,
75 File* file) {
76 if ((file->Length() - file->Position()) < kAppSnapshotHeaderSize) {
77 return nullptr;
78 }
79
80 int64_t header[5];
81 ASSERT(sizeof(header) == kAppSnapshotHeaderSize);
82 if (!file->ReadFully(&header, kAppSnapshotHeaderSize)) {
83 return nullptr;
84 }
85 ASSERT(sizeof(header[0]) == appjit_magic_number.length);
86 if (memcmp(&header[0], appjit_magic_number.bytes,
87 appjit_magic_number.length) != 0) {
88 return nullptr;
89 }
90
91 int64_t vm_data_size = header[1];
92 int64_t vm_data_position =
93 Utils::RoundUp(file->Position(), kAppSnapshotPageSize);
94 int64_t vm_instructions_size = header[2];
95 int64_t vm_instructions_position = vm_data_position + vm_data_size;
96 if (vm_instructions_size != 0) {
97 vm_instructions_position =
98 Utils::RoundUp(vm_instructions_position, kAppSnapshotPageSize);
99 }
100 int64_t isolate_data_size = header[3];
101 int64_t isolate_data_position = Utils::RoundUp(
102 vm_instructions_position + vm_instructions_size, kAppSnapshotPageSize);
103 int64_t isolate_instructions_size = header[4];
104 int64_t isolate_instructions_position =
105 isolate_data_position + isolate_data_size;
106 if (isolate_instructions_size != 0) {
107 isolate_instructions_position =
108 Utils::RoundUp(isolate_instructions_position, kAppSnapshotPageSize);
109 }
110
111 MappedMemory* vm_data_mapping = nullptr;
112 if (vm_data_size != 0) {
113 vm_data_mapping =
114 file->Map(File::kReadOnly, vm_data_position, vm_data_size);
115 if (vm_data_mapping == nullptr) {
116 FATAL1("Failed to memory map snapshot: %s\n", script_name);
117 }
118 }
119
120 MappedMemory* vm_instr_mapping = nullptr;
121 if (vm_instructions_size != 0) {
122 vm_instr_mapping = file->Map(File::kReadExecute, vm_instructions_position,
123 vm_instructions_size);
124 if (vm_instr_mapping == nullptr) {
125 FATAL1("Failed to memory map snapshot: %s\n", script_name);
126 }
127 }
128
129 MappedMemory* isolate_data_mapping = nullptr;
130 if (isolate_data_size != 0) {
131 isolate_data_mapping =
132 file->Map(File::kReadOnly, isolate_data_position, isolate_data_size);
133 if (isolate_data_mapping == nullptr) {
134 FATAL1("Failed to memory map snapshot: %s\n", script_name);
135 }
136 }
137
138 MappedMemory* isolate_instr_mapping = nullptr;
139 if (isolate_instructions_size != 0) {
140 isolate_instr_mapping =
141 file->Map(File::kReadExecute, isolate_instructions_position,
142 isolate_instructions_size);
143 if (isolate_instr_mapping == nullptr) {
144 FATAL1("Failed to memory map snapshot: %s\n", script_name);
145 }
146 }
147
148 return new MappedAppSnapshot(vm_data_mapping, vm_instr_mapping,
149 isolate_data_mapping, isolate_instr_mapping);
150}
151
152static AppSnapshot* TryReadAppSnapshotBlobs(const char* script_name) {
153 File* file = File::Open(NULL, script_name, File::kRead);
154 if (file == nullptr) {
155 return nullptr;
156 }
157 RefCntReleaseScope<File> rs(file);
158 return TryReadAppSnapshotBlobs(script_name, file);
159}
160
161#if defined(DART_PRECOMPILED_RUNTIME)
162class ElfAppSnapshot : public AppSnapshot {
163 public:
164 ElfAppSnapshot(Dart_LoadedElf* elf,
165 const uint8_t* vm_snapshot_data,
166 const uint8_t* vm_snapshot_instructions,
167 const uint8_t* isolate_snapshot_data,
168 const uint8_t* isolate_snapshot_instructions)
169 : elf_(elf),
170 vm_snapshot_data_(vm_snapshot_data),
171 vm_snapshot_instructions_(vm_snapshot_instructions),
172 isolate_snapshot_data_(isolate_snapshot_data),
173 isolate_snapshot_instructions_(isolate_snapshot_instructions) {}
174
175 virtual ~ElfAppSnapshot() { Dart_UnloadELF(elf_); }
176
177 void SetBuffers(const uint8_t** vm_data_buffer,
178 const uint8_t** vm_instructions_buffer,
179 const uint8_t** isolate_data_buffer,
180 const uint8_t** isolate_instructions_buffer) {
181 *vm_data_buffer = vm_snapshot_data_;
182 *vm_instructions_buffer = vm_snapshot_instructions_;
183 *isolate_data_buffer = isolate_snapshot_data_;
184 *isolate_instructions_buffer = isolate_snapshot_instructions_;
185 }
186
187 private:
188 Dart_LoadedElf* elf_;
189 const uint8_t* vm_snapshot_data_;
190 const uint8_t* vm_snapshot_instructions_;
191 const uint8_t* isolate_snapshot_data_;
192 const uint8_t* isolate_snapshot_instructions_;
193};
194
195static AppSnapshot* TryReadAppSnapshotElf(
196 const char* script_name,
197 uint64_t file_offset,
198 bool force_load_elf_from_memory = false) {
199 const char* error = nullptr;
200 const uint8_t *vm_data_buffer = nullptr, *vm_instructions_buffer = nullptr,
201 *isolate_data_buffer = nullptr,
202 *isolate_instructions_buffer = nullptr;
203 Dart_LoadedElf* handle = nullptr;
204#if !defined(HOST_OS_FUCHSIA)
205 if (force_load_elf_from_memory) {
206#endif
207 File* const file =
208 File::Open(/*namespc=*/nullptr, script_name, File::kRead);
209 if (file == nullptr) return nullptr;
210 MappedMemory* memory = file->Map(File::kReadOnly, /*position=*/0,
211 /*length=*/file->Length());
212 if (memory == nullptr) return nullptr;
213 const uint8_t* address =
214 reinterpret_cast<const uint8_t*>(memory->address());
215 handle =
216 Dart_LoadELF_Memory(address + file_offset, file->Length(), &error,
217 &vm_data_buffer, &vm_instructions_buffer,
218 &isolate_data_buffer, &isolate_instructions_buffer);
219 delete memory;
220 file->Release();
221#if !defined(HOST_OS_FUCHSIA)
222 } else {
223 handle = Dart_LoadELF(script_name, file_offset, &error, &vm_data_buffer,
224 &vm_instructions_buffer, &isolate_data_buffer,
225 &isolate_instructions_buffer);
226 }
227#endif
228 if (handle == nullptr) {
229 Syslog::PrintErr("Loading failed: %s\n", error);
230 return nullptr;
231 }
232 return new ElfAppSnapshot(handle, vm_data_buffer, vm_instructions_buffer,
233 isolate_data_buffer, isolate_instructions_buffer);
234 return nullptr;
235}
236
237AppSnapshot* Snapshot::TryReadAppendedAppSnapshotElf(
238 const char* container_path) {
239 File* file = File::Open(NULL, container_path, File::kRead);
240 if (file == nullptr) {
241 return nullptr;
242 }
243 RefCntReleaseScope<File> rs(file);
244
245 // Check for payload appended at the end of the container file.
246 // If header is found, jump to payload offset.
247 int64_t appended_header[2];
248 if (!file->SetPosition(file->Length() - sizeof(appended_header))) {
249 return nullptr;
250 }
251 if (!file->ReadFully(&appended_header, sizeof(appended_header))) {
252 return nullptr;
253 }
254 // Length is always encoded as Little Endian.
255 const uint64_t appended_offset =
256 Utils::LittleEndianToHost64(appended_header[0]);
257 if (memcmp(&appended_header[1], appjit_magic_number.bytes,
258 appjit_magic_number.length) != 0 ||
259 appended_offset <= 0) {
260 return nullptr;
261 }
262
263 return TryReadAppSnapshotElf(container_path, appended_offset);
264}
265
266class DylibAppSnapshot : public AppSnapshot {
267 public:
268 DylibAppSnapshot(void* library,
269 const uint8_t* vm_snapshot_data,
270 const uint8_t* vm_snapshot_instructions,
271 const uint8_t* isolate_snapshot_data,
272 const uint8_t* isolate_snapshot_instructions)
273 : library_(library),
274 vm_snapshot_data_(vm_snapshot_data),
275 vm_snapshot_instructions_(vm_snapshot_instructions),
276 isolate_snapshot_data_(isolate_snapshot_data),
277 isolate_snapshot_instructions_(isolate_snapshot_instructions) {}
278
279 ~DylibAppSnapshot() { Extensions::UnloadLibrary(library_); }
280
281 void SetBuffers(const uint8_t** vm_data_buffer,
282 const uint8_t** vm_instructions_buffer,
283 const uint8_t** isolate_data_buffer,
284 const uint8_t** isolate_instructions_buffer) {
285 *vm_data_buffer = vm_snapshot_data_;
286 *vm_instructions_buffer = vm_snapshot_instructions_;
287 *isolate_data_buffer = isolate_snapshot_data_;
288 *isolate_instructions_buffer = isolate_snapshot_instructions_;
289 }
290
291 private:
292 void* library_;
293 const uint8_t* vm_snapshot_data_;
294 const uint8_t* vm_snapshot_instructions_;
295 const uint8_t* isolate_snapshot_data_;
296 const uint8_t* isolate_snapshot_instructions_;
297};
298
299static AppSnapshot* TryReadAppSnapshotDynamicLibrary(const char* script_name) {
300 void* library = Extensions::LoadExtensionLibrary(script_name);
301 if (library == NULL) {
302 return NULL;
303 }
304
305 const uint8_t* vm_data_buffer = reinterpret_cast<const uint8_t*>(
306 Extensions::ResolveSymbol(library, kVmSnapshotDataCSymbol));
307
308 const uint8_t* vm_instructions_buffer = reinterpret_cast<const uint8_t*>(
309 Extensions::ResolveSymbol(library, kVmSnapshotInstructionsCSymbol));
310
311 const uint8_t* isolate_data_buffer = reinterpret_cast<const uint8_t*>(
312 Extensions::ResolveSymbol(library, kIsolateSnapshotDataCSymbol));
313 if (isolate_data_buffer == NULL) {
314 FATAL1("Failed to resolve symbol '%s'\n", kIsolateSnapshotDataCSymbol);
315 }
316
317 const uint8_t* isolate_instructions_buffer = reinterpret_cast<const uint8_t*>(
318 Extensions::ResolveSymbol(library, kIsolateSnapshotInstructionsCSymbol));
319 if (isolate_instructions_buffer == NULL) {
320 FATAL1("Failed to resolve symbol '%s'\n",
321 kIsolateSnapshotInstructionsCSymbol);
322 }
323
324 return new DylibAppSnapshot(library, vm_data_buffer, vm_instructions_buffer,
325 isolate_data_buffer, isolate_instructions_buffer);
326}
327
328#endif // defined(DART_PRECOMPILED_RUNTIME)
329
330AppSnapshot* Snapshot::TryReadAppSnapshot(const char* script_uri,
331 bool force_load_elf_from_memory) {
332 auto decoded_path = File::UriToPath(script_uri);
333 if (decoded_path == nullptr) {
334 return nullptr;
335 }
336
337 const char* script_name = decoded_path.get();
338 if (File::GetType(nullptr, script_name, true) != File::kIsFile) {
339 // If 'script_name' refers to a pipe, don't read to check for an app
340 // snapshot since we cannot rewind if it isn't (and couldn't mmap it in
341 // anyway if it was).
342 return nullptr;
343 }
344 AppSnapshot* snapshot = TryReadAppSnapshotBlobs(script_name);
345 if (snapshot != nullptr) {
346 return snapshot;
347 }
348#if defined(DART_PRECOMPILED_RUNTIME)
349 // For testing AOT with the standalone embedder, we also support loading
350 // from a dynamic library to simulate what happens on iOS.
351
352#if defined(TARGET_OS_LINUX) || defined(TARGET_OS_MACOS)
353 // On Linux and OSX, resolve the script path before passing into dlopen()
354 // since dlopen will not search the filesystem for paths like 'libtest.so'.
355 std::unique_ptr<char, decltype(std::free)*> absolute_path{
356 realpath(script_name, nullptr), std::free};
357 script_name = absolute_path.get();
358#endif
359
360 if (!force_load_elf_from_memory) {
361 snapshot = TryReadAppSnapshotDynamicLibrary(script_name);
362 if (snapshot != nullptr) {
363 return snapshot;
364 }
365 }
366
367 snapshot = TryReadAppSnapshotElf(script_name, /*file_offset=*/0,
368 force_load_elf_from_memory);
369 if (snapshot != nullptr) {
370 return snapshot;
371 }
372#endif // defined(DART_PRECOMPILED_RUNTIME)
373 return nullptr;
374}
375
376#if !defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) && !defined(TESTING)
377static void WriteSnapshotFile(const char* filename,
378 const uint8_t* buffer,
379 const intptr_t size) {
380 File* file = File::Open(NULL, filename, File::kWriteTruncate);
381 if (file == NULL) {
382 ErrorExit(kErrorExitCode, "Unable to open file %s for writing snapshot\n",
383 filename);
384 }
385
386 if (!file->WriteFully(buffer, size)) {
387 ErrorExit(kErrorExitCode, "Unable to write file %s for writing snapshot\n",
388 filename);
389 }
390 file->Release();
391}
392#endif
393
394static bool WriteInt64(File* file, int64_t size) {
395 return file->WriteFully(&size, sizeof(size));
396}
397
398void Snapshot::WriteAppSnapshot(const char* filename,
399 uint8_t* vm_data_buffer,
400 intptr_t vm_data_size,
401 uint8_t* vm_instructions_buffer,
402 intptr_t vm_instructions_size,
403 uint8_t* isolate_data_buffer,
404 intptr_t isolate_data_size,
405 uint8_t* isolate_instructions_buffer,
406 intptr_t isolate_instructions_size) {
407 File* file = File::Open(NULL, filename, File::kWriteTruncate);
408 if (file == NULL) {
409 ErrorExit(kErrorExitCode, "Unable to write snapshot file '%s'\n", filename);
410 }
411
412 file->WriteFully(appjit_magic_number.bytes, appjit_magic_number.length);
413 WriteInt64(file, vm_data_size);
414 WriteInt64(file, vm_instructions_size);
415 WriteInt64(file, isolate_data_size);
416 WriteInt64(file, isolate_instructions_size);
417 ASSERT(file->Position() == kAppSnapshotHeaderSize);
418
419 file->SetPosition(Utils::RoundUp(file->Position(), kAppSnapshotPageSize));
420 if (LOG_SECTION_BOUNDARIES) {
421 Syslog::PrintErr("%" Px64 ": VM Data\n", file->Position());
422 }
423 if (!file->WriteFully(vm_data_buffer, vm_data_size)) {
424 ErrorExit(kErrorExitCode, "Unable to write snapshot file '%s'\n", filename);
425 }
426
427 if (vm_instructions_size != 0) {
428 file->SetPosition(Utils::RoundUp(file->Position(), kAppSnapshotPageSize));
429 if (LOG_SECTION_BOUNDARIES) {
430 Syslog::PrintErr("%" Px64 ": VM Instructions\n", file->Position());
431 }
432 if (!file->WriteFully(vm_instructions_buffer, vm_instructions_size)) {
433 ErrorExit(kErrorExitCode, "Unable to write snapshot file '%s'\n",
434 filename);
435 }
436 }
437
438 file->SetPosition(Utils::RoundUp(file->Position(), kAppSnapshotPageSize));
439 if (LOG_SECTION_BOUNDARIES) {
440 Syslog::PrintErr("%" Px64 ": Isolate Data\n", file->Position());
441 }
442 if (!file->WriteFully(isolate_data_buffer, isolate_data_size)) {
443 ErrorExit(kErrorExitCode, "Unable to write snapshot file '%s'\n", filename);
444 }
445
446 if (isolate_instructions_size != 0) {
447 file->SetPosition(Utils::RoundUp(file->Position(), kAppSnapshotPageSize));
448 if (LOG_SECTION_BOUNDARIES) {
449 Syslog::PrintErr("%" Px64 ": Isolate Instructions\n", file->Position());
450 }
451 if (!file->WriteFully(isolate_instructions_buffer,
452 isolate_instructions_size)) {
453 ErrorExit(kErrorExitCode, "Unable to write snapshot file '%s'\n",
454 filename);
455 }
456 }
457
458 file->Flush();
459 file->Release();
460}
461
462void Snapshot::GenerateKernel(const char* snapshot_filename,
463 const char* script_name,
464 const char* package_config) {
465#if !defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) && !defined(TESTING)
466 uint8_t* kernel_buffer = NULL;
467 intptr_t kernel_buffer_size = 0;
468 dfe.ReadScript(script_name, &kernel_buffer, &kernel_buffer_size);
469 if (kernel_buffer != NULL) {
470 WriteSnapshotFile(snapshot_filename, kernel_buffer, kernel_buffer_size);
471 free(kernel_buffer);
472 } else {
473 Dart_KernelCompilationResult result =
474 dfe.CompileScript(script_name, false, package_config);
475 if (result.status != Dart_KernelCompilationStatus_Ok) {
476 ErrorExit(kErrorExitCode, "%s\n", result.error);
477 }
478 WriteSnapshotFile(snapshot_filename, result.kernel, result.kernel_size);
479 free(result.kernel);
480 }
481#else
482 UNREACHABLE();
483#endif // !defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) && !defined(TESTING)
484}
485
486void Snapshot::GenerateAppJIT(const char* snapshot_filename) {
487#if defined(TARGET_ARCH_IA32)
488 // Snapshots with code are not supported on IA32.
489 uint8_t* isolate_buffer = NULL;
490 intptr_t isolate_size = 0;
491
492 Dart_Handle result =
493 Dart_CreateSnapshot(NULL, NULL, &isolate_buffer, &isolate_size);
494 if (Dart_IsError(result)) {
495 ErrorExit(kErrorExitCode, "%s\n", Dart_GetError(result));
496 }
497
498 WriteAppSnapshot(snapshot_filename, NULL, 0, NULL, 0, isolate_buffer,
499 isolate_size, NULL, 0);
500#else
501 uint8_t* isolate_data_buffer = NULL;
502 intptr_t isolate_data_size = 0;
503 uint8_t* isolate_instructions_buffer = NULL;
504 intptr_t isolate_instructions_size = 0;
505 Dart_Handle result = Dart_CreateAppJITSnapshotAsBlobs(
506 &isolate_data_buffer, &isolate_data_size, &isolate_instructions_buffer,
507 &isolate_instructions_size);
508 if (Dart_IsError(result)) {
509 ErrorExit(kErrorExitCode, "%s\n", Dart_GetError(result));
510 }
511 WriteAppSnapshot(snapshot_filename, NULL, 0, NULL, 0, isolate_data_buffer,
512 isolate_data_size, isolate_instructions_buffer,
513 isolate_instructions_size);
514#endif
515}
516
517static void StreamingWriteCallback(void* callback_data,
518 const uint8_t* buffer,
519 intptr_t size) {
520 File* file = reinterpret_cast<File*>(callback_data);
521 if (!file->WriteFully(buffer, size)) {
522 ErrorExit(kErrorExitCode, "Unable to write snapshot file\n");
523 }
524}
525
526void Snapshot::GenerateAppAOTAsAssembly(const char* snapshot_filename) {
527 File* file = File::Open(NULL, snapshot_filename, File::kWriteTruncate);
528 RefCntReleaseScope<File> rs(file);
529 if (file == NULL) {
530 ErrorExit(kErrorExitCode, "Unable to open file %s for writing snapshot\n",
531 snapshot_filename);
532 }
533 Dart_Handle result = Dart_CreateAppAOTSnapshotAsAssembly(
534 StreamingWriteCallback, file, /*strip=*/false,
535 /*debug_callback_data=*/nullptr);
536 if (Dart_IsError(result)) {
537 ErrorExit(kErrorExitCode, "%s\n", Dart_GetError(result));
538 }
539}
540
541bool Snapshot::IsAOTSnapshot(const char* snapshot_filename) {
542 // Header is simply "ELF" prefixed with the DEL character.
543 const char elf_header[] = {0x7F, 0x45, 0x4C, 0x46, 0x0};
544 const int64_t elf_header_len = strlen(elf_header);
545 File* file = File::Open(NULL, snapshot_filename, File::kRead);
546 if (file == nullptr) {
547 return false;
548 }
549 if (file->Length() < elf_header_len) {
550 file->Release();
551 return false;
552 }
553 auto buf = std::unique_ptr<char[]>(new char[elf_header_len]);
554 bool success = file->ReadFully(buf.get(), elf_header_len);
555 file->Release();
556 ASSERT(success);
557 return (strncmp(elf_header, buf.get(), elf_header_len) == 0);
558}
559
560} // namespace bin
561} // namespace dart
562