Undefined reference to main when using Real-Time For the Masses - rust

I'm trying to write a multi-threaded bare-metal application for the STM32F4Discovery using the Real-Time For the Masses (RTFM) crate. I've frankensteined together a minimal application from an example for the STM32F3Discovery board and this example:
#![deny(unsafe_code)]
#![no_main]
#![no_std]
extern crate cortex_m;
extern crate cortex_m_rtfm as rtfm;
extern crate cortex_m_semihosting;
extern crate panic_semihosting;
extern crate stm32f4;
use stm32f4::stm32f407;
use rtfm::app;
app! {
device: stm32f407,
}
fn init(_p: init::Peripherals) {
}
fn idle() -> ! {
loop {
rtfm::wfi();
}
}
I can get it to compile but linking with rust-lld fails with
= note: rust-lld: error: undefined symbol: main
I am confused because when I run cargo expand I do get a main function:
fn main() {
#![allow(path_statements)]
let init: fn(init::Peripherals) = init;
rtfm::atomic(unsafe { &mut rtfm::Threshold::new(0) },
|_t|
unsafe {
let _late_resources =
init(init::Peripherals{core:
::stm32f407::CorePeripherals::steal(),
device:
::stm32f407::Peripherals::steal(),});
});
let idle: fn() -> ! = idle;
idle();
}
I'm new to Rust (in fact I was hoping to learn the language with this project) and have no idea where the error might be located.

As you ask the compiler to not insert main, there is no main symbol in your program.
Rust uses symbol mangling so your main function doesn't have a symbol named main.
The answer depends on your contextm but generally this should do it:
#[no_mangle] // ensure that this symbol is called `main` in the output
pub extern fn main(argc: i32, argv: *const *const u8) -> i32 {
}
All additional information can be found here

Related

How do I re-export a symbol from a crate via FFI with a different name?

I have a crate, ffizz-string, which has a number of public functions with an fz_string_ prefix. In another crate taskchampion-lib, I would like to export these same functions, but with a different prefix (tc_string_). The idea here is that ffizz-string provides some generic FFI string utilities, and the crate using those utilities wants to "rebrand" the functions to match the crate's other FFI functions.
For Rust libraries, this renaming is easy: pub use fz_string_borrow as tc_string_borrow. However, this does not seem to do the trick for exported functions. I have a few things I've tried below, and for each I looked at the resulting symbols using `nm ./target/debug/deps/libtaskchampion_lib-....rlib | grep string_borrow
#![allow(non_upper_case_globals)]
// attempt 0: demonstrate that regular function symbols appear in the nm grep
#[no_mangle]
pub unsafe extern "C" fn tc_string_borrow_0() {
todo!()
}
// result: appears in nm grep as 'tc_string_borrow_0`:
// 0000000000000000 T tc_string_borrow_0
// attempt 1: just rename using 'as'
pub use ffizz_string::fz_string_borrow as tc_string_borrow_1;
// result: appears in nm grep as 'fz_string_borrow`
// attempt 2: rename with `link_name`
// link_name only applies to foreign fns and statics
extern "C" {
#[link_name = "tc_string_borrow_2"]
pub static tc_string_borrow_2:
unsafe extern "C" fn(cstr: *const i8) -> ffizz_string::fz_string_t;
}
// result: not renamed (turns out link_name was the wrong attribute)
// attempt 3: export_name on a fn
/*
// "attribute should be applied to a free function, impl method or static"
#[export_name="tc_string_borrow_3"]
pub use ffizz_string::fz_string_borrow as tc_string_borrow_3;
*/
// result: error: attribute should be applied to a free function, impl method or static
// attempt 4: export_name on a static
#[export_name = "tc_string_borrow_4"]
pub static tc_string_borrow_4: unsafe extern "C" fn(cstr: *const i8) -> ffizz_string::fz_string_t =
ffizz_string::fz_string_borrow;
// result: compiles, but tc_string_borrow_4 is a pointer to fz_string_borrow:
// 0000000000000000 D tc_string_borrow_4
// attempt 5: export_name on a static
#[used]
pub static tc_string_borrow_5: unsafe extern "C" fn(cstr: *const i8) -> ffizz_string::fz_string_t =
ffizz_string::fz_string_borrow;
// result: not renamed
The one approach that I've found effective is to wrap the utility functions with another function:
#[inline]
pub unsafe extern "C" fn tc_string_borrow(cstr: *const i8) -> ffizz_string::fz_string_t {
ffizz_string::fz_string_borrow(cstr)
}
But this relies on the compiler to inline those cross-crate calls, and also requires reiterating the function signature. Is there a better way?

Missing `start` lang_item when using Real-Time For the Masses

I'm trying to write a multi-threaded bare-metal application for the STM32F4Discovery using the Real-Time For the Masses (RTFM) crate. I've frankensteined together a minimal application from an example for the STM32F3Discovery board and this example:
#![no_std]
extern crate cortex_m;
extern crate cortex_m_rtfm as rtfm;
extern crate cortex_m_semihosting;
extern crate panic_semihosting;
extern crate stm32f4;
use stm32f4::stm32f407;
use rtfm::app;
app! {
device: stm32f407,
}
fn init(_p: init::Peripherals) {
}
fn idle() -> ! {
loop {
rtfm::wfi();
}
}
Compilation fails with
error: requires `start` lang_item
I'm new to Rust (in fact I was hoping to learn the language with this project) and have no idea where the error might be located. Is it just that the app! macro is broken or am I missing something?
I've found a workaround in RTFM's issues on GitHub that allows to compile the code above:
import cortex-m-rt and its entry macro
#[macro_use(entry)]
extern crate cortex_m_rt as rt;
add #![no_main] to the head of the source
wrap the main function using the entry macro:
#[entry]
fn entry_wrap() -> ! {
main();
loop {}
}
I haven't tested this yet but it compiles without problems.

Rust not creating function in lib extern FFI

I have many Rust functions working perfectly across Ruby FFI. But following directions from two different sites for creating a free_array method is not making the method available in the linked library.
This example is the working example of freeing a String returned from Ruby.
use libc::c_char;
use std::ffi::CString;
#[no_mangle]
pub extern "C" fn free_string(s: *mut c_char) {
unsafe {
if s.is_null() { return }
CString::from_raw(s)
};
}
And here are two attempts at implementing a way to free the memory of an Array.
use std::mem::transmute;
use ruby_array::RubyArray;
#[no_mangle]
pub extern "C" fn free_array(ra: *mut RubyArray) {
let _ra: Box<RubyArray> = unsafe{ transmute(ra) };
}
// OR
#[no_mangle]
pub extern "C" fn free_array(ptr: *mut RubyArray) {
if ptr.is_null() { return }
unsafe { Box::from_raw(ptr); }
}
This results in an error:
Function 'free_array' not found in [/libfaster_path.so] (FFI::NotFoundError)
Here's the Struct I'm using and which gets created perfectly in to Ruby from Rust.
use libc;
use std::mem;
#[repr(C)]
pub struct RubyArray {
len: libc::size_t,
data: *const libc::c_void,
}
impl RubyArray {
#[allow(dead_code)]
pub fn from_vec<T>(vec: Vec<T>) -> RubyArray {
let array = RubyArray {
data: vec.as_ptr() as *const libc::c_void,
len: vec.len() as libc::size_t
};
mem::forget(vec);
array
}
}
But that's not relevant as it's not the issue. The issue is the method is not being made available in the library output for FFI to read from. What's wrong with this? Rust is happy and I've written many other methods in similar manner that work. What makes this not found in the .so file?
The file is included in the main src/lib.rs with pub mod so there's nothing wrong there. It's the same as the other working methods.
I'll be posting a blog with much fuller implementation details later and I'll add a link to the comment section below for those who are interested.
Minor Update
I'm pretty sure this is an issue with Rust. I've used both Ruby's Fiddle and FFI to verify that this method couldn't be accessed where as other methods could be by both.
I grepped the binary and found the text showing the free_array method in the binary but apparently that's not compiled correctly to be read by FFI.
The information I provided was not enough to debug the issue. The real problem was I had changed from building source from release to debug and had not updated the FFI linked library folder to reflect that. So the code kept pointing at and old lib.

How do I use a macro defined in another crate?

I've seen a couple tutorials to create a Python module using the cpython crate but still have errors when building:
extern crate cpython;
use cpython::{PyObject, PyResult, Python, PyTuple, PyDict, ToPyObject, PythonObject};
fn add_two(py: Python, args: &PyTuple, _: Option<&PyDict>) -> PyResult<PyObject> {
match args.as_slice() {
[ref a_obj, ref b_obj] => {
let a = a_obj.extract::<i32>(py).unwrap();
let b = b_obj.extract::<i32>(py).unwrap();
let mut acc:i32 = 0;
for _ in 0..1000 {
acc += a + b;
}
Ok(acc.to_py_object(py).into_object())
},
_ => Ok(py.None())
}
}
py_module_initializer!(example, |py, module| {
try!(module.add(py, "add_two", py_fn!(add_two)));
Ok(())
});
I get:
error: macro undefined: 'py_module_initializer!'
Where do I get it? I am using Rust 1.12.
UPD
Need to add #[macro_use] (as in answers)
For other errors - see examples here
You probably need to declare cpython as follows:
#[macro_use] extern crate cpython;
To be able to use cpython's macros. You can consult the example in its docs.
You must add the #[macro_use] attribute on the extern crate declaration to ask the compiler to bring the macros exported by the crate into your crate's namespace.
#[macro_use]
extern crate cpython;

In Rust how can I define or import a C struct from a third party library?

The Rust FFI guide (http://static.rust-lang.org/doc/master/guide-ffi.html) nicely demonstrates how to import C functions that use standard C types that are wrapped by the Rust std::lib library, such as size_t. But how do I bring C data structures defined in third party libraries into a Rust program?
I'm working with libmemcached, which has the following function:
memcached_st* memcached_create(memcached_st *ptr)
which is typically invoked like so in a C program (to kick things off):
#include <libmemcached/memcached.h>
// ...
memcached_st *memc;
memc = memcached_create(NULL);
The memcached_st is an opaque C struct - how do I declare and use that in a Rust program? Here are my failed attempts so far:
use std::libc::*;
use ptr;
#[link(name = "memcached")]
extern {
struct memcached_st; // error: unexpected token: `struct`
memcached_st* memcached_create(memcached_st *ptr);
}
fn main() {
unsafe {
let memc = memcached_create(ptr:null());
println!("{:?}", memc);
}
}
and
use std::libc::*;
use ptr;
#[link(name = "memcached")]
extern {
// error: unexpected token: `memcached_st`
memcached_st* memcached_create(memcached_st *ptr);
}
fn main() {
unsafe {
let memc = memcached_create(ptr:null());
println!("{:?}", memc);
}
}
Using empty structure is a valid approach. You almost got it, you just don't need to put the struct definition in extern block. Also you can't use C code inside Rust sources - extern definitions have to follow standard Rust syntax.
use std::ptr;
struct memcached_st;
#[link(name = "memcached")]
extern {
fn memcached_create(ptr: *memcached_st) -> *memcached_st;
}
fn main() {
unsafe {
let memc = memcached_create(ptr::null());
println!("{:?}", memc);
}
}

Resources