diff --git a/Cargo.lock b/Cargo.lock index 1d58c871ca..87c40d6773 100644 --- a/Cargo.lock +++ b/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" diff --git a/Cargo.toml b/Cargo.toml index 4b63147ed3..73d1316dab 100644 --- a/Cargo.toml +++ b/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 diff --git a/Makefile b/Makefile index 9d36ce6b94..2d1cf0ec82 100644 --- a/Makefile +++ b/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 $@" > "$@.mri" for lib in $^; do\ echo "addlib $$lib" >> "$@.mri"; \ @@ -88,13 +90,18 @@ $(BUILD)/release/libc.a: $(BUILD)/release/librelibc.a $(BUILD)/pthreads-emb/libp echo "end" >> "$@.mri" ar -M < "$@.mri" -$(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 $@" > "$@.mri" + for lib in $^; do\ + echo "addlib $$lib" >> "$@.mri"; \ + done + echo "save" >> "$@.mri" + echo "end" >> "$@.mri" + ar -M < "$@.mri" + +$(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 $@ $@.partial mkdir -p $@.partial diff --git a/src/header/sys_auxv/mod.rs b/src/header/sys_auxv/mod.rs index eeabca08ff..be95453e81 100644 --- a/src/header/sys_auxv/mod.rs +++ b/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; diff --git a/src/header/unistd/mod.rs b/src/header/unistd/mod.rs index 961bfbc2e1..48d1ae6c6f 100644 --- a/src/header/unistd/mod.rs +++ b/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) } diff --git a/src/ld_so/Cargo.toml b/src/ld_so/Cargo.toml new file mode 100644 index 0000000000..d5e0298cd8 --- /dev/null +++ b/src/ld_so/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "ld_so" +version = "0.1.0" +authors = ["Jeremy Soller "] + +[lib] +name = "ld_so" +crate-type = ["staticlib"] diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs new file mode 100644 index 0000000000..39bedaaf8b --- /dev/null +++ b/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> +} + +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 { + 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) + )) + } +} diff --git a/src/ld_so/mod.rs b/src/ld_so/mod.rs new file mode 100644 index 0000000000..8eb53c7144 --- /dev/null +++ b/src/ld_so/mod.rs @@ -0,0 +1,2 @@ +pub mod linker; +pub mod start; diff --git a/src/ld_so/src/lib.rs b/src/ld_so/src/lib.rs new file mode 100644 index 0000000000..d0adafc0e5 --- /dev/null +++ b/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) +} diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs new file mode 100644 index 0000000000..88437c43cf --- /dev/null +++ b/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 {} + } + } +} diff --git a/src/lib.rs b/src/lib.rs index e7ba6c3744..d0d06db628 100644 --- a/src/lib.rs +++ b/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;