mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 23:28:09 +00:00
[vm] Do not obfuscate code or object symbols in the static symbol table.
Also changes pkg/native_stack_traces to return a parsed MachO file if the MachO file doesn't contain DWARF information, so we can examine its static symbol information for the new test. Issue: https://github.com/flutter/flutter/issues/124715 TEST=vm/dart/unobfuscated_static_symbols Change-Id: I07d3ced56eeba852ebe4178dfd2b66ebb899eb76 Cq-Include-Trybots: luci.dart.try:vm-aot-dwarf-linux-product-x64-try,vm-aot-linux-product-x64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-release-x64-try,vm-aot-obfuscate-linux-release-x64-try,vm-aot-mac-product-arm64-try,vm-aot-mac-release-arm64-try,vm-aot-mac-release-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/305720 Commit-Queue: Tess Strickland <sstrickl@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
parent
9fecc29dbb
commit
ecf1968122
|
@ -1,3 +1,8 @@
|
|||
## 0.5.6
|
||||
|
||||
- Added retrieval of the static symbol table contents for use in Dart tests.
|
||||
- Don't require Mach-O files to contain DWARF to be read by the Mach-O reader.
|
||||
|
||||
## 0.5.5
|
||||
|
||||
- Fixed issue introduced by 0.5.4.
|
||||
|
|
|
@ -29,6 +29,7 @@ abstract class DwarfContainer {
|
|||
|
||||
String? get buildId;
|
||||
|
||||
Iterable<DwarfContainerSymbol> get staticSymbols;
|
||||
DwarfContainerStringTable? get debugStringTable;
|
||||
DwarfContainerStringTable? get debugLineStringTable;
|
||||
|
||||
|
|
|
@ -1153,6 +1153,7 @@ class Elf extends DwarfContainer {
|
|||
|
||||
/// Returns an iterable of the symbols in the static symbol table(s).
|
||||
/// The ordering of the symbols is not guaranteed.
|
||||
@override
|
||||
Iterable<Symbol> get staticSymbols sync* {
|
||||
for (final section in namedSections('.symtab')) {
|
||||
final symtab = section as SymbolTable;
|
||||
|
|
|
@ -426,7 +426,7 @@ class MachO extends DwarfContainer {
|
|||
final MachOHeader _header;
|
||||
final List<LoadCommand> _commands;
|
||||
final SymbolTable _symbolTable;
|
||||
final SegmentCommand _dwarfSegment;
|
||||
final SegmentCommand? _dwarfSegment;
|
||||
final StringTable? _debugStringTable;
|
||||
final StringTable? _debugLineStringTable;
|
||||
|
||||
|
@ -456,23 +456,21 @@ class MachO extends DwarfContainer {
|
|||
final dwarfSegment = commands
|
||||
.whereType<SegmentCommand?>()
|
||||
.firstWhere((sc) => sc!.segname == '__DWARF', orElse: () => null);
|
||||
if (dwarfSegment == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final debugStringTableSection = dwarfSegment.sections['__debug_str'];
|
||||
StringTable? debugStringTable;
|
||||
if (debugStringTableSection != null) {
|
||||
debugStringTable =
|
||||
StringTable.fromReader(debugStringTableSection.shrink(reader));
|
||||
}
|
||||
|
||||
final debugLineStringTableSection =
|
||||
dwarfSegment.sections['__debug_line_str'];
|
||||
StringTable? debugLineStringTable;
|
||||
if (debugLineStringTableSection != null) {
|
||||
debugLineStringTable =
|
||||
StringTable.fromReader(debugLineStringTableSection.shrink(reader));
|
||||
if (dwarfSegment != null) {
|
||||
final debugStringTableSection = dwarfSegment.sections['__debug_str'];
|
||||
if (debugStringTableSection != null) {
|
||||
debugStringTable =
|
||||
StringTable.fromReader(debugStringTableSection.shrink(reader));
|
||||
}
|
||||
|
||||
final debugLineStringTableSection =
|
||||
dwarfSegment.sections['__debug_line_str'];
|
||||
if (debugLineStringTableSection != null) {
|
||||
debugLineStringTable =
|
||||
StringTable.fromReader(debugLineStringTableSection.shrink(reader));
|
||||
}
|
||||
}
|
||||
|
||||
// Set the wordSize and endian of the original reader before returning.
|
||||
|
@ -498,6 +496,7 @@ class MachO extends DwarfContainer {
|
|||
MachO.fromReader(Reader.fromFile(MachO.handleDSYM(fileName)));
|
||||
|
||||
bool get isDSYM => _header.isDSYM;
|
||||
bool get hasDwarf => _dwarfSegment != null;
|
||||
|
||||
Reader applyWordSizeAndEndian(Reader reader) =>
|
||||
Reader.fromTypedData(reader.bdata,
|
||||
|
@ -508,13 +507,13 @@ class MachO extends DwarfContainer {
|
|||
|
||||
@override
|
||||
Reader abbreviationsTableReader(Reader containerReader) =>
|
||||
_dwarfSegment.sections['__debug_abbrev']!.shrink(containerReader);
|
||||
_dwarfSegment!.sections['__debug_abbrev']!.shrink(containerReader);
|
||||
@override
|
||||
Reader lineNumberInfoReader(Reader containerReader) =>
|
||||
_dwarfSegment.sections['__debug_line']!.shrink(containerReader);
|
||||
_dwarfSegment!.sections['__debug_line']!.shrink(containerReader);
|
||||
@override
|
||||
Reader debugInfoReader(Reader containerReader) =>
|
||||
_dwarfSegment.sections['__debug_info']!.shrink(containerReader);
|
||||
_dwarfSegment!.sections['__debug_info']!.shrink(containerReader);
|
||||
|
||||
@override
|
||||
int? get vmStartAddress => _symbolTable[constants.vmSymbolName]?.value;
|
||||
|
@ -545,6 +544,9 @@ class MachO extends DwarfContainer {
|
|||
return bestSym;
|
||||
}
|
||||
|
||||
@override
|
||||
Iterable<Symbol> get staticSymbols => _symbolTable.values;
|
||||
|
||||
@override
|
||||
void writeToStringBuffer(StringBuffer buffer) {
|
||||
buffer
|
||||
|
@ -748,11 +750,15 @@ class UniversalBinary {
|
|||
(macho.isolateStartAddress == null))) {
|
||||
continue;
|
||||
}
|
||||
if (macho.isDSYM) {
|
||||
if (!arches.containsKey(cpuType)) {
|
||||
arches[cpuType] = arch;
|
||||
} else if (macho.isDSYM) {
|
||||
// Always take a dSYM section above a non-dSYM section. If there are
|
||||
// multiple dSYM sections for some reason, the last one read is fine.
|
||||
arches[cpuType] = arch;
|
||||
} else if (!arches.containsKey(cpuType)) {
|
||||
} else if (!contents[arches[cpuType]!]!.hasDwarf) {
|
||||
// If the old section didn't have DWARF information but the new one
|
||||
// does, take it instead.
|
||||
arches[cpuType] = arch;
|
||||
}
|
||||
contents[arch] = macho;
|
||||
|
@ -794,7 +800,7 @@ class UniversalBinary {
|
|||
..writeln('')
|
||||
..writeln('')
|
||||
..writeln('----------------------------------------------------------')
|
||||
..writeln(' DWARF-containing Mach-O Contents for $cpuType')
|
||||
..writeln(' Selected Mach-O Contents for $cpuType')
|
||||
..writeln('----------------------------------------------------------')
|
||||
..writeln('');
|
||||
_contents[_arches[cpuType]!]!.writeToStringBuffer(buffer);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: native_stack_traces
|
||||
version: 0.5.5
|
||||
version: 0.5.6
|
||||
description: Utilities for working with non-symbolic stack traces.
|
||||
repository: https://github.com/dart-lang/sdk/tree/main/pkg/native_stack_traces
|
||||
|
||||
|
|
281
runtime/tests/vm/dart/unobfuscated_static_symbols_test.dart
Normal file
281
runtime/tests/vm/dart/unobfuscated_static_symbols_test.dart
Normal file
|
@ -0,0 +1,281 @@
|
|||
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// This test ensures that when running in obfuscated mode, the AOT compiler
|
||||
// generates a snapshot with obfuscated runtime information, but that static
|
||||
// symbol tables in the unstripped snapshot and/or separate debugging
|
||||
// information remains unobfuscated.
|
||||
|
||||
// OtherResources=use_save_debugging_info_flag_program.dart
|
||||
|
||||
import "dart:io";
|
||||
import "dart:math";
|
||||
import "dart:typed_data";
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:native_stack_traces/elf.dart';
|
||||
import 'package:native_stack_traces/native_stack_traces.dart';
|
||||
import 'package:native_stack_traces/src/dwarf_container.dart';
|
||||
import 'package:native_stack_traces/src/macho.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'use_flag_test_helper.dart';
|
||||
|
||||
Future<void> main(List<String> args) async {
|
||||
if (!isAOTRuntime) {
|
||||
return; // Running in JIT: AOT binaries not available.
|
||||
}
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
return; // SDK tree and dart_bootstrap not available on the test device.
|
||||
}
|
||||
|
||||
// These are the tools we need to be available to run on a given platform:
|
||||
if (!await testExecutable(genSnapshot)) {
|
||||
throw "Cannot run test as $genSnapshot not available";
|
||||
}
|
||||
if (!await testExecutable(dartPrecompiledRuntime)) {
|
||||
throw "Cannot run test as $dartPrecompiledRuntime not available";
|
||||
}
|
||||
if (!File(platformDill).existsSync()) {
|
||||
throw "Cannot run test as $platformDill does not exist";
|
||||
}
|
||||
|
||||
await withTempDir('unobfuscated-static-symbols-test', (String tempDir) async {
|
||||
final cwDir = path.dirname(Platform.script.toFilePath());
|
||||
final script =
|
||||
path.join(cwDir, 'use_save_debugging_info_flag_program.dart');
|
||||
final scriptDill = path.join(tempDir, 'flag_program.dill');
|
||||
|
||||
// Compile script to Kernel IR.
|
||||
await run(genKernel, <String>[
|
||||
'--aot',
|
||||
'--platform=$platformDill',
|
||||
'-o',
|
||||
scriptDill,
|
||||
script,
|
||||
]);
|
||||
|
||||
await checkElf(tempDir, scriptDill);
|
||||
await checkAssembly(tempDir, scriptDill);
|
||||
});
|
||||
}
|
||||
|
||||
const commonGenSnapshotArgs = <String>[
|
||||
// Make sure that the runs are deterministic so we can depend on the same
|
||||
// snapshot being generated each time.
|
||||
'--deterministic',
|
||||
];
|
||||
|
||||
Future<void> checkElf(String tempDir, String scriptDill) async {
|
||||
// Run the AOT compiler without Dwarf stack trace, once without obfuscation,
|
||||
// once with obfuscation, and once with obfuscation and saving debugging
|
||||
// information.
|
||||
final scriptUnobfuscatedSnapshot = path.join(tempDir, 'unobfuscated-elf.so');
|
||||
await run(genSnapshot, <String>[
|
||||
...commonGenSnapshotArgs,
|
||||
'--snapshot-kind=app-aot-elf',
|
||||
'--elf=$scriptUnobfuscatedSnapshot',
|
||||
scriptDill,
|
||||
]);
|
||||
final unobfuscatedCase = TestCase(
|
||||
scriptUnobfuscatedSnapshot, Elf.fromFile(scriptUnobfuscatedSnapshot)!);
|
||||
|
||||
final scriptObfuscatedOnlySnapshot =
|
||||
path.join(tempDir, 'obfuscated-only-elf.so');
|
||||
await run(genSnapshot, <String>[
|
||||
...commonGenSnapshotArgs,
|
||||
'--obfuscate',
|
||||
'--snapshot-kind=app-aot-elf',
|
||||
'--elf=$scriptObfuscatedOnlySnapshot',
|
||||
scriptDill,
|
||||
]);
|
||||
final obfuscatedOnlyCase = TestCase(scriptObfuscatedOnlySnapshot,
|
||||
Elf.fromFile(scriptObfuscatedOnlySnapshot)!);
|
||||
|
||||
final scriptObfuscatedSnapshot = path.join(tempDir, 'obfuscated-elf.so');
|
||||
final scriptDebuggingInfo = path.join(tempDir, 'obfuscated-debug-elf.so');
|
||||
await run(genSnapshot, <String>[
|
||||
...commonGenSnapshotArgs,
|
||||
'--obfuscate',
|
||||
'--snapshot-kind=app-aot-elf',
|
||||
'--elf=$scriptObfuscatedSnapshot',
|
||||
'--save-debugging-info=$scriptDebuggingInfo',
|
||||
scriptDill,
|
||||
]);
|
||||
final obfuscatedCase = TestCase(
|
||||
scriptObfuscatedSnapshot,
|
||||
Elf.fromFile(scriptObfuscatedSnapshot)!,
|
||||
Elf.fromFile(scriptDebuggingInfo)!);
|
||||
|
||||
await checkCases(unobfuscatedCase, <TestCase>[
|
||||
obfuscatedOnlyCase,
|
||||
obfuscatedCase,
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> checkAssembly(String tempDir, String scriptDill) async {
|
||||
// Currently there are no appropriate buildtools on the simulator trybots as
|
||||
// normally they compile to ELF and don't need them for compiling assembly
|
||||
// snapshots.
|
||||
if (isSimulator || (!Platform.isLinux && !Platform.isMacOS)) return;
|
||||
|
||||
// Run the AOT compiler without Dwarf stack trace, once without obfuscation,
|
||||
// once with obfuscation, and once with obfuscation and saving debugging
|
||||
// information.
|
||||
final scriptUnobfuscatedAssembly =
|
||||
path.join(tempDir, 'unobfuscated-assembly.S');
|
||||
final scriptUnobfuscatedSnapshot =
|
||||
path.join(tempDir, 'unobfuscated-assembly.so');
|
||||
await run(genSnapshot, <String>[
|
||||
...commonGenSnapshotArgs,
|
||||
'--snapshot-kind=app-aot-assembly',
|
||||
'--assembly=$scriptUnobfuscatedAssembly',
|
||||
scriptDill,
|
||||
]);
|
||||
await assembleSnapshot(
|
||||
scriptUnobfuscatedAssembly, scriptUnobfuscatedSnapshot);
|
||||
final unobfuscatedCase = TestCase(
|
||||
scriptUnobfuscatedSnapshot,
|
||||
Platform.isMacOS
|
||||
? MachO.fromFile(scriptUnobfuscatedSnapshot)!
|
||||
: Elf.fromFile(scriptUnobfuscatedSnapshot)!);
|
||||
|
||||
final scriptObfuscatedOnlyAssembly =
|
||||
path.join(tempDir, 'obfuscated-only-assembly.S');
|
||||
final scriptObfuscatedOnlySnapshot =
|
||||
path.join(tempDir, 'obfuscated-only-assembly.so');
|
||||
await run(genSnapshot, <String>[
|
||||
...commonGenSnapshotArgs,
|
||||
'--obfuscate',
|
||||
'--snapshot-kind=app-aot-assembly',
|
||||
'--assembly=$scriptObfuscatedOnlyAssembly',
|
||||
scriptDill,
|
||||
]);
|
||||
await assembleSnapshot(
|
||||
scriptObfuscatedOnlyAssembly, scriptObfuscatedOnlySnapshot);
|
||||
final obfuscatedOnlyCase = TestCase(
|
||||
scriptObfuscatedOnlySnapshot,
|
||||
Platform.isMacOS
|
||||
? MachO.fromFile(scriptObfuscatedOnlySnapshot)!
|
||||
: Elf.fromFile(scriptObfuscatedOnlySnapshot)!);
|
||||
|
||||
await checkCases(unobfuscatedCase, <TestCase>[
|
||||
obfuscatedOnlyCase,
|
||||
]);
|
||||
}
|
||||
|
||||
class TestCase {
|
||||
final String snapshotPath;
|
||||
final DwarfContainer container;
|
||||
final DwarfContainer? debuggingInfoContainer;
|
||||
|
||||
TestCase(this.snapshotPath, this.container, [this.debuggingInfoContainer]);
|
||||
}
|
||||
|
||||
Future<void> checkCases(
|
||||
TestCase unobfuscated, List<TestCase> obfuscateds) async {
|
||||
checkStaticSymbolTables(unobfuscated, obfuscateds);
|
||||
await checkTraces(unobfuscated, obfuscateds);
|
||||
}
|
||||
|
||||
Future<void> checkTraces(
|
||||
TestCase unobfuscated, List<TestCase> obfuscateds) async {
|
||||
// Run the resulting scripts, saving the stack traces.
|
||||
final expectedTrace = await runError(dartPrecompiledRuntime, <String>[
|
||||
unobfuscated.snapshotPath,
|
||||
]);
|
||||
|
||||
print('');
|
||||
print("Original stack trace:");
|
||||
expectedTrace.forEach(print);
|
||||
|
||||
final obfuscatedTraces = <List<String>>[];
|
||||
for (int i = 0; i < obfuscateds.length; i++) {
|
||||
obfuscatedTraces.add(await runError(dartPrecompiledRuntime, <String>[
|
||||
obfuscateds[i].snapshotPath,
|
||||
]));
|
||||
|
||||
print('');
|
||||
print("Obfuscated stack trace ${i + 1}:");
|
||||
obfuscatedTraces[i].forEach(print);
|
||||
|
||||
if (i != 0) {
|
||||
// Compare with the previous trace, as all obfuscated traces should be
|
||||
// the same as the obfuscation is deterministic.
|
||||
Expect.deepEquals(obfuscatedTraces[i - 1], obfuscatedTraces[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// The unobfuscated trace should differ from all obfuscated traces.
|
||||
Expect.isNotEmpty(obfuscateds);
|
||||
final gotTrace = obfuscatedTraces[0];
|
||||
Expect.equals(expectedTrace.length, gotTrace.length);
|
||||
bool differs = false;
|
||||
for (int i = 0; i < expectedTrace.length; i++) {
|
||||
if (expectedTrace[i] != gotTrace[i]) {
|
||||
differs = true;
|
||||
}
|
||||
}
|
||||
Expect.isTrue(
|
||||
differs, 'The obfuscated traces are identical to the unobfuscated trace');
|
||||
}
|
||||
|
||||
void checkStaticSymbolTables(TestCase expected, List<TestCase> cases) {
|
||||
final expectedSymbolNames =
|
||||
expected.container.staticSymbols.map((o) => o.name);
|
||||
|
||||
if (expected.debuggingInfoContainer != null) {
|
||||
expectSimilarStaticSymbols(expectedSymbolNames,
|
||||
expected.debuggingInfoContainer!.staticSymbols.map((o) => o.name));
|
||||
}
|
||||
|
||||
for (final got in cases) {
|
||||
expectSimilarStaticSymbols(
|
||||
expectedSymbolNames, got.container.staticSymbols.map((o) => o.name));
|
||||
if (got.debuggingInfoContainer != null) {
|
||||
expectSimilarStaticSymbols(expectedSymbolNames,
|
||||
got.debuggingInfoContainer!.staticSymbols.map((o) => o.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const kMaxPercentAllowedDifferences = 0.01;
|
||||
|
||||
void expectSimilarStaticSymbols(
|
||||
Iterable<String> expected, Iterable<String> got) {
|
||||
final allowedDifferences =
|
||||
(expected.length * kMaxPercentAllowedDifferences).floor();
|
||||
// There are cases where we cannot assume that we have the exact same symbols
|
||||
// in both snapshots (e.g., because we're using an assembler that adds
|
||||
// symbols with randomly generated names). Instead, we compare them manually,
|
||||
// counting the number of symbols not found in one or the other, and allow
|
||||
// a small number of differences. (We generate _a lot_ of static symbols, so
|
||||
// if the vast majority match we can assume that no obfuscation happened.)
|
||||
final onlyExpected = <String>[];
|
||||
for (final name in expected) {
|
||||
if (!got.contains(name)) {
|
||||
onlyExpected.add(name);
|
||||
}
|
||||
}
|
||||
print('');
|
||||
print('Symbols found only in expected:');
|
||||
onlyExpected.forEach(print);
|
||||
|
||||
final onlyGot = <String>[];
|
||||
for (final name in got) {
|
||||
if (!expected.contains(name)) {
|
||||
onlyGot.add(name);
|
||||
}
|
||||
}
|
||||
print('');
|
||||
print('Symbols found only in got:');
|
||||
onlyGot.forEach(print);
|
||||
|
||||
final differences = onlyExpected.length + onlyGot.length;
|
||||
Expect.isTrue(
|
||||
differences <= allowedDifferences,
|
||||
'Got $differences different symbols, which is '
|
||||
'more than $allowedDifferences.');
|
||||
}
|
|
@ -6577,22 +6577,26 @@ static void CreateAppAOTSnapshot(
|
|||
|
||||
const bool generate_debug = debug_callback_data != nullptr;
|
||||
|
||||
auto* const deobfuscation_trie =
|
||||
strip ? nullptr : ImageWriter::CreateReverseObfuscationTrie(T);
|
||||
|
||||
if (as_elf) {
|
||||
StreamingWriteStream elf_stream(kInitialSize, callback, callback_data);
|
||||
StreamingWriteStream debug_stream(generate_debug ? kInitialDebugSize : 0,
|
||||
callback, debug_callback_data);
|
||||
|
||||
auto const dwarf = strip ? nullptr : new (Z) Dwarf(Z);
|
||||
auto const dwarf = strip ? nullptr : new (Z) Dwarf(Z, deobfuscation_trie);
|
||||
auto const elf = new (Z) Elf(Z, &elf_stream, Elf::Type::Snapshot, dwarf);
|
||||
// Re-use the same DWARF object if the snapshot is unstripped.
|
||||
auto const debug_elf =
|
||||
generate_debug ? new (Z) Elf(Z, &debug_stream, Elf::Type::DebugInfo,
|
||||
strip ? new (Z) Dwarf(Z) : dwarf)
|
||||
: nullptr;
|
||||
generate_debug
|
||||
? new (Z) Elf(Z, &debug_stream, Elf::Type::DebugInfo,
|
||||
strip ? new (Z) Dwarf(Z, deobfuscation_trie) : dwarf)
|
||||
: nullptr;
|
||||
|
||||
BlobImageWriter image_writer(T, &vm_snapshot_instructions,
|
||||
&isolate_snapshot_instructions, debug_elf,
|
||||
elf);
|
||||
&isolate_snapshot_instructions,
|
||||
deobfuscation_trie, debug_elf, elf);
|
||||
FullSnapshotWriter writer(Snapshot::kFullAOT, &vm_snapshot_data,
|
||||
&isolate_snapshot_data, &image_writer,
|
||||
&image_writer);
|
||||
|
@ -6615,10 +6619,11 @@ static void CreateAppAOTSnapshot(
|
|||
|
||||
auto const elf = generate_debug
|
||||
? new (Z) Elf(Z, &debug_stream, Elf::Type::DebugInfo,
|
||||
new (Z) Dwarf(Z))
|
||||
new (Z) Dwarf(Z, deobfuscation_trie))
|
||||
: nullptr;
|
||||
|
||||
AssemblyImageWriter image_writer(T, &assembly_stream, strip, elf);
|
||||
AssemblyImageWriter image_writer(T, &assembly_stream, deobfuscation_trie,
|
||||
strip, elf);
|
||||
FullSnapshotWriter writer(Snapshot::kFullAOT, &vm_snapshot_data,
|
||||
&isolate_snapshot_data, &image_writer,
|
||||
&image_writer);
|
||||
|
|
|
@ -91,52 +91,9 @@ class InliningNode : public ZoneAllocated {
|
|||
InliningNode* children_next;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Trie<T>* Trie<T>::AddString(Zone* zone,
|
||||
Trie<T>* trie,
|
||||
const char* key,
|
||||
const T* value) {
|
||||
ASSERT(key != nullptr);
|
||||
if (trie == nullptr) {
|
||||
trie = new (zone) Trie<T>();
|
||||
}
|
||||
if (*key == '\0') {
|
||||
ASSERT(trie->value_ == nullptr);
|
||||
trie->value_ = value;
|
||||
} else {
|
||||
auto const index = ChildIndex(*key);
|
||||
ASSERT(index >= 0 && index < kNumValidChars);
|
||||
trie->children_[index] =
|
||||
AddString(zone, trie->children_[index], key + 1, value);
|
||||
}
|
||||
|
||||
return trie;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T* Trie<T>::Lookup(const Trie<T>* trie, const char* key, intptr_t* end) {
|
||||
intptr_t i = 0;
|
||||
for (; key[i] != '\0'; i++) {
|
||||
auto const index = ChildIndex(key[i]);
|
||||
ASSERT(index < kNumValidChars);
|
||||
if (index < 0) {
|
||||
if (end == nullptr) return nullptr;
|
||||
break;
|
||||
}
|
||||
// Still find the longest valid trie prefix when no stored value.
|
||||
if (trie == nullptr) continue;
|
||||
trie = trie->children_[index];
|
||||
}
|
||||
if (end != nullptr) {
|
||||
*end = i;
|
||||
}
|
||||
if (trie == nullptr) return nullptr;
|
||||
return trie->value_;
|
||||
}
|
||||
|
||||
Dwarf::Dwarf(Zone* zone)
|
||||
Dwarf::Dwarf(Zone* zone, const Trie<const char>* deobfuscation_trie)
|
||||
: zone_(zone),
|
||||
reverse_obfuscation_trie_(CreateReverseObfuscationTrie(zone)),
|
||||
deobfuscation_trie_(deobfuscation_trie),
|
||||
codes_(zone, 1024),
|
||||
code_to_label_(zone),
|
||||
functions_(zone, 1024),
|
||||
|
@ -365,7 +322,8 @@ void Dwarf::WriteAbstractFunctions(DwarfWriteStream* stream) {
|
|||
name = function.QualifiedUserVisibleName();
|
||||
script = function.script();
|
||||
const intptr_t file = LookupScript(script);
|
||||
auto const name_cstr = Deobfuscate(name.ToCString());
|
||||
auto const name_cstr =
|
||||
ImageWriter::Deobfuscate(zone_, deobfuscation_trie_, name.ToCString());
|
||||
|
||||
stream->RegisterAbstractOrigin(i);
|
||||
stream->uleb128(kAbstractFunction);
|
||||
|
@ -906,7 +864,8 @@ void Dwarf::WriteLineNumberProgram(DwarfWriteStream* stream) {
|
|||
} else {
|
||||
uri = script.url();
|
||||
ASSERT(!uri.IsNull());
|
||||
uri_cstr = Deobfuscate(uri.ToCString());
|
||||
uri_cstr = ImageWriter::Deobfuscate(zone_, deobfuscation_trie_,
|
||||
uri.ToCString());
|
||||
}
|
||||
RELEASE_ASSERT(strlen(uri_cstr) != 0);
|
||||
|
||||
|
@ -947,49 +906,6 @@ void Dwarf::WriteLineNumberProgram(DwarfWriteStream* stream) {
|
|||
});
|
||||
}
|
||||
|
||||
const char* Dwarf::Deobfuscate(const char* cstr) {
|
||||
if (reverse_obfuscation_trie_ == nullptr) return cstr;
|
||||
TextBuffer buffer(256);
|
||||
// Used to avoid Zone-allocating strings if no deobfuscation was performed.
|
||||
bool changed = false;
|
||||
intptr_t i = 0;
|
||||
while (cstr[i] != '\0') {
|
||||
intptr_t offset;
|
||||
auto const value = reverse_obfuscation_trie_->Lookup(cstr + i, &offset);
|
||||
if (offset == 0) {
|
||||
// The first character was an invalid key element (that isn't the null
|
||||
// terminator due to the while condition), copy it and skip to the next.
|
||||
buffer.AddChar(cstr[i++]);
|
||||
} else if (value != nullptr) {
|
||||
changed = true;
|
||||
buffer.AddString(value);
|
||||
} else {
|
||||
buffer.AddRaw(reinterpret_cast<const uint8_t*>(cstr + i), offset);
|
||||
}
|
||||
i += offset;
|
||||
}
|
||||
if (!changed) return cstr;
|
||||
return OS::SCreate(zone_, "%s", buffer.buffer());
|
||||
}
|
||||
|
||||
Trie<const char>* Dwarf::CreateReverseObfuscationTrie(Zone* zone) {
|
||||
auto const map_array = IsolateGroup::Current()->obfuscation_map();
|
||||
if (map_array == nullptr) return nullptr;
|
||||
|
||||
Trie<const char>* trie = nullptr;
|
||||
for (intptr_t i = 0; map_array[i] != nullptr; i += 2) {
|
||||
auto const key = map_array[i];
|
||||
auto const value = map_array[i + 1];
|
||||
ASSERT(value != nullptr);
|
||||
// Don't include identity mappings.
|
||||
if (strcmp(key, value) == 0) continue;
|
||||
// Otherwise, any value in the obfuscation map should be a valid key.
|
||||
ASSERT(Trie<const char>::IsValidKey(value));
|
||||
trie = Trie<const char>::AddString(zone, trie, value, key);
|
||||
}
|
||||
return trie;
|
||||
}
|
||||
|
||||
#endif // DART_PRECOMPILER
|
||||
|
||||
} // namespace dart
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "vm/allocation.h"
|
||||
#include "vm/hash.h"
|
||||
#include "vm/hash_map.h"
|
||||
#include "vm/image_snapshot.h"
|
||||
#include "vm/object.h"
|
||||
#include "vm/zone.h"
|
||||
|
||||
|
@ -124,80 +125,6 @@ struct DwarfCodeKeyValueTrait {
|
|||
template <typename T>
|
||||
using DwarfCodeMap = DirectChainedHashMap<DwarfCodeKeyValueTrait<T>>;
|
||||
|
||||
template <typename T>
|
||||
class Trie : public ZoneAllocated {
|
||||
public:
|
||||
// Returns whether [key] is a valid trie key (that is, a C string that
|
||||
// contains only characters for which charIndex returns a non-negative value).
|
||||
static bool IsValidKey(const char* key) {
|
||||
for (intptr_t i = 0; key[i] != '\0'; i++) {
|
||||
if (ChildIndex(key[i]) < 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Adds a binding of [key] to [value] in [trie]. Assumes that the string in
|
||||
// [key] is a valid trie key and does not already have a value in [trie].
|
||||
//
|
||||
// If [trie] is nullptr, then a new trie is created and a pointer to the new
|
||||
// trie is returned. Otherwise, [trie] will be returned.
|
||||
static Trie<T>* AddString(Zone* zone,
|
||||
Trie<T>* trie,
|
||||
const char* key,
|
||||
const T* value);
|
||||
|
||||
// Adds a binding of [key] to [value]. Assumes that the string in [key] is a
|
||||
// valid trie key and does not already have a value in this trie.
|
||||
void AddString(Zone* zone, const char* key, const T* value) {
|
||||
AddString(zone, this, key, value);
|
||||
}
|
||||
|
||||
// Looks up the value stored for [key] in [trie]. If one is not found, then
|
||||
// nullptr is returned.
|
||||
//
|
||||
// If [end] is not nullptr, then the longest prefix of [key] that is a valid
|
||||
// trie key prefix will be used for the lookup and the value pointed to by
|
||||
// [end] is set to the index after that prefix. Otherwise, the whole [key]
|
||||
// is used.
|
||||
static const T* Lookup(const Trie<T>* trie,
|
||||
const char* key,
|
||||
intptr_t* end = nullptr);
|
||||
|
||||
// Looks up the value stored for [key]. If one is not found, then nullptr is
|
||||
// returned.
|
||||
//
|
||||
// If [end] is not nullptr, then the longest prefix of [key] that is a valid
|
||||
// trie key prefix will be used for the lookup and the value pointed to by
|
||||
// [end] is set to the index after that prefix. Otherwise, the whole [key]
|
||||
// is used.
|
||||
const T* Lookup(const char* key, intptr_t* end = nullptr) const {
|
||||
return Lookup(this, key, end);
|
||||
}
|
||||
|
||||
private:
|
||||
// Currently, only the following characters can appear in obfuscated names:
|
||||
// '_', '@', '0-9', 'a-z', 'A-Z'
|
||||
static constexpr intptr_t kNumValidChars = 64;
|
||||
|
||||
Trie() {
|
||||
for (intptr_t i = 0; i < kNumValidChars; i++) {
|
||||
children_[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static intptr_t ChildIndex(char c) {
|
||||
if (c == '_') return 0;
|
||||
if (c == '@') return 1;
|
||||
if (c >= '0' && c <= '9') return ('9' - c) + 2;
|
||||
if (c >= 'a' && c <= 'z') return ('z' - c) + 12;
|
||||
if (c >= 'A' && c <= 'Z') return ('Z' - c) + 38;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const T* value_ = nullptr;
|
||||
Trie<T>* children_[kNumValidChars];
|
||||
};
|
||||
|
||||
class DwarfWriteStream : public ValueObject {
|
||||
public:
|
||||
DwarfWriteStream() {}
|
||||
|
@ -228,7 +155,7 @@ class DwarfWriteStream : public ValueObject {
|
|||
|
||||
class Dwarf : public ZoneAllocated {
|
||||
public:
|
||||
explicit Dwarf(Zone* zone);
|
||||
explicit Dwarf(Zone* zone, const Trie<const char>* deobfuscation_trie);
|
||||
|
||||
const ZoneGrowableArray<const Code*>& codes() const { return codes_; }
|
||||
|
||||
|
@ -317,11 +244,8 @@ class Dwarf : public ZoneAllocated {
|
|||
void WriteLineNumberProgramFromCodeSourceMaps(
|
||||
LineNumberProgramWriter* writer);
|
||||
|
||||
const char* Deobfuscate(const char* cstr);
|
||||
static Trie<const char>* CreateReverseObfuscationTrie(Zone* zone);
|
||||
|
||||
Zone* const zone_;
|
||||
Trie<const char>* const reverse_obfuscation_trie_;
|
||||
const Trie<const char>* const deobfuscation_trie_;
|
||||
ZoneGrowableArray<const Code*> codes_;
|
||||
DwarfCodeMap<intptr_t> code_to_label_;
|
||||
ZoneGrowableArray<const Function*> functions_;
|
||||
|
|
|
@ -172,7 +172,13 @@ bool ObjectOffsetTrait::IsKeyEqual(Pair pair, Key key) {
|
|||
}
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if defined(DART_PRECOMPILER)
|
||||
ImageWriter::ImageWriter(Thread* t,
|
||||
bool generates_assembly,
|
||||
const Trie<const char>* deobfuscation_trie)
|
||||
#else
|
||||
ImageWriter::ImageWriter(Thread* t, bool generates_assembly)
|
||||
#endif
|
||||
: thread_(ASSERT_NOTNULL(t)),
|
||||
zone_(t->zone()),
|
||||
next_data_offset_(0),
|
||||
|
@ -180,7 +186,9 @@ ImageWriter::ImageWriter(Thread* t, bool generates_assembly)
|
|||
objects_(),
|
||||
instructions_(),
|
||||
#if defined(DART_PRECOMPILER)
|
||||
namer_(t->zone(), /*for_assembly=*/generates_assembly),
|
||||
namer_(t->zone(),
|
||||
deobfuscation_trie,
|
||||
/*for_assembly=*/generates_assembly),
|
||||
#endif
|
||||
image_type_(TagObjectTypeAsReadOnly(zone_, "Image")),
|
||||
instructions_section_type_(
|
||||
|
@ -1099,25 +1107,32 @@ class DwarfAssemblyStream : public DwarfWriteStream {
|
|||
DISALLOW_COPY_AND_ASSIGN(DwarfAssemblyStream);
|
||||
};
|
||||
|
||||
static inline Dwarf* AddDwarfIfUnstripped(Zone* zone, bool strip, Elf* elf) {
|
||||
static inline Dwarf* AddDwarfIfUnstripped(
|
||||
Zone* zone,
|
||||
bool strip,
|
||||
Elf* elf,
|
||||
const Trie<const char>* deobfuscation_trie) {
|
||||
if (!strip) {
|
||||
if (elf != nullptr) {
|
||||
// Reuse the existing DWARF object.
|
||||
ASSERT(elf->dwarf() != nullptr);
|
||||
return elf->dwarf();
|
||||
}
|
||||
return new (zone) Dwarf(zone);
|
||||
return new (zone) Dwarf(zone, deobfuscation_trie);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AssemblyImageWriter::AssemblyImageWriter(Thread* thread,
|
||||
BaseWriteStream* stream,
|
||||
bool strip,
|
||||
Elf* debug_elf)
|
||||
: ImageWriter(thread, /*generates_assembly=*/true),
|
||||
AssemblyImageWriter::AssemblyImageWriter(
|
||||
Thread* thread,
|
||||
BaseWriteStream* stream,
|
||||
const Trie<const char>* deobfuscation_trie,
|
||||
bool strip,
|
||||
Elf* debug_elf)
|
||||
: ImageWriter(thread, /*generates_assembly=*/true, deobfuscation_trie),
|
||||
assembly_stream_(stream),
|
||||
assembly_dwarf_(AddDwarfIfUnstripped(zone_, strip, debug_elf)),
|
||||
assembly_dwarf_(
|
||||
AddDwarfIfUnstripped(zone_, strip, debug_elf, deobfuscation_trie)),
|
||||
debug_elf_(debug_elf),
|
||||
label_to_symbol_name_(zone_) {
|
||||
// Set up the label mappings for the section symbols for use in relocations.
|
||||
|
@ -1185,13 +1200,37 @@ void ImageWriter::SnapshotTextObjectNamer::AddNonUniqueNameFor(
|
|||
}
|
||||
} else if (object.IsClass()) {
|
||||
const char* name = Class::Cast(object).UserVisibleNameCString();
|
||||
buffer->AddString(name);
|
||||
const char* deobfuscated_name =
|
||||
ImageWriter::Deobfuscate(zone_, deobfuscation_trie_, name);
|
||||
buffer->AddString(deobfuscated_name);
|
||||
} else if (object.IsAbstractType()) {
|
||||
AbstractType::Cast(object).PrintName(Object::kUserVisibleName, buffer);
|
||||
const AbstractType& type = AbstractType::Cast(object);
|
||||
if (deobfuscation_trie_ == nullptr) {
|
||||
// Print directly to the output buffer.
|
||||
type.PrintName(Object::kUserVisibleName, buffer);
|
||||
} else {
|
||||
// Use an intermediate buffer for deobfuscation purposes.
|
||||
ZoneTextBuffer temp_buffer(zone_);
|
||||
type.PrintName(Object::kUserVisibleName, &temp_buffer);
|
||||
const char* deobfuscated_name = ImageWriter::Deobfuscate(
|
||||
zone_, deobfuscation_trie_, temp_buffer.buffer());
|
||||
buffer->AddString(deobfuscated_name);
|
||||
}
|
||||
} else if (object.IsFunction()) {
|
||||
const Function& func = Function::Cast(object);
|
||||
func.PrintName({Object::kUserVisibleName, Object::NameDisambiguation::kNo},
|
||||
buffer);
|
||||
NameFormattingParams params(
|
||||
{Object::kUserVisibleName, Object::NameDisambiguation::kNo});
|
||||
if (deobfuscation_trie_ == nullptr) {
|
||||
// Print directly to the output buffer.
|
||||
func.PrintName(params, buffer);
|
||||
} else {
|
||||
// Use an intermediate buffer for deobfuscation purposes.
|
||||
ZoneTextBuffer temp_buffer(zone_);
|
||||
func.PrintName(params, &temp_buffer);
|
||||
const char* deobfuscated_name = ImageWriter::Deobfuscate(
|
||||
zone_, deobfuscation_trie_, temp_buffer.buffer());
|
||||
buffer->AddString(deobfuscated_name);
|
||||
}
|
||||
} else if (object.IsCompressedStackMaps()) {
|
||||
buffer->AddString("CompressedStackMaps");
|
||||
} else if (object.IsPcDescriptors()) {
|
||||
|
@ -1274,6 +1313,52 @@ const char* ImageWriter::SnapshotTextObjectNamer::SnapshotNameFor(
|
|||
return printer.buffer();
|
||||
}
|
||||
|
||||
Trie<const char>* ImageWriter::CreateReverseObfuscationTrie(Thread* thread) {
|
||||
auto* const zone = thread->zone();
|
||||
auto* const map_array = thread->isolate_group()->obfuscation_map();
|
||||
if (map_array == nullptr) return nullptr;
|
||||
|
||||
Trie<const char>* trie = nullptr;
|
||||
for (intptr_t i = 0; map_array[i] != nullptr; i += 2) {
|
||||
auto const key = map_array[i];
|
||||
auto const value = map_array[i + 1];
|
||||
ASSERT(value != nullptr);
|
||||
// Don't include identity mappings.
|
||||
if (strcmp(key, value) == 0) continue;
|
||||
// Otherwise, any value in the obfuscation map should be a valid key.
|
||||
ASSERT(Trie<const char>::IsValidKey(value));
|
||||
trie = Trie<const char>::AddString(zone, trie, value, key);
|
||||
}
|
||||
return trie;
|
||||
}
|
||||
|
||||
const char* ImageWriter::Deobfuscate(Zone* zone,
|
||||
const Trie<const char>* trie,
|
||||
const char* cstr) {
|
||||
if (trie == nullptr) return cstr;
|
||||
TextBuffer buffer(256);
|
||||
// Used to avoid Zone-allocating strings if no deobfuscation was performed.
|
||||
bool changed = false;
|
||||
intptr_t i = 0;
|
||||
while (cstr[i] != '\0') {
|
||||
intptr_t offset;
|
||||
auto const value = trie->Lookup(cstr + i, &offset);
|
||||
if (offset == 0) {
|
||||
// The first character was an invalid key element (that isn't the null
|
||||
// terminator due to the while condition), copy it and skip to the next.
|
||||
buffer.AddChar(cstr[i++]);
|
||||
} else if (value != nullptr) {
|
||||
changed = true;
|
||||
buffer.AddString(value);
|
||||
} else {
|
||||
buffer.AddRaw(reinterpret_cast<const uint8_t*>(cstr + i), offset);
|
||||
}
|
||||
i += offset;
|
||||
}
|
||||
if (!changed) return cstr;
|
||||
return OS::SCreate(zone, "%s", buffer.buffer());
|
||||
}
|
||||
|
||||
void AssemblyImageWriter::WriteBss(bool vm) {
|
||||
EnterSection(ProgramSection::Bss, vm, ImageWriter::kBssAlignment);
|
||||
auto const entry_count =
|
||||
|
@ -1636,12 +1721,22 @@ intptr_t AssemblyImageWriter::Align(intptr_t alignment,
|
|||
}
|
||||
#endif // defined(DART_PRECOMPILER)
|
||||
|
||||
#if defined(DART_PRECOMPILER)
|
||||
BlobImageWriter::BlobImageWriter(Thread* thread,
|
||||
NonStreamingWriteStream* vm_instructions,
|
||||
NonStreamingWriteStream* isolate_instructions,
|
||||
const Trie<const char>* deobfuscation_trie,
|
||||
Elf* debug_elf,
|
||||
Elf* elf)
|
||||
: ImageWriter(thread, /*generates_assembly=*/false, deobfuscation_trie),
|
||||
#else
|
||||
BlobImageWriter::BlobImageWriter(Thread* thread,
|
||||
NonStreamingWriteStream* vm_instructions,
|
||||
NonStreamingWriteStream* isolate_instructions,
|
||||
Elf* debug_elf,
|
||||
Elf* elf)
|
||||
: ImageWriter(thread, /*generates_assembly=*/false),
|
||||
#endif
|
||||
vm_instructions_(vm_instructions),
|
||||
isolate_instructions_(isolate_instructions),
|
||||
elf_(elf),
|
||||
|
|
|
@ -233,9 +233,134 @@ struct ImageWriterCommand {
|
|||
};
|
||||
};
|
||||
|
||||
#if defined(DART_PRECOMPILER)
|
||||
template <typename T>
|
||||
class Trie : public ZoneAllocated {
|
||||
public:
|
||||
// Returns whether [key] is a valid trie key (that is, a C string that
|
||||
// contains only characters for which charIndex returns a non-negative value).
|
||||
static bool IsValidKey(const char* key) {
|
||||
for (intptr_t i = 0; key[i] != '\0'; i++) {
|
||||
if (ChildIndex(key[i]) < 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Adds a binding of [key] to [value] in [trie]. Assumes that the string in
|
||||
// [key] is a valid trie key and does not already have a value in [trie].
|
||||
//
|
||||
// If [trie] is nullptr, then a new trie is created and a pointer to the new
|
||||
// trie is returned. Otherwise, [trie] will be returned.
|
||||
static Trie<T>* AddString(Zone* zone,
|
||||
Trie<T>* trie,
|
||||
const char* key,
|
||||
const T* value);
|
||||
|
||||
// Adds a binding of [key] to [value]. Assumes that the string in [key] is a
|
||||
// valid trie key and does not already have a value in this trie.
|
||||
void AddString(Zone* zone, const char* key, const T* value) {
|
||||
AddString(zone, this, key, value);
|
||||
}
|
||||
|
||||
// Looks up the value stored for [key] in [trie]. If one is not found, then
|
||||
// nullptr is returned.
|
||||
//
|
||||
// If [end] is not nullptr, then the longest prefix of [key] that is a valid
|
||||
// trie key prefix will be used for the lookup and the value pointed to by
|
||||
// [end] is set to the index after that prefix. Otherwise, the whole [key]
|
||||
// is used.
|
||||
static const T* Lookup(const Trie<T>* trie,
|
||||
const char* key,
|
||||
intptr_t* end = nullptr);
|
||||
|
||||
// Looks up the value stored for [key]. If one is not found, then nullptr is
|
||||
// returned.
|
||||
//
|
||||
// If [end] is not nullptr, then the longest prefix of [key] that is a valid
|
||||
// trie key prefix will be used for the lookup and the value pointed to by
|
||||
// [end] is set to the index after that prefix. Otherwise, the whole [key]
|
||||
// is used.
|
||||
const T* Lookup(const char* key, intptr_t* end = nullptr) const {
|
||||
return Lookup(this, key, end);
|
||||
}
|
||||
|
||||
private:
|
||||
// Currently, only the following characters can appear in obfuscated names:
|
||||
// '_', '@', '0-9', 'a-z', 'A-Z'
|
||||
static constexpr intptr_t kNumValidChars = 64;
|
||||
|
||||
Trie() {
|
||||
for (intptr_t i = 0; i < kNumValidChars; i++) {
|
||||
children_[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static intptr_t ChildIndex(char c) {
|
||||
if (c == '_') return 0;
|
||||
if (c == '@') return 1;
|
||||
if (c >= '0' && c <= '9') return ('9' - c) + 2;
|
||||
if (c >= 'a' && c <= 'z') return ('z' - c) + 12;
|
||||
if (c >= 'A' && c <= 'Z') return ('Z' - c) + 38;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const T* value_ = nullptr;
|
||||
Trie<T>* children_[kNumValidChars];
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Trie<T>* Trie<T>::AddString(Zone* zone,
|
||||
Trie<T>* trie,
|
||||
const char* key,
|
||||
const T* value) {
|
||||
ASSERT(key != nullptr);
|
||||
if (trie == nullptr) {
|
||||
trie = new (zone) Trie<T>();
|
||||
}
|
||||
if (*key == '\0') {
|
||||
ASSERT(trie->value_ == nullptr);
|
||||
trie->value_ = value;
|
||||
} else {
|
||||
auto const index = ChildIndex(*key);
|
||||
ASSERT(index >= 0 && index < kNumValidChars);
|
||||
trie->children_[index] =
|
||||
AddString(zone, trie->children_[index], key + 1, value);
|
||||
}
|
||||
|
||||
return trie;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T* Trie<T>::Lookup(const Trie<T>* trie, const char* key, intptr_t* end) {
|
||||
intptr_t i = 0;
|
||||
for (; key[i] != '\0'; i++) {
|
||||
auto const index = ChildIndex(key[i]);
|
||||
ASSERT(index < kNumValidChars);
|
||||
if (index < 0) {
|
||||
if (end == nullptr) return nullptr;
|
||||
break;
|
||||
}
|
||||
// Still find the longest valid trie prefix when no stored value.
|
||||
if (trie == nullptr) continue;
|
||||
trie = trie->children_[index];
|
||||
}
|
||||
if (end != nullptr) {
|
||||
*end = i;
|
||||
}
|
||||
if (trie == nullptr) return nullptr;
|
||||
return trie->value_;
|
||||
}
|
||||
#endif
|
||||
|
||||
class ImageWriter : public ValueObject {
|
||||
public:
|
||||
explicit ImageWriter(Thread* thread, bool generates_assembly);
|
||||
#if defined(DART_PRECOMPILER)
|
||||
ImageWriter(Thread* thread,
|
||||
bool generates_assembly,
|
||||
const Trie<const char>* deobfuscation_trie = nullptr);
|
||||
#else
|
||||
ImageWriter(Thread* thread, bool generates_assembly);
|
||||
#endif
|
||||
virtual ~ImageWriter() {}
|
||||
|
||||
// Alignment constants used in writing ELF or assembly snapshots.
|
||||
|
@ -328,6 +453,11 @@ class ImageWriter : public ValueObject {
|
|||
// The initial 1 is to ensure the result is positive.
|
||||
return 1 + 2 * static_cast<int>(section) + ((shared || vm) ? 0 : 1);
|
||||
}
|
||||
|
||||
static Trie<const char>* CreateReverseObfuscationTrie(Thread* thread);
|
||||
static const char* Deobfuscate(Zone* zone,
|
||||
const Trie<const char>* trie,
|
||||
const char* str);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
@ -514,8 +644,11 @@ class ImageWriter : public ValueObject {
|
|||
#if defined(DART_PRECOMPILER)
|
||||
class SnapshotTextObjectNamer : ValueObject {
|
||||
public:
|
||||
explicit SnapshotTextObjectNamer(Zone* zone, bool for_assembly)
|
||||
explicit SnapshotTextObjectNamer(Zone* zone,
|
||||
const Trie<const char>* deobfuscation_trie,
|
||||
bool for_assembly)
|
||||
: zone_(ASSERT_NOTNULL(zone)),
|
||||
deobfuscation_trie_(deobfuscation_trie),
|
||||
lib_(Library::Handle(zone)),
|
||||
cls_(Class::Handle(zone)),
|
||||
parent_(Function::Handle(zone)),
|
||||
|
@ -548,6 +681,7 @@ class ImageWriter : public ValueObject {
|
|||
void ModifyForAssembly(BaseTextBuffer* buffer);
|
||||
|
||||
Zone* const zone_;
|
||||
const Trie<const char>* const deobfuscation_trie_;
|
||||
Library& lib_;
|
||||
Class& cls_;
|
||||
Function& parent_;
|
||||
|
@ -643,6 +777,7 @@ class AssemblyImageWriter : public ImageWriter {
|
|||
public:
|
||||
AssemblyImageWriter(Thread* thread,
|
||||
BaseWriteStream* stream,
|
||||
const Trie<const char>* deobfuscation_trie = nullptr,
|
||||
bool strip = false,
|
||||
Elf* debug_elf = nullptr);
|
||||
void Finalize();
|
||||
|
@ -699,11 +834,20 @@ class AssemblyImageWriter : public ImageWriter {
|
|||
|
||||
class BlobImageWriter : public ImageWriter {
|
||||
public:
|
||||
#if defined(DART_PRECOMPILER)
|
||||
BlobImageWriter(Thread* thread,
|
||||
NonStreamingWriteStream* vm_instructions,
|
||||
NonStreamingWriteStream* isolate_instructions,
|
||||
const Trie<const char>* deobfuscation_trie = nullptr,
|
||||
Elf* debug_elf = nullptr,
|
||||
Elf* elf = nullptr);
|
||||
#else
|
||||
BlobImageWriter(Thread* thread,
|
||||
NonStreamingWriteStream* vm_instructions,
|
||||
NonStreamingWriteStream* isolate_instructions,
|
||||
Elf* debug_elf = nullptr,
|
||||
Elf* elf = nullptr);
|
||||
#endif
|
||||
|
||||
private:
|
||||
virtual void WriteBss(bool vm);
|
||||
|
|
Loading…
Reference in a new issue