5

I'm trying to build a minimal program in C that calls Rust functions, preferably compiled with #![no_std], in Windows, using GCC 6.1.0 and rustc 1.11.0-nightly (bb4a79b08 2016-06-15) x86_64-pc-windows-gnu. Here's what I tried first:

main.c

#include <stdio.h>

int sum(int, int);

int main()
{
    printf("Sum is %d.\n", sum(2, 3));
    return 0;
}

sum.rs

#![no_std]
#![feature(libc)]
extern crate libc;

#[no_mangle]
pub extern "C" fn sum(x: libc::c_int, y: libc::c_int) -> libc::c_int
{
    x + y
}

Then I tried running:

rustc --crate-type=staticlib --emit=obj sum.rs

But got:

error: language item required, but not found: `panic_fmt`
error: language item required, but not found: `eh_personality`
error: language item required, but not found: `eh_unwind_resume`
error: aborting due to 3 previous errors

OK, so some of those errors are related to panic unwinding. I found out about a Rust compiler setting to remove unwinding support, -C panic=abort. Using that, the errors about eh_personality and eh_unwind_resume disappeared, but Rust still required the panic_fmt function. So I found its signature at the Rust docs, then I added that to the file:

sum.rs

#![no_std]
#![feature(lang_items, libc)]
extern crate libc;

#[lang = "panic_fmt"]
pub fn panic_fmt(_fmt: core::fmt::Arguments, _file_line: &(&'static str, u32)) -> !
    { loop { } }

#[no_mangle]
pub extern "C" fn sum(x: libc::c_int, y: libc::c_int) -> libc::c_int
{
    x + y
}

Then, I tried building the whole program again:

rustc --crate-type=staticlib --emit=obj -C panic=abort sum.rs
gcc -c main.c
gcc sum.o main.o -o program.exe

But got:

sum.o:(.text+0x3e): undefined reference to `core::panicking::panic::h907815f47e914305'
collect2.exe: error: ld returned 1 exit status

The panic function reference is probably from a overflow check in the addition at sum(). That's all fine and desirable. According to this page, I need to define my own panic function to work with libcore. But I can't find instructions on how to do so: the function for which I am supposed to provide a definition is called panic_impl in the docs, however the linker is complaining about panic::h907815f47e914305, whatever that's supposed to be.

Using objdump, I was able to find the missing function's name, and hacked that into C:

main.c

#include <stdio.h>
#include <stdlib.h>

int sum(int, int);

void _ZN4core9panicking5panic17h907815f47e914305E()
{
    printf("Panic!\n");
    abort();
}

int main()
{
    printf("Sum is %d.\n", sum(2, 3));
    return 0;
}

Now, the whole program compiles and links successfully, and even works correctly.

If I then try using arrays in Rust, another kind of panic function (for bounds checks) is generated, so I need to provide a definition for that too. Whenever I try something more complex in Rust, new errors arise. And, by the way, panic_fmt seems to never be called, even when a panic does happen.

Anyways, this all seems very unreliable, and contradicts every information I could find via Google on the matter. There's this, but I tried to follow the instructions to no avail.

It seems such a simple and fundamental thing, but I can't get it to work the right way. Perhaps it's a Rust nightly bug? But I need libc and lang_items. How can I generate a Rust object file/static library without unwinding or panic support? It should probably just execute an illegal processor instruction when it wants to panic, or call a panic function I can safely define in C.

1
  • Please do not edit your question to include an answer. Stack Overflow explicitly encourages you to add your own answer below. Commented Jun 21, 2016 at 13:19

2 Answers 2

5

You shouldn't use --emit=obj; just rustc --crate-type=staticlib -C panic=abort sum.rs should do the right thing. (This fixes the _ZN4core9panicking5panic17h907815f47e914305E link error.)

To fix another link error, you need to write panic_fmt correctly (note the use of extern):

#[lang="panic_fmt"]
extern fn panic_fmt(_: ::core::fmt::Arguments, _: &'static str, _: u32) -> ! {
    loop {}
}

With those changes, everything appears to work the way it's supposed to.


You need panic_fmt so you can decide what to do when a panic happens: if you use #![no_std], rustc assumes there is no standard library/libc/kernel, so it can't just call abort() or expect an illegal instruction to do anything useful. It's something which should be exposed in stable Rust somehow, but I don't know if anyone is working on stabilizing it.

You don't need to use #![feature(libc)] to get libc; you should use the version posted on crates.io instead (or you can declare the functions you need by hand).

Sign up to request clarification or add additional context in comments.

1 Comment

error[E0433]: failed to resolve: maybe a missing crate core? --> lib.rs:13:26 | 13 | extern fn panic_fmt(_: ::core::fmt::Arguments, _: &'static str, _: u32) -> ! | ^^^^ maybe a missing crate core? [...] Some errors have detailed explanations: E0433, E0522, E0658.
4

So, the solution, from the accepted answer, was:

main.c

#include <stdio.h>
#include <stdlib.h>

int sum(int, int);

void panic(const char* filename_unterminated, int filename_size, int line_num)
{
    printf("Panic! At line %d, file ", line_num);

    for (int i = 0; i < filename_size; i++)
        printf("%c", filename_unterminated[i]);

    abort();
}

int main()
{
    // Sum as u8 will overflow to test panicking.
    printf("Sum is %d.\n", sum(0xff, 3));
    return 0;
}

sum.rs

#![no_std]
#![feature(lang_items, libc)]
extern crate libc;

extern "C"
{
    fn panic(
        filename_unterminated: *const libc::c_char,
        filename_size: libc::c_int,
        line_num: libc::c_int) -> !;
}

#[lang="panic_fmt"]
extern fn panic_fmt(_: ::core::fmt::Arguments, filename: &'static str, line_num: u32) -> !
{
    unsafe { panic(filename.as_ptr() as _, filename.len() as _, line_num as _); }
}

#[no_mangle]
pub extern "C" fn sum(x: libc::c_int, y: libc::c_int) -> libc::c_int
{
    // Convert to u8 to test overflow panicking.
    ((x as u8) + (y as u8)) as _
}

And compiling with:

rustc --crate-type=staticlib -C panic=abort sum.rs
gcc -c main.c
gcc main.o -L . -l sum -o program.exe

Now everything works, and I have a panic handler in C that shows where the error occurred!

1 Comment

error[E0554]: #![feature] may not be used on the stable release channel --> src/sum.rs:2:1 | 2 | #![feature(lang_items, libc)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0522]: definition of an unknown language item: panic_fmt --> src/sum.rs:13:1 | 13 | #[lang="panic_fmt"] | ^^^^^^^^^^^^^^^^^^^ definition of unknown language item panic_fmt and many other errors

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.