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 | |
29 | namespace dart { |
30 | |
31 | #if defined(DART_PRECOMPILER) |
32 | DEFINE_FLAG(bool, |
33 | print_instruction_stats, |
34 | false, |
35 | "Print instruction statistics" ); |
36 | |
37 | DEFINE_FLAG(charp, |
38 | print_instructions_sizes_to, |
39 | NULL, |
40 | "Print sizes of all instruction objects to the given file" ); |
41 | #endif |
42 | |
43 | intptr_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 | |
60 | bool 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) |
85 | ImageWriter::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 | |
98 | void 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 | |
131 | int32_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 | |
147 | static 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) |
167 | static 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 | |
174 | static intptr_t StringPayloadSize(intptr_t len, bool isOneByteString) { |
175 | return len * (isOneByteString ? OneByteString::kBytesPerElement |
176 | : TwoByteString::kBytesPerElement); |
177 | } |
178 | |
179 | static 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 | |
186 | static 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 | |
193 | static 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 | |
200 | intptr_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) |
236 | intptr_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 | |
246 | uint32_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 | |
254 | intptr_t ImageWriter::GetTextObjectCount() const { |
255 | return instructions_.length(); |
256 | } |
257 | |
258 | void 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. |
271 | const 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 | |
285 | const 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) |
291 | void 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 | |
304 | void 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 | |
377 | void 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 | |
388 | void 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 | |
428 | void 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) |
539 | class 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 | |
657 | static 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 | |
671 | AssemblyImageWriter::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 | |
681 | void 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) |
699 | static 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 | |
710 | const 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 | |
744 | const 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 | |
753 | void 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 | |
1093 | void 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 | |
1161 | void 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 | |
1170 | intptr_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 | |
1192 | intptr_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 | |
1198 | BlobImageWriter::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 | |
1215 | intptr_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 | |
1222 | void 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 = |
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 | |
1518 | ImageReader::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 | |
1525 | ApiErrorPtr 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) |
1536 | uword ImageReader::GetBareInstructionsAt(uint32_t offset) const { |
1537 | ASSERT(Utils::IsAligned(offset, ImageWriter::kBareInstructionsAlignment)); |
1538 | return reinterpret_cast<uword>(instructions_image_) + offset; |
1539 | } |
1540 | |
1541 | uword ImageReader::GetBareInstructionsEnd() const { |
1542 | Image image(instructions_image_); |
1543 | return reinterpret_cast<uword>(image.object_start()) + image.object_size(); |
1544 | } |
1545 | #endif |
1546 | |
1547 | InstructionsPtr 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 | |
1558 | ObjectPtr 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 | |