| 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 |  | 
|---|