ARM GCC Cross Compilation in Visual Studio


⏱ Updated on January 10, 2018 to cover addition of debugging support in Visual Studio 2017 15.6

In Visual Studio 2017 15.5 we are introduced support for cross compilation targeting ARM microcontrollers. The 15.6 Preview 2 release adds debugging support. To enable this in the installation choose the Linux development with C++ workload and select the option for Embedded and IoT Development. This adds the ARM GCC cross compilation tools and Make to your installation.

Our cross compilation support uses our Open Folder capabilities so there is no project system involved. We are using the same JSON configuration files from other Open Folder scenarios and have added additional options to support the toolchains introduced here. We hope that this provides flexibility for many styles of embedded development. The best way to get started with this and understand the capabilities is with a project exported from the ARM mbed online compiler. We’ll cover the basics here, to learn more about the online compiler see ARM’s tutorials, and you can sign up for an account here.

ARM’s online compiler lets you select your target platform and configures your project accordingly. I’m using an ST Nucleo-F411RE, but any board supported by the compiler should be fine. Click New, and in the dialog that opens choose a template to get started with. I chose “Blinky LED test for ST Nucleo boards”.

ARM new project dialog

Then in the Program Workspace select the program, right click and choose Export Program.

In the Export program dialog choose GCC (ARM Embedded) for the Export Target.

This will download a zip file to your computer. Extract the contents of that file to where you like and open Visual Studio. Now open it with File > Open > Folder. We need to inform VS of what type of C++ code this is, so go the Project menu and select Edit Settings > CppProperties.json. Modify the contents so there is only one entry that matches the below.

  "configurations": [
      "inheritEnvironments": [
      "name": "gccarm",
      "includePath": [
      "defines": [


The environment definition for gcc-arm (gcc_arm in 15.5) sets the path to the ARM GCC toolchain that VS installed. It also triggers scanning of the subfolder named mbed to properly set include paths for use in IntelliSense. You will see this when you open main.cpp. Everything you expect to work will, auto completion, go to definition, peek definition, etc.  You can specify additional include directories, but note that subdirectories when present must also be explicitly declared at this time.

Open folder also supports things like syntax highlighting for makefiles, so go ahead and open it to see that. To compile the project right click the Makefile and select build.

You’ll see the build output in the output window.

You will now see a new BUILD output folder in the Solution Explorer. Within that is your output binary which is your program name .bin you can flash onto your board.

One of the great things about mbed boards is how easy they are to flash with that binary. When connected to your PC they show up as a USB mass storage device, so you simply drag and drop the bin file to the drive, the board resets and your program runs. If you want to control this from Visual Studio you can use tasks. Select the binary, right click and choose Configure Tasks. This will create a file called tasks.vs.json where you can configure tasks that will show up in the context menu for the file it is configured for. The example task shown below will copy the binary specified to the D drive, you can modify this to match your system. The command ${env.COMSPEC} provides a command prompt environment that the arguments are passed to.

  "version": "0.2.1",
  "tasks": [
      "taskName": "flash",
      "appliesTo": "BUILD/Nucleo_blink_led.bin",
      "type": "launch",
      "command": "${env.COMSPEC}",
      "args": [
        "copy BUILD\\Nucleo_blink_led.bin D:"


Debugging is supported as of the Visual Studio 2017 15.6 Preview 2 release.

First it is important to ensure your output has debugging symbols. In the case of the GCC projects exported from the ARM online compiler they do not. To add them edit the makefile under the tools and flags section and add the -g flag for the GCC and G++ commands like the below.

CC      = 'arm-none-eabi-gcc' '-g' ...
CPP     = 'arm-none-eabi-g++' '-g' ...

Now after you have built your binary and flashed the device, right click on the binary output and select Debug and Launch Settings.

In the dialog that pops up select C/C++ Debug microcontroller (gdbserver).

This will create a launch.vs.json that has many options expressed that are relevant for embedded debugging. There are many ways to debug these kinds of devices so what you fill in here will be specific to your board, the hardware debugger and its associated software that provides a gdbserver interface. We are providing as many defaults and hints as we can to help you. In this preview some of the environment variables emitted are not working yet, you will need to substitute these with the values needed.

  • ${workspaceRootFolderName}, your folder name
  • ${env.gccpath}, your VS installation path followed by Linux\gcc_arm\bin
  • ${debugInfo.linuxNatvisPath}, path to a Natvis file if you have one. This is fine to remove as it is for specific scenarios.

I will walk through configuring this with the ST Nucleo-F411RE using OpenOCD. The process is similar for most boards.

First, change the program name in the output to point to your .elf file.

"program": "${workspaceRoot}\\BUILD\\Nucleo_blink_led.elf",

Change the miDebuggerPath to point to the full path to arm-none-eabi-gdb.exe.

"miDebuggerPath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Internal\\Enterprise\\Linux\\gcc_arm\\bin\\arm-none-eabi-gdb.exe",

In the setupCommands you can remove the documentation link section (leaving it is not harmful). Change the symbol load to point to your .elf file.

"text": "-file-exec-and-symbols Nucleo_blink_led.elf",

You can execute additional commands to get your board into a proper state for debugging, often you need to halt and then reset the board at the beginning of a session. To do so add those command as part of the setup commands array as follows.

"text": "monitor reset halt",
"ignoreFailures": true
"text": "monitor reset init",
"ignoreFailures": true

Make sure the miDebuggerServerAddress matches the location your hardware debugger will provide. This is the default for OpenOCD.

"miDebuggerServerAddress": "localhost:3333",

If you want to launch your hardware interface yourself manually you can omit these lines. If you want VS to start the software to interface with your hardware debugger this is an example for launching OpenOCD with a config file. You should check this command by running it manually to make sure you have the correct config file and that the text used to validate the server has started is correct.

"debugServerPath": "D:\\openocd-0.10.0\\bin-x64\\openocd.exe",
"debugServerArgs": "-f d:/openocd-0.10.0/scripts/board/st_nucleo_f4.cfg",
"serverStarted": "Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints",

Now with our configuration done use F5 to start debugging your embedded ARM application on your device from Visual Studio.

What’s next

Download the Visual Studio 2017 Preview, install the Linux C++ Workload, select the option for Embedded and IoT Development and give it a try with your projects.

We’re actively working on additional support for embedded scenarios. Your feedback here is very important to us. We look forward to hearing from you and seeing the things you make.

The best way to reach us is via our GitHub hosted issue list, directly via mail at or find me on Twitter @robotdad.