So recently I announced dryad, which is an experimental dynamic linker written entirely in Rust (and some asm), currently for GNU/Linux x86-64.
I’ve been procrastinating getting cargo setup and working, and I’ve noticed that when I mention I can’t get cargo working with my project, there is some surprise that it isn’t easy/possible. So, I’ve come here to ask help from a rust cargo foo master, because I can’t seem to get it working.
This has some important (negative) consequences:
- I cannot write cargo tests
- I cannot use any extern crates
- I cannot write cargo benchmarks
- I cannot tell people to just run
cargo buildto build the project, like everyone else gets to do
Diving in, here are important preliminaries describing my requirements:
- I must build asm stubs. In particular, they are in
a. I would prefer not to have these integrated into the rust source code (i.e., no inline asm).
b. Mutabah attempted to fix some of the relocation issues using indirect loads, otherwise
cargo builderrors out with PIC errors. Unfortunately, I had to revert these changes as while it allowed cargo to build the project, the binary doesn’t run, and the new relocations cause
./dryad.so.1to segfault early in the process, so they did not help
- The rust code is currently compiled as an object file with
- Using the asm and rust objects, I hand link with some special flags to produce the final executable. This is the snag in the pipeline, as a dynamic linker requires all of the following, with link flags following the description:
a. the binary’s
sonamehas to be set to
b. the binary must be compiled as a PIE. What this means is that the binary is technically a shared object (it’s
e_typein the ELF header is
ET_DYN), but it also has a valid
e_entry(set to the
_startsymbol defined in the
c. the binary must be linked without the C stdlib (we will receive all of it by statically linking with musl libc):
d. as a consequence of
c., we must set the entry point, in this case
_startas defined in
e. we should “bind references to global symbols to the definition within the shared library”, i.e.:
f. we should set the path of the program interpreter (e.g., the dynamic linker) of dryad (a dynamic linker) to itself. (technically I don’t need to do this, but I think it’s hilarious, and afaik no other dynamic linker does this. It may come as a surprise, but it does work, which you can verify by running
./dryad.so.1after you build it with
g. we should gc sections so our binary isn’t enormous:
h. finally, we should properly link against all the musl rust stdlibs and
i. the resulting binary should not have any
DT_NEEDEDelements in its
_DYNAMICarray, i.e, it does not have any dynamic library dependencies.
Essentially everything above is encapsulated in my
make.sh script, if you want to see how it all works to produce an almost working dynamic linker (having TLS issues, but that’s another post ;)).
Currently, if you clone the repo and do
cargo build, it ~should~ won’t build (assuming you have nightly cargo). With Mutabah’s fix (currently commented in the
asm.s), cargo will build without complaining about PIC relocations, but the binary will segfault early in the process if built using
./make.sh (I don’t know why, another mystery), and the resulting cargo executable is not an executable, but a shared library without an entry point, and does not satisfy every one of the above requirements, e.g., it has rust dynamic lib dependencies, as well as libc, doesn’t have the custom soname, etc.
As such, I reverted the asm changes, and cannot build using
cargo build at all…
Due to the nature of this project, I understand these are some unusual requests, but what else is a systems language for if not writing something like a dynamic linker
As a result, if you are someone interested in highly esoteric aspects of the ELF linking process, and would like to help me getting cargo to build an executable with the above requirements, I would be greatly and deeply indebted .
Unfortunately, this would almost be a moot point if we could pass linker flags via the rust build script using the
-C switch, e.g.:
println!("cargo:rustc-flags=-C hi there")
But cargo currently refuses, and errors out with:
error: Only `-l` and `-L` flags are allowed in build script of `dryad v0.1.1 (file:///home/m4b/projects/dryad)`: `-C hi there`
So again, if anyone has any ideas at all, I’m all
Live long and prosper ,