dynamic linking support
Created by: m4b
Currently redox requires statically linked ELF binaries.
So first question, are there any plans to support dynamic linking?
If so, would you consider using/adopting dryad ? In short, I would love to see a home/use for it, considering all the work that went into it, and would be willing to help port it over to Redox/mentor anyone who wants to help on that front, once a direction is decided.
The long is I'll give some motivations for adopting it as the dynamic linker if there are such plans, and some difficulties/considerations to be made about creating a dynamic linking platform.
Dryad started as an experiment to write a parallel dynamic linker (I still thinks this is possible) and also just because I was interested in their design. I originally targeted only x86-64, but since have added support for 32-bit arm (which I've successfully tested on my arm phone), as well as 64-bit arm (unfortunately I cannot currently link it properly (missing symbols), but I suspect it will work with some tweaking). And by successful, I mean it segfaults ;)
Along the way, I encountered serious technical issues deep in GNU's libc/ld-linux-so-x86_64 which made development difficult (and uninteresting) - as I would essentially be required to copy the unspecified ABI of glibc's internal structs, in particular, rtld_global_ro
- let alone any other libc. The common theme here appears that most libc's cache results from locales, auxv
accesses, and sometimes environment variable pointers in a secret, non-standardized struct that the dynamic linker has access to. But this pervasiveness goes even further - in recent versions of my glibc, (2.25), the GNU_IFUNC
resolver for __exp_finite
accesses the dynamic linker struct (which has not been initialized because the dynamic linker isn't the glibc dynamic linker), and hence segfaults. There's really no way to know that any arbitrary function in glibc (which can access the rtld_*
structs because it's built in the same compilation unit as the dynamic linker) is going to rely on this private, practically uninitializable struct.
Consequently, I largely stopped working on dryad
, except for keeping it up to date and still compiling. If you do anything, do not make this mistake that glibc made. At the very least, specify the structure and expected runtime values required to initialize and load any libraries, similar to the PLT/GOT specification. They fused their libc implementation with their dynamic linking implementation, for seemingly for no particularly good reason. (musl libc's intentionally does this - musl libc binary is the dynamic linker too). Here is a more in depth, fairly sarcastic and tongue-in-cheek investigation of this issue: https://github.com/m4b/dryad/issues/5
Nevertheless, in terms of features, I think it is quite robust:
- Correctly relocates and can run itself (e.g.,
./dryad.so.1
) - multiple architectures (someone needs to write
AARCH64
assembly stubs for runtime resolution, as well asm stubs fori386
) - gdb support (this was particularly vexing as there was no documentation on how to do this)
- runtime (aka lazy) symbol resolution
- partial (read currently non-working) TLS initializer/implementation - largely adopted from glibc/musl. I just use the internal musl implementation for now
- Partial support for dynamic linking environment variables, e.g.,
LD_DEBUG
,LD_BIND_NOW
, etc. - All major relocation directives are supported, including
GNU_IFUNC
(more exotic x86 relocations are ignored, but most of these aren't emitted by any major toolchain anymore) - Fairly painless rustup + makefile compilation (this is substantially more work than you would think, see the
Makefile
if you don't believe me) - Uses gnu hashes/bloom filter for symbol resolution which makes it extremely fast (probably need to also support regular hashes, but I wasn't that interested since every linux linker emits GNU_HASH dynamic entries)
If this sounds good I would be interested in helping get dryad ported to work on a redox system; this will be some work, as there are a few linux assumptions made, but I don't think that in particular will be the difficult part.
The first issue will be hammering down the TLS implementation, as this is potentially the most OS-based assumption (besides auxv
accesses, or how the so-called "kernel block" is setup). Ideally, I would like the implementation in tls.rs
to have essentially a single entry-point across multiple systems, where the implementation is switched at compile time depending on the OS target. This particular area was/still is a WIP, and currently I just default to using the statically linked musl initializer.
I haven't looked at redox, so I don't know what requirements are here, how it sets up the the TLV, TP, etc. But since it seems able to run x86 ELF binaries (which I assume must be accessing the re-appropriated fs
or gs
segmented registers for TLS), it is perhaps the same?
The second issue is essentially what kind of runtime system library you want (e.g., a libc), and in the case of reusing glibc, what to do about the rtld_*
problem. If you do end up just using glibc, then you are likely committed to using their dynamic linker (unless someone feels like cloning the ABI of libc and having dryad pretend it is ld-linux
)
This last issue is the hard part I believe, and it's really more of an infrastructure issue/planning/what kind of system do you want to build problem, and all the pros and cons associated with any decision thereof.
Anyway, I've typed enough; let me know what you think. My feelings won't be hurt if you do want to implement dynamic linking, but don't want to use dryad as your dynamic linker. I'm just looking for a home for all the code that was written at this point, but if not, that's ok too :)