Browse Source

Add ld_so executable

Jeremy Soller 6 years ago
parent
commit
084b69b361
11 changed files with 640 additions and 6 deletions
  1. 96 0
      Cargo.lock
  2. 6 1
      Cargo.toml
  3. 39 2
      Makefile
  4. 0 1
      src/header/sys_auxv/mod.rs
  5. 0 2
      src/header/unistd/mod.rs
  6. 8 0
      src/ld_so/Cargo.toml
  7. 355 0
      src/ld_so/linker.rs
  8. 2 0
      src/ld_so/mod.rs
  9. 64 0
      src/ld_so/src/lib.rs
  10. 68 0
      src/ld_so/start.rs
  11. 2 0
      src/lib.rs

+ 96 - 0
Cargo.lock

@@ -93,6 +93,16 @@ name = "fuchsia-cprng"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "goblin"
+version = "0.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "itoa"
 version = "0.4.3"
@@ -106,6 +116,10 @@ dependencies = [
  "spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "ld_so"
+version = "0.1.0"
+
 [[package]]
 name = "libc"
 version = "0.2.51"
@@ -132,6 +146,11 @@ name = "num-traits"
 version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "plain"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "posix-regex"
 version = "0.1.0"
@@ -144,11 +163,27 @@ dependencies = [
  "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "proc-macro2"
+version = "0.4.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "quote"
 version = "0.3.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "quote"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "ralloc"
 version = "1.0.0"
@@ -226,6 +261,7 @@ dependencies = [
  "cbitset 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)",
  "core_io 0.1.20181107",
+ "goblin 0.0.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "posix-regex 0.1.0",
  "ralloc 1.0.0",
@@ -252,6 +288,14 @@ dependencies = [
  "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "rustc_version"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "ryu"
 version = "0.2.7"
@@ -262,11 +306,43 @@ name = "sc"
 version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "scroll"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "scroll_derive"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "semver"
 version = "0.1.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "semver"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "serde"
 version = "1.0.90"
@@ -342,6 +418,16 @@ dependencies = [
  "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "syn"
+version = "0.15.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "synom"
 version = "0.11.3"
@@ -452,14 +538,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4"
 "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
 "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
+"checksum goblin 0.0.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6a4013e9182f2345c6b7829b9ef6e670bce0dfca12c6f974457ed2160c2c7fe9"
 "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
 "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
 "checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917"
 "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
 "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
 "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
+"checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
 "checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0"
+"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915"
 "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
+"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db"
 "checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
 "checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
 "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
@@ -469,9 +559,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
 "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
 "checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
+"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
 "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7"
 "checksum sc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4ebbb026ba4a707c25caec2db5ef59ad8b41f7ad77cad06257e06229c891f376"
+"checksum scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383"
+"checksum scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb"
 "checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
+"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
+"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
 "checksum serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "aa5f7c20820475babd2c077c3ab5f8c77a31c15e16ea38687b4c02d3e48680f4"
 "checksum serde_derive 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)" = "652bc323d694dc925829725ec6c890156d8e70ae5202919869cb00fe2eff3788"
 "checksum serde_derive_internals 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "32f1926285523b2db55df263d2aa4eb69ddcfa7a7eade6430323637866b513ab"
@@ -481,6 +576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum standalone-syn 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "115808f5187c07c23cb93eee49d542fae54c6e8285d3a24c6ff683fcde9243db"
 "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
 "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
+"checksum syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)" = "66c8865bf5a7cbb662d8b011950060b3c8743dca141b054bf7195b20d314d8e2"
 "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
 "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
 "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"

+ 6 - 1
Cargo.toml

@@ -8,7 +8,7 @@ name = "relibc"
 crate-type = ["staticlib"]
 
 [workspace]
-members = ["src/crt0", "src/crti", "src/crtn", "cbindgen"]
+members = ["src/crt0", "src/crti", "src/crtn", "src/ld_so", "cbindgen"]
 exclude = ["core_io", "ralloc"]
 
 [build-dependencies]
@@ -22,6 +22,11 @@ posix-regex = { path = "posix-regex", features = ["no_std"] }
 rand = { version = "0.5.5", default-features = false }
 va_list = { path = "va_list", features = ["no_std"] }
 
+[dependencies.goblin]
+version = "0.0.21"
+default-features = false
+features = ["elf32", "elf64", "endian_fd"]
+
 [dependencies.ralloc]
 path = "ralloc"
 default-features = false

+ 39 - 2
Makefile

@@ -79,7 +79,9 @@ sysroot: all
 test: sysroot
 	$(MAKE) -C tests run
 
-$(BUILD)/release/libc.a: $(BUILD)/release/librelibc.a $(BUILD)/pthreads-emb/libpthread.a $(BUILD)/openlibm/libopenlibm.a
+# Debug targets
+
+$(BUILD)/debug/libc.a: $(BUILD)/debug/librelibc.a $(BUILD)/pthreads-emb/libpthread.a $(BUILD)/openlibm/libopenlibm.a
 	echo "create $@" > "[email protected]"
 	for lib in $^; do\
 		echo "addlib $$lib" >> "[email protected]"; \
@@ -88,13 +90,18 @@ $(BUILD)/release/libc.a: $(BUILD)/release/librelibc.a $(BUILD)/pthreads-emb/libp
 	echo "end" >> "[email protected]"
 	ar -M < "[email protected]"
 
-$(BUILD)/release/libc.so: $(BUILD)/release/librelibc.patched.a $(BUILD)/pthreads-emb/libpthread.a $(BUILD)/openlibm/libopenlibm.a
+$(BUILD)/debug/libc.so: $(BUILD)/debug/librelibc.patched.a $(BUILD)/pthreads-emb/libpthread.a $(BUILD)/openlibm/libopenlibm.a
 	$(CC) -nostdlib -shared -Wl,--whole-archive $^ -Wl,--no-whole-archive -o $@
 
 $(BUILD)/debug/librelibc.a: $(SRC)
 	$(CARGO) rustc $(CARGOFLAGS) -- --emit link=$@ $(RUSTCFLAGS)
 	touch $@
 
+$(BUILD)/debug/librelibc.patched.a: $(BUILD)/debug/librelibc.a
+	# Patch out clzsi2.o from libgcc
+	cp $< $@
+	ar d $@ clzsi2.o
+
 $(BUILD)/debug/crt0.o: $(SRC)
 	CARGO_INCREMENTAL=0 $(CARGO) rustc --manifest-path src/crt0/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
 	touch $@
@@ -107,6 +114,27 @@ $(BUILD)/debug/crtn.o: $(SRC)
 	CARGO_INCREMENTAL=0 $(CARGO) rustc --manifest-path src/crtn/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
 	touch $@
 
+$(BUILD)/debug/ld_so.o: $(SRC)
+	CARGO_INCREMENTAL=0 $(CARGO) rustc --manifest-path src/ld_so/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
+	touch $@
+
+$(BUILD)/debug/ld_so: $(BUILD)/debug/ld_so.o $(BUILD)/debug/crti.o $(BUILD)/debug/libc.a $(BUILD)/debug/crtn.o
+	$(LD) $^ -o $@
+
+# Release targets
+
+$(BUILD)/release/libc.a: $(BUILD)/release/librelibc.a $(BUILD)/pthreads-emb/libpthread.a $(BUILD)/openlibm/libopenlibm.a
+	echo "create $@" > "[email protected]"
+	for lib in $^; do\
+		echo "addlib $$lib" >> "[email protected]"; \
+	done
+	echo "save" >> "[email protected]"
+	echo "end" >> "[email protected]"
+	ar -M < "[email protected]"
+
+$(BUILD)/release/libc.so: $(BUILD)/release/librelibc.patched.a $(BUILD)/pthreads-emb/libpthread.a $(BUILD)/openlibm/libopenlibm.a
+	$(CC) -nostdlib -shared -Wl,--whole-archive $^ -Wl,--no-whole-archive -o $@
+
 $(BUILD)/release/librelibc.a: $(SRC)
 	$(CARGO) rustc --release $(CARGOFLAGS) -- --emit link=$@ $(RUSTCFLAGS)
 	touch $@
@@ -128,6 +156,15 @@ $(BUILD)/release/crtn.o: $(SRC)
 	CARGO_INCREMENTAL=0 $(CARGO) rustc --release --manifest-path src/crtn/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
 	touch $@
 
+$(BUILD)/release/ld_so.o: $(SRC)
+	CARGO_INCREMENTAL=0 $(CARGO) rustc --release --manifest-path src/ld_so/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
+	touch $@
+
+$(BUILD)/release/ld_so: $(BUILD)/release/ld_so.o $(BUILD)/release/crti.o $(BUILD)/release/libc.a $(BUILD)/release/crtn.o
+	$(LD) $^ -o $@
+
+# Other targets
+
 $(BUILD)/include: $(SRC)
 	rm -rf $@ [email protected]
 	mkdir -p [email protected]

+ 0 - 1
src/header/sys_auxv/mod.rs

@@ -1,7 +1,6 @@
 //! sys/auxv.h implementation
 
 use platform::types::*;
-use platform::{Pal, Sys};
 
 pub const AT_HWCAP: usize = 16;
 

+ 0 - 2
src/header/unistd/mod.rs

@@ -679,8 +679,6 @@ pub extern "C" fn vfork() -> pid_t {
 
 #[no_mangle]
 pub extern "C" fn write(fildes: c_int, buf: *const c_void, nbyte: size_t) -> ssize_t {
-    use core::slice;
-
     let buf = unsafe { slice::from_raw_parts(buf as *const u8, nbyte as usize) };
     Sys::write(fildes, buf)
 }

+ 8 - 0
src/ld_so/Cargo.toml

@@ -0,0 +1,8 @@
+[package]
+name = "ld_so"
+version = "0.1.0"
+authors = ["Jeremy Soller <[email protected]>"]
+
+[lib]
+name = "ld_so"
+crate-type = ["staticlib"]

+ 355 - 0
src/ld_so/linker.rs

@@ -0,0 +1,355 @@
+use alloc::boxed::Box;
+use alloc::collections::BTreeMap;
+use alloc::string::{String, ToString};
+use alloc::vec::Vec;
+use core::{ptr, slice};
+use goblin::elf::{Elf, program_header, reloc, sym};
+use goblin::error::{Error, Result};
+
+use c_str::CString;
+use fs::File;
+use header::{fcntl, sys_mman, unistd};
+use io::Read;
+use platform::types::c_void;
+
+const PAGE_SIZE: usize = 4096;
+
+#[cfg(target_os = "redox")]
+const PATH_SEP: char = ';';
+
+#[cfg(target_os = "linux")]
+const PATH_SEP: char = ':';
+
+pub struct Linker {
+    library_path: String,
+    objects: BTreeMap<String, Box<[u8]>>
+}
+
+impl Linker {
+    pub fn new(library_path: &str) -> Self {
+        Self {
+            library_path: library_path.to_string(),
+            objects: BTreeMap::new(),
+        }
+    }
+
+    pub fn load(&mut self, name: &str, path: &str) -> Result<()> {
+        println!("load {}: {}", name, path);
+
+        let mut data = Vec::new();
+
+        let path_c = CString::new(path).map_err(|err| Error::Malformed(
+            format!("invalid path '{}': {}", path, err)
+        ))?;
+
+        {
+            let flags = fcntl::O_RDONLY | fcntl::O_CLOEXEC;
+            let mut file = File::open(&path_c, flags).map_err(|err| Error::Malformed(
+                format!("failed to open '{}': {}", path, err)
+            ))?;
+
+            file.read_to_end(&mut data).map_err(|err| Error::Malformed(
+                format!("failed to read '{}': {}", path, err)
+            ))?;
+        }
+
+        self.load_data(name, data.into_boxed_slice())
+    }
+
+    pub fn load_data(&mut self, name: &str, data: Box<[u8]>) -> Result<()> {
+        //TODO: Prevent failures due to recursion
+        {
+            let elf = Elf::parse(&data)?;
+            //println!("{:#?}", elf);
+
+            for library in elf.libraries.iter() {
+                if ! self.objects.contains_key(&library.to_string()) {
+                    self.load_library(library)?;
+                }
+            }
+        }
+
+        self.objects.insert(name.to_string(), data);
+
+        Ok(())
+    }
+
+    pub fn load_library(&mut self, name: &str) -> Result<()> {
+        if name.contains('/') {
+            self.load(name, name)
+        } else {
+            let library_path = self.library_path.clone();
+            for part in library_path.split(PATH_SEP) {
+                let path = if part.is_empty() {
+                    format!("./{}", name)
+                } else {
+                    format!("{}/{}", part, name)
+                };
+
+                println!("check {}", path);
+
+                let access = unsafe {
+                    let path_c = CString::new(path.as_bytes()).map_err(|err| Error::Malformed(
+                        format!("invalid path '{}': {}", path, err)
+                    ))?;
+
+                    // TODO: Use R_OK | X_OK
+                    unistd::access(path_c.as_ptr(), unistd::F_OK) == 0
+                };
+
+                if access {
+                    self.load(name, &path)?;
+                    return Ok(());
+                }
+            }
+
+            Err(Error::Malformed(
+                format!("failed to locate '{}'", name)
+            ))
+        }
+    }
+
+    pub fn link(&mut self, primary: &str) -> Result<usize> {
+        let elfs = {
+            let mut elfs = BTreeMap::new();
+            for (name, data) in self.objects.iter() {
+                elfs.insert(name.as_str(), Elf::parse(&data)?);
+            }
+            elfs
+        };
+
+        // Load all ELF files into memory and find all globals
+        let mut mmaps = BTreeMap::new();
+        let mut globals = BTreeMap::new();
+        for (elf_name, elf) in elfs.iter() {
+            println!("load {}", elf_name);
+
+            let object = match self.objects.get(*elf_name) {
+                Some(some) => some,
+                None => continue,
+            };
+
+            // Calculate virtual memory bounds
+            let bounds = {
+                let mut bounds_opt: Option<(usize, usize)> = None;
+                for ph in elf.program_headers.iter() {
+                    match ph.p_type {
+                        program_header::PT_LOAD => {
+                            let voff = ph.p_vaddr as usize % PAGE_SIZE;
+                            let vaddr = ph.p_vaddr as usize - voff;
+                            let vsize = ((ph.p_memsz as usize + voff + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
+
+                            println!("  load {:#x}, {:#x}: {:x?}", vaddr, vsize, ph);
+
+                            if let Some(ref mut bounds) = bounds_opt {
+                                if vaddr < bounds.0 {
+                                    bounds.0 = vaddr;
+                                }
+                                if vaddr + vsize > bounds.1 {
+                                    bounds.1 = vaddr + vsize;
+                                }
+                            } else {
+                                bounds_opt = Some((vaddr, vaddr + vsize));
+                            }
+                        },
+                        _ => ()
+                    }
+                }
+                match bounds_opt {
+                    Some(some) => some,
+                    None => continue,
+                }
+            };
+            println!("  bounds {:#x}, {:#x}", bounds.0, bounds.1);
+
+            // Allocate memory
+            let mmap = unsafe {
+                let size = bounds.1 /* - bounds.0 */;
+                let ptr = sys_mman::mmap(
+                    ptr::null_mut(),
+                    size,
+                    //TODO: Make it possible to not specify PROT_EXEC on Redox
+                    sys_mman::PROT_READ | sys_mman::PROT_WRITE,
+                    sys_mman::MAP_ANONYMOUS | sys_mman::MAP_PRIVATE,
+                    -1,
+                    0
+                );
+                if ptr as usize == !0 /* MAP_FAILED */ {
+                    return Err(Error::Malformed(
+                        format!("failed to map {}", elf_name)
+                    ));
+                }
+                slice::from_raw_parts_mut(ptr as *mut u8, size)
+            };
+            println!("  mmap {:p}, {:#x}", mmap.as_mut_ptr(), mmap.len());
+
+            // Locate all globals
+            for sym in elf.dynsyms.iter() {
+                if sym.st_bind() == sym::STB_GLOBAL && sym.st_value != 0 {
+                    if let Some(name_res) = elf.dynstrtab.get(sym.st_name) {
+                        let name = name_res?;
+                        let value = mmap.as_ptr() as usize + sym.st_value as usize;
+                        println!("  global {}: {:x?} = {:#x}", name, sym, value);
+                        globals.insert(name, value);
+                    }
+                }
+            }
+
+            // Copy data
+            for ph in elf.program_headers.iter() {
+                match ph.p_type {
+                    program_header::PT_LOAD => {
+                        let voff = ph.p_vaddr as usize % PAGE_SIZE;
+                        let vaddr = ph.p_vaddr as usize - voff;
+                        let vsize = ((ph.p_memsz as usize + voff + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
+
+                        // Copy data
+                        let obj_data = {
+                            let range = ph.file_range();
+                            match object.get(range.clone()) {
+                                Some(some) => some,
+                                None => return Err(Error::Malformed(
+                                    format!("failed to read {:?}", range)
+                                )),
+                            }
+                        };
+
+                        let mmap_data = {
+                            let range = ph.p_vaddr as usize..ph.p_vaddr as usize + obj_data.len();
+                            match mmap.get_mut(range.clone()) {
+                                Some(some) => some,
+                                None => return Err(Error::Malformed(
+                                    format!("failed to write {:?}", range)
+                                )),
+                            }
+                        };
+
+                        println!("  copy {:#x}, {:#x}: {:#x}, {:#x}", vaddr, vsize, voff, obj_data.len());
+
+                        mmap_data.copy_from_slice(obj_data);
+                    },
+                    _ => ()
+                }
+            }
+
+            mmaps.insert(elf_name, mmap);
+        }
+
+        // Perform relocations and protect pages
+        let mut entry_opt = None;
+        for (elf_name, elf) in elfs.iter() {
+            let mmap = match mmaps.get_mut(elf_name) {
+                Some(some) => some,
+                None => continue
+            };
+
+            println!("link {}", elf_name);
+
+            if *elf_name == primary {
+                entry_opt = Some(mmap.as_mut_ptr() as usize + elf.header.e_entry as usize);
+            }
+
+            // Relocate
+            for rel in elf.dynrelas.iter().chain(elf.dynrels.iter()).chain(elf.pltrelocs.iter()) {
+                println!("  rel {}: {:x?}",
+                    reloc::r_to_str(rel.r_type, elf.header.e_machine),
+                    rel
+                );
+
+                let a = rel.r_addend.unwrap_or(0) as usize;
+
+                let b = mmap.as_ptr() as usize;
+
+                let s = if rel.r_sym > 0 {
+                    let sym = elf.dynsyms.get(rel.r_sym).ok_or(Error::Malformed(
+                        format!("missing symbol for relocation {:?}", rel)
+                    ))?;
+
+                    let name = elf.dynstrtab.get(sym.st_name).ok_or(Error::Malformed(
+                        format!("missing name for symbol {:?}", sym)
+                    ))??;
+
+                    if let Some(value) = globals.get(name) {
+                        println!("    sym {}: {:x?} = {:#x}", name, sym, value);
+                        *value
+                    } else {
+                        println!("    sym {}: {:x?} = undefined", name, sym);
+                        0
+                    }
+                } else {
+                    0
+                };
+
+                let ptr = unsafe {
+                    mmap.as_mut_ptr().add(rel.r_offset as usize)
+                };
+
+                let set_u64 = |value| {
+                    println!("    set_u64 {:#x}", value);
+                    unsafe { *(ptr as *mut u64) = value; }
+                };
+
+                match rel.r_type {
+                    reloc::R_X86_64_64 => {
+                        set_u64((s + a) as u64);
+                    },
+                    reloc::R_X86_64_GLOB_DAT | reloc::R_X86_64_JUMP_SLOT => {
+                        set_u64(s as u64);
+                    },
+                    reloc::R_X86_64_RELATIVE => {
+                        set_u64((b + a) as u64);
+                    },
+                    _ => {
+                        println!("    unsupported");
+                    }
+                }
+            }
+
+            // Protect pages
+            for ph in elf.program_headers.iter() {
+                match ph.p_type {
+                    program_header::PT_LOAD => {
+                        let voff = ph.p_vaddr as usize % PAGE_SIZE;
+                        let vaddr = ph.p_vaddr as usize - voff;
+                        let vsize = ((ph.p_memsz as usize + voff + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
+
+                        let mut prot = 0;
+
+                        if ph.p_flags & program_header::PF_R == program_header::PF_R {
+                            prot |= sys_mman::PROT_READ;
+                        }
+
+                        // W ^ X. If it is executable, do not allow it to be writable, even if requested
+                        if ph.p_flags & program_header::PF_X == program_header::PF_X {
+                            prot |= sys_mman::PROT_EXEC;
+                        } else if ph.p_flags & program_header::PF_W == program_header::PF_W {
+                            prot |= sys_mman::PROT_WRITE;
+                        }
+
+                        let res = unsafe {
+                            let ptr = mmap.as_mut_ptr().add(vaddr);
+                            println!("  prot {:#x}, {:#x}: {:p}, {:#x}", vaddr, vsize, ptr, prot);
+
+                            sys_mman::mprotect(
+                                ptr as *mut c_void,
+                                vsize,
+                                prot
+                            )
+                        };
+
+                        if res < 0 {
+                            return Err(Error::Malformed(
+                                format!("failed to mprotect {}", elf_name)
+                            ));
+                        }
+                    },
+                    _ => ()
+                }
+            }
+        }
+
+        entry_opt.ok_or(Error::Malformed(
+            format!("missing entry for {}", primary)
+        ))
+    }
+}

+ 2 - 0
src/ld_so/mod.rs

@@ -0,0 +1,2 @@
+pub mod linker;
+pub mod start;

+ 64 - 0
src/ld_so/src/lib.rs

@@ -0,0 +1,64 @@
+#![no_std]
+#![feature(asm)]
+#![feature(linkage)]
+#![feature(naked_functions)]
+
+#[naked]
+#[no_mangle]
+pub unsafe extern "C" fn _start() {
+    #[cfg(target_arch = "x86_64")]
+    asm!("
+        # Save original stack and align stack to 16 bytes
+        mov rbp, rsp
+        and rsp, 0xFFFFFFFFFFFFFFF0
+
+        # Call ld_so_start(stack)
+        mov rdi, rbp
+        call relibc_ld_so_start
+
+        # Restore original stack, clear registers, and jump to new start function
+        mov rsp, rbp
+        xor rcx, rcx
+        xor rdx, rdx
+        xor rdi, rdi
+        xor rsi, rsi
+        xor r8, r8
+        xor r9, r9
+        xor r10, r10
+        xor r11, r11
+        jmp rax
+        "
+        :
+        :
+        :
+        : "intel", "volatile"
+    );
+    #[cfg(target_arch = "aarch64")]
+    asm!("
+        mov x0, sp
+        bl ld_so_start
+        TODO
+        "
+        :
+        :
+        :
+        : "volatile"
+    );
+}
+
+#[naked]
+#[no_mangle]
+pub unsafe extern "C" fn main(argc: isize, argv: *const *const i8) -> usize {
+    // LD
+    0x1D
+}
+
+#[panic_handler]
+#[linkage = "weak"]
+#[no_mangle]
+pub unsafe extern "C" fn rust_begin_unwind(pi: &::core::panic::PanicInfo) -> ! {
+    extern "C" {
+        fn relibc_panic(pi: &::core::panic::PanicInfo) -> !;
+    }
+    relibc_panic(pi)
+}

+ 68 - 0
src/ld_so/start.rs

@@ -0,0 +1,68 @@
+// Start code adapted from https://gitlab.redox-os.org/redox-os/relibc/blob/master/src/start.rs
+
+use c_str::CStr;
+use header::unistd;
+use platform::types::c_char;
+
+use super::linker::Linker;
+
+#[repr(C)]
+pub struct Stack {
+    argc: isize,
+    argv0: *const c_char,
+}
+
+#[no_mangle]
+pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack) -> usize {
+    if sp.argc < 2 {
+        eprintln!("ld.so [executable] [arguments...]");
+        unistd::_exit(1);
+        loop {}
+    }
+
+    // Pop the first argument (path to ld_so), and get the path of the program
+    // TODO: Also retrieve LD_LIBRARY_PATH this way
+    let path_c = unsafe {
+        let mut argv = &mut sp.argv0 as *mut *const c_char;
+        loop {
+            let next_argv = argv.add(1);
+            let arg = *next_argv;
+            *argv = arg;
+            argv = next_argv;
+            if arg.is_null() {
+                break;
+            }
+        }
+        sp.argc -= 1;
+
+        CStr::from_ptr(sp.argv0)
+    };
+
+    let path = match path_c.to_str() {
+        Ok(ok) => ok,
+        Err(err) => {
+            eprintln!("ld.so: failed to parse path: {}", err);
+            unistd::_exit(1);
+            loop {}
+        }
+    };
+
+    let mut linker = Linker::new("/usr/local/lib:/lib/x86_64-linux-gnu");
+    match linker.load(&path, &path) {
+        Ok(()) => (),
+        Err(err) => {
+            eprintln!("ld.so: failed to load '{}': {}", path, err);
+            unistd::_exit(1);
+            loop {}
+        }
+    }
+
+    match linker.link(&path) {
+        Ok(entry) => entry,
+        Err(err) => {
+            eprintln!("ld.so: failed to link '{}': {}", path, err);
+            unistd::_exit(1);
+            loop {}
+        }
+    }
+}

+ 2 - 0
src/lib.rs

@@ -26,6 +26,7 @@
 extern crate alloc;
 extern crate cbitset;
 extern crate core_io;
+extern crate goblin;
 #[macro_use]
 extern crate lazy_static;
 extern crate posix_regex;
@@ -50,6 +51,7 @@ pub mod db;
 pub mod fs;
 pub mod header;
 pub mod io;
+pub mod ld_so;
 pub mod mutex;
 pub mod platform;
 pub mod start;