Getting Rust on the GBA 2

Posted on November 21, 2015

Alright, to pick up from last time, we were successfully compiling with clang, using this script.

#!/bin/bash -ex
# -ex will stop on the first error, and print every command
# as it gets run.

clang -nostdlib -target arm-none-eabi -mcpu=arm7tdmi -c first.c -o first.clang.o
# Clang is a cross compiler, so we don't need to rebuild it from source.
# We do need to pass it the target triple (arm-none-eabi), and what cpu
# we are using.
# Also, since we aren't using libc, we pass -nostdlib

arm-none-eabi-gcc -nostdlib -mthumb-interwork -mthumb \
    -c gba_crt0.s -o gba_crt0.o
# Compile gba_crt0.s using gcc still.

clang -nostdlib -target arm-none-eabi -Tgba_cart.ld \
    first.clang.o gba_crt0.o crti.o -o first.clang.elf

# Call clang to link everything.  Actually, clang will just call out to
# arm-none-eabi-gcc to link everything properly.  It needs to be in the path.

arm-none-eabi-objcopy -O binary first.clang.elf first.clang.gba
gbafix first.clang.gba

And now we want to use Rust.

To keep things simple, I'm going to compile some Rust code into an object file, and call it from C to start with.

// second.c

unsigned int rust_main();

int main()
{
	*(unsigned int*)0x04000000= 0x0403;

	((unsigned short*)0x06000000)[120+2*rust_main()*240]= 0x001F;
	((unsigned short*)0x06000000)[136+80*240]= 0x03E0;
	((unsigned short*)0x06000000)[120+96*240]= 0x7C00;

	while(1);

	return 0;
}

And then I'm using a super stripped down Rust file (stolen from Hanno:

#![feature(intrinsics, lang_items, no_std, no_core)]

#![no_core]

#[lang = "sized"]
trait Sized {}
#[lang = "copy"]
trait Copy {}

#[lang = "eh_personality"] extern fn eh_personality() {}
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }

// I'm not 100% sure what this function does, but references to it are compiled
// into the program by the Rust compiler. I think it would be called in the case
// of a program panic.
#[no_mangle]
pub fn __aeabi_unwind_cpp_pr0() {
    loop {}
}

#[no_mangle]
pub fn rust_main() -> u32 {
    42
}

This is super trivial, but that's ok, I just want to get it to work.

The build script:

#!/bin/bash -ex

clang -nostdlib -target arm-none-eabi -mcpu=arm7tdmi -c second.c -o second.o

arm-none-eabi-gcc -nostdlib -mthumb-interwork -mthumb \
    -c gba_crt0.s -o gba_crt0.o
# Compile gba_crt0.s using gcc still.

rustc --target=target.json --crate-type staticlib --emit obj -o first_lib.o first_lib.rs
# Call rustc to compile our first Rust object

clang -nostdlib -target arm-none-eabi -Tgba_cart.ld \
    second.o gba_crt0.o crti.o first_lib.o -o first.rustc.elf

arm-none-eabi-objcopy -O binary first.rustc.elf first.rustc.gba
gbafix first.rustc.gba

This actually works! I was really happy.

Oh, and let me show you target.json

{
	"data-layout"         : "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:64:128-a:0:64-n32",
	"llvm-target"         : "arm-none-eabi",
	"linker"              : "arm-none-eabi-gcc",
	"target-endian"       : "little",
	"target-pointer-width": "32",
	"cpu"                 : "arm7tdmi",
	"arch"                : "arm",
	"os"                  : "none",
	"executables"         : true
}

Again, this was stolen from Hanno, and just tweaked to work for the gba target.


Now, to include libcore. This actually wasn't too bad. I have a clone of the rust repository two levels up, in ../../rust. I checked what commit my rustc was built at with rustc --version, and I just did a git checkout 9303055f3.

Then, in my buildscript, I run

#!/bin/bash -ex

clang -nostdlib -target arm-none-eabi -mcpu=arm7tdmi -c second.c -o second.o

# Compile gba_crt0.s using gcc still.
arm-none-eabi-gcc -nostdlib -mthumb-interwork -mthumb \
    -c gba_crt0.s -o gba_crt0.o

# Call rustc to compile libcore for us.
# This is slow, maybe switch to a makefile?
[ -e libs/libcore.rlib ] ||
  rustc --target=target.json -o libs/libcore.rlib ../../rust/src/libcore/lib.rs

# Call rustc to compile our first Rust object
rustc -L libs --target=target.json --crate-type staticlib --emit obj -o first_lib.o first_lib.rs

clang -nostdlib -target arm-none-eabi -Tgba_cart.ld \
    second.o gba_crt0.o crti.o first_lib.o -o first.rustc.elf

arm-none-eabi-objcopy -O binary first.rustc.elf first.rustc.gba
gbafix first.rustc.gba

And our modified lib:

#![feature(intrinsics, lang_items, no_std)]

#![no_std]

// // Declare some intrinsic functions that are provided to us by the compiler.
// extern "rust-intrinsic" {
//     fn overflowing_add<T>(a: T, b: T) -> T;
//     fn u32_sub_with_overflow(x: u32, y: u32) -> (u32, bool);
// }

#[lang = "panic_fmt"]
pub extern fn panic_fmt() -> ! { loop {} }

#[lang = "stack_exhausted"]
pub extern fn stack_exhausted() -> ! { loop {} }

#[lang = "eh_personality"]
pub extern fn eh_personality() -> ! { loop {} }

// // I'm not 100% sure what this function does, but references to it are compiled
// // into the program by the Rust compiler. I think it would be called in the case
// // of a program panic.
#[no_mangle]
pub fn __aeabi_unwind_cpp_pr0() {
    loop {}
}


#[no_mangle]
pub fn rust_main() -> u32 {
    40 + 2
}

See, we now can add numbers! libcore is great.

Next time, I'll remove things from the C file.