use crate::obj::ELF_WASMTIME_ADDRMAP;
use object::write::{Object, StandardSegment};
use object::{Bytes, LittleEndian, SectionKind, U32Bytes};
use serde_derive::{Deserialize, Serialize};
use std::ops::Range;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct InstructionAddressMap {
pub srcloc: FilePos,
pub code_offset: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct FilePos(u32);
impl FilePos {
pub fn new(pos: u32) -> FilePos {
assert!(pos != u32::MAX);
FilePos(pos)
}
pub fn file_offset(self) -> Option<u32> {
if self.0 == u32::MAX {
None
} else {
Some(self.0)
}
}
}
impl Default for FilePos {
fn default() -> FilePos {
FilePos(u32::MAX)
}
}
#[derive(Default)]
pub struct AddressMapSection {
offsets: Vec<U32Bytes<LittleEndian>>,
positions: Vec<U32Bytes<LittleEndian>>,
last_offset: u32,
}
impl AddressMapSection {
pub fn push(&mut self, func: Range<u64>, instrs: &[InstructionAddressMap]) {
let func_start = u32::try_from(func.start).unwrap();
let func_end = u32::try_from(func.end).unwrap();
self.offsets.reserve(instrs.len());
self.positions.reserve(instrs.len());
for map in instrs {
let pos = func_start + map.code_offset;
assert!(pos >= self.last_offset);
self.offsets.push(U32Bytes::new(LittleEndian, pos));
self.positions
.push(U32Bytes::new(LittleEndian, map.srcloc.0));
self.last_offset = pos;
}
self.last_offset = func_end;
}
pub fn append_to(self, obj: &mut Object) {
let section = obj.add_section(
obj.segment_name(StandardSegment::Data).to_vec(),
ELF_WASMTIME_ADDRMAP.as_bytes().to_vec(),
SectionKind::ReadOnlyData,
);
let amt = u32::try_from(self.offsets.len()).unwrap();
obj.append_section_data(section, &amt.to_le_bytes(), 1);
obj.append_section_data(section, object::bytes_of_slice(&self.offsets), 1);
obj.append_section_data(section, object::bytes_of_slice(&self.positions), 1);
}
}
fn parse_address_map(
section: &[u8],
) -> Option<(&[U32Bytes<LittleEndian>], &[U32Bytes<LittleEndian>])> {
let mut section = Bytes(section);
let count = section.read::<U32Bytes<LittleEndian>>().ok()?;
let count = usize::try_from(count.get(LittleEndian)).ok()?;
let (offsets, section) =
object::slice_from_bytes::<U32Bytes<LittleEndian>>(section.0, count).ok()?;
let (positions, section) =
object::slice_from_bytes::<U32Bytes<LittleEndian>>(section, count).ok()?;
debug_assert!(section.is_empty());
Some((offsets, positions))
}
pub fn lookup_file_pos(section: &[u8], offset: usize) -> Option<FilePos> {
let (offsets, positions) = parse_address_map(section)?;
let offset = u32::try_from(offset).ok()?;
let index = match offsets.binary_search_by_key(&offset, |v| v.get(LittleEndian)) {
Ok(i) => i,
Err(0) => return None,
Err(n) => n - 1,
};
let pos = positions.get(index)?;
Some(FilePos(pos.get(LittleEndian)))
}
pub fn iterate_address_map<'a>(
section: &'a [u8],
) -> Option<impl Iterator<Item = (u32, FilePos)> + 'a> {
let (offsets, positions) = parse_address_map(section)?;
Some(
offsets
.iter()
.map(|o| o.get(LittleEndian))
.zip(positions.iter().map(|pos| FilePos(pos.get(LittleEndian)))),
)
}