mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 13:57:58 +00:00
[vm] Add symbol size and type information to the assembly output.
Add symbol size and type information to the assembly output when compiling for Linux-based platforms, so that the symbol tables in the assembled output include that information. Since symbol tables in Mach-O files do not include symbol size information, we don't output either currently for MacOS or iOS targets. TEST=vm/dart{,_2}/use_add_readonly_data_symbols_flag Change-Id: I4219b898249153dc84214565e85ac9d3cf802538 Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-dwarf-linux-product-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,vm-kernel-gcc-linux-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/260820 Reviewed-by: Slava Egorov <vegorov@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Tess Strickland <sstrickl@google.com>
This commit is contained in:
parent
823934f1c5
commit
1d081c8bdd
|
@ -779,24 +779,72 @@ class StringTable extends Section implements DwarfContainerStringTable {
|
|||
|
||||
/// An enumeration of recognized symbol binding values used by the ELF format.
|
||||
enum SymbolBinding {
|
||||
STB_LOCAL,
|
||||
STB_GLOBAL,
|
||||
STB_WEAK,
|
||||
// We only list the standard types here, not OS-specific ones.
|
||||
STB_LOCAL(0, 'local'),
|
||||
STB_GLOBAL(1, 'global'),
|
||||
STB_WEAK(2, 'weak');
|
||||
|
||||
final int code;
|
||||
final String description;
|
||||
|
||||
const SymbolBinding(this.code, this.description);
|
||||
|
||||
static SymbolBinding? fromCode(int code) {
|
||||
for (final value in values) {
|
||||
if (value.code == code) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// An enumeration of recognized symbol types used by the ELF format.
|
||||
enum SymbolType {
|
||||
STT_NOTYPE,
|
||||
STT_OBJECT,
|
||||
STT_FUNC,
|
||||
STT_SECTION,
|
||||
// We only list the standard types here, not OS-specific ones.
|
||||
STT_NOTYPE(0, 'notype'),
|
||||
STT_OBJECT(1, 'object'),
|
||||
STT_FUNC(2, 'function'),
|
||||
STT_SECTION(3, 'section'),
|
||||
STT_FILE(4, 'file'),
|
||||
STT_COMMON(5, 'common'),
|
||||
STT_TLS(6, 'thread-local');
|
||||
|
||||
final int code;
|
||||
final String description;
|
||||
|
||||
const SymbolType(this.code, this.description);
|
||||
|
||||
static SymbolType? fromCode(int code) {
|
||||
for (final value in values) {
|
||||
if (value.code == code) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
enum SymbolVisibility {
|
||||
STV_DEFAULT,
|
||||
STV_INTERNAL,
|
||||
STV_HIDDEN,
|
||||
STV_PROTECTED,
|
||||
// We only list the standard values here.
|
||||
STV_DEFAULT(0, 'public'),
|
||||
STV_INTERNAL(1, 'internal'),
|
||||
STV_HIDDEN(2, 'hidden'),
|
||||
STV_PROTECTED(3, 'protected');
|
||||
|
||||
final int code;
|
||||
final String description;
|
||||
|
||||
const SymbolVisibility(this.code, this.description);
|
||||
|
||||
static SymbolVisibility? fromCode(int code) {
|
||||
for (final value in values) {
|
||||
if (value.code == code) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// A symbol in an ELF file, which names a portion of the virtual address space.
|
||||
|
@ -837,40 +885,20 @@ class Symbol implements DwarfContainerSymbol {
|
|||
nameIndex, info, other, sectionIndex, value, size, wordSize);
|
||||
}
|
||||
|
||||
SymbolBinding get bind => SymbolBinding.values[info >> 4];
|
||||
SymbolType get type => SymbolType.values[info & 0x0f];
|
||||
SymbolVisibility get visibility => SymbolVisibility.values[other & 0x03];
|
||||
SymbolBinding? get bind => SymbolBinding.fromCode(info >> 4);
|
||||
SymbolType? get type => SymbolType.fromCode(info & 0x0f);
|
||||
SymbolVisibility? get visibility => SymbolVisibility.fromCode(other & 0x03);
|
||||
|
||||
void writeToStringBuffer(StringBuffer buffer) {
|
||||
buffer
|
||||
..write('"')
|
||||
..write(name)
|
||||
..write('" =>');
|
||||
switch (bind) {
|
||||
case SymbolBinding.STB_GLOBAL:
|
||||
buffer.write(' a global');
|
||||
break;
|
||||
case SymbolBinding.STB_LOCAL:
|
||||
buffer.write(' a local');
|
||||
break;
|
||||
case SymbolBinding.STB_WEAK:
|
||||
buffer.write(' a weak');
|
||||
break;
|
||||
}
|
||||
switch (visibility) {
|
||||
case SymbolVisibility.STV_DEFAULT:
|
||||
break;
|
||||
case SymbolVisibility.STV_HIDDEN:
|
||||
buffer.write(' hidden');
|
||||
break;
|
||||
case SymbolVisibility.STV_INTERNAL:
|
||||
buffer.write(' internal');
|
||||
break;
|
||||
case SymbolVisibility.STV_PROTECTED:
|
||||
buffer.write(' protected');
|
||||
break;
|
||||
}
|
||||
buffer
|
||||
..write('" => a ')
|
||||
..write(bind?.description ?? '<binding unrecognized>')
|
||||
..write(' ')
|
||||
..write(type?.description ?? '<type unrecognized>')
|
||||
..write(' ')
|
||||
..write(visibility?.description ?? '<visibility unrecognized>')
|
||||
..write(' symbol that points to ')
|
||||
..write(size)
|
||||
..write(' bytes at location 0x')
|
||||
|
|
|
@ -64,15 +64,38 @@ main(List<String> args) async {
|
|||
|
||||
checkElf(scriptSnapshot);
|
||||
checkElf(scriptDebuggingInfo);
|
||||
|
||||
if (Platform.isLinux) {
|
||||
final scriptAssembly = path.join(tempDir, 'snapshot.S');
|
||||
final scriptAssemblySnapshot = path.join(tempDir, 'assembly.so');
|
||||
final scriptAssemblyDebuggingInfo =
|
||||
path.join(tempDir, 'assembly_debug.so');
|
||||
|
||||
await run(genSnapshot, <String>[
|
||||
'--add-readonly-data-symbols',
|
||||
'--dwarf-stack-traces-mode',
|
||||
'--save-debugging-info=$scriptAssemblyDebuggingInfo',
|
||||
'--snapshot-kind=app-aot-assembly',
|
||||
'--assembly=$scriptAssembly',
|
||||
scriptDill,
|
||||
]);
|
||||
|
||||
await assembleSnapshot(scriptAssembly, scriptAssemblySnapshot,
|
||||
debug: true);
|
||||
|
||||
checkElf(scriptAssemblySnapshot, isAssembled: true);
|
||||
checkElf(scriptAssemblyDebuggingInfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void checkElf(String filename) {
|
||||
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.toList();
|
||||
print('Dynamic symbols:');
|
||||
for (final symbol in dynamicSymbols) {
|
||||
// All symbol tables have an initial entry with zero-valued fields.
|
||||
if (symbol.name == '') {
|
||||
|
@ -80,23 +103,43 @@ void checkElf(String filename) {
|
|||
Expect.equals(SymbolType.STT_NOTYPE, symbol.type);
|
||||
Expect.equals(0, symbol.value);
|
||||
} else {
|
||||
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);
|
||||
continue;
|
||||
}
|
||||
Expect.equals(SymbolBinding.STB_GLOBAL, symbol.bind);
|
||||
Expect.equals(SymbolType.STT_OBJECT, symbol.type);
|
||||
Expect.isTrue(symbol.name.startsWith('_kDart'),
|
||||
'unexpected symbol name ${symbol.name}');
|
||||
// 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) {
|
||||
// Currently we only write local object symbols.
|
||||
print(symbol);
|
||||
// There should be no static-only global object symbols.
|
||||
Expect.equals(SymbolBinding.STB_LOCAL, symbol.bind);
|
||||
// All object symbols are prefixed with the type of the C++ object.
|
||||
final objectType = symbol.name.substring(0, symbol.name.indexOf('_'));
|
||||
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);
|
||||
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);
|
||||
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.
|
||||
|
|
|
@ -66,15 +66,38 @@ main(List<String> args) async {
|
|||
|
||||
checkElf(scriptSnapshot);
|
||||
checkElf(scriptDebuggingInfo);
|
||||
|
||||
if (Platform.isLinux) {
|
||||
final scriptAssembly = path.join(tempDir, 'snapshot.S');
|
||||
final scriptAssemblySnapshot = path.join(tempDir, 'assembly.so');
|
||||
final scriptAssemblyDebuggingInfo =
|
||||
path.join(tempDir, 'assembly_debug.so');
|
||||
|
||||
await run(genSnapshot, <String>[
|
||||
'--add-readonly-data-symbols',
|
||||
'--dwarf-stack-traces-mode',
|
||||
'--save-debugging-info=$scriptAssemblyDebuggingInfo',
|
||||
'--snapshot-kind=app-aot-assembly',
|
||||
'--assembly=$scriptAssembly',
|
||||
scriptDill,
|
||||
]);
|
||||
|
||||
await assembleSnapshot(scriptAssembly, scriptAssemblySnapshot,
|
||||
debug: true);
|
||||
|
||||
checkElf(scriptAssemblySnapshot, isAssembled: true);
|
||||
checkElf(scriptAssemblyDebuggingInfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void checkElf(String filename) {
|
||||
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.toList();
|
||||
print('Dynamic symbols:');
|
||||
for (final symbol in dynamicSymbols) {
|
||||
// All symbol tables have an initial entry with zero-valued fields.
|
||||
if (symbol.name == '') {
|
||||
|
@ -82,23 +105,43 @@ void checkElf(String filename) {
|
|||
Expect.equals(SymbolType.STT_NOTYPE, symbol.type);
|
||||
Expect.equals(0, symbol.value);
|
||||
} else {
|
||||
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);
|
||||
continue;
|
||||
}
|
||||
Expect.equals(SymbolBinding.STB_GLOBAL, symbol.bind);
|
||||
Expect.equals(SymbolType.STT_OBJECT, symbol.type);
|
||||
Expect.isTrue(symbol.name.startsWith('_kDart'),
|
||||
'unexpected symbol name ${symbol.name}');
|
||||
// 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) {
|
||||
// Currently we only write local object symbols.
|
||||
print(symbol);
|
||||
// There should be no static-only global object symbols.
|
||||
Expect.equals(SymbolBinding.STB_LOCAL, symbol.bind);
|
||||
// All object symbols are prefixed with the type of the C++ object.
|
||||
final objectType = symbol.name.substring(0, symbol.name.indexOf('_'));
|
||||
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);
|
||||
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);
|
||||
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.
|
||||
|
|
|
@ -1294,6 +1294,17 @@ void AssemblyImageWriter::WriteROData(NonStreamingWriteStream* clustered_stream,
|
|||
for (const auto& symbol : *current_symbols_) {
|
||||
WriteBytes(bytes + last_position, symbol.offset - last_position);
|
||||
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);
|
||||
#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.
|
||||
#else
|
||||
UNIMPLEMENTED();
|
||||
#endif
|
||||
last_position = symbol.offset;
|
||||
}
|
||||
WriteBytes(bytes + last_position, len - last_position);
|
||||
|
@ -1379,6 +1390,17 @@ void AssemblyImageWriter::ExitSection(ProgramSection name,
|
|||
// 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_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_);
|
||||
#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.
|
||||
#else
|
||||
UNIMPLEMENTED();
|
||||
#endif
|
||||
// We need to generate a text segment of the appropriate size in the ELF
|
||||
// for two reasons:
|
||||
//
|
||||
|
@ -1460,6 +1482,17 @@ void AssemblyImageWriter::AddCodeSymbol(const Code& code,
|
|||
debug_elf_->dwarf()->AddCode(code, 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);
|
||||
#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.
|
||||
#else
|
||||
UNIMPLEMENTED();
|
||||
#endif
|
||||
}
|
||||
|
||||
void AssemblyImageWriter::AddDataSymbol(const char* symbol,
|
||||
|
|
Loading…
Reference in a new issue