Revert "[vm, gen_snapshot] Add app-aot-macho-dylib option for AOT snapshots."
This reverts commit 38ef28a0589c0ac3009856a636b353e419e25c46.
Reason for revert: Breaks build on debug mode 32-bit arches,
release mode simarm_x64
Issue: https://github.com/dart-lang/sdk/issues/60307
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>
Issue: https://github.com/dart-lang/sdk/issues/60307
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
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Change-Id: Iff2ab4c84a513a184784129f456bd357ae8e3a67
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/431220
Commit-Queue: Slava Egorov <vegorov@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Auto-Submit: Tess Strickland <sstrickl@google.com>
diff --git a/pkg/native_stack_traces/CHANGELOG.md b/pkg/native_stack_traces/CHANGELOG.md
index c339ca5..1ad73eb 100644
--- a/pkg/native_stack_traces/CHANGELOG.md
+++ b/pkg/native_stack_traces/CHANGELOG.md
@@ -1,6 +1,3 @@
-## 0.6.1
-- Add handling for Mach-O UUID load commands.
-
## 0.6.1-wip
- Update SDK constraint to `^3.5.0`.
diff --git a/pkg/native_stack_traces/lib/src/macho.dart b/pkg/native_stack_traces/lib/src/macho.dart
index 15b65f4..c8d7e30 100644
--- a/pkg/native_stack_traces/lib/src/macho.dart
+++ b/pkg/native_stack_traces/lib/src/macho.dart
@@ -124,7 +124,6 @@
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.
@@ -140,9 +139,6 @@
case LC_SYMTAB:
command = SymbolTableCommand.fromReader(reader, cmd, cmdsize);
break;
- case LC_UUID:
- command = UuidCommand.fromReader(reader, cmd, cmdsize);
- break;
default:
break;
}
@@ -328,29 +324,6 @@
}
}
-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;
@@ -550,8 +523,7 @@
_symbolTable[constants.isolateSymbolName]?.value;
@override
- String? get buildId =>
- _commands.whereType<UuidCommand>().firstOrNull?.uuidString;
+ String? get buildId => null;
@override
DwarfContainerStringTable? get debugStringTable => _debugStringTable;
diff --git a/pkg/native_stack_traces/pubspec.yaml b/pkg/native_stack_traces/pubspec.yaml
index d6b09a5..466fad1 100644
--- a/pkg/native_stack_traces/pubspec.yaml
+++ b/pkg/native_stack_traces/pubspec.yaml
@@ -1,5 +1,5 @@
name: native_stack_traces
-version: 0.6.1
+version: 0.6.1-wip
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 @@
dependencies:
args: ^2.0.0
- path: ^1.9.0
+ path: ^1.8.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
diff --git a/runtime/bin/dart_api_win.c b/runtime/bin/dart_api_win.c
index 626b3dc..c8924a21 100644
--- a/runtime/bin/dart_api_win.c
+++ b/runtime/bin/dart_api_win.c
@@ -424,13 +424,6 @@
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*);
@@ -731,8 +724,6 @@
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;
@@ -1302,9 +1293,6 @@
Dart_CreateAppAOTSnapshotAsElfsFn =
(Dart_CreateAppAOTSnapshotAsElfsType)GetProcAddress(
process, "Dart_CreateAppAOTSnapshotAsElfs");
- Dart_CreateAppAOTSnapshotAsBinaryFn =
- (Dart_CreateAppAOTSnapshotAsBinaryType)GetProcAddress(
- process, "Dart_CreateAppAOTSnapshotAsBinary");
Dart_CreateVMAOTSnapshotAsAssemblyFn =
(Dart_CreateVMAOTSnapshotAsAssemblyType)GetProcAddress(
process, "Dart_CreateVMAOTSnapshotAsAssembly");
@@ -2563,18 +2551,6 @@
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) {
diff --git a/runtime/bin/dartutils.cc b/runtime/bin/dartutils.cc
index 0c5251c..78e2c31 100644
--- a/runtime/bin/dartutils.cc
+++ b/runtime/bin/dartutils.cc
@@ -14,7 +14,6 @@
#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.
@@ -35,6 +34,9 @@
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}};
@@ -402,8 +404,9 @@
MagicNumber magic_number = DartUtils::kUnknownMagicNumber;
ASSERT(kMaxMagicNumberSize == appjit_magic_number.length);
ASSERT(aotelf_magic_number.length <= appjit_magic_number.length);
- ASSERT(static_cast<intptr_t>(sizeof(mach_o::mach_header::magic)) <=
- 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(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);
@@ -450,19 +453,16 @@
return kAotELFMagicNumber;
}
- // 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, 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;
}
if (CheckMagicNumber(buffer, buffer_length, aotcoff_arm32_magic_number)) {
diff --git a/runtime/bin/dartutils.h b/runtime/bin/dartutils.h
index 2d0aa81..6ec605b 100644
--- a/runtime/bin/dartutils.h
+++ b/runtime/bin/dartutils.h
@@ -260,10 +260,9 @@
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,
@@ -279,24 +278,7 @@
(number <= DartUtils::kAotCoffRISCV64MagicNumber);
}
- // 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.
+ // Checks if the buffer is a script snapshot, kernel file, or gzip file.
static MagicNumber SniffForMagicNumber(const char* filename);
// Checks if the buffer is a script snapshot, kernel file, or gzip file.
diff --git a/runtime/bin/gen_snapshot.cc b/runtime/bin/gen_snapshot.cc
index dcdd49f..c5e7b2e 100644
--- a/runtime/bin/gen_snapshot.cc
+++ b/runtime/bin/gen_snapshot.cc
@@ -78,7 +78,6 @@
kAppJIT,
kAppAOTAssembly,
kAppAOTElf,
- kAppAOTMachODylib,
kVMAOTAssembly,
};
static SnapshotKind snapshot_kind = kCore;
@@ -91,7 +90,6 @@
"app-jit",
"app-aot-assembly",
"app-aot-elf",
- "app-aot-macho-dylib",
"vm-aot-assembly",
nullptr,
// clang-format on
@@ -110,7 +108,6 @@
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)
@@ -140,7 +137,6 @@
static bool IsSnapshottingForPrecompilation() {
return (snapshot_kind == kAppAOTAssembly) || (snapshot_kind == kAppAOTElf) ||
- (snapshot_kind == kAppAOTMachODylib) ||
(snapshot_kind == kVMAOTAssembly);
}
@@ -180,15 +176,6 @@
"[--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"
@@ -280,15 +267,6 @@
}
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) {
@@ -619,99 +597,78 @@
static void CreateAndWritePrecompiledSnapshot() {
ASSERT(IsSnapshottingForPrecompilation());
-
- 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);
+ Dart_Handle result;
// Precompile with specified embedder entry points
- Dart_Handle result = Dart_Precompile();
+ 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 (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 (snapshot_kind == kAppAOTAssembly) {
+ if (strip && (debugging_info_filename == nullptr)) {
+ Syslog::PrintErr(
+ "Warning: Generating assembly code without DWARF debugging"
+ " information.\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;
+ 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);
}
- CHECK_RESULT(result);
+ 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");
+ }
} else {
- 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);
+ UNREACHABLE();
}
// Serialize obfuscation map if requested.
@@ -812,10 +769,15 @@
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();
}
diff --git a/runtime/bin/snapshot_utils.cc b/runtime/bin/snapshot_utils.cc
index dced66d..460832a 100644
--- a/runtime/bin/snapshot_utils.cc
+++ b/runtime/bin/snapshot_utils.cc
@@ -2,11 +2,10 @@
// 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 "bin/snapshot_utils.h"
-
-#include <cerrno>
#include <memory>
+#include "bin/snapshot_utils.h"
+
#include "bin/dartutils.h"
#include "bin/dfe.h"
#include "bin/elf_loader.h"
@@ -24,13 +23,6 @@
#define LOG_SECTION_BOUNDARIES false
-#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
-
namespace dart {
namespace bin {
@@ -153,105 +145,6 @@
#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(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);
-}
-
class ElfAppSnapshot : public AppSnapshot {
public:
ElfAppSnapshot(Dart_LoadedElf* elf,
@@ -291,18 +184,6 @@
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;
@@ -339,8 +220,7 @@
AppSnapshot* Snapshot::TryReadAppendedAppSnapshotElfFromMachO(
const char* container_path) {
// Ensure file is actually MachO-formatted.
- DartUtils::MagicNumber magic_number;
- if (!IsMachOFormattedBinary(container_path, &magic_number)) {
+ if (!IsMachOFormattedBinary(container_path)) {
Syslog::PrintErr("Expected a Mach-O binary.\n");
return nullptr;
}
@@ -355,19 +235,17 @@
// 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;
- if (!file->ReadFully(&header, sizeof(header))) {
- Syslog::PrintErr("Could not read a complete Mach-O header.\n");
+ 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");
return nullptr;
}
- 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);
+ 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));
}
// Now we search through the load commands to find our snapshot note, which
@@ -522,29 +400,108 @@
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)
-bool Snapshot::IsMachOFormattedBinary(const char* filename,
- DartUtils::MagicNumber* out) {
+#if defined(DART_TARGET_OS_MACOS)
+bool Snapshot::IsMachOFormattedBinary(const char* filename) {
File* file = File::Open(nullptr, filename, File::kRead);
if (file == nullptr) {
return false;
}
RefCntReleaseScope<File> rs(file);
- uint8_t header[DartUtils::kMaxMagicNumberSize];
- if (!file->ReadFully(&header, DartUtils::kMaxMagicNumberSize)) {
+ const uint64_t size = file->Length();
+ // Parse the first 4 bytes and check the magic numbers.
+ uint32_t magic;
+ if (size < sizeof(magic)) {
// The file isn't long enough to contain the magic bytes.
return false;
}
- DartUtils::MagicNumber magic_number =
- DartUtils::SniffForMagicNumber(header, sizeof(header));
- if (out != nullptr) {
- *out = magic_number;
+ 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;
}
- 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) {
@@ -640,20 +597,23 @@
// For testing AOT with the standalone embedder, we also support loading
// from a dynamic library to simulate what happens on iOS.
- 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)) {
+
+#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) {
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.
diff --git a/runtime/bin/snapshot_utils.h b/runtime/bin/snapshot_utils.h
index 2b25de9..f77035b 100644
--- a/runtime/bin/snapshot_utils.h
+++ b/runtime/bin/snapshot_utils.h
@@ -46,8 +46,9 @@
static void GenerateAppJIT(const char* snapshot_filename);
static void GenerateAppAOTAsAssembly(const char* snapshot_filename);
- static bool IsMachOFormattedBinary(const char* container_path,
- DartUtils::MagicNumber* out = nullptr);
+#if defined(DART_TARGET_OS_MACOS)
+ static bool IsMachOFormattedBinary(const char* container_path);
+#endif
#if defined(DART_TARGET_OS_WINDOWS)
static bool IsPEFormattedBinary(const char* container_path);
#endif
diff --git a/runtime/bin/snapshot_utils_test.cc b/runtime/bin/snapshot_utils_test.cc
index 153d68c..93317ba 100644
--- a/runtime/bin/snapshot_utils_test.cc
+++ b/runtime/bin/snapshot_utils_test.cc
@@ -8,11 +8,12 @@
#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,
@@ -55,19 +56,12 @@
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);
- // Only host-endian MachO files are recognized.
- EXPECT_EQ(host_endian,
- bin::Snapshot::IsMachOFormattedBinary(testcase.filename));
+ EXPECT(bin::Snapshot::IsMachOFormattedBinary(testcase.filename));
EXPECT(bin::File::Delete(nullptr, testcase.filename));
}
@@ -76,5 +70,6 @@
bin::test::GetFileName("runtime/bin/snapshot_utils_test.cc");
EXPECT(!bin::Snapshot::IsMachOFormattedBinary(kFilename));
}
+#endif
} // namespace dart
diff --git a/runtime/configs.gni b/runtime/configs.gni
index e55a876..ea79bcb 100644
--- a/runtime/configs.gni
+++ b/runtime/configs.gni
@@ -203,10 +203,6 @@
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,
@@ -245,9 +241,6 @@
sources += snapshot_sources
}
} else {
- if (conf.compiler) {
- deps += extra_precompiler_deps
- }
if (defined(snapshot_sources)) {
not_needed([ "snapshot_sources" ])
}
@@ -278,10 +271,6 @@
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}") {
@@ -314,7 +303,6 @@
sources += snapshot_sources
}
} else {
- deps += extra_precompiler_deps
if (defined(snapshot_sources)) {
not_needed([ "snapshot_sources" ])
}
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index 675c98b..7394525 100644
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -3987,7 +3987,7 @@
*
* 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_RUNTIME. The kDartVmSnapshotData and
+ * DART_PRECOMPILED_SNAPSHOT. The kDartVmSnapshotData and
* kDartVmSnapshotInstructions should be passed to Dart_Initialize. The
* kDartIsolateSnapshotData and kDartIsolateSnapshotInstructions should be
* passed to Dart_CreateIsolateGroup.
@@ -4027,7 +4027,7 @@
* - _kDartIsolateSnapshotInstructions
*
* The shared library should be dynamically loaded by the embedder.
- * Running this snapshot requires a VM compiled with DART_PRECOMPILED_RUNTIME.
+ * Running this snapshot requires a VM compiled with DART_PRECOMPILED_SNAPSHOT.
* The kDartVmSnapshotData and kDartVmSnapshotInstructions should be passed to
* Dart_Initialize. The kDartIsolateSnapshotData and
* kDartIsolateSnapshotInstructions should be passed to Dart_CreateIsolate.
@@ -4054,52 +4054,6 @@
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
diff --git a/runtime/platform/mach_o.h b/runtime/platform/mach_o.h
index 12c8f71..b437582 100644
--- a/runtime/platform/mach_o.h
+++ b/runtime/platform/mach_o.h
@@ -15,43 +15,8 @@
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;
-
-// 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;
@@ -79,455 +44,20 @@
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;
-// 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;
- // 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 cmd;
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 {
- 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 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)
} // namespace mach_o
diff --git a/runtime/platform/utils.cc b/runtime/platform/utils.cc
index 3d1038a..074c539 100644
--- a/runtime/platform/utils.cc
+++ b/runtime/platform/utils.cc
@@ -10,7 +10,6 @@
#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>
@@ -399,21 +398,4 @@
}
}
-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
diff --git a/runtime/platform/utils.h b/runtime/platform/utils.h
index 2eed829..dd63212 100644
--- a/runtime/platform/utils.h
+++ b/runtime/platform/utils.h
@@ -663,11 +663,6 @@
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|.
@@ -687,14 +682,6 @@
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
diff --git a/runtime/tests/vm/dart/analyze_snapshot_binary_test.dart b/runtime/tests/vm/dart/analyze_snapshot_binary_test.dart
index 408e7dc..ad49f6f 100644
--- a/runtime/tests/vm/dart/analyze_snapshot_binary_test.dart
+++ b/runtime/tests/vm/dart/analyze_snapshot_binary_test.dart
@@ -84,7 +84,7 @@
if (useAsm) {
final assemblyPath = path.join(tempDir, 'test.S');
- await (disassemble ? runSilent : run)(genSnapshot, <String>[
+ await run(genSnapshot, <String>[
'--snapshot-kind=app-aot-assembly',
'--assembly=$assemblyPath',
...commonSnapshotArgs,
@@ -92,7 +92,7 @@
await assembleSnapshot(assemblyPath, snapshotPath);
} else {
- await (disassemble ? runSilent : run)(genSnapshot, <String>[
+ await run(genSnapshot, <String>[
'--snapshot-kind=app-aot-elf',
'--elf=$snapshotPath',
...commonSnapshotArgs,
diff --git a/runtime/tests/vm/dart/disassemble_aot_test.dart b/runtime/tests/vm/dart/disassemble_aot_test.dart
index 4aeecf7..e3e3c46 100644
--- a/runtime/tests/vm/dart/disassemble_aot_test.dart
+++ b/runtime/tests/vm/dart/disassemble_aot_test.dart
@@ -53,7 +53,7 @@
// Run the AOT compiler with the disassemble flags set.
final elfFile = path.join(tempDir, 'aot.snapshot');
- await runSilent(genSnapshot, <String>[
+ await run(genSnapshot, <String>[
'--disassemble',
'--disassemble_stubs',
'--always_generate_trampolines_for_testing',
@@ -63,7 +63,7 @@
]);
// Run the AOT runtime with the disassemble flags set.
- await runSilent(dartPrecompiledRuntime, <String>[
+ await run(dartPrecompiledRuntime, <String>[
'--disassemble',
'--disassemble_stubs',
elfFile,
diff --git a/runtime/tests/vm/dart/exported_symbols_test.dart b/runtime/tests/vm/dart/exported_symbols_test.dart
index eed3979..fd49710 100644
--- a/runtime/tests/vm/dart/exported_symbols_test.dart
+++ b/runtime/tests/vm/dart/exported_symbols_test.dart
@@ -71,7 +71,6 @@
"Dart_CreateAppAOTSnapshotAsAssembly",
"Dart_CreateAppAOTSnapshotAsElf",
"Dart_CreateAppAOTSnapshotAsElfs",
- "Dart_CreateAppAOTSnapshotAsBinary",
"Dart_CreateAppJITSnapshotAsBlobs",
"Dart_CreateIsolateGroup",
"Dart_CreateIsolateGroupFromKernel",
diff --git a/runtime/tests/vm/dart/unobfuscated_static_symbols_test.dart b/runtime/tests/vm/dart/unobfuscated_static_symbols_test.dart
index 4ba1a54..384ddf4 100644
--- a/runtime/tests/vm/dart/unobfuscated_static_symbols_test.dart
+++ b/runtime/tests/vm/dart/unobfuscated_static_symbols_test.dart
@@ -57,7 +57,6 @@
]);
await checkElf(tempDir, scriptDill);
- await checkMachO(tempDir, scriptDill);
await checkAssembly(tempDir, scriptDill);
});
}
@@ -68,165 +67,145 @@
'--deterministic',
];
-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 {
+Future<void> checkElf(String tempDir, String scriptDill) async {
// Run the AOT compiler without Dwarf stack trace, once without obfuscation,
// once with obfuscation, and once with obfuscation and saving debugging
// information.
- final scriptUnobfuscatedSnapshot = path.join(
- tempDir,
- 'unobfuscated-$snapshotType.so',
- );
- await createSnapshot(scriptDill, snapshotType, scriptUnobfuscatedSnapshot);
+ final scriptUnobfuscatedSnapshot = path.join(tempDir, 'unobfuscated-elf.so');
+ await run(genSnapshot, <String>[
+ ...commonGenSnapshotArgs,
+ '--snapshot-kind=app-aot-elf',
+ '--elf=$scriptUnobfuscatedSnapshot',
+ scriptDill,
+ ]);
final unobfuscatedCase = TestCase(
scriptUnobfuscatedSnapshot,
- snapshotType.fromFile(scriptUnobfuscatedSnapshot)!,
+ Elf.fromFile(scriptUnobfuscatedSnapshot)!,
);
final scriptObfuscatedOnlySnapshot = path.join(
tempDir,
- 'obfuscated-only-$snapshotType.so',
+ 'obfuscated-only-elf.so',
);
- await createSnapshot(scriptDill, snapshotType, scriptObfuscatedOnlySnapshot, [
+ await run(genSnapshot, <String>[
+ ...commonGenSnapshotArgs,
'--obfuscate',
+ '--snapshot-kind=app-aot-elf',
+ '--elf=$scriptObfuscatedOnlySnapshot',
+ scriptDill,
]);
final obfuscatedOnlyCase = TestCase(
scriptObfuscatedOnlySnapshot,
- snapshotType.fromFile(scriptObfuscatedOnlySnapshot)!,
+ Elf.fromFile(scriptObfuscatedOnlySnapshot)!,
);
- // 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 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)!,
+ );
- 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)!,
- );
- }
+ 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)!,
+ );
await checkCases(unobfuscatedCase, <TestCase>[
obfuscatedOnlyCase,
- if (obfuscatedCase != null) obfuscatedCase,
- if (strippedCase != null) strippedCase,
+ obfuscatedCase,
+ 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;
- await checkSnapshotType(tempDir, scriptDill, SnapshotType.assembly);
+
+ // Run the AOT compiler without Dwarf stack trace, once without obfuscation,
+ // once with obfuscation, and once with obfuscation and saving debugging
+ // information.
+ final scriptUnobfuscatedAssembly = path.join(
+ tempDir,
+ 'unobfuscated-assembly.S',
+ );
+ final scriptUnobfuscatedSnapshot = path.join(
+ tempDir,
+ 'unobfuscated-assembly.so',
+ );
+ await run(genSnapshot, <String>[
+ ...commonGenSnapshotArgs,
+ '--snapshot-kind=app-aot-assembly',
+ '--assembly=$scriptUnobfuscatedAssembly',
+ scriptDill,
+ ]);
+ await assembleSnapshot(
+ scriptUnobfuscatedAssembly,
+ scriptUnobfuscatedSnapshot,
+ );
+ final unobfuscatedCase = TestCase(
+ scriptUnobfuscatedSnapshot,
+ Platform.isMacOS
+ ? MachO.fromFile(scriptUnobfuscatedSnapshot)!
+ : Elf.fromFile(scriptUnobfuscatedSnapshot)!,
+ );
+
+ final scriptObfuscatedOnlyAssembly = path.join(
+ tempDir,
+ 'obfuscated-only-assembly.S',
+ );
+ final scriptObfuscatedOnlySnapshot = path.join(
+ tempDir,
+ 'obfuscated-only-assembly.so',
+ );
+ await run(genSnapshot, <String>[
+ ...commonGenSnapshotArgs,
+ '--obfuscate',
+ '--snapshot-kind=app-aot-assembly',
+ '--assembly=$scriptObfuscatedOnlyAssembly',
+ scriptDill,
+ ]);
+ await assembleSnapshot(
+ scriptObfuscatedOnlyAssembly,
+ scriptObfuscatedOnlySnapshot,
+ );
+ final obfuscatedOnlyCase = TestCase(
+ scriptObfuscatedOnlySnapshot,
+ Platform.isMacOS
+ ? MachO.fromFile(scriptObfuscatedOnlySnapshot)!
+ : Elf.fromFile(scriptObfuscatedOnlySnapshot)!,
+ );
+
+ await checkCases(unobfuscatedCase, <TestCase>[obfuscatedOnlyCase]);
}
class TestCase {
@@ -242,12 +221,6 @@
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);
}
diff --git a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_deferred_test.dart b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_deferred_test.dart
index 8804ba0..0b6f60f 100644
--- a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_deferred_test.dart
+++ b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_deferred_test.dart
@@ -35,7 +35,8 @@
'use_dwarf_stack_traces_flag_deferred_program.dart',
),
runNonDwarf,
- [runElf, runAssembly],
+ runElf,
+ runAssembly,
);
}
@@ -103,10 +104,10 @@
class DeferredElfState extends ElfState<DwarfMap> {
DeferredElfState(
- super.output,
- super.outputWithOppositeFlag,
super.snapshot,
super.debugInfo,
+ super.output,
+ super.outputWithOppositeFlag,
);
@override
@@ -162,19 +163,19 @@
final snapshotDwarfMap = useSnapshotForDwarfPath(pathManifest).dwarfMap;
return DeferredElfState(
- output,
- outputWithOppositeFlag,
snapshotDwarfMap,
debugInfoDwarfMap,
+ output,
+ outputWithOppositeFlag,
);
}
class DeferredAssemblyState extends AssemblyState<DwarfMap> {
DeferredAssemblyState(
- super.output,
- super.outputWithOppositeFlag,
super.snapshot,
- super.debugInfo, [
+ super.debugInfo,
+ super.output,
+ super.outputWithOppositeFlag, [
super.singleArch,
super.multiArch,
]);
@@ -297,10 +298,10 @@
}
return DeferredAssemblyState(
- output,
- outputWithOppositeFlag,
snapshotDwarfMap,
debugInfoDwarfMap,
+ output,
+ outputWithOppositeFlag,
singleArchSnapshotDwarfMap,
multiArchSnapshotDwarfMap,
);
diff --git a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_helper.dart b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_helper.dart
index 9c94865..aaccbd2 100644
--- a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_helper.dart
+++ b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_helper.dart
@@ -46,128 +46,67 @@
DwarfTestOutput(this.trace, this.allocateObjectStart, this.allocateObjectEnd);
}
-abstract class State {
+class NonDwarfState {
final DwarfTestOutput output;
final DwarfTestOutput outputWithOppositeFlag;
- State(this.output, this.outputWithOppositeFlag);
-}
-
-class NonDwarfState extends State {
- NonDwarfState(super.output, super.outputWithOppositeFlag);
+ NonDwarfState(this.output, this.outputWithOppositeFlag);
void check() => expect(outputWithOppositeFlag.trace, equals(output.trace));
}
-abstract class DwarfState<T> extends State {
+abstract class ElfState<T> {
final T snapshot;
final T debugInfo;
- DwarfState(
- super.output,
- super.outputWithOppositeFlag,
+ final DwarfTestOutput output;
+ final DwarfTestOutput outputWithOppositeFlag;
+
+ ElfState(
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 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> {
+abstract class AssemblyState<T> {
+ final T snapshot;
+ final T debugInfo;
+ final DwarfTestOutput output;
+ final DwarfTestOutput outputWithOppositeFlag;
final T? singleArch;
final T? multiArch;
- MultiArchDwarfState(
- super.output,
- super.outputWithOppositeFlag,
- super.snapshot,
- super.debugInfo, [
+ AssemblyState(
+ this.snapshot,
+ this.debugInfo,
+ this.output,
+ this.outputWithOppositeFlag, [
this.singleArch,
this.multiArch,
]);
- @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,
- );
- }
+ Future<void> check(Trace trace, T t);
}
-abstract class AssemblyState<T> extends MultiArchDwarfState<T> {
- AssemblyState(
- super.output,
- super.outputWithOppositeFlag,
- super.snapshot,
- super.debugInfo, [
- super.singleArch,
- super.multiArch,
- ]);
+abstract class UniversalBinaryState<T> {
+ final T singleArch;
+ final T multiArch;
- @override
- String get description => 'assembly';
-}
+ UniversalBinaryState(this.singleArch, this.multiArch);
-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> checkSingleArch(Trace trace, AssemblyState assemblyState);
+ Future<void> checkMultiArch(Trace trace, AssemblyState assemblyState);
}
Future<void> runTests<T>(
String tempPrefix,
String scriptPath,
Future<NonDwarfState> Function(String, String) runNonDwarf,
- Iterable<Future<DwarfState?> Function(String, String)> runDwarfs,
+ Future<ElfState<T>> Function(String, String) runElf,
+ Future<AssemblyState<T>?> Function(String, String) runAssembly,
) async {
if (!isAOTRuntime) {
return; // Running in JIT: AOT binaries not available.
@@ -204,17 +143,44 @@
]);
final nonDwarfState = await runNonDwarf(tempDir, scriptDill);
- final dwarfStates = [
- for (final f in runDwarfs) await f(tempDir, scriptDill),
- ];
+ final elfState = await runElf(tempDir, scriptDill);
+ final assemblyState = await runAssembly(tempDir, scriptDill);
test('Testing symbolic traces', nonDwarfState.check);
final nonDwarfTrace = nonDwarfState.output.trace;
- for (final dwarfState in dwarfStates.whereType<DwarfState>()) {
- dwarfState.makeTests(nonDwarfTrace);
- }
+ 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);
});
}
diff --git a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart
index dccb920..466df02 100644
--- a/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart
+++ b/runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart
@@ -29,13 +29,8 @@
'use_dwarf_stack_traces_flag_program.dart',
),
runNonDwarf,
- [
- 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,
- ],
+ runElf,
+ runAssembly,
);
}
@@ -69,10 +64,10 @@
class DwarfElfState extends ElfState<Dwarf> {
DwarfElfState(
- super.output,
- super.outputWithOppositeFlag,
super.snapshot,
super.debugInfo,
+ super.output,
+ super.outputWithOppositeFlag,
);
@override
@@ -81,9 +76,8 @@
}
Future<DwarfElfState> runElf(String tempDir, String scriptDill) async {
- print("Generating ELF snapshots");
- final snapshotPath = path.join(tempDir, 'dwarf_elf.so');
- final debugInfoPath = path.join(tempDir, 'debug_info_elf.so');
+ final snapshotPath = path.join(tempDir, 'dwarf.so');
+ final debugInfoPath = path.join(tempDir, 'debug_info.so');
await run(genSnapshot, <String>[
'--dwarf-stack-traces-mode',
'--save-debugging-info=$debugInfoPath',
@@ -96,7 +90,7 @@
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,
@@ -107,15 +101,15 @@
<String>['--no-dwarf-stack-traces-mode', snapshotPath, scriptDill],
);
- return DwarfElfState(output, outputWithOppositeFlag, snapshot, debugInfo);
+ return DwarfElfState(snapshot, debugInfo, output, outputWithOppositeFlag);
}
class DwarfAssemblyState extends AssemblyState<Dwarf> {
DwarfAssemblyState(
- super.output,
- super.outputWithOppositeFlag,
super.snapshot,
- super.debugInfo, [
+ super.debugInfo,
+ super.output,
+ super.outputWithOppositeFlag, [
super.singleArch,
super.multiArch,
]);
@@ -142,7 +136,6 @@
// 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
@@ -159,7 +152,6 @@
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',
@@ -190,7 +182,6 @@
emptyFiles[arch] = emptyPath;
}
- print("Generating multi-arch assembly debugging information");
final singleArchSnapshotPath = path.join(tempDir, "ub-single");
await run(lipo, <String>[
debugSnapshotPath,
@@ -212,98 +203,10 @@
}
return DwarfAssemblyState(
- 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,
);
@@ -345,9 +248,8 @@
checkTranslatedTrace(nonDwarfTrace, translatedDwarfTrace1);
- // 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.
+ // 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.
// The offsets of absolute addresses from their respective DSO base
// should be the same for both traces.
diff --git a/runtime/tests/vm/dart/use_flag_test_helper.dart b/runtime/tests/vm/dart/use_flag_test_helper.dart
index 07195e8..a4bba14 100644
--- a/runtime/tests/vm/dart/use_flag_test_helper.dart
+++ b/runtime/tests/vm/dart/use_flag_test_helper.dart
@@ -173,23 +173,20 @@
await run(strip, <String>['-o', strippedPath, snapshotPath]);
}
-Future<ProcessResult> runHelper(
- String executable,
- List<String> args, {
- bool printStdout = true,
- bool printStderr = true,
-}) async {
+Future<ProcessResult> runHelper(String executable, List<String> args) async {
print('Running $executable ${args.join(' ')}');
final result = await Process.run(executable, args);
print('Subcommand terminated with exit code ${result.exitCode}.');
- if (printStdout && result.stdout.isNotEmpty) {
+ if (result.stdout.isNotEmpty) {
print('Subcommand stdout:');
print(result.stdout);
}
- if (printStderr && result.stderr.isNotEmpty) {
- print('Subcommand stderr:');
- print(result.stderr);
+ if (result.exitCode != 0) {
+ if (result.stderr.isNotEmpty) {
+ print('Subcommand stderr:');
+ print(result.stderr);
+ }
}
return result;
@@ -213,19 +210,6 @@
}
}
-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);
diff --git a/runtime/vm/BUILD.gn b/runtime/vm/BUILD.gn
index a28bd8d..19cfec7 100644
--- a/runtime/vm/BUILD.gn
+++ b/runtime/vm/BUILD.gn
@@ -180,10 +180,6 @@
"//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",
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 48608cc..8bdc0e8 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -31,7 +31,6 @@
#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"
@@ -6430,16 +6429,20 @@
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,
- Dart_AotBinaryFormat format,
+ AOTSnapshotType type,
void* debug_callback_data,
GrowableArray<LoadingUnitSerializationData*>* units,
LoadingUnitSerializationData* unit,
- uint32_t program_hash,
- const char* identifier) {
+ uint32_t program_hash) {
Thread* T = Thread::Current();
NOT_IN_PRODUCT(TimelineBeginEndScope tbes2(T, Timeline::GetIsolateStream(),
@@ -6462,21 +6465,14 @@
Dwarf* debug_dwarf = nullptr;
SharedObjectWriter* debug_so = nullptr;
if (generate_debug) {
- 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);
- }
+ debug_dwarf = new (Z) Dwarf(Z, deobfuscation_trie);
+ debug_so = new (Z) ElfWriter(
+ Z, &debug_stream, SharedObjectWriter::Type::DebugInfo, debug_dwarf);
}
- StreamingWriteStream output_stream(format == Dart_AotBinaryFormat_Assembly
- ? kAssemblyInitialSize
- : kInitialSize,
- callback, callback_data);
+ StreamingWriteStream output_stream(
+ type == AOTSnapshotType::kAssembly ? kAssemblyInitialSize : kInitialSize,
+ callback, callback_data);
auto const use_output_writer = [&](ImageWriter* image_writer) {
FullSnapshotWriter writer(Snapshot::kFullAOT, &vm_snapshot_data,
@@ -6491,21 +6487,16 @@
image_writer->Finalize();
};
- Dwarf* const dwarf =
- (format == Dart_AotBinaryFormat_Assembly || strip) ? nullptr
- : generate_debug ? debug_dwarf
- : new (Z) Dwarf(Z, deobfuscation_trie, identifier);
+ Dwarf* const dwarf = (type == AOTSnapshotType::kAssembly || strip) ? nullptr
+ : generate_debug ? debug_dwarf
+ : new (Z) Dwarf(Z, deobfuscation_trie);
SharedObjectWriter* so = nullptr;
- if (format == Dart_AotBinaryFormat_Elf) {
+ if (type == AOTSnapshotType::kElf) {
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 (format == Dart_AotBinaryFormat_Assembly) {
+ if (type == AOTSnapshotType::kAssembly) {
ASSERT(so == nullptr);
AssemblyImageWriter assembly_writer(T, &output_stream, deobfuscation_trie,
strip, debug_so);
@@ -6521,7 +6512,7 @@
static void Split(Dart_CreateLoadingUnitCallback next_callback,
void* next_callback_data,
bool strip,
- Dart_AotBinaryFormat format,
+ AOTSnapshotType type,
Dart_StreamingWriteCallback write_callback,
Dart_StreamingCloseCallback close_callback) {
Thread* T = Thread::Current();
@@ -6553,9 +6544,9 @@
next_callback(next_callback_data, id, &write_callback_data,
&write_debug_callback_data);
}
- CreateAppAOTSnapshot(write_callback, write_callback_data, strip, format,
+ CreateAppAOTSnapshot(write_callback, write_callback_data, strip, type,
write_debug_callback_data, &data, data[id],
- program_hash, /*identifier=*/nullptr);
+ program_hash);
{
TransitionVMToNative transition(T);
close_callback(write_callback_data);
@@ -6588,8 +6579,8 @@
T->isolate_group()->object_store()->set_loading_units(Object::null_array());
CreateAppAOTSnapshot(callback, callback_data, strip,
- Dart_AotBinaryFormat_Assembly, debug_callback_data,
- nullptr, nullptr, 0, /*identifier=*/nullptr);
+ AOTSnapshotType::kAssembly, debug_callback_data, nullptr,
+ nullptr, 0);
return Api::Success();
#endif
@@ -6615,7 +6606,7 @@
CHECK_NULL(write_callback);
CHECK_NULL(close_callback);
- Split(next_callback, next_callback_data, strip, Dart_AotBinaryFormat_Assembly,
+ Split(next_callback, next_callback_data, strip, AOTSnapshotType::kAssembly,
write_callback, close_callback);
return Api::Success();
@@ -6669,9 +6660,8 @@
// Mark as not split.
T->isolate_group()->object_store()->set_loading_units(Object::null_array());
- CreateAppAOTSnapshot(callback, callback_data, strip, Dart_AotBinaryFormat_Elf,
- debug_callback_data, nullptr, nullptr, 0,
- /*identifier=*/nullptr);
+ CreateAppAOTSnapshot(callback, callback_data, strip, AOTSnapshotType::kElf,
+ debug_callback_data, nullptr, nullptr, 0);
return Api::Success();
#endif
@@ -6695,40 +6685,13 @@
CHECK_NULL(write_callback);
CHECK_NULL(close_callback);
- Split(next_callback, next_callback_data, strip, Dart_AotBinaryFormat_Elf,
+ Split(next_callback, next_callback_data, strip, AOTSnapshotType::kElf,
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.");
diff --git a/runtime/vm/dwarf.cc b/runtime/vm/dwarf.cc
index 26c0ac0..0aa736f 100644
--- a/runtime/vm/dwarf.cc
+++ b/runtime/vm/dwarf.cc
@@ -92,20 +92,8 @@
InliningNode* children_next;
};
-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)
+Dwarf::Dwarf(Zone* zone, const Trie<const char>* deobfuscation_trie)
: 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),
@@ -286,7 +274,10 @@
// compilation unit. Note we write attributes in the same order we declared
// them in our abbreviation above in WriteAbbreviations.
stream->uleb128(kCompilationUnit);
- stream->string(compilation_unit_name_); // DW_AT_name
+ 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
const char* producer = zone_->PrintToString("Dart %s\n", Version::String());
stream->string(producer); // DW_AT_producer
stream->string(""); // DW_AT_comp_dir
diff --git a/runtime/vm/dwarf.h b/runtime/vm/dwarf.h
index 04d335d..da65810 100644
--- a/runtime/vm/dwarf.h
+++ b/runtime/vm/dwarf.h
@@ -155,12 +155,7 @@
class Dwarf : public ZoneAllocated {
public:
- // 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);
+ explicit Dwarf(Zone* zone, const Trie<const char>* deobfuscation_trie);
const ZoneGrowableArray<const Code*>& codes() const { return codes_; }
@@ -250,7 +245,6 @@
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_;
diff --git a/runtime/vm/elf.h b/runtime/vm/elf.h
index 64d3d89..ddba357 100644
--- a/runtime/vm/elf.h
+++ b/runtime/vm/elf.h
@@ -20,7 +20,7 @@
// 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 shared object section or segment should have a larger alignment.
+// and no ELF 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
diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc
index 66d9c3b..2171ca0 100644
--- a/runtime/vm/image_snapshot.cc
+++ b/runtime/vm/image_snapshot.cc
@@ -7,7 +7,6 @@
#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"
@@ -107,8 +106,6 @@
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;
@@ -122,8 +119,6 @@
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;
@@ -153,10 +148,10 @@
bool Image::compiled_to_elf() const {
#if defined(DART_PRECOMPILED_RUNTIME)
if (!compiled_to_shared_object()) 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;
+ 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;
}
return true;
#else
@@ -164,19 +159,6 @@
#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());
diff --git a/runtime/vm/image_snapshot.h b/runtime/vm/image_snapshot.h
index baeb115..7b7fa0d 100644
--- a/runtime/vm/image_snapshot.h
+++ b/runtime/vm/image_snapshot.h
@@ -91,10 +91,6 @@
// 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;
diff --git a/runtime/vm/mach_o.cc b/runtime/vm/mach_o.cc
deleted file mode 100644
index de90d4d..0000000
--- a/runtime/vm/mach_o.cc
+++ /dev/null
@@ -1,2520 +0,0 @@
-// 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.
-
-#include "vm/mach_o.h"
-
-#if defined(DART_PRECOMPILER)
-
-#include <utility>
-
-#include "openssl/sha.h"
-#include "platform/mach_o.h"
-#include "vm/dwarf.h"
-#include "vm/dwarf_so_writer.h"
-#include "vm/hash_map.h"
-#include "vm/os.h"
-#include "vm/zone_text_buffer.h"
-
-namespace dart {
-
-static constexpr intptr_t kLinearInitValue = -1;
-
-#define DEFINE_LINEAR_FIELD_METHODS(name) \
- intptr_t name() const { \
- ASSERT(name##_ != kLinearInitValue); \
- return name##_; \
- } \
- bool name##_is_set() const { \
- return name##_ != kLinearInitValue; \
- } \
- void set_##name(intptr_t value) { \
- ASSERT(value != kLinearInitValue); \
- ASSERT_EQUAL(name##_, kLinearInitValue); \
- name##_ = value; \
- }
-
-#define DEFINE_LINEAR_FIELD(name) intptr_t name##_ = kLinearInitValue;
-
-// Only subclasses of MachOContents that need to be distinguished dynamically
-// via Is/As checks are listed here.
-#define FOR_EACH_CHECKABLE_MACHO_CONTENTS_TYPE(V) \
- V(MachOCommand) \
- V(MachOSegment) \
- V(MachOSection) \
- V(MachOHeader)
-
-#define DEFINE_TYPE_CHECK_FOR(Type) \
- bool Is##Type() const override { \
- return true; \
- }
-
-// All concrete subclasses of MachOContents should go here:
-#define FOR_EACH_CONCRETE_MACHO_CONTENTS_TYPE(V) \
- V(MachOHeader) \
- V(MachOSegment) \
- V(MachOSection) \
- V(MachOSymbolTable) \
- V(MachODynamicSymbolTable) \
- V(MachOUuid) \
- V(MachOBuildVersion) \
- V(MachOIdDylib) \
- V(MachOLoadDylib) \
- V(MachOCodeSignature)
-
-#define DECLARE_CONTENTS_TYPE_CLASS(Type) class Type;
-FOR_EACH_CHECKABLE_MACHO_CONTENTS_TYPE(DECLARE_CONTENTS_TYPE_CLASS)
-FOR_EACH_CONCRETE_MACHO_CONTENTS_TYPE(DECLARE_CONTENTS_TYPE_CLASS)
-#undef DECLARE_CONTENTS_TYPE_CLASS
-
-// The interface for a SharedObjectWriter::WriteStream with MachO-specific
-// utility methods.
-//
-// If HasHashes() is true, the stream calculates and store hashes of
-// written content up to the point that FinalizeHashedContent() is called.
-class MachOWriteStream : public SharedObjectWriter::WriteStream {
- template <typename T, typename S>
- using only_if_unsigned = typename std::enable_if_t<std::is_unsigned_v<T>, S>;
-
- public:
- explicit MachOWriteStream(const MachOWriter& macho)
- : SharedObjectWriter::WriteStream(), macho_(macho) {}
-
- const MachOSegment& TextSegment() const;
-
- // Write methods that write values of a certain size out to disk.
- // The disk are written in host endian format, which matches the
- // header's magic value (since it is also written with this).
- void Write16(uword value) { WriteBytes(&value, sizeof(uint16_t)); }
- void Write32(uint32_t value) { WriteBytes(&value, sizeof(uint32_t)); }
- void Write64(uint64_t value) { WriteBytes(&value, sizeof(uint64_t)); }
- void WriteWord(compiler::target::uword value) {
- WriteBytes(&value, sizeof(compiler::target::uword));
- }
-
- // Write methods that force big endian output. Used in the code signature.
- void WriteBE16(uint16_t value) { Write16(Utils::HostToBigEndian16(value)); }
- void WriteBE32(uint32_t value) { Write32(Utils::HostToBigEndian32(value)); }
- void WriteBE64(uint64_t value) { Write64(Utils::HostToBigEndian64(value)); }
-
- // Many load commands have adjacent uint32_t fields that correspond to an
- // offset into the file and a number of bytes or objects to read starting
- // from that offset, so abstract that out to make such writes stand out.
- void WriteOffsetCount(uintptr_t offset, uintptr_t count) {
- ASSERT(Utils::IsUint(32, offset));
- Write32(offset);
- ASSERT(Utils::IsUint(32, count));
- Write32(count);
- }
-
- void WriteNullTerminatedCString(const char* str) {
- WriteBytes(str, strlen(str) + 1);
- }
-
- // Writes the first n bytes of the given string. If the string is shorter
- // than n bytes, then the remainder of the space is padded with '\0'.
- void WriteFixedLengthCString(const char* str, intptr_t n) {
- const intptr_t len = strlen(str);
- WriteBytes(str, n - len <= 0 ? n : len);
- for (intptr_t i = n - len; i > 0; --i) {
- WriteByte('\0');
- }
- }
-
- bool HasValueForLabel(intptr_t label, intptr_t* value) const override;
-
- // The maximum size of a chunk of hashed content.
- static constexpr intptr_t kChunkSize = 1 << 12;
- static_assert(Utils::IsPowerOfTwo(kChunkSize));
-
- // Used for cs_code_directory::hash_type.
- static constexpr uint8_t kHashType = mach_o::CS_HASHTYPE_SHA256;
- // used for cs_code_directory::hash_size.
- static constexpr uint8_t kHashSize = SHA256_DIGEST_LENGTH;
-
- // Whether or not this MachOWriter supports hashing content.
- virtual bool HasHashes() const = 0;
- // The number of hashes calculated from the hashed content.
- // Assumes the hashed content has already been finalized.
- virtual intptr_t num_hashes() const = 0;
- // Writes the calculated hashes to the stream.
- // Assumes the hashed content has already been finalized.
- virtual void WriteHashes() = 0;
- // Call once all content that should be hashed has been written to the stream.
- virtual void FinalizeHashedContent() = 0;
-
- protected:
- const MachOWriter& macho_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MachOWriteStream);
-};
-
-// A MachOWriteStream that strictly delegates to the provided BaseWriteStream
-// without any internal caching.
-class NonHashingMachOWriteStream
- : public SharedObjectWriter::DelegatingWriteStream,
- public MachOWriteStream {
- public:
- explicit NonHashingMachOWriteStream(BaseWriteStream* stream,
- const MachOWriter& macho)
- : SharedObjectWriter::DelegatingWriteStream(stream, macho),
- MachOWriteStream(macho) {}
-
- intptr_t Position() const override {
- return SharedObjectWriter::DelegatingWriteStream::Position();
- }
- void WriteByte(const uint8_t value) override {
- SharedObjectWriter::DelegatingWriteStream::WriteByte(value);
- }
- void WriteBytes(const void* bytes, intptr_t len) override {
- SharedObjectWriter::DelegatingWriteStream::WriteBytes(bytes, len);
- }
- intptr_t Align(intptr_t alignment, intptr_t offset = 0) override {
- return SharedObjectWriter::DelegatingWriteStream::Align(alignment, offset);
- }
- bool HasValueForLabel(intptr_t label, intptr_t* value) const override {
- return MachOWriteStream::HasValueForLabel(label, value);
- }
-
- bool HasHashes() const override { return false; }
- intptr_t num_hashes() const override { UNREACHABLE(); }
- void WriteHashes() override { UNREACHABLE(); }
- void FinalizeHashedContent() override { UNREACHABLE(); }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(NonHashingMachOWriteStream);
-};
-
-// A wrapper around an BaseWriteStream that calculates hashes for kChunkSize
-// chunks being flushed.
-//
-// FinalizeHashedContent() is called after the last write of content that
-// should be hashed; further writes skip the hashing process.
-// (E.g., FinalizeHashes() is called before writing the code signature in
-// a Mach-O file.)
-class HashingMachOWriteStream : public BaseWriteStream,
- public MachOWriteStream {
- public:
- HashingMachOWriteStream(Zone* zone,
- BaseWriteStream* stream,
- const MachOWriter& macho)
- : BaseWriteStream(stream->initial_size()),
- MachOWriteStream(macho),
- zone_(zone),
- wrapped_stream_(stream),
- hashes_(zone, SHA256_DIGEST_LENGTH) {
- // So that we can use the underlying stream's Align, as all alignments
- // will be less than or equal to this alignment.
- ASSERT(Utils::IsAligned(stream->Position(), macho_.page_size()));
- }
-
- ~HashingMachOWriteStream() {
- // Hashed content should always been finalized earlier so the
- // hashes can be retrieved before destruction.
- ASSERT(!hashing_);
- Flush(/*chunks_only=*/false); // Flush all bytes.
- ASSERT_EQUAL(BaseWriteStream::Position(), 0);
- }
-
- intptr_t Position() const override {
- return flushed_size_ + BaseWriteStream::Position();
- }
- void WriteByte(const uint8_t value) override {
- BaseWriteStream::WriteByte(value);
- }
- void WriteBytes(const void* bytes, intptr_t len) override {
- BaseWriteStream::WriteBytes(bytes, len);
- }
- intptr_t Align(intptr_t alignment, intptr_t offset = 0) override {
- ASSERT(Utils::IsPowerOfTwo(alignment));
- ASSERT(alignment <= macho_.page_size());
- return BaseWriteStream::Align(alignment, offset);
- }
-
- bool HasHashes() const override { return true; }
- intptr_t num_hashes() const override {
- ASSERT(!hashing_); // Don't allow uses until hashes are finalized.
- return num_hashes_;
- }
- void WriteHashes() override {
- ASSERT(!hashing_); // Don't allow uses until hashes are finalized.
- WriteBytes(hashes_.buffer(), num_hashes_ * kHashSize);
- }
-
- // First hashes and then flushes all data in the internal buffer. Afterwards,
- // the internal buffer is empty and future Flush() calls no longer perform
- // hashing before flushing to the wrapped stream.
- //
- // Changes current_ and flushed_size_ accordingly.
- void FinalizeHashedContent() override {
- Flush(/*chunks_only=*/false);
- hashing_ = false; // End of the hashed content.
- // The only content in the hashes buffer should be the hashes themselves.
- ASSERT_EQUAL(num_hashes_ * kHashSize, hashes_.Position());
- }
-
- private:
- // Hashes [count] bytes of [buffer_] in [kChunkSize]-sized chunks and
- // returns the number of bytes hashed.
- intptr_t Hash(intptr_t count) {
- ASSERT(count >= 0);
- if (count > 0) {
- ASSERT(count <= BaseWriteStream::Position());
- for (intptr_t offset = 0; offset < count; offset += kChunkSize) {
- const intptr_t len = Utils::Minimum(count - offset, kChunkSize);
- SHA256(buffer_ + offset, len, digest_);
- hashes_.WriteBytes(digest_, kHashSize);
- num_hashes_ += 1;
- }
- }
- return count;
- }
-
- // If hashing, then hash all complete chunks and, if [chunks_only] is false,
- // a final incomplete one, then flush all hashed bytes to the wrapped stream.
- // The internal buffer is then reset to contain only unhashed bytes (if any).
- //
- // If not hashing, then all cached content is flushed immediately.
- //
- // Changes current_ and flushed_size_ accordingly.
- void Flush(bool chunks_only) {
- intptr_t size_to_flush = BaseWriteStream::Position();
- if (hashing_) {
- intptr_t size_to_hash = size_to_flush;
- if (chunks_only) {
- size_to_hash -= size_to_hash % kChunkSize;
- }
- size_to_flush = Hash(size_to_hash);
- }
- FlushBytes(size_to_flush);
- }
-
- // Flushes the initial [count] bytes of [buffer_] to the wrapped stream.
- //
- // Changes current_ and flushed_size_ accordingly.
- void FlushBytes(intptr_t count) {
- ASSERT(count >= 0);
- if (count == 0) return;
- const intptr_t remaining = BaseWriteStream::Position() - count;
- ASSERT(remaining >= 0);
- wrapped_stream_->WriteBytes(buffer_, count);
- flushed_size_ += count;
- if (remaining > 0) {
- memmove(buffer_, buffer_ + count, remaining);
- }
- current_ = buffer_ + remaining;
- }
-
- void Realloc(intptr_t new_size) override {
- Flush(/*chunks_only=*/true);
- // Check whether there's enough space after flushing.
- if (new_size <= Remaining()) return;
- // There isn't, so realloc the buffer.
- const intptr_t old_offset = BaseWriteStream::Position();
- buffer_ = zone_->Realloc(buffer_, capacity_, new_size);
- capacity_ = buffer_ != nullptr ? new_size : 0;
- current_ = buffer_ != nullptr ? buffer_ + old_offset : nullptr;
- }
-
- void SetPosition(intptr_t value) override {
- // Make sure we're not trying to set the position to already-flushed data.
- ASSERT(value >= flushed_size_);
- BaseWriteStream::SetPosition(value - flushed_size_);
- }
-
- Zone* const zone_;
- BaseWriteStream* const wrapped_stream_;
- ZoneWriteStream hashes_;
- bool hashing_ = true;
- intptr_t flushed_size_ = 0;
- intptr_t num_hashes_ = 0;
- uint8_t digest_[kHashSize]; // Used for SHA256().
-
- DISALLOW_COPY_AND_ASSIGN(HashingMachOWriteStream);
-};
-
-// A superclass for all objects that represent some content in the MachO output.
-class MachOContents : public ZoneAllocated {
- public:
- explicit MachOContents(bool needs_offset = true, bool in_segment = true)
- // Set the file offset and/or (relative) memory address to 0 if unneeded.
- : file_offset_(needs_offset ? kLinearInitValue : 0),
- memory_address_(in_segment ? kLinearInitValue : 0) {}
- virtual ~MachOContents() {}
-
- struct Visitor : public ValueObject {
- public:
- Visitor() {}
- virtual ~Visitor() {}
-
- virtual void Default(MachOContents* c) {}
-
-#define DEFINE_VISIT_METHOD(Type) \
- virtual void Visit##Type(Type* m) { \
- Default(reinterpret_cast<MachOContents*>(m)); \
- }
- FOR_EACH_CONCRETE_MACHO_CONTENTS_TYPE(DEFINE_VISIT_METHOD)
-#undef DEFINE_VISIT_METHOD
-
- private:
- DISALLOW_COPY_AND_ASSIGN(Visitor);
- };
-
- virtual void Accept(Visitor* visitor) = 0;
- virtual void VisitChildren(Visitor* visitor) {}
-
- // Content methods.
-
- // Whether WriteSelf() for this object or any nested object writes content
- // to the file. For most objects, the file offset is set to 0 at construction
- // if no content is written by it or nested objects.
- //
- // Overwrite this if the computed file offset can be 0 (e.g., the header).
- virtual bool HasContents() const { return file_offset_ != 0; }
-
- // Returns the size written to disk by WriteSelf().
- //
- // Only needs to be overwritten for unallocated objects or objects where
- // the number of bytes written by WriteSelf() does not match SelfMemorySize().
- virtual intptr_t SelfFileSize() const {
- if (!HasContents()) return 0;
- return SelfMemorySize();
- }
-
- // Writes the file contents for this object to the stream.
- //
- // Note that this does not write the load command for a command, as that
- // is handled separately by MachOCommand::WriteLoadCommand().
- //
- // Only needs to be overwritten for objects with non-zero SelfFileSize().
- virtual void WriteSelf(MachOWriteStream* stream) const {
- ASSERT_EQUAL(SelfFileSize(), 0);
- return;
- }
-
- // Returns whether the contents of an object is a segment or contained within
- // a segment and thus has an assigned relative memory address. If it has none,
- // then the memory offset is set to 0 at construction.
- //
- // Note: While technically load commands are in a segment due to being in the
- // header, this returns false for commands that only generate load commands.
- //
- // Should be overwritten if a segment or segment-contained object has a
- // computed relative memory address of 0 (e.g., the header).
- virtual bool IsAllocated() const { return memory_address_ != 0; }
-
- // Returns the size allocated in the output's memory space for this object
- // without including any allocation for nested objects.
- //
- // Should be overridden for allocated objects.
- virtual intptr_t SelfMemorySize() const {
- if (!IsAllocated()) return 0;
- UNREACHABLE();
- }
-
- // Utility/miscellaneous methods.
-
-#define DEFINE_BASE_TYPE_CHECKS(Type) \
- Type* As##Type() { \
- return Is##Type() ? reinterpret_cast<Type*>(this) : nullptr; \
- } \
- const Type* As##Type() const { \
- return const_cast<Type*>(const_cast<MachOContents*>(this)->As##Type()); \
- } \
- virtual bool Is##Type() const { return false; }
-
- FOR_EACH_CHECKABLE_MACHO_CONTENTS_TYPE(DEFINE_BASE_TYPE_CHECKS)
-#undef DEFINE_BASE_TYPE_CHECKS
-
- // Returns the alignment needed for the non-header contents.
- virtual intptr_t Alignment() const {
- // No need to override for non-allocated commands with no contents.
- ASSERT(!IsAllocated() && !HasContents());
- UNREACHABLE();
- }
-
- // The size of the contents written to disk by WriteSelf() for this
- // object and any nested subobjects.
- //
- // Should be overwritten for objects that can have different
- // file and memory sizes.
- virtual intptr_t FileSize() const {
- if (!HasContents()) return 0;
- ASSERT(IsAllocated());
- return MemorySize();
- }
-
- // The size of this object and any subobjects combined in the output's memory
- // space. Note that objects may have a different MemorySize() than FileSize()
- // (e.g., a segment that contains zerofill sections).
- //
- // Should be overridden when the object contains nested objects.
- virtual intptr_t MemorySize() const { return SelfMemorySize(); }
-
-#define FOR_EACH_CONTENTS_LINEAR_FIELD(M) \
- M(file_offset) \
- M(memory_address)
-
- FOR_EACH_CONTENTS_LINEAR_FIELD(DEFINE_LINEAR_FIELD_METHODS);
-
- private:
- FOR_EACH_CONTENTS_LINEAR_FIELD(DEFINE_LINEAR_FIELD);
-
-#undef FOR_EACH_CONTENTS_LINEAR_FIELD
-
- DISALLOW_COPY_AND_ASSIGN(MachOContents);
-};
-
-// Each MachO command corresponds to two parts in the file contents:
-// the load command in the header that describes how to load the command
-// contents and the command contents somewhere after the header.
-//
-// The load command is written via WriteLoadCommand() while WriteSelf()
-// handles writing the command contents.
-//
-// Each concrete subclass of MachOCommand should define
-// static constexpr uint32_t kCommandCode = ...
-// with the appropriate mach_o::LC_* constant.
-class MachOCommand : public MachOContents {
- public:
- explicit MachOCommand(intptr_t cmd,
- bool needs_offset = true,
- bool in_segment = true)
- : MachOContents(needs_offset, in_segment), cmd_(cmd) {
- ASSERT(Utils::IsUint(32, cmd));
- }
-
- DEFINE_TYPE_CHECK_FOR(MachOCommand)
-
- // Load command fields and methods.
-
- // The value identifying the type of section the load command represents.
- // Should be one of the LC_* constants in platform/mach_o.h.
- uint32_t cmd() const { return cmd_; }
-
- // The alignment expected for load commands.
- static constexpr intptr_t kLoadCommandAlignment = compiler::target::kWordSize;
-
- // The size of the load command representing this command in the header.
- //
- // Note that all load commands must have a size that is a multiple of
- // kLoadCommandAlignment, so padding may be required.
- virtual uint32_t cmdsize() const = 0;
-
- // Each load command has a common prefix, which is written by the
- // class's WriteLoadCommand. Call the base class's implementation
- // prior to writing the rest of the load command for the subclass.
- virtual void WriteLoadCommand(MachOWriteStream* stream) const {
- stream->Write32(cmd());
- stream->Write32(cmdsize());
- }
-
- // Only the offset within the header is defined since the file offset
- // and memory address for the load command can be derived from the
- // header's file offset and memory address using this offset.
-#define FOR_EACH_COMMAND_LINEAR_FIELD(M) M(header_offset)
-
- FOR_EACH_COMMAND_LINEAR_FIELD(DEFINE_LINEAR_FIELD_METHODS);
-
- private:
- FOR_EACH_COMMAND_LINEAR_FIELD(DEFINE_LINEAR_FIELD);
-
-#undef FOR_EACH_COMMAND_LINEAR_FIELD
-
- private:
- uint32_t cmd_;
-
- DISALLOW_COPY_AND_ASSIGN(MachOCommand);
-};
-
-class MachOSection : public MachOContents {
-#if defined(TARGET_ARCH_IS_32_BIT)
- using SectionType = mach_o::section;
-#else
- using SectionType = mach_o::section_64;
-#endif
-
- public:
- MachOSection(Zone* zone,
- const char* name,
- intptr_t type = mach_o::S_REGULAR,
- intptr_t attributes = mach_o::S_NO_ATTRIBUTES,
- bool has_contents = true,
- intptr_t alignment = MachOWriter::kPageSize)
- : MachOContents(/*needs_offset=*/has_contents,
- /*in_segment=*/true),
- name_(name),
- flags_(mach_o::SectionFlags(type, attributes)),
- alignment_(alignment),
- portions_(zone, 0) {
- ASSERT(strlen(name) <= sizeof(SectionType::sectname));
- ASSERT(Utils::IsPowerOfTwo(alignment));
- ASSERT_EQUAL(type & mach_o::SECTION_TYPE, type);
- ASSERT_EQUAL(attributes & mach_o::SECTION_ATTRIBUTES, attributes);
- if (type == mach_o::S_ZEROFILL && type == mach_o::S_GB_ZEROFILL) {
- ASSERT(!has_contents);
- }
- }
-
- DEFINE_TYPE_CHECK_FOR(MachOSection)
-
- intptr_t Alignment() const override { return alignment_; }
-
- const char* name() const { return name_; }
-
- bool HasName(const char* name) const { return strcmp(name_, name) == 0; }
-
- struct Portion {
- void Write(MachOWriteStream* stream, intptr_t section_start) const {
- ASSERT(bytes != nullptr);
- if (relocations != nullptr) {
- const intptr_t address = section_start + offset;
- stream->WriteBytesWithRelocations(bytes, size, address, *relocations);
- } else {
- stream->WriteBytes(bytes, size);
- }
- }
-
- bool ContainsSymbols() const {
- return symbol_name != nullptr ||
- (symbols != nullptr && !symbols->is_empty());
- }
-
- intptr_t offset;
- const char* symbol_name;
- intptr_t label;
- const uint8_t* bytes;
- intptr_t size;
- const SharedObjectWriter::RelocationArray* relocations;
- const SharedObjectWriter::SymbolDataArray* symbols;
-
- private:
- DISALLOW_ALLOCATION();
- };
-
- const GrowableArray<Portion>& portions() const { return portions_; }
-
- void AddPortion(const uint8_t* bytes,
- intptr_t size,
- const SharedObjectWriter::RelocationArray* relocations,
- const SharedObjectWriter::SymbolDataArray* symbols = nullptr,
- const char* symbol_name = nullptr,
- intptr_t label = 0) {
- // Any named portion should also have a valid symbol label.
- ASSERT(symbol_name == nullptr || label > 0);
- ASSERT(!HasContents() || bytes != nullptr);
- ASSERT(bytes != nullptr || relocations == nullptr);
- // Make sure all portions are consistent in containing bytes.
- ASSERT(portions_.is_empty() ||
- (portions_[0].bytes != nullptr) == (bytes != nullptr));
- intptr_t offset = 0;
- if (!portions_.is_empty()) {
- const auto& last = portions_.Last();
- offset = last.offset + last.size;
- }
- // Each portion is aligned within the section.
- offset = Utils::RoundUp(offset, Alignment());
- portions_.Add(
- {offset, symbol_name, label, bytes, size, relocations, symbols});
- }
-
- intptr_t SelfMemorySize() const override {
- const auto& last = portions_.Last();
- return last.offset + last.size;
- }
-
- void WriteSelf(MachOWriteStream* stream) const override {
- if (!HasContents()) return;
- for (const auto& portion : portions_) {
- // Each portion is aligned within the section.
- stream->Align(Alignment());
- ASSERT_EQUAL(stream->Position(), file_offset() + portion.offset);
- portion.Write(stream, memory_address());
- }
- }
-
- const Portion* FindPortion(const char* symbol_name) const {
- for (const auto& portion : portions_) {
- if (strcmp(symbol_name, portion.symbol_name) == 0) {
- return &portion;
- }
- }
- return nullptr;
- }
-
- bool ContainsSymbols() const {
- for (const auto& p : portions_) {
- if (p.ContainsSymbols()) return true;
- }
- return false;
- }
-
- void Accept(Visitor* visitor) override { visitor->VisitMachOSection(this); }
-
- private:
- uint32_t HeaderInfoSize() const { return sizeof(SectionType); }
-
- // Called during MachOSegment::WriteLoadCommand.
- void WriteHeaderInfo(MachOWriteStream* stream, const char* segname) const {
- auto const start = stream->Position();
- stream->WriteFixedLengthCString(name_, sizeof(SectionType::sectname));
- stream->WriteFixedLengthCString(segname, sizeof(SectionType::segname));
- // While
- stream->WriteWord(memory_address());
- stream->WriteWord(MemorySize());
- stream->Write32(file_offset());
- stream->Write32(Utils::ShiftForPowerOfTwo(Alignment()));
- stream->WriteOffsetCount(0, 0); // No relocation entries.
- stream->Write32(flags_);
- // All reserved fields are 0 for our purposes.
- stream->Write32(0); // reserved1
- stream->Write32(0); // reserved2
-#if defined(TARGET_ARCH_IS_64_BIT)
- stream->Write32(0); // reserved3
-#endif
- ASSERT_EQUAL(stream->Position(), start + HeaderInfoSize());
- }
-
- const char* const name_;
- const decltype(SectionType::flags) flags_ = 0;
- const intptr_t alignment_;
- GrowableArray<Portion> portions_;
-
- friend class MachOSegment;
-
- DISALLOW_COPY_AND_ASSIGN(MachOSection);
-};
-
-class MachOSegment : public MachOCommand {
-#if defined(TARGET_ARCH_IS_32_BIT)
- using SegmentCommandType = mach_o::segment_command;
-#else
- using SegmentCommandType = mach_o::segment_command_64;
-#endif
-
- public:
-#if defined(TARGET_ARCH_IS_32_BIT)
- static constexpr uint32_t kCommandCode = mach_o::LC_SEGMENT;
-#else
- static constexpr uint32_t kCommandCode = mach_o::LC_SEGMENT_64;
-#endif
-
- MachOSegment(Zone* zone,
- const char* name,
- intptr_t initial_vm_protection = mach_o::VM_PROT_READ,
- intptr_t max_vm_protection = mach_o::VM_PROT_READ)
- // We don't know if a segment has a file offset until we
- // know what it contains, so set it to 0 in ComputeOffsets()
- // if there are no contents.
- : MachOCommand(kCommandCode),
- name_(name),
- initial_vm_protection_(initial_vm_protection),
- max_vm_protection_(max_vm_protection),
- contents_(zone, 0) {
- ASSERT(Utils::IsInt(32, initial_vm_protection));
- ASSERT(Utils::IsInt(32, max_vm_protection));
- ASSERT(strlen(name) <= sizeof(SegmentCommandType::segname));
- }
-
- DEFINE_TYPE_CHECK_FOR(MachOSegment)
-
- const char* name() const { return name_; }
- const GrowableArray<MachOContents*>& contents() const { return contents_; }
-
- bool IsReadable() const {
- return (initial_vm_protection_ & mach_o::VM_PROT_READ) != 0;
- }
- bool IsWritable() const {
- return (initial_vm_protection_ & mach_o::VM_PROT_WRITE) != 0;
- }
- bool IsExecutable() const {
- return (initial_vm_protection_ & mach_o::VM_PROT_EXECUTE) != 0;
- }
-
- intptr_t Alignment() const override { return MachOWriter::kPageSize; }
-
- // The text segment has a file and memory offset of 0, so the superclass's
- // implementations give false negatives after ComputeOffsets.
- bool HasContents() const override { return next_contents_index_ > 0; }
- bool IsAllocated() const override { return true; }
-
- uint32_t cmdsize() const override {
- uword size = sizeof(SegmentCommandType);
- // The header information for sections is nested within the
- // segment load command.
- for (auto* const c : contents_) {
- if (auto* const s = c->AsMachOSection()) {
- size += s->HeaderInfoSize();
- }
- }
- ASSERT(Utils::IsUint(32, size));
- return size;
- }
-
- bool PadFileSizeToAlignment() const {
- // The linkedit segment should _not_ be padded to alignment, because
- // that means the code signature isn't the last contents of the file
- // when applicable.
- return !HasName(mach_o::SEG_LINKEDIT);
- }
-
- // Segments do not contain any header information, just nested content.
- intptr_t SelfMemorySize() const override { return 0; }
-
- intptr_t FileSize() const override {
- intptr_t file_size = SelfFileSize();
- for (auto* const c : contents_) {
- if (!c->HasContents()) continue;
- file_size = Utils::RoundUp(file_size, c->Alignment());
- file_size += c->FileSize();
- }
- if (PadFileSizeToAlignment()) {
- file_size = Utils::RoundUp(file_size, Alignment());
- }
- return file_size;
- }
-
- intptr_t MemorySize() const override {
- intptr_t memory_size = SelfMemorySize();
- for (auto* const c : contents_) {
- ASSERT(c->IsAllocated()); // Segments never contain unallocated contents.
- memory_size = Utils::RoundUp(memory_size, c->Alignment());
- memory_size += c->MemorySize();
- }
- return Utils::RoundUp(memory_size, Alignment());
- }
-
- // The initial segment of the Mach-O file always includes the header
- // as its first contents.
- bool IsInitial() const { return header() != nullptr; }
-
- // Returns the header if this is the initial segment (which contains it),
- // otherwise nullptr.
- const MachOHeader* header() const {
- return contents_.is_empty() ? nullptr : contents_[0]->AsMachOHeader();
- }
-
- bool HasName(const char* name) const { return strcmp(name_, name) == 0; }
-
- bool ContainsSymbols() const {
- for (auto* const c : contents_) {
- if (auto* const s = c->AsMachOSection()) {
- if (s->ContainsSymbols()) {
- return true;
- }
- }
- }
- return false;
- }
-
- void AddContents(MachOContents* c);
-
- bool IsDebugOnly() const {
- // Currently, the dwarf segment is the only debug-only info we add.
- return HasName(mach_o::SEG_DWARF);
- }
-
- void WriteLoadCommand(MachOWriteStream* stream) const override {
- MachOCommand::WriteLoadCommand(stream);
- stream->WriteFixedLengthCString(name_, sizeof(SegmentCommandType::segname));
- stream->WriteWord(memory_address());
- stream->WriteWord(MemorySize());
- stream->WriteWord(file_offset());
- // Only report the actual file size if there is non-header content.
- if (IsInitial() && next_contents_index_ == 1) {
- stream->WriteWord(0);
- } else {
- stream->WriteWord(FileSize());
- }
- stream->Write32(max_vm_protection_);
- stream->Write32(initial_vm_protection_);
- stream->Write32(NumSections());
- // The writer never uses segment flags.
- stream->Write32(0);
- // The load command for a segment also contains descriptions for its
- // sections instead of these being in separate load commands.
- for (auto* const c : contents_) {
- if (!c->IsMachOSection()) continue;
- c->AsMachOSection()->WriteHeaderInfo(stream, name_);
- }
- }
-
- MachOSection* FindSection(const char* name) const {
- for (auto* const c : contents_) {
- if (auto* const s = c->AsMachOSection()) {
- if (s->HasName(name)) return s;
- }
- }
- return nullptr;
- }
-
- intptr_t NumSections() const {
- intptr_t count = 0;
- for (auto* const c : contents_) {
- if (c->IsMachOSection()) {
- count += 1;
- }
- }
- return count;
- }
-
- void Accept(Visitor* visitor) override { visitor->VisitMachOSegment(this); }
- void VisitChildren(Visitor* visitor) override {
- for (auto* const c : contents_) {
- c->Accept(visitor);
- }
- }
-
- private:
- const char* const name_;
- bool has_contents_ = false;
- intptr_t next_contents_index_ = 0;
- mach_o::vm_prot_t initial_vm_protection_;
- mach_o::vm_prot_t max_vm_protection_;
- GrowableArray<MachOContents*> contents_;
-
- DISALLOW_COPY_AND_ASSIGN(MachOSegment);
-};
-
-class MachOUuid : public MachOCommand {
- public:
- static constexpr uint32_t kCommandCode = mach_o::LC_UUID;
-
- explicit MachOUuid(const void* bytes, intptr_t len)
- : MachOCommand(kCommandCode,
- /*needs_offset=*/false,
- /*in_segment=*/false),
- bytes_() {
- // Make sure the length of the byte buffer matches the UUID length, so
- // that the provided UUID isn't unexpectedly truncated or extended.
- ASSERT_EQUAL(len, sizeof(bytes_));
- memmove(bytes_, bytes, sizeof(bytes_));
- }
-
- uint32_t cmdsize() const override { return sizeof(mach_o::uuid_command); }
-
- void WriteLoadCommand(MachOWriteStream* stream) const override {
- MachOCommand::WriteLoadCommand(stream);
- stream->WriteBytes(bytes_, sizeof(bytes_));
- }
-
- void Accept(Visitor* visitor) override { visitor->VisitMachOUuid(this); }
-
- private:
- uint8_t bytes_[sizeof(mach_o::uuid_command::uuid)];
- DISALLOW_COPY_AND_ASSIGN(MachOUuid);
-};
-
-#define MACHO_XYZ_VERSION_ENCODING(x, y, z) \
- static_cast<uint32_t>(((x) << 16) | ((y) << 8) | (z))
-
-class MachOBuildVersion : public MachOCommand {
- public:
- static constexpr uint32_t kCommandCode = mach_o::LC_BUILD_VERSION;
-
- MachOBuildVersion()
- : MachOCommand(kCommandCode,
- /*needs_offset=*/false,
- /*in_segment=*/false) {}
-
- uint32_t cmdsize() const override {
- return sizeof(mach_o::build_version_command);
- }
-
- uint32_t platform() const {
-#if defined(DART_TARGET_OS_MACOS_IOS)
- return mach_o::PLATFORM_IOS;
-#elif defined(DART_TARGET_OS_MACOS)
- return mach_o::PLATFORM_MACOS;
-#else
- return mach_o::PLATFORM_UNKNOWN;
-#endif
- }
-
- uint32_t minos() const {
-#if defined(DART_TARGET_OS_MACOS_IOS)
- // TODO(sstrickl): No minimum version for iOS currently defined.
- UNIMPLEMENTED();
-#elif defined(DART_TARGET_OS_MACOS)
- return kMinMacOSVersion;
-#else
- return 0; // No version for the unknown platform.
-#endif
- }
-
- uint32_t sdk() const {
-#if defined(DART_TARGET_OS_MACOS_IOS)
- // TODO(sstrickl): No SDK version for iOS currently defined.
- UNIMPLEMENTED();
-#elif defined(DART_TARGET_OS_MACOS)
- return kMacOSSdkVersion;
-#else
- return 0; // No version for the unknown platform.
-#endif
- }
-
- void WriteLoadCommand(MachOWriteStream* stream) const override {
- MachOCommand::WriteLoadCommand(stream);
- stream->Write32(platform());
- stream->Write32(minos());
- stream->Write32(sdk());
- stream->Write32(0); // No tool versions.
- }
-
- void Accept(Visitor* visitor) override {
- visitor->VisitMachOBuildVersion(this);
- }
-
- private:
- static constexpr auto kMinMacOSVersion = MACHO_XYZ_VERSION_ENCODING(15, 0, 0);
- static constexpr auto kMacOSSdkVersion = MACHO_XYZ_VERSION_ENCODING(15, 4, 0);
-
- DISALLOW_COPY_AND_ASSIGN(MachOBuildVersion);
-};
-
-class MachODylib : public MachOCommand {
- public:
- uint32_t cmdsize() const override {
- intptr_t size = NameOffset() + strlen(name_) + 1;
- return Utils::RoundUp(size, kLoadCommandAlignment);
- }
-
- void WriteLoadCommand(MachOWriteStream* stream) const override {
- MachOCommand::WriteLoadCommand(stream);
- stream->Write32(NameOffset());
- stream->Write32(timestamp_);
- stream->Write32(current_version_);
- stream->Write32(compatibility_version_);
- stream->WriteNullTerminatedCString(name_);
- stream->Align(kLoadCommandAlignment);
- }
-
- static constexpr auto kNoVersion = MACHO_XYZ_VERSION_ENCODING(0, 0, 0);
-
- protected:
- // This is really an abstract class, with concrete subclasses providing
- // the command code.
- MachODylib(intptr_t cmd,
- const char* name,
- intptr_t timestamp,
- intptr_t current_version = kNoVersion,
- intptr_t compatibility_version = kNoVersion)
- : MachOCommand(cmd,
- /*needs_offset=*/false,
- /*in_segment=*/false),
- name_(ASSERT_NOTNULL(name)),
- timestamp_(timestamp),
- current_version_(current_version),
- compatibility_version_(compatibility_version) {
- ASSERT(Utils::IsUint(32, timestamp));
- ASSERT(Utils::IsUint(32, current_version));
- ASSERT(Utils::IsUint(32, compatibility_version));
- }
-
- private:
- uint32_t NameOffset() const { return sizeof(mach_o::dylib_command); }
-
- const char* const name_;
- const uint32_t timestamp_;
- const uint32_t current_version_;
- const uint32_t compatibility_version_;
-
- DISALLOW_COPY_AND_ASSIGN(MachODylib);
-};
-
-class MachOIdDylib : public MachODylib {
- public:
- static constexpr uint32_t kCommandCode = mach_o::LC_ID_DYLIB;
-
- explicit MachOIdDylib(const char* name = kDefaultSnapshotName,
- intptr_t current_version = kNoVersion,
- intptr_t compatibility_version = kNoVersion)
- : MachODylib(kCommandCode,
- name,
- 0, // Snapshots aren't copied into user.
- current_version,
- compatibility_version) {}
-
- void Accept(Visitor* visitor) override { visitor->VisitMachOIdDylib(this); }
-
- private:
- static constexpr char kDefaultSnapshotName[] = "aot.snapshot";
- DISALLOW_COPY_AND_ASSIGN(MachOIdDylib);
-};
-
-class MachOLoadDylib : public MachODylib {
- public:
- static constexpr uint32_t kCommandCode = mach_o::LC_LOAD_DYLIB;
-
- static MachOLoadDylib* CreateLoadSystemDylib(Zone* zone) {
- return new (zone) MachOLoadDylib(kSystemDylibName, 0, kSystemCurrentVersion,
- kSystemCompatVersion);
- }
-
- void Accept(Visitor* visitor) override { visitor->VisitMachOLoadDylib(this); }
-
- private:
- MachOLoadDylib(const char* name,
- intptr_t timestamp,
- intptr_t current_version,
- intptr_t compatibility_version)
- : MachODylib(kCommandCode,
- name,
- timestamp,
- current_version,
- compatibility_version) {}
-
- static constexpr char kSystemDylibName[] = "/usr/lib/libSystem.B.dylib";
- static constexpr auto kSystemCurrentVersion =
- MACHO_XYZ_VERSION_ENCODING(1351, 0, 0);
- static constexpr auto kSystemCompatVersion =
- MACHO_XYZ_VERSION_ENCODING(1, 0, 0);
-
- DISALLOW_COPY_AND_ASSIGN(MachOLoadDylib);
-};
-
-#undef MACHO_XYZ_VERSION_ENCODING
-
-class MachOSymbolTable : public MachOCommand {
- public:
- static constexpr uint32_t kCommandCode = mach_o::LC_SYMTAB;
-
- explicit MachOSymbolTable(Zone* zone)
- : MachOCommand(kCommandCode),
- zone_(zone),
- strings_(zone),
- symbols_(zone, 0),
- by_label_index_(zone) {}
-
- class StringTable : public ValueObject {
- public:
- explicit StringTable(Zone* zone) : text_(zone), text_indices_(zone) {
- // Ensure the string containing a single space is always at index 0.
- const intptr_t index = Add(" ");
- ASSERT_EQUAL(index, 0);
- // Assign the empty string the index of the null byte in the
- // string added above.
- text_indices_.Insert({"", index + 1});
- }
-
- intptr_t Add(const char* str) {
- ASSERT(str != nullptr);
- if (auto const kv = text_indices_.Lookup(str)) {
- return kv->value;
- }
- intptr_t offset = text_.length();
- text_.AddString(str);
- text_.AddChar('\0');
- text_indices_.Insert({str, offset});
- return offset;
- }
-
- const char* At(intptr_t index) const {
- if (index >= text_.length()) return nullptr;
- return text_.buffer() + index;
- }
-
- intptr_t FileSize() const { return text_.length(); }
-
- void Write(MachOWriteStream* stream) const {
- stream->WriteBytes(text_.buffer(), text_.length());
- }
-
- private:
- ZoneTextBuffer text_;
- CStringIntMap text_indices_;
- DISALLOW_COPY_AND_ASSIGN(StringTable);
- };
-
- struct Symbol {
- Symbol(intptr_t n_idx,
- intptr_t n_type,
- intptr_t n_sect,
- intptr_t n_desc,
- uword n_value)
- : name_index(n_idx),
- type(n_type),
- section_index(n_sect),
- description(n_desc),
- value(n_value) {
- ASSERT(Utils::IsUint(32, n_idx));
- ASSERT(Utils::IsUint(8, n_type));
- ASSERT(Utils::IsUint(8, n_sect));
- ASSERT(Utils::IsUint(16, n_desc));
- ASSERT(Utils::IsUint(sizeof(compiler::target::uword) * kBitsPerByte,
- n_value));
- }
-
- void Write(MachOWriteStream* stream) const {
- const intptr_t start = stream->Position();
- stream->Write32(name_index);
- stream->WriteByte(type);
- stream->WriteByte(section_index);
- stream->Write16(description);
- stream->WriteWord(value);
- ASSERT_EQUAL(stream->Position() - start, sizeof(mach_o::nlist));
- }
-
- // The index of the name in the symbol table's string table.
- uint32_t name_index;
- // See the mach_o::N_* constants for the encoding of this field.
- uint8_t type;
- // The section to which this symbol belongs if not equal to mach_o::NO_SECT.
- // The sections are indexed by their appearance in the load commands
- // (e.g., the first section of the first segment command that contains
- // sections has index 1, and the first section of the second segment command
- // that contains sections has index [k + 1] if the first segment contains
- // [k] sections).
- uint8_t section_index;
- // See the mach_o::N_* constants for the encoding of this field.
- uint16_t description;
- // For symbols where section_index != macho_o::NO_SECT, this is the section
- // offset until finalization, when it is converted to the offset into the
- // snapshot.
- compiler::target::uword value;
-
- DISALLOW_ALLOCATION();
- };
-
- const StringTable& strings() const { return strings_; }
- const GrowableArray<Symbol>& symbols() const { return symbols_; }
- DEBUG_ONLY(intptr_t max_label() const { return max_label_; })
-
- void AddSymbol(const char* name,
- intptr_t type,
- intptr_t section_index,
- intptr_t description,
- uword value,
- intptr_t label = -1) {
- // Section symbols should always have labels, and other symbols
- // (including symbolic debugging symbols) do not.
- if ((type & mach_o::N_STAB) != 0) {
- ASSERT(label <= 0);
- } else {
- ASSERT_EQUAL((type & mach_o::N_TYPE) == mach_o::N_SECT, label > 0);
- }
- ASSERT(!file_offset_is_set()); // Can grow until offsets computed.
- auto const name_index = strings_.Add(name);
- ASSERT(*name == '\0' || name_index != 0);
- const intptr_t new_index = num_symbols();
- symbols_.Add({name_index, type, section_index, description, value});
- if (label > 0) {
- DEBUG_ONLY(max_label_ = max_label_ > label ? max_label_ : label);
- // Store an 1-based index since 0 is kNoValue for IntMap.
- by_label_index_.Insert(label, new_index + 1);
- }
- }
-
- const Symbol* FindLabel(intptr_t label) const {
- ASSERT(label > 0);
- // The stored index is 1-based.
- const intptr_t symbols_index = by_label_index_.Lookup(label) - 1;
- if (symbols_index < 0) return nullptr; // Not found.
- return &symbols_[symbols_index];
- }
-
- void Initialize(const GrowableArray<MachOSection*>& sections,
- bool is_stripped);
-
- void UpdateSectionIndices(const GrowableArray<intptr_t>& index_map) {
- const intptr_t map_size = index_map.length();
-#if defined(DEBUG)
- for (intptr_t i = 0; i < map_size; i++) {
- const intptr_t new_index = index_map[i];
- ASSERT(Utils::IsUint(8, new_index));
- ASSERT(new_index < map_size);
- if (i == mach_o::NO_SECT) {
- ASSERT_EQUAL(new_index, mach_o::NO_SECT);
- } else {
- ASSERT(new_index != mach_o::NO_SECT);
- }
- }
-#endif
- for (auto& symbol : symbols_) {
- const uint8_t old_index = symbol.section_index;
- ASSERT(old_index < map_size);
- symbol.section_index = index_map[old_index];
- }
- }
-
- void Finalize(const GrowableArray<uword>& address_map) {
- const intptr_t map_size = address_map.length();
-#if defined(DEBUG)
- for (intptr_t i = 0; i < map_size; i++) {
- if (i == mach_o::NO_SECT) {
- // The entry for NO_SECT must be 0 so that symbols with that index,
- // like global symbols, are unchanged.
- ASSERT_EQUAL(address_map[mach_o::NO_SECT], 0);
- } else {
- // No valid section begins at the start of the snapshot.
- ASSERT(address_map[i] > 0);
- }
- }
-#endif
- for (auto& symbol : symbols_) {
- ASSERT(symbol.section_index < map_size);
- symbol.value += address_map[symbol.section_index];
- }
- }
-
- uint32_t cmdsize() const override { return sizeof(mach_o::symtab_command); }
-
- intptr_t SelfMemorySize() const override {
- return SymbolsSize() + strings_.FileSize();
- }
-
- intptr_t Alignment() const override { return compiler::target::kWordSize; }
-
- void WriteLoadCommand(MachOWriteStream* stream) const override {
- MachOCommand::WriteLoadCommand(stream);
- stream->WriteOffsetCount(file_offset(), num_symbols());
- stream->WriteOffsetCount(file_offset() + SymbolsSize(),
- strings_.FileSize());
- }
-
- void WriteSelf(MachOWriteStream* stream) const override {
- for (const auto& symbol : symbols_) {
- symbol.Write(stream);
- }
- strings_.Write(stream);
- }
-
- intptr_t num_symbols() const { return symbols_.length(); }
-
- void Accept(Visitor* visitor) override {
- visitor->VisitMachOSymbolTable(this);
- }
-
-#define FOR_EACH_SYMBOL_TABLE_LINEAR_FIELD(M) \
- M(num_local_symbols) \
- M(num_external_symbols)
-
- FOR_EACH_SYMBOL_TABLE_LINEAR_FIELD(DEFINE_LINEAR_FIELD_METHODS);
-
- private:
- intptr_t SymbolsSize() const { return num_symbols() * sizeof(mach_o::nlist); }
-
- Zone* const zone_;
- StringTable strings_;
- GrowableArray<Symbol> symbols_;
- // Maps symbol labels (positive integers) to indexes in symbols_.
- IntMap<intptr_t> by_label_index_;
- DEBUG_ONLY(intptr_t max_label_ = 0;) // For consistency checks.
-
- FOR_EACH_SYMBOL_TABLE_LINEAR_FIELD(DEFINE_LINEAR_FIELD);
-#undef FOR_EACH_SYMBOL_TABLE_LINEAR_FIELD
-
- DISALLOW_COPY_AND_ASSIGN(MachOSymbolTable);
-};
-
-class MachODynamicSymbolTable : public MachOCommand {
- public:
- static constexpr uint32_t kCommandCode = mach_o::LC_DYSYMTAB;
-
- explicit MachODynamicSymbolTable(const MachOSymbolTable& table)
- : MachOCommand(kCommandCode), table_(table) {}
-
- uint32_t cmdsize() const override { return sizeof(mach_o::dysymtab_command); }
-
- intptr_t Alignment() const override { return compiler::target::kWordSize; }
-
- void WriteLoadCommand(MachOWriteStream* stream) const override {
- MachOCommand::WriteLoadCommand(stream);
- // The symbol table contains local symbols and then external symbols.
- intptr_t index = 0;
- stream->WriteOffsetCount(index, table_.num_local_symbols());
- index += table_.num_local_symbols();
- stream->WriteOffsetCount(index, table_.num_external_symbols());
- index += table_.num_external_symbols();
- // No undefined symbols.
- stream->WriteOffsetCount(index, 0);
- // The rest of the fields are 0-filled.
- for (intptr_t i = 0; i < kUnusedOffsetCountPairs; ++i) {
- stream->WriteOffsetCount(0, 0);
- }
- }
-
- // Currently no contents are written to the linkedit segment, as the
- // only non-zero fields are indexes/counts into the symbol table.
- intptr_t SelfMemorySize() const override { return 0; }
-
- void Accept(Visitor* visitor) override {
- visitor->VisitMachODynamicSymbolTable(this);
- }
-
- private:
- static constexpr intptr_t kUnusedOffsetCountPairs = 6;
-
- const MachOSymbolTable& table_;
- DISALLOW_COPY_AND_ASSIGN(MachODynamicSymbolTable);
-};
-
-class MachOLinkEditData : public MachOCommand {
- public:
- uint32_t cmdsize() const override {
- return sizeof(mach_o::linkedit_data_command);
- }
-
- void WriteLoadCommand(MachOWriteStream* stream) const override {
- MachOCommand::WriteLoadCommand(stream);
- stream->WriteOffsetCount(file_offset(), FileSize());
- }
-
- protected:
- // This is really an abstract class, with concrete subclasses providing
- // the command code.
- explicit MachOLinkEditData(intptr_t cmd)
- : MachOCommand(cmd, /*needs_offset=*/true, /*in_segment=*/true) {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MachOLinkEditData);
-};
-
-class MachOCodeSignature : public MachOLinkEditData {
- public:
- static constexpr uint32_t kCommandCode = mach_o::LC_CODE_SIGNATURE;
-
- explicit MachOCodeSignature(const char* identifier)
- : MachOLinkEditData(kCommandCode), identifier_(identifier) {}
-
- static constexpr intptr_t kHeaderAlignment = 8;
- static constexpr intptr_t kHashAlignment = 16;
-
- intptr_t Alignment() const override { return kHashAlignment; }
-
- intptr_t SelfMemorySize() const override {
- return DirectoryOffset() + DirectoryLength();
- }
-
- void WriteSelf(MachOWriteStream* stream) const override {
- // The code signature marks the end of the hashed content, as
- // it contains the hashes that ensure the previous content has
- // not been modified (modulo hash collisions).
- stream->FinalizeHashedContent();
- ASSERT_EQUAL(stream->num_hashes(), ExpectedNumHashes());
- const intptr_t start = stream->Position();
- // The superblob header, which includes a single blob index.
- stream->WriteBE32(mach_o::CSMAGIC_EMBEDDED_SIGNATURE); // magic
- stream->WriteBE32(FileSize()); // length
- stream->WriteBE32(1); // count
- // Blob index for the code directory.
- stream->WriteBE32(mach_o::CSSLOT_CODEDIRECTORY); // type
- stream->WriteBE32(DirectoryOffset()); // offset
- stream->Align(kHeaderAlignment);
- // Now the header for the code directory.
- ASSERT_EQUAL(stream->Position() - start, DirectoryOffset());
- const intptr_t directory_start = stream->Position();
- stream->WriteBE32(mach_o::CSMAGIC_CODEDIRECTORY); // magic
- stream->WriteBE32(DirectoryLength()); // length
- stream->WriteBE32(mach_o::CS_SUPPORTSEXECSEG); // version
- stream->WriteBE32(mach_o::CS_ADHOC | mach_o::CS_LINKER_SIGNED); // flags
- stream->WriteBE32(HashOffset());
- stream->WriteBE32(IdentOffset());
- stream->WriteBE32(0); // num special slots (hashes)
- stream->WriteBE32(stream->num_hashes()); // num code slots (hashes)
- stream->WriteBE32(file_offset()); // code limit
- stream->WriteByte(MachOWriteStream::kHashSize);
- stream->WriteByte(MachOWriteStream::kHashType);
- stream->WriteByte(0); // platform
- // The page size is represented by its base 2 logarithm.
- stream->WriteByte(Utils::ShiftForPowerOfTwo(MachOWriteStream::kChunkSize));
- stream->WriteBE32(0); // spare2 (always 0)
- // version >= 0x20100 (CS_SUPPORTSSCATTER)
- stream->WriteBE32(0); // scatter offset
- // version >= 0x20200 (CS_SUPPORTSTEAMID)
- stream->WriteBE32(0); // teamid offset
- // version >= 0x20300 (CS_SUPPORTSCODELIMIT64)
- stream->WriteBE32(0); // spare3 (always 0)
- stream->WriteBE64(0); // code limit (64-bit)
- // version >= 0x20400 (CS_SUPPORTSEXECSEG)
- stream->WriteBE64(stream->TextSegment().file_offset()); // offset
- stream->WriteBE64(stream->TextSegment().FileSize()); // limit
- stream->WriteBE64(0); // flags
- stream->Align(kHeaderAlignment);
- ASSERT_EQUAL(stream->Position() - directory_start, IdentOffset());
- stream->WriteFixedLengthCString(identifier_, strlen(identifier_) + 1);
- stream->Align(kHashAlignment);
- ASSERT_EQUAL(stream->Position() - directory_start, HashOffset());
- stream->WriteHashes();
- ASSERT_EQUAL(stream->Position() - directory_start, DirectoryLength());
- }
-
- void Accept(Visitor* visitor) override {
- visitor->VisitMachOCodeSignature(this);
- }
-
- private:
- // The offset of the code directory in the code signature.
- intptr_t DirectoryOffset() const {
- // A single blob index for the code directory.
- const intptr_t offset =
- sizeof(mach_o::cs_superblob) + sizeof(mach_o::cs_blob_index);
- return Utils::RoundUp(offset, kHeaderAlignment);
- }
-
- intptr_t DirectoryLength() const {
- return HashOffset() + ExpectedNumHashes() * MachOWriteStream::kHashSize;
- }
-
- // The offset of the identifier within the code directory.
- intptr_t IdentOffset() const {
- // Include the directory offset to ensure proper alignment, but the
- // returned value is relative to the code directory start.
- intptr_t signature_offset =
- DirectoryOffset() + sizeof(mach_o::cs_code_directory);
- return Utils::RoundUp(signature_offset, kHeaderAlignment) -
- DirectoryOffset();
- }
-
- // The offset of the list of hashes within the code directory.
- intptr_t HashOffset() const {
- // Include the directory offset to ensure proper alignment, but the
- // returned value is relative to the code directory start.
- const intptr_t signature_offset =
- DirectoryOffset() + IdentOffset() + strlen(identifier_) + 1;
- return Utils::RoundUp(signature_offset, kHashAlignment) - DirectoryOffset();
- }
-
- intptr_t ExpectedNumHashes() const {
- // The actual hashes are stored in the stream, which isn't available yet.
- // However, if the file offsets of the code signature has been computed, the
- // number of hashes that should be contained in the stream can be computed.
- const intptr_t chunk_size = MachOWriteStream::kChunkSize;
- return (file_offset() + chunk_size - 1) / chunk_size;
- }
-
- const char* const identifier_;
-
- DISALLOW_COPY_AND_ASSIGN(MachOCodeSignature);
-};
-
-// A representation of the header of the Mach-O file. This contains
-// any commands that have load commands within the header.
-class MachOHeader : public MachOContents {
-#if defined(TARGET_ARCH_IS_32_BIT)
- using HeaderType = mach_o::mach_header;
-#else
- using HeaderType = mach_o::mach_header_64;
-#endif
-
- using SnapshotType = SharedObjectWriter::Type;
-
- public:
- MachOHeader(Zone* zone,
- SnapshotType type,
- bool is_stripped,
- const char* identifier,
- Dwarf* dwarf)
- : MachOContents(),
- zone_(zone),
- type_(type),
- is_stripped_(is_stripped),
- identifier_(identifier != nullptr ? identifier : ""),
- dwarf_(dwarf),
- commands_(zone, 0),
- full_symtab_(zone) {
-#if defined(DART_TARGET_OS_MACOS)
- // A non-nullptr identifier must be provided for MacOS targets.
- ASSERT(identifier != nullptr);
-#endif
- // Unstripped content must have DWARF information available.
- ASSERT(dwarf != nullptr || is_stripped_);
- // Only snapshots should be stripped.
- ASSERT(!is_stripped_ || type == SnapshotType::Snapshot);
- }
-
- DEFINE_TYPE_CHECK_FOR(MachOHeader)
-
- Zone* zone() const { return zone_; }
- const GrowableArray<MachOCommand*>& commands() const { return commands_; }
- const MachOSymbolTable& relocation_symbol_table() const {
- return full_symtab_;
- }
- const MachOSegment& text_segment() const {
- ASSERT(text_segment_ != nullptr);
- return *text_segment_;
- }
-
- intptr_t NumSections() const {
- intptr_t num_sections = 0;
- for (auto* const command : commands()) {
- if (auto* const s = command->AsMachOSegment()) {
- num_sections += s->NumSections();
- }
- }
- return num_sections;
- }
-
- // The contents of the header is always at offset/address 0, so the
- // superclass's check returns a false negative here after ComputeOffsets.
- bool HasContents() const override { return true; }
- bool IsAllocated() const override { return true; }
- intptr_t Alignment() const override { return compiler::target::kWordSize; }
-
- // The header uses the default MemorySize() implementation, because
- // VisitChildren() doesn't visit the load commands and so the header is
- // not considered to contain nested content.
- //
- // This should be used if the size of the header without the load commands
- // is desired.
- intptr_t SizeWithoutLoadCommands() const {
- const intptr_t size = sizeof(HeaderType);
- ASSERT(Utils::IsAligned(size, MachOCommand::kLoadCommandAlignment));
- return size;
- }
-
- intptr_t SelfMemorySize() const override {
- intptr_t size = SizeWithoutLoadCommands();
- for (auto* const command : commands_) {
- size += command->cmdsize();
- }
- return size;
- }
-
- uint32_t filetype() const {
- if (type_ == SnapshotType::Snapshot) {
- return mach_o::MH_DYLIB;
- }
- ASSERT(type_ == SnapshotType::DebugInfo);
- return mach_o::MH_DSYM;
- }
-
- uint32_t flags() const {
- if (type_ == SnapshotType::Snapshot) {
- return mach_o::MH_NOUNDEFS | mach_o::MH_DYLDLINK |
- mach_o::MH_NO_REEXPORTED_DYLIBS;
- }
- ASSERT(type_ == SnapshotType::DebugInfo);
- return 0;
- }
-
- mach_o::cpu_type_t cpu_type() const {
-#if defined(TARGET_ARCH_X64)
- return mach_o::CPU_TYPE_X86_64;
-#elif defined(TARGET_ARCH_ARM64)
- return mach_o::CPU_TYPE_ARM64;
-#elif defined(TARGET_ARCH_IA32)
- return mach_o::CPU_TYPE_I386;
-#elif defined(TARGET_ARCH_ARM)
- return mach_o::CPU_TYPE_ARM;
-#else
- // No constant currently for this architecture.
- UNIMPLEMENTED();
-#endif
- }
-
- mach_o::cpu_subtype_t cpu_subtype() const {
-#if defined(TARGET_ARCH_X64)
- return mach_o::CPU_SUBTYPE_X86_64_ALL;
-#elif defined(TARGET_ARCH_ARM64)
- return mach_o::CPU_SUBTYPE_ARM64_ALL;
-#elif defined(TARGET_ARCH_IA32)
- return mach_o::CPU_SUBTYPE_I386_ALL;
-#elif defined(TARGET_ARCH_ARM)
- return mach_o::CPU_SUBTYPE_ARM_ALL;
-#else
- // No constant currently for this architecture.
- UNIMPLEMENTED();
-#endif
- }
-
- void WriteSelf(MachOWriteStream* stream) const override {
- intptr_t start = stream->Position();
- ASSERT_EQUAL(start, 0);
-#if defined(TARGET_ARCH_IS_32_BIT)
- stream->Write32(mach_o::MH_MAGIC);
-#else
- stream->Write32(mach_o::MH_MAGIC_64);
-#endif
- stream->Write32(cpu_type());
- stream->Write32(cpu_subtype());
- stream->Write32(filetype());
- stream->Write32(commands_.length());
- uint32_t sizeofcmds = 0;
- for (auto* const command : commands_) {
- sizeofcmds += command->cmdsize();
- }
- stream->Write32(sizeofcmds);
- stream->Write32(flags());
-#if !defined(TARGET_ARCH_IS_32_BIT)
- stream->Write32(0); // Reserved field.
-#endif
- ASSERT_EQUAL(stream->Position() - start, sizeof(HeaderType));
- for (auto* const command : commands_) {
- const intptr_t load_start = stream->Position();
- ASSERT_EQUAL(load_start, start + command->header_offset());
- command->WriteLoadCommand(stream);
- ASSERT_EQUAL(stream->Position() - load_start, command->cmdsize());
- }
- }
-
- // Returns the command with the given concrete subclass of MachOCommand
- // (that is, a subclass that defines a kCommandCode constant). Should only
- // be used for commands that appear at most once (e.g., not segments).
- template <typename T>
- T* FindCommand() const {
- return reinterpret_cast<T*>(FindCommand(T::kCommandCode));
- }
-
- // Returns the command with the given command code. Should only be used
- // for commands that appear at most once (e.g., not segments).
- MachOCommand* FindCommand(uint32_t cmd) const {
- MachOCommand* result = nullptr;
- for (auto* const command : commands_) {
- if (command->cmd() == cmd) {
- ASSERT(result == nullptr);
- result = command;
-#if !defined(DEBUG)
- break; // No checking, so don't continue iterating.
-#endif
- }
- }
- return result;
- }
-
- // Returns whether there is a command has the given command code.
- bool HasCommand(uint32_t cmd) const {
- for (auto* const command : commands_) {
- if (command->cmd() == cmd) return true;
- }
- return false;
- }
-
- // Returns the segment with name [name] or nullptr if there is none.
- MachOSegment* FindSegment(const char* name) const {
- for (auto* const command : commands_) {
- if (auto* const s = command->AsMachOSegment()) {
- if (s->HasName(name)) return s;
- }
- }
- return nullptr;
- }
-
- // Returns the section with name [sectname] in segment [segname]
- // or nullptr if there is none.
- MachOSection* FindSection(const char* segname, const char* sectname) const {
- auto* const s = FindSegment(segname);
- if (s == nullptr) return nullptr;
- return s->FindSection(sectname);
- }
-
- MachOSegment* EnsureTextSegment() {
- if (text_segment_ == nullptr) {
- // Make sure it didn't get added outside this method.
- ASSERT(FindSegment(mach_o::SEG_TEXT) == nullptr);
- auto const vm_protection = mach_o::VM_PROT_READ | mach_o::VM_PROT_EXECUTE;
- text_segment_ = new (zone())
- MachOSegment(zone(), mach_o::SEG_TEXT, vm_protection, vm_protection);
- commands_.Add(text_segment_);
- }
- return text_segment_;
- }
-
- void Finalize();
-
- void Accept(Visitor* visitor) override { visitor->VisitMachOHeader(this); }
-
- // Since the header is in the initial segment, visiting the load commands
- // here and also visiting the header in MachOSegment::VisitChildren() would
- // cause a cycle if, say, Default() is overridden to be recursive.
- // Thus, the default VisitChildren implementation here does no recursion,
- void VisitChildren(Visitor* visitor) override {}
- void VisitSegments(Visitor* visitor) {
- for (auto* const c : commands_) {
- if (!c->IsMachOSegment()) continue;
- c->Accept(visitor);
- }
- }
-
- private:
- void GenerateUuid();
- void CreateBSS();
- void GenerateMiscellaneousCommands();
- void InitializeSymbolTables();
- void FinalizeDwarfSections();
- void FinalizeCommands();
- void ComputeOffsets();
-
- // Returns the symbol table that is included in the output, which
- // may or may not be the full symbol table.
- //
- // Returns nullptr if called before symbol table initialization.
- MachOSymbolTable* IncludedSymbolTable() {
- // True when the symbol tables haven't been initialized.
- if (full_symtab_.symbols().is_empty()) return nullptr;
- // The full symbol table is reused for unstripped contents.
- if (!is_stripped_) return &full_symtab_;
- return FindCommand<MachOSymbolTable>();
- }
-
- Zone* const zone_;
- const SnapshotType type_;
- // Used to determine whether to include non-global symbols in the
- // symbol table written to disk.
- bool const is_stripped_;
- // The identifier, used in the LC_ID_DYLIB command and the code signature.
- const char* const identifier_;
- Dwarf* const dwarf_;
- GrowableArray<MachOCommand*> commands_;
- // Contains all symbols for relocation calculations.
- MachOSymbolTable full_symtab_;
- MachOSegment* text_segment_ = nullptr;
-
- DISALLOW_COPY_AND_ASSIGN(MachOHeader);
-};
-
-void MachOSegment::AddContents(MachOContents* c) {
- ASSERT(c != nullptr);
- // Segment contents are always allocated.
- ASSERT(c->IsAllocated());
- // The order of segment contents is as follows:
- // 1) The header (if this is the initial segment).
- // 2) Content-containing sections and commands (in the linkedit segment).
- // 3) Sections without contents like zerofill sections.
- if (c->IsMachOHeader()) {
- ASSERT(c->AsMachOHeader()->commands()[0] == this);
- contents_.InsertAt(0, c);
- next_contents_index_ += 1;
- } else if (c->HasContents()) {
- ASSERT_EQUAL(c->IsMachOCommand(), HasName(mach_o::SEG_LINKEDIT));
- contents_.InsertAt(next_contents_index_, c);
- next_contents_index_ += 1;
- } else {
- ASSERT(c->IsMachOSection());
- contents_.Add(c);
- }
-}
-
-bool MachOWriteStream::HasValueForLabel(intptr_t label, intptr_t* value) const {
- ASSERT(value != nullptr);
- const auto& header = macho_.header();
- if (label == SharedObjectWriter::kBuildIdLabel) {
- // Unlike ELF, the uuid is not in a MachO section and so can't have a symbol
- // assigned. Instead, we look up its load command offset in the header.
- auto* const uuid = header.FindCommand<MachOUuid>();
- if (uuid == nullptr) return false;
- *value = header.file_offset() + uuid->header_offset();
- return true;
- }
- const auto& symtab = header.relocation_symbol_table();
- auto* const symbol = symtab.FindLabel(label);
- if (symbol == nullptr) return false;
- *value = symbol->value;
- return true;
-}
-
-const MachOSegment& MachOWriteStream::TextSegment() const {
- return macho_.header().text_segment();
-}
-
-MachOWriter::MachOWriter(Zone* zone,
- BaseWriteStream* stream,
- Type type,
- const char* id,
- Dwarf* dwarf)
- : SharedObjectWriter(zone, stream, type, dwarf),
- header_(*new (zone)
- MachOHeader(zone, type, IsStripped(dwarf), id, dwarf)) {}
-
-void MachOWriter::AddText(const char* name,
- intptr_t label,
- const uint8_t* bytes,
- intptr_t size,
- const ZoneGrowableArray<Relocation>* relocations,
- const ZoneGrowableArray<SymbolData>* symbols) {
- auto* const text_segment = header_.EnsureTextSegment();
- auto* text_section = text_segment->FindSection(mach_o::SECT_TEXT);
- if (text_section == nullptr) {
- const bool has_contents = type_ == Type::Snapshot;
- const intptr_t attributes =
- mach_o::S_ATTR_PURE_INSTRUCTIONS | mach_o::S_ATTR_SOME_INSTRUCTIONS;
- text_section = new (zone()) MachOSection(
- zone(), mach_o::SECT_TEXT, mach_o::S_REGULAR, attributes, has_contents);
- text_segment->AddContents(text_section);
- }
- text_section->AddPortion(bytes, size, relocations, symbols, name, label);
-}
-
-void MachOWriter::AddROData(const char* name,
- intptr_t label,
- const uint8_t* bytes,
- intptr_t size,
- const ZoneGrowableArray<Relocation>* relocations,
- const ZoneGrowableArray<SymbolData>* symbols) {
- // Const data goes in the text segment, not the data one.
- auto* const text_segment = header_.EnsureTextSegment();
- auto* const_section = text_segment->FindSection(mach_o::SECT_CONST);
- if (const_section == nullptr) {
- const bool has_contents = type_ == Type::Snapshot;
- const_section =
- new (zone()) MachOSection(zone(), mach_o::SECT_CONST, mach_o::S_REGULAR,
- mach_o::S_NO_ATTRIBUTES, has_contents);
- text_segment->AddContents(const_section);
- }
- const_section->AddPortion(bytes, size, relocations, symbols, name, label);
-}
-
-class WriteVisitor : public MachOContents::Visitor {
- public:
- explicit WriteVisitor(MachOWriteStream* stream) : stream_(stream) {}
-
- void Default(MachOContents* contents) override {
- if (!contents->HasContents()) return;
- stream_->Align(contents->Alignment());
- const intptr_t start = stream_->Position();
- ASSERT_EQUAL(start, contents->file_offset());
- contents->WriteSelf(stream_);
- ASSERT_EQUAL(stream_->Position() - start, contents->SelfFileSize());
- contents->VisitChildren(this);
- // Segments include post-nested content alignment.
- if (auto* const s = contents->AsMachOSegment()) {
- if (s->PadFileSizeToAlignment()) {
- stream_->Align(contents->Alignment());
- }
- }
- ASSERT_EQUAL(stream_->Position() - start, contents->FileSize());
- }
-
- private:
- MachOWriteStream* stream_;
- DISALLOW_COPY_AND_ASSIGN(WriteVisitor);
-};
-
-void MachOWriter::Finalize() {
- header_.Finalize();
- if (header_.HasCommand(MachOCodeSignature::kCommandCode)) {
- HashingMachOWriteStream wrapped(zone_, unwrapped_stream_, *this);
- WriteVisitor visitor(&wrapped);
- header_.VisitSegments(&visitor);
- } else {
- NonHashingMachOWriteStream wrapped(unwrapped_stream_, *this);
- WriteVisitor visitor(&wrapped);
- header_.VisitSegments(&visitor);
- }
-}
-
-void MachOHeader::Finalize() {
- // Generate the UUID now that we have all user-provided sections.
- GenerateUuid();
-
- // We add a BSS section for all Mach-O output with text sections, even in
- // the separate debugging information, to ensure that relocated addresses
- // are consistent between snapshots and the corresponding separate
- // debugging information.
- CreateBSS();
-
- FinalizeDwarfSections();
-
- // Create and initialize the dynamic and static symbol tables.
- InitializeSymbolTables();
-
- // Generate miscellenous load commands needed for the final output.
- GenerateMiscellaneousCommands();
-
- // Reorders the added commands as well as adding segments and commands
- // that must appear at the end of the file.
- FinalizeCommands();
-
- // Calculate file and memory offsets, and finalizes symbol values in any
- // symbol tables.
- ComputeOffsets();
-}
-
-void MachOWriter::AssertConsistency(const MachOWriter* snapshot,
- const MachOWriter* debug_info) {
-#if defined(DEBUG)
- // For now, just check that the symbol information for both match
- // in that all labelled symbols used for relocation have the same
- // value.
- const auto& snapshot_symtab = snapshot->header().relocation_symbol_table();
- const auto& debug_info_symtab =
- debug_info->header().relocation_symbol_table();
-
- intptr_t max_label = snapshot_symtab.max_label();
- ASSERT_EQUAL(max_label, debug_info_symtab.max_label());
- for (intptr_t i = 1; i < max_label; ++i) {
- if (auto* const snapshot_symbol = snapshot_symtab.FindLabel(i)) {
- auto* const debug_info_symbol = debug_info_symtab.FindLabel(i);
- ASSERT(debug_info_symbol != nullptr);
- if (snapshot_symbol->value != debug_info_symbol->value) {
- FATAL("Snapshot: %s -> %" Px64 ", %s -> %" Px64 "",
- snapshot_symtab.strings().At(snapshot_symbol->name_index),
- static_cast<uint64_t>(snapshot_symbol->value),
- debug_info_symtab.strings().At(debug_info_symbol->name_index),
- static_cast<uint64_t>(debug_info_symbol->value));
- }
- } else {
- ASSERT(debug_info_symtab.FindLabel(i) == nullptr);
- }
- }
-#endif
-}
-
-static uint32_t HashPortion(const MachOSection::Portion& portion) {
- if (portion.bytes == nullptr) return 0;
- const uint32_t hash = Utils::StringHash(portion.bytes, portion.size);
- // Ensure a non-zero return.
- return hash == 0 ? 1 : hash;
-}
-
-// For the UUID, we generate a 128-bit hash, where each 32 bits is a
-// hash of the contents of the following segments in order:
-//
-// .text(VM) | .text(Isolate) | .rodata(VM) | .rodata(Isolate)
-//
-// Any component of the build ID which does not have an associated section
-// in the output is kept as 0.
-void MachOHeader::GenerateUuid() {
- // Not idempotent.
- ASSERT(!HasCommand(MachOUuid::kCommandCode));
- // Currently, we construct the UUID out of data from two different
- // sections in the text segment: the text section and the const section.
- auto* const text_segment = FindSegment(mach_o::SEG_TEXT);
- if (text_segment == nullptr) return;
-
- auto* const text_section = text_segment->FindSection(mach_o::SECT_TEXT);
- // If there is no text section, then a UUID is not needed, as it is only
- // used to symbolicize non-symbolic stack traces.
- if (text_section == nullptr) return;
-
- auto* const vm_instructions =
- text_section->FindPortion(kVmSnapshotInstructionsAsmSymbol);
- auto* const isolate_instructions =
- text_section->FindPortion(kIsolateSnapshotInstructionsAsmSymbol);
- // All MachO snapshots have at least one of the two instruction sections.
- ASSERT(vm_instructions != nullptr || isolate_instructions != nullptr);
-
- auto* const data_section = text_segment->FindSection(mach_o::SECT_CONST);
- auto* const vm_data =
- data_section == nullptr
- ? nullptr
- : data_section->FindPortion(kVmSnapshotDataAsmSymbol);
- auto* const isolate_data =
- data_section == nullptr
- ? nullptr
- : data_section->FindPortion(kIsolateSnapshotDataAsmSymbol);
-
- uint32_t hashes[4];
- hashes[0] = vm_instructions == nullptr ? 0 : HashPortion(*vm_instructions);
- hashes[1] =
- isolate_instructions == nullptr ? 0 : HashPortion(*isolate_instructions);
- hashes[2] = vm_data == nullptr ? 0 : HashPortion(*vm_data);
- hashes[3] = isolate_data == nullptr ? 0 : HashPortion(*isolate_data);
-
- auto* const uuid_command = new (zone()) MachOUuid(hashes, sizeof(hashes));
- commands_.Add(uuid_command);
-}
-
-void MachOHeader::CreateBSS() {
- // No text section means no BSS section.
- auto* const text_section = FindSection(mach_o::SEG_TEXT, mach_o::SECT_TEXT);
- ASSERT(text_section != nullptr);
-
- // Not idempotent. Currently the data segment only contains BSS data, so it
- // shouldn't already exist.
- ASSERT(FindSegment(mach_o::SEG_DATA) == nullptr);
- auto const vm_protection = mach_o::VM_PROT_READ | mach_o::VM_PROT_WRITE;
- auto* const data_segment = new (zone())
- MachOSegment(zone(), mach_o::SEG_DATA, vm_protection, vm_protection);
- commands_.Add(data_segment);
-
- auto* const bss_section =
- new (zone()) MachOSection(zone(), mach_o::SECT_BSS, mach_o::S_ZEROFILL,
- mach_o::S_NO_ATTRIBUTES, /*has_contents=*/false,
- /*alignment=*/compiler::target::kWordSize);
- data_segment->AddContents(bss_section);
-
- for (const auto& portion : text_section->portions()) {
- size_t size;
- const char* symbol_name;
- intptr_t label;
- // First determine whether this is the VM's text portion or the isolate's.
- if (strcmp(portion.symbol_name, kVmSnapshotInstructionsAsmSymbol) == 0) {
- size = BSS::kVmEntryCount * compiler::target::kWordSize;
- symbol_name = kVmSnapshotBssAsmSymbol;
- label = SharedObjectWriter::kVmBssLabel;
- } else if (strcmp(portion.symbol_name,
- kIsolateSnapshotInstructionsAsmSymbol) == 0) {
- size = BSS::kIsolateGroupEntryCount * compiler::target::kWordSize;
- symbol_name = kIsolateSnapshotBssAsmSymbol;
- label = SharedObjectWriter::kIsolateBssLabel;
- } else {
- // Not VM or isolate text.
- UNREACHABLE();
- }
-
- // For the BSS section, we add the section symbols as local symbols in the
- // static symbol table, as these addresses are only used for relocation.
- // (This matches the behavior in the assembly output.)
- auto* symbols = new (zone_) SharedObjectWriter::SymbolDataArray(zone_, 1);
- symbols->Add({symbol_name, SharedObjectWriter::SymbolData::Type::Section, 0,
- size, label});
- bss_section->AddPortion(/*bytes=*/nullptr, size, /*relocations=*/nullptr,
- symbols);
- }
-}
-
-void MachOHeader::GenerateMiscellaneousCommands() {
- // Not idempotent;
- ASSERT(!HasCommand(MachOBuildVersion::kCommandCode));
- ASSERT(!HasCommand(MachOIdDylib::kCommandCode));
- ASSERT(!HasCommand(MachOLoadDylib::kCommandCode));
-
- commands_.Add(new (zone_) MachOBuildVersion());
- if (type_ == SnapshotType::Snapshot) {
- commands_.Add(new (zone_) MachOIdDylib(identifier_));
-#if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
- commands_.Add(MachOLoadDylib::CreateLoadSystemDylib(zone_));
-#endif
- }
-}
-
-void MachOHeader::InitializeSymbolTables() {
- // Not idempotent.
- ASSERT_EQUAL(full_symtab_.num_symbols(), 0);
- ASSERT(!HasCommand(MachOSymbolTable::kCommandCode));
-
- // Grab all the sections in order.
- GrowableArray<MachOSection*> sections(zone_, 0);
- for (auto* const command : commands_) {
- // Should be run before ComputeOffsets.
- ASSERT(!command->HasContents() || !command->file_offset_is_set());
- if (auto* const s = command->AsMachOSegment()) {
- for (auto* const c : s->contents()) {
- if (auto* const section = c->AsMachOSection()) {
- sections.Add(section);
- }
- }
- }
- }
-
- // This symbol table is for the MachOWriter's internal use. All symbols
- // should be added to it so the writer can resolve relocations.
- full_symtab_.Initialize(sections, /*is_stripped=*/false);
- auto* table = &full_symtab_;
- if (is_stripped_) {
- // Create a separate symbol table that is actually written to the output.
- // This one will only contain what's needed for the dynamic symbol table.
- auto* const table = new (zone()) MachOSymbolTable(zone());
- table->Initialize(sections, is_stripped_);
- }
- commands_.Add(table);
-
- // For snapshots, include a dynamic symbol table as well.
- if (type_ == SnapshotType::Snapshot) {
- auto* const dynamic_symtab = new (zone()) MachODynamicSymbolTable(*table);
- commands_.Add(dynamic_symtab);
- }
-}
-
-void MachOHeader::FinalizeDwarfSections() {
- if (dwarf_ == nullptr) return;
-
- // Currently we only output DWARF information involving code.
-#if defined(DEBUG)
- auto* const text_segment = FindSegment(mach_o::SEG_TEXT);
- ASSERT(text_segment != nullptr);
- ASSERT(text_segment->FindSection(mach_o::SECT_TEXT) != nullptr);
-#endif
-
- // Create the DWARF segment, which should not already exist.
- ASSERT(FindSegment(mach_o::SEG_DWARF) == nullptr);
- auto const init_vm_protection = mach_o::VM_PROT_READ | mach_o::VM_PROT_WRITE;
- auto const max_vm_protection = init_vm_protection | mach_o::VM_PROT_EXECUTE;
- auto* const dwarf_segment = new (zone()) MachOSegment(
- zone(), mach_o::SEG_DWARF, init_vm_protection, max_vm_protection);
- commands_.Add(dwarf_segment);
-
- const intptr_t alignment = 1; // No extra padding.
- auto add_debug = [&](const char* name,
- const DwarfSharedObjectStream& stream) {
- auto* const section = new (zone())
- MachOSection(zone(), name, mach_o::S_REGULAR, mach_o::S_ATTR_DEBUG,
- /*has_contents=*/true, alignment);
- section->AddPortion(stream.buffer(), stream.bytes_written(),
- stream.relocations());
- dwarf_segment->AddContents(section);
- };
-
- {
- ZoneWriteStream stream(zone(), DwarfSharedObjectStream::kInitialBufferSize);
- DwarfSharedObjectStream dwarf_stream(zone_, &stream);
- dwarf_->WriteAbbreviations(&dwarf_stream);
- add_debug(mach_o::SECT_DEBUG_ABBREV, dwarf_stream);
- }
-
- {
- ZoneWriteStream stream(zone(), DwarfSharedObjectStream::kInitialBufferSize);
- DwarfSharedObjectStream dwarf_stream(zone_, &stream);
- dwarf_->WriteDebugInfo(&dwarf_stream);
- add_debug(mach_o::SECT_DEBUG_INFO, dwarf_stream);
- }
-
- {
- ZoneWriteStream stream(zone(), DwarfSharedObjectStream::kInitialBufferSize);
- DwarfSharedObjectStream dwarf_stream(zone_, &stream);
- dwarf_->WriteLineNumberProgram(&dwarf_stream);
- add_debug(mach_o::SECT_DEBUG_LINE, dwarf_stream);
- }
-}
-
-void MachOHeader::FinalizeCommands() {
- // Not idempotent.
- ASSERT(FindSegment(mach_o::SEG_LINKEDIT) == nullptr);
- ASSERT(!HasCommand(MachOCodeSignature::kCommandCode));
-
- intptr_t num_commands = commands_.length();
- // We shouldn't be writing empty Mach-O snapshots.
- ASSERT(num_commands != 0);
- GrowableArray<MachOCommand*> reordered_commands(zone_, num_commands);
-
- // Now do a single pass over the commands, sorting them into bins based on
- // the desired final ordering and also calculating a map from old section
- // indices in the old order to new section indices in the new order.
-
- // First, any commands that are only part of the header.
- GrowableArray<MachOCommand*> header_only_commands(zone_, 0);
-
- // Ensure the text segment is the initial segment. This means the
- // text segment contains the header in its file contents/memory space.
- MachOSegment* text_segment = text_segment_;
- // We should be writing instructions and/or const data.
- ASSERT(text_segment != nullptr);
-
- // Then all segments that have defined symbols. These segments
- // are present in both snapshots and separate debugging information,
- // and the symbols defined in these sections should have consistent
- // relocated memory addresses in both.
- GrowableArray<MachOSegment*> symbol_segments(zone_, 0);
-
- // Then all other segments added prior to calling this function.
- // These need to be before the linkedit segment, which is created
- // below, so that they are also protected by the code signature
- // (if there is one).
- GrowableArray<MachOSegment*> other_segments(zone_, 0);
-
- // Next comes any non-segment load commands that have allocated content
- // outside of the header like the symbol table. A linkedit segment
- // is created later to contain the non-header contents of these commands.
- GrowableArray<MachOCommand*> linkedit_commands(zone_, 0);
-
- // Maps segments to the section count and old initial section index for
- // that segment. (Sections are not reordered during this, so this is
- // all that's needed to calculate new section indices.)
- using SegmentMapTrait =
- RawPointerKeyValueTrait<const MachOSegment,
- std::pair<intptr_t, intptr_t>>;
- DirectChainedHashMap<SegmentMapTrait> section_info(zone_, num_commands);
- intptr_t num_sections = 0;
- for (auto* const command : commands_) {
- // Check that we're not reordering after offsets have been computed.
- ASSERT(!command->HasContents() || !command->file_offset_is_set());
- if (auto* const s = command->AsMachOSegment()) {
- const intptr_t count = s->NumSections();
- if (count != 0) {
- // Section indices start from 1.
- section_info.Insert({s, {count, num_sections + 1}});
- num_sections += count;
- }
- if (s->HasName(mach_o::SEG_TEXT)) {
- ASSERT(text_segment == s);
- } else if (s->ContainsSymbols()) {
- symbol_segments.Add(s);
- } else {
- other_segments.Add(s);
- }
- } else if (!command->HasContents()) {
- header_only_commands.Add(command);
- } else {
- linkedit_commands.Add(command);
- }
- }
-
- // We should always have a symbol table, even in stripped files where
- // it only contains global exported symbols, which means there should
- // be a linkedit segment.
- ASSERT(!linkedit_commands.is_empty());
- auto* const linkedit_segment =
- new (zone_) MachOSegment(zone_, mach_o::SEG_LINKEDIT);
- num_commands += 1;
- for (auto* const c : linkedit_commands) {
- linkedit_segment->AddContents(c);
- }
- if (type_ == SnapshotType::Snapshot) {
- // Also include an embedded ad-hoc linker signed code signature as the
- // last contents of the linkedit segment (which is the last segment).
- auto* const signature = new (zone_) MachOCodeSignature(identifier_);
- linkedit_segment->AddContents(signature);
- linkedit_commands.Add(signature);
- num_commands += 1;
- }
-
- GrowableArray<MachOSegment*> segments(
- zone_, symbol_segments.length() + other_segments.length() + 2);
- // Put the text, data, and linkedit segments in the expected ordering.
- segments.Add(text_segment);
- segments.AddArray(symbol_segments);
- segments.AddArray(other_segments);
- segments.Add(linkedit_segment);
-
- // The initial segment in the file should have the header as its initial
- // contents. Since the header is not a section, this won't change the
- // section numbering.
- segments[0]->AddContents(this);
-
- // Now populate reordered_commands.
- reordered_commands.AddArray(header_only_commands);
-
- // While adding segments, also map old section indices to new ones. Include
- // a map of mach_o::NO_SECT to mach_o::NO_SECT so that changing the section
- // index on a non-section symbol is a no-op.
- GrowableArray<intptr_t> index_map(zone_, num_sections + 1);
- index_map.FillWith(mach_o::NO_SECT, 0, num_sections + 1);
- // Section indices start from 1.
- intptr_t current_section_index = 1;
- for (auto* const s : segments) {
- reordered_commands.Add(s);
- auto* const kv = section_info.Lookup(s);
- if (kv != nullptr) {
- const auto& [num_sections, old_start] = SegmentMapTrait::ValueOf(*kv);
- ASSERT(num_sections > 0); // Otherwise it's not in the map.
- ASSERT(old_start != mach_o::NO_SECT);
- for (intptr_t i = 0; i < num_sections; ++i) {
- ASSERT(current_section_index != mach_o::NO_SECT);
- index_map[old_start + i] = current_section_index++;
- }
- }
- }
- reordered_commands.AddArray(linkedit_commands);
-
- // All sections should have been accounted for in the loops above as well as
- // the new linkedit segment (and, if applicable, the code signature).
- ASSERT_EQUAL(reordered_commands.length(), num_commands);
- // Replace the content of commands_ with the reordered commands.
- commands_.Clear();
- commands_.AddArray(reordered_commands);
-
- // This must be true for uses of the map to be correct.
- ASSERT_EQUAL(index_map[mach_o::NO_SECT], mach_o::NO_SECT);
-#if defined(DEBUG)
- for (intptr_t i = 1; i < num_sections; ++i) {
- ASSERT(index_map[i] != mach_o::NO_SECT);
- }
-#endif
-
- // Update the section indices of any section-owned symbols.
- full_symtab_.UpdateSectionIndices(index_map);
- auto* const table = IncludedSymbolTable();
- if (table != &full_symtab_) {
- ASSERT(is_stripped_);
- table->UpdateSectionIndices(index_map);
- }
-}
-
-struct ContentOffsetsVisitor : public MachOContents::Visitor {
- explicit ContentOffsetsVisitor(Zone* zone) : address_map(zone, 1) {
- // Add NO_SECT -> 0 mapping.
- address_map.Add(0);
- }
-
- void Default(MachOContents* contents) {
- ASSERT_EQUAL(contents->IsMachOHeader(), file_offset == 0);
- ASSERT_EQUAL(contents->IsMachOHeader(), memory_address == 0);
- // Increment the file and memory offsets by the appropriate amounts.
- if (contents->HasContents()) {
- file_offset = Utils::RoundUp(file_offset, contents->Alignment());
- contents->set_file_offset(file_offset);
- file_offset += contents->SelfFileSize();
- }
- if (contents->IsAllocated()) {
- memory_address = Utils::RoundUp(memory_address, contents->Alignment());
- contents->set_memory_address(memory_address);
- memory_address += contents->SelfMemorySize();
- }
- contents->VisitChildren(this);
- if (contents->HasContents()) {
- ASSERT_EQUAL(file_offset, contents->file_offset() + contents->FileSize());
- }
- if (contents->IsAllocated()) {
- ASSERT_EQUAL(memory_address,
- contents->memory_address() + contents->MemorySize());
- }
- }
-
- void VisitMachOSegment(MachOSegment* segment) {
- ASSERT_EQUAL(segment->IsInitial(), file_offset == 0);
- ASSERT_EQUAL(segment->IsInitial(), memory_address == 0);
- // Segments are always allocated and we set the file offset even
- // when the segment doesn't actually write any contents.
- file_offset = Utils::RoundUp(file_offset, segment->Alignment());
- segment->set_file_offset(file_offset);
- file_offset += segment->SelfFileSize();
- memory_address = Utils::RoundUp(memory_address, segment->Alignment());
- segment->set_memory_address(memory_address);
- memory_address += segment->SelfMemorySize();
- segment->VisitChildren(this);
- if (segment->PadFileSizeToAlignment()) {
- file_offset = Utils::RoundUp(file_offset, segment->Alignment());
- }
- memory_address = Utils::RoundUp(memory_address, segment->Alignment());
- ASSERT_EQUAL(file_offset, segment->file_offset() + segment->FileSize());
- ASSERT_EQUAL(memory_address,
- segment->memory_address() + segment->MemorySize());
- }
-
- void VisitMachOSection(MachOSection* section) {
- // Sections do not contain other sections, so the visitor can use the
- // default behavior without worrying about adding to the address map in
- // the wrong order.
- Visitor::VisitMachOSection(section);
- address_map.Add(section->memory_address());
- }
-
- // Maps indices of allocated sections in the section table to memory offsets.
- // Note that sections are 1-indexed, with 0 (NO_SECT) mapping to 0.
- GrowableArray<uword> address_map;
- intptr_t file_offset = 0;
- intptr_t memory_address = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ContentOffsetsVisitor);
-};
-
-void MachOHeader::ComputeOffsets() {
- intptr_t header_offset = SizeWithoutLoadCommands();
- for (auto* const c : commands_) {
- ASSERT(
- Utils::IsAligned(header_offset, MachOCommand::kLoadCommandAlignment));
- c->set_header_offset(header_offset);
- header_offset += c->cmdsize();
- }
-
- ContentOffsetsVisitor visitor(zone());
- // All commands with non-header content should be part of a segment.
- // In addition, the header is visited during the initial segment.
- VisitSegments(&visitor);
-
- // Finalize the dynamic symbol table, now that the file offset for the
- // symbol table has been calculated.
-
- // Entry for NO_SECT + 1-indexed entries for sections.
- ASSERT_EQUAL(visitor.address_map.length(), NumSections() + 1);
-
- // Adjust addresses in symbol tables as we now have section memory offsets.
- full_symtab_.Finalize(visitor.address_map);
- auto* const table = IncludedSymbolTable();
- if (table != &full_symtab_) {
- ASSERT(is_stripped_);
- table->Finalize(visitor.address_map);
- }
-}
-
-void MachOSymbolTable::Initialize(const GrowableArray<MachOSection*>& sections,
- bool is_stripped) {
- // Not idempotent.
- ASSERT(!num_local_symbols_is_set());
-
- // If symbolic debugging symbols are emitted, then any section
- // symbols are marked as alternate entries in favor of the symbolic
- // debugging symbols.
- const intptr_t desc = is_stripped ? 0 : mach_o::N_ALT_ENTRY;
-
- // For unstripped symbol tables, we do two initial passes. In the first
- // pass, we add section symbols for local static symbols.
- if (!is_stripped) {
- for (intptr_t i = 0, n = sections.length(); i < n; ++i) {
- auto* const section = sections[i];
- const intptr_t section_index = i + 1; // 1-indexed, as 0 is NO_SECT.
- for (const auto& portion : section->portions()) {
- if (portion.symbols != nullptr) {
- for (const auto& symbol_data : *portion.symbols) {
- AddSymbol(symbol_data.name, mach_o::N_SECT, section_index, desc,
- portion.offset + symbol_data.offset, symbol_data.label);
- }
- }
- }
- }
-
- // In the second pass, we add appropriate symbolic debugging symbols.
- using Type = SharedObjectWriter::SymbolData::Type;
- auto add_symbolic_debugging_symbols =
- [&](const char* name, Type type, intptr_t section_index,
- intptr_t offset, intptr_t size, bool is_global) {
- switch (type) {
- case Type::Function: {
- AddSymbol("", mach_o::N_BNSYM, section_index, /*desc=*/0, offset);
- AddSymbol(name, mach_o::N_FUN, section_index, /*desc=*/0, offset);
- // The size is output as an unnamed N_FUN symbol with no section
- // following the actual N_FUN symbol.
- AddSymbol("", mach_o::N_FUN, mach_o::NO_SECT, /*desc=*/0, size);
- AddSymbol("", mach_o::N_ENSYM, section_index, /*desc=*/0,
- offset + size);
-
- break;
- }
- case Type::Section:
- case Type::Object: {
- if (is_global) {
- AddSymbol(name, mach_o::N_GSYM, mach_o::NO_SECT, /*desc=*/0,
- /*value=*/0);
- } else {
- AddSymbol(name, mach_o::N_STSYM, section_index,
- /*desc=*/0, offset);
- }
- break;
- }
- }
- };
-
- for (intptr_t i = 0, n = sections.length(); i < n; ++i) {
- auto* const section = sections[i];
- const intptr_t section_index = i + 1; // 1-indexed, as 0 is NO_SECT.
- // We handle global symbols for text sections slightly differently than
- // those for other sections.
- const bool is_text_section = section->HasName(mach_o::SECT_TEXT);
- for (const auto& portion : section->portions()) {
- if (portion.symbol_name != nullptr) {
- // Matching the symbolic debugging symbols created for assembled
- // snapshots.
- auto const type = is_text_section ? Type::Function : Type::Section;
- // The "size" of a function symbol created for start of a text portion
- // is up to the first function symbol.
- auto const size = is_text_section && portion.symbols != nullptr
- ? portion.symbols->At(0).offset
- : portion.size;
- add_symbolic_debugging_symbols(portion.symbol_name, type,
- section_index, portion.offset, size,
- /*is_global=*/true);
- }
- if (portion.symbols != nullptr) {
- for (const auto& symbol_data : *portion.symbols) {
- add_symbolic_debugging_symbols(
- symbol_data.name, symbol_data.type, section_index,
- portion.offset + symbol_data.offset, symbol_data.size,
- /*is_global=*/false);
- }
- }
- }
- }
- }
- set_num_local_symbols(num_symbols());
-
- // In the final pass, we add external symbols for section global symbols
- // (so added to both stripped and unstripped symbol tables).
- for (intptr_t i = 0, n = sections.length(); i < n; ++i) {
- auto* const section = sections[i];
- const intptr_t section_index = i + 1; // 1-indexed, as 0 is NO_SECT.
- for (const auto& portion : section->portions()) {
- if (portion.symbol_name != nullptr) {
- AddSymbol(portion.symbol_name, mach_o::N_SECT | mach_o::N_EXT,
- section_index, desc, portion.offset, portion.label);
- }
- }
- }
- set_num_external_symbols(num_symbols() - num_local_symbols());
-}
-
-} // namespace dart
-
-#endif // defined(DART_PRECOMPILER)
diff --git a/runtime/vm/mach_o.h b/runtime/vm/mach_o.h
deleted file mode 100644
index f652f97..0000000
--- a/runtime/vm/mach_o.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// 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_
diff --git a/runtime/vm/os_macos.cc b/runtime/vm/os_macos.cc
index 4a84c7c..26ba160 100644
--- a/runtime/vm/os_macos.cc
+++ b/runtime/vm/os_macos.cc
@@ -304,9 +304,7 @@
const uint8_t* dso_base = GetAppDSOBase(snapshot_instructions);
const auto& macho_header =
*reinterpret_cast<const struct mach_header*>(dso_base);
- // 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.
+ // We assume host endianness in the Mach-O file.
if (macho_header.magic != MH_MAGIC && macho_header.magic != MH_MAGIC_64) {
return {0, nullptr};
}
diff --git a/runtime/vm/timeline.cc b/runtime/vm/timeline.cc
index 8b499d7..8e010f3 100644
--- a/runtime/vm/timeline.cc
+++ b/runtime/vm/timeline.cc
@@ -16,10 +16,6 @@
#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"
diff --git a/runtime/vm/timeline.h b/runtime/vm/timeline.h
index f73ecb8..f22a189 100644
--- a/runtime/vm/timeline.h
+++ b/runtime/vm/timeline.h
@@ -29,17 +29,10 @@
#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)
diff --git a/runtime/vm/timeline_macos.cc b/runtime/vm/timeline_macos.cc
index 7972e4b..ee77188a 100644
--- a/runtime/vm/timeline_macos.cc
+++ b/runtime/vm/timeline_macos.cc
@@ -5,8 +5,6 @@
#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"
diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni
index 7e74862..dc2209f 100644
--- a/runtime/vm/vm_sources.gni
+++ b/runtime/vm/vm_sources.gni
@@ -166,8 +166,6 @@
"log.h",
"longjump.cc",
"longjump.h",
- "mach_o.cc",
- "mach_o.h",
"megamorphic_cache_table.cc",
"megamorphic_cache_table.h",
"memory_region.cc",