123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528 |
- use alloc::boxed::Box;
- use alloc::collections::BTreeMap;
- use alloc::string::{String, ToString};
- use alloc::vec::Vec;
- use core::{mem, ptr, slice};
- use goblin::elf::{program_header, reloc, sym, Elf};
- 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;
- use super::tcb::{Master, Tcb};
- use super::PAGE_SIZE;
- #[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 tls_primary = 0;
- let mut tls_size = 0;
- let mut mmaps = BTreeMap::new();
- let mut globals = BTreeMap::new();
- for (elf_name, elf) in elfs.iter() {
- println!("map {}", 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() {
- 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;
- match ph.p_type {
- program_header::PT_LOAD => {
- 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));
- }
- }
- program_header::PT_TLS => {
- println!(" load tls {:#x}: {:x?}", vsize, ph);
- tls_size += vsize;
- if *elf_name == primary {
- tls_primary += 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);
- }
- }
- }
- mmaps.insert(elf_name, mmap);
- }
- // Allocate TLS
- let tcb = unsafe { Tcb::new(tls_size)? };
- println!("tcb {:x?}", tcb);
- // Copy data
- let mut tls_offset = tls_primary;
- let mut tcb_masters = Vec::new();
- let mut tls_index = 0;
- let mut tls_ranges = BTreeMap::new();
- for (elf_name, elf) in elfs.iter() {
- let object = match self.objects.get(*elf_name) {
- Some(some) => some,
- None => continue,
- };
- let mmap = match mmaps.get_mut(elf_name) {
- Some(some) => some,
- None => continue,
- };
- println!("load {}", elf_name);
- // Copy data
- for ph in elf.program_headers.iter() {
- 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;
- match ph.p_type {
- program_header::PT_LOAD => {
- 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);
- }
- program_header::PT_TLS => {
- let valign = if ph.p_align > 0 {
- ((ph.p_memsz + (ph.p_align - 1)) / ph.p_align) * ph.p_align
- } else {
- ph.p_memsz
- } as usize;
- let mut tcb_master = Master {
- ptr: unsafe { mmap.as_ptr().add(ph.p_vaddr as usize) },
- len: ph.p_filesz as usize,
- offset: tls_size - valign,
- };
- println!(
- " tls master {:p}, {:#x}: {:#x}, {:#x}",
- tcb_master.ptr, tcb_master.len, tcb_master.offset, valign,
- );
- if *elf_name == primary {
- tls_ranges.insert(elf_name, (0, tcb_master.range()));
- tcb_masters.insert(0, tcb_master);
- } else {
- tcb_master.offset -= tls_offset;
- tls_offset += vsize;
- tls_index += 1;
- tls_ranges.insert(elf_name, (tls_index, tcb_master.range()));
- tcb_masters.push(tcb_master);
- }
- }
- _ => (),
- }
- }
- }
- // Set master images for TLS and copy TLS data
- unsafe {
- tcb.set_masters(tcb_masters.into_boxed_slice());
- tcb.copy_masters()?;
- }
- // Perform relocations, and protect pages
- for (elf_name, elf) in elfs.iter() {
- let mmap = match mmaps.get_mut(elf_name) {
- Some(some) => some,
- None => continue,
- };
- println!("link {}", elf_name);
- // 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_mut_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 (tm, t) = if let Some((tls_index, tls_range)) = tls_ranges.get(elf_name) {
- (*tls_index, tls_range.start)
- } else {
- (0, 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_DTPMOD64 => {
- set_u64(tm 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);
- }
- reloc::R_X86_64_TPOFF64 => {
- set_u64((s + a).wrapping_sub(t) as u64);
- }
- reloc::R_X86_64_IRELATIVE => (), // Handled below
- _ => {
- println!(
- " {} unsupported",
- reloc::r_to_str(rel.r_type, elf.header.e_machine)
- );
- }
- }
- }
- // Protect pages
- for ph in elf.program_headers.iter() {
- if 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)));
- }
- }
- }
- }
- // Activate TLS
- unsafe {
- tcb.activate();
- }
- // Perform indirect relocations (necessary evil), gather entry point
- 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!("entry {}", 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_mut_ptr() as usize;
- 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;
- }
- };
- if rel.r_type == reloc::R_X86_64_IRELATIVE {
- unsafe {
- let f: unsafe extern "C" fn() -> u64 = mem::transmute(b + a);
- set_u64(f());
- }
- }
- }
- // Protect pages
- for ph in elf.program_headers.iter() {
- if let program_header::PT_LOAD = ph.p_type {
- 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)))
- }
- }
|