Bare-metal Rust linking with C static library


#1

I’m working on building out a Rust interface to the nRF51x series parts. I have a bare metal system working quite well. The nRF51x has a bluetooth stack (called Softdevice). This stack requires the use of supervisor calls to request the stack to perform certain functions. My plan is to write a C library wrapper around these service calls, compile with arm-none-wabi-gcc and then link this to my Bare-metal rust system. A large chunk of the work I have done thus far is based off of STM32 example work done by Jorge Aparicio (https://github.com/japaric).

Since I have the basics up and running, I am working on trying to get a C static library built with arm-none-eabi-gcc and archived with arm-none-eabi-ar to properly link in with my Rust code.

I have the following (very basic) .c file:

uint32_t sum(uint32_t a, uint32_t b)
{
	return a + b;
}

I am compiling and linking with the following commands:
arm-none-eabi-gcc -Wall -mcpu=cortex-m0 -mthumb -fPIC --specs=nosys.specs -shared test.c -o test.o
arm-none-eabi-ar -rs libtest.a test.o

In my rust file:

[link(name="test", kind="static")]
extern {
	pub fn sum(a: u32, b: u32) -> u32;
}

I then invoke it as a test:

pub fn main() {
        let test_sum = unsafe { sum(2, 3) };
}

I am using a Makefile to execute the rust compiler for some specific arguments, such as my specific target:

# rustc target
TARGET = thumbv6m-none-eabi

# toolchain prefix
TRIPLE = arm-none-eabi

APP_DIR = src/app
OUT_DIR = target/$(TARGET)/release

DEPS_DIR = $(OUT_DIR)/deps

BINS = $(OUT_DIR)/%.hex
HEXS = $(OUT_DIR)/%.hex
ELFS = $(OUT_DIR)/%.elf
OBJECTS = $(OUT_DIR)/intermediate/%.o
SOURCES = $(APP_DIR)/%.rs

APPS = $(patsubst $(SOURCES),$(BINS),$(wildcard $(APP_DIR)/*.rs))

RUSTC_FLAGS := -C lto -g $(RUSTC_FLAGS)

# don't delete my elf files!
.SECONDARY:

all: rlibs  $(APPS)

clean:
	cargo clean

# TODO $(APPS) should get recompiled when the `rlibs` change

$(OBJECTS): $(SOURCES)
	mkdir -p $(dir $@)
	rustc \
		$(RUSTC_FLAGS) \
		--crate-type staticlib \
		--emit obj \
		--target $(TARGET) \
		-L $(DEPS_DIR) \
		-L ../sd110_lib \
		--verbose \
		-o $@ \
		-ltest \
		$<

$(ELFS): $(OBJECTS)
	$(TRIPLE)-ld \
	--gc-sections \
	-T layout.ld \
	-o $@ \
	$<
	#size $@

$(BINS): $(ELFS)
	$(TRIPLE)-objcopy \
		-O ihex \
		$< \
		$@

rlibs:
	cargo build --target $(TARGET) --verbose --release

The cargo.toml is as follows:

[package]
name = "bmd200eval"
version = "0.1.0"
authors = ["Eric Stutzenberger <eric.stutzenberger@rigado.com>"]

[dependencies.nrf51822]
path = "../nrf51822.rs"

When I run make, I get the following output:

.
.
.
mkdir -p target/thumbv6m-none-eabi/release/intermediate/
rustc \
		-C lto -g  \
		--crate-type staticlib \
		--emit obj \
		--target thumbv6m-none-eabi \
		-L target/thumbv6m-none-eabi/release/deps \
		-L ../sd110_lib \
		--verbose \
		-o target/thumbv6m-none-eabi/release/intermediate/blink.o \
		-ltest \
		src/app/blink.rs
src/app/blink.rs:42:9: 42:17 warning: unused variable: `test_sum`, #[warn(unused_variables)] on by default
src/app/blink.rs:42     let test_sum = unsafe { sum(2, 3) };
                            ^~~~~~~~
arm-none-eabi-ld \
	--gc-sections \
	-T layout.ld \
	-o target/thumbv6m-none-eabi/release/blink.elf \
	target/thumbv6m-none-eabi/release/intermediate/blink.o
target/thumbv6m-none-eabi/release/intermediate/blink.o: In function `blink::main':
git/rust-nrf/bmd200eval.rs/src/app/blink.rs:42: undefined reference to `sum' 

I have found numerous different references to linking Rust with C and calling C from Rust but I haven’t found a specific answer as to why this will not link. As you can see in the Makefile, I have tried to force rustc’s hand in finding and linking against the library, but this doesn’t seem to make a difference.

Is there an issue with how I am building a library?
Since rustc is generating a staticlib in this case, is there some different method that needs to be used?

Note that I am avoiding the Clang compiler for the moment due to the following:
https://devzone.nordicsemi.com/question/29628/using-clang-and-the-s110-issues-with-supervisor-calls-to-the-softdevice/

Essentially, the gist of the above is that Clang is not quite producing the correct supervisor assembly code for calling in to the bluetooth stack, especially when optimizations are enabled.

Cheers,
Eric


#2

You’ve told rustc to emit a .o file rather than invoking the linker or ar itself as it would normally do, so the #[link()] argument won’t do anything; you’d have to add the C object or static library to the linker command, or tell rustc to emit a static library or executable instead.


#3

Ahh, yep. That was the issue.

Thanks comex!