Reland "[vm, gen_snapshot] Add app-aot-macho-dylib option for AOT snapshots."
This is a reland of commit 38ef28a058
Fixes:
* Fix comparisons in ASSERT_EQUAL statements on 32-bit architectures.
* Take simulated architectures into account when deciding whether
to use dlopen() for native shared object formats.
* Fix struct/field name collision for GCC.
* Use CPU_TYPE_ANY/CPU_SUBTYPE_ANY for architectures that do not
have more specific cpu_type_t/cpu_subtype_t constants defined.
Original change's description:
> [vm, gen_snapshot] Add app-aot-macho-dylib option for AOT snapshots.
>
> This is the initial framework for creating snapshots as Mach-O dynamic
> libraries. Note that this framework is not 100% feature complete
> compared to generating Mach-O snapshots via assembly. In particular,
> the directly-compiled Mach-O dylib does not yet contain compact
> unwinding information.
>
> Other changes:
>
> * Adds UuidCommand to the native_stack_traces package's Mach-O reader,
> which now appropriately returns the UUID as the build ID for Mach-O
> shared objects.
>
> * Adds Utils::Basename(path) for portably retrieving the basename
> from a path. (Returns nullptr for all arguments where it is not
> currently implemented on Fuchsia or Windows.)
>
> * Adjusts vm/timeline.h to avoid pulling in <mach_o/loader.h> on MacOS,
> as that interferes with uses of the namespaced Mach-O definitions
> in platform/mach_o.h.
>
> * Only attempt to dlopen() a snapshot if ELF is the native format
> for the host platform or the snapshot is not an ELF shared object.
> If dlopen() is used, report the error message if it fails rather
> than attempting to manually load the snapshot as an ELF shared object.
>
> * Fix the magic number stored in DylibAppSnapshot for loaded non-ELF
> dynamic libraries.
>
> * Remove the detection of reverse-endian Mach-O magic numbers in
> DartUtils::SniffForMagicNumber(), since all our Mach-O related code
> assumes host-endian Mach-O files and so there's no point other than
> to give a slightly better error message when failing.
>
> TEST=vm/dart/exported_symbols_test
> vm/dart/unobfuscated_static_symbols_test
> vm/dart/use_dwarf_stack_traces_flag_test
> vm/cc/CanDetectMachOFiles
>
> Issue: https://github.com/dart-lang/sdk/issues/60307
> Change-Id: Idf5b49d6c6d035ab033509613212b95520d65965
> Cq-Include-Trybots: luci.dart.try:vm-aot-linux-debug-x64-try,vm-mac-release-arm64-try,vm-aot-mac-release-arm64-try,vm-aot-mac-release-x64-try,vm-aot-dwarf-linux-product-x64-try,vm-linux-debug-x64-try,vm-mac-debug-arm64-try,vm-fuchsia-release-x64-try,vm-fuchsia-release-arm64-try
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/415020
> Reviewed-by: Slava Egorov <vegorov@google.com>
> Commit-Queue: Tess Strickland <sstrickl@google.com>
TEST=vm/dart/exported_symbols_test
vm/dart/unobfuscated_static_symbols_test
vm/dart/use_dwarf_stack_traces_flag_test
vm/cc/CanDetectMachOFiles
ci on trybots that failed on the original CL
Cq-Include-Trybots: luci.dart.try:vm-aot-linux-debug-x64-try,vm-mac-release-arm64-try,vm-aot-mac-release-arm64-try,vm-aot-mac-release-x64-try,vm-aot-dwarf-linux-product-x64-try,vm-linux-debug-x64-try,vm-mac-debug-arm64-try,vm-fuchsia-release-x64-try,vm-fuchsia-release-arm64-try,vm-linux-debug-ia32-try,vm-aot-linux-debug-simarm_x64-try,vm-aot-linux-debug-simriscv32-try,vm-aot-linux-debug-simriscv64-try,vm-aot-linux-release-simarm_x64-try,vm-gcc-linux-try,vm-ubsan-linux-release-arm64-try
Change-Id: Iaffea0ddc6173100c8b5b2a9fe46c45f4f611a2e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/431240
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Tess Strickland <sstrickl@google.com>
This commit is contained in:
committed by
Commit Queue
parent
38ea3a8f5e
commit
6b53073eae
@@ -1,3 +1,6 @@
|
||||
## 0.6.1
|
||||
- Add handling for Mach-O UUID load commands.
|
||||
|
||||
## 0.6.1-wip
|
||||
- Update SDK constraint to `^3.5.0`.
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@ class LoadCommand {
|
||||
static const LC_SEGMENT = 0x1;
|
||||
static const LC_SYMTAB = 0x2;
|
||||
static const LC_SEGMENT_64 = 0x19;
|
||||
static const LC_UUID = 0x1b;
|
||||
|
||||
static LoadCommand fromReader(Reader reader) {
|
||||
final start = reader.offset; // cmdsize includes size of cmd and cmdsize.
|
||||
@@ -139,6 +140,9 @@ class LoadCommand {
|
||||
case LC_SYMTAB:
|
||||
command = SymbolTableCommand.fromReader(reader, cmd, cmdsize);
|
||||
break;
|
||||
case LC_UUID:
|
||||
command = UuidCommand.fromReader(reader, cmd, cmdsize);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -324,6 +328,29 @@ class SymbolTableCommand extends LoadCommand {
|
||||
}
|
||||
}
|
||||
|
||||
class UuidCommand extends LoadCommand {
|
||||
Uint8List uuid;
|
||||
|
||||
static const kUuidSize = 16;
|
||||
|
||||
UuidCommand._(super.cmd, super.cmdsize, this.uuid) : super._();
|
||||
|
||||
static UuidCommand fromReader(Reader reader, int cmd, int cmdsize) {
|
||||
final uuid = Uint8List.sublistView(
|
||||
reader.bytes, reader.offset, reader.offset + kUuidSize);
|
||||
return UuidCommand._(cmd, cmdsize, uuid);
|
||||
}
|
||||
|
||||
String get uuidString => uuid.map((i) => paddedHex(i, 1)).join();
|
||||
|
||||
@override
|
||||
void writeToStringBuffer(StringBuffer buffer) {
|
||||
buffer
|
||||
..write('UUID: ')
|
||||
..write(uuidString);
|
||||
}
|
||||
}
|
||||
|
||||
class MachOHeader {
|
||||
final int magic;
|
||||
final int cputype;
|
||||
@@ -523,7 +550,8 @@ class MachO extends DwarfContainer {
|
||||
_symbolTable[constants.isolateSymbolName]?.value;
|
||||
|
||||
@override
|
||||
String? get buildId => null;
|
||||
String? get buildId =>
|
||||
_commands.whereType<UuidCommand>().firstOrNull?.uuidString;
|
||||
|
||||
@override
|
||||
DwarfContainerStringTable? get debugStringTable => _debugStringTable;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: native_stack_traces
|
||||
version: 0.6.1-wip
|
||||
version: 0.6.1
|
||||
description: Utilities for working with non-symbolic stack traces.
|
||||
repository: https://github.com/dart-lang/sdk/tree/main/pkg/native_stack_traces
|
||||
|
||||
@@ -13,7 +13,7 @@ executables:
|
||||
|
||||
dependencies:
|
||||
args: ^2.0.0
|
||||
path: ^1.8.0
|
||||
path: ^1.9.0
|
||||
|
||||
# We use 'any' version constraints here as we get our package versions from
|
||||
# the dart-lang/sdk repo's DEPS file. Note that this is a special case; the
|
||||
|
||||
@@ -424,6 +424,13 @@ typedef Dart_Handle (*Dart_CreateAppAOTSnapshotAsElfsType)(
|
||||
bool,
|
||||
Dart_StreamingWriteCallback,
|
||||
Dart_StreamingCloseCallback);
|
||||
typedef Dart_Handle (*Dart_CreateAppAOTSnapshotAsBinaryType)(
|
||||
Dart_AotBinaryFormat,
|
||||
Dart_StreamingWriteCallback,
|
||||
void*,
|
||||
bool,
|
||||
void*,
|
||||
const char*);
|
||||
typedef Dart_Handle (*Dart_CreateVMAOTSnapshotAsAssemblyType)(
|
||||
Dart_StreamingWriteCallback,
|
||||
void*);
|
||||
@@ -724,6 +731,8 @@ static Dart_CreateAppAOTSnapshotAsElfType Dart_CreateAppAOTSnapshotAsElfFn =
|
||||
NULL;
|
||||
static Dart_CreateAppAOTSnapshotAsElfsType Dart_CreateAppAOTSnapshotAsElfsFn =
|
||||
NULL;
|
||||
static Dart_CreateAppAOTSnapshotAsBinaryType
|
||||
Dart_CreateAppAOTSnapshotAsBinaryFn = NULL;
|
||||
static Dart_CreateVMAOTSnapshotAsAssemblyType
|
||||
Dart_CreateVMAOTSnapshotAsAssemblyFn = NULL;
|
||||
static Dart_SortClassesType Dart_SortClassesFn = NULL;
|
||||
@@ -1293,6 +1302,9 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
|
||||
Dart_CreateAppAOTSnapshotAsElfsFn =
|
||||
(Dart_CreateAppAOTSnapshotAsElfsType)GetProcAddress(
|
||||
process, "Dart_CreateAppAOTSnapshotAsElfs");
|
||||
Dart_CreateAppAOTSnapshotAsBinaryFn =
|
||||
(Dart_CreateAppAOTSnapshotAsBinaryType)GetProcAddress(
|
||||
process, "Dart_CreateAppAOTSnapshotAsBinary");
|
||||
Dart_CreateVMAOTSnapshotAsAssemblyFn =
|
||||
(Dart_CreateVMAOTSnapshotAsAssemblyType)GetProcAddress(
|
||||
process, "Dart_CreateVMAOTSnapshotAsAssembly");
|
||||
@@ -2551,6 +2563,18 @@ Dart_Handle Dart_CreateAppAOTSnapshotAsElfs(
|
||||
close_callback);
|
||||
}
|
||||
|
||||
Dart_Handle Dart_CreateAppAOTSnapshotAsBinary(
|
||||
Dart_AotBinaryFormat format,
|
||||
Dart_StreamingWriteCallback callback,
|
||||
void* callback_data,
|
||||
bool stripped,
|
||||
void* debug_callback_data,
|
||||
const char* identifier) {
|
||||
return Dart_CreateAppAOTSnapshotAsBinaryFn(format, callback, callback_data,
|
||||
stripped, debug_callback_data,
|
||||
identifier);
|
||||
}
|
||||
|
||||
Dart_Handle Dart_CreateVMAOTSnapshotAsAssembly(
|
||||
Dart_StreamingWriteCallback callback,
|
||||
void* callback_data) {
|
||||
|
||||
+16
-16
@@ -14,6 +14,7 @@
|
||||
#include "include/dart_native_api.h"
|
||||
#include "platform/assert.h"
|
||||
#include "platform/globals.h"
|
||||
#include "platform/mach_o.h"
|
||||
#include "platform/utils.h"
|
||||
|
||||
// Return the error from the containing function if handle is in error handle.
|
||||
@@ -34,9 +35,6 @@ dart::SimpleHashMap* DartUtils::environment_ = nullptr;
|
||||
|
||||
MagicNumberData appjit_magic_number = {8, {0xdc, 0xdc, 0xf6, 0xf6, 0, 0, 0, 0}};
|
||||
MagicNumberData aotelf_magic_number = {4, {0x7F, 0x45, 0x4C, 0x46, 0x0}};
|
||||
MagicNumberData aotmacho32_magic_number = {4, {0xFE, 0xED, 0xFA, 0xCE}};
|
||||
MagicNumberData aotmacho64_magic_number = {4, {0xFE, 0xED, 0xFA, 0xCF}};
|
||||
MagicNumberData aotmacho64_arm64_magic_number = {4, {0xCF, 0xFA, 0xED, 0xFE}};
|
||||
MagicNumberData aotcoff_arm32_magic_number = {2, {0x01, 0xC0}};
|
||||
MagicNumberData aotcoff_arm64_magic_number = {2, {0xAA, 0x64}};
|
||||
MagicNumberData aotcoff_riscv32_magic_number = {2, {0x50, 0x32}};
|
||||
@@ -404,9 +402,8 @@ DartUtils::MagicNumber DartUtils::SniffForMagicNumber(const char* filename) {
|
||||
MagicNumber magic_number = DartUtils::kUnknownMagicNumber;
|
||||
ASSERT(kMaxMagicNumberSize == appjit_magic_number.length);
|
||||
ASSERT(aotelf_magic_number.length <= appjit_magic_number.length);
|
||||
ASSERT(aotmacho32_magic_number.length <= appjit_magic_number.length);
|
||||
ASSERT(aotmacho64_magic_number.length <= appjit_magic_number.length);
|
||||
ASSERT(aotmacho64_arm64_magic_number.length <= appjit_magic_number.length);
|
||||
ASSERT(static_cast<intptr_t>(sizeof(mach_o::mach_header::magic)) <=
|
||||
appjit_magic_number.length);
|
||||
ASSERT(aotcoff_arm32_magic_number.length <= appjit_magic_number.length);
|
||||
ASSERT(aotcoff_arm64_magic_number.length <= appjit_magic_number.length);
|
||||
ASSERT(aotcoff_riscv32_magic_number.length <= appjit_magic_number.length);
|
||||
@@ -453,16 +450,19 @@ DartUtils::MagicNumber DartUtils::SniffForMagicNumber(const uint8_t* buffer,
|
||||
return kAotELFMagicNumber;
|
||||
}
|
||||
|
||||
if (CheckMagicNumber(buffer, buffer_length, aotmacho32_magic_number)) {
|
||||
return kAotMachO32MagicNumber;
|
||||
}
|
||||
|
||||
if (CheckMagicNumber(buffer, buffer_length, aotmacho64_magic_number)) {
|
||||
return kAotMachO64MagicNumber;
|
||||
}
|
||||
|
||||
if (CheckMagicNumber(buffer, buffer_length, aotmacho64_arm64_magic_number)) {
|
||||
return kAotMachO64Arm64MagicNumber;
|
||||
// Mach-O magic numbers are reported by whether the endianness of the file
|
||||
// matches the endianness of the system. Here, we only bother looking for
|
||||
// host-endian magic numbers, as our Mach-O parsing code won't handle the
|
||||
// reverse endian case.
|
||||
if (static_cast<intptr_t>(sizeof(mach_o::mach_header::magic)) <=
|
||||
buffer_length) {
|
||||
const uint32_t magic =
|
||||
reinterpret_cast<const mach_o::mach_header*>(buffer)->magic;
|
||||
if (magic == mach_o::MH_MAGIC) {
|
||||
return kAotMachO32MagicNumber;
|
||||
} else if (magic == mach_o::MH_MAGIC_64) {
|
||||
return kAotMachO64MagicNumber;
|
||||
}
|
||||
}
|
||||
|
||||
if (CheckMagicNumber(buffer, buffer_length, aotcoff_arm32_magic_number)) {
|
||||
|
||||
+20
-2
@@ -260,9 +260,10 @@ class DartUtils {
|
||||
kKernelListMagicNumber,
|
||||
kGzipMagicNumber,
|
||||
kAotELFMagicNumber,
|
||||
// Only the host-endian magic numbers are recognized, not the reverse-endian
|
||||
// ("cigam") ones, as we can't load a reverse-endian snapshot anyway.
|
||||
kAotMachO32MagicNumber,
|
||||
kAotMachO64MagicNumber,
|
||||
kAotMachO64Arm64MagicNumber,
|
||||
kAotCoffARM32MagicNumber,
|
||||
kAotCoffARM64MagicNumber,
|
||||
kAotCoffRISCV32MagicNumber,
|
||||
@@ -278,7 +279,24 @@ class DartUtils {
|
||||
(number <= DartUtils::kAotCoffRISCV64MagicNumber);
|
||||
}
|
||||
|
||||
// Checks if the buffer is a script snapshot, kernel file, or gzip file.
|
||||
// Returns the bitsize corresponding to the magic number if the bitsize
|
||||
// is specified by the magic number, otherwise returns -1.
|
||||
static intptr_t MagicNumberBitSize(MagicNumber number) {
|
||||
if (number == DartUtils::kAotMachO32MagicNumber ||
|
||||
number == DartUtils::kAotCoffARM32MagicNumber ||
|
||||
number == DartUtils::kAotCoffRISCV32MagicNumber) {
|
||||
return 32;
|
||||
}
|
||||
if (number == DartUtils::kAotMachO64MagicNumber ||
|
||||
number == DartUtils::kAotCoffARM64MagicNumber ||
|
||||
number == DartUtils::kAotCoffRISCV64MagicNumber) {
|
||||
return 64;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Checks if the file is a script snapshot, kernel file, or gzip file
|
||||
// by reading the first kMaxMagicNumberSize bytes of the file.
|
||||
static MagicNumber SniffForMagicNumber(const char* filename);
|
||||
|
||||
// Checks if the buffer is a script snapshot, kernel file, or gzip file.
|
||||
|
||||
+109
-71
@@ -78,6 +78,7 @@ enum SnapshotKind {
|
||||
kAppJIT,
|
||||
kAppAOTAssembly,
|
||||
kAppAOTElf,
|
||||
kAppAOTMachODylib,
|
||||
kVMAOTAssembly,
|
||||
};
|
||||
static SnapshotKind snapshot_kind = kCore;
|
||||
@@ -90,6 +91,7 @@ static const char* const kSnapshotKindNames[] = {
|
||||
"app-jit",
|
||||
"app-aot-assembly",
|
||||
"app-aot-elf",
|
||||
"app-aot-macho-dylib",
|
||||
"vm-aot-assembly",
|
||||
nullptr,
|
||||
// clang-format on
|
||||
@@ -108,6 +110,7 @@ static const char* const kSnapshotKindNames[] = {
|
||||
V(blobs_container_filename, blobs_container_filename) \
|
||||
V(assembly, assembly_filename) \
|
||||
V(elf, elf_filename) \
|
||||
V(macho, macho_filename) \
|
||||
V(loading_unit_manifest, loading_unit_manifest_filename) \
|
||||
V(save_debugging_info, debugging_info_filename) \
|
||||
V(save_obfuscation_map, obfuscation_map_filename)
|
||||
@@ -137,6 +140,7 @@ DEFINE_CB_OPTION(ProcessEnvironmentOption);
|
||||
|
||||
static bool IsSnapshottingForPrecompilation() {
|
||||
return (snapshot_kind == kAppAOTAssembly) || (snapshot_kind == kAppAOTElf) ||
|
||||
(snapshot_kind == kAppAOTMachODylib) ||
|
||||
(snapshot_kind == kVMAOTAssembly);
|
||||
}
|
||||
|
||||
@@ -176,6 +180,15 @@ static void PrintUsage() {
|
||||
"[--save-obfuscation-map=<map-filename>] \n"
|
||||
"<dart-kernel-file> \n"
|
||||
" \n"
|
||||
"To create an AOT application snapshot as an Mach-O dynamic library (dylib): \n"
|
||||
"--snapshot_kind=app-aot-macho-dylib \n"
|
||||
"--macho=<output-file> \n"
|
||||
"[--strip] \n"
|
||||
"[--obfuscate] \n"
|
||||
"[--save-debugging-info=<debug-filename>] \n"
|
||||
"[--save-obfuscation-map=<map-filename>] \n"
|
||||
"<dart-kernel-file> \n"
|
||||
" \n"
|
||||
"AOT snapshots can be obfuscated: that is all identifiers will be renamed \n"
|
||||
"during compilation. This mode is enabled with --obfuscate flag. Mapping \n"
|
||||
"between original and obfuscated names can be serialized as a JSON array \n"
|
||||
@@ -267,6 +280,15 @@ static int ParseArguments(int argc,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kAppAOTMachODylib: {
|
||||
if (macho_filename == nullptr) {
|
||||
Syslog::PrintErr(
|
||||
"Building an AOT snapshot as a Mach-O dynamic library requires "
|
||||
" specifying an output file for --macho.\n\n");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kAppAOTAssembly:
|
||||
case kVMAOTAssembly: {
|
||||
if (assembly_filename == nullptr) {
|
||||
@@ -597,78 +619,99 @@ static void NextElfCallback(void* callback_data,
|
||||
|
||||
static void CreateAndWritePrecompiledSnapshot() {
|
||||
ASSERT(IsSnapshottingForPrecompilation());
|
||||
Dart_Handle result;
|
||||
|
||||
if (snapshot_kind == kVMAOTAssembly) {
|
||||
File* file = OpenFile(assembly_filename);
|
||||
RefCntReleaseScope<File> rs(file);
|
||||
Dart_Handle result =
|
||||
Dart_CreateVMAOTSnapshotAsAssembly(StreamingWriteCallback, file);
|
||||
CHECK_RESULT(result);
|
||||
return;
|
||||
}
|
||||
|
||||
Dart_AotBinaryFormat format;
|
||||
const char* kind_str = nullptr;
|
||||
const char* filename = nullptr;
|
||||
// Default to the assembly ones just to avoid having to type-specify here.
|
||||
auto* next_callback = NextAsmCallback;
|
||||
auto* create_multiple_callback = Dart_CreateAppAOTSnapshotAsAssemblies;
|
||||
switch (snapshot_kind) {
|
||||
case kAppAOTAssembly:
|
||||
kind_str = "assembly code";
|
||||
filename = assembly_filename;
|
||||
format = Dart_AotBinaryFormat_Assembly;
|
||||
break;
|
||||
case kAppAOTElf:
|
||||
kind_str = "ELF library";
|
||||
filename = elf_filename;
|
||||
format = Dart_AotBinaryFormat_Elf;
|
||||
next_callback = NextElfCallback;
|
||||
create_multiple_callback = Dart_CreateAppAOTSnapshotAsElfs;
|
||||
break;
|
||||
case kAppAOTMachODylib:
|
||||
kind_str = "MachO dynamic library";
|
||||
filename = macho_filename;
|
||||
format = Dart_AotBinaryFormat_MachO_Dylib;
|
||||
// Not currently implemented.
|
||||
next_callback = nullptr;
|
||||
create_multiple_callback = nullptr;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
ASSERT(kind_str != nullptr);
|
||||
ASSERT(filename != nullptr);
|
||||
|
||||
// Precompile with specified embedder entry points
|
||||
result = Dart_Precompile();
|
||||
Dart_Handle result = Dart_Precompile();
|
||||
CHECK_RESULT(result);
|
||||
|
||||
if (strip && (debugging_info_filename == nullptr)) {
|
||||
Syslog::PrintErr(
|
||||
"Warning: Generating %s without DWARF debugging"
|
||||
" information.\n",
|
||||
kind_str);
|
||||
}
|
||||
|
||||
char* identifier = Utils::Basename(filename);
|
||||
|
||||
// Create a precompiled snapshot.
|
||||
if (snapshot_kind == kAppAOTAssembly) {
|
||||
if (strip && (debugging_info_filename == nullptr)) {
|
||||
Syslog::PrintErr(
|
||||
"Warning: Generating assembly code without DWARF debugging"
|
||||
" information.\n");
|
||||
if (loading_unit_manifest_filename == nullptr) {
|
||||
File* file = OpenFile(filename);
|
||||
RefCntReleaseScope<File> rs(file);
|
||||
File* debug_file = nullptr;
|
||||
if (debugging_info_filename != nullptr) {
|
||||
debug_file = OpenFile(debugging_info_filename);
|
||||
}
|
||||
if (loading_unit_manifest_filename == nullptr) {
|
||||
File* file = OpenFile(assembly_filename);
|
||||
RefCntReleaseScope<File> rs(file);
|
||||
File* debug_file = nullptr;
|
||||
if (debugging_info_filename != nullptr) {
|
||||
debug_file = OpenFile(debugging_info_filename);
|
||||
}
|
||||
result = Dart_CreateAppAOTSnapshotAsAssembly(StreamingWriteCallback, file,
|
||||
strip, debug_file);
|
||||
if (debug_file != nullptr) debug_file->Release();
|
||||
CHECK_RESULT(result);
|
||||
} else {
|
||||
File* manifest_file = OpenLoadingUnitManifest();
|
||||
result = Dart_CreateAppAOTSnapshotAsAssemblies(
|
||||
NextAsmCallback, manifest_file, strip, StreamingWriteCallback,
|
||||
StreamingCloseCallback);
|
||||
CHECK_RESULT(result);
|
||||
CloseLoadingUnitManifest(manifest_file);
|
||||
}
|
||||
if (obfuscate && !strip) {
|
||||
Syslog::PrintErr(
|
||||
"Warning: The generated assembly code contains unobfuscated DWARF "
|
||||
"debugging information.\n"
|
||||
" To avoid this, use --strip to remove it.\n");
|
||||
}
|
||||
} else if (snapshot_kind == kAppAOTElf) {
|
||||
if (strip && (debugging_info_filename == nullptr)) {
|
||||
Syslog::PrintErr(
|
||||
"Warning: Generating ELF library without DWARF debugging"
|
||||
" information.\n");
|
||||
}
|
||||
if (loading_unit_manifest_filename == nullptr) {
|
||||
File* file = OpenFile(elf_filename);
|
||||
RefCntReleaseScope<File> rs(file);
|
||||
File* debug_file = nullptr;
|
||||
if (debugging_info_filename != nullptr) {
|
||||
debug_file = OpenFile(debugging_info_filename);
|
||||
}
|
||||
result = Dart_CreateAppAOTSnapshotAsElf(StreamingWriteCallback, file,
|
||||
strip, debug_file);
|
||||
if (debug_file != nullptr) debug_file->Release();
|
||||
CHECK_RESULT(result);
|
||||
} else {
|
||||
File* manifest_file = OpenLoadingUnitManifest();
|
||||
result = Dart_CreateAppAOTSnapshotAsElfs(NextElfCallback, manifest_file,
|
||||
strip, StreamingWriteCallback,
|
||||
StreamingCloseCallback);
|
||||
CHECK_RESULT(result);
|
||||
CloseLoadingUnitManifest(manifest_file);
|
||||
}
|
||||
if (obfuscate && !strip) {
|
||||
Syslog::PrintErr(
|
||||
"Warning: The generated ELF library contains unobfuscated DWARF "
|
||||
"debugging information.\n"
|
||||
" To avoid this, use --strip to remove it and "
|
||||
"--save-debugging-info=<...> to save it to a separate file.\n");
|
||||
result = Dart_CreateAppAOTSnapshotAsBinary(
|
||||
format, StreamingWriteCallback, file, strip, debug_file, identifier);
|
||||
if (debug_file != nullptr) debug_file->Release();
|
||||
if (identifier != nullptr) {
|
||||
free(identifier);
|
||||
identifier = nullptr;
|
||||
}
|
||||
CHECK_RESULT(result);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
ASSERT(create_multiple_callback != nullptr);
|
||||
ASSERT(next_callback != nullptr);
|
||||
File* manifest_file = OpenLoadingUnitManifest();
|
||||
result = create_multiple_callback(next_callback, manifest_file, strip,
|
||||
StreamingWriteCallback,
|
||||
StreamingCloseCallback);
|
||||
if (identifier != nullptr) {
|
||||
free(identifier);
|
||||
identifier = nullptr;
|
||||
}
|
||||
CHECK_RESULT(result);
|
||||
CloseLoadingUnitManifest(manifest_file);
|
||||
}
|
||||
|
||||
if (obfuscate && !strip) {
|
||||
Syslog::PrintErr(
|
||||
"Warning: The generated %s contains unobfuscated DWARF "
|
||||
"debugging information.\n"
|
||||
" To avoid this, use --strip to remove it.\n",
|
||||
kind_str);
|
||||
}
|
||||
|
||||
// Serialize obfuscation map if requested.
|
||||
@@ -769,15 +812,10 @@ static int CreateIsolateAndSnapshot(const CommandLineOptions& inputs) {
|
||||
break;
|
||||
case kAppAOTAssembly:
|
||||
case kAppAOTElf:
|
||||
case kAppAOTMachODylib:
|
||||
case kVMAOTAssembly:
|
||||
CreateAndWritePrecompiledSnapshot();
|
||||
break;
|
||||
case kVMAOTAssembly: {
|
||||
File* file = OpenFile(assembly_filename);
|
||||
RefCntReleaseScope<File> rs(file);
|
||||
result = Dart_CreateVMAOTSnapshotAsAssembly(StreamingWriteCallback, file);
|
||||
CHECK_RESULT(result);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
+161
-114
@@ -2,10 +2,11 @@
|
||||
// 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.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "bin/snapshot_utils.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <memory>
|
||||
|
||||
#include "bin/dartutils.h"
|
||||
#include "bin/dfe.h"
|
||||
#include "bin/elf_loader.h"
|
||||
@@ -23,6 +24,15 @@
|
||||
|
||||
#define LOG_SECTION_BOUNDARIES false
|
||||
|
||||
#if !defined(USING_SIMULATOR)
|
||||
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || \
|
||||
defined(DART_HOST_OS_FUCHSIA)
|
||||
#define NATIVE_SHARED_OBJECT_FORMAT_ELF 1
|
||||
#elif defined(DART_HOST_OS_MACOS)
|
||||
#define NATIVE_SHARED_OBJECT_FORMAT_MACHO 1
|
||||
#endif
|
||||
#endif // !defined(USING_SIMULATOR)
|
||||
|
||||
namespace dart {
|
||||
namespace bin {
|
||||
|
||||
@@ -145,6 +155,110 @@ static AppSnapshot* TryReadAppSnapshotBlobs(const char* script_name,
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
class DylibAppSnapshot : public AppSnapshot {
|
||||
public:
|
||||
DylibAppSnapshot(DartUtils::MagicNumber magic_number,
|
||||
void* library,
|
||||
const uint8_t* vm_snapshot_data,
|
||||
const uint8_t* vm_snapshot_instructions,
|
||||
const uint8_t* isolate_snapshot_data,
|
||||
const uint8_t* isolate_snapshot_instructions)
|
||||
: AppSnapshot(magic_number),
|
||||
library_(library),
|
||||
vm_snapshot_data_(vm_snapshot_data),
|
||||
vm_snapshot_instructions_(vm_snapshot_instructions),
|
||||
isolate_snapshot_data_(isolate_snapshot_data),
|
||||
isolate_snapshot_instructions_(isolate_snapshot_instructions) {}
|
||||
|
||||
~DylibAppSnapshot() { Utils::UnloadDynamicLibrary(library_); }
|
||||
|
||||
void SetBuffers(const uint8_t** vm_data_buffer,
|
||||
const uint8_t** vm_instructions_buffer,
|
||||
const uint8_t** isolate_data_buffer,
|
||||
const uint8_t** isolate_instructions_buffer) {
|
||||
*vm_data_buffer = vm_snapshot_data_;
|
||||
*vm_instructions_buffer = vm_snapshot_instructions_;
|
||||
*isolate_data_buffer = isolate_snapshot_data_;
|
||||
*isolate_instructions_buffer = isolate_snapshot_instructions_;
|
||||
}
|
||||
|
||||
private:
|
||||
void* library_;
|
||||
const uint8_t* vm_snapshot_data_;
|
||||
const uint8_t* vm_snapshot_instructions_;
|
||||
const uint8_t* isolate_snapshot_data_;
|
||||
const uint8_t* isolate_snapshot_instructions_;
|
||||
};
|
||||
|
||||
static AppSnapshot* TryReadAppSnapshotDynamicLibrary(
|
||||
DartUtils::MagicNumber magic_number,
|
||||
const char* script_name,
|
||||
const char** error) {
|
||||
#if defined(USING_SIMULATOR)
|
||||
*error = "running on a simulated architecture";
|
||||
return nullptr;
|
||||
#else
|
||||
#if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_MACOS)
|
||||
// On Linux and OSX, resolve the script path before passing into dlopen()
|
||||
// since dlopen will not search the filesystem for paths like 'libtest.so'.
|
||||
CStringUniquePtr absolute_path(realpath(script_name, nullptr));
|
||||
script_name = absolute_path.get();
|
||||
if (script_name == nullptr) {
|
||||
const intptr_t err = errno;
|
||||
const int kBufferSize = 1024;
|
||||
char error_buf[kBufferSize];
|
||||
Utils::StrError(err, error_buf, kBufferSize);
|
||||
*error = Utils::SCreate("could not resolve path: %s", error_buf);
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
void* library = Utils::LoadDynamicLibrary(script_name, error);
|
||||
if (library == nullptr) {
|
||||
#if defined(NATIVE_SHARED_OBJECT_FORMAT_ELF)
|
||||
if (*error == nullptr && magic_number != DartUtils::kAotELFMagicNumber) {
|
||||
*error = "not an ELF shared object";
|
||||
}
|
||||
#elif defined(NATIVE_SHARED_OBJECT_FORMAT_MACHO)
|
||||
if (*error == nullptr &&
|
||||
magic_number != DartUtils::kAotMachO32MagicNumber &&
|
||||
magic_number != DartUtils::kAotMachO64MagicNumber) {
|
||||
*error = "not a Mach-O shared object";
|
||||
}
|
||||
#endif
|
||||
if (*error == nullptr) {
|
||||
*error = "unknown failure loading dynamic library (wrong format?)";
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint8_t* vm_data_buffer = reinterpret_cast<const uint8_t*>(
|
||||
Utils::ResolveSymbolInDynamicLibrary(library, kVmSnapshotDataCSymbol));
|
||||
|
||||
const uint8_t* vm_instructions_buffer =
|
||||
reinterpret_cast<const uint8_t*>(Utils::ResolveSymbolInDynamicLibrary(
|
||||
library, kVmSnapshotInstructionsCSymbol));
|
||||
|
||||
const uint8_t* isolate_data_buffer =
|
||||
reinterpret_cast<const uint8_t*>(Utils::ResolveSymbolInDynamicLibrary(
|
||||
library, kIsolateSnapshotDataCSymbol));
|
||||
if (isolate_data_buffer == nullptr) {
|
||||
FATAL("Failed to resolve symbol '%s'\n", kIsolateSnapshotDataCSymbol);
|
||||
}
|
||||
|
||||
const uint8_t* isolate_instructions_buffer =
|
||||
reinterpret_cast<const uint8_t*>(Utils::ResolveSymbolInDynamicLibrary(
|
||||
library, kIsolateSnapshotInstructionsCSymbol));
|
||||
if (isolate_instructions_buffer == nullptr) {
|
||||
FATAL("Failed to resolve symbol '%s'\n",
|
||||
kIsolateSnapshotInstructionsCSymbol);
|
||||
}
|
||||
|
||||
return new DylibAppSnapshot(magic_number, library, vm_data_buffer,
|
||||
vm_instructions_buffer, isolate_data_buffer,
|
||||
isolate_instructions_buffer);
|
||||
#endif // defined(USING_SIMULATOR)
|
||||
}
|
||||
|
||||
class ElfAppSnapshot : public AppSnapshot {
|
||||
public:
|
||||
ElfAppSnapshot(Dart_LoadedElf* elf,
|
||||
@@ -184,6 +298,18 @@ static AppSnapshot* TryReadAppSnapshotElf(
|
||||
uint64_t file_offset,
|
||||
bool force_load_elf_from_memory = false) {
|
||||
const char* error = nullptr;
|
||||
#if defined(NATIVE_SHARED_OBJECT_FORMAT_ELF)
|
||||
if (file_offset == 0 && !force_load_elf_from_memory) {
|
||||
// The load as a dynamic library should succeed, since this is a platform
|
||||
// that natively understands ELF.
|
||||
if (auto* const snapshot = TryReadAppSnapshotDynamicLibrary(
|
||||
DartUtils::kAotELFMagicNumber, script_name, &error)) {
|
||||
return snapshot;
|
||||
}
|
||||
Syslog::PrintErr("Loading dynamic library failed: %s\n", error);
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
const uint8_t *vm_data_buffer = nullptr, *vm_instructions_buffer = nullptr,
|
||||
*isolate_data_buffer = nullptr,
|
||||
*isolate_instructions_buffer = nullptr;
|
||||
@@ -220,7 +346,8 @@ static AppSnapshot* TryReadAppSnapshotElf(
|
||||
AppSnapshot* Snapshot::TryReadAppendedAppSnapshotElfFromMachO(
|
||||
const char* container_path) {
|
||||
// Ensure file is actually MachO-formatted.
|
||||
if (!IsMachOFormattedBinary(container_path)) {
|
||||
DartUtils::MagicNumber magic_number;
|
||||
if (!IsMachOFormattedBinary(container_path, &magic_number)) {
|
||||
Syslog::PrintErr("Expected a Mach-O binary.\n");
|
||||
return nullptr;
|
||||
}
|
||||
@@ -235,17 +362,19 @@ AppSnapshot* Snapshot::TryReadAppendedAppSnapshotElfFromMachO(
|
||||
// as the 32-bit header, just with an extra field for alignment, so we can
|
||||
// safely load a 32-bit header to get all the information we need.
|
||||
mach_o::mach_header header;
|
||||
file->ReadFully(&header, sizeof(header));
|
||||
|
||||
if (header.magic == mach_o::MH_CIGAM || header.magic == mach_o::MH_CIGAM_64) {
|
||||
Syslog::PrintErr(
|
||||
"Expected a host endian header but found a byte-swapped header.\n");
|
||||
if (!file->ReadFully(&header, sizeof(header))) {
|
||||
Syslog::PrintErr("Could not read a complete Mach-O header.\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (header.magic == mach_o::MH_MAGIC_64) {
|
||||
// Set the file position as if we had read a 64-bit header.
|
||||
file->SetPosition(sizeof(mach_o::mach_header_64));
|
||||
auto const bitsize = DartUtils::MagicNumberBitSize(magic_number);
|
||||
if (bitsize == 64) {
|
||||
// The load commands start immediately after the full header.
|
||||
if (!file->SetPosition(sizeof(mach_o::mach_header_64))) {
|
||||
Syslog::PrintErr("Could not read a complete Mach-O 64-bit header.\n");
|
||||
}
|
||||
} else {
|
||||
ASSERT_EQUAL(bitsize, 32);
|
||||
}
|
||||
|
||||
// Now we search through the load commands to find our snapshot note, which
|
||||
@@ -400,108 +529,29 @@ AppSnapshot* Snapshot::TryReadAppendedAppSnapshotElf(
|
||||
|
||||
return TryReadAppSnapshotElf(container_path, appended_offset);
|
||||
}
|
||||
|
||||
class DylibAppSnapshot : public AppSnapshot {
|
||||
public:
|
||||
DylibAppSnapshot(void* library,
|
||||
const uint8_t* vm_snapshot_data,
|
||||
const uint8_t* vm_snapshot_instructions,
|
||||
const uint8_t* isolate_snapshot_data,
|
||||
const uint8_t* isolate_snapshot_instructions)
|
||||
: AppSnapshot(DartUtils::kAotELFMagicNumber),
|
||||
library_(library),
|
||||
vm_snapshot_data_(vm_snapshot_data),
|
||||
vm_snapshot_instructions_(vm_snapshot_instructions),
|
||||
isolate_snapshot_data_(isolate_snapshot_data),
|
||||
isolate_snapshot_instructions_(isolate_snapshot_instructions) {}
|
||||
|
||||
~DylibAppSnapshot() { Utils::UnloadDynamicLibrary(library_); }
|
||||
|
||||
void SetBuffers(const uint8_t** vm_data_buffer,
|
||||
const uint8_t** vm_instructions_buffer,
|
||||
const uint8_t** isolate_data_buffer,
|
||||
const uint8_t** isolate_instructions_buffer) {
|
||||
*vm_data_buffer = vm_snapshot_data_;
|
||||
*vm_instructions_buffer = vm_snapshot_instructions_;
|
||||
*isolate_data_buffer = isolate_snapshot_data_;
|
||||
*isolate_instructions_buffer = isolate_snapshot_instructions_;
|
||||
}
|
||||
|
||||
private:
|
||||
void* library_;
|
||||
const uint8_t* vm_snapshot_data_;
|
||||
const uint8_t* vm_snapshot_instructions_;
|
||||
const uint8_t* isolate_snapshot_data_;
|
||||
const uint8_t* isolate_snapshot_instructions_;
|
||||
};
|
||||
|
||||
static AppSnapshot* TryReadAppSnapshotDynamicLibrary(const char* script_name) {
|
||||
void* library = Utils::LoadDynamicLibrary(script_name);
|
||||
if (library == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint8_t* vm_data_buffer = reinterpret_cast<const uint8_t*>(
|
||||
Utils::ResolveSymbolInDynamicLibrary(library, kVmSnapshotDataCSymbol));
|
||||
|
||||
const uint8_t* vm_instructions_buffer =
|
||||
reinterpret_cast<const uint8_t*>(Utils::ResolveSymbolInDynamicLibrary(
|
||||
library, kVmSnapshotInstructionsCSymbol));
|
||||
|
||||
const uint8_t* isolate_data_buffer =
|
||||
reinterpret_cast<const uint8_t*>(Utils::ResolveSymbolInDynamicLibrary(
|
||||
library, kIsolateSnapshotDataCSymbol));
|
||||
if (isolate_data_buffer == nullptr) {
|
||||
FATAL("Failed to resolve symbol '%s'\n", kIsolateSnapshotDataCSymbol);
|
||||
}
|
||||
|
||||
const uint8_t* isolate_instructions_buffer =
|
||||
reinterpret_cast<const uint8_t*>(Utils::ResolveSymbolInDynamicLibrary(
|
||||
library, kIsolateSnapshotInstructionsCSymbol));
|
||||
if (isolate_instructions_buffer == nullptr) {
|
||||
FATAL("Failed to resolve symbol '%s'\n",
|
||||
kIsolateSnapshotInstructionsCSymbol);
|
||||
}
|
||||
|
||||
return new DylibAppSnapshot(library, vm_data_buffer, vm_instructions_buffer,
|
||||
isolate_data_buffer, isolate_instructions_buffer);
|
||||
}
|
||||
|
||||
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
#if defined(DART_TARGET_OS_MACOS)
|
||||
bool Snapshot::IsMachOFormattedBinary(const char* filename) {
|
||||
bool Snapshot::IsMachOFormattedBinary(const char* filename,
|
||||
DartUtils::MagicNumber* out) {
|
||||
File* file = File::Open(nullptr, filename, File::kRead);
|
||||
if (file == nullptr) {
|
||||
return false;
|
||||
}
|
||||
RefCntReleaseScope<File> rs(file);
|
||||
|
||||
const uint64_t size = file->Length();
|
||||
// Parse the first 4 bytes and check the magic numbers.
|
||||
uint32_t magic;
|
||||
if (size < sizeof(magic)) {
|
||||
uint8_t header[DartUtils::kMaxMagicNumberSize];
|
||||
if (!file->ReadFully(&header, DartUtils::kMaxMagicNumberSize)) {
|
||||
// The file isn't long enough to contain the magic bytes.
|
||||
return false;
|
||||
}
|
||||
file->SetPosition(0);
|
||||
file->ReadFully(&magic, sizeof(magic));
|
||||
|
||||
// Depending on the magic numbers, check that the size of the file is
|
||||
// large enough for either a 32-bit or 64-bit header.
|
||||
switch (magic) {
|
||||
case mach_o::MH_MAGIC:
|
||||
case mach_o::MH_CIGAM:
|
||||
return size >= sizeof(mach_o::mach_header);
|
||||
case mach_o::MH_MAGIC_64:
|
||||
case mach_o::MH_CIGAM_64:
|
||||
return size >= sizeof(mach_o::mach_header_64);
|
||||
default:
|
||||
// Not a Mach-O formatted file.
|
||||
return false;
|
||||
DartUtils::MagicNumber magic_number =
|
||||
DartUtils::SniffForMagicNumber(header, sizeof(header));
|
||||
if (out != nullptr) {
|
||||
*out = magic_number;
|
||||
}
|
||||
return magic_number == DartUtils::kAotMachO32MagicNumber ||
|
||||
magic_number == DartUtils::kAotMachO64MagicNumber;
|
||||
}
|
||||
#endif // defined(DART_TARGET_OS_MACOS)
|
||||
|
||||
#if defined(DART_TARGET_OS_WINDOWS)
|
||||
bool Snapshot::IsPEFormattedBinary(const char* filename) {
|
||||
@@ -597,23 +647,20 @@ AppSnapshot* Snapshot::TryReadAppSnapshot(const char* script_uri,
|
||||
|
||||
// For testing AOT with the standalone embedder, we also support loading
|
||||
// from a dynamic library to simulate what happens on iOS.
|
||||
|
||||
#if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_MACOS)
|
||||
// On Linux and OSX, resolve the script path before passing into dlopen()
|
||||
// since dlopen will not search the filesystem for paths like 'libtest.so'.
|
||||
CStringUniquePtr absolute_path(realpath(script_name, nullptr));
|
||||
script_name = absolute_path.get();
|
||||
#endif
|
||||
|
||||
AppSnapshot* snapshot = nullptr;
|
||||
if (!force_load_elf_from_memory) {
|
||||
snapshot = TryReadAppSnapshotDynamicLibrary(script_name);
|
||||
if (snapshot != nullptr) {
|
||||
const intptr_t file_offset = 0;
|
||||
if (magic_number == DartUtils::kAotELFMagicNumber) {
|
||||
return TryReadAppSnapshotElf(script_name, file_offset,
|
||||
force_load_elf_from_memory);
|
||||
} else {
|
||||
// This is not a format for which we have a non-native loader, so
|
||||
// attempt to load it as a native dynamic library.
|
||||
const char* error = nullptr;
|
||||
if (auto* const snapshot = TryReadAppSnapshotDynamicLibrary(
|
||||
magic_number, script_name, &error)) {
|
||||
return snapshot;
|
||||
}
|
||||
Syslog::PrintErr("Loading dynamic library failed: %s\n", error);
|
||||
}
|
||||
return TryReadAppSnapshotElf(script_name, /*file_offset=*/0,
|
||||
force_load_elf_from_memory);
|
||||
#else
|
||||
if (magic_number == DartUtils::kAppJITMagicNumber) {
|
||||
// Return the JIT snapshot.
|
||||
|
||||
@@ -46,9 +46,8 @@ class Snapshot {
|
||||
static void GenerateAppJIT(const char* snapshot_filename);
|
||||
static void GenerateAppAOTAsAssembly(const char* snapshot_filename);
|
||||
|
||||
#if defined(DART_TARGET_OS_MACOS)
|
||||
static bool IsMachOFormattedBinary(const char* container_path);
|
||||
#endif
|
||||
static bool IsMachOFormattedBinary(const char* container_path,
|
||||
DartUtils::MagicNumber* out = nullptr);
|
||||
#if defined(DART_TARGET_OS_WINDOWS)
|
||||
static bool IsPEFormattedBinary(const char* container_path);
|
||||
#endif
|
||||
|
||||
@@ -8,12 +8,11 @@
|
||||
#include "bin/test_utils.h"
|
||||
#include "platform/assert.h"
|
||||
#include "platform/globals.h"
|
||||
#include "platform/mach_o.h"
|
||||
#include "vm/unit_test.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
#if defined(DART_TARGET_OS_MACOS)
|
||||
|
||||
static const unsigned char kMachO32BitLittleEndianHeader[] = {
|
||||
0xce, 0xfa, 0xed, 0xfe, 0x07, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00,
|
||||
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
@@ -56,12 +55,19 @@ static const struct {
|
||||
TEST_CASE(CanDetectMachOFiles) {
|
||||
for (uintptr_t i = 0; i < ARRAY_SIZE(kTestcases); i++) {
|
||||
const auto& testcase = kTestcases[i];
|
||||
auto const magic =
|
||||
reinterpret_cast<const mach_o::mach_header*>(testcase.contents)->magic;
|
||||
const bool host_endian =
|
||||
magic == mach_o::MH_MAGIC || magic == mach_o::MH_MAGIC_64;
|
||||
|
||||
auto* const file =
|
||||
bin::DartUtils::OpenFile(testcase.filename, /*write=*/true);
|
||||
bin::DartUtils::WriteFile(testcase.contents, testcase.contents_size, file);
|
||||
bin::DartUtils::CloseFile(file);
|
||||
|
||||
EXPECT(bin::Snapshot::IsMachOFormattedBinary(testcase.filename));
|
||||
// Only host-endian MachO files are recognized.
|
||||
EXPECT_EQ(host_endian,
|
||||
bin::Snapshot::IsMachOFormattedBinary(testcase.filename));
|
||||
|
||||
EXPECT(bin::File::Delete(nullptr, testcase.filename));
|
||||
}
|
||||
@@ -70,6 +76,5 @@ TEST_CASE(CanDetectMachOFiles) {
|
||||
bin::test::GetFileName("runtime/bin/snapshot_utils_test.cc");
|
||||
EXPECT(!bin::Snapshot::IsMachOFormattedBinary(kFilename));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace dart
|
||||
|
||||
@@ -203,6 +203,10 @@ template("library_for_all_configs") {
|
||||
if (defined(invoker.extra_nonproduct_deps)) {
|
||||
extra_nonproduct_deps += invoker.extra_nonproduct_deps
|
||||
}
|
||||
extra_precompiler_deps = []
|
||||
if (defined(invoker.extra_precompiler_deps)) {
|
||||
extra_precompiler_deps += invoker.extra_precompiler_deps
|
||||
}
|
||||
foreach(conf, _all_configs) {
|
||||
target(invoker.target_type, "${target_name}${conf.suffix}") {
|
||||
forward_variables_from(invoker,
|
||||
@@ -241,6 +245,9 @@ template("library_for_all_configs") {
|
||||
sources += snapshot_sources
|
||||
}
|
||||
} else {
|
||||
if (conf.compiler) {
|
||||
deps += extra_precompiler_deps
|
||||
}
|
||||
if (defined(snapshot_sources)) {
|
||||
not_needed([ "snapshot_sources" ])
|
||||
}
|
||||
@@ -271,6 +278,10 @@ template("library_for_all_configs_with_compiler") {
|
||||
if (defined(invoker.extra_nonproduct_deps)) {
|
||||
extra_nonproduct_deps += invoker.extra_nonproduct_deps
|
||||
}
|
||||
extra_precompiler_deps = []
|
||||
if (defined(invoker.extra_precompiler_deps)) {
|
||||
extra_precompiler_deps += invoker.extra_precompiler_deps
|
||||
}
|
||||
foreach(conf, _all_configs) {
|
||||
if (conf.compiler) {
|
||||
target(invoker.target_type, "${target_name}${conf.suffix}") {
|
||||
@@ -303,6 +314,7 @@ template("library_for_all_configs_with_compiler") {
|
||||
sources += snapshot_sources
|
||||
}
|
||||
} else {
|
||||
deps += extra_precompiler_deps
|
||||
if (defined(snapshot_sources)) {
|
||||
not_needed([ "snapshot_sources" ])
|
||||
}
|
||||
|
||||
@@ -3987,7 +3987,7 @@ DART_EXPORT Dart_Handle Dart_LoadingUnitLibraryUris(intptr_t loading_unit_id);
|
||||
*
|
||||
* The assembly should be compiled as a static or shared library and linked or
|
||||
* loaded by the embedder. Running this snapshot requires a VM compiled with
|
||||
* DART_PRECOMPILED_SNAPSHOT. The kDartVmSnapshotData and
|
||||
* DART_PRECOMPILED_RUNTIME. The kDartVmSnapshotData and
|
||||
* kDartVmSnapshotInstructions should be passed to Dart_Initialize. The
|
||||
* kDartIsolateSnapshotData and kDartIsolateSnapshotInstructions should be
|
||||
* passed to Dart_CreateIsolateGroup.
|
||||
@@ -4027,7 +4027,7 @@ Dart_CreateAppAOTSnapshotAsAssemblies(
|
||||
* - _kDartIsolateSnapshotInstructions
|
||||
*
|
||||
* The shared library should be dynamically loaded by the embedder.
|
||||
* Running this snapshot requires a VM compiled with DART_PRECOMPILED_SNAPSHOT.
|
||||
* Running this snapshot requires a VM compiled with DART_PRECOMPILED_RUNTIME.
|
||||
* The kDartVmSnapshotData and kDartVmSnapshotInstructions should be passed to
|
||||
* Dart_Initialize. The kDartIsolateSnapshotData and
|
||||
* kDartIsolateSnapshotInstructions should be passed to Dart_CreateIsolate.
|
||||
@@ -4054,6 +4054,52 @@ Dart_CreateAppAOTSnapshotAsElfs(Dart_CreateLoadingUnitCallback next_callback,
|
||||
Dart_StreamingWriteCallback write_callback,
|
||||
Dart_StreamingCloseCallback close_callback);
|
||||
|
||||
typedef enum {
|
||||
Dart_AotBinaryFormat_Elf = 0,
|
||||
Dart_AotBinaryFormat_Assembly = 1,
|
||||
Dart_AotBinaryFormat_MachO_Dylib = 2,
|
||||
} Dart_AotBinaryFormat;
|
||||
|
||||
/**
|
||||
* Creates a precompiled snapshot.
|
||||
* - A root library must have been loaded.
|
||||
* - Dart_Precompile must have been called.
|
||||
*
|
||||
* Outputs a snapshot in the specified binary format defining the symbols
|
||||
* - _kDartVmSnapshotData
|
||||
* - _kDartVmSnapshotInstructions
|
||||
* - _kDartIsolateSnapshotData
|
||||
* - _kDartIsolateSnapshotInstructions
|
||||
*
|
||||
* The shared library should be dynamically loaded by the embedder.
|
||||
* Running this snapshot requires a VM compiled with DART_PRECOMPILED_RUNTIME.
|
||||
* The kDartVmSnapshotData and kDartVmSnapshotInstructions should be passed to
|
||||
* Dart_Initialize. The kDartIsolateSnapshotData and
|
||||
* kDartIsolateSnapshotInstructions should be passed to Dart_CreateIsolate.
|
||||
*
|
||||
* The callback will be invoked one or more times to provide the binary output.
|
||||
*
|
||||
* If stripped is true, then the binary output will not include DWARF
|
||||
* debugging sections.
|
||||
*
|
||||
* If debug_callback_data is provided, debug_callback_data will be used with
|
||||
* the callback to provide separate debugging information.
|
||||
*
|
||||
* The identifier should be an appropriate string for identifying the resulting
|
||||
* dynamic library. For example, the identifier is used in ID_DYLIB and
|
||||
* CODE_SIGNATURE load commands for Mach-O dynamic libraries and for DW_AT_name
|
||||
* in the Dart progam's root DWARF compilation unit.
|
||||
*
|
||||
* \return A valid handle if no error occurs during the operation.
|
||||
*/
|
||||
DART_EXPORT DART_API_WARN_UNUSED_RESULT Dart_Handle
|
||||
Dart_CreateAppAOTSnapshotAsBinary(Dart_AotBinaryFormat format,
|
||||
Dart_StreamingWriteCallback callback,
|
||||
void* callback_data,
|
||||
bool stripped,
|
||||
void* debug_callback_data,
|
||||
const char* identifier);
|
||||
|
||||
/**
|
||||
* Like Dart_CreateAppAOTSnapshotAsAssembly, but only includes
|
||||
* kDartVmSnapshotData and kDartVmSnapshotInstructions. It also does
|
||||
|
||||
+479
-4
@@ -15,8 +15,48 @@ namespace mach_o {
|
||||
|
||||
typedef int cpu_type_t;
|
||||
typedef int cpu_subtype_t;
|
||||
|
||||
// Mask for architecture variant bits.
|
||||
static constexpr cpu_type_t CPU_ARCH_MASK = 0xff000000;
|
||||
// CPU with a 64-bit ABI.
|
||||
static constexpr cpu_type_t CPU_ARCH_ABI64 = 0x01000000;
|
||||
|
||||
// Fallback for architectures without more specific constants (e.g.,
|
||||
// architectures like RISCV that MacOS doesn't run on natively).
|
||||
static constexpr cpu_type_t CPU_TYPE_ANY = -1;
|
||||
static constexpr cpu_subtype_t CPU_SUBTYPE_ANY = -1;
|
||||
|
||||
// x86-family CPUs.
|
||||
static constexpr cpu_type_t CPU_TYPE_X86 = 7;
|
||||
static constexpr cpu_type_t CPU_TYPE_I386 = CPU_TYPE_X86;
|
||||
static constexpr cpu_type_t CPU_TYPE_X86_64 = CPU_TYPE_X86 | CPU_ARCH_ABI64;
|
||||
|
||||
// x86-family CPU subtypes.
|
||||
constexpr cpu_subtype_t CPU_SUBTYPE_INTEL(uint8_t f, cpu_subtype_t m) {
|
||||
return f + (m << 4);
|
||||
}
|
||||
static constexpr cpu_subtype_t CPU_SUBTYPE_I386_ALL = CPU_SUBTYPE_INTEL(3, 0);
|
||||
static constexpr cpu_subtype_t CPU_SUBTYPE_X86_ALL = CPU_SUBTYPE_I386_ALL;
|
||||
static constexpr cpu_subtype_t CPU_SUBTYPE_X86_64_ALL = CPU_SUBTYPE_I386_ALL;
|
||||
|
||||
// ARM-family CPUs.
|
||||
static constexpr cpu_type_t CPU_TYPE_ARM = 12;
|
||||
static constexpr cpu_type_t CPU_TYPE_ARM64 = CPU_TYPE_ARM | CPU_ARCH_ABI64;
|
||||
|
||||
// ARM-family CPU subtypes.
|
||||
static constexpr cpu_type_t CPU_SUBTYPE_ARM_ALL = 0;
|
||||
static constexpr cpu_type_t CPU_SUBTYPE_ARM64_ALL = CPU_SUBTYPE_ARM_ALL;
|
||||
|
||||
typedef int vm_prot_t;
|
||||
|
||||
static constexpr vm_prot_t VM_PROT_NONE = 0x00;
|
||||
static constexpr vm_prot_t VM_PROT_READ = 0x01;
|
||||
static constexpr vm_prot_t VM_PROT_WRITE = 0x02;
|
||||
static constexpr vm_prot_t VM_PROT_EXECUTE = 0x04;
|
||||
static constexpr vm_prot_t VM_PROT_DEFAULT = (VM_PROT_READ | VM_PROT_WRITE);
|
||||
static constexpr vm_prot_t VM_PROT_ALL =
|
||||
(VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE);
|
||||
|
||||
struct mach_header {
|
||||
uint32_t magic;
|
||||
cpu_type_t cputype;
|
||||
@@ -44,18 +84,453 @@ struct mach_header_64 {
|
||||
static constexpr uint32_t MH_MAGIC_64 = 0xfeedfacf;
|
||||
static constexpr uint32_t MH_CIGAM_64 = 0xcffaedfe;
|
||||
|
||||
// Filetypes for the Mach-O header.
|
||||
|
||||
// A relocatable object file (e.g., an executable).
|
||||
static constexpr uint32_t MH_OBJECT = 0x1;
|
||||
// A dynamically bound shared library.
|
||||
static constexpr uint32_t MH_DYLIB = 0x6;
|
||||
// An object file that only contains debugging information.
|
||||
static constexpr uint32_t MH_DSYM = 0xa;
|
||||
|
||||
// Flag values for the Mach-O header.
|
||||
|
||||
// The object file has no undefined references.
|
||||
static constexpr uint32_t MH_NOUNDEFS = 0x1;
|
||||
// The object file is an appropriate input for the dynamic linker
|
||||
// and cannot be statically link edited again.
|
||||
static constexpr uint32_t MH_DYLDLINK = 0x4;
|
||||
// The object file does not re-export any of its input dynamic
|
||||
// libraries.
|
||||
static constexpr uint32_t MH_NO_REEXPORTED_DYLIBS = 0x100000;
|
||||
|
||||
struct load_command {
|
||||
// The tag that specifies the load command for the following
|
||||
// bytes. One of the LC_* constants below.
|
||||
uint32_t cmd;
|
||||
// The total size of the load command, including cmd and cmdsize.
|
||||
uint32_t cmdsize;
|
||||
};
|
||||
|
||||
// The description of the LC_* constants are followed by the name of
|
||||
// the specific C structure describing their contents in parentheses.
|
||||
|
||||
// A portion of the file that is mapped into memory when the
|
||||
// object file is loaded. (segment_command)
|
||||
static constexpr uint32_t LC_SEGMENT = 0x1;
|
||||
// The static symbol table. (symtab_command)
|
||||
static constexpr uint32_t LC_SYMTAB = 0x2;
|
||||
// The dynamic symbol table. (dysymtab_command)
|
||||
static constexpr uint32_t LC_DYSYMTAB = 0xb;
|
||||
// A dynamic library that must be loaded to use this object file.
|
||||
// (dylib_command)
|
||||
static constexpr uint32_t LC_LOAD_DYLIB = 0xc;
|
||||
// The identifier for this dynamic library (for MH_DYLIB files).
|
||||
// (dylib_command)
|
||||
static constexpr uint32_t LC_ID_DYLIB = 0xd;
|
||||
// A 64-bit segment. (segment_command_64)
|
||||
static constexpr uint32_t LC_SEGMENT_64 = 0x19;
|
||||
// The UUID, used as a build identifier. (uuid_command)
|
||||
static constexpr uint32_t LC_UUID = 0x1b;
|
||||
// The code signature which protects the preceding portion of the object file.
|
||||
// Must be the last contents in the object file. (linkedit_data_command)
|
||||
static constexpr uint32_t LC_CODE_SIGNATURE = 0x1d;
|
||||
// An arbitrary piece of data not specified by the Mach-O format. (note_command)
|
||||
static constexpr uint32_t LC_NOTE = 0x31;
|
||||
struct note_command {
|
||||
uint32_t cmd;
|
||||
// The target platform and minimum and target OS versions for this object file.
|
||||
// (build_version_command)
|
||||
static constexpr uint32_t LC_BUILD_VERSION = 0x32;
|
||||
|
||||
struct segment_command {
|
||||
uint32_t cmd; // LC_SEGMENT
|
||||
uint32_t cmdsize;
|
||||
char data_owner[16];
|
||||
uint64_t offset;
|
||||
// The name of the segment. Must be unique within a given object file.
|
||||
char segname[16];
|
||||
// The starting virtual address and the size of the segment in memory.
|
||||
uint32_t vmaddr;
|
||||
uint32_t vmsize;
|
||||
// The starting file offset and size of the segment in the object file.
|
||||
// The file size and memory size of the segment may be different, for
|
||||
// example, if the segment contains zerofill sections.
|
||||
uint32_t fileoff;
|
||||
uint32_t filesize;
|
||||
// The maximum memory protection possible for this segment.
|
||||
vm_prot_t maxprot;
|
||||
// The initial memory protection for this segment once loaded.
|
||||
vm_prot_t initprot;
|
||||
// The number of sections in the variable-length payload of this load command.
|
||||
uint32_t nsects;
|
||||
//
|
||||
uint32_t flags;
|
||||
// section_command[]
|
||||
};
|
||||
|
||||
// Contains the same fields as segment_command, but the starting memory
|
||||
// address and size and the file offset and size are 64-bit fields.
|
||||
struct segment_command_64 {
|
||||
uint32_t cmd; // LC_SEGMENT_64
|
||||
uint32_t cmdsize;
|
||||
char segname[16];
|
||||
uint64_t vmaddr;
|
||||
uint64_t vmsize;
|
||||
uint64_t fileoff;
|
||||
uint64_t filesize;
|
||||
vm_prot_t maxprot;
|
||||
vm_prot_t initprot;
|
||||
uint32_t nsects;
|
||||
uint32_t flags;
|
||||
// section_command_64[]
|
||||
};
|
||||
|
||||
struct section {
|
||||
char sectname[16];
|
||||
char segname[16];
|
||||
uint32_t addr;
|
||||
uint32_t size;
|
||||
uint32_t offset;
|
||||
uint32_t align;
|
||||
uint32_t reloff;
|
||||
uint32_t nreloc;
|
||||
uint32_t flags;
|
||||
uint32_t reserved1;
|
||||
uint32_t reserved2;
|
||||
};
|
||||
|
||||
struct section_64 {
|
||||
char sectname[16];
|
||||
char segname[16];
|
||||
uint64_t addr;
|
||||
uint64_t size;
|
||||
uint32_t offset;
|
||||
uint32_t align;
|
||||
uint32_t reloff;
|
||||
uint32_t nreloc;
|
||||
uint32_t flags;
|
||||
uint32_t reserved1;
|
||||
uint32_t reserved2;
|
||||
uint32_t reserved3;
|
||||
};
|
||||
|
||||
static constexpr uint32_t SECTION_TYPE = 0x000000ff;
|
||||
static constexpr uint32_t SECTION_ATTRIBUTES = 0xffffff00;
|
||||
|
||||
// Creates section flags from the type and attributes.
|
||||
constexpr uint32_t SectionFlags(intptr_t type, intptr_t attributes) {
|
||||
// Note that the S_* attribute values below do not need shifting.
|
||||
return (attributes & SECTION_ATTRIBUTES) | (type & SECTION_TYPE);
|
||||
}
|
||||
|
||||
// Section types.
|
||||
|
||||
static constexpr uint32_t S_REGULAR = 0x0;
|
||||
static constexpr uint32_t S_ZEROFILL = 0x1;
|
||||
static constexpr uint32_t S_GB_ZEROFILL = 0xc;
|
||||
|
||||
// Section attributes. Note that these values do not need shifting when
|
||||
// combining with a type and so the type bits are always 0.
|
||||
|
||||
static constexpr uint32_t S_NO_ATTRIBUTES = 0;
|
||||
// The section only contains instructions.
|
||||
static constexpr uint32_t S_ATTR_PURE_INSTRUCTIONS = 0x80000000;
|
||||
// The section only contains information needed for debugging.
|
||||
// No symbols should refer to this section and it must have type S_REGULAR.
|
||||
static constexpr uint32_t S_ATTR_DEBUG = 0x02000000;
|
||||
// The section contains some instructions. Should be set if
|
||||
// S_ATTR_PURE_INSTRUCTIONS is also set.
|
||||
static constexpr uint32_t S_ATTR_SOME_INSTRUCTIONS = 0x00000400;
|
||||
|
||||
// Special segment and section names used by Mach-O files. Only the
|
||||
// ones used in our Mach-O writer are listed.
|
||||
|
||||
// Segment and section names for the text segment, which also contains
|
||||
// constant data.
|
||||
static constexpr char SEG_TEXT[] = "__TEXT";
|
||||
static constexpr char SECT_TEXT[] = "__text";
|
||||
static constexpr char SECT_CONST[] = "__const";
|
||||
|
||||
// Segment and section names for the data segment, which contains
|
||||
// non-constant data (like the BSS section).
|
||||
static constexpr char SEG_DATA[] = "__DATA";
|
||||
static constexpr char SECT_BSS[] = "__bss";
|
||||
|
||||
// Segment and section names for the DWARF segment.
|
||||
static constexpr char SEG_DWARF[] = "__DWARF";
|
||||
static constexpr char SECT_DEBUG_LINE[] = "__debug_line";
|
||||
static constexpr char SECT_DEBUG_INFO[] = "__debug_info";
|
||||
static constexpr char SECT_DEBUG_ABBREV[] = "__debug_abbrev";
|
||||
|
||||
// Segment name for the linkedit segment. Does not contain sections but rather
|
||||
// the non-header contents for other non-segment link commands like the symbol
|
||||
// table and code signature.
|
||||
static constexpr char SEG_LINKEDIT[] = "__LINKEDIT";
|
||||
|
||||
struct symtab_command {
|
||||
uint32_t cmd; // LC_SYMTAB
|
||||
uint32_t cmdsize;
|
||||
uint32_t symoff; // The offset of the symbol table data in the object file.
|
||||
uint32_t nsyms; // The number of symbols in the symbol table data.
|
||||
uint32_t stroff; // The offset of the string table for the symbol table.
|
||||
uint32_t strsize; // The size of the string table in bytes.
|
||||
};
|
||||
|
||||
// The structure used for symbols in the symbol table.
|
||||
struct nlist {
|
||||
uint32_t n_idx; // The index of the symbol name in the string table.
|
||||
uint8_t n_type; // The type of the syble (see below).
|
||||
uint8_t n_sect; // For section symbols, the section that owns this symbol.
|
||||
uint16_t n_desc; // Interpreted based on the type of the symbol.
|
||||
// This is normally defined as a uword, but it must match the target
|
||||
// architecture's bitsize, not the host.
|
||||
#if defined(TARGET_ARCH_IS_32_BIT)
|
||||
uint32_t n_value;
|
||||
#else
|
||||
uint64_t n_value;
|
||||
#endif
|
||||
};
|
||||
|
||||
// The "section" for symbols not belonging to a specific section.
|
||||
static constexpr uint8_t NO_SECT = 0;
|
||||
|
||||
// Masks for n_type.
|
||||
|
||||
// If any bits in (n_type & N_STAB) are set, then the symbol is
|
||||
// a symbolic debugging symbol and so n_type is a specific constant.
|
||||
static constexpr uint8_t N_STAB = 0xe0;
|
||||
|
||||
// Otherwise, n_type is a bitfield described by the following masks:
|
||||
|
||||
// The private external symbol bit.
|
||||
static constexpr uint8_t N_PEXT = 0x10;
|
||||
// A mask for the actual type of the symbol.
|
||||
static constexpr uint8_t N_TYPE = 0xe;
|
||||
// The external symbol bit.
|
||||
static constexpr uint8_t N_EXT = 0x1;
|
||||
|
||||
// Values for the N_TYPE bits when no bits in N_STAB are set.
|
||||
|
||||
// An undefined symbol. (n_sect == NO_SECT)
|
||||
static constexpr uint8_t N_UNDEF = 0x0;
|
||||
// A symbol to an absolute offset in the Mach-O file. (n_sect == NO_SECT)
|
||||
static constexpr uint8_t N_ABS = 0x2;
|
||||
// A symbol defined in a specific section (load command index in n_sect).
|
||||
static constexpr uint8_t N_SECT = 0xe;
|
||||
|
||||
// Values for the N_TYPE bits that set bits in N_STAB.
|
||||
|
||||
// A global symbol. (n_sect == NO_SECT, value = 0).
|
||||
static constexpr uint8_t N_GSYM = 0x20;
|
||||
// A function defined in a specific section.
|
||||
static constexpr uint8_t N_FUN = 0x24;
|
||||
// A static (object) symbol defined in a specific section.
|
||||
static constexpr uint8_t N_STSYM = 0x26;
|
||||
// The start of a function symbol in a specific section.
|
||||
static constexpr uint8_t N_BNSYM = 0x2e;
|
||||
// The end of a function symbol in a specific section.
|
||||
static constexpr uint8_t N_ENSYM = 0x4e;
|
||||
|
||||
// Values for n_desc.
|
||||
|
||||
// Indicates an alternate symbol definition for a symbol value that
|
||||
// is already defined elsewhere.
|
||||
static constexpr uint16_t N_ALT_ENTRY = 0x0200;
|
||||
|
||||
struct dysymtab_command {
|
||||
uint32_t cmd; // LC_DYSYMTAB
|
||||
uint32_t cmdsize;
|
||||
|
||||
// The initial fields pairs are offsets into the symbol table information
|
||||
// in the linkedit segment. The first field is the symbol table index of
|
||||
// the first corresponding symbol (not file offset) and the second field
|
||||
// is the number of symbols starting at that index.
|
||||
|
||||
// The local symbols in the symbol table.
|
||||
uint32_t ilocalsym;
|
||||
uint32_t nlocalsym;
|
||||
// The defined external symbols in the symbol table.
|
||||
uint32_t iextdefsym;
|
||||
uint32_t nextdefsym;
|
||||
// The undefined external symbols in the symbol table.
|
||||
uint32_t iundefsym;
|
||||
uint32_t nundefsym;
|
||||
|
||||
// The remaining fields pairs are offsets into the linkedit segment.
|
||||
// The first field is the file offset and the second field is the number
|
||||
// of objects to read starting at that index.
|
||||
//
|
||||
// The Mach-O writer in the VM does not use these fields, so there's
|
||||
// no need for further documentation (they are populated with 0 values).
|
||||
|
||||
uint32_t tocoff;
|
||||
uint32_t ntoc;
|
||||
uint32_t modtaboff;
|
||||
uint32_t nmodtab;
|
||||
uint32_t extrefsymoff;
|
||||
uint32_t nextrefsyms;
|
||||
uint32_t indirectsymoff;
|
||||
uint32_t nindirectsyms;
|
||||
uint32_t extreloff;
|
||||
uint32_t nextrel;
|
||||
uint32_t locreloff;
|
||||
uint32_t nlocrel;
|
||||
};
|
||||
|
||||
struct note_command {
|
||||
uint32_t cmd; // LC_NOTE
|
||||
uint32_t cmdsize;
|
||||
// An identifier used to determine the owner of this note (e.g., to
|
||||
// determine how to interpret the contents of the note.)
|
||||
char data_owner[16];
|
||||
// The file offset of the note contents.
|
||||
uint64_t offset;
|
||||
// The size of the note contents in bytes.
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
struct uuid_command {
|
||||
uint32_t cmd; // LC_UUID
|
||||
uint32_t cmdsize;
|
||||
uint8_t uuid[16]; // The 128-bit UUID of this object file.
|
||||
};
|
||||
|
||||
struct build_version_command {
|
||||
uint32_t cmd; // LC_BUILD_VERSION
|
||||
uint32_t cmdsize;
|
||||
uint32_t platform; // See PLATFORM_* constants.
|
||||
// minos and sdk are X.Y.Z versions encoded as a bitfield:
|
||||
// From most to least significant:
|
||||
// X : 16
|
||||
// Y : 8
|
||||
// Z : 8
|
||||
uint32_t minos; // Minimum OS version.
|
||||
uint32_t sdk; // Target OS version.
|
||||
// The number of build_tool_version structs in the variable-length
|
||||
// payload of this load command. For our purposes, always 0 and
|
||||
// so there is no definition of the build_tool_version struct here.
|
||||
uint32_t ntools;
|
||||
};
|
||||
|
||||
// Values for platform.
|
||||
|
||||
static constexpr uint32_t PLATFORM_UNKNOWN = 0x0;
|
||||
static constexpr uint32_t PLATFORM_ANY = 0xffffffff;
|
||||
|
||||
static constexpr uint32_t PLATFORM_MACOS = 0x1;
|
||||
static constexpr uint32_t PLATFORM_IOS = 0x2;
|
||||
|
||||
union lc_str {
|
||||
// The offset of the string in the load command contents.
|
||||
uint32_t offset;
|
||||
// We don't include the in-memory pointer alternative here.
|
||||
};
|
||||
|
||||
struct dylib_info {
|
||||
lc_str name;
|
||||
// The timestamp the library was built and copied into user.
|
||||
uint32_t timestamp;
|
||||
// Version format is same as in build_version_command.
|
||||
uint32_t current_version;
|
||||
uint32_t compatibility_version;
|
||||
};
|
||||
|
||||
struct dylib_command {
|
||||
uint32_t cmd; // LC_LOAD_DYLIB and LC_ID_DYLIB among others
|
||||
uint32_t cmdsize;
|
||||
dylib_info dylib;
|
||||
};
|
||||
|
||||
struct linkedit_data_command {
|
||||
uint32_t cmd; // LC_CODE_SIGNATURE among others
|
||||
uint32_t cmdsize;
|
||||
// The file offset of the corresponding contents. (Note that this is
|
||||
// _not_ the offset into the linkedit segment.)
|
||||
uint32_t dataoff;
|
||||
// The size of the contents in bytes.
|
||||
uint32_t datasize;
|
||||
};
|
||||
|
||||
// Magic numbers for code signature blobs.
|
||||
|
||||
static constexpr uint32_t CSMAGIC_CODEDIRECTORY = 0xfade0c02;
|
||||
static constexpr uint32_t CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0;
|
||||
|
||||
// Types for code signature blobs.
|
||||
|
||||
static constexpr uint32_t CSSLOT_CODEDIRECTORY = 0;
|
||||
|
||||
// Code signature code directory flags.
|
||||
|
||||
static constexpr uint32_t CS_ADHOC = 0x00000002;
|
||||
static constexpr uint32_t CS_LINKER_SIGNED = 0x00020000;
|
||||
|
||||
// Code signature hash types.
|
||||
|
||||
static constexpr uint8_t CS_HASHTYPE_SHA256 = 0x2;
|
||||
|
||||
// Code signature version numbers.
|
||||
|
||||
// The earliest version that can appear in a code signature.
|
||||
static constexpr uint32_t CS_SUPPORTSNONE = 0x20001;
|
||||
static constexpr uint32_t CS_SUPPORTSSCATTER = 0x20100;
|
||||
static constexpr uint32_t CS_SUPPORTSTEAMID = 0x20200;
|
||||
static constexpr uint32_t CS_SUPPORTSCODELIMIT64 = 0x20300;
|
||||
static constexpr uint32_t CS_SUPPORTSEXECSEG = 0x20400;
|
||||
|
||||
struct cs_blob_index {
|
||||
uint32_t type; // e.g., CSSLOT_CODEDIRECTORY
|
||||
// the offset of the nested blob within the superblob
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
struct cs_superblob {
|
||||
uint32_t magic; // CSMAGIC_EMBEDDED_SIGNATURE
|
||||
// The length of the superblob, which includes any nested blobs.
|
||||
uint32_t length;
|
||||
// The number of nested blobs in this blob.
|
||||
uint32_t count;
|
||||
// The blob indices for the nested blobs.
|
||||
cs_blob_index index[];
|
||||
// The variable length payload also contains the contents of the nested blobs
|
||||
// after the blob indices. The blob indices are not aligned, and the data for
|
||||
// each nested blob is 8-byte aligned.
|
||||
};
|
||||
|
||||
struct cs_code_directory {
|
||||
uint32_t magic; // CSMAGIC_CODEDIRECTORY
|
||||
// The length of the code directory, including the identifier and hashes.
|
||||
uint32_t length;
|
||||
uint32_t version; // For us, CS_SUPPORTSEXECSEG above.
|
||||
uint32_t flags; // For us, CS_ADHOC | CS_LINKED_SIGNED.
|
||||
uint32_t hash_offset; // The file offset of the hashes.
|
||||
uint32_t ident_offset; // The file offset of the identifier.
|
||||
uint32_t num_special_slots; // Unused by us, so 0.
|
||||
// The number of hashes (one for each page up to the code limit,
|
||||
// including one for the final incomplete page if any).
|
||||
uint32_t num_code_slots;
|
||||
// The end of the file covered by this code directory (for us, the file
|
||||
// offset of the superblob).
|
||||
uint32_t code_limit;
|
||||
// The size of each hash in the special and code slots.
|
||||
uint8_t hash_size;
|
||||
// The type of each hash in the special and code slots.
|
||||
uint8_t hash_type;
|
||||
uint8_t platform; // Unused by us, so 0.
|
||||
uint8_t page_size; // log2(page size)
|
||||
uint32_t spare2; // always 0.
|
||||
uint32_t scatter_offset; // Unused by us, so 0.
|
||||
uint32_t teamid_offset; // Unused by us, so 0.
|
||||
uint32_t spare3; // always 0.
|
||||
uint64_t code_limit_64; // Code limit if larger than 32 bits.
|
||||
uint64_t exec_seg_base; // file offset of the executable segment
|
||||
uint64_t exec_seg_limit; // file size of the executable segment
|
||||
uint64_t exec_seg_flags; // For our purposes, always 0.
|
||||
|
||||
// Technically there can be more with later code signature versions,
|
||||
// but the Mach-O writer doesn't output those in the ad-hoc linker
|
||||
// signed signature.
|
||||
|
||||
// The variable length payload contains the identifier followed by
|
||||
// the hashes in the special and code slots. The identifier data is
|
||||
// 8-byte aligned (like blobs) and the hash data is 16-byte aligned.
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
|
||||
defined(DART_HOST_OS_ANDROID)
|
||||
#include <dlfcn.h>
|
||||
#include <libgen.h>
|
||||
#elif defined(DART_HOST_OS_FUCHSIA)
|
||||
#include <dlfcn.h>
|
||||
#include <fuchsia/io/cpp/fidl.h>
|
||||
@@ -398,4 +399,21 @@ void Utils::UnloadDynamicLibrary(void* library_handle, char** error) {
|
||||
}
|
||||
}
|
||||
|
||||
char* Utils::Basename(const char* path) {
|
||||
#if defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_WINDOWS)
|
||||
// Not handled for these operating systems.
|
||||
return nullptr;
|
||||
#else
|
||||
if (path == nullptr) return nullptr;
|
||||
char* const path_copy = Utils::StrDup(path);
|
||||
char* result = basename(path_copy);
|
||||
// The result may be in statically allocated memory, so copy.
|
||||
result = Utils::StrDup(result);
|
||||
// The result may point to a portion of the passed in string, so
|
||||
// only free the copy after duplicating the result.
|
||||
free(path_copy);
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
|
||||
@@ -663,6 +663,11 @@ class Utils {
|
||||
static void* LoadDynamicLibrary(const char* library_path,
|
||||
bool search_dll_load_dir = false,
|
||||
char** error = nullptr);
|
||||
static void* LoadDynamicLibrary(const char* library_path,
|
||||
char** error = nullptr) {
|
||||
return LoadDynamicLibrary(library_path, /*search_dll_load_dir=*/false,
|
||||
error);
|
||||
}
|
||||
|
||||
// Resolve the given |symbol| within the library referenced by the
|
||||
// given |library_handle|.
|
||||
@@ -682,6 +687,14 @@ class Utils {
|
||||
static void UnloadDynamicLibrary(void* library_handle,
|
||||
char** error = nullptr);
|
||||
|
||||
// Returns the basename of the given path. The returned string is malloced
|
||||
// and must be freed by the caller once no longer needed.
|
||||
//
|
||||
// If path is nullptr, returns nullptr.
|
||||
//
|
||||
// Returns nullptr if the operating system does not support this operation.
|
||||
static char* Basename(const char* path);
|
||||
|
||||
#if defined(DART_HOST_OS_LINUX)
|
||||
static bool IsWindowsSubsystemForLinux();
|
||||
#endif
|
||||
|
||||
@@ -84,7 +84,7 @@ Future<void> testAOT(
|
||||
if (useAsm) {
|
||||
final assemblyPath = path.join(tempDir, 'test.S');
|
||||
|
||||
await run(genSnapshot, <String>[
|
||||
await (disassemble ? runSilent : run)(genSnapshot, <String>[
|
||||
'--snapshot-kind=app-aot-assembly',
|
||||
'--assembly=$assemblyPath',
|
||||
...commonSnapshotArgs,
|
||||
@@ -92,7 +92,7 @@ Future<void> testAOT(
|
||||
|
||||
await assembleSnapshot(assemblyPath, snapshotPath);
|
||||
} else {
|
||||
await run(genSnapshot, <String>[
|
||||
await (disassemble ? runSilent : run)(genSnapshot, <String>[
|
||||
'--snapshot-kind=app-aot-elf',
|
||||
'--elf=$snapshotPath',
|
||||
...commonSnapshotArgs,
|
||||
|
||||
@@ -53,7 +53,7 @@ Future<void> main(List<String> args) async {
|
||||
|
||||
// Run the AOT compiler with the disassemble flags set.
|
||||
final elfFile = path.join(tempDir, 'aot.snapshot');
|
||||
await run(genSnapshot, <String>[
|
||||
await runSilent(genSnapshot, <String>[
|
||||
'--disassemble',
|
||||
'--disassemble_stubs',
|
||||
'--always_generate_trampolines_for_testing',
|
||||
@@ -63,7 +63,7 @@ Future<void> main(List<String> args) async {
|
||||
]);
|
||||
|
||||
// Run the AOT runtime with the disassemble flags set.
|
||||
await run(dartPrecompiledRuntime, <String>[
|
||||
await runSilent(dartPrecompiledRuntime, <String>[
|
||||
'--disassemble',
|
||||
'--disassemble_stubs',
|
||||
elfFile,
|
||||
|
||||
@@ -71,6 +71,7 @@ main() {
|
||||
"Dart_CreateAppAOTSnapshotAsAssembly",
|
||||
"Dart_CreateAppAOTSnapshotAsElf",
|
||||
"Dart_CreateAppAOTSnapshotAsElfs",
|
||||
"Dart_CreateAppAOTSnapshotAsBinary",
|
||||
"Dart_CreateAppJITSnapshotAsBlobs",
|
||||
"Dart_CreateIsolateGroup",
|
||||
"Dart_CreateIsolateGroupFromKernel",
|
||||
|
||||
@@ -57,6 +57,7 @@ Future<void> main(List<String> args) async {
|
||||
]);
|
||||
|
||||
await checkElf(tempDir, scriptDill);
|
||||
await checkMachO(tempDir, scriptDill);
|
||||
await checkAssembly(tempDir, scriptDill);
|
||||
});
|
||||
}
|
||||
@@ -67,145 +68,165 @@ const commonGenSnapshotArgs = <String>[
|
||||
'--deterministic',
|
||||
];
|
||||
|
||||
Future<void> checkElf(String tempDir, String scriptDill) async {
|
||||
enum SnapshotType {
|
||||
elf,
|
||||
machoDylib,
|
||||
assembly;
|
||||
|
||||
String get kindString {
|
||||
switch (this) {
|
||||
case elf:
|
||||
return 'app-aot-elf';
|
||||
case machoDylib:
|
||||
return 'app-aot-macho-dylib';
|
||||
case assembly:
|
||||
return 'app-aot-assembly';
|
||||
}
|
||||
}
|
||||
|
||||
String get fileArgumentName {
|
||||
switch (this) {
|
||||
case elf:
|
||||
return 'elf';
|
||||
case machoDylib:
|
||||
return 'macho';
|
||||
case assembly:
|
||||
return 'assembly';
|
||||
}
|
||||
}
|
||||
|
||||
DwarfContainer? fromFile(String filename) {
|
||||
switch (this) {
|
||||
case elf:
|
||||
return Elf.fromFile(filename);
|
||||
case machoDylib:
|
||||
return MachO.fromFile(filename);
|
||||
case assembly:
|
||||
return Elf.fromFile(filename) ?? MachO.fromFile(filename);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => name;
|
||||
}
|
||||
|
||||
Future<void> createSnapshot(
|
||||
String scriptDill,
|
||||
SnapshotType snapshotType,
|
||||
String finalPath, [
|
||||
List<String> extraArgs = const [],
|
||||
]) async {
|
||||
String output = finalPath;
|
||||
if (snapshotType == SnapshotType.assembly) {
|
||||
output = path.withoutExtension(finalPath) + '.S';
|
||||
}
|
||||
await run(genSnapshot, <String>[
|
||||
...commonGenSnapshotArgs,
|
||||
...extraArgs,
|
||||
'--snapshot-kind=${snapshotType.kindString}',
|
||||
'--${snapshotType.fileArgumentName}=$output',
|
||||
scriptDill,
|
||||
]);
|
||||
if (snapshotType == SnapshotType.assembly) {
|
||||
await assembleSnapshot(output, finalPath);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> checkSnapshotType(
|
||||
String tempDir,
|
||||
String scriptDill,
|
||||
SnapshotType snapshotType,
|
||||
) 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 scriptUnobfuscatedSnapshot = path.join(
|
||||
tempDir,
|
||||
'unobfuscated-$snapshotType.so',
|
||||
);
|
||||
await createSnapshot(scriptDill, snapshotType, scriptUnobfuscatedSnapshot);
|
||||
final unobfuscatedCase = TestCase(
|
||||
scriptUnobfuscatedSnapshot,
|
||||
Elf.fromFile(scriptUnobfuscatedSnapshot)!,
|
||||
snapshotType.fromFile(scriptUnobfuscatedSnapshot)!,
|
||||
);
|
||||
|
||||
final scriptObfuscatedOnlySnapshot = path.join(
|
||||
tempDir,
|
||||
'obfuscated-only-elf.so',
|
||||
'obfuscated-only-$snapshotType.so',
|
||||
);
|
||||
await run(genSnapshot, <String>[
|
||||
...commonGenSnapshotArgs,
|
||||
await createSnapshot(scriptDill, snapshotType, scriptObfuscatedOnlySnapshot, [
|
||||
'--obfuscate',
|
||||
'--snapshot-kind=app-aot-elf',
|
||||
'--elf=$scriptObfuscatedOnlySnapshot',
|
||||
scriptDill,
|
||||
]);
|
||||
final obfuscatedOnlyCase = TestCase(
|
||||
scriptObfuscatedOnlySnapshot,
|
||||
Elf.fromFile(scriptObfuscatedOnlySnapshot)!,
|
||||
snapshotType.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)!,
|
||||
);
|
||||
// Don't compare to separate debugging information for assembled snapshots
|
||||
// because the assembled code introduces a lot of local static symbols for
|
||||
// relocations and so the two won't contain similar amounts of static symbols.
|
||||
TestCase? obfuscatedCase;
|
||||
TestCase? strippedCase;
|
||||
if (snapshotType != SnapshotType.assembly) {
|
||||
final scriptObfuscatedSnapshot = path.join(
|
||||
tempDir,
|
||||
'obfuscated-$snapshotType.so',
|
||||
);
|
||||
final scriptDebuggingInfo = path.join(
|
||||
tempDir,
|
||||
'obfuscated-debug-$snapshotType.so',
|
||||
);
|
||||
await createSnapshot(scriptDill, snapshotType, scriptObfuscatedSnapshot, [
|
||||
'--obfuscate',
|
||||
'--save-debugging-info=$scriptDebuggingInfo',
|
||||
]);
|
||||
obfuscatedCase = TestCase(
|
||||
scriptObfuscatedSnapshot,
|
||||
snapshotType.fromFile(scriptObfuscatedSnapshot)!,
|
||||
snapshotType.fromFile(scriptDebuggingInfo)!,
|
||||
);
|
||||
|
||||
final scriptStrippedSnapshot = path.join(
|
||||
tempDir,
|
||||
'obfuscated-stripped-elf.so',
|
||||
);
|
||||
final scriptSeparateDebuggingInfo = path.join(
|
||||
tempDir,
|
||||
'obfuscated-separate-debug-elf.so',
|
||||
);
|
||||
await run(genSnapshot, <String>[
|
||||
...commonGenSnapshotArgs,
|
||||
'--strip',
|
||||
'--obfuscate',
|
||||
'--snapshot-kind=app-aot-elf',
|
||||
'--elf=$scriptStrippedSnapshot',
|
||||
'--save-debugging-info=$scriptSeparateDebuggingInfo',
|
||||
scriptDill,
|
||||
]);
|
||||
final strippedCase = TestCase(
|
||||
scriptStrippedSnapshot,
|
||||
/*container=*/ null, // No static symbols in stripped snapshot.
|
||||
Elf.fromFile(scriptSeparateDebuggingInfo)!,
|
||||
);
|
||||
final scriptStrippedSnapshot = path.join(
|
||||
tempDir,
|
||||
'obfuscated-stripped-$snapshotType.so',
|
||||
);
|
||||
final scriptSeparateDebuggingInfo = path.join(
|
||||
tempDir,
|
||||
'obfuscated-separate-debug-$snapshotType.so',
|
||||
);
|
||||
await createSnapshot(scriptDill, snapshotType, scriptStrippedSnapshot, [
|
||||
'--strip',
|
||||
'--obfuscate',
|
||||
'--save-debugging-info=$scriptSeparateDebuggingInfo',
|
||||
]);
|
||||
strippedCase = TestCase(
|
||||
scriptStrippedSnapshot,
|
||||
/*container=*/ null, // No static symbols in stripped snapshot.
|
||||
snapshotType.fromFile(scriptSeparateDebuggingInfo)!,
|
||||
);
|
||||
}
|
||||
|
||||
await checkCases(unobfuscatedCase, <TestCase>[
|
||||
obfuscatedOnlyCase,
|
||||
obfuscatedCase,
|
||||
strippedCase,
|
||||
if (obfuscatedCase != null) obfuscatedCase,
|
||||
if (strippedCase != null) strippedCase,
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> checkElf(String tempDir, String scriptDill) async {
|
||||
await checkSnapshotType(tempDir, scriptDill, SnapshotType.elf);
|
||||
}
|
||||
|
||||
Future<void> checkMachO(String tempDir, String scriptDill) async {
|
||||
await checkSnapshotType(tempDir, scriptDill, SnapshotType.machoDylib);
|
||||
}
|
||||
|
||||
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]);
|
||||
await checkSnapshotType(tempDir, scriptDill, SnapshotType.assembly);
|
||||
}
|
||||
|
||||
class TestCase {
|
||||
@@ -221,6 +242,12 @@ Future<void> checkCases(
|
||||
List<TestCase> obfuscateds,
|
||||
) async {
|
||||
checkStaticSymbolTables(unobfuscated, obfuscateds);
|
||||
if (!Platform.isMacOS && unobfuscated.container is! Elf) {
|
||||
assert(unobfuscated.container is MachO);
|
||||
// Don't try and run Mach-O snapshots on systems where it is not the native
|
||||
// format because there is no MachOLoader in the runtime.
|
||||
return;
|
||||
}
|
||||
await checkTraces(unobfuscated, obfuscateds);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,7 @@ Future<void> main() async {
|
||||
'use_dwarf_stack_traces_flag_deferred_program.dart',
|
||||
),
|
||||
runNonDwarf,
|
||||
runElf,
|
||||
runAssembly,
|
||||
[runElf, runAssembly],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -104,10 +103,10 @@ typedef DwarfMap = Map<int, Dwarf>;
|
||||
|
||||
class DeferredElfState extends ElfState<DwarfMap> {
|
||||
DeferredElfState(
|
||||
super.snapshot,
|
||||
super.debugInfo,
|
||||
super.output,
|
||||
super.outputWithOppositeFlag,
|
||||
super.snapshot,
|
||||
super.debugInfo,
|
||||
);
|
||||
|
||||
@override
|
||||
@@ -163,19 +162,19 @@ Future<DeferredElfState> runElf(String tempDir, String scriptDill) async {
|
||||
final snapshotDwarfMap = useSnapshotForDwarfPath(pathManifest).dwarfMap;
|
||||
|
||||
return DeferredElfState(
|
||||
snapshotDwarfMap,
|
||||
debugInfoDwarfMap,
|
||||
output,
|
||||
outputWithOppositeFlag,
|
||||
snapshotDwarfMap,
|
||||
debugInfoDwarfMap,
|
||||
);
|
||||
}
|
||||
|
||||
class DeferredAssemblyState extends AssemblyState<DwarfMap> {
|
||||
DeferredAssemblyState(
|
||||
super.snapshot,
|
||||
super.debugInfo,
|
||||
super.output,
|
||||
super.outputWithOppositeFlag, [
|
||||
super.outputWithOppositeFlag,
|
||||
super.snapshot,
|
||||
super.debugInfo, [
|
||||
super.singleArch,
|
||||
super.multiArch,
|
||||
]);
|
||||
@@ -298,10 +297,10 @@ Future<DeferredAssemblyState?> runAssembly(
|
||||
}
|
||||
|
||||
return DeferredAssemblyState(
|
||||
snapshotDwarfMap,
|
||||
debugInfoDwarfMap,
|
||||
output,
|
||||
outputWithOppositeFlag,
|
||||
snapshotDwarfMap,
|
||||
debugInfoDwarfMap,
|
||||
singleArchSnapshotDwarfMap,
|
||||
multiArchSnapshotDwarfMap,
|
||||
);
|
||||
|
||||
@@ -46,67 +46,128 @@ class DwarfTestOutput {
|
||||
DwarfTestOutput(this.trace, this.allocateObjectStart, this.allocateObjectEnd);
|
||||
}
|
||||
|
||||
class NonDwarfState {
|
||||
abstract class State {
|
||||
final DwarfTestOutput output;
|
||||
final DwarfTestOutput outputWithOppositeFlag;
|
||||
|
||||
NonDwarfState(this.output, this.outputWithOppositeFlag);
|
||||
State(this.output, this.outputWithOppositeFlag);
|
||||
}
|
||||
|
||||
class NonDwarfState extends State {
|
||||
NonDwarfState(super.output, super.outputWithOppositeFlag);
|
||||
|
||||
void check() => expect(outputWithOppositeFlag.trace, equals(output.trace));
|
||||
}
|
||||
|
||||
abstract class ElfState<T> {
|
||||
abstract class DwarfState<T> extends State {
|
||||
final T snapshot;
|
||||
final T debugInfo;
|
||||
final DwarfTestOutput output;
|
||||
final DwarfTestOutput outputWithOppositeFlag;
|
||||
|
||||
ElfState(
|
||||
DwarfState(
|
||||
super.output,
|
||||
super.outputWithOppositeFlag,
|
||||
this.snapshot,
|
||||
this.debugInfo,
|
||||
this.output,
|
||||
this.outputWithOppositeFlag,
|
||||
);
|
||||
|
||||
String get description;
|
||||
|
||||
Future<void> check(Trace trace, T t);
|
||||
|
||||
Future<void> makeTests(Trace nonDwarfTrace) async {
|
||||
test(
|
||||
'Testing $description traces with separate debugging info',
|
||||
() async => await check(nonDwarfTrace, debugInfo),
|
||||
);
|
||||
|
||||
test(
|
||||
'Testing $description traces with original snapshot',
|
||||
() async => await check(nonDwarfTrace, snapshot),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AssemblyState<T> {
|
||||
final T snapshot;
|
||||
final T debugInfo;
|
||||
final DwarfTestOutput output;
|
||||
final DwarfTestOutput outputWithOppositeFlag;
|
||||
abstract class ElfState<T> extends DwarfState<T> {
|
||||
ElfState(
|
||||
super.output,
|
||||
super.outputWithOppositeFlag,
|
||||
super.snapshot,
|
||||
super.debugInfo,
|
||||
);
|
||||
|
||||
@override
|
||||
String get description => 'ELF';
|
||||
}
|
||||
|
||||
abstract class MultiArchDwarfState<T> extends DwarfState<T> {
|
||||
final T? singleArch;
|
||||
final T? multiArch;
|
||||
|
||||
AssemblyState(
|
||||
this.snapshot,
|
||||
this.debugInfo,
|
||||
this.output,
|
||||
this.outputWithOppositeFlag, [
|
||||
MultiArchDwarfState(
|
||||
super.output,
|
||||
super.outputWithOppositeFlag,
|
||||
super.snapshot,
|
||||
super.debugInfo, [
|
||||
this.singleArch,
|
||||
this.multiArch,
|
||||
]);
|
||||
|
||||
Future<void> check(Trace trace, T t);
|
||||
@override
|
||||
Future<void> makeTests(Trace nonDwarfTrace) async {
|
||||
await super.makeTests(nonDwarfTrace);
|
||||
|
||||
test(
|
||||
'Testing $description single-architecture universal binary',
|
||||
() async {
|
||||
expect(singleArch, isNotNull);
|
||||
await check(nonDwarfTrace, singleArch!);
|
||||
},
|
||||
skip: skipUniversalBinary,
|
||||
);
|
||||
|
||||
test(
|
||||
'Testing $description multi-architecture universal binary',
|
||||
() async {
|
||||
expect(multiArch, isNotNull);
|
||||
await check(nonDwarfTrace, multiArch!);
|
||||
},
|
||||
skip: skipUniversalBinary,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class UniversalBinaryState<T> {
|
||||
final T singleArch;
|
||||
final T multiArch;
|
||||
abstract class AssemblyState<T> extends MultiArchDwarfState<T> {
|
||||
AssemblyState(
|
||||
super.output,
|
||||
super.outputWithOppositeFlag,
|
||||
super.snapshot,
|
||||
super.debugInfo, [
|
||||
super.singleArch,
|
||||
super.multiArch,
|
||||
]);
|
||||
|
||||
UniversalBinaryState(this.singleArch, this.multiArch);
|
||||
@override
|
||||
String get description => 'assembly';
|
||||
}
|
||||
|
||||
Future<void> checkSingleArch(Trace trace, AssemblyState assemblyState);
|
||||
Future<void> checkMultiArch(Trace trace, AssemblyState assemblyState);
|
||||
abstract class MachOState<T> extends MultiArchDwarfState<T> {
|
||||
MachOState(
|
||||
super.output,
|
||||
super.outputWithOppositeFlag,
|
||||
super.snapshot,
|
||||
super.debugInfo, [
|
||||
super.singleArch,
|
||||
super.multiArch,
|
||||
]);
|
||||
|
||||
@override
|
||||
String get description => 'Mach-O';
|
||||
}
|
||||
|
||||
Future<void> runTests<T>(
|
||||
String tempPrefix,
|
||||
String scriptPath,
|
||||
Future<NonDwarfState> Function(String, String) runNonDwarf,
|
||||
Future<ElfState<T>> Function(String, String) runElf,
|
||||
Future<AssemblyState<T>?> Function(String, String) runAssembly,
|
||||
Iterable<Future<DwarfState?> Function(String, String)> runDwarfs,
|
||||
) async {
|
||||
if (!isAOTRuntime) {
|
||||
return; // Running in JIT: AOT binaries not available.
|
||||
@@ -143,44 +204,17 @@ Future<void> runTests<T>(
|
||||
]);
|
||||
|
||||
final nonDwarfState = await runNonDwarf(tempDir, scriptDill);
|
||||
final elfState = await runElf(tempDir, scriptDill);
|
||||
final assemblyState = await runAssembly(tempDir, scriptDill);
|
||||
final dwarfStates = [
|
||||
for (final f in runDwarfs) await f(tempDir, scriptDill),
|
||||
];
|
||||
|
||||
test('Testing symbolic traces', nonDwarfState.check);
|
||||
|
||||
final nonDwarfTrace = nonDwarfState.output.trace;
|
||||
|
||||
test(
|
||||
'Testing ELF traces with separate debugging info',
|
||||
() async => await elfState.check(nonDwarfTrace, elfState.debugInfo),
|
||||
);
|
||||
|
||||
test(
|
||||
'Testing ELF traces with original snapshot',
|
||||
() async => await elfState.check(nonDwarfTrace, elfState.snapshot),
|
||||
);
|
||||
|
||||
test('Testing assembly traces with separate debugging info', () async {
|
||||
expect(assemblyState, isNotNull);
|
||||
await assemblyState!.check(nonDwarfTrace, assemblyState.debugInfo);
|
||||
}, skip: skipAssembly);
|
||||
|
||||
test('Testing assembly traces with debug snapshot ', () async {
|
||||
expect(assemblyState, isNotNull);
|
||||
await assemblyState!.check(nonDwarfTrace, assemblyState.snapshot);
|
||||
}, skip: skipAssembly);
|
||||
|
||||
test('Testing single-architecture universal binary', () async {
|
||||
expect(assemblyState, isNotNull);
|
||||
expect(assemblyState!.singleArch, isNotNull);
|
||||
await assemblyState.check(nonDwarfTrace, assemblyState.singleArch!);
|
||||
}, skip: skipUniversalBinary);
|
||||
|
||||
test('Testing multi-architecture universal binary', () async {
|
||||
expect(assemblyState, isNotNull);
|
||||
expect(assemblyState!.multiArch, isNotNull);
|
||||
await assemblyState.check(nonDwarfTrace, assemblyState.multiArch!);
|
||||
}, skip: skipUniversalBinary);
|
||||
for (final dwarfState in dwarfStates.whereType<DwarfState>()) {
|
||||
dwarfState.makeTests(nonDwarfTrace);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,13 @@ Future<void> main() async {
|
||||
'use_dwarf_stack_traces_flag_program.dart',
|
||||
),
|
||||
runNonDwarf,
|
||||
runElf,
|
||||
runAssembly,
|
||||
[
|
||||
runElf,
|
||||
// Only generate Mach-O on MacOS, since there is no MachOLoader
|
||||
// to run the binary on platforms where that isn't the native format.
|
||||
if (Platform.isMacOS) runMachODylib,
|
||||
runAssembly,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -64,10 +69,10 @@ Future<NonDwarfState> runNonDwarf(String tempDir, String scriptDill) async {
|
||||
|
||||
class DwarfElfState extends ElfState<Dwarf> {
|
||||
DwarfElfState(
|
||||
super.snapshot,
|
||||
super.debugInfo,
|
||||
super.output,
|
||||
super.outputWithOppositeFlag,
|
||||
super.snapshot,
|
||||
super.debugInfo,
|
||||
);
|
||||
|
||||
@override
|
||||
@@ -76,8 +81,9 @@ class DwarfElfState extends ElfState<Dwarf> {
|
||||
}
|
||||
|
||||
Future<DwarfElfState> runElf(String tempDir, String scriptDill) async {
|
||||
final snapshotPath = path.join(tempDir, 'dwarf.so');
|
||||
final debugInfoPath = path.join(tempDir, 'debug_info.so');
|
||||
print("Generating ELF snapshots");
|
||||
final snapshotPath = path.join(tempDir, 'dwarf_elf.so');
|
||||
final debugInfoPath = path.join(tempDir, 'debug_info_elf.so');
|
||||
await run(genSnapshot, <String>[
|
||||
'--dwarf-stack-traces-mode',
|
||||
'--save-debugging-info=$debugInfoPath',
|
||||
@@ -90,7 +96,7 @@ Future<DwarfElfState> runElf(String tempDir, String scriptDill) async {
|
||||
final debugInfo = Dwarf.fromFile(debugInfoPath)!;
|
||||
|
||||
// Run the resulting Dwarf-AOT compiled script.
|
||||
|
||||
print("Generating ELF snapshot outputs");
|
||||
final output = await runTestProgram(dartPrecompiledRuntime, <String>[
|
||||
'--dwarf-stack-traces-mode',
|
||||
snapshotPath,
|
||||
@@ -101,15 +107,15 @@ Future<DwarfElfState> runElf(String tempDir, String scriptDill) async {
|
||||
<String>['--no-dwarf-stack-traces-mode', snapshotPath, scriptDill],
|
||||
);
|
||||
|
||||
return DwarfElfState(snapshot, debugInfo, output, outputWithOppositeFlag);
|
||||
return DwarfElfState(output, outputWithOppositeFlag, snapshot, debugInfo);
|
||||
}
|
||||
|
||||
class DwarfAssemblyState extends AssemblyState<Dwarf> {
|
||||
DwarfAssemblyState(
|
||||
super.snapshot,
|
||||
super.debugInfo,
|
||||
super.output,
|
||||
super.outputWithOppositeFlag, [
|
||||
super.outputWithOppositeFlag,
|
||||
super.snapshot,
|
||||
super.debugInfo, [
|
||||
super.singleArch,
|
||||
super.multiArch,
|
||||
]);
|
||||
@@ -136,6 +142,7 @@ Future<DwarfAssemblyState?> runAssembly(
|
||||
// We get a separate .dSYM bundle on MacOS.
|
||||
var debugSnapshotPath = snapshotPath + (Platform.isMacOS ? '.dSYM' : '');
|
||||
|
||||
print("Generating assembly snapshots");
|
||||
await run(genSnapshot, <String>[
|
||||
// We test --dwarf-stack-traces-mode, not --dwarf-stack-traces, because
|
||||
// the latter is a handler that sets the former and also may change
|
||||
@@ -152,6 +159,7 @@ Future<DwarfAssemblyState?> runAssembly(
|
||||
|
||||
await assembleSnapshot(asmPath, snapshotPath, debug: true);
|
||||
|
||||
print("Generating assembly snapshot outputs");
|
||||
// Run the resulting Dwarf-AOT compiled script.
|
||||
final output = await runTestProgram(dartPrecompiledRuntime, <String>[
|
||||
'--dwarf-stack-traces-mode',
|
||||
@@ -182,6 +190,7 @@ Future<DwarfAssemblyState?> runAssembly(
|
||||
emptyFiles[arch] = emptyPath;
|
||||
}
|
||||
|
||||
print("Generating multi-arch assembly debugging information");
|
||||
final singleArchSnapshotPath = path.join(tempDir, "ub-single");
|
||||
await run(lipo, <String>[
|
||||
debugSnapshotPath,
|
||||
@@ -203,10 +212,98 @@ Future<DwarfAssemblyState?> runAssembly(
|
||||
}
|
||||
|
||||
return DwarfAssemblyState(
|
||||
snapshot,
|
||||
debugInfo,
|
||||
output,
|
||||
outputWithOppositeFlag,
|
||||
snapshot,
|
||||
debugInfo,
|
||||
singleArchSnapshot,
|
||||
multiArchSnapshot,
|
||||
);
|
||||
}
|
||||
|
||||
class DwarfMachOState extends MachOState<Dwarf> {
|
||||
DwarfMachOState(
|
||||
super.output,
|
||||
super.outputWithOppositeFlag,
|
||||
super.snapshot,
|
||||
super.debugInfo, [
|
||||
super.singleArch,
|
||||
super.multiArch,
|
||||
]);
|
||||
|
||||
@override
|
||||
Future<void> check(Trace trace, Dwarf dwarf) =>
|
||||
compareTraces(trace, output, outputWithOppositeFlag, dwarf);
|
||||
}
|
||||
|
||||
Future<DwarfMachOState> runMachODylib(String tempDir, String scriptDill) async {
|
||||
print("Generating Mach-O snapshots");
|
||||
final snapshotPath = path.join(tempDir, 'dwarf_macho_dylib.so');
|
||||
final debugInfoPath = path.join(tempDir, 'debug_info_macho_dylib.so');
|
||||
await run(genSnapshot, <String>[
|
||||
'--dwarf-stack-traces-mode',
|
||||
'--save-debugging-info=$debugInfoPath',
|
||||
'--snapshot-kind=app-aot-macho-dylib',
|
||||
'--macho=$snapshotPath',
|
||||
scriptDill,
|
||||
]);
|
||||
|
||||
final snapshot = Dwarf.fromFile(snapshotPath)!;
|
||||
final debugInfo = Dwarf.fromFile(debugInfoPath)!;
|
||||
|
||||
// Run the resulting Dwarf-AOT compiled script.
|
||||
print("Generating Mach-O snapshot outputs");
|
||||
final output = await runTestProgram(dartPrecompiledRuntime, <String>[
|
||||
'--dwarf-stack-traces-mode',
|
||||
snapshotPath,
|
||||
scriptDill,
|
||||
]);
|
||||
final outputWithOppositeFlag = await runTestProgram(
|
||||
dartPrecompiledRuntime,
|
||||
<String>['--no-dwarf-stack-traces-mode', snapshotPath, scriptDill],
|
||||
);
|
||||
|
||||
Dwarf? singleArchSnapshot;
|
||||
Dwarf? multiArchSnapshot;
|
||||
if (skipUniversalBinary == false) {
|
||||
// Create empty MachO files (just a header) for each of the possible
|
||||
// architectures.
|
||||
final emptyFiles = <String, String>{};
|
||||
for (final arch in machOArchNames.values) {
|
||||
// Don't create an empty file for the current architecture.
|
||||
if (arch == dartNameForCurrentArchitecture) continue;
|
||||
final contents = emptyMachOForArchitecture(arch)!;
|
||||
final emptyPath = path.join(tempDir, "empty_${arch}.so");
|
||||
await File(emptyPath).writeAsBytes(contents, flush: true);
|
||||
emptyFiles[arch] = emptyPath;
|
||||
}
|
||||
|
||||
print("Generating multi-arch Mach-O debugging information");
|
||||
final singleArchSnapshotPath = path.join(tempDir, "ub-single");
|
||||
await run(lipo, <String>[
|
||||
debugInfoPath,
|
||||
'-create',
|
||||
'-output',
|
||||
singleArchSnapshotPath,
|
||||
]);
|
||||
singleArchSnapshot = Dwarf.fromFile(singleArchSnapshotPath)!;
|
||||
|
||||
final multiArchSnapshotPath = path.join(tempDir, "ub-multiple");
|
||||
await run(lipo, <String>[
|
||||
...emptyFiles.values,
|
||||
debugInfoPath,
|
||||
'-create',
|
||||
'-output',
|
||||
multiArchSnapshotPath,
|
||||
]);
|
||||
multiArchSnapshot = Dwarf.fromFile(multiArchSnapshotPath)!;
|
||||
}
|
||||
|
||||
return DwarfMachOState(
|
||||
output,
|
||||
outputWithOppositeFlag,
|
||||
snapshot,
|
||||
debugInfo,
|
||||
singleArchSnapshot,
|
||||
multiArchSnapshot,
|
||||
);
|
||||
@@ -248,8 +345,9 @@ Future<void> compareTraces(
|
||||
|
||||
checkTranslatedTrace(nonDwarfTrace, translatedDwarfTrace1);
|
||||
|
||||
// Since we compiled directly to ELF, there should be a DSO base address
|
||||
// in the stack trace header and 'virt' markers in the stack frames.
|
||||
// Since we compiled directly to a shared object, there should be a
|
||||
// DSO base address in the stack trace header and 'virt' markers in
|
||||
// the stack frames.
|
||||
|
||||
// The offsets of absolute addresses from their respective DSO base
|
||||
// should be the same for both traces.
|
||||
|
||||
@@ -173,20 +173,23 @@ Future<void> stripSnapshot(
|
||||
await run(strip, <String>['-o', strippedPath, snapshotPath]);
|
||||
}
|
||||
|
||||
Future<ProcessResult> runHelper(String executable, List<String> args) async {
|
||||
Future<ProcessResult> runHelper(
|
||||
String executable,
|
||||
List<String> args, {
|
||||
bool printStdout = true,
|
||||
bool printStderr = true,
|
||||
}) async {
|
||||
print('Running $executable ${args.join(' ')}');
|
||||
|
||||
final result = await Process.run(executable, args);
|
||||
print('Subcommand terminated with exit code ${result.exitCode}.');
|
||||
if (result.stdout.isNotEmpty) {
|
||||
if (printStdout && result.stdout.isNotEmpty) {
|
||||
print('Subcommand stdout:');
|
||||
print(result.stdout);
|
||||
}
|
||||
if (result.exitCode != 0) {
|
||||
if (result.stderr.isNotEmpty) {
|
||||
print('Subcommand stderr:');
|
||||
print(result.stderr);
|
||||
}
|
||||
if (printStderr && result.stderr.isNotEmpty) {
|
||||
print('Subcommand stderr:');
|
||||
print(result.stderr);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -210,6 +213,19 @@ Future<void> run(String executable, List<String> args) async {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> runSilent(String executable, List<String> args) async {
|
||||
final result = await runHelper(
|
||||
executable,
|
||||
args,
|
||||
printStdout: false,
|
||||
printStderr: false,
|
||||
);
|
||||
|
||||
if (result.exitCode != 0) {
|
||||
throw 'Command failed with unexpected exit code (was ${result.exitCode})';
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<String>> runOutput(String executable, List<String> args) async {
|
||||
final result = await runHelper(executable, args);
|
||||
|
||||
|
||||
@@ -180,6 +180,10 @@ library_for_all_configs("libdart_vm") {
|
||||
"//third_party/icu:icui18n",
|
||||
"//third_party/icu:icuuc",
|
||||
]
|
||||
extra_precompiler_deps = [
|
||||
# The Mach-O writer uses BoringSSL's SHA256 function for code signatures.
|
||||
"//third_party/boringssl",
|
||||
]
|
||||
if (is_fuchsia) {
|
||||
extra_deps += [
|
||||
"$fuchsia_sdk/fidl/fuchsia.intl",
|
||||
|
||||
+64
-27
@@ -31,6 +31,7 @@
|
||||
#include "vm/isolate_reload.h"
|
||||
#include "vm/kernel_isolate.h"
|
||||
#include "vm/lockers.h"
|
||||
#include "vm/mach_o.h"
|
||||
#include "vm/message.h"
|
||||
#include "vm/message_handler.h"
|
||||
#include "vm/message_snapshot.h"
|
||||
@@ -6429,20 +6430,16 @@ static constexpr intptr_t kAssemblyInitialSize = 512 * KB;
|
||||
static constexpr intptr_t kInitialSize = 2 * MB;
|
||||
static constexpr intptr_t kInitialDebugSize = 1 * MB;
|
||||
|
||||
enum class AOTSnapshotType {
|
||||
kAssembly,
|
||||
kElf,
|
||||
};
|
||||
|
||||
static void CreateAppAOTSnapshot(
|
||||
Dart_StreamingWriteCallback callback,
|
||||
void* callback_data,
|
||||
bool strip,
|
||||
AOTSnapshotType type,
|
||||
Dart_AotBinaryFormat format,
|
||||
void* debug_callback_data,
|
||||
GrowableArray<LoadingUnitSerializationData*>* units,
|
||||
LoadingUnitSerializationData* unit,
|
||||
uint32_t program_hash) {
|
||||
uint32_t program_hash,
|
||||
const char* identifier) {
|
||||
Thread* T = Thread::Current();
|
||||
|
||||
NOT_IN_PRODUCT(TimelineBeginEndScope tbes2(T, Timeline::GetIsolateStream(),
|
||||
@@ -6465,14 +6462,21 @@ static void CreateAppAOTSnapshot(
|
||||
Dwarf* debug_dwarf = nullptr;
|
||||
SharedObjectWriter* debug_so = nullptr;
|
||||
if (generate_debug) {
|
||||
debug_dwarf = new (Z) Dwarf(Z, deobfuscation_trie);
|
||||
debug_so = new (Z) ElfWriter(
|
||||
Z, &debug_stream, SharedObjectWriter::Type::DebugInfo, debug_dwarf);
|
||||
debug_dwarf = new (Z) Dwarf(Z, deobfuscation_trie, identifier);
|
||||
if (format == Dart_AotBinaryFormat_MachO_Dylib) {
|
||||
debug_so = new (Z)
|
||||
MachOWriter(Z, &debug_stream, SharedObjectWriter::Type::DebugInfo,
|
||||
identifier, debug_dwarf);
|
||||
} else {
|
||||
debug_so = new (Z) ElfWriter(
|
||||
Z, &debug_stream, SharedObjectWriter::Type::DebugInfo, debug_dwarf);
|
||||
}
|
||||
}
|
||||
|
||||
StreamingWriteStream output_stream(
|
||||
type == AOTSnapshotType::kAssembly ? kAssemblyInitialSize : kInitialSize,
|
||||
callback, callback_data);
|
||||
StreamingWriteStream output_stream(format == Dart_AotBinaryFormat_Assembly
|
||||
? kAssemblyInitialSize
|
||||
: kInitialSize,
|
||||
callback, callback_data);
|
||||
|
||||
auto const use_output_writer = [&](ImageWriter* image_writer) {
|
||||
FullSnapshotWriter writer(Snapshot::kFullAOT, &vm_snapshot_data,
|
||||
@@ -6487,16 +6491,21 @@ static void CreateAppAOTSnapshot(
|
||||
image_writer->Finalize();
|
||||
};
|
||||
|
||||
Dwarf* const dwarf = (type == AOTSnapshotType::kAssembly || strip) ? nullptr
|
||||
: generate_debug ? debug_dwarf
|
||||
: new (Z) Dwarf(Z, deobfuscation_trie);
|
||||
Dwarf* const dwarf =
|
||||
(format == Dart_AotBinaryFormat_Assembly || strip) ? nullptr
|
||||
: generate_debug ? debug_dwarf
|
||||
: new (Z) Dwarf(Z, deobfuscation_trie, identifier);
|
||||
SharedObjectWriter* so = nullptr;
|
||||
if (type == AOTSnapshotType::kElf) {
|
||||
if (format == Dart_AotBinaryFormat_Elf) {
|
||||
so = new (Z)
|
||||
ElfWriter(Z, &output_stream, SharedObjectWriter::Type::Snapshot, dwarf);
|
||||
} else if (format == Dart_AotBinaryFormat_MachO_Dylib) {
|
||||
so = new (Z)
|
||||
MachOWriter(Z, &output_stream, SharedObjectWriter::Type::Snapshot,
|
||||
identifier, dwarf);
|
||||
}
|
||||
|
||||
if (type == AOTSnapshotType::kAssembly) {
|
||||
if (format == Dart_AotBinaryFormat_Assembly) {
|
||||
ASSERT(so == nullptr);
|
||||
AssemblyImageWriter assembly_writer(T, &output_stream, deobfuscation_trie,
|
||||
strip, debug_so);
|
||||
@@ -6512,7 +6521,7 @@ static void CreateAppAOTSnapshot(
|
||||
static void Split(Dart_CreateLoadingUnitCallback next_callback,
|
||||
void* next_callback_data,
|
||||
bool strip,
|
||||
AOTSnapshotType type,
|
||||
Dart_AotBinaryFormat format,
|
||||
Dart_StreamingWriteCallback write_callback,
|
||||
Dart_StreamingCloseCallback close_callback) {
|
||||
Thread* T = Thread::Current();
|
||||
@@ -6544,9 +6553,9 @@ static void Split(Dart_CreateLoadingUnitCallback next_callback,
|
||||
next_callback(next_callback_data, id, &write_callback_data,
|
||||
&write_debug_callback_data);
|
||||
}
|
||||
CreateAppAOTSnapshot(write_callback, write_callback_data, strip, type,
|
||||
CreateAppAOTSnapshot(write_callback, write_callback_data, strip, format,
|
||||
write_debug_callback_data, &data, data[id],
|
||||
program_hash);
|
||||
program_hash, /*identifier=*/nullptr);
|
||||
{
|
||||
TransitionVMToNative transition(T);
|
||||
close_callback(write_callback_data);
|
||||
@@ -6579,8 +6588,8 @@ Dart_CreateAppAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback,
|
||||
T->isolate_group()->object_store()->set_loading_units(Object::null_array());
|
||||
|
||||
CreateAppAOTSnapshot(callback, callback_data, strip,
|
||||
AOTSnapshotType::kAssembly, debug_callback_data, nullptr,
|
||||
nullptr, 0);
|
||||
Dart_AotBinaryFormat_Assembly, debug_callback_data,
|
||||
nullptr, nullptr, 0, /*identifier=*/nullptr);
|
||||
|
||||
return Api::Success();
|
||||
#endif
|
||||
@@ -6606,7 +6615,7 @@ DART_EXPORT Dart_Handle Dart_CreateAppAOTSnapshotAsAssemblies(
|
||||
CHECK_NULL(write_callback);
|
||||
CHECK_NULL(close_callback);
|
||||
|
||||
Split(next_callback, next_callback_data, strip, AOTSnapshotType::kAssembly,
|
||||
Split(next_callback, next_callback_data, strip, Dart_AotBinaryFormat_Assembly,
|
||||
write_callback, close_callback);
|
||||
|
||||
return Api::Success();
|
||||
@@ -6660,8 +6669,9 @@ Dart_CreateAppAOTSnapshotAsElf(Dart_StreamingWriteCallback callback,
|
||||
// Mark as not split.
|
||||
T->isolate_group()->object_store()->set_loading_units(Object::null_array());
|
||||
|
||||
CreateAppAOTSnapshot(callback, callback_data, strip, AOTSnapshotType::kElf,
|
||||
debug_callback_data, nullptr, nullptr, 0);
|
||||
CreateAppAOTSnapshot(callback, callback_data, strip, Dart_AotBinaryFormat_Elf,
|
||||
debug_callback_data, nullptr, nullptr, 0,
|
||||
/*identifier=*/nullptr);
|
||||
|
||||
return Api::Success();
|
||||
#endif
|
||||
@@ -6685,13 +6695,40 @@ Dart_CreateAppAOTSnapshotAsElfs(Dart_CreateLoadingUnitCallback next_callback,
|
||||
CHECK_NULL(write_callback);
|
||||
CHECK_NULL(close_callback);
|
||||
|
||||
Split(next_callback, next_callback_data, strip, AOTSnapshotType::kElf,
|
||||
Split(next_callback, next_callback_data, strip, Dart_AotBinaryFormat_Elf,
|
||||
write_callback, close_callback);
|
||||
|
||||
return Api::Success();
|
||||
#endif
|
||||
}
|
||||
|
||||
DART_EXPORT Dart_Handle
|
||||
Dart_CreateAppAOTSnapshotAsBinary(Dart_AotBinaryFormat format,
|
||||
Dart_StreamingWriteCallback callback,
|
||||
void* callback_data,
|
||||
bool strip,
|
||||
void* debug_callback_data,
|
||||
const char* identifier) {
|
||||
#if defined(TARGET_ARCH_IA32)
|
||||
return Api::NewError("AOT compilation is not supported on IA32.");
|
||||
#elif !defined(DART_PRECOMPILER)
|
||||
return Api::NewError(
|
||||
"This VM was built without support for AOT compilation.");
|
||||
#else
|
||||
DARTSCOPE(Thread::Current());
|
||||
API_TIMELINE_DURATION(T);
|
||||
CHECK_NULL(callback);
|
||||
|
||||
// Mark as not split.
|
||||
T->isolate_group()->object_store()->set_loading_units(Object::null_array());
|
||||
|
||||
CreateAppAOTSnapshot(callback, callback_data, strip, format,
|
||||
debug_callback_data, nullptr, nullptr, 0, identifier);
|
||||
|
||||
return Api::Success();
|
||||
#endif
|
||||
}
|
||||
|
||||
DART_EXPORT Dart_Handle Dart_LoadingUnitLibraryUris(intptr_t loading_unit_id) {
|
||||
#if defined(TARGET_ARCH_IA32)
|
||||
return Api::NewError("AOT compilation is not supported on IA32.");
|
||||
|
||||
+14
-5
@@ -92,8 +92,20 @@ class InliningNode : public ZoneAllocated {
|
||||
InliningNode* children_next;
|
||||
};
|
||||
|
||||
Dwarf::Dwarf(Zone* zone, const Trie<const char>* deobfuscation_trie)
|
||||
static const char* GetRootLibraryName(Zone* zone) {
|
||||
const auto& root_library = Library::Handle(
|
||||
zone, IsolateGroup::Current()->object_store()->root_library());
|
||||
const auto& root_uri = String::Handle(zone, root_library.url());
|
||||
return root_uri.ToCString();
|
||||
}
|
||||
|
||||
Dwarf::Dwarf(Zone* zone,
|
||||
const Trie<const char>* deobfuscation_trie,
|
||||
const char* compilation_unit_name)
|
||||
: zone_(zone),
|
||||
compilation_unit_name_(compilation_unit_name != nullptr
|
||||
? compilation_unit_name
|
||||
: GetRootLibraryName(zone)),
|
||||
deobfuscation_trie_(deobfuscation_trie),
|
||||
codes_(zone, 1024),
|
||||
code_to_label_(zone),
|
||||
@@ -274,10 +286,7 @@ void Dwarf::WriteDebugInfo(DwarfWriteStream* stream) {
|
||||
// compilation unit. Note we write attributes in the same order we declared
|
||||
// them in our abbreviation above in WriteAbbreviations.
|
||||
stream->uleb128(kCompilationUnit);
|
||||
const Library& root_library = Library::Handle(
|
||||
zone_, IsolateGroup::Current()->object_store()->root_library());
|
||||
const String& root_uri = String::Handle(zone_, root_library.url());
|
||||
stream->string(root_uri.ToCString()); // DW_AT_name
|
||||
stream->string(compilation_unit_name_); // DW_AT_name
|
||||
const char* producer = zone_->PrintToString("Dart %s\n", Version::String());
|
||||
stream->string(producer); // DW_AT_producer
|
||||
stream->string(""); // DW_AT_comp_dir
|
||||
|
||||
+7
-1
@@ -155,7 +155,12 @@ class DwarfWriteStream : public ValueObject {
|
||||
|
||||
class Dwarf : public ZoneAllocated {
|
||||
public:
|
||||
explicit Dwarf(Zone* zone, const Trie<const char>* deobfuscation_trie);
|
||||
// The compilation unit name is used as the DW_AT_name for the
|
||||
// Dart program's compilation unit. If nullptr, then the name of
|
||||
// the root library is used instead.
|
||||
Dwarf(Zone* zone,
|
||||
const Trie<const char>* deobfuscation_trie,
|
||||
const char* compilation_unit_name = nullptr);
|
||||
|
||||
const ZoneGrowableArray<const Code*>& codes() const { return codes_; }
|
||||
|
||||
@@ -245,6 +250,7 @@ class Dwarf : public ZoneAllocated {
|
||||
LineNumberProgramWriter* writer);
|
||||
|
||||
Zone* const zone_;
|
||||
const char* const compilation_unit_name_;
|
||||
const Trie<const char>* const deobfuscation_trie_;
|
||||
ZoneGrowableArray<const Code*> codes_;
|
||||
DwarfCodeMap<intptr_t> code_to_label_;
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ namespace dart {
|
||||
|
||||
// The max page size on all supported architectures. Used to determine
|
||||
// the alignment of load segments, so that they are guaranteed page-aligned,
|
||||
// and no ELF section or segment should have a larger alignment.
|
||||
// and no shared object section or segment should have a larger alignment.
|
||||
#if defined(DART_TARGET_OS_LINUX) && defined(TARGET_ARCH_ARM64)
|
||||
// Some Linux distributions on ARM64 select 64 KB page size.
|
||||
// Follow LLVM (https://reviews.llvm.org/D25079) and set maximum page size
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "include/dart_api.h"
|
||||
#include "platform/assert.h"
|
||||
#include "platform/elf.h"
|
||||
#include "platform/mach_o.h"
|
||||
#include "vm/bss_relocs.h"
|
||||
#include "vm/class_id.h"
|
||||
#include "vm/compiler/runtime_api.h"
|
||||
@@ -106,6 +107,8 @@ const uint8_t* Image::build_id() const {
|
||||
if (compiled_to_elf()) {
|
||||
auto* const note = reinterpret_cast<const elf::Note*>(start);
|
||||
return note->data + note->name_size;
|
||||
} else if (compiled_to_macho()) {
|
||||
return reinterpret_cast<const mach_o::uuid_command*>(start)->uuid;
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
@@ -119,6 +122,8 @@ intptr_t Image::build_id_length() const {
|
||||
if (compiled_to_elf()) {
|
||||
auto const note = reinterpret_cast<const elf::Note*>(start);
|
||||
return note->description_size;
|
||||
} else if (compiled_to_macho()) {
|
||||
return sizeof(mach_o::uuid_command::uuid);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
@@ -148,10 +153,10 @@ const uint8_t* Image::shared_object_start() const {
|
||||
bool Image::compiled_to_elf() const {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (!compiled_to_shared_object()) return false;
|
||||
constexpr intptr_t len = ARRAY_SIZE(elf::ELFMAG);
|
||||
const uint8_t* so_start = shared_object_start();
|
||||
for (intptr_t i = 0; i < len; ++i) {
|
||||
if (so_start[i] != elf::ELFMAG[i]) return false;
|
||||
auto* const ident =
|
||||
reinterpret_cast<const elf::ElfHeader*>(shared_object_start())->ident;
|
||||
for (size_t i = 0; i < ARRAY_SIZE(elf::ELFMAG); ++i) {
|
||||
if (ident[i] != elf::ELFMAG[i]) return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
@@ -159,6 +164,19 @@ bool Image::compiled_to_elf() const {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Image::compiled_to_macho() const {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (!compiled_to_shared_object()) return false;
|
||||
auto const magic =
|
||||
reinterpret_cast<const mach_o::mach_header*>(shared_object_start())
|
||||
->magic;
|
||||
return magic == mach_o::MH_MAGIC || magic == mach_o::MH_CIGAM ||
|
||||
magic == mach_o::MH_MAGIC_64 || magic == mach_o::MH_CIGAM_64;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
uword ObjectOffsetTrait::Hash(Key key) {
|
||||
ObjectPtr obj = key;
|
||||
ASSERT(!obj->IsSmi());
|
||||
|
||||
@@ -91,6 +91,10 @@ class Image : ValueObject {
|
||||
// Only valid for instructions images from precompiled snapshots.
|
||||
bool compiled_to_elf() const;
|
||||
|
||||
// Returns whether this instructions section was directly compiled to MachO.
|
||||
// Only valid for instructions images from precompiled snapshots.
|
||||
bool compiled_to_macho() const;
|
||||
|
||||
private:
|
||||
// For snapshots directly compiled to a shared object, returns a pointer to
|
||||
// the beginning of the build id container. Otherwise returns nullptr;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,79 @@
|
||||
// Copyright (c) 2025, 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.
|
||||
|
||||
#ifndef RUNTIME_VM_MACH_O_H_
|
||||
#define RUNTIME_VM_MACH_O_H_
|
||||
|
||||
#include "platform/globals.h"
|
||||
|
||||
#if defined(DART_PRECOMPILER)
|
||||
|
||||
#include "vm/allocation.h"
|
||||
#include "vm/compiler/runtime_api.h"
|
||||
#include "vm/datastream.h"
|
||||
#include "vm/growable_array.h"
|
||||
#include "vm/so_writer.h"
|
||||
#include "vm/zone.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
class MachOHeader;
|
||||
class MachOSymbolTable;
|
||||
class MachOWriteStream;
|
||||
|
||||
class MachOWriter : public SharedObjectWriter {
|
||||
public:
|
||||
MachOWriter(Zone* zone,
|
||||
BaseWriteStream* stream,
|
||||
Type type,
|
||||
const char* id,
|
||||
Dwarf* dwarf = nullptr);
|
||||
|
||||
#if defined(TARGET_ARCH_ARM64)
|
||||
static constexpr intptr_t kPageSize = 16 * KB;
|
||||
#else
|
||||
static constexpr intptr_t kPageSize = 4 * KB;
|
||||
#endif
|
||||
intptr_t page_size() const override { return kPageSize; }
|
||||
|
||||
Output output() const override { return Output::MachO; }
|
||||
const MachOHeader& header() const { return header_; }
|
||||
|
||||
void AddText(const char* name,
|
||||
intptr_t label,
|
||||
const uint8_t* bytes,
|
||||
intptr_t size,
|
||||
const ZoneGrowableArray<Relocation>* relocations,
|
||||
const ZoneGrowableArray<SymbolData>* symbol) override;
|
||||
void AddROData(const char* name,
|
||||
intptr_t label,
|
||||
const uint8_t* bytes,
|
||||
intptr_t size,
|
||||
const ZoneGrowableArray<Relocation>* relocations,
|
||||
const ZoneGrowableArray<SymbolData>* symbols) override;
|
||||
|
||||
void Finalize() override;
|
||||
|
||||
void AssertConsistency(const SharedObjectWriter* debug) const override {
|
||||
if (auto* const debug_macho = debug->AsMachOWriter()) {
|
||||
AssertConsistency(this, debug_macho);
|
||||
} else {
|
||||
FATAL("Expected both snapshot and debug to be MachO");
|
||||
}
|
||||
}
|
||||
|
||||
const MachOWriter* AsMachOWriter() const override { return this; }
|
||||
|
||||
private:
|
||||
static void AssertConsistency(const MachOWriter* snapshot,
|
||||
const MachOWriter* debug_info);
|
||||
|
||||
MachOHeader& header_;
|
||||
};
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // DART_PRECOMPILER
|
||||
|
||||
#endif // RUNTIME_VM_MACH_O_H_
|
||||
@@ -304,7 +304,9 @@ OS::BuildId OS::GetAppBuildId(const uint8_t* snapshot_instructions) {
|
||||
const uint8_t* dso_base = GetAppDSOBase(snapshot_instructions);
|
||||
const auto& macho_header =
|
||||
*reinterpret_cast<const struct mach_header*>(dso_base);
|
||||
// We assume host endianness in the Mach-O file.
|
||||
// If the Mach-O file is not host endian, then we'd need to adjust the code
|
||||
// below (and also the snapshot loading code) to load multibyte integers
|
||||
// as reverse endian.
|
||||
if (macho_header.magic != MH_MAGIC && macho_header.magic != MH_MAGIC_64) {
|
||||
return {0, nullptr};
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#if defined(DART_HOST_OS_MACOS)
|
||||
#include <os/signpost.h>
|
||||
#endif
|
||||
|
||||
#include "platform/atomic.h"
|
||||
#include "platform/hashmap.h"
|
||||
#include "vm/isolate.h"
|
||||
|
||||
@@ -29,10 +29,17 @@
|
||||
#if defined(FUCHSIA_SDK) || defined(DART_HOST_OS_FUCHSIA)
|
||||
#include <lib/trace-engine/context.h>
|
||||
#include <lib/trace-engine/instrumentation.h>
|
||||
#elif defined(DART_HOST_OS_MACOS)
|
||||
#include <os/signpost.h>
|
||||
#endif // defined(FUCHSIA_SDK) || defined(DART_HOST_OS_FUCHSIA)
|
||||
|
||||
#if defined(DART_HOST_OS_MACOS)
|
||||
// Including <os/signpost.h> in this header leads to an include of
|
||||
// <mach-o/loader.h>, which causes files that use this header and the
|
||||
// definitions in "platform/mach_o.h" to fail to build. To avoid this,
|
||||
// duplicate the typedef that <os/log.h> creates here and only include
|
||||
// <os/signpost.h> in timeline.cc and timeline_macos.cc.
|
||||
typedef struct os_log_s* os_log_t;
|
||||
#endif
|
||||
|
||||
namespace dart {
|
||||
|
||||
#if !defined(SUPPORT_TIMELINE)
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "vm/globals.h"
|
||||
#if defined(DART_HOST_OS_MACOS) && defined(SUPPORT_TIMELINE)
|
||||
|
||||
#include <os/signpost.h>
|
||||
|
||||
#include "vm/log.h"
|
||||
#include "vm/timeline.h"
|
||||
|
||||
|
||||
@@ -166,6 +166,8 @@ vm_sources = [
|
||||
"log.h",
|
||||
"longjump.cc",
|
||||
"longjump.h",
|
||||
"mach_o.cc",
|
||||
"mach_o.h",
|
||||
"megamorphic_cache_table.cc",
|
||||
"megamorphic_cache_table.h",
|
||||
"memory_region.cc",
|
||||
|
||||
Reference in New Issue
Block a user