• Stars
    star
    162
  • Rank 232,284 (Top 5 %)
  • Language
    Rust
  • License
    BSD 3-Clause "New...
  • Created over 8 years ago
  • Updated almost 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An almost-parallel, semi-functioning, dynamic linker experiment, written in Rust

Welcome

Build Status Floobits Status

dryad

dryad is the first and only parallel, 64-bit ELF dynamic linker for GNU/Linux, written from scratch in Rust, and is:

  1. not parallel
  2. not ready for production
  3. a prototype
  4. doesn't really work
  5. in a massive state of flux
  6. parallel might be a) impossible, b) not performant, but it will be interesting to try

but all most of these things will disappear in time!

Work has stalled on this for a number of reasons, primarily as outlined here, but I tinker with it from now and then.

I have some ideas to fix things, but so many things to work on!

If you want to contribute, PRs or suggestions, comments, issues, always welcome :) If you want to hack on some other fun binary stuff, goblin or cargo-sym could always use an extra hand or two.

Build

You need to install rustup tool, and then switch to nightly and add the musl target:

rustup default nightly
rustup target add x86_64-unknown-linux-musl

All you really need now is rustup, an internet connection, and a linker for the target you're linking:

  • ld (or ld.gold)
  • curl
  • an internet connection
  • an x86-64 GNU/Linux box

Unfortunately, I currently do not support cross compiling at the moment (which is an unusual use case anyway), so you will need an x86-64 GNU/Linux machine, otherwise it will fail.

Once that's settled you can then proceed as normal:

  1. ./gen_tests.sh - builds the test binaries (do this once) (will add this as a make target soon)
  2. make - compiles dryad.so.1 and copies it to /tmp
  3. make run - runs ./dryad.so.1, this should run correctly without segfaulting, please file a bug if it does not.
  4. test/test - runs the test binary test, whose PT_INTERPRETER is /tmp/dryad.so.1

Compilation and Linking Requirements

The Makefile does three things:

  1. compiles dryad into a static library, essentially: cargo build -target=x86_64-unknown-linux-musl --lib
  2. links the libasm entry function and runtime resolver functions with the dryad static library, and then the rust standard libs, and pthreads and libc and etc., and provides the very important linker flags such as -pie, -Bsymbolic, -I/tmp/dryad.so.1, -soname dryad.so.1, etc.
  3. copies the resulting binary, dryad.so.1, into /tmp/dryad.so.1 because that's what PT_INTERPRETER is set to in the test binaries. In the future we'll obviously make this /usr/lib/dryad.so.1, or wherever the appropriate place for the dynamic linker is (GNU's is called ld-linux-x86-64.so.2 btw).

Really, stage 1 and 2 from above is the problem in the cargo pipeline, which is why I still need to manually link. Additionally, rustc doesn't like to compile a musl binary as a shared object.

I believe some of these issues will go away if I transfer the start assembly into inline assembly in Rust source code (thereby potentially eliminating step 1), but the musl issue could be a problem. (we use inline asm in separate rust crate now!)

Running

The last step, running test/test (or any of the other test binaries in test), will output a ton of information and then segfault your machine, or perhaps not run at all, or really do any number of things --- I really can't say, since I've only tested on a single machine so far.

NOTE: if you're on Ubuntu or another linux distro which doesn't place libc in /usr/lib, you'll need to pass LD_LIBRARY_PATH=/path/to/libc to your test/test, i.e.: LD_LIBRARY_PATH=/path/to/libc test/test. Furthermore, if libc doesn't have symbolic links for the soname pointing to the actual binary, or the actual binary is installed as the soname, then it also won't work. We need ld.so.cache reader and parser for this - feel free to work on it!

However, dryad is almost capable of interpreting a (simple) binary (like test/test) which uses libc.so.6.

Specifically, this means is that dryad at a high level does the following:

  1. relocates itself
  2. loads and mmap's all binaries in the flattened dependency list
  3. relocates every loaded binary (technically, relocates a subset of the most common relocation symbols)
  4. sets up each binary's GOT with its runtime symbol resolution function (_dryad_resolve_symbol), and its "rendezvous" data structure
  5. resolves GNU ifuncs, and if LD_BIND_NOW is set, prebinds all function symbols.
  6. passes control to the executable
  7. (optionally, if LD_BIND_NOW is not set) lazily binds function calls
  8. segfaults

There are several major, and many minor tasks that need to be finished to be even remotely "complete". The first and most major one is properly setting up TLS. Currently, it hacks it about by just calling the musl symbol __init_tls so we don't segfault on fs:0 accesses and their ilk.

But it really needs to be properly setup, as it's a delicate procedure.

This is easily the least documented part of the entire dynamic linking process I have come across, so work is slow going. Also there are some questions about how this will work exactly, which I'll detail at some other time, or in a blog post.

Lastly, dryad should be capable of interpreting itself, which you can verify by invoking ./dryad.so.1 (yes, dryad is it's own program interpreter).

Project Goals

1. Documenting a Dynamic Linker

The primary goal of this project is to completely document:

  1. the dynamic linking process on an GNU/Linux ELF x86-64 system
  2. an implementation of such a process

The current state of documentation and information on this subject is an embarassment, and I'm continually appalled at the lack of materials, documentation, etc. I've jokingly told people I'm worried what will happen when all the old C programmers die - but I'm not really joking.

Code is not documentation. If it were, then this project would have been easy and finished some time ago.

As such, I hope to thoroughly document the implementation, the process, and maybe even my experiences.

I will be updating this section with more content shortly, please bear with me.

2. Implementing a Dynamic Linker

The current target implementation for dryad is an ELF x86-64 GNU/Linux system.

This is important to note:

  1. The ELF loader only supports the 64-bit variant
  2. The asm assumes an x86-64 instruction set
  3. The linker currently targets Linux, although this need not be set in stone.

I would like to have a working implementation for an ELF x86-64 GNU/Linux target before/if beginning work on other architectures or systems.

That being said, in particular I'm not very interested in porting dryad to work on 32-bit Linux systems, because:

  1. 32-bit systems are in obsolescence in my opinion
  2. Will significantly complicate the ELF target in the source code, as cfg flags would be needed depending on what target we want to switch at build time, etc.
  3. 32-bit ELF dynamic linking is much better documented, and I want to document a 64-bit dynamic linker

Contributing

Contributions wholeheartedly welcome! Let's build a production dynamic linker in Rust for use in x86-64 GNU/Linux systems (and beyond)! Or not, that's cool too.

If you don't know anything about dynamic linking on x86-64 GNU systems for ELF, that's totally OK, because as far as I can tell, no one really does anymore. Here are some random resources if you're curious:

  1. The ELF specification
  2. x86-64 System V Application Binary Interface
  3. ELF TLS spec
  4. google's bionic dynamic linker source code
  5. glibc dynamic linker source code
  6. musl dynlink.c code
  7. sco dynamic linking document
  8. iecc dynamic linking article
  9. ELF loading tutorial
  10. Info on the GOT[0] - GOT[2] values
  11. man ld-so for dynamic linking basics
  12. man dlopen for runtime dynamic linking basics
  13. man 3 getauxval for information on the auxiliary vector passed by the kernel to programs
  14. I'll also hopefully add a couple articles on some of my _mis_adventures on my essentially defunct blog

TODOs

Here are some major todos off the top of my head

  1. MAJOR: properly init dynamic linker's TLS: it's the final countdown.
  2. MAJOR: dlfcn.h implementation and shared object bindings for runtime dynamic loading support
  3. MINOR: /etc/ld.so.cache loader and parser
  4. better documentation
  5. fix any number of the todos littered across the code
  6. make unsafe code safer with rust best practices; rust experts definitely needed!
  7. add profiling configs
  8. add tests
  9. actually implement dynamic linking without segfaulting
  10. x all the things

Coda

Always remember:

Be excellent to each other

More Repositories

1

bingrep

like ~~grep~~ UBER, but for binaries
Rust
1,704
star
2

goblin

An impish, cross-platform binary parsing crate, written in Rust
Rust
1,167
star
3

faerie

Magical ELF and Mach-o object file writer backend
Rust
263
star
4

scroll

Scroll - making scrolling through buffers fun since 2016
Rust
151
star
5

rdr

Rdr is a cross-platform binary analysis and reverse engineering library, utilizing a unique symbol map for global analysis.
OCaml
83
star
6

raml

OCaml runtime and FFI bindings directly in Rust
Rust
78
star
7

elf2json

Converts an ELF binary to a JSON representation
OCaml
32
star
8

bin2json

Converts ELF, mach-o, or PE binaries to a JSON representation
OCaml
29
star
9

cargo-sym

Prints various binary symbols in your crate
Rust
13
star
10

silicon-disassembler

A high-performance, asynchronous disassembler which uses capstone.js as the backend and Web Workers for non-blocking requests
HTML
11
star
11

compilers-vm

A sandmark VM, in OCaml. Very fast.
OCaml
8
star
12

lazy_transducer

Lazy, parallel, indexable, generic data iterators
Rust
7
star
13

apli

Haskell APL Interpreter - ver 0.1
Haskell
6
star
14

stackup

Statically modify a program binary's runtime stack size on Mac OS X --- without recompiling.
Swift
6
star
15

algos

Algebraic arithmetization of boolean algebra and its implementation.
OCaml
6
star
16

silicon-hex-table

An efficient web-component hex table for all your online hexadecimal needs.
HTML
4
star
17

cloaked-robot

compilers final project --- Data Flow
Haskell
3
star
18

silicon-instructions

Display assembly instructions in a list-like format, using highlight.js
CSS
3
star
19

paper-trek

Boldly going where no paper element has gone before!
JavaScript
2
star
20

derp-archer

Project 2: Grammar Analysis and Parsing, CS454/554
Haskell
2
star
21

dotfiles

dotfiles and other configuration files
Haskell
2
star
22

metagoblin

WIP: Provides abstracted meta information about a binary
Rust
1
star
23

element-party

The splash page for http://element-party.xyz --- for those times when you just need to party.
HTML
1
star
24

scroll_derive

Macros 1.1 implementing #[derive(Pread, Pwrite)]
Rust
1
star
25

m4b.github.io

HTML
1
star
26

chat

Simple OCaml chat program
OCaml
1
star
27

ms-present

Master's Examination slides
TeX
1
star
28

silicon-symbols

Displays a list of symbols
HTML
1
star
29

drag-drop-behaviors

HTML
1
star
30

topo-tango

NM Game Jam Topological Tango
Lua
1
star
31

silicon-upload

A simple binary upload widget, to deliver all those bits to the server of your choice
HTML
1
star
32

get-polymer-imports

Simple python script to get all the polymer imports from a bower_components directory
Python
1
star
33

avant-lexeme

haskell compilers project
Haskell
1
star
34

foiled

Scala Implementation of FOIL
Scala
1
star
35

silicon-image

Displays a rasterized binary image with `silicon-image-behavior`, to update binary offsets via mouse movement, and etc.
HTML
1
star