use crate::{
core::{
error::{Error, ErrorKind, Result},
virtual_memory_reader::VirtualMemoryReader,
},
memory::virtual_address::VirtualAddress,
};
use btfparse::{Offset, TypeInformation};
use std::fmt;
const READ_STRING_BYTES_CHUNK_SIZE: usize = 128;
#[derive(Clone, Copy)]
pub struct VirtualStruct<'a> {
vmem_reader: &'a VirtualMemoryReader<'a>,
type_information: &'a TypeInformation,
tid: u32,
virtual_address: VirtualAddress,
}
impl<'a> VirtualStruct<'a> {
pub fn from_id(
vmem_reader: &'a VirtualMemoryReader,
type_information: &'a TypeInformation,
tid: u32,
virtual_address: &VirtualAddress,
) -> Result<Self> {
if type_information.from_id(tid).is_none() {
Err(Error::new(
ErrorKind::TypeInformationError,
&format!("Invalid virtual struct ID: {tid}"),
))
} else {
Ok(Self {
vmem_reader,
type_information,
tid,
virtual_address: *virtual_address,
})
}
}
pub fn from_name(
vmem_reader: &'a VirtualMemoryReader,
type_information: &'a TypeInformation,
name: &str,
virtual_address: &VirtualAddress,
) -> Result<Self> {
type_information
.id_of(name)
.map(|tid| Self::from_id(vmem_reader, type_information, tid, virtual_address))
.unwrap_or_else(|| {
Err(Error::new(
ErrorKind::TypeInformationError,
&format!("Invalid virtual struct type name: {name}"),
))
})
}
pub fn tid(&self) -> u32 {
self.tid
}
pub fn virtual_address(&self) -> VirtualAddress {
self.virtual_address
}
pub fn traverse(&self, path: &str) -> Result<Self> {
let (destination_tid, destination_offset) =
self.type_information.offset_of(self.tid, path).map_err(
|error| {
Error::new(
ErrorKind::TypeTraversalError,
&format!("The following path could not be used to traverse type #{}: {path}. BTF error: {error:?}", self.tid),
)
}
)?;
let virtual_address = self.virtual_address
+ match destination_offset {
Offset::ByteOffset(offset) => offset as u64,
_ => {
return Err(Error::new(
ErrorKind::TypeInformationError,
&format!("Invalid offset: {destination_offset:?}"),
));
}
};
Ok(Self {
vmem_reader: self.vmem_reader,
type_information: self.type_information,
tid: destination_tid,
virtual_address,
})
}
pub fn dereference(&self) -> Result<Self> {
let pointee_tid = self
.type_information
.pointee_tid(self.tid)
.map_err(|error| {
Error::new(
ErrorKind::TypeTraversalError,
&format!(
"Failed to get the pointee type id for type #{}. BTF error: {:?}",
self.tid, error
),
)
})?;
let virtual_address = self.read_vaddr()?;
Ok(Self {
vmem_reader: self.vmem_reader,
type_information: self.type_information,
tid: pointee_tid,
virtual_address,
})
}
#[allow(unused)]
pub fn cast_to(&self, name: &str) -> Result<Self> {
self.type_information
.id_of(name)
.map(|tid| Self {
vmem_reader: self.vmem_reader,
type_information: self.type_information,
tid,
virtual_address: self.virtual_address,
})
.ok_or_else(|| {
Error::new(
ErrorKind::TypeInformationError,
&format!("Invalid virtual struct type name: {name}"),
)
})
}
pub fn read_vaddr(&self) -> Result<VirtualAddress> {
self.vmem_reader.read_vaddr(self.virtual_address)
}
#[allow(unused)]
pub fn read_u8(&self) -> Result<u8> {
self.vmem_reader.read_u8(self.virtual_address)
}
pub fn read_u16(&self) -> Result<u16> {
self.vmem_reader.read_u16(self.virtual_address)
}
pub fn read_u32(&self) -> Result<u32> {
self.vmem_reader.read_u32(self.virtual_address)
}
pub fn read_u64(&self) -> Result<u64> {
self.vmem_reader.read_u64(self.virtual_address)
}
fn read_string_bytes(&self, max_size: Option<usize>) -> Result<Vec<u8>> {
let mut buffer = Vec::new();
let mut offset = 0;
loop {
let remaining = max_size.map(|max| max.saturating_sub(offset));
let chunk_size = match remaining {
Some(0) => break,
Some(r) => r.min(READ_STRING_BYTES_CHUNK_SIZE),
None => READ_STRING_BYTES_CHUNK_SIZE,
};
let mut chunk = vec![0u8; chunk_size];
let bytes_read = match self
.vmem_reader
.read(&mut chunk, self.virtual_address + offset as u64)
{
Ok(n) => n,
Err(e) => {
if !buffer.is_empty() {
log::debug!(
"String read error at offset {}, returning partial data ({} bytes)",
offset,
buffer.len()
);
return Ok(buffer);
}
return Err(e);
}
};
if bytes_read == 0 {
break;
}
if let Some(null_pos) = chunk[..bytes_read].iter().position(|&b| b == 0) {
buffer.extend_from_slice(&chunk[..null_pos]);
break;
}
buffer.extend_from_slice(&chunk[..bytes_read]);
offset += bytes_read;
if bytes_read < chunk_size {
break;
}
}
Ok(buffer)
}
pub fn read_string_lossy(&self, max_size: Option<usize>) -> Result<String> {
let buffer = self.read_string_bytes(max_size)?;
let mut string = String::from_utf8_lossy(&buffer).to_string();
if let Some(0) = string.as_bytes().first().cloned() {
string.clear();
}
Ok(string)
}
pub fn read_bytes(&self, size: usize) -> Result<Vec<u8>> {
if size == 0 {
return Ok(Vec::new());
}
let mut buffer = vec![0u8; size];
self.vmem_reader
.read_exact(&mut buffer, self.virtual_address)?;
Ok(buffer)
}
}
impl fmt::Debug for VirtualStruct<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"tid:{}, virtual_address:{:?}",
self.tid, self.virtual_address
)
}
}