0% found this document useful (0 votes)
100 views118 pages

Windows Arm

This document provides comprehensive guidance on building and deploying desktop applications for Windows 10 and 11, with a focus on Arm-based devices. It covers development tools, design principles, deployment strategies, and the importance of creating Arm-native applications for optimal performance. Additionally, it includes instructions for adding Arm support to existing apps and troubleshooting common issues.

Uploaded by

michael.hong
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
100 views118 pages

Windows Arm

This document provides comprehensive guidance on building and deploying desktop applications for Windows 10 and 11, with a focus on Arm-based devices. It covers development tools, design principles, deployment strategies, and the importance of creating Arm-native applications for optimal performance. Additionally, it includes instructions for adding Arm support to existing apps and troubleshooting common issues.

Uploaded by

michael.hong
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 118

Tell us about your PDF experience.

Build desktop apps for Windows


This documentation provides the latest guidance about building desktop apps for
Windows 11 and Windows 10.

Get ready for development

b GET STARTED

Start here!

An overview of Windows development options

Learning paths and modules

Code samples

Design and UI

e OVERVIEW

Design and code your app UI

Windows 11 design principles

Make your apps great on Windows 11

Windows developer FAQ

Deploy and Publish

e OVERVIEW

Deployment overview

Deploy apps

The MSIX app package format

Publish Windows apps and games

Artificial Intelligence and Machine Learning


p CONCEPT

Windows AI

GitHub Copilot extension for Visual Studio

Windows Machine Learning

Windows Machine Learning samples

Community and support

e OVERVIEW

Microsoft Q&A Forum

@WindowsDocs

OneDevMinute on YouTube

Windows developer support

Platforms

Y ARCHITECTURE

Windows App SDK

.NET MAUI

Universal Windows Platform (UWP)

Windows Presentation Foundation (WPF)

Windows Forms

Other development technologies

API Reference

i REFERENCE

WinRT API for the Windows SDK


Win32 API for the Windows SDK

WinRT API for the Windows App SDK

Win32 API for the Windows App SDK

WinUI API reference


Windows on Arm
Article • 12/21/2023

Windows has traditionally run on machines that are powered by x86 / x64 processors,
but more recently, also runs on devices powered by Arm processors.

Arm-powered devices are particularly interesting because the power-frugal nature of


the Arm architecture enables these devices to offer longer battery life while delivering
great performance. Arm Systems on Chip (SoC) often include other key features such as
a powerful CPU, GPU, Wi-Fi & mobile data networks, as well as Neural Processor Units
(NPUs) for accelerating AI workloads.

Build Windows apps that run on Arm


Windows 10 enables existing unmodified x86 apps to run on Arm devices. Windows 11
adds the ability to run unmodified x64 Windows apps on Arm devices! This ability to run
x86 & x64 apps on Arm devices gives end-users confidence that the majority of their
existing apps & tools will run well even on new Arm-powered devices.

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.

Arm developer tools


Microsoft is working to deliver an Arm-native developer toolset that includes Arm-
native Visual Studio 2022, VSCode, VC++ toolchain, classic .NET Framework, modern
.NET, and Java. Microsoft is also working with several 3rd parties and open-source
communities to port common tools, runtimes, frameworks and libraries to natively
target Windows on Arm. See the announcement from Build 2022 about this
comprehensive suite of tools, services, and devices that enable developers to build and
port apps that natively target Arm just as easily as when targeting x64.

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.

Learn more about using Windows on Arm Virtual Machines:

Windows on Arm Virtual Machine FAQ


Azure Virtual Machines with Ampere Altra Arm–based processors—generally
available
Deploy an Arm-based Azure Kubernetes Service (AKS) Cluster using Terraform -
ARM Developer Hub
Learn more about build and test automation via Continual Integration / Continual
Deployment (CI/CD) hosted in the cloud, such as Azure DevOps or GitHub .

Arm developer devices


Developers need Arm devices upon which to build and test Arm-native Windows apps.
Several Arm-powered devices are already available from Microsoft partners. These
portable devices, whether a laptop form-factor device or convertible-tablet, offer great
performance, battery life, and run the growing array of Arm-native developer tools.

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 - Build apps for Windows 11 on Arm

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

Support for existing Windows apps on Arm


Windows on Arm runs native Arm apps, as well as many unmodified x86 & x64 apps, but
for the best performance and battery life, apps should be built to be Arm-native
wherever possible. Windows apps can be built using many different tools and
technologies, including native C/C++ Win32 apps, classic .NET Framework
WinForms/WPF apps, modern .NET or MAUI apps, or even apps built using Java, Python,
node, etc.

Find tools for Arm development

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.

Additional tips for developing Windows apps


that run on Arm devices
We recommend using MSIX to package your app for distribution. For more
information on how MSIX supports Arm and Arm64, see App package
architectures: Arm and Arm64.

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.

For more information about architectures, see App package architectures.

App Assure Arm Advisory Service


While our guidance to Add Arm support to your Windows app walks through how to
create an Arm-optimized version of your app(s). 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. Learn more .

Sign up for Windows Arm Advisory Service .

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.

Emulation on Arm-based devices for x86 or x64


Windows apps
Arm versions of Windows 10 include emulation technology that enables existing
unmodified x86 apps to run on Arm devices. Windows 11 extends that emulation to run
unmodified x64 Windows apps on Arm-powered devices.

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:

Optimizing the power-consumption of your app to extend device battery life.


Optimizing performance for CPU, GPU, and NPUs to accelerate workflows,
particularly when working with AI.

Additionally, Kernel drivers are required to be built as native Arm64. There is no


emulation present in the kernel. This primarily impacts virtualization scenarios. For apps
that utilize device drivers requiring direct access to the internals of the OS or hardware
running in kernel mode, rather than user mode, and that have not yet been updated to
support Arm64 processors, see Building Arm64 Drivers with the WDK.

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:

Visual Studio 2022 v17.x​(Recommended)


Visual Studio 2019 v16.x​
Visual Studio 2017 v15.9 onwards (UWP, Desktop bridge, win32 C++)​
LLVM (Clang) v12+

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.

Steps to add Arm64 native support


To update your app to run natively on Arm64:

1. Add an Arm64 configuration to your project in Visual Studio


2. Test and debug the newly built Arm64 app
3. Build and test your app on Arm devices

Once you've confirmed that your app has successfully been optimized for Arm devices:

4. Update your installer and publish your updated app


5. Plan for ongoing updates
Step 1 - Add an Arm64 configuration to your
project in Visual Studio
To add a new ARM64 solution platform with debug and release targets to your existing
x64 or x86 app project:

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) .

Step 2 - Test and debug the newly built Arm64


app
To check whether your Arm64 Solution builds successfully after adding the Arm64
solution platform to your project in Visual Studio:

1. Close the "Active solution platform" window.


2. Change the build setting from Debug to Release.
3. In the "Build" drop-down menu, select Rebuild Solution and wait for the project to
rebuild.
4. You will receive a "Rebuild All succeeded" output. If not, see the Troubleshooting
section below.

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.

Windows on Arm hardware or virtual machines available


for testing
If you are looking for hardware to use for Continuous Integration (CI) and testing, these
are a few of the Windows devices with an Arm64-based processor:

Windows Dev Kit 2023


Surface Pro 9 5G
Lenovo x13s

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".

Step 3 - Build and test your app on Arm


devices
Adding a test automation pass is an important considering for your Continuous
Integrations and Continuous Delivery (CI/CD) strategy. For Arm64 solutions running on
Windows, it’s important to run your test suite on Arm64 architecture -- this could be
actual Windows on Arm hardware, using one of the Arm devices listed above, or a
Virtual Machine, from the VMs listed above.

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.

Step 4 - Update your installer and publish your


updated app
If you publish to the Microsoft Store, once you have built an Arm64 version of your app
by following the steps above, you can update your existing app package in the
Microsoft Store by visiting your Partner Center dashboard and adding the newly built
ARM64 binaries to the submission.

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.

Step 5 - Plan for ongoing updates


Now that you have an Arm64 version of your app published, you’ll want to ensure that it
stays updated the same way that other versions of your app do. It’s best to keep
versions and features aligned across architectures to avoid customer confusion in the
future.

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.

A dependency not compiled for ARM64 is blocking you


from a successful build
If you can’t build due to a dependency, whether internal, from a 3rd party, or from an
open-source library, you will need to either find a way to update that dependency to
support ARM64 architecture or remove it.

For internal dependencies, we recommend rebuilding the dependency for ARM64


support.

For 3rd party dependencies, we recommend filing a request for the maintainer to
rebuild with ARM64 support.

For open source dependencies, consider checking vcpkg to see if a newer


version of the dependency that includes ARM64 support exists that you can
update to. If no update exists, consider contributing the addition of ARM64
support to the package yourself. Many open source maintainers would be thankful
for the contribution.

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.

Consider using Arm64EC. Arm64EC versions of dependencies can be used to


rebuild an application while still utilizing x64 versions of dependencies. Any x64
code, including code from dependencies, in an Arm64EC process will run under
emulation in your app. (Arm64 versions of dependencies won't be usable in this
case.)

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 .

Your app relies on a kernel driver


Kernel drivers are required to be built as native Arm64. There is no emulation present in
the kernel. This primarily impacts virtualization scenarios. For apps that utilize device
drivers requiring direct access to the internals of the OS or hardware running in kernel
mode, rather than user mode, and that have not yet been updated to support Arm64
processors, see Building Arm64 Drivers with the WDK.

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.

Toolchain for Windows on Arm


In addition to support for Visual Studio and LLVM (CLANG) as shared in the
Prerequisites section of this guide, the following tools and frameworks are also
supported for Arm64:

.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.

As well as 3rd-party frameworks, including:

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 .

Sign up for Windows Arm Advisory Services .


Quickstart: Create a Windows on Arm
virtual machine in the Azure portal
(Preview)
Article • 12/21/2023

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 .

Create virtual machine


1. Enter virtual machines in the search.

2. Under Services, select Virtual machines.

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.

10. Under Administrator account, provide a username, such as azureuser and a


password. The password must be at least 12 characters long and meet the defined
complexity requirements.

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.

15. After deployment is complete, select Go to resource.


Connect to virtual machine


Create a remote desktop connection to the virtual machine. These directions tell you
how to connect to your VM from a Windows computer. On a Mac, you need an RDP
client such as this Remote Desktop Client from the Mac App Store.

1. On the overview page for your virtual machine, select Connect > Connect.

2. In the Native RDP section, click Download RDP file.


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

Remember to configure the time zone correctly to match your requirements, as


(UTC) Coordinated Universal Time is the default setting in the Time zone dropdown.

For more information see Auto-shutdown.

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.

Azure Windows virtual machine 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.

Arm Advisory Service

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.

Windows on Arm FAQs


Do I need to create an Arm version of my
Windows app?
Arm-based devices are becoming increasingly popular. While Windows supports
emulation for apps with an x64 or x86-based architecture, providing an Arm-native build
of your Windows app will improve performance and give your customers the best
experience when using an Arm-based device.

Why update my app to Arm when x64 and x86


are both supported via emulation on Windows
11?
While your customers will be able to use your x64 or x86-based app on their Arm
device, there is performance overhead that comes with emulation. Treat your customers
to an Arm-native version of your Windows apps for the best performance,
responsiveness, and optimized battery life on their Arm device.

How do I add an Arm64 native configuration to


my Windows app?
See Add Arm support to your Windows app.

Are drivers supported by emulation? Or do I


have to add Arm64 native support to any drivers
used in my app?
In all cases, kernel-mode drivers and user-mode print drivers MUST be built as native
Arm64 binaries to work on Arm64 devices. Additionally, you cannot execute an x86 or
x64 setup program to install an Arm64 driver on Arm64 devices. See Building Arm64
Drivers with the WDK.

Does Visual Studio work on Arm?


Both Visual Studio and Visual Studio Code offer Arm-native versions for you to install.

How do I test and debug on Arm64?


See Add Arm support to your Windows app - Test and Debug. For testing, you will need
a Windows on Arm device, such as the Windows Dev Kit 2023, or you can run a
Windows 11 Arm64 virtual machine.

What if I am blocked from building an Arm-


native version of my app by a 3rd party
dependency?
There are a few ways to address the scenario when your Windows app relies on a 3rd-
party dependency that has not (yet) been updated for Arm. We first recommend
reaching out to the owner of the dependency to ask whether there are plans to update
the dependency to support Arm. You might also check resources like NuGet or
vcpkg , or reach out to the Arm Open Source community for help updating
dependencies (such as Linaro ). You could rewrite the dependency yourself -
Microsoft's "App Assure program " may be able to help. Or you may want to write
your app using Arm64EC, which enables you to mix x64 and Arm64 code for a partial
Arm-native implementation focused on optimizing the most important aspects of your
app for Arm.

How do I update my C++ Windows app to


support Arm devices?
See Configure C++ projects for Arm processors.

What Program Files folder should I use to install


my ARM64 application?
You may notice the following directories on your Windows device, used for installing
and storing different application file types.

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

about How x86 emulation works on Arm.

C:\Program Files (Arm) : This directory was used for 32-bit Arm applications, which

are no longer being supported in future versions of Windows.

Windows on Arm Virtual Machine FAQ


How do I create a Windows 11 Arm64 Virtual
Machine (VM)?
For help on how to create and deploy Windows 11 Arm64 VMs with with Ampere Altra
Arm–based processors on Azure, see Quickstart: Create a Windows on Arm virtual
machine in the Azure portal.

Can I host a Windows 11 virtual machine using


Hyper-V on a Windows 11 Arm64 device?
While Hyper-V is a supported component on Windows 11 Arm64 devices, Hyper-V is
not a supported Windows 11 virtual machine host since there is no TPM support. Setup
will block if you try installing Windows 11 from an ISO. Windows Hyper-V Manager will
display an "Operation failed" error message if you try to enable TPM on a virtual
machine. There is work in-progress to add virtual TPM support in a future Windows on
Arm VM.

Are there any trainings available for adding Arm


support to a Windows app and running a VM for
testing and debugging?
Yes, see Introduction to Windows on Arm - Port a .NET application to natively support
Arm-based processors.
Is CPU Sampling with the Windows Performance
Recorder tool supported on ARM-based Hyper-V
virtual machines?
Yes, but the feature is currently only available in the Windows Insider Preview Canary
channel. If you are willing to enroll your physical ARM device that is hosting your virtual
machines in the Canary channel, CPU sampling will be supported in virtual machines; the
feature is already supported on physical ARM devices. Sample usage: wpr -start cpu .

6 Collaborate with us on Windows on Arm feedback


GitHub
Windows on Arm is an open source
The source for this content can project. Select a link to provide
be found on GitHub, where you feedback:
can also create and review
issues and pull requests. For  Open a documentation issue
more information, see our
contributor guide.  Provide product feedback
How x86 emulation works on Arm
Article • 10/23/2023

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.

Arm versions of Windows 10 include emulation technology that enables existing


unmodified x86 apps to run on Arm devices. Windows 11 extends that emulation to run
unmodified x64 Windows apps on Arm-powered devices.

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.

Updating to support an Arm64 version of your


app
While the ability to emulate x64 and x86 on Arm devices is a great step forward, your
app may be able to take advantage of native performance gains and the unique
qualities of Arm64-powered devices by updating to support an Arm64 version of the
app.

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:

RJ45 for ethernet


3 x USB-A ports
2 x USB-C ports
Bluetooth & WiFi

The device supports up to three displays by using the mDP port and the two USB-C
ports.

7 Note

Unified Extensible Firmware Interface (UEFI) replaces the standard basic


input/output system (BIOS) with new features including faster startup and
improved security. You can use UEFI to manage the firmware features on your
device.

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:

Seeing the startup logo when turning on the device.


Booting into the UEFI to change firmware settings.
Installing the recovery image for the device, downloaded from the Recovery Image
page .
BitLocker processes (such as a recovery key prompt or a pre-boot PIN).
Any Windows OS boot (startup) activity that requires seeing something on the
screen before Windows loads, like a Windows startup error or a bug-check boot
loop.
Windows Automatic Recovery.
Booting into Windows Recovery Environment (WinRE) or Windows PE (WinPE)
using a USB boot disk.
Taking ownership of firmware using SEMM.

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.

Ports Transmission Max Data Supported Displays (max Comments


Mode Speed resolution)

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)

3840x2160 @ 60Hz (RB2), MST: without UEFI


(x2) 2560x1600 @ 60Hz (CVT, RB) menu

Install Arm-native developer tools


A fully Arm-native suite of developer tools are available for installing on Windows 11,
including:

Visual Studio 2022 17.4 for Arm64

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).

.NET 7 Arm64 SDK

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 .

Visual Studio Code for Arm

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:

ONNX Runtime + Windows Dev Kit 2023 = NPU powered AI

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.

Qualcomm developer network: Windows on Snapdragon

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.

QNN Execution Provider for ONNX Runtime

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.

Azure Virtual Machines with Ampere Altra Arm-based processors

Engineered to efficiently run scale-out workloads, web servers, application servers,


open-source databases, cloud-native as well as rich .NET applications, Java
applications, gaming servers, media servers, and more.

Support for building Arm-native apps and porting existing x64 apps is also available,
including:

Arm64EC

Arm64EC (“Emulation Compatible”) is a new application binary interface (ABI)


enabling you to build new native apps or incrementally transition existing x64 apps
to take advantage of the native speed and performance possible with Arm-
powered devices, including better power consumption, battery life, and
accelerated AI & ML workloads.

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.

Additional developer tools supported by Windows 11 on Arm, include:

Windows Subsystem for Linux

Enabling Linux distributions to be installed on Windows without the overhead of a


traditional virtual machine or dual-boot setup.

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.

Windows Package Manager


Offering a comprehensive package manager solution that consists of a command
line tool (winget) and set of services for installing applications that will choose the
best available package based on your hardware architecture.

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.

Windows Subsystem for Android

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.

To boot your dev kit device from a recovery drive:

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.

3. Double-click on USB Storage to boot to the USB key.

How do I update a driver to work on a Windows 11 Arm-based PC?

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.

Does this device support assistive technology?

Windows 11 provides built-in accessibility features that help you do more on


your device, in addition to assistive technology apps in the Microsoft Store, such as
the OneStep Reader or the Read &Write extension for Microsoft Edge. NVDA
also offers a Windows 11 Arm-based screen reader (see the NV Access download
site ). Check the Microsoft Store or contact your assistive software vendor to
see if your preferred apps are available for a Windows 11 Arm-based PC.

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.

Are custom OS images supported?

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 learn more, see FAQs for Windows Arm-based PCs .


Windows Dev Kit 2023 update history
Article • 09/15/2023

To keep your device performing its best, make sure that you have the latest updates.

Check for Windows updates

Check for optional updates

September 2023 updates


The following update is available for Surface Pro 9 with 5G (SQ3 Processor) devices
running Windows 11 Update, Version 22H2, or greater. This update addresses critical
security vulnerability and improves system stability.

Driver Version Device Manager

4.15.12412.20015 ELAN WBF Fingerprint Sensor

6.196.139.0 Microsoft Devices Telemetry Service

1.0.3551.9400 Qualcomm(R) ACPI Bridge Device

30.0.3776.0300 Qualcomm(R) Adreno(TM) 8cx Gen 3

30.0.3741.8500 Qualcomm(R) Adreno(TM) 8cx Gen 3 Surface Extension

1.0.3551.9400 Qualcomm(R) BAM Bus Device

1.0.3741.8500 Qualcomm(R) Bluetooth Radio Driver

1.0.3741.8500 Qualcomm(R) Bluetooth UART Transport Driver

1.0.3741.8500 Qualcomm(R) Combined Subsystem Driver

1.0.3741.8500 Qualcomm(R) Compute DSP Subsystem Device Extension

1.0.3551.9400 Qualcomm(R) Data IPC Router Device

1.0.3638.8100 Qualcomm(R) Device Crash Dump Injector Service

1.0.3638.8100 Qualcomm(R) FastRPC Device

1.0.3551.9400 Qualcomm(R) GPI Bus Device

1.0.3611.3300 Qualcomm(R) I2C Bus Device

1.0.3586.6600 Qualcomm(R) I2C Bus Device Extension


Driver Version Device Manager

1.0.3600.9600 Qualcomm(R) Inter Processor Communication Interrupt Device

1.0.3681.5800 Qualcomm(R) IOMMU Device

1.0.3681.5800 Qualcomm(R) IOMMU Device Extension

18.52.1.16 Qualcomm(R) Mobile Broadband Update Device

1.0.3681.5800 Qualcomm(R) PCIe Platform Extension Plugin

1.0.3681.5800 Qualcomm(R) Peripheral Image Loader Device

1.0.3681.5800 Qualcomm(R) Peripheral Image Loader Device Extension

1.0.3741.8500 Qualcomm(R) Peripheral Image Loader Device Extension

1.0.3638.8100 Qualcomm(R) Power Management FGBCL Device

1.0.3726.4700 Qualcomm(R) Power Management PMIC Apps Device

1.0.3638.8100 Qualcomm(R) Power Management PMIC Device

1.0.3726.4700 Qualcomm(R) Power Management PMIC GLink Device

1.0.3638.8100 Qualcomm(R) Power Management PML0 Extension

1.0.3638.8100 Qualcomm(R) Protection Domain Service Registry Device

18.52.1.8 Qualcomm(R) QMUX Interconnect

1.0.3638.8100 Qualcomm(R) Reset Power Error Notifier Device

1.0.3681.5800 Qualcomm(R) Secure Kernel Extension

1.0.3741.8500 Qualcomm(R) Secure Processor Device

1.0.3741.8500 Qualcomm(R) Secure Processor Subsystem Device Extension

1.0.3681.5800 Qualcomm(R) Shared Memory Port Device

1.0.3638.8100 Qualcomm(R) Slimbus Device

1.0.3681.5800 Qualcomm(R) SOC Partition Interface Device

1.0.3564.4300 Qualcomm(R) SPI Device

1.0.3551.9400 Qualcomm(R) SPMI Bus Device

1.0.3551.9400 Qualcomm(R) SSG Secure Services Device

1.0.3741.8500 Qualcomm(R) Subsystem Thermal Manager Driver


Driver Version Device Manager

1.0.3638.8100 Qualcomm(R) System Cache Device

1.0.3551.9400 Qualcomm(R) System Manager Device

1.0.3681.5800 Qualcomm(R) System Manager Device

1.0.3681.5800 Qualcomm(R) System Manager Device Extension

1.0.3551.9400 Qualcomm(R) System Manager DiagRouter Device

1.0.3638.8100 Qualcomm(R) System Manager GPIO Device

1.0.3681.5800 Qualcomm(R) System Manager PMIC GPIO Framework Device Extension

1.0.3681.5800 Qualcomm(R) System Manager Power Engine Plug-in Device

1.0.3681.5801 Qualcomm(R) System Manager Qcom Device Extension

1.0.3741.8500 Qualcomm(R) System Manager SCM Device

1.0.3741.8500 Qualcomm(R) System Manager Secapp Device

1.0.3638.8100 Qualcomm(R) System MMU Device

1.0.3681.5800 Qualcomm(R) TFTP Device

1.0.3551.9400 Qualcomm(R) UART Bus Device

1.0.3681.5800 Qualcomm(R) URS Extension

1.0.3726.4700 Qualcomm(R) USB Type-C Device

1.0.3681.5800 Qualcomm(R) USB3(TM) Device Controller

1.0.3681.5800 Qualcomm(R) USB3(TM) eXtensible Host Controller

1.0.3741.8500 Qualcomm(R) WCN685x Wi-Fi 6E Dual Band Simultaneous (DBS) WiFiCx


Network Adapter

1.0.3741.8500 Qualcomm(R) WCN685x Wi-Fi 6E Dual Band Simultaneous (DBS) WiFiCx


Network Adapter Extension

1.0.3551.9400 Qualcomm(R) Windows WLAN Sleep Manager Driver

1.42.137.0 Surface ACPI Wake Alarm

4.95.139.0 Surface Button

6.213.2.0 Surface Integration Service

9.140.139.0 Surface Serial Hub Driver


Driver Version Device Manager

8.150.139.0 Surface SMF Client Driver

2.136.139.0 Surface SPT Core

10.95.137.0 Surface System Telemetry Driver

8.89.139.0 Surface Thermal Policy Driver

2.26.4.0 surface Thunderbolt(TM) 4 Dock Firmware Update

11.1.235.0 Surface UEFI

July 2023 updates


The following update is available for Surface Pro 9 with 5G (SQ3 Processor) devices
running Windows 11 Update, Version 22H2, or greater.

Driver Device Manager


Version

30.0.3741.85 Qualcomm(R) Adreno(TM) 680 GPU - Improves stability and addresses system
bugcheck.

June 2023 updates


The following update is available for Windows Dev Kit 2023 devices running Windows
11 Update, Version 22H2, or greater.

Driver Version Device Manager

30.0.3681.5802 Qualcomm(R) Adreno(TM) 8cx Gen 3 - Improves graphics stability.

255.31.3.0 Surface Integration

November 2022 updates


Driver Device Manager
Version

30.0.3542.22 Qualcomm(R) Adreno(TM) 8cx Gen 3 - Display adapters

30.0.3530.98 Qualcomm(R) Adreno(TM) 8cx Gen 3 Surface - Extension


Driver Device Manager
Version

1.0.3530.98 Qualcomm(R) Aqstic(TM) - Sound, video and game controllers

1.0.3564.43 Qualcomm(R) Aqstic(TM) Audio Adapter Device - Sound, video and game
controllers

1.0.3530.98 Qualcomm(R) Audio DSP Subsystem Device - Extension

1.0.3530.98 Qualcomm(R) Audio RPC Daemon Device - System devices

1.0.3531.25 Qualcomm(R) Bluetooth Radio Driver - Bluetooth

1.0.3531.25 Qualcomm(R) Bluetooth UART Transport Driver - Bluetooth

1.0.3530.98 Qualcomm(R) Combined Subsystem Driver - System devices

1.0.3530.9801 Qualcomm(R) Compute DSP Subsystem Device - Extension

1.0.3530.98 Qualcomm(R) FastRPC Device - System devices

1.0.3508.39 Qualcomm(R) I2C Bus Device - Extension

1.0.3508.39 Qualcomm(R) I2C Bus Device - System devices

1.0.3517.77 Qualcomm(R) IOMMU Device - Extension

1.0.3517.77 Qualcomm(R) IOMMU Device - System devices

18.52.1.11 Qualcomm(R) Mobile Broadband Update Device - Firmware

1.0.3530.98 Qualcomm(R) PCIe Platform Extension Plugin - System devices

1.0.3530.98 Qualcomm(R) Peripheral Image Loader Device - System devices

1.0.3564.43 Qualcomm(R) Power Management PMIC Apps Device - System devices

18.52.1.5 Qualcomm(R) QMUX Interconnect - System devices

1.0.3517.77 Qualcomm(R) Secure Kernel Extension - System devices

1.0.3517.77 Qualcomm(R) Secure Processor Device - System devices

1.0.3530.98 Qualcomm(R) Secure Processor Subsystem Device - Extension

1.0.3517.77 Qualcomm(R) SOC Partition Interface Device - System devices

1.0.3530.98 Qualcomm(R) Subsystem Thermal Manager Driver - System devices

1.0.3517.77 Qualcomm(R) System Manager Device - System devices

1.0.3530.98 Qualcomm(R) System Manager DiagRouter Device - System devices


Driver Device Manager
Version

1.0.3564.43 Qualcomm(R) System Manager Power Engine Plug-in Device - System devices

1.0.3530.98 Qualcomm(R) System Manager Qcom Device Extension - System devices

1.0.3517.77 Qualcomm(R) System Manager SCM Device - System devices

1.0.3517.77 Qualcomm(R) System Manager Secapp Device - System devices

18.52.1.5 Qualcomm(R) Thermal MDM Sensing and Mitigation Driver - System devices

1.0.3530.98 Qualcomm(R) USB Type-C Device - Universal Serial Bus 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.3532.92 Qualcomm(R) Wlan Thermal Mitigation Device Extension - System devices

1.40.137.0 Surface ACPI Wake Alarm - System devices

5.63.139.0 Surface Firmware Update - Firmware

255.30.139.0 Surface Integration Device - System devices

1.0.3530.98 Surface Pro Qualcomm(R) Aqstic(TM) Audio Codec Driver - Sound, video and
game controllers

13.5.139.0 Surface System Aggregator - Firmware

2.49.139.0 Surface UCM UCSI HID Client - Surface Connector Managers

7.31.139.0 Surface UEFI - Firmware


Arm64EC - Build and port apps for
native performance on Arm
Article • 02/06/2023

Arm64EC (“Emulation Compatible”) enables you to build new native apps or


incrementally transition existing x64 apps to take advantage of the native speed and
performance possible with Arm-powered devices, including better power consumption,
battery life, and accelerated AI & ML workloads.

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.

Arm64EC guarantees interoperability with x64 by following x64 software conventions


including calling convention, stack usage, data structure layout, and preprocessor
definitions. However, Arm64EC code is not compatible with code built as Arm64, which
uses a different set of software conventions.

The Windows 11 on Arm operating system itself relies heavily on Arm64EC's


interoperability to enable running x64 applications. Most operating system code loaded
by an x64 app running on Windows 11 on Arm will have been compiled as Arm64EC,
enabling native performance for that code without the application knowing.

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.

Process architecture x64 binary Arm64EC binary Arm64 binary

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.

PE architecture x64 lib Arm64EC lib Arm64 lib

Arm64EC ✔ ✔ ❌

Arm64 ❌ ❌ ✔

✔ = Supported, ❌ = Not supported

For more detail about how the Arm64EC ABI enables interoperability, see Understanding
Arm64EC ABI and assembly code.

Use Arm64EC to make an existing app faster on


Windows 11 on Arm
Arm64EC enables to you to incrementally transition the code in your existing app from
emulated to native. At each step along the way, your application continues to run well
without the need to be recompiled all at once.

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:

1. Starting as a fully emulated x64 workload


2. After recompiling the most CPU-intensive parts as Arm64EC
3. After continuing to recompile more x64 modules over time
4. Ending result of a fully native Arm64EC app

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.

Identifying Arm64EC binaries and apps


Apps running on Windows 11 on Arm will interact with Arm64EC binaries as though
they are x64 binaries. The app does not need to know to what extent the code in the
binary has been recompiled as Arm64EC.

For developers interested in identifying these binaries, you can see them in a developer
command prompt using link /dump /headers .

PowerShell

File Type: EXECUTABLE IMAGE


FILE HEADER VALUES
8664 machine (x64) (ARM64X)

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

If your project is using a pre-Windows 11 SDK or a version of MSVC older


than VS 17.3, you'll need to retarget the solution to use the latest version of
each.

2. To add the Arm64EC platform:

In the Build menu, select Configuration Manager.


In the Active solution platform box, select <New…> to create a new platform.
Select ARM64EC, Copy settings from x64, and check the Create new project
platforms checkbox.

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.

2. Open up the CMakePresets.json file by going to the active configuration dropdown


and selecting Manage Configurations.

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.

Developer Command Prompt


If you are interested in using the Visual Studio Developer Command Prompt for
compiling and linking source files for Arm64EC, you need to use the Arm64 Developer
Command Prompt, and then run your cl and link commands separately. Use the
/arm64EC switch for cl and /MACHINE:ARM64EC for link to build and link Arm64EC code.
C++

cl /arm64EC /c <args>

link /MACHINE:ARM64EC <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

Arm64EC (“Emulation Compatible”) is a new application binary interface (ABI) for


building apps for Windows 11 on Arm. For an overview of Arm64EC and how to start
building Win32 apps as Arm64EC, see Using Arm64EC to build apps for Windows 11 on
Arm devices.

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.

Arm64EC vs Arm64 Classic ABI


The following list points out where Arm64EC has diverged from Arm64 Classic ABI.

Register mapping and blocked registers


Call checkers
Stack checkers
Variadic calling convention

These are small changes when seen in perspective of how much the entire ABI defines.

Register mapping and blocked registers


For there to be type-level interoperability with x64 code, Arm64EC code is compiled with
the same pre-processor architecture definitions as x64 code.

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:

mov x15, <target>


adrp x16, __guard_check_icall_fptr
ldr x16, [x16, __guard_check_icall_fptr]
blr x16 ; check target function
blr x15 ; call function

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.

In Arm64EC, the same call checker use would become:

mov x11, <target>


adrp x9, __os_arm64x_check_icall_cfg
ldr x9, [x9, __os_arm64x_check_icall_cfg]
adrp x10, <name of the exit thunk>
add x10, x10, <name of the exit thunk>
blr x9 ; check target function
blr x11 ; call function

Slight differences from Classic Arm64 include:

Symbol name for the call checker is different.


The target address is supplied in x11 instead of x15 .
The target address ( x11 ) is [in, out] instead of [in] .
There is an extra parameter, provided through x10 , called an “Exit Thunk”.
An Exit Thunk is a funclet which transforms function parameters from Arm64EC calling
convention to x64 calling convention.

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.

Binary Code Unprotected indirect CFG protected indirect call


call

x64 x64 no call checker __guard_check_icall_fptr or


__guard_dispatch_icall_fptr

Arm64 Arm64 no call checker __guard_check_icall_fptr


Classic

Arm64EC x64 no call checker __guard_check_icall_fptr or


__guard_dispatch_icall_fptr

Arm64EC __os_arm64x_check_icall __os_arm64x_check_icall_cfg

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.

Everything said above about __chkstk applies equally to __security_check_cookie and


its Arm64EC counterpart: __security_check_cookie_arm64ec .

Variadic calling convention


Arm64EC follows the Classic Arm64 ABI calling convention, except for Variadic functions
(aka varargs, aka functions with the ellipsis (. . .) parameter keyword).

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:

Only the first 4 registers are used for parameter passing: x0 , x1 , x2 , x3 .


Remaining parameters are spilled onto the stack. This follows the x64 variadic
calling convention exactly, and differs from Arm64 Classic, where registers x0 -> x7
are used.
Floating Point / SIMD parameters being passed by register will use a General-
Purpose register, not a SIMD one. This is similar to Arm64 Classic, and differs from
x64, where FP/SIMD parameters are passed in both a General-Purpose and SIMD
register. For example, for a function f1(int, …) being called as f1(int, double) ,
on x64, the second parameter will be assigned to both RDX and XMM1 . On
Arm64EC, the second parameter will be assigned to just x1 .
When passing structures by value through a register, x64 size rules apply:
Structures with sizes exactly 1, 2, 4 and 8 will be loaded directly into the General-
Purpose register. Structures with other sizes are spilled onto the stack, and a
pointer to the spilled location is assigned to the register. This essentially demotes
by-value into by-reference, at the low-level. On the Classic Arm64 ABI, structures of
any size up to 16 bytes are assigned directly to General-Purposed registers.
X4 register is loaded with a pointer to the first parameter passed via stack (the 5th
parameter). This does not include structures spilled due to the size restrictions
outlined above.
X5 register is loaded with the size, in bytes, of all parameters passed by stack (size
of all parameters, starting with the 5th). This does not include structures passed by
value spilled due to the size restrictions outlined above.

In the following example: pt_nova_function below takes parameters in a non-variadic


form, therefore following the Classic Arm64 calling convention. It then calls
pt_va_function with the exact same parameters but in a variadic call instead.

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

calling convention rules:

‘f ‘ is a double. It will be assigned to d0.


‘tc’ is a struct, with a size of 3 bytes. It will be assigned to x0.
ull1 is an 8-byte integer. It will be assigned to x1.
ull2 is an 8-byte integer. It will be assigned to x2.
ull3 is an 8-byte integer. It will be assigned to x3.

pt_va_function is a variadic function, so it will follow the Arm64EC variadic rules

outlined above:

‘f ‘ is a double. It will be assigned to x0.


‘tc’ is a struct, with a size of 3 bytes. It will be spilled onto the stack and its location
loaded into x1.
ull1 is an 8-byte integer. It will be assigned to x2.
ull2 is an 8-byte integer. It will be assigned to x3.
ull3 is an 8-byte integer. It will be assigned directly to the stack.
x4 is loaded with the location of ull3 in the stack.
x5 is loaded with the size of ull3.

The following shows a possible compilation output for pt_nova_function , which


illustrates the parameter assignment differences outlined above.

stp fp,lr,[sp,#-0x30]!
mov fp,sp
sub sp,sp,#0x10

str x3,[sp] ; Spill 5th parameter


mov x3,x2 ; 4th parameter to x3 (from x2)
mov x2,x1 ; 3rd parameter to x2 (from x1)
str w0,[sp,#0x20] ; Spill 2nd parameter
add x1,sp,#0x20 ; Address of 2nd parameter to x1
fmov x0,d0 ; 1st parameter to x0 (from d0)
mov x4,sp ; Address of the 1st in-stack parameter to x4
mov x5,#8 ; Size of the in-stack parameter area

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.

The following list includes these additions:

Entry and Exit Thunks


Exit Thunks
Entry Thunks
Adjustor Thunks
Fast-Forward Sequences

Entry and Exit Thunks


Entry and Exit Thunks take care of translating the Arm64EC calling convention (mostly
the same as Classic Arm64) into the x64 calling convention, and vice-versa.

A common misconception is that calling conventions can be converted by following a


single rule applied to all function signatures. The reality is that calling conventions have
parameter assignment rules. These rules depend on the parameter type and are
different from ABI to ABI. A consequence is that translation between ABIs will be specific
to each function signature, varying with the type of each parameter.

Consider the following function:

C++

int fJ(int a, int b, int c, int d);

Parameter assignment will occur as follows:

Arm64: a -> x0, b -> x1, c -> x2, d -> x3


x64: a -> RCX, b -> RDX, c -> R8, d -> r9
Arm64 -> x64 translation: x0 -> RCX, x1 -> RDX, x2 -> R8, x3 -> R9

Now consider a different function:

C++
int fK(int a, double b, int c, double d);

Parameter assignment will occur as follows:

Arm64: a -> x0, b -> d0, c -> x1, d -> d1


x64: a -> RCX, b -> XMM1, c -> R8, d -> XMM3
Arm64 -> x64 translation: x0 -> RCX, d0 -> XMM1, x1 -> R8, d1 -> XMM3

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.

Consider the following example:

C++

struct SC {
char a;
char b;
char c;
};

int fB(int a, double b, int i1, int i2, int i3);

int fC(int a, struct SC c, int i1, int i2, int i3);

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:

Code for ‘fA’.


Entry Thunk for ‘fA’
Exit Thunk for ‘fB’
Exit Thunk for ‘fC’

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.

Additional considerations for Exit Thunks:

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

When calling the call checker:

x11 supplies the address of the target function to call ( fB in this case). It may not

be known, at this point, if the target function is Arm64EC or x64.


x10 supplies an Exit Thunk matching the signature of the function being called ( fB
in this case).

The data returned by the call checker will depend on the target function being Arm64EC
or x64.

If the target is Arm64EC:

x11 will return the address of the Arm64EC code to call. This may or may not be
the same value that was provided in.

If the target is x64 code:

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

was provided in via x11 .

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

The address of the target function is provided by the emulator in x9 .

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 .

When it comes to non-volatile SIMD registers, there is a significant difference between


the Arm64 and x64 calling conventions. On Arm64, the low 8 bytes (64 bits) of the
register are considered non-volatile. In other words, only the Dn part of the Qn registers
is non-volatile. On x64, the entire 16 bytes of the XMMn register is considered non-
volatile. Furthermore, on x64, XMM6 and XMM7 are non-volatile registers whereas D6 and
D7 (the corresponding Arm64 registers) are volatile.

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.

Below is a real-world example:


[thunk]:CObjectContext::Release`adjustor{8}':
sub x0,x0,#8
b CObjectContext::Release

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.

In the example above, consider how the code looks in Arm64EC.

Adjustor Thunk in Arm64EC

[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

Adjustor Thunk’s Entry Trunk

[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.

At the high-level, the hooking process is simple. In detail, however, hooking is


architecture specific and quite complex given the potential variations the hooking logic
must address.

In general terms, the process involves the following:

Determine the address of the function to hook.


Replace the first instruction of the function with a jump to the hook routine.
When the hook is done, come back to the original logic, which includes running
the displaced original instruction.

The variations arise from things like:

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.

Consider the following example (error handling omitted for brevity):

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.

This is an example of a Fast-Forward Sequence:

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
[...]

It would be quite inefficient if it was required to run 5 emulated x64 instructions


between two Arm64EC functions. FFS functions are special. FFS functions don’t really run
if they remain unaltered. The call-checker helper will efficiently check if the FFS hasn’t
been changed. If that is the case, the call will be transferred directly to the real
destination. If the FFS has been changed in any possible way, then it will no longer be an
FFS. Execution will be transferred to the altered FFS and run whichever code may be
there, emulating the detour and any hooking logic.

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.

Authoring Arm64EC in Assembly


Windows SDK headers and the C compiler can simplify the job of authoring Arm64EC
assembly. For example, the C compiler can be used to generate Entry and Exit Thunks
for functions not compiled from C code.

Consider the example of an equivalent to the following function fD that must be


authored in Assembly (ASM). This function can be called by both Arm64EC and x64 code
and the pfE function pointer may point at either Arm64EC or x64 code as well.

C++

typedef int (PF_E)(int, double);

extern PF_E * pfE;

int fD(int i, double d) {


return (*pfE)(i, d);
}

Writing fD in ASM would look something like:


#include "ksarm64.h"

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!

adrp x11, pfE ; Get the global


function
ldr x11, [x11, pfE] ; pointer pfE

adrp x9, __os_arm64x_check_icall_cfg ; Get the EC call


checker
ldr x9, [x9, __os_arm64x_check_icall_cfg] ; with CFG
adrp x10, |$iexit_thunk$cdecl$i8$i8d| ; Get the Exit
Thunk for
add x10, x10, |$iexit_thunk$cdecl$i8$i8d| ; int f(int,
double);
blr x9 ; Invoke the call
checker

blr x11 ; Invoke the


function

EPILOG_RESTORE_REG_PAIR fp, lr, #16!


EPILOG_RETURN

NESTED_END

end

In the example above:

Arm64EC uses the same procedure declaration and prolog/epilog macros as


Arm64.
Function names should be wrapped by the A64NAME macro. When compiling
C/C++ code as Arm64EC, the compiler marks the OBJ as Arm64EC containing
Arm64EC code. This does not happen with ArmASM . When compiling ASM code
there is an alternate way to inform the linker that the produced code is Arm64EC.
This is by prefixing the function name with # . The A64NAME macro performs this
operation when _Arm64EC_ is defined and leaves the name unchanged when
_Arm64EC_ is not defined. This makes it possible to share source code between

Arm64 and Arm64EC.


The pfE function pointer must first be run through the EC call checker, together
with the appropriate Exit Thunk, in case the target function is x64.

Generating Entry and Exit Thunks


The next step is to generate the Entry Thunk for fD and the Exit Thunk for pfE . The C
compiler can perform this task with minimal effort, using the _Arm64XGenerateThunk
compiler keyword.

C++

void _Arm64XGenerateThunk(int);

int fD2(int i, double d) {


UNREFERENCED_PARAMETER(i);
UNREFERENCED_PARAMETER(d);
_Arm64XGenerateThunk(2);
return 0;
}

int fE(int i, double d) {


UNREFERENCED_PARAMETER(i);
UNREFERENCED_PARAMETER(d);
_Arm64XGenerateThunk(1);
return 0;
}

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.

Custom Entry Thunks


Macros have been added to the SDK to help authoring custom, hand-coded, Entry
Thunks. One case where this can be used is when authoring custom Adjustor Thunks.

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.

Below is an example in Arm64 Classic code:


NESTED_ENTRY MyAdjustorThunk
PROLOG_SAVE_REG_PAIR fp, lr, #-16!
ldr x15, [x0, 0x18]
adrp x16, __guard_check_icall_fptr
ldr x16, [x16, __guard_check_icall_fptr]
blr xip0
EPILOG_RESTORE_REG_PAIR fp, lr, #16
EPILOG_END br x15
NESTED_END

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.

Dynamically Generating (JIT Compiling)


Arm64EC code
In Arm64EC processes there are two types of executable memory: Arm64EC code and
x64 code.

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.

Usually this implies:

Allocating writable memory ( VirtualAlloc ).


Producing the code into the allocated memory.
Re-protecting the memory from Read-Write to Read-Execute ( VirtualProtect ).
Add unwind function entries for all non-trivial (non-leaf) generated functions
( RtlAddFunctionTable or RtlAddGrowableFunctionTable ).

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:

When allocating the memory, use newer VirtualAlloc2 (instead of VirtualAlloc


or VirtualAllocEx ) and provide the MEM_EXTENDED_PARAMETER_EC_CODE attribute.
When adding function entries:
They must be in Arm64 format. When compiling Arm64EC code, the
RUNTIME_FUNCTION type will match the x64 format. For Arm64 format when
compiling Am64EC, use the ARM64_RUNTIME_FUNCTION type instead.
Do not use the older RtlAddFunctionTable API. Always use the newer
RtlAddGrowableFunctionTable API instead.
Below is an example of memory allocation:

C++

MEM_EXTENDED_PARAMETER Parameter = { 0 };
Parameter.Type = MemExtendedParameterAttributeFlags;
Parameter.ULong64 = MEM_EXTENDED_PARAMETER_EC_CODE;

HANDLE process = GetCurrentProcess();


ULONG allocationType = MEM_RESERVE;
DWORD protection = PAGE_EXECUTE_READ | PAGE_TARGETS_INVALID;

address = VirtualAlloc2 (
process,
NULL,
numBytesToAllocate,
allocationType,
protection,
&Parameter,
1);

And an example of adding one unwind function entry:

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.

How do Arm64X binaries work?


Fundamentally, an Arm64X binary contains all of the content that would be in separate
x64/Arm64EC and Arm64 binaries, but merged into one more efficient file on disk. The
built Arm64X binary has two sets of code, entry points, etc., while eliminating redundant
parts to save space on disk.

When an Arm64X binary is loaded by an application, the operating system applies


transformations to expose the correct sections depending on the architecture of the
process it is being loaded into. You can think of an Arm64X binary like old 3D images,
with both a red and blue image that can be viewed through the red or blue lenses on a
pair of 3D glasses. An x64 app will see the DLL as though it is an x64 DLL, while an
Arm64 app will see the same DLL as an Arm64 DLL.
The transparent operating system transformations allow both x64 and Arm64
applications to load the same Arm64X binary without ever knowing that it also contains
code corresponding to the other architecture. For that reason, Arm64X binaries are
nicknamed 'chameleon' as they take on the 'color' of their surroundings.

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.

How does the operating system use Arm64X


binaries?
Windows 11 on Arm introduced the ability to run x64 applications on Arm64. However,
unlike x86 emulation, which includes a SysWoW64 folder, there is no separate folder of
pure x64 operating system binaries. With Windows 11 on Arm, both x64 applications
and Arm64 applications are able to load binaries and call APIs using the binaries in
System32 . This flexibility is possible because any binaries in System32 that an app may
need to load have been recompiled as Arm64X binaries.

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.

Arm64X for use with middleware or plugins


The core function of an Arm64X binary is to enable one file on disk to support both
x64/Arm64EC and Arm64 processes. Most app developers will be focused on building
their application as either Arm64EC or Arm64, not both, in which case Arm64X likely
won't be necessary.

Arm64X should be considered by developers of middleware or plugins, however,


because such code has the potential to be loaded into x64 or Arm64 processes.

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.

Example situations that would require Arm64X


There are some situations that will require using an Arm64X binary to support both x64
and Arm64 apps. Those include:

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.

For details on building Arm64X binaries, see Build Arm64X binaries.


Build Arm64X binaries
Article • 03/10/2023

You can build Arm64X binaries, also known as Arm64X PE files, to support loading a
single binary into both x64/Arm64EC and Arm64 processes.

Building an Arm64X binary from a Visual


Studio project
To enable building Arm64X binaries, the property pages of Arm64EC configuration has a
new "Build Project as ARM64X" property, known as BuildAsX in the project file.

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

Directory.Build.props file to add an ARM64ConfigurationNameForX property to the

Arm64EC configuration that provides the name of the Arm64 configuration.

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.

Building an Arm64X pure forwarder DLL


An Arm64X pure forwarder DLL is a small Arm64X DLL that forwards APIs to separate
DLLs depending on their type:

Arm64 APIs are forwarded to an Arm64 DLL, and


x64 APIs are forwarded to an x64 or Arm64EC DLL.

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

for Arm64EC ( empty_x64.obj ):

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++

link /lib /machine:x64 /def:foo_x64.def /out:foo_x64.lib


link /lib /machine:arm64 /def:foo_arm64.def /out:foo_arm64.lib

4. Link the empty OBJ and import LIB files using the flag /MACHINE:ARM64X to
produce the Arm6X pure forwarder DLL:

C++

link /dll /noentry /machine:arm64x /defArm64Native:foo_arm64.def


/def:foo_x64.def empty_arm64.obj empty_x64.obj /out:foo.dll
foo_arm64.lib foo_x64.lib

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.

Get started with Arm64 Visual Studio


To get started with the native Arm64 Visual Studio experience:

Ensure you have an Arm64 device with Windows 11.


Uninstall any prior versions of Visual Studio from your Arm64 device.
Download and install the latest version Visual Studio 2022 .

Installing Arm64 Visual Studio


There's a single installer for both Visual Studio x64 and Arm64 architectures. The Visual
Studio Installer detects if the system architecture is Arm64. The installer then downloads
and installs the Arm64 version of Visual Studio on your Arm64 device (support is for
Windows 11). If you're installing the product via a layout, you need to explicitly
configure the layout to include ARM binaries.

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:

.NET desktop development


Desktop development with C++
ASP.NET and web development
Node.js development
Visual Studio extension development
Game development with C++
Game development with Unity
Windows application development
.NET Multi-platform App UI development (.NET MAUI)
Linux and embedded development with C++
Database development with SQL Server Data Tools

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

Host architecture Target architecture Installation path


(platform the (platform the compiler is
compiler is running generating binaries for)
on)

Arm64 Arm64 <Install location>\VC\Tools\MSVC\


<version>\bin\HostARM64\ARM64

Arm64 X64 <Install location>\VC\Tools\MSVC\


<version>\bin\HostARM64\x64

Arm64 X86 <Install location>\VC\Tools\MSVC\


<version>\bin\HostARM64\x86

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.

We need your help!


We’d love to hear from you about the experiences we're bringing online. Let us know
what you like and whether you have suggestions for making Visual Studio even better
on Arm64. You can share feedback with us via Developer Community : report any bugs
or issues via report a problem and share your suggestions for prioritizing more
workloads.

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.

Overview of ARM64 ABI conventions


Describes the application binary interface used by Windows on ARM64 for register
usage, calling conventions and exception handling.

Common MSVC ARM migration issues


Describes C++ code elements that are commonly assumed to be portable across
architectures, but that produce different results for ARM than for x86 and x64.

ARM exception handling


Describes the encoding scheme for stack unwinding during structured exception
handling in Windows on ARM.

ARM64 exception handling


Describes the encoding scheme for stack unwinding during structured exception
handling in Windows on ARM64.

Related Sections
Get started with Arm64EC
Describes how to get started building your app or project using Arm64EC.

How to: Configure projects to target platforms


Describes how to set up your build to target different processor architectures, including
Arm64.

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

Windows devices running on an Arm processor (for example, Snapdragon processors


from Qualcomm) will no longer support AArch32 (Arm32). This change impacts
Universal Windows Platform apps that presently target AArch32 (Arm32). Support
for 32-bit Arm versions of applications will be removed in a future release of
Windows 11. . System binaries for ARM32 support (present in the sysarm32 folder)
will also be removed. After this change, for the small number of applications
affected, app features might be different and you might notice a difference in
performance. Therefore, we recommend updating your targeted platforms to
AArch64 (Arm64), which is supported on all Windows on Arm devices, as soon as
possible in order to ensure your customers can continue to enjoy the best possible
experience. Follow the guidance on this page to update your applications to
AArch64 (Arm64).

Add an Arm64 configuration to your project


To add an ARM64 solution platform to your existing app project code:
1. Open your solution (project code) in Visual Studio (Visual Studio 2017 version 15.9
or newer is required).
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 ARM with the "Create new
project platforms" checkbox enabled, then select OK.

Build your Arm64 Solution


Once you have added the Arm64 solution platform to your existing project or solution, if
you want to confirm that the Arm64 version of your app builds correctly, close the
"Active solution platform" window and change the build setting from Debug to Release.
In the "Build" drop-down menu, select Rebuild Solution and wait for the project to
rebuild. You should receive a "Rebuild All succeeded" output. If not, see the
Troubleshooting section below.

(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

command: dumpbin /headers .\<appname>.exe . Scrolling up in your terminal's output


results, find the FILE HEADER VALUES section and confirm the first line is AA64 machine
(ARM64) .

Publish your updated app in the Microsoft


Store
Once you have built an Arm64 version of your app by following the configuration steps
above, you can update your existing app package in the Microsoft Store by visiting your
Partner Center dashboard and adding the newly built ARM64 binaries to the
submission. (It is an option to also remove the previous ARM32 binaries).

(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.

A dependency not compiled for ARM64 is blocking you


from a successful build
If you can’t build due to a dependency, whether internal, from a 3rd party, or from an
open-source library, you will need to either find a way to update that dependency to
support ARM64 architecture or remove it.

For internal dependencies, we recommend rebuilding the dependency for ARM64


support.

For 3rd party dependencies, we recommend filing a request for the maintainer to
rebuild with ARM64 support.

For open source dependencies, we recommend first checking vcpkg to see if a


newer version of the dependency that includes ARM64 support exists that you can
update to. If no update exists, consider contributing the addition of ARM64
support to the package yourself. Many open source maintainers would be thankful
for the contribution.

The last choice would be to remove and/or replace the dependency on your app
project.

Need assistance? Leverage our App Assure service


Learn more about App Assure compatibility assistance to help with porting your
Windows app or driver to Arm64. To register and connect with App Assure,
visit aka.ms/AppAssureRequest or send an email to achelp@microsoft.com to submit
your request for Windows on Arm compatibility support.
Building Arm64 Drivers with the WDK
Article • 06/29/2024

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:

MSVC v143 - VS 2022 C++ ARM64/ARM64EC Spectre-mitigated libs (Latest)


MSVC v143 - VS 2022 C++ x64/x86 Spectre-mitigated libs (Latest)
C++ ATL for latest v143 build tools with Spectre Mitigations
(ARM64/ARM64EC)
C++ ATL for latest v143 build tools with Spectre Mitigations (x86 & x64)
C++ MFC for latest v143 build tools with Spectre Mitigations
(ARM64/ARM64EC)
C++ MFC for latest v143 build tools with Spectre Mitigations (x86 & x64)

2. Install and restart Visual Studio.

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.

Building an Arm64 Driver with the WDK


1. In Visual Studio, open a driver solution. You can use your own, or one from the
Windows-driver-samples repo.
2. Select Solutions platform and select Configuration Manager.

3. Under Active Solution Platform, select New.

4. From Type or Select new Platform, select Arm64. Copy settings from Win32.
Select OK and Close.

5. Select Arm64 as the target platform and rebuild.

See Also
Debugging Arm64
Windows on Arm
HLK Arm64 Getting Started Guide

Feedback
Was this page helpful?  Yes  No

Provide product feedback | Get help at Microsoft Q&A


Debugging on Arm64
Article • 04/04/2023

This article describes debugging Windows 10 on ARM processors. For general


information about Windows 10 on Arm, see Windows 10 desktop on Arm64.

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.

Getting Arm Debugging Tools for Windows


You can get debugging tools for Arm64 by downloading the Windows SDK (version
10.0.16299 or later). During the installation, select the Debugging Tools for Windows box.

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 .

Debugging Arm64 Code


Arm64 WinDbg is required to debug Arm64 code. The debugging experience is similar
to debugging x86 applications with x86 WinDbg on x86 Windows, except for the
following differences.

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.

Debugging x86 User Mode Code


In the rare cases that you need to use Arm64 WinDbg to debug your x86 user mode
code, you can use the following WinDbg commands to switch between contexts:

.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.

If a thread isn't being actively debugged (for example, single-stepped,


encountered a breakpoint), not reporting an exception, and not in a system call,
the register context may not be up-to-date.
The emulator internally generates Data misaligned, Illegal instruction, In-page I/O
error exceptions and handles the ones it generates. When you're using WinDbg,
consider configuring these exceptions as Ignored under the Debug / Event Filters…
menu item.
If using Arm64 WinDbg in user mode, single-stepping across x86 & CHPE function
boundaries isn't supported. To work around this, set breakpoints on the target
code.

For general information about ARM64 and WOW64, see Running 32-bit Applications in
the 64-bit Windows programming guide.

For information on debugging applications running under WOW64, see Debugging


WOW64.

Debugging in Visual Studio


For information on debugging Arm in Visual Studio, see Remote Debugging.

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.

Best practices for WOW


One common problem occurs when an app discovers that it's running under WOW and
then assumes that it is on an x64 system. Having made this assumption, the app may do
the following:

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.

This is a security mitigation some apps enable on their process using


SetProcessMitigationPolicy API with the ProcessDynamicCodePolicy flag. To run
successfully on Arm64 as an x86 process, this mitigation policy will have to be disabled.
Troubleshooting Arm UWP apps
Article • 03/29/2023

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.

Using Windows 10 Mobile-only APIs on Arm-based


processors
Arm apps may run into problems when using mobile-only APIs (for example,
HardwareButtons). To mitigate this, you can dynamically detect whether your app is
running on Windows 10 Mobile before calling these APIs. For more info, see
Dynamically detecting features with API contracts.

Including dependencies not supported by UWP apps


Universal Windows Platform (UWP) apps that aren't properly built with Visual Studio and
the UWP SDK may have dependencies on OS components that aren't available to Arm
apps running on an Arm64 system. Examples of these dependencies include:

Expecting parts of the .NET Framework to be available.


Referencing third-party .NET components that aren't compatible with UWP.

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

Compiling an app with an older version of Visual Studio


and SDK
If you're running into issues, be sure to use the latest versions of Microsoft Visual Studio
and the Windows SDK to compile your app. Apps compiled with an earlier version of
Visual Studio and the SDK may have issues that have been fixed in later versions.

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.

Start the Program Compatibility Troubleshooter


You start the Program Compatibility Troubleshooter manually in the same way on any
Windows PC: right-click an executable (.exe) file and select Troubleshoot compatibility.
This screen appears.

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.

Toggling emulation settings

2 Warning

Changing emulation settings may result in your application unexpectedly crashing


or not launching at all.

You can toggle emulation settings by right-clicking the executable and selecting
Properties.

On ARM, a section titled Windows 10 on ARM or Windows 11 on ARM will be available


in the Compatibility tab. Click Change emulation settings to launch a second window
as here.
This window provides two ways to modify emulation settings. You may select a pre-
defined group of emulation settings, or you may click the Use advanced settings option
to enable choosing individual settings.

The grouped emulation settings reduce performance optimizations in favor of quality.


Below are some grouped settings that you can select.
Select Use advanced settings to choose individual settings as described in this table.

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

You can also select multi-core settings, as shown here.


These settings change the number of memory barriers used to synchronize memory
accesses between cores in apps during emulation. Fast is the default mode, but the
strict and very strict options will increase the number of barriers. This slows down the
app, but reduces the risk of app errors. The single-core option removes all barriers but
forces all app threads to run on a single core.

If changing a specific setting resolves your issue, please email


woafeedback@microsoft.com with details, so that we may incorporate your feedback.
Set up your development environment
on Windows
Article • 09/28/2023

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

Sudo for Windows


Sudo for Windows is a new way for users to run elevated commands directly from an
unelevated console session.
Enable and configure Sudo for Windows
Windows AI
A new era of AI has arrived at Microsoft. See how AI is being integrated in Windows 11.
Explore Windows AI

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 .

Sign up to receive updates

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.

Get started with Python


Install Python and get your development environment setup on Windows or Windows
Subsystem for Linux.

Get started with Android


Install Android Studio, or choose a cross-platform solution like .NET MAUI, React, or
creating a PWA, and get your development environment setup on Windows.
Get started building Windows apps
Get started building desktop apps for Windows using the Windows App SDK, UWP,
Win32, WPF, Windows Forms, or updating and deploying existing desktop apps with
MSIX and XAML Islands.

Get started with C++ and C


Get started with C++, C, and assembly to develop apps, services, and tools.

Get started with C#


Get started building apps using C# and .NET.
Get started with F#
Get started building apps using F# and .NET.

Get started with Rust


Get started programming with Rust—including how to set up Rust for Windows by
consuming the windows crate.

Get started with PowerShell


Get started with cross-platform task automation and configuration management using
PowerShell, a command-line shell and scripting language.
Get started with Docker Desktop for Windows
Create remote development containers with support from Visual Studio, VS Code, .NET,
Windows Subsystem for Linux, or a variety of Azure services.

Get started with Blazor


Get started with Blazor, a client-side UI framework within ASP.NET Core. Use HTML, CSS,
and C# (rather than JavaScript) to create UI components and single page applications
for the web.

More for developers


VS Code
A lightweight source code editor with built-in support for JavaScript, TypeScript, Node.js,
a rich ecosystem of extensions (C++, C#, Java, Python, PHP, Go) and runtimes (such as
.NET and Unity).
Install VS Code

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

Run Windows and Linux


Windows Subsystem for Linux (WSL) allows developers to run a Linux operating system
right alongside Windows. Both share the same hard drive (and can access each other’s
files), the clipboard supports copy-and-paste between the two naturally, there's no need
for dual-booting. WSL enables you to use BASH and will provide the kind of
environment most familiar to Mac users.

Learn more in the WSL docs.


https://learn.microsoft.com/shows/One-Dev-Minute/What-can-I-do-with-WSL--One-
Dev-Question/player?format=ny

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.

Learn more in the Windows Terminal docs.


https://learn.microsoft.com/shows/One-Dev-Minute/What-are-the-main-features-of-
the-new-Terminal--One-Dev-Question/player?format=ny

Transitioning between Mac and Windows


Check out our guide to transitioning between a Mac and Windows (or Windows
Subsystem for Linux) development environment. It can help you map the difference
between:

Keyboard shortcuts
Trackpad shortcuts
Terminal and shell tools
Apps and utilities

Game development documentation


Microsoft's Game Dev documentation

You might also like