Many libraries written in C and C++ provide binary builds that make them easy to use right away. These libraries range from gaming libraries like SDL, to encryption libraries like OpenSSL, to networking libraries like Curl. The article walks through the steps necessary to create native Windows on Arm builds of several popular libraries. This will serve as a how-to guide for both library authors who want to provide Arm native builds, and library users who need to build a library from source if a Windows on Arm binary isn't available (which seems likely based on the author’s experience).
Getting the Tools
Until recently, if you needed to write code for Windows on an Arm processor, such as the Snapdragon 835 that powers the Asus NovaGo 2-in-1 portable computer, you were limited to the GCC compiler collection, which relatively few Windows developers have or want. Setting up GCC is a significant undertaking, requiring many steps. Recent developments coming from Microsoft have replaced that dirt road with a 4-lane highway, which is the focus of this tutorial. The first announcement, "Early preview of Visual Studio support for Windows 10 on Arm development," came on 08 May 2018. The 15th of November brought more news, including support for 64-bit binaries, announced in Official support for Windows 10 on Arm development. The more recent article includes step-by-step instructions for adding the necessary cross compilers to your Visual Studio installation; the same instructions apply to all editions, including the Community edition employed by the author.
The simplest way to present these instructions is as a series of captioned screen captures; please follow along with the following pictures and their instructional captions to get the tools installed in a matter of minutes, assuming your Internet connection is reasonably robust.
Above, we see where the Visual Studio Installer program appears on the Start menu. Once activated, the installer presents a UAC elevation prompt, since all installers need Administrator permission.
Next, we see the main Visual Studio Installer window, which appears after the few seconds required for it to organize a list of what’s already installed on your machine. Select the Modify button from the row of three buttons on the left side of the screen, about 2/3 of the way down the page.
This is the window that appears shortly after you press the Modify button shown in the previous image. Though they don’t look much like it, the four hyperlinks across the top of the screen behave like tabs; select the second tab, labeled "Individual Components."
This is the window that appears when you select the Individual Components tab on the window shown in the previous image. Though it is not immediately obvious at first glance, there is a scroll bar to the left of the column labeled "Installation Details," which you must use to scroll through the list of components. Scroll down until your window looks more like the next image.
This shows the Visual C++ compilers and libraries for Arm and Arm64. Since this picture was made after they were installed, both are already selected.
These are the SDKs, libraries, and frameworks group, one of the largest, where you find ATL and MFC libraries for Arm processors.
Arming the Target Computer to Run Your Visual C/C++ Code
While the foregoing steps get the tools installed onto your Intel or AMD powered development machine, the machine or machines on which you expect to run your Arm software need a few things added that aren’t part of a stock Windows installation. Unless you intend to statically link everything you write with the Visual C/C++ runtime libraries, you must install a copy of them onto the Arm computer.
To use these libraries on any supported platform, you must acquire and install the Microsoft C Runtime Library, version 14.00.24234.1 or later, called vcruntime140.dll on all platforms. Microsoft distributes it as part of the "Microsoft Visual C++ 2015 Redistributable Update 3 RC" package, available from https://www.microsoft.com/en-us/download/details.aspx?id=52685 for Intel and AMD CPUs, and from https://dotnet.myget.org/F/dotnet-core/api/v2/package/vc-runtime/2.0.0/ for Arm processors.
The Arm package comes as a NuGet package that has an extension of nupkg. Since the copy that you download from the URL cited above didn’t arrive via the NuGet package manager, the best way to work with it is by changing the file extension to zip and treating it as an ordinary ZIP archive. Both Arm and Arm64 (32 and 64 bit) runtimes come in one package.
To get the goods, extract the archive into a new directory, and use the file explorer to drill down into it. Inside the directory content\VC\Redist\MSVC\14.14.26405\onecore are two subdirectories, helpfully named Arm and Arm64. Unlike the Intel/AMD packages, the Arm package is a straightforward, if opaque, ZIP archive, and the contents of the two leaf directories need only to be copied into a directory that is in your PATH list, and they are ready for use.
It should come as no surprise that the runtimes are included with the package that you installed into Visual Studio, but it’s a lot easier to just go ahead and get the NuGet package from the URL cited above.
Building a Library
Now that you have the compilers and libraries, you are set to build your first Windows on Arm compatible code. We’ll use zlib, the famous and widely used library for creating and extracting zip archives.
- Obtain the source code. For zlib, that requires a visit to https://www.zlib.net/, where you find the source code in a variety of archive formats, including standard Zip archive and GZip (tar.gz) and tar.xx hybrids.
- Extracting the source code usually produces a directory tree that is both large and deep. For example, the zlib source archive yields a tree of which the branch that is of the greatest interest to a Windows programmer is contrib/vstudio/vc14, assuming your Visual Studio installation is the latest (2017). Among other things, this directory contains zlibvc.sln, a Visual Studio 2012 solution file, and a series of Visual Studio C/C++ project (.vcxproj) files.
- The first time you open zlibvc.sln, Visual Studio prompts you to upgrade the solution and the projects contained within so that they build with the latest toolset. When this short task finishes, you have a standard Visual C/C++ project ready for work.
- Before you can build for Windows on Arm, you must add at least two new configurations, consisting of Debug and Release configurations for a new platform, Arm. Thanks to the new tools you installed, Arm, and maybe Arm64 (if you selected the Arm64 compiler and libraries in the Individual components shown earlier). The easiest way to make this happen is to let the Configuration Manager (the next to last item on the Build menu) copy one of the other project configurations.
- While this is usually enough, before you attempt to build for Arm, review the settings listed in Table 1.
- Building for Windows on Arm is not especially different than building for Windows on Intel processors.
|Section || |
Floating point exceptions
Enable or Disable
Enhanced Instruction Set
None, SSE, SSE2
Image has safe exception handlers
Yes, if they really are
Omit Frame Pointers
Yes or No
Table 1 lists the settings that must be changed for Arm configurations.
Be aware that your Arm binaries are not superficially different from their Intel compatible siblings; they have properties, version resources, and all the other attributes of programs built to run on Intel processors. As on Windows with Intel processors, your libraries may be either static or dynamic, and the rules about where dynamic link libraries must go are the same on both platforms.
Show and Tell
Let’s walk through what happens when we used an Arm powered Windows laptop to try running several builds of zlib.
The captions show a progression through the four platforms for which the zlib library was built.
Here, we see the Win32 build running in the Intel x86 emulator that is integrated into the Arm version of Windows 10. Though it has relatively little impact on the test program, all programs usually run somewhat more slowly in an emulator, because it must translate the machine instructions to equivalent instructions for the target processor.
Next, we see that an x64 build didn't fare so well. This is as expected, because the emulator included in Arm builds of Windows 10 currently only supports 32-bit applications.
Next, we see that 32-bit Arm binary ran as expected.
Here, we see that the Arm64 binary also worked as expected.
This is what happens when you attempt to run the Arm image on an Intel-powered Windows computer.
See for Yourself
In addition to showing you how to port any library for which you have access to the source code to run as a native Arm application, during the process of writing this article I modified several popular libraries to make them work on Windows Arm devices.
Feel free to use these as-is, or take a look at the changes that were made to gain an understanding of what you may need to do to get your own complex libraries and applications ported to Windows on Arm.
Table 2 is an index of the GitHub repositories for popular open source libraries.
- This repository of zip archive library zlib includes builds for four platforms: Win32 (x86), Win64 (x64), Arm (a 32-bit native Arm instruction set), and Arm64 (the native 64-bit Arm instruction set). Three of the four work on Arm, and both Intel targets work on Intel powered desktops.
- This repository of international encoding conversion library iconv includes builds for four platforms, of which the two 32-bit builds work as expected. Due to pointer overflow issues, neither 64-bit build is trustworthy. Fixing it is beyond the scope of this article, but the author’s intention is to resolve the pointer overflow issue, so that the 64-bit builds can be trusted.
- Since XML parsing library depends on the other two, support on both platforms is limited to 32 bits until the 64-bit pointer issues in iconv are resolved.
The solution directory of each repository contains a directory inhabited by a zip archive for each working platform that contains pre-built binaries, accompanied by the appropriate version of vcruntime140.dll. Since the solution directory is deeply nested, the zlib archives are in https://github.com/txwizard/zlib_x64_and_Arm/tree/master/contrib/vstudio/vc14/Binaries_Built. The root directory is the solution directory of the other two.
As we’ve seen, building existing C and C++ applications for Windows on Arm isn’t a difficult undertaking. If you have any code that uses CPU-specific features, you’ll need to modify it before proceeding. Aside from that, porting to Arm is usually as simple as installing the Visual Studio tooling and adding new build targets.