Windows Arm
Windows Arm
b GET STARTED
Start here!
Code samples
Design and UI
e OVERVIEW
e OVERVIEW
Deployment overview
Deploy apps
Windows AI
e OVERVIEW
@WindowsDocs
OneDevMinute on YouTube
Platforms
Y ARCHITECTURE
.NET MAUI
Windows Forms
API Reference
i REFERENCE
Windows has traditionally run on machines that are powered by x86 / x64 processors,
but more recently, also runs on devices powered by Arm processors.
For the best performance, responsiveness, and battery life, users will want and need
Arm-native Windows apps, which means that developers will need to build or port Arm-
native Windows apps.
Virtual Machines
You can create and deploy Windows 11 Arm64 VMs with with Ampere Altra Arm–based
processors on Azure. Learn how in this Quickstart article.
Windows Dev Kit 2023 (code name “Project Volterra”) is the latest Arm device built to
support Windows developers, AI researchers, and developers looking to integrate AI
into their apps and experiences.
Arm64EC (“Emulation Compatible”) enables you to incrementally build new apps, or port
existing apps, to take advantage of native Arm performance where possible, while
utilizing existing x64 code & libraries until fully migrated. Learn more:
     Using Arm64EC to build apps for Arm devices
     Understanding Arm64EC ABI and assembly code
Windows offers a variety of tools and frameworks to support app development for Arm,
on Arm.
     The new Arm-native Visual Studio includes Visual C++, .NET & .NET Framework
     and Java and will enable developers to natively build and debug Arm apps on
     Arm-based devices. Learn more in the blog announcement.
     Visual Studio Code natively supports Arm and can be installed on Arm devices.
     The VS Code C++ extension also offers C++ IntelliSense and build support for
     developing Windows apps that run natively on Arm64 devices.
     .NET 6 already supports Arm, both for native Arm execution and x64 emulation. To
     develop .NET apps that run natively on Arm64 devices, we recommend installing
     the new Arm native Visual Studio 2022 17.4, and .NET 7 Arm64 SDK. Learn more
     about .NET 7 support for Arm and the performance improvements for Arm64 on
     the .NET Blog.
   .NET 6 Arm64 SDK: By default, if you dotnet run a .NET 6 app with the Arm64
   SDK, it will run as Arm64. The dotnet-runtimeinfo tool can be used to discover the
   environment that .NET is running on. See the .NET 6 blog announcement on
   Arm64 support to learn more.
7 Note
 We use the term Arm as a shorthand for PCs that run the desktop version of
 Windows on Arm64 (also commonly called AArch64) processors. We use the term
 Arm32 here as a shorthand for the 32-bit Arm architecture (commonly called Arm
 in other documentation). PCs powered by Arm provide great application
 compatibility and allow you to run your existing unmodified x86 win32 applications.
 Arm apps run natively without any emulation, while x86 and x64 apps run under
 emulation on Arm devices.
   Not all Visual Studio projects are configured to launch projects locally when you
   start debugging (F5) from an Arm device. You may need to configure Visual Studio
   for remote debugging, even though your app is running locally. For more
   information, see remote debugging.
   To find and install the recommended packages on Visual Studio, visit the Visual
   Studio downloads page .
      For the Remote Tools for Visual Studio 2022, scroll below the All downloads
      section and expand the Tools for Visual Studio 2022 drop-down menu. Remote
      Tools for Visual Studio 2022 will be listed there. Be sure to check the Arm64
      radio button, then Download.
      For the Microsoft Visual C++ Redistributable, scroll below the All downloads
      section and expand the Other tools and Frameworks drop-down menu.
      Microsoft Visual C++ Redistributable for Visual Studio 2022 will be listed
      there. Be sure to check the Arm64 radio button, then Download.
      If you are using an older version of Visual Studio, select the Older Downloads
      link at the bottom of the page to search for the downloads associated with your
      version of Visual Studio.
     When a user installs your app on an Arm device from the Microsoft Store,
     Windows 11 will automatically select the optimal version of your app that is
     available. If you submit x86, Arm32, and Arm64 versions of your app to the
     Microsoft Store, the operating system will automatically install the Arm64 version
     of your app. If you only submit x86 and Arm32 versions of your app, the operating
     system will install the Arm32 version. If you only submit the x86 version of your
     app, the operating system will install that version and run it under emulation.
     When given the choice of app architecture, choose the 32-bit x86 version to run
     the app's 32-bit version on a Windows on Arm PC. If an app's x64 Win32 version
     doesn't work, most apps will have an x86 version available.
Additional resources
     Satya Nadella's Build 2022 keynote announcing "Project Volterra"
     How x86 emulation works on Arm
     Troubleshooting x86 desktop apps
     Troubleshooting Arm UWP apps
     Program Compatibility Troubleshooter on Arm
     Building Arm64 Drivers with the WDK: Instructions for building an Arm64 driver.
     Debugging x86 apps on Arm | Guidance for debugging x86 apps on Arm.
     Video: Building Arm64 Win32 C++ Apps
     Windows 10 on Arm for developers (Microsoft Build 2018 video presentation)
     Blog: Original announcement of Windows 10 support for Arm development
     Report a bug
External resources
     Developer Resources for Windows on Snapdragon by Qualcomm
Developer.arm.com: Windows on Arm
Developer.arm.com: Port applications to Windows on Arm
Developer.arm.com: Building a Native Windows on Arm App with WinUI 3
Developer.arm.com: Building libraries for Windows on Arm
Add Arm support to your Windows app
Article • 12/19/2023
Arm-based devices are becoming increasingly popular due to their power-frugal nature,
longer battery life, and impressive processing power, in addition to the Windows on
Arm support for Neural Processing Units (NPUs) tuned for the increasingly popular AI
and Machine Learning workloads.
This guide will cover the steps for adding support to your Windows app(s) for devices
powered by Arm64 processors. Guidance will also cover ways to address any potential
issues or blockers (such as 3rd party dependencies or plug-ins) that may interfere with
creating an Arm64-based version of your app.
While the ability to emulate x64 and x86 on Arm devices is a great step forward, this
guide will help you to add Arm-native support, so that your app can take advantage of
native performance gains and the unique qualities of Arm64-powered devices,
including:
7 Note
  Progressive Web Apps (PWAs) will already execute with native Arm64 performance.
Prerequisites
If you are updating your app using an Arm-based device (native compiling - generating
the code for the same platform on which you're running), you can use:
     Visual Studio 2022 v17.4    or later. This is the first GA release of Visual Studio that
     natively supports building and debugging Arm64 apps on Arm-based processors.
     Both Visual Studio 2022 17.4 and and Microsoft Visual C++ (MSVC) native Arm64
     versions provide significantly better performance compared with previous
     emulated versions.
     (Optional) LLVM (Clang) v12+        or later. LLVM 12 adds official binary release
     hosted on Windows on Arm64, including a Clang compiler, LLD Linker, and
     compiler-rt runtime libraries.
If you are updating your Windows app to support Arm using an x64 or x86 Intel-based
device (cross compiling), you can use:
There are several factors to consider when choosing between cross compilation or
native compilation such as available hardware and simplicity of test execution.
7 Note
GCC, the GNU Compiler Collection support is targeted for the near future.
Once you've confirmed that your app has successfully been optimized for Arm devices:
   1. Open your solution (project code) in Visual Studio (see prerequisites for the
     supported versions).
   2. In the "Solution Platforms" drop-down menu on the Standard toolbar (or in the
     "Build" menu), select Configuration Manager...
   3. Open the "Active solution platform" drop-down menu and select <New...>.
   4. In the "Type or select the new platform" drop-down menu, select ARM64 and
     ensure that the "Copy settings from" value is set to x64 with the "Create new
     project platforms" checkbox enabled, then select OK.
Congratulations! You have started adding Arm support to your app. Next, check to see
whether your Arm64 Solution builds successfully.
If your Solution does not build successfully, you will need to resolve the issues that are
causing the build to fail. The most likely reason is that a dependency is not available for
ARM64, which is covered in Troubleshooting below.
(Optional): If you want to verify first-hand that your app binary is now built for Arm64,
you can open your project directory in PowerShell (right-click your app project in Visual
Studio Solution Explorer and select Open in Terminal). Change directories so that your
project's new bin\ARM64\Debug or Release directory is selected. Enter the command:
dumpbin /headers .\<appname>.exe (replacing <appname> with the name of your app).
Scroll up in your terminal's output results to find the FILE HEADER VALUES section and
confirm the first line is AA64 machine (ARM64) .
Once the binaries are built for your app to support Arm64, you’ll want to test them. That
will require having a device or a virtual machine running Windows on Arm.
If you are doing development on a Windows on Arm device, then you have an easy
setup with Visual Studio local debugging. If cross-compiling (using a device that is not
running on an Arm-processor), then you will want to use remote debugging on a
Windows on Arm device or a virtual machine to enable your development experience in
Visual Studio while running the Arm64 app on another device.
For help setting up a virtual machine (VM) running Windows on Arm to support CI and
testing, see Quickstart: Create a Windows on Arm virtual machine in the Azure portal.
     Read the Azure blog announcement of general availability for Azure Virtual
     Machines with Ampere Altra Arm-based processors         with the ability to run
     Arm64-based versions of Windows 11 Pro and Enterprise.
     Learn more about the Windows 11 on Arm Insider Preview (VHDX)           for creating a
     local Windows on Arm VM using Hyper-V and the Windows Insider VHDX. *Arm64
     VMs are only supported on devices that meet the prerequisites. Creating Arm64
     VMs is not supported on x64 hardware - you will need to host the VM in the cloud,
     see the quickstart link above.
Check out the video "Ask the Expert: Create Apps with Ampere-based Azure VMs".
Compiling the app is more convenient when done on the same machine as the tests, but
in many cases is not required. Instead, you can consider extending the existing build
infrastructure to produce a cross-compiled output for Arm64.
If your app is not already published in the Microsoft Store, you can follow the
instructions to create an app submission based on whether you want to submit an MSI
or EXE, MSIX package, PWA, or App add-on.
If you build your own installer, you should ensure it is able to install your new Arm64
version successfully. Most installer frameworks, such as WiX    , Squirrel   , InnoSetup   ,
InstallAware   , and others support Windows on Arm without issue.
If you offer your app's installer from a webpage, you can use User-Agent Client Hints to
detect when your customer is visiting from a Windows on Arm device and offer them
the updated Arm-native version of your app. Unlike the user-agent string, User-Agent
Client Hints allows you to differentiate customers on Arm from customers on x86
devices.
Troubleshooting
Common issues that may interfere with or block you from adding an Arm64 version of
your existing x64 or x86 Windows app include:
     A dependency not compiled for ARM64 is blocking you from a successful build.
     Code is written for a specific architecture other than Arm64.
     Your app relies on a kernel driver.
     You're stuck and need assistance.
     For 3rd party dependencies, we recommend filing a request for the maintainer to
     rebuild with ARM64 support.
     The Linaro organization also works with businesses and open source communities
     to develop software on Arm-based technology. You can file a request with the
     Linaro Service Desk to help update package support      for any missing
     dependencies related to Windows on Arm.
     The last choice would be to remove and/or replace the dependency on your app
     project.
Code is written for a specific architecture other than
Arm64
     CPU specific assembly or inline intrinsic function calls will need to be modified to
     match available instructions and functions on the Arm CPU. For guidance, see:
     Using Assembly and Intrinsics in C or C++ Code .
Additionally, drivers on Windows are required to be built as Arm64 and can not be
emulated. For apps that rely on software drivers that have not yet been updated to
support Arm64 processors, see Building Arm64 Drivers with the WDK.
     .NET 7
     .NET 6 (LTS)
     .NET 5.0.8+
     .NET Framework 4.8.1
     clang-cl    compiles C++ code for Windows and can serve as a drop-in
     replacement for the MSVC compiler and linker. It still uses headers and libraries
     from MSVC and is ABI-compatible with MSVC.
     Qt for Windows        , Boost C++ Library , Bazel, an open-source build and test
     tool .
     Support for GCC and Mingw / GNU Toolchain for Windows on Arm is in-progress
     over at Linaro    .
     For a more complete list, see Windows On Arm (WOA) - Confluence
     (atlassian.net)   .
Need assistance? Leverage our App Assure
service
The App Assure Arm Advisory Service is available to help developers build Arm-
optimized apps. This service is in addition to our existing promise: your apps will run on
Windows on Arm, and if you encounter any issues, Microsoft will help you remediate
them. Learn more    .
You can create and deploy Windows 11 Arm64 VMs with with Ampere Altra Arm–based
processors on Azure. While there are many ways to create an Azure virtual machine, the
easiest way to get started is using the Azure portal. This method provides a browser-
based user interface to create VMs and their associated resources.
This quickstart shows you how to use the Azure portal to deploy a virtual machine (VM)
in Azure that runs Windows 11 Professional on Arm-based processors. To see your VM
in action, you then connect to the VM using a Remote Desktop client.
If you don't have an Azure subscription, create a free account before you begin.
Sign in to Azure
Sign in to the Azure portal   .
   3. In the Virtual machines page, select Create and then Azure virtual machine. The
      Create a virtual machine page opens.
   4. Under Instance details, enter a name for the Virtual machine name (for example
      myVM), No infrastructure redundancy required for Availability options, and
      Standard for Security type as shown here:
                                                                                   
5. Under Image, select See all images. The Select an image page opens.
6. Enter Windows in the Search the Marketplace box. Next, in the Image type drop-
  down, select Arm64. This will filter the images to only display results for available
  Windows on Arm64 VMs.
7. In the Windows 11 result card, select Select to display available images. Choose
  your preferred image from among the list.
                                                                            
8. Under Size, select See all sizes. The Select a VM size page opens.
9. Expand one of the VM Size headers, then choose your desired VM size from the
  available options. We recommend D2ps_v5 to get started. Press the Select button
  to return to the Create a virtual machine page.
                                                                                 
11. Under Inbound port rules, choose Allow selected ports and then select RDP
   (3389) from the drop-down.
12. Review Subscription Licenses that qualify for Multitenant Hosting Rights to ensure
   you have the necessary Windows license. This is required in order to use Windows
   11 images in Azure for any production workload. Click the checkbox under
   Licensing after confirming this.
13. Leave the remaining defaults and then select the Review + create button at the
   bottom of the page.
                                                                  
14. After validation runs, select the Create button at the bottom of the page.
1. On the overview page for your virtual machine, select Connect > Connect.
3. Open the downloaded RDP file and click Connect when prompted.
   4. In the Windows Security window, select More choices and then Use a different
     account. Type the username as localhost\username, enter the password you
     created for the virtual machine, and then click OK.
   5. You may receive a certificate warning during the sign-in process. Click Yes or
     Continue to create the connection.
Clean up resources
Delete resources
When no longer needed, you can delete the resource group, virtual machine, and all
related resources.
   1. On the Overview page for the VM, select the Resource group link.
   2. At the top of the page for the resource group, select Delete resource group.
   3. A page will open warning you that you are about to delete resources. Type the
     name of the resource group and select Delete to finish deleting the resources and
     the resource group.
Auto-shutdown
If the VM is still needed, Azure provides an Auto-shutdown feature for virtual machines
to help manage costs and ensure you are not billed for unused resources.
   1. On the Operations section for the VM, select the Auto-shutdown option.
   2. A page will open where you can configure the auto-shutdown time. Select the On
     option to enable and then set a time that works for you.
   3. Once you have set the time, select Save at the top to enable your Auto-shutdown
     configuration.
7 Note
Next steps
In this quickstart, you deployed a simple virtual machine, opened a network port for
remote desktop traffic, and connected using a Remote Desktop, To learn more about
Azure Windows virtual machines, continue on to the detailed tutorials.
The App Assure Arm Advisory Service is available to help if you get stuck. This service is
in addition to our existing promise: your apps will run on Windows on Arm, and if you
encounter any issues, Microsoft will help you remediate them.
Additional resources
     Announcing Azure Virtual Machines with Ampere Altra Arm–based processors
     Dpsv5 and Dpdsv5 virtual machine series documentation
     Microsoft Windows 11 Preview arm64        image details on Azure Marketplace
     Windows 11 release information
     Windows Commercial Licensing overview
Frequently asked questions about
Windows on Arm
FAQ
Use the following questions and answers to better understand the support for Arm-
based devices running on Windows.
C:\Program Files : This directory is used for x64 applications, Arm64 applications,
     Arm64X applications. Learn more about how to port your app to Arm64 or build
     Arm64X binaries.
C:\Program Files (x86) : This directory is used for x86 applications. Learn more
C:\Program Files (Arm) : This directory was used for 32-bit Arm applications, which
Emulation for x86 apps makes the rich ecosystem of Win32 apps available on Arm. This
provides the user the magical experience of running an existing x86 win32 app without
any modifications to the app.
WOW64 APIs
An x86 (or x64) app doesn’t even know that it is running on a Windows on Arm PC,
unless it calls specific APIs (IsWoW64Process2).
The WOW64 layer of Windows allows x86 code to run on the Arm64 version of
Windows. x86 emulation works by compiling blocks of x86 instructions into Arm64
instructions with optimizations to improve performance. A service caches these
translated blocks of code to reduce the overhead of instruction translation and allow for
optimization when the code runs again. The caches are produced for each module so
that other apps can make use of them on first launch.
See Add Arm support to your Windows app for guidance on how to create an Arm64
version of your apps and what sort of advantages, challenges, and tooling may be
involved, as well as available support for creating an Arm64 version of your app (and any
related dependencies).
Windows Dev Kit 2023
Article • 07/29/2023
Windows Dev Kit 2023 (code name “Project Volterra”) is the latest Arm device built for
Windows developers with a Neural Processing Unit (NPU) that provides best-in-class AI
computing capacity, multiple ports, and a stackable design for desktops and rack
deployment. Purpose-built with everything you need to develop, debug, and test native
Windows apps for Arm.
https://learn-video.azurefd.net/vod/player?show=tabs-vs-spaces&ep=windows-
developer-kit-for-arm&locale=en-us&embedUrl=%2Fwindows%2Farm%2Fdev-kit%2F
Device specifics
      32GB LPDDR4x RAM and 512GB fast NVMe storage
      Snapdragon® 8cx Gen 3 compute platform
      Ports: 3x USB-A, 2x USB-C, Mini-Display (HBR2 support), Ethernet (RJ45)
      Made with 20% recycled ocean plastic
      Available in the Microsoft Store
Device set up
When the device is turned on and connects to the Internet for the first time, follow the
getting started prompts and configuration for Windows Update to ensure the latest
software is running on the device.
Identify buttons and external ports
With the device flat on the table, the 3 buttons on the left side of the device, from left to
right, are:
      Boot to USB button: Hold the Power button + the Boot to USB button to boot to
      the USB-C thumb drive. This method can be used to re-image the device with the
      latest Recovery Image    .
      UEFI button: Hold the Power button + the UEFI button to boot into the UEFI menu.
      (USB-C monitor connections are not compatible when exploring UEFI.)
      Power button
All external ports will be available after the device boots into Windows 11 including:
The device supports up to three displays by using the mDP port and the two USB-C
ports.
7 Note
Set up power
The dev kit includes a 90W power supply. Attach the power supply to the back on the
far left of the device.
      The device will default into "Connected Standby Mode" when not in use. You can
      choose to hibernate the device using OS controls.
      Fan control is supported and controlled by firmware. The Fan will come on as
      needed to manage thermal load.
      There is no battery on the device, hence the system will only run on AC. There is no
      DC mode to test against.
Set up display - How to connect monitors
For the best experience, we recommend you use the mDP port as your main display for
setting up this device. Until the device is booted into Windows, all display output
defaults to the monitor connected to the mDP port.
Scenarios that will require you to use the mDP port include:
Requirements and notes for using Windows Developer Kit device display ports:
     If the only display connected to the device is USB-C, if you don’t use the mDP port
     (as noted above), you won't see a startup screen output when you turn on the
     device until Windows is booted. The Windows boot process should take ~25
     seconds.
     If connecting an HDMI or a DVI monitor to the mDP port, an active mini-DP to
     HDMI or active mini-DP to DVI adapter is required. *If the connection is not
     working, you may be using a passive adapter or a cable with a passive adapter
     built in. Cables should be 2m/6ft or less.
     When connecting an external keyboard or mouse, use the USB-A ports, not USB-C.
     Using USB-C to connect a keyboard or mouse will only work intermittently.
 mDP     HBR2            4 lane x 5.4   SST: 3840 x 2160 @ 60Hz, MST:   Default monitor
                         Gbps/lane      (x2) 2560 x 1600 @ 60Hz         port to boot with
                                                                        UEFI menu
 USB-C   HBR3            4 lane x 8.1   SST: 5120x2880 @ 60Hz, SST:     Default monitor
 (x2)                    Gbps/lane      4096x2160 @ 60Hz, MST: (x2)     port to boot
 Ports   Transmission    Max Data      Supported Displays (max            Comments
         Mode            Speed         resolution)
     This is the first native Arm64 version of Visual Studio available with workloads
     enabled for desktop development with C++ (for MSBuild-based projects), .NET
     desktop development, web development, game development, and Node.js
     development and also includes support for Windows SDK and Win App SDK
     components (Win UI).
     Native support for Arm64 is available starting with .NET 6, along with the .NET
     Framework 4.8.1 runtime     and SDK, and that support has been extended in .NET
     7. Read more about Arm64 performance improvements in .NET 7           .
     VS Code has supported an Arm64 architecture since the September 2020 version
     1.50 release, including extensions for Remote Development        .
Bringing together local compute on the CPU, GPU, and NPU and cloud compute with
Azure, including:
     Unlock the NPU power to accelerate AI/ML workloads using ONNX Runtime with
     frameworks like PyTorch or TensorFlow - get started with these instructions and
     tutorials.
     Learn more about the Snapdragon compute platform that powers Windows on
     Snapdragon® devices with native AArch64 (64-bit Arm) app support. You will also
     find a link to download the Qualcomm Neural Processing SDK for Windows. The
     Qualcomm® Neural Processing SDK is engineered to help developers save time
     and effort in optimizing performance of trained neural networks on devices with
     Qualcomm® AI products.
     The QNN Execution Provider for ONNX Runtime enables hardware accelerated
     execution on Qualcomm chipsets. It uses the Qualcomm AI Engine Direct SDK
     (QNN SDK)     to construct a QNN graph from an ONNX model which can be
     executed by a supported accelerator backend library.
Support for building Arm-native apps and porting existing x64 apps is also available,
including:
Arm64EC
Arm64X
     Arm64X is a new type of binary that can contain both the classic Arm64 code and
     Arm64EC code together, making it a particularly good fit for middleware or plugins
     that may be used by both ABIs.
Windows Terminal
     A modern way to run multiple command lines side-by-side in tabs or panes, fully
     customizable with a GPU-accelerated text rendering engine and command palette.
Microsoft PowerToys
     A set of utilities for power users to tune and streamline their Windows experience
     for greater productivity, including the FancyZones window manager, a keyboard
     manager, mouse utilities, PowerRename, and more.
     Enabling Windows 11 to run Android applications that are available in the Amazon
     Appstore.
Support
For hardware or warranty support with your Windows on Arm developer kit, open a
support request on the Support for business       services hub page.
FAQs
     How do I set up a recovery drive?
     To create a USB recovery drive in order to capture the default device state to return
     to as needed, you will need an empty 16gb USB drive. (This process will erase any
     data already stored on the drive.)
        1. In the search box on the task bar, search for Create a recovery drive. After
          selecting, you may be asked to enter an admin password or confirm your
          choice.
        2. When the tool opens, ensure Back up system files to the recovery drive is
          selected. Select Next.
        3. Connect a USB drive, select it, select Next, and then Create. Many files will be
          copied to the recovery drive, so this will take some time.
        1. Connect your USB drive and then hold the Power button + the UEFI button to
          boot into UEFI menu.
       2. Once in UEFI, use the external USB-A keyboard or mouse to navigate to the
           Boot Configuration menu.
    Drivers for hardware, games, and apps may only work if they're designed for a
    Windows 11 Arm-based PC. Check directly with the organization that developed
    the driver to find relevant Arm64 updates.
    Where can I download a recovery image to reset Windows Developer Kit 2023 to
    the factory condition?
    The Recovery Image page       offers an image specifically for "Windows Dev Kit
    2023". You will need to enter the device Serial Number.
    No, currently custom operating system images are not supported on Microsoft
    Arm devices. Only the Windows OS image provided on the device when purchased
    is supported. This image can be reinstalled if necessary using the downloadable
    recovery image on the Recovery Image page .
To keep your device performing its best, make sure that you have the latest updates.
 30.0.3741.85     Qualcomm(R) Adreno(TM) 680 GPU - Improves stability and addresses system
                  bugcheck.
1.0.3564.43     Qualcomm(R) Aqstic(TM) Audio Adapter Device - Sound, video and game
                controllers
1.0.3564.43 Qualcomm(R) System Manager Power Engine Plug-in Device - System devices
18.52.1.5 Qualcomm(R) Thermal MDM Sensing and Mitigation Driver - System devices
1.0.3530.98 Qualcomm(R) USB3(TM) eXtensible Host Controller - Universal Serial Bus devices
1.0.3532.92    Qualcomm(R) WCN685x Wi-Fi 6E Dual Band Simultaneous (DBS) WiFiCx Network
               Adapter - Network adapters
1.0.3530.98    Surface Pro Qualcomm(R) Aqstic(TM) Audio Codec Driver - Sound, video and
               game controllers
Arm64EC is a new application binary interface (ABI) for apps running on Arm devices
with Windows 11. It is a Windows 11 feature that requires the use of the Windows 11
SDK and is not available on Windows 10 on Arm.
Interoperability
Code built as Arm64EC is interoperable with x64 code running under emulation within
the same process. The Arm64EC code in the process runs with native performance, while
any x64 code runs using emulation that comes built-in with Windows 11. Even if your
app relies on existing dependencies or plugins that don't yet support Arm, you can start
to rebuild parts of your app as Arm64EC to gain the benefits of native performance.
An x64 or Arm64EC process can load and call into both x64 and Arm64EC binaries,
whereas an Arm64 process can only load Arm64 binaries. Both architectures can load
Arm64X binaries as those contain code for both x64 and Arm64.
x64/Arm64EC ✔ ✔ ❌
 Arm64                        ❌                 ❌                    ✔
✔ = Supported, ❌ = Not supported
Similarly, at build time, Arm64EC binaries can link in both x64 and Arm64EC libs, while
Arm64 binaries can only link in Arm64 libs.
Arm64EC ✔ ✔ ❌
Arm64 ❌ ❌ ✔
For more detail about how the Arm64EC ABI enables interoperability, see Understanding
Arm64EC ABI and assembly code.
The image above shows a simplified example of a fully-emulated x64 workload taking
some amount of time that is then incrementally improved using Arm64EC:
By recompiling the modules that take the most time or are the most CPU-intensive from
x64 to Arm64EC, the resulting workload receives the most improvement for the least
amount of effort each step of the way.
App dependencies
When using Arm64EC to rebuild an application, you will want to use Arm64EC versions
of dependencies but you can also rely on x64 versions of dependencies. Arm64 versions
of dependencies won't be usable.
Any x64 code, including code from dependencies, in an Arm64EC process will run under
emulation in your app. Prioritizing the most CPU-intensive dependencies to transition
from x64 to Arm64EC will have the greatest impact toward improving your app's
performance.
For developers interested in identifying these binaries, you can see them in a developer
command prompt using link /dump /headers .
PowerShell
The combination of (x64) and (ARM64X) indicates that some portion of the binary has
been recompiled as Arm64EC, even though the binary still appears to be x64. A binary
with a machine header that contains (ARM64) and (ARM64X) is an Arm64X PE file that
can be loaded into both x64 and Arm64 apps.
Windows Task Manager can also be used to identify if an app has been compiled as
Arm64EC. In the Details tab of Task manager, the Architecture column will show ARM64
(x64 compatible) for applications whose main executable has been partially or
completely compiled as Arm64EC.
Next steps
See Get started with Arm64EC to learn how to build or update Win32 apps using
Arm64EC.
Get started with Arm64EC
Article • 12/13/2022
To get started building your app or project using Arm64EC, you will need to install some
prerequisites and add an Arm64EC configuration.
Prerequisites
      The latest Windows 11 SDK build. If using the Windows 11 SDK version 22000, the
      updated version on July 29, 2022 includes key fixes for building Arm64EC apps.
      Visual Studio 2022 version 17.3 or later.
      Arm64EC tools installed with the Visual Studio Installer.
In the Visual Studio Installer, you can add the Arm64EC tools by searching under
Individual components and selecting the MSVC v143 - VS 2022 C++ ARM64 build
tools checkbox.
) Important
  As of Visual Studio 2022 version 17.4, the Arm64EC tools are included when
  installing the Arm64 tools (MSVC v143 - VS 2022 C++ ARM64 build tools). You no
  longer need to select a separate option for Arm64EC tools.
Once you have installed the prerequisites and tools, you can target Arm64EC in your
MSBuild and CMake projects.
MSBuild Projects
   1. With the tools and SDK installed, create a new C++ project or open an existing
     one.
7 Note
     You can choose to leave parts of the solution as x64 as needed. However, the more
     code built as Arm64EC, the more code that will run with native performance on
     Windows 11 on Arm. For any external dependencies, ensure that your project links
     against the x64 or Arm64EC versions of those projects.
   3. With the new solution platform in place and selected, select Build in Visual Studio
     to start building Arm64EC binaries.
By design, not all projects in an Arm64EC solution need to be targeting Arm64EC as they
can target x64 instead. For any such projects that you want to remain as x64, ensure that
you configure those projects in the configuration manager to target x64 under the
ARM64EC solution build.
CMake Projects
   1. Open your "C++ CMake" project or create a new one.
   3. Modify the architecture property under the windows configuration you want for
     Arm64EC.
C++
         "architecture": {
            "value": "arm64ec",
            "strategy": "external"
         }
     The default generator is Ninja. If using the Visual Studio generator, change the
     strategy field to set.
   4. If using the Ninja generator, you’ll also need to set some environment variables by
     adding the environment object to your CMakePresets configuration.
C++
         "environment": {
            "CXXFLAGS": "/arm64EC",
            "CFLAGS": "/arm64EC"
         }
   5. Save the CMakePresets file and make sure the active configuration is set to the
     Arm64EC configuration. From the menu bar, select project menu, then select
     Configure [Project Name] in order to generate your CMake cache.
   6. Build your CMake Project targeting Arm64EC like any other CMake project by
     navigating to the Build Menu and selecting Build All.
cl /arm64EC /c <args>
Learn more about how to Use the Microsoft C++ toolset from the command line.
Understanding Arm64EC ABI and
assembly code
Article • 07/08/2022
The purpose of this document is to provide a detailed view of the Arm64EC ABI with
enough information for an application developer to write and debug code compiled for
Arm64EC, including low-level/assembler debugging and writing assembly code
targeting the Arm64EC ABI.
Design of Arm64EC
Arm64EC was designed to deliver native-level functionality and performance, while
providing transparent and direct interoperability with x64 code running under
emulation.
Arm64EC is mostly additive to the Classic Arm64 ABI. Very little of the Classic ABI was
changed, but portions were added to enable x64 interoperability.
In this document, the original, standard Arm64 ABI shall be referred to as “Classic ABI”.
This avoids the ambiguity inherent to overloaded terms like “Native”. Arm64EC, to be
clear, is every bit as native as the original ABI.
These are small changes when seen in perspective of how much the entire ABI defines.
In other words, _M_AMD64 and _AMD64_ are defined. One of the types affected by this rule
is the CONTEXT structure. The CONTEXT structure defines the state of the CPU at a given
point. It is used for things like Exception Handling and GetThreadContext APIs. Existing
x64 code expects the CPU context to be represented as an x64 CONTEXT structure or, in
other words, the CONTEXT structure as it is defined during x64 compilation.
This structure must be used to represent the CPU context while executing x64 code, as
well as Arm64EC code. Existing code would not understand a novel concept, such as the
CPU register set changing from function to function. If the x64 CONTEXT structure is used
to represent Arm64 execution states, this implies Arm64 registers are effectively mapped
into x64 registers.
It also implies that any Arm64 registers which cannot be fitted into the x64 CONTEXT
must not be used, as their values can be lost anytime an operation using CONTEXT occurs
(and some can be asynchronous and unexpected, such as the Garbage Collection
operation of a Managed Language Runtime, or an APC).
The mapping rules between Arm64EC and x64 registers are represented by the
ARM64EC_NT_CONTEXT structure in the Windows headers, present in the SDK. This structure
is essentially a union of the CONTEXT structure, exactly as it is defined for x64, but with an
extra Arm64 register overlay.
For example, RCX maps to X0 , RDX to X1 , RSP to SP , RIP to PC , etc. We can also see
how the registers x13 , x14 , x23 , x24 , x28 , v16 - v31 have no representation and, thus,
cannot be used in Arm64EC.
This register usage restriction is the first difference between the Arm64 Classic and EC
ABIs.
Call checkers
Call checkers have been a part of Windows ever since Control Flow Guard (CFG) was
introduced in Windows 8.1. Call checkers are address sanitizers for function pointers
(before these things were called address sanitizers). Every time code is compiled with
the option /guard:cf the compiler will generate an extra call to the checker function
just before every indirect call/jump. The checker function itself is provided by Windows
and, for CFG, it performs a validity check against the known-to-be-good call targets.
This information is also included in binaries compiled with /guard:cf .
This is an example of a call checker use in Classic Arm64:
In the CFG case, the call checker will simply return if the target is valid, or fast-fail the
process if it is not. Call checkers have custom calling conventions. They take the function
pointer in a register not used by the normal calling convention and preserving all
normal calling-convention registers. This way, they don’t introduce register spillage
around them.
Call checkers are optional on all other Windows ABIs, but mandatory on Arm64EC. On
Arm64EC, call checkers accumulate the task of verifying the architecture of the function
being called. They verify whether the call is another EC (“Emulation Compatible”)
function or an x64 function that must be executed under emulation. In many cases, this
can only be verified at runtime.
Arm64EC call checkers build on top of the existing Arm64 checkers, but they have a
slightly different custom calling convention. They take an extra parameter and they may
modify the register containing the target address. For example, if the target is x64 code,
control must be transferred to the emulation scaffolding logic first.
The Arm64EC call checker is located through a different symbol than is used for the
other ABIs in Windows. On the Classic Arm64 ABI, the symbol for the call checker is
__guard_check_icall_fptr . This symbol will be present in Arm64EC, but it is there for
x64 statically linked code to use, not Arm64EC code itself. Arm64EC code will use either
__os_arm64x_check_icall or __os_arm64x_check_icall_cfg .
On Arm64EC, call checkers are not optional. However, CFG is still optional, as is the case
for other ABIs. CFG may be disabled at compile-time, or there may be a legitimate
reason to not perform a CFG check even when CFG is enabled (e.g. the function pointer
never resides in RW memory). For an indirect call with CFG check, the
__os_arm64x_check_icall_cfg checker should be used. If CFG is disabled or unnecessary,
__os_arm64x_check_icall should be used instead.
Below is a summary table of the call checker usage on Classic Arm64, x64 and Arm64EC
noting the fact that an Arm64EC binary can have two options depending on the
architecture of the code.
Independently of the ABI, having CFG enabled code (code with reference to the CFG
call-checkers), does not imply CFG protection at runtime. CFG protected binaries can run
down-level, on systems not supporting CFG: the call-checker is initialized with a no-op
helper at compile time. A process may also have CFG disabled by configuration. When
CFG is disabled (or OS support is not present) on previous ABIs the OS will simply not
update the call-checker when the binary is loaded. On Arm64EC, if CFG protection is
disabled, the OS will set __os_arm64x_check_icall_cfg the same as
__os_arm64x_check_icall , which will still provide the needed target architecture check in
all cases, but not CFG protection.
As with CFG in Classic Arm64, the call to the target function ( x11 ) must immediately
follow the call to the Call Checker. The address of the Call Checker must be placed in a
volatile register and neither it, nor the address of the target function, should ever be
copied to another register or spilled to memory.
Stack Checkers
__chkstk is used automatically by the compiler every time a function allocates an area
on the stack larger than a page. To avoid skipping over the stack guard page protecting
the end of the stack, __chkstk is called to make sure all pages in the allocated area are
probed.
__chkstk is usually called from the prolog of the function. For that reason, and for
optimal code generation, it uses a custom calling convention.
This implies that x64 code and Arm64EC code need their own, distinct, __chkstk
functions, as Entry and Exit thunks assume standard calling conventions.
x64 and Arm64EC share the same symbol namespace so there can’t be two functions
named __chkstk . To accommodate compatibility with pre-existing x64 code, __chkstk
name will be associated with the x64 stack checker. Arm64EC code will use
__chkstk_arm64ec instead.
The custom calling convention for __chkstk_arm64ec is the same as for Classic Arm64
__chkstk : x15 provides the size of the allocation in bytes, divided by 16. All non-volatile
registers, as well as all volatile registers involved in the standard calling convention are
preserved.
For the variadic specific case, Arm64EC follows a calling convention very similar to x64
variadic, with only a few differences. Below are the major rules for Arm64EC variadic:
C++
  struct three_char {
      char a;
      char b;
      char c;
  };
  void
  pt_va_function (
      double f,
      ...
  );
  void
  pt_nova_function (
      double f,
      struct three_char tc,
      __int64 ull1,
      __int64 ull2,
      __int64 ull3
  )
  {
        pt_va_function(f, tc, ull1, ull2, ull3);
  }
pt_nova_function takes 5 parameters which will be assigned following the Classic Arm64
outlined above:
  stp             fp,lr,[sp,#-0x30]!
  mov             fp,sp
  sub             sp,sp,#0x10
bl pt_va_function
  add             sp,sp,#0x10
  ldp           fp,lr,[sp],#0x30
  ret
ABI additions
To achieve transparent interoperability with x64 code, many additions have been made
to the Classic Arm64 ABI. They handle the calling conventions differences between
Arm64EC and x64.
C++
  C++
  int fK(int a, double b, int c, double d);
These examples demonstrate that parameter assignment and translation vary by type,
but also the types of the preceding parameters in the list are depended upon. This detail
is illustrated by the 3rd parameter. In both functions, the parameter’s type is “int”, but
the resulting translation is different.
Entry and Exit Thunks exist for this reason and are specifically tailored for each individual
function signature.
Both types of thunks are, themselves, functions. Entry Thunks are automatically invoked
by the emulator when x64 functions call into Arm64EC functions (execution Enters
Arm64EC). Exit Thunks are automatically invoked by the call checkers when Arm64EC
functions call into x64 functions (execution Exits Arm64EC).
When compiling Arm64EC code, the compiler will generate an Entry Thunk for each
Arm64EC function, matching its signature. The compiler will also generate an Exit Thunk
for every function an Arm64EC function calls.
C++
  struct SC {
      char a;
      char b;
      char c;
  };
  int fA(int a, double b, struct SC c, int i1, int i2, int i3) {
      return fB(a, b, i1, i2, i3) + fC(a, c, i1, i2, i3);
  }
When compiling the code above targeting Arm64EC, the compiler will generate:
The fA Entry Thunk is generated in case fA and called from x64 code. Exit Thunks for
fB and fC are generated in case fB and/or fC and turn out to be x64 code.
The same Exit Thunk may be generated multiple times, given the compiler will generate
them at the call site rather than the function itself. This may result in a considerable
amount of redundant thunks so, in reality, the compiler will apply trivial optimization
rules to make sure only the required thunks make it into the final binary.
For example, in a binary where Arm64EC function A calls Arm64EC function B , B is not
exported and its address is never known outside of A . It is safe to eliminate the Exit
Thunk from A to B , along with the Entry Thunk for B . It is also safe to alias together all
Exit and Entry thunks which contain the same code, even if they were generated for
distinct functions.
Exit Thunks
Using the example functions fA , fB and fC above, this is how the compiler would
generate both fB and fC Exit Thunks:
Exit Thunk to int fB(int a, double b, int i1, int i2, int i3);
  $iexit_thunk$cdecl$i8$i8di8i8i8:
      stp         fp,lr,[sp,#-0x10]!
      mov         fp,sp
      sub         sp,sp,#0x30
      adrp        x8,__os_arm64x_dispatch_call_no_redirect
      ldr         xip0,[x8]
      str         x3,[sp,#0x20] ; Spill 5th param (i3) into the stack
      fmov        d1,d0          ; Move 2nd param (b) from d0 to XMM1 (x1)
      mov         x3,x2          ; Move 4th param (i2) from x2 to R9 (x3)
      mov         x2,x1          ; Move 3rd param (i1) from x1 to R8 (x2)
      blr         xip0           ; Call the emulator
      mov         x0,x8          ; Move return from RAX (x8) to x0
      add         sp,sp,#0x30
      ldp         fp,lr,[sp],#0x10
      ret
Exit Thunk to int fC(int a, struct SC c, int i1, int i2, int i3);
  $iexit_thunk$cdecl$i8$i8m3i8i8i8:
      stp         fp,lr,[sp,#-0x20]!
      mov         fp,sp
      sub         sp,sp,#0x30
      adrp        x8,__os_arm64x_dispatch_call_no_redirect
      ldr         xip0,[x8]
      str         w1,[sp,#0x40]       ; Spill 2nd param (c) onto the stack
      add         x1,sp,#0x40         ; Make RDX (x1) point to the spilled 2nd
  param
      str         x4,[sp,#0x20]       ; Spill 5th param (i3) into the stack
      blr         xip0                ; Call the emulator
      mov         x0,x8               ; Move return from RAX (x8) to x0
      add         sp,sp,#0x30
      ldp         fp,lr,[sp],#0x20
      ret
In the fB case, we can see how the presence of a ‘double’ parameter will cause the
remaining GP register assignment to reshuffle, a result of Arm64 and x64’s different
assignment rules. We can also see x64 only assigns 4 parameters to registers, so the 5th
parameter must be spilled onto the stack.
In the fC case, the second parameter is a structure of 3-byte length. Arm64 will allow
any size structure to be assigned to a register directly. x64 only allows sizes 1, 2, 4 and 8.
This Exit Thunk must then transfer this struct from the register onto the stack and
assign a pointer to the register instead. This still consumes one register (to carry the
pointer) so it does not change assignments for the remaining registers: no register
reshuffling happens for the 3rd and 4th parameter. Just as for the fB case, the 5th
parameter must be spilled onto the stack.
     The compiler will name them not by the function name they translate from->to,
     but rather the signature they address. This makes it easier to find redundancies.
     The Exit Thunk is called with the register x9 carrying the address of the target (x64)
     function. This is set by the call checker and passes through the Exit Thunk,
     undisturbed, into the emulator.
After rearranging the parameters, the Exit Thunk then calls into the emulator via
__os_arm64x_dispatch_call_no_redirect .
It is worth, at this point, reviewing the function of the call checker, and detail about its
own custom ABI. This is what an indirect call to fB would look like:
   mov         x11, <target>
   adrp        x9, __os_arm64x_check_icall_cfg
   ldr         x9, [x9, __os_arm64x_check_icall_cfg]
   adrp        x10, $iexit_thunk$cdecl$i8$i8di8i8i8    ; fB function’s exit thunk
   add         x10, x10, $iexit_thunk$cdecl$i8$i8di8i8i8
   blr         x9                                      ; check target function
   blr         x11                                     ; call function
x11 supplies the address of the target function to call ( fB in this case). It may not
The data returned by the call checker will depend on the target function being Arm64EC
or x64.
       x11 will return the address of the Arm64EC code to call. This may or may not be
       the same value that was provided in.
x11 will return the address of the Exit Thunk. This is copied from input provided in
x10 .
       x10 will return the address of the Exit Thunk, undisturbed from input.
       x9 will return the target x64 function. This may or may not be the same value it
Call checkers will always leave calling convention parameter registers undisturbed, so
the calling code should follow the call to the call checker immediately with blr x11 (or
br x11 in case of a tail-call). These are the registers call checkers. They will always
preserve above and beyond standard non-volatile registers: x0 - x8 , x15 ( chkstk ) and
q0 - q7 .
Entry Thunks
Entry Thunks take care of the transformations required from the x64 to the Arm64
calling conventions. This is, essentially, the reverse of Exit Thunks but there are a few
more aspects to consider.
Consider the previous example of compiling fA , an Entry Thunk is generated so that fA
can be called by x64 code.
Entry Thunk for int fA(int a, double b, struct SC c, int i1, int i2, int i3)
  $ientry_thunk$cdecl$i8$i8dm3i8i8i8:
      stp          q6,q7,[sp,#-0xA0]!        ; Spill full non-volatile XMM registers
      stp          q8,q9,[sp,#0x20]
      stp          q10,q11,[sp,#0x40]
      stp          q12,q13,[sp,#0x60]
      stp          q14,q15,[sp,#0x80]
      stp          fp,lr,[sp,#-0x10]!
      mov          fp,sp
      ldrh         w1,[x2]                   ; Load 3rd param (c) bits [15..0]
  directly into x1
      ldrb         w8,[x2,#2]                ; Load 3rd param (c) bits [16..23] into
  temp w8
      bfi          w1,w8,#0x10,#8            ; Merge 3rd param (c) bits [16..23] into
  x1
      mov          x2,x3                     ; Move the 4th param (i1) from R9 (x3)
  to x2
      fmov         d0,d1                     ; Move the 2nd param (b) from XMM1 (d1)
  to d0
      ldp          x3,x4,[x4,#0x20]          ; Load the 5th (i2) and 6th (i3) params
                                             ; from the stack into x3 and x4 (using
  x4)
      blr           x9                       ; Call the function (fA)
      mov           x8,x0                    ; Move the return from x0 to x8 (RAX)
      ldp           fp,lr,[sp],#0x10
      ldp           q14,q15,[sp,#0x80]       ; Restore full non-volatile XMM
  registers
      ldp           q12,q13,[sp,#0x60]
      ldp           q10,q11,[sp,#0x40]
      ldp           q8,q9,[sp,#0x20]
      ldp           q6,q7,[sp],#0xA0
      adrp          xip0,__os_arm64x_dispatch_ret
      ldr           xip0,[xip0,__os_arm64x_dispatch_ret]
      br            xip0
Before calling the Entry Thunk, the x64 emulator will pop the return address from the
stack into the LR register. It is then expected that LR will be pointing at x64 code when
control is transferred to the Entry Thunk.
The emulator may also perform another adjustment to the stack, depending on the
following: Both Arm64 and x64 ABIs define a stack alignment requirement where the
stack must be aligned to 16-bytes at the point a function is called. When running Arm64
code, hardware enforces this rule, but there is no hardware enforcement for x64. While
running x64 code, erroneously calling functions with an unaligned stack may go
unnoticed indefinitely, until some 16-byte alignment instruction is used (some SSE
instructions do) or Arm64EC code is called.
To address this potential compatibility problem, before calling the Entry Thunk, the
emulator will always align-down the Stack Pointer to 16-bytes and store its original
value in the x4 register. This way Entry Thunks always start executing with an aligned
stack but can still correctly reference the parameters passed on the stack, via x4 .
To address these SIMD register manipulation asymmetries, Entry Thunks must explicitly
save all SIMD registers which are considered non-volatile in x64. This is only needed on
Entry Thunks (not Exit Thunks) because x64 is stricter than Arm64. In other words,
register saving/preservation rules in x64 exceed the Arm64 requirements in all cases.
To address the correct recovery of these register values when unwinding the stack (e.g.
setjmp + longjmp, or throw + catch), a new unwind opcode was introduced:
save_any_reg (0xE7) . This new 3-byte unwind opcode enables saving any General
Purpose or SIMD register (including the ones considered volatile) and including full-
sized Qn registers. This new opcode is used for the Qn register spills/fill operations
above. save_any_reg is compatible with save_next_pair (0xE6) .
For reference, below is the corresponding unwind information belonging to the Entry
Thunk presented above:
     Prolog unwind:
        06: E76689..    +0004 stp     q6,q7,[sp,#-0xA0]! ; Actual=stp        q6,q7,
  [sp,#-0xA0]!
        05: E6......    +0008 stp     q8,q9,[sp,#0x20]     ; Actual=stp      q8,q9,
  [sp,#0x20]
        04: E6......    +000C stp     q10,q11,[sp,#0x40] ; Actual=stp        q10,q11,
  [sp,#0x40]
        03: E6......    +0010 stp     q12,q13,[sp,#0x60] ; Actual=stp        q12,q13,
  [sp,#0x60]
        02: E6......    +0014 stp     q14,q15,[sp,#0x80] ; Actual=stp        q14,q15,
  [sp,#0x80]
        01: 81...... +0018 stp   fp,lr,[sp,#-0x10]!         ; Actual=stp     fp,lr,
  [sp,#-0x10]!
        00: E1...... +001C mov   fp,sp                      ; Actual=mov     fp,sp
                     +0020 (end sequence)
     Epilog #1 unwind:
        0B: 81...... +0044 ldp   fp,lr,[sp],#0x10           ; Actual=ldp     fp,lr,
  [sp],#0x10
        0C: E74E88.. +0048 ldp   q14,q15,[sp,#0x80]         ; Actual=ldp     q14,q15,
  [sp,#0x80]
        0F: E74C86.. +004C ldp   q12,q13,[sp,#0x60]         ; Actual=ldp     q12,q13,
  [sp,#0x60]
        12: E74A84.. +0050 ldp   q10,q11,[sp,#0x40]         ; Actual=ldp     q10,q11,
  [sp,#0x40]
        15: E74882.. +0054 ldp   q8,q9,[sp,#0x20]           ; Actual=ldp     q8,q9,
  [sp,#0x20]
        18: E76689.. +0058 ldp   q6,q7,[sp],#0xA0           ; Actual=ldp     q6,q7,
  [sp],#0xA0
        1C: E3...... +0060 nop                              ; Actual=90000030
        1D: E3...... +0064 nop                              ; Actual=ldr   xip0,
  [xip0,#8]
        1E: E4...... +0068 end                              ; Actual=br      xip0
                     +0070 (end sequence)
After the Arm64EC function returns, the __os_arm64x_dispatch_ret routine is used to re-
enter the emulator, back to x64 code (pointed to by LR ).
Arm64EC functions have the 4 bytes before the first instruction in the function reserved
for storing information to be used at runtime. It is in these 4 bytes that the relative
address of Entry Thunk for the function can be found. When performing a call from an
x64 function to an Arm64EC function, the emulator will read the 4 bytes before the start
of the function, mask-out the lower two bits and add that amount to the address of the
function. This will produce the address of the Entry Thunk to call.
Adjustor Thunks
Adjustor Thunks are signature-less functions which simply transfer control to (tail-call)
another function, after performing some transformation to one of the parameters. The
type of the parameter(s) being transformed is known, but all the remaining parameters
can be anything and, in any number – Adjustor Thunks will not touch any register
potentially holding a parameter and will not touch the stack. This is what makes
Adjustor Thunks signature-less functions.
Adjustor Thunks can be generated automatically by the compiler. This is common, for
example, with C++ multiple-inheritance, where any virtual method may be delegated to
the parent class, unmodified, aside from an adjustment to the this pointer.
The thunk subtracts 8 bytes to the this pointer and forwards the call to the parent
class.
In summary, Arm64EC functions callable from x64 functions must have an associated
Entry Thunk. The Entry Thunk is signature specific. Arm64 signature-less functions, such
as Adjustor Thunks need a different mechanism which can handle signature-less
functions.
The Entry Thunk of an Adjustor Thunk uses the __os_arm64x_x64_jump helper to defer
the execution of the real Entry Thunk work (adjust the parameters from one convention
to the other) to the next call. It is at this time that the signature becomes apparent. This
includes the option of not doing calling convention adjustments at all, if the target of
the Adjustor Thunk turns out to be an x64 function. Remember that by the time an Entry
Thunk starts running, the parameters are in their x64 form.
  [thunk]:CObjectContext::Release`adjustor{8}':
      sub         x0,x0,#8
      adrp        x9,CObjectContext::Release
      add         x11,x9,CObjectContext::Release
      stp         fp,lr,[sp,#-0x10]!
      mov         fp,sp
      adrp        xip0, __os_arm64x_check_icall
      ldr         xip0,[xip0, __os_arm64x_check_icall]
      blr         xip0
      ldp         fp,lr,[sp],#0x10
      br          x11
  [thunk]:CObjectContext::Release$entry_thunk`adjustor{8}':
      sub         x0,x0,#8
      adrp        x9,CObjectContext::Release
      add         x9,x9,CObjectContext::Release
       adrp          xip0,__os_arm64x_x64_jump
       ldr           xip0,[xip0,__os_arm64x_x64_jump]
       br            xip0
Fast-Forward Sequences
Some applications make run-time modifications to functions residing in binaries that
they do not own but depend on – commonly operating system binaries – for the
purpose of detouring execution when the function is called. This is also known as
hooking.
     The size of the 1st instruction: It is a good idea to replace it with a JMP which is the
     same size or smaller, to avoid replacing the top of the function while other thread
     may be running it in flight.
     The type of the first instruction: If the first instruction has some PC relative nature
     to it, relocating it may require changing things like the displacement fields. Since
     they are likely to overflow when an instruction is moved to a distant place, this may
     require providing equivalent logic with different instructions altogether.
Due to all of this complexity, robust and generic hooking logic is rare to find. Frequently
the logic present in applications can only cope with a limited set of cases that the
application is expecting to encounter in the specific APIs it is interested in. It is not hard
to imagine how much of an application compatibility problem this is. Even a simple
change in the code or compiler optimizations may render applications unusable if the
code no longer looks exactly as expected.
What would happen to these applications if they were to encounter Arm64 code when
setting up a hook? They would most certainly fail.
Fast-Forward Sequence (FFS) functions address this compatibility requirement in
Arm64EC.
FFS are very small x64 functions which contain no real logic and tail-call to the real
Arm64EC function. They are optional but enabled by default for all DLL exports and for
any function decorated with __declspec(hybrid_patchable) .
For these cases, when code obtains a pointer to a given function, either by
GetProcAddress in the export case, or by &function in the
__declspec(hybrid_patchable) case, the resulting address will contain x64 code. That
x64 code will pass for a legitimate x64 function, satisfying most of the hooking logic
currently available.
C++
  auto module_handle =
      GetModuleHandleW(L"api-ms-win-core-processthreads-l1-1-7.dll");
  auto pgma =
      (decltype(&GetMachineTypeAttributes))
          GetProcAddress(module_handle, "GetMachineTypeAttributes");
hr = (*pgma)(IMAGE_FILE_MACHINE_Arm64, &MachineAttributes);
The function pointer value in the pgma variable will contain the address of
GetMachineTypeAttributes ’s FFS.
  kernelbase!EXP+#GetMachineTypeAttributes:
  00000001`800034e0 488bc4          mov           rax,rsp
  00000001`800034e3 48895820        mov           qword ptr [rax+20h],rbx
  00000001`800034e7 55              push          rbp
  00000001`800034e8 5d              pop           rbp
  00000001`800034e9 e922032400      jmp           00000001`80243810
The FFS x64 function has a canonical prolog and epilog, ending with a tail-call (jump) to
the real GetMachineTypeAttributes function in Arm64EC code:
  kernelbase!GetMachineTypeAttributes:
  00000001`80243810 d503237f pacibsp
  00000001`80243814 a9bc7bfd stp                fp,lr,[sp,#-0x40]!
  00000001`80243818 a90153f3 stp                x19,x20,[sp,#0x10]
  00000001`8024381c a9025bf5 stp                x21,x22,[sp,#0x20]
  00000001`80243820 f9001bf9 str                x25,[sp,#0x30]
  00000001`80243824 910003fd mov                fp,sp
  00000001`80243828 97fbe65e bl                 kernelbase!#__security_push_cookie
  00000001`8024382c d10083ff sub                sp,sp,#0x20
                             [...]
When the hook transfers execution back to the end of the FFS, it will eventually reach
the tail-call to the Arm64EC code, which will then execute after the hook, just as the
application is expecting it would.
C++
            IMPORT __os_arm64x_check_icall_cfg
            IMPORT |$iexit_thunk$cdecl$i8$i8d|
            IMPORT pfE
            NESTED_ENTRY_COMDAT A64NAME(fD)
            PROLOG_SAVE_REG_PAIR fp, lr, #-16!
NESTED_END
end
C++
void _Arm64XGenerateThunk(int);
The _Arm64XGenerateThunk keyword tells the C compiler to use the function signature,
ignore the body, and generate either an Exit Thunk (when the parameter is 1) or an Entry
Thunk (when the parameter is 2).
It is recommended to place thunk generation in its own C file. Being in isolated files
makes it simpler to confirm the symbol names by dumping the corresponding OBJ
symbols or even disassembly.
Most Adjustor Thunks are generated by the C++ compiler, but they can also be
generated manually. This can be found in cases where a generic callback transfers
control to the real callback, identified by one of the parameters.
In this example, the target function address is retrieved from the element of a structure,
provided by reference, through the 1st parameter. Because the structure is writable, the
target address must be validated through Control Flow Guard (CFG).
The below example demonstrates how the equivalent Adjustor Thunk would look when
ported to Arm64EC:
       NESTED_ENTRY_COMDAT A64NAME(MyAdjustorThunk)
       PROLOG_SAVE_REG_PAIR    fp, lr, #-16!
       ldr     x11, [x0, 0x18]
       adrp    xip0, __os_arm64x_check_icall_cfg
       ldr     xip0, [xip0, __os_arm64x_check_icall_cfg]
       blr     xip0
       EPILOG_RESTORE_REG_PAIR fp, lr, #16
       EPILOG_END              br x11
       NESTED_END
The code above does not supply an Exit Thunk (in register x10). This is not possible since
the code can be executed for many different signatures. This code is taking advantage
of the caller having set x10 to the Exit Thunk. The caller would have made the call
targeting an explicit signature.
The above code does need an Entry Thunk to address the case when the caller is x64
code. This is how to author the corresponding Entry Thunk, using the macro for custom
Entry Thunks:
       Arm64EC_CUSTOM_ENTRY_THUNK A64NAME(MyAdjustorThunk)
       ldr      x9, [x0, 0x18]
       adrp     xip0, __os_arm64x_x64_jump
       ldr      xip0, [xip0, __os_arm64x_x64_jump]
       br       xip0
       LEAF_END
Unlike other functions, this Entry Thunk does not eventually transfer control to the
associated function (the Adjustor Thunk). In this case, the functionality itself (performing
the parameter adjustment) is embedded into the Entry Thunk and control is transferred
directly to the end target, via the __os_arm64x_x64_jump helper.
The operating system extracts this information from the loaded binaries. x64 binaries are
all x64 and Arm64EC contain a range-table for Arm64EC vs x64 code pages.
What about Dynamically Generated code? Just-in-time (JIT) compilers generate code, at
runtime, which is not backed by any binary file.
For trivial compatibility reasons, any application performing these steps in an Arm64EC
process will result in the code being considered x64 code. This will happen for any
process using the unmodified x64 Java Runtime, .NET runtime, JavaScript engine, etc.
To generate Arm64EC dynamic code, the process is mostly the same with only two
differences:
C++
        MEM_EXTENDED_PARAMETER Parameter = { 0 };
        Parameter.Type = MemExtendedParameterAttributeFlags;
        Parameter.ULong64 = MEM_EXTENDED_PARAMETER_EC_CODE;
        address = VirtualAlloc2 (
            process,
            NULL,
            numBytesToAllocate,
            allocationType,
            protection,
            &Parameter,
            1);
C++
Arm64_RUNTIME_FUNCTION FunctionTable[1];
  FunctionTable[0].BeginAddress = 0;
  FunctionTable[0].Flags = PdataPackedUnwindFunction;
  FunctionTable[0].FunctionLength = nSize / 4;
  FunctionTable[0].RegF = 0;                   // no D regs saved
  FunctionTable[0].RegI = 0;                   // no X regs saved beyond fp,lr
  FunctionTable[0].H = 0;                      // no home for x0-x7
  FunctionTable[0].CR = PdataCrChained;        // stp fp,lr,[sp,#-0x10]!
                                               // mov fp,sp
  FunctionTable[0].FrameSize = 1;              // 16 / 16 = 1
  this->DynamicTable = NULL;
  Result == RtlAddGrowableFunctionTable(
      &this->DynamicTable,
      reinterpret_cast<PRUNTIME_FUNCTION>(FunctionTable),
      1,
      1,
      reinterpret_cast<ULONG_PTR>(pBegin),
      reinterpret_cast<ULONG_PTR>(reinterpret_cast<PBYTE>(pBegin) + nSize)
  );
Arm64X PE files
Article • 08/13/2022
Arm64X is a new type of binary that can contain both the classic Arm64 code and
Arm64EC code together. This makes Arm64x compatible for both the classic Arm64 and
Arm64EC processes on a Windows on Arm device and a particularly good fit for
middleware or plugins that may be used by both ABIs.
Introduced in the Windows 11 SDK, the Arm64X binary is a type of Portable Executable
(PE) file that works with both Windows 11 on Arm and Windows 10 on Arm. To build
Arm64X binaries, see Build Arm64X Binaries.
By default, Arm64X binaries appear to be Arm64 binaries. This allows a system running
Windows 10 on Arm, which does not know the Arm64X format or how to apply
transformations, to load the Arm64X binary into an Arm64 process successfully.
Both x64 and Arm64 applications can load and interact with the binaries in System32 ,
without need for a separate copy of all system binaries like SysWoW64 for x86.
You can support both x64 and Arm64 processes without using Arm64X, but you may
find that it is easier to allow the operating system to handle loading the correct
architecture of binary into a given 64-bit process.
Three conceptual ways to support both architectures on Windows 11 on Arm include:
     Separate binaries: Since standard practices today use separate binaries when
     supporting multiple architectures, you may find that building and shipping
     separate x64 and Arm64 binaries works better for your solution. You can use your
     existing mechanisms to ensure that the correct binary is loaded into the associated
     architecture process.
     Arm64X binary: You can build an Arm64X binary that contains all of the
     x64/Arm64EC and Arm64 code in one binary.
     Arm64X pure forwarder: If you need flexibility of Arm64X but want to avoid
     putting all of your app code into an Arm64X binary, you can choose to use the
     pure forwarder approach, where a small Arm64X binary with no code is used to
     redirect the loader to the correct architecture of DLL.
     A 64-bit COM server that may be called by both x64 or Arm64 apps
     A plugin that may be loaded into either an x64 or Arm64 app
     A single binary that gets injected into an x64 or Arm64 process
In each of these cases, you can use an Arm64X binary or an Arm64X pure forwarder to
enable one binary to support both architectures.
You can build Arm64X binaries, also known as Arm64X PE files, to support loading a
single binary into both x64/Arm64EC and Arm64 processes.
When a user builds a project, Visual Studio would normally compile for Arm64EC and
then link the outputs into an Arm64EC binary. When BuildAsX is set to true , Visual
Studio will instead compile for both Arm64EC and Arm64. The Arm64EC link step is then
used to link both together into a single Arm64X binary. The output directory for this
Arm64X binary will be whatever the output directory is set to under the Arm64EC
configuration.
For BuildAsX to work correctly, the user must have an existing Arm64 configuration, in
addition to the Arm64EC configuration. The Arm64 and Arm64EC configurations must
have the same C runtime and C++ standard library (e.g., both set /MT). To avoid build
inefficiencies, such as building full Arm64 projects rather than just compilation, all direct
and indirect references of the project should have BuildAsX set to true.
The build system assumes that the Arm64 and Arm64EC configurations have the same
name. If the Arm64 and Arm64EC configurations have different names (such as
Debug|ARM64 and MyDebug|ARM64EC ), you can manually edit the vcxproj or
If the desired Arm64X binary is a combination of two separate projects, one as Arm64
and one as Arm64EC, you can manually edit the vxcproj of the Arm64EC project to add
an ARM64ProjectForX property and specify the path to the Arm64 project. The two
projects must be in the same solution.
An Arm64X pure forwarder enables the advantages of using an Arm64X binary even if
there are challenges with building a merged Arm64X binary containing all of the
Arm64EC and Arm64 code. Learn more about Arm64X pure forwarder DLLs in the
Arm64X PE files overview page.
You can build an Arm64X pure forwarder from the Arm64 developer command prompt
following the steps below. The resulting Arm64X pure forwarder will route x64 calls to
foo_x64.DLL and Arm64 calls to foo_arm64.DLL .
   1. Create empty OBJ files that will later be used by the linker to create the pure
     forwarder. These are empty as the pure forwarder has no code in it. To do this,
     create an empty file. For the example below, we named the file empty.cpp. Empty
      OBJ files are then created using cl , with one for Arm64 ( empty_arm64.obj ) and one
C++
        cl /c /Foempty_arm64.obj empty.cpp
        cl /c /arm64EC /Foempty_x64.obj empty.cpp
       If the error message "cl : Command line warning D9002 : ignoring unknown
       option '-arm64EC'" appears, the incorrect compiler is being used. To resolve
       that please switch to the ARM64 Developer Command Prompt .
   2. Create DEF files for both x64 and Arm64. These files enumerate all of the API
     exports of the DLL and points the loader to the name of the DLL that can fulfill
     those API calls.
foo_x64.def :
C++
       EXPORTS
           MyAPI1       =   foo_x64.MyAPI1
           MyAPI2       =   foo_x64.MyAPI2
foo_arm64.def :
C++
       EXPORTS
           MyAPI1       =   foo_arm64.MyAPI1
           MyAPI2       =   foo_arm64.MyAPI2
3. You can then use link to create LIB import files for both x64 and Arm64:
C++
   4. Link the empty OBJ and import LIB files using the flag /MACHINE:ARM64X to
     produce the Arm6X pure forwarder DLL:
C++
The resulting foo.dll can be loaded into either an Arm64 or an x64/Arm64EC process.
When an Arm64 process loads foo.dll , the operating system will immediately load
foo_arm64.dll in its place and any API calls will be handled by foo_arm64.dll .
Visual Studio on Arm-powered devices
Article • 06/17/2024
Visual Studio 2022 version 17.4 is now available as a native Arm64 application       on
Windows 11 Arm64. It's the first version of Visual Studio that natively supports building
and debugging Arm64 apps on Arm-based processors. With Visual Studio 2022 version
17.4 and later, Visual Studio eliminates the dependence on x64 emulation for most
developer workloads.
7 Note
  For Windows 11 Arm64, you must uninstall all previous versions of Visual Studio
  (x64, x86) before installing Visual Studio 2022 version 17.4 or later.
Supported workloads
The Arm64 GA supports the following workloads:
Managed developers
With this release, you can now build desktop applications (Windows Forms and WPF)
using both .NET 6+ and .NET Framework 4.8.1. .NET Framework 4.8.1 is included in the
next major update for Windows 11 and will be available for previous operating systems
in the future.
Native developers
With the Visual Studio 2022 version 17.4 release or later, you can access the new native
Arm64 MSVC (Microsoft Visual C++) compiler toolset, including C++ Code Analysis,
while still targeting all platforms currently supported by MSVC.
ノ Expand table
Many C++ libraries are already available on Arm64. Vcpkg also runs natively on Arm64,
and while some dependent third party tools may still run emulated, you can successfully
build and consume 1700+ C++ libraries directly in your native Arm64 build
environment.
By installing the C++ Desktop workload, you can load any desktop C++ projects and
solutions using MSBuild, and then use the editing, building, and debugging capabilities
you're already familiar with in Visual Studio.
Visual Studio versions before 17.4
Visual Studio 2022 versions before 17.4 can run on ARM-powered devices via x64
emulation, though some features aren't supported on ARM. As such, we don't
recommend running these versions of Visual Studio on devices that use ARM-based
processors, and instead recommend remotely targeted ARM devices.
See Visual Studio 2022 System Requirements for supported operating systems,
hardware, supported languages, and other requirements and guidance.
Feedback
Was this page helpful?    Yes    No
Configure C++ projects for ARM
processors
Article • 07/27/2023
This section of the documentation contains information about how to use the MSVC
build tools to target ARM hardware.
In This Section
Overview of ARM ABI conventions
Describes the application binary interface used by Windows on ARM for register usage,
calling conventions and exception handling.
Related Sections
Get started with Arm64EC
Describes how to get started building your app or project using Arm64EC.
ARM intrinsics
Describes compiler intrinsics for processors that use the ARM architecture.
ARM64 intrinsics
Describes compiler intrinsics for processors that use the ARM64 architecture.
Update app architecture from Arm32 to
Arm64
Article • 09/27/2023
This guide will cover the recommended steps for changing an existing app with support
for 32-bit Arm platform architecture over to the more updated 64-bit Arm architecture
by adding the necessary configuration using Visual Studio. This update will help your
app to run on the latest Windows on Arm devices which use 64-bit Arm
(ARM64/AArch64) processors.
This topic is relevant for UWP apps that do not have an ARM64 target. Older UWP
project templates generated an ARM32 (or AArch32) target, but did not include support
for ARM64 (AArch64).
To check the current Solution Platform for your app to see if ARM64 is present, open
your app project code in Visual Studio and in the "Solution Platforms" drop-down menu
on the Standard toolbar, select Configuration Manager... (also available in the Build
menu) where you will be able to view the list of Solution Platforms and confirm whether
ARM64 is present.
7 Note
(Optional): Check that your app binary is now built for Arm64 architecture by opening
your project directory in PowerShell (right-click your app project in Visual Studio
Solution Explorer and select Open in Terminal). Change directories so that your project's
new bin\ARM64\Release directory is selected. Enter the command: dumpbin .\
<appname>.exe (replacing <appname> with the name of your app). Then enter the
(optionally) removing the previous Arm32 binaries. For more information on options,
see Publish your app in the Microsoft Store.
Troubleshooting
If you run into issues while porting your Arm32 app to Arm64, here are a few common
solutions.
     For 3rd party dependencies, we recommend filing a request for the maintainer to
     rebuild with ARM64 support.
     The last choice would be to remove and/or replace the dependency on your app
     project.
Starting with WDK version 10.0.26100.1 (released May 22, 2024), the WDK now supports
development, testing, and deployment of drivers on Arm64 machines. The WDK can be
installed and run natively on Arm64 hardware, in addition to the previously supported
emulation of x86 KMDF/UMDF2 drivers on Arm64 hardware. There is also support for
debugging and deployment of drivers to an Arm64 target machine from both Arm64
and x64 host machines. The process of installing the WDK on Arm64 machines will
automatically identify and install all the necessary dependencies including build tools,
binaries, and libraries.
This page describes how to build an Arm64 driver with the WDK.
Setup
   1. Download Visual Studio 2022          . You'll need at minimum version 17.0.0 or later.
      Ensure that you have the following components installed:
   3. Download the Windows SDK. Ensure that you have SDK version 16299 (Windows
      10, version 1709) or later.
4. Download the WDK. Ensure that you have WDK version 16299 or later.
 4. From Type or Select new Platform, select Arm64. Copy settings from Win32.
   Select OK and Close.
See Also
   Debugging Arm64
   Windows on Arm
   HLK Arm64 Getting Started Guide
Feedback
Was this page helpful?      Yes     No
In general, developers debugging user mode apps should use the version of the
debugger that matches the architecture of the target app. Use the Arm64 version of
WinDbg to debug user mode Arm64 applications and use the Arm version of WinDbg to
debug user mode ARM32 applications. Use the x86 version of WinDbg to debug user
mode x86 applications running on Arm64 processors.
In rare cases where you need to debug system code – such as WOW64 or CHPE – you
can use the Arm64 version of WinDbg. If you're debugging the Arm64 kernel from
another machine, use the version of WinDbg that matches the architecture of that other
machine.
The debugging tools are located in the Debuggers folder in the kit installation directory.
The x86 tools are under Debuggers\x86 , the ARM32 tools are under Debuggers\Arm , and
the Arm64 tools are under Debuggers\Arm64 .
      There are 32 general purpose registers - x0 to x28 and fp, lr, sp.
      Program counter register, pc, isn't a general purpose register.
      All general purpose registers and pc register are 64-bit in width.
      At most two active data breakpoints for execution and two active data breakpoints
      for read/write memory. For more information, see Processor Breakpoints.
     .effmach x86: Switch to and see x86 context, simulating the effect of using x86
     WinDbg.
     .effmach arm64: Switch to and see Arm64 context
     .effmach chpe: Switch to and see CHPE context.
For more information about the .effmach, see .effmach (Effective Machine).
When debugging x86 apps in user mode, regardless of which WinDbg version you're
using, be aware of these considerations.
For general information about ARM64 and WOW64, see Running 32-bit Applications in
the 64-bit Windows programming guide.
See Also
     Building Arm64 Drivers with the WDK
     Windows Community Standup discussing the Always Connected PC
Troubleshooting x86 desktop apps
Article • 05/24/2022
) Important
  With Visual Studio 2017 or later, it is possible recompile your app to Arm64 or
  Arm64EC so that your app runs at full native speed. For more info about compiling
  as Arm64, see the blog post: Official support for Windows 10 on Arm
  development          . For information about Arm64EC, see Announcing Arm64EC:
  Building Native and Interoperable Apps for Windows 11 on Arm .
If an x86 desktop app doesn't work the way it does on an x86 machine, here's some
guidance to help you troubleshoot.
Issue Solution
 Your app relies on a     Recompile your x86 driver to Arm64. See Building Arm64 Drivers with the
 driver that isn't        WDK.
 designed for Arm.
 Your app is available    If you develop for Microsoft Store, submit an Arm version of your app. For
 only for x64.            more info, see App package architectures. If you're a Win32 developer, we
                          recommend you recompile your app to Arm64. For more info see Early
                          preview of Visual Studio support for Windows 10 on Arm development .
 Your app uses an         Use the DirectX mode of the app, if it's available. x86 apps that use
 OpenGL version later     DirectX 9, DirectX 10, DirectX 11, and DirectX 12 will work on Arm. For
 than 1.1 or requires     more info, see DirectX Graphics and Gaming.
 hardware-
 accelerated OpenGL.
 Your x86 app does        Try using the Compatibility Troubleshooter by following guidance from
 not work as              Program Compatibility Troubleshooter on Arm. For some other
 expected.                troubleshooting steps, see the Troubleshooting x86 apps on Arm article.
      Try to install the x64 version of itself, which isn't supported on Arm.
      Check for other software under the native registry view.
        Assume that a 64-bit .NET framework is available.
Generally, an app should not make assumptions about the host system when it is
determined to run under WOW. Avoid interacting with native components of the OS as
much as possible.
An app may place registry keys under the native registry view, or perform functions
based on the presence of WOW. The original IsWow64Process indicates only whether
the app is running on an x64 machine. Apps should now use IsWow64Process2 to
determine whether they're running on a system with WOW support.
Drivers
All kernel-mode drivers, User-Mode Driver Framework (UMDF) drivers, and print drivers
must be compiled to match the architecture of the OS. If an x86 app has a driver, then
that driver must be recompiled for Arm64. The x86 app may run fine under emulation
however, its driver will need to be recompiled for Arm64 and any app experience that
depends on the driver will not be available. For more info about compiling your driver
for Arm64, see Building Arm64 Drivers with the WDK.
Shell extensions
Apps that try to hook Windows components or load their DLLs into Windows processes
will need to recompile those DLLs to match the architecture of the system; i.e. Arm64.
Typically, these are used by input method editors (IMEs), assistive technologies, and
shell extension apps (for example, to show cloud storage icons in Explorer or a right
click Context menu). To learn how to recompile your apps or DLLs to Arm64, see the
Early preview of Visual Studio support for Windows 10 on Arm development        blog
post.
Debugging
To investigate your app's behavior in more depth, see Debugging on Arm to learn more
about tools and strategies for debugging on Arm.
Virtual Machines
The Windows Hypervisor platform is not supported on the Qualcomm Snapdragon 835
Mobile PC Platform. Hence, running virtual machines using Hyper-V will not work. We
continue to make investments in these technologies on future Qualcomm chipsets.
Dynamic Code Generation
X86 desktop apps are emulated on Arm64 by the system generating Arm64 instructions
at runtime. This means if an x86 desktop app prevents dynamic code generation or
modification in its process, that app cannot be supported to run as x86 on Arm64.
If your Arm32 or Arm64 UWP app isn't working correctly on Arm, here's some guidance
that may help.
7 Note
  To build your UWP application to natively target the Arm64 platform, you must
  have Visual Studio 2017 version 15.9 or later, or Visual Studio 2019. For more
  information, see this blog post   .
Common issues
Here are some common issues to keep in mind when troubleshooting Arm32 and
Arm64 apps.
These issues can be resolved by: removing the unavailable dependencies and rebuilding
the app by using the latest Microsoft Visual Studio and UWP SDK versions; or as a last
resort, removing the Arm app from the Microsoft Store, so that the x86 version of the
app (if available) is downloaded to users' PCs.
For more info on .NET APIs available for UWP apps, see .NET for UWP apps
Debugging
You can use existing tools for developing apps for the Arm platform. Here are some
helpful resources.
     Visual Studio 15.5 Preview 1 and later supports running Arm32 apps by using
     Universal Authentication mode. This automatically bootstraps the necessary remote
     debugging tools.
     See Debugging on Arm64 to learn more about tools and strategies for debugging
     on Arm.
Program Compatibility Troubleshooter
on Arm
Article • 09/01/2022
Emulation to support x86 apps is a new feature created for Windows on Arm64.
Sometimes the emulation performs optimizations that don't result in the best
experience. You can use the Program Compatibility Troubleshooter to toggle emulation
settings for your x86 app, reducing the default optimizations and potentially increasing
compatibility.
If you click on Troubleshoot program you will be presented with the following options.
All options enable the settings that are applicable and applied on Windows Desktop
PCs. In addition, the first, second, and fourth options apply the Disable application cache
and Disable hybrid execution mode emulation settings.
2 Warning
You can toggle emulation settings by right-clicking the executable and selecting
Properties.
 Emulation      Result
 setting
 Disable        The operating system will cache compiled blocks of code to reduce emulation
 application    overhead on subsequent executions. This setting requires the emulator to
 cache          recompile all app code at runtime.
 Disable        Compiled Hybrid Portable Executable (CHPE), binaries are x86 compatible binaries
 hybrid         that include native Arm64 code to improve performance, but that may not be
 execution      compatible with some apps. This setting forces use of x86-only binaries.
 mode
 Strict self-   Enable this to ensure that any self-modifying code is correctly supported in
 modifying      emulation. The most common self-modifying code scenarios are covered by the
 code           default emulator behavior. Enabling this option significantly reduces performance
 support        of self-modifying code during execution.
 Disable RWX    This optimization improves the performance of code on readable, writable, and
 page           executable (RWX) pages, but may be incompatible with some apps.
 performance
 optimization
Windows invites you to code as you are. Use whatever coding language or framework
you prefer - whether developing with tools on Windows or with Linux tools on the
Windows Subsystem for Linux, this guide will help you get set up and install what you
need to start coding, debugging, and accessing services to put your work into
production.
Developer tools
Dev Home
Monitor your work in the centralized dashboard, GitHub and System performance
widgets. Get setup and onboard new projects with the Machine configuration tool.
Install Dev Home
Dev Drive
Improve performance by storing project files on a Dev Drive and keep files secure with
trust designation, antivirus configuration, and attached filters.
Create a Dev Drive
WinGet Configuration
Consolidate manual machine setup and project onboarding to a single command that is
reliable and repeatable.
Author a configuration file
Windows Subsystem for Linux
Use your favorite Linux distribution fully integrated with Windows (no more need for
dual-boot).
Install WSL
Windows Terminal
Customize your terminal environment to work with multiple command line shells.
Install Terminal
Windows Package Manager
Use the winget.exe client, a comprehensive package manager, with your command line
to install applications on Windows.
Install Windows Package Manager
Microsoft PowerToys
Tune and streamline your Windows experience for greater productivity with this set of
power user utilities.
Install PowerToys
Windows Subsystem for Android
Windows Subsystem for Android™️support ends March 5, 2025.
Learn more
https://learn-video.azurefd.net/vod/player?id=54e6c532-a86c-4a39-81ab-
40e28ce2ba96&locale=en-us&embedUrl=%2Fwindows%2Fdev-environment%2F
Windows Copilot
The first PC platform to provide centralized AI assistance and designed to help people
easily take action and get things done is coming soon! See the Blog announcement .
https://learn-video.azurefd.net/vod/player?id=72ad293b-b7aa-4a78-9111-
46eb0e072d7b&locale=en-us&embedUrl=%2Fwindows%2Fdev-environment%2F
Development paths
Get started with JavaScript
Get started with JavaScript by setting up your development environment on Windows or
Windows Subsystem for Linux and install Node.js, React, Vue, Express, Gatsby, Next.js, or
Nuxt.js.
Visual Studio
An integrated development environment that you can use to edit, debug, build code,
and publish apps, including compilers, intellisense code completion, and many more
features.
Install Visual Studio
Azure
A complete cloud platform to host your existing apps and streamline new development.
Azure services integrate everything you need to develop, test, deploy, and manage your
apps.
Set up an Azure account
.NET
An open source development platform with tools and libraries for building any type of
app, including web, mobile, desktop, gaming, IoT, cloud, and microservices.
Install .NET
You can also use Windows Terminal to open all of your favorite command line tools in
the same window with multiple tabs, or in multiple panes, whether that's PowerShell,
Windows Command Prompt, Ubuntu, Debian, Azure CLI, Oh-my-Zsh, Git Bash, or all of
the above.
     Keyboard shortcuts
     Trackpad shortcuts
     Terminal and shell tools
     Apps and utilities