feat(ld.so): partially switch to object

Signed-off-by: Anhad Singh <andypython@protonmail.com>
This commit is contained in:
Anhad Singh
2025-01-07 13:03:47 +11:00
parent e219f9768e
commit e49072655c
6 changed files with 365 additions and 369 deletions
Generated
+8 -1
View File
@@ -274,6 +274,13 @@ dependencies = [
"autocfg",
]
[[package]]
name = "object"
version = "0.36.7"
dependencies = [
"memchr",
]
[[package]]
name = "parse-zoneinfo"
version = "0.3.1"
@@ -484,11 +491,11 @@ dependencies = [
"chrono-tz",
"dlmalloc",
"generic-rt",
"goblin",
"libc",
"libm",
"md-5",
"memchr",
"object",
"pbkdf2",
"plain",
"posix-regex",
+1 -5
View File
@@ -49,11 +49,7 @@ generic-rt = { path = "generic-rt" }
chrono-tz = {version = "0.10", default-features = false}
chrono = {version = "0.4", default-features = false, features = ["alloc"]}
libm = "0.2"
[dependencies.goblin]
version = "0.7"
default-features = false
features = ["elf32", "elf64", "endian_fd"]
object = { version = "0.36.7", path = "../object", default-features = false, features = ["elf", "read_core"] }
[dependencies.dlmalloc]
path = "dlmalloc-rs"
+1 -1
View File
@@ -112,7 +112,7 @@ impl LinkMap {
}
unsafe {
(*node).l_prev = last;
(*last).l_next = node;
last.l_next = node;
}
}
}
+276 -282
View File
@@ -2,8 +2,18 @@
//! * <https://refspecs.linuxfoundation.org/elf/elf.pdf>
//! * <https://www.akkadia.org/drepper/dsohowto.pdf>
use object::{
elf::{self, Dyn64, FileHeader64},
read::elf::{
Dyn as _, ElfFile64, FileHeader, GnuHashTable, HashTable as SysVHashTable, ProgramHeader,
Sym, SymbolTable, Version, VersionTable,
},
Endianness, NativeEndian, Object, ObjectSection, ReadRef, SectionKind, StringTable,
SymbolIndex,
};
use super::{
debug::{RTLDDebug, _r_debug},
debug::_r_debug,
linker::{Scope, Symbol},
tcb::Master,
};
@@ -17,35 +27,70 @@ use alloc::{
vec::Vec,
};
use core::{
mem::{size_of, transmute},
mem::transmute,
ptr::{self, NonNull},
slice,
};
#[cfg(target_pointer_width = "32")]
use goblin::elf32::{
dynamic::{Dyn, DT_DEBUG, DT_RUNPATH},
header::ET_DYN,
program_header,
section_header::{SHN_UNDEF, SHT_FINI_ARRAY, SHT_INIT_ARRAY},
sym,
};
#[cfg(target_pointer_width = "64")]
use goblin::elf64::{
dynamic::{Dyn, DT_DEBUG, DT_RUNPATH},
header::ET_DYN,
program_header,
section_header::{SHT_FINI_ARRAY, SHT_INIT_ARRAY},
sym,
};
use goblin::{
elf::{
dynamic::{DT_GNU_HASH, DT_PLTGOT},
sym::{STB_GLOBAL, STB_WEAK},
Dynamic, Elf, Sym,
},
error::{Error, Result},
strtab::Strtab,
#[cfg(target_pointer_width = "64")]
use goblin::elf64::{
header::ET_DYN,
program_header,
section_header::{SHT_FINI_ARRAY, SHT_INIT_ARRAY},
};
use goblin::error::{Error, Result};
type Dyn = Dyn64<NativeEndian>;
enum HashTable<'a, Elf: FileHeader> {
Gnu(GnuHashTable<'a, Elf>),
Sysv(SysVHashTable<'a, Elf>),
}
impl<'a, Elf> HashTable<'a, Elf>
where
Elf: FileHeader,
{
/// Use the hash table to find the symbol table entry with the given name, hash, and version.
#[inline]
pub fn find<R: ReadRef<'a>>(
&self,
endian: Elf::Endian,
name: &str,
version: Option<&Version<'_>>,
symbols: &SymbolTable<'a, Elf, R>,
versions: &VersionTable<'a, Elf>,
) -> Option<(SymbolIndex, &'a Elf::Sym)> {
let name = name.as_bytes();
match self {
Self::Gnu(hash_table) => {
let hash = elf::gnu_hash(name);
hash_table.find(endian, name, hash, version, symbols, versions)
}
Self::Sysv(hash_table) => {
let hash = elf::hash(name);
hash_table.find(endian, name, hash, version, symbols, versions)
}
}
}
}
pub(super) struct Dynamic<'a> {
runpath: Option<String>,
got: Option<NonNull<usize>>,
needed: Vec<&'a str>,
pub(super) jmprel: usize,
hash_table: HashTable<'a, FileHeader64<Endianness>>,
pub(super) dynstrtab: StringTable<'a>,
soname: Option<&'a str>,
}
#[derive(Debug, PartialEq)]
#[repr(u8)]
@@ -53,10 +98,10 @@ pub enum SymbolBinding {
/// Global symbols are visible to all object files being combined. One
/// file's definition of a global symbol will satisfy another file's
/// undefined reference to the same global symbol.
Global = STB_GLOBAL,
Global = elf::STB_GLOBAL,
/// Weak symbols resemble global symbols, but their definitions have lower
/// precedence.
Weak = STB_WEAK,
Weak = elf::STB_WEAK,
}
impl SymbolBinding {
@@ -66,106 +111,16 @@ impl SymbolBinding {
}
}
enum SymbolHashTable {
Gnu {
bloom_filter: NonNull<[u64]>,
buckets: NonNull<[u32]>,
chains: NonNull<u32>,
bloom_shift: u32,
symbol_off: u32,
},
Sysv,
}
impl SymbolHashTable {
fn find<'a>(&self, name: &str, dynsyms: &'a [Sym], dynstrtab: &'a Strtab) -> Option<&'a Sym> {
match self {
Self::Gnu {
bloom_filter,
buckets,
chains,
bloom_shift,
symbol_off,
} => {
#[cfg(target_pointer_width = "64")]
const BLOOM_WIDTH: u32 = 64;
let h1 = self.hash(name);
let h2 = h1 >> bloom_shift;
let bloom_filter = unsafe { bloom_filter.as_ref() };
let mask: u64 = (1 << (h1 & (BLOOM_WIDTH - 1))) | (1 << (h2 & (BLOOM_WIDTH - 1)));
let bloom_idx = (h1 / BLOOM_WIDTH) & (bloom_filter.len() as u32 - 1);
if bloom_filter.get(bloom_idx as usize)? & mask != mask {
return None;
}
let buckets = unsafe { buckets.as_ref() };
let mut idx = *buckets.get(h1 as usize % buckets.len())?;
if idx == 0 {
return None;
}
let chains = chains.as_ptr();
loop {
let h2 = unsafe { chains.add((idx - symbol_off) as usize).read() };
if let Some(sym) = dynsyms.get(idx as usize) {
let n = dynstrtab.get_at(sym.st_name).unwrap();
if n == name && [STB_GLOBAL, STB_WEAK].contains(&sym.st_bind()) {
return Some(sym);
}
}
if h2 & 1 != 0 {
return None;
}
idx += 1;
}
}
Self::Sysv => unimplemented!(),
}
}
fn hash(&self, name: &str) -> u32 {
match self {
Self::Sysv => {
let mut h = 0u32;
let mut g;
for c in name.chars() {
h = (h << 4) + c as u32;
g = h & 0xf0000000;
if g != 0 {
h ^= g >> 24;
}
h &= !g;
}
h
}
Self::Gnu { .. } => {
let mut h = 5381;
for c in name.chars() {
h = (h << 5) + h + c as u32;
}
h
}
}
}
}
/// Use to represent a library as well as all the symbols that is loaded withen it.
pub struct DSO {
pub name: String,
pub id: usize,
pub dlopened: bool,
pub entry_point: usize,
pub runpath: Option<String>,
/// Loaded library in-memory data
pub mmap: &'static [u8],
pub global_syms: BTreeMap<String, Symbol>,
pub weak_syms: BTreeMap<String, Symbol>,
pub dependencies: Vec<String>,
/// .init_array addr and len
pub init_array: (usize, usize),
/// .fini_array addr and len
@@ -173,11 +128,8 @@ pub struct DSO {
pub tls_module_id: usize,
pub tls_offset: usize,
pub dynamic: Option<Dynamic>,
pub dynsyms: Vec<goblin::elf::sym::Sym>,
pub dynstrtab: Strtab<'static>,
hash_table: SymbolHashTable,
pub(super) dynamic: Dynamic<'static>,
pub(super) symbols: SymbolTable<'static, FileHeader64<Endianness>>,
pub scope: Scope,
/// Position Independent Executable.
@@ -197,56 +149,21 @@ impl DSO {
tls_module_id: usize,
tls_offset: usize,
) -> Result<(DSO, Option<Master>)> {
let elf = Elf::parse(data)?;
let (mmap, tcb_master) = DSO::mmap_and_copy(path, &elf, data, base_addr, tls_offset)?;
let elf = ElfFile64::<object::Endianness>::parse(data).unwrap();
let (mmap, tcb_master, dynamic) =
DSO::mmap_and_copy(path, &elf, data, base_addr, tls_offset).unwrap();
let dynamic = elf.dynamic.as_ref().unwrap();
let base = if is_pie_enabled(&elf) {
mmap.as_ptr() as usize
} else {
0
};
let mut hash_table = None;
for dynamic in dynamic.dyns.iter() {
if dynamic.d_tag == DT_GNU_HASH {
#[derive(Debug)]
#[repr(C)]
struct Header {
n_buckets: u32,
symbol_offset: u32,
bloom_size: u32,
bloom_shift: u32,
}
let (header, bloom_filter, buckets, chains);
unsafe {
let ptr = NonNull::new(dynamic.d_val as *mut u8).unwrap().add(base);
header = ptr.cast::<Header>().as_ref();
assert!(header.bloom_size.is_power_of_two());
bloom_filter = ptr.add(size_of::<Header>()).cast::<u64>();
buckets = bloom_filter.add(header.bloom_size as usize).cast::<u32>();
chains = buckets.add(header.n_buckets as usize);
}
hash_table = Some(SymbolHashTable::Gnu {
bloom_filter: NonNull::slice_from_raw_parts(
bloom_filter,
header.bloom_size as usize,
),
buckets: NonNull::slice_from_raw_parts(buckets, header.n_buckets as usize),
chains,
bloom_shift: header.bloom_shift,
symbol_off: header.symbol_offset,
});
}
}
// dynamic.hash_table = HashTable::Gnu(
// elf.elf_section_table()
// .gnu_hash(Endianness::default(), data)
// .unwrap()
// .unwrap()
// .0,
// );
let (init_array, fini_array) = DSO::init_fini_arrays(&elf, mmap.as_ptr() as usize);
let name = match elf.soname {
let name = match dynamic.soname {
Some(soname) => soname.to_string(),
_ => basename(path),
};
@@ -255,20 +172,21 @@ impl DSO {
_ => 0,
};
let entry_point = if is_pie_enabled(&elf) {
mmap.as_ptr() as usize + elf.header.e_entry as usize
mmap.as_ptr() as usize + elf.entry() as usize
} else {
elf.header.e_entry as usize
elf.entry() as usize
};
let endian = Endianness::default();
let sections = elf.elf_header().sections(endian, data).unwrap();
let symbols = sections.symbols(endian, data, elf::SHT_DYNSYM).unwrap();
let dso = DSO {
name,
id,
dlopened,
entry_point,
runpath: DSO::get_runpath(path, &elf)?,
mmap,
global_syms: BTreeMap::new(),
weak_syms: BTreeMap::new(),
dependencies: elf.libraries.iter().map(|s| s.to_string()).collect(),
init_array,
fini_array,
tls_module_id: if tcb_master.is_some() {
@@ -279,44 +197,39 @@ impl DSO {
tls_offset,
pie: is_pie_enabled(&elf),
dynamic: elf.dynamic,
dynsyms: elf.dynsyms.to_vec(),
dynstrtab: elf.dynstrtab,
symbols,
dynamic,
scope: Scope::local(),
hash_table: hash_table.expect("required hash table not present"),
};
Ok((dso, tcb_master))
}
/// Global Offset Table
pub(super) fn got(&self) -> Option<NonNull<usize>> {
let dynamic = self.dynamic.as_ref()?;
let object_base_addr = self.mmap.as_ptr() as u64;
#[inline]
pub fn got(&self) -> Option<NonNull<usize>> {
self.dynamic.got
}
let got = if let Some(ptr) = {
dynamic
.dyns
.iter()
.find(|r#dyn| r#dyn.d_tag == DT_PLTGOT)
.map(|r#dyn| r#dyn.d_val)
} {
if self.pie {
(object_base_addr + ptr) as *mut usize
} else {
ptr as *mut usize
}
} else {
assert_eq!(dynamic.info.jmprel, 0);
return None;
};
#[inline]
pub fn runpath(&self) -> Option<&String> {
self.dynamic.runpath.as_ref()
}
Some(NonNull::new(got).expect("global offset table"))
#[inline]
pub fn dependencies(&self) -> &[&str] {
&self.dynamic.needed
}
pub fn get_sym(&self, name: &str) -> Option<(Symbol, SymbolBinding)> {
let sym = self.hash_table.find(name, &self.dynsyms, &self.dynstrtab)?;
let endian = Endianness::default();
let (_index, sym) = self.dynamic.hash_table.find(
endian,
name,
None,
&self.symbols,
&VersionTable::default(),
)?;
Some((
Symbol {
@@ -325,14 +238,14 @@ impl DSO {
} else {
0
},
value: sym.st_value as usize,
size: sym.st_size as usize,
sym_type: sym::st_type(sym.st_info),
value: sym.st_value(endian) as usize,
size: sym.st_size(endian) as usize,
sym_type: sym.st_type(),
},
// TODO(andypython): move this into [`Symbol`]
match sym.st_bind() {
STB_GLOBAL => SymbolBinding::Global,
STB_WEAK => SymbolBinding::Weak,
elf::STB_GLOBAL => SymbolBinding::Global,
elf::STB_WEAK => SymbolBinding::Weak,
bind => unreachable!("get_sym bind {bind}"),
},
))
@@ -362,46 +275,29 @@ impl DSO {
}
}
fn get_runpath(path: &str, elf: &Elf) -> Result<Option<String>> {
if let Some(dynamic) = &elf.dynamic {
let entry = dynamic.dyns.iter().find(|d| d.d_tag == DT_RUNPATH);
match entry {
Some(entry) => {
let runpath = elf
.dynstrtab
.get_at(entry.d_val as usize)
.ok_or(Error::Malformed("Missing RUNPATH in dynstrtab".to_string()))?;
let base = dirname(path);
return Ok(Some(runpath.replace("$ORIGIN", &base)));
}
_ => return Ok(None),
}
}
Ok(None)
}
fn mmap_and_copy(
fn mmap_and_copy<'a>(
path: &str,
elf: &Elf,
data: &[u8],
elf: &ElfFile64<'a>,
data: &'a [u8],
base_addr: Option<usize>,
tls_offset: usize,
) -> Result<(&'static mut [u8], Option<Master>)> {
) -> object::Result<(&'static [u8], Option<Master>, Dynamic<'a>)> {
let endian = elf.endian();
trace!("# {}", path);
// data for struct LinkMap
let mut l_ld = 0;
// 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 % ph.p_align;
let vaddr = (ph.p_vaddr - voff) as usize;
let vsize = ((ph.p_memsz + voff) as usize).next_multiple_of(ph.p_align as usize);
for ph in elf.elf_program_headers() {
let voff = ph.p_vaddr(endian) % ph.p_align(endian);
let vaddr = (ph.p_vaddr(endian) - voff) as usize;
let vsize = ((ph.p_memsz(endian) + voff) as usize)
.next_multiple_of(ph.p_align(endian) as usize);
match ph.p_type {
match ph.p_type(endian) {
program_header::PT_DYNAMIC => {
l_ld = ph.p_vaddr;
l_ld = ph.p_vaddr(endian);
}
program_header::PT_LOAD => {
trace!(" load {:#x}, {:#x}: {:x?}", vaddr, vsize, ph);
@@ -419,9 +315,11 @@ impl DSO {
_ => (),
}
}
bounds_opt.ok_or(Error::Malformed(
"Unable to find PT_LOAD section".to_string(),
))?
bounds_opt
.ok_or(Error::Malformed(
"Unable to find PT_LOAD section".to_string(),
))
.unwrap()
};
trace!(" bounds {:#x}, {:#x}", bounds.0, bounds.1);
// Allocate memory
@@ -451,7 +349,8 @@ impl DSO {
-1,
0,
)
.map_err(|e| Error::Malformed(format!("failed to map {}. errno: {}", path, e.0)))?;
.map_err(|e| Error::Malformed(format!("failed to map {}. errno: {}", path, e.0)))
.unwrap();
if !(start as *mut c_void).is_null() {
assert_eq!(
@@ -469,19 +368,29 @@ impl DSO {
let skip_load_segment_copy = base_addr.is_some();
let mut tcb_master = None;
// Copy data
for ph in elf.program_headers.iter() {
let voff = ph.p_vaddr % ph.p_align;
// let vaddr = (ph.p_vaddr - voff) as usize;
let vsize = ((ph.p_memsz + voff) as usize).next_multiple_of(ph.p_align as usize);
let base = if is_pie_enabled(elf) {
mmap.as_ptr() as usize
} else {
0
};
match ph.p_type {
// Copy data
let mut dynamic = None;
for ph in elf.elf_program_headers() {
let voff = ph.p_vaddr(endian) % ph.p_align(endian);
// let vaddr = (ph.p_vaddr - voff) as usize;
let vsize = ((ph.p_memsz(endian) + voff) as usize)
.next_multiple_of(ph.p_align(endian) as usize);
match ph.p_type(endian) {
program_header::PT_LOAD => {
if skip_load_segment_copy {
continue;
}
let obj_data = {
let range = ph.file_range();
let (offset, size) = ph.file_range(endian);
let offset = offset as usize;
let range = offset..(offset + size as usize);
match data.get(range.clone()) {
Some(some) => some,
None => {
@@ -489,16 +398,17 @@ impl DSO {
"failed to read {:x?}",
range
)))
.unwrap()
}
}
};
let mmap_data = {
let range = if is_pie_enabled(elf) {
let addr = ph.p_vaddr as usize;
let addr = ph.p_vaddr(endian) as usize;
addr..addr + obj_data.len()
} else {
let addr = ph.p_vaddr as usize - mmap.as_ptr() as usize;
let addr = ph.p_vaddr(endian) as usize - mmap.as_ptr() as usize;
addr..addr + obj_data.len()
};
match mmap.get_mut(range.clone()) {
@@ -507,7 +417,8 @@ impl DSO {
return Err(Error::Malformed(format!(
"failed to write {:x?}",
range
)));
)))
.unwrap();
}
}
};
@@ -523,68 +434,151 @@ impl DSO {
program_header::PT_TLS => {
let ptr = unsafe {
if is_pie_enabled(elf) {
mmap.as_ptr().add(ph.p_vaddr as usize)
mmap.as_ptr().add(ph.p_vaddr(endian) as usize)
} else {
ph.p_vaddr as *const u8
ph.p_vaddr(endian) as *const u8
}
};
tcb_master = Some(Master {
ptr,
len: ph.p_filesz as usize,
len: ph.p_filesz(endian) as usize,
offset: tls_offset + vsize,
});
trace!(" tcb master {:x?}", tcb_master);
}
program_header::PT_DYNAMIC => {
// overwrite DT_DEBUG if exist in DYNAMIC segment
// first we identify the location of DYNAMIC segment
let dyn_start = ph.p_vaddr as usize;
let mut debug_start = None;
// next we identify the location of DT_DEBUG in .dynamic section
if let Some(dynamic) = elf.dynamic.as_ref() {
for (i, entry) in dynamic.dyns.iter().enumerate() {
if entry.d_tag == DT_DEBUG {
debug_start = Some(i);
break;
}
}
}
if let Some(i) = debug_start {
let bytes: [u8; size_of::<Dyn>() / 2] =
unsafe { transmute((&_r_debug) as *const RTLDDebug as usize) };
let start = if is_pie_enabled(elf) {
dyn_start + i * size_of::<Dyn>() + size_of::<Dyn>() / 2
} else {
dyn_start + i * size_of::<Dyn>() + size_of::<Dyn>() / 2
- mmap.as_mut_ptr() as usize
};
mmap[start..start + size_of::<Dyn>() / 2].clone_from_slice(&bytes);
}
dynamic = Some(ph.dynamic(endian, data).unwrap().unwrap())
}
_ => (),
}
}
Ok((mmap, tcb_master))
Ok((
mmap,
tcb_master,
Self::parse_dynamic(path, base, endian, mmap, dynamic.unwrap())?,
))
}
fn init_fini_arrays(elf: &Elf, mmap_addr: usize) -> ((usize, usize), (usize, usize)) {
fn parse_dynamic<'a>(
path: &str,
base: usize,
endian: Endianness,
mmap: &'static [u8],
entries: &'a [Dyn64<Endianness>],
) -> object::Result<Dynamic<'a>> {
let mut runpath = None;
let mut got = None;
let mut needed = vec![];
let mut jmprel = None;
let mut soname = None;
let mut hash_table = None;
let (mut strtab_offset, mut strtab_size) = (None, None);
for (i, entry) in entries.iter().enumerate() {
let val = entry.d_val(endian);
let tag = entry.d_tag(endian) as u32;
match tag {
elf::DT_DEBUG => {
// let vaddr = ph.p_vaddr(endian) as usize;
// let bytes: [u8; size_of::<Dyn>() / 2] =
// unsafe { transmute((&_r_debug) as *const RTLDDebug as usize) };
// let start = if is_pie_enabled(elf) {
// vaddr + i * size_of::<Dyn>() + size_of::<Dyn>() / 2
// } else {
// vaddr + i * size_of::<Dyn>() + size_of::<Dyn>() / 2
// - mmap.as_mut_ptr() as usize
// };
// mmap[start..start + size_of::<Dyn>() / 2].clone_from_slice(&bytes);
}
elf::DT_GNU_HASH => {
let value = GnuHashTable::parse(endian, &mmap[val as usize..])?;
hash_table = Some(HashTable::Gnu(value));
}
elf::DT_HASH if hash_table.is_none() => {
hash_table = Some(HashTable::Sysv(SysVHashTable::parse(
endian,
&mmap[val as usize..],
)?));
}
elf::DT_PLTGOT => {
let ptr = NonNull::new(val as *mut usize).expect("DT_PLTGOT is NULL");
got = Some(unsafe { ptr.byte_add(base) });
}
elf::DT_NEEDED => needed.push(entry),
elf::DT_JMPREL => jmprel = Some(val as usize),
elf::DT_RUNPATH => runpath = Some(entry),
elf::DT_STRTAB => strtab_offset = Some(val),
elf::DT_STRSZ => strtab_size = Some(val),
elf::DT_SONAME => soname = Some(entry),
_ => {}
}
}
let strtab_offset = strtab_offset.expect("mandatory DT_STRTAB not present");
let strtab_size = strtab_size.expect("mandatory DT_STRSZ not present");
let dynstrtab = StringTable::new(mmap, strtab_offset, strtab_offset + strtab_size);
let get_str = |entry: &Dyn64<Endianness>| {
entry
.string(endian, dynstrtab)
.map(|bytes| core::str::from_utf8(bytes).expect("non utf-8 elf symbol name"))
};
let needed = needed
.into_iter()
.map(get_str)
.collect::<object::Result<Vec<_>>>()?;
let base = dirname(path);
let runpath = runpath
.map(get_str)
.transpose()?
.map(|value| value.replace("$ORIGIN", &base));
let soname = soname.map(get_str).transpose()?;
let jmprel = jmprel.unwrap_or_default();
let hash_table = hash_table.expect("either DT_GNU_HASH and/or DT_HASH mut be present");
Ok(Dynamic {
runpath,
got,
needed,
jmprel,
soname,
hash_table,
dynstrtab,
})
}
fn init_fini_arrays(elf: &ElfFile64, mmap_addr: usize) -> ((usize, usize), (usize, usize)) {
let mut init_array: (usize, usize) = (0, 0);
let mut fini_array: (usize, usize) = (0, 0);
for section in elf
.section_headers
.iter()
.filter(|s| s.sh_type == SHT_INIT_ARRAY || s.sh_type == SHT_FINI_ARRAY)
{
for section in elf.sections().filter(|s| {
matches!(
s.kind(),
SectionKind::Elf(SHT_INIT_ARRAY) | SectionKind::Elf(SHT_FINI_ARRAY)
)
}) {
let addr = if is_pie_enabled(elf) {
mmap_addr + section.vm_range().start
mmap_addr + section.address() as usize
} else {
section.vm_range().start
section.address() as usize
};
if section.sh_type == SHT_INIT_ARRAY {
init_array = (addr, section.sh_size as usize);
if let SectionKind::Elf(SHT_INIT_ARRAY) = section.kind() {
init_array = (addr, section.size() as usize);
} else {
fini_array = (addr, section.sh_size as usize);
fini_array = (addr, section.size() as usize);
}
}
@@ -599,8 +593,8 @@ impl Drop for DSO {
}
}
pub fn is_pie_enabled(elf: &Elf) -> bool {
elf.header.e_type == ET_DYN
pub fn is_pie_enabled(elf: &ElfFile64) -> bool {
elf.elf_header().e_type.get(elf.endian()) == ET_DYN
}
fn basename(path: &str) -> String {
+59 -57
View File
@@ -5,12 +5,19 @@ use alloc::{
sync::{Arc, Weak},
vec::Vec,
};
use object::{
elf::Rela64,
read::elf::{Rela, Sym},
Endianness,
};
use core::{
cell::RefCell,
mem::transmute,
ptr::{self, NonNull},
};
use goblin::elf::{dynamic::DT_STRTAB, program_header, reloc, sym::STT_TLS, Elf};
use goblin::elf::{program_header, reloc, sym::STT_TLS, Elf};
use crate::{
c_str::{CStr, CString},
@@ -20,10 +27,10 @@ use crate::{
fcntl, sys_mman,
unistd::F_OK,
},
io::{self, Read},
io,
ld_so::dso::SymbolBinding,
platform::{
types::{c_char, c_int, c_uint, c_void},
types::{c_int, c_uint, c_void},
Pal, Sys,
},
sync::rwlock::RwLock,
@@ -33,7 +40,7 @@ use super::{
access::accessible,
callbacks::LinkerCallbacks,
debug::{RTLDState, _dl_debug_state, _r_debug},
dso::{is_pie_enabled, DSO},
dso::DSO,
tcb::{Master, Tcb},
PATH_SEP,
};
@@ -401,7 +408,7 @@ impl Linker {
let parent_runpath = &self
.objects
.get(&ROOT_ID)
.and_then(|parent| parent.runpath.clone());
.and_then(|parent| parent.runpath().map(|path| path.to_string()));
Ok(ObjectHandle::new(self.load_object(
name,
@@ -484,10 +491,10 @@ impl Linker {
}
let _ = self.objects.remove(&obj.id).unwrap();
for dep in obj.dependencies.iter() {
for dep in obj.dependencies() {
self.unload(ObjectHandle::new(
self.objects
.get(self.name_to_object_id_map.get(dep).unwrap())
.get(self.name_to_object_id_map.get(*dep).unwrap())
.unwrap()
.clone(),
));
@@ -640,7 +647,7 @@ impl Linker {
// new_objects is already reversed.
for (i, obj) in new_objects.into_iter().enumerate() {
self.relocate(&obj, &objects_data[i], resolve)?;
self.relocate(&obj, objects_data[i], resolve)?;
self.run_init(&obj);
self.register_object(obj);
}
@@ -663,7 +670,7 @@ impl Linker {
base_addr: Option<usize>,
dlopened: bool,
new_objects: &mut Vec<Arc<DSO>>,
objects_data: &mut Vec<Vec<u8>>,
objects_data: &mut Vec<&[u8]>,
tcb_masters: &mut Vec<Master>,
// The object that caused this object to be loaded.
dependent: Option<&mut DSO>,
@@ -718,8 +725,12 @@ impl Linker {
self.next_tls_module_id += 1;
}
let runpath = obj.runpath.clone();
let dependencies = obj.dependencies.clone();
let runpath = obj.runpath().map(|rpath| rpath.to_string());
let dependencies = obj
.dependencies()
.iter()
.map(|dep| dep.to_string())
.collect::<Vec<_>>();
for dep_name in dependencies.iter() {
self.load_objects_recursive(
@@ -797,10 +808,9 @@ impl Linker {
Err(DlError::NotFound)
}
fn read_file(&self, path: &str) -> Result<Vec<u8>> {
fn read_file(&self, path: &str) -> Result<&'static [u8]> {
let debug = self.config.debug_flags.contains(DebugFlags::SEARCH);
let mut data = Vec::new();
let path_c = CString::new(path).map_err(|err| {
if debug {
eprintln!("[ld.so]: invalid path '{}': {}", path, err)
@@ -810,7 +820,7 @@ impl Linker {
})?;
let flags = fcntl::O_RDONLY | fcntl::O_CLOEXEC;
let mut file = File::open(CStr::borrow(&path_c), flags).map_err(|err| {
let file = File::open(CStr::borrow(&path_c), flags).map_err(|err| {
if debug {
eprintln!("[ld.so]: failed to open '{}': {}", path, err)
}
@@ -818,15 +828,24 @@ impl Linker {
DlError::NotFound
})?;
file.read_to_end(&mut data).map_err(|err| {
if debug {
eprintln!("[ld.so]: failed to read '{}': {}", path, err)
}
let mut stat = crate::header::sys_stat::stat::default();
unsafe {
Sys::fstat(file.fd, &mut stat).unwrap();
}
DlError::NotFound
})?;
let mmap_ptr = unsafe {
Sys::mmap(
ptr::null_mut(),
stat.st_size as usize,
sys_mman::PROT_READ,
sys_mman::MAP_PRIVATE,
file.fd,
0,
)
}
.unwrap();
Ok(data)
Ok(unsafe { core::slice::from_raw_parts(mmap_ptr as *const u8, stat.st_size as usize) })
}
/// Perform lazy relocations.
@@ -841,14 +860,14 @@ impl Linker {
}
for rel in elf.pltrelocs.iter() {
let ptr = if is_pie_enabled(elf) {
let ptr = if obj.pie {
(object_base_addr + rel.r_offset) as *mut u64
} else {
rel.r_offset as *mut u64
};
match (rel.r_type, resolve) {
(reloc::R_X86_64_JUMP_SLOT, Resolve::Lazy) if is_pie_enabled(elf) => unsafe {
(reloc::R_X86_64_JUMP_SLOT, Resolve::Lazy) if obj.pie => unsafe {
*ptr += object_base_addr;
},
@@ -954,7 +973,7 @@ impl Linker {
let a = rel.r_addend.unwrap_or(0) as usize;
let ptr = if is_pie_enabled(&elf) {
let ptr = if obj.pie {
(b + rel.r_offset as usize) as *mut u8
} else {
rel.r_offset as *mut u8
@@ -1047,7 +1066,7 @@ impl Linker {
}
unsafe {
let ptr = if is_pie_enabled(&elf) {
let ptr = if obj.pie {
mmap.as_ptr().add(vaddr)
} else {
vaddr as *const u8
@@ -1101,52 +1120,35 @@ extern "C" fn __plt_resolve_inner(obj: *const DSO, relocation_index: c_uint) ->
let obj = unsafe { &*obj };
let obj_base = obj.mmap.as_ptr() as usize;
let dynamic = obj.dynamic.as_ref().unwrap();
let jmprel = dynamic.info.jmprel;
let jmprel = obj.dynamic.jmprel;
let rela = unsafe {
&*((obj_base + jmprel) as *const reloc::reloc64::Rela).add(relocation_index as usize)
&*((obj_base + jmprel) as *const Rela64<Endianness>).add(relocation_index as usize)
};
assert_eq!(
reloc::reloc64::r_type(rela.r_info),
reloc::R_X86_64_JUMP_SLOT
);
let endian = Endianness::default();
assert_eq!(rela.r_type(endian, false), reloc::R_X86_64_JUMP_SLOT);
let sym = obj
.dynsyms
.get(reloc::reloc64::r_sym(rela.r_info) as usize)
.symbols
.symbol(rela.symbol(endian, false).unwrap())
.expect("symbol not found");
assert_ne!(sym.st_name, 0);
assert_ne!(sym.st_name(endian), 0);
let strtab_offset = dynamic
.dyns
.iter()
.find(|r#dyn| r#dyn.d_tag == DT_STRTAB)
.unwrap()
.d_val;
let name = core::str::from_utf8(obj.dynamic.dynstrtab.get(sym.st_name(endian)).unwrap())
.expect("non utf8 symbol name");
let name = unsafe {
CStr::from_ptr(
(strtab_offset + sym.st_name as u64 + if obj.pie { obj_base as u64 } else { 0u64 })
as *const c_char,
)
};
let resolved = resolve_sym(name.to_str().unwrap(), &[&GLOBAL_SCOPE.read(), &obj.scope])
.unwrap_or_else(|| panic!("symbol '{}' not found", name.to_str().unwrap()))
let resolved = resolve_sym(name, &[&GLOBAL_SCOPE.read(), &obj.scope])
.unwrap_or_else(|| panic!("symbol '{name}' not found"))
.as_ptr();
trace!(
"@plt: {} -> *mut {:#x}",
name.to_string_lossy(),
resolved as usize
);
trace!("@plt: {} -> *mut {:#x}", name, resolved as usize);
let ptr = if obj.pie {
(obj_base as u64 + rela.r_offset) as *mut u64
(obj_base as u64 + rela.r_offset(endian)) as *mut u64
} else {
rela.r_offset as *mut u64
rela.r_offset(endian) as *mut u64
};
unsafe { *ptr = resolved as u64 }
+20 -23
View File
@@ -80,31 +80,28 @@ pub fn static_init(sp: &'static Stack) {
// 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_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;
if ph.p_type == 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;
unsafe {
STATIC_TCB_MASTER.ptr = ph.p_vaddr as usize as *const u8;
STATIC_TCB_MASTER.len = ph.p_filesz as usize;
STATIC_TCB_MASTER.offset = valign;
unsafe {
STATIC_TCB_MASTER.ptr = ph.p_vaddr as usize as *const u8;
STATIC_TCB_MASTER.len = ph.p_filesz as usize;
STATIC_TCB_MASTER.offset = valign;
let tcb = Tcb::new(vsize).expect_notls("failed to allocate TCB");
tcb.masters_ptr = &mut STATIC_TCB_MASTER;
tcb.masters_len = mem::size_of::<Master>();
tcb.copy_masters()
.expect_notls("failed to copy TLS master data");
tcb.activate();
}
//TODO: Warning on multiple TLS sections?
return;
let tcb = Tcb::new(vsize).expect_notls("failed to allocate TCB");
tcb.masters_ptr = ptr::addr_of_mut!(STATIC_TCB_MASTER);
tcb.masters_len = mem::size_of::<Master>();
tcb.copy_masters()
.expect_notls("failed to copy TLS master data");
tcb.activate();
}
_ => (),
//TODO: Warning on multiple TLS sections?
return;
}
}
}
@@ -175,7 +172,7 @@ pub unsafe fn init(sp: &'static Stack) {
pub unsafe fn fini() {
if let Some(tcb) = Tcb::current() {
if !tcb.linker_ptr.is_null() {
let linker = (&*tcb.linker_ptr).lock();
let linker = (*tcb.linker_ptr).lock();
linker.fini();
}
}