diff --git a/pkg/native_stack_traces/lib/src/dwarf.dart b/pkg/native_stack_traces/lib/src/dwarf.dart index 05e2313bcf6..e8d7ef1aff3 100644 --- a/pkg/native_stack_traces/lib/src/dwarf.dart +++ b/pkg/native_stack_traces/lib/src/dwarf.dart @@ -1257,7 +1257,9 @@ class Dwarf { final note = sections.single as Note; if (note.type != constants.buildIdNoteType) return null; if (note.name != constants.buildIdNoteName) return null; - return note.description.map((i) => i.toRadixString(16)).join(); + return note.description + .map((i) => i.toRadixString(16).padLeft(2, '0')) + .join(); } /// The call information for the given virtual address. There may be diff --git a/runtime/bin/elf_loader.cc b/runtime/bin/elf_loader.cc index c67f3918bd5..22c2cbc8d3a 100644 --- a/runtime/bin/elf_loader.cc +++ b/runtime/bin/elf_loader.cc @@ -476,7 +476,6 @@ bool LoadedElf::ReadSections() { ".bss does not have enough space."); vm_bss_ = reinterpret_cast(base_->start() + header.memory_offset); isolate_bss_ = vm_bss_ + BSS::kVmEntryCount; - // We set applicable BSS entries in ResolveSymbols(). } } @@ -504,22 +503,10 @@ bool LoadedElf::ResolveSymbols(const uint8_t** vm_data, output = vm_data; } else if (strcmp(name, kVmSnapshotInstructionsAsmSymbol) == 0) { output = vm_instrs; - if (output != nullptr) { - // Store the value of the symbol in the VM BSS, as it contains the - // address of the VM instructions section relative to the DSO base. - BSS::InitializeBSSEntry(BSS::Relocation::InstructionsRelocatedAddress, - sym.value, vm_bss_); - } } else if (strcmp(name, kIsolateSnapshotDataAsmSymbol) == 0) { output = isolate_data; } else if (strcmp(name, kIsolateSnapshotInstructionsAsmSymbol) == 0) { output = isolate_instrs; - if (output != nullptr) { - // Store the value of the symbol in the isolate BSS, as it contains the - // address of the isolate instructions section relative to the DSO base. - BSS::InitializeBSSEntry(BSS::Relocation::InstructionsRelocatedAddress, - sym.value, isolate_bss_); - } } if (output != nullptr) { diff --git a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart index 7a340c0b7b8..5d9fe49564d 100644 --- a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart +++ b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart @@ -113,7 +113,15 @@ main(List args) async { // Check that translating the DWARF stack trace (without internal frames) // matches the symbolic stack trace. final dwarf = Dwarf.fromFile(scriptDwarfDebugInfo)!; - assert(dwarf != null); + + // Check that build IDs match for traces. + final buildId1 = buildId(dwarfTrace1); + Expect.isFalse(buildId1.isEmpty); + Expect.equals(dwarf.buildId!, buildId1); + final buildId2 = buildId(dwarfTrace2); + Expect.isFalse(buildId2.isEmpty); + Expect.equals(dwarf.buildId!, buildId2); + final translatedDwarfTrace1 = await Stream.fromIterable(dwarfTrace1) .transform(DwarfStackTraceDecoder(dwarf)) .toList(); @@ -178,6 +186,17 @@ main(List args) async { }); } +final _buildIdRE = RegExp(r"build_id: '([a-f\d]+)'"); +String buildId(Iterable lines) { + for (final line in lines) { + final match = _buildIdRE.firstMatch(line); + if (match != null) { + return match.group(1)!; + } + } + return ''; +} + final _symbolicFrameRE = RegExp(r'^#\d+\s+'); Iterable onlySymbolicFrameLines(Iterable lines) { diff --git a/runtime/tests/vm/dart_2/use_dwarf_stack_traces_flag_test.dart b/runtime/tests/vm/dart_2/use_dwarf_stack_traces_flag_test.dart index 40aa94a0f0a..7f3557e68c0 100644 --- a/runtime/tests/vm/dart_2/use_dwarf_stack_traces_flag_test.dart +++ b/runtime/tests/vm/dart_2/use_dwarf_stack_traces_flag_test.dart @@ -113,7 +113,17 @@ main(List args) async { // Check that translating the DWARF stack trace (without internal frames) // matches the symbolic stack trace. final dwarf = Dwarf.fromFile(scriptDwarfDebugInfo); - assert(dwarf != null); + Expect.isNotNull(dwarf); + + // Check that build IDs match for traces. + Expect.isNotNull(dwarf.buildId); + final buildId1 = buildId(dwarfTrace1); + Expect.isFalse(buildId1.isEmpty); + Expect.equals(dwarf.buildId, buildId1); + final buildId2 = buildId(dwarfTrace2); + Expect.isFalse(buildId2.isEmpty); + Expect.equals(dwarf.buildId, buildId2); + final translatedDwarfTrace1 = await Stream.fromIterable(dwarfTrace1) .transform(DwarfStackTraceDecoder(dwarf)) .toList(); @@ -178,6 +188,17 @@ main(List args) async { }); } +final _buildIdRE = RegExp(r"build_id: '([a-f\d]+)'"); +String buildId(Iterable lines) { + for (final line in lines) { + final match = _buildIdRE.firstMatch(line); + if (match != null) { + return match.group(1); + } + } + return ''; +} + final _symbolicFrameRE = RegExp(r'^#\d+\s+'); Iterable onlySymbolicFrameLines(Iterable lines) { diff --git a/runtime/vm/bss_relocs.cc b/runtime/vm/bss_relocs.cc index 80f50f51ff6..2cb3157a560 100644 --- a/runtime/vm/bss_relocs.cc +++ b/runtime/vm/bss_relocs.cc @@ -31,8 +31,8 @@ void BSS::Initialize(Thread* current, uword* bss_start, bool vm) { auto const instructions = reinterpret_cast( current->isolate_group()->source()->snapshot_instructions); uword dso_base; - // For non-natively loaded snapshots, this is instead initialized in - // LoadedElf::ResolveSymbols(). + // Needed for assembly snapshots. For ELF snapshots, we set up the relocated + // address information directly in the text segment ImageHeader. if (NativeSymbolResolver::LookupSharedObject(instructions, &dso_base)) { InitializeBSSEntry(Relocation::InstructionsRelocatedAddress, instructions - dso_base, bss_start); diff --git a/runtime/vm/class_id.h b/runtime/vm/class_id.h index 0664cbb4d9f..97f932542d7 100644 --- a/runtime/vm/class_id.h +++ b/runtime/vm/class_id.h @@ -91,7 +91,8 @@ typedef uint16_t ClassIdTagType; V(FutureOr) \ V(UserTag) \ V(TransferableTypedData) \ - V(WeakSerializationReference) + V(WeakSerializationReference) \ + V(ImageHeader) #define CLASS_LIST_ARRAYS(V) \ V(Array) \ diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc index 1918244efa9..564a54b4928 100644 --- a/runtime/vm/clustered_snapshot.cc +++ b/runtime/vm/clustered_snapshot.cc @@ -7217,12 +7217,8 @@ ApiErrorPtr FullSnapshotReader::ReadVMSnapshot() { // Initialize entries in the VM portion of the BSS segment. ASSERT(Snapshot::IncludesCode(kind_)); Image image(instructions_image_); - if (image.bss_offset() != 0) { - // The const cast is safe because we're translating from the start of the - // instructions (read-only) to the start of the BSS (read-write). - uword* const bss_start = const_cast(reinterpret_cast( - instructions_image_ + image.bss_offset())); - BSS::Initialize(thread_, bss_start, /*vm=*/true); + if (auto const bss = image.bss()) { + BSS::Initialize(thread_, bss, /*vm=*/true); } #endif // defined(DART_PRECOMPILED_RUNTIME) @@ -7353,12 +7349,8 @@ void FullSnapshotReader::InitializeBSS() { // Initialize entries in the isolate portion of the BSS segment. ASSERT(Snapshot::IncludesCode(kind_)); Image image(instructions_image_); - if (image.bss_offset() != 0) { - // The const cast is safe because we're translating from the start of the - // instructions (read-only) to the start of the BSS (read-write). - uword* const bss_start = const_cast(reinterpret_cast( - instructions_image_ + image.bss_offset())); - BSS::Initialize(thread_, bss_start, /*vm=*/false); + if (auto const bss = image.bss()) { + BSS::Initialize(thread_, bss, /*vm=*/false); } #endif // defined(DART_PRECOMPILED_RUNTIME) } diff --git a/runtime/vm/compiler/runtime_api.cc b/runtime/vm/compiler/runtime_api.cc index 0aecfbd170f..c313f530b29 100644 --- a/runtime/vm/compiler/runtime_api.cc +++ b/runtime/vm/compiler/runtime_api.cc @@ -800,6 +800,10 @@ word ForwardingCorpse::FakeInstance::InstanceSize() { return 0; } +word ImageHeader::InstanceSize() { + return RoundedAllocationSize(UnroundedSize()); +} + word Instance::NextFieldOffset() { return TranslateOffsetInWords(dart::Instance::NextFieldOffset()); } @@ -808,6 +812,10 @@ word Pointer::NextFieldOffset() { return TranslateOffsetInWords(dart::Pointer::NextFieldOffset()); } +word ImageHeader::NextFieldOffset() { + return -kWordSize; +} + word WeakSerializationReference::NextFieldOffset() { return -kWordSize; } diff --git a/runtime/vm/compiler/runtime_api.h b/runtime/vm/compiler/runtime_api.h index 1a75739895b..5ec71e8c225 100644 --- a/runtime/vm/compiler/runtime_api.h +++ b/runtime/vm/compiler/runtime_api.h @@ -1179,6 +1179,13 @@ class Code : public AllStatic { static word NextFieldOffset(); }; +class ImageHeader : public AllStatic { + public: + static word UnroundedSize(); + static word InstanceSize(); + static word NextFieldOffset(); +}; + class WeakSerializationReference : public AllStatic { public: static word InstanceSize(); diff --git a/runtime/vm/compiler/runtime_offsets_extracted.h b/runtime/vm/compiler/runtime_offsets_extracted.h index 49947494e44..6ad8752ba2e 100644 --- a/runtime/vm/compiler/runtime_offsets_extracted.h +++ b/runtime/vm/compiler/runtime_offsets_extracted.h @@ -454,6 +454,7 @@ static constexpr dart::compiler::target::word FutureOr_InstanceSize = 8; static constexpr dart::compiler::target::word GrowableObjectArray_InstanceSize = 16; static constexpr dart::compiler::target::word ICData_InstanceSize = 32; +static constexpr dart::compiler::target::word ImageHeader_UnroundedSize = 20; static constexpr dart::compiler::target::word Instance_InstanceSize = 4; static constexpr dart::compiler::target::word Instructions_InstanceSize = 8; static constexpr dart::compiler::target::word Instructions_UnalignedHeaderSize = @@ -966,6 +967,7 @@ static constexpr dart::compiler::target::word FutureOr_InstanceSize = 16; static constexpr dart::compiler::target::word GrowableObjectArray_InstanceSize = 32; static constexpr dart::compiler::target::word ICData_InstanceSize = 56; +static constexpr dart::compiler::target::word ImageHeader_UnroundedSize = 40; static constexpr dart::compiler::target::word Instance_InstanceSize = 8; static constexpr dart::compiler::target::word Instructions_InstanceSize = 12; static constexpr dart::compiler::target::word Instructions_UnalignedHeaderSize = @@ -1469,6 +1471,7 @@ static constexpr dart::compiler::target::word FutureOr_InstanceSize = 8; static constexpr dart::compiler::target::word GrowableObjectArray_InstanceSize = 16; static constexpr dart::compiler::target::word ICData_InstanceSize = 32; +static constexpr dart::compiler::target::word ImageHeader_UnroundedSize = 20; static constexpr dart::compiler::target::word Instance_InstanceSize = 4; static constexpr dart::compiler::target::word Instructions_InstanceSize = 8; static constexpr dart::compiler::target::word Instructions_UnalignedHeaderSize = @@ -1982,6 +1985,7 @@ static constexpr dart::compiler::target::word FutureOr_InstanceSize = 16; static constexpr dart::compiler::target::word GrowableObjectArray_InstanceSize = 32; static constexpr dart::compiler::target::word ICData_InstanceSize = 56; +static constexpr dart::compiler::target::word ImageHeader_UnroundedSize = 40; static constexpr dart::compiler::target::word Instance_InstanceSize = 8; static constexpr dart::compiler::target::word Instructions_InstanceSize = 12; static constexpr dart::compiler::target::word Instructions_UnalignedHeaderSize = @@ -2484,6 +2488,7 @@ static constexpr dart::compiler::target::word FutureOr_InstanceSize = 8; static constexpr dart::compiler::target::word GrowableObjectArray_InstanceSize = 16; static constexpr dart::compiler::target::word ICData_InstanceSize = 32; +static constexpr dart::compiler::target::word ImageHeader_UnroundedSize = 20; static constexpr dart::compiler::target::word Instance_InstanceSize = 4; static constexpr dart::compiler::target::word Instructions_InstanceSize = 8; static constexpr dart::compiler::target::word Instructions_UnalignedHeaderSize = @@ -2990,6 +2995,7 @@ static constexpr dart::compiler::target::word FutureOr_InstanceSize = 16; static constexpr dart::compiler::target::word GrowableObjectArray_InstanceSize = 32; static constexpr dart::compiler::target::word ICData_InstanceSize = 56; +static constexpr dart::compiler::target::word ImageHeader_UnroundedSize = 40; static constexpr dart::compiler::target::word Instance_InstanceSize = 8; static constexpr dart::compiler::target::word Instructions_InstanceSize = 12; static constexpr dart::compiler::target::word Instructions_UnalignedHeaderSize = @@ -3487,6 +3493,7 @@ static constexpr dart::compiler::target::word FutureOr_InstanceSize = 8; static constexpr dart::compiler::target::word GrowableObjectArray_InstanceSize = 16; static constexpr dart::compiler::target::word ICData_InstanceSize = 32; +static constexpr dart::compiler::target::word ImageHeader_UnroundedSize = 20; static constexpr dart::compiler::target::word Instance_InstanceSize = 4; static constexpr dart::compiler::target::word Instructions_InstanceSize = 8; static constexpr dart::compiler::target::word Instructions_UnalignedHeaderSize = @@ -3994,6 +4001,7 @@ static constexpr dart::compiler::target::word FutureOr_InstanceSize = 16; static constexpr dart::compiler::target::word GrowableObjectArray_InstanceSize = 32; static constexpr dart::compiler::target::word ICData_InstanceSize = 56; +static constexpr dart::compiler::target::word ImageHeader_UnroundedSize = 40; static constexpr dart::compiler::target::word Instance_InstanceSize = 8; static constexpr dart::compiler::target::word Instructions_InstanceSize = 12; static constexpr dart::compiler::target::word Instructions_UnalignedHeaderSize = @@ -4542,6 +4550,8 @@ static constexpr dart::compiler::target::word AOT_FutureOr_InstanceSize = 8; static constexpr dart::compiler::target::word AOT_GrowableObjectArray_InstanceSize = 16; static constexpr dart::compiler::target::word AOT_ICData_InstanceSize = 24; +static constexpr dart::compiler::target::word AOT_ImageHeader_UnroundedSize = + 20; static constexpr dart::compiler::target::word AOT_Instance_InstanceSize = 4; static constexpr dart::compiler::target::word AOT_Instructions_InstanceSize = 8; static constexpr dart::compiler::target::word @@ -5105,6 +5115,8 @@ static constexpr dart::compiler::target::word AOT_FutureOr_InstanceSize = 16; static constexpr dart::compiler::target::word AOT_GrowableObjectArray_InstanceSize = 32; static constexpr dart::compiler::target::word AOT_ICData_InstanceSize = 48; +static constexpr dart::compiler::target::word AOT_ImageHeader_UnroundedSize = + 40; static constexpr dart::compiler::target::word AOT_Instance_InstanceSize = 8; static constexpr dart::compiler::target::word AOT_Instructions_InstanceSize = 12; @@ -5673,6 +5685,8 @@ static constexpr dart::compiler::target::word AOT_FutureOr_InstanceSize = 16; static constexpr dart::compiler::target::word AOT_GrowableObjectArray_InstanceSize = 32; static constexpr dart::compiler::target::word AOT_ICData_InstanceSize = 48; +static constexpr dart::compiler::target::word AOT_ImageHeader_UnroundedSize = + 40; static constexpr dart::compiler::target::word AOT_Instance_InstanceSize = 8; static constexpr dart::compiler::target::word AOT_Instructions_InstanceSize = 12; @@ -6229,6 +6243,8 @@ static constexpr dart::compiler::target::word AOT_FutureOr_InstanceSize = 8; static constexpr dart::compiler::target::word AOT_GrowableObjectArray_InstanceSize = 16; static constexpr dart::compiler::target::word AOT_ICData_InstanceSize = 24; +static constexpr dart::compiler::target::word AOT_ImageHeader_UnroundedSize = + 20; static constexpr dart::compiler::target::word AOT_Instance_InstanceSize = 4; static constexpr dart::compiler::target::word AOT_Instructions_InstanceSize = 8; static constexpr dart::compiler::target::word @@ -6785,6 +6801,8 @@ static constexpr dart::compiler::target::word AOT_FutureOr_InstanceSize = 16; static constexpr dart::compiler::target::word AOT_GrowableObjectArray_InstanceSize = 32; static constexpr dart::compiler::target::word AOT_ICData_InstanceSize = 48; +static constexpr dart::compiler::target::word AOT_ImageHeader_UnroundedSize = + 40; static constexpr dart::compiler::target::word AOT_Instance_InstanceSize = 8; static constexpr dart::compiler::target::word AOT_Instructions_InstanceSize = 12; @@ -7346,6 +7364,8 @@ static constexpr dart::compiler::target::word AOT_FutureOr_InstanceSize = 16; static constexpr dart::compiler::target::word AOT_GrowableObjectArray_InstanceSize = 32; static constexpr dart::compiler::target::word AOT_ICData_InstanceSize = 48; +static constexpr dart::compiler::target::word AOT_ImageHeader_UnroundedSize = + 40; static constexpr dart::compiler::target::word AOT_Instance_InstanceSize = 8; static constexpr dart::compiler::target::word AOT_Instructions_InstanceSize = 12; diff --git a/runtime/vm/compiler/runtime_offsets_list.h b/runtime/vm/compiler/runtime_offsets_list.h index 6f1dcb67c3b..01874c4fd15 100644 --- a/runtime/vm/compiler/runtime_offsets_list.h +++ b/runtime/vm/compiler/runtime_offsets_list.h @@ -306,6 +306,7 @@ SIZEOF(FutureOr, InstanceSize, FutureOrLayout) \ SIZEOF(GrowableObjectArray, InstanceSize, GrowableObjectArrayLayout) \ SIZEOF(ICData, InstanceSize, ICDataLayout) \ + SIZEOF(ImageHeader, UnroundedSize, ImageHeaderLayout) \ SIZEOF(Instance, InstanceSize, InstanceLayout) \ SIZEOF(Instructions, InstanceSize, InstructionsLayout) \ SIZEOF(Instructions, UnalignedHeaderSize, InstructionsLayout) \ diff --git a/runtime/vm/elf.cc b/runtime/vm/elf.cc index 606ef610c55..3dee6c58876 100644 --- a/runtime/vm/elf.cc +++ b/runtime/vm/elf.cc @@ -127,7 +127,7 @@ class Section : public ZoneAllocated { FOR_EACH_SEGMENT_LINEAR_FIELD(DEFINE_LINEAR_FIELD_METHODS); // Each section belongs to at most one PT_LOAD segment. - const Segment* load_segment = nullptr; + Segment* load_segment = nullptr; virtual intptr_t MemorySize() const = 0; @@ -234,6 +234,9 @@ class Segment : public ZoneAllocated { // a memory offset since we use it to determine the segment memory offset. ASSERT(initial_section->IsAllocated()); ASSERT(initial_section->memory_offset_is_set()); + // Make sure the memory offset chosen for the initial section is consistent + // with the alignment for the segment. + ASSERT(Utils::IsAligned(initial_section->memory_offset(), Alignment(type))); sections_.Add(initial_section); if (type == elf::ProgramHeaderType::PT_LOAD) { ASSERT(initial_section->load_segment == nullptr); @@ -245,6 +248,7 @@ class Segment : public ZoneAllocated { static intptr_t Alignment(elf::ProgramHeaderType segment_type) { switch (segment_type) { + case elf::ProgramHeaderType::PT_PHDR: case elf::ProgramHeaderType::PT_DYNAMIC: return compiler::target::kWordSize; case elf::ProgramHeaderType::PT_NOTE: @@ -319,6 +323,30 @@ class Segment : public ZoneAllocated { return true; } + void Replace(Section* old_section, Section* new_section) { + ASSERT(old_section->load_segment == this); + // All these must be true for replacement to be safe. + ASSERT_EQUAL(static_cast(old_section->type), + static_cast(new_section->type)); + ASSERT_EQUAL(old_section->MemorySize(), new_section->MemorySize()); + ASSERT_EQUAL(old_section->IsExecutable(), new_section->IsExecutable()); + ASSERT_EQUAL(old_section->IsWritable(), new_section->IsWritable()); + ASSERT(old_section->memory_offset_is_set()); + ASSERT(!new_section->memory_offset_is_set()); + for (intptr_t i = 0; i < sections_.length(); i++) { + auto const section = sections_[i]; + if (section != old_section) { + continue; + } + new_section->set_memory_offset(old_section->memory_offset()); + sections_[i] = new_section; + new_section->load_segment = this; + old_section->load_segment = nullptr; + return; + } + UNREACHABLE(); + } + intptr_t FileOffset() const { return sections_[0]->file_offset(); } intptr_t FileSize() const { @@ -792,6 +820,7 @@ class NoteSegment : public Segment { } }; +// We assume that the final program table fits in a single page of memory. static const intptr_t kProgramTableSegmentSize = Elf::kPageSize; // Here, both VM and isolate will be compiled into a single snapshot. @@ -834,9 +863,20 @@ Elf::Elf(Zone* zone, StreamingWriteStream* stream, Type type, Dwarf* dwarf) auto const start_segment = new (zone_) ProgramTableLoadSegment(zone_, kProgramTableSegmentSize); segments_.Add(start_segment); - // Note that the BSS segment must be the first user-defined segment because + // We allocate an initial build ID of all zeroes, since we need the build ID + // memory offset during ImageHeader creation (see BlobImageWriter::WriteText). + // We replace it with the real build ID during finalization. (We add this + // prior to BSS because we make the BuildID section writable also, so they are + // placed in the same segment before any non-writable ones, and if we add it + // after, then in separate debugging information, it'll go into a separate + // segment because the BSS section for debugging info is NOBITS.) + build_id_ = GenerateBuildId(); + AddSection(build_id_, kBuildIdNoteName, kSnapshotBuildIdAsmSymbol); + // Note that the BSS segment must be in the first user-defined segment because // it cannot be placed in between any two non-writable segments, due to a bug - // in Jelly Bean's ELF loader. See also Elf::WriteProgramTable(). + // in Jelly Bean's ELF loader. (For this reason, the program table segments + // generated during finalization are marked as writable.) See also + // Elf::WriteProgramTable(). // // We add it in all cases, even to the separate debugging information ELF, // to ensure that relocated addresses are consistent between ELF snapshots @@ -844,8 +884,14 @@ Elf::Elf(Zone* zone, StreamingWriteStream* stream, Type type, Dwarf* dwarf) AddSection(bss_, ".bss", kSnapshotBssAsmSymbol); } -intptr_t Elf::NextMemoryOffset() const { - return Utils::RoundUp(LastLoadSegment()->MemoryEnd(), Elf::kPageSize); +intptr_t Elf::NextMemoryOffset(intptr_t alignment) const { + // Without more information, we won't know whether we might create a new + // segment or put the section into the current one. Thus, for now, only allow + // the offset to be queried ahead of time if it matches the load segment + // alignment. + auto const type = elf::ProgramHeaderType::PT_LOAD; + ASSERT_EQUAL(alignment, Segment::Alignment(type)); + return Utils::RoundUp(LastLoadSegment()->MemoryEnd(), alignment); } uword Elf::BssStart(bool vm) const { @@ -870,8 +916,10 @@ intptr_t Elf::AddSection(Section* section, // We can't add this section to the last load segment, so create a new one. // The new segment starts at the next aligned address. auto const type = elf::ProgramHeaderType::PT_LOAD; + intptr_t alignment = + Utils::Maximum(section->alignment, Segment::Alignment(type)); auto const start_address = - Utils::RoundUp(last_load->MemoryEnd(), Segment::Alignment(type)); + Utils::RoundUp(last_load->MemoryEnd(), alignment); section->set_memory_offset(start_address); auto const segment = new (zone_) Segment(zone_, section, type); segments_.Add(segment); @@ -882,13 +930,31 @@ intptr_t Elf::AddSection(Section* section, return section->memory_offset(); } +void Elf::ReplaceSection(Section* old_section, Section* new_section) { + ASSERT(section_table_file_size_ < 0); + ASSERT(old_section->index_is_set()); + ASSERT(!new_section->index_is_set()); + ASSERT_EQUAL(new_section->IsAllocated(), old_section->IsAllocated()); + new_section->set_name(old_section->name()); + new_section->set_index(old_section->index()); + sections_[old_section->index()] = new_section; + + if (!old_section->IsAllocated()) { + return; + } + + ASSERT(program_table_file_size_ < 0); + ASSERT(old_section->load_segment != nullptr); + old_section->load_segment->Replace(old_section, new_section); +} + intptr_t Elf::AddText(const char* name, const uint8_t* bytes, intptr_t size) { // When making a separate debugging info file for assembly, we don't have // the binary text segment contents. ASSERT(type_ == Type::DebugInfo || bytes != nullptr); - auto const image = new (zone_) - BitsContainer(type_, /*executable=*/true, - /*writable=*/false, size, bytes, Elf::kPageSize); + auto const image = new (zone_) BitsContainer(type_, /*executable=*/true, + /*writable=*/false, size, bytes, + ImageWriter::kTextAlignment); return AddSection(image, ".text", name); } @@ -904,14 +970,14 @@ Section* Elf::CreateBSS(Zone* zone, Type type, intptr_t size) { memset(bytes, 0, size); } return new (zone) BitsContainer(type, /*executable=*/false, /*writable=*/true, - kBssSize, bytes, Image::kBssAlignment); + kBssSize, bytes, ImageWriter::kBssAlignment); } intptr_t Elf::AddROData(const char* name, const uint8_t* bytes, intptr_t size) { ASSERT(bytes != nullptr); - auto const image = new (zone_) - BitsContainer(type_, /*executable=*/false, - /*writable=*/false, size, bytes, kMaxObjectAlignment); + auto const image = new (zone_) BitsContainer(type_, /*executable=*/false, + /*writable=*/false, size, bytes, + ImageWriter::kRODataAlignment); return AddSection(image, ".rodata", name); } @@ -1189,11 +1255,11 @@ void Elf::Finalize() { // without changing how we add the .text and .rodata sections (since we // determine memory offsets for those sections when we add them, and the // text sections must have the memory offsets to do BSS relocations). - if (auto const build_id = GenerateBuildId()) { - AddSection(build_id, ".note.gnu.build-id", kSnapshotBuildIdAsmSymbol); + if (auto const new_build_id = GenerateBuildId()) { + ReplaceSection(build_id_, new_build_id); // Add a PT_NOTE segment for the build ID. - segments_.Add(new (zone_) NoteSegment(zone_, build_id)); + segments_.Add(new (zone_) NoteSegment(zone_, new_build_id)); } // Adding the dynamic symbol table and associated sections. @@ -1291,6 +1357,13 @@ static uint32_t HashBitsContainer(const BitsContainer* bits) { return FinalizeHash(hash, 32); } +uword Elf::BuildIdStart(intptr_t* size) { + ASSERT(size != nullptr); + ASSERT(build_id_ != nullptr); + *size = kBuildIdDescriptionLength; + return build_id_->memory_offset() + kBuildIdDescriptionOffset; +} + Section* Elf::GenerateBuildId() { uint8_t* notes_buffer = nullptr; WriteStream stream(¬es_buffer, ZoneReallocate, kBuildIdSize); @@ -1319,9 +1392,14 @@ Section* Elf::GenerateBuildId() { } ASSERT_EQUAL(stream.bytes_written() - description_start, kBuildIdDescriptionLength); + ASSERT_EQUAL(stream.bytes_written(), kBuildIdSize); + // While the build ID section does not need to be writable, it and the + // BSS section are allocated segments at the same time. Having the same flags + // ensures they will be combined in the same segment and not unnecessarily + // aligned into a new page. return new (zone_) BitsContainer( elf::SectionHeaderType::SHT_NOTE, /*allocate=*/true, /*executable=*/false, - /*writable=*/false, stream.bytes_written(), notes_buffer, kNoteAlignment); + /*writable=*/true, stream.bytes_written(), notes_buffer, kNoteAlignment); } void Elf::FinalizeProgramTable() { diff --git a/runtime/vm/elf.h b/runtime/vm/elf.h index da0af3c61e6..7f547b6e82a 100644 --- a/runtime/vm/elf.h +++ b/runtime/vm/elf.h @@ -35,7 +35,7 @@ class Elf : public ZoneAllocated { Type type, Dwarf* dwarf = nullptr); - static const intptr_t kPageSize = 4096; + static constexpr intptr_t kPageSize = 4096; bool IsStripped() const { return dwarf_ == nullptr; } @@ -44,13 +44,13 @@ class Elf : public ZoneAllocated { Dwarf* dwarf() { return dwarf_; } uword BssStart(bool vm) const; + uword BuildIdStart(intptr_t* size); - // What the next memory offset for a kPageSize-aligned section would be. + // What the next memory offset for an appropriately aligned section would be. // // Only used by BlobImageWriter::WriteText() to determine the memory offset // for the text section before it is added. - intptr_t NextMemoryOffset() const; - intptr_t AddNoBits(const char* name, const uint8_t* bytes, intptr_t size); + intptr_t NextMemoryOffset(intptr_t alignment) const; intptr_t AddText(const char* name, const uint8_t* bytes, intptr_t size); intptr_t AddROData(const char* name, const uint8_t* bytes, intptr_t size); void AddDebug(const char* name, const uint8_t* bytes, intptr_t size); @@ -58,6 +58,8 @@ class Elf : public ZoneAllocated { void Finalize(); private: + static constexpr const char* kBuildIdNoteName = ".note.gnu.build-id"; + static Section* CreateBSS(Zone* zone, Type type, intptr_t size); // Adds the section and also creates a PT_LOAD segment for the section if it @@ -71,6 +73,11 @@ class Elf : public ZoneAllocated { intptr_t AddSection(Section* section, const char* name, const char* symbol_name = nullptr); + // Replaces [old_section] with [new_section] in all appropriate places. If the + // section is allocated, the memory size of the section must be the same as + // the original to ensure any already-calculated memory offsets are unchanged. + void ReplaceSection(Section* old_section, Section* new_section); + void AddStaticSymbol(const char* name, intptr_t info, intptr_t section_index, @@ -118,6 +125,12 @@ class Elf : public ZoneAllocated { StringTable* strtab_ = nullptr; SymbolTable* symtab_ = nullptr; + // We always create a GNU build ID for all Elf files. In order to create + // the appropriate offset to it in an ImageHeader object, we create an + // initial build ID section as a placeholder and then replace that section + // during finalization once we have the information to calculate the real one. + Section* build_id_; + GrowableArray sections_; GrowableArray segments_; intptr_t memory_offset_; diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc index 3d52db564e0..f00a955f04c 100644 --- a/runtime/vm/image_snapshot.cc +++ b/runtime/vm/image_snapshot.cc @@ -6,6 +6,7 @@ #include "include/dart_api.h" #include "platform/assert.h" +#include "vm/bss_relocs.h" #include "vm/class_id.h" #include "vm/compiler/runtime_api.h" #include "vm/dwarf.h" @@ -36,10 +37,84 @@ DEFINE_FLAG(bool, DEFINE_FLAG(charp, print_instructions_sizes_to, - NULL, + nullptr, "Print sizes of all instruction objects to the given file"); #endif +const ImageHeaderLayout* Image::ExtraInfo(const uword raw_memory, + const uword size) { +#if defined(DART_PRECOMPILED_RUNTIME) + auto const raw_value = FieldValue(raw_memory, HeaderField::ImageHeaderOffset); + if (raw_value != kNoImageHeader) { + ASSERT(raw_value >= kHeaderSize); + ASSERT(raw_value <= size - ImageHeader::InstanceSize()); + return reinterpret_cast(raw_memory + raw_value); + } +#endif + return nullptr; +} + +uword* Image::bss() const { +#if defined(DART_PRECOMPILED_RUNTIME) + ASSERT(extra_info_ != nullptr); + // There should always be a non-zero BSS offset. + ASSERT(extra_info_->bss_offset_ != 0); + // Returning a non-const uword* is safe because we're translating from + // the start of the instructions (read-only) to the start of the BSS + // (read-write). + return reinterpret_cast(raw_memory_ + extra_info_->bss_offset_); +#else + return nullptr; +#endif +} + +uword Image::instructions_relocated_address() const { +#if defined(DART_PRECOMPILED_RUNTIME) + ASSERT(extra_info_ != nullptr); + // For assembly snapshots, we need to retrieve this from the initialized BSS. + const uword address = + compiled_to_elf() ? extra_info_->instructions_relocated_address_ + : bss()[BSS::RelocationIndex( + BSS::Relocation::InstructionsRelocatedAddress)]; + ASSERT(address != kNoRelocatedAddress); + return address; +#else + return kNoRelocatedAddress; +#endif +} + +const uint8_t* Image::build_id() const { +#if defined(DART_PRECOMPILED_RUNTIME) + ASSERT(extra_info_ != nullptr); + if (extra_info_->build_id_offset_ != kNoBuildId) { + return reinterpret_cast(raw_memory_ + + extra_info_->build_id_offset_); + } +#endif + return nullptr; +} + +intptr_t Image::build_id_length() const { +#if defined(DART_PRECOMPILED_RUNTIME) + ASSERT(extra_info_ != nullptr); + return extra_info_->build_id_length_; +#else + return 0; +#endif +} + +bool Image::compiled_to_elf() const { +#if defined(DART_PRECOMPILED_RUNTIME) + ASSERT(extra_info_ != nullptr); + // Since assembly snapshots can't set up this field correctly (instead, + // it's initialized in BSS at snapshot load time), we use it to detect + // direct-to-ELF snapshots. + return extra_info_->instructions_relocated_address_ != kNoRelocatedAddress; +#else + return false; +#endif +} + intptr_t ObjectOffsetTrait::Hashcode(Key key) { ObjectPtr obj = key; ASSERT(!obj->IsSmi()); @@ -88,6 +163,8 @@ ImageWriter::ImageWriter(Thread* t) next_text_offset_(0), objects_(), instructions_(), + image_type_(TagObjectTypeAsReadOnly(t->zone(), "Image")), + image_header_type_(TagObjectTypeAsReadOnly(t->zone(), "ImageHeader")), instructions_section_type_( TagObjectTypeAsReadOnly(t->zone(), "InstructionsSection")), instructions_type_(TagObjectTypeAsReadOnly(t->zone(), "Instructions")), @@ -412,33 +489,36 @@ void ImageWriter::Write(WriteStream* clustered_stream, bool vm) { data.obj_ = &Object::Handle(zone, data.raw_obj_); } - // Append the direct-mapped RO data objects after the clustered snapshot. - // We need to do this before WriteText because WriteText currently adds the - // finalized contents of the clustered_stream as data sections. - offset_space_ = vm ? V8SnapshotProfileWriter::kVmData - : V8SnapshotProfileWriter::kIsolateData; - WriteROData(clustered_stream); + // Needs to happen before WriteText, as we add information about the + // BSSsection in the text section as an initial ImageHeader object. + WriteBss(vm); offset_space_ = vm ? V8SnapshotProfileWriter::kVmText : V8SnapshotProfileWriter::kIsolateText; - // Needs to happen after WriteROData, because all image writers currently - // add the clustered data information to their output in WriteText(). - WriteText(clustered_stream, vm); + WriteText(vm); + + // Append the direct-mapped RO data objects after the clustered snapshot + // and then for ELF and assembly outputs, add appropriate sections with + // that combined data. + offset_space_ = vm ? V8SnapshotProfileWriter::kVmData + : V8SnapshotProfileWriter::kIsolateData; + WriteROData(clustered_stream, vm); } -void ImageWriter::WriteROData(WriteStream* stream) { +void ImageWriter::WriteROData(WriteStream* stream, bool vm) { #if defined(DART_PRECOMPILER) const intptr_t start_position = stream->Position(); #endif - stream->Align(kMaxObjectAlignment); + stream->Align(ImageWriter::kRODataAlignment); // Heap page starts here. intptr_t section_start = stream->Position(); stream->WriteWord(next_data_offset_); // Data length. - // Zero values for other image header fields. - stream->Align(kMaxObjectAlignment); + stream->WriteWord(0); // No ImageHeader object in data sections. + // Zero values for the rest of the Image object header bytes. + stream->Align(Image::kHeaderSize); ASSERT(stream->Position() - section_start == Image::kHeaderSize); #if defined(DART_PRECOMPILER) if (profile_writer_ != nullptr) { @@ -751,7 +831,60 @@ const char* SnapshotTextObjectNamer::SnapshotNameFor( return SnapshotNameFor(index, *data.code_); } -void AssemblyImageWriter::WriteText(WriteStream* clustered_stream, bool vm) { +#if defined(DART_PRECOMPILER) +static const char* const kVmSnapshotBssAsmSymbol = "_kDartVmSnapshotBss"; +static const char* const kIsolateSnapshotBssAsmSymbol = + "_kDartIsolateSnapshotBss"; +#endif + +void AssemblyImageWriter::WriteBss(bool vm) { +#if defined(DART_PRECOMPILER) + auto const bss_symbol = + vm ? kVmSnapshotBssAsmSymbol : kIsolateSnapshotBssAsmSymbol; + assembly_stream_.Print(".bss\n"); + // Align the BSS contents as expected by the Image class. + Align(ImageWriter::kBssAlignment); + assembly_stream_.Print("%s:\n", bss_symbol); + + auto const entry_count = vm ? BSS::kVmEntryCount : BSS::kIsolateEntryCount; + for (intptr_t i = 0; i < entry_count; i++) { + WriteWordLiteralText(0); + } +#endif +} + +void AssemblyImageWriter::WriteROData(WriteStream* clustered_stream, bool vm) { + ImageWriter::WriteROData(clustered_stream, vm); +#if defined(DART_PRECOMPILED_RUNTIME) + UNREACHABLE(); +#else +#if defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID) || \ + defined(TARGET_OS_FUCHSIA) + assembly_stream_.Print(".section .rodata\n"); +#elif defined(TARGET_OS_MACOS) || defined(TARGET_OS_MACOS_IOS) + assembly_stream_.Print(".const\n"); +#else + UNIMPLEMENTED(); +#endif + + const char* data_symbol = + vm ? kVmSnapshotDataAsmSymbol : kIsolateSnapshotDataAsmSymbol; + assembly_stream_.Print(".globl %s\n", data_symbol); + Align(ImageWriter::kRODataAlignment); + assembly_stream_.Print("%s:\n", data_symbol); + const uword buffer = reinterpret_cast(clustered_stream->buffer()); + const intptr_t length = clustered_stream->bytes_written(); + WriteByteSequence(buffer, buffer + length); +#if defined(DART_PRECOMPILER) + if (debug_elf_ != nullptr) { + // Add a NoBits section for the ROData as well. + debug_elf_->AddROData(data_symbol, clustered_stream->buffer(), length); + } +#endif // defined(DART_PRECOMPILER) +#endif // !defined(DART_PRECOMPILED_RUNTIME) +} + +void AssemblyImageWriter::WriteText(bool vm) { #if defined(DART_PRECOMPILED_RUNTIME) UNREACHABLE(); #else @@ -760,26 +893,26 @@ void AssemblyImageWriter::WriteText(WriteStream* clustered_stream, bool vm) { const bool bare_instruction_payloads = FLAG_precompiled_mode && FLAG_use_bare_instructions; -#if defined(DART_PRECOMPILER) - const char* bss_symbol = - vm ? "_kDartVmSnapshotBss" : "_kDartIsolateSnapshotBss"; - intptr_t debug_segment_base = 0; - if (debug_elf_ != nullptr) { - debug_segment_base = debug_elf_->NextMemoryOffset(); - } -#endif - const char* instructions_symbol = vm ? kVmSnapshotInstructionsAsmSymbol : kIsolateSnapshotInstructionsAsmSymbol; assembly_stream_.Print(".text\n"); assembly_stream_.Print(".globl %s\n", instructions_symbol); // Start snapshot at page boundary. - ASSERT(VirtualMemory::PageSize() >= kMaxObjectAlignment); - ASSERT(VirtualMemory::PageSize() >= Image::kBssAlignment); - Align(VirtualMemory::PageSize()); + ASSERT(ImageWriter::kTextAlignment >= VirtualMemory::PageSize()); + Align(ImageWriter::kTextAlignment); assembly_stream_.Print("%s:\n", instructions_symbol); +#if defined(DART_PRECOMPILER) + auto const bss_symbol = + vm ? kVmSnapshotBssAsmSymbol : kIsolateSnapshotBssAsmSymbol; + intptr_t debug_segment_base = 0; + if (debug_elf_ != nullptr) { + debug_segment_base = + debug_elf_->NextMemoryOffset(ImageWriter::kTextAlignment); + } +#endif + intptr_t text_offset = 0; #if defined(DART_PRECOMPILER) // Parent used for later profile objects. Starts off as the Image. When @@ -793,64 +926,113 @@ void AssemblyImageWriter::WriteText(WriteStream* clustered_stream, bool vm) { const intptr_t image_size = Utils::RoundUp( next_text_offset_, compiler::target::ObjectAlignment::kObjectAlignment); text_offset += WriteWordLiteralText(image_size); - -#if defined(DART_PRECOMPILER) - assembly_stream_.Print("%s %s - %s\n", kLiteralPrefix, bss_symbol, - instructions_symbol); - text_offset += compiler::target::kWordSize; -#else - text_offset += WriteWordLiteralText(0); // No relocations. -#endif - - text_offset += Align(kMaxObjectAlignment, text_offset); - ASSERT_EQUAL(text_offset, Image::kHeaderSize); -#if defined(DART_PRECOMPILER) - if (profile_writer_ != nullptr) { - profile_writer_->SetObjectTypeAndName(parent_id, "Image", - instructions_symbol); - // Assign post-instruction padding to the Image, unless we're writing bare - // instruction payloads, in which case we'll assign it to the - // InstructionsSection object. - const intptr_t padding = - bare_instruction_payloads ? 0 : image_size - next_text_offset_; - profile_writer_->AttributeBytesTo(parent_id, Image::kHeaderSize + padding); - profile_writer_->AddRoot(parent_id); + if (FLAG_precompiled_mode) { + // Output the offset to the ImageHeader object from the start of the image. + text_offset += WriteWordLiteralText(Image::kHeaderSize); + } else { + text_offset += WriteWordLiteralText(Image::kNoImageHeader); } -#endif + // Zero values for the rest of the Image object header bytes. + text_offset += Align(Image::kHeaderSize, text_offset); + ASSERT_EQUAL(text_offset, Image::kHeaderSize); - if (bare_instruction_payloads) { #if defined(DART_PRECOMPILER) + if (FLAG_precompiled_mode) { + if (profile_writer_ != nullptr) { + profile_writer_->SetObjectTypeAndName(parent_id, image_type_, + instructions_symbol); + // Assign post-instruction padding to the Image, unless we're writing bare + // instruction payloads, in which case we'll assign it to the + // InstructionsSection object. + const intptr_t padding = + bare_instruction_payloads ? 0 : image_size - next_text_offset_; + profile_writer_->AttributeBytesTo(parent_id, + Image::kHeaderSize + padding); + profile_writer_->AddRoot(parent_id); + } + + // Write the ImageHeader object, starting with the header. + const intptr_t image_header_size = + compiler::target::ImageHeader::InstanceSize(); if (profile_writer_ != nullptr) { const V8SnapshotProfileWriter::ObjectId id(offset_space_, text_offset); - profile_writer_->SetObjectTypeAndName(id, instructions_section_type_, + profile_writer_->SetObjectTypeAndName(id, image_header_type_, instructions_symbol); - const intptr_t padding = image_size - next_text_offset_; - profile_writer_->AttributeBytesTo( - id, compiler::target::InstructionsSection::HeaderSize() + padding); + profile_writer_->AttributeBytesTo(id, image_header_size); const intptr_t element_offset = id.second - parent_id.second; profile_writer_->AttributeReferenceTo( parent_id, {id, V8SnapshotProfileWriter::Reference::kElement, element_offset}); - // Later objects will have the InstructionsSection as a parent. - parent_id = id; } -#endif - const intptr_t section_size = image_size - text_offset; - // Add the RawInstructionsSection header. const compiler::target::uword marked_tags = ObjectLayout::OldBit::encode(true) | ObjectLayout::OldAndNotMarkedBit::encode(false) | ObjectLayout::OldAndNotRememberedBit::encode(true) | ObjectLayout::NewBit::encode(false) | - ObjectLayout::SizeTag::encode(AdjustObjectSizeForTarget(section_size)) | - ObjectLayout::ClassIdTag::encode(kInstructionsSectionCid); + ObjectLayout::SizeTag::encode( + AdjustObjectSizeForTarget(image_header_size)) | + ObjectLayout::ClassIdTag::encode(kImageHeaderCid); text_offset += WriteWordLiteralText(marked_tags); - // Calculated using next_text_offset_, which doesn't include post-payload - // padding to object alignment. - const intptr_t instructions_length = - next_text_offset_ - (text_offset + compiler::target::kWordSize); - text_offset += WriteWordLiteralText(instructions_length); + + // An ImageHeader has four fields: + // 1) The BSS offset from this section. + assembly_stream_.Print("%s %s - %s\n", kLiteralPrefix, bss_symbol, + instructions_symbol); + text_offset += compiler::target::kWordSize; + // 2) The relocated address of the instructions. + // + // For assembly snapshots, we can't generate assembly to get the absolute + // address of the text section, as using the section symbol gives us a + // relative offset from the section start, which is 0. Instead, depend on + // the BSS initialization to retrieve this for us at runtime. As a side + // effect, this field also doubles as a way to detect whether we compiled to + // assembly or directly to ELF. + text_offset += WriteWordLiteralText(Image::kNoRelocatedAddress); + // TODO(dartbug.com/43274): Change once we generate consistent build IDs + // between assembly snapshots and their debugging information. + // 3) The GNU build ID offset from this section. + text_offset += WriteWordLiteralText(Image::kNoBuildId); + // 4) The GNU build ID length. + text_offset += WriteWordLiteralText(0); + text_offset += + Align(compiler::target::ObjectAlignment::kObjectAlignment, text_offset); + + ASSERT_EQUAL(text_offset, Image::kHeaderSize + image_header_size); + + if (bare_instruction_payloads) { + if (profile_writer_ != nullptr) { + const V8SnapshotProfileWriter::ObjectId id(offset_space_, text_offset); + profile_writer_->SetObjectTypeAndName(id, instructions_section_type_, + instructions_symbol); + const intptr_t padding = image_size - next_text_offset_; + profile_writer_->AttributeBytesTo( + id, compiler::target::InstructionsSection::HeaderSize() + padding); + const intptr_t element_offset = id.second - parent_id.second; + profile_writer_->AttributeReferenceTo( + parent_id, + {id, V8SnapshotProfileWriter::Reference::kElement, element_offset}); + // Later objects will have the InstructionsSection as a parent. + parent_id = id; + } + const intptr_t section_size = image_size - text_offset; + // Add the RawInstructionsSection header. + const compiler::target::uword marked_tags = + ObjectLayout::OldBit::encode(true) | + ObjectLayout::OldAndNotMarkedBit::encode(false) | + ObjectLayout::OldAndNotRememberedBit::encode(true) | + ObjectLayout::NewBit::encode(false) | + ObjectLayout::SizeTag::encode( + AdjustObjectSizeForTarget(section_size)) | + ObjectLayout::ClassIdTag::encode(kInstructionsSectionCid); + text_offset += WriteWordLiteralText(marked_tags); + // Calculated using next_text_offset_, which doesn't include post-payload + // padding to object alignment. + const intptr_t instructions_length = + next_text_offset_ - (text_offset + compiler::target::kWordSize); + text_offset += WriteWordLiteralText(instructions_length); + } } +#endif FrameUnwindPrologue(); @@ -932,7 +1114,7 @@ void AssemblyImageWriter::WriteText(WriteStream* clustered_stream, bool vm) { text_offset += sizeof(compiler::target::uword); WriteWordLiteralText(insns.raw_ptr()->size_and_flags_); text_offset += sizeof(compiler::target::uword); -#else // defined(IS_SIMARM_X64) +#else // defined(IS_SIMARM_X64) uword object_start = reinterpret_cast(insns.raw_ptr()); WriteWordLiteralText(marked_tags); object_start += sizeof(uword); @@ -1053,41 +1235,7 @@ void AssemblyImageWriter::WriteText(WriteStream* clustered_stream, bool vm) { // writing the text section. ASSERT(debug_segment_base2 == debug_segment_base); } - - assembly_stream_.Print(".bss\n"); - // Align the BSS contents as expected by the Image class. - Align(Image::kBssAlignment); - assembly_stream_.Print("%s:\n", bss_symbol); - - auto const entry_count = vm ? BSS::kVmEntryCount : BSS::kIsolateEntryCount; - for (intptr_t i = 0; i < entry_count; i++) { - WriteWordLiteralText(0); - } #endif - -#if defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID) || \ - defined(TARGET_OS_FUCHSIA) - assembly_stream_.Print(".section .rodata\n"); -#elif defined(TARGET_OS_MACOS) || defined(TARGET_OS_MACOS_IOS) - assembly_stream_.Print(".const\n"); -#else - UNIMPLEMENTED(); -#endif - - const char* data_symbol = - vm ? kVmSnapshotDataAsmSymbol : kIsolateSnapshotDataAsmSymbol; - assembly_stream_.Print(".globl %s\n", data_symbol); - Align(kMaxObjectAlignment); - assembly_stream_.Print("%s:\n", data_symbol); - const uword buffer = reinterpret_cast(clustered_stream->buffer()); - const intptr_t length = clustered_stream->bytes_written(); - WriteByteSequence(buffer, buffer + length); -#if defined(DART_PRECOMPILER) - if (debug_elf_ != nullptr) { - // Add a NoBits section for the ROData as well. - debug_elf_->AddROData(data_symbol, clustered_stream->buffer(), length); - } -#endif // defined(DART_PRECOMPILER) #endif // !defined(DART_PRECOMPILED_RUNTIME) } @@ -1220,7 +1368,35 @@ intptr_t BlobImageWriter::WriteByteSequence(uword start, uword end) { return size; } -void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) { +void BlobImageWriter::WriteBss(bool vm) { +#if defined(DART_PRECOMPILER) + // We don't actually write a BSS segment, it's created as part of the + // Elf constructor, but make sure it has an non-zero start. + ASSERT(elf_->BssStart(vm) != 0); +#endif +} + +void BlobImageWriter::WriteROData(WriteStream* clustered_stream, bool vm) { + ImageWriter::WriteROData(clustered_stream, vm); +#if defined(DART_PRECOMPILER) + auto const data_symbol = + vm ? kVmSnapshotDataAsmSymbol : kIsolateSnapshotDataAsmSymbol; + if (elf_ != nullptr) { + elf_->AddROData(data_symbol, clustered_stream->buffer(), + clustered_stream->bytes_written()); + } + if (debug_elf_ != nullptr) { + // To keep memory addresses consistent, we create elf::SHT_NOBITS sections + // in the debugging information. We still pass along the buffers because + // we'll need the buffer bytes at generation time to calculate the build ID + // so it'll match the one in the snapshot. + debug_elf_->AddROData(data_symbol, clustered_stream->buffer(), + clustered_stream->bytes_written()); + } +#endif +} + +void BlobImageWriter::WriteText(bool vm) { const bool bare_instruction_payloads = FLAG_precompiled_mode && FLAG_use_bare_instructions; auto const zone = Thread::Current()->zone(); @@ -1230,11 +1406,12 @@ void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) { : kIsolateSnapshotInstructionsAsmSymbol; intptr_t segment_base = 0; if (elf_ != nullptr) { - segment_base = elf_->NextMemoryOffset(); + segment_base = elf_->NextMemoryOffset(ImageWriter::kTextAlignment); } intptr_t debug_segment_base = 0; if (debug_elf_ != nullptr) { - debug_segment_base = debug_elf_->NextMemoryOffset(); + debug_segment_base = + debug_elf_->NextMemoryOffset(ImageWriter::kTextAlignment); // If we're also generating an ELF snapshot, we want the virtual addresses // in it and the separately saved DWARF information to match. ASSERT(elf_ == nullptr || segment_base == debug_segment_base); @@ -1254,71 +1431,120 @@ void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) { const intptr_t image_size = Utils::RoundUp( next_text_offset_, compiler::target::ObjectAlignment::kObjectAlignment); instructions_blob_stream_.WriteTargetWord(image_size); -#if defined(DART_PRECOMPILER) - // Store the offset of the BSS section from the instructions section here. - // If not compiling to ELF (and thus no BSS segment), write 0. - const word bss_offset = - elf_ != nullptr ? elf_->BssStart(vm) - segment_base : 0; - ASSERT_EQUAL(Utils::RoundDown(bss_offset, Image::kBssAlignment), bss_offset); - // Set the lowest bit if we are compiling to ELF. - const word compiled_to_elf = elf_ != nullptr ? 0x1 : 0x0; - instructions_blob_stream_.WriteTargetWord(bss_offset | compiled_to_elf); -#else - instructions_blob_stream_.WriteTargetWord(0); // No relocations. -#endif - instructions_blob_stream_.Align(kMaxObjectAlignment); + if (FLAG_precompiled_mode) { + // Output the offset to the ImageHeader object from the start of the image. + instructions_blob_stream_.WriteTargetWord(Image::kHeaderSize); + } else { + instructions_blob_stream_.WriteTargetWord(0); // No ImageHeader object. + } + // Zero values for the rest of the Image object header bytes. + instructions_blob_stream_.Align(Image::kHeaderSize); ASSERT_EQUAL(instructions_blob_stream_.Position(), Image::kHeaderSize); text_offset += Image::kHeaderSize; -#if defined(DART_PRECOMPILER) - if (profile_writer_ != nullptr) { - profile_writer_->SetObjectTypeAndName(parent_id, "Image", - instructions_symbol); - // Assign post-instruction padding to the Image, unless we're writing bare - // instruction payloads, in which case we'll assign it to the - // InstructionsSection object. - const intptr_t padding = - bare_instruction_payloads ? 0 : image_size - next_text_offset_; - profile_writer_->AttributeBytesTo(parent_id, Image::kHeaderSize + padding); - profile_writer_->AddRoot(parent_id); - } -#endif - if (bare_instruction_payloads) { #if defined(DART_PRECOMPILER) + if (FLAG_precompiled_mode) { + if (profile_writer_ != nullptr) { + profile_writer_->SetObjectTypeAndName(parent_id, image_type_, + instructions_symbol); + // Assign post-instruction padding to the Image, unless we're writing bare + // instruction payloads, in which case we'll assign it to the + // InstructionsSection object. + const intptr_t padding = + bare_instruction_payloads ? 0 : image_size - next_text_offset_; + profile_writer_->AttributeBytesTo(parent_id, + Image::kHeaderSize + padding); + profile_writer_->AddRoot(parent_id); + } + + // Write the ImageHeader object, starting with the header. + const intptr_t image_header_size = + compiler::target::ImageHeader::InstanceSize(); if (profile_writer_ != nullptr) { const V8SnapshotProfileWriter::ObjectId id(offset_space_, text_offset); - profile_writer_->SetObjectTypeAndName(id, instructions_section_type_, + profile_writer_->SetObjectTypeAndName(id, image_header_type_, instructions_symbol); - const intptr_t padding = image_size - next_text_offset_; - profile_writer_->AttributeBytesTo( - id, compiler::target::InstructionsSection::HeaderSize() + padding); + profile_writer_->AttributeBytesTo(id, image_header_size); const intptr_t element_offset = id.second - parent_id.second; profile_writer_->AttributeReferenceTo( parent_id, {id, V8SnapshotProfileWriter::Reference::kElement, element_offset}); - // Later objects will have the InstructionsSection as a parent. - parent_id = id; } -#endif - const intptr_t section_size = image_size - Image::kHeaderSize; - // Add the RawInstructionsSection header. const compiler::target::uword marked_tags = ObjectLayout::OldBit::encode(true) | ObjectLayout::OldAndNotMarkedBit::encode(false) | ObjectLayout::OldAndNotRememberedBit::encode(true) | ObjectLayout::NewBit::encode(false) | - ObjectLayout::SizeTag::encode(AdjustObjectSizeForTarget(section_size)) | - ObjectLayout::ClassIdTag::encode(kInstructionsSectionCid); + ObjectLayout::SizeTag::encode( + AdjustObjectSizeForTarget(image_header_size)) | + ObjectLayout::ClassIdTag::encode(kImageHeaderCid); instructions_blob_stream_.WriteTargetWord(marked_tags); - // Uses next_text_offset_ to avoid any post-payload padding. - const intptr_t instructions_length = - next_text_offset_ - Image::kHeaderSize - - compiler::target::InstructionsSection::HeaderSize(); - instructions_blob_stream_.WriteTargetWord(instructions_length); + + ASSERT(elf_ != nullptr); + // An ImageHeader has four fields: + // 1) The BSS offset from this section. + const word bss_offset = elf_->BssStart(vm) - segment_base; + ASSERT(bss_offset != Image::kNoBssSection); + instructions_blob_stream_.WriteTargetWord(bss_offset); + // 2) The relocated address of the instructions. + // + // Since we set this to a non-zero value for ELF snapshots, we also use this + // to detect compiled-to-ELF snapshots. + ASSERT(segment_base != Image::kNoRelocatedAddress); + instructions_blob_stream_.WriteTargetWord(segment_base); + // 3) The GNU build ID offset from this section. + intptr_t build_id_length = 0; + const word build_id_offset = + elf_->BuildIdStart(&build_id_length) - segment_base; + ASSERT(build_id_offset != Image::kNoBuildId); + instructions_blob_stream_.WriteTargetWord(build_id_offset); + // 4) The GNU build ID length. + ASSERT(build_id_length != 0); + instructions_blob_stream_.WriteTargetWord(build_id_length); + instructions_blob_stream_.Align( + compiler::target::ObjectAlignment::kObjectAlignment); + ASSERT_EQUAL(instructions_blob_stream_.Position() - text_offset, - compiler::target::InstructionsSection::HeaderSize()); - text_offset += compiler::target::InstructionsSection::HeaderSize(); + image_header_size); + text_offset += image_header_size; + + if (bare_instruction_payloads) { + if (profile_writer_ != nullptr) { + const V8SnapshotProfileWriter::ObjectId id(offset_space_, text_offset); + profile_writer_->SetObjectTypeAndName(id, instructions_section_type_, + instructions_symbol); + const intptr_t padding = image_size - next_text_offset_; + profile_writer_->AttributeBytesTo( + id, compiler::target::InstructionsSection::HeaderSize() + padding); + const intptr_t element_offset = id.second - parent_id.second; + profile_writer_->AttributeReferenceTo( + parent_id, + {id, V8SnapshotProfileWriter::Reference::kElement, element_offset}); + // Later objects will have the InstructionsSection as a parent. + parent_id = id; + } + const intptr_t section_size = image_size - text_offset; + // Add the RawInstructionsSection header. + const compiler::target::uword marked_tags = + ObjectLayout::OldBit::encode(true) | + ObjectLayout::OldAndNotMarkedBit::encode(false) | + ObjectLayout::OldAndNotRememberedBit::encode(true) | + ObjectLayout::NewBit::encode(false) | + ObjectLayout::SizeTag::encode( + AdjustObjectSizeForTarget(section_size)) | + ObjectLayout::ClassIdTag::encode(kInstructionsSectionCid); + instructions_blob_stream_.WriteTargetWord(marked_tags); + // Uses next_text_offset_ to avoid any post-payload padding. + const intptr_t instructions_length = + next_text_offset_ - text_offset - + compiler::target::InstructionsSection::HeaderSize(); + instructions_blob_stream_.WriteTargetWord(instructions_length); + ASSERT_EQUAL(instructions_blob_stream_.Position() - text_offset, + compiler::target::InstructionsSection::HeaderSize()); + text_offset += compiler::target::InstructionsSection::HeaderSize(); + } } +#endif ASSERT_EQUAL(text_offset, instructions_blob_stream_.Position()); @@ -1403,7 +1629,7 @@ void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) { instructions_blob_stream_.Align(alignment); const intptr_t end_offset = instructions_blob_stream_.bytes_written(); text_offset += (end_offset - start_offset); -#else // defined(IS_SIMARM_X64) +#else // defined(IS_SIMARM_X64) // Only payload is output in AOT snapshots. const uword header_size = bare_instruction_payloads @@ -1447,6 +1673,7 @@ void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) { PcDescriptors::Iterator iterator( descriptors, /*kind_mask=*/PcDescriptorsLayout::kBSSRelocation); + const intptr_t bss_offset = elf_->BssStart(vm) - segment_base; while (iterator.MoveNext()) { const intptr_t reloc_offset = iterator.PcOffset(); @@ -1488,17 +1715,12 @@ void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) { ASSERT_EQUAL(text_offset, instructions_blob_stream_.bytes_written()); ASSERT_EQUAL(text_offset, image_size); -#ifdef DART_PRECOMPILER - auto const data_symbol = - vm ? kVmSnapshotDataAsmSymbol : kIsolateSnapshotDataAsmSymbol; +#if defined(DART_PRECOMPILER) if (elf_ != nullptr) { auto const segment_base2 = elf_->AddText(instructions_symbol, instructions_blob_stream_.buffer(), instructions_blob_stream_.bytes_written()); ASSERT_EQUAL(segment_base2, segment_base); - // Write the .rodata section here like the AssemblyImageWriter. - elf_->AddROData(data_symbol, clustered_stream->buffer(), - clustered_stream->bytes_written()); } if (debug_elf_ != nullptr) { // To keep memory addresses consistent, we create elf::SHT_NOBITS sections @@ -1509,8 +1731,6 @@ void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) { instructions_symbol, instructions_blob_stream_.buffer(), instructions_blob_stream_.bytes_written()); ASSERT_EQUAL(debug_segment_base2, debug_segment_base); - debug_elf_->AddROData(data_symbol, clustered_stream->buffer(), - clustered_stream->bytes_written()); } #endif } @@ -1518,10 +1738,8 @@ void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) { ImageReader::ImageReader(const uint8_t* data_image, const uint8_t* instructions_image) - : data_image_(data_image), instructions_image_(instructions_image) { - ASSERT(data_image != NULL); - ASSERT(instructions_image != NULL); -} + : data_image_(ASSERT_NOTNULL(data_image)), + instructions_image_(ASSERT_NOTNULL(instructions_image)) {} ApiErrorPtr ImageReader::VerifyAlignment() const { if (!Utils::IsAligned(data_image_, kObjectAlignment) || diff --git a/runtime/vm/image_snapshot.h b/runtime/vm/image_snapshot.h index 6727ca0c05c..9eafb004001 100644 --- a/runtime/vm/image_snapshot.h +++ b/runtime/vm/image_snapshot.h @@ -13,6 +13,7 @@ #include "vm/allocation.h" #include "vm/compiler/runtime_api.h" #include "vm/datastream.h" +#include "vm/elf.h" #include "vm/globals.h" #include "vm/growable_array.h" #include "vm/hash_map.h" @@ -26,65 +27,117 @@ namespace dart { // Forward declarations. class Code; class Dwarf; -class Elf; class Instructions; class Object; class Image : ValueObject { public: - explicit Image(const void* raw_memory) : raw_memory_(raw_memory) { + explicit Image(const void* raw_memory) + : Image(reinterpret_cast(raw_memory)) {} + explicit Image(const uword raw_memory) + : raw_memory_(raw_memory), + snapshot_size_(FieldValue(raw_memory, HeaderField::ImageSize)), + extra_info_(ExtraInfo(raw_memory_, snapshot_size_)) { ASSERT(Utils::IsAligned(raw_memory, kMaxObjectAlignment)); } + // Even though an Image is read-only memory, we must return a void* here. + // All objects in an Image are pre-marked, though, so the GC will not attempt + // to change the returned memory. void* object_start() const { - return reinterpret_cast(reinterpret_cast(raw_memory_) + - kHeaderSize); + return reinterpret_cast(raw_memory_ + kHeaderSize); } - uword object_size() const { - uword snapshot_size = *reinterpret_cast(raw_memory_); - return snapshot_size - kHeaderSize; - } + uword object_size() const { return snapshot_size_ - kHeaderSize; } bool contains(uword address) const { uword start = reinterpret_cast(object_start()); return address >= start && (address - start < object_size()); } - // Returns the offset of the BSS section from this image. Only has meaning for - // instructions images. - word bss_offset() const { - auto const raw_value = *(reinterpret_cast(raw_memory_) + 1); - return Utils::RoundDown(raw_value, kBssAlignment); - } + // Returns the address of the BSS section, or nullptr if one is not available. + // Only has meaning for instructions images from precompiled snapshots. + uword* bss() const; - // Returns true if the image was compiled directly to ELF. Only has meaning - // for instructions images. - bool compiled_to_elf() const { - auto const raw_value = *(reinterpret_cast(raw_memory_) + 1); - return (raw_value & 0x1) == 0x1; - } + // Returns the relocated address of the isolate's instructions, or 0 if + // one is not available. Only has meaning for instructions images from + // precompiled snapshots. + uword instructions_relocated_address() const; + + // Returns the GNU build ID, or nullptr if not available. See + // build_id_length() for the length of the returned buffer. Only has meaning + // for instructions images from precompiled snapshots. + const uint8_t* build_id() const; + + // Returns the length of the GNU build ID returned by build_id(). Only has + // meaning for instructions images from precompiled snapshots. + intptr_t build_id_length() const; + + // Returns whether this instructions section was compiled to ELF. Only has + // meaning for instructions images from precompiled snapshots. + bool compiled_to_elf() const; private: - static constexpr intptr_t kHeaderFields = 2; + // Word-sized fields in an Image object header. + enum class HeaderField : intptr_t { + // The size of the image (total of header and payload). + ImageSize, + // The offset of the ImageHeader object in the image. Note this offset + // is from the start of the _image_, _not_ from its payload start, so we + // can detect images without ImageHeaders by a 0 value here. + ImageHeaderOffset, + // If adding more fields, updating kHeaderFields below. (However, more + // fields _can't_ be added on 64-bit architectures, see the restrictions + // on kHeaderSize below.) + }; + + // Number of fields described by the HeaderField enum. + static constexpr intptr_t kHeaderFields = + static_cast(HeaderField::ImageHeaderOffset) + 1; + + static uword FieldValue(uword raw_memory, HeaderField field) { + return reinterpret_cast( + raw_memory)[static_cast(field)]; + } + + // Constants used to denote special values for the offsets in the Image + // object header and the fields of the ImageHeader object. + static constexpr intptr_t kNoImageHeader = 0; + static constexpr intptr_t kNoBssSection = 0; + static constexpr intptr_t kNoRelocatedAddress = 0; + static constexpr intptr_t kNoBuildId = 0; + + // The size of the Image object header. + // + // Note: Image::kHeaderSize is _not_ an architecture-dependent constant, + // and so there is no compiler::target::Image::kHeaderSize. static constexpr intptr_t kHeaderSize = kMaxObjectAlignment; // Explicitly double-checking kHeaderSize is never changed. Increasing the // Image header size would mean objects would not start at a place expected // by parts of the VM (like the GC) that use Image pages as HeapPages. static_assert(kHeaderSize == kMaxObjectAlignment, "Image page cannot be used as HeapPage"); + // Make sure that the number of fields in the Image header fit both on the + // host and target architectures. + static_assert(kHeaderFields * kWordSize <= kHeaderSize, + "Too many fields in Image header for host architecture"); + static_assert(kHeaderFields * compiler::target::kWordSize <= kHeaderSize, + "Too many fields in Image header for target architecture"); - // Determines how many bits we have for encoding any extra information in - // the BSS offset. - static constexpr intptr_t kBssAlignment = compiler::target::kWordSize; + // We don't use a handle or the tagged pointer because this object cannot be + // moved in memory by the GC. + static const ImageHeaderLayout* ExtraInfo(const uword raw_memory, + const uword size); - const void* raw_memory_; // The symbol kInstructionsSnapshot. + // Most internal uses would cast this to uword, so just store it as such. + const uword raw_memory_; + const intptr_t snapshot_size_; + const ImageHeaderLayout* const extra_info_; // For access to private constants. friend class AssemblyImageWriter; friend class BlobImageWriter; friend class ImageWriter; - friend class Elf; DISALLOW_COPY_AND_ASSIGN(Image); }; @@ -178,12 +231,35 @@ class ImageWriter : public ValueObject { explicit ImageWriter(Thread* thread); virtual ~ImageWriter() {} + // Alignment constants used in writing ELF or assembly snapshots. + + // BSS sections contain word-sized data. + static constexpr intptr_t kBssAlignment = compiler::target::kWordSize; + // ROData sections contain objects wrapped in an Image object. + static constexpr intptr_t kRODataAlignment = kMaxObjectAlignment; + // Text sections contain objects (even in bare instructions mode) wrapped + // in an Image object, and for now we also align them to the same page + // size assumed by Elf objects. + static_assert(Elf::kPageSize >= kMaxObjectAlignment, + "Page alignment must be consistent with max object alignment"); + static constexpr intptr_t kTextAlignment = Elf::kPageSize; + void ResetOffsets() { next_data_offset_ = Image::kHeaderSize; next_text_offset_ = Image::kHeaderSize; - if (FLAG_use_bare_instructions && FLAG_precompiled_mode) { - next_text_offset_ += compiler::target::InstructionsSection::HeaderSize(); +#if defined(DART_PRECOMPILER) + if (FLAG_precompiled_mode) { + // We reserve space for the initial ImageHeader object. It is manually + // serialized since it involves offsets to other parts of the snapshot. + next_text_offset_ += compiler::target::ImageHeader::InstanceSize(); + if (FLAG_use_bare_instructions) { + // For bare instructions mode, we wrap all the instruction payloads + // in a single InstructionsSection object. + next_text_offset_ += + compiler::target::InstructionsSection::HeaderSize(); + } } +#endif objects_.Clear(); instructions_.Clear(); } @@ -254,8 +330,9 @@ class ImageWriter : public ValueObject { static const char* TagObjectTypeAsReadOnly(Zone* zone, const char* type); protected: - void WriteROData(WriteStream* stream); - virtual void WriteText(WriteStream* clustered_stream, bool vm) = 0; + virtual void WriteBss(bool vm) = 0; + virtual void WriteROData(WriteStream* clustered_stream, bool vm); + virtual void WriteText(bool vm) = 0; void DumpInstructionStats(); void DumpInstructionsSizes(); @@ -309,6 +386,8 @@ class ImageWriter : public ValueObject { V8SnapshotProfileWriter::IdSpace offset_space_ = V8SnapshotProfileWriter::kSnapshot; V8SnapshotProfileWriter* profile_writer_ = nullptr; + const char* const image_type_; + const char* const image_header_type_; const char* const instructions_section_type_; const char* const instructions_type_; const char* const trampoline_type_; @@ -337,14 +416,13 @@ class TraceImageObjectScope { stream_(ASSERT_NOTNULL(stream)), section_offset_(section_offset), start_offset_(stream_->Position() - section_offset), - object_(object) {} + object_type_(writer->ObjectTypeForProfile(object)) {} ~TraceImageObjectScope() { if (writer_->profile_writer_ == nullptr) return; ASSERT(writer_->IsROSpace()); writer_->profile_writer_->SetObjectTypeAndName( - {writer_->offset_space_, start_offset_}, - writer_->ObjectTypeForProfile(object_), nullptr); + {writer_->offset_space_, start_offset_}, object_type_, nullptr); writer_->profile_writer_->AttributeBytesTo( {writer_->offset_space_, start_offset_}, stream_->Position() - section_offset_ - start_offset_); @@ -355,7 +433,7 @@ class TraceImageObjectScope { const T* const stream_; const intptr_t section_offset_; const intptr_t start_offset_; - const Object& object_; + const char* const object_type_; }; class SnapshotTextObjectNamer { @@ -391,9 +469,11 @@ class AssemblyImageWriter : public ImageWriter { Elf* debug_elf = nullptr); void Finalize(); - virtual void WriteText(WriteStream* clustered_stream, bool vm); - private: + virtual void WriteBss(bool vm); + virtual void WriteROData(WriteStream* clustered_stream, bool vm); + virtual void WriteText(bool vm); + void FrameUnwindPrologue(); void FrameUnwindEpilogue(); intptr_t WriteByteSequence(uword start, uword end); @@ -431,13 +511,15 @@ class BlobImageWriter : public ImageWriter { Elf* debug_elf = nullptr, Elf* elf = nullptr); - virtual void WriteText(WriteStream* clustered_stream, bool vm); - intptr_t InstructionsBlobSize() const { return instructions_blob_stream_.bytes_written(); } private: + virtual void WriteBss(bool vm); + virtual void WriteROData(WriteStream* clustered_stream, bool vm); + virtual void WriteText(bool vm); + intptr_t WriteByteSequence(uword start, uword end); WriteStream instructions_blob_stream_; diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index 119901ab323..941dc19b857 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -16051,6 +16051,10 @@ ICDataPtr ICData::Clone(const ICData& from) { } #endif +const char* ImageHeader::ToCString() const { + return "ImageHeader"; +} + const char* WeakSerializationReference::ToCString() const { #if defined(DART_PRECOMPILED_RUNTIME) return Symbols::OptimizedOut().ToCString(); @@ -24400,8 +24404,7 @@ StackTracePtr StackTrace::New(const Array& code_array, static void PrintNonSymbolicStackFrameBody(BaseTextBuffer* buffer, uword call_addr, uword isolate_instructions, - uword vm_instructions, - uword isolate_relocated_address) { + uword vm_instructions) { const Image vm_image(reinterpret_cast(vm_instructions)); const Image isolate_image( reinterpret_cast(isolate_instructions)); @@ -24412,7 +24415,9 @@ static void PrintNonSymbolicStackFrameBody(BaseTextBuffer* buffer, // Only print the relocated address of the call when we know the saved // debugging information (if any) will have the same relocated address. if (isolate_image.compiled_to_elf()) { - buffer->Printf(" virt %" Pp "", isolate_relocated_address + offset); + const uword relocated_section_start = + isolate_image.instructions_relocated_address(); + buffer->Printf(" virt %" Pp "", relocated_section_start + offset); } buffer->Printf(" %s+0x%" Px "", symbol_name, offset); } else if (vm_image.contains(call_addr)) { @@ -24484,16 +24489,6 @@ static void PrintSymbolicStackFrame(Zone* zone, PrintSymbolicStackFrameBody(buffer, function_name, url, line, column); } -// Find the relocated base of the given instructions section. -uword InstructionsRelocatedAddress(uword instructions_start) { - Image image(reinterpret_cast(instructions_start)); - auto const bss_start = - reinterpret_cast(instructions_start + image.bss_offset()); - auto const index = - BSS::RelocationIndex(BSS::Relocation::InstructionsRelocatedAddress); - return bss_start[index]; -} - const char* StackTrace::ToCString() const { auto const T = Thread::Current(); auto const zone = T->zone(); @@ -24512,11 +24507,15 @@ const char* StackTrace::ToCString() const { T->isolate_group()->source()->snapshot_instructions); auto const vm_instructions = reinterpret_cast( Dart::vm_isolate()->group()->source()->snapshot_instructions); - auto const vm_relocated_address = - InstructionsRelocatedAddress(vm_instructions); - auto const isolate_relocated_address = - InstructionsRelocatedAddress(isolate_instructions); if (FLAG_dwarf_stack_traces_mode) { + const Image isolate_instructions_image( + reinterpret_cast(isolate_instructions)); + const Image vm_instructions_image( + reinterpret_cast(vm_instructions)); + auto const isolate_relocated_address = + isolate_instructions_image.instructions_relocated_address(); + auto const vm_relocated_address = + vm_instructions_image.instructions_relocated_address(); // The Dart standard requires the output of StackTrace.toString to include // all pending activations with precise source locations (i.e., to expand // inlined frames and provide line and column numbers). @@ -24530,6 +24529,14 @@ const char* StackTrace::ToCString() const { OSThread* thread = OSThread::Current(); buffer.Printf("pid: %" Pd ", tid: %" Pd ", name %s\n", OS::ProcessId(), OSThread::ThreadIdToIntPtr(thread->id()), thread->name()); + if (auto const build_id = isolate_instructions_image.build_id()) { + const intptr_t length = isolate_instructions_image.build_id_length(); + buffer.Printf("build_id: '"); + for (intptr_t i = 0; i < length; i++) { + buffer.Printf("%02.2x", build_id[i]); + } + buffer.Printf("'\n"); + } // Print the dso_base of the VM and isolate_instructions. We print both here // as the VM and isolate may be loaded from different snapshot images. buffer.Printf("isolate_dso_base: %" Px "", @@ -24591,8 +24598,7 @@ const char* StackTrace::ToCString() const { // prints call addresses instead of return addresses. buffer.Printf(" #%02" Pd " abs %" Pp "", frame_index, call_addr); PrintNonSymbolicStackFrameBody( - &buffer, call_addr, isolate_instructions, vm_instructions, - isolate_relocated_address); + &buffer, call_addr, isolate_instructions, vm_instructions); frame_index++; continue; } else if (function.IsNull()) { @@ -24601,8 +24607,7 @@ const char* StackTrace::ToCString() const { // non-symbolic stack traces. PrintSymbolicStackFrameIndex(&buffer, frame_index); PrintNonSymbolicStackFrameBody( - &buffer, call_addr, isolate_instructions, vm_instructions, - isolate_relocated_address); + &buffer, call_addr, isolate_instructions, vm_instructions); frame_index++; continue; } diff --git a/runtime/vm/object.h b/runtime/vm/object.h index 22e28100afd..5084dff7fe2 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -5959,6 +5959,26 @@ class ExceptionHandlers : public Object { friend class Object; }; +// An ImageHeader contains extra information about serialized AOT snapshots. +// +// To avoid changing the embedder to return more information about an AOT +// snapshot and possibly disturbing existing clients of that interface, we +// serialize a single ImageHeader object at the start of any text segments. +class ImageHeader : public Object { + public: + static intptr_t InstanceSize() { + return RoundedAllocationSize(sizeof(ImageHeaderLayout)); + } + // There are no public methods for the ImageHeader contents, because + // all access to the contents is handled by methods on the Image class. + + private: + // Note there are no New() methods for ImageHeaders. Unstead, the serializer + // writes the ImageHeaderLayout object manually at the start of the text + // segment in precompiled snapshots. + FINAL_HEAP_OBJECT_IMPLEMENTATION(ImageHeader, Object); +}; + // A WeakSerializationReference (WSR) denotes a type of weak reference to a // target object. In particular, objects that can only be reached from roots via // WSR edges during serialization of AOT snapshots should not be serialized. Of diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc index 319cff63a52..4eda15a317b 100644 --- a/runtime/vm/object_service.cc +++ b/runtime/vm/object_service.cc @@ -684,6 +684,10 @@ void InstructionsSection::PrintJSONImpl(JSONStream* stream, bool ref) const { Object::PrintJSONImpl(stream, ref); } +void ImageHeader::PrintJSONImpl(JSONStream* stream, bool ref) const { + Object::PrintJSONImpl(stream, ref); +} + void WeakSerializationReference::PrintJSONImpl(JSONStream* stream, bool ref) const { JSONObject jsobj(stream); diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc index 3dfd910f058..de63846419c 100644 --- a/runtime/vm/raw_object.cc +++ b/runtime/vm/raw_object.cc @@ -223,6 +223,10 @@ intptr_t ObjectLayout::HeapSizeFromClass(uint32_t tags) const { instance_size = element->HeapSize(); break; } + case kImageHeaderCid: { + instance_size = ImageHeader::InstanceSize(); + break; + } case kWeakSerializationReferenceCid: { instance_size = WeakSerializationReference::InstanceSize(); break; @@ -564,6 +568,7 @@ NULL_VISITOR(Bool) NULL_VISITOR(Capability) NULL_VISITOR(SendPort) NULL_VISITOR(TransferableTypedData) +NULL_VISITOR(ImageHeader) REGULAR_VISITOR(Pointer) NULL_VISITOR(DynamicLibrary) VARIABLE_NULL_VISITOR(Instructions, Instructions::Size(raw_obj)) diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h index 39e0254ecfc..82b9df0c50f 100644 --- a/runtime/vm/raw_object.h +++ b/runtime/vm/raw_object.h @@ -1404,6 +1404,25 @@ class KernelProgramInfoLayout : public ObjectLayout { } }; +class ImageHeaderLayout : public ObjectLayout { + RAW_HEAP_OBJECT_IMPLEMENTATION(ImageHeader); + + VISIT_NOTHING(); + // The offset of the corresponding BSS section from this text section. + uword bss_offset_; + // The relocated address of this text section in the shared object. Properly + // filled for ELF snapshots, always 0 in assembly snapshots. (For the latter, + // we instead get the value during BSS initialization and store it there.) + uword instructions_relocated_address_; + // The offset of the GNU build ID section description field from this text + // section. + uword build_id_offset_; + // The length of the GNU build ID section description field. + uword build_id_length_; + + friend class Image; +}; + class WeakSerializationReferenceLayout : public ObjectLayout { RAW_HEAP_OBJECT_IMPLEMENTATION(WeakSerializationReference); diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc index 68a56b302ad..36deccf038d 100644 --- a/runtime/vm/raw_object_snapshot.cc +++ b/runtime/vm/raw_object_snapshot.cc @@ -582,6 +582,7 @@ MESSAGE_SNAPSHOT_UNREACHABLE(MonomorphicSmiableCall); MESSAGE_SNAPSHOT_UNREACHABLE(UnwindError); MESSAGE_SNAPSHOT_UNREACHABLE(FutureOr); MESSAGE_SNAPSHOT_UNREACHABLE(WeakSerializationReference); +MESSAGE_SNAPSHOT_UNREACHABLE(ImageHeader); MESSAGE_SNAPSHOT_ILLEGAL(DynamicLibrary); MESSAGE_SNAPSHOT_ILLEGAL(MirrorReference); diff --git a/runtime/vm/tagged_pointer.h b/runtime/vm/tagged_pointer.h index b81e29dfe73..327f431596e 100644 --- a/runtime/vm/tagged_pointer.h +++ b/runtime/vm/tagged_pointer.h @@ -242,6 +242,7 @@ DEFINE_TAGGED_POINTER(Script, Object) DEFINE_TAGGED_POINTER(Library, Object) DEFINE_TAGGED_POINTER(Namespace, Object) DEFINE_TAGGED_POINTER(KernelProgramInfo, Object) +DEFINE_TAGGED_POINTER(ImageHeader, Object) DEFINE_TAGGED_POINTER(WeakSerializationReference, Object) DEFINE_TAGGED_POINTER(Code, Object) DEFINE_TAGGED_POINTER(Bytecode, Object)