[build] Support llvm-ifs

This change adds support for llvm-ifs tool in preparation for migrating
from llvm-elfabi to llvm-ifs.

Bug: 79232
Change-Id: I83ea801e5fb0a98fd53f8c1428357f905b27cf77
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/546548
Commit-Queue: Haowei Wu <haowei@google.com>
Reviewed-by: Roland McGrath <mcgrathr@google.com>
diff --git a/build/config/clang/clang.gni b/build/config/clang/clang.gni
index 7ffc35c..5075e28 100644
--- a/build/config/clang/clang.gni
+++ b/build/config/clang/clang.gni
@@ -46,3 +46,36 @@
                                 "trim string",
                                 [])
 }
+
+# TODO(fxbug.dev/79232): Remove this after migration completes.
+# This is a string identifying the particular toolchain version in use.  Its
+# only purpose is to be unique enough that it changes when switching to a new
+# toolchain, so that recompilations with the new compiler can be triggered.
+#
+# When using the prebuilt, the CIPD instance ID of the prebuilt is perfect.
+# When not using the prebuilt, there isn't a quick way to extract the compiler
+# version.  But at least changing the setting to a different compiler will
+# change the string.
+clang_version_string = ""
+if (clang_prefix == default_clang_prefix) {
+  _prebuilt_clang_dir = "//prebuilt/third_party/clang/$host_platform"
+  clang_tool_dir = "${_prebuilt_clang_dir}/bin"
+  _prebuilt_cipd_version =
+      read_file("$_prebuilt_clang_dir/.versions/clang.cipd_version", "json")
+  clang_version_string = _prebuilt_cipd_version.instance_id
+}
+
+clang_has_llvm_ifs = false
+if (clang_version_string != "aUPvom0rfD2jLssugLimJkywf60eqgg9CG0Vk6-ASuUC" &&
+    clang_version_string != "duSz1BCClOVUA8LxhlJaouzmvGqe9rU61OhnAE_UG5oC" &&
+    clang_version_string != "rm-r5Uy50UmkWHJNZYzaS8cy_Q5S53cCwGw4BplIgecC" &&
+    clang_version_string != "d9nnr4pVXG3B0JL8TTt0ZjWSs4xet-6Yc8438AXPWIUC" &&
+    clang_version_string != "qrYJhq3WHgPd8qyip_1FhuyfoX5w0PKayUDZsbII1sAC" &&
+    clang_version_string != "AA9aCrWNrXs7g4G7wq4Cyv1ICLD9eHJ65008O3q-tNgC" &&
+    clang_version_string != "Lwq5cThR2BvFSjddIVmzF2-LHzOr-ZXdeXf4bw5tyigC" &&
+    clang_version_string != "eDzL2utDIub9b8u_zmphLvcLIvA5H4IH31T4VG0f9kQC" &&
+    clang_version_string != "ON1yKdqDlcTmDj9XpJrI0NBCmVUDNS8iG65NoucTyYoC" &&
+    clang_version_string != "GUxdhOlaKw1Xbg8SVnYiK3kDkTOQYAr-xInC_WKtsNUC" &&
+    clang_version_string != "B05Qdyd1KtFQSprZP0S3beRMzTeiKW8l12GssdHShncC") {
+  clang_has_llvm_ifs = true
+}
diff --git a/build/toolchain/clang_toolchain.gni b/build/toolchain/clang_toolchain.gni
index f0dd423..63d773e 100644
--- a/build/toolchain/clang_toolchain.gni
+++ b/build/toolchain/clang_toolchain.gni
@@ -535,9 +535,24 @@
       outfile = "{{output_dir}}/$outname"
       rspfile = "$outfile.rsp"
 
+      # TODO(fxbug.dev/79232): Remove elfabi after migration completes.
+      use_llvm_ifs = false
+      use_llvm_elfabi = false
+      if (clang_has_llvm_ifs) {
+        use_llvm_ifs =
+            invoker.toolchain_os != "mac" && invoker.toolchain_os != "win"
+      } else {
+        use_llvm_elfabi =
+            invoker.toolchain_os != "mac" && invoker.toolchain_os != "win"
+      }
+
+      # TODO(fxbug.dev/79232): Re-enable this after migration completes.
       # All other OS possibilities are ELF.
-      use_llvm_elfabi =
-          invoker.toolchain_os != "mac" && invoker.toolchain_os != "win"
+      # use_llvm_ifs =
+      #    invoker.toolchain_os != "mac" && invoker.toolchain_os != "win"
+      if (use_llvm_ifs) {
+        llvm_ifs = "${prefix}/llvm-ifs"
+      }
       if (use_llvm_elfabi) {
         llvm_elfabi = "${prefix}/llvm-elfabi"
       }
@@ -583,6 +598,21 @@
         outputs += [ "$outfile.gsym" ]
         command += " && $gsymutil --convert=\"$unstripped_outfile\" --out-file=\"$outfile.gsym\""
       }
+      if (use_llvm_ifs) {
+        # The Ninja dependency for linking in the shared library will be
+        # the .ifs file (depend_output), though the actual linking *input*
+        # will be the original .so file (link_output).  Ninja will restat
+        # the output files after running the commands.  llvm-ifs will
+        # not touch the .ifs file if its contents haven't changed.  Hence
+        # Ninja will only re-run any linking commands depending on this
+        # shared library if the .ifs file has actually changed, indicating
+        # that the linking ABI has actually changed.
+        restat = true
+        depend_output = "${outfile}.ifs"
+        link_output = unstripped_outfile
+        outputs += [ depend_output ]
+        command += " && $llvm_ifs --output-format=IFS --write-if-changed --output=$depend_output $link_output"
+      }
       if (use_llvm_elfabi) {
         # The Ninja dependency for linking in the shared library will be
         # the .tbe file (depend_output), though the actual linking *input*
diff --git a/build/toolchain/ifs_shared_library.gni b/build/toolchain/ifs_shared_library.gni
new file mode 100644
index 0000000..a6f6217
--- /dev/null
+++ b/build/toolchain/ifs_shared_library.gni
@@ -0,0 +1,92 @@
+# Copyright 2021 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/toolchain/zircon/clang.gni")
+
+_llvm_ifs = "$clang_tool_dir/llvm-ifs"
+
+# Define a linkable shared library built from a text ABI (.ifs) specification.
+#
+# This uses a `.ifs` file kept in the source tree to create a linkable ELF
+# shared library stub file.  This can be used at link time to build executable
+# and (non-stub) shared library binaries without reference to the actual
+# runtime shared library.  The real (non-stub) shared library to be used at
+# runtime must be compiled separately and included at runtime as necessary.
+# This target only takes care of satisfying any link-time dependencies.
+# Its label is used in `deps` like a shared_library() target.
+#
+# This defines two targets.  The main target acts like shared_library() for
+# linking purposes, but is not a GN target with outputs.  An additional target
+# "$target_name.stub" is defined whose outputs include the linking stub ELF
+# file itself, e.g. for use as input to a copy() target or the like.
+#
+# Parameters
+#
+#   * abi
+#     - Required: Source path to a `.ifs` file defining the library's ELF ABI.
+#     - Type: file
+#
+#   * output_name
+#     - Optional: The plain name of the linkable library file to write,
+#     without the `lib` prefix or the `.so` extension.
+#     - Type: string
+#     - Default: $target_name
+#
+#   * public, public_configs, public_deps
+#     - Optional: As for shared_library().
+#
+template("ifs_shared_library") {
+  main_target = target_name
+  stub_target = "$target_name.stub"
+
+  if (defined(invoker.output_name)) {
+    output_name = invoker.output_name
+  } else {
+    output_name = target_name
+  }
+
+  stub_file = "$root_out_dir/lib$output_name.so"
+
+  source_set(main_target) {
+    forward_variables_from(invoker,
+                           [
+                             "visibility",
+                             "public",
+                             "public_configs",
+                             "public_deps",
+                             "testonly",
+                           ])
+    libs = [ stub_file ]
+    deps = [ ":$stub_target" ]
+  }
+
+  action(stub_target) {
+    forward_variables_from(invoker,
+                           [
+                             "visibility",
+                             "testonly",
+                           ])
+    if (defined(visibility)) {
+      visibility += [ ":$main_target" ]
+    }
+
+    # Output timestamps are not freshened if contents do not change.
+    all_outputs_fresh = false
+
+    script = _llvm_ifs
+    sources = [ invoker.abi ]
+    outputs = [ stub_file ]
+    rebased_stub_file = rebase_path(stub_file, root_build_dir)
+    args = [
+      "--input-format=IFS",
+      "--output-format=ELF",
+      "--arch=${clang_cpu}",
+      "--endianness=little",
+      "--bitwidth=64",
+      "--write-if-changed",
+      "--output=${rebased_stub_file}",
+      rebase_path(invoker.abi, root_build_dir),
+    ]
+  }
+}
diff --git a/build/toolchain/zircon/clang.gni b/build/toolchain/zircon/clang.gni
index 144456f..abfaed1 100644
--- a/build/toolchain/zircon/clang.gni
+++ b/build/toolchain/zircon/clang.gni
@@ -32,6 +32,22 @@
   }
 }
 
+# TODO(fxbug.dev/79232): Remove this after migration completes.
+clang_has_llvm_ifs = false
+if (clang_version_string != "aUPvom0rfD2jLssugLimJkywf60eqgg9CG0Vk6-ASuUC" &&
+    clang_version_string != "duSz1BCClOVUA8LxhlJaouzmvGqe9rU61OhnAE_UG5oC" &&
+    clang_version_string != "rm-r5Uy50UmkWHJNZYzaS8cy_Q5S53cCwGw4BplIgecC" &&
+    clang_version_string != "d9nnr4pVXG3B0JL8TTt0ZjWSs4xet-6Yc8438AXPWIUC" &&
+    clang_version_string != "qrYJhq3WHgPd8qyip_1FhuyfoX5w0PKayUDZsbII1sAC" &&
+    clang_version_string != "AA9aCrWNrXs7g4G7wq4Cyv1ICLD9eHJ65008O3q-tNgC" &&
+    clang_version_string != "Lwq5cThR2BvFSjddIVmzF2-LHzOr-ZXdeXf4bw5tyigC" &&
+    clang_version_string != "eDzL2utDIub9b8u_zmphLvcLIvA5H4IH31T4VG0f9kQC" &&
+    clang_version_string != "ON1yKdqDlcTmDj9XpJrI0NBCmVUDNS8iG65NoucTyYoC" &&
+    clang_version_string != "GUxdhOlaKw1Xbg8SVnYiK3kDkTOQYAr-xInC_WKtsNUC" &&
+    clang_version_string != "B05Qdyd1KtFQSprZP0S3beRMzTeiKW8l12GssdHShncC") {
+  clang_has_llvm_ifs = true
+}
+
 _clang_lib_dir = "$clang_tool_dir/../lib"
 
 # Human-readable identifier for the toolchain version.
diff --git a/build/toolchain/zircon/zircon_toolchain.gni b/build/toolchain/zircon/zircon_toolchain.gni
index 12ad849..7b3021d 100644
--- a/build/toolchain/zircon/zircon_toolchain.gni
+++ b/build/toolchain/zircon/zircon_toolchain.gni
@@ -558,9 +558,20 @@
       assert(invoker.os == "fuchsia",
              "shared library creation not supported for ${invoker.os}")
 
-      use_llvm_elfabi = is_elf
-      if (use_llvm_elfabi) {
-        llvm_elfabi = rebase_path("$clang_tool_dir/llvm-elfabi", root_build_dir)
+      # TODO(fxbug.dev/79232): Remove elfabi after migration completes.
+      use_llvm_ifs = false
+      use_llvm_elfabi = false
+      if (clang_has_llvm_ifs) {
+        use_llvm_ifs = is_elf
+        if (use_llvm_ifs) {
+          llvm_ifs = rebase_path("$clang_tool_dir/llvm-ifs", root_build_dir)
+        }
+      } else {
+        use_llvm_elfabi = is_elf
+        if (use_llvm_elfabi) {
+          llvm_elfabi =
+              rebase_path("$clang_tool_dir/llvm-elfabi", root_build_dir)
+        }
       }
 
       solink_common = {
@@ -594,6 +605,20 @@
 
         command = "$ld $switches $link_inputs$strip_command"
 
+        if (use_llvm_ifs) {
+          # The Ninja dependency for linking in the shared library will be
+          # the .ifs file (depend_output), though the actual linking *input*
+          # will be the original .so file (link_output).  Ninja will restat
+          # the output files after running the commands.  llvm-ifs will
+          # not touch the .ifs file if its contents haven't changed.  Hence
+          # Ninja will only re-run any linking commands depending on this
+          # shared library if the .ifs file has actually changed, indicating
+          # that the linking ABI has actually changed.
+          restat = true
+          depend_output = "${outfile}.ifs"
+          outputs += [ depend_output ]
+          command += " && $llvm_ifs --output-format=IFS --write-if-changed --output=$depend_output $link_output"
+        }
         if (use_llvm_elfabi) {
           # The Ninja dependency for linking in the shared library will be
           # the .tbe file (depend_output), though the actual linking *input*
diff --git a/src/zircon/lib/zircon/BUILD.gn b/src/zircon/lib/zircon/BUILD.gn
index 9a5c046..73ea044 100644
--- a/src/zircon/lib/zircon/BUILD.gn
+++ b/src/zircon/lib/zircon/BUILD.gn
@@ -4,19 +4,29 @@
 
 import("//build/sdk/sdk_noop_atom.gni")
 import("//build/toolchain/elfabi_shared_library.gni")
+import("//build/toolchain/ifs_shared_library.gni")
 import("//build/toolchain/toolchain_environment.gni")
+import("//build/unification/global_variables.gni")
 
 # The vDSO ABI is provided at link time by a stub.  The actual runtime image
 # comes from the kernel and is not part of the build or SDK for userland at
 # all.  The kernel's internal build of the vDSO verifies that the ABI matches.
 if (is_fuchsia && toolchain_base_environment != "kernel") {
-  elfabi_shared_library("zircon") {
-    # TODO(mcgrathr): The ABIs are identical across machines, but the .tbe file
-    # insists on embedding "Arch:".  llvm-elfabi should get features to allow
-    # emitting a file without arch tag and to specify --target= to set the
-    # arch/class/data of an ELF stub when the .tbe file doesn't say.
-    abi = "zircon-$current_cpu.tbe"
-    public_deps = [ ":headers" ]
+  # TODO(fxbug.dev/79232, haowei): Remove elfabi once migration completes.
+  if (clang_has_llvm_ifs) {
+    ifs_shared_library("zircon") {
+      abi = "zircon.ifs"
+      public_deps = [ ":headers" ]
+    }
+  } else {
+    elfabi_shared_library("zircon") {
+      # TODO(mcgrathr): The ABIs are identical across machines, but the .tbe file
+      # insists on embedding "Arch:".  llvm-ifs should get features to allow
+      # emitting a file without arch tag and to specify --target= to set the
+      # arch/class/data of an ELF stub when the .tbe file doesn't say.
+      abi = "zircon-$current_cpu.tbe"
+      public_deps = [ ":headers" ]
+    }
   }
 } else {
   # References from other environments only get the headers.  This
diff --git a/src/zircon/lib/zircon/zircon.ifs b/src/zircon/lib/zircon/zircon.ifs
new file mode 100644
index 0000000..6d073e0
--- /dev/null
+++ b/src/zircon/lib/zircon/zircon.ifs
@@ -0,0 +1,371 @@
+--- !ifs-v1
+IfsVersion:      3.0
+SoName:          libzircon.so
+Symbols:
+  - { Name: _zx_bti_create, Type: Func }
+  - { Name: _zx_bti_pin, Type: Func }
+  - { Name: _zx_bti_release_quarantine, Type: Func }
+  - { Name: _zx_cache_flush, Type: Func }
+  - { Name: _zx_channel_call, Type: Func }
+  - { Name: _zx_channel_call_etc, Type: Func }
+  - { Name: _zx_channel_create, Type: Func }
+  - { Name: _zx_channel_read, Type: Func }
+  - { Name: _zx_channel_read_etc, Type: Func }
+  - { Name: _zx_channel_write, Type: Func }
+  - { Name: _zx_channel_write_etc, Type: Func }
+  - { Name: _zx_clock_create, Type: Func }
+  - { Name: _zx_clock_get_details, Type: Func }
+  - { Name: _zx_clock_get_monotonic, Type: Func }
+  - { Name: _zx_clock_read, Type: Func }
+  - { Name: _zx_clock_update, Type: Func }
+  - { Name: _zx_cprng_add_entropy, Type: Func }
+  - { Name: _zx_cprng_draw, Type: Func }
+  - { Name: _zx_deadline_after, Type: Func }
+  - { Name: _zx_debug_read, Type: Func }
+  - { Name: _zx_debug_send_command, Type: Func }
+  - { Name: _zx_debug_write, Type: Func }
+  - { Name: _zx_debuglog_create, Type: Func }
+  - { Name: _zx_debuglog_read, Type: Func }
+  - { Name: _zx_debuglog_write, Type: Func }
+  - { Name: _zx_event_create, Type: Func }
+  - { Name: _zx_eventpair_create, Type: Func }
+  - { Name: _zx_exception_get_process, Type: Func }
+  - { Name: _zx_exception_get_string, Type: Func }
+  - { Name: _zx_exception_get_thread, Type: Func }
+  - { Name: _zx_fifo_create, Type: Func }
+  - { Name: _zx_fifo_read, Type: Func }
+  - { Name: _zx_fifo_write, Type: Func }
+  - { Name: _zx_framebuffer_get_info, Type: Func }
+  - { Name: _zx_framebuffer_set_range, Type: Func }
+  - { Name: _zx_futex_get_owner, Type: Func }
+  - { Name: _zx_futex_requeue, Type: Func }
+  - { Name: _zx_futex_requeue_single_owner, Type: Func }
+  - { Name: _zx_futex_wait, Type: Func }
+  - { Name: _zx_futex_wake, Type: Func }
+  - { Name: _zx_futex_wake_handle_close_thread_exit, Type: Func }
+  - { Name: _zx_futex_wake_single_owner, Type: Func }
+  - { Name: _zx_guest_create, Type: Func }
+  - { Name: _zx_guest_set_trap, Type: Func }
+  - { Name: _zx_handle_close, Type: Func }
+  - { Name: _zx_handle_close_many, Type: Func }
+  - { Name: _zx_handle_duplicate, Type: Func }
+  - { Name: _zx_handle_replace, Type: Func }
+  - { Name: _zx_interrupt_ack, Type: Func }
+  - { Name: _zx_interrupt_bind, Type: Func }
+  - { Name: _zx_interrupt_bind_vcpu, Type: Func }
+  - { Name: _zx_interrupt_create, Type: Func }
+  - { Name: _zx_interrupt_destroy, Type: Func }
+  - { Name: _zx_interrupt_trigger, Type: Func }
+  - { Name: _zx_interrupt_wait, Type: Func }
+  - { Name: _zx_iommu_create, Type: Func }
+  - { Name: _zx_ioports_release, Type: Func }
+  - { Name: _zx_ioports_request, Type: Func }
+  - { Name: _zx_job_create, Type: Func }
+  - { Name: _zx_job_set_critical, Type: Func }
+  - { Name: _zx_job_set_policy, Type: Func }
+  - { Name: _zx_ktrace_control, Type: Func }
+  - { Name: _zx_ktrace_read, Type: Func }
+  - { Name: _zx_ktrace_write, Type: Func }
+  - { Name: _zx_msi_allocate, Type: Func }
+  - { Name: _zx_msi_create, Type: Func }
+  - { Name: _zx_mtrace_control, Type: Func }
+  - { Name: _zx_nanosleep, Type: Func }
+  - { Name: _zx_object_get_child, Type: Func }
+  - { Name: _zx_object_get_info, Type: Func }
+  - { Name: _zx_object_get_property, Type: Func }
+  - { Name: _zx_object_set_profile, Type: Func }
+  - { Name: _zx_object_set_property, Type: Func }
+  - { Name: _zx_object_signal, Type: Func }
+  - { Name: _zx_object_signal_peer, Type: Func }
+  - { Name: _zx_object_wait_async, Type: Func }
+  - { Name: _zx_object_wait_many, Type: Func }
+  - { Name: _zx_object_wait_one, Type: Func }
+  - { Name: _zx_pager_create, Type: Func }
+  - { Name: _zx_pager_create_vmo, Type: Func }
+  - { Name: _zx_pager_detach_vmo, Type: Func }
+  - { Name: _zx_pager_op_range, Type: Func }
+  - { Name: _zx_pager_supply_pages, Type: Func }
+  - { Name: _zx_pc_firmware_tables, Type: Func }
+  - { Name: _zx_pci_add_subtract_io_range, Type: Func }
+  - { Name: _zx_pci_cfg_pio_rw, Type: Func }
+  - { Name: _zx_pci_config_read, Type: Func }
+  - { Name: _zx_pci_config_write, Type: Func }
+  - { Name: _zx_pci_enable_bus_master, Type: Func }
+  - { Name: _zx_pci_get_bar, Type: Func }
+  - { Name: _zx_pci_get_nth_device, Type: Func }
+  - { Name: _zx_pci_init, Type: Func }
+  - { Name: _zx_pci_map_interrupt, Type: Func }
+  - { Name: _zx_pci_query_irq_mode, Type: Func }
+  - { Name: _zx_pci_reset_device, Type: Func }
+  - { Name: _zx_pci_set_irq_mode, Type: Func }
+  - { Name: _zx_pmt_unpin, Type: Func }
+  - { Name: _zx_port_cancel, Type: Func }
+  - { Name: _zx_port_create, Type: Func }
+  - { Name: _zx_port_queue, Type: Func }
+  - { Name: _zx_port_wait, Type: Func }
+  - { Name: _zx_process_create, Type: Func }
+  - { Name: _zx_process_exit, Type: Func }
+  - { Name: _zx_process_read_memory, Type: Func }
+  - { Name: _zx_process_start, Type: Func }
+  - { Name: _zx_process_write_memory, Type: Func }
+  - { Name: _zx_profile_create, Type: Func }
+  - { Name: _zx_resource_create, Type: Func }
+  - { Name: _zx_smc_call, Type: Func }
+  - { Name: _zx_socket_create, Type: Func }
+  - { Name: _zx_socket_read, Type: Func }
+  - { Name: _zx_socket_shutdown, Type: Func }
+  - { Name: _zx_socket_write, Type: Func }
+  - { Name: _zx_status_get_string, Type: Func }
+  - { Name: _zx_stream_create, Type: Func }
+  - { Name: _zx_stream_readv, Type: Func }
+  - { Name: _zx_stream_readv_at, Type: Func }
+  - { Name: _zx_stream_seek, Type: Func }
+  - { Name: _zx_stream_writev, Type: Func }
+  - { Name: _zx_stream_writev_at, Type: Func }
+  - { Name: _zx_syscall_test_0, Type: Func }
+  - { Name: _zx_syscall_test_1, Type: Func }
+  - { Name: _zx_syscall_test_2, Type: Func }
+  - { Name: _zx_syscall_test_3, Type: Func }
+  - { Name: _zx_syscall_test_4, Type: Func }
+  - { Name: _zx_syscall_test_5, Type: Func }
+  - { Name: _zx_syscall_test_6, Type: Func }
+  - { Name: _zx_syscall_test_7, Type: Func }
+  - { Name: _zx_syscall_test_8, Type: Func }
+  - { Name: _zx_syscall_test_handle_create, Type: Func }
+  - { Name: _zx_syscall_test_widening_signed_narrow, Type: Func }
+  - { Name: _zx_syscall_test_widening_signed_wide, Type: Func }
+  - { Name: _zx_syscall_test_widening_unsigned_narrow, Type: Func }
+  - { Name: _zx_syscall_test_widening_unsigned_wide, Type: Func }
+  - { Name: _zx_syscall_test_wrapper, Type: Func }
+  - { Name: _zx_system_get_dcache_line_size, Type: Func }
+  - { Name: _zx_system_get_event, Type: Func }
+  - { Name: _zx_system_get_features, Type: Func }
+  - { Name: _zx_system_get_num_cpus, Type: Func }
+  - { Name: _zx_system_get_page_size, Type: Func }
+  - { Name: _zx_system_get_physmem, Type: Func }
+  - { Name: _zx_system_get_version, Type: Func }
+  - { Name: _zx_system_get_version_string, Type: Func }
+  - { Name: _zx_system_mexec, Type: Func }
+  - { Name: _zx_system_mexec_payload_get, Type: Func }
+  - { Name: _zx_system_powerctl, Type: Func }
+  - { Name: _zx_task_create_exception_channel, Type: Func }
+  - { Name: _zx_task_kill, Type: Func }
+  - { Name: _zx_task_suspend, Type: Func }
+  - { Name: _zx_task_suspend_token, Type: Func }
+  - { Name: _zx_thread_create, Type: Func }
+  - { Name: _zx_thread_exit, Type: Func }
+  - { Name: _zx_thread_read_state, Type: Func }
+  - { Name: _zx_thread_start, Type: Func }
+  - { Name: _zx_thread_write_state, Type: Func }
+  - { Name: _zx_ticks_get, Type: Func }
+  - { Name: _zx_ticks_per_second, Type: Func }
+  - { Name: _zx_timer_cancel, Type: Func }
+  - { Name: _zx_timer_create, Type: Func }
+  - { Name: _zx_timer_set, Type: Func }
+  - { Name: _zx_vcpu_create, Type: Func }
+  - { Name: _zx_vcpu_interrupt, Type: Func }
+  - { Name: _zx_vcpu_read_state, Type: Func }
+  - { Name: _zx_vcpu_resume, Type: Func }
+  - { Name: _zx_vcpu_write_state, Type: Func }
+  - { Name: _zx_vmar_allocate, Type: Func }
+  - { Name: _zx_vmar_destroy, Type: Func }
+  - { Name: _zx_vmar_map, Type: Func }
+  - { Name: _zx_vmar_op_range, Type: Func }
+  - { Name: _zx_vmar_protect, Type: Func }
+  - { Name: _zx_vmar_unmap, Type: Func }
+  - { Name: _zx_vmar_unmap_handle_close_thread_exit, Type: Func }
+  - { Name: _zx_vmo_create, Type: Func }
+  - { Name: _zx_vmo_create_child, Type: Func }
+  - { Name: _zx_vmo_create_contiguous, Type: Func }
+  - { Name: _zx_vmo_create_physical, Type: Func }
+  - { Name: _zx_vmo_get_size, Type: Func }
+  - { Name: _zx_vmo_op_range, Type: Func }
+  - { Name: _zx_vmo_read, Type: Func }
+  - { Name: _zx_vmo_replace_as_executable, Type: Func }
+  - { Name: _zx_vmo_set_cache_policy, Type: Func }
+  - { Name: _zx_vmo_set_size, Type: Func }
+  - { Name: _zx_vmo_write, Type: Func }
+  - { Name: zx_bti_create, Type: Func, Weak: true }
+  - { Name: zx_bti_pin, Type: Func, Weak: true }
+  - { Name: zx_bti_release_quarantine, Type: Func, Weak: true }
+  - { Name: zx_cache_flush, Type: Func, Weak: true }
+  - { Name: zx_channel_call, Type: Func, Weak: true }
+  - { Name: zx_channel_call_etc, Type: Func, Weak: true }
+  - { Name: zx_channel_create, Type: Func, Weak: true }
+  - { Name: zx_channel_read, Type: Func, Weak: true }
+  - { Name: zx_channel_read_etc, Type: Func, Weak: true }
+  - { Name: zx_channel_write, Type: Func, Weak: true }
+  - { Name: zx_channel_write_etc, Type: Func, Weak: true }
+  - { Name: zx_clock_create, Type: Func, Weak: true }
+  - { Name: zx_clock_get_details, Type: Func, Weak: true }
+  - { Name: zx_clock_get_monotonic, Type: Func, Weak: true }
+  - { Name: zx_clock_read, Type: Func, Weak: true }
+  - { Name: zx_clock_update, Type: Func, Weak: true }
+  - { Name: zx_cprng_add_entropy, Type: Func, Weak: true }
+  - { Name: zx_cprng_draw, Type: Func, Weak: true }
+  - { Name: zx_deadline_after, Type: Func, Weak: true }
+  - { Name: zx_debug_read, Type: Func, Weak: true }
+  - { Name: zx_debug_send_command, Type: Func, Weak: true }
+  - { Name: zx_debug_write, Type: Func, Weak: true }
+  - { Name: zx_debuglog_create, Type: Func, Weak: true }
+  - { Name: zx_debuglog_read, Type: Func, Weak: true }
+  - { Name: zx_debuglog_write, Type: Func, Weak: true }
+  - { Name: zx_event_create, Type: Func, Weak: true }
+  - { Name: zx_eventpair_create, Type: Func, Weak: true }
+  - { Name: zx_exception_get_process, Type: Func, Weak: true }
+  - { Name: zx_exception_get_string, Type: Func, Weak: true }
+  - { Name: zx_exception_get_thread, Type: Func, Weak: true }
+  - { Name: zx_fifo_create, Type: Func, Weak: true }
+  - { Name: zx_fifo_read, Type: Func, Weak: true }
+  - { Name: zx_fifo_write, Type: Func, Weak: true }
+  - { Name: zx_framebuffer_get_info, Type: Func, Weak: true }
+  - { Name: zx_framebuffer_set_range, Type: Func, Weak: true }
+  - { Name: zx_futex_get_owner, Type: Func, Weak: true }
+  - { Name: zx_futex_requeue, Type: Func, Weak: true }
+  - { Name: zx_futex_requeue_single_owner, Type: Func, Weak: true }
+  - { Name: zx_futex_wait, Type: Func, Weak: true }
+  - { Name: zx_futex_wake, Type: Func, Weak: true }
+  - { Name: zx_futex_wake_handle_close_thread_exit, Type: Func, Weak: true }
+  - { Name: zx_futex_wake_single_owner, Type: Func, Weak: true }
+  - { Name: zx_guest_create, Type: Func, Weak: true }
+  - { Name: zx_guest_set_trap, Type: Func, Weak: true }
+  - { Name: zx_handle_close, Type: Func, Weak: true }
+  - { Name: zx_handle_close_many, Type: Func, Weak: true }
+  - { Name: zx_handle_duplicate, Type: Func, Weak: true }
+  - { Name: zx_handle_replace, Type: Func, Weak: true }
+  - { Name: zx_interrupt_ack, Type: Func, Weak: true }
+  - { Name: zx_interrupt_bind, Type: Func, Weak: true }
+  - { Name: zx_interrupt_bind_vcpu, Type: Func, Weak: true }
+  - { Name: zx_interrupt_create, Type: Func, Weak: true }
+  - { Name: zx_interrupt_destroy, Type: Func, Weak: true }
+  - { Name: zx_interrupt_trigger, Type: Func, Weak: true }
+  - { Name: zx_interrupt_wait, Type: Func, Weak: true }
+  - { Name: zx_iommu_create, Type: Func, Weak: true }
+  - { Name: zx_ioports_release, Type: Func, Weak: true }
+  - { Name: zx_ioports_request, Type: Func, Weak: true }
+  - { Name: zx_job_create, Type: Func, Weak: true }
+  - { Name: zx_job_set_critical, Type: Func, Weak: true }
+  - { Name: zx_job_set_policy, Type: Func, Weak: true }
+  - { Name: zx_ktrace_control, Type: Func, Weak: true }
+  - { Name: zx_ktrace_read, Type: Func, Weak: true }
+  - { Name: zx_ktrace_write, Type: Func, Weak: true }
+  - { Name: zx_msi_allocate, Type: Func, Weak: true }
+  - { Name: zx_msi_create, Type: Func, Weak: true }
+  - { Name: zx_mtrace_control, Type: Func, Weak: true }
+  - { Name: zx_nanosleep, Type: Func, Weak: true }
+  - { Name: zx_object_get_child, Type: Func, Weak: true }
+  - { Name: zx_object_get_info, Type: Func, Weak: true }
+  - { Name: zx_object_get_property, Type: Func, Weak: true }
+  - { Name: zx_object_set_profile, Type: Func, Weak: true }
+  - { Name: zx_object_set_property, Type: Func, Weak: true }
+  - { Name: zx_object_signal, Type: Func, Weak: true }
+  - { Name: zx_object_signal_peer, Type: Func, Weak: true }
+  - { Name: zx_object_wait_async, Type: Func, Weak: true }
+  - { Name: zx_object_wait_many, Type: Func, Weak: true }
+  - { Name: zx_object_wait_one, Type: Func, Weak: true }
+  - { Name: zx_pager_create, Type: Func, Weak: true }
+  - { Name: zx_pager_create_vmo, Type: Func, Weak: true }
+  - { Name: zx_pager_detach_vmo, Type: Func, Weak: true }
+  - { Name: zx_pager_op_range, Type: Func, Weak: true }
+  - { Name: zx_pager_supply_pages, Type: Func, Weak: true }
+  - { Name: zx_pc_firmware_tables, Type: Func, Weak: true }
+  - { Name: zx_pci_add_subtract_io_range, Type: Func, Weak: true }
+  - { Name: zx_pci_cfg_pio_rw, Type: Func, Weak: true }
+  - { Name: zx_pci_config_read, Type: Func, Weak: true }
+  - { Name: zx_pci_config_write, Type: Func, Weak: true }
+  - { Name: zx_pci_enable_bus_master, Type: Func, Weak: true }
+  - { Name: zx_pci_get_bar, Type: Func, Weak: true }
+  - { Name: zx_pci_get_nth_device, Type: Func, Weak: true }
+  - { Name: zx_pci_init, Type: Func, Weak: true }
+  - { Name: zx_pci_map_interrupt, Type: Func, Weak: true }
+  - { Name: zx_pci_query_irq_mode, Type: Func, Weak: true }
+  - { Name: zx_pci_reset_device, Type: Func, Weak: true }
+  - { Name: zx_pci_set_irq_mode, Type: Func, Weak: true }
+  - { Name: zx_pmt_unpin, Type: Func, Weak: true }
+  - { Name: zx_port_cancel, Type: Func, Weak: true }
+  - { Name: zx_port_create, Type: Func, Weak: true }
+  - { Name: zx_port_queue, Type: Func, Weak: true }
+  - { Name: zx_port_wait, Type: Func, Weak: true }
+  - { Name: zx_process_create, Type: Func, Weak: true }
+  - { Name: zx_process_exit, Type: Func, Weak: true }
+  - { Name: zx_process_read_memory, Type: Func, Weak: true }
+  - { Name: zx_process_start, Type: Func, Weak: true }
+  - { Name: zx_process_write_memory, Type: Func, Weak: true }
+  - { Name: zx_profile_create, Type: Func, Weak: true }
+  - { Name: zx_resource_create, Type: Func, Weak: true }
+  - { Name: zx_smc_call, Type: Func, Weak: true }
+  - { Name: zx_socket_create, Type: Func, Weak: true }
+  - { Name: zx_socket_read, Type: Func, Weak: true }
+  - { Name: zx_socket_shutdown, Type: Func, Weak: true }
+  - { Name: zx_socket_write, Type: Func, Weak: true }
+  - { Name: zx_status_get_string, Type: Func, Weak: true }
+  - { Name: zx_stream_create, Type: Func, Weak: true }
+  - { Name: zx_stream_readv, Type: Func, Weak: true }
+  - { Name: zx_stream_readv_at, Type: Func, Weak: true }
+  - { Name: zx_stream_seek, Type: Func, Weak: true }
+  - { Name: zx_stream_writev, Type: Func, Weak: true }
+  - { Name: zx_stream_writev_at, Type: Func, Weak: true }
+  - { Name: zx_syscall_test_0, Type: Func, Weak: true }
+  - { Name: zx_syscall_test_1, Type: Func, Weak: true }
+  - { Name: zx_syscall_test_2, Type: Func, Weak: true }
+  - { Name: zx_syscall_test_3, Type: Func, Weak: true }
+  - { Name: zx_syscall_test_4, Type: Func, Weak: true }
+  - { Name: zx_syscall_test_5, Type: Func, Weak: true }
+  - { Name: zx_syscall_test_6, Type: Func, Weak: true }
+  - { Name: zx_syscall_test_7, Type: Func, Weak: true }
+  - { Name: zx_syscall_test_8, Type: Func, Weak: true }
+  - { Name: zx_syscall_test_handle_create, Type: Func, Weak: true }
+  - { Name: zx_syscall_test_widening_signed_narrow, Type: Func, Weak: true }
+  - { Name: zx_syscall_test_widening_signed_wide, Type: Func, Weak: true }
+  - { Name: zx_syscall_test_widening_unsigned_narrow, Type: Func, Weak: true }
+  - { Name: zx_syscall_test_widening_unsigned_wide, Type: Func, Weak: true }
+  - { Name: zx_syscall_test_wrapper, Type: Func, Weak: true }
+  - { Name: zx_system_get_dcache_line_size, Type: Func, Weak: true }
+  - { Name: zx_system_get_event, Type: Func, Weak: true }
+  - { Name: zx_system_get_features, Type: Func, Weak: true }
+  - { Name: zx_system_get_num_cpus, Type: Func, Weak: true }
+  - { Name: zx_system_get_page_size, Type: Func, Weak: true }
+  - { Name: zx_system_get_physmem, Type: Func, Weak: true }
+  - { Name: zx_system_get_version, Type: Func, Weak: true }
+  - { Name: zx_system_get_version_string, Type: Func, Weak: true }
+  - { Name: zx_system_mexec, Type: Func, Weak: true }
+  - { Name: zx_system_mexec_payload_get, Type: Func, Weak: true }
+  - { Name: zx_system_powerctl, Type: Func, Weak: true }
+  - { Name: zx_task_create_exception_channel, Type: Func, Weak: true }
+  - { Name: zx_task_kill, Type: Func, Weak: true }
+  - { Name: zx_task_suspend, Type: Func, Weak: true }
+  - { Name: zx_task_suspend_token, Type: Func, Weak: true }
+  - { Name: zx_thread_create, Type: Func, Weak: true }
+  - { Name: zx_thread_exit, Type: Func, Weak: true }
+  - { Name: zx_thread_read_state, Type: Func, Weak: true }
+  - { Name: zx_thread_start, Type: Func, Weak: true }
+  - { Name: zx_thread_write_state, Type: Func, Weak: true }
+  - { Name: zx_ticks_get, Type: Func, Weak: true }
+  - { Name: zx_ticks_per_second, Type: Func, Weak: true }
+  - { Name: zx_timer_cancel, Type: Func, Weak: true }
+  - { Name: zx_timer_create, Type: Func, Weak: true }
+  - { Name: zx_timer_set, Type: Func, Weak: true }
+  - { Name: zx_vcpu_create, Type: Func, Weak: true }
+  - { Name: zx_vcpu_interrupt, Type: Func, Weak: true }
+  - { Name: zx_vcpu_read_state, Type: Func, Weak: true }
+  - { Name: zx_vcpu_resume, Type: Func, Weak: true }
+  - { Name: zx_vcpu_write_state, Type: Func, Weak: true }
+  - { Name: zx_vmar_allocate, Type: Func, Weak: true }
+  - { Name: zx_vmar_destroy, Type: Func, Weak: true }
+  - { Name: zx_vmar_map, Type: Func, Weak: true }
+  - { Name: zx_vmar_op_range, Type: Func, Weak: true }
+  - { Name: zx_vmar_protect, Type: Func, Weak: true }
+  - { Name: zx_vmar_unmap, Type: Func, Weak: true }
+  - { Name: zx_vmar_unmap_handle_close_thread_exit, Type: Func, Weak: true }
+  - { Name: zx_vmo_create, Type: Func, Weak: true }
+  - { Name: zx_vmo_create_child, Type: Func, Weak: true }
+  - { Name: zx_vmo_create_contiguous, Type: Func, Weak: true }
+  - { Name: zx_vmo_create_physical, Type: Func, Weak: true }
+  - { Name: zx_vmo_get_size, Type: Func, Weak: true }
+  - { Name: zx_vmo_op_range, Type: Func, Weak: true }
+  - { Name: zx_vmo_read, Type: Func, Weak: true }
+  - { Name: zx_vmo_replace_as_executable, Type: Func, Weak: true }
+  - { Name: zx_vmo_set_cache_policy, Type: Func, Weak: true }
+  - { Name: zx_vmo_set_size, Type: Func, Weak: true }
+  - { Name: zx_vmo_write, Type: Func, Weak: true }
+...
diff --git a/zircon/kernel/lib/userabi/vdso/BUILD.gn b/zircon/kernel/lib/userabi/vdso/BUILD.gn
index 952bf62..f161502 100644
--- a/zircon/kernel/lib/userabi/vdso/BUILD.gn
+++ b/zircon/kernel/lib/userabi/vdso/BUILD.gn
@@ -137,14 +137,48 @@
   if (is_kernel) {
     # Make sure the vDSO that goes into the kernel matches the ABI
     # that userland links against.
-    abi_tbe = "//src/zircon/lib/zircon/zircon-$current_cpu.tbe"
+    # TODO(fxbug.dev/79232): Remove elfabi after migration completes.
+    if (clang_has_llvm_ifs) {
+      abi_tbe = "//src/zircon/lib/zircon/zircon.ifs"
+    } else {
+      abi_tbe = "//src/zircon/lib/zircon/zircon-$current_cpu.tbe"
+    }
 
     vdso_out_dir = get_label_info(vdso_label, "root_out_dir")
-    vdso_tbe = "$vdso_out_dir/libzircon.so.tbe"
+    if (clang_has_llvm_ifs) {
+      vdso_tbe_unstripped = "$vdso_out_dir/libzircon.so.ifs"
+      vdso_tbe = "$vdso_out_dir/libzircon_stripped.so.ifs"
+      action("strip-abi-target") {
+        visibility = [ ":*" ]
+
+        deps = [
+          "//src/zircon/lib/zircon",
+          vdso_label,
+        ]
+
+        sources = [ vdso_tbe_unstripped ]
+        outputs = [ vdso_tbe ]
+
+        script = "$clang_tool_dir/llvm-ifs"
+        args = [
+          "--input-format=IFS",
+          "--output-format=IFS",
+          "--strip-ifs-target",
+          "--write-if-changed",
+          "--output=" + rebase_path(vdso_tbe, root_build_dir),
+          rebase_path(vdso_tbe_unstripped, root_build_dir),
+        ]
+      }
+    } else {
+      vdso_tbe = "$vdso_out_dir/libzircon.so.tbe"
+    }
 
     action("verify-abi") {
       visibility = [ ":*" ]
 
+      if (clang_has_llvm_ifs) {
+        vdso_label = ":strip-abi-target"
+      }
       deps = [
         "//src/zircon/lib/zircon",
         vdso_label,