feat(ld.so): switch to object crate

Signed-off-by: Anhad Singh <andypython@protonmail.com>
This commit is contained in:
Anhad Singh
2025-01-07 18:23:32 +11:00
parent e49072655c
commit aae27c5e0c
9 changed files with 611 additions and 537 deletions
Generated
+1
View File
@@ -277,6 +277,7 @@ dependencies = [
[[package]]
name = "object"
version = "0.36.7"
source = "git+https://gitlab.redox-os.org/andypython/object#7270e3f0d06e5ef4c2b80abc6166d31f4ddf4fea"
dependencies = [
"memchr",
]
+1 -1
View File
@@ -49,7 +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"
object = { version = "0.36.7", path = "../object", default-features = false, features = ["elf", "read_core"] }
object = { version = "0.36.7", git = "https://gitlab.redox-os.org/andypython/object", default-features = false, features = ["elf", "read_core"] }
[dependencies.dlmalloc]
path = "dlmalloc-rs"
+17 -15
View File
@@ -15,6 +15,8 @@ CARGOFLAGS+=--target=$(TARGET)
TARGET_HEADERS?=$(BUILD)/include
export CFLAGS=-I$(TARGET_HEADERS)
PROFILE?=release
HEADERS_UNPARSED=$(shell find src/header -mindepth 1 -maxdepth 1 -type d -not -name "_*" -printf "%f\n")
HEADERS_DEPS=$(shell find src/header -type f \( -name "cbindgen.toml" -o -name "*.rs" \))
#HEADERS=$(patsubst %,%.h,$(subst _,/,$(HEADERS_UNPARSED)))
@@ -42,7 +44,7 @@ ifeq ($(TARGET),x86_64-unknown-linux-gnu)
export LD=x86_64-linux-gnu-ld
export AR=x86_64-linux-gnu-ar
export NM=x86_64-linux-gnu-nm
export OBJCOPY=x86_64-linux-gnu-objcopy
export OBJCOPY=objcopy
export CPPFLAGS=
endif
@@ -118,23 +120,23 @@ install-headers: headers libs
cp -rv "$(TARGET_HEADERS)"/* "$(DESTDIR)/include"
libs: \
$(BUILD)/release/libc.a \
$(BUILD)/release/libc.so \
$(BUILD)/release/crt0.o \
$(BUILD)/release/crti.o \
$(BUILD)/release/crtn.o \
$(BUILD)/release/ld_so
$(BUILD)/$(PROFILE)/libc.a \
$(BUILD)/$(PROFILE)/libc.so \
$(BUILD)/$(PROFILE)/crt0.o \
$(BUILD)/$(PROFILE)/crti.o \
$(BUILD)/$(PROFILE)/crtn.o \
$(BUILD)/$(PROFILE)/ld_so
install-libs: headers libs
mkdir -pv "$(DESTDIR)/lib"
cp -v "$(BUILD)/release/libc.a" "$(DESTDIR)/lib"
cp -v "$(BUILD)/release/libc.so" "$(DESTDIR)/lib"
cp -v "$(BUILD)/$(PROFILE)/libc.a" "$(DESTDIR)/lib"
cp -v "$(BUILD)/$(PROFILE)/libc.so" "$(DESTDIR)/lib"
ln -vnfs libc.so "$(DESTDIR)/lib/libc.so.6"
cp -v "$(BUILD)/release/crt0.o" "$(DESTDIR)/lib"
cp -v "$(BUILD)/$(PROFILE)/crt0.o" "$(DESTDIR)/lib"
ln -vnfs crt0.o "$(DESTDIR)/lib/crt1.o"
cp -v "$(BUILD)/release/crti.o" "$(DESTDIR)/lib"
cp -v "$(BUILD)/release/crtn.o" "$(DESTDIR)/lib"
cp -v "$(BUILD)/release/ld_so" "$(DESTDIR)/lib/ld64.so.1"
cp -v "$(BUILD)/$(PROFILE)/crti.o" "$(DESTDIR)/lib"
cp -v "$(BUILD)/$(PROFILE)/crtn.o" "$(DESTDIR)/lib"
cp -v "$(BUILD)/$(PROFILE)/ld_so" "$(DESTDIR)/lib/ld64.so.1"
cp -v "$(BUILD)/openlibm/libopenlibm.a" "$(DESTDIR)/lib/libm.a"
# Empty libraries for dl, pthread, and rt
$(AR) -rcs "$(DESTDIR)/lib/libdl.a"
@@ -181,7 +183,7 @@ $(BUILD)/debug/libc.so: $(BUILD)/debug/librelibc.a $(BUILD)/openlibm/libopenlibm
$(CC) -nostdlib -shared -Wl,--allow-multiple-definition -Wl,--whole-archive $^ -Wl,--no-whole-archive -Wl,-soname,libc.so.6 -lgcc -o $@
$(BUILD)/debug/librelibc.a: $(SRC)
$(CARGO) rustc $(CARGOFLAGS) -- --emit link=$@ $(RUSTCFLAGS)
$(CARGO) rustc $(CARGOFLAGS) -- --emit link=$@ -g -C debug-assertions=no $(RUSTCFLAGS)
./renamesyms.sh "$@" "$(BUILD)/debug/deps/"
touch $@
@@ -198,7 +200,7 @@ $(BUILD)/debug/crtn.o: $(SRC)
touch $@
$(BUILD)/debug/ld_so.o: $(SRC)
$(CARGO) rustc --manifest-path ld_so/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
$(CARGO) rustc --manifest-path ld_so/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort -g -C debug-assertions=no $(RUSTCFLAGS)
touch $@
$(BUILD)/debug/ld_so: $(BUILD)/debug/ld_so.o $(BUILD)/debug/crti.o $(BUILD)/debug/libc.a $(BUILD)/debug/crtn.o
+460 -188
View File
@@ -3,18 +3,17 @@
//! * <https://www.akkadia.org/drepper/dsohowto.pdf>
use object::{
elf::{self, Dyn64, FileHeader64},
elf::{self, Sym64},
read::elf::{
Dyn as _, ElfFile64, FileHeader, GnuHashTable, HashTable as SysVHashTable, ProgramHeader,
Sym, SymbolTable, Version, VersionTable,
Dyn as _, GnuHashTable, HashTable as SysVHashTable, ProgramHeader as _, Rel as _,
Rela as _, Sym as _, Version, VersionTable,
},
Endianness, NativeEndian, Object, ObjectSection, ReadRef, SectionKind, StringTable,
SymbolIndex,
Endianness, NativeEndian, Object, StringTable, SymbolIndex,
};
use super::{
debug::_r_debug,
linker::{Scope, Symbol},
debug::{RTLDDebug, _r_debug},
linker::{Resolve, Scope, Symbol, __plt_resolve_trampoline, GLOBAL_SCOPE},
tcb::Master,
};
use crate::{
@@ -27,69 +26,152 @@ use alloc::{
vec::Vec,
};
use core::{
mem::transmute,
mem::size_of,
ptr::{self, NonNull},
slice,
};
#[cfg(target_pointer_width = "32")]
use goblin::elf32::{
header::ET_DYN,
program_header,
section_header::{SHT_FINI_ARRAY, SHT_INIT_ARRAY},
};
#[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>),
mod shim {
use object::{elf::*, read::elf::ElfFile64, NativeEndian};
pub type Dyn = Dyn64<NativeEndian>;
pub type Rel = Rel64<NativeEndian>;
pub type Rela = Rela64<NativeEndian>;
pub type Sym = Sym64<NativeEndian>;
pub type FileHeader = FileHeader64<NativeEndian>;
pub type ProgramHeader = ProgramHeader64<NativeEndian>;
pub type ElfFile<'a> = ElfFile64<'a, NativeEndian>;
}
impl<'a, Elf> HashTable<'a, Elf>
where
Elf: FileHeader,
{
#[cfg(target_pointer_width = "64")]
pub use shim::*;
#[cfg(target_pointer_width = "32")]
type Dyn = Dyn32<NativeEndian>;
enum HashTable<'a> {
Gnu(GnuHashTable<'a, FileHeader>),
Sysv(SysVHashTable<'a, FileHeader>),
}
impl<'a> HashTable<'a> {
/// Use the hash table to find the symbol table entry with the given name, hash, and version.
#[inline]
pub fn find<R: ReadRef<'a>>(
pub fn find(
&self,
endian: Elf::Endian,
name: &str,
version: Option<&Version<'_>>,
symbols: &SymbolTable<'a, Elf, R>,
versions: &VersionTable<'a, Elf>,
) -> Option<(SymbolIndex, &'a Elf::Sym)> {
symbols: &'a [Sym],
strings: StringTable<'a>,
versions: &VersionTable<'a, FileHeader>,
) -> Option<(SymbolIndex, &'a 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)
hash_table.find(
NativeEndian,
name,
hash,
version,
symbols,
strings,
versions,
)
}
Self::Sysv(hash_table) => {
let hash = elf::hash(name);
hash_table.find(endian, name, hash, version, symbols, versions)
hash_table.find(
NativeEndian,
name,
hash,
version,
symbols,
strings,
versions,
)
}
}
}
fn symbol_table_length(&self) -> usize {
match self {
Self::Gnu(hash_table) => hash_table
.symbol_table_length(NativeEndian)
.expect("empty GNU symbol hash table")
as usize,
Self::Sysv(hash_table) => hash_table.symbol_table_length() as usize,
}
}
}
pub(super) struct Dynamic<'a> {
type InitFn = unsafe extern "C" fn();
pub(super) struct Dynamic<'data> {
runpath: Option<String>,
got: Option<NonNull<usize>>,
needed: Vec<&'a str>,
needed: Vec<&'data str>,
pub(super) jmprel: usize,
hash_table: HashTable<'a, FileHeader64<Endianness>>,
pub(super) dynstrtab: StringTable<'a>,
soname: Option<&'a str>,
hash_table: HashTable<'data>,
pub(super) dynstrtab: StringTable<'data>,
soname: Option<&'data str>,
init_array: &'data [unsafe extern "C" fn()],
fini_array: &'data [unsafe extern "C" fn()],
rela: &'data [Rela],
rel: &'data [Rel],
symbols: &'data [Sym],
explicit_addend: bool,
pltrelsz: usize,
}
impl<'data> Dynamic<'data> {
pub fn symbol(&self, index: SymbolIndex) -> Option<&'data Sym> {
// Symbol table entry for index 0 is reserved.
assert!(index != SymbolIndex(0));
self.symbols.get(index.0)
}
fn symbol_name(&self, index: SymbolIndex) -> Option<&'data str> {
let sym = self.symbol(index)?;
let name = sym.name(NativeEndian, self.dynstrtab).ok()?;
Some(core::str::from_utf8(name).expect("non UTF-8 ELF symbol name"))
}
}
unsafe impl Send for Dynamic<'_> {}
unsafe impl Sync for Dynamic<'_> {}
#[derive(Debug)]
struct Relocation {
offset: usize,
addend: usize,
sym: SymbolIndex,
kind: u32,
}
impl From<&Rela> for Relocation {
fn from(reloc: &Rela) -> Self {
let is_mips64el = cfg!(all(target_arch = "mips64", target_endian = "little"));
Self {
offset: reloc.r_offset(NativeEndian) as usize,
addend: reloc.r_addend(NativeEndian) as usize,
sym: SymbolIndex(reloc.r_sym(NativeEndian, is_mips64el) as usize),
kind: reloc.r_type(NativeEndian, is_mips64el),
}
}
}
impl From<&Rel> for Relocation {
fn from(reloc: &Rel) -> Self {
Self {
offset: reloc.r_offset(NativeEndian) as usize,
addend: 0,
sym: SymbolIndex(reloc.r_sym(NativeEndian) as usize),
kind: reloc.r_type(NativeEndian),
}
}
}
#[derive(Debug, PartialEq)]
@@ -121,48 +203,30 @@ pub struct DSO {
pub mmap: &'static [u8],
pub global_syms: BTreeMap<String, Symbol>,
pub weak_syms: BTreeMap<String, Symbol>,
/// .init_array addr and len
pub init_array: (usize, usize),
/// .fini_array addr and len
pub fini_array: (usize, usize),
pub tls_module_id: usize,
pub tls_offset: usize,
pub(super) dynamic: Dynamic<'static>,
pub(super) symbols: SymbolTable<'static, FileHeader64<Endianness>>,
pub scope: Scope,
/// Position Independent Executable.
pub pie: bool,
}
unsafe impl Send for DSO {}
unsafe impl Sync for DSO {}
impl DSO {
pub fn new(
pub fn new<'a>(
path: &str,
data: &'static [u8],
data: &'a [u8],
base_addr: Option<usize>,
dlopened: bool,
id: usize,
tls_module_id: usize,
tls_offset: usize,
) -> Result<(DSO, Option<Master>)> {
let elf = ElfFile64::<object::Endianness>::parse(data).unwrap();
) -> object::Result<(DSO, Option<Master>, Vec<ProgramHeader>)> {
let elf = ElfFile::parse(data).unwrap();
let (mmap, tcb_master, dynamic) =
DSO::mmap_and_copy(path, &elf, data, base_addr, tls_offset).unwrap();
// 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 dynamic.soname {
Some(soname) => soname.to_string(),
_ => basename(path),
@@ -176,9 +240,7 @@ impl DSO {
} else {
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,
@@ -187,8 +249,6 @@ impl DSO {
mmap,
global_syms: BTreeMap::new(),
weak_syms: BTreeMap::new(),
init_array,
fini_array,
tls_module_id: if tcb_master.is_some() {
tls_module_id
} else {
@@ -197,12 +257,11 @@ impl DSO {
tls_offset,
pie: is_pie_enabled(&elf),
symbols,
dynamic,
scope: Scope::local(),
};
Ok((dso, tcb_master))
Ok((dso, tcb_master, elf.elf_program_headers().to_vec()))
}
/// Global Offset Table
@@ -222,15 +281,18 @@ impl DSO {
}
pub fn get_sym(&self, name: &str) -> Option<(Symbol, SymbolBinding)> {
let endian = Endianness::default();
let (_index, sym) = self.dynamic.hash_table.find(
endian,
let (_, sym) = self.dynamic.hash_table.find(
name,
None,
&self.symbols,
&self.dynamic.symbols,
self.dynamic.dynstrtab,
&VersionTable::default(),
)?;
if sym.st_shndx(NativeEndian) == elf::SHN_UNDEF {
return None;
}
Some((
Symbol {
base: if self.pie {
@@ -238,8 +300,8 @@ impl DSO {
} else {
0
},
value: sym.st_value(endian) as usize,
size: sym.st_size(endian) as usize,
value: sym.st_value(NativeEndian) as usize,
size: sym.st_size(NativeEndian) as usize,
sym_type: sym.st_type(),
},
// TODO(andypython): move this into [`Symbol`]
@@ -252,36 +314,24 @@ impl DSO {
}
pub fn run_init(&self) {
unsafe {
let (addr, size) = self.init_array;
for i in (0..size).step_by(8) {
let func = transmute::<usize, *const Option<extern "C" fn()>>(addr + i);
if let Some(f) = *func {
f();
}
}
for f in self.dynamic.init_array {
unsafe { f() }
}
}
pub fn run_fini(&self) {
unsafe {
let (addr, size) = self.fini_array;
for i in (0..size).step_by(8).rev() {
let func = transmute::<usize, *const Option<extern "C" fn()>>(addr + i);
if let Some(f) = *func {
f();
}
}
for f in self.dynamic.fini_array.iter().rev() {
unsafe { f() }
}
}
fn mmap_and_copy<'a>(
path: &str,
elf: &ElfFile64<'a>,
elf: &ElfFile<'a>,
data: &'a [u8],
base_addr: Option<usize>,
tls_offset: usize,
) -> object::Result<(&'static [u8], Option<Master>, Dynamic<'a>)> {
) -> object::Result<(&'static [u8], Option<Master>, Dynamic<'static>)> {
let endian = elf.endian();
trace!("# {}", path);
// data for struct LinkMap
@@ -296,10 +346,10 @@ impl DSO {
.next_multiple_of(ph.p_align(endian) as usize);
match ph.p_type(endian) {
program_header::PT_DYNAMIC => {
elf::PT_DYNAMIC => {
l_ld = ph.p_vaddr(endian);
}
program_header::PT_LOAD => {
elf::PT_LOAD => {
trace!(" load {:#x}, {:#x}: {:x?}", vaddr, vsize, ph);
if let Some(ref mut bounds) = bounds_opt {
if vaddr < bounds.0 {
@@ -316,9 +366,7 @@ impl DSO {
}
}
bounds_opt
.ok_or(Error::Malformed(
"Unable to find PT_LOAD section".to_string(),
))
.ok_or("Unable to find PT_LOAD section".to_string())
.unwrap()
};
trace!(" bounds {:#x}, {:#x}", bounds.0, bounds.1);
@@ -349,7 +397,7 @@ impl DSO {
-1,
0,
)
.map_err(|e| Error::Malformed(format!("failed to map {}. errno: {}", path, e.0)))
.map_err(|e| format!("failed to map {}. errno: {}", path, e.0))
.unwrap();
if !(start as *mut c_void).is_null() {
@@ -368,12 +416,6 @@ impl DSO {
let skip_load_segment_copy = base_addr.is_some();
let mut tcb_master = None;
let base = if is_pie_enabled(elf) {
mmap.as_ptr() as usize
} else {
0
};
// Copy data
let mut dynamic = None;
for ph in elf.elf_program_headers() {
@@ -383,7 +425,7 @@ impl DSO {
.next_multiple_of(ph.p_align(endian) as usize);
match ph.p_type(endian) {
program_header::PT_LOAD => {
elf::PT_LOAD => {
if skip_load_segment_copy {
continue;
}
@@ -393,13 +435,7 @@ impl DSO {
let range = offset..(offset + size as usize);
match data.get(range.clone()) {
Some(some) => some,
None => {
return Err(Error::Malformed(format!(
"failed to read {:x?}",
range
)))
.unwrap()
}
None => return Err(format!("failed to read {:x?}", range)).unwrap(),
}
};
@@ -414,11 +450,7 @@ impl DSO {
match mmap.get_mut(range.clone()) {
Some(some) => some,
None => {
return Err(Error::Malformed(format!(
"failed to write {:x?}",
range
)))
.unwrap();
return Err(format!("failed to write {:x?}", range)).unwrap();
}
}
};
@@ -431,7 +463,7 @@ impl DSO {
);
mmap_data.copy_from_slice(obj_data);
}
program_header::PT_TLS => {
elf::PT_TLS => {
let ptr = unsafe {
if is_pie_enabled(elf) {
mmap.as_ptr().add(ph.p_vaddr(endian) as usize)
@@ -447,77 +479,131 @@ impl DSO {
trace!(" tcb master {:x?}", tcb_master);
}
program_header::PT_DYNAMIC => {
dynamic = Some(ph.dynamic(endian, data).unwrap().unwrap())
}
elf::PT_DYNAMIC => dynamic = Some((ph, ph.dynamic(endian, data).unwrap().unwrap())),
_ => (),
}
}
Ok((
mmap,
tcb_master,
Self::parse_dynamic(path, base, endian, mmap, dynamic.unwrap())?,
))
let (parsed_dynamic, debug) =
Self::parse_dynamic(path, mmap, is_pie_enabled(elf), dynamic.unwrap())?;
if let Some(i) = debug {
// FIXME: cleanup
let (ph, _) = dynamic.unwrap();
let vaddr = ph.p_vaddr(endian) as usize;
let bytes: [u8; size_of::<Dyn>() / 2] =
unsafe { core::mem::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_ptr().cast_mut() as usize
};
unsafe {
ptr::copy_nonoverlapping(
bytes.as_ptr(),
mmap.as_ptr().cast_mut().add(start),
bytes.len(),
);
}
}
Ok((mmap, tcb_master, parsed_dynamic))
}
fn parse_dynamic<'a>(
path: &str,
base: usize,
endian: Endianness,
mmap: &'static [u8],
entries: &'a [Dyn64<Endianness>],
) -> object::Result<Dynamic<'a>> {
mmap: &'a [u8],
is_pie: bool,
(_, entries): (&ProgramHeader, &[Dyn]),
) -> object::Result<(Dynamic<'a>, Option<usize>)> {
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 explicit_addend = None;
let mut pltrelsz = None;
let mut debug = None;
let mut symtab_ptr = None;
let (mut rel_ptr, mut rel_size) = (None, None);
let (mut strtab_offset, mut strtab_size) = (None, None);
let (mut init_array_ptr, mut init_array_len) = (None, None);
let (mut fini_array_ptr, mut fini_array_len) = (None, None);
let (mut rela_offset, mut rela_size) = (None, None);
for (i, entry) in entries.iter().enumerate() {
let val = entry.d_val(endian);
let tag = entry.d_tag(endian) as u32;
let val = entry.d_val(NativeEndian);
let relative_idx = val - if is_pie { 0 } else { mmap.as_ptr() as u64 };
let ptr = (val + if is_pie { mmap.as_ptr() as u64 } else { 0 }) as *const u8;
let tag = entry.d_tag(NativeEndian) 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_DEBUG => debug = Some(i),
// {Gnu,SysV}HashTable::parse()
//
// > The header does not contain a length field, and so all of
// > `data` will be used as the hash table values. It does not
// > matter if this is longer than needed...
elf::DT_GNU_HASH => {
let value = GnuHashTable::parse(endian, &mmap[val as usize..])?;
let value = GnuHashTable::parse(NativeEndian, &mmap[relative_idx as usize..])?;
hash_table = Some(HashTable::Gnu(value));
}
// XXX: Both GNU_HASH and HASH may be present, we give priority
// to GNU_HASH as it is significantly faster.
elf::DT_HASH if hash_table.is_none() => {
hash_table = Some(HashTable::Sysv(SysVHashTable::parse(
endian,
&mmap[val as usize..],
)?));
let value = SysVHashTable::parse(NativeEndian, &mmap[relative_idx as usize..])?;
hash_table = Some(HashTable::Sysv(value));
}
elf::DT_PLTGOT => {
let ptr = NonNull::new(val as *mut usize).expect("DT_PLTGOT is NULL");
got = Some(unsafe { ptr.byte_add(base) });
got = Some(NonNull::new(ptr as *mut usize).expect("DT_PLTGOT is NULL"));
}
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_JMPREL => jmprel = Some(ptr as usize),
elf::DT_RUNPATH => runpath = Some(entry), // FIXME(andypython): rpath
elf::DT_STRTAB => strtab_offset = Some(relative_idx),
elf::DT_STRSZ => strtab_size = Some(val),
elf::DT_SONAME => soname = Some(entry),
elf::DT_RELA => rela_offset = Some(ptr.cast::<Rela>()),
elf::DT_RELASZ => rela_size = Some(val as usize / size_of::<Rela>()),
elf::DT_RELAENT => {
assert_eq!(val, size_of::<elf::Rela64<Endianness>>() as u64)
}
elf::DT_REL => rel_ptr = Some(ptr.cast::<Rel>()),
elf::DT_RELSZ => rel_size = Some(val as usize / size_of::<Rel>()),
elf::DT_RELENT => {
assert_eq!(val, size_of::<elf::Rel64<Endianness>>() as u64)
}
elf::DT_PLTREL => {
let val = val as u32;
if val == elf::DT_RELA {
explicit_addend = Some(true);
} else {
assert_eq!(val, elf::DT_REL);
explicit_addend = Some(false);
}
}
elf::DT_PLTRELSZ => pltrelsz = Some(val as usize),
elf::DT_INIT_ARRAY if val != 0 => init_array_ptr = Some(ptr.cast::<InitFn>()),
elf::DT_INIT_ARRAYSZ => init_array_len = Some(val as usize / size_of::<InitFn>()),
elf::DT_FINI_ARRAY if val != 0 => fini_array_ptr = Some(ptr.cast::<InitFn>()),
elf::DT_FINI_ARRAYSZ => fini_array_len = Some(val as usize / size_of::<InitFn>()),
elf::DT_SYMTAB => symtab_ptr = Some(ptr as *const Sym),
elf::DT_SYMENT => {
assert_eq!(val, size_of::<Sym64<NativeEndian>>() as u64);
}
_ => {}
}
}
@@ -525,14 +611,24 @@ impl DSO {
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 dynstrtab = StringTable::new(&*mmap, strtab_offset, strtab_offset + strtab_size);
let get_str = |entry: &Dyn64<Endianness>| {
let get_str = |entry: &Dyn| {
entry
.string(endian, dynstrtab)
.string(NativeEndian, dynstrtab)
.map(|bytes| core::str::from_utf8(bytes).expect("non utf-8 elf symbol name"))
};
unsafe fn get_array<'a, T>(ptr: Option<*const T>, len: Option<usize>) -> &'a [T] {
if let Some(ptr) = ptr {
let len = len.expect("dynamic entry was present without it's corresponding size");
core::slice::from_raw_parts(ptr, len)
} else {
assert!(len.is_none());
&[]
}
}
let needed = needed
.into_iter()
.map(get_str)
@@ -550,39 +646,215 @@ impl DSO {
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,
})
let init_array = unsafe { get_array(init_array_ptr, init_array_len) };
let fini_array = unsafe { get_array(fini_array_ptr, fini_array_len) };
let rela = unsafe { get_array(rela_offset, rela_size) };
let rel = unsafe { get_array(rel_ptr, rel_size) };
Ok((
Dynamic {
symbols: unsafe { get_array(symtab_ptr, Some(hash_table.symbol_table_length())) },
runpath,
got,
needed,
jmprel,
soname,
hash_table,
dynstrtab,
init_array,
fini_array,
rela,
rel,
explicit_addend: explicit_addend.unwrap_or_default(),
pltrelsz: pltrelsz.unwrap_or_default(),
},
debug,
))
}
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.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.address() as usize
fn static_relocate(&self, reloc: Relocation) -> object::Result<()> {
let b = self.mmap.as_ptr() as usize;
let sym = if reloc.sym.0 > 0 {
let name = self.dynamic.symbol_name(reloc.sym).unwrap();
GLOBAL_SCOPE
.read()
.get_sym(name)
.or_else(|| self.scope.get_sym(name))
.map(|(sym, _, obj)| (sym, obj.tls_offset))
} else {
None
};
let (s, t) = sym
.as_ref()
.map(|(sym, t)| (sym.as_ptr() as usize, *t))
.unwrap_or((0, 0));
let a = reloc.addend;
let ptr = if self.pie {
(b + reloc.offset) as *mut u8
} else {
reloc.offset as *mut u8
};
let set_u64 = |value| unsafe {
*(ptr as *mut u64) = value;
};
match reloc.kind {
elf::R_X86_64_64 => set_u64((s + a) as u64),
elf::R_X86_64_DTPMOD64 => set_u64(self.tls_module_id as u64),
elf::R_X86_64_DTPOFF64 => {
if s != 0 {
set_u64((s - b) as u64);
} else {
set_u64(s as u64);
}
}
elf::R_X86_64_GLOB_DAT => set_u64(s as u64),
elf::R_X86_64_RELATIVE => set_u64((b + a) as u64),
elf::R_X86_64_TPOFF64 => {
if reloc.sym.0 > 0 {
let (sym, _) = sym
.as_ref()
.expect("R_X86_64_TPOFF64 called without valid symbol");
set_u64((sym.value + a).wrapping_sub(t) as u64);
} else {
set_u64(a.wrapping_sub(t) as u64);
}
}
elf::R_X86_64_IRELATIVE => unsafe {
let f: unsafe extern "C" fn() -> u64 = core::mem::transmute(b + a);
set_u64(f());
},
elf::R_X86_64_COPY => unsafe {
let (sym, _) = sym
.as_ref()
.expect("R_X86_64_COPY called without valid symbol");
ptr::copy_nonoverlapping(sym.as_ptr() as *const u8, ptr, sym.size);
},
_ => unimplemented!("relocation type {:#x}", reloc.kind),
}
Ok(())
}
fn lazy_relocate(&self, resolve: Resolve) -> object::Result<()> {
let Some(got) = self.got() else {
assert_eq!(self.dynamic.jmprel, 0);
return Ok(());
};
let object_base_addr = self.mmap.as_ptr() as usize;
let jmprel = self.dynamic.jmprel;
let pltrelsz = self.dynamic.pltrelsz;
unsafe {
got.add(1).write(core::ptr::addr_of!(*self) as usize);
got.add(2).write(__plt_resolve_trampoline as usize);
}
let relsz = if self.dynamic.explicit_addend {
size_of::<Rela>()
} else {
size_of::<Rel>()
};
for addr in (jmprel..(jmprel + pltrelsz)).step_by(relsz) {
let reloc: Relocation = if self.dynamic.explicit_addend {
unsafe { &*(addr as *const Rela) }.into()
} else {
section.address() as usize
unsafe { &*(addr as *const Rel) }.into()
};
if let SectionKind::Elf(SHT_INIT_ARRAY) = section.kind() {
init_array = (addr, section.size() as usize);
let ptr = if self.pie {
(object_base_addr + reloc.offset) as *mut usize
} else {
fini_array = (addr, section.size() as usize);
reloc.offset as *mut usize
};
match (reloc.kind, resolve) {
(elf::R_X86_64_JUMP_SLOT, Resolve::Lazy) if self.pie => unsafe {
*ptr += object_base_addr;
},
(elf::R_X86_64_JUMP_SLOT, Resolve::Lazy) => {
// NOP.
}
(elf::R_X86_64_JUMP_SLOT, Resolve::Now) => {
let name = self.dynamic.symbol_name(reloc.sym).unwrap();
let resolved = GLOBAL_SCOPE
.read()
.get_sym(name)
.or_else(|| self.scope.get_sym(name))
.map(|(sym, _, _)| sym.as_ptr() as usize)
.expect("unresolved symbol");
unsafe {
*ptr = resolved + reloc.addend;
}
}
_ => {
unimplemented!("relocation type {:#x}", reloc.kind)
}
}
}
(init_array, fini_array)
Ok(())
}
pub fn relocate(&self, ph: &[ProgramHeader], resolve: Resolve) -> object::Result<()> {
self.dynamic
.rela
.iter()
.try_for_each(|reloc| self.static_relocate(reloc.into()))?;
self.dynamic
.rel
.iter()
.try_for_each(|reloc| self.static_relocate(reloc.into()))?;
self.lazy_relocate(resolve)?;
// Protect pages
for ph in ph
.iter()
.filter(|ph| ph.p_type(NativeEndian) == elf::PT_LOAD)
{
let voff = ph.p_vaddr(NativeEndian) % ph.p_align(NativeEndian);
let vaddr = (ph.p_vaddr(NativeEndian) - voff) as usize;
let vsize = ((ph.p_memsz(NativeEndian) + voff) as usize)
.next_multiple_of(ph.p_align(NativeEndian) as usize);
let mut prot = 0;
if ph.p_flags(NativeEndian) & elf::PF_R == elf::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(NativeEndian) & elf::PF_X == elf::PF_X {
prot |= sys_mman::PROT_EXEC;
} else if ph.p_flags(NativeEndian) & elf::PF_W == elf::PF_W {
prot |= sys_mman::PROT_WRITE;
}
unsafe {
let ptr = if self.pie {
self.mmap.as_ptr().add(vaddr)
} else {
vaddr as *const u8
};
trace!(" prot {:#x}, {:#x}: {:p}, {:#x}", vaddr, vsize, ptr, prot);
Sys::mprotect(ptr as *mut c_void, vsize, prot).expect("[ld.so]: mprotect failed");
}
}
Ok(())
}
}
@@ -593,8 +865,8 @@ impl Drop for DSO {
}
}
pub fn is_pie_enabled(elf: &ElfFile64) -> bool {
elf.elf_header().e_type.get(elf.endian()) == ET_DYN
pub fn is_pie_enabled(elf: &ElfFile) -> bool {
elf.elf_header().e_type.get(elf.endian()) == elf::ET_DYN
}
fn basename(path: &str) -> String {
+76 -306
View File
@@ -6,19 +6,16 @@ use alloc::{
vec::Vec,
};
use object::{
elf::Rela64,
read::elf::{Rela, Sym},
Endianness,
elf,
read::elf::{Rela as _, Sym},
NativeEndian,
};
use core::{
cell::RefCell,
mem::transmute,
ptr::{self, NonNull},
};
use goblin::elf::{program_header, reloc, sym::STT_TLS, Elf};
use crate::{
c_str::{CStr, CString},
error::Errno,
@@ -27,7 +24,6 @@ use crate::{
fcntl, sys_mman,
unistd::F_OK,
},
io,
ld_so::dso::SymbolBinding,
platform::{
types::{c_int, c_uint, c_void},
@@ -40,7 +36,7 @@ use super::{
access::accessible,
callbacks::LinkerCallbacks,
debug::{RTLDState, _dl_debug_state, _r_debug},
dso::DSO,
dso::{ProgramHeader, Rela, DSO},
tcb::{Master, Tcb},
PATH_SEP,
};
@@ -53,6 +49,8 @@ pub enum DlError {
Malformed,
/// Invalid DSO handle.
InvalidHandle,
/// Out of memory.
Oom,
}
impl DlError {
@@ -70,31 +68,56 @@ impl DlError {
DlError::InvalidHandle => {
c_str!("Invalid DSO handle. Set `LD_DEBUG=all` for more information.")
}
DlError::Oom => c_str!("Out of memory."),
}
}
}
pub type Result<T> = core::result::Result<T, DlError>;
static GLOBAL_SCOPE: RwLock<Scope> = RwLock::new(Scope::global());
pub(super) static GLOBAL_SCOPE: RwLock<Scope> = RwLock::new(Scope::global());
/// Same as [`crate::fs::File`], but does not touch [`crate::platform::ERRNO`] as the dynamic
/// linker does not have thread-local storage.
struct File {
fd: c_int,
struct MmapFile {
fd: i32,
ptr: *mut c_void,
size: usize,
}
impl File {
pub fn open(path: CStr, oflag: c_int) -> core::result::Result<Self, Errno> {
Ok(Self {
fd: Sys::open(path, oflag, 0)?,
})
impl MmapFile {
fn open(path: CStr, oflag: c_int) -> core::result::Result<Self, Errno> {
let fd = Sys::open(path, oflag, 0 /* mode */)?;
let mut stat = crate::header::sys_stat::stat::default();
unsafe {
Sys::fstat(fd, &mut stat)?;
}
let size = stat.st_size as usize;
let ptr = unsafe {
Sys::mmap(
ptr::null_mut(),
size,
sys_mman::PROT_READ,
sys_mman::MAP_PRIVATE,
fd,
0,
)
}?;
Ok(Self { fd, ptr, size })
}
fn data(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.ptr.cast::<u8>(), self.size) }
}
}
impl io::Read for File {
fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, io::Error> {
Sys::read(self.fd, buf).map_err(|err| io::Error::from_raw_os_error(err.0))
impl Drop for MmapFile {
fn drop(&mut self) {
unsafe {
Sys::munmap(self.ptr, self.size).unwrap();
Sys::close(self.fd).unwrap();
}
}
}
@@ -188,7 +211,7 @@ impl Scope {
}
}
fn get_sym(&self, name: &str) -> Option<(Symbol, SymbolBinding, Arc<DSO>)> {
pub(super) fn get_sym(&self, name: &str) -> Option<(Symbol, SymbolBinding, Arc<DSO>)> {
let mut res = None;
let get_sym = |obj: Arc<DSO>| {
@@ -452,7 +475,7 @@ impl Linker {
}
.get_sym(name)
.map(|(symbol, _, obj)| {
if symbol.sym_type != STT_TLS {
if symbol.sym_type != elf::STT_TLS {
symbol.as_ptr()
} else {
let mut tls_index = dl_tls_index {
@@ -554,7 +577,7 @@ impl Linker {
use redox_rt::signal::tmp_disable_signals;
let old_tcb = Tcb::current().expect("failed to get bootstrap TCB");
let new_tcb = Tcb::new(self.tls_size).map_err(|_| DlError::Malformed)?; // This actually allocates TCB, TLS and ABI page.
let new_tcb = Tcb::new(self.tls_size)?; // This actually allocates TCB, TLS and ABI page.
// Stash
let new_tls_end = new_tcb.generic.tls_end;
@@ -595,7 +618,7 @@ impl Linker {
};
#[cfg(not(target_os = "redox"))]
let tcb = Tcb::new(self.tls_size).map_err(|_| DlError::Malformed)?;
let tcb = Tcb::new(self.tls_size)?;
// We are now loading the main program or its dependencies. The TLS for all initially
// loaded objects reside in the static TLS block. Depending on the architecture, the
@@ -647,7 +670,7 @@ impl Linker {
// new_objects is already reversed.
for (i, obj) in new_objects.into_iter().enumerate() {
self.relocate(&obj, objects_data[i], resolve)?;
obj.relocate(&objects_data[i], resolve).unwrap();
self.run_init(&obj);
self.register_object(obj);
}
@@ -663,14 +686,14 @@ impl Linker {
self.objects.insert(obj.id, obj);
}
fn load_objects_recursive(
fn load_objects_recursive<'a>(
&mut self,
name: &str,
parent_runpath: &Option<String>,
base_addr: Option<usize>,
dlopened: bool,
new_objects: &mut Vec<Arc<DSO>>,
objects_data: &mut Vec<&[u8]>,
objects_data: &mut Vec<Vec<ProgramHeader>>,
tcb_masters: &mut Vec<Master>,
// The object that caused this object to be loaded.
dependent: Option<&mut DSO>,
@@ -688,10 +711,11 @@ impl Linker {
let debug = self.config.debug_flags.contains(DebugFlags::LOAD);
let path = self.search_object(name, parent_runpath)?;
let data = self.read_file(&path)?;
let (mut obj, tcb_master) = DSO::new(
let file = self.read_file(&path)?;
let data = file.data();
let (mut obj, tcb_master, elf) = DSO::new(
&path,
&data,
data,
base_addr,
dlopened,
self.next_object_id,
@@ -760,7 +784,7 @@ impl Linker {
GLOBAL_SCOPE.write().add(&obj);
}
objects_data.push(data);
objects_data.push(elf);
new_objects.push(obj.clone());
Ok(obj)
@@ -808,7 +832,7 @@ impl Linker {
Err(DlError::NotFound)
}
fn read_file(&self, path: &str) -> Result<&'static [u8]> {
fn read_file(&self, path: &str) -> Result<MmapFile> {
let debug = self.config.debug_flags.contains(DebugFlags::SEARCH);
let path_c = CString::new(path).map_err(|err| {
@@ -820,7 +844,7 @@ impl Linker {
})?;
let flags = fcntl::O_RDONLY | fcntl::O_CLOEXEC;
let file = File::open(CStr::borrow(&path_c), flags).map_err(|err| {
let file = MmapFile::open(CStr::borrow(&path_c), flags).map_err(|err| {
if debug {
eprintln!("[ld.so]: failed to open '{}': {}", path, err)
}
@@ -828,261 +852,7 @@ impl Linker {
DlError::NotFound
})?;
let mut stat = crate::header::sys_stat::stat::default();
unsafe {
Sys::fstat(file.fd, &mut stat).unwrap();
}
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(unsafe { core::slice::from_raw_parts(mmap_ptr as *const u8, stat.st_size as usize) })
}
/// Perform lazy relocations.
fn lazy_relocate(&self, obj: &Arc<DSO>, elf: &Elf, resolve: Resolve) -> Result<()> {
let Some(got) = obj.got() else { return Ok(()) };
let object_base_addr = obj.mmap.as_ptr() as u64;
let debug = self.config.debug_flags.contains(DebugFlags::LOAD);
unsafe {
got.add(1).write(Arc::as_ptr(obj) as usize);
got.add(2).write(__plt_resolve_trampoline as usize);
}
for rel in elf.pltrelocs.iter() {
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 obj.pie => unsafe {
*ptr += object_base_addr;
},
(reloc::R_X86_64_JUMP_SLOT, Resolve::Lazy) => {
// NOP.
}
(reloc::R_X86_64_JUMP_SLOT, Resolve::Now) => {
let sym = elf.dynsyms.get(rel.r_sym).ok_or_else(|| {
if debug {
eprintln!("missing symbol for relocation {:?}", rel)
}
DlError::Malformed
})?;
let name = elf.dynstrtab.get_at(sym.st_name).ok_or_else(|| {
if debug {
eprintln!("missing name for symbol {:?}", sym)
}
DlError::Malformed
})?;
let resolved = GLOBAL_SCOPE
.read()
.get_sym(name)
.or_else(|| obj.scope.get_sym(name))
.map(|(sym, _, _)| sym.as_ptr())
.expect("unresolved symbol");
let addend = rel.r_addend.unwrap_or(0) as u64;
unsafe {
*ptr = resolved as u64 + addend;
}
}
_ => todo!("unsupported relocation type {:?}", rel.r_type),
}
}
Ok(())
}
fn relocate(&self, obj: &Arc<DSO>, object_data: &[u8], resolve: Resolve) -> Result<()> {
// Perform static relocations.
let elf = Elf::parse(object_data).or(Err(DlError::Malformed))?;
let debug = self.config.debug_flags.contains(DebugFlags::LOAD); // FIXME
trace!("link {}", obj.name);
let mmap = &obj.mmap;
let b = mmap.as_ptr() as usize;
// Relocate
for rel in elf.dynrelas.iter().chain(elf.dynrels.iter()) {
trace!(
" rel {}: {:x?}",
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_else(|| {
if debug {
eprintln!("[ld.so]: missing symbol for relocation {:?}", rel)
}
DlError::Malformed
})?;
let name = elf.dynstrtab.get_at(sym.st_name).ok_or_else(|| {
if debug {
eprintln!("[ld.so]: missing name for symbol {:?}", sym)
}
DlError::Malformed
})?;
let symbol = GLOBAL_SCOPE
.read()
.get_sym(name)
.or_else(|| obj.scope.get_sym(name))
.map(|(sym, _, obj)| (sym, obj.tls_offset));
// TODO: below doesn't work because of missing __preinit_array_{start,end} and __init_array_{start,end} symbols in dynamic linked programs
// TODO: found := symbol.is_some()
/*
if !found {
return Err(Error::Malformed(format!("missing symbol for name {}", name)));
}
*/
symbol
} else {
None
};
let (s, t) = symbol
.as_ref()
.map(|(sym, t)| (sym.as_ptr() as usize, *t))
.unwrap_or((0, 0));
let a = rel.r_addend.unwrap_or(0) as usize;
let ptr = if obj.pie {
(b + rel.r_offset as usize) as *mut u8
} else {
rel.r_offset as *mut u8
};
let set_u64 = |value| {
trace!(
" set_u64 r_type={} value={:#x} ptr={:p} base={:#x} mmap_len={:#x} a={:#x}",
rel.r_type,
value,
ptr,
b,
mmap.len(),
a
);
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(obj.tls_module_id as u64);
}
reloc::R_X86_64_DTPOFF64 => {
if s != 0 {
set_u64((s - b) as u64);
} else {
set_u64(s 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 => {
if rel.r_sym > 0 {
let (sym, _) = symbol
.as_ref()
.expect("R_X86_64_TPOFF64 called without valid symbol");
set_u64((sym.value + a).wrapping_sub(t) as u64);
} else {
set_u64(a.wrapping_sub(t) as u64);
}
}
reloc::R_X86_64_IRELATIVE => unsafe {
let f: unsafe extern "C" fn() -> u64 = transmute(b + a);
set_u64(f());
},
reloc::R_X86_64_COPY => unsafe {
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);
},
_ => {
panic!(
" {} unsupported",
reloc::r_to_str(rel.r_type, elf.header.e_machine)
);
}
}
}
self.lazy_relocate(obj, &elf, resolve)?;
// Protect pages
for ph in elf
.program_headers
.iter()
.filter(|ph| ph.p_type == program_header::PT_LOAD)
{
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 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;
}
unsafe {
let ptr = if obj.pie {
mmap.as_ptr().add(vaddr)
} else {
vaddr as *const u8
};
trace!(" prot {:#x}, {:#x}: {:p}, {:#x}", vaddr, vsize, ptr, prot);
Sys::mprotect(ptr as *mut c_void, vsize, prot).map_err(|err| {
if debug {
eprintln!("[ld.so]: failed to mprotect: {}", err)
}
DlError::Malformed
})?;
}
}
Ok(())
Ok(file)
}
fn run_init(&self, obj: &DSO) {
@@ -1122,41 +892,41 @@ extern "C" fn __plt_resolve_inner(obj: *const DSO, relocation_index: c_uint) ->
let obj_base = obj.mmap.as_ptr() as usize;
let jmprel = obj.dynamic.jmprel;
let rela = unsafe {
&*((obj_base + jmprel) as *const Rela64<Endianness>).add(relocation_index as usize)
};
let endian = Endianness::default();
assert_eq!(rela.r_type(endian, false), reloc::R_X86_64_JUMP_SLOT);
let rela = unsafe { &*(jmprel as *const Rela).add(relocation_index as usize) };
assert_eq!(rela.r_type(NativeEndian, false), elf::R_X86_64_JUMP_SLOT);
let sym = obj
.symbols
.symbol(rela.symbol(endian, false).unwrap())
.dynamic
.symbol(rela.symbol(NativeEndian, false).unwrap())
.expect("symbol not found");
assert_ne!(sym.st_name(endian), 0);
assert_ne!(sym.st_name(NativeEndian), 0);
let name = core::str::from_utf8(obj.dynamic.dynstrtab.get(sym.st_name(endian)).unwrap())
.expect("non utf8 symbol name");
let name = core::str::from_utf8(
obj.dynamic
.dynstrtab
.get(sym.st_name(NativeEndian))
.unwrap(),
)
.expect("non utf8 symbol name");
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, resolved as usize);
let ptr = if obj.pie {
(obj_base as u64 + rela.r_offset(endian)) as *mut u64
(obj_base as u64 + rela.r_offset(NativeEndian)) as *mut u64
} else {
rela.r_offset(endian) as *mut u64
rela.r_offset(NativeEndian) as *mut u64
};
trace!("@plt: {} -> *mut {:p}", name, ptr);
unsafe { *ptr = resolved as u64 }
resolved
}
extern "C" {
fn __plt_resolve_trampoline() -> usize;
pub(super) fn __plt_resolve_trampoline() -> usize;
}
#[cfg(target_arch = "x86_64")]
+40 -17
View File
@@ -5,7 +5,11 @@
#![warn(warnings, unused_variables)]
use core::{mem, ptr};
use goblin::elf::program_header::{self, program_header32, program_header64, ProgramHeader};
use object::{
elf::{self, ProgramHeader32, ProgramHeader64},
read::elf::ProgramHeader,
Endianness,
};
use self::tcb::{Master, Tcb};
use crate::{
@@ -38,6 +42,9 @@ static mut STATIC_TCB_MASTER: Master = Master {
#[inline(never)]
pub fn static_init(sp: &'static Stack) {
const SIZEOF_PHDR64: usize = mem::size_of::<ProgramHeader64<Endianness>>();
const SIZEOF_PHDR32: usize = mem::size_of::<ProgramHeader32<Endianness>>();
let mut phdr_opt = None;
let mut phent_opt = None;
let mut phnum_opt = None;
@@ -65,31 +72,47 @@ pub fn static_init(sp: &'static Stack) {
for i in 0..phnum {
let ph_addr = phdr + phent * i;
let ph: ProgramHeader = match phent {
program_header32::SIZEOF_PHDR => {
unsafe { *(ph_addr as *const program_header32::ProgramHeader) }.into()
}
program_header64::SIZEOF_PHDR => {
unsafe { *(ph_addr as *const program_header64::ProgramHeader) }.into()
}
let endian = Endianness::default();
let (p_align, p_filesz, p_memsz, p_type, p_vaddr) = match phent {
SIZEOF_PHDR64 => unsafe {
let ph = &*(ph_addr as *const ProgramHeader64<Endianness>);
(
ph.p_align(endian) as usize,
ph.p_filesz(endian) as usize,
ph.p_memsz(endian) as usize,
ph.p_type(endian),
ph.p_vaddr(endian) as usize,
)
},
SIZEOF_PHDR32 => unsafe {
let ph = &*(ph_addr as *const ProgramHeader32<Endianness>);
(
ph.p_align(endian) as usize,
ph.p_filesz(endian) as usize,
ph.p_memsz(endian) as usize,
ph.p_type(endian),
ph.p_vaddr(endian) as usize,
)
},
_ => panic_notls(format_args!("unknown AT_PHENT size {}", phent)),
};
let page_size = Sys::getpagesize();
let voff = ph.p_vaddr as usize % page_size;
let voff = p_vaddr % 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 vsize = ((p_memsz + voff + page_size - 1) / page_size) * page_size;
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
if p_type == elf::PT_TLS {
let valign = if p_align > 0 {
((p_memsz + (p_align - 1)) / p_align) * p_align
} else {
ph.p_memsz
} as usize;
p_memsz
};
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.ptr = p_vaddr as *const u8;
STATIC_TCB_MASTER.len = p_filesz;
STATIC_TCB_MASTER.offset = valign;
let tcb = Tcb::new(vsize).expect_notls("failed to allocate TCB");
+6 -1
View File
@@ -26,7 +26,12 @@ use super::{
PATH_SEP,
};
use crate::header::sys_auxv::{AT_ENTRY, AT_PHDR};
use goblin::elf::header::header64::SIZEOF_EHDR;
#[cfg(target_pointer_width = "32")]
pub const SIZEOF_EHDR: usize = 52;
#[cfg(target_pointer_width = "64")]
pub const SIZEOF_EHDR: usize = 64;
unsafe fn get_argv(mut ptr: *const usize) -> (Vec<String>, *const usize) {
//traverse the stack and collect argument vector
+9 -8
View File
@@ -1,4 +1,4 @@
use alloc::{string::ToString, vec::Vec};
use alloc::vec::Vec;
use core::{
cell::UnsafeCell,
mem,
@@ -7,7 +7,6 @@ use core::{
sync::atomic::AtomicBool,
};
use generic_rt::GenericTcb;
use goblin::error::{Error, Result};
use crate::{
header::sys_mman,
@@ -17,6 +16,8 @@ use crate::{
sync::{mutex::Mutex, waitval::Waitval},
};
use super::linker::DlError;
#[repr(C)]
#[derive(Debug, Clone)]
pub struct Master {
@@ -76,7 +77,7 @@ impl Tcb {
/// Create a new TCB
///
/// `size` is the size of the TLS in bytes.
pub unsafe fn new(size: usize) -> Result<&'static mut Self> {
pub unsafe fn new(size: usize) -> Result<&'static mut Self, DlError> {
let page_size = Sys::getpagesize();
let (_abi_page, tls, tcb_page) = Self::os_new(size.next_multiple_of(page_size))?;
@@ -145,7 +146,7 @@ impl Tcb {
}
/// Copy data from masters
pub unsafe fn copy_masters(&mut self) -> Result<()> {
pub unsafe fn copy_masters(&mut self) -> Result<(), DlError> {
//TODO: Complain if masters or tls exist without the other
if let Some(tls) = self.tls() {
if let Some(masters) = self.masters() {
@@ -175,7 +176,7 @@ impl Tcb {
);
tls_data.copy_from_slice(data);
} else {
return Err(Error::Malformed(format!("failed to copy tls master {}", i)));
return Err(DlError::Malformed);
}
}
self.num_copied_masters = masters.len();
@@ -238,7 +239,7 @@ impl Tcb {
}
/// Mapping with correct flags for TCB and TLS
unsafe fn map(size: usize) -> Result<&'static mut [u8]> {
unsafe fn map(size: usize) -> Result<&'static mut [u8], DlError> {
let ptr = Sys::mmap(
ptr::null_mut(),
size,
@@ -247,7 +248,7 @@ impl Tcb {
-1,
0,
)
.map_err(|_| Error::Malformed("failed to map tls".to_string()))?;
.map_err(|_| DlError::Oom)?;
ptr::write_bytes(ptr as *mut u8, 0, size);
Ok(slice::from_raw_parts_mut(ptr as *mut u8, size))
@@ -257,7 +258,7 @@ impl Tcb {
#[cfg(any(target_os = "linux", target_os = "redox"))]
unsafe fn os_new(
size: usize,
) -> Result<(&'static mut [u8], &'static mut [u8], &'static mut [u8])> {
) -> Result<(&'static mut [u8], &'static mut [u8], &'static mut [u8]), DlError> {
let page_size = Sys::getpagesize();
let abi_tls_tcb = Self::map(page_size + size + page_size)?;
let (abi, tls_tcb) = abi_tls_tcb.split_at_mut(page_size);
+1 -1
View File
@@ -23,6 +23,7 @@
#![feature(lang_items)]
#![feature(let_chains)]
#![feature(linkage)]
#![feature(naked_functions)]
#![feature(pointer_is_aligned_to)]
#![feature(ptr_as_uninit)]
#![feature(slice_as_chunks)]
@@ -44,7 +45,6 @@
#[macro_use]
extern crate alloc;
extern crate cbitset;
extern crate goblin;
extern crate memchr;
extern crate posix_regex;
extern crate rand;