Flutter Engine
The Flutter Engine
image_snapshot.cc
Go to the documentation of this file.
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 "vm/image_snapshot.h"
6
7#include "include/dart_api.h"
8#include "platform/assert.h"
9#include "platform/elf.h"
10#include "vm/bss_relocs.h"
11#include "vm/class_id.h"
13#include "vm/dwarf.h"
14#include "vm/elf.h"
15#include "vm/hash.h"
16#include "vm/hash_map.h"
17#include "vm/heap/heap.h"
18#include "vm/instructions.h"
19#include "vm/json_writer.h"
20#include "vm/object.h"
21#include "vm/object_store.h"
22#include "vm/program_visitor.h"
23#include "vm/stub_code.h"
24#include "vm/timeline.h"
26#include "vm/zone_text_buffer.h"
27
28#if !defined(DART_PRECOMPILED_RUNTIME)
30#endif // !defined(DART_PRECOMPILED_RUNTIME)
31
32namespace dart {
33
34#if defined(DART_PRECOMPILER)
35DEFINE_FLAG(bool,
36 print_instruction_stats,
37 false,
38 "Print instruction statistics");
39
41 print_instructions_sizes_to,
42 nullptr,
43 "Print sizes of all instruction objects to the given file");
44#endif
45
46const UntaggedInstructionsSection* Image::ExtraInfo(const uword raw_memory,
47 const uword size) {
48#if defined(DART_PRECOMPILED_RUNTIME)
49 auto const raw_value =
50 FieldValue(raw_memory, HeaderField::InstructionsSectionOffset);
51 if (raw_value != kNoInstructionsSection) {
52 ASSERT(raw_value >= kHeaderSize);
54 auto const layout = reinterpret_cast<const UntaggedInstructionsSection*>(
55 raw_memory + raw_value);
56 // The instructions section is likely non-empty in bare instructions mode
57 // (unless splitting into multiple outputs and there are no Code objects
58 // in this particular output), but is guaranteed empty otherwise (the
59 // instructions follow the InstructionsSection object instead).
60 ASSERT(raw_value <=
61 size - InstructionsSection::InstanceSize(layout->payload_length_));
62 return layout;
63 }
64#endif
65 return nullptr;
66}
67
68uword* Image::bss() const {
69#if defined(DART_PRECOMPILED_RUNTIME)
70 ASSERT(extra_info_ != nullptr);
71 // There should always be a non-zero BSS offset.
72 ASSERT(extra_info_->bss_offset_ != 0);
73 // Returning a non-const uword* is safe because we're translating from
74 // the start of the instructions (read-only) to the start of the BSS
75 // (read-write).
76 return reinterpret_cast<uword*>(raw_memory_ + extra_info_->bss_offset_);
77#else
78 return nullptr;
79#endif
80}
81
83#if defined(DART_PRECOMPILED_RUNTIME)
84 ASSERT(extra_info_ != nullptr);
85 return extra_info_->instructions_relocated_address_;
86#else
87 return kNoRelocatedAddress;
88#endif
89}
90
91const uint8_t* Image::build_id() const {
92#if defined(DART_PRECOMPILED_RUNTIME)
93 ASSERT(extra_info_ != nullptr);
94 if (extra_info_->build_id_offset_ != kNoBuildId) {
95 auto const note = reinterpret_cast<elf::Note*>(
96 raw_memory_ + extra_info_->build_id_offset_);
97 return note->data + note->name_size;
98 }
99#endif
100 return nullptr;
101}
102
103intptr_t Image::build_id_length() const {
104#if defined(DART_PRECOMPILED_RUNTIME)
105 ASSERT(extra_info_ != nullptr);
106 if (extra_info_->build_id_offset_ != kNoBuildId) {
107 auto const note = reinterpret_cast<elf::Note*>(
108 raw_memory_ + extra_info_->build_id_offset_);
109 return note->description_size;
110 }
111#endif
112 return 0;
113}
114
116#if defined(DART_PRECOMPILED_RUNTIME)
117 ASSERT(extra_info_ != nullptr);
118 // Since assembly snapshots can't set up this field correctly (instead,
119 // it's initialized in BSS at snapshot load time), we use it to detect
120 // direct-to-ELF snapshots.
121 return extra_info_->instructions_relocated_address_ != kNoRelocatedAddress;
122#else
123 return false;
124#endif
125}
126
128 ObjectPtr obj = key;
129 ASSERT(!obj->IsSmi());
130
131 uword body = UntaggedObject::ToAddr(obj) + sizeof(UntaggedObject);
132 uword end = UntaggedObject::ToAddr(obj) + obj->untag()->HeapSize();
133
134 uint32_t hash = obj->GetClassId();
135 // Don't include the header. Objects in the image are pre-marked, but objects
136 // in the current isolate are not.
137 for (uword cursor = body; cursor < end; cursor += sizeof(uint32_t)) {
138 hash = CombineHashes(hash, *reinterpret_cast<uint32_t*>(cursor));
139 }
140
141 return FinalizeHash(hash, 30);
142}
143
145 ObjectPtr a = pair.object;
146 ObjectPtr b = key;
147 ASSERT(!a->IsSmi());
148 ASSERT(!b->IsSmi());
149
150 if (a->GetClassId() != b->GetClassId()) {
151 return false;
152 }
153
154 intptr_t heap_size = a->untag()->HeapSize();
155 if (b->untag()->HeapSize() != heap_size) {
156 return false;
157 }
158
159 // Don't include the header. Objects in the image are pre-marked, but objects
160 // in the current isolate are not.
161 uword body_a = UntaggedObject::ToAddr(a) + sizeof(UntaggedObject);
162 uword body_b = UntaggedObject::ToAddr(b) + sizeof(UntaggedObject);
163 uword body_size = heap_size - sizeof(UntaggedObject);
164 return 0 == memcmp(reinterpret_cast<const void*>(body_a),
165 reinterpret_cast<const void*>(body_b), body_size);
166}
167
168#if !defined(DART_PRECOMPILED_RUNTIME)
169#if defined(DART_PRECOMPILER)
171 bool generates_assembly,
172 const Trie<const char>* deobfuscation_trie)
173#else
174ImageWriter::ImageWriter(Thread* t, bool generates_assembly)
175#endif
176 : thread_(ASSERT_NOTNULL(t)),
177 zone_(t->zone()),
178 next_data_offset_(0),
179 next_text_offset_(0),
180 objects_(),
181 instructions_(),
182#if defined(DART_PRECOMPILER)
183 namer_(t->zone(),
184 deobfuscation_trie,
185 /*for_assembly=*/generates_assembly),
186#endif
187 image_type_(TagObjectTypeAsReadOnly(zone_, "Image")),
188 instructions_section_type_(
189 TagObjectTypeAsReadOnly(zone_, "InstructionsSection")),
190 instructions_type_(TagObjectTypeAsReadOnly(zone_, "Instructions")),
191 trampoline_type_(TagObjectTypeAsReadOnly(zone_, "Trampoline")),
192 padding_type_(TagObjectTypeAsReadOnly(zone_, "Padding")) {
193 ResetOffsets();
194}
195
198 if (commands != nullptr) {
199 const intptr_t initial_offset = next_text_offset_;
200 for (auto& inst : *commands) {
201 ASSERT((initial_offset + inst.expected_offset) == next_text_offset_);
202 switch (inst.op) {
204 Heap* const heap = thread_->heap();
205 CodePtr code = inst.insert_instruction_of_code.code;
206 InstructionsPtr instructions = Code::InstructionsOf(code);
207 const intptr_t offset = next_text_offset_;
208 instructions_.Add(InstructionsData(instructions, code, offset));
209 next_text_offset_ += SizeInSnapshot(instructions);
210 ASSERT(heap->GetObjectId(instructions) == 0);
211 heap->SetObjectId(instructions, offset);
212 break;
213 }
215 auto trampoline_bytes = inst.insert_trampoline_bytes.buffer;
216 auto trampoline_length = inst.insert_trampoline_bytes.buffer_length;
217 const intptr_t offset = next_text_offset_;
218 instructions_.Add(
219 InstructionsData(trampoline_bytes, trampoline_length, offset));
220 next_text_offset_ += trampoline_length;
221 break;
222 }
224 auto padding_length = inst.insert_padding.padding_length;
225 const intptr_t offset = next_text_offset_;
226 instructions_.Add(InstructionsData(nullptr, padding_length, offset));
227 next_text_offset_ += padding_length;
228 break;
229 }
230 default:
231 UNREACHABLE();
232 }
233 }
234 }
235}
236
237int32_t ImageWriter::GetTextOffsetFor(InstructionsPtr instructions,
238 CodePtr code) {
239 Heap* const heap = thread_->heap();
240 intptr_t offset = heap->GetObjectId(instructions);
241 if (offset != 0) {
242 return offset;
243 }
244
246 heap->SetObjectId(instructions, offset);
247 next_text_offset_ += SizeInSnapshot(instructions);
248 instructions_.Add(InstructionsData(instructions, code, offset));
249
250 ASSERT(offset != 0);
251 return offset;
252}
253
254intptr_t ImageWriter::SizeInSnapshotForBytes(intptr_t length) {
255 // We are just going to write it out as a string.
258}
259
261 const classid_t cid = raw_object->GetClassId();
262
263 switch (cid) {
264 case kCompressedStackMapsCid: {
265 auto raw_maps = CompressedStackMaps::RawCast(raw_object);
268 }
269 case kCodeSourceMapCid: {
270 auto raw_map = CodeSourceMap::RawCast(raw_object);
272 raw_map->untag()->length_);
273 }
274 case kPcDescriptorsCid: {
275 auto raw_desc = PcDescriptors::RawCast(raw_object);
277 raw_desc->untag()->length_);
278 }
279 case kInstructionsCid: {
280 auto raw_insns = Instructions::RawCast(raw_object);
282 Instructions::Size(raw_insns));
283 }
284 case kOneByteStringCid: {
285 auto raw_str = String::RawCast(raw_object);
288 }
289 case kTwoByteStringCid: {
290 auto raw_str = String::RawCast(raw_object);
293 }
294 default: {
295 const Class& clazz = Class::Handle(Object::Handle(raw_object).clazz());
296 FATAL("Unsupported class %s in rodata section.\n", clazz.ToCString());
297 return 0;
298 }
299 }
300}
301
302#if defined(SNAPSHOT_BACKTRACE)
303uint32_t ImageWriter::GetDataOffsetFor(ObjectPtr raw_object,
304 ObjectPtr raw_parent) {
305#else
307#endif
308 const intptr_t snap_size = SizeInSnapshot(raw_object);
309 const intptr_t offset = next_data_offset_;
310 next_data_offset_ += snap_size;
311#if defined(SNAPSHOT_BACKTRACE)
312 objects_.Add(ObjectData(raw_object, raw_parent));
313#else
314 objects_.Add(ObjectData(raw_object));
315#endif
316 return offset;
317}
318
319uint32_t ImageWriter::AddBytesToData(uint8_t* bytes, intptr_t length) {
320 const intptr_t snap_size = SizeInSnapshotForBytes(length);
321 const intptr_t offset = next_data_offset_;
322 next_data_offset_ += snap_size;
323 objects_.Add(ObjectData(bytes, length));
324 return offset;
325}
326
328 return instructions_.length();
329}
330
331void ImageWriter::GetTrampolineInfo(intptr_t* count, intptr_t* size) const {
332 ASSERT(count != nullptr && size != nullptr);
333 *count = 0;
334 *size = 0;
335 for (auto const& data : instructions_) {
336 if (data.trampoline_length != 0) {
337 *count += 1;
338 *size += data.trampoline_length;
339 }
340 }
341}
342
343// Returns nullptr if there is no profile writer.
344const char* ImageWriter::ObjectTypeForProfile(const Object& object) const {
345 if (profile_writer_ == nullptr) return nullptr;
346 ASSERT(IsROSpace());
349 Class& klass = thread_->ClassHandle();
350 String& name = thread_->StringHandle();
351 klass = object.clazz();
352 name = klass.UserVisibleName();
353 auto const name_str = name.ToCString();
354 return TagObjectTypeAsReadOnly(zone_, name_str);
355}
356
357const char* ImageWriter::TagObjectTypeAsReadOnly(Zone* zone, const char* type) {
358 ASSERT(zone != nullptr && type != nullptr);
359 return OS::SCreate(zone, "(RO) %s", type);
360}
361
362#if defined(DART_PRECOMPILER)
364 std::unique_ptr<CombinedCodeStatistics> instruction_stats(
366 for (intptr_t i = 0; i < instructions_.length(); i++) {
367 auto& data = instructions_[i];
368 CodeStatistics* stats = data.insns_->stats();
369 if (stats != nullptr) {
370 stats->AppendTo(instruction_stats.get());
371 }
372 }
373 instruction_stats->DumpStatistics();
374}
375
377 auto& cls = Class::Handle(zone_);
378 auto& lib = Library::Handle(zone_);
379 auto& owner = Object::Handle(zone_);
380 auto& url = String::Handle(zone_);
381 auto& name = String::Handle(zone_);
382 intptr_t trampolines_total_size = 0;
383
384 JSONWriter js;
385 js.OpenArray();
386 for (intptr_t i = 0; i < instructions_.length(); i++) {
387 auto& data = instructions_[i];
388 // We count alignment padding into trampolines for now.
389 const bool is_trampoline = data.trampoline_length != 0;
390 if (is_trampoline) {
391 trampolines_total_size += data.trampoline_length;
392 continue;
393 }
394 owner = WeakSerializationReference::Unwrap(data.code_->owner());
395 js.OpenObject();
396 if (owner.IsFunction()) {
397 cls = Function::Cast(owner).Owner();
398 name = cls.ScrubbedName();
399 lib = cls.library();
400 url = lib.url();
401 js.PrintPropertyStr("l", url);
402 js.PrintPropertyStr("c", name);
403 } else if (owner.IsClass()) {
404 cls ^= owner.ptr();
405 name = cls.ScrubbedName();
406 lib = cls.library();
407 url = lib.url();
408 js.PrintPropertyStr("l", url);
409 js.PrintPropertyStr("c", name);
410 }
411 js.PrintProperty("n",
412 data.code_->QualifiedName(
415 js.PrintProperty("s", SizeInSnapshot(data.insns_->ptr()));
416 js.CloseObject();
417 }
418 if (trampolines_total_size != 0) {
419 js.OpenObject();
420 js.PrintProperty("n", "[Stub] Trampoline");
421 js.PrintProperty("s", trampolines_total_size);
422 js.CloseObject();
423 }
424 js.CloseArray();
425
426 auto file_open = Dart::file_open_callback();
427 auto file_write = Dart::file_write_callback();
428 auto file_close = Dart::file_close_callback();
429 if ((file_open == nullptr) || (file_write == nullptr) ||
430 (file_close == nullptr)) {
431 OS::PrintErr("warning: Could not access file callbacks.");
432 return;
433 }
434
435 const char* filename = FLAG_print_instructions_sizes_to;
436 void* file = file_open(filename, /*write=*/true);
437 if (file == nullptr) {
438 OS::PrintErr("warning: Failed to write instruction sizes: %s\n", filename);
439 return;
440 }
441
442 char* output = nullptr;
443 intptr_t output_length = 0;
444 js.Steal(&output, &output_length);
445 file_write(output, output_length, file);
446 free(output);
447 file_close(file);
448}
449
451 if (FLAG_print_instruction_stats) {
453 }
454
455 if (FLAG_print_instructions_sizes_to != nullptr) {
457 }
458}
459#endif
460
461void ImageWriter::Write(NonStreamingWriteStream* clustered_stream, bool vm) {
462 Heap* heap = thread_->heap();
463 TIMELINE_DURATION(thread_, Isolate, "WriteInstructions");
464
465 // Handlify collected raw pointers as building the names below
466 // will allocate on the Dart heap.
467 for (intptr_t i = 0; i < instructions_.length(); i++) {
469 if (data.trampoline_length != 0) continue;
470
471 data.insns_ = &Instructions::Handle(zone_, data.raw_insns_);
472 ASSERT(data.raw_code_ != nullptr);
473 data.code_ = &Code::Handle(zone_, data.raw_code_);
474
475 // Reset object id as an isolate snapshot after a VM snapshot will not use
476 // the VM snapshot's text image.
477 heap->SetObjectId(data.insns_->ptr(), 0);
478 }
479 for (auto& data : objects_) {
480 if (data.is_object()) {
481 data.obj = &Object::Handle(zone_, data.raw_obj);
482#if defined(SNAPSHOT_BACKTRACE)
483 data.parent = &Object::Handle(zone_, data.raw_parent);
484#endif
485 }
486 }
487
488 // Once we have everything handlified we are going to do convert raw bytes
489 // to string objects. String is used for simplicity as a bit container,
490 // can't use TypedData because it has an internal pointer (data_) field.
491 for (auto& data : objects_) {
492 if (!data.is_object()) {
493 const auto bytes = data.bytes;
494 data.obj = &Object::Handle(
495 zone_, OneByteString::New(bytes.buf, bytes.length, Heap::kOld));
496#if defined(SNAPSHOT_BACKTRACE)
497 data.parent = &Object::null_object();
498#endif
499 data.set_is_object(true);
500 String::Cast(*data.obj).Hash();
501 free(bytes.buf);
502 }
503 }
504
505 // Needs to happen before WriteText, as we add information about the
506 // BSSsection in the text section as an initial InstructionsSection object.
507 WriteBss(vm);
508
510 WriteText(vm);
511
512 // Append the direct-mapped RO data objects after the clustered snapshot
513 // and then for ELF and assembly outputs, add appropriate sections with
514 // that combined data.
516 WriteROData(clustered_stream, vm);
517}
518
521 // Heap page starts here.
522 intptr_t section_start = stream->Position();
523
524 stream->WriteWord(next_data_offset_); // Data length.
525 stream->WriteWord(Image::kNoInstructionsSection);
526 // Zero values for the rest of the Image object header bytes.
527 stream->Align(Image::kHeaderSize);
528 ASSERT_EQUAL(stream->Position() - section_start, Image::kHeaderSize);
529#if defined(DART_PRECOMPILER)
530 if (profile_writer_ != nullptr) {
531 // Attribute the Image header to the artificial root.
534 }
535#endif
536
537 // Heap page objects start here.
538
539 for (auto entry : objects_) {
540 ASSERT(entry.is_object());
541 const Object& obj = *entry.obj;
542#if defined(DART_PRECOMPILER)
543 AutoTraceImage(obj, section_start, stream);
544 const char* object_name = namer_.SnapshotNameFor(entry);
545#endif
546 auto const object_start = stream->Position();
547
548 NoSafepointScope no_safepoint;
549
550 // Write object header with the mark and read-only bits set.
551 stream->WriteTargetWord(GetMarkedTags(obj));
552 if (obj.IsCompressedStackMaps()) {
553 const CompressedStackMaps& map = CompressedStackMaps::Cast(obj);
554 const intptr_t payload_size = map.payload_size();
555 stream->WriteFixed<uint32_t>(
556 map.ptr()->untag()->payload()->flags_and_size());
557 stream->WriteBytes(map.ptr()->untag()->payload()->data(), payload_size);
558 } else if (obj.IsCodeSourceMap()) {
559 const CodeSourceMap& map = CodeSourceMap::Cast(obj);
560 stream->WriteTargetWord(map.Length());
561 ASSERT_EQUAL(stream->Position() - object_start,
563 stream->WriteBytes(map.Data(), map.Length());
564 } else if (obj.IsPcDescriptors()) {
565 const PcDescriptors& desc = PcDescriptors::Cast(obj);
566 stream->WriteTargetWord(desc.Length());
567 ASSERT_EQUAL(stream->Position() - object_start,
569 stream->WriteBytes(desc.ptr()->untag()->data(), desc.Length());
570 } else if (obj.IsString()) {
571 const String& str = String::Cast(obj);
574
575#if !defined(HASH_IN_OBJECT_HEADER)
576 stream->WriteTargetWord(static_cast<uword>(str.ptr()->untag()->hash()));
577#endif
578 stream->WriteTargetWord(static_cast<uword>(str.ptr()->untag()->length()));
579 ASSERT_EQUAL(stream->Position() - object_start,
581 stream->WriteBytes(
582 str.IsOneByteString()
583 ? static_cast<const void*>(OneByteString::DataStart(str))
584 : static_cast<const void*>(TwoByteString::DataStart(str)),
585 str.Length() * (str.IsOneByteString()
588 } else {
589 const Class& clazz = Class::Handle(obj.clazz());
590 FATAL("Unsupported class %s in rodata section.\n", clazz.ToCString());
591 }
593 ASSERT_EQUAL(stream->Position() - object_start, SizeInSnapshot(obj));
594#if defined(DART_PRECOMPILER)
595 AddDataSymbol(object_name, object_start, stream->Position() - object_start);
596#endif
597 }
598}
599
600static constexpr uword kReadOnlyGCBits =
605
607 intptr_t size,
608 bool is_canonical /* = false */) {
609 // UntaggedObject::SizeTag expects a size divisible by kObjectAlignment and
610 // checks this in debug mode, but the size on the target machine may not be
611 // divisible by the host machine's object alignment if they differ.
612 //
613 // We define [adjusted_size] as [size] * m, where m is the host alignment
614 // divided by the target alignment. This means [adjusted_size] encodes on the
615 // host machine to the same bits that decode to [size] on the target machine.
616 // That is,
617 // [adjusted_size] / host align ==
618 // [size] * (host align / target align) / host align ==
619 // [size] / target align
620 //
621 // Since alignments are always powers of 2, we use shifts and logs.
622 const intptr_t adjusted_size =
625
627 UntaggedObject::SizeTag::encode(adjusted_size) |
629}
630
632 uword tags = GetMarkedTags(obj.ptr()->untag()->GetClassId(),
633 SizeInSnapshot(obj), obj.IsCanonical());
634#if defined(HASH_IN_OBJECT_HEADER)
635 tags = UntaggedObject::HashTag::update(obj.ptr()->untag()->GetHeaderHash(),
636 tags);
637#endif
638 return tags;
639}
640
641const char* ImageWriter::SectionSymbol(ProgramSection section, bool vm) {
642 switch (section) {
652 }
653 UNREACHABLE();
654 return nullptr;
655}
656
657#if (defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)) && \
658 defined(TARGET_ARCH_ARM64)
659// When generating ARM64 Mach-O LLVM tends to generate Compact Unwind Info
660// (__unwind_info) rather than traditional DWARF unwinding information
661// (__eh_frame).
662//
663// Unfortunately when generating __unwind_info LLVM seems to only apply CFI
664// rules to the region between two non-local symbols that contains these CFI
665// directives. In other words given:
666//
667// Abc:
668// .cfi_startproc
669// .cfi_def_cfa x29, 16
670// .cfi_offset x30, -8
671// .cfi_offset x29, -16
672// ;; ...
673// Xyz:
674// ;; ...
675// .cfi_endproc
676//
677// __unwind_info would specify proper unwinding information only for the region
678// between Abc and Xyz symbols. And the region Xyz onwards will have no
679// unwinding information.
680//
681// There also seems to be a difference in how unwinding information is
682// canonicalized and compressed: when building __unwind_info from CFI directives
683// LLVM will fold together similar entries, the same does not happen for
684// __eh_frame. This means that emitting CFI directives for each function would
685// balloon the size of __eh_frame.
686//
687// Hence to work around the problem of incorrect __unwind_info without
688// ballooning snapshot size when __eh_frame is generated we choose to emit CFI
689// directives per function specifically on ARM64 Mac OS X and iOS.
690//
691// See also |useCompactUnwind| method in LLVM (https://github.com/llvm/llvm-project/blob/b27430f9f46b88bcd54d992debc8d72e131e1bd0/llvm/lib/MC/MCObjectFileInfo.cpp#L28-L50)
692#define EMIT_UNWIND_DIRECTIVES_PER_FUNCTION 1
693#endif
694
696 const bool bare_instruction_payloads = FLAG_precompiled_mode;
697
698 // Start snapshot at page boundary.
699 intptr_t alignment_padding = 0;
701 &alignment_padding)) {
702 return;
703 }
704
705 intptr_t text_offset = 0;
706#if defined(DART_PRECOMPILER)
707 // Parent used for later profile objects. Starts off as the Image. When
708 // writing bare instructions payloads, this is later updated with the
709 // InstructionsSection object which contains all the bare payloads.
710 V8SnapshotProfileWriter::ObjectId parent_id(offset_space_, text_offset);
711#endif
712
713 // This head also provides the gap to make the instructions snapshot
714 // look like a Page.
715 const intptr_t image_size = Utils::RoundUp(
717 text_offset += WriteTargetWord(image_size);
718 // Output the offset to the InstructionsSection object from the start of the
719 // image, if any.
720 text_offset +=
721 WriteTargetWord(FLAG_precompiled_mode ? Image::kHeaderSize
722 : Image::kNoInstructionsSection);
723 // Zero values for the rest of the Image object header bytes.
724 text_offset += Align(Image::kHeaderSize, 0, text_offset);
725 ASSERT_EQUAL(text_offset, Image::kHeaderSize);
726
727#if defined(DART_PRECOMPILER)
728 const char* instructions_symbol = SectionSymbol(ProgramSection::Text, vm);
729 ASSERT(instructions_symbol != nullptr);
730 intptr_t instructions_label = SectionLabel(ProgramSection::Text, vm);
731 ASSERT(instructions_label > 0);
732 const char* bss_symbol = SectionSymbol(ProgramSection::Bss, vm);
733 ASSERT(bss_symbol != nullptr);
734 intptr_t bss_label = SectionLabel(ProgramSection::Bss, vm);
735 ASSERT(bss_label > 0);
736
737 if (profile_writer_ != nullptr) {
739 instructions_symbol);
741 parent_id, ImageWriter::kTextAlignment + alignment_padding);
742 profile_writer_->AddRoot(parent_id);
743 }
744
745 if (FLAG_precompiled_mode) {
746 const intptr_t section_header_length =
748 // Calculated using next_text_offset_, which doesn't include post-payload
749 // padding to object alignment. Note that if not in bare instructions mode,
750 // the section has no contents, instead the instructions objects follow it.
751 const intptr_t section_payload_length =
752 bare_instruction_payloads
753 ? next_text_offset_ - text_offset - section_header_length
754 : 0;
755 const intptr_t section_size =
757 section_payload_length);
758
760 if (profile_writer_ != nullptr) {
762 instructions_symbol);
764 section_size - section_payload_length);
765 const intptr_t element_offset = id.nonce() - parent_id.nonce();
767 parent_id,
769 // Later objects will have the InstructionsSection as a parent if in
770 // bare instructions mode, otherwise the image.
771 if (bare_instruction_payloads) {
772 parent_id = id;
773 }
774 }
775
776 // Add the RawInstructionsSection header.
777 text_offset +=
778 WriteTargetWord(GetMarkedTags(kInstructionsSectionCid, section_size));
779 // An InstructionsSection has five fields:
780 // 1) The length of the payload.
781 text_offset += WriteTargetWord(section_payload_length);
782 // 2) The BSS offset from this section.
783 text_offset += Relocation(text_offset, instructions_label, bss_label);
784 // 3) The relocated address of the instructions.
785 text_offset += RelocatedAddress(text_offset, instructions_label);
786 // 4) The GNU build ID note offset from this section.
787 text_offset += Relocation(text_offset, instructions_label,
788 SectionLabel(ProgramSection::BuildId, vm));
789
790 const intptr_t section_contents_alignment =
791 bare_instruction_payloads
794 const intptr_t alignment_offset =
796 const intptr_t expected_size =
797 bare_instruction_payloads
800 text_offset +=
801 Align(section_contents_alignment, alignment_offset, text_offset);
802 ASSERT_EQUAL(text_offset - id.nonce(), expected_size);
803 }
804#endif
805
806#if !defined(EMIT_UNWIND_DIRECTIVES_PER_FUNCTION)
808#endif
809
810#if defined(DART_PRECOMPILER)
812#endif
813
814 // We don't expect more than 64 bytes of padding.
815 uint8_t padding_bytes[64];
816 memset(&padding_bytes[0], 0, sizeof(padding_bytes));
817
819 for (intptr_t i = 0; i < instructions_.length(); i++) {
820 auto& data = instructions_[i];
821 const bool is_trampoline = data.trampoline_bytes != nullptr;
822 const bool is_padding =
823 data.trampoline_bytes == nullptr && data.trampoline_length != 0;
824 ASSERT_EQUAL(data.text_offset_, text_offset);
825
826#if defined(DART_PRECOMPILER)
827 const char* object_name = namer_.SnapshotNameFor(data);
828
829 if (profile_writer_ != nullptr) {
831 auto const type = is_trampoline ? trampoline_type_
832 : is_padding ? padding_type_
834 const intptr_t size = (is_trampoline || is_padding)
835 ? data.trampoline_length
836 : SizeInSnapshot(data.insns_->ptr());
837 profile_writer_->SetObjectTypeAndName(id, type, object_name);
839 const intptr_t element_offset = id.nonce() - parent_id.nonce();
841 parent_id,
843 }
844#endif
845
846 if (is_trampoline) {
847 text_offset += WriteBytes(data.trampoline_bytes, data.trampoline_length);
848 delete[] data.trampoline_bytes;
849 data.trampoline_bytes = nullptr;
850 continue;
851 }
852
853 if (is_padding) {
854 text_offset += WriteBytes(padding_bytes, data.trampoline_length);
855 continue;
856 }
857
858 const intptr_t instr_start = text_offset;
859 const auto& insns = *data.insns_;
860
861 // 1. Write from the object start to the payload start. This includes the
862 // object header and the fixed fields. Not written for AOT snapshots using
863 // bare instructions.
864 if (!bare_instruction_payloads) {
865 NoSafepointScope no_safepoint;
866
867 // Write Instructions with the mark and read-only bits set.
868 text_offset += WriteTargetWord(GetMarkedTags(insns));
869 text_offset += WriteFixed(insns.untag()->size_and_flags_);
870 text_offset +=
873 text_offset);
874 }
875
876 ASSERT_EQUAL(text_offset - instr_start,
878
879#if defined(DART_PRECOMPILER)
880 const auto& code = *data.code_;
881 // 2. Add a symbol for the code at the entry point in precompiled snapshots.
882 // Linux's perf uses these labels.
883 AddCodeSymbol(code, object_name, text_offset);
884#endif
885
886#if defined(EMIT_UNWIND_DIRECTIVES_PER_FUNCTION)
888#endif
889
890 {
891 NoSafepointScope no_safepoint;
892
893 // 3. Write from the payload start to payload end. For AOT snapshots
894 // with bare instructions, this is the only part serialized other than
895 // any padding needed for alignment.
896 auto const payload_start =
897 reinterpret_cast<const uint8_t*>(insns.PayloadStart());
898 // Double-check the payload alignment, since we will load and write
899 // target-sized words starting from that address.
901 const uword payload_size = insns.Size();
902 auto const payload_end = payload_start + payload_size;
903 auto cursor = payload_start;
904#if defined(DART_PRECOMPILER)
905 descriptors = code.pc_descriptors();
907 descriptors, /*kind_mask=*/UntaggedPcDescriptors::kBSSRelocation);
908 while (iterator.MoveNext()) {
909 // We only generate BSS relocations in the precompiler.
910 ASSERT(FLAG_precompiled_mode);
911 auto const next_reloc_offset = iterator.PcOffset();
912 auto const next_reloc_address = payload_start + next_reloc_offset;
913 // We only generate BSS relocations that are target word-sized and at
914 // target word-aligned offsets in the payload. Double-check this.
915 ASSERT(
916 Utils::IsAligned(next_reloc_address, compiler::target::kWordSize));
917 text_offset += WriteBytes(cursor, next_reloc_address - cursor);
918
919 // The instruction stream at the relocation position holds the target
920 // offset into the BSS section.
921 const auto target_offset =
922 *reinterpret_cast<const compiler::target::word*>(
923 next_reloc_address);
924 text_offset += Relocation(text_offset, instructions_label, text_offset,
925 bss_label, target_offset);
926 cursor = next_reloc_address + compiler::target::kWordSize;
927 }
928#endif
929 text_offset += WriteBytes(cursor, payload_end - cursor);
930 }
931
932 // 4. Add appropriate padding. Note we can't simply copy from the object
933 // because the host object may have less alignment filler than the target
934 // object in the cross-word case.
935 const intptr_t alignment =
936 bare_instruction_payloads
939 text_offset += AlignWithBreakInstructions(alignment, text_offset);
940
941 ASSERT_EQUAL(text_offset - instr_start, SizeInSnapshot(insns.ptr()));
942#if defined(EMIT_UNWIND_DIRECTIVES_PER_FUNCTION)
944#endif
945 }
946
947 // Should be a no-op unless writing bare instruction payloads, in which case
948 // we need to add post-payload padding for the InstructionsSection object.
949 // Since this follows instructions, we'll use break instructions for padding.
950 ASSERT(bare_instruction_payloads ||
951 Utils::IsAligned(text_offset,
953 text_offset += AlignWithBreakInstructions(
955
956 ASSERT_EQUAL(text_offset, image_size);
957
958#if !defined(EMIT_UNWIND_DIRECTIVES_PER_FUNCTION)
960#endif
961
962 ExitSection(ProgramSection::Text, vm, text_offset);
963}
964
965intptr_t ImageWriter::AlignWithBreakInstructions(intptr_t alignment,
966 intptr_t offset) {
967 intptr_t bytes_written = 0;
968 uword remaining;
969 for (remaining = Utils::RoundUp(offset, alignment) - offset;
970 remaining >= compiler::target::kWordSize;
971 remaining -= compiler::target::kWordSize) {
973 }
974 // clang-format off
975#if defined(TARGET_ARCH_ARM)
976 // All instructions are 4 bytes long on ARM architectures, so on 32-bit ARM
977 // there won't be any padding.
978 ASSERT_EQUAL(remaining, 0);
979#elif defined(TARGET_ARCH_ARM64)
980 // All instructions are 4 bytes long on ARM architectures, so on 64-bit ARM
981 // there is only 0 or 4 bytes of padding.
982 if (remaining != 0) {
983 ASSERT_EQUAL(remaining, 4);
984 bytes_written += WriteBytes(&kBreakInstructionFiller, remaining);
985 }
986#elif defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_IA32) || \
987 defined(TARGET_ARCH_RISCV32) || defined(TARGET_ARCH_RISCV64)
988 // The break instruction is a single byte, repeated to fill a word.
989 bytes_written += WriteBytes(&kBreakInstructionFiller, remaining);
990#else
991#error Unexpected architecture.
992#endif
993 // clang-format on
994 ASSERT_EQUAL(bytes_written, Utils::RoundUp(offset, alignment) - offset);
995 return bytes_written;
996}
997
998#if defined(DART_PRECOMPILER)
999
1000// Indices are log2(size in bytes).
1001static constexpr const char* kSizeDirectives[] = {".byte", ".2byte", ".long",
1002 ".quad"};
1003
1004static constexpr const char* kWordDirective =
1005 kSizeDirectives[compiler::target::kWordSizeLog2];
1006
1007class DwarfAssemblyStream : public DwarfWriteStream {
1008 public:
1009 explicit DwarfAssemblyStream(Zone* zone,
1010 BaseWriteStream* stream,
1011 const IntMap<const char*>& label_to_name)
1012 : zone_(ASSERT_NOTNULL(zone)),
1013 stream_(ASSERT_NOTNULL(stream)),
1014 label_to_name_(label_to_name) {}
1015
1016 void sleb128(intptr_t value) { stream_->Printf(".sleb128 %" Pd "\n", value); }
1017 void uleb128(uintptr_t value) {
1018 stream_->Printf(".uleb128 %" Pd "\n", value);
1019 }
1020 void u1(uint8_t value) {
1021 stream_->Printf("%s %u\n", kSizeDirectives[kInt8SizeLog2], value);
1022 }
1023 void u2(uint16_t value) {
1024 stream_->Printf("%s %u\n", kSizeDirectives[kInt16SizeLog2], value);
1025 }
1026 void u4(uint32_t value) {
1027 stream_->Printf("%s %" Pu32 "\n", kSizeDirectives[kInt32SizeLog2], value);
1028 }
1029 void u8(uint64_t value) {
1030 stream_->Printf("%s %" Pu64 "\n", kSizeDirectives[kInt64SizeLog2], value);
1031 }
1032 void string(const char* cstr) { // NOLINT
1033 stream_->Printf(".string \"%s\"\n", cstr); // NOLINT
1034 }
1035 void WritePrefixedLength(const char* prefix, std::function<void()> body) {
1036 ASSERT(prefix != nullptr);
1037 const char* const length_prefix_symbol =
1038 OS::SCreate(zone_, ".L%s_length_prefix", prefix);
1039 // Assignment to temp works around buggy Mac assembler.
1040 stream_->Printf("L%s_size = .L%s_end - .L%s_start\n", prefix, prefix,
1041 prefix);
1042 // We assume DWARF v2 currently, so all sizes are 32-bit.
1043 stream_->Printf("%s: %s L%s_size\n", length_prefix_symbol,
1044 kSizeDirectives[kInt32SizeLog2], prefix);
1045 // All sizes for DWARF sections measure the size of the section data _after_
1046 // the size value.
1047 stream_->Printf(".L%s_start:\n", prefix);
1048 body();
1049 stream_->Printf(".L%s_end:\n", prefix);
1050 }
1051 void OffsetFromSymbol(intptr_t label, intptr_t offset) {
1052 const char* symbol = label_to_name_.Lookup(label);
1053 ASSERT(symbol != nullptr);
1054 if (offset == 0) {
1055 PrintNamedAddress(symbol);
1056 } else {
1057 PrintNamedAddressWithOffset(symbol, offset);
1058 }
1059 }
1060
1061 // No-op, we'll be using labels.
1062 void InitializeAbstractOrigins(intptr_t size) {}
1063 void RegisterAbstractOrigin(intptr_t index) {
1064 // Label for DW_AT_abstract_origin references
1065 stream_->Printf(".Lfunc%" Pd ":\n", index);
1066 }
1067 void AbstractOrigin(intptr_t index) {
1068 // Assignment to temp works around buggy Mac assembler.
1069 stream_->Printf("Ltemp%" Pd " = .Lfunc%" Pd " - %s\n", temp_, index,
1070 kDebugInfoLabel);
1071 stream_->Printf("%s Ltemp%" Pd "\n", kSizeDirectives[kInt32SizeLog2],
1072 temp_);
1073 temp_++;
1074 }
1075
1076 // Methods for writing the assembly prologues for various DWARF sections.
1077 void AbbreviationsPrologue() {
1078#if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
1079 stream_->WriteString(".section __DWARF,__debug_abbrev,regular,debug\n");
1080#elif defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
1081 defined(DART_TARGET_OS_FUCHSIA)
1082 stream_->WriteString(".section .debug_abbrev,\"\"\n");
1083#else
1084 UNIMPLEMENTED();
1085#endif
1086 }
1087 void DebugInfoPrologue() {
1088#if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
1089 stream_->WriteString(".section __DWARF,__debug_info,regular,debug\n");
1090#elif defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
1091 defined(DART_TARGET_OS_FUCHSIA)
1092 stream_->WriteString(".section .debug_info,\"\"\n");
1093#else
1094 UNIMPLEMENTED();
1095#endif
1096 // Used to calculate abstract origin values.
1097 stream_->Printf("%s:\n", kDebugInfoLabel);
1098 }
1099 void LineNumberProgramPrologue() {
1100#if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
1101 stream_->WriteString(".section __DWARF,__debug_line,regular,debug\n");
1102#elif defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
1103 defined(DART_TARGET_OS_FUCHSIA)
1104 stream_->WriteString(".section .debug_line,\"\"\n");
1105#else
1106 UNIMPLEMENTED();
1107#endif
1108 }
1109
1110 private:
1111 static constexpr const char* kDebugInfoLabel = ".Ldebug_info";
1112
1113 void PrintNamedAddress(const char* name) {
1114 stream_->Printf("%s \"%s\"\n", kWordDirective, name);
1115 }
1116 void PrintNamedAddressWithOffset(const char* name, intptr_t offset) {
1117 stream_->Printf("%s \"%s\" + %" Pd "\n", kWordDirective, name, offset);
1118 }
1119
1120 Zone* const zone_;
1121 BaseWriteStream* const stream_;
1122 const IntMap<const char*>& label_to_name_;
1123 intptr_t temp_ = 0;
1124
1125 DISALLOW_COPY_AND_ASSIGN(DwarfAssemblyStream);
1126};
1127
1128static inline Dwarf* AddDwarfIfUnstripped(
1129 Zone* zone,
1130 bool strip,
1131 Elf* elf,
1132 const Trie<const char>* deobfuscation_trie) {
1133 if (!strip) {
1134 if (elf != nullptr) {
1135 // Reuse the existing DWARF object.
1136 ASSERT(elf->dwarf() != nullptr);
1137 return elf->dwarf();
1138 }
1139 return new (zone) Dwarf(zone, deobfuscation_trie);
1140 }
1141 return nullptr;
1142}
1143
1144AssemblyImageWriter::AssemblyImageWriter(
1145 Thread* thread,
1146 BaseWriteStream* stream,
1147 const Trie<const char>* deobfuscation_trie,
1148 bool strip,
1149 Elf* debug_elf)
1150 : ImageWriter(thread, /*generates_assembly=*/true, deobfuscation_trie),
1151 assembly_stream_(stream),
1152 assembly_dwarf_(
1153 AddDwarfIfUnstripped(zone_, strip, debug_elf, deobfuscation_trie)),
1154 debug_elf_(debug_elf),
1155 label_to_symbol_name_(zone_) {
1156 // Set up the label mappings for the section symbols for use in relocations.
1157 for (intptr_t i = 0; i < kNumProgramSections; i++) {
1158 auto const section = static_cast<ProgramSection>(i);
1159
1160 auto const vm_name = SectionSymbol(section, /*vm=*/true);
1161 auto const vm_label = SectionLabel(section, /*vm=*/true);
1162 label_to_symbol_name_.Insert(vm_label, vm_name);
1163
1164 auto const isolate_name = SectionSymbol(section, /*vm=*/false);
1165 auto const isolate_label = SectionLabel(section, /*vm=*/false);
1166 if (vm_label != isolate_label) {
1167 label_to_symbol_name_.Insert(isolate_label, isolate_name);
1168 } else {
1169 // Make sure the names also match.
1170 ASSERT_EQUAL(strcmp(vm_name, isolate_name), 0);
1171 }
1172 }
1173}
1174
1175void AssemblyImageWriter::Finalize() {
1176 if (assembly_dwarf_ != nullptr) {
1177 DwarfAssemblyStream dwarf_stream(zone_, assembly_stream_,
1178 label_to_symbol_name_);
1179 dwarf_stream.AbbreviationsPrologue();
1180 assembly_dwarf_->WriteAbbreviations(&dwarf_stream);
1181 dwarf_stream.DebugInfoPrologue();
1182 assembly_dwarf_->WriteDebugInfo(&dwarf_stream);
1183 dwarf_stream.LineNumberProgramPrologue();
1184 assembly_dwarf_->WriteLineNumberProgram(&dwarf_stream);
1185 }
1186 if (debug_elf_ != nullptr) {
1187 debug_elf_->Finalize();
1188 }
1189
1190#if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
1191 defined(DART_TARGET_OS_FUCHSIA)
1192 // Non-executable stack.
1193#if defined(TARGET_ARCH_ARM)
1194 assembly_stream_->WriteString(".section .note.GNU-stack,\"\",%progbits\n");
1195#else
1196 assembly_stream_->WriteString(".section .note.GNU-stack,\"\",@progbits\n");
1197#endif
1198#endif
1199}
1200
1201void ImageWriter::SnapshotTextObjectNamer::AddNonUniqueNameFor(
1202 BaseTextBuffer* buffer,
1203 const Object& object) {
1204 if (object.IsCode()) {
1205 const Code& code = Code::Cast(object);
1206 if (code.IsStubCode()) {
1207 buffer->AddString("stub ");
1208 insns_ = code.instructions();
1209 const char* name = StubCode::NameOfStub(insns_.EntryPoint());
1210 ASSERT(name != nullptr);
1211 buffer->AddString(name);
1212 } else {
1213 if (code.IsAllocationStubCode()) {
1214 buffer->AddString("new ");
1215 } else if (code.IsTypeTestStubCode()) {
1216 buffer->AddString("assert type is ");
1217 } else {
1218 ASSERT(code.IsFunctionCode());
1219 }
1220 owner_ = code.owner();
1221 AddNonUniqueNameFor(buffer, owner_);
1222 }
1223 } else if (object.IsClass()) {
1224 const char* name = Class::Cast(object).UserVisibleNameCString();
1225 const char* deobfuscated_name =
1226 ImageWriter::Deobfuscate(zone_, deobfuscation_trie_, name);
1227 buffer->AddString(deobfuscated_name);
1228 } else if (object.IsAbstractType()) {
1229 const AbstractType& type = AbstractType::Cast(object);
1230 if (deobfuscation_trie_ == nullptr) {
1231 // Print directly to the output buffer.
1233 } else {
1234 // Use an intermediate buffer for deobfuscation purposes.
1235 ZoneTextBuffer temp_buffer(zone_);
1236 type.PrintName(Object::kUserVisibleName, &temp_buffer);
1237 const char* deobfuscated_name = ImageWriter::Deobfuscate(
1238 zone_, deobfuscation_trie_, temp_buffer.buffer());
1239 buffer->AddString(deobfuscated_name);
1240 }
1241 } else if (object.IsFunction()) {
1242 const Function& func = Function::Cast(object);
1243 NameFormattingParams params(
1245 if (deobfuscation_trie_ == nullptr) {
1246 // Print directly to the output buffer.
1247 func.PrintName(params, buffer);
1248 } else {
1249 // Use an intermediate buffer for deobfuscation purposes.
1250 ZoneTextBuffer temp_buffer(zone_);
1251 func.PrintName(params, &temp_buffer);
1252 const char* deobfuscated_name = ImageWriter::Deobfuscate(
1253 zone_, deobfuscation_trie_, temp_buffer.buffer());
1254 buffer->AddString(deobfuscated_name);
1255 }
1256 } else if (object.IsCompressedStackMaps()) {
1257 buffer->AddString("CompressedStackMaps");
1258 } else if (object.IsPcDescriptors()) {
1259 buffer->AddString("PcDescriptors");
1260 } else if (object.IsCodeSourceMap()) {
1261 buffer->AddString("CodeSourceMap");
1262 } else if (object.IsString()) {
1263 const String& str = String::Cast(object);
1264 if (str.IsOneByteString()) {
1265 buffer->AddString("OneByteString");
1266 } else if (str.IsTwoByteString()) {
1267 buffer->AddString("TwoByteString");
1268 }
1269 } else {
1270 UNREACHABLE();
1271 }
1272}
1273
1274void ImageWriter::SnapshotTextObjectNamer::ModifyForAssembly(
1275 BaseTextBuffer* buffer) {
1276 if (buffer->buffer()[0] == 'L') {
1277 // Assembler treats labels starting with `L` as local which can cause
1278 // some issues down the line e.g. on Mac the linker might fail to encode
1279 // compact unwind information because multiple functions end up being
1280 // treated as a single function. See https://github.com/flutter/flutter/issues/102281.
1281 //
1282 // Avoid this by prepending an underscore.
1283 auto* const result = OS::SCreate(zone_, "_%s", buffer->buffer());
1284 buffer->Clear();
1285 buffer->AddString(result);
1286 }
1287 auto* const pair = usage_count_.Lookup(buffer->buffer());
1288 if (pair == nullptr) {
1289 usage_count_.Insert({buffer->buffer(), 1});
1290 } else {
1291 buffer->Printf(" (#%" Pd ")", ++pair->value);
1292 }
1293}
1294
1295const char* ImageWriter::SnapshotTextObjectNamer::SnapshotNameFor(
1296 const InstructionsData& data) {
1297 ZoneTextBuffer printer(zone_);
1298 if (data.trampoline_bytes != nullptr) {
1299 printer.AddString("Trampoline");
1300 } else if (data.trampoline_length != 0) {
1301 printer.AddString("Padding");
1302 } else {
1303 AddNonUniqueNameFor(&printer, *data.code_);
1304 }
1305 if (for_assembly_) {
1306 ModifyForAssembly(&printer);
1307 }
1308 return printer.buffer();
1309}
1310
1311const char* ImageWriter::SnapshotTextObjectNamer::SnapshotNameFor(
1312 const ObjectData& data) {
1313 ASSERT(data.is_object());
1314 ZoneTextBuffer printer(zone_);
1315 if (data.is_original_object()) {
1316 const Object& obj = *data.obj;
1317 AddNonUniqueNameFor(&printer, obj);
1318#if defined(SNAPSHOT_BACKTRACE)
1319 // It's less useful knowing the parent of a String than other read-only
1320 // data objects, and this avoids us having to handle other classes
1321 // in AddNonUniqueNameFor.
1322 if (!obj.IsString()) {
1323 const Object& parent = *data.parent;
1324 if (!parent.IsNull()) {
1325 printer.AddString(" (");
1326 AddNonUniqueNameFor(&printer, parent);
1327 printer.AddString(")");
1328 }
1329 }
1330#endif
1331 } else {
1332 printer.AddString("RawBytes");
1333 }
1334 if (for_assembly_) {
1335 ModifyForAssembly(&printer);
1336 }
1337 return printer.buffer();
1338}
1339
1340Trie<const char>* ImageWriter::CreateReverseObfuscationTrie(Thread* thread) {
1341 auto* const zone = thread->zone();
1342 auto* const map_array = thread->isolate_group()->obfuscation_map();
1343 if (map_array == nullptr) return nullptr;
1344
1345 Trie<const char>* trie = nullptr;
1346 for (intptr_t i = 0; map_array[i] != nullptr; i += 2) {
1347 auto const key = map_array[i];
1348 auto const value = map_array[i + 1];
1349 ASSERT(value != nullptr);
1350 // Don't include identity mappings.
1351 if (strcmp(key, value) == 0) continue;
1352 // Otherwise, any value in the obfuscation map should be a valid key.
1353 ASSERT(Trie<const char>::IsValidKey(value));
1354 trie = Trie<const char>::AddString(zone, trie, value, key);
1355 }
1356 return trie;
1357}
1358
1359const char* ImageWriter::Deobfuscate(Zone* zone,
1360 const Trie<const char>* trie,
1361 const char* cstr) {
1362 if (trie == nullptr) return cstr;
1363 TextBuffer buffer(256);
1364 // Used to avoid Zone-allocating strings if no deobfuscation was performed.
1365 bool changed = false;
1366 intptr_t i = 0;
1367 while (cstr[i] != '\0') {
1368 intptr_t offset;
1369 auto const value = trie->Lookup(cstr + i, &offset);
1370 if (offset == 0) {
1371 // The first character was an invalid key element (that isn't the null
1372 // terminator due to the while condition), copy it and skip to the next.
1373 buffer.AddChar(cstr[i++]);
1374 } else if (value != nullptr) {
1375 changed = true;
1376 buffer.AddString(value);
1377 } else {
1378 buffer.AddRaw(reinterpret_cast<const uint8_t*>(cstr + i), offset);
1379 }
1380 i += offset;
1381 }
1382 if (!changed) return cstr;
1383 return OS::SCreate(zone, "%s", buffer.buffer());
1384}
1385
1386void AssemblyImageWriter::WriteBss(bool vm) {
1387 EnterSection(ProgramSection::Bss, vm, ImageWriter::kBssAlignment);
1388 auto const entry_count =
1390 for (intptr_t i = 0; i < entry_count; i++) {
1391 // All bytes in the .bss section must be zero.
1392 WriteTargetWord(0);
1393 }
1394 ExitSection(ProgramSection::Bss, vm,
1395 entry_count * compiler::target::kWordSize);
1396}
1397
1398void AssemblyImageWriter::WriteROData(NonStreamingWriteStream* clustered_stream,
1399 bool vm) {
1400 if (!EnterSection(ProgramSection::Data, vm, ImageWriter::kRODataAlignment)) {
1401 return;
1402 }
1403 // The clustered stream already has some data on it from the serializer, so
1404 // make sure that the read-only objects start at the appropriate alignment
1405 // within the stream, as we'll write the entire clustered stream to the
1406 // assembly output (which was aligned in EnterSection).
1407 const intptr_t start_position = clustered_stream->Position();
1408 clustered_stream->Align(ImageWriter::kRODataAlignment);
1409 if (profile_writer_ != nullptr) {
1410 // Attribute any padding needed to the artificial root.
1411 const intptr_t padding = clustered_stream->Position() - start_position;
1412 profile_writer_->AttributeBytesTo(
1414 }
1415 // First write the read-only data objects to the clustered stream.
1416 ImageWriter::WriteROData(clustered_stream, vm);
1417 // Next, write the bytes of the clustered stream (along with any symbols
1418 // if appropriate) to the assembly output.
1419 const uint8_t* bytes = clustered_stream->buffer();
1420 const intptr_t len = clustered_stream->bytes_written();
1421 intptr_t last_position = 0;
1422 for (const auto& symbol : *current_symbols_) {
1423 WriteBytes(bytes + last_position, symbol.offset - last_position);
1424 assembly_stream_->Printf("\"%s\":\n", symbol.name);
1425#if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
1426 defined(DART_TARGET_OS_FUCHSIA)
1427 // Output size and type of the read-only data symbol to the assembly stream.
1428 assembly_stream_->Printf(".size \"%s\", %zu\n", symbol.name, symbol.size);
1429 assembly_stream_->Printf(".type \"%s\", %%object\n", symbol.name);
1430#elif defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
1431 // MachO symbol tables don't include the size of the symbol, so don't bother
1432 // printing it to the assembly output.
1433#else
1434 UNIMPLEMENTED();
1435#endif
1436 last_position = symbol.offset;
1437 }
1438 WriteBytes(bytes + last_position, len - last_position);
1439 ExitSection(ProgramSection::Data, vm, len);
1440}
1441
1442bool AssemblyImageWriter::EnterSection(ProgramSection section,
1443 bool vm,
1444 intptr_t alignment,
1445 intptr_t* alignment_padding) {
1446 ASSERT(FLAG_precompiled_mode);
1447 ASSERT(current_symbols_ == nullptr);
1448 bool global_symbol = false;
1449 switch (section) {
1450 case ProgramSection::Text:
1451 if (debug_elf_ != nullptr) {
1452 current_symbols_ =
1453 new (zone_) ZoneGrowableArray<Elf::SymbolData>(zone_, 0);
1454 }
1455 assembly_stream_->WriteString(".text\n");
1456 global_symbol = true;
1457 break;
1459 // We create a SymbolData array even if there is no debug_elf_ because we
1460 // may be writing RO data symbols, and RO data is written in two steps:
1461 // 1. Serializing the read-only data objects to the clustered stream
1462 // 2. Writing the bytes of the clustered stream to the assembly output.
1463 // Thus, we'll need to interleave the symbols with the cluster bytes
1464 // during step 2.
1465 current_symbols_ =
1466 new (zone_) ZoneGrowableArray<Elf::SymbolData>(zone_, 0);
1467#if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
1468 defined(DART_TARGET_OS_FUCHSIA)
1469 assembly_stream_->WriteString(".section .rodata\n");
1470#elif defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
1471 assembly_stream_->WriteString(".const\n");
1472#else
1473 UNIMPLEMENTED();
1474#endif
1475 global_symbol = true;
1476 break;
1477 case ProgramSection::Bss:
1478 assembly_stream_->WriteString(".bss\n");
1479 break;
1480 case ProgramSection::BuildId:
1481 break;
1482 }
1483 current_section_label_ = SectionLabel(section, vm);
1484 ASSERT(current_section_label_ > 0);
1485 if (global_symbol) {
1486 assembly_stream_->Printf(".globl %s\n", SectionSymbol(section, vm));
1487 }
1488 intptr_t padding = Align(alignment, 0, 0);
1489 if (alignment_padding != nullptr) {
1490 *alignment_padding = padding;
1491 }
1492 assembly_stream_->Printf("%s:\n", SectionSymbol(section, vm));
1493 return true;
1494}
1495
1496static void ElfAddSection(
1497 Elf* elf,
1499 const char* symbol,
1500 intptr_t label,
1501 uint8_t* bytes,
1502 intptr_t size,
1503 ZoneGrowableArray<Elf::SymbolData>* symbols,
1504 ZoneGrowableArray<Elf::Relocation>* relocations = nullptr) {
1505 if (elf == nullptr) return;
1506 switch (section) {
1508 elf->AddText(symbol, label, bytes, size, relocations, symbols);
1509 break;
1511 elf->AddROData(symbol, label, bytes, size, relocations, symbols);
1512 break;
1513 default:
1514 // Other sections are handled by the Elf object internally.
1515 break;
1516 }
1517}
1518
1519void AssemblyImageWriter::ExitSection(ProgramSection name,
1520 bool vm,
1521 intptr_t size) {
1522 // We should still be in the same section as the last EnterSection.
1523 ASSERT_EQUAL(current_section_label_, SectionLabel(name, vm));
1524#if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
1525 defined(DART_TARGET_OS_FUCHSIA)
1526 // Output the size of the section symbol to the assembly stream.
1527 assembly_stream_->Printf(".size %s, %zu\n", SectionSymbol(name, vm), size);
1528 assembly_stream_->Printf(".type %s, %%object\n", SectionSymbol(name, vm));
1529#elif defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
1530 // MachO symbol tables don't include the size of the symbol, so don't bother
1531 // printing it to the assembly output.
1532#else
1533 UNIMPLEMENTED();
1534#endif
1535 // We need to generate a text segment of the appropriate size in the ELF
1536 // for two reasons:
1537 //
1538 // * We need unique virtual addresses for each text section in the DWARF
1539 // file and that the virtual addresses for payloads within those sections
1540 // do not overlap.
1541 //
1542 // * Our tools for converting DWARF stack traces back to "normal" Dart
1543 // stack traces calculate an offset into the appropriate instructions
1544 // section, and then add that offset to the virtual address of the
1545 // corresponding segment to get the virtual address for the frame.
1546 //
1547 // Since we don't want to add the actual contents of the segment in the
1548 // separate debugging information, we pass nullptr for the bytes, which
1549 // creates an appropriate NOBITS section instead of PROGBITS.
1550 ElfAddSection(debug_elf_, name, SectionSymbol(name, vm),
1551 current_section_label_, /*bytes=*/nullptr, size,
1552 current_symbols_);
1553 current_section_label_ = 0;
1554 current_symbols_ = nullptr;
1555}
1556
1557intptr_t AssemblyImageWriter::WriteTargetWord(word value) {
1559 // Padding is helpful for comparing the .S with --disassemble.
1560 assembly_stream_->Printf("%s 0x%.*" Px "\n", kWordDirective,
1561 static_cast<int>(2 * compiler::target::kWordSize),
1562 value);
1564}
1565
1566intptr_t AssemblyImageWriter::Relocation(intptr_t section_offset,
1567 intptr_t source_label,
1568 intptr_t source_offset,
1569 intptr_t target_label,
1570 intptr_t target_offset) {
1571 // TODO(dartbug.com/43274): Remove once we generate consistent build IDs
1572 // between assembly snapshots and their debugging information.
1573 if (target_label == SectionLabel(ProgramSection::BuildId, /*vm=*/false)) {
1574 return WriteTargetWord(Image::kNoBuildId);
1575 }
1576
1577 // All relocations are word-sized.
1578 assembly_stream_->Printf("%s ", kWordDirective);
1579 if (target_label == current_section_label_) {
1580 assembly_stream_->WriteString("(.)");
1581 target_offset -= section_offset;
1582 } else {
1583 const char* target_symbol = label_to_symbol_name_.Lookup(target_label);
1584 ASSERT(target_symbol != nullptr);
1585 assembly_stream_->Printf("%s", target_symbol);
1586 }
1587 if (target_offset != 0) {
1588 assembly_stream_->Printf(" + %" Pd "", target_offset);
1589 }
1590
1591 if (source_label == current_section_label_) {
1592 assembly_stream_->WriteString(" - (.)");
1593 source_offset -= section_offset;
1594 } else {
1595 const char* source_symbol = label_to_symbol_name_.Lookup(source_label);
1596 ASSERT(source_symbol != nullptr);
1597 assembly_stream_->Printf(" - %s", source_symbol);
1598 }
1599 if (source_offset != 0) {
1600 assembly_stream_->Printf(" - %" Pd "", source_offset);
1601 }
1602 assembly_stream_->WriteString("\n");
1604}
1605
1606void AssemblyImageWriter::AddCodeSymbol(const Code& code,
1607 const char* symbol,
1608 intptr_t offset) {
1609 auto const label = next_label_++;
1610 label_to_symbol_name_.Insert(label, symbol);
1611 if (assembly_dwarf_ != nullptr) {
1612 assembly_dwarf_->AddCode(code, label);
1613 }
1614 if (debug_elf_ != nullptr) {
1615 current_symbols_->Add({symbol, elf::STT_FUNC, offset, code.Size(), label});
1616 debug_elf_->dwarf()->AddCode(code, label);
1617 }
1618 assembly_stream_->Printf("\"%s\":\n", symbol);
1619#if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
1620 defined(DART_TARGET_OS_FUCHSIA)
1621 // Output the size of the code symbol to the assembly stream.
1622 assembly_stream_->Printf(".size \"%s\", %zu\n", symbol, code.Size());
1623 assembly_stream_->Printf(".type \"%s\", %%function\n", symbol);
1624#elif defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
1625 // MachO symbol tables don't include the size of the symbol, so don't bother
1626 // printing it to the assembly output.
1627#else
1628 UNIMPLEMENTED();
1629#endif
1630}
1631
1632void AssemblyImageWriter::AddDataSymbol(const char* symbol,
1633 intptr_t offset,
1634 size_t size) {
1635 if (!FLAG_add_readonly_data_symbols) return;
1636 auto const label = next_label_++;
1637 label_to_symbol_name_.Insert(label, symbol);
1638 current_symbols_->Add({symbol, elf::STT_OBJECT, offset, size, label});
1639}
1640
1641void AssemblyImageWriter::FrameUnwindPrologue() {
1642 // Creates DWARF's .debug_frame
1643 // CFI = Call frame information
1644 // CFA = Canonical frame address
1645 assembly_stream_->WriteString(".cfi_startproc\n");
1646
1647 // Below .cfi_def_cfa defines CFA as caller's SP, while .cfi_offset R, offs
1648 // tells unwinder that caller's value of register R is stored at address
1649 // CFA+offs.
1650
1651 // In the code below we emit .cfi_offset directive in the specific order:
1652 // PC first then FP. This should not actually matter, but we discovered
1653 // that LLVM code responsible for emitting compact unwind information
1654 // expects this specific ordering of CFI directives. If we don't
1655 // follow the order then LLVM fails to emit compact unwind info and emits
1656 // __eh_frame instead which is very large.
1657 // See also https://github.com/llvm/llvm-project/issues/62574 and
1658 // https://github.com/flutter/flutter/issues/126004.
1659
1660#if defined(TARGET_ARCH_IA32)
1661 UNREACHABLE();
1662#elif defined(TARGET_ARCH_X64)
1663 assembly_stream_->WriteString(".cfi_def_cfa rbp, 16\n");
1664 assembly_stream_->WriteString(".cfi_offset rip, -8\n");
1665 assembly_stream_->WriteString(".cfi_offset rbp, -16\n");
1666#elif defined(TARGET_ARCH_ARM64)
1667 COMPILE_ASSERT(R29 == FP);
1669 assembly_stream_->WriteString(".cfi_def_cfa x29, 16\n");
1670 assembly_stream_->WriteString(".cfi_offset x30, -8\n");
1671 assembly_stream_->WriteString(".cfi_offset x29, -16\n");
1672#elif defined(TARGET_ARCH_ARM)
1673#if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
1674 COMPILE_ASSERT(FP == R7);
1675 assembly_stream_->WriteString(".cfi_def_cfa r7, 8\n");
1676#else
1677 COMPILE_ASSERT(FP == R11);
1678 assembly_stream_->WriteString(".cfi_def_cfa r11, 8\n");
1679#endif
1680 assembly_stream_->WriteString(".cfi_offset lr, -4\n");
1681#if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
1682 COMPILE_ASSERT(FP == R7);
1683 assembly_stream_->WriteString(".cfi_offset r7, -8\n");
1684#else
1685 COMPILE_ASSERT(FP == R11);
1686 assembly_stream_->WriteString(".cfi_offset r11, -8\n");
1687#endif
1688// libunwind on ARM may use .ARM.exidx instead of .debug_frame
1689#if !defined(DART_TARGET_OS_MACOS) && !defined(DART_TARGET_OS_MACOS_IOS)
1690 COMPILE_ASSERT(FP == R11);
1691 assembly_stream_->WriteString(".fnstart\n");
1692 assembly_stream_->WriteString(".save {r11, lr}\n");
1693 assembly_stream_->WriteString(".setfp r11, sp, #0\n");
1694#endif
1695#elif defined(TARGET_ARCH_RISCV32)
1696 assembly_stream_->WriteString(".cfi_def_cfa fp, 0\n");
1697 assembly_stream_->WriteString(".cfi_offset ra, -4\n");
1698 assembly_stream_->WriteString(".cfi_offset fp, -8\n");
1699#elif defined(TARGET_ARCH_RISCV64)
1700 assembly_stream_->WriteString(".cfi_def_cfa fp, 0\n");
1701 assembly_stream_->WriteString(".cfi_offset ra, -8\n");
1702 assembly_stream_->WriteString(".cfi_offset fp, -16\n");
1703#else
1704#error Unexpected architecture.
1705#endif
1706}
1707
1708void AssemblyImageWriter::FrameUnwindEpilogue() {
1709#if defined(TARGET_ARCH_ARM)
1710#if !defined(DART_TARGET_OS_MACOS) && !defined(DART_TARGET_OS_MACOS_IOS)
1711 assembly_stream_->WriteString(".fnend\n");
1712#endif
1713#endif
1714 assembly_stream_->WriteString(".cfi_endproc\n");
1715}
1716
1717intptr_t AssemblyImageWriter::WriteBytes(const void* bytes, intptr_t size) {
1718 ASSERT(size >= 0);
1719 auto const start = reinterpret_cast<const uint8_t*>(bytes);
1720 auto const end_of_words =
1722 for (auto cursor = reinterpret_cast<const compiler::target::word*>(start);
1723 cursor < reinterpret_cast<const compiler::target::word*>(end_of_words);
1724 cursor++) {
1725 WriteTargetWord(*cursor);
1726 }
1727 auto const end = start + size;
1728 if (end != end_of_words) {
1729 assembly_stream_->WriteString(kSizeDirectives[kInt8SizeLog2]);
1730 for (auto cursor = end_of_words; cursor < end; cursor++) {
1731 assembly_stream_->Printf("%s 0x%.2x", cursor != end_of_words ? "," : "",
1732 *cursor);
1733 }
1734 assembly_stream_->WriteString("\n");
1735 }
1736 return size;
1737}
1738
1739intptr_t AssemblyImageWriter::Align(intptr_t alignment,
1740 intptr_t offset,
1741 intptr_t position) {
1742 ASSERT(offset == 0);
1743 const intptr_t next_position = Utils::RoundUp(position, alignment);
1744 assembly_stream_->Printf(".balign %" Pd ", 0\n", alignment);
1745 return next_position - position;
1746}
1747#endif // defined(DART_PRECOMPILER)
1748
1749#if defined(DART_PRECOMPILER)
1751 NonStreamingWriteStream* vm_instructions,
1752 NonStreamingWriteStream* isolate_instructions,
1753 const Trie<const char>* deobfuscation_trie,
1754 Elf* debug_elf,
1755 Elf* elf)
1756 : ImageWriter(thread, /*generates_assembly=*/false, deobfuscation_trie),
1757#else
1759 NonStreamingWriteStream* vm_instructions,
1760 NonStreamingWriteStream* isolate_instructions,
1761 Elf* debug_elf,
1762 Elf* elf)
1763 : ImageWriter(thread, /*generates_assembly=*/false),
1764#endif
1765 vm_instructions_(vm_instructions),
1766 isolate_instructions_(isolate_instructions),
1767 elf_(elf),
1768 debug_elf_(debug_elf) {
1769#if defined(DART_PRECOMPILER)
1770 ASSERT_EQUAL(FLAG_precompiled_mode, elf_ != nullptr);
1771 ASSERT(debug_elf_ == nullptr || debug_elf_->dwarf() != nullptr);
1772#else
1773 RELEASE_ASSERT(elf_ == nullptr);
1774#endif
1775}
1776
1777intptr_t BlobImageWriter::WriteBytes(const void* bytes, intptr_t size) {
1778 current_section_stream_->WriteBytes(bytes, size);
1779 return size;
1780}
1781
1783#if defined(DART_PRECOMPILER)
1784 // We don't actually write a BSS segment, it's created as part of the
1785 // Elf constructor.
1786#endif
1787}
1788
1790 bool vm) {
1791#if defined(DART_PRECOMPILER)
1792 const intptr_t start_position = clustered_stream->Position();
1793#endif
1794 current_section_stream_ = ASSERT_NOTNULL(clustered_stream);
1796 return;
1797 }
1798#if defined(DART_PRECOMPILER)
1799 if (profile_writer_ != nullptr) {
1800 // Attribute any padding needed to the artificial root.
1801 const intptr_t padding = clustered_stream->Position() - start_position;
1804 }
1805#endif
1806 ImageWriter::WriteROData(clustered_stream, vm);
1807 ExitSection(ProgramSection::Data, vm, clustered_stream->bytes_written());
1808}
1809
1811 bool vm,
1812 intptr_t alignment,
1813 intptr_t* alignment_padding) {
1814#if defined(DART_PRECOMPILER)
1815 ASSERT_EQUAL(elf_ != nullptr, FLAG_precompiled_mode);
1816 ASSERT(current_relocations_ == nullptr);
1817 ASSERT(current_symbols_ == nullptr);
1818#endif
1819 ASSERT(section == ProgramSection::Data || current_section_stream_ == nullptr);
1820 switch (section) {
1822 current_section_stream_ =
1823 ASSERT_NOTNULL(vm ? vm_instructions_ : isolate_instructions_);
1824#if defined(DART_PRECOMPILER)
1825 current_relocations_ =
1827 current_symbols_ =
1829#endif
1830 break;
1832 // The stream to use is passed into WriteROData and set there.
1833 ASSERT(current_section_stream_ != nullptr);
1834#if defined(DART_PRECOMPILER)
1835 current_relocations_ =
1837 current_symbols_ =
1839#endif
1840 break;
1842 // The BSS section is pre-made in the Elf object for precompiled snapshots
1843 // and unused otherwise, so there's no work that needs doing here.
1844 return false;
1846 // The GNU build ID is handled specially in the Elf object, and does not
1847 // get used for non-precompiled snapshots.
1848 return false;
1849 }
1850 intptr_t padding = current_section_stream_->Align(alignment);
1851 if (alignment_padding != nullptr) {
1852 *alignment_padding = padding;
1853 }
1854 return true;
1855}
1856
1858#if defined(DART_PRECOMPILER)
1859 ElfAddSection(elf_, name, SectionSymbol(name, vm), SectionLabel(name, vm),
1860 current_section_stream_->buffer(), size, current_symbols_,
1861 current_relocations_);
1862 // We create the corresponding segment in the debugging information as well,
1863 // since it needs the contents to create the correct build ID.
1864 ElfAddSection(debug_elf_, name, SectionSymbol(name, vm),
1865 SectionLabel(name, vm), current_section_stream_->buffer(), size,
1866 current_symbols_, current_relocations_);
1867 current_relocations_ = nullptr;
1868 current_symbols_ = nullptr;
1869#endif
1870 current_section_stream_ = nullptr;
1871}
1872
1874 current_section_stream_->WriteTargetWord(value);
1876}
1877
1878intptr_t BlobImageWriter::Align(intptr_t alignment,
1879 intptr_t offset,
1880 intptr_t position) {
1881 const intptr_t stream_padding =
1882 current_section_stream_->Align(alignment, offset);
1883 // Double-check that the position has the same alignment.
1884 ASSERT_EQUAL(Utils::RoundUp(position, alignment, offset) - position,
1885 stream_padding);
1886 return stream_padding;
1887}
1888
1889#if defined(DART_PRECOMPILER)
1890intptr_t BlobImageWriter::Relocation(intptr_t section_offset,
1891 intptr_t source_label,
1892 intptr_t source_offset,
1893 intptr_t target_label,
1894 intptr_t target_offset) {
1895 ASSERT(FLAG_precompiled_mode);
1896 current_relocations_->Add({compiler::target::kWordSize, section_offset,
1897 source_label, source_offset, target_label,
1898 target_offset});
1899 // We write break instructions so it's easy to tell if a relocation doesn't
1900 // get replaced appropriately.
1902}
1903
1904void BlobImageWriter::AddCodeSymbol(const Code& code,
1905 const char* symbol,
1906 intptr_t offset) {
1907 const intptr_t label = next_label_++;
1908 current_symbols_->Add({symbol, elf::STT_FUNC, offset, code.Size(), label});
1909 if (elf_ != nullptr && elf_->dwarf() != nullptr) {
1910 elf_->dwarf()->AddCode(code, label);
1911 }
1912 if (debug_elf_ != nullptr) {
1913 debug_elf_->dwarf()->AddCode(code, label);
1914 }
1915}
1916
1917void BlobImageWriter::AddDataSymbol(const char* symbol,
1918 intptr_t offset,
1919 size_t size) {
1920 if (!FLAG_add_readonly_data_symbols) return;
1921 const intptr_t label = next_label_++;
1922 current_symbols_->Add({symbol, elf::STT_OBJECT, offset, size, label});
1923}
1924#endif // defined(DART_PRECOMPILER)
1925#endif // !defined(DART_PRECOMPILED_RUNTIME)
1926
1927ImageReader::ImageReader(const uint8_t* data_image,
1928 const uint8_t* instructions_image)
1929 : data_image_(ASSERT_NOTNULL(data_image)),
1930 instructions_image_(ASSERT_NOTNULL(instructions_image)) {}
1931
1932ApiErrorPtr ImageReader::VerifyAlignment() const {
1933 if (!Utils::IsAligned(data_image_, kObjectStartAlignment) ||
1934 !Utils::IsAligned(instructions_image_, kObjectStartAlignment)) {
1935 return ApiError::New(
1936 String::Handle(String::New("Snapshot is misaligned", Heap::kOld)),
1937 Heap::kOld);
1938 }
1939 return ApiError::null();
1940}
1941
1942#if defined(DART_PRECOMPILED_RUNTIME)
1943uword ImageReader::GetBareInstructionsAt(uint32_t offset) const {
1945 return reinterpret_cast<uword>(instructions_image_) + offset;
1946}
1947
1948uword ImageReader::GetBareInstructionsEnd() const {
1949 Image image(instructions_image_);
1950 return reinterpret_cast<uword>(image.object_start()) + image.object_size();
1951}
1952#endif
1953
1954InstructionsPtr ImageReader::GetInstructionsAt(uint32_t offset) const {
1955 ASSERT(!FLAG_precompiled_mode);
1957
1959 reinterpret_cast<uword>(instructions_image_) + offset);
1960 ASSERT(result->IsInstructions());
1961 ASSERT(result->untag()->IsMarked());
1962
1964}
1965
1968
1970 UntaggedObject::FromAddr(reinterpret_cast<uword>(data_image_) + offset);
1971 ASSERT(result->untag()->IsMarked());
1972
1973 return result;
1974}
1975
1976} // namespace dart
Align
int count
Definition: FontMgrTest.cpp:50
static uint32_t hash(const SkShaderBase::GradientInfo &v)
#define UNREACHABLE()
Definition: assert.h:248
#define ASSERT_EQUAL(expected, actual)
Definition: assert.h:309
#define RELEASE_ASSERT(cond)
Definition: assert.h:327
#define ASSERT_NOTNULL(ptr)
Definition: assert.h:323
GLenum type
static constexpr intptr_t kIsolateGroupEntryCount
Definition: bss_relocs.h:30
static constexpr intptr_t kVmEntryCount
Definition: bss_relocs.h:27
void WriteBytes(const void *addr, intptr_t len)
Definition: datastream.h:424
intptr_t Align(intptr_t alignment, intptr_t offset=0)
Definition: datastream.h:341
DART_FORCE_INLINE intptr_t bytes_written() const
Definition: datastream.h:338
virtual intptr_t Position() const
Definition: datastream.h:339
void WriteTargetWord(word value)
Definition: datastream.cc:12
static constexpr uword encode(bool value)
Definition: bitfield.h:165
virtual void WriteBss(bool vm)
virtual bool EnterSection(ProgramSection section, bool vm, intptr_t alignment, intptr_t *alignment_padding=nullptr)
virtual void WriteROData(NonStreamingWriteStream *clustered_stream, bool vm)
virtual void ExitSection(ProgramSection name, bool vm, intptr_t size)
BlobImageWriter(Thread *thread, NonStreamingWriteStream *vm_instructions, NonStreamingWriteStream *isolate_instructions, Elf *debug_elf=nullptr, Elf *elf=nullptr)
virtual intptr_t Align(intptr_t alignment, intptr_t offset, intptr_t position)
virtual intptr_t WriteBytes(const void *bytes, intptr_t size)
virtual intptr_t WriteTargetWord(word value)
StringPtr UserVisibleName() const
Definition: object.cc:2989
static InstructionsPtr InstructionsOf(const CodePtr code)
Definition: object.h:6775
static uintptr_t PayloadSizeOf(const CompressedStackMapsPtr raw)
Definition: object.h:6270
static Dart_FileWriteCallback file_write_callback()
Definition: dart.h:125
static Dart_FileOpenCallback file_open_callback()
Definition: dart.h:119
static Dart_FileCloseCallback file_close_callback()
Definition: dart.h:128
@ kOld
Definition: heap.h:39
intptr_t GetObjectId(ObjectPtr raw_obj) const
Definition: heap.h:197
void SetObjectId(ObjectPtr raw_obj, intptr_t object_id)
Definition: heap.h:193
ObjectPtr GetObjectAt(uint32_t offset) const
InstructionsPtr GetInstructionsAt(uint32_t offset) const
ApiErrorPtr VerifyAlignment() const
ImageReader(const uint8_t *data_image, const uint8_t *instructions_image)
intptr_t GetTextObjectCount() const
intptr_t next_text_offset_
virtual void FrameUnwindEpilogue()=0
static intptr_t SizeInSnapshot(ObjectPtr object)
bool IsROSpace() const
V8SnapshotProfileWriter * profile_writer_
const char *const instructions_type_
intptr_t WriteFixed(T value)
GrowableArray< ObjectData > objects_
virtual intptr_t Align(intptr_t alignment, intptr_t offset, intptr_t position)=0
intptr_t next_data_offset_
void Write(NonStreamingWriteStream *clustered_stream, bool vm)
GrowableArray< InstructionsData > instructions_
void DumpInstructionStats()
static constexpr intptr_t kRODataAlignment
virtual void FrameUnwindPrologue()=0
const char *const padding_type_
static const char * TagObjectTypeAsReadOnly(Zone *zone, const char *type)
intptr_t AlignWithBreakInstructions(intptr_t alignment, intptr_t offset)
void DumpInstructionsSizes()
virtual void ExitSection(ProgramSection name, bool vm, intptr_t size)=0
const char *const image_type_
static constexpr intptr_t kTextAlignment
virtual void WriteROData(NonStreamingWriteStream *clustered_stream, bool vm)
ImageWriter(Thread *thread, bool generates_assembly)
const char *const instructions_section_type_
virtual intptr_t WriteBytes(const void *bytes, intptr_t size)=0
static constexpr intptr_t kBssAlignment
static constexpr intptr_t kNumProgramSections
void GetTrampolineInfo(intptr_t *count, intptr_t *size) const
virtual void WriteBss(bool vm)=0
int32_t GetTextOffsetFor(InstructionsPtr instructions, CodePtr code)
const char * ObjectTypeForProfile(const Object &object) const
virtual intptr_t WriteTargetWord(word value)=0
uint32_t GetDataOffsetFor(ObjectPtr raw_object)
void PrepareForSerialization(GrowableArray< ImageWriterCommand > *commands)
Thread *const thread_
virtual bool EnterSection(ProgramSection name, bool vm, intptr_t alignment, intptr_t *alignment_padding=nullptr)=0
const char *const trampoline_type_
uint32_t AddBytesToData(uint8_t *bytes, intptr_t length)
static const char * SectionSymbol(ProgramSection section, bool vm)
static uword GetMarkedTags(classid_t cid, intptr_t size, bool is_canonical=false)
void WriteText(bool vm)
uword * bss() const
const uint8_t * build_id() const
intptr_t build_id_length() const
bool compiled_to_elf() const
uword instructions_relocated_address() const
static intptr_t HeaderSize()
Definition: object.h:5949
static intptr_t InstanceSize()
Definition: object.h:5935
static constexpr intptr_t kPayloadAlignment
Definition: object.h:5945
static constexpr intptr_t kBarePayloadAlignment
Definition: object.h:5815
intptr_t Size() const
Definition: object.h:5740
uint8_t * buffer() const
Definition: datastream.h:615
static void static void PrintErr(const char *format,...) PRINTF_ATTRIBUTE(1
static char * SCreate(Zone *zone, const char *format,...) PRINTF_ATTRIBUTE(2
static bool IsKeyEqual(Pair pair, Key key)
static uword Hash(Key key)
UntaggedObject * untag() const
intptr_t GetClassId() const
Definition: raw_object.h:885
@ kUserVisibleName
Definition: object.h:645
@ kInternalName
Definition: object.h:622
static ObjectPtr null()
Definition: object.h:433
ObjectPtr ptr() const
Definition: object.h:332
bool IsCanonical() const
Definition: object.h:335
virtual const char * ToCString() const
Definition: object.h:366
static Object & Handle()
Definition: object.h:407
static ObjectPtr RawCast(ObjectPtr obj)
Definition: object.h:325
ClassPtr clazz() const
Definition: object.h:13218
static OneByteStringPtr New(intptr_t len, Heap::Space space)
Definition: object.cc:24368
static constexpr intptr_t kBytesPerElement
Definition: object.h:10542
bool IsOneByteString() const
Definition: object.h:10311
intptr_t Length() const
Definition: object.h:10210
static StringPtr New(const char *cstr, Heap::Space space=Heap::kNew)
Definition: object.cc:23698
bool IsTwoByteString() const
Definition: object.h:10315
static uint32_t GetCachedHash(const StringPtr obj)
Definition: object.h:10445
static intptr_t LengthOf(StringPtr obj)
Definition: object.h:10211
static const char * NameOfStub(uword entry_point)
Definition: stub_code.cc:330
Zone * zone() const
Definition: thread_state.h:37
Heap * heap() const
Definition: thread.cc:943
static constexpr intptr_t kBytesPerElement
Definition: object.h:10683
static constexpr uword encode(intptr_t size)
Definition: raw_object.h:204
static ObjectPtr FromAddr(uword addr)
Definition: raw_object.h:516
static uword ToAddr(const UntaggedObject *raw_obj)
Definition: raw_object.h:522
intptr_t HeapSize() const
Definition: raw_object.h:401
intptr_t GetClassId() const
Definition: raw_object.h:392
static constexpr T RoundUp(T x, uintptr_t alignment, uintptr_t offset=0)
Definition: utils.h:120
static constexpr size_t BitLength(int64_t value)
Definition: utils.h:213
static constexpr T RoundDown(T x, intptr_t alignment)
Definition: utils.h:108
static constexpr bool IsAligned(T x, uintptr_t alignment, uintptr_t offset=0)
Definition: utils.h:92
static const ObjectId kArtificialRootId
void AttributeReferenceTo(const ObjectId &from_object_id, const Reference &reference, const ObjectId &to_object_id)
void SetObjectTypeAndName(const ObjectId &object_id, const char *type, const char *name)
void AddRoot(const ObjectId &object_id, const char *name=nullptr)
void AttributeBytesTo(const ObjectId &object_id, size_t num_bytes)
static ObjectPtr Unwrap(ObjectPtr obj)
Definition: object.h:6667
static const word kNonBarePayloadAlignment
Definition: runtime_api.h:1384
#define LINK_REGISTER
#define kIsolateSnapshotDataAsmSymbol
Definition: dart_api.h:3967
#define kIsolateSnapshotBssAsmSymbol
Definition: dart_api.h:3970
#define kIsolateSnapshotInstructionsAsmSymbol
Definition: dart_api.h:3968
#define kVmSnapshotBssAsmSymbol
Definition: dart_api.h:3966
#define kVmSnapshotDataAsmSymbol
Definition: dart_api.h:3964
#define kVmSnapshotInstructionsAsmSymbol
Definition: dart_api.h:3965
#define kSnapshotBuildIdAsmSymbol
Definition: dart_api.h:3963
#define UNIMPLEMENTED
const EmbeddedViewParams * params
#define ASSERT(E)
static bool b
struct MyStruct a[10]
#define FATAL(error)
glong glong end
uint8_t value
GAsyncResult * result
const char * charp
Definition: flags.h:12
Dart_NativeFunction function
Definition: fuchsia.cc:51
size_t length
sk_sp< const SkImage > image
Definition: SkRecords.h:269
static constexpr intptr_t kWordSize
Definition: runtime_api.h:274
static constexpr word kBitsPerWord
Definition: runtime_api.h:291
static constexpr intptr_t STT_FUNC
Definition: elf.h:196
static constexpr intptr_t STT_OBJECT
Definition: elf.h:195
Definition: dart_vm.cc:33
static constexpr intptr_t kObjectStartAlignment
const char *const name
uint32_t CombineHashes(uint32_t hash, uint32_t other_hash)
Definition: hash.h:12
int32_t classid_t
Definition: globals.h:524
static constexpr uword kReadOnlyGCBits
constexpr intptr_t kInt32SizeLog2
Definition: globals.h:449
constexpr intptr_t kWordSizeLog2
Definition: globals.h:507
uintptr_t uword
Definition: globals.h:501
intptr_t word
Definition: globals.h:500
constexpr uword kBreakInstructionFiller
constexpr intptr_t kInt16SizeLog2
Definition: globals.h:446
static char * vm_name
Definition: service.cc:356
constexpr intptr_t kInt8SizeLog2
Definition: globals.h:443
DEFINE_FLAG(bool, print_cluster_information, false, "Print information about clusters written to snapshot")
const intptr_t cid
uint32_t FinalizeHash(uint32_t hash, intptr_t hashbits=kBitsPerInt32)
Definition: hash.h:20
constexpr intptr_t kInt64SizeLog2
Definition: globals.h:452
static constexpr intptr_t kObjectAlignment
static int8_t data[kExtLength]
static constexpr intptr_t kObjectAlignmentLog2
COMPILE_ASSERT(kUnreachableReference==WeakTable::kNoValue)
dictionary commands
Definition: dom.py:171
struct PathData * Data(SkPath *path)
Definition: path_ops.cc:52
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
CanvasImage Image
Definition: dart_ui.cc:55
dictionary stats
Definition: malisc.py:20
inst
Definition: malisc.py:37
SI auto map(std::index_sequence< I... >, Fn &&fn, const Args &... args) -> skvx::Vec< sizeof...(I), decltype(fn(args[0]...))>
Definition: SkVx.h:680
#define Px
Definition: globals.h:410
#define Pu32
Definition: globals.h:413
#define Pd
Definition: globals.h:408
#define Pu64
Definition: globals.h:417
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition: globals.h:581
#define REUSABLE_CLASS_HANDLESCOPE(thread)
#define REUSABLE_STRING_HANDLESCOPE(thread)
SeparatedVector2 offset
static NameFormattingParams DisambiguatedWithoutClassName(Object::NameVisibility visibility)
Definition: object.h:2968
static constexpr intptr_t kObjectAlignmentLog2
static constexpr intptr_t kObjectAlignment
static constexpr intptr_t kOldObjectAlignmentOffset
static Reference Element(intptr_t offset)
uint8_t data[]
Definition: elf.h:152
uint32_t description_size
Definition: elf.h:150
const uintptr_t id
#define TIMELINE_DURATION(thread, stream, name)
Definition: timeline.h:39