From 98ea9696753173b325af1f7fc01e5fbf3451c743 Mon Sep 17 00:00:00 2001 From: Mark Poliakov <mark@alnyan.me> Date: Fri, 15 Nov 2024 11:13:41 +0200 Subject: [PATCH] ld: call global constructors --- userspace/dyn-loader/src/main.rs | 6 ++ userspace/dyn-loader/src/object.rs | 61 +++++++++++++++++-- userspace/dyn-loader/src/relocation/x86_64.rs | 2 +- 3 files changed, 63 insertions(+), 6 deletions(-) diff --git a/userspace/dyn-loader/src/main.rs b/userspace/dyn-loader/src/main.rs index 866735f3..e05d88a3 100644 --- a/userspace/dyn-loader/src/main.rs +++ b/userspace/dyn-loader/src/main.rs @@ -53,6 +53,12 @@ fn run(binary: &str, args: &[String]) -> Result<!, Error> { lib.relocate(&mut state)?; } + // Call global constructors before handing off control to the program + for (_, lib) in libraries.iter_mut() { + lib.call_constructors(); + } + root.call_constructors(); + let entry = root.entry().ok_or(Error::NoEntrypoint)?; debug_trace!("entry = {:p}", entry); diff --git a/userspace/dyn-loader/src/object.rs b/userspace/dyn-loader/src/object.rs index 3c0b1a14..2268e1ce 100644 --- a/userspace/dyn-loader/src/object.rs +++ b/userspace/dyn-loader/src/object.rs @@ -1,15 +1,14 @@ use std::{ fs::File, io::{BufReader, Read, Seek, SeekFrom}, + ops::Range, path::{Path, PathBuf}, rc::Rc, }; use elf::{ abi::{ - DF_1_PIE, DT_FLAGS_1, DT_NEEDED, ET_DYN, ET_EXEC, PT_DYNAMIC, PT_GNU_EH_FRAME, - PT_GNU_RELRO, PT_GNU_STACK, PT_INTERP, PT_LOAD, PT_NOTE, PT_NULL, PT_PHDR, SHN_UNDEF, - SHT_REL, SHT_RELA, STB_GLOBAL, STB_LOCAL, STB_WEAK, + DF_1_PIE, DT_FINI, DT_FINI_ARRAY, DT_FINI_ARRAYSZ, DT_FLAGS_1, DT_INIT, DT_INIT_ARRAY, DT_INIT_ARRAYSZ, DT_NEEDED, DT_PREINIT_ARRAY, DT_PREINIT_ARRAYSZ, ET_DYN, ET_EXEC, PT_DYNAMIC, PT_GNU_EH_FRAME, PT_GNU_RELRO, PT_GNU_STACK, PT_INTERP, PT_LOAD, PT_NOTE, PT_NULL, PT_PHDR, SHN_UNDEF, SHT_REL, SHT_RELA, STB_GLOBAL, STB_LOCAL, STB_WEAK }, endian::AnyEndian, symbol::Symbol, @@ -55,6 +54,8 @@ pub struct Object { mapping: Option<Mapping>, dynamic_symbol_array: Vec<DynamicSymbol>, + + init_array: Option<Range<usize>>, } impl ResolvedSymbol<'_> { @@ -142,6 +143,8 @@ impl Object { mapping: None, dynamic_symbol_array, + + init_array: None }) } @@ -157,7 +160,9 @@ impl Object { // TODO segment granularity mappings let mapping_size = self.vma_end - self.vma_start; let mut mapping = match self.ty { - ElfType::Relocatable(_) => Mapping::new(mapping_size, MappingFlags::WRITE | MappingFlags::EXEC)?, + ElfType::Relocatable(_) => { + Mapping::new(mapping_size, MappingFlags::WRITE | MappingFlags::EXEC)? + } // TODO fixed mapping for this one ElfType::Static => todo!(), }; @@ -168,6 +173,34 @@ impl Object { mapping.base() as i64 - self.vma_start as i64 ); + // Parse .dynamic section again to extract extra info + if let Some(dynamic) = self.elf.dynamic()? { + let mut dt_init_array = None; + let mut dt_init_array_sz = None; + + for dynamic in dynamic { + match dynamic.d_tag { + DT_INIT_ARRAY => dt_init_array = Some(dynamic.d_ptr()), + DT_INIT_ARRAYSZ => dt_init_array_sz = Some(dynamic.d_val()), + DT_FINI_ARRAY => todo!(), + DT_FINI_ARRAYSZ => todo!(), + DT_INIT => todo!(), + DT_FINI => todo!(), + DT_PREINIT_ARRAY => todo!(), + DT_PREINIT_ARRAYSZ => todo!(), + _ => () + } + } + + if let (Some(ptr), Some(size)) = (dt_init_array, dt_init_array_sz) { + // This address is subject to relocation + debug_trace!("{:?}: DT_INIT_ARRAY: {:#x?}", self.path, ptr..ptr + size); + let ptr = ptr as i64 + mapping.base() as i64 - self.vma_start as i64; + let ptr = ptr as usize; + self.init_array = Some(ptr..ptr + size as usize); + } + } + // Load segments for segment in self.elf.segments() { let mem_size = segment.p_memsz as usize; @@ -220,6 +253,24 @@ impl Object { Ok(()) } + pub fn call_constructors(&mut self) { + type Constructor = extern "C" fn(); + + if let Some(init_array) = self.init_array.as_ref() { + for slot in init_array.clone().step_by(size_of::<usize>()) { + let func = unsafe { core::ptr::with_exposed_provenance::<usize>(slot).read_unaligned() }; + + if func == 0 || func == usize::MAX { + continue; + } + + let func = unsafe { core::mem::transmute::<_, Constructor>(func) }; + + func(); + } + } + } + pub fn entry(&self) -> Option<extern "C" fn(usize)> { let entry = self.elf.ehdr.e_entry as i64; let offset = match &self.ty { @@ -273,7 +324,7 @@ impl Object { for rela in rela_section { let dynsym = &self.dynamic_symbol_array[rela.r_sym as usize]; let sym = match dynsym.raw.st_bind() { - STB_GLOBAL => ResolvedSymbol::Global(state.lookup(dynsym).unwrap()), + STB_GLOBAL | STB_WEAK => ResolvedSymbol::Global(state.lookup(dynsym).unwrap()), STB_LOCAL => { if dynsym.name.is_empty() { ResolvedSymbol::Null diff --git a/userspace/dyn-loader/src/relocation/x86_64.rs b/userspace/dyn-loader/src/relocation/x86_64.rs index 28d4d758..8ccf2bc4 100644 --- a/userspace/dyn-loader/src/relocation/x86_64.rs +++ b/userspace/dyn-loader/src/relocation/x86_64.rs @@ -28,7 +28,7 @@ impl Relocation for Rela { // S R_X86_64_JUMP_SLOT | R_X86_64_GLOB_DAT => Ok(Some(RelaValue::QWord(s))), // S + A - R_X86_64_64 => todo!(), + R_X86_64_64 => Ok(Some(RelaValue::QWord(s + self.r_addend))), // B + A R_X86_64_RELATIVE => Ok(Some(RelaValue::QWord(load_base as i64 + self.r_addend))), // TLS