How do I pass the linker the -rlink argument? - rust

I'm trying to cross compile a rust project with cross. My project depends on libcurl. The linker can find the libcurl .so but not the libraries which it depends on:
I'm running:
cross build -vv --target=arm-unknown-linux-gnueabihf
which gives me:
= note: /usr/arm-linux-gnueabihf/bin/../lib/gcc/arm-linux-gnueabihf/8.3.0/../../../../arm-linux-gnueabihf/bin/ld: warning: libnghttp2.so.14, needed by /usr/lib/arm-linux-gnueabihf/libcurl.so, not found (try using -rpath or -rpath-link)
I've already confirmed that the library its looking for exists:
root#80a13d68ad84:/# find /usr/lib -name "libnghttp2.so.14"
/usr/lib/x86_64-linux-gnu/libnghttp2.so.14
/usr/lib/arm-linux-gnueabihf/libnghttp2.so.14
I've read about rust build scripts here, where I can add paths to the library search path, but that has not resolved the issue, this is what my build.rs looks like:
fn main() {
println!("cargo:rustc-link-search=native=/usr/lib/arm-linux-gnueabihf");
}
The error suggests using -rpath or -rpath-link, so how do I configure the rpath in rust? Or is there a different way to resolve this issue?

You might be able to specify linker arguments by using an environment variable like
RUSTFLAGS="-C link-arg=--rpath=..."
Cargo allows placing the same kind of flag in the configuration file (see here) but I'm not sure if cross has the same support for something like that.

Related

Statically linking screen_capture_lite in Rust

I am attempting to write a Rust wrapper for screen_capture_lite, my code for which is located here. I've gotten the cmake crate compiling screen_capture_lite from source, but I get 'unresolved external symbol' linker errors when trying to run my window_count example script. These errors appear to be for Windows API symbols or C++ standard library symbols. Here is a pastebin with the errors.
From what I've figured out about linking, I assume this is because I'm trying to statically link to screen_capture_lite, which gets rid of screen_capture_lite's dynamic dependencies. This seems true, because when I link to all of the system libraries manually, my window_count example script works and correctly reports the number of open windows. This is what my build script looks like when I do that:
extern crate cmake;
use cmake::Config;
fn main() {
let dest = Config::new(".").profile("Release").build();
println!("cargo:rustc-link-search={}", dest.join("lib").display());
println!("cargo:rustc-link-lib=static=screen_capture_lite");
println!("cargo:rustc-link-lib=kernel32");
println!("cargo:rustc-link-lib=user32");
println!("cargo:rustc-link-lib=gdi32");
println!("cargo:rustc-link-lib=winspool");
println!("cargo:rustc-link-lib=shell32");
println!("cargo:rustc-link-lib=ole32");
println!("cargo:rustc-link-lib=oleaut32");
println!("cargo:rustc-link-lib=uuid");
println!("cargo:rustc-link-lib=comdlg32");
println!("cargo:rustc-link-lib=advapi32");
println!("cargo:rustc-link-lib=dwmapi");
}
Manually linking the system libraries doesn't seem like a good solution to me, so is there a way to export a list of paths from cmake to a file for my build script to consume?

How to export lib incorporated with C dynllib?

Say, I have a lib package with a C shared library in a sub-directory. This rust library package compiles without errors, by means of tailored build.rs which sub-calls make in the sub-directory and then dynamicly links against the *.so product of make.
The problem arises when I try to link a binary package against this rust lib has the C shared lib within itself.
error: linking with `cc` failed: exit status: 1
= note: /usr/bin/ld: cannot find -lcuda_wrapper
collect2: error: ld returned 1 exit status
The dependency graph can be shown as the following one:
C-shared-lib < rust-lib < rust-bin
And cargo build fails to build rust-bin for not finding that C-shared-lib.
providing LD_LIBRARY_PATH for cargo build neither by command line nor build.rs within rust-bin facilitates the problem and lifts the error.
How to propagate the directory wherein resides libcuda_wrapper.so?
You can link your C library dynamically or statically.
In order to do that you can use add one of the following lines to your build.rs (note that you linkely won't need a lib prefix):
println!("cargo:rustc-link-lib=static=cuda_wrapper");
println!("cargo:rustc-link-lib=dylib=cuda_wrapper");
In order to define a search path, add this line to build.rs
println!("cargo:rustc-link-search=native={}", path);
You would likely want to add it to your rust-lib build.rs. Altenatively, you can create a build.rs for every executable which depends on rust-bin and add the same set of lines here.
It is a good idea to ensure that your C library build output is located under OUT_DIR env variable (you can get it using env::var("OUT_DIR").unwrap() in build.rs). The way cargo clean will work as expected as well.
More details here:
https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib
https://doc.rust-lang.org/rustc/command-line-arguments.html#option-l-link-lib
https://doc.rust-lang.org/cargo/reference/build-script-examples.html#building-a-native-library
https://doc.rust-lang.org/cargo/reference/environment-variables.html

Why Rust build.rs is unable to find C headers?

Basically I am trying to cargo build a crate which has a build.rs file.
This crate is located inside a bigger project and it's supposed to be a lib crate.
And inside this build.rs file, I am trying to compile a .C file which includes a couple of headers.
Fun fact: I got this build.rs file and crate structure from another little demo crate, and in that demo crate I had no problem to compile this exact C file with these headers.
FULL ERROR HERE:
Here is a link to github: https://github.com/mihaidogaru2537/FirecrackerPlayground/tree/dpdk_component/firecracker/src/dpdk_component
There is the crate I am talking about and you can also see the bigger project in which it resides. In the README file you can see the full error.
Either I do cargo build from the root of the big project or from the root of this problematic crate, the error is the same.
"cargo:warning=/usr/include/asm-generic/errno.h:5:10: fatal error: asm-generic/errno-base.h: No such file or directory
cargo:warning= 5 | #include <asm-generic/errno-base.h>"
The missing file might change depending on the .flag("-I/path/..") calls I am doing inside the build.rs
As you can see, right now it's unable to find errno-base.h, but I am including the path to asm-generic.
Here is the code of the build.rs file from the crate where the compilation of this C file works, as you can see, I did not have to add any include flags before calling compile.
fn main() {
// Tell cargo to tell rustc to link the system bzip2
// shared library.
// println!("cargo:rustc-link-lib=rte_ring");
// println!("cargo:rustc-link-lib=rte_mempool");
// Tell cargo to invalidate the built crate whenever the wrapper changes
// println!("cargo:rerun-if-changed=wrapper.h");
let _src = ["src/static-functions.c"];
println!("cargo:rerun-if-changed=build.rs");
let mut builder = cc::Build::new();
let build = builder
.file("src/static-functions.c")
.flag("-Wno-unused-parameter");
build.compile("foo");
}
Additional info:
The problematic crate is pretty small, see the link above. There is the build.rs file, C file and header file is inside the include directory.
One thing that I suspect, is that the target of the bigger project:
TARGET = Some("x86_64-unknown-linux-musl")
might affect the way the C file is compiled.
In the project where the compilation is working, I am not using that linux-musl stuff.
I am a total noob when it comes to Rust, but I do have a decent understanding of how C/C++ works.
I am running the project on Ubuntu 20.04
Those missing headers are a result of importing DPDK headers, I have DPDK libraries installed on the machine in question.
Let me know if you have any questions, sorry for the long read and thank you.
I somehow managed to fix it by adjusting the cargo build command to use x86_64-unknown-linux-gnu as target instead of x86_64-unknown-linux-musl (By default cargo build was doing the musl target somehow)
So if you are trying to build a rust app which is using DPDK libraries and you are getting missing headers, make sure to try the following:
cargo build --target=x86_64-unknown-linux-gnu
Well if you have to use musl and there is no alternative, I don't have an answer. But to me this was enough.
If someone has an explanation why musl is not working in this scenario, please let us know.
Reddit Rust community helped me as well, check this link out if you are interested:
https://www.reddit.com/r/rust/comments/mo3i08/unable_to_compile_c_file_inside_buildrs_headers/
So Why build.rs was unable to find .C headers?
ANSWER
Because I was using x86_64-unknown-linux-musl as target.

Linking failed in rust bindgen

So I have a header library (a directory of .h files) that I need to make use of in my rust code. To get this working I have been looking into various crates to help me achieve the this and it seems like bindgen is the most promising option.
i followed the documentation for bindgen and have created a wrapper.h and a build.rs file.
in the build.rs file i have whitelisted all the needed functions, and the link parameter mentioned in the documentation specified as println! ("cargo:rustc-link-lib=C/complete path to my header files");.
when doing a cargo build this however fails with this error:
error: linking with link.exe failed: exit code: 1181
which I have not been able to find a solution for.
Any help or suggestions are greatly appreciated.
rustc-link-lib directive is only for .a/.so/.dll and cannot work with either .h or .rs files. It's completely inappropriate here.
If you make your build.rs script write to a directory specified by std::env::var("OUT_DIR") path, then your main library code can include it dynamically with:
include!(concat!(env!("OUT_DIR"), "/temp_file_built_by_build_dot_rs.rs"));
Alternatively, don't bother with build.rs at all. Use command-line bindgen and include generated .rs file as a module in your project (just as a regular source code file).

How to link a C library without calling one of its functions?

I'd like to link with -lm. Right now, I'm doing it this way:
let _link_lm = f64::sin(3.0);
I tried putting this in .cargo/config:
[build]
rustflags = ["-C", "link-args=-lm"]
This doesn't dynamically link the library.
Also, using cargo:rustc-link-lib=m in a build script is the same as calling cargo rustc -- -lm which does not work either.
I check that the library is not linked with ldd.
It matters to link the library because this is for a JIT compiler which can call these functions by fetching them using dlsym.
How I can link to this library without calling one of its functions?
It turns out that rustc call the linker with -Wl,--as-needed, so the solution for me was to disable this option:
[build]
rustflags = ["-C", "link-arg=-Wl,--no-as-needed", "-C", "link-arg=-lm"]

Resources