[vm/aot] Move ELF section manipulation out of CreateAppAOTSnapshotAsElf.
Instead, we just always add a BSS section and we add the ROData
sections for ELF snapshots in BlobImageWriter::WriteText() (similar
to how AssemblyImageWriter::WriteText() handles it for assembly).
Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-mac-release-simarm64-try,vm-kernel-precomp-win-release-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-release-simarm_x64-try
Change-Id: I87ce64037821ce10f2344964e7f379376c4538df
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/150937
Reviewed-by: Martin Kustermann <kustermann@google.com>
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 103df3f..b3800b5 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -6443,9 +6443,10 @@
StreamingWriteStream debug_stream(generate_debug ? kInitialDebugSize : 0,
callback, debug_callback_data);
- auto const elf = generate_debug ? new (Z)
- Elf(Z, &debug_stream, new (Z) Dwarf(Z))
- : nullptr;
+ auto const elf = generate_debug
+ ? new (Z) Elf(Z, &debug_stream, Elf::Type::DebugInfo,
+ new (Z) Dwarf(Z))
+ : nullptr;
AssemblyImageWriter image_writer(T, callback, callback_data, strip, elf);
uint8_t* vm_snapshot_data_buffer = NULL;
@@ -6517,49 +6518,23 @@
callback, debug_callback_data);
auto const dwarf = strip ? nullptr : new (Z) Dwarf(Z);
- auto const elf = new (Z) Elf(Z, &elf_stream, dwarf);
- // Re-use the same DWARF object if unstripped.
+ auto const elf = new (Z) Elf(Z, &elf_stream, Elf::Type::Snapshot, dwarf);
+ // Re-use the same DWARF object if the snapshot is unstripped.
auto const debug_elf =
- generate_debug
- ? new (Z) Elf(Z, &debug_stream, strip ? new (Z) Dwarf(Z) : dwarf)
- : nullptr;
-
- // Here, both VM and isolate will be compiled into a single snapshot.
- // In assembly generation, each serialized text section gets a separate
- // pointer into the BSS segment and BSS slots are created for each, since
- // we may not serialize both VM and isolate. Here, we always serialize both,
- // so make a BSS segment large enough for both, with the VM entries coming
- // first.
- auto const isolate_offset = BSS::kVmEntryCount * compiler::target::kWordSize;
- auto const bss_size =
- isolate_offset + BSS::kIsolateEntryCount * compiler::target::kWordSize;
- // Note that the BSS section must come first 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().
- const intptr_t vm_bss_base = elf->AddBSSData("_kDartBSSData", bss_size);
- const intptr_t isolate_bss_base = vm_bss_base + isolate_offset;
- // Add the BSS section to the separately saved debugging information, even
- // though there will be no code in it to relocate, since it precedes the
- // .text sections and thus affects their virtual addresses.
- if (debug_elf != nullptr) {
- debug_elf->AddBSSData("_kDartBSSData", bss_size);
- }
+ generate_debug ? new (Z) Elf(Z, &debug_stream, Elf::Type::DebugInfo,
+ strip ? new (Z) Dwarf(Z) : dwarf)
+ : nullptr;
BlobImageWriter vm_image_writer(T, &vm_snapshot_instructions_buffer,
- ApiReallocate, kInitialSize, debug_elf,
- vm_bss_base, elf);
+ ApiReallocate, kInitialSize, debug_elf, elf);
BlobImageWriter isolate_image_writer(T, &isolate_snapshot_instructions_buffer,
ApiReallocate, kInitialSize, debug_elf,
- isolate_bss_base, elf);
+ elf);
FullSnapshotWriter writer(Snapshot::kFullAOT, &vm_snapshot_data_buffer,
&isolate_snapshot_data_buffer, ApiReallocate,
&vm_image_writer, &isolate_image_writer);
writer.WriteFullSnapshot();
- elf->AddROData(kVmSnapshotDataAsmSymbol, vm_snapshot_data_buffer,
- writer.VmIsolateSnapshotSize());
- elf->AddROData(kIsolateSnapshotDataAsmSymbol, isolate_snapshot_data_buffer,
- writer.IsolateSnapshotSize());
elf->Finalize();
if (debug_elf != nullptr) {
diff --git a/runtime/vm/elf.cc b/runtime/vm/elf.cc
index ee170f2..0fd4084 100644
--- a/runtime/vm/elf.cc
+++ b/runtime/vm/elf.cc
@@ -678,14 +678,29 @@
static const intptr_t kProgramTableSegmentSize = Elf::kPageSize;
-Elf::Elf(Zone* zone, StreamingWriteStream* stream, Dwarf* dwarf)
+// Here, both VM and isolate will be compiled into a single snapshot.
+// In assembly generation, each serialized text section gets a separate
+// pointer into the BSS segment and BSS slots are created for each, since
+// we may not serialize both VM and isolate. Here, we always serialize both,
+// so make a BSS segment large enough for both, with the VM entries coming
+// first.
+static const intptr_t kBssIsolateOffset =
+ BSS::kVmEntryCount * compiler::target::kWordSize;
+static const intptr_t kBssSize =
+ kBssIsolateOffset + BSS::kIsolateEntryCount * compiler::target::kWordSize;
+
+Elf::Elf(Zone* zone, StreamingWriteStream* stream, Type type, Dwarf* dwarf)
: zone_(zone),
unwrapped_stream_(stream),
+ type_(type),
dwarf_(dwarf),
+ bss_(CreateBSS(zone, type, kBssSize)),
shstrtab_(new (zone) StringTable(/*allocate=*/false)),
dynstrtab_(new (zone) StringTable(/*allocate=*/true)),
dynsym_(new (zone) SymbolTable(/*dynamic=*/true)),
memory_offset_(kProgramTableSegmentSize) {
+ // Separate debugging information should always have a Dwarf object.
+ ASSERT(type_ == Type::Snapshot || dwarf_ != nullptr);
// Assumed by various offset logic in this file.
ASSERT_EQUAL(unwrapped_stream_->position(), 0);
// The first section in the section header table is always a reserved
@@ -702,6 +717,19 @@
AddSection(symtab_, ".symtab");
symtab_->section_link = strtab_->section_index();
}
+ // Note that the BSS segment must be 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().
+ //
+ // We add it in all cases, even to the separate debugging information ELF,
+ // to ensure that relocated addresses are consistent between ELF snapshots
+ // and ELF separate debugging information.
+ AddSection(bss_, ".bss");
+ AddSegmentSymbol(bss_, "_kDartBSSData");
+}
+
+uword Elf::BssStart(bool vm) const {
+ return bss_->memory_offset() + (vm ? 0 : kBssIsolateOffset);
}
void Elf::AddSection(Section* section, const char* name) {
@@ -734,11 +762,14 @@
}
intptr_t Elf::AddText(const char* name, const uint8_t* bytes, intptr_t size) {
- Section* image = nullptr;
- if (bytes != nullptr) {
- image = new (zone_) ProgramBits(true, true, false, bytes, size);
- } else {
+ // When making a separate debugging info file for assembly, we don't have
+ // the binary text segment contents.
+ ASSERT(type_ == Type::DebugInfo || bytes != nullptr);
+ Section* image;
+ if (type_ == Type::DebugInfo) {
image = new (zone_) NoBits(true, true, false, size);
+ } else {
+ image = new (zone_) ProgramBits(true, true, false, bytes, size);
}
AddSection(image, ".text");
@@ -776,28 +807,34 @@
return FindSymbol(strtab_, symtab_, name, offset, size);
}
-intptr_t Elf::AddBSSData(const char* name, intptr_t size) {
- // Ideally the BSS segment would take no space in the object, but Android's
- // "strip" utility truncates the memory-size of our segments to their
- // file-size.
- //
- // Therefore we must insert zero-filled pages for the BSS.
- uint8_t* const bytes = zone_->Alloc<uint8_t>(size);
- memset(bytes, 0, size);
-
- ProgramBits* const image =
- new (zone_) ProgramBits(true, false, true, bytes, size);
+Section* Elf::CreateBSS(Zone* zone, Type type, intptr_t size) {
+ Section* image;
+ if (type == Type::DebugInfo) {
+ image = new (zone) NoBits(true, false, true, size);
+ } else {
+ // Ideally the BSS segment would take no space in the object, but Android's
+ // "strip" utility truncates the memory-size of our segments to their
+ // file-size.
+ //
+ // Therefore we must insert zero-filled pages for the BSS.
+ uint8_t* const bytes = zone->Alloc<uint8_t>(size);
+ memset(bytes, 0, size);
+ image = new (zone) ProgramBits(true, false, true, bytes, size);
+ }
static_assert(Image::kBssAlignment <= kPageSize,
"ELF .bss section is not aligned as expected by Image class");
ASSERT_EQUAL(image->alignment, kPageSize);
- AddSection(image, ".bss");
-
- return AddSegmentSymbol(image, name);
+ return image;
}
intptr_t Elf::AddROData(const char* name, const uint8_t* bytes, intptr_t size) {
ASSERT(bytes != nullptr);
- ProgramBits* image = new (zone_) ProgramBits(true, false, false, bytes, size);
+ Section* image;
+ if (type_ == Type::DebugInfo) {
+ image = new (zone_) NoBits(true, false, false, size);
+ } else {
+ image = new (zone_) ProgramBits(true, false, false, bytes, size);
+ }
AddSection(image, ".rodata");
return AddSegmentSymbol(image, name);
diff --git a/runtime/vm/elf.h b/runtime/vm/elf.h
index a269192..b410bff4 100644
--- a/runtime/vm/elf.h
+++ b/runtime/vm/elf.h
@@ -24,7 +24,18 @@
class Elf : public ZoneAllocated {
public:
- Elf(Zone* zone, StreamingWriteStream* stream, Dwarf* dwarf = nullptr);
+ enum class Type {
+ // A snapshot that should include segment contents.
+ Snapshot,
+ // Separately compiled debugging information that should not include
+ // most segment contents.
+ DebugInfo,
+ };
+
+ Elf(Zone* zone,
+ StreamingWriteStream* stream,
+ Type type,
+ Dwarf* dwarf = nullptr);
static const intptr_t kPageSize = 4096;
@@ -32,11 +43,12 @@
const Dwarf* dwarf() const { return dwarf_; }
Dwarf* dwarf() { return dwarf_; }
+ uword BssStart(bool vm) const;
+
intptr_t NextMemoryOffset() const { return memory_offset_; }
intptr_t NextSectionIndex() const { return sections_.length(); }
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);
- intptr_t AddBSSData(const char* name, intptr_t size);
void AddDebug(const char* name, const uint8_t* bytes, intptr_t size);
// Returns whether the symbol was found. If found, sets the contents of
@@ -66,6 +78,8 @@
intptr_t address,
intptr_t size);
+ static Section* CreateBSS(Zone* zone, Type type, intptr_t size);
+
const Section* FindSegmentForAddress(intptr_t address) const;
void FinalizeDwarfSections();
@@ -79,10 +93,15 @@
Zone* const zone_;
StreamingWriteStream* const unwrapped_stream_;
+ const Type type_;
// If nullptr, then the ELF file should be stripped of static information like
// the static symbol table (and its corresponding string table).
Dwarf* const dwarf_;
+ // We always create a BSS section for all Elf files, though it may be NOBITS
+ // if this is separate debugging information.
+ Section* const bss_;
+
// All our strings would fit in a single page. However, we use separate
// .shstrtab and .dynstr to work around a bug in Android's strip utility.
StringTable* const shstrtab_;
diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc
index fef47d6..ccb436a 100644
--- a/runtime/vm/image_snapshot.cc
+++ b/runtime/vm/image_snapshot.cc
@@ -418,6 +418,8 @@
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);
}
@@ -1190,12 +1192,10 @@
ReAlloc alloc,
intptr_t initial_size,
Elf* debug_elf,
- intptr_t bss_base,
Elf* elf)
: ImageWriter(thread),
instructions_blob_stream_(instructions_blob_buffer, alloc, initial_size),
elf_(elf),
- bss_base_(bss_base),
debug_elf_(debug_elf) {
#if defined(DART_PRECOMPILER)
ASSERT(debug_elf_ == nullptr || debug_elf_->dwarf() != nullptr);
@@ -1248,7 +1248,8 @@
#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 ? bss_base_ - segment_base : 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;
@@ -1479,17 +1480,26 @@
ASSERT_EQUAL(text_offset, image_size);
#ifdef DART_PRECOMPILER
+ auto const data_symbol =
+ vm ? kVmSnapshotDataAsmSymbol : kIsolateSnapshotDataAsmSymbol;
if (elf_ != nullptr) {
auto const segment_base2 =
elf_->AddText(instructions_symbol, instructions_blob_stream_.buffer(),
instructions_blob_stream_.bytes_written());
ASSERT(segment_base == segment_base2);
+ // Write the .rodata section here like the AssemblyImageWriter.
+ elf_->AddROData(data_symbol, clustered_stream->buffer(),
+ clustered_stream->bytes_written());
}
if (debug_elf_ != nullptr) {
- auto const debug_segment_base2 =
- debug_elf_->AddText(instructions_symbol, nullptr,
- instructions_blob_stream_.bytes_written());
+ // To keep memory addresses consistent, we need to add corresponding
+ // sections (though these will be NOBITS sections).
+ auto const debug_segment_base2 = debug_elf_->AddText(
+ instructions_symbol, instructions_blob_stream_.buffer(),
+ instructions_blob_stream_.bytes_written());
ASSERT(debug_segment_base == debug_segment_base2);
+ debug_elf_->AddROData(data_symbol, clustered_stream->buffer(),
+ clustered_stream->bytes_written());
}
#endif
}
diff --git a/runtime/vm/image_snapshot.h b/runtime/vm/image_snapshot.h
index 7304ea7..6727ca0 100644
--- a/runtime/vm/image_snapshot.h
+++ b/runtime/vm/image_snapshot.h
@@ -429,7 +429,6 @@
ReAlloc alloc,
intptr_t initial_size,
Elf* debug_elf = nullptr,
- intptr_t bss_base = 0,
Elf* elf = nullptr);
virtual void WriteText(WriteStream* clustered_stream, bool vm);
@@ -443,7 +442,6 @@
WriteStream instructions_blob_stream_;
Elf* const elf_;
- const intptr_t bss_base_;
Elf* const debug_elf_;
DISALLOW_COPY_AND_ASSIGN(BlobImageWriter);
diff --git a/tests/standalone_2/dwarf_stack_trace_test.dart b/tests/standalone_2/dwarf_stack_trace_test.dart
index 8af577b..0ecac2e 100644
--- a/tests/standalone_2/dwarf_stack_trace_test.dart
+++ b/tests/standalone_2/dwarf_stack_trace_test.dart
@@ -71,9 +71,12 @@
final absolutes = absoluteAddresses(rawLines);
final relocatedAddresses = absolutes.map((a) => a - dsoBase);
final explicits = explicitVirtualAddresses(rawLines);
- Expect.deepEquals(relocatedAddresses, virtualAddresses);
- // Explicits will be empty if not generating ELF snapshots directly.
+
+ // Explicits will be empty if not generating ELF snapshots directly, which
+ // means we can't depend on virtual addresses in the snapshot lining up with
+ // those in the separate debugging information.
if (explicits.isNotEmpty) {
+ Expect.deepEquals(relocatedAddresses, virtualAddresses);
Expect.deepEquals(explicits, virtualAddresses);
}