AVR GCC Toolchain for MacOS

October 16, 2017

Setting up the AVR GCC toolchain on MacOS

Background

Recently I have been experimenting with exploit development for Atmel AVR microcontroller devices. Examples of such devices are Arduino, home automation devices such as the Phillips Hue lightbulb, and maybe even atomic weapons1.

When programming for common architectures like x86, we don’t give that much thought to the toolchain. GCC/Clang compiles everything in the background, and these tools are usually bundled into IDEs. The architecture in this case is an 8-bit RISC, modified Harvard architecture. We’re going to need to be able to cross-compile from our x86_64 machines to this new architecture.

This post will detail how to get started on MacOS.

The toolchain

1. Install Homebrew

If you haven’t already, you’re going to need to install Homebrew. Homebrew is a package manager for MacOS, the closest thing there is to apt in debian systems.

Run the following command in a terminal.

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Piping unknown content into ruby. What could go wrong? :-)

2. Add the Crosspack tap

A “tap” is an additional repository that can be added to Brew for access to additional packages.

brew tap osx-cross/avr

These awesome people have provided support for the entire avr-gcc toolchain as Homebrew formulae! You can take a look here https://github.com/osx-cross/homebrew-avr .

3. Install the toolchain

Now you’re ready to install the necessary packages.

brew install avr-gcc
brew install avr-gdb

After this completes you’ll have the necessary tools to compile and debug on AVR architecture!

4. Compile and objdump

Save some toy code, such as the following, as main.c .

This is just random example code, so that we can look at some generated assembly after compilation.

/*
 *  Written in AVR Studio 5 / AVR Studio 6
 *  Compiler: AVR GNU C Compiler (GCC)
 *
 *  Author: AVR Tutorials
 *  Website: www.AVR-Tutorials.com
*/

#include<avr/io.h>

int main()
{
        DDRB = 0x00;            //configure portB as input
        DDRC = 0xFF;            //configure portC as output

        while(1)
        {
                PORTC = PINB;
        }
        return 0;
}

To compile it, you’ll need to know the tag of your target microcontroller.

avr-gcc -Wall -Os -mmcu=$(DEVICE) -c main.c -o main.o
avr-gcc -Wall -Os -mmcu=$(DEVICE) -o main.elf main.o
avr-objcopy -j .text -j .data -O ihex main.elf main.hex avr-size \
--format=avr --mcu=$(DEVICE) main.elf

This will compile the C code, and link the object into a .elf file. Then it will dump a hex output, which is what we need to flash to the microcontroller.

We can also take a look at the generated assembly with the following command:

avr-objdump -D main.elf | grep -C 50 .text

And we see some assembly!

main.elf:     file format elf32-avr

Disassembly of section .text:

[..snip..]

0000006c <main>:
  6c:	17 ba       	out	0x17, r1	; 23
  6e:	8f ef       	ldi	r24, 0xFF	; 255
  70:	84 bb       	out	0x14, r24	; 20
  72:	86 b3       	in	r24, 0x16	; 22
  74:	85 bb       	out	0x15, r24	; 21
  76:	fd cf       	rjmp	.-6      	; 0x72 <main+0x6>

00000078 <_exit>:
  78:	f8 94       	cli