use crate::error::{Error, Result};
use crate::process::Process;
use crate::procfs;
use crate::types::VirtAddr;
#[derive(Debug, Clone)]
pub struct SharedLibrary {
pub name: String,
pub base_addr: u64,
}
#[derive(Debug, Clone)]
pub struct RendezvousInfo {
pub breakpoint_addr: VirtAddr,
pub libraries: Vec<SharedLibrary>,
}
pub fn read_shared_libraries(process: &Process) -> Result<Vec<SharedLibrary>> {
let info = read_rendezvous(process)?;
Ok(info.libraries)
}
pub fn read_rendezvous(process: &Process) -> Result<RendezvousInfo> {
let pid = process.pid();
let auxv = procfs::read_auxv(pid)?;
let phdr_addr = procfs::auxv_lookup(&auxv, procfs::AT_PHDR)
.ok_or_else(|| Error::Other("AT_PHDR not found in auxv".into()))?;
let phnum = procfs::auxv_lookup(&auxv, procfs::AT_PHNUM)
.ok_or_else(|| Error::Other("AT_PHNUM not found in auxv".into()))?;
let phent = procfs::auxv_lookup(&auxv, procfs::AT_PHENT).unwrap_or(56);
let phdrs_size = (phnum * phent) as usize;
let phdrs_data = process.read_memory(VirtAddr(phdr_addr), phdrs_size)?;
let mut dynamic_vaddr = None;
for i in 0..phnum as usize {
let off = i * phent as usize;
if off + 56 > phdrs_data.len() {
break;
}
let p_type = u32::from_le_bytes(phdrs_data[off..off + 4].try_into().unwrap());
if p_type == 2 {
let p_vaddr = u64::from_le_bytes(phdrs_data[off + 16..off + 24].try_into().unwrap());
dynamic_vaddr = Some(p_vaddr);
break;
}
}
let dynamic_vaddr = dynamic_vaddr.ok_or_else(|| Error::Other("PT_DYNAMIC not found".into()))?;
let dynamic_data = process.read_memory(VirtAddr(dynamic_vaddr), 4096)?;
let mut r_debug_addr: u64 = 0;
for chunk in dynamic_data.chunks(16) {
if chunk.len() < 16 {
break;
}
let d_tag = i64::from_le_bytes(chunk[0..8].try_into().unwrap());
let d_val = u64::from_le_bytes(chunk[8..16].try_into().unwrap());
if d_tag == 0 {
break; }
if d_tag == 21 {
r_debug_addr = d_val;
break;
}
}
if r_debug_addr == 0 {
return Err(Error::Other(
"DT_DEBUG not found or not yet initialized".into(),
));
}
let r_debug_data = process.read_memory(VirtAddr(r_debug_addr), 40)?;
let r_map = u64::from_le_bytes(r_debug_data[8..16].try_into().unwrap());
let r_brk = u64::from_le_bytes(r_debug_data[16..24].try_into().unwrap());
let libraries = walk_link_map(process, r_map)?;
Ok(RendezvousInfo {
breakpoint_addr: VirtAddr(r_brk),
libraries,
})
}
fn walk_link_map(process: &Process, head: u64) -> Result<Vec<SharedLibrary>> {
let mut libs = Vec::new();
let mut current = head;
let mut count = 0;
const MAX_LIBS: usize = 1024;
while current != 0 && count < MAX_LIBS {
let lm_data = process.read_memory(VirtAddr(current), 40)?;
let l_addr = u64::from_le_bytes(lm_data[0..8].try_into().unwrap());
let l_name_ptr = u64::from_le_bytes(lm_data[8..16].try_into().unwrap());
let l_next = u64::from_le_bytes(lm_data[24..32].try_into().unwrap());
let name = if l_name_ptr != 0 {
read_cstring(process, l_name_ptr, 512)?
} else {
String::new()
};
libs.push(SharedLibrary {
name,
base_addr: l_addr,
});
current = l_next;
count += 1;
}
Ok(libs)
}
fn read_cstring(process: &Process, addr: u64, max_len: usize) -> Result<String> {
let data = process.read_memory(VirtAddr(addr), max_len)?;
let end = data.iter().position(|&b| b == 0).unwrap_or(data.len());
Ok(String::from_utf8_lossy(&data[..end]).to_string())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn shared_library_display() {
let lib = SharedLibrary {
name: "/usr/lib/libc.so.6".into(),
base_addr: 0x7f000000,
};
assert_eq!(lib.name, "/usr/lib/libc.so.6");
assert_eq!(lib.base_addr, 0x7f000000);
}
#[test]
fn rendezvous_info_fields() {
let info = RendezvousInfo {
breakpoint_addr: VirtAddr(0x400100),
libraries: vec![],
};
assert_eq!(info.breakpoint_addr.addr(), 0x400100);
assert!(info.libraries.is_empty());
}
}