Rust binary and library can't find each other

Viewed 581

I have a static library (OS kernel in Rust), so I have a file tree:

  • src/
  • src/lib.rs

I added a binary so that I could actually run my kernel (the kernel functions are implemented in the library). This is a feature of a project, once enabled, it "enables" the additional src/main.rs file to compile and run the kernel as well as makes the project depend on the bootloader crate; if the feature is not enabled, the src/main.rs file isn't compiled, the bootloader crate isn't used for the project, and I call the src/lib.rs functions myself directly from an assembly file by linking the static library produces by rustc later myself:

[package]
name = "myos"
version = "0.1.0"
edition = "2018"

[lib]
crate-type = ["staticlib"]

# This is for the case of `src/bin/main.rs` which doesn't work as well.
#[[bin]]
#name = "main"
#required-features = ["bootloader"]

[dependencies.bootloader]
version = "0.10"
optional = true

[profile.dev]
panic = "abort"

[profile.release]
panic = "abort"

[features]
default = ["bootloader"]

The problem is that the main.rs can't see the lib.rs no matter what I try:

lib.rs

#![no_std]

#[no_mangle]
pub extern "C" fn main() -> i32 {
    0
}

#[no_mangle]
pub extern "C" fn kernel_early() {

}

pub fn myos_panic(info: &core::panic::PanicInfo<'_>) -> ! {
    loop {}
}

main.rs

#![no_std]
#![no_main]

use myos::{kernel_early, main};

#[no_mangle]
pub extern "C" fn _start() -> ! {
    kernel_early();
    main();

    loop {}
}

use core::panic::PanicInfo;

#[panic_handler]
fn panic(info: &PanicInfo<'_>) -> ! {
    crate::myos_panic(info);
}

The crate's name of the project is myos. Here is what I've tried so far to use the kernel_early and main functions from the lib.rs module in the main.rs:

use myos::{kernel_early, main};
use crate::{kernel_early, main};
use super::{kernel_early, main};

Tried even playing with mod lib; use lib::*, of course, it only made things worse.

How do I make the binary actually work with my library? I made tons of cargo examples and I know how they work, it just seems it doesn't work with such a project structure where a binary and a library are all within one single crate. I also tried moving the src/main.rs to src/bin/main.rs but the effect was exactly the same.

The errors I have are:

 --> src/main.rs:4:5
  |
4 | use myos::{kernel_early, main};
  |     ^^^^ use of undeclared crate or module `myos`

error[E0432]: unresolved imports `crate::kernel_early`, `crate::main`
 --> src/main.rs:4:13
  |
4 | use crate::{kernel_early, main};
  |             ^^^^^^^^^^^^  ^^^^ no `main` in the root
  |             |
  |             no `kernel_early` in the root

error[E0425]: cannot find function `myos_panic` in the crate root
  --> src/main.rs:18:12
   |
18 |     crate::myos_panic(info);
   |            ^^^^^^^^^^ not found in the crate root

P.S. The problem is not in the name mangling, as I also tried calling functions without it disabled.

1 Answers

You've specified crate-type = ["staticlib"] for your library, which produces, only, a library in the target's native static library format. You cannot use such a library as an ordinary Rust crate (use myos::), because that requires an "rlib" (or equivalently for the time being, "lib") crate type.

I think it might work if you declare the functions in the library like they were C functions, i.e.

extern {
   fn main() -> i32;
   fn kernel_early();
}

and link to the compiled staticlib as you would to a C library. (I don't know how that actually works; I've only ever used -sys crates myself.)

However, if you want to do anything Rusty that can't be done through the C ABI (like generic functions), this won't help you. If I understand correctly, your binary is just for testing the library code. In that case, you can change your target configuration to

[lib]
crate-type = ["staticlib", "lib"]

and then Cargo will build both formats; the lib for your test binary and the staticlib for your bare metal bootloader.

Related