feat(core): implement end-to-end BionicLinker library loader
This commit is contained in:
parent
4702aaa37c
commit
0ffddd2f3d
3 changed files with 91 additions and 0 deletions
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
pub mod dynamic;
|
||||
pub mod elf;
|
||||
pub mod linker;
|
||||
pub mod mmap;
|
||||
pub mod reloc;
|
||||
pub mod symbols;
|
||||
|
|
|
|||
81
crates/molotov-core/src/linker.rs
Normal file
81
crates/molotov-core/src/linker.rs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
use crate::elf::ElfBinary;
|
||||
use crate::mmap::MappedImage;
|
||||
use crate::symbols::SymbolTriage;
|
||||
|
||||
pub struct LoadedLibrary {
|
||||
pub mapping: MappedImage,
|
||||
pub base_addr: u64,
|
||||
}
|
||||
|
||||
pub struct BionicLinker {
|
||||
triage: SymbolTriage,
|
||||
}
|
||||
|
||||
impl Default for BionicLinker {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl BionicLinker {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
triage: SymbolTriage::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn triage(&self) -> &SymbolTriage {
|
||||
&self.triage
|
||||
}
|
||||
|
||||
pub fn triage_mut(&mut self) -> &mut SymbolTriage {
|
||||
&mut self.triage
|
||||
}
|
||||
|
||||
pub fn load_from_bytes(&self, bytes: &[u8]) -> Result<LoadedLibrary, String> {
|
||||
let elf = ElfBinary::parse(bytes).map_err(|e| e.to_string())?;
|
||||
|
||||
// Calculate total virtual memory span
|
||||
let mut min_vaddr = u64::MAX;
|
||||
let mut max_vaddr = 0u64;
|
||||
|
||||
for seg in elf.load_segments() {
|
||||
if seg.p_vaddr < min_vaddr {
|
||||
min_vaddr = seg.p_vaddr;
|
||||
}
|
||||
let seg_end = seg.p_vaddr + seg.p_memsz;
|
||||
if seg_end > max_vaddr {
|
||||
max_vaddr = seg_end;
|
||||
}
|
||||
}
|
||||
|
||||
if min_vaddr > max_vaddr {
|
||||
return Err("No PT_LOAD segments found in ELF binary".to_string());
|
||||
}
|
||||
|
||||
let total_size = (max_vaddr - min_vaddr) as usize;
|
||||
let mapping = MappedImage::allocate_anonymous(total_size)?;
|
||||
let base_addr = mapping.base_addr() as u64;
|
||||
|
||||
// Copy PT_LOAD segments into virtual memory
|
||||
for seg in elf.load_segments() {
|
||||
let seg_dest_offset = (seg.p_vaddr - min_vaddr) as usize;
|
||||
let file_start = seg.p_offset as usize;
|
||||
let file_end = file_start + seg.p_filesz as usize;
|
||||
|
||||
if file_end <= bytes.len() {
|
||||
let slice = &bytes[file_start..file_end];
|
||||
unsafe {
|
||||
let dest_ptr = mapping.base_addr().add(seg_dest_offset);
|
||||
std::ptr::copy_nonoverlapping(slice.as_ptr(), dest_ptr, slice.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(LoadedLibrary {
|
||||
mapping,
|
||||
base_addr,
|
||||
})
|
||||
}
|
||||
}
|
||||
9
crates/molotov-core/tests/linker_test.rs
Normal file
9
crates/molotov-core/tests/linker_test.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
use molotov_core::linker::BionicLinker;
|
||||
|
||||
#[test]
|
||||
fn test_linker_rejects_empty_data() {
|
||||
let linker = BionicLinker::new();
|
||||
let res = linker.load_from_bytes(&[]);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
Reference in a new issue