Reland "Add gen_snapshot binaries, producing Linux ARM64/x64 snapshots"

This is a reland of commit e6d55c6c2f6193a7ec5ab7aaae0c25eaebd620b9

Reworks guards for 64-bit Windows unwinding support code so that
it is not included in any 64-bit Windows gen_snapshot executable that
targets a non-64-bit Windows platform.

TEST=ci, build only

Original change's description:
> Add gen_snapshot binaries, producing Linux ARM64/x64 snapshots
>
> Inspired by `//runtime/bin:gen_snapshot_fuchsia`, required for go/dart-cross-compile.
>
> TEST=ci, build only
>
> Change-Id: Ie521c984a8f44d25f3f78d946af9416582a572dc
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/410402
> Reviewed-by: Ryan Macnak <rmacnak@google.com>
> Commit-Queue: Ivan Inozemtsev <iinozemtsev@google.com>

Change-Id: I4f1323ac8f7ab215c2dffeef188f70c466fb62ea
Cq-Include-Trybots: luci.dart.try:vm-win-release-arm64-try,vm-win-release-ia32-try,vm-win-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/413402
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ivan Inozemtsev <iinozemtsev@google.com>
Commit-Queue: Ivan Inozemtsev <iinozemtsev@google.com>
diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn
index 450411e..f60f6be 100644
--- a/runtime/BUILD.gn
+++ b/runtime/BUILD.gn
@@ -128,6 +128,26 @@
   defines = [ "DART_TARGET_OS_FUCHSIA" ]
 }
 
+# We need to build gen_snapshot targeting Linux ARM64 during a build
+# of the SDK. This configuration is used to unconditionally target
+# Linux ARM64. It should not be combined with dart_os_config and dart_arch_config.
+config("dart_linux_arm64_config") {
+  defines = [
+    "DART_TARGET_OS_LINUX",
+    "TARGET_ARCH_ARM64",
+  ]
+}
+
+# We need to build gen_snapshot targeting Linux ARM64 during a build
+# of the SDK. This configuration is used to unconditionally target
+# Linux ARM64. It should not be combined with dart_os_config and dart_arch_config.
+config("dart_linux_x64_config") {
+  defines = [
+    "DART_TARGET_OS_LINUX",
+    "TARGET_ARCH_X64",
+  ]
+}
+
 config("dart_arch_config") {
   defines = []
 
diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn
index b3a1350..6b53a0b 100644
--- a/runtime/bin/BUILD.gn
+++ b/runtime/bin/BUILD.gn
@@ -107,6 +107,20 @@
   extra_configs = [ "..:dart_product_config" ]
 }
 
+build_libdart_builtin("libdart_builtin_product_linux_x64") {
+  extra_configs = [
+    "..:dart_product_config",
+    "..:dart_linux_x64_config",
+  ]
+}
+
+build_libdart_builtin("libdart_builtin_product_linux_arm64") {
+  extra_configs = [
+    "..:dart_product_config",
+    "..:dart_linux_arm64_config",
+  ]
+}
+
 template("build_native_assets_api") {
   extra_configs = []
   if (defined(invoker.extra_configs)) {
@@ -330,6 +344,31 @@
   ]
 }
 
+build_gen_snapshot("gen_snapshot_product_linux_x64") {
+  extra_configs = [
+    "..:dart_product_config",
+    "..:dart_linux_x64_config",
+  ]
+  extra_deps = [
+    ":gen_snapshot_dart_io_product_linux_x64",
+    ":libdart_builtin_product_linux_x64",
+    "..:libdart_precompiler_product_linux_x64",
+    "../platform:libdart_platform_precompiler_product_linux_x64",
+  ]
+}
+
+build_gen_snapshot("gen_snapshot_product_linux_arm64") {
+  extra_configs = [
+    "..:dart_product_config",
+    "..:dart_linux_arm64_config",
+  ]
+  extra_deps = [
+    ":gen_snapshot_dart_io_product_linux_arm64",
+    ":libdart_builtin_product_linux_arm64",
+    "..:libdart_precompiler_product_linux_arm64",
+    "../platform:libdart_platform_precompiler_product_linux_arm64",
+  ]
+}
 build_gen_snapshot("gen_snapshot_host_targeting_host") {
   extra_configs = [ "..:dart_maybe_product_config" ]
   extra_deps = [
@@ -434,6 +473,20 @@
   ]
 }
 
+build_gen_snapshot_dart_io("gen_snapshot_dart_io_product_linux_x64") {
+  extra_configs = [
+    "..:dart_product_config",
+    "..:dart_linux_x64_config",
+  ]
+}
+
+build_gen_snapshot_dart_io("gen_snapshot_dart_io_product_linux_arm64") {
+  extra_configs = [
+    "..:dart_product_config",
+    "..:dart_linux_arm64_config",
+  ]
+}
+
 # A source set for the implementation of 'dart:io' library.
 template("dart_io") {
   extra_configs = []
diff --git a/runtime/bin/elf_loader.cc b/runtime/bin/elf_loader.cc
index 70baa9c..6c8b656 100644
--- a/runtime/bin/elf_loader.cc
+++ b/runtime/bin/elf_loader.cc
@@ -241,8 +241,7 @@
   const dart::elf::Symbol* dynamic_symbol_table_ = nullptr;
   uword dynamic_symbol_count_ = 0;
 
-#if defined(DART_HOST_OS_WINDOWS) &&                                           \
-    (defined(HOST_ARCH_X64) || defined(HOST_ARCH_ARM64))
+#if defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT)
   // Dynamic table for looking up unwinding exceptions info.
   // Initialized by LoadSegments as we load executable segment.
   MallocGrowableArray<void*> dynamic_runtime_function_tables_;
@@ -295,8 +294,7 @@
 }
 
 LoadedElf::~LoadedElf() {
-#if defined(DART_HOST_OS_WINDOWS) &&                                           \
-    (defined(HOST_ARCH_X64) || defined(HOST_ARCH_ARM64))
+#if defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT)
   for (intptr_t i = 0; i < dynamic_runtime_function_tables_.length(); i++) {
     UnwindingRecordsPlatform::UnregisterDynamicTable(
         dynamic_runtime_function_tables_[i]);
@@ -465,8 +463,7 @@
     CHECK_ERROR(memory != nullptr, "Could not map segment.");
     CHECK_ERROR(memory->address() == memory_start,
                 "Mapping not at requested address.");
-#if defined(DART_HOST_OS_WINDOWS) &&                                           \
-    (defined(HOST_ARCH_X64) || defined(HOST_ARCH_ARM64))
+#if defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT)
     // For executable pages register unwinding information that should be
     // present on the page.
     if (map_type == File::kReadExecute) {
diff --git a/runtime/configs.gni b/runtime/configs.gni
index c8ef631..c4b1dd1 100644
--- a/runtime/configs.gni
+++ b/runtime/configs.gni
@@ -63,6 +63,18 @@
 _precompiler_product_host_targeting_host_config =
     _base_host_targeting_host_config + _precompiler_base + _product
 
+_precompiler_product_linux_x64_config =
+    [
+      "$_dart_runtime:dart_config",
+      "$_dart_runtime:dart_linux_x64_config",
+    ] + _product + _precompiler_base
+
+_precompiler_product_linux_arm64_config =
+    [
+      "$_dart_runtime:dart_config",
+      "$_dart_runtime:dart_linux_arm64_config",
+    ] + _product + _precompiler_base
+
 _all_configs = [
   {
     suffix = "_jit"
@@ -148,6 +160,20 @@
     compiler = true
     is_product = false
   },
+  {
+    suffix = "_precompiler_product_linux_x64"
+    configs = _precompiler_product_linux_x64_config
+    snapshot = false
+    compiler = true
+    is_product = true
+  },
+  {
+    suffix = "_precompiler_product_linux_arm64"
+    configs = _precompiler_product_linux_arm64_config
+    snapshot = false
+    compiler = true
+    is_product = true
+  },
 ]
 
 # This template creates a target for each of the configurations listed above.
diff --git a/runtime/platform/unwinding_records.cc b/runtime/platform/unwinding_records.cc
index e6e69d0..835464e 100644
--- a/runtime/platform/unwinding_records.cc
+++ b/runtime/platform/unwinding_records.cc
@@ -7,17 +7,19 @@
 
 namespace dart {
 
-#if (!defined(DART_TARGET_OS_WINDOWS) && !defined(DART_HOST_OS_WINDOWS)) ||    \
-    (!defined(TARGET_ARCH_X64) && !defined(TARGET_ARCH_ARM64))
+#if !(defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT) ||     \
+      defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT))
 
 intptr_t UnwindingRecordsPlatform::SizeInBytes() {
   return 0;
 }
 
-#endif  // (!defined(DART_TARGET_OS_WINDOWS) && !defined(DART_HOST_OS_WINDOWS))
+#endif  // !defined(DART_TARGET_OS_WINDOWS) && !defined(DART_HOST_OS_WINDOWS)
 
-#if !defined(DART_HOST_OS_WINDOWS) ||                                          \
-    (!defined(TARGET_ARCH_X64) && !defined(TARGET_ARCH_ARM64))
+// Also use empty definitions when running gen_snapshot on 64-bit Windows, as it
+// does not use the ELF loader, which is the client of these methods.
+#if !defined(DART_HOST_OS_WINDOWS) || !defined(ARCH_IS_64_BIT) ||              \
+    (defined(DART_PRECOMPILER) && !defined(TESTING))
 
 void UnwindingRecordsPlatform::RegisterExecutableMemory(
     void* start,
@@ -25,6 +27,6 @@
     void** pp_dynamic_table) {}
 void UnwindingRecordsPlatform::UnregisterDynamicTable(void* p_dynamic_table) {}
 
-#endif  // !defined(DART_HOST_OS_WINDOWS) ...
+#endif  // !defined(DART_HOST_OS_WINDOWS) || ...
 
 }  // namespace dart
diff --git a/runtime/platform/unwinding_records.h b/runtime/platform/unwinding_records.h
index 83d242a..0da447b 100644
--- a/runtime/platform/unwinding_records.h
+++ b/runtime/platform/unwinding_records.h
@@ -20,10 +20,25 @@
   static void UnregisterDynamicTable(void* p_dynamic_table);
 };
 
-#if (defined(DART_TARGET_OS_WINDOWS) || defined(DART_HOST_OS_WINDOWS)) &&      \
-    defined(TARGET_ARCH_X64)
+// These definitions are only needed if targeting 64-bit Windows, or if the
+// ELF loader may be used on 64-bit Windows.
+//
+// More specifically, a 64-bit Windows gen_snapshot that does not target
+// 64-bit Windows does not need these definitions, as the generated snapshot
+// should not contain Windows-specific unwinding records and gen_snapshot
+// does not uses the ELF loader.
+#if (defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT)) ||     \
+    (defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT) &&               \
+     (!defined(DART_PRECOMPILER) || defined(TESTING)))
 
 #pragma pack(push, 1)
+
+#if !defined(DART_HOST_OS_WINDOWS) || !defined(HOST_ARCH_X64)
+typedef uint32_t ULONG;
+typedef uint32_t DWORD;
+#endif
+
+#if defined(TARGET_ARCH_X64)
 //
 // Refer to https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64
 //
@@ -49,7 +64,6 @@
 } UNWIND_INFO, *PUNWIND_INFO;
 
 #if !defined(DART_HOST_OS_WINDOWS)
-typedef uint32_t ULONG;
 typedef struct _RUNTIME_FUNCTION {
   ULONG BeginAddress;
   ULONG EndAddress;
@@ -97,12 +111,7 @@
   RUNTIME_FUNCTION runtime_function[1];
 };
 
-#pragma pack(pop)
-
-#elif defined(TARGET_ARCH_ARM64) &&                                            \
-    (defined(DART_TARGET_OS_WINDOWS) || defined(DART_HOST_OS_WINDOWS))
-
-#pragma pack(push, 1)
+#elif defined(TARGET_ARCH_ARM64)
 
 // ARM64 unwind codes are defined in below doc.
 // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#unwind-codes
@@ -139,12 +148,9 @@
   uint32_t CodeWords : 5;
 };
 
-#if !defined(DART_HOST_OS_WINDOWS)
-typedef uint32_t ULONG;
-typedef uint32_t DWORD;
+#if !defined(DART_HOST_OS_WINDOWS) || !defined(HOST_ARCH_ARM64)
 typedef struct _RUNTIME_FUNCTION {
   ULONG BeginAddress;
-  ULONG EndAddress;
   ULONG UnwindData;
 } RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;
 #endif
@@ -231,9 +237,13 @@
   RUNTIME_FUNCTION runtime_function[kDefaultRuntimeFunctionCount];
 };
 
+#else
+#error Unhandled Windows architecture.
+#endif
+
 #pragma pack(pop)
 
-#endif  // (defined(DART_TARGET_OS_WINDOWS) || defined(DART_HOST_OS_WINDOWS))
+#endif  // (defined(DART_TARGET_OS_WINDOWS) || ...
 
 }  // namespace dart
 
diff --git a/runtime/platform/unwinding_records_win.cc b/runtime/platform/unwinding_records_win.cc
index 937cd1d..6f9c6dc 100644
--- a/runtime/platform/unwinding_records_win.cc
+++ b/runtime/platform/unwinding_records_win.cc
@@ -9,23 +9,27 @@
 
 namespace dart {
 
-#if (defined(DART_TARGET_OS_WINDOWS) || defined(DART_HOST_OS_WINDOWS)) &&      \
-    (defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_ARM64))
+#if (defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT)) ||     \
+    (defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT))
 
 #if defined(TARGET_ARCH_X64)
 const intptr_t kReservedUnwindingRecordsSizeBytes = 64;
-#else
+#elif defined(TARGET_ARCH_ARM64)
 const intptr_t kReservedUnwindingRecordsSizeBytes = 4 * KB;
+#else
+#error Unhandled Windows architecture.
 #endif
 
 intptr_t UnwindingRecordsPlatform::SizeInBytes() {
   return kReservedUnwindingRecordsSizeBytes;
 }
 
-#endif  // defined(DART_TARGET_OS_WINDOWS) || defined(DART_HOST_OS_WINDOWS)
+#endif  // defined(DART_TARGET_OS_WINDOWS) ...
 
-#if defined(DART_HOST_OS_WINDOWS) &&                                           \
-    (defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_ARM64))
+// Only use these definitions when the ELF loader may be used on 64-bit Windows,
+// as it is the only client of these methods (e.g., _not_ in gen_snapshot).
+#if defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT) &&                \
+    (!defined(DART_PRECOMPILER) || defined(TESTING))
 
 void UnwindingRecordsPlatform::RegisterExecutableMemory(
     void* start,
@@ -54,6 +58,6 @@
   RtlDeleteGrowableFunctionTable(p_dynamic_table);
 }
 
-#endif  // defined(DART_HOST_OS_WINDOWS)
+#endif  // defined(DART_HOST_OS_WINDOWS) ...
 
 }  // namespace dart
diff --git a/runtime/vm/elf.cc b/runtime/vm/elf.cc
index acf0d72..0cea903 100644
--- a/runtime/vm/elf.cc
+++ b/runtime/vm/elf.cc
@@ -1430,8 +1430,7 @@
   // No text section added means no .eh_frame.
   if (text_section == nullptr) return;
 
-#if defined(DART_TARGET_OS_WINDOWS) &&                                         \
-    (defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_ARM64))
+#if defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT)
   // Append Windows unwinding instructions to the end of .text section.
   {  // NOLINT
     auto* const unwinding_instructions_frame = new (zone_) TextSection(type_);
@@ -1493,8 +1492,7 @@
 
   // Emit an FDE covering each .text section.
   for (const auto& portion : text_section->portions()) {
-#if defined(DART_TARGET_OS_WINDOWS) &&                                         \
-    (defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_ARM64))
+#if defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT)
     if (portion.label == 0) {
       // Unwinding instructions sections doesn't have label, doesn't dwarf
       continue;
diff --git a/runtime/vm/unwinding_records.cc b/runtime/vm/unwinding_records.cc
index 13109d6..e442118 100644
--- a/runtime/vm/unwinding_records.cc
+++ b/runtime/vm/unwinding_records.cc
@@ -7,22 +7,23 @@
 
 namespace dart {
 
-#if (!defined(DART_TARGET_OS_WINDOWS) && !defined(DART_HOST_OS_WINDOWS)) ||    \
-    (!defined(TARGET_ARCH_X64) && !defined(TARGET_ARCH_ARM64))
+#if !defined(DART_TARGET_OS_WINDOWS) || !defined(TARGET_ARCH_IS_64_BIT)
 
 const void* UnwindingRecords::GenerateRecordsInto(intptr_t offset,
                                                   uint8_t* target_buffer) {
   return nullptr;
 }
 
-#endif
+#endif  // !defined(DART_TARGET_OS_WINDOWS)
 
-#if !defined(DART_HOST_OS_WINDOWS) ||                                          \
-    (!defined(TARGET_ARCH_X64) && !defined(TARGET_ARCH_ARM64))
+// Also use empty definitions when running gen_snapshot on 64-bit Windows, as
+// it does not use the ELF loader, which is the client of these methods.
+#if !defined(DART_HOST_OS_WINDOWS) || !defined(ARCH_IS_64_BIT) ||              \
+    (defined(DART_PRECOMPILER) && !defined(TESTING))
 
 void UnwindingRecords::RegisterExecutablePage(Page* page) {}
 void UnwindingRecords::UnregisterExecutablePage(Page* page) {}
 
-#endif
+#endif  // !defined(DART_HOST_OS_WINDOWS)
 
 }  // namespace dart
diff --git a/runtime/vm/unwinding_records_win.cc b/runtime/vm/unwinding_records_win.cc
index a92c293..01e5410 100644
--- a/runtime/vm/unwinding_records_win.cc
+++ b/runtime/vm/unwinding_records_win.cc
@@ -10,8 +10,11 @@
 
 namespace dart {
 
-#if (defined(DART_TARGET_OS_WINDOWS) || defined(DART_HOST_OS_WINDOWS)) &&      \
-    (defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_ARM64))
+// When the host is 64-bit Windows but the target is not, only define this
+// when the ELF loader may be used, so _not_ in gen_snapshot.
+#if (defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT)) ||     \
+    (defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT) &&               \
+     (!defined(DART_PRECOMPILER) || defined(TESTING)))
 
 static void InitUnwindingRecord(intptr_t offset,
                                 CodeRangeUnwindingRecord* record,
@@ -88,11 +91,15 @@
   // cover it, more pages will need to reserved for unwind data.
   ASSERT(remaining_size_in_bytes <= 0);
 #else
-#error What architecture?
+#error Unhandled Windows architecture.
 #endif
   record->magic = kUnwindingRecordMagic;
 }
 
+#endif  // defined(DART_TARGET_OS_WINDOWS) || ...
+
+#if defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT)
+
 const void* UnwindingRecords::GenerateRecordsInto(intptr_t offset,
                                                   uint8_t* target_buffer) {
   CodeRangeUnwindingRecord* record =
@@ -101,10 +108,13 @@
   return target_buffer;
 }
 
-#endif  // (defined(DART_TARGET_OS_WINDOWS) || defined(DART_HOST_OS_WINDOWS))
+#endif  // defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT)
 
-#if defined(DART_HOST_OS_WINDOWS) &&                                           \
-    (defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_ARM64))
+// Only use these definitions when the ELF loader may be used on 64-bit
+// Windows, as it is the only client of these methods (e.g., _not_ in
+// gen_snapshot).
+#if defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT) &&                \
+    (!defined(DART_PRECOMPILER) || defined(TESTING))
 
 // Special exception-unwinding records are put at the end of executable
 // page on Windows for 64-bit applications.
@@ -144,6 +154,6 @@
   RtlDeleteGrowableFunctionTable(record->dynamic_table);
 }
 
-#endif  // defined(DART_HOST_OS_WINDOWS)
+#endif  // defined(DART_HOST_OS_WINDOWS) ...
 
 }  // namespace dart
diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn
index fb7179c..cd014e3 100644
--- a/sdk/BUILD.gn
+++ b/sdk/BUILD.gn
@@ -855,6 +855,16 @@
       public_deps += [ ":copy_wasm_opt" ]
     }
   }
+
+  if (dart_target_arch != "ia32" && dart_target_arch != "x86" &&
+      dart_target_arch != "arm") {
+    # Do not include gen_snapshot binaries for cross-compilation into
+    # SDK, but add them as a dependency, so that they are built.
+    public_deps += [
+      "../runtime/bin:gen_snapshot_product_linux_arm64",
+      "../runtime/bin:gen_snapshot_product_linux_x64",
+    ]
+  }
 }
 
 # Build a SDK with less stuff. It excludes dart2js, ddc, and web libraries.