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.

Posted on May 9 '18 by:

rrampage profile

Raunak Ramakrishnan

@rrampage

Passionate about databases, distributed systems and functional programming.

Discussion

markdown guide
 

Why was it broken, in the first place?

 

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.