Getting Rust on the GBA 2
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.gbaAnd 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.gbaThis 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.gbaAnd 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.