ld: call global constructors

This commit is contained in:
Mark Poliakov 2024-11-15 11:13:41 +02:00
parent 6dc77143b0
commit 98ea969675
3 changed files with 63 additions and 6 deletions

View File

@ -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);

View File

@ -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

View File

@ -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