Version 2.14.3
* Cherry-pick 06bb7992d201993496f9b5cb617e5d4f7f6417dc to stable
* Cherry-pick 6207d1da351fcbe385d09625bf4cbe31ff16aaa0 to stable
* Cherry-pick d16ad3d64bee94ffa7de95e78d65d953e67a1a34 to stable
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4df1b37..1dbd93d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,14 @@
+## 2.14.3 - 2021-09-30
+
+This is a patch release that fixes:
+
+- a code completion performance regression [flutter/flutter-intellij#5761][].
+- debug information emitted by the Dart VM [#47289][].
+
+[flutter/flutter-intellij#5761]:
+ https://github.com/flutter/flutter-intellij/issues/5761
+[#47289]: https://github.com/dart-lang/sdk/issues/47289
+
## 2.14.2 - 2021-09-16
This is a patch release that fixes:
diff --git a/pkg/analysis_server/lib/src/cider/completion.dart b/pkg/analysis_server/lib/src/cider/completion.dart
index 458ce08..8591b99 100644
--- a/pkg/analysis_server/lib/src/cider/completion.dart
+++ b/pkg/analysis_server/lib/src/cider/completion.dart
@@ -99,7 +99,6 @@
return await manager.computeSuggestions(
performance,
completionRequest,
- enableImportedReferenceContributor: false,
enableOverrideContributor: false,
enableUriContributor: false,
);
diff --git a/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart b/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
index 54ffabf..dce10de 100644
--- a/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
+++ b/pkg/analysis_server/lib/src/domains/completion/available_suggestions.dart
@@ -38,9 +38,8 @@
) {
int relevance;
if (importedUriSet.contains(library.uri)) {
- return;
- }
- if (library.isDeprecated) {
+ relevance = importedRelevance;
+ } else if (library.isDeprecated) {
relevance = deprecatedRelevance;
} else {
relevance = otherwiseRelevance;
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
index b71fa3e..8ee5426 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
@@ -92,7 +92,6 @@
Future<List<CompletionSuggestion>> computeSuggestions(
OperationPerformanceImpl performance,
CompletionRequest request, {
- bool enableImportedReferenceContributor = true,
bool enableOverrideContributor = true,
bool enableUriContributor = true,
CompletionPreference? completionPreference,
@@ -131,7 +130,6 @@
CombinatorContributor(),
ExtensionMemberContributor(),
FieldFormalContributor(),
- if (enableImportedReferenceContributor) ImportedReferenceContributor(),
KeywordContributor(),
LabelContributor(),
LibraryMemberContributor(),
@@ -150,6 +148,8 @@
if (includedElementKinds != null) {
_addIncludedElementKinds(dartRequest);
_addIncludedSuggestionRelevanceTags(dartRequest);
+ } else {
+ contributors.add(ImportedReferenceContributor());
}
try {
diff --git a/pkg/analysis_server/test/client/completion_driver_test.dart b/pkg/analysis_server/test/client/completion_driver_test.dart
index 13d807c..657cc19 100644
--- a/pkg/analysis_server/test/client/completion_driver_test.dart
+++ b/pkg/analysis_server/test/client/completion_driver_test.dart
@@ -71,7 +71,6 @@
}
void assertSuggestion({
- bool allowMultiple = false,
required String completion,
ElementKind? element,
CompletionSuggestionKind? kind,
@@ -79,7 +78,6 @@
}) {
expect(
suggestionWith(
- allowMultiple: allowMultiple,
completion: completion,
element: element,
kind: kind,
@@ -184,7 +182,6 @@
completion: completion, element: element, kind: kind, file: file));
CompletionSuggestion suggestionWith({
- bool allowMultiple = false,
required String completion,
ElementKind? element,
CompletionSuggestionKind? kind,
@@ -192,9 +189,7 @@
}) {
final matches = suggestionsWith(
completion: completion, element: element, kind: kind, file: file);
- if (!allowMultiple) {
- expect(matches, hasLength(1));
- }
+ expect(matches, hasLength(1));
return matches.first;
}
@@ -269,10 +264,7 @@
}
''');
- // TODO(brianwilkerson) There should be a single suggestion here after we
- // figure out how to stop the duplication.
assertSuggestion(
- allowMultiple: true,
completion: 'A',
element: ElementKind.CONSTRUCTOR,
kind: CompletionSuggestionKind.INVOCATION);
@@ -296,11 +288,7 @@
^
}
''');
-
- // TODO(brianwilkerson) There should be a single suggestion here after we
- // figure out how to stop the duplication.
assertSuggestion(
- allowMultiple: true,
completion: 'E.e',
element: ElementKind.ENUM_CONSTANT,
kind: CompletionSuggestionKind.INVOCATION);
@@ -325,10 +313,7 @@
}
''');
- // TODO(brianwilkerson) There should be a single suggestion here after we
- // figure out how to stop the duplication.
assertSuggestion(
- allowMultiple: true,
completion: 'A.a',
element: ElementKind.CONSTRUCTOR,
kind: CompletionSuggestionKind.INVOCATION);
@@ -656,15 +641,13 @@
}
''');
- // TODO(brianwilkerson) There should be a single suggestion here after we
- // figure out how to stop the duplication.
expect(
suggestionsWith(
completion: 'Future.value',
file: '/sdk/lib/async/async.dart',
element: ElementKind.CONSTRUCTOR,
kind: CompletionSuggestionKind.INVOCATION),
- hasLength(2));
+ hasLength(1));
}
Future<void> test_sdk_lib_suggestions() async {
diff --git a/pkg/analysis_server/test/lsp/completion_dart_test.dart b/pkg/analysis_server/test/lsp/completion_dart_test.dart
index 0328fa0..9ccf020 100644
--- a/pkg/analysis_server/test/lsp/completion_dart_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_dart_test.dart
@@ -1560,7 +1560,7 @@
expect(completions, hasLength(1));
final resolved = await resolveCompletion(completions.first);
// It should not include auto-import text since it's already imported.
- expect(resolved.detail, isNot(contains('Auto import from')));
+ expect(resolved.detail, isNull);
}
Future<void> test_suggestionSets_filtersOutAlreadyImportedSymbols() async {
diff --git a/pkg/analysis_server/test/services/completion/dart/relevance/bool_assignment_test.dart b/pkg/analysis_server/test/services/completion/dart/relevance/bool_assignment_test.dart
index b796a44..039c915 100644
--- a/pkg/analysis_server/test/services/completion/dart/relevance/bool_assignment_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/relevance/bool_assignment_test.dart
@@ -15,13 +15,7 @@
@reflectiveTest
class BoolAssignmentTest extends CompletionRelevanceTest {
- @failingTest
Future<void> test_boolLiterals_imported() async {
- // TODO(brianwilkerson) This test is arguably invalid given that there's no
- // data to suggest that the boolean keywords should appear before a
- // constructor in the list, but it does seem likely to be valid in this
- // case, so we should investigate features that might cause this test to
- // start passing again.
await addTestFile('''
foo() {
bool b;
diff --git a/pkg/native_stack_traces/CHANGELOG.md b/pkg/native_stack_traces/CHANGELOG.md
index 7c60f28..7a0f2b5 100644
--- a/pkg/native_stack_traces/CHANGELOG.md
+++ b/pkg/native_stack_traces/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## 0.4.4
+
+- Added handling of dynamic tables for testing.
+
## 0.4.3
- Exported some more of the ELF utilities for use in Dart tests.
diff --git a/pkg/native_stack_traces/lib/elf.dart b/pkg/native_stack_traces/lib/elf.dart
index 3b1624b..3316655 100644
--- a/pkg/native_stack_traces/lib/elf.dart
+++ b/pkg/native_stack_traces/lib/elf.dart
@@ -2,7 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-export 'src/elf.dart' show Elf, Section, Symbol;
+export 'src/elf.dart' show DynamicTable, DynamicTableTag, Elf, Section, Symbol;
export 'src/constants.dart'
show
isolateDataSymbolName,
diff --git a/pkg/native_stack_traces/lib/src/elf.dart b/pkg/native_stack_traces/lib/src/elf.dart
index 548b5ac..268d5d80 100644
--- a/pkg/native_stack_traces/lib/src/elf.dart
+++ b/pkg/native_stack_traces/lib/src/elf.dart
@@ -214,7 +214,10 @@
static const _ELFDATA2MSB = 0x02;
void writeToStringBuffer(StringBuffer buffer) {
- buffer..write('Format is ')..write(wordSize * 8)..write(' bits');
+ buffer
+ ..write('Format is ')
+ ..write(wordSize * 8)
+ ..write(' bits');
switch (endian) {
case Endian.little:
buffer..writeln(' and little-endian');
@@ -342,6 +345,15 @@
int get length => _entries.length;
ProgramHeaderEntry operator [](int index) => _entries[index];
+ ProgramHeaderEntry? loadSegmentFor(int address) {
+ for (final entry in _entries) {
+ if (entry.vaddr <= address && address <= entry.vaddr + entry.memsz) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
static ProgramHeader fromReader(Reader reader, ElfHeader header) {
final programReader = reader.refocusedCopy(
header.programHeaderOffset, header.programHeaderSize);
@@ -352,7 +364,10 @@
void writeToStringBuffer(StringBuffer buffer) {
for (var i = 0; i < length; i++) {
- if (i != 0) buffer..writeln()..writeln();
+ if (i != 0)
+ buffer
+ ..writeln()
+ ..writeln();
buffer
..write('Entry ')
..write(i)
@@ -422,6 +437,17 @@
static const _SHT_NOBITS = 8;
static const _SHT_DYNSYM = 11;
+ // sh_flags constants from ELF specification.
+ static const _SHF_WRITE = 0x1;
+ static const _SHF_ALLOC = 0x2;
+ static const _SHF_EXECINSTR = 0x4;
+
+ bool get isWritable => flags & _SHF_WRITE != 0;
+ bool get isAllocated => flags & _SHF_ALLOC != 0;
+ bool get isExecutable => flags & _SHF_EXECINSTR != 0;
+
+ bool get hasBits => type != _SHT_NOBITS;
+
void setName(StringTable nameTable) {
name = nameTable[nameIndex]!;
}
@@ -495,7 +521,10 @@
void writeToStringBuffer(StringBuffer buffer) {
for (var i = 0; i < entries.length; i++) {
- if (i != 0) buffer..writeln()..writeln();
+ if (i != 0)
+ buffer
+ ..writeln()
+ ..writeln();
buffer
..write('Entry ')
..write(i)
@@ -536,6 +565,8 @@
return SymbolTable.fromReader(reader, entry);
case SectionHeaderEntry._SHT_NOTE:
return Note.fromReader(reader, entry);
+ case SectionHeaderEntry._SHT_DYNAMIC:
+ return DynamicTable.fromReader(reader, entry);
default:
return Section._(entry);
}
@@ -713,7 +744,10 @@
SymbolVisibility get visibility => SymbolVisibility.values[other & 0x03];
void writeToStringBuffer(StringBuffer buffer) {
- buffer..write('"')..write(name)..write('" =>');
+ buffer
+ ..write('"')
+ ..write(name)
+ ..write('" =>');
switch (bind) {
case SymbolBinding.STB_GLOBAL:
buffer..write(' a global');
@@ -794,6 +828,109 @@
}
}
+/// Represents d_tag constants from ELF specification.
+enum DynamicTableTag {
+ DT_NULL,
+ DT_NEEDED,
+ DT_PLTRELSZ,
+ DT_PLTGOT,
+ DT_HASH,
+ DT_STRTAB,
+ DT_SYMTAB,
+ DT_RELA,
+ DT_RELASZ,
+ DT_RELAENT,
+ DT_STRSZ,
+ DT_SYMENT,
+ // Later d_tag values are not currently used in Dart ELF files.
+}
+
+/// The dynamic table, which contains entries pointing to various relocated
+/// addresses.
+class DynamicTable extends Section {
+ // We don't use DynamicTableTag for the key so that we can handle ELF files
+ // that may use unknown (to us) tags.
+ final Map<int, int> _entries;
+ final _wordSize;
+
+ DynamicTable._(SectionHeaderEntry entry, this._entries, this._wordSize)
+ : super._(entry);
+
+ static DynamicTable fromReader(Reader reader, SectionHeaderEntry entry) {
+ final sectionReader = reader.refocusedCopy(entry.offset, entry.size);
+ final entries = <int, int>{};
+ while (true) {
+ // Each entry is a tag and a value, both native word sized.
+ final tag = _readElfNative(sectionReader);
+ final value = _readElfNative(sectionReader);
+ // A DT_NULL entry signfies the end of entries.
+ if (tag == DynamicTableTag.DT_NULL.index) break;
+ entries[tag] = value;
+ }
+ return DynamicTable._(entry, entries, sectionReader.wordSize);
+ }
+
+ int? operator [](DynamicTableTag tag) => _entries[tag.index];
+ bool containsKey(DynamicTableTag tag) => _entries.containsKey(tag.index);
+
+ // To avoid depending on EnumName.name from 2.15.
+ static const _tagStrings = {
+ DynamicTableTag.DT_NULL: 'DT_NULL',
+ DynamicTableTag.DT_NEEDED: 'DT_NEEDED',
+ DynamicTableTag.DT_PLTRELSZ: 'DT_PLTRELSZ',
+ DynamicTableTag.DT_PLTGOT: 'DT_PLTGOT',
+ DynamicTableTag.DT_HASH: 'DT_HASH',
+ DynamicTableTag.DT_STRTAB: 'DT_STRTAB',
+ DynamicTableTag.DT_SYMTAB: 'DT_SYMTAB',
+ DynamicTableTag.DT_RELA: 'DT_RELA',
+ DynamicTableTag.DT_RELASZ: 'DT_RELASZ',
+ DynamicTableTag.DT_STRSZ: 'DT_STRSZ',
+ DynamicTableTag.DT_SYMENT: 'DT_SYMENT',
+ };
+ static final _maxTagStringLength = (_tagStrings.values.toList()
+ ..sort((s1, s2) => s2.length - s1.length))
+ .first
+ .length;
+
+ @override
+ void writeToStringBuffer(StringBuffer buffer) {
+ buffer
+ ..write('Section "')
+ ..write(headerEntry.name)
+ ..writeln('" is a dynamic table:');
+ for (var kv in _entries.entries) {
+ buffer.write(' ');
+ if (kv.key < DynamicTableTag.values.length) {
+ final tag = DynamicTableTag.values[kv.key];
+ buffer
+ ..write(_tagStrings[tag]?.padRight(_maxTagStringLength))
+ ..write(' => ');
+ switch (tag) {
+ // These are relocated addresses.
+ case DynamicTableTag.DT_HASH:
+ case DynamicTableTag.DT_PLTGOT:
+ case DynamicTableTag.DT_SYMTAB:
+ case DynamicTableTag.DT_STRTAB:
+ case DynamicTableTag.DT_RELA:
+ buffer
+ ..write('0x')
+ ..writeln(paddedHex(kv.value, _wordSize));
+ break;
+ // Other entries are just values or offsets.
+ default:
+ buffer.writeln(kv.value);
+ }
+ } else {
+ buffer
+ ..write("Unknown tag ")
+ ..write(kv.key)
+ ..write(' => ')
+ ..writeln(kv.value);
+ }
+ }
+ }
+}
+
/// Information parsed from an Executable and Linking Format (ELF) file.
class Elf {
final ElfHeader _header;
@@ -819,6 +956,21 @@
Iterable<Section> namedSections(String name) =>
_sectionsByName[name] ?? <Section>[];
+ /// Checks that the contents of a given section have valid addresses when the
+ /// file contents for the corresponding segment is loaded into memory.
+ ///
+ /// Returns false for sections that are not allocated or where the address
+ /// does not correspond to file contents (i.e., NOBITS sections).
+ bool sectionHasValidSegmentAddresses(Section section) {
+ final headerEntry = section.headerEntry;
+ if (!headerEntry.isAllocated || !headerEntry.hasBits) return false;
+ final segment = _programHeader.loadSegmentFor(headerEntry.addr);
+ if (segment == null) return false;
+ return (headerEntry.addr < (segment.vaddr + segment.filesz)) &&
+ (headerEntry.addr + headerEntry.size) <=
+ (segment.vaddr + segment.filesz);
+ }
+
/// Lookup of a dynamic symbol by name.
///
/// Returns -1 if there is no dynamic symbol that matches [name].
diff --git a/pkg/native_stack_traces/pubspec.yaml b/pkg/native_stack_traces/pubspec.yaml
index bdf1f94..7dbc3b2 100644
--- a/pkg/native_stack_traces/pubspec.yaml
+++ b/pkg/native_stack_traces/pubspec.yaml
@@ -1,6 +1,6 @@
name: native_stack_traces
description: Utilities for working with non-symbolic stack traces.
-version: 0.4.3
+version: 0.4.4
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/native_stack_traces
diff --git a/runtime/tests/vm/dart/use_save_debugging_info_flag_test.dart b/runtime/tests/vm/dart/use_save_debugging_info_flag_test.dart
index 091087d..2bd55a2 100644
--- a/runtime/tests/vm/dart/use_save_debugging_info_flag_test.dart
+++ b/runtime/tests/vm/dart/use_save_debugging_info_flag_test.dart
@@ -13,6 +13,7 @@
import "dart:typed_data";
import 'package:expect/expect.dart';
+import 'package:native_stack_traces/elf.dart';
import 'package:native_stack_traces/native_stack_traces.dart';
import 'package:path/path.dart' as path;
@@ -63,6 +64,7 @@
'--elf=$scriptWholeSnapshot',
scriptDill,
]);
+ checkElf(scriptWholeSnapshot);
final scriptStrippedOnlySnapshot = path.join(tempDir, 'stripped_only.so');
await run(genSnapshot, <String>[
@@ -72,6 +74,7 @@
'--strip',
scriptDill,
]);
+ checkElf(scriptStrippedOnlySnapshot);
final scriptStrippedSnapshot = path.join(tempDir, 'stripped.so');
final scriptDebuggingInfo = path.join(tempDir, 'debug.so');
@@ -83,6 +86,8 @@
'--save-debugging-info=$scriptDebuggingInfo',
scriptDill,
]);
+ checkElf(scriptStrippedSnapshot);
+ checkElf(scriptDebuggingInfo);
// Run the resulting scripts, saving the stack traces.
final wholeTrace = await runError(aotRuntime, <String>[
@@ -184,3 +189,46 @@
}
}
}
+
+void checkElf(String filename) {
+ print("Checking ELF file $filename:");
+ final elf = Elf.fromFile(filename);
+ Expect.isNotNull(elf);
+ final dynamic = elf!.namedSections(".dynamic").single as DynamicTable;
+
+ // Check the dynamic string table information.
+ Expect.isTrue(
+ dynamic.containsKey(DynamicTableTag.DT_STRTAB), "no string table entry");
+ final dynstr = elf.namedSections(".dynstr").single;
+ print(".dynstr address = ${dynamic[DynamicTableTag.DT_STRTAB]}");
+ Expect.isTrue(elf.sectionHasValidSegmentAddresses(dynstr),
+ "string table addresses are invalid");
+ Expect.equals(dynamic[DynamicTableTag.DT_STRTAB], dynstr.headerEntry.addr);
+ Expect.isTrue(dynamic.containsKey(DynamicTableTag.DT_STRSZ),
+ "no string table size entry");
+ print(".dynstr size = ${dynamic[DynamicTableTag.DT_STRSZ]}");
+ Expect.equals(dynamic[DynamicTableTag.DT_STRSZ], dynstr.headerEntry.size);
+
+ // Check the dynamic symbol table information.
+ Expect.isTrue(
+ dynamic.containsKey(DynamicTableTag.DT_SYMTAB), "no symbol table entry");
+ print(".dynsym address = ${dynamic[DynamicTableTag.DT_SYMTAB]}");
+ final dynsym = elf.namedSections(".dynsym").single;
+ Expect.isTrue(elf.sectionHasValidSegmentAddresses(dynsym),
+ "string table addresses are invalid");
+ Expect.equals(dynamic[DynamicTableTag.DT_SYMTAB], dynsym.headerEntry.addr);
+ Expect.isTrue(dynamic.containsKey(DynamicTableTag.DT_SYMENT),
+ "no symbol table entry size entry");
+ print(".dynsym entry size = ${dynamic[DynamicTableTag.DT_SYMENT]}");
+ Expect.equals(
+ dynamic[DynamicTableTag.DT_SYMENT], dynsym.headerEntry.entrySize);
+
+ // Check the hash table information.
+ Expect.isTrue(
+ dynamic.containsKey(DynamicTableTag.DT_HASH), "no hash table entry");
+ print(".hash address = ${dynamic[DynamicTableTag.DT_HASH]}");
+ final hash = elf.namedSections(".hash").single;
+ Expect.isTrue(elf.sectionHasValidSegmentAddresses(hash),
+ "hash table addresses are invalid");
+ Expect.equals(dynamic[DynamicTableTag.DT_HASH], hash.headerEntry.addr);
+}
diff --git a/runtime/tests/vm/dart_2/use_save_debugging_info_flag_test.dart b/runtime/tests/vm/dart_2/use_save_debugging_info_flag_test.dart
index 1a21971..0af1e8c 100644
--- a/runtime/tests/vm/dart_2/use_save_debugging_info_flag_test.dart
+++ b/runtime/tests/vm/dart_2/use_save_debugging_info_flag_test.dart
@@ -13,6 +13,7 @@
import "dart:typed_data";
import 'package:expect/expect.dart';
+import 'package:native_stack_traces/elf.dart';
import 'package:native_stack_traces/native_stack_traces.dart';
import 'package:path/path.dart' as path;
@@ -63,6 +64,7 @@
'--elf=$scriptWholeSnapshot',
scriptDill,
]);
+ checkElf(scriptWholeSnapshot);
final scriptStrippedOnlySnapshot = path.join(tempDir, 'stripped_only.so');
await run(genSnapshot, <String>[
@@ -72,6 +74,7 @@
'--strip',
scriptDill,
]);
+ checkElf(scriptStrippedOnlySnapshot);
final scriptStrippedSnapshot = path.join(tempDir, 'stripped.so');
final scriptDebuggingInfo = path.join(tempDir, 'debug.so');
@@ -83,6 +86,8 @@
'--save-debugging-info=$scriptDebuggingInfo',
scriptDill,
]);
+ checkElf(scriptStrippedSnapshot);
+ checkElf(scriptDebuggingInfo);
// Run the resulting scripts, saving the stack traces.
final wholeTrace = await runError(aotRuntime, <String>[
@@ -184,3 +189,46 @@
}
}
}
+
+void checkElf(String filename) {
+ print("Checking ELF file $filename:");
+ final elf = Elf.fromFile(filename);
+ Expect.isNotNull(elf);
+ final dynamic = elf.namedSections(".dynamic").single as DynamicTable;
+
+ // Check the dynamic string table information.
+ Expect.isTrue(
+ dynamic.containsKey(DynamicTableTag.DT_STRTAB), "no string table entry");
+ final dynstr = elf.namedSections(".dynstr").single;
+ print(".dynstr address = ${dynamic[DynamicTableTag.DT_STRTAB]}");
+ Expect.isTrue(elf.sectionHasValidSegmentAddresses(dynstr),
+ "string table addresses are invalid");
+ Expect.equals(dynamic[DynamicTableTag.DT_STRTAB], dynstr.headerEntry.addr);
+ Expect.isTrue(dynamic.containsKey(DynamicTableTag.DT_STRSZ),
+ "no string table size entry");
+ print(".dynstr size = ${dynamic[DynamicTableTag.DT_STRSZ]}");
+ Expect.equals(dynamic[DynamicTableTag.DT_STRSZ], dynstr.headerEntry.size);
+
+ // Check the dynamic symbol table information.
+ Expect.isTrue(
+ dynamic.containsKey(DynamicTableTag.DT_SYMTAB), "no symbol table entry");
+ print(".dynsym address = ${dynamic[DynamicTableTag.DT_SYMTAB]}");
+ final dynsym = elf.namedSections(".dynsym").single;
+ Expect.isTrue(elf.sectionHasValidSegmentAddresses(dynsym),
+ "string table addresses are invalid");
+ Expect.equals(dynamic[DynamicTableTag.DT_SYMTAB], dynsym.headerEntry.addr);
+ Expect.isTrue(dynamic.containsKey(DynamicTableTag.DT_SYMENT),
+ "no symbol table entry size entry");
+ print(".dynsym entry size = ${dynamic[DynamicTableTag.DT_SYMENT]}");
+ Expect.equals(
+ dynamic[DynamicTableTag.DT_SYMENT], dynsym.headerEntry.entrySize);
+
+ // Check the hash table information.
+ Expect.isTrue(
+ dynamic.containsKey(DynamicTableTag.DT_HASH), "no hash table entry");
+ print(".hash address = ${dynamic[DynamicTableTag.DT_HASH]}");
+ final hash = elf.namedSections(".hash").single;
+ Expect.isTrue(elf.sectionHasValidSegmentAddresses(hash),
+ "hash table addresses are invalid");
+ Expect.equals(dynamic[DynamicTableTag.DT_HASH], hash.headerEntry.addr);
+}
diff --git a/runtime/vm/elf.cc b/runtime/vm/elf.cc
index a4f550f..28e8224 100644
--- a/runtime/vm/elf.cc
+++ b/runtime/vm/elf.cc
@@ -173,6 +173,8 @@
}
bool IsWritable() const { return (flags & elf::SHF_WRITE) == elf::SHF_WRITE; }
+ bool HasBits() const { return type != elf::SectionHeaderType::SHT_NOBITS; }
+
// Returns whether the size of a section can change.
bool HasBeenFinalized() const {
// Sections can grow or shrink up until Elf::ComputeOffsets has been run,
@@ -1559,8 +1561,27 @@
// We now do several passes over the collected sections to reorder them in
// a way that minimizes segments (and thus padding) in the resulting snapshot.
- // If a build ID was created, we put it after the program table so it can
- // be read with a minimum number of bytes from the ELF file.
+ auto add_sections_matching =
+ [&](const std::function<bool(Section*)>& should_add) {
+ // We order the sections in a segment so all non-NOBITS sections come
+ // before NOBITS sections, since the former sections correspond to the
+ // file contents for the segment.
+ for (auto* const section : sections_) {
+ if (!section->HasBits()) continue;
+ if (should_add(section)) {
+ add_to_reordered_sections(section);
+ }
+ }
+ for (auto* const section : sections_) {
+ if (section->HasBits()) continue;
+ if (should_add(section)) {
+ add_to_reordered_sections(section);
+ }
+ }
+ };
+
+ // If a build ID was created, we put it right after the program table so it
+ // can be read with a minimum number of bytes from the ELF file.
auto* const build_id = Find(Elf::kBuildIdNoteName);
if (build_id != nullptr) {
ASSERT(build_id->type == elf::SectionHeaderType::SHT_NOTE);
@@ -1568,38 +1589,31 @@
}
// Now add the other non-writable, non-executable allocated sections.
- for (auto* const section : sections_) {
- if (section == build_id) continue; // Already added.
- if (section->IsAllocated() && !section->IsWritable() &&
- !section->IsExecutable()) {
- add_to_reordered_sections(section);
- }
- }
+ add_sections_matching([&](Section* section) -> bool {
+ if (section == build_id) return false; // Already added.
+ return section->IsAllocated() && !section->IsWritable() &&
+ !section->IsExecutable();
+ });
// Now add the executable sections in a new segment.
- for (auto* const section : sections_) {
- if (section->IsExecutable()) { // Implies IsAllocated() && !IsWritable()
- add_to_reordered_sections(section);
- }
- }
+ add_sections_matching([](Section* section) -> bool {
+ return section->IsExecutable(); // Implies IsAllocated() && !IsWritable()
+ });
// Now add all the writable sections.
- for (auto* const section : sections_) {
- if (section->IsWritable()) { // Implies IsAllocated() && !IsExecutable()
- add_to_reordered_sections(section);
- }
- }
+ add_sections_matching([](Section* section) -> bool {
+ return section->IsWritable(); // Implies IsAllocated() && !IsExecutable()
+ });
// We put all non-reserved unallocated sections last. Otherwise, they would
// affect the file offset but not the memory offset of any following allocated
// sections. Doing it in this order makes it easier to keep file and memory
// offsets page-aligned with respect to each other, which is required for
// some loaders.
- for (intptr_t i = 1; i < num_sections; i++) {
- auto* const section = sections_[i];
- if (section->IsAllocated()) continue;
- add_to_reordered_sections(section);
- }
+ add_sections_matching([](Section* section) -> bool {
+ // Don't re-add the initial reserved section.
+ return !section->IsReservedSection() && !section->IsAllocated();
+ });
// All sections should have been accounted for in the loops above.
ASSERT_EQUAL(sections_.length(), reordered_sections.length());
diff --git a/tools/VERSION b/tools/VERSION
index 4444ab4..d26e6ca 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -26,6 +26,6 @@
CHANNEL stable
MAJOR 2
MINOR 14
-PATCH 2
+PATCH 3
PRERELEASE 0
PRERELEASE_PATCH 0
\ No newline at end of file