Suppose I have three crates: foo, bar, and baz.
Cargo.toml:
[workspace]
members = [
"foo",
"bar",
"baz",
]
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
foo/Cargo.toml:
[package]
name = "foo"
version = "0.1.0"
edition = "2018"
[lib]
name = "foo"
crate-type = ["staticlib"]
[dependencies]
bar = { path = "../bar", default-features = false }
foo/src/lib.rs:
#![no_std]
extern crate bar as _;
use core::alloc::{GlobalAlloc, Layout};
#[global_allocator]
static ALLOCATOR: Allocator = Allocator;
struct Allocator;
unsafe impl GlobalAlloc for Allocator {
unsafe fn alloc(&self, _: Layout) -> *mut u8 {
todo!()
}
unsafe fn dealloc(&self, _: *mut u8, _: Layout) {
todo!()
}
}
bar/Cargo.toml:
[package]
name = "bar"
version = "0.1.0"
edition = "2018"
[lib]
name = "bar"
[features]
default = ["heap"]
heap = []
bar/src/lib.rs:
#![no_std]
#![feature(alloc_error_handler)]
use core::alloc::GlobalAlloc;
use core::alloc::Layout;
extern crate alloc;
#[cfg(feature = "heap")]
#[global_allocator]
static ALLOCATOR: Allocator = Allocator;
struct Allocator;
unsafe impl GlobalAlloc for Allocator {
unsafe fn alloc(&self, _: Layout) -> *mut u8 {
todo!()
}
unsafe fn dealloc(&self, _: *mut u8, _: Layout) {
todo!()
}
}
#[panic_handler]
fn panic(_: &core::panic::PanicInfo<'_>) -> ! {
todo!();
}
#[alloc_error_handler]
fn alloc_fail(_: Layout) -> ! {
todo!();
}
baz/Cargo.toml:
[package]
name = "baz"
version = "0.1.0"
edition = "2018"
[lib]
name = "baz"
crate-type = ["staticlib"]
[dependencies]
bar = { path = "../bar" }
baz/src/lib.rs:
#![no_std]
extern crate bar as _;
Here, bar provides the global allocator, and both foo and baz depend on bar. However, foo doesn't enable bar's global allocator to use its original allocator, so foo depends on bar without the default features.
Running cargo clippy on each directory (foo/, bar/, and baz/) succeeds. However, running the command on the project root causes an error:
%cargo clippy
Checking bar v0.1.0 (/tmp/tmp.wlBjM4wNFx/bar)
Checking baz v0.1.0 (/tmp/tmp.wlBjM4wNFx/baz)
Checking foo v0.1.0 (/tmp/tmp.wlBjM4wNFx/foo)
error: the `#[global_allocator]` in this crate conflicts with global allocator in: bar
error: could not compile `foo` due to previous error
Actually, there are no global allocator conflicts as foo uses its original global allocator and disables bar's one. How to avoid this error?
Versions
rustup 1.24.3 (ce5817a94 2021-05-31)
cargo 1.56.0-nightly (b51439fd8 2021-08-09)
rustc 1.56.0-nightly (0035d9dce 2021-08-16)
This is workspace's property. bjorn3 said on Zulip:
If foo, bar and baz are all compiled together, the heap feature will be globally enabled for bar if either foo or baz depends on it. Cargo will take the union of all the features each dependent crate requires and compile dependencies once with these features.
So, bar is compiled with the heap feature because baz requires it, its feature is also enabled when foo is compiled. Thus, both foo's and bar's global allocators are enabled simultaneously, causing the error.
Related
I want to build a no_std static library with rust.
I got the following:
[package]
name = "nostdlb"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["staticlib"]
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
lib.rs:
#![no_std]
pub fn add(left: usize, right: usize) -> usize {
left + right
}
Despite setting the panic behaviour for both dev and release to abort cargo gives the following error:
error: `#[panic_handler]` function required, but not found
error: could not compile `nostdlb` due to previous error
I thought the panic handler is only required when there is no stack unwinding provided by std?
No, you need to write your own one. If you need just aborting, consider using the following one:
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}
I also highly recommend to unmangle the function and use C calling convention if you’re going to use this symbol somewhere (not import it the library as a crate, but link it manually):
#[no_mangle]
extern fn add(right: usize, left: usize) -> usize {
right + left
}
I am working on a library that is configurable with cargo features, and I can't figure out how to get the bencher crate (for benchmarking) to work. In lib.rs I have
#[cfg(feature = "single_threaded")]
pub fn my_function() {...}
In benches/bench.rs I have
#[macro_use]
extern crate bencher;
extern crate my_crate;
use bencher::Bencher;
use my_crate::*;
fn single_thread(bench: &mut Bencher) {
bench.iter(|| {
for _ in 0..10 {
my_function();
}
})
}
benchmark_group!(benches, single_thread);
benchmark_main!(benches);
As is, the compiler says that it cannot find my_function because I haven't specified a configuration. If I add #[cfg(feature = "single_threaded")] above fn single_thread(), it can then find my_function, but that seems to put single_thread in a different context from everything else, such that the two macros at the bottom cannot find single_thread().
If I add #[cfg(feature = "single_threaded")] above each of the two macros, the compiler says to "consider adding a main function to benches/bench.rs," but a main function is added by benchmark_main!. If I put the entire file into a module and declare #[cfg(feature = "single_threaded")] once for the whole module, I get the same error about not having a main function. Any suggestions?
Oh and my Cargo.toml looks like this
[package]
name = "my_crate"
version = "0.1.0"
edition = "2021"
authors = ["Me"]
[dependencies]
[dev-dependencies]
bencher = "0.1.5"
[features]
single_threaded = []
[[bench]]
name = "benches"
harness = false
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.
As the title indicates I'm confused as to how shared libraries work with thread locals in rust. I have a minimal example below:
In a crate called minimal_thread_local_example:
Cargo.toml:
[package]
name = "minimal_thread_local_example"
version = "0.1.0"
edition = "2018"
[dependencies]
has_thread_local = {path ="./has_thread_local"}
libloading = "0.5"
[workspace]
members = ["shared_library","has_thread_local"]
src/main.rs:
extern crate libloading;
use libloading::{Library, Symbol};
use has_thread_local::{set_thread_local, get_thread_local};
fn main() {
let lib = Library::new("libshared_library.so").unwrap();
set_thread_local(10);
unsafe {
let func: Symbol<unsafe extern fn() -> u32> = lib.get(b"print_local").unwrap();
func();
};
println!("From static executable:{}", get_thread_local());
}
In a crate called has_thread_local:
Cargo.toml:
[package]
name = "has_thread_local"
version = "0.1.0"
edition = "2018"
[lib]
[dependencies]
src/lib.rs:
use std::cell::RefCell;
use std::ops::Deref;
thread_local! {
pub static A_THREAD_LOCAL : RefCell<u64> = RefCell::new(0);
}
pub fn set_thread_local(val: u64) {
A_THREAD_LOCAL.with(|refcell| { refcell.replace(val); })
}
pub fn get_thread_local() -> u64 {
A_THREAD_LOCAL.with(|refcell| *refcell.borrow().deref())
}
In a crate called shared_library:
Cargo.toml:
[package]
name = "shared-library"
version = "0.1.0"
edition = "2018"
[lib]
crate-type = ["cdylib"]
[dependencies]
has_thread_local = {path = "../has_thread_local"}
src/lib.rs:
use has_thread_local::get_thread_local;
#[no_mangle]
unsafe extern "system" fn print_local() {
println!("From shared library:{}",get_thread_local());
}
Here's a github link for the above.
In essence I have a static executable and a shared library, with a thread local variable declared in the static executable. I then set that variable to 10 and access it from the shared library and static executable.
This outputs:
From shared library:0
From static executable:10
I'm confused as to why this is outputted(occurs on both stable and nightly). I would have imagined that both would be 10, since the thread local is declared in a static executable and is only accessed via functions also located in that static executable. I'm looking for an explanation as to why I am observing this behavior, and how to make my thread local have the same value across the entire thread, aka have the same value in the shared library and static library.
The reason this behavior is observed is because the shared library contains it's own copy of the code of crates it depends on, resulting in two different thread local declarations.
The solution to this is to pass a reference to the thread local in question, instead of directly accessing the thread local. See here for more information on how to obtain a reference to a thread local: How to create a thread local variable inside of a Rust struct?
I'm trying to do some dynamic library loading in Rust. I'm getting a segmentation fault when passing a large Vec from a dynamically loaded library function. It's a basic function that creates a Vec<i32> of a specified size. If the Vec gets much bigger than 8MB, the program hits a segfault on OSX. I haven't had the same problem when running on linux, can anyone look at this and tell me if I'm doing something wrong here? I'm running this with:
$ cargo build --release
$ ./target/release/linkfoo
8281
[1] 84253 segmentation fault ./target/release/linkfoo
Cargo.toml
[package]
name = "linkfoo"
version = "0.1.0"
authors = ["Nate Mara <nathan.mara#kroger.com>"]
[dependencies]
libloading = "0.3.0"
[lib]
name = "foo"
crate-type = ["dylib"]
main.rs
extern crate libloading as lib;
fn main() {
let dylib = lib::Library::new("./target/release/libfoo.dylib").expect("Failed to load library");
let func = unsafe {
let wrapped_func: lib::Symbol<fn(&[i32]) -> Vec<i32>> = dylib.get(b"alloc")
.expect("Failed to load function");
wrapped_func.into_raw()
};
let args = vec![8182];
println!("{}", func(&args).len());
}
lib.rs
#[no_mangle]
pub fn alloc(args: &[i32]) -> Vec<i32> {
let size = args[0] as usize;
let mut mat = Vec::with_capacity(size);
for _ in 0..size {
mat.push(0);
}
mat
}
Rust uses the system allocator for dynamic libraries, and jemalloc for all other code. This difference in loaders was causing the segfault, and I was able to fix it by adding this to the top of main.rs:
#![feature(alloc_system)]
extern crate alloc_system;