What is the difference between binaries in ~/.rustup vs ~/.cargo? - rust

I just installed Rust with rustup on MacOS and noticed that there are two rustc and two cargo binaries:
~/.cargo/bin/rustc (cargo)
~/.rustup/toolchains/stable-x86_64-apple-darwin/bin/rustc (cargo)
Their versions are exactly the same, but diff shows there exists some difference. So why are there two different rustc (cargo) binaries and which one should I use?

The reason there are two files named rustc is because rustup is a toolchain multiplexer. It lets you install many versions of Rust and easily switch between them.
The binary installed at ~/.cargo/bin/rustc proxies to the current toolchain that you have selected. Each installed compiler is kept under the toolchains directory.
Although the compiler in the toolchains directory appears to be a smaller file, that's only because it's dynamically linked instead of statically linked.
More information can be found on rustup's README.

Related

Download Only Pre-Compiled Binaries of Rust Crates

I use multiple rust packages in my desktop. To install those packages i use
cargo install fd-find sd ripgrep bat exa git-delta du-dust choose alacritty nu tealdeer broot
To update those packages , i use cargo-update sub-command.
cargo install cargo-update
cargo install-update -a
Is there any command or sub-command or technique i can use to download only pre-compiled binaries (instead of downloading the source code and compiling in my machine) to install or update rust crates.
Since crates can be compiled with or without certain features which result in different code, there isn't any mechanism to up- or download pre-compiled crates.
On top of that, the list of supported targets is very long which would make it very likely that the platform you're on doesn't have pre-compiled binaries.
Finally, there'd need to be additional mechanisms to sign the code and verify that the pre-compiled code matches the source code.
So all in all there are several obstacles that render implementing this impractical.

Build and bind against older libc version

I have dependencies in my code that requires libc. When building (cargo build --release) on Ubuntu 20.04 (glibc 2.31) the resulting executable doesn't run on CentOS 7 (glibc 2.17). It throws an error saying it requires GLIBC 2.18.
When build the same code on CentOS 7 the resulting executable runs on CentOS 7 and Ubuntu 20.04.
Is there a way to control which GLIBC version is required to build this version on Ubuntu 20.04 too?
If your project does not depend on any native libraries, then probably the easiest way would be to use the x86_64-unknown-linux-musl target.
This target statically links against MUSL Libc rather than dynamically linking against the system's libc. As a result it produces completely static binaries which should run on a wide range of systems.
To install this target:
rustup target add x86_64-unknown-linux-musl
To build your project using this target:
cargo build --target x86_64-unknown-linux-musl
See the edition guide for more details.
If you are using any non-rust libraries it becomes more difficult, because they may be dynamically linked and may in turn depend on the system libc. In that case you would either need to statically link the external libraries (assuming that is even possible, and that the libraries you are using will work with MUSL libc), or make different builds for each platform you want to target.
If you end up having to make different builds for each platform, a docker container would be the easiest way to achieve that.
Try cross.
Install it globally:
cargo install cross
Then build your project with it:
cross build --target x86_64-unknown-linux-gnu --release
cross take the same arguments as cargo but you have to specify a target explicitly. Also, the build directory is always target/{TARGET}/(debug|release), not target/(debug|release)
cross uses docker images prebuilt for different target architectures but nothing stops you from "cross-compiling" against the host architecture. The glibc version in these docker images should be conservative enough. If it isn't, you can always configure cross to use a custom image.
In general, you need to build binaries for a given OS on that OS, or at the very least build on the oldest OS you intend to support.
glibc uses symbol versioning to preserve the behavior of older programs while adding support for new functionality. For example, a newer version of pthread_mutex_lock may support lock elision, while the old one would not. You're seeing this error because when you link against libc, you link against the default version of the symbol if a version isn't explicitly specified, and in at least one case, the version you linked against is from glibc 2.18. Changing this would require recompiling libstd (and the libc crate, if you're using it) with custom changes to pick the old versioned symbols, which is a lot of work for little gain.
If your only dependency is glibc, then it might be sufficient to just compile on CentOS 7. However, if you depend on other libraries, like OpenSSL, then those just aren't compatible across OS versions because their SONAMEs differ, and there's no way around that. So that's why generally you want to build different binaries per OS.

glibc version for aarch64

I'm cross-compiling an application for aarch64 on my x86 Ubuntu Bionic system, and I have problems with glibc version mismatch. My cross-compile toolchain was using v2.27, while the system that is to run the application has v2.24. I thought that it might be due to my toolchain having a too high version, so I decided to downgrade.
After removing all previous cross-compilation installs, I installed gcc-4.8-aarch64-linux-gnu (as I had successfully cross-compiled the application with this version on a different host system), thinking that it would install an older aarch64 version of glibc to /usr/aarch64-linux-gnu/lib/. However, again, v2.27 was installed (I verified that this directory didn't exist before installing the new cross-compilation toolchain).
So my question is twofold:
What determines which aarch64 version of glibc is installed on my system when installing gcc-4.8-aarch64-linux-gnu? Is it directly tied to my own system's x86 version of glibc?
Is there a correct way to install the aarch64 version of glibc v2.24 (or lower) on my system?
I concur with your hypothesis. After battling similar symptoms for 40 hours straight, I've discovered this confirmation:
https://packages.ubuntu.com/impish/gcc-10-aarch64-linux-gnu
https://packages.debian.org/bullseye/gcc-aarch64-linux-gnu
Note that Ubuntu 21.10 (Impish) and Debian 11 (Bullseye) have packages for a gcc 10 cross compiler. Be wary of the very confusing fact the Ubuntu's default package is actually gcc 11, but Debian 11's default is gcc 10. The similar version numbers of Debian and gcc are a coincidence. Also ignore for now the fact that Ubuntu's package is gcc 10.3.0 and Debian's is gcc 10.2.1.
Focus instead on the recommendations and dependencies of each package. Ultimately the Ubuntu package calls up libc >= 2.34, while the Debian package calls up libc >= 2.28.
Sure enough, when I cross-compile from Impish on x86 for Bullseye on aarch64 (despite having a complete SYSROOT for the target), I get this at runtime:
/lib/aarch64-linux-gnu/libc.so.6: version 'GLIBC_2.34' not found
But your question remains, is there any tie between the host libc and that used by the cross-compiler? The answer is a definite maybe.
See this excellent answer and links for an overview of a cross-compiler. The take-away:
You don't just cross-compile glibc, you need to cross-compile an entire toolchain. Toolchain components are ALWAYS: ld + gcc + libc + gdb.
So the C library is an integral part of the cross-compiler.
What shenanigans then, are going on when you install gcc-aarch64-linux-gnu? It's just a compiler - only one of the four parts of a toolchain.
Well apparently there's some flexibility. Technically, a cross-compiler can be naked. That's typically only useful when you're compiling an operating system, rather than an executable that runs on an operating system. So you can construct special toolchains for special purposes.
But for the standard purpose (cross compiling for Linux on another architecture) you want a typical toolchain. Which is where the package's dependencies and recommendations come in. A gcc is always in want of an ld which is always in want of a libc, and the ménage à trois is intimate. In fact, gcc is built with libc using ld in a complex do-si-do. See this example from a great guide by Preshing on Programming:
It's possible to force separation and link to other libraries, but it's not easy.
For example, the linker you use has a set of default search directories that are baked in. From the fine manual:
The default set of paths searched (without being specified with -L) depends on which emulation mode ld is using, and in some cases also on how it was configured.
And it gets more intwined. By default, gcc will call on a dynamic linker whose location is hard-coded. For a cross-compiler, it might be something like /lib/ld-linux-aarch64.so.1. Not only that, the executable may also end up with the hardcoded path, as its program interpreter.
Again, if you're careful you can tear apart the toolchain and override things. But not only is it tricky to enforce, particularly if you have a complex build, the multitude of combinations of options and paths means there are also often bugs. So your host environment can easily leak into your cross-compiling toolchain.
So in summary, cross-compiling requires a toolchain. While pulling a cross-compiler from a package manager seems like an easy and legitimate thing to do, it comes with a lot of implicit baggage. You can either carefully follow the package dependencies to check what version you're getting, or use one of the many dedicated toolchain environments, such as crosstool-NG.

Buildroot toolchain with openssl

I am using Buildroot (2017.02.5) to build a custom cross compilation toolchain. I have two buildroot configurations; one to build the RFS and one purely to build a toolchain. I have things configured this way because I don't want the toolchain to be rebuilt unless I intentionally rebuild it- the configuration which builds the RFS references this toolchain as an external toolchain.
Generally, the built toolchain works fine, but I have some existing applications (Linux userspace) which #include's <openssl/md5.h>. When I try to compile this, I get a "<openssl/md5.h>: No such file or directory" error, which is expected because the sysroot dir of the generated toolchain does not contain an openssl directory.
How can I make buildroot include openssl in the toolchain? All searches I have done seem to point to cross compiling openssl for my embedded target, which is not an issue. The issue is that I need to include it in the toolchain.
I have Target packages --> Libraries --> Crypto --> openssl set to y, but I don't think this makes any difference in this scenario since I believe it relates only to the RFS (and the defconfig in question does not build an RFS, only a toolchain).
I could compile OpenSSL outside of the buildroot tree and install it to the sysroot dir, but this doesn't seem correct as it would pollute sysroot.
I'm sure I'm missing something simple here- any help would be appreciated.
After some further reading of the buildroot documentation (which is very good), I figured that packages selected under Target packages do in fact get pushed into the sysroot of the toolchain (or are supposed to at least) which would make sense. The reason this didn't appear to be working was because I was doing a make toolchain as opposed to make all (or just a simple make). The packages didn't get built with the former, so they weren't in the sysroot of the toolchain.

How to solve librustdoc.so missing when using rustbook tool?

I've used
cargo install --git https://github.com/steveklabnik/rustbook.git
to install rustbook successfully, but when I run rustbook I get an error:
rustbook: error while loading shared libraries: librustdoc-c0dcaea09a16c7ec.so: cannot open shared object file: No such file or directory
But I can find out this .so file.
./.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustdoc-c0dcaea09a16c7ec.so
./.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustdoc-c0dcaea09a16c7ec.so
How can I let rustbook know the path to librustdoc-c0dcaea09a16c7ec.so?
rustc 1.16.0-nightly (4ecc85beb 2016-12-28)
rustdoc 1.16.0-nightly (4ecc85beb 2016-12-28)
rustup show:
Default host: x86_64-unknown-linux-gnu
nightly-x86_64-unknown-linux-gnu (default)
rustc 1.16.0-nightly (4ecc85beb 2016-12-28)
This appears to be a known issue with rustup (#350, #765), specifically around cargo-installed binaries that were compiled against the nightly toolchain. If your application requires nightly to even compile, there's not much you can do other than rustup run nightly myprogram. This will start the program with the appropriate environment.
If the application doesn't require nightly Rust, then install it using the stable toolchain.
List the directory containing your shared library into /etc/ld.so.conf, or make a symlink from a path already listed there, or from /usr/lib (/usr/local/lib would be preferable, but maybe you have to add it to the list in ld.so.conf, so this would be again case 1).

Resources