pub fn apply_e8_filter(data: &mut [u8], data_start_offset: u64, translate_e9: bool) {
if data.len() < 5 {
return;
}
if data_start_offset >= 0x4000_0000 {
return;
}
let scan_end = data.len() - 5;
let mut i = 0usize;
while i <= scan_end {
let b = data[i];
let is_call = b == 0xE8 || (translate_e9 && b == 0xE9);
if !is_call {
i += 1;
continue;
}
let cur_pos = (data_start_offset + i as u64 + 1) as i32;
let rel = i32::from_le_bytes([data[i + 1], data[i + 2], data[i + 3], data[i + 4]]);
let new = rel.wrapping_sub(cur_pos);
let bytes = new.to_le_bytes();
data[i + 1] = bytes[0];
data[i + 2] = bytes[1];
data[i + 3] = bytes[2];
data[i + 4] = bytes[3];
i += 5;
}
}
#[cfg(test)]
mod tests {
use super::*;
extern crate std;
#[test]
fn ignores_short_buffers() {
let mut data = *b"\xE8\x00\x00";
apply_e8_filter(&mut data, 0, false);
assert_eq!(&data, b"\xE8\x00\x00");
}
#[test]
fn rewrites_e8_callsite() {
let mut data = [0xE8, 0x00, 0x01, 0x00, 0x00];
apply_e8_filter(&mut data, 0, false);
assert_eq!(data, [0xE8, 0xFF, 0x00, 0x00, 0x00]);
}
#[test]
fn skips_e9_when_disabled() {
let mut data = [0xE9, 0x00, 0x01, 0x00, 0x00];
apply_e8_filter(&mut data, 0, false);
assert_eq!(data, [0xE9, 0x00, 0x01, 0x00, 0x00]); }
#[test]
fn translates_e9_when_enabled() {
let mut data = [0xE9, 0x00, 0x01, 0x00, 0x00];
apply_e8_filter(&mut data, 0, true);
assert_eq!(data, [0xE9, 0xFF, 0x00, 0x00, 0x00]);
}
#[test]
fn bypassed_above_one_gib() {
let mut data = [0xE8, 0x00, 0x01, 0x00, 0x00];
apply_e8_filter(&mut data, 0x4000_0000, false);
assert_eq!(data, [0xE8, 0x00, 0x01, 0x00, 0x00]); }
}