loading...

Cargo refused to build my project - A Rust debugging story!

rrampage profile image Raunak Ramakrishnan ・2 min read

Background

Today, I resumed a rust project of mine after a long time. In order to check my last working code, I ran cargo run. But it refused to run with error message:

...."-Wl,-Bdynamic" "-l" "dl" "-l" "rt" "-l" "pthread" "-l" "pthread" "-l" "gcc_s" "-l" "c" "-l" "m" "-l" "rt" "-l" "pthread" "-l" "util" "-l" "util"
= note: /usr/bin/ld: cannot find Scrt1.o: No such file or directory
          collect2: error: ld returned 1 exit status

Linker Issues!

ld is used to link libraries. So, the error was not in the code but in the linking phase. I googled the error and the solution was to have build-essential package installed. But the package was already installed on my machine (it is one of the first packages I install on any development machine).

Some more googling revealed that cargo uses the system cc linker to link to the C runtime. Running which cc gave me $HOME/anaconda3/bin/cc. This cc is part of my Anaconda root environment. (Anaconda is a package manager for scientific computing packages. It is a convenient way for installing multiple versions of packages in different environments).

On Linux, the linker knows where to find the required libraries using the shared library cache. I ran ldconfig -v to refresh it and then try again. Same error!

It is possible to explicitly list directories to include using LD_LIBRARY_PATH environment variable. I tried setting the LD_LIBRARY_PATH to point to the required directory and then run cargo as LD_LIBRARY_PATH=DIR cargo build -v. But it gave the same error.

I thought that cargo must be setting the linker value somewhere, so instead let me try directly compiling with rustc. Even that gave the same error. With this, I eliminated the possibility of some environment variable only affecting cargo.

More googling!

Further searching for the error with "rust" added showed me results of people having trouble cross-compiling. From this, I learned that cargo has different targets i.e different instruction sets (e,g x86-64, arm, x86, mips), different OSs (e.g linux, windows, freebsd) and different C runtimes (e.g glibc, musl, msvc). The Rust documentation on cargo mentioned that this is called a target triple. The Cargo book mentioned that you can direct cargo to explicitly use a particular linker using RUSTFLAGS environment variable.

Since I am only building for my machine, I had to find out the exact value of the target. Rust gives an exhaustive list of all supported targets by running rustc --print target-list. My target was x86_64-unknown-linux-gnu.

It is possible to pass a linker to cargo explicitly as RUSTFLAGS="-C linker=x86_64-unknown-linux-gnu" cargo build -v. It worked!

All-time fix!

I do not want to do this every time I run cargo. The Cargo book says that cargo uses a global config file in $HOME/.cargo/config.

I added the following to the file:

# Specify which linker to use for this target
[target.x86_64-unknown-linux-gnu]
linker = "x86_64-linux-gnu-gcc"

Now, cargo build works without issues.

Discussion

pic
Editor guide
Collapse
rruenroeng profile image
Ryan Ruenroeng

How did you determine which target you needed to feed to cargo?

Collapse
62mkv profile image
Kirill Marchuk

Why was it broken, in the first place?

Collapse
rrampage profile image
Raunak Ramakrishnan Author

Cargo itself was working fine. The issue was a combination of a non-standard gcc and cargo not being able to find the correct linker path by default.