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 "vm/class_id.h"
10#include "vm/compiler/runtime_api.h"
11#include "vm/dwarf.h"
12#include "vm/elf.h"
13#include "vm/hash.h"
14#include "vm/hash_map.h"
15#include "vm/heap/heap.h"
16#include "vm/instructions.h"
17#include "vm/json_writer.h"
18#include "vm/object.h"
19#include "vm/object_store.h"
20#include "vm/program_visitor.h"
21#include "vm/stub_code.h"
22#include "vm/timeline.h"
23#include "vm/type_testing_stubs.h"
24
25#if !defined(DART_PRECOMPILED_RUNTIME)
26#include "vm/compiler/backend/code_statistics.h"
27#endif // !defined(DART_PRECOMPILED_RUNTIME)
28
29namespace dart {
30
31#if defined(DART_PRECOMPILER)
32DEFINE_FLAG(bool,
33 print_instruction_stats,
34 false,
35 "Print instruction statistics");
36
37DEFINE_FLAG(charp,
38 print_instructions_sizes_to,
39 NULL,
40 "Print sizes of all instruction objects to the given file");
41#endif
42
43intptr_t ObjectOffsetTrait::Hashcode(Key key) {
44 ObjectPtr obj = key;
45 ASSERT(!obj->IsSmi());
46
47 uword body = ObjectLayout::ToAddr(obj) + sizeof(ObjectLayout);
48 uword end = ObjectLayout::ToAddr(obj) + obj->ptr()->HeapSize();
49
50 uint32_t hash = obj->GetClassId();
51 // Don't include the header. Objects in the image are pre-marked, but objects
52 // in the current isolate are not.
53 for (uword cursor = body; cursor < end; cursor += sizeof(uint32_t)) {
54 hash = CombineHashes(hash, *reinterpret_cast<uint32_t*>(cursor));
55 }
56
57 return FinalizeHash(hash, 30);
58}
59
60bool ObjectOffsetTrait::IsKeyEqual(Pair pair, Key key) {
61 ObjectPtr a = pair.object;
62 ObjectPtr b = key;
63 ASSERT(!a->IsSmi());
64 ASSERT(!b->IsSmi());
65
66 if (a->GetClassId() != b->GetClassId()) {
67 return false;
68 }
69
70 intptr_t heap_size = a->ptr()->HeapSize();
71 if (b->ptr()->HeapSize() != heap_size) {
72 return false;
73 }
74
75 // Don't include the header. Objects in the image are pre-marked, but objects
76 // in the current isolate are not.
77 uword body_a = ObjectLayout::ToAddr(a) + sizeof(ObjectLayout);
78 uword body_b = ObjectLayout::ToAddr(b) + sizeof(ObjectLayout);
79 uword body_size = heap_size - sizeof(ObjectLayout);
80 return 0 == memcmp(reinterpret_cast<const void*>(body_a),
81 reinterpret_cast<const void*>(body_b), body_size);
82}
83
84#if !defined(DART_PRECOMPILED_RUNTIME)
85ImageWriter::ImageWriter(Thread* t)
86 : heap_(t->heap()),
87 next_data_offset_(0),
88 next_text_offset_(0),
89 objects_(),
90 instructions_(),
91 instructions_section_type_(
92 TagObjectTypeAsReadOnly(t->zone(), "InstructionsSection")),
93 instructions_type_(TagObjectTypeAsReadOnly(t->zone(), "Instructions")),
94 trampoline_type_(TagObjectTypeAsReadOnly(t->zone(), "Trampoline")) {
95 ResetOffsets();
96}
97
98void ImageWriter::PrepareForSerialization(
99 GrowableArray<ImageWriterCommand>* commands) {
100 if (commands != nullptr) {
101 const intptr_t initial_offset = next_text_offset_;
102 for (auto& inst : *commands) {
103 ASSERT((initial_offset + inst.expected_offset) == next_text_offset_);
104 switch (inst.op) {
105 case ImageWriterCommand::InsertInstructionOfCode: {
106 CodePtr code = inst.insert_instruction_of_code.code;
107 InstructionsPtr instructions = Code::InstructionsOf(code);
108 const intptr_t offset = next_text_offset_;
109 instructions_.Add(InstructionsData(instructions, code, offset));
110 next_text_offset_ += SizeInSnapshot(instructions);
111 ASSERT(heap_->GetObjectId(instructions) == 0);
112 heap_->SetObjectId(instructions, offset);
113 break;
114 }
115 case ImageWriterCommand::InsertBytesOfTrampoline: {
116 auto trampoline_bytes = inst.insert_trampoline_bytes.buffer;
117 auto trampoline_length = inst.insert_trampoline_bytes.buffer_length;
118 const intptr_t offset = next_text_offset_;
119 instructions_.Add(
120 InstructionsData(trampoline_bytes, trampoline_length, offset));
121 next_text_offset_ += trampoline_length;
122 break;
123 }
124 default:
125 UNREACHABLE();
126 }
127 }
128 }
129}
130
131int32_t ImageWriter::GetTextOffsetFor(InstructionsPtr instructions,
132 CodePtr code) {
133 intptr_t offset = heap_->GetObjectId(instructions);
134 if (offset != 0) {
135 return offset;
136 }
137
138 offset = next_text_offset_;
139 heap_->SetObjectId(instructions, offset);
140 next_text_offset_ += SizeInSnapshot(instructions);
141 instructions_.Add(InstructionsData(instructions, code, offset));
142
143 ASSERT(offset != 0);
144 return offset;
145}
146
147static intptr_t InstructionsSizeInSnapshot(InstructionsPtr raw) {
148 if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
149 // Currently, we align bare instruction payloads on 4 byte boundaries.
150 //
151 // If we later decide to align on larger boundaries to put entries at the
152 // start of cache lines, make sure to account for entry points that are
153 // _not_ at the start of the payload.
154 return Utils::RoundUp(Instructions::Size(raw),
155 ImageWriter::kBareInstructionsAlignment);
156 }
157#if defined(IS_SIMARM_X64)
158 return Utils::RoundUp(
159 compiler::target::Instructions::HeaderSize() + Instructions::Size(raw),
160 compiler::target::ObjectAlignment::kObjectAlignment);
161#else
162 return raw->ptr()->HeapSize();
163#endif
164}
165
166#if defined(IS_SIMARM_X64)
167static intptr_t CompressedStackMapsSizeInSnapshot(intptr_t payload_size) {
168 const intptr_t unrounded_size_in_bytes =
169 compiler::target::CompressedStackMaps::HeaderSize() + payload_size;
170 return Utils::RoundUp(unrounded_size_in_bytes,
171 compiler::target::ObjectAlignment::kObjectAlignment);
172}
173
174static intptr_t StringPayloadSize(intptr_t len, bool isOneByteString) {
175 return len * (isOneByteString ? OneByteString::kBytesPerElement
176 : TwoByteString::kBytesPerElement);
177}
178
179static intptr_t StringSizeInSnapshot(intptr_t len, bool isOneByteString) {
180 const intptr_t unrounded_size_in_bytes =
181 (String::kSizeofRawString / 2) + StringPayloadSize(len, isOneByteString);
182 return Utils::RoundUp(unrounded_size_in_bytes,
183 compiler::target::ObjectAlignment::kObjectAlignment);
184}
185
186static intptr_t CodeSourceMapSizeInSnapshot(intptr_t len) {
187 const intptr_t unrounded_size_in_bytes =
188 2 * compiler::target::kWordSize + len;
189 return Utils::RoundUp(unrounded_size_in_bytes,
190 compiler::target::ObjectAlignment::kObjectAlignment);
191}
192
193static intptr_t PcDescriptorsSizeInSnapshot(intptr_t len) {
194 const intptr_t unrounded_size_in_bytes =
195 2 * compiler::target::kWordSize + len;
196 return Utils::RoundUp(unrounded_size_in_bytes,
197 compiler::target::ObjectAlignment::kObjectAlignment);
198}
199
200intptr_t ImageWriter::SizeInSnapshot(ObjectPtr raw_object) {
201 const classid_t cid = raw_object->GetClassId();
202
203 switch (cid) {
204 case kCompressedStackMapsCid: {
205 CompressedStackMapsPtr raw_maps =
206 static_cast<CompressedStackMapsPtr>(raw_object);
207 auto const payload_size = CompressedStackMaps::PayloadSizeOf(raw_maps);
208 return CompressedStackMapsSizeInSnapshot(payload_size);
209 }
210 case kOneByteStringCid:
211 case kTwoByteStringCid: {
212 StringPtr raw_str = static_cast<StringPtr>(raw_object);
213 return StringSizeInSnapshot(Smi::Value(raw_str->ptr()->length_),
214 cid == kOneByteStringCid);
215 }
216 case kCodeSourceMapCid: {
217 CodeSourceMapPtr raw_map = static_cast<CodeSourceMapPtr>(raw_object);
218 return CodeSourceMapSizeInSnapshot(raw_map->ptr()->length_);
219 }
220 case kPcDescriptorsCid: {
221 PcDescriptorsPtr raw_desc = static_cast<PcDescriptorsPtr>(raw_object);
222 return PcDescriptorsSizeInSnapshot(raw_desc->ptr()->length_);
223 }
224 case kInstructionsCid: {
225 InstructionsPtr raw_insns = static_cast<InstructionsPtr>(raw_object);
226 return InstructionsSizeInSnapshot(raw_insns);
227 }
228 default: {
229 const Class& clazz = Class::Handle(Object::Handle(raw_object).clazz());
230 FATAL1("Unsupported class %s in rodata section.\n", clazz.ToCString());
231 return 0;
232 }
233 }
234}
235#else // defined(IS_SIMARM_X64)
236intptr_t ImageWriter::SizeInSnapshot(ObjectPtr raw) {
237 switch (raw->GetClassId()) {
238 case kInstructionsCid:
239 return InstructionsSizeInSnapshot(static_cast<InstructionsPtr>(raw));
240 default:
241 return raw->ptr()->HeapSize();
242 }
243}
244#endif // defined(IS_SIMARM_X64)
245
246uint32_t ImageWriter::GetDataOffsetFor(ObjectPtr raw_object) {
247 intptr_t snap_size = SizeInSnapshot(raw_object);
248 intptr_t offset = next_data_offset_;
249 next_data_offset_ += snap_size;
250 objects_.Add(ObjectData(raw_object));
251 return offset;
252}
253
254intptr_t ImageWriter::GetTextObjectCount() const {
255 return instructions_.length();
256}
257
258void ImageWriter::GetTrampolineInfo(intptr_t* count, intptr_t* size) const {
259 ASSERT(count != nullptr && size != nullptr);
260 *count = 0;
261 *size = 0;
262 for (auto const data : instructions_) {
263 if (data.trampoline_length != 0) {
264 *count += 1;
265 *size += data.trampoline_length;
266 }
267 }
268}
269
270// Returns nullptr if there is no profile writer.
271const char* ImageWriter::ObjectTypeForProfile(const Object& object) const {
272 if (profile_writer_ == nullptr) return nullptr;
273 ASSERT(IsROSpace());
274 Thread* thread = Thread::Current();
275 REUSABLE_CLASS_HANDLESCOPE(thread);
276 REUSABLE_STRING_HANDLESCOPE(thread);
277 Class& klass = thread->ClassHandle();
278 String& name = thread->StringHandle();
279 klass = object.clazz();
280 name = klass.UserVisibleName();
281 auto const name_str = name.ToCString();
282 return TagObjectTypeAsReadOnly(thread->zone(), name_str);
283}
284
285const char* ImageWriter::TagObjectTypeAsReadOnly(Zone* zone, const char* type) {
286 ASSERT(zone != nullptr && type != nullptr);
287 return OS::SCreate(zone, "(RO) %s", type);
288}
289
290#if defined(DART_PRECOMPILER)
291void ImageWriter::DumpInstructionStats() {
292 std::unique_ptr<CombinedCodeStatistics> instruction_stats(
293 new CombinedCodeStatistics());
294 for (intptr_t i = 0; i < instructions_.length(); i++) {
295 auto& data = instructions_[i];
296 CodeStatistics* stats = data.insns_->stats();
297 if (stats != nullptr) {
298 stats->AppendTo(instruction_stats.get());
299 }
300 }
301 instruction_stats->DumpStatistics();
302}
303
304void ImageWriter::DumpInstructionsSizes() {
305 auto thread = Thread::Current();
306 auto zone = thread->zone();
307
308 auto& cls = Class::Handle(zone);
309 auto& lib = Library::Handle(zone);
310 auto& owner = Object::Handle(zone);
311 auto& url = String::Handle(zone);
312 auto& name = String::Handle(zone);
313 intptr_t trampolines_total_size = 0;
314
315 JSONWriter js;
316 js.OpenArray();
317 for (intptr_t i = 0; i < instructions_.length(); i++) {
318 auto& data = instructions_[i];
319 const bool is_trampoline = data.code_ == nullptr;
320 if (is_trampoline) {
321 trampolines_total_size += data.trampoline_length;
322 continue;
323 }
324 owner = WeakSerializationReference::Unwrap(data.code_->owner());
325 js.OpenObject();
326 if (owner.IsFunction()) {
327 cls = Function::Cast(owner).Owner();
328 name = cls.ScrubbedName();
329 lib = cls.library();
330 url = lib.url();
331 js.PrintPropertyStr("l", url);
332 js.PrintPropertyStr("c", name);
333 } else if (owner.IsClass()) {
334 cls ^= owner.raw();
335 name = cls.ScrubbedName();
336 lib = cls.library();
337 js.PrintPropertyStr("l", url);
338 js.PrintPropertyStr("c", name);
339 }
340 js.PrintProperty("n",
341 data.code_->QualifiedName(
342 NameFormattingParams::DisambiguatedWithoutClassName(
343 Object::kInternalName)));
344 js.PrintProperty("s", SizeInSnapshot(data.insns_->raw()));
345 js.CloseObject();
346 }
347 if (trampolines_total_size != 0) {
348 js.OpenObject();
349 js.PrintProperty("n", "[Stub] Trampoline");
350 js.PrintProperty("s", trampolines_total_size);
351 js.CloseObject();
352 }
353 js.CloseArray();
354
355 auto file_open = Dart::file_open_callback();
356 auto file_write = Dart::file_write_callback();
357 auto file_close = Dart::file_close_callback();
358 if ((file_open == nullptr) || (file_write == nullptr) ||
359 (file_close == nullptr)) {
360 return;
361 }
362
363 auto file = file_open(FLAG_print_instructions_sizes_to, /*write=*/true);
364 if (file == nullptr) {
365 OS::PrintErr("Failed to open file %s\n", FLAG_print_instructions_sizes_to);
366 return;
367 }
368
369 char* output = nullptr;
370 intptr_t output_length = 0;
371 js.Steal(&output, &output_length);
372 file_write(output, output_length, file);
373 free(output);
374 file_close(file);
375}
376
377void ImageWriter::DumpStatistics() {
378 if (FLAG_print_instruction_stats) {
379 DumpInstructionStats();
380 }
381
382 if (FLAG_print_instructions_sizes_to != nullptr) {
383 DumpInstructionsSizes();
384 }
385}
386#endif
387
388void ImageWriter::Write(WriteStream* clustered_stream, bool vm) {
389 Thread* thread = Thread::Current();
390 Zone* zone = thread->zone();
391 Heap* heap = thread->isolate()->heap();
392 TIMELINE_DURATION(thread, Isolate, "WriteInstructions");
393
394 // Handlify collected raw pointers as building the names below
395 // will allocate on the Dart heap.
396 for (intptr_t i = 0; i < instructions_.length(); i++) {
397 InstructionsData& data = instructions_[i];
398 const bool is_trampoline = data.trampoline_bytes != nullptr;
399 if (is_trampoline) continue;
400
401 data.insns_ = &Instructions::Handle(zone, data.raw_insns_);
402 ASSERT(data.raw_code_ != nullptr);
403 data.code_ = &Code::Handle(zone, data.raw_code_);
404
405 // Reset object id as an isolate snapshot after a VM snapshot will not use
406 // the VM snapshot's text image.
407 heap->SetObjectId(data.insns_->raw(), 0);
408 }
409 for (intptr_t i = 0; i < objects_.length(); i++) {
410 ObjectData& data = objects_[i];
411 data.obj_ = &Object::Handle(zone, data.raw_obj_);
412 }
413
414 // Append the direct-mapped RO data objects after the clustered snapshot.
415 // We need to do this before WriteText because WriteText currently adds the
416 // finalized contents of the clustered_stream as data sections.
417 offset_space_ = vm ? V8SnapshotProfileWriter::kVmData
418 : V8SnapshotProfileWriter::kIsolateData;
419 WriteROData(clustered_stream);
420
421 offset_space_ = vm ? V8SnapshotProfileWriter::kVmText
422 : V8SnapshotProfileWriter::kIsolateText;
423 // Needs to happen after WriteROData, because all image writers currently
424 // add the clustered data information to their output in WriteText().
425 WriteText(clustered_stream, vm);
426}
427
428void ImageWriter::WriteROData(WriteStream* stream) {
429#if defined(DART_PRECOMPILER)
430 const intptr_t start_position = stream->Position();
431#endif
432 stream->Align(kMaxObjectAlignment);
433
434 // Heap page starts here.
435
436 intptr_t section_start = stream->Position();
437
438 stream->WriteWord(next_data_offset_); // Data length.
439 // Zero values for other image header fields.
440 stream->Align(kMaxObjectAlignment);
441 ASSERT(stream->Position() - section_start == Image::kHeaderSize);
442#if defined(DART_PRECOMPILER)
443 if (profile_writer_ != nullptr) {
444 const intptr_t end_position = stream->Position();
445 profile_writer_->AttributeBytesTo(
446 V8SnapshotProfileWriter::ArtificialRootId(),
447 end_position - start_position);
448 }
449#endif
450
451 // Heap page objects start here.
452
453 for (intptr_t i = 0; i < objects_.length(); i++) {
454 const Object& obj = *objects_[i].obj_;
455 AutoTraceImage(obj, section_start, stream);
456
457 NoSafepointScope no_safepoint;
458 uword start = static_cast<uword>(obj.raw()) - kHeapObjectTag;
459
460 // Write object header with the mark and read-only bits set.
461 uword marked_tags = obj.raw()->ptr()->tags_;
462 marked_tags = ObjectLayout::OldBit::update(true, marked_tags);
463 marked_tags = ObjectLayout::OldAndNotMarkedBit::update(false, marked_tags);
464 marked_tags =
465 ObjectLayout::OldAndNotRememberedBit::update(true, marked_tags);
466 marked_tags = ObjectLayout::NewBit::update(false, marked_tags);
467#if defined(HASH_IN_OBJECT_HEADER)
468 marked_tags |= static_cast<uword>(obj.raw()->ptr()->hash_) << 32;
469#endif
470
471#if defined(IS_SIMARM_X64)
472 if (obj.IsCompressedStackMaps()) {
473 const CompressedStackMaps& map = CompressedStackMaps::Cast(obj);
474 auto const object_start = stream->Position();
475
476 const intptr_t payload_size = map.payload_size();
477 const intptr_t size_in_bytes =
478 CompressedStackMapsSizeInSnapshot(payload_size);
479 marked_tags = UpdateObjectSizeForTarget(size_in_bytes, marked_tags);
480
481 stream->WriteTargetWord(marked_tags);
482 stream->WriteFixed<uint32_t>(map.raw()->ptr()->flags_and_size_);
483 ASSERT_EQUAL(stream->Position() - object_start,
484 compiler::target::CompressedStackMaps::HeaderSize());
485 stream->WriteBytes(map.raw()->ptr()->data(), payload_size);
486 stream->Align(compiler::target::ObjectAlignment::kObjectAlignment);
487 } else if (obj.IsString()) {
488 const String& str = String::Cast(obj);
489 RELEASE_ASSERT(String::GetCachedHash(str.raw()) != 0);
490 RELEASE_ASSERT(str.IsOneByteString() || str.IsTwoByteString());
491 const intptr_t size_in_bytes =
492 StringSizeInSnapshot(str.Length(), str.IsOneByteString());
493 marked_tags = UpdateObjectSizeForTarget(size_in_bytes, marked_tags);
494
495 stream->WriteTargetWord(marked_tags);
496 stream->WriteTargetWord(static_cast<uword>(str.raw()->ptr()->length_));
497 stream->WriteTargetWord(static_cast<uword>(str.raw()->ptr()->hash_));
498 stream->WriteBytes(
499 reinterpret_cast<const void*>(start + String::kSizeofRawString),
500 StringPayloadSize(str.Length(), str.IsOneByteString()));
501 stream->Align(compiler::target::ObjectAlignment::kObjectAlignment);
502 } else if (obj.IsCodeSourceMap()) {
503 const CodeSourceMap& map = CodeSourceMap::Cast(obj);
504 const intptr_t size_in_bytes = CodeSourceMapSizeInSnapshot(map.Length());
505 marked_tags = UpdateObjectSizeForTarget(size_in_bytes, marked_tags);
506
507 stream->WriteTargetWord(marked_tags);
508 stream->WriteTargetWord(map.Length());
509 stream->WriteBytes(map.Data(), map.Length());
510 stream->Align(compiler::target::ObjectAlignment::kObjectAlignment);
511 } else if (obj.IsPcDescriptors()) {
512 const PcDescriptors& desc = PcDescriptors::Cast(obj);
513
514 const intptr_t size_in_bytes = PcDescriptorsSizeInSnapshot(desc.Length());
515 marked_tags = UpdateObjectSizeForTarget(size_in_bytes, marked_tags);
516
517 stream->WriteTargetWord(marked_tags);
518 stream->WriteTargetWord(desc.Length());
519 stream->WriteBytes(desc.raw()->ptr()->data(), desc.Length());
520 stream->Align(compiler::target::ObjectAlignment::kObjectAlignment);
521 } else {
522 const Class& clazz = Class::Handle(obj.clazz());
523 FATAL1("Unsupported class %s in rodata section.\n", clazz.ToCString());
524 }
525#else // defined(IS_SIMARM_X64)
526 const uword end = start + obj.raw()->ptr()->HeapSize();
527
528 stream->WriteWord(marked_tags);
529 start += sizeof(uword);
530 for (uword* cursor = reinterpret_cast<uword*>(start);
531 cursor < reinterpret_cast<uword*>(end); cursor++) {
532 stream->WriteWord(*cursor);
533 }
534#endif // defined(IS_SIMARM_X64)
535 }
536}
537
538#if defined(DART_PRECOMPILER)
539class DwarfAssemblyStream : public DwarfWriteStream {
540 public:
541 explicit DwarfAssemblyStream(StreamingWriteStream* stream)
542 : stream_(ASSERT_NOTNULL(stream)) {}
543
544 void sleb128(intptr_t value) { Print(".sleb128 %" Pd "\n", value); }
545 void uleb128(uintptr_t value) { Print(".uleb128 %" Pd "\n", value); }
546 void u1(uint8_t value) { Print(".byte %u\n", value); }
547 void u2(uint16_t value) { Print(".2byte %u\n", value); }
548 void u4(uint32_t value) { Print(".4byte %" Pu32 "\n", value); }
549 void u8(uint64_t value) { Print(".8byte %" Pu64 "\n", value); }
550 void string(const char* cstr) { // NOLINT
551 Print(".string \"%s\"\n", cstr); // NOLINT
552 }
553 // Uses labels, so doesn't output to start or return a useful fixup position.
554 intptr_t ReserveSize(const char* prefix, intptr_t* start) {
555 // Assignment to temp works around buggy Mac assembler.
556 Print("L%s_size = .L%s_end - .L%s_start\n", prefix, prefix, prefix);
557 Print(".4byte L%s_size\n", prefix);
558 Print(".L%s_start:\n", prefix);
559 return -1;
560 }
561 // Just need to label the end so the assembler can calculate the size, so
562 // start and the fixup position is unused.
563 void SetSize(intptr_t fixup, const char* prefix, intptr_t start) {
564 Print(".L%s_end:\n", prefix);
565 }
566 void OffsetFromSymbol(const char* symbol, intptr_t offset) {
567 if (offset == 0) {
568 PrintNamedAddress(symbol);
569 } else {
570 PrintNamedAddressWithOffset(symbol, offset);
571 }
572 }
573 void DistanceBetweenSymbolOffsets(const char* symbol1,
574 intptr_t offset1,
575 const char* symbol2,
576 intptr_t offset2) {
577 Print(".uleb128 %s - %s + %" Pd "\n", symbol1, symbol2, offset1 - offset2);
578 }
579
580 // No-op, we'll be using labels.
581 void InitializeAbstractOrigins(intptr_t size) {}
582 void RegisterAbstractOrigin(intptr_t index) {
583 // Label for DW_AT_abstract_origin references
584 Print(".Lfunc%" Pd ":\n", index);
585 }
586 void AbstractOrigin(intptr_t index) {
587 // Assignment to temp works around buggy Mac assembler.
588 Print("Ltemp%" Pd " = .Lfunc%" Pd " - %s\n", temp_, index, kDebugInfoLabel);
589 Print(".4byte Ltemp%" Pd "\n", temp_);
590 temp_++;
591 }
592
593 // Methods for writing the assembly prologues for various DWARF sections.
594 void AbbreviationsPrologue() {
595#if defined(TARGET_OS_MACOS) || defined(TARGET_OS_MACOS_IOS)
596 Print(".section __DWARF,__debug_abbrev,regular,debug\n");
597#elif defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID) || \
598 defined(TARGET_OS_FUCHSIA)
599 Print(".section .debug_abbrev,\"\"\n");
600#else
601 UNIMPLEMENTED();
602#endif
603 }
604 void DebugInfoPrologue() {
605#if defined(TARGET_OS_MACOS) || defined(TARGET_OS_MACOS_IOS)
606 Print(".section __DWARF,__debug_info,regular,debug\n");
607#elif defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID) || \
608 defined(TARGET_OS_FUCHSIA)
609 Print(".section .debug_info,\"\"\n");
610#else
611 UNIMPLEMENTED();
612#endif
613 // Used to calculate abstract origin values.
614 Print("%s:\n", kDebugInfoLabel);
615 }
616 void LineNumberProgramPrologue() {
617#if defined(TARGET_OS_MACOS) || defined(TARGET_OS_MACOS_IOS)
618 Print(".section __DWARF,__debug_line,regular,debug\n");
619#elif defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID) || \
620 defined(TARGET_OS_FUCHSIA)
621 Print(".section .debug_line,\"\"\n");
622#else
623 UNIMPLEMENTED();
624#endif
625 }
626
627 private:
628 static constexpr const char* kDebugInfoLabel = ".Ldebug_info";
629
630 void Print(const char* format, ...) PRINTF_ATTRIBUTE(2, 3) {
631 va_list args;
632 va_start(args, format);
633 stream_->VPrint(format, args);
634 va_end(args);
635 }
636
637#if defined(TARGET_ARCH_IS_32_BIT)
638#define FORM_ADDR ".4byte"
639#elif defined(TARGET_ARCH_IS_64_BIT)
640#define FORM_ADDR ".8byte"
641#endif
642
643 void PrintNamedAddress(const char* name) { Print(FORM_ADDR " %s\n", name); }
644 void PrintNamedAddressWithOffset(const char* name, intptr_t offset) {
645 Print(FORM_ADDR " %s + %" Pd "\n", name, offset);
646 }
647
648#undef FORM_ADDR
649
650 StreamingWriteStream* const stream_;
651 intptr_t temp_ = 0;
652
653 DISALLOW_COPY_AND_ASSIGN(DwarfAssemblyStream);
654};
655#endif
656
657static inline Dwarf* AddDwarfIfUnstripped(Zone* zone, bool strip, Elf* elf) {
658#if defined(DART_PRECOMPILER)
659 if (!strip) {
660 if (elf != nullptr) {
661 // Reuse the existing DWARF object.
662 ASSERT(elf->dwarf() != nullptr);
663 return elf->dwarf();
664 }
665 return new (zone) Dwarf(zone);
666 }
667#endif
668 return nullptr;
669}
670
671AssemblyImageWriter::AssemblyImageWriter(Thread* thread,
672 Dart_StreamingWriteCallback callback,
673 void* callback_data,
674 bool strip,
675 Elf* debug_elf)
676 : ImageWriter(thread),
677 assembly_stream_(512 * KB, callback, callback_data),
678 assembly_dwarf_(AddDwarfIfUnstripped(thread->zone(), strip, debug_elf)),
679 debug_elf_(debug_elf) {}
680
681void AssemblyImageWriter::Finalize() {
682#if defined(DART_PRECOMPILER)
683 if (assembly_dwarf_ != nullptr) {
684 DwarfAssemblyStream dwarf_stream(&assembly_stream_);
685 dwarf_stream.AbbreviationsPrologue();
686 assembly_dwarf_->WriteAbbreviations(&dwarf_stream);
687 dwarf_stream.DebugInfoPrologue();
688 assembly_dwarf_->WriteDebugInfo(&dwarf_stream);
689 dwarf_stream.LineNumberProgramPrologue();
690 assembly_dwarf_->WriteLineNumberProgram(&dwarf_stream);
691 }
692 if (debug_elf_ != nullptr) {
693 debug_elf_->Finalize();
694 }
695#endif
696}
697
698#if !defined(DART_PRECOMPILED_RUNTIME)
699static void EnsureAssemblerIdentifier(char* label) {
700 for (char c = *label; c != '\0'; c = *++label) {
701 if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) ||
702 ((c >= '0') && (c <= '9'))) {
703 continue;
704 }
705 *label = '_';
706 }
707}
708#endif // !defined(DART_PRECOMPILED_RUNTIME)
709
710const char* SnapshotTextObjectNamer::SnapshotNameFor(intptr_t code_index,
711 const Code& code) {
712 ASSERT(!code.IsNull());
713 const char* prefix = FLAG_precompiled_mode ? "Precompiled_" : "";
714 owner_ = code.owner();
715 if (owner_.IsNull()) {
716 insns_ = code.instructions();
717 const char* name = StubCode::NameOfStub(insns_.EntryPoint());
718 ASSERT(name != nullptr);
719 return OS::SCreate(zone_, "%sStub_%s", prefix, name);
720 }
721 // The weak reference to the Code's owner should never have been removed via
722 // an intermediate serialization, since WSRs are only introduced during
723 // precompilation.
724 owner_ = WeakSerializationReference::Unwrap(owner_);
725 ASSERT(!owner_.IsNull());
726 if (owner_.IsClass()) {
727 string_ = Class::Cast(owner_).Name();
728 const char* name = string_.ToCString();
729 EnsureAssemblerIdentifier(const_cast<char*>(name));
730 return OS::SCreate(zone_, "%sAllocationStub_%s_%" Pd, prefix, name,
731 code_index);
732 } else if (owner_.IsAbstractType()) {
733 const char* name = namer_.StubNameForType(AbstractType::Cast(owner_));
734 return OS::SCreate(zone_, "%s%s_%" Pd, prefix, name, code_index);
735 } else if (owner_.IsFunction()) {
736 const char* name = Function::Cast(owner_).ToQualifiedCString();
737 EnsureAssemblerIdentifier(const_cast<char*>(name));
738 return OS::SCreate(zone_, "%s%s_%" Pd, prefix, name, code_index);
739 } else {
740 UNREACHABLE();
741 }
742}
743
744const char* SnapshotTextObjectNamer::SnapshotNameFor(
745 intptr_t index,
746 const ImageWriter::InstructionsData& data) {
747 if (data.trampoline_bytes != nullptr) {
748 return OS::SCreate(zone_, "Trampoline_%" Pd "", index);
749 }
750 return SnapshotNameFor(index, *data.code_);
751}
752
753void AssemblyImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
754#if defined(DART_PRECOMPILED_RUNTIME)
755 UNREACHABLE();
756#else
757 Zone* zone = Thread::Current()->zone();
758
759 const bool bare_instruction_payloads =
760 FLAG_precompiled_mode && FLAG_use_bare_instructions;
761
762#if defined(DART_PRECOMPILER)
763 const char* bss_symbol =
764 vm ? "_kDartVmSnapshotBss" : "_kDartIsolateSnapshotBss";
765 intptr_t debug_segment_base = 0;
766 if (debug_elf_ != nullptr) {
767 debug_segment_base = debug_elf_->NextMemoryOffset();
768 }
769#endif
770
771 const char* instructions_symbol = vm ? kVmSnapshotInstructionsAsmSymbol
772 : kIsolateSnapshotInstructionsAsmSymbol;
773 assembly_stream_.Print(".text\n");
774 assembly_stream_.Print(".globl %s\n", instructions_symbol);
775
776 // Start snapshot at page boundary.
777 ASSERT(VirtualMemory::PageSize() >= kMaxObjectAlignment);
778 ASSERT(VirtualMemory::PageSize() >= Image::kBssAlignment);
779 Align(VirtualMemory::PageSize());
780 assembly_stream_.Print("%s:\n", instructions_symbol);
781
782 intptr_t text_offset = 0;
783#if defined(DART_PRECOMPILER)
784 // Parent used for later profile objects. Starts off as the Image. When
785 // writing bare instructions payloads, this is later updated with the
786 // InstructionsSection object which contains all the bare payloads.
787 V8SnapshotProfileWriter::ObjectId parent_id(offset_space_, text_offset);
788#endif
789
790 // This head also provides the gap to make the instructions snapshot
791 // look like a OldPage.
792 const intptr_t image_size = Utils::RoundUp(
793 next_text_offset_, compiler::target::ObjectAlignment::kObjectAlignment);
794 text_offset += WriteWordLiteralText(image_size);
795
796#if defined(DART_PRECOMPILER)
797 assembly_stream_.Print("%s %s - %s\n", kLiteralPrefix, bss_symbol,
798 instructions_symbol);
799 text_offset += compiler::target::kWordSize;
800#else
801 text_offset += WriteWordLiteralText(0); // No relocations.
802#endif
803
804 text_offset += Align(kMaxObjectAlignment, text_offset);
805 ASSERT_EQUAL(text_offset, Image::kHeaderSize);
806#if defined(DART_PRECOMPILER)
807 if (profile_writer_ != nullptr) {
808 profile_writer_->SetObjectTypeAndName(parent_id, "Image",
809 instructions_symbol);
810 // Assign post-instruction padding to the Image, unless we're writing bare
811 // instruction payloads, in which case we'll assign it to the
812 // InstructionsSection object.
813 const intptr_t padding =
814 bare_instruction_payloads ? 0 : image_size - next_text_offset_;
815 profile_writer_->AttributeBytesTo(parent_id, Image::kHeaderSize + padding);
816 profile_writer_->AddRoot(parent_id);
817 }
818#endif
819
820 if (bare_instruction_payloads) {
821#if defined(DART_PRECOMPILER)
822 if (profile_writer_ != nullptr) {
823 const V8SnapshotProfileWriter::ObjectId id(offset_space_, text_offset);
824 profile_writer_->SetObjectTypeAndName(id, instructions_section_type_,
825 instructions_symbol);
826 const intptr_t padding = image_size - next_text_offset_;
827 profile_writer_->AttributeBytesTo(
828 id, compiler::target::InstructionsSection::HeaderSize() + padding);
829 const intptr_t element_offset = id.second - parent_id.second;
830 profile_writer_->AttributeReferenceTo(
831 parent_id,
832 {id, V8SnapshotProfileWriter::Reference::kElement, element_offset});
833 // Later objects will have the InstructionsSection as a parent.
834 parent_id = id;
835 }
836#endif
837 const intptr_t section_size = image_size - text_offset;
838 // Add the RawInstructionsSection header.
839 const compiler::target::uword marked_tags =
840 ObjectLayout::OldBit::encode(true) |
841 ObjectLayout::OldAndNotMarkedBit::encode(false) |
842 ObjectLayout::OldAndNotRememberedBit::encode(true) |
843 ObjectLayout::NewBit::encode(false) |
844 ObjectLayout::SizeTag::encode(AdjustObjectSizeForTarget(section_size)) |
845 ObjectLayout::ClassIdTag::encode(kInstructionsSectionCid);
846 text_offset += WriteWordLiteralText(marked_tags);
847 // Calculated using next_text_offset_, which doesn't include post-payload
848 // padding to object alignment.
849 const intptr_t instructions_length =
850 next_text_offset_ - (text_offset + compiler::target::kWordSize);
851 text_offset += WriteWordLiteralText(instructions_length);
852 }
853
854 FrameUnwindPrologue();
855
856 PcDescriptors& descriptors = PcDescriptors::Handle(zone);
857 SnapshotTextObjectNamer namer(zone);
858
859 ASSERT(offset_space_ != V8SnapshotProfileWriter::kSnapshot);
860 for (intptr_t i = 0; i < instructions_.length(); i++) {
861 auto& data = instructions_[i];
862 const bool is_trampoline = data.trampoline_bytes != nullptr;
863 ASSERT_EQUAL(data.text_offset_, text_offset);
864
865 intptr_t dwarf_index = i;
866#if defined(DART_PRECOMPILER)
867 if (!is_trampoline && assembly_dwarf_ != nullptr) {
868 dwarf_index =
869 assembly_dwarf_->AddCode(*data.code_, SegmentRelativeOffset(vm));
870 }
871#endif
872
873 const auto object_name = namer.SnapshotNameFor(dwarf_index, data);
874
875#if defined(DART_PRECOMPILER)
876 if (profile_writer_ != nullptr) {
877 const V8SnapshotProfileWriter::ObjectId id(offset_space_, text_offset);
878 auto const type = is_trampoline ? trampoline_type_ : instructions_type_;
879 const intptr_t size = is_trampoline ? data.trampoline_length
880 : SizeInSnapshot(data.insns_->raw());
881 profile_writer_->SetObjectTypeAndName(id, type, object_name);
882 profile_writer_->AttributeBytesTo(id, size);
883 const intptr_t element_offset = id.second - parent_id.second;
884 profile_writer_->AttributeReferenceTo(
885 parent_id,
886 {id, V8SnapshotProfileWriter::Reference::kElement, element_offset});
887 }
888#endif
889
890 if (is_trampoline) {
891 const auto start = reinterpret_cast<uword>(data.trampoline_bytes);
892 const auto end = start + data.trampoline_length;
893 text_offset += WriteByteSequence(start, end);
894 delete[] data.trampoline_bytes;
895 data.trampoline_bytes = nullptr;
896 continue;
897 }
898
899 const intptr_t instr_start = text_offset;
900
901 const auto& code = *data.code_;
902 const auto& insns = *data.insns_;
903 descriptors = code.pc_descriptors();
904
905 const uword payload_start = insns.PayloadStart();
906
907 // 1. Write from the object start to the payload start. This includes the
908 // object header and the fixed fields. Not written for AOT snapshots using
909 // bare instructions.
910 if (!bare_instruction_payloads) {
911 NoSafepointScope no_safepoint;
912
913 // Write Instructions with the mark and read-only bits set.
914 uword marked_tags = insns.raw_ptr()->tags_;
915 marked_tags = ObjectLayout::OldBit::update(true, marked_tags);
916 marked_tags =
917 ObjectLayout::OldAndNotMarkedBit::update(false, marked_tags);
918 marked_tags =
919 ObjectLayout::OldAndNotRememberedBit::update(true, marked_tags);
920 marked_tags = ObjectLayout::NewBit::update(false, marked_tags);
921#if defined(HASH_IN_OBJECT_HEADER)
922 // Can't use GetObjectTagsAndHash because the update methods discard the
923 // high bits.
924 marked_tags |= static_cast<uword>(insns.raw_ptr()->hash_) << 32;
925#endif
926
927#if defined(IS_SIMARM_X64)
928 const intptr_t size_in_bytes = InstructionsSizeInSnapshot(insns.raw());
929 marked_tags = UpdateObjectSizeForTarget(size_in_bytes, marked_tags);
930 WriteWordLiteralText(marked_tags);
931 text_offset += sizeof(compiler::target::uword);
932 WriteWordLiteralText(insns.raw_ptr()->size_and_flags_);
933 text_offset += sizeof(compiler::target::uword);
934#else // defined(IS_SIMARM_X64)
935 uword object_start = reinterpret_cast<uword>(insns.raw_ptr());
936 WriteWordLiteralText(marked_tags);
937 object_start += sizeof(uword);
938 text_offset += sizeof(uword);
939 text_offset += WriteByteSequence(object_start, payload_start);
940#endif // defined(IS_SIMARM_X64)
941
942 ASSERT((text_offset - instr_start) ==
943 compiler::target::Instructions::HeaderSize());
944 }
945
946#if defined(DART_PRECOMPILER)
947 if (debug_elf_ != nullptr) {
948 debug_elf_->dwarf()->AddCode(code, {vm, text_offset});
949 }
950#endif
951 // 2. Write a label at the entry point.
952 // Linux's perf uses these labels.
953 assembly_stream_.Print("%s:\n", object_name);
954
955 {
956 // 3. Write from the payload start to payload end. For AOT snapshots
957 // with bare instructions, this is the only part serialized.
958 NoSafepointScope no_safepoint;
959 assert(kBareInstructionsAlignment <=
960 compiler::target::ObjectAlignment::kObjectAlignment);
961 const auto payload_align = bare_instruction_payloads
962 ? kBareInstructionsAlignment
963 : sizeof(compiler::target::uword);
964 const uword payload_size = Utils::RoundUp(insns.Size(), payload_align);
965 const uword payload_end = payload_start + payload_size;
966
967 ASSERT(Utils::IsAligned(text_offset, payload_align));
968
969#if defined(DART_PRECOMPILER)
970 PcDescriptors::Iterator iterator(descriptors,
971 PcDescriptorsLayout::kBSSRelocation);
972 uword next_reloc_offset = iterator.MoveNext() ? iterator.PcOffset() : -1;
973
974 // We only generate BSS relocations that are word-sized and at
975 // word-aligned offsets in the payload.
976 auto const possible_relocations_end =
977 Utils::RoundDown(payload_end, sizeof(compiler::target::uword));
978 for (uword cursor = payload_start; cursor < possible_relocations_end;
979 cursor += sizeof(compiler::target::uword)) {
980 compiler::target::uword data =
981 *reinterpret_cast<compiler::target::uword*>(cursor);
982 if ((cursor - payload_start) == next_reloc_offset) {
983 assembly_stream_.Print("%s %s - (.) + %" Pd "\n", kLiteralPrefix,
984 bss_symbol, /*addend=*/data);
985 text_offset += compiler::target::kWordSize;
986 next_reloc_offset = iterator.MoveNext() ? iterator.PcOffset() : -1;
987 } else {
988 text_offset += WriteWordLiteralText(data);
989 }
990 }
991 assert(next_reloc_offset != (possible_relocations_end - payload_start));
992 text_offset += WriteByteSequence(possible_relocations_end, payload_end);
993#else
994 text_offset += WriteByteSequence(payload_start, payload_end);
995#endif
996
997 // 4. Write from the payload end to object end. Note we can't simply copy
998 // from the object because the host object may have less alignment filler
999 // than the target object in the cross-word case. Not written for AOT
1000 // snapshots using bare instructions.
1001 if (!bare_instruction_payloads) {
1002 uword unaligned_size =
1003 compiler::target::Instructions::HeaderSize() + payload_size;
1004 uword alignment_size =
1005 Utils::RoundUp(
1006 unaligned_size,
1007 compiler::target::ObjectAlignment::kObjectAlignment) -
1008 unaligned_size;
1009 while (alignment_size > 0) {
1010 text_offset += WriteWordLiteralText(kBreakInstructionFiller);
1011 alignment_size -= sizeof(compiler::target::uword);
1012 }
1013
1014 ASSERT(kWordSize != compiler::target::kWordSize ||
1015 (text_offset - instr_start) == insns.raw()->ptr()->HeapSize());
1016 }
1017 }
1018
1019 ASSERT((text_offset - instr_start) == SizeInSnapshot(insns.raw()));
1020 }
1021
1022 // Should be a no-op unless writing bare instruction payloads, in which case
1023 // we need to add post-payload padding to the object alignment. The alignment
1024 // needs to match the one we used for image_size above.
1025 text_offset +=
1026 Align(compiler::target::ObjectAlignment::kObjectAlignment, text_offset);
1027
1028 ASSERT_EQUAL(text_offset, image_size);
1029
1030 FrameUnwindEpilogue();
1031
1032#if defined(DART_PRECOMPILER)
1033 if (debug_elf_ != nullptr) {
1034 // We need to generate a text segment of the appropriate size in the ELF
1035 // for two reasons:
1036 //
1037 // * We need unique virtual addresses for each text section in the DWARF
1038 // file and that the virtual addresses for payloads within those sections
1039 // do not overlap.
1040 //
1041 // * Our tools for converting DWARF stack traces back to "normal" Dart
1042 // stack traces calculate an offset into the appropriate instructions
1043 // section, and then add that offset to the virtual address of the
1044 // corresponding segment to get the virtual address for the frame.
1045 //
1046 // Since we don't want to add the actual contents of the segment in the
1047 // separate debugging information, we pass nullptr for the bytes, which
1048 // creates an appropriate NOBITS section instead of PROGBITS.
1049 auto const debug_segment_base2 = debug_elf_->AddText(
1050 instructions_symbol, /*bytes=*/nullptr, text_offset);
1051 // Double-check that no other ELF sections were added in the middle of
1052 // writing the text section.
1053 ASSERT(debug_segment_base2 == debug_segment_base);
1054 }
1055
1056 assembly_stream_.Print(".bss\n");
1057 // Align the BSS contents as expected by the Image class.
1058 Align(Image::kBssAlignment);
1059 assembly_stream_.Print("%s:\n", bss_symbol);
1060
1061 auto const entry_count = vm ? BSS::kVmEntryCount : BSS::kIsolateEntryCount;
1062 for (intptr_t i = 0; i < entry_count; i++) {
1063 WriteWordLiteralText(0);
1064 }
1065#endif
1066
1067#if defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID) || \
1068 defined(TARGET_OS_FUCHSIA)
1069 assembly_stream_.Print(".section .rodata\n");
1070#elif defined(TARGET_OS_MACOS) || defined(TARGET_OS_MACOS_IOS)
1071 assembly_stream_.Print(".const\n");
1072#else
1073 UNIMPLEMENTED();
1074#endif
1075
1076 const char* data_symbol =
1077 vm ? kVmSnapshotDataAsmSymbol : kIsolateSnapshotDataAsmSymbol;
1078 assembly_stream_.Print(".globl %s\n", data_symbol);
1079 Align(kMaxObjectAlignment);
1080 assembly_stream_.Print("%s:\n", data_symbol);
1081 const uword buffer = reinterpret_cast<uword>(clustered_stream->buffer());
1082 const intptr_t length = clustered_stream->bytes_written();
1083 WriteByteSequence(buffer, buffer + length);
1084#if defined(DART_PRECOMPILER)
1085 if (debug_elf_ != nullptr) {
1086 // Add a NoBits section for the ROData as well.
1087 debug_elf_->AddROData(data_symbol, clustered_stream->buffer(), length);
1088 }
1089#endif // defined(DART_PRECOMPILER)
1090#endif // !defined(DART_PRECOMPILED_RUNTIME)
1091}
1092
1093void AssemblyImageWriter::FrameUnwindPrologue() {
1094 // Creates DWARF's .debug_frame
1095 // CFI = Call frame information
1096 // CFA = Canonical frame address
1097 assembly_stream_.Print(".cfi_startproc\n");
1098
1099#if defined(TARGET_ARCH_X64)
1100 assembly_stream_.Print(".cfi_def_cfa rbp, 0\n"); // CFA is fp+0
1101 assembly_stream_.Print(".cfi_offset rbp, 0\n"); // saved fp is *(CFA+0)
1102 assembly_stream_.Print(".cfi_offset rip, 8\n"); // saved pc is *(CFA+8)
1103 // saved sp is CFA+16
1104 // Should be ".cfi_value_offset rsp, 16", but requires gcc newer than late
1105 // 2016 and not supported by Android's libunwind.
1106 // DW_CFA_expression 0x10
1107 // uleb128 register (rsp) 7 (DWARF register number)
1108 // uleb128 size of operation 2
1109 // DW_OP_plus_uconst 0x23
1110 // uleb128 addend 16
1111 assembly_stream_.Print(".cfi_escape 0x10, 31, 2, 0x23, 16\n");
1112
1113#elif defined(TARGET_ARCH_ARM64)
1114 COMPILE_ASSERT(FP == R29);
1115 COMPILE_ASSERT(LR == R30);
1116 assembly_stream_.Print(".cfi_def_cfa x29, 0\n"); // CFA is fp+0
1117 assembly_stream_.Print(".cfi_offset x29, 0\n"); // saved fp is *(CFA+0)
1118 assembly_stream_.Print(".cfi_offset x30, 8\n"); // saved pc is *(CFA+8)
1119 // saved sp is CFA+16
1120 // Should be ".cfi_value_offset sp, 16", but requires gcc newer than late
1121 // 2016 and not supported by Android's libunwind.
1122 // DW_CFA_expression 0x10
1123 // uleb128 register (x31) 31
1124 // uleb128 size of operation 2
1125 // DW_OP_plus_uconst 0x23
1126 // uleb128 addend 16
1127 assembly_stream_.Print(".cfi_escape 0x10, 31, 2, 0x23, 16\n");
1128
1129#elif defined(TARGET_ARCH_ARM)
1130#if defined(TARGET_OS_MACOS) || defined(TARGET_OS_MACOS_IOS)
1131 COMPILE_ASSERT(FP == R7);
1132 assembly_stream_.Print(".cfi_def_cfa r7, 0\n"); // CFA is fp+j0
1133 assembly_stream_.Print(".cfi_offset r7, 0\n"); // saved fp is *(CFA+0)
1134#else
1135 COMPILE_ASSERT(FP == R11);
1136 assembly_stream_.Print(".cfi_def_cfa r11, 0\n"); // CFA is fp+0
1137 assembly_stream_.Print(".cfi_offset r11, 0\n"); // saved fp is *(CFA+0)
1138#endif
1139 assembly_stream_.Print(".cfi_offset lr, 4\n"); // saved pc is *(CFA+4)
1140 // saved sp is CFA+8
1141 // Should be ".cfi_value_offset sp, 8", but requires gcc newer than late
1142 // 2016 and not supported by Android's libunwind.
1143 // DW_CFA_expression 0x10
1144 // uleb128 register (sp) 13
1145 // uleb128 size of operation 2
1146 // DW_OP_plus_uconst 0x23
1147 // uleb128 addend 8
1148 assembly_stream_.Print(".cfi_escape 0x10, 13, 2, 0x23, 8\n");
1149
1150// libunwind on ARM may use .ARM.exidx instead of .debug_frame
1151#if !defined(TARGET_OS_MACOS) && !defined(TARGET_OS_MACOS_IOS)
1152 COMPILE_ASSERT(FP == R11);
1153 assembly_stream_.Print(".fnstart\n");
1154 assembly_stream_.Print(".save {r11, lr}\n");
1155 assembly_stream_.Print(".setfp r11, sp, #0\n");
1156#endif
1157
1158#endif
1159}
1160
1161void AssemblyImageWriter::FrameUnwindEpilogue() {
1162#if defined(TARGET_ARCH_ARM)
1163#if !defined(TARGET_OS_MACOS) && !defined(TARGET_OS_MACOS_IOS)
1164 assembly_stream_.Print(".fnend\n");
1165#endif
1166#endif
1167 assembly_stream_.Print(".cfi_endproc\n");
1168}
1169
1170intptr_t AssemblyImageWriter::WriteByteSequence(uword start, uword end) {
1171 assert(end >= start);
1172 auto const end_of_words =
1173 Utils::RoundDown(end, sizeof(compiler::target::uword));
1174 for (auto cursor = reinterpret_cast<compiler::target::uword*>(start);
1175 cursor < reinterpret_cast<compiler::target::uword*>(end_of_words);
1176 cursor++) {
1177 WriteWordLiteralText(*cursor);
1178 }
1179 if (end != end_of_words) {
1180 auto start_of_rest = reinterpret_cast<const uint8_t*>(end_of_words);
1181 assembly_stream_.Print(".byte ");
1182 for (auto cursor = start_of_rest;
1183 cursor < reinterpret_cast<const uint8_t*>(end); cursor++) {
1184 if (cursor != start_of_rest) assembly_stream_.Print(", ");
1185 assembly_stream_.Print("0x%0.2" Px "", *cursor);
1186 }
1187 assembly_stream_.Print("\n");
1188 }
1189 return end - start;
1190}
1191
1192intptr_t AssemblyImageWriter::Align(intptr_t alignment, uword position) {
1193 const uword next_position = Utils::RoundUp(position, alignment);
1194 assembly_stream_.Print(".balign %" Pd ", 0\n", alignment);
1195 return next_position - position;
1196}
1197
1198BlobImageWriter::BlobImageWriter(Thread* thread,
1199 uint8_t** instructions_blob_buffer,
1200 ReAlloc alloc,
1201 intptr_t initial_size,
1202 Elf* debug_elf,
1203 Elf* elf)
1204 : ImageWriter(thread),
1205 instructions_blob_stream_(instructions_blob_buffer, alloc, initial_size),
1206 elf_(elf),
1207 debug_elf_(debug_elf) {
1208#if defined(DART_PRECOMPILER)
1209 ASSERT(debug_elf_ == nullptr || debug_elf_->dwarf() != nullptr);
1210#else
1211 RELEASE_ASSERT(elf_ == nullptr);
1212#endif
1213}
1214
1215intptr_t BlobImageWriter::WriteByteSequence(uword start, uword end) {
1216 const uword size = end - start;
1217 instructions_blob_stream_.WriteBytes(reinterpret_cast<const void*>(start),
1218 size);
1219 return size;
1220}
1221
1222void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
1223 const bool bare_instruction_payloads =
1224 FLAG_precompiled_mode && FLAG_use_bare_instructions;
1225 auto const zone = Thread::Current()->zone();
1226
1227#if defined(DART_PRECOMPILER)
1228 auto const instructions_symbol = vm ? kVmSnapshotInstructionsAsmSymbol
1229 : kIsolateSnapshotInstructionsAsmSymbol;
1230 intptr_t segment_base = 0;
1231 if (elf_ != nullptr) {
1232 segment_base = elf_->NextMemoryOffset();
1233 }
1234 intptr_t debug_segment_base = 0;
1235 if (debug_elf_ != nullptr) {
1236 debug_segment_base = debug_elf_->NextMemoryOffset();
1237 // If we're also generating an ELF snapshot, we want the virtual addresses
1238 // in it and the separately saved DWARF information to match.
1239 ASSERT(elf_ == nullptr || segment_base == debug_segment_base);
1240 }
1241#endif
1242
1243 intptr_t text_offset = 0;
1244#if defined(DART_PRECOMPILER)
1245 // Parent used for later profile objects. Starts off as the Image. When
1246 // writing bare instructions payloads, this is later updated with the
1247 // InstructionsSection object which contains all the bare payloads.
1248 V8SnapshotProfileWriter::ObjectId parent_id(offset_space_, text_offset);
1249#endif
1250
1251 // This header provides the gap to make the instructions snapshot look like a
1252 // OldPage.
1253 const intptr_t image_size = Utils::RoundUp(
1254 next_text_offset_, compiler::target::ObjectAlignment::kObjectAlignment);
1255 instructions_blob_stream_.WriteTargetWord(image_size);
1256#if defined(DART_PRECOMPILER)
1257 // Store the offset of the BSS section from the instructions section here.
1258 // If not compiling to ELF (and thus no BSS segment), write 0.
1259 const word bss_offset =
1260 elf_ != nullptr ? elf_->BssStart(vm) - segment_base : 0;
1261 ASSERT_EQUAL(Utils::RoundDown(bss_offset, Image::kBssAlignment), bss_offset);
1262 // Set the lowest bit if we are compiling to ELF.
1263 const word compiled_to_elf = elf_ != nullptr ? 0x1 : 0x0;
1264 instructions_blob_stream_.WriteTargetWord(bss_offset | compiled_to_elf);
1265#else
1266 instructions_blob_stream_.WriteTargetWord(0); // No relocations.
1267#endif
1268 instructions_blob_stream_.Align(kMaxObjectAlignment);
1269 ASSERT_EQUAL(instructions_blob_stream_.Position(), Image::kHeaderSize);
1270 text_offset += Image::kHeaderSize;
1271#if defined(DART_PRECOMPILER)
1272 if (profile_writer_ != nullptr) {
1273 profile_writer_->SetObjectTypeAndName(parent_id, "Image",
1274 instructions_symbol);
1275 // Assign post-instruction padding to the Image, unless we're writing bare
1276 // instruction payloads, in which case we'll assign it to the
1277 // InstructionsSection object.
1278 const intptr_t padding =
1279 bare_instruction_payloads ? 0 : image_size - next_text_offset_;
1280 profile_writer_->AttributeBytesTo(parent_id, Image::kHeaderSize + padding);
1281 profile_writer_->AddRoot(parent_id);
1282 }
1283#endif
1284
1285 if (bare_instruction_payloads) {
1286#if defined(DART_PRECOMPILER)
1287 if (profile_writer_ != nullptr) {
1288 const V8SnapshotProfileWriter::ObjectId id(offset_space_, text_offset);
1289 profile_writer_->SetObjectTypeAndName(id, instructions_section_type_,
1290 instructions_symbol);
1291 const intptr_t padding = image_size - next_text_offset_;
1292 profile_writer_->AttributeBytesTo(
1293 id, compiler::target::InstructionsSection::HeaderSize() + padding);
1294 const intptr_t element_offset = id.second - parent_id.second;
1295 profile_writer_->AttributeReferenceTo(
1296 parent_id,
1297 {id, V8SnapshotProfileWriter::Reference::kElement, element_offset});
1298 // Later objects will have the InstructionsSection as a parent.
1299 parent_id = id;
1300 }
1301#endif
1302 const intptr_t section_size = image_size - Image::kHeaderSize;
1303 // Add the RawInstructionsSection header.
1304 const compiler::target::uword marked_tags =
1305 ObjectLayout::OldBit::encode(true) |
1306 ObjectLayout::OldAndNotMarkedBit::encode(false) |
1307 ObjectLayout::OldAndNotRememberedBit::encode(true) |
1308 ObjectLayout::NewBit::encode(false) |
1309 ObjectLayout::SizeTag::encode(AdjustObjectSizeForTarget(section_size)) |
1310 ObjectLayout::ClassIdTag::encode(kInstructionsSectionCid);
1311 instructions_blob_stream_.WriteTargetWord(marked_tags);
1312 // Uses next_text_offset_ to avoid any post-payload padding.
1313 const intptr_t instructions_length =
1314 next_text_offset_ - Image::kHeaderSize -
1315 compiler::target::InstructionsSection::HeaderSize();
1316 instructions_blob_stream_.WriteTargetWord(instructions_length);
1317 ASSERT_EQUAL(instructions_blob_stream_.Position() - text_offset,
1318 compiler::target::InstructionsSection::HeaderSize());
1319 text_offset += compiler::target::InstructionsSection::HeaderSize();
1320 }
1321
1322 ASSERT_EQUAL(text_offset, instructions_blob_stream_.Position());
1323
1324#if defined(DART_PRECOMPILER)
1325 auto& descriptors = PcDescriptors::Handle(zone);
1326#endif
1327 SnapshotTextObjectNamer namer(zone);
1328
1329 NoSafepointScope no_safepoint;
1330 for (intptr_t i = 0; i < instructions_.length(); i++) {
1331 auto& data = instructions_[i];
1332 const bool is_trampoline = data.trampoline_bytes != nullptr;
1333 ASSERT(data.text_offset_ == text_offset);
1334
1335#if defined(DART_PRECOMPILER)
1336 const auto object_name = namer.SnapshotNameFor(i, data);
1337 if (profile_writer_ != nullptr) {
1338 const V8SnapshotProfileWriter::ObjectId id(offset_space_, text_offset);
1339 auto const type = is_trampoline ? trampoline_type_ : instructions_type_;
1340 const intptr_t size = is_trampoline ? data.trampoline_length
1341 : SizeInSnapshot(data.insns_->raw());
1342 profile_writer_->SetObjectTypeAndName(id, type, object_name);
1343 profile_writer_->AttributeBytesTo(id, size);
1344 // If the object is wrapped in an InstructionSection, then add an
1345 // element reference.
1346 const intptr_t element_offset = id.second - parent_id.second;
1347 profile_writer_->AttributeReferenceTo(
1348 parent_id,
1349 {id, V8SnapshotProfileWriter::Reference::kElement, element_offset});
1350 }
1351#endif
1352
1353 if (is_trampoline) {
1354 const auto start = reinterpret_cast<uword>(data.trampoline_bytes);
1355 const auto end = start + data.trampoline_length;
1356 text_offset += WriteByteSequence(start, end);
1357 delete[] data.trampoline_bytes;
1358 data.trampoline_bytes = nullptr;
1359 continue;
1360 }
1361
1362 const intptr_t instr_start = text_offset;
1363
1364 const auto& insns = *data.insns_;
1365 const uword payload_start = insns.PayloadStart();
1366
1367 ASSERT(Utils::IsAligned(payload_start, sizeof(compiler::target::uword)));
1368
1369 // Write Instructions with the mark and read-only bits set.
1370 uword marked_tags = insns.raw_ptr()->tags_;
1371 marked_tags = ObjectLayout::OldBit::update(true, marked_tags);
1372 marked_tags = ObjectLayout::OldAndNotMarkedBit::update(false, marked_tags);
1373 marked_tags =
1374 ObjectLayout::OldAndNotRememberedBit::update(true, marked_tags);
1375 marked_tags = ObjectLayout::NewBit::update(false, marked_tags);
1376#if defined(HASH_IN_OBJECT_HEADER)
1377 // Can't use GetObjectTagsAndHash because the update methods discard the
1378 // high bits.
1379 marked_tags |= static_cast<uword>(insns.raw_ptr()->hash_) << 32;
1380#endif
1381
1382#if defined(IS_SIMARM_X64)
1383 const intptr_t start_offset = instructions_blob_stream_.bytes_written();
1384
1385 if (!bare_instruction_payloads) {
1386 const intptr_t size_in_bytes = InstructionsSizeInSnapshot(insns.raw());
1387 marked_tags = UpdateObjectSizeForTarget(size_in_bytes, marked_tags);
1388 instructions_blob_stream_.WriteTargetWord(marked_tags);
1389 instructions_blob_stream_.WriteFixed<uint32_t>(
1390 insns.raw_ptr()->size_and_flags_);
1391 } else {
1392 ASSERT(Utils::IsAligned(instructions_blob_stream_.Position(),
1393 kBareInstructionsAlignment));
1394 }
1395 const intptr_t payload_offset = instructions_blob_stream_.Position();
1396 instructions_blob_stream_.WriteBytes(
1397 reinterpret_cast<const void*>(insns.PayloadStart()), insns.Size());
1398 const intptr_t alignment =
1399 bare_instruction_payloads
1400 ? kBareInstructionsAlignment
1401 : compiler::target::ObjectAlignment::kObjectAlignment;
1402 instructions_blob_stream_.Align(alignment);
1403 const intptr_t end_offset = instructions_blob_stream_.bytes_written();
1404 text_offset += (end_offset - start_offset);
1405#else // defined(IS_SIMARM_X64)
1406 // Only payload is output in AOT snapshots.
1407 const uword header_size =
1408 bare_instruction_payloads
1409 ? 0
1410 : compiler::target::Instructions::HeaderSize();
1411 const uword payload_size = SizeInSnapshot(insns.raw()) - header_size;
1412 const uword object_end = payload_start + payload_size;
1413 if (!bare_instruction_payloads) {
1414 uword object_start = reinterpret_cast<uword>(insns.raw_ptr());
1415 instructions_blob_stream_.WriteWord(marked_tags);
1416 text_offset += sizeof(uword);
1417 object_start += sizeof(uword);
1418 text_offset += WriteByteSequence(object_start, payload_start);
1419 } else {
1420 ASSERT(Utils::IsAligned(instructions_blob_stream_.Position(),
1421 kBareInstructionsAlignment));
1422 }
1423 const intptr_t payload_offset = instructions_blob_stream_.Position();
1424 text_offset += WriteByteSequence(payload_start, object_end);
1425#endif
1426
1427#if defined(DART_PRECOMPILER)
1428 const auto& code = *data.code_;
1429 if (elf_ != nullptr && elf_->dwarf() != nullptr) {
1430 elf_->dwarf()->AddCode(code, {vm, payload_offset});
1431 }
1432 if (debug_elf_ != nullptr) {
1433 debug_elf_->dwarf()->AddCode(code, {vm, payload_offset});
1434 }
1435
1436 // Don't patch the relocation if we're not generating ELF. The regular blobs
1437 // format does not yet support these relocations. Use
1438 // Code::VerifyBSSRelocations to check whether the relocations are patched
1439 // or not after loading.
1440 if (elf_ != nullptr) {
1441 const intptr_t current_stream_position =
1442 instructions_blob_stream_.Position();
1443
1444 descriptors = code.pc_descriptors();
1445
1446 PcDescriptors::Iterator iterator(
1447 descriptors, /*kind_mask=*/PcDescriptorsLayout::kBSSRelocation);
1448
1449 while (iterator.MoveNext()) {
1450 const intptr_t reloc_offset = iterator.PcOffset();
1451
1452 // The instruction stream at the relocation position holds an offset
1453 // into BSS corresponding to the symbol being resolved. This addend is
1454 // factored into the relocation.
1455 const auto addend = *reinterpret_cast<compiler::target::word*>(
1456 insns.PayloadStart() + reloc_offset);
1457
1458 // Overwrite the relocation position in the instruction stream with the
1459 // offset of the BSS segment from the relocation position plus the
1460 // addend in the relocation.
1461 auto const reloc_pos = payload_offset + reloc_offset;
1462 instructions_blob_stream_.SetPosition(reloc_pos);
1463
1464 const compiler::target::word offset = bss_offset - reloc_pos + addend;
1465 instructions_blob_stream_.WriteTargetWord(offset);
1466 }
1467
1468 // Restore stream position after the relocation was patched.
1469 instructions_blob_stream_.SetPosition(current_stream_position);
1470 }
1471#else
1472 USE(payload_offset);
1473#endif
1474
1475 ASSERT((text_offset - instr_start) ==
1476 ImageWriter::SizeInSnapshot(insns.raw()));
1477 }
1478
1479 // Should be a no-op unless writing bare instruction payloads, in which case
1480 // we need to add post-payload padding to the object alignment. The alignment
1481 // should match the alignment used in image_size above.
1482 instructions_blob_stream_.Align(
1483 compiler::target::ObjectAlignment::kObjectAlignment);
1484 text_offset = Utils::RoundUp(
1485 text_offset, compiler::target::ObjectAlignment::kObjectAlignment);
1486
1487 ASSERT_EQUAL(text_offset, instructions_blob_stream_.bytes_written());
1488 ASSERT_EQUAL(text_offset, image_size);
1489
1490#ifdef DART_PRECOMPILER
1491 auto const data_symbol =
1492 vm ? kVmSnapshotDataAsmSymbol : kIsolateSnapshotDataAsmSymbol;
1493 if (elf_ != nullptr) {
1494 auto const segment_base2 =
1495 elf_->AddText(instructions_symbol, instructions_blob_stream_.buffer(),
1496 instructions_blob_stream_.bytes_written());
1497 ASSERT_EQUAL(segment_base2, segment_base);
1498 // Write the .rodata section here like the AssemblyImageWriter.
1499 elf_->AddROData(data_symbol, clustered_stream->buffer(),
1500 clustered_stream->bytes_written());
1501 }
1502 if (debug_elf_ != nullptr) {
1503 // To keep memory addresses consistent, we create elf::SHT_NOBITS sections
1504 // in the debugging information. We still pass along the buffers because
1505 // we'll need the buffer bytes at generation time to calculate the build ID
1506 // so it'll match the one in the snapshot.
1507 auto const debug_segment_base2 = debug_elf_->AddText(
1508 instructions_symbol, instructions_blob_stream_.buffer(),
1509 instructions_blob_stream_.bytes_written());
1510 ASSERT_EQUAL(debug_segment_base2, debug_segment_base);
1511 debug_elf_->AddROData(data_symbol, clustered_stream->buffer(),
1512 clustered_stream->bytes_written());
1513 }
1514#endif
1515}
1516#endif // !defined(DART_PRECOMPILED_RUNTIME)
1517
1518ImageReader::ImageReader(const uint8_t* data_image,
1519 const uint8_t* instructions_image)
1520 : data_image_(data_image), instructions_image_(instructions_image) {
1521 ASSERT(data_image != NULL);
1522 ASSERT(instructions_image != NULL);
1523}
1524
1525ApiErrorPtr ImageReader::VerifyAlignment() const {
1526 if (!Utils::IsAligned(data_image_, kObjectAlignment) ||
1527 !Utils::IsAligned(instructions_image_, kMaxObjectAlignment)) {
1528 return ApiError::New(
1529 String::Handle(String::New("Snapshot is misaligned", Heap::kOld)),
1530 Heap::kOld);
1531 }
1532 return ApiError::null();
1533}
1534
1535#if defined(DART_PRECOMPILED_RUNTIME)
1536uword ImageReader::GetBareInstructionsAt(uint32_t offset) const {
1537 ASSERT(Utils::IsAligned(offset, ImageWriter::kBareInstructionsAlignment));
1538 return reinterpret_cast<uword>(instructions_image_) + offset;
1539}
1540
1541uword ImageReader::GetBareInstructionsEnd() const {
1542 Image image(instructions_image_);
1543 return reinterpret_cast<uword>(image.object_start()) + image.object_size();
1544}
1545#endif
1546
1547InstructionsPtr ImageReader::GetInstructionsAt(uint32_t offset) const {
1548 ASSERT(Utils::IsAligned(offset, kObjectAlignment));
1549
1550 ObjectPtr result = ObjectLayout::FromAddr(
1551 reinterpret_cast<uword>(instructions_image_) + offset);
1552 ASSERT(result->IsInstructions());
1553 ASSERT(result->ptr()->IsMarked());
1554
1555 return Instructions::RawCast(result);
1556}
1557
1558ObjectPtr ImageReader::GetObjectAt(uint32_t offset) const {
1559 ASSERT(Utils::IsAligned(offset, kObjectAlignment));
1560
1561 ObjectPtr result =
1562 ObjectLayout::FromAddr(reinterpret_cast<uword>(data_image_) + offset);
1563 ASSERT(result->ptr()->IsMarked());
1564
1565 return result;
1566}
1567
1568} // namespace dart
1569