2016-03-04

Small Fixes

It's been a while since we looked at our blinker program. Since then, I've made a number of small fixes and improvements that I'd like to present today.

The most prominent source of changes were upgrades to later Rust versions[1]. While upgrading to a newer version of the Rust language generally doesn't require changes to your program, we've been using a number of unstable features, which are not guaranteed to stay the same.

Let's take a look of two of those features again: no_std and core. To reiterate, no_std indicates that we're not using Rust's standard library. The standard library is designed to run in typical operating system environments, and is not suitable for a microprocessor like the one we're working with. core indicates that we're using Rust's core library instead. The core library is a subset of the standard library that provides many useful things and can run just about anywhere.

Both no_std and core have been stabilized, so we don't need to specify them anymore. Here's the updated feature specification:

#![feature(intrinsics, lang_items)]
            

Hopefully in time, those remaining two will also be stabilized. At that point we'll no longer be required to develop using a nightly version and can rely on stable Rust.

In addition to being stabilized, no_std has changed a bit. By not using the standard library, we now opt into using the core library automatically. This makes our code a bit simpler. Here's what we had to do previously:

#![no_std]


#[macro_use]
extern crate core;

use core::prelude::*;
            

And this is what the same code looks like now:

#![no_std]
            

Doesn't make much of a difference for us, but it certainly makes things easier for anyone who has to figure this out for the first time.

The rest of the changes concern the build process and were all made to target.json. I'm going to describe each of those changes separately, and then link to the final version of target.json after those descriptions.

The first modification to target.json was made because of a linker error that I got when trying to set up the build process on another computer. I'm not 100% sure what caused that error, but I think it's because I had a newer version of arm-none-eabi-gcc there. Here's the error message:

error: linking with `arm-none-eabi-gcc` failed: exit code: 1
note: "arm-none-eabi-gcc" "-L" "/home/hanno/.multirust/toolchains/nightly/lib/rustlib/target.json/lib" "output/blink.0.o" "-o" "output/blink.elf" "-Wl,--gc-sections" "-nodefaultlibs" "-L" "output" "-L" "/home/hanno/.multirust/toolchains/nightly/lib/rustlib/target.json/lib" "-Wl,-Bstatic" "-Wl,-Bdynamic" "/home/hanno/Projects/embedded/output/libcore.rlib" "-Tlinker-script.ld"
note: /usr/lib/gcc/arm-none-eabi/5.3.0/../../../../arm-none-eabi/bin/ld: cannot find crt0.o: No such file or directory
collect2: error: ld returned 1 exit status

error: aborting due to previous error
            

The important bit of that error message is the following: cannot find crt0.o: No such file or directory. Not sure what that file is, but it seems we don't need it. Passing -nostartfiles to the linker solves the problem, and we can easily do that by adding the following line to target.json:

"pre-link-args": [ "-nostartfiles" ],
            

The next change is a simplification that I found out about while snooping around the zinc.rs repository[2]. Back when we translated the program into Rust, we had to add an empty library called libcompiler-rt.a that the Rust compiler linked to, even though none of its contents were used by our program.

We can simply tell Rust that we don't need it by adding the following line to target.json:

"no-compiler-rt": true,
            

The last change was required when a Rust upgrade suddenly caused the following error:

rustc: /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/llvm/lib/CodeGen/MachineFunction.cpp:108: llvm::MachineFunction::MachineFunction(const llvm::Function*, const llvm::TargetMachine&, unsigned int, llvm::MachineModuleInfo&): Assertion `TM.isCompatibleDataLayout(getDataLayout()) && "Can't create a MachineFunction using a Module with a " "Target-incompatible DataLayout attached\n"' failed.
            

That error is documented in Rust issue #31367. As per that issue, the solution is simple: Remove the data-layout specification from target.json. No one seems to know what that error is really about though, and it's unclear whether that really is a reliable solution, so I hope this won't cause any hard-to-debug problems in the future.

And that's it. The updated version of target.json looks like this:

{
    "llvm-target"         : "thumbv7m-unknown-none-eabi",
    "linker"              : "arm-none-eabi-gcc",
    "pre-link-args"       : [ "-nostartfiles" ],
    "no-compiler-rt"      : true,
    "target-endian"       : "little",
    "target-pointer-width": "32",
    "arch"                : "arm",
    "os"                  : "none",
    "executables"         : true
}
            

That wraps use up for today. As always, the full code is available on GitHub. See you next time!