From 9826cea092c3b92e33186140eeacc1f74934fbe4 Mon Sep 17 00:00:00 2001 From: oddcoder Date: Tue, 18 Feb 2020 20:35:53 +0200 Subject: [PATCH 1/4] Add SONAME for libc.so Usually it is possible to refer to library either by the file name or by elf "soname" soname is very similar for specifying something like (LIB/API version) combination so if for example you have ./prog that loads libx.so which is version 5.1.1 and there is ./plugin.so that ./prog would load that requires libx.so version 5.1.2 both libx.so should have the same soname to hint that they offer the exact same functionality. And this patch specifies the soname for relibc libc.so. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d564b4a2c6..5377941438 100644 --- a/Makefile +++ b/Makefile @@ -110,7 +110,7 @@ $(BUILD)/debug/libc.a: $(BUILD)/debug/librelibc.a $(BUILD)/pthreads-emb/libpthre $(AR) -M < "$@.mri" $(BUILD)/debug/libc.so: $(BUILD)/debug/librelibc.a $(BUILD)/pthreads-emb/libpthread.a $(BUILD)/openlibm/libopenlibm.a - $(CC) -nostdlib -shared -Wl,--allow-multiple-definition -Wl,--whole-archive $^ -Wl,--no-whole-archive -o $@ + $(CC) -nostdlib -shared -Wl,--allow-multiple-definition -Wl,--whole-archive $^ -Wl,--no-whole-archive -Wl,-soname,libc.so.6 -o $@ $(BUILD)/debug/librelibc.a: $(SRC) CARGO_INCREMENTAL=0 $(CARGO) rustc $(CARGOFLAGS) -- --emit link=$@ $(RUSTCFLAGS) @@ -149,7 +149,7 @@ $(BUILD)/release/libc.a: $(BUILD)/release/librelibc.a $(BUILD)/pthreads-emb/libp $(AR) -M < "$@.mri" $(BUILD)/release/libc.so: $(BUILD)/release/librelibc.a $(BUILD)/pthreads-emb/libpthread.a $(BUILD)/openlibm/libopenlibm.a - $(CC) -nostdlib -shared -Wl,--allow-multiple-definition -Wl,--whole-archive $^ -Wl,--no-whole-archive -o $@ + $(CC) -nostdlib -shared -Wl,--allow-multiple-definition -Wl,--whole-archive $^ -Wl,--no-whole-archive -Wl,-soname,libc.so.6 -o $@ $(BUILD)/release/librelibc.a: $(SRC) CARGO_INCREMENTAL=0 $(CARGO) rustc --release $(CARGOFLAGS) -- --emit link=$@ $(RUSTCFLAGS) From 61fcc018fcf5f154f3eb0532a589745febe84e62 Mon Sep 17 00:00:00 2001 From: oddcoder Date: Mon, 10 Aug 2020 23:22:17 +0200 Subject: [PATCH 2/4] Refer to libraries with soname if available and avoid loading libs twice It is usually not optimal to load a library twice and for specifics, it is **terrible** idea to load libc twice it was enough trouble dealing with libc statically linked into ld.so. So What this patch does it check for soname and if a library is already loaded it won't get loaded again. Why soname ? because unfortunately some bins gets linked againt libc.so while of their dependencies gets linked against libc.so.6 while one is usually symbolic link for the other. --- src/ld_so/linker.rs | 79 ++++++++++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index c55e4a7d67..34030513d8 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -15,9 +15,7 @@ use goblin::{ header::ET_DYN, program_header, r#dyn::{Dyn, DT_DEBUG}, - reloc, - sym, - Elf, + reloc, sym, Elf, }, error::{Error, Result}, }; @@ -25,7 +23,7 @@ use goblin::{ use crate::{ c_str::CString, fs::File, - header::{fcntl, sys_mman, unistd, errno::STR_ERROR}, + header::{errno::STR_ERROR, fcntl, sys_mman, unistd}, io::Read, platform::{errno, types::c_void}, }; @@ -147,9 +145,14 @@ impl Linker { deps.push(dep); } } - - lib.objects.insert(name.to_string(), data); - + let elf = Elf::parse(&data)?; + let key = match elf.soname { + Some(soname) => soname, + _ => name, + }; + if !lib.objects.contains_key(key) { + lib.objects.insert(key.to_string(), data); + } return Ok(deps); } @@ -471,11 +474,19 @@ impl Linker { -1, 0, ); - if ptr as usize == !0 /* MAP_FAILED */ { - return Err(Error::Malformed(format!("failed to map {}. errno: {}", elf_name, STR_ERROR[errno as usize]))); + if ptr as usize == !0 + /* MAP_FAILED */ + { + return Err(Error::Malformed(format!( + "failed to map {}. errno: {}", + elf_name, STR_ERROR[errno as usize] + ))); } if start as *mut c_void != ptr::null_mut() { - assert_eq!(ptr, start as *mut c_void, "mmap must always map on the destination we requested"); + assert_eq!( + ptr, start as *mut c_void, + "mmap must always map on the destination we requested" + ); } } start = addr + vaddr + vsize @@ -486,7 +497,10 @@ impl Linker { sys_mman::PROT_READ | sys_mman::PROT_WRITE, ); _r_debug.insert_first(addr as usize, &elf_name, addr + l_ld as usize); - (addr as usize, slice::from_raw_parts_mut(addr as *mut u8, size)) + ( + addr as usize, + slice::from_raw_parts_mut(addr as *mut u8, size), + ) } else { let (start, end) = bounds; let size = end - start; @@ -504,11 +518,19 @@ impl Linker { -1, 0, ); - if ptr as usize == !0 /* MAP_FAILED */ { - return Err(Error::Malformed(format!("failed to map {}. errno: {}", elf_name, STR_ERROR[errno as usize]))); + if ptr as usize == !0 + /* MAP_FAILED */ + { + return Err(Error::Malformed(format!( + "failed to map {}. errno: {}", + elf_name, STR_ERROR[errno as usize] + ))); } if start as *mut c_void != ptr::null_mut() { - assert_eq!(ptr, start as *mut c_void, "mmap must always map on the destination we requested"); + assert_eq!( + ptr, start as *mut c_void, + "mmap must always map on the destination we requested" + ); } ptr::write_bytes(ptr as *mut u8, 0, size); _r_debug.insert(ptr as usize, &elf_name, ptr as usize + l_ld as usize); @@ -590,7 +612,8 @@ impl Linker { }; let mmap_data = { - let range = ph.p_vaddr as usize - base_addr..ph.p_vaddr as usize + obj_data.len() - base_addr; + let range = ph.p_vaddr as usize - base_addr + ..ph.p_vaddr as usize + obj_data.len() - base_addr; match mmap.get_mut(range.clone()) { Some(some) => some, None => { @@ -598,7 +621,7 @@ impl Linker { return Err(Error::Malformed(format!( "failed to write {:x?}", range - ))) + ))); } } }; @@ -690,18 +713,20 @@ impl Linker { let name = elf.dynstrtab - .get(sym.st_name) - .ok_or(Error::Malformed(format!( - "missing name for symbol {:?}", - sym - )))??; - lib.get_sym(name) - .or_else(|| self.root.get_sym(name)) + .get(sym.st_name) + .ok_or(Error::Malformed(format!( + "missing name for symbol {:?}", + sym + )))??; + lib.get_sym(name).or_else(|| self.root.get_sym(name)) } else { None }; - let s = symbol.as_ref().map(|sym| sym.as_ptr() as usize).unwrap_or(0); + let s = symbol + .as_ref() + .map(|sym| sym.as_ptr() as usize) + .unwrap_or(0); let a = rel.r_addend.unwrap_or(0) as usize; @@ -757,9 +782,11 @@ impl Linker { reloc::R_X86_64_IRELATIVE => (), // Handled below reloc::R_X86_64_COPY => unsafe { // TODO: Make this work - let sym = symbol.as_ref().expect("R_X86_64_COPY called without valid symbol"); + let sym = symbol + .as_ref() + .expect("R_X86_64_COPY called without valid symbol"); ptr::copy_nonoverlapping(sym.as_ptr() as *const u8, ptr, sym.size as usize); - } + }, _ => { panic!( " {} unsupported", From 7b29f6eb27fbba311ea659ce0be1ecaa83d7557f Mon Sep 17 00:00:00 2001 From: oddcoder Date: Mon, 10 Aug 2020 23:43:24 +0200 Subject: [PATCH 3/4] Avoid relinking already linked libs This patch avoids collecting symbols, resolving relocs if they are already done (usually for example libc.so during a dlopen for another libfoo.so). This patch is purely for performance boost. --- src/ld_so/linker.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index 34030513d8..5f7644bd60 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -1,6 +1,6 @@ use alloc::{ boxed::Box, - collections::BTreeMap, + collections::{BTreeMap, BTreeSet}, rc::Rc, string::{String, ToString}, vec::Vec, @@ -350,12 +350,15 @@ impl Linker { ) -> Result> { unsafe { _r_debug.state = RTLDState::RT_ADD }; _dl_debug_state(); + let mut skip_list = BTreeSet::new(); let elfs = { let mut elfs = BTreeMap::new(); for (name, data) in lib.objects.iter() { // Skip already linked libraries if !lib.mmaps.contains_key(&*name) && !self.root.mmaps.contains_key(&*name) { elfs.insert(name.as_str(), Elf::parse(&data)?); + } else { + skip_list.insert(name.as_str()); } } elfs @@ -365,6 +368,9 @@ impl Linker { let mut tls_primary = 0; let mut tls_size = 0; for (elf_name, elf) in elfs.iter() { + if skip_list.contains(elf_name) { + continue; + } if self.verbose { println!("map {}", elf_name); } @@ -566,6 +572,9 @@ impl Linker { }); let mut tls_ranges = BTreeMap::new(); for (elf_name, elf) in elfs.iter() { + if skip_list.contains(elf_name) { + continue; + } let same_elf = if let Some(prog) = dso.as_ref() { if prog.name == *elf_name { true @@ -690,6 +699,9 @@ impl Linker { // Perform relocations, and protect pages for (elf_name, elf) in elfs.iter() { + if skip_list.contains(elf_name) { + continue; + } if self.verbose { println!("link {}", elf_name); } @@ -888,6 +900,9 @@ impl Linker { // Perform indirect relocations (necessary evil), gather entry point let mut entry_opt = None; for (elf_name, elf) in elfs.iter() { + if skip_list.contains(elf_name) { + continue; + } let (_, mmap) = match lib.mmaps.get_mut(*elf_name) { Some(some) => some, None => continue, From ab92ff9d41498b86427237edfcbefa8baffae1e0 Mon Sep 17 00:00:00 2001 From: oddcoder Date: Wed, 12 Aug 2020 14:54:07 +0200 Subject: [PATCH 4/4] make all ld printlns happen in case of verbose = true and apply cargo fmt It seams that stdout of ld.so is not that much of an issue but actually it unfortunately is. The major problem here is that sometimes programs generate header files in stdout (./getmy_custom_headers > header.h) and we need to keep that cleen. and this is very very popular in gcc. --- src/header/stdio/printf.rs | 1 - src/ld_so/callbacks.rs | 2 +- src/ld_so/library.rs | 2 +- src/ld_so/linker.rs | 10 ++++++---- src/ld_so/mod.rs | 3 +-- src/ld_so/start.rs | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/header/stdio/printf.rs b/src/header/stdio/printf.rs index f6f6f18341..d06570fa86 100644 --- a/src/header/stdio/printf.rs +++ b/src/header/stdio/printf.rs @@ -690,7 +690,6 @@ unsafe fn inner_printf(w: W, format: *const c_char, mut ap: VaList) -> _ => None, }; - let index = arg.index.map(|i| i - 1).unwrap_or_else(|| { if fmtkind == FmtKind::Percent { 0 diff --git a/src/ld_so/callbacks.rs b/src/ld_so/callbacks.rs index 031ecb47ad..d53f21561b 100644 --- a/src/ld_so/callbacks.rs +++ b/src/ld_so/callbacks.rs @@ -1,4 +1,4 @@ -use super::linker::{DSO, Linker, Symbol}; +use super::linker::{Linker, Symbol, DSO}; use alloc::boxed::Box; use goblin::error::Result; diff --git a/src/ld_so/library.rs b/src/ld_so/library.rs index 156234b9c4..88958e01c8 100644 --- a/src/ld_so/library.rs +++ b/src/ld_so/library.rs @@ -1,10 +1,10 @@ +use super::linker::Symbol; use alloc::{ boxed::Box, collections::{BTreeMap, BTreeSet}, string::String, vec::Vec, }; -use super::linker::Symbol; #[derive(Default, Debug)] pub struct DepTree { diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index 5f7644bd60..e9cbc145cc 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -466,7 +466,9 @@ impl Linker { let mut start = addr; for (vaddr, vsize) in ranges.iter() { if start < addr + vaddr { - println!("mmap({:#x}, {})", start, addr + vaddr - start); + if self.verbose { + println!("mmap({:#x}, {})", start, addr + vaddr - start); + } let mut flags = sys_mman::MAP_ANONYMOUS | sys_mman::MAP_PRIVATE; if start != 0 { flags |= sys_mman::MAP_FIXED_NOREPLACE; @@ -510,7 +512,9 @@ impl Linker { } else { let (start, end) = bounds; let size = end - start; - println!("mmap({:#x}, {})", start, size); + if self.verbose { + println!("mmap({:#x}, {})", start, size); + } let mut flags = sys_mman::MAP_ANONYMOUS | sys_mman::MAP_PRIVATE; if start != 0 { flags |= sys_mman::MAP_FIXED_NOREPLACE; @@ -716,7 +720,6 @@ impl Linker { // reloc::r_to_str(rel.r_type, elf.header.e_machine), // rel // ); - let symbol = if rel.r_sym > 0 { let sym = elf.dynsyms.get(rel.r_sym).ok_or(Error::Malformed(format!( "missing symbol for relocation {:?}", @@ -760,7 +763,6 @@ impl Linker { } else { rel.r_offset as *mut u8 }; - let set_u64 = |value| { // println!(" set_u64 {:#x}", value); unsafe { diff --git a/src/ld_so/mod.rs b/src/ld_so/mod.rs index 1321c33c89..19dfe05f52 100644 --- a/src/ld_so/mod.rs +++ b/src/ld_so/mod.rs @@ -1,9 +1,8 @@ use core::{mem, ptr}; use goblin::elf::program_header::{self, program_header32, program_header64, ProgramHeader}; -use crate::header::sys_auxv::AT_NULL; -use crate::start::Stack; use self::tcb::{Master, Tcb}; +use crate::{header::sys_auxv::AT_NULL, start::Stack}; pub const PAGE_SIZE: usize = 4096; diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs index b6eb7a3b15..c5261a286b 100644 --- a/src/ld_so/start.rs +++ b/src/ld_so/start.rs @@ -4,11 +4,11 @@ use alloc::{borrow::ToOwned, boxed::Box, collections::BTreeMap, string::String, use crate::{ c_str::CStr, - header::{unistd, sys_auxv::AT_NULL}, + header::{sys_auxv::AT_NULL, unistd}, platform::{new_mspace, types::c_char}, start::Stack, sync::mutex::Mutex, - ALLOCATOR + ALLOCATOR, }; use super::{