use std::fmt::Debug;
use object::{Object, ObjectSection, ObjectSegment, SectionKind};
#[derive(Clone)]
pub struct SvmaFileRange {
pub svma: u64,
pub file_offset: u64,
pub size: u64,
}
impl SvmaFileRange {
pub fn from_segment<'data, S: ObjectSegment<'data>>(segment: S) -> Self {
let svma = segment.address();
let (file_offset, size) = segment.file_range();
SvmaFileRange {
svma,
file_offset,
size,
}
}
pub fn from_section<'data, S: ObjectSection<'data>>(section: S) -> Option<Self> {
let svma = section.address();
let (file_offset, size) = section.file_range()?;
Some(SvmaFileRange {
svma,
file_offset,
size,
})
}
pub fn encompasses_file_range(&self, other_file_offset: u64, other_file_size: u64) -> bool {
let self_file_range_end = self.file_offset + self.size;
let other_file_range_end = other_file_offset + other_file_size;
self.file_offset <= other_file_offset && other_file_range_end <= self_file_range_end
}
pub fn is_encompassed_by_file_range(
&self,
other_file_offset: u64,
other_file_size: u64,
) -> bool {
let self_file_range_end = self.file_offset + self.size;
let other_file_range_end = other_file_offset + other_file_size;
other_file_offset <= self.file_offset && self_file_range_end <= other_file_range_end
}
}
impl Debug for SvmaFileRange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SvmaFileRange")
.field("svma", &format!("{:#x}", &self.svma))
.field("file_offset", &format!("{:#x}", &self.file_offset))
.field("size", &format!("{:#x}", &self.size))
.finish()
}
}
pub fn compute_vma_bias<'data, O: Object<'data>>(
file: &O,
mapping_start_file_offset: u64,
mapping_start_avma: u64,
mapping_size: u64,
) -> Option<u64> {
let mut contributions: Vec<SvmaFileRange> =
file.segments().map(SvmaFileRange::from_segment).collect();
if contributions.is_empty() {
contributions = file
.sections()
.filter(|s| s.kind() == SectionKind::Text)
.filter_map(SvmaFileRange::from_section)
.collect();
}
compute_vma_bias_impl(
&contributions,
mapping_start_file_offset,
mapping_start_avma,
mapping_size,
)
}
fn compute_vma_bias_impl(
contributions: &[SvmaFileRange],
mapping_file_offset: u64,
mapping_avma: u64,
mapping_size: u64,
) -> Option<u64> {
let ref_contribution = if let Some(contribution) = contributions.iter().find(|contribution| {
contribution.encompasses_file_range(mapping_file_offset, mapping_size)
|| contribution.is_encompassed_by_file_range(mapping_file_offset, mapping_size)
}) {
contribution
} else {
println!(
"Could not find segment or section overlapping the file offset range 0x{:x}..0x{:x}",
mapping_file_offset,
mapping_file_offset + mapping_size,
);
return None;
};
let ref_avma = if ref_contribution.file_offset > mapping_file_offset {
mapping_avma + (ref_contribution.file_offset - mapping_file_offset)
} else {
mapping_avma - (mapping_file_offset - ref_contribution.file_offset)
};
let bias = ref_avma.wrapping_sub(ref_contribution.svma);
Some(bias)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_compute_base_avma_impl() {
let js_segments = &[
SvmaFileRange {
svma: 0x0,
file_offset: 0x0,
size: 0x14bd0bc,
},
SvmaFileRange {
svma: 0x14be0c0,
file_offset: 0x14bd0c0,
size: 0xf5bf60,
},
SvmaFileRange {
svma: 0x241b020,
file_offset: 0x2419020,
size: 0x08e920,
},
SvmaFileRange {
svma: 0x24aa940,
file_offset: 0x24a7940,
size: 0x002d48,
},
];
assert_eq!(
compute_vma_bias_impl(js_segments, 0x14bd0c0, 0x100014be0c0, 0xf5bf60),
Some(0x10000000000)
);
assert_eq!(
compute_vma_bias_impl(js_segments, 0x14bd000, 0x55d605384000, 0xf5d000),
Some(0x55d603ec6000)
);
let d8_segments = &[
SvmaFileRange {
svma: 0x0,
file_offset: 0x0,
size: 0x3c8ed8,
},
SvmaFileRange {
svma: 0x03ca000,
file_offset: 0x3c9000,
size: 0xfec770,
},
SvmaFileRange {
svma: 0x13b7770,
file_offset: 0x13b5770,
size: 0x0528d0,
},
SvmaFileRange {
svma: 0x140c000,
file_offset: 0x1409000,
size: 0x0118f0,
},
];
assert_eq!(
compute_vma_bias_impl(d8_segments, 0x1056000, 0x55d15fe80000, 0x180000),
Some(0x55d15ee29000)
);
}
}