use crate::{
container::{Arch, Container},
util,
};
#[derive(Debug, Clone)]
pub struct TNimTypeV2Fields {
pub size: u64,
pub align: i16,
pub depth: i16,
pub flags: u64,
pub name: Option<String>,
pub destructor_addr: Option<u64>,
pub display_addr: Option<u64>,
pub trace_impl_addr: Option<u64>,
pub type_info_v1_addr: Option<u64>,
}
pub fn read(container: &Container<'_>, va: u64) -> Option<TNimTypeV2Fields> {
let is_64 = is_64bit(container);
let ptr_size: usize = if is_64 { 8 } else { 4 };
let bytes = container.bytes();
let offset = va_to_offset(container, va)?;
let min_size = ptr_size.saturating_mul(6).saturating_add(4); if offset.checked_add(min_size)? > bytes.len() {
return None;
}
let mut pos = offset;
let destructor = read_ptr(bytes, pos, is_64);
pos = pos.saturating_add(ptr_size);
let size = read_ptr(bytes, pos, is_64);
pos = pos.saturating_add(ptr_size);
let align = util::read_i16_le(bytes, pos);
pos = pos.saturating_add(2);
let depth = util::read_i16_le(bytes, pos);
pos = pos.saturating_add(2);
if is_64 {
pos = pos.saturating_add(4);
}
let display = read_ptr(bytes, pos, is_64);
pos = pos.saturating_add(ptr_size);
let candidate_name_ptr = read_ptr(bytes, pos, is_64);
let name = read_cstring_at_va(container, bytes, candidate_name_ptr);
let (trace_impl, type_info_v1, flags) = if name.is_some() {
pos = pos.saturating_add(ptr_size); let ti = read_ptr(bytes, pos, is_64);
pos = pos.saturating_add(ptr_size);
let tv1 = read_ptr(bytes, pos, is_64);
pos = pos.saturating_add(ptr_size);
let fl = read_ptr(bytes, pos, is_64);
(ti, tv1, fl)
} else {
let ti = candidate_name_ptr;
pos = pos.saturating_add(ptr_size); let tv1 = read_ptr(bytes, pos, is_64);
pos = pos.saturating_add(ptr_size);
let fl = read_ptr(bytes, pos, is_64);
(ti, tv1, fl)
};
Some(TNimTypeV2Fields {
size,
align,
depth,
flags,
name,
destructor_addr: non_null(destructor),
display_addr: non_null(display),
trace_impl_addr: non_null(trace_impl),
type_info_v1_addr: non_null(type_info_v1),
})
}
fn non_null(addr: u64) -> Option<u64> {
if addr != 0 { Some(addr) } else { None }
}
fn is_64bit(container: &Container<'_>) -> bool {
matches!(
container.arch(),
Arch::Amd64 | Arch::Aarch64 | Arch::PowerPc64 | Arch::Riscv64
)
}
pub(crate) fn read_ptr(data: &[u8], offset: usize, is_64: bool) -> u64 {
if is_64 {
util::read_u64_le(data, offset)
} else {
u64::from(util::read_u32_le(data, offset))
}
}
pub(crate) fn read_cstring_at_va(
container: &Container<'_>,
bytes: &[u8],
va: u64,
) -> Option<String> {
if va == 0 {
return None;
}
let off = va_to_offset(container, va)?;
let cstr = util::slice_cstring(bytes, off, 256)?;
std::str::from_utf8(cstr).ok().map(|s| s.to_owned())
}
pub(crate) fn va_to_offset(container: &Container<'_>, va: u64) -> Option<usize> {
for section in container.sections() {
let Some(end) = section.vm_addr.checked_add(section.vm_size) else {
continue;
};
if va >= section.vm_addr && va < end {
let section_offset = va.wrapping_sub(section.vm_addr) as usize;
if section_offset < section.data.len() {
let base = section.data.as_ptr() as usize;
let container_base = container.bytes().as_ptr() as usize;
if base >= container_base {
return Some(
base.wrapping_sub(container_base)
.wrapping_add(section_offset),
);
}
}
}
}
None
}