use std::mem::size_of;
use zerocopy::IntoBytes;
use crate::dof_bindings::*;
use crate::Section;
fn build_section_data(section: &Section) -> Vec<(u32, Vec<u8>)> {
let mut probe_sections = Vec::new();
let mut provider_sections = Vec::new();
let mut strings = Vec::<u8>::new();
strings.push(0); let mut arguments = Vec::<u8>::new();
let mut offsets = Vec::new();
let mut enabled_offsets = Vec::new();
for (i, provider) in section.providers.values().enumerate() {
let mut provider_section = dof_provider {
dofpv_name: strings.len() as _,
..Default::default()
};
strings.extend_from_slice(provider.name.as_bytes());
strings.push(0);
provider_section.dofpv_strtab = 0;
provider_section.dofpv_prargs = 1;
provider_section.dofpv_proffs = 2;
provider_section.dofpv_prenoffs = 3;
provider_section.dofpv_probes = (4 + i) as _;
let mut probe_section = Vec::with_capacity(provider.probes.len() * size_of::<dof_probe>());
for probe in provider.probes.values() {
let mut probe_t = dof_probe {
dofpr_addr: probe.address,
..Default::default()
};
probe_t.dofpr_func = strings.len() as _;
strings.extend_from_slice(probe.function.as_bytes());
strings.push(0);
probe_t.dofpr_name = strings.len() as _;
strings.extend_from_slice(probe.name.as_bytes());
strings.push(0);
probe_t.dofpr_argidx = arguments.len() as _;
let argv = strings.len() as u32;
for (i, arg) in probe.arguments.iter().enumerate() {
strings.extend_from_slice(arg.as_bytes());
strings.push(0);
arguments.push(i as _);
}
probe_t.dofpr_nargv = argv;
probe_t.dofpr_nargc = probe.arguments.len() as _;
probe_t.dofpr_xargv = argv;
probe_t.dofpr_xargc = probe.arguments.len() as _;
probe_t.dofpr_offidx = offsets.len() as _;
offsets.extend_from_slice(&probe.offsets);
probe_t.dofpr_noffs = probe.offsets.len() as _;
probe_t.dofpr_enoffidx = enabled_offsets.len() as _;
enabled_offsets.extend_from_slice(&probe.enabled_offsets);
probe_t.dofpr_nenoffs = probe.enabled_offsets.len() as _;
probe_section.extend_from_slice(probe_t.as_bytes());
}
probe_sections.push(probe_section);
provider_sections.push(provider_section.as_bytes().to_vec());
}
let mut section_data = Vec::with_capacity(4 + 2 * probe_sections.len());
section_data.push((DOF_SECT_STRTAB, strings));
if arguments.is_empty() {
arguments.push(0);
}
section_data.push((DOF_SECT_PRARGS, arguments));
let offset_section = offsets
.iter()
.flat_map(|offset| offset.to_ne_bytes().to_vec())
.collect::<Vec<_>>();
section_data.push((DOF_SECT_PROFFS, offset_section));
let enabled_offset_section = enabled_offsets
.iter()
.flat_map(|offset| offset.to_ne_bytes().to_vec())
.collect::<Vec<_>>();
section_data.push((DOF_SECT_PRENOFFS, enabled_offset_section));
for probe_section in probe_sections.into_iter() {
section_data.push((DOF_SECT_PROBES, probe_section));
}
for provider_section in provider_sections.into_iter() {
section_data.push((DOF_SECT_PROVIDER, provider_section));
}
section_data
}
fn build_section_headers(
sections: Vec<(u32, Vec<u8>)>,
mut offset: usize,
) -> (Vec<dof_sec>, Vec<Vec<u8>>, usize) {
let mut section_headers = Vec::with_capacity(sections.len());
let mut section_data = Vec::<Vec<u8>>::with_capacity(sections.len());
for (sec_type, data) in sections.into_iter() {
let (alignment, entry_size) = match sec_type {
DOF_SECT_STRTAB | DOF_SECT_PRARGS => (1, 1),
DOF_SECT_PROFFS | DOF_SECT_PRENOFFS => (size_of::<u32>(), size_of::<u32>()),
DOF_SECT_PROVIDER => (size_of::<u32>(), 0),
DOF_SECT_PROBES => (size_of::<u64>(), size_of::<dof_probe>()),
_ => unimplemented!(),
};
if offset % alignment > 0 {
let padding = alignment - offset % alignment;
section_data.last_mut().unwrap().extend(vec![0; padding]);
offset += padding;
}
let header = dof_sec {
dofs_type: sec_type,
dofs_align: alignment as u32,
dofs_flags: DOF_SECF_LOAD,
dofs_entsize: entry_size as u32,
dofs_offset: offset as u64,
dofs_size: data.len() as u64,
};
offset += data.len();
section_headers.push(header);
section_data.push(data);
}
(section_headers, section_data, offset)
}
pub fn serialize_section(section: &Section) -> Vec<u8> {
let sections = build_section_data(section);
let hdr_size = size_of::<dof_hdr>() + sections.len() * size_of::<dof_sec>();
let (section_headers, section_data, size) = build_section_headers(sections, hdr_size);
let header = dof_hdr {
dofh_ident: section.ident.as_bytes(),
dofh_flags: 0,
dofh_hdrsize: size_of::<dof_hdr>() as _,
dofh_secsize: size_of::<dof_sec>() as _,
dofh_secnum: section_headers.len() as _,
dofh_secoff: size_of::<dof_hdr>() as _,
dofh_loadsz: size as _,
dofh_filesz: size as _,
dofh_pad: 0,
};
let mut file_data = Vec::with_capacity(header.dofh_filesz as _);
file_data.extend(header.as_bytes());
for header in section_headers.into_iter() {
file_data.extend(header.as_bytes());
}
for data in section_data.into_iter() {
file_data.extend(data);
}
file_data
}
#[cfg(test)]
mod test {
use super::build_section_headers;
use crate::dof_bindings::*;
#[test]
fn test_padding() {
let sections = vec![
(DOF_SECT_STRTAB, vec![96_u8]),
(DOF_SECT_PROFFS, vec![0x11_u8, 0x22_u8, 0x33_u8, 0x44_u8]),
];
assert_eq!(sections[0].1.len(), 1);
let (_, section_data, size) = build_section_headers(sections, 0);
assert_eq!(section_data[0].len(), 4);
assert_eq!(size, 8);
}
}