Files
sdk/pkg/vm/test/obfuscation_test.dart
T
Tony 08139af589 Add compact AOT patching support and related APIs
- Introduced Dart_SetObfuscationMap to restore obfuscation maps before AOT precompilation.
- Added Dart_AotPatchInstallOptions structure for AOT patch installation options.
- Implemented Dart_AotPatchingEnabled to check if compact AOT patching is supported.
- Created Dart_SetAotPatchKeyCallback for AES key resolution during AOT patch installation.
- Developed Dart_InstallAotPatch for validating and installing encrypted AOT patches.
- Added Dart_FreeAotPatchPayload to free memory allocated for patch payloads.
- Updated runtime_args.gni to include dart_enable_aot_patching flag.
- Added tests for AOT patching functionality and ensured exported symbols include new APIs.
- Refactored existing code to accommodate new AOT patching features and improve error handling.
2026-06-24 03:00:14 +08:00

305 lines
8.6 KiB
Dart

// Copyright (c) 2022, 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.
import 'dart:convert';
import "dart:io";
import 'package:front_end/src/api_unstable/vm.dart'
show computePlatformBinariesLocation;
main() {
final Directory tmpDir = Directory.systemTemp.createTempSync(
"obfuscationtest",
);
try {
final Uri tmpDirUri = tmpDir.uri;
final Uri secretfilename = tmpDirUri.resolve("secretfilename.dart");
final File secretfilenameFile = new File.fromUri(secretfilename);
secretfilenameFile.writeAsStringSync("""
import "secretfilename2.dart";
main() {
print("Hello, World!");
verySecretFoo();
}
""");
final Uri secretfilename2 = tmpDirUri.resolve("secretfilename2.dart");
final File secretfilename2File = new File.fromUri(secretfilename2);
secretfilename2File.writeAsStringSync("""
@pragma('vm:entry-point')
void verySecretFoo() {
print("foo!");
alsoVerySecretFoo();
}
void alsoVerySecretFoo() {
print("foo too!");
}
""");
List<MappingPair> mapping = getSnapshotMap(
tmpDir,
secretfilenameFile,
outputPrefix: "base",
);
bool good = verify(
mapping,
{
// This contains @pragma('vm:entry-point') and the uri should not change.
secretfilename2.toString(),
// This contains @pragma('vm:entry-point') and the name should not change.
"verySecretFoo",
},
{
// This is not special and should have been obfuscated.
secretfilename.toString(),
// This is not special and should have been obfuscated.
"alsoVerySecretFoo",
},
);
if (!good) throw "Obfuscation didn't work as expected";
secretfilename2File.writeAsStringSync("""
@pragma('vm:entry-point')
void verySecretFoo() {
print("foo!");
alsoVerySecretFoo();
newPatchOnlySecretFoo();
}
void alsoVerySecretFoo() {
print("foo too!");
}
void newPatchOnlySecretFoo() {
print("patched foo!");
}
""");
final File baseObfuscationMapFile = new File.fromUri(
tmpDirUri.resolve("obfuscation-base.map"),
);
List<MappingPair> patchMapping = getSnapshotMap(
tmpDir,
secretfilenameFile,
outputPrefix: "patch",
loadObfuscationMapFile: baseObfuscationMapFile,
);
good = verifyLoadedObfuscationMap(
mapping,
patchMapping,
"newPatchOnlySecretFoo",
);
if (!good) throw "Loaded obfuscation map didn't work as expected";
print("Good");
} finally {
tmpDir.deleteSync(recursive: true);
}
}
List<MappingPair> getSnapshotMap(
Directory tmpDir,
File compileDartFile, {
required String outputPrefix,
File? loadObfuscationMapFile,
}) {
final Uri genKernel = Platform.script.resolve('../bin/gen_kernel.dart');
final File genKernelFile = new File.fromUri(genKernel);
if (!genKernelFile.existsSync()) {
throw "Didn't find gen_kernel at $genKernel";
}
File resolvedExecutableFile = new File(Platform.resolvedExecutable);
Uri resolvedExecutable = resolvedExecutableFile.uri;
String genSnapshotFilename = "gen_snapshot";
if (Platform.isWindows) genSnapshotFilename += ".exe";
Uri genSnapshot = resolvedExecutable.resolve(genSnapshotFilename);
File genSnapshotFile = new File.fromUri(genSnapshot);
if (!genSnapshotFile.existsSync()) {
print(
"Didn't find gen_kernel at $genSnapshot... Trying utils/$genSnapshotFilename",
);
genSnapshot = resolvedExecutable.resolve("utils/$genSnapshotFilename");
genSnapshotFile = new File.fromUri(genSnapshot);
if (!genSnapshotFile.existsSync()) {
throw "Didn't find gen_kernel at $genSnapshot";
}
}
final Uri platformDill = computePlatformBinariesLocation().resolve(
'vm_platform_product.dill',
);
final File platformDillFile = new File.fromUri(platformDill);
if (!platformDillFile.existsSync()) {
throw "Didn't find vm_platform_product at $platformDill";
}
final Uri tmpDirUri = tmpDir.uri;
final Uri kernelDill = tmpDirUri.resolve("kernel-$outputPrefix.dill");
final File kernelDillFile = new File.fromUri(kernelDill);
print("Running gen_kernel");
// Extracted from pkg/dart2native/lib/dart2native.dart.
final ProcessResult kernelRun = Process.runSync(Platform.resolvedExecutable, [
genKernelFile.path,
"--platform",
platformDillFile.path,
"--aot",
"-Ddart.vm.product=true",
"-o",
kernelDillFile.path,
"--invocation-modes=compile",
"--verbosity=all",
compileDartFile.path,
]);
if (kernelRun.exitCode != 0) {
throw "Got exit code ${kernelRun.exitCode}\n"
"stdout: ${kernelRun.stdout}\n"
"stderr: ${kernelRun.stderr}";
}
final Uri aotElf = tmpDirUri.resolve("aot-$outputPrefix.elf");
final File aotElfFile = new File.fromUri(aotElf);
final Uri obfuscationMap = tmpDirUri.resolve("obfuscation-$outputPrefix.map");
final File obfuscationMapFile = new File.fromUri(obfuscationMap);
print("Running $genSnapshot");
// Extracted from pkg/dart2native/lib/dart2native.dart.
final List<String> genSnapshotArgs = [
"--snapshot-kind=app-aot-elf",
"--elf=${aotElfFile.path}",
"--dwarf-stack-traces",
"--obfuscate",
"--strip",
"--save-obfuscation-map=${obfuscationMapFile.path}",
kernelDillFile.path,
];
if (loadObfuscationMapFile != null) {
genSnapshotArgs.insert(
genSnapshotArgs.length - 1,
"--load-obfuscation-map=${loadObfuscationMapFile.path}",
);
}
final ProcessResult snapshotRun = Process.runSync(
genSnapshotFile.path,
genSnapshotArgs,
);
if (snapshotRun.exitCode != 0) {
throw "Got exit code ${snapshotRun.exitCode}\n"
"stdout: ${snapshotRun.stdout}\n"
"stderr: ${snapshotRun.stderr}";
}
print("Reading $obfuscationMap");
return readJsonMapping(obfuscationMapFile);
}
List<MappingPair> readJsonMapping(File file) {
List<MappingPair> result = [];
List<dynamic> json = jsonDecode(file.readAsStringSync());
for (int i = 0; i < json.length; i += 2) {
result.add(new MappingPair(json[i] as String, json[i + 1] as String));
}
return result;
}
Map<String, String> toMap(List<MappingPair> mapping) {
return {for (MappingPair pair in mapping) pair.from: pair.to};
}
class MappingPair {
final String from;
final String to;
MappingPair(this.from, this.to);
String toString() => "MappingPair[$from->$to]";
}
bool verify(
List<MappingPair> mapping,
Set<String> expectedIdentity,
Set<String> expectedDifferent,
) {
bool good = true;
Set<String> missingKeys = new Set<String>.of(expectedIdentity)
..addAll(expectedDifferent);
for (MappingPair entry in mapping) {
missingKeys.remove(entry.from);
if (expectedIdentity.contains(entry.from) && entry.from != entry.to) {
print(
"Expected ${entry.from} to map to itself, "
"but mapped to ${entry.to}",
);
good = false;
}
if (expectedDifferent.contains(entry.from) && entry.from == entry.to) {
print(
"Expected ${entry.from} to map to something different, "
"but it didn't.",
);
good = false;
}
}
if (missingKeys.isNotEmpty) {
print("Expected to have seen the following entries which wasn't found:");
for (String missingKey in missingKeys) {
print("- $missingKey");
}
good = false;
}
return good;
}
bool verifyLoadedObfuscationMap(
List<MappingPair> baseMapping,
List<MappingPair> patchMapping,
String patchOnlyName,
) {
bool good = true;
final Map<String, String> patchMap = toMap(patchMapping);
for (MappingPair baseEntry in baseMapping) {
final String? patchValue = patchMap[baseEntry.from];
if (patchValue == null) {
print("Patch map is missing loaded entry ${baseEntry.from}");
good = false;
} else if (patchValue != baseEntry.to) {
print(
"Expected ${baseEntry.from} to keep ${baseEntry.to}, "
"but patch map used $patchValue",
);
good = false;
}
}
final String? patchOnlyValue = patchMap[patchOnlyName];
if (patchOnlyValue == null) {
print("Patch map is missing $patchOnlyName");
good = false;
} else {
if (patchOnlyValue == patchOnlyName) {
print("Expected $patchOnlyName to be obfuscated");
good = false;
}
final Set<String> baseValues = baseMapping
.where((MappingPair pair) => pair.from != pair.to)
.map((MappingPair pair) => pair.to)
.toSet();
if (baseValues.contains(patchOnlyValue)) {
print(
"Patch-only rename $patchOnlyName->$patchOnlyValue collides with "
"the loaded map",
);
good = false;
}
}
return good;
}