[vm] Make symbol names more user-readable.

Instead of trying to make names assembler-friendly, instead just
quote the symbols names in the assembly output and use user visible
names for function and object symbols.

More specifically:

* This CL changes the symbol name from an assembler-safe version of the
  scrubbed name to an assembler-unsafe version of the user visible name
  that is then quoted when used in assembly output.

* For code symbols, regular stubs are prefixed with `stub`, allocation
  stubs are prefixed with `new` and type testing stubs are prefixed with
  `assert type is` (e.g., `stub Await`, `new RegExp`,
  `assert type is List<Int>`). Function symbols have no prefix.

* Readonly data symbols begin with the type of the readonly data. If
  there's any additional information (e.g., the function name for
  objects like `PcDescriptors` when `SNAPSHOT_BACKTRACE` is defined),
  that follows the type in parentheses.

* For direct-to-ELF snapshots, we allow symbols to have duplicate names.
  Thus, internally we create unique ids for all created symbols and
  refactor the ELF and DWARF subsystems to pass and store these ids
  instead of the symbol names.

* For assembly output, symbols must have unique names. The namer now
  has a `CStringIntMap` that keeps counts of the number of times a
  given symbol name has been generated for assembly output. If the
  symbol name is in the `CStringIntMap`, then the new count in the map
  is added as a suffix to ensure uniqueness.

  For example, the first symbol created using the name `_StringBase.[]`
  has no suffix. If a second symbol is created using the same name,
  the second symbol is instead named `_StringBase.[] (#2)`.

  This also happens for read-only objects with additional information if
  that additional information is not sufficient to make it unique, and
  is added separately from that additional information.

* A further restriction for assembly output is that symbols that begin
  with 'L' have special meaning to the assembler. Thus, we keep the
  old system of prepending such symbol names with an underscore to
  avoid this behavior.

TEST=vm/dart{,_2}/use_add_readonly_data_symbol_flag

Bug: b/248012965
Change-Id: I1b6e876f4fb4f42f4a3b90e03110a34a87a03a7c
Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-dwarf-linux-product-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try,vm-kernel-precomp-nnbd-mac-release-arm64-try,vm-kernel-precomp-mac-product-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/262242
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Tess Strickland 2022-10-06 10:33:09 +00:00 committed by Commit Queue
parent e77f2ee5ff
commit 3b2c41d789
8 changed files with 677 additions and 425 deletions

View file

@ -62,10 +62,14 @@ main(List<String> args) async {
scriptDill,
]);
checkElf(scriptSnapshot);
checkElf(scriptDebuggingInfo);
final snapshotElf = Elf.fromFile(scriptSnapshot);
Expect.isNotNull(snapshotElf);
final debugInfoElf = Elf.fromFile(scriptDebuggingInfo);
Expect.isNotNull(debugInfoElf);
if (Platform.isLinux && !isSimulator) {
checkElf(snapshotElf!, debugInfoElf!, isAssembled: false);
if ((Platform.isLinux || Platform.isMacOS) && !isSimulator) {
final scriptAssembly = path.join(tempDir, 'snapshot.S');
final scriptAssemblySnapshot = path.join(tempDir, 'assembly.so');
final scriptAssemblyDebuggingInfo =
@ -83,63 +87,57 @@ main(List<String> args) async {
await assembleSnapshot(scriptAssembly, scriptAssemblySnapshot,
debug: true);
checkElf(scriptAssemblySnapshot, isAssembled: true);
checkElf(scriptAssemblyDebuggingInfo);
// This one may be null if on MacOS.
final assembledElf = Elf.fromFile(scriptAssemblySnapshot);
if (Platform.isLinux) {
Expect.isNotNull(assembledElf);
}
final assembledDebugElf = Elf.fromFile(scriptAssemblyDebuggingInfo);
Expect.isNotNull(assembledDebugElf);
checkElf(assembledElf, assembledDebugElf!, isAssembled: true);
}
});
}
void checkElf(String filename, {bool isAssembled = false}) {
// Check that the static symbol table contains entries that are not in the
// dynamic symbol table, have STB_LOCAL binding, and are of type STT_OBJECT.
final elf = Elf.fromFile(filename);
Expect.isNotNull(elf);
final dynamicSymbols = elf!.dynamicSymbols;
// All symbol tables have an initial entry with zero-valued fields.
Expect.isNotEmpty(dynamicSymbols);
print('Dynamic symbols:');
final initialDynamic = dynamicSymbols.first;
print(initialDynamic);
Expect.equals(SymbolBinding.STB_LOCAL, initialDynamic.bind);
Expect.equals(SymbolType.STT_NOTYPE, initialDynamic.type);
Expect.equals(0, initialDynamic.value);
for (final symbol in dynamicSymbols.skip(1)) {
final _uniqueSuffixRegexp = RegExp(r' \(#\d+\)');
void checkVMSymbolsAreValid(Iterable<Symbol> symbols,
{required String source, required bool isAssembled}) {
print("Checking VM symbols from $source:");
for (final symbol in symbols) {
print(symbol);
if (!symbol.name.startsWith('_kDart')) {
// The VM only adds symbols with names starting with _kDart, so this
// must be an assembled snapshot.
Expect.isTrue(isAssembled);
// All VM-created symbols should have sizes.
Expect.notEquals(0, symbol.size);
if (symbol.bind != SymbolBinding.STB_GLOBAL &&
symbol.bind != SymbolBinding.STB_LOCAL) {
Expect.fail('Unexpected symbol binding ${symbol.bind}');
}
if (symbol.bind == SymbolBinding.STB_GLOBAL) {
// All global symbols created by the VM are currently object symbols.
Expect.equals(SymbolType.STT_OBJECT, symbol.type);
Expect.isTrue(symbol.name.startsWith('_kDart'),
'Unexpected symbol name ${symbol.name}');
Expect.isFalse(_uniqueSuffixRegexp.hasMatch(symbol.name),
'Global VM symbols should have no numeric suffix');
continue;
}
Expect.equals(SymbolBinding.STB_GLOBAL, symbol.bind);
Expect.equals(SymbolType.STT_OBJECT, symbol.type);
// All VM-generated read-only object symbols should have a non-zero size.
Expect.notEquals(0, symbol.size);
}
print("");
final onlyStaticSymbols = elf.staticSymbols
.where((s1) => !dynamicSymbols.any((s2) => s1.name == s2.name));
Expect.isNotEmpty(onlyStaticSymbols, 'no static-only symbols');
final objectSymbols =
onlyStaticSymbols.where((s) => s.type == SymbolType.STT_OBJECT);
Expect.isNotEmpty(objectSymbols, 'no static-only object symbols');
print("Static-only object symbols:");
for (final symbol in objectSymbols) {
print(symbol);
// There should be no static-only global object symbols.
Expect.equals(SymbolBinding.STB_LOCAL, symbol.bind);
final objectTypeEnd = symbol.name.indexOf('_');
// All VM-generated read-only object symbols are prefixed with the type of
// the C++ object followed by an underscore. If assembling the snapshot,
// the assembler might introduce other object symbols which either start
// with an underscore or have no underscore.
if (objectTypeEnd <= 0) {
Expect.isTrue(isAssembled);
if (symbol.type == SymbolType.STT_FUNC ||
symbol.type == SymbolType.STT_SECTION) {
// Currently we don't do any additional checking on these.
continue;
}
// All VM-generated read-only object symbols should have a non-zero size.
Expect.notEquals(0, symbol.size);
final objectType = symbol.name.substring(0, objectTypeEnd);
if (symbol.type != SymbolType.STT_OBJECT) {
Expect.fail('Unexpected symbol type ${symbol.type}');
}
// The name of object symbols are prefixed with the type of the object. If
// there is useful additional information, the additional information is
// provided after the type in parentheses.
int objectTypeEnd = symbol.name.indexOf(' (');
final objectType = objectTypeEnd > 0
? symbol.name.substring(0, objectTypeEnd)
: symbol.name;
switch (objectType) {
// Used for entries in the non-clustered portion of the read-only data
// section that don't correspond to a specific Dart object.
@ -152,8 +150,101 @@ void checkElf(String filename, {bool isAssembled = false}) {
case 'PcDescriptors':
case 'CompressedStackMaps':
break;
// In assembly snapshots, we use local object symbols to initialize the
// InstructionsSection header with the right offset to the BSS section.
case '_kDartVmSnapshotBss':
case '_kDartIsolateSnapshotBss':
Expect.isTrue(isAssembled);
break;
default:
Expect.fail('unexpected object type $objectType');
Expect.fail('unexpected object type $objectType in "${symbol.name}"');
}
}
}
void checkSymbols(List<Symbol>? snapshotSymbols, List<Symbol> debugInfoSymbols,
{required bool isAssembled}) {
// All symbols in the separate debugging info are created by the VM.
Expect.isNotEmpty(debugInfoSymbols);
checkVMSymbolsAreValid(debugInfoSymbols,
source: 'debug info', isAssembled: isAssembled);
if (snapshotSymbols == null) return;
List<Symbol> checkedSnapshotSymbols = snapshotSymbols;
if (isAssembled) {
final names = debugInfoSymbols.map((s) => s.name).toSet();
// All VM-generated symbols should have unique names in assembled output.
Expect.equals(names.length, debugInfoSymbols.length);
// For assembled snapshots, we may have symbols that are created by the
// assembler and not the VM, so ignore those symbols. Since all VM symbols
// have unique names when generating assembly, we just check that the
// debug info has a symbol with the same name.
checkedSnapshotSymbols = <Symbol>[];
for (final symbol in snapshotSymbols) {
if (names.contains(symbol.name)) {
checkedSnapshotSymbols.add(symbol);
}
}
}
checkVMSymbolsAreValid(checkedSnapshotSymbols,
source: 'snapshot', isAssembled: isAssembled);
}
void checkElf(Elf? snapshot, Elf debugInfo, {required bool isAssembled}) {
// All symbol tables have an initial entry with zero-valued fields.
final snapshotDynamicSymbols = snapshot?.dynamicSymbols?.skip(1)?.toList();
final debugDynamicSymbols = debugInfo.dynamicSymbols.skip(1).toList();
final snapshotStaticSymbols = snapshot?.staticSymbols?.skip(1)?.toList();
final debugStaticSymbols = debugInfo.staticSymbols.skip(1).toList();
// First, do our general round of checks against each group of tables.
checkSymbols(snapshotDynamicSymbols, debugDynamicSymbols,
isAssembled: isAssembled);
checkSymbols(snapshotStaticSymbols, debugStaticSymbols,
isAssembled: isAssembled);
// Now do some additional spot checks to make sure we actually haven't missed
// generating some expected VM symbols by examining the debug info tables,
// which only contain VM generated symbols.
// For the dynamic symbol tables, we expect that all VM symbols are global
// object symbols.
for (final symbol in debugDynamicSymbols) {
Expect.equals(symbol.bind, SymbolBinding.STB_GLOBAL,
'Expected all global symbols in the dynamic table, got $symbol');
Expect.equals(symbol.type, SymbolType.STT_OBJECT,
'Expected all object symbols in the dynamic table, got $symbol');
}
final debugLocalSymbols =
debugStaticSymbols.where((s) => s.bind == SymbolBinding.STB_LOCAL);
final debugLocalObjectSymbols =
debugLocalSymbols.where((s) => s.type == SymbolType.STT_OBJECT);
// We should be generating at least _some_ local object symbols, since we're
// using the --add-readonly-data-symbols flag.
Expect.isNotEmpty(debugLocalObjectSymbols);
// We expect exactly two local object symbols with names starting with
// 'RawBytes'.
int rawBytesSeen = debugLocalObjectSymbols.fold<int>(
0, (i, s) => i + (s.name.startsWith('RawBytes') ? 1 : 0));
Expect.equals(2, rawBytesSeen,
'saw $rawBytesSeen (!= 2) RawBytes local object symbols');
// All snapshots include at least one (and likely many) duplicate local
// symbols. For assembly snapshots and their separate debugging information,
// these symbols will have a numeric suffix to make them unique. In
// direct-to-ELF snapshots and their separate debugging information, we allow
// duplicate symbol names and thus there should be no numeric suffixes.
bool sawUniqueSuffix = false;
for (final symbol in debugLocalSymbols) {
if (_uniqueSuffixRegexp.hasMatch(symbol.name)) {
if (!isAssembled) {
Expect.fail('Saw numeric suffix on symbol: $symbol');
}
sawUniqueSuffix = true;
}
}
if (isAssembled) {
Expect.isTrue(sawUniqueSuffix, 'No numeric suffixes seen');
}
}

View file

@ -64,10 +64,14 @@ main(List<String> args) async {
scriptDill,
]);
checkElf(scriptSnapshot);
checkElf(scriptDebuggingInfo);
final snapshotElf = Elf.fromFile(scriptSnapshot);
Expect.isNotNull(snapshotElf);
final debugInfoElf = Elf.fromFile(scriptDebuggingInfo);
Expect.isNotNull(debugInfoElf);
if (Platform.isLinux && !isSimulator) {
checkElf(snapshotElf, debugInfoElf, isAssembled: false);
if ((Platform.isLinux || Platform.isMacOS) && !isSimulator) {
final scriptAssembly = path.join(tempDir, 'snapshot.S');
final scriptAssemblySnapshot = path.join(tempDir, 'assembly.so');
final scriptAssemblyDebuggingInfo =
@ -85,63 +89,57 @@ main(List<String> args) async {
await assembleSnapshot(scriptAssembly, scriptAssemblySnapshot,
debug: true);
checkElf(scriptAssemblySnapshot, isAssembled: true);
checkElf(scriptAssemblyDebuggingInfo);
// This one may be null if on MacOS.
final assembledElf = Elf.fromFile(scriptAssemblySnapshot);
if (Platform.isLinux) {
Expect.isNotNull(assembledElf);
}
final assembledDebugElf = Elf.fromFile(scriptAssemblyDebuggingInfo);
Expect.isNotNull(assembledDebugElf);
checkElf(assembledElf, assembledDebugElf, isAssembled: true);
}
});
}
void checkElf(String filename, {bool isAssembled = false}) {
// Check that the static symbol table contains entries that are not in the
// dynamic symbol table, have STB_LOCAL binding, and are of type STT_OBJECT.
final elf = Elf.fromFile(filename);
Expect.isNotNull(elf);
final dynamicSymbols = elf.dynamicSymbols;
// All symbol tables have an initial entry with zero-valued fields.
Expect.isNotEmpty(dynamicSymbols);
print('Dynamic symbols:');
final initialDynamic = dynamicSymbols.first;
print(initialDynamic);
Expect.equals(SymbolBinding.STB_LOCAL, initialDynamic.bind);
Expect.equals(SymbolType.STT_NOTYPE, initialDynamic.type);
Expect.equals(0, initialDynamic.value);
for (final symbol in dynamicSymbols.skip(1)) {
final _uniqueSuffixRegexp = RegExp(r' \(#\d+\)');
void checkVMSymbolsAreValid(Iterable<Symbol> symbols,
{String source, bool isAssembled}) {
print("Checking VM symbols from $source:");
for (final symbol in symbols) {
print(symbol);
if (!symbol.name.startsWith('_kDart')) {
// The VM only adds symbols with names starting with _kDart, so this
// must be an assembled snapshot.
Expect.isTrue(isAssembled);
// All VM-created symbols should have sizes.
Expect.notEquals(0, symbol.size);
if (symbol.bind != SymbolBinding.STB_GLOBAL &&
symbol.bind != SymbolBinding.STB_LOCAL) {
Expect.fail('Unexpected symbol binding ${symbol.bind}');
}
if (symbol.bind == SymbolBinding.STB_GLOBAL) {
// All global symbols created by the VM are currently object symbols.
Expect.equals(SymbolType.STT_OBJECT, symbol.type);
Expect.isTrue(symbol.name.startsWith('_kDart'),
'Unexpected symbol name ${symbol.name}');
Expect.isFalse(_uniqueSuffixRegexp.hasMatch(symbol.name),
'Global VM symbols should have no numeric suffix');
continue;
}
Expect.equals(SymbolBinding.STB_GLOBAL, symbol.bind);
Expect.equals(SymbolType.STT_OBJECT, symbol.type);
// All VM-generated read-only object symbols should have a non-zero size.
Expect.notEquals(0, symbol.size);
}
print("");
final onlyStaticSymbols = elf.staticSymbols
.where((s1) => !dynamicSymbols.any((s2) => s1.name == s2.name));
Expect.isNotEmpty(onlyStaticSymbols, 'no static-only symbols');
final objectSymbols =
onlyStaticSymbols.where((s) => s.type == SymbolType.STT_OBJECT);
Expect.isNotEmpty(objectSymbols, 'no static-only object symbols');
print("Static-only object symbols:");
for (final symbol in objectSymbols) {
print(symbol);
// There should be no static-only global object symbols.
Expect.equals(SymbolBinding.STB_LOCAL, symbol.bind);
final objectTypeEnd = symbol.name.indexOf('_');
// All VM-generated read-only object symbols are prefixed with the type of
// the C++ object followed by an underscore. If assembling the snapshot,
// the assembler might introduce other object symbols which either start
// with an underscore or have no underscore.
if (objectTypeEnd <= 0) {
Expect.isTrue(isAssembled);
if (symbol.type == SymbolType.STT_FUNC ||
symbol.type == SymbolType.STT_SECTION) {
// Currently we don't do any additional checking on these.
continue;
}
// All VM-generated read-only object symbols should have a non-zero size.
Expect.notEquals(0, symbol.size);
final objectType = symbol.name.substring(0, objectTypeEnd);
if (symbol.type != SymbolType.STT_OBJECT) {
Expect.fail('Unexpected symbol type ${symbol.type}');
}
// The name of object symbols are prefixed with the type of the object. If
// there is useful additional information, the additional information is
// provided after the type in parentheses.
int objectTypeEnd = symbol.name.indexOf(' (');
final objectType = objectTypeEnd > 0
? symbol.name.substring(0, objectTypeEnd)
: symbol.name;
switch (objectType) {
// Used for entries in the non-clustered portion of the read-only data
// section that don't correspond to a specific Dart object.
@ -154,8 +152,101 @@ void checkElf(String filename, {bool isAssembled = false}) {
case 'PcDescriptors':
case 'CompressedStackMaps':
break;
// In assembly snapshots, we use local object symbols to initialize the
// InstructionsSection header with the right offset to the BSS section.
case '_kDartVmSnapshotBss':
case '_kDartIsolateSnapshotBss':
Expect.isTrue(isAssembled);
break;
default:
Expect.fail('unexpected object type $objectType');
Expect.fail('unexpected object type $objectType in "${symbol.name}"');
}
}
}
void checkSymbols(List<Symbol> snapshotSymbols, List<Symbol> debugInfoSymbols,
{bool isAssembled}) {
// All symbols in the separate debugging info are created by the VM.
Expect.isNotEmpty(debugInfoSymbols);
checkVMSymbolsAreValid(debugInfoSymbols,
source: 'debug info', isAssembled: isAssembled);
if (snapshotSymbols == null) return;
List<Symbol> checkedSnapshotSymbols = snapshotSymbols;
if (isAssembled) {
final names = debugInfoSymbols.map((s) => s.name).toSet();
// All VM-generated symbols should have unique names in assembled output.
Expect.equals(names.length, debugInfoSymbols.length);
// For assembled snapshots, we may have symbols that are created by the
// assembler and not the VM, so ignore those symbols. Since all VM symbols
// have unique names when generating assembly, we just check that the
// debug info has a symbol with the same name.
checkedSnapshotSymbols = <Symbol>[];
for (final symbol in snapshotSymbols) {
if (names.contains(symbol.name)) {
checkedSnapshotSymbols.add(symbol);
}
}
}
checkVMSymbolsAreValid(checkedSnapshotSymbols,
source: 'snapshot', isAssembled: isAssembled);
}
void checkElf(Elf snapshot, Elf debugInfo, {bool isAssembled}) {
// All symbol tables have an initial entry with zero-valued fields.
final snapshotDynamicSymbols = snapshot?.dynamicSymbols?.skip(1)?.toList();
final debugDynamicSymbols = debugInfo.dynamicSymbols.skip(1).toList();
final snapshotStaticSymbols = snapshot?.staticSymbols?.skip(1)?.toList();
final debugStaticSymbols = debugInfo.staticSymbols.skip(1).toList();
// First, do our general round of checks against each group of tables.
checkSymbols(snapshotDynamicSymbols, debugDynamicSymbols,
isAssembled: isAssembled);
checkSymbols(snapshotStaticSymbols, debugStaticSymbols,
isAssembled: isAssembled);
// Now do some additional spot checks to make sure we actually haven't missed
// generating some expected VM symbols by examining the debug info tables,
// which only contain VM generated symbols.
// For the dynamic symbol tables, we expect that all VM symbols are global
// object symbols.
for (final symbol in debugDynamicSymbols) {
Expect.equals(symbol.bind, SymbolBinding.STB_GLOBAL,
'Expected all global symbols in the dynamic table, got $symbol');
Expect.equals(symbol.type, SymbolType.STT_OBJECT,
'Expected all object symbols in the dynamic table, got $symbol');
}
final debugLocalSymbols =
debugStaticSymbols.where((s) => s.bind == SymbolBinding.STB_LOCAL);
final debugLocalObjectSymbols =
debugLocalSymbols.where((s) => s.type == SymbolType.STT_OBJECT);
// We should be generating at least _some_ local object symbols, since we're
// using the --add-readonly-data-symbols flag.
Expect.isNotEmpty(debugLocalObjectSymbols);
// We expect exactly two local object symbols with names starting with
// 'RawBytes'.
int rawBytesSeen = debugLocalObjectSymbols.fold<int>(
0, (i, s) => i + (s.name.startsWith('RawBytes') ? 1 : 0));
Expect.equals(2, rawBytesSeen,
'saw $rawBytesSeen (!= 2) RawBytes local object symbols');
// All snapshots include at least one (and likely many) duplicate local
// symbols. For assembly snapshots and their separate debugging information,
// these symbols will have a numeric suffix to make them unique. In
// direct-to-ELF snapshots and their separate debugging information, we allow
// duplicate symbol names and thus there should be no numeric suffixes.
bool sawUniqueSuffix = false;
for (final symbol in debugLocalSymbols) {
if (_uniqueSuffixRegexp.hasMatch(symbol.name)) {
if (!isAssembled) {
Expect.fail('Saw numeric suffix on symbol: $symbol');
}
sawUniqueSuffix = true;
}
}
if (isAssembled) {
Expect.isTrue(sawUniqueSuffix, 'No numeric suffixes seen');
}
}

View file

@ -138,23 +138,21 @@ Dwarf::Dwarf(Zone* zone)
: zone_(zone),
reverse_obfuscation_trie_(CreateReverseObfuscationTrie(zone)),
codes_(zone, 1024),
code_to_name_(zone),
code_to_label_(zone),
functions_(zone, 1024),
function_to_index_(zone),
scripts_(zone, 1024),
script_to_index_(zone),
temp_(0) {}
script_to_index_(zone) {}
void Dwarf::AddCode(const Code& orig_code, const char* name) {
void Dwarf::AddCode(const Code& orig_code, intptr_t label) {
ASSERT(!orig_code.IsNull());
ASSERT(name != nullptr);
ASSERT(label > 0);
if (auto const old_pair = code_to_name_.Lookup(&orig_code)) {
if (auto const old_pair = code_to_label_.Lookup(&orig_code)) {
// Dwarf objects can be shared, so we may get the same information for a
// given code object in different calls. In DEBUG mode, make sure the
// information is the same before returning.
ASSERT(old_pair->value != nullptr);
ASSERT_EQUAL(strcmp(old_pair->value, name), 0);
ASSERT_EQUAL(label, old_pair->value);
return;
}
@ -163,7 +161,7 @@ void Dwarf::AddCode(const Code& orig_code, const char* name) {
codes_.Add(&code);
// Currently assumes the name has the same lifetime as the Zone of the
// Dwarf object (which is currently true). Otherwise, need to copy.
code_to_name_.Insert({&code, name});
code_to_label_.Insert({&code, label});
if (code.IsFunctionCode() && !code.IsUnknownDartCode()) {
const Function& function = Function::Handle(zone_, code.function());
@ -323,7 +321,9 @@ void Dwarf::WriteDebugInfo(DwarfWriteStream* stream) {
// The lowest instruction address in this object file that is part of our
// compilation unit. Dwarf consumers use this to quickly decide which
// compilation unit DIE to consult for a given pc.
stream->OffsetFromSymbol(kIsolateSnapshotInstructionsAsmSymbol, 0);
auto const isolate_instructions_label = ImageWriter::SectionLabel(
ImageWriter::ProgramSection::Text, /*vm=*/false);
stream->OffsetFromSymbol(isolate_instructions_label, 0);
// DW_AT_high_pc
// The highest instruction address in this object file that is part of our
@ -331,12 +331,12 @@ void Dwarf::WriteDebugInfo(DwarfWriteStream* stream) {
// compilation unit DIE to consult for a given pc.
if (codes_.is_empty()) {
// No code objects in this program, so set high_pc to same as low_pc.
stream->OffsetFromSymbol(kIsolateSnapshotInstructionsAsmSymbol, 0);
stream->OffsetFromSymbol(isolate_instructions_label, 0);
} else {
const Code& last_code = *codes_.Last();
auto const last_code_name = code_to_name_.LookupValue(&last_code);
ASSERT(last_code_name != nullptr);
stream->OffsetFromSymbol(last_code_name, last_code.Size());
auto const last_code_label = code_to_label_.LookupValue(&last_code);
ASSERT(last_code_label > 0);
stream->OffsetFromSymbol(last_code_label, last_code.Size());
}
// DW_AT_stmt_list (offset into .debug_line)
@ -389,8 +389,8 @@ void Dwarf::WriteConcreteFunctions(DwarfWriteStream* stream) {
function = code.function();
intptr_t function_index = LookupFunction(function);
script = function.script();
const char* asm_name = code_to_name_.LookupValue(&code);
ASSERT(asm_name != nullptr);
intptr_t label = code_to_label_.LookupValue(&code);
ASSERT(label > 0);
stream->uleb128(kConcreteFunction);
// DW_AT_abstract_origin
@ -398,9 +398,9 @@ void Dwarf::WriteConcreteFunctions(DwarfWriteStream* stream) {
stream->AbstractOrigin(function_index);
// DW_AT_low_pc
stream->OffsetFromSymbol(asm_name, 0);
stream->OffsetFromSymbol(label, 0);
// DW_AT_high_pc
stream->OffsetFromSymbol(asm_name, code.Size());
stream->OffsetFromSymbol(label, code.Size());
// DW_AT_artificial
stream->u1(function.is_visible() ? 0 : 1);
@ -408,7 +408,7 @@ void Dwarf::WriteConcreteFunctions(DwarfWriteStream* stream) {
if (node != NULL) {
for (InliningNode* child = node->children_head; child != NULL;
child = child->children_next) {
WriteInliningNode(stream, child, asm_name, script);
WriteInliningNode(stream, child, label, script);
}
}
@ -497,8 +497,9 @@ InliningNode* Dwarf::ExpandInliningTree(const Code& code) {
void Dwarf::WriteInliningNode(DwarfWriteStream* stream,
InliningNode* node,
const char* root_asm_name,
intptr_t root_label,
const Script& parent_script) {
ASSERT(root_label > 0);
intptr_t file = LookupScript(parent_script);
intptr_t function_index = LookupFunction(node->function);
const Script& script = Script::Handle(zone_, node->function.script());
@ -509,9 +510,9 @@ void Dwarf::WriteInliningNode(DwarfWriteStream* stream,
stream->AbstractOrigin(function_index);
// DW_AT_low_pc
stream->OffsetFromSymbol(root_asm_name, node->start_pc_offset);
stream->OffsetFromSymbol(root_label, node->start_pc_offset);
// DW_AT_high_pc
stream->OffsetFromSymbol(root_asm_name, node->end_pc_offset);
stream->OffsetFromSymbol(root_label, node->end_pc_offset);
// DW_AT_call_file
stream->uleb128(file);
@ -522,7 +523,7 @@ void Dwarf::WriteInliningNode(DwarfWriteStream* stream,
for (InliningNode* child = node->children_head; child != NULL;
child = child->children_next) {
WriteInliningNode(stream, child, root_asm_name, script);
WriteInliningNode(stream, child, root_label, script);
}
stream->uleb128(0); // End of children.
@ -539,9 +540,9 @@ class LineNumberProgramWriter {
void EmitRow(intptr_t file,
intptr_t line,
intptr_t column,
const char* asm_name,
intptr_t label,
intptr_t pc_offset) {
if (AddRow(file, line, column, asm_name, pc_offset)) {
if (AddRow(file, line, column, label, pc_offset)) {
// Address register must be updated from 0 before emitting an LNP row
// (dartbug.com/41756).
stream_->u1(Dwarf::DW_LNS_copy);
@ -554,7 +555,7 @@ class LineNumberProgramWriter {
DART_WARN_UNUSED_RESULT bool AddRow(intptr_t file,
intptr_t line,
intptr_t column,
const char* asm_name,
intptr_t label,
intptr_t pc_offset) {
ASSERT_EQUAL(end_sequence_, false);
bool source_info_changed = false;
@ -585,7 +586,7 @@ class LineNumberProgramWriter {
// addresses between two line number program rows inherit the source
// information from the first.
if (source_info_changed) {
SetCurrentPosition(asm_name, pc_offset);
SetCurrentPosition(label, pc_offset);
}
return source_info_changed;
}
@ -599,32 +600,32 @@ class LineNumberProgramWriter {
end_sequence_ = true;
}
void MarkEnd(const char* asm_name, intptr_t pc_offset) {
void MarkEnd(intptr_t label, intptr_t pc_offset) {
ASSERT_EQUAL(end_sequence_, false);
SetCurrentPosition(asm_name, pc_offset);
SetCurrentPosition(label, pc_offset);
MarkEnd();
}
private:
void SetCurrentPosition(const char* asm_name, intptr_t pc_offset) {
void SetCurrentPosition(intptr_t label, intptr_t pc_offset) {
// Each LNP row is either in a different function from the previous row
// or is at an increasing PC offset into the same function.
ASSERT(asm_name != nullptr);
ASSERT(label > 0);
ASSERT(pc_offset >= 0);
ASSERT(asm_name_ != asm_name || pc_offset > pc_offset_);
if (asm_name_ != asm_name) {
ASSERT(label_ != label || pc_offset > pc_offset_);
if (label_ != label) {
// Set the address register to the given offset into the new code payload.
auto const instr_size = 1 + compiler::target::kWordSize;
stream_->u1(0); // This is an extended opcode
stream_->u1(instr_size); // that is 5 or 9 bytes long
stream_->u1(Dwarf::DW_LNE_set_address);
stream_->OffsetFromSymbol(asm_name, pc_offset);
stream_->OffsetFromSymbol(label, pc_offset);
} else {
// Change the address register by the difference in the two offsets.
stream_->u1(Dwarf::DW_LNS_advance_pc);
stream_->uleb128(pc_offset - pc_offset_);
}
asm_name_ = asm_name;
label_ = label;
pc_offset_ = pc_offset;
}
@ -638,7 +639,7 @@ class LineNumberProgramWriter {
intptr_t end_sequence_ = false;
// Other info not stored in the state machine registers.
const char* asm_name_ = nullptr;
intptr_t label_ = 0;
};
void Dwarf::WriteSyntheticLineNumberProgram(LineNumberProgramWriter* writer) {
@ -669,14 +670,14 @@ void Dwarf::WriteSyntheticLineNumberProgram(LineNumberProgramWriter* writer) {
for (intptr_t i = 0; i < codes_.length(); i++) {
const Code& code = *(codes_[i]);
auto const asm_name = code_to_name_.LookupValue(&code);
ASSERT(asm_name != nullptr);
auto const label = code_to_label_.LookupValue(&code);
ASSERT(label > 0);
auto& comments = code.comments();
for (intptr_t i = 0, len = comments.Length(); i < len;) {
intptr_t current_pc_offset = comments.PCOffsetAt(i);
writer->EmitRow(comments_file_index, current_line,
DwarfPosition::kNoColumn, asm_name, current_pc_offset);
DwarfPosition::kNoColumn, label, current_pc_offset);
while (i < len && current_pc_offset == comments.PCOffsetAt(i)) {
comments_buffer.AddString(comments.CommentAt(i));
comments_buffer.AddChar('\n');
@ -701,8 +702,8 @@ void Dwarf::WriteLineNumberProgramFromCodeSourceMaps(
for (intptr_t i = 0; i < codes_.length(); i++) {
const Code& code = *(codes_[i]);
auto const asm_name = code_to_name_.LookupValue(&code);
ASSERT(asm_name != nullptr);
auto const label = code_to_label_.LookupValue(&code);
ASSERT(label > 0);
map = code.code_source_map();
if (map.IsNull()) {
@ -774,7 +775,7 @@ void Dwarf::WriteLineNumberProgramFromCodeSourceMaps(
}
if (should_emit) {
writer->EmitRow(file, line, column, asm_name,
writer->EmitRow(file, line, column, label,
current_pc_offset + pc_offset_adjustment);
}
@ -928,9 +929,9 @@ void Dwarf::WriteLineNumberProgram(DwarfWriteStream* stream) {
const intptr_t last_code_index = codes_.length() - 1;
const Code& last_code = *(codes_[last_code_index]);
const intptr_t last_pc_offset = last_code.Size();
const char* last_asm_name = code_to_name_.LookupValue(&last_code);
ASSERT(last_asm_name != nullptr);
lnp_writer.MarkEnd(last_asm_name, last_pc_offset);
auto const last_label = code_to_label_.LookupValue(&last_code);
ASSERT(last_label > 0);
lnp_writer.MarkEnd(last_label, last_pc_offset);
} else {
lnp_writer.MarkEnd();
}

View file

@ -211,48 +211,13 @@ class DwarfWriteStream : public ValueObject {
virtual void u8(uint64_t value) = 0;
virtual void string(const char* cstr) = 0; // NOLINT
class EncodedPosition : public ValueObject {
public:
explicit EncodedPosition(intptr_t position)
: type_(Type::kPosition), position_(position) {}
explicit EncodedPosition(const char* symbol)
: type_(Type::kSymbol), symbol_(symbol) {}
enum class Type {
kPosition,
kSymbol,
};
bool IsPosition() const { return type_ == Type::kPosition; }
intptr_t position() const {
ASSERT(IsPosition());
return position_;
}
bool IsSymbol() const { return type_ == Type::kSymbol; }
const char* symbol() const {
ASSERT(IsSymbol());
return symbol_;
}
private:
const Type type_;
union {
intptr_t position_;
const char* symbol_;
};
DISALLOW_COPY_AND_ASSIGN(EncodedPosition);
};
// Prefixes the content added by body with its length. Returns an
// appropriately encoded representation of the start of the content added by
// the body (including the length prefix).
// Prefixes the content added by body with its length.
//
// symbol_prefix is used when a local symbol is created for the length.
virtual EncodedPosition WritePrefixedLength(const char* symbol_prefix,
std::function<void()> body) = 0;
virtual void WritePrefixedLength(const char* symbol_prefix,
std::function<void()> body) = 0;
virtual void OffsetFromSymbol(const char* symbol, intptr_t offset) = 0;
virtual void OffsetFromSymbol(intptr_t label, intptr_t offset) = 0;
virtual void InitializeAbstractOrigins(intptr_t size) = 0;
virtual void RegisterAbstractOrigin(intptr_t index) = 0;
@ -268,7 +233,7 @@ class Dwarf : public ZoneAllocated {
const ZoneGrowableArray<const Code*>& codes() const { return codes_; }
// Stores the code object for later creating the line number program.
void AddCode(const Code& code, const char* name);
void AddCode(const Code& code, intptr_t label);
intptr_t AddFunction(const Function& function);
intptr_t AddScript(const Script& script);
@ -345,7 +310,7 @@ class Dwarf : public ZoneAllocated {
InliningNode* ExpandInliningTree(const Code& code);
void WriteInliningNode(DwarfWriteStream* stream,
InliningNode* node,
const char* root_code_name,
intptr_t root_label,
const Script& parent_script);
void WriteSyntheticLineNumberProgram(LineNumberProgramWriter* writer);
@ -358,12 +323,11 @@ class Dwarf : public ZoneAllocated {
Zone* const zone_;
Trie<const char>* const reverse_obfuscation_trie_;
ZoneGrowableArray<const Code*> codes_;
DwarfCodeMap<const char*> code_to_name_;
DwarfCodeMap<intptr_t> code_to_label_;
ZoneGrowableArray<const Function*> functions_;
FunctionIndexMap function_to_index_;
ZoneGrowableArray<const Script*> scripts_;
ScriptIndexMap script_to_index_;
intptr_t temp_;
};
#endif // DART_PRECOMPILER

View file

@ -456,14 +456,14 @@ class SymbolTable : public Section {
table_(table),
dynamic_(dynamic),
symbols_(zone, 1),
by_name_index_(zone) {
by_label_index_(zone) {
link = table_->index;
entry_size = sizeof(elf::Symbol);
// The first symbol table entry is reserved and must be all zeros.
// (String tables always have the empty string at the 0th index.)
ASSERT_EQUAL(table_->Lookup(""), 0);
symbols_.Add({/*name_index=*/0, elf::STB_LOCAL, elf::STT_NOTYPE, /*size=*/0,
elf::SHN_UNDEF, /*offset=*/0});
elf::SHN_UNDEF, /*offset=*/0, /*label =*/0});
// The info field on a symbol table section holds the index of the first
// non-local symbol, so since there are none yet, it points past the single
// symbol we do have.
@ -505,6 +505,9 @@ class SymbolTable : public Section {
// Initialized to the section-relative offset, must be updated to the
// snapshot-relative offset before writing.
intptr_t offset;
// Only used within the VM and not written as part of the ELF file. If 0,
// this symbol cannot be looked up via label.
intptr_t label;
private:
DISALLOW_ALLOCATION();
@ -527,16 +530,18 @@ class SymbolTable : public Section {
intptr_t type,
intptr_t size,
intptr_t index,
intptr_t offset) {
intptr_t offset,
intptr_t label) {
ASSERT(label > 0);
ASSERT(!table_->HasBeenFinalized());
auto const name_index = table_->Add(name);
ASSERT(name_index != 0);
const intptr_t new_index = symbols_.length();
symbols_.Add({name_index, binding, type, size, index, offset});
by_name_index_.Insert(name_index, new_index);
symbols_.Add({name_index, binding, type, size, index, offset, label});
by_label_index_.Insert(label, new_index);
// The info field on a symbol table section holds the index of the first
// non-local symbol, so they can be skipped if desired. Thus, we need to
// make sure local symbols are before any non-local ones.
// non-local symbol, so that local symbols can be skipped if desired. Thus,
// we need to make sure local symbols are before any non-local ones.
if (binding == elf::STB_LOCAL) {
if (info != new_index) {
// There are non-local symbols, as otherwise [info] would be the
@ -545,9 +550,9 @@ class SymbolTable : public Section {
// [info] is incremented it will point just past the new local symbol.
ASSERT(symbols_[info].binding != elf::STB_LOCAL);
symbols_.Swap(info, new_index);
// Since by_name_index has indices into symbols_, we need to update it.
by_name_index_.Update({symbols_[info].name_index, info});
by_name_index_.Update({symbols_[new_index].name_index, new_index});
// Since by_label_index has indices into symbols_, we need to update it.
by_label_index_.Update({symbols_[info].label, info});
by_label_index_.Update({symbols_[new_index].label, new_index});
}
info += 1;
}
@ -587,13 +592,9 @@ class SymbolTable : public Section {
}
}
const Symbol* Find(const char* name) const {
ASSERT(name != nullptr);
const intptr_t name_index = table_->Lookup(name);
// 0 is kNoValue for by_name_index, but luckily that's the name of the
// initial reserved symbol.
if (name_index == 0) return &symbols_[0];
const intptr_t symbols_index = by_name_index_.Lookup(name_index);
const Symbol* FindUid(intptr_t label) const {
ASSERT(label > 0);
const intptr_t symbols_index = by_label_index_.Lookup(label);
if (symbols_index == 0) return nullptr; // Not found.
return &symbols_[symbols_index];
}
@ -603,9 +604,9 @@ class SymbolTable : public Section {
StringTable* const table_;
const bool dynamic_;
GrowableArray<Symbol> symbols_;
// Maps name indexes in table_ to indexes in symbols_. Does not include an
// entry for the reserved symbol (name ""), as 0 is kNoValue.
IntMap<intptr_t> by_name_index_;
// Maps positive symbol labels to indexes in symbols_. No entry for the
// reserved symbol, which has index 0, the same as the IntMap's kNoValue.
IntMap<intptr_t> by_label_index_;
};
class SymbolHashTable : public Section {
@ -810,21 +811,23 @@ class BitsContainer : public Section {
reloc.section_offset - current_pos);
}
intptr_t source_address = reloc.source_offset;
if (reloc.source_symbol != nullptr) {
auto* const source_symbol = symtab.Find(reloc.source_symbol);
if (reloc.source_label > 0) {
auto* const source_symbol = symtab.FindUid(reloc.source_label);
ASSERT(source_symbol != nullptr);
source_address += source_symbol->offset;
} else {
} else if (reloc.source_label == Elf::Relocation::kSelfRelative) {
source_address += section_start + offset + reloc.section_offset;
} else {
ASSERT_EQUAL(reloc.source_label, Elf::Relocation::kSnapshotRelative);
// No change to source_address.
}
ASSERT(reloc.size_in_bytes <= kWordSize);
word to_write = reloc.target_offset - source_address;
if (reloc.target_symbol != nullptr) {
if (auto* const symbol = symtab.Find(reloc.target_symbol)) {
to_write += symbol->offset;
if (reloc.target_label > 0) {
if (auto* const target_symbol = symtab.FindUid(reloc.target_label)) {
to_write += target_symbol->offset;
} else {
ASSERT_EQUAL(strcmp(reloc.target_symbol, kSnapshotBuildIdAsmSymbol),
0);
ASSERT_EQUAL(reloc.target_label, Elf::kBuildIdLabel);
ASSERT_EQUAL(reloc.target_offset, 0);
ASSERT_EQUAL(reloc.source_offset, 0);
ASSERT_EQUAL(reloc.size_in_bytes, compiler::target::kWordSize);
@ -833,8 +836,11 @@ class BitsContainer : public Section {
// InstructionsSection when there is no build ID.
to_write = Image::kNoRelocatedAddress;
}
} else {
} else if (reloc.target_label == Elf::Relocation::kSelfRelative) {
to_write += section_start + offset + reloc.section_offset;
} else {
ASSERT_EQUAL(reloc.target_label, Elf::Relocation::kSnapshotRelative);
// No change to source_address.
}
ASSERT(Utils::IsInt(reloc.size_in_bytes * kBitsPerByte, to_write));
stream->WriteBytes(reinterpret_cast<const uint8_t*>(&to_write),
@ -846,6 +852,7 @@ class BitsContainer : public Section {
intptr_t offset;
const char* symbol_name;
intptr_t label;
const uint8_t* bytes;
intptr_t size;
const ZoneGrowableArray<Elf::Relocation>* relocations;
@ -862,13 +869,17 @@ class BitsContainer : public Section {
intptr_t size,
const ZoneGrowableArray<Elf::Relocation>* relocations = nullptr,
const ZoneGrowableArray<Elf::SymbolData>* symbols = nullptr,
const char* symbol_name = nullptr) {
const char* symbol_name = nullptr,
intptr_t label = 0) {
// Any named portion should also have a valid symbol label.
ASSERT(symbol_name == nullptr || label > 0);
ASSERT(IsNoBits() || bytes != nullptr);
ASSERT(bytes != nullptr || relocations == nullptr);
// Make sure all portions are consistent in containing bytes.
ASSERT(portions_.is_empty() || HasBytes() == (bytes != nullptr));
const intptr_t offset = Utils::RoundUp(total_size_, alignment);
portions_.Add({offset, symbol_name, bytes, size, relocations, symbols});
portions_.Add(
{offset, symbol_name, label, bytes, size, relocations, symbols});
const Portion& portion = portions_.Last();
total_size_ = offset + size;
return portion;
@ -933,7 +944,7 @@ class ConcatenableBitsContainer : public BitsContainer {
ASSERT(CanMergeWith(other));
for (const auto& portion : other.AsBitsContainer()->portions()) {
AddPortion(portion.bytes, portion.size, portion.relocations,
portion.symbols, portion.symbol_name);
portion.symbols, portion.symbol_name, portion.label);
}
}
};
@ -1149,12 +1160,13 @@ Elf::Elf(Zone* zone, BaseWriteStream* stream, Type type, Dwarf* dwarf)
}
void Elf::AddText(const char* name,
intptr_t label,
const uint8_t* bytes,
intptr_t size,
const ZoneGrowableArray<Relocation>* relocations,
const ZoneGrowableArray<SymbolData>* symbols) {
auto* const container = new (zone_) TextSection(type_);
container->AddPortion(bytes, size, relocations, symbols, name);
container->AddPortion(bytes, size, relocations, symbols, name, label);
section_table_->Add(container, kTextName);
}
@ -1170,14 +1182,17 @@ void Elf::CreateBSS() {
for (const auto& portion : text_section->AsBitsContainer()->portions()) {
size_t size;
const char* symbol_name;
intptr_t label;
// First determine whether this is the VM's text portion or the isolate's.
if (strcmp(portion.symbol_name, kVmSnapshotInstructionsAsmSymbol) == 0) {
size = BSS::kVmEntryCount * compiler::target::kWordSize;
symbol_name = kVmSnapshotBssAsmSymbol;
label = kVmBssLabel;
} else if (strcmp(portion.symbol_name,
kIsolateSnapshotInstructionsAsmSymbol) == 0) {
size = BSS::kIsolateEntryCount * compiler::target::kWordSize;
symbol_name = kIsolateSnapshotBssAsmSymbol;
label = kIsolateBssLabel;
} else {
// Not VM or isolate text.
UNREACHABLE();
@ -1198,7 +1213,7 @@ void Elf::CreateBSS() {
// static symbol table, as these addresses are only used for relocation.
// (This matches the behavior in the assembly output.)
auto* symbols = new (zone_) ZoneGrowableArray<Elf::SymbolData>();
symbols->Add({symbol_name, elf::STT_SECTION, 0, size});
symbols->Add({symbol_name, elf::STT_SECTION, 0, size, label});
bss_container->AddPortion(bytes, size, /*relocations=*/nullptr, symbols);
}
@ -1206,12 +1221,13 @@ void Elf::CreateBSS() {
}
void Elf::AddROData(const char* name,
intptr_t label,
const uint8_t* bytes,
intptr_t size,
const ZoneGrowableArray<Relocation>* relocations,
const ZoneGrowableArray<SymbolData>* symbols) {
auto* const container = new (zone_) DataSection(type_);
container->AddPortion(bytes, size, relocations, symbols, name);
container->AddPortion(bytes, size, relocations, symbols, name, label);
section_table_->Add(container, kDataName);
}
@ -1238,8 +1254,7 @@ class DwarfElfStream : public DwarfWriteStream {
stream_->WriteBytes(cstr, strlen(cstr) + 1);
}
// The prefix is ignored for DwarfElfStreams.
EncodedPosition WritePrefixedLength(const char* symbol_prefix,
std::function<void()> body) {
void WritePrefixedLength(const char* unused, std::function<void()> body) {
const intptr_t fixup = stream_->Position();
// We assume DWARF v2 currently, so all sizes are 32-bit.
u4(0);
@ -1251,22 +1266,21 @@ class DwarfElfStream : public DwarfWriteStream {
stream_->SetPosition(fixup);
u4(end - start);
stream_->SetPosition(end);
return EncodedPosition(fixup);
}
// Shorthand for when working directly with DwarfElfStreams.
intptr_t WritePrefixedLength(std::function<void()> body) {
const EncodedPosition& pos = WritePrefixedLength(nullptr, body);
return pos.position();
void WritePrefixedLength(std::function<void()> body) {
WritePrefixedLength(nullptr, body);
}
void OffsetFromSymbol(const char* symbol, intptr_t offset) {
relocations_->Add(
{kAddressSize, stream_->Position(), "", 0, symbol, offset});
void OffsetFromSymbol(intptr_t label, intptr_t offset) {
relocations_->Add({kAddressSize, stream_->Position(),
Elf::Relocation::kSnapshotRelative, 0, label, offset});
addr(0); // Resolved later.
}
template <typename T>
void RelativeSymbolOffset(const char* symbol) {
relocations_->Add({sizeof(T), stream_->Position(), nullptr, 0, symbol, 0});
void RelativeSymbolOffset(intptr_t label) {
relocations_->Add({sizeof(T), stream_->Position(),
Elf::Relocation::kSelfRelative, 0, label, 0});
stream_->WriteFixed<T>(0); // Resolved later.
}
void InitializeAbstractOrigins(intptr_t size) {
@ -1327,14 +1341,14 @@ void SymbolTable::Initialize(const GrowableArray<Section*>& sections) {
// dynamic symbol table when it exists and only use it, so put all
// dynamic symbols there also. (see dartbug.com/41783).
AddSymbol(portion.symbol_name, binding, type, portion.size,
section->index, portion.offset);
section->index, portion.offset, portion.label);
}
if (!dynamic_ && portion.symbols != nullptr) {
for (const auto& symbol_data : *portion.symbols) {
// Local static-only symbols, e.g., code payloads or RO objects.
AddSymbol(symbol_data.name, elf::STB_LOCAL, symbol_data.type,
symbol_data.size, section->index,
portion.offset + symbol_data.offset);
portion.offset + symbol_data.offset, symbol_data.label);
}
}
}
@ -1408,7 +1422,8 @@ void Elf::FinalizeEhFrame() {
// Emit CIE.
// Used to calculate offset to CIE in FDEs.
const intptr_t cie_start = dwarf_stream.WritePrefixedLength([&] {
const intptr_t cie_start = dwarf_stream.Position();
dwarf_stream.WritePrefixedLength([&] {
dwarf_stream.u4(0); // CIE
dwarf_stream.u1(1); // Version (must be 1 or 3)
// Augmentation String
@ -1434,13 +1449,13 @@ void Elf::FinalizeEhFrame() {
// Emit an FDE covering each .text section.
for (const auto& portion : text_section->portions()) {
ASSERT(portion.symbol_name != nullptr); // Needed for relocations.
ASSERT(portion.label != 0); // Needed for relocations.
dwarf_stream.WritePrefixedLength([&]() {
// Offset to CIE. Note that unlike pcrel this offset is encoded
// backwards: it will be subtracted from the current position.
dwarf_stream.u4(stream.Position() - cie_start);
// Start address as a PC relative reference.
dwarf_stream.RelativeSymbolOffset<int32_t>(portion.symbol_name);
dwarf_stream.RelativeSymbolOffset<int32_t>(portion.label);
dwarf_stream.u4(portion.size); // Size.
dwarf_stream.u1(0); // Augmentation Data length.
@ -1818,7 +1833,7 @@ void Elf::GenerateBuildId() {
auto* const container = new (zone_) NoteSection();
container->AddPortion(stream.buffer(), stream.bytes_written(),
/*relocations=*/nullptr, /*symbols=*/nullptr,
kSnapshotBuildIdAsmSymbol);
kSnapshotBuildIdAsmSymbol, kBuildIdLabel);
section_table_->Add(container, kBuildIdNoteName);
}

View file

@ -64,17 +64,39 @@ class Elf : public ZoneAllocated {
// Stores the information needed to appropriately generate a
// relocation from the target to the source at the given section offset.
// If a given symbol name is nullptr, then the corresponding offset is
// relative from the location of the relocation itself.
// If a given symbol name is "", then the corresponding offset is relative to
// the start of the snapshot.
struct Relocation {
size_t size_in_bytes;
intptr_t section_offset;
const char* source_symbol;
intptr_t source_label;
intptr_t source_offset;
const char* target_symbol;
intptr_t target_label;
intptr_t target_offset;
// Used when the corresponding offset is relative from the location of the
// relocation itself.
static constexpr intptr_t kSelfRelative = -1;
// Used when the corresponding offset is relative to the start of the
// snapshot.
static constexpr intptr_t kSnapshotRelative = -2;
Relocation(size_t size_in_bytes,
intptr_t section_offset,
intptr_t source_label,
intptr_t source_offset,
intptr_t target_label,
intptr_t target_offset)
: size_in_bytes(size_in_bytes),
section_offset(section_offset),
source_label(source_label),
source_offset(source_offset),
target_label(target_label),
target_offset(target_offset) {
// Other than special values, all labels should be positive.
ASSERT(source_label > 0 || source_label == kSelfRelative ||
source_label == kSnapshotRelative);
ASSERT(target_label > 0 || target_label == kSelfRelative ||
target_label == kSnapshotRelative);
}
};
// Stores the information needed to appropriately generate a symbol
@ -84,14 +106,34 @@ class Elf : public ZoneAllocated {
intptr_t type;
intptr_t offset;
size_t size;
// A positive unique ID only used internally in the Dart VM, not part of
// the Elf output.
intptr_t label;
SymbolData(const char* name,
intptr_t type,
intptr_t offset,
size_t size,
intptr_t label)
: name(name), type(type), offset(offset), size(size), label(label) {
ASSERT(label > 0);
}
};
// Must be the same value as the values returned by ImageWriter::SectionLabel
// for the appropriate section and vm values.
static constexpr intptr_t kVmBssLabel = 5;
static constexpr intptr_t kIsolateBssLabel = 6;
static constexpr intptr_t kBuildIdLabel = 7;
void AddText(const char* name,
intptr_t label,
const uint8_t* bytes,
intptr_t size,
const ZoneGrowableArray<Relocation>* relocations,
const ZoneGrowableArray<SymbolData>* symbol);
void AddROData(const char* name,
intptr_t label,
const uint8_t* bytes,
intptr_t size,
const ZoneGrowableArray<Relocation>* relocations,

View file

@ -172,7 +172,7 @@ bool ObjectOffsetTrait::IsKeyEqual(Pair pair, Key key) {
}
#if !defined(DART_PRECOMPILED_RUNTIME)
ImageWriter::ImageWriter(Thread* t)
ImageWriter::ImageWriter(Thread* t, bool generates_assembly)
: thread_(ASSERT_NOTNULL(t)),
zone_(t->zone()),
next_data_offset_(0),
@ -180,7 +180,7 @@ ImageWriter::ImageWriter(Thread* t)
objects_(),
instructions_(),
#if defined(DART_PRECOMPILER)
namer_(t->zone()),
namer_(t->zone(), /*for_assembly=*/generates_assembly),
#endif
image_type_(TagObjectTypeAsReadOnly(zone_, "Image")),
instructions_section_type_(
@ -628,7 +628,7 @@ uword ImageWriter::GetMarkedTags(const Object& obj) {
return tags;
}
const char* ImageWriter::SectionSymbol(ProgramSection section, bool vm) const {
const char* ImageWriter::SectionSymbol(ProgramSection section, bool vm) {
switch (section) {
case ProgramSection::Text:
return vm ? kVmSnapshotInstructionsAsmSymbol
@ -716,8 +716,12 @@ void ImageWriter::WriteText(bool vm) {
#if defined(DART_PRECOMPILER)
const char* instructions_symbol = SectionSymbol(ProgramSection::Text, vm);
ASSERT(instructions_symbol != nullptr);
intptr_t instructions_label = SectionLabel(ProgramSection::Text, vm);
ASSERT(instructions_label > 0);
const char* bss_symbol = SectionSymbol(ProgramSection::Bss, vm);
ASSERT(bss_symbol != nullptr);
intptr_t bss_label = SectionLabel(ProgramSection::Bss, vm);
ASSERT(bss_label > 0);
if (profile_writer_ != nullptr) {
profile_writer_->SetObjectTypeAndName(parent_id, image_type_,
@ -765,12 +769,12 @@ void ImageWriter::WriteText(bool vm) {
// 1) The length of the payload.
text_offset += WriteTargetWord(section_payload_length);
// 2) The BSS offset from this section.
text_offset += Relocation(text_offset, instructions_symbol, bss_symbol);
text_offset += Relocation(text_offset, instructions_label, bss_label);
// 3) The relocated address of the instructions.
text_offset += RelocatedAddress(text_offset, instructions_symbol);
text_offset += RelocatedAddress(text_offset, instructions_label);
// 4) The GNU build ID note offset from this section.
text_offset += Relocation(text_offset, instructions_symbol,
SectionSymbol(ProgramSection::BuildId, vm));
text_offset += Relocation(text_offset, instructions_label,
SectionLabel(ProgramSection::BuildId, vm));
const intptr_t section_contents_alignment =
bare_instruction_payloads
@ -892,8 +896,8 @@ void ImageWriter::WriteText(bool vm) {
const auto target_offset =
*reinterpret_cast<const compiler::target::word*>(
next_reloc_address);
text_offset += Relocation(text_offset, instructions_symbol, text_offset,
bss_symbol, target_offset);
text_offset += Relocation(text_offset, instructions_label, text_offset,
bss_label, target_offset);
cursor = next_reloc_address + compiler::target::kWordSize;
}
#endif
@ -975,8 +979,12 @@ static constexpr const char* kWordDirective =
class DwarfAssemblyStream : public DwarfWriteStream {
public:
explicit DwarfAssemblyStream(Zone* zone, BaseWriteStream* stream)
: zone_(ASSERT_NOTNULL(zone)), stream_(ASSERT_NOTNULL(stream)) {}
explicit DwarfAssemblyStream(Zone* zone,
BaseWriteStream* stream,
const IntMap<const char*>& label_to_name)
: zone_(ASSERT_NOTNULL(zone)),
stream_(ASSERT_NOTNULL(stream)),
label_to_name_(label_to_name) {}
void sleb128(intptr_t value) { stream_->Printf(".sleb128 %" Pd "\n", value); }
void uleb128(uintptr_t value) {
@ -997,8 +1005,7 @@ class DwarfAssemblyStream : public DwarfWriteStream {
void string(const char* cstr) { // NOLINT
stream_->Printf(".string \"%s\"\n", cstr); // NOLINT
}
EncodedPosition WritePrefixedLength(const char* prefix,
std::function<void()> body) {
void WritePrefixedLength(const char* prefix, std::function<void()> body) {
ASSERT(prefix != nullptr);
const char* const length_prefix_symbol =
OS::SCreate(zone_, ".L%s_length_prefix", prefix);
@ -1013,9 +1020,10 @@ class DwarfAssemblyStream : public DwarfWriteStream {
stream_->Printf(".L%s_start:\n", prefix);
body();
stream_->Printf(".L%s_end:\n", prefix);
return EncodedPosition(length_prefix_symbol);
}
void OffsetFromSymbol(const char* symbol, intptr_t offset) {
void OffsetFromSymbol(intptr_t label, intptr_t offset) {
const char* symbol = label_to_name_.Lookup(label);
ASSERT(symbol != nullptr);
if (offset == 0) {
PrintNamedAddress(symbol);
} else {
@ -1076,14 +1084,15 @@ class DwarfAssemblyStream : public DwarfWriteStream {
static constexpr const char* kDebugInfoLabel = ".Ldebug_info";
void PrintNamedAddress(const char* name) {
stream_->Printf("%s %s\n", kWordDirective, name);
stream_->Printf("%s \"%s\"\n", kWordDirective, name);
}
void PrintNamedAddressWithOffset(const char* name, intptr_t offset) {
stream_->Printf("%s %s + %" Pd "\n", kWordDirective, name, offset);
stream_->Printf("%s \"%s\" + %" Pd "\n", kWordDirective, name, offset);
}
Zone* const zone_;
BaseWriteStream* const stream_;
const IntMap<const char*>& label_to_name_;
intptr_t temp_ = 0;
DISALLOW_COPY_AND_ASSIGN(DwarfAssemblyStream);
@ -1105,14 +1114,34 @@ AssemblyImageWriter::AssemblyImageWriter(Thread* thread,
BaseWriteStream* stream,
bool strip,
Elf* debug_elf)
: ImageWriter(thread),
: ImageWriter(thread, /*generates_assembly=*/true),
assembly_stream_(stream),
assembly_dwarf_(AddDwarfIfUnstripped(zone_, strip, debug_elf)),
debug_elf_(debug_elf) {}
debug_elf_(debug_elf),
label_to_symbol_name_(zone_) {
// Set up the label mappings for the section symbols for use in relocations.
for (intptr_t i = 0; i < kNumProgramSections; i++) {
auto const section = static_cast<ProgramSection>(i);
auto const vm_name = SectionSymbol(section, /*vm=*/true);
auto const vm_label = SectionLabel(section, /*vm=*/true);
label_to_symbol_name_.Insert(vm_label, vm_name);
auto const isolate_name = SectionSymbol(section, /*vm=*/false);
auto const isolate_label = SectionLabel(section, /*vm=*/false);
if (vm_label != isolate_label) {
label_to_symbol_name_.Insert(isolate_label, isolate_name);
} else {
// Make sure the names also match.
ASSERT_EQUAL(strcmp(vm_name, isolate_name), 0);
}
}
}
void AssemblyImageWriter::Finalize() {
if (assembly_dwarf_ != nullptr) {
DwarfAssemblyStream dwarf_stream(zone_, assembly_stream_);
DwarfAssemblyStream dwarf_stream(zone_, assembly_stream_,
label_to_symbol_name_);
dwarf_stream.AbbreviationsPrologue();
assembly_dwarf_->WriteAbbreviations(&dwarf_stream);
dwarf_stream.DebugInfoPrologue();
@ -1131,88 +1160,37 @@ void AssemblyImageWriter::Finalize() {
#endif
}
static void AddAssemblerIdentifier(BaseTextBuffer* printer, const char* label) {
ASSERT(label[0] != '.');
if (label[0] == 'L' && printer->length() == 0) {
// Assembler treats labels starting with `L` as local which can cause
// some issues down the line e.g. on Mac the linker might fail to encode
// compact unwind information because multiple functions end up being
// treated as a single function. See https://github.com/flutter/flutter/issues/102281.
//
// Avoid this by prepending an underscore.
printer->AddString("_");
}
for (char c = *label; c != '\0'; c = *++label) {
#define OP(dart_name, asm_name) \
if (strncmp(label, dart_name, strlen(dart_name)) == 0) { \
printer->AddString(asm_name); \
label += (strlen(dart_name) - 1); \
continue; \
}
OP("+", "operator_add")
OP("-", "operator_sub")
OP("*", "operator_mul")
OP("/", "operator_div")
OP("~/", "operator_truncdiv")
OP("%", "operator_mod")
OP("~", "operator_not")
OP("&", "operator_and")
OP("|", "operator_or")
OP("^", "operator_xor")
OP("<<", "operator_sll")
OP(">>>", "operator_srl")
OP(">>", "operator_sra")
OP("[]=", "operator_set")
OP("[]", "operator_get")
OP("unary-", "operator_neg")
OP("==", "operator_eq")
OP("<anonymous closure>", "anonymous_closure")
OP("<=", "operator_le")
OP("<", "operator_lt")
OP(">=", "operator_ge")
OP(">", "operator_gt")
#undef OP
if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) ||
((c >= '0') && (c <= '9')) || (c == '.')) {
printer->AddChar(c);
continue;
}
printer->AddChar('_');
}
}
void ImageWriter::SnapshotTextObjectNamer::AddNonUniqueNameFor(
BaseTextBuffer* buffer,
const Object& object) {
if (object.IsCode()) {
const Code& code = Code::Cast(object);
owner_ = WeakSerializationReference::Unwrap(code.owner());
if (owner_.IsNull()) {
buffer->AddString("Stub_");
if (code.IsStubCode()) {
buffer->AddString("stub ");
insns_ = code.instructions();
const char* name = StubCode::NameOfStub(insns_.EntryPoint());
ASSERT(name != nullptr);
buffer->AddString(name);
} else {
if (owner_.IsClass()) {
buffer->AddString("AllocationStub_");
} else if (!owner_.IsAbstractType() && !owner_.IsFunction()) {
// Double-check that we didn't get an invalid owner for the Code object.
UNREACHABLE();
if (code.IsAllocationStubCode()) {
buffer->AddString("new ");
} else if (code.IsTypeTestStubCode()) {
buffer->AddString("assert type is ");
} else {
ASSERT(code.IsFunctionCode());
}
owner_ = code.owner();
AddNonUniqueNameFor(buffer, owner_);
}
} else if (object.IsClass()) {
const char* name = Class::Cast(object).ScrubbedNameCString();
AddAssemblerIdentifier(buffer, name);
const char* name = Class::Cast(object).UserVisibleNameCString();
buffer->AddString(name);
} else if (object.IsAbstractType()) {
namer_.WriteStubNameForTypeTo(buffer, AbstractType::Cast(object));
AbstractType::Cast(object).PrintName(Object::kUserVisibleName, buffer);
} else if (object.IsFunction()) {
const char* name = Function::Cast(object).QualifiedScrubbedNameCString();
AddAssemblerIdentifier(buffer, name);
const Function& func = Function::Cast(object);
func.PrintName({Object::kUserVisibleName, Object::NameDisambiguation::kNo},
buffer);
} else if (object.IsCompressedStackMaps()) {
buffer->AddString("CompressedStackMaps");
} else if (object.IsPcDescriptors()) {
@ -1231,6 +1209,27 @@ void ImageWriter::SnapshotTextObjectNamer::AddNonUniqueNameFor(
}
}
void ImageWriter::SnapshotTextObjectNamer::ModifyForAssembly(
BaseTextBuffer* buffer) {
if (buffer->buffer()[0] == 'L') {
// Assembler treats labels starting with `L` as local which can cause
// some issues down the line e.g. on Mac the linker might fail to encode
// compact unwind information because multiple functions end up being
// treated as a single function. See https://github.com/flutter/flutter/issues/102281.
//
// Avoid this by prepending an underscore.
auto* const result = OS::SCreate(zone_, "_%s", buffer->buffer());
buffer->Clear();
buffer->AddString(result);
}
auto* const pair = usage_count_.Lookup(buffer->buffer());
if (pair == nullptr) {
usage_count_.Insert({buffer->buffer(), 1});
} else {
buffer->Printf(" (#%" Pd ")", ++pair->value);
}
}
const char* ImageWriter::SnapshotTextObjectNamer::SnapshotNameFor(
const InstructionsData& data) {
ZoneTextBuffer printer(zone_);
@ -1239,7 +1238,9 @@ const char* ImageWriter::SnapshotTextObjectNamer::SnapshotNameFor(
} else {
AddNonUniqueNameFor(&printer, *data.code_);
}
printer.Printf("_%" Pd, nonce_++);
if (for_assembly_) {
ModifyForAssembly(&printer);
}
return printer.buffer();
}
@ -1257,15 +1258,18 @@ const char* ImageWriter::SnapshotTextObjectNamer::SnapshotNameFor(
if (!obj.IsString()) {
const Object& parent = *data.parent;
if (!parent.IsNull()) {
printer.AddString("_");
printer.AddString(" (");
AddNonUniqueNameFor(&printer, parent);
printer.AddString(")");
}
}
#endif
} else {
printer.AddString("RawBytes");
}
printer.Printf("_%" Pd, nonce_++);
if (for_assembly_) {
ModifyForAssembly(&printer);
}
return printer.buffer();
}
@ -1306,12 +1310,12 @@ void AssemblyImageWriter::WriteROData(NonStreamingWriteStream* clustered_stream,
intptr_t last_position = 0;
for (const auto& symbol : *current_symbols_) {
WriteBytes(bytes + last_position, symbol.offset - last_position);
assembly_stream_->Printf("%s:\n", symbol.name);
assembly_stream_->Printf("\"%s\":\n", symbol.name);
#if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
defined(DART_TARGET_OS_FUCHSIA)
// Output size and type of the read-only data symbol to the assembly stream.
assembly_stream_->Printf(".size %s, %zu\n", symbol.name, symbol.size);
assembly_stream_->Printf(".type %s, %%object\n", symbol.name);
assembly_stream_->Printf(".size \"%s\", %zu\n", symbol.name, symbol.size);
assembly_stream_->Printf(".type \"%s\", %%object\n", symbol.name);
#elif defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
// MachO symbol tables don't include the size of the symbol, so don't bother
// printing it to the assembly output.
@ -1329,7 +1333,6 @@ bool AssemblyImageWriter::EnterSection(ProgramSection section,
intptr_t alignment,
intptr_t* alignment_padding) {
ASSERT(FLAG_precompiled_mode);
ASSERT(current_section_symbol_ == nullptr);
ASSERT(current_symbols_ == nullptr);
bool global_symbol = false;
switch (section) {
@ -1366,16 +1369,16 @@ bool AssemblyImageWriter::EnterSection(ProgramSection section,
case ProgramSection::BuildId:
break;
}
current_section_symbol_ = SectionSymbol(section, vm);
ASSERT(current_section_symbol_ != nullptr);
current_section_label_ = SectionLabel(section, vm);
ASSERT(current_section_label_ > 0);
if (global_symbol) {
assembly_stream_->Printf(".globl %s\n", current_section_symbol_);
assembly_stream_->Printf(".globl %s\n", SectionSymbol(section, vm));
}
intptr_t padding = Align(alignment, 0, 0);
if (alignment_padding != nullptr) {
*alignment_padding = padding;
}
assembly_stream_->Printf("%s:\n", current_section_symbol_);
assembly_stream_->Printf("%s:\n", SectionSymbol(section, vm));
return true;
}
@ -1383,6 +1386,7 @@ static void ElfAddSection(
Elf* elf,
ImageWriter::ProgramSection section,
const char* symbol,
intptr_t label,
uint8_t* bytes,
intptr_t size,
ZoneGrowableArray<Elf::SymbolData>* symbols,
@ -1390,10 +1394,10 @@ static void ElfAddSection(
if (elf == nullptr) return;
switch (section) {
case ImageWriter::ProgramSection::Text:
elf->AddText(symbol, bytes, size, relocations, symbols);
elf->AddText(symbol, label, bytes, size, relocations, symbols);
break;
case ImageWriter::ProgramSection::Data:
elf->AddROData(symbol, bytes, size, relocations, symbols);
elf->AddROData(symbol, label, bytes, size, relocations, symbols);
break;
default:
// Other sections are handled by the Elf object internally.
@ -1405,13 +1409,12 @@ void AssemblyImageWriter::ExitSection(ProgramSection name,
bool vm,
intptr_t size) {
// We should still be in the same section as the last EnterSection.
ASSERT(current_section_symbol_ != nullptr);
ASSERT_EQUAL(strcmp(SectionSymbol(name, vm), current_section_symbol_), 0);
ASSERT_EQUAL(current_section_label_, SectionLabel(name, vm));
#if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
defined(DART_TARGET_OS_FUCHSIA)
// Output the size of the section symbol to the assembly stream.
assembly_stream_->Printf(".size %s, %zu\n", current_section_symbol_, size);
assembly_stream_->Printf(".type %s, %%object\n", current_section_symbol_);
assembly_stream_->Printf(".size %s, %zu\n", SectionSymbol(name, vm), size);
assembly_stream_->Printf(".type %s, %%object\n", SectionSymbol(name, vm));
#elif defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
// MachO symbol tables don't include the size of the symbol, so don't bother
// printing it to the assembly output.
@ -1433,9 +1436,10 @@ void AssemblyImageWriter::ExitSection(ProgramSection name,
// Since we don't want to add the actual contents of the segment in the
// separate debugging information, we pass nullptr for the bytes, which
// creates an appropriate NOBITS section instead of PROGBITS.
ElfAddSection(debug_elf_, name, current_section_symbol_, /*bytes=*/nullptr,
size, current_symbols_);
current_section_symbol_ = nullptr;
ElfAddSection(debug_elf_, name, SectionSymbol(name, vm),
current_section_label_, /*bytes=*/nullptr, size,
current_symbols_);
current_section_label_ = 0;
current_symbols_ = nullptr;
}
@ -1448,37 +1452,36 @@ intptr_t AssemblyImageWriter::WriteTargetWord(word value) {
}
intptr_t AssemblyImageWriter::Relocation(intptr_t section_offset,
const char* source_symbol,
intptr_t source_label,
intptr_t source_offset,
const char* target_symbol,
intptr_t target_label,
intptr_t target_offset) {
ASSERT(source_symbol != nullptr);
ASSERT(target_symbol != nullptr);
// TODO(dartbug.com/43274): Remove once we generate consistent build IDs
// between assembly snapshots and their debugging information.
const char* build_id_symbol =
SectionSymbol(ProgramSection::BuildId, /*vm=*/false);
if (strcmp(target_symbol, build_id_symbol) == 0) {
if (target_label == SectionLabel(ProgramSection::BuildId, /*vm=*/false)) {
return WriteTargetWord(Image::kNoBuildId);
}
// All relocations are word-sized.
assembly_stream_->Printf("%s ", kWordDirective);
if (strcmp(target_symbol, current_section_symbol_) == 0) {
if (target_label == current_section_label_) {
assembly_stream_->WriteString("(.)");
target_offset -= section_offset;
} else {
const char* target_symbol = label_to_symbol_name_.Lookup(target_label);
ASSERT(target_symbol != nullptr);
assembly_stream_->Printf("%s", target_symbol);
}
if (target_offset != 0) {
assembly_stream_->Printf(" + %" Pd "", target_offset);
}
if (strcmp(source_symbol, current_section_symbol_) == 0) {
if (source_label == current_section_label_) {
assembly_stream_->WriteString(" - (.)");
source_offset -= section_offset;
} else {
const char* source_symbol = label_to_symbol_name_.Lookup(source_label);
ASSERT(source_symbol != nullptr);
assembly_stream_->Printf(" - %s", source_symbol);
}
if (source_offset != 0) {
@ -1491,19 +1494,21 @@ intptr_t AssemblyImageWriter::Relocation(intptr_t section_offset,
void AssemblyImageWriter::AddCodeSymbol(const Code& code,
const char* symbol,
intptr_t offset) {
auto const label = next_label_++;
label_to_symbol_name_.Insert(label, symbol);
if (assembly_dwarf_ != nullptr) {
assembly_dwarf_->AddCode(code, symbol);
assembly_dwarf_->AddCode(code, label);
}
if (debug_elf_ != nullptr) {
current_symbols_->Add({symbol, elf::STT_FUNC, offset, code.Size()});
debug_elf_->dwarf()->AddCode(code, symbol);
current_symbols_->Add({symbol, elf::STT_FUNC, offset, code.Size(), label});
debug_elf_->dwarf()->AddCode(code, label);
}
assembly_stream_->Printf("%s:\n", symbol);
assembly_stream_->Printf("\"%s\":\n", symbol);
#if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \
defined(DART_TARGET_OS_FUCHSIA)
// Output the size of the code symbol to the assembly stream.
assembly_stream_->Printf(".size %s, %zu\n", symbol, code.Size());
assembly_stream_->Printf(".type %s, %%function\n", symbol);
assembly_stream_->Printf(".size \"%s\", %zu\n", symbol, code.Size());
assembly_stream_->Printf(".type \"%s\", %%function\n", symbol);
#elif defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
// MachO symbol tables don't include the size of the symbol, so don't bother
// printing it to the assembly output.
@ -1516,7 +1521,9 @@ void AssemblyImageWriter::AddDataSymbol(const char* symbol,
intptr_t offset,
size_t size) {
if (!FLAG_add_readonly_data_symbols) return;
current_symbols_->Add({symbol, elf::STT_OBJECT, offset, size});
auto const label = next_label_++;
label_to_symbol_name_.Insert(label, symbol);
current_symbols_->Add({symbol, elf::STT_OBJECT, offset, size, label});
}
void AssemblyImageWriter::FrameUnwindPrologue() {
@ -1618,7 +1625,7 @@ BlobImageWriter::BlobImageWriter(Thread* thread,
NonStreamingWriteStream* isolate_instructions,
Elf* debug_elf,
Elf* elf)
: ImageWriter(thread),
: ImageWriter(thread, /*generates_assembly=*/false),
vm_instructions_(vm_instructions),
isolate_instructions_(isolate_instructions),
elf_(elf),
@ -1674,7 +1681,6 @@ bool BlobImageWriter::EnterSection(ProgramSection section,
ASSERT(current_symbols_ == nullptr);
#endif
ASSERT(section == ProgramSection::Data || current_section_stream_ == nullptr);
ASSERT(current_section_symbol_ == nullptr);
switch (section) {
case ProgramSection::Text:
current_section_stream_ =
@ -1705,7 +1711,6 @@ bool BlobImageWriter::EnterSection(ProgramSection section,
// get used for non-precompiled snapshots.
return false;
}
current_section_symbol_ = SectionSymbol(section, vm);
intptr_t padding = current_section_stream_->Align(alignment);
if (alignment_padding != nullptr) {
*alignment_padding = padding;
@ -1714,22 +1719,18 @@ bool BlobImageWriter::EnterSection(ProgramSection section,
}
void BlobImageWriter::ExitSection(ProgramSection name, bool vm, intptr_t size) {
// We should still be in the same section as the last EnterSection.
ASSERT(current_section_symbol_ != nullptr);
ASSERT_EQUAL(strcmp(SectionSymbol(name, vm), current_section_symbol_), 0);
#if defined(DART_PRECOMPILER)
ElfAddSection(elf_, name, current_section_symbol_,
ElfAddSection(elf_, name, SectionSymbol(name, vm), SectionLabel(name, vm),
current_section_stream_->buffer(), size, current_symbols_,
current_relocations_);
// We create the corresponding segment in the debugging information as well,
// since it needs the contents to create the correct build ID.
ElfAddSection(debug_elf_, name, current_section_symbol_,
current_section_stream_->buffer(), size, current_symbols_,
current_relocations_);
ElfAddSection(debug_elf_, name, SectionSymbol(name, vm),
SectionLabel(name, vm), current_section_stream_->buffer(), size,
current_symbols_, current_relocations_);
current_relocations_ = nullptr;
current_symbols_ = nullptr;
#endif
current_section_symbol_ = nullptr;
current_section_stream_ = nullptr;
}
@ -1751,13 +1752,13 @@ intptr_t BlobImageWriter::Align(intptr_t alignment,
#if defined(DART_PRECOMPILER)
intptr_t BlobImageWriter::Relocation(intptr_t section_offset,
const char* source_symbol,
intptr_t source_label,
intptr_t source_offset,
const char* target_symbol,
intptr_t target_label,
intptr_t target_offset) {
ASSERT(FLAG_precompiled_mode);
current_relocations_->Add({compiler::target::kWordSize, section_offset,
source_symbol, source_offset, target_symbol,
source_label, source_offset, target_label,
target_offset});
// We write break instructions so it's easy to tell if a relocation doesn't
// get replaced appropriately.
@ -1767,12 +1768,13 @@ intptr_t BlobImageWriter::Relocation(intptr_t section_offset,
void BlobImageWriter::AddCodeSymbol(const Code& code,
const char* symbol,
intptr_t offset) {
current_symbols_->Add({symbol, elf::STT_FUNC, offset, code.Size()});
const intptr_t label = next_label_++;
current_symbols_->Add({symbol, elf::STT_FUNC, offset, code.Size(), label});
if (elf_ != nullptr && elf_->dwarf() != nullptr) {
elf_->dwarf()->AddCode(code, symbol);
elf_->dwarf()->AddCode(code, label);
}
if (debug_elf_ != nullptr) {
debug_elf_->dwarf()->AddCode(code, symbol);
debug_elf_->dwarf()->AddCode(code, label);
}
}
@ -1780,7 +1782,8 @@ void BlobImageWriter::AddDataSymbol(const char* symbol,
intptr_t offset,
size_t size) {
if (!FLAG_add_readonly_data_symbols) return;
current_symbols_->Add({symbol, elf::STT_OBJECT, offset, size});
const intptr_t label = next_label_++;
current_symbols_->Add({symbol, elf::STT_OBJECT, offset, size, label});
}
#endif // defined(DART_PRECOMPILER)
#endif // !defined(DART_PRECOMPILED_RUNTIME)

View file

@ -235,7 +235,7 @@ struct ImageWriterCommand {
class ImageWriter : public ValueObject {
public:
explicit ImageWriter(Thread* thread);
explicit ImageWriter(Thread* thread, bool generates_assembly);
virtual ~ImageWriter() {}
// Alignment constants used in writing ELF or assembly snapshots.
@ -312,8 +312,24 @@ class ImageWriter : public ValueObject {
Data, // Read-only data.
Bss, // Statically allocated variables initialized at load.
BuildId, // GNU build ID (when applicable)
// Adjust kNumProgramSections below to use last enum value added.
};
static constexpr intptr_t kNumProgramSections =
static_cast<int>(ProgramSection::BuildId) + 1;
#if defined(DART_PRECOMPILER)
// Returns a predetermined label for the given section in the VM isolate
// (if vm is true) or application isolate (otherwise) section. Some sections
// are shared by both.
static constexpr intptr_t SectionLabel(ProgramSection section, bool vm) {
// Both vm and isolate share the build id section.
const bool shared = section == ProgramSection::BuildId;
// The initial 1 is to ensure the result is positive.
return 1 + 2 * static_cast<int>(section) + ((shared || vm) ? 0 : 1);
}
#endif
protected:
virtual void WriteBss(bool vm) = 0;
virtual void WriteROData(NonStreamingWriteStream* clustered_stream, bool vm);
@ -322,7 +338,7 @@ class ImageWriter : public ValueObject {
// Returns the standard Dart dynamic symbol name for the given VM isolate (if
// vm is true) or application isolate (otherwise) section. Some sections are
// shared by both.
const char* SectionSymbol(ProgramSection section, bool vm) const;
static const char* SectionSymbol(ProgramSection section, bool vm);
static uword GetMarkedTags(classid_t cid,
intptr_t size,
@ -452,14 +468,14 @@ class ImageWriter : public ValueObject {
// address of the source, the final value is:
// (T + target_offset + target_addend) - (S + source_offset)
virtual intptr_t Relocation(intptr_t section_offset,
const char* source_symbol,
intptr_t source_label,
intptr_t source_offset,
const char* target_symbol,
intptr_t target_label,
intptr_t target_offset) = 0;
// Writes a target word-sized value that contains the relocated address
// pointed to by the given symbol.
virtual intptr_t RelocatedAddress(intptr_t section_offset,
const char* symbol) = 0;
intptr_t label) = 0;
// Creates a static symbol for the given Code object when appropriate.
virtual void AddCodeSymbol(const Code& code,
const char* symbol,
@ -474,9 +490,9 @@ class ImageWriter : public ValueObject {
// An overload of Relocation where the target and source offsets and
// target addend are 0.
intptr_t Relocation(intptr_t section_offset,
const char* source_symbol,
const char* target_symbol) {
return Relocation(section_offset, source_symbol, 0, target_symbol, 0);
intptr_t source_label,
intptr_t target_label) {
return Relocation(section_offset, source_label, 0, target_label, 0);
}
#endif
// Writes a fixed-sized value of type T to the section contents.
@ -498,42 +514,59 @@ class ImageWriter : public ValueObject {
#if defined(DART_PRECOMPILER)
class SnapshotTextObjectNamer : ValueObject {
public:
explicit SnapshotTextObjectNamer(Zone* zone)
explicit SnapshotTextObjectNamer(Zone* zone, bool for_assembly)
: zone_(ASSERT_NOTNULL(zone)),
lib_(Library::Handle(zone)),
cls_(Class::Handle(zone)),
parent_(Function::Handle(zone)),
owner_(Object::Handle(zone)),
string_(String::Handle(zone)),
insns_(Instructions::Handle(zone)),
store_(IsolateGroup::Current()->object_store()) {}
store_(IsolateGroup::Current()->object_store()),
for_assembly_(for_assembly),
usage_count_(zone) {}
const char* StubNameForType(const AbstractType& type) const;
// Returns a unique assembly-safe name for text data to use in symbols.
// Returns a unique name for text data to use in symbols. The name is
// not assembly-safe and must be appropriately quoted in assembly output.
// Assumes that code in the InstructionsData has been allocated a handle.
const char* SnapshotNameFor(const InstructionsData& data);
// Returns a unique assembly-safe name for read-only data to use in symbols.
// Returns a unique name for read-only data to use in symbols. The name is
// not assembly-safe and must be appropriately quoted in assembly output.
// Assumes that the ObjectData has already been converted to object handles.
const char* SnapshotNameFor(const ObjectData& data);
private:
// Returns a unique assembly-safe name for the given code or read-only
// data object for use in symbols.
// Returns a unique name for the given code or read-only data object for use
// in symbols. The name is not assembly-safe and must be appropriately
// quoted in assembly output.
const char* SnapshotNameFor(const Object& object);
// Adds a non-unique assembly-safe name for the given object to the given
// buffer.
// Adds a non-unique name for the given object to the given buffer.
void AddNonUniqueNameFor(BaseTextBuffer* buffer, const Object& object);
// Modifies the symbol name in the buffer as needed for assembly use.
void ModifyForAssembly(BaseTextBuffer* buffer);
Zone* const zone_;
Library& lib_;
Class& cls_;
Function& parent_;
Object& owner_;
String& string_;
Instructions& insns_;
ObjectStore* const store_;
TypeTestingStubNamer namer_;
intptr_t nonce_ = 0;
// Used to decide whether we need to add a uniqueness suffix.
bool for_assembly_;
CStringIntMap usage_count_;
DISALLOW_COPY_AND_ASSIGN(SnapshotTextObjectNamer);
};
SnapshotTextObjectNamer namer_;
// Reserve two postive labels for each of the ProgramSection values (one for
// vm, one for isolate).
intptr_t next_label_ = 1 + 2 * kNumProgramSections;
#endif
IdSpace offset_space_ = IdSpace::kSnapshot;
@ -553,6 +586,19 @@ class ImageWriter : public ValueObject {
};
#if defined(DART_PRECOMPILER)
static_assert(ImageWriter::SectionLabel(ImageWriter::ProgramSection::Bss,
/*vm=*/true) == Elf::kVmBssLabel,
"unexpected label for VM BSS section");
static_assert(ImageWriter::SectionLabel(ImageWriter::ProgramSection::Bss,
/*vm=*/false) == Elf::kIsolateBssLabel,
"unexpected label for isolate BSS section");
static_assert(ImageWriter::SectionLabel(ImageWriter::ProgramSection::BuildId,
/*vm=*/true) == Elf::kBuildIdLabel,
"unexpected label for build id section");
static_assert(ImageWriter::SectionLabel(ImageWriter::ProgramSection::BuildId,
/*vm=*/false) == Elf::kBuildIdLabel,
"unexpected label for build id section");
#define AutoTraceImage(object, section_offset, stream) \
TraceImageObjectScope<std::remove_pointer<decltype(stream)>::type> \
AutoTraceImageObjectScopeVar##__COUNTER__(this, section_offset, stream, \
@ -616,12 +662,11 @@ class AssemblyImageWriter : public ImageWriter {
intptr_t offset,
intptr_t position);
virtual intptr_t Relocation(intptr_t section_offset,
const char* source_symbol,
intptr_t source_label,
intptr_t source_offset,
const char* target_symbol,
intptr_t target_label,
intptr_t target_offset);
virtual intptr_t RelocatedAddress(intptr_t section_offset,
const char* symbol) {
virtual intptr_t RelocatedAddress(intptr_t section_offset, intptr_t label) {
// Cannot calculate snapshot-relative addresses in assembly snapshots.
return WriteTargetWord(Image::kNoRelocatedAddress);
}
@ -637,12 +682,17 @@ class AssemblyImageWriter : public ImageWriter {
Elf* const debug_elf_;
// Used in Relocation to output "(.)" for relocations involving the current
// section position and creating local symbols in AddCodeSymbol.
const char* current_section_symbol_ = nullptr;
// section position.
intptr_t current_section_label_ = 0;
// Used for creating local symbols for code and data objects in the
// debugging info, if separately written.
ZoneGrowableArray<Elf::SymbolData>* current_symbols_ = nullptr;
// Maps labels to the appropriate symbol names for relocations and DWARF
// output.
IntMap<const char*> label_to_symbol_name_;
DISALLOW_COPY_AND_ASSIGN(AssemblyImageWriter);
};
#endif
@ -675,14 +725,13 @@ class BlobImageWriter : public ImageWriter {
virtual void FrameUnwindEpilogue() {}
#if defined(DART_PRECOMPILER)
virtual intptr_t Relocation(intptr_t section_offset,
const char* source_symbol,
intptr_t source_label,
intptr_t source_offset,
const char* target_symbol,
intptr_t target_label,
intptr_t target_offset);
virtual intptr_t RelocatedAddress(intptr_t section_offset,
const char* target_symbol) {
// ELF symbol tables always have a reserved symbol with name "" and value 0.
return ImageWriter::Relocation(section_offset, "", target_symbol);
virtual intptr_t RelocatedAddress(intptr_t section_offset, intptr_t label) {
return ImageWriter::Relocation(section_offset,
Elf::Relocation::kSnapshotRelative, label);
}
virtual void AddCodeSymbol(const Code& code,
const char* symbol,
@ -695,7 +744,6 @@ class BlobImageWriter : public ImageWriter {
// Set on section entrance to a new array containing the local symbol data
// for the current section.
ZoneGrowableArray<Elf::SymbolData>* current_symbols_ = nullptr;
#endif
NonStreamingWriteStream* const vm_instructions_;
@ -703,9 +751,6 @@ class BlobImageWriter : public ImageWriter {
Elf* const elf_;
Elf* const debug_elf_;
// Used to detect relocations or relocated address requests involving the
// current section and creating local symbols in AddCodeSymbol.
const char* current_section_symbol_ = nullptr;
// Set on section entrance to the stream that should be used by the writing
// methods.
NonStreamingWriteStream* current_section_stream_ = nullptr;