Does Rust libc crate inhibit the compilation of custom panic handler? - rust

So we are currently trying to compile some Rust code that we can then link to some C code. To do this we are using Bindgen to generate an FFI, and then we will use it to call some C functions from Rust.
However, we must first have the crate "libc" as a dependency in the Cargo.toml file of the project. The project we are currently working on demands that we use the crate wide !#[no_std] attribute, as we don't want the entire stdlib of Rust. We only need the core. The libc crate says we can "request" it not be linked to the standard library by putting some options in the Cargo.toml file, namely:
[dependencies]
libc = { version = "0.2", default-features = false }
This is all fine and dandy, but when we try to compile we get the following error message.
Ubuntu:~/nautilus/src/rust/example# cargo build
Compiling example v0.0.0 (/home/me/nautilus/src/rust/example)
error[E0152]: duplicate lang item found: `panic_impl`.
--> src/lib.rs:33:1
|
33 | / pub fn nk_rust_panic(_info: &PanicInfo) -> !
34 | | {
35 | | // should call nk_panic here...
36 | | loop { }
37 | | }
| |_^
|
= note: first defined in crate `std`.
error: aborting due to previous error
For more information about this error, try `rustc --explain E0152`.
error: Could not compile `example`.
To learn more, run the command again with --verbose.
E0152 is the following
A lang item was redefined.
Erroneous code example:
```
#![feature(lang_items)]
#[lang = "arc"]
struct Foo; // error: duplicate lang item found: `arc`
```
Lang items are already implemented in the standard library. Unless you are
writing a free-standing application (e.g. a kernel), you do not need to provide
them yourself.
You can build a free-standing crate by adding `#![no_std]` to the crate
attributes:
```
#![no_std]
```
Our lib.rs file has #![no_std] already in it, but it appears that since libc would normally link against stdlib, that maybe Rust thinks that we cannot have a custom panic handler if we are using libc.
The problem relieves itself when we remove libc from the Cargo.toml file, and remove the extern crate lib from lib.rs.
lib.rs
// no stdlib
#![no_std]
// Give us this feature to override?
#![feature(start)]
#![feature(lang_items)]
// avoid buildins - we want it to use our library
#![no_builtins]
// The following cruft is here to handle Rust->OS dependencies
// currently only one: Rust needs to know how to panic
use core::panic::PanicInfo;
extern crate libc;
#[panic_handler]
#[no_mangle]
pub fn nk_rust_panic(_info: &PanicInfo) -> !
{
// should call nk_panic here... (panic handler of OS)
loop { }
}
Cargo.toml
[package]
name = "example" # this is for core-kernel
version = "0.0.0"
[lib]
crate-type = ["staticlib"]
[dependencies]
libc = { version = "0.2", default-features = false }
[build-dependencies]
bindgen = "0.42.2"
[profile.dev]
panic = "abort" # no stack unwind on rust panic
[profile.release]
panic = "abort" # no stuck unwind on rust panic
So, in sum, does using libc as a dependency make us unable to use our own custom panic handler? Are there any wonky compiler flags we can pass directly to rustc to eliminate this problem?
Compiler is 1.32.0-nightly
Libc version is libc v0.2.44

You are running into a bug in Cargo.
Features are a bit subtle. They can be enabled by any transitive dependency, which is expected behaviour. However, they can even be enabled by transitive build dependencies, which is a bug.

Related

structopt unable to find its Args parser when defined in a separate file

I'm starting on a command-line tool in Rust, and hitting a wall right from the get-go. I can parse command-line arguments using StructOpt if the Opt struct is defined in main.rs, but since I want to be able to pass the Opt struct into the library code, I'm defining it in its own file so that other parts of the library know what it's all about.
Here's the version of the code that's dumping the fewest errors, although I concede that it's largely cobbled together by trying things suggested by the compiler and some random SO suggestions, so it may be completely wrong.
The build error I'm getting is:
$ cargo run
Compiling basic v0.1.0 (/home/mpalmer/src/action-validator/blobble)
error[E0433]: failed to resolve: maybe a missing crate `structopt`?
--> src/opt.rs:8:5
|
8 | /// Activate debug mode
| ^^^^^^^^^^^^^^^^^^^^^^^ not found in `structopt::clap`
|
help: consider importing this struct
|
3 | use opt::structopt::clap::Arg;
|
For more information about this error, try `rustc --explain E0433`.
error: could not compile `basic` due to previous error
$ cargo --version
cargo 1.56.0 (4ed5d137b 2021-10-04)
$ rustc --version
rustc 1.56.0 (09c42c458 2021-10-18)
(Yes, I have tried adding use opt::structopt::clap::Arg;, just in case, but the error doesn't go away and I get a warning about an unused import -- while still being told to try adding the same use that is unused, which is amusing)
Cargo.toml
[package]
name = "basic"
version = "0.1.0"
authors = ["User"]
[dependencies]
structopt = "0.3"
src/main.rs
extern crate basic;
extern crate structopt;
use basic::Opt;
use structopt::StructOpt;
fn main() {
let opt = Opt::from_args();
println!("{:#?}", opt)
}
src/lib.rs
mod opt;
pub use crate::opt::Opt;
src/opt.ts
extern crate structopt;
use self::structopt::StructOpt;
#[derive(StructOpt, Debug)]
#[structopt(name = "basic")]
pub struct Opt {
/// Activate debug mode
#[structopt(short,long)]
debug: bool,
}
Suggestions gratefully appreciated.
A working version is
Cargo.toml
[package]
name = "basic"
version = "0.1.0"
authors = ["User"]
edition = "2018"
[dependencies]
structopt = "0.3"
lib.rs
#[macro_use] extern crate structopt;
use structopt::StructOpt;
pub mod opt;
opt.rs
#[derive(StructOpt, Debug)]
#[structopt(name = "basic")]
pub struct Opt {
/// Activate debug mode
#[structopt(short,long)]
debug: bool,
}
main.rs
#[macro_use] extern crate structopt;
use structopt::StructOpt;
fn main() {
let opt = basic::opt::Opt::from_args();
println!("{:#?}", opt);
}
You need to declare use structopt::StructOpt because from_args trait must be in the scope.

sin(), cos(), log10() (float) not found for target thumbv7em-none-eabihf

I'm using Rust 1.51 and this minimal crate:
#![no_std]
fn main() {
let a = 2.0.cos();
}
I'm building it with cargo check --target thumbv7em-none-eabihf and the compiler complains with this message: no method named 'cos' found for type '{float}' in the current scope. Same for sin() and log10().
I found https://docs.rust-embedded.org/cortex-m-quickstart/cortex_m_quickstart/ and I would expect the above message for targets thumbv6m-none-eabi or thumbv7em-none-eabi but not for thumbv7em-none-eabihf which has FPU support.
How can I solve this?
In Rust 1.51 (and below) functions like sin, cos, or log10 are not part of the core library (core::) but only the standard library (std::), therefore they are not available.
A practical solution is to use the crate libm which offers typical mathematic functions for no_std-environments.
#![no_std]
fn main() {
let a = libm::cosf(2.0);
}
See:
libm crate: https://crates.io/crates/libm
Discussion to move math functions to core library https://github.com/rust-lang/rfcs/issues/2505
What #phip1611 said is correct, cos is no longer part of the core library and hence not usable in no_std out of the box. You can use libm directly OR via a std friendly wrapper using the num-traits crate with the libm feature enabled. This lets you write no_std code as you would write std code. E.g.
# cargo.toml
num-traits = { version = "0.2", default-features = false, features = ["libm"] }
And then in your code:
#![no_std]
#[allow(unused_imports)]
use num_traits::real::Real;
fn main() {
let a = 2.0.cos();
}

How do I use std::collections::BitSet in stable Rust?

I am trying to use BitSet data structure, but it gives me a compile error saying it was not able to find the BitSet. Has std::collections::BitSet been released in the stable version?
use std::collections::BitSet;
fn main() {
println!("Hello, world!");
}
Produces the error:
error[E0432]: unresolved import `std::collections::BitSet`
--> src/main.rs:1:5
|
1 | use std::collections::BitSet;
| ^^^^^^^^^^^^^^^^^^^^^^^^ no `BitSet` in `collections`
It seems that BitSet existed in Rust 1.3.0, which is very old, but was already deprecated at that time and finally removed by this commit.
Instead, you can use bit-set crate, as suggested by the deprecation message above. There's also documentation.
extern crate bit_set;
use bit_set::BitSet;
fn main() {
let mut s = BitSet::new();
s.insert(32);
s.insert(37);
s.insert(3);
println!("s = {:?}", s);
}
You'll have to add a dependency to the bit-set crate in some way. It's easy if you're using Cargo:
[package]
name = "foo"
version = "0.1.0"
authors = ["Foo Bar <foo#example.com>"]
[dependencies]
bit-set = "0.4.0" # Add this line
If you're using the official Rust Playground, you can automatically use bit-set, because it is one of the top 100 downloaded crates or a dependency of one of them.

How do I specify the linker path in Rust?

I'm trying to link a Rust program with libsoundio. I'm using Windows and there's a GCC binary download available. I can link it like this if I put it in the same folder as my project:
#[link(name = ":libsoundio-1.1.0/i686/libsoundio.a")]
#[link(name = "ole32")]
extern {
fn soundio_version_string() -> *const c_char;
}
But I really want to specify #[link(name = "libsoundio")] or even #[link(name = "soundio")], and then provide a linker path somewhere else.
Where can I specify that path?
I tried the rustc-link-search suggestion as follows:
#[link(name = "libsoundio")]
#[link(name = "ole32")]
extern {
fn soundio_version_string() -> *const c_char;
}
And in .cargo/config:
[target.i686-pc-windows-gnu.libsoundio]
rustc-link-search = ["libsoundio-1.1.0/i686"]
rustc-link-lib = ["libsoundio.a"]
[target.x86_64-pc-windows-gnu.libsoundio]
rustc-link-search = ["libsoundio-1.1.0/x86_64"]
rustc-link-lib = ["libsoundio.a"]
But it still only passes "-l" "libsoundio" to gcc and fails with the same ld: cannot find -llibsoundio. Am I missing something really obvious? The docs seem to suggest this should work.
As stated in the documentation for a build script:
All the lines printed to stdout by a build script [... starting] with cargo: is interpreted directly by Cargo [...] rustc-link-search indicates the specified value should be passed to the compiler as a -L flag.
In your Cargo.toml:
[package]
name = "link-example"
version = "0.1.0"
authors = ["An Devloper <an.devloper#example.com>"]
build = "build.rs"
And your build.rs:
fn main() {
println!(r"cargo:rustc-link-search=C:\Rust\linka\libsoundio-1.1.0\i686");
}
Note that your build script can use all the power of Rust and can output different values depending on target platform (e.g. 32- and 64-bit).
Finally, your code:
extern crate libc;
use libc::c_char;
use std::ffi::CStr;
#[link(name = "soundio")]
extern {
fn soundio_version_string() -> *const c_char;
}
fn main() {
let v = unsafe { CStr::from_ptr(soundio_version_string()) };
println!("{:?}", v);
}
The proof is in the pudding:
$ cargo run
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
Running `target\debug\linka.exe`
"1.0.3"
Ideally, you will create a soundio-sys package, using the convention for *-sys packages. That simply has a build script that links to the appropriate libraries and exposes the C methods. It will use the Cargo links key to uniquely identify the native library and prevent linking to it multiple times. Other libraries can then include this new crate and not worry about those linking details.
Another possible way is setting the RUSTFLAGS like:
RUSTFLAGS='-L my/lib/location' cargo build # or cargo run
I don't know if this is the most organized and recommended approach, but it worked for my simple project.
I found something that works OK: you can specify links in your Cargo.toml:
[package]
links = "libsoundio"
build = "build.rs"
This specifies that the project links to libsoundio. Now you can specify the search path and library name in the .cargo/config file:
[target.i686-pc-windows-gnu.libsoundio]
rustc-link-search = ["libsoundio-1.1.0/i686"]
rustc-link-lib = [":libsoundio.a"]
[target.x86_64-pc-windows-gnu.libsoundio]
rustc-link-search = ["libsoundio-1.1.0/x86_64"]
rustc-link-lib = [":libsoundio.a"]
(The : prefix tells GCC to use the actual filename and not to do all its idiotic lib-prepending and extension magic.)
You also need to create an empty build.rs:
fn main() {}
This file is never run, because the values in .cargo/config override its output, but for some reason Cargo still requires it - any time you use links = you have to have build =, even if it isn't used.
Finally in main.rs:
#[link(name = "libsoundio")]
#[link(name = "ole32")]
extern {
fn soundio_version_string() -> *const c_char;
}

Unable to compile project using MIO - use of unstable library feature 'udp_extras'

I am trying to reproduce a TCP client/server chat from an article. I cannot compile the skeleton for the server which uses the mio crate. Here is my code:
extern crate mio;
use mio::*;
struct WebSocketServer;
impl Handler for WebSocketServer {
type Timeout = usize;
type Message = ();
}
fn main() {
let mut event_loop = EventLoop::new().unwrap();
let mut handler = WebSocketServer;
event_loop.run(&mut handler).unwrap();
}
And here is error message which is absolutely unclear for me:
C:\Users\ZZZ\.cargo\git\checkouts\mio-75006fe295376f74\master\src\sys\windows\udp.rs:177:44: 177:61 error: use of unstab
le library feature 'udp_extras': available through the `net2` crate on crates.io
C:\Users\ZZZ\.cargo\git\checkouts\mio-75006fe295376f74\master\src\sys\windows\udp.rs:177 try!(self.inner().socke
t.socket()).set_broadcast(on)
^~~~~~~~~~~~~~~~~
error: aborting due to previous error
Could not compile `mio`.
And here is Cargo.toml:
[package]
name = "rust-chat"
version = "0.2.0"
authors = ["Alex <zzz#yyy.com>"]
[dependencies.mio]
git = "https://github.com/carllerche/mio"
I tried to add net2 to section dependencies explicitly but that still does not work.
Looks like Windows support of mio (or rust) still not very good. So this code, which does not compiled with rustc 1.3-stable was compiled successfully with rusts 1.5-nightly.

Resources