Skip to content

Commit d16ad3d

Browse files
sstricklcommit-bot@chromium.org
authored andcommitted
[vm/elf] Reorder non-NOBITS sections before NOBITS sections.
This ensures that the relocated addresses of sections with file contents are contained in the loaded segment. Fixes #47289 TEST=vm/dart{,_2}/use_save_debugging_info_flag Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-nnbd-linux-debug-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try Change-Id: I6f7f94900ab1f9f0cb5ead4b0dd63bd2402e5a19 Fixed: 47289 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/214800 Commit-Queue: Tess Strickland <[email protected]> Reviewed-by: Slava Egorov <[email protected]>
1 parent 6ff1f83 commit d16ad3d

File tree

7 files changed

+296
-30
lines changed

7 files changed

+296
-30
lines changed

pkg/native_stack_traces/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 0.4.4
4+
5+
- Added handling of dynamic tables for testing.
6+
37
## 0.4.3
48

59
- Exported some more of the ELF utilities for use in Dart tests.

pkg/native_stack_traces/lib/elf.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
export 'src/elf.dart' show Elf, Section, Symbol;
5+
export 'src/elf.dart' show DynamicTable, DynamicTableTag, Elf, Section, Symbol;
66
export 'src/constants.dart'
77
show
88
isolateDataSymbolName,

pkg/native_stack_traces/lib/src/elf.dart

Lines changed: 156 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,10 @@ class ElfHeader {
214214
static const _ELFDATA2MSB = 0x02;
215215

216216
void writeToStringBuffer(StringBuffer buffer) {
217-
buffer..write('Format is ')..write(wordSize * 8)..write(' bits');
217+
buffer
218+
..write('Format is ')
219+
..write(wordSize * 8)
220+
..write(' bits');
218221
switch (endian) {
219222
case Endian.little:
220223
buffer..writeln(' and little-endian');
@@ -342,6 +345,15 @@ class ProgramHeader {
342345
int get length => _entries.length;
343346
ProgramHeaderEntry operator [](int index) => _entries[index];
344347

348+
ProgramHeaderEntry? loadSegmentFor(int address) {
349+
for (final entry in _entries) {
350+
if (entry.vaddr <= address && address <= entry.vaddr + entry.memsz) {
351+
return entry;
352+
}
353+
}
354+
return null;
355+
}
356+
345357
static ProgramHeader fromReader(Reader reader, ElfHeader header) {
346358
final programReader = reader.refocusedCopy(
347359
header.programHeaderOffset, header.programHeaderSize);
@@ -352,7 +364,10 @@ class ProgramHeader {
352364

353365
void writeToStringBuffer(StringBuffer buffer) {
354366
for (var i = 0; i < length; i++) {
355-
if (i != 0) buffer..writeln()..writeln();
367+
if (i != 0)
368+
buffer
369+
..writeln()
370+
..writeln();
356371
buffer
357372
..write('Entry ')
358373
..write(i)
@@ -422,6 +437,17 @@ class SectionHeaderEntry {
422437
static const _SHT_NOBITS = 8;
423438
static const _SHT_DYNSYM = 11;
424439

440+
// sh_flags constants from ELF specification.
441+
static const _SHF_WRITE = 0x1;
442+
static const _SHF_ALLOC = 0x2;
443+
static const _SHF_EXECINSTR = 0x4;
444+
445+
bool get isWritable => flags & _SHF_WRITE != 0;
446+
bool get isAllocated => flags & _SHF_ALLOC != 0;
447+
bool get isExecutable => flags & _SHF_EXECINSTR != 0;
448+
449+
bool get hasBits => type != _SHT_NOBITS;
450+
425451
void setName(StringTable nameTable) {
426452
name = nameTable[nameIndex]!;
427453
}
@@ -495,7 +521,10 @@ class SectionHeader {
495521

496522
void writeToStringBuffer(StringBuffer buffer) {
497523
for (var i = 0; i < entries.length; i++) {
498-
if (i != 0) buffer..writeln()..writeln();
524+
if (i != 0)
525+
buffer
526+
..writeln()
527+
..writeln();
499528
buffer
500529
..write('Entry ')
501530
..write(i)
@@ -536,6 +565,8 @@ class Section {
536565
return SymbolTable.fromReader(reader, entry);
537566
case SectionHeaderEntry._SHT_NOTE:
538567
return Note.fromReader(reader, entry);
568+
case SectionHeaderEntry._SHT_DYNAMIC:
569+
return DynamicTable.fromReader(reader, entry);
539570
default:
540571
return Section._(entry);
541572
}
@@ -713,7 +744,10 @@ class Symbol {
713744
SymbolVisibility get visibility => SymbolVisibility.values[other & 0x03];
714745

715746
void writeToStringBuffer(StringBuffer buffer) {
716-
buffer..write('"')..write(name)..write('" =>');
747+
buffer
748+
..write('"')
749+
..write(name)
750+
..write('" =>');
717751
switch (bind) {
718752
case SymbolBinding.STB_GLOBAL:
719753
buffer..write(' a global');
@@ -794,6 +828,109 @@ class SymbolTable extends Section {
794828
}
795829
}
796830

831+
/// Represents d_tag constants from ELF specification.
832+
enum DynamicTableTag {
833+
DT_NULL,
834+
DT_NEEDED,
835+
DT_PLTRELSZ,
836+
DT_PLTGOT,
837+
DT_HASH,
838+
DT_STRTAB,
839+
DT_SYMTAB,
840+
DT_RELA,
841+
DT_RELASZ,
842+
DT_RELAENT,
843+
DT_STRSZ,
844+
DT_SYMENT,
845+
// Later d_tag values are not currently used in Dart ELF files.
846+
}
847+
848+
/// The dynamic table, which contains entries pointing to various relocated
849+
/// addresses.
850+
class DynamicTable extends Section {
851+
// We don't use DynamicTableTag for the key so that we can handle ELF files
852+
// that may use unknown (to us) tags.
853+
final Map<int, int> _entries;
854+
final _wordSize;
855+
856+
DynamicTable._(SectionHeaderEntry entry, this._entries, this._wordSize)
857+
: super._(entry);
858+
859+
static DynamicTable fromReader(Reader reader, SectionHeaderEntry entry) {
860+
final sectionReader = reader.refocusedCopy(entry.offset, entry.size);
861+
final entries = <int, int>{};
862+
while (true) {
863+
// Each entry is a tag and a value, both native word sized.
864+
final tag = _readElfNative(sectionReader);
865+
final value = _readElfNative(sectionReader);
866+
// A DT_NULL entry signfies the end of entries.
867+
if (tag == DynamicTableTag.DT_NULL.index) break;
868+
entries[tag] = value;
869+
}
870+
return DynamicTable._(entry, entries, sectionReader.wordSize);
871+
}
872+
873+
int? operator [](DynamicTableTag tag) => _entries[tag.index];
874+
bool containsKey(DynamicTableTag tag) => _entries.containsKey(tag.index);
875+
876+
// To avoid depending on EnumName.name from 2.15.
877+
static const _tagStrings = {
878+
DynamicTableTag.DT_NULL: 'DT_NULL',
879+
DynamicTableTag.DT_NEEDED: 'DT_NEEDED',
880+
DynamicTableTag.DT_PLTRELSZ: 'DT_PLTRELSZ',
881+
DynamicTableTag.DT_PLTGOT: 'DT_PLTGOT',
882+
DynamicTableTag.DT_HASH: 'DT_HASH',
883+
DynamicTableTag.DT_STRTAB: 'DT_STRTAB',
884+
DynamicTableTag.DT_SYMTAB: 'DT_SYMTAB',
885+
DynamicTableTag.DT_RELA: 'DT_RELA',
886+
DynamicTableTag.DT_RELASZ: 'DT_RELASZ',
887+
DynamicTableTag.DT_STRSZ: 'DT_STRSZ',
888+
DynamicTableTag.DT_SYMENT: 'DT_SYMENT',
889+
};
890+
static final _maxTagStringLength = (_tagStrings.values.toList()
891+
..sort((s1, s2) => s2.length - s1.length))
892+
.first
893+
.length;
894+
895+
@override
896+
void writeToStringBuffer(StringBuffer buffer) {
897+
buffer
898+
..write('Section "')
899+
..write(headerEntry.name)
900+
..writeln('" is a dynamic table:');
901+
for (var kv in _entries.entries) {
902+
buffer.write(' ');
903+
if (kv.key < DynamicTableTag.values.length) {
904+
final tag = DynamicTableTag.values[kv.key];
905+
buffer
906+
..write(_tagStrings[tag]?.padRight(_maxTagStringLength))
907+
..write(' => ');
908+
switch (tag) {
909+
// These are relocated addresses.
910+
case DynamicTableTag.DT_HASH:
911+
case DynamicTableTag.DT_PLTGOT:
912+
case DynamicTableTag.DT_SYMTAB:
913+
case DynamicTableTag.DT_STRTAB:
914+
case DynamicTableTag.DT_RELA:
915+
buffer
916+
..write('0x')
917+
..writeln(paddedHex(kv.value, _wordSize));
918+
break;
919+
// Other entries are just values or offsets.
920+
default:
921+
buffer.writeln(kv.value);
922+
}
923+
} else {
924+
buffer
925+
..write("Unknown tag ")
926+
..write(kv.key)
927+
..write(' => ')
928+
..writeln(kv.value);
929+
}
930+
}
931+
}
932+
}
933+
797934
/// Information parsed from an Executable and Linking Format (ELF) file.
798935
class Elf {
799936
final ElfHeader _header;
@@ -819,6 +956,21 @@ class Elf {
819956
Iterable<Section> namedSections(String name) =>
820957
_sectionsByName[name] ?? <Section>[];
821958

959+
/// Checks that the contents of a given section have valid addresses when the
960+
/// file contents for the corresponding segment is loaded into memory.
961+
///
962+
/// Returns false for sections that are not allocated or where the address
963+
/// does not correspond to file contents (i.e., NOBITS sections).
964+
bool sectionHasValidSegmentAddresses(Section section) {
965+
final headerEntry = section.headerEntry;
966+
if (!headerEntry.isAllocated || !headerEntry.hasBits) return false;
967+
final segment = _programHeader.loadSegmentFor(headerEntry.addr);
968+
if (segment == null) return false;
969+
return (headerEntry.addr < (segment.vaddr + segment.filesz)) &&
970+
(headerEntry.addr + headerEntry.size) <=
971+
(segment.vaddr + segment.filesz);
972+
}
973+
822974
/// Lookup of a dynamic symbol by name.
823975
///
824976
/// Returns -1 if there is no dynamic symbol that matches [name].

pkg/native_stack_traces/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: native_stack_traces
22
description: Utilities for working with non-symbolic stack traces.
3-
version: 0.4.3
3+
version: 0.4.4
44

55
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/native_stack_traces
66

runtime/tests/vm/dart/use_save_debugging_info_flag_test.dart

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import "dart:math";
1313
import "dart:typed_data";
1414

1515
import 'package:expect/expect.dart';
16+
import 'package:native_stack_traces/elf.dart';
1617
import 'package:native_stack_traces/native_stack_traces.dart';
1718
import 'package:path/path.dart' as path;
1819

@@ -63,6 +64,7 @@ main(List<String> args) async {
6364
'--elf=$scriptWholeSnapshot',
6465
scriptDill,
6566
]);
67+
checkElf(scriptWholeSnapshot);
6668

6769
final scriptStrippedOnlySnapshot = path.join(tempDir, 'stripped_only.so');
6870
await run(genSnapshot, <String>[
@@ -72,6 +74,7 @@ main(List<String> args) async {
7274
'--strip',
7375
scriptDill,
7476
]);
77+
checkElf(scriptStrippedOnlySnapshot);
7578

7679
final scriptStrippedSnapshot = path.join(tempDir, 'stripped.so');
7780
final scriptDebuggingInfo = path.join(tempDir, 'debug.so');
@@ -83,6 +86,8 @@ main(List<String> args) async {
8386
'--save-debugging-info=$scriptDebuggingInfo',
8487
scriptDill,
8588
]);
89+
checkElf(scriptStrippedSnapshot);
90+
checkElf(scriptDebuggingInfo);
8691

8792
// Run the resulting scripts, saving the stack traces.
8893
final wholeTrace = await runError(aotRuntime, <String>[
@@ -184,3 +189,46 @@ void printDiff(Map<int, List<int>> map, [int maxOutput = 100]) {
184189
}
185190
}
186191
}
192+
193+
void checkElf(String filename) {
194+
print("Checking ELF file $filename:");
195+
final elf = Elf.fromFile(filename);
196+
Expect.isNotNull(elf);
197+
final dynamic = elf!.namedSections(".dynamic").single as DynamicTable;
198+
199+
// Check the dynamic string table information.
200+
Expect.isTrue(
201+
dynamic.containsKey(DynamicTableTag.DT_STRTAB), "no string table entry");
202+
final dynstr = elf.namedSections(".dynstr").single;
203+
print(".dynstr address = ${dynamic[DynamicTableTag.DT_STRTAB]}");
204+
Expect.isTrue(elf.sectionHasValidSegmentAddresses(dynstr),
205+
"string table addresses are invalid");
206+
Expect.equals(dynamic[DynamicTableTag.DT_STRTAB], dynstr.headerEntry.addr);
207+
Expect.isTrue(dynamic.containsKey(DynamicTableTag.DT_STRSZ),
208+
"no string table size entry");
209+
print(".dynstr size = ${dynamic[DynamicTableTag.DT_STRSZ]}");
210+
Expect.equals(dynamic[DynamicTableTag.DT_STRSZ], dynstr.headerEntry.size);
211+
212+
// Check the dynamic symbol table information.
213+
Expect.isTrue(
214+
dynamic.containsKey(DynamicTableTag.DT_SYMTAB), "no symbol table entry");
215+
print(".dynsym address = ${dynamic[DynamicTableTag.DT_SYMTAB]}");
216+
final dynsym = elf.namedSections(".dynsym").single;
217+
Expect.isTrue(elf.sectionHasValidSegmentAddresses(dynsym),
218+
"string table addresses are invalid");
219+
Expect.equals(dynamic[DynamicTableTag.DT_SYMTAB], dynsym.headerEntry.addr);
220+
Expect.isTrue(dynamic.containsKey(DynamicTableTag.DT_SYMENT),
221+
"no symbol table entry size entry");
222+
print(".dynsym entry size = ${dynamic[DynamicTableTag.DT_SYMENT]}");
223+
Expect.equals(
224+
dynamic[DynamicTableTag.DT_SYMENT], dynsym.headerEntry.entrySize);
225+
226+
// Check the hash table information.
227+
Expect.isTrue(
228+
dynamic.containsKey(DynamicTableTag.DT_HASH), "no hash table entry");
229+
print(".hash address = ${dynamic[DynamicTableTag.DT_HASH]}");
230+
final hash = elf.namedSections(".hash").single;
231+
Expect.isTrue(elf.sectionHasValidSegmentAddresses(hash),
232+
"hash table addresses are invalid");
233+
Expect.equals(dynamic[DynamicTableTag.DT_HASH], hash.headerEntry.addr);
234+
}

0 commit comments

Comments
 (0)