pub const BOOTSTRAP_X86_64: &[u8] = include_bytes!(env!("ONELF_BOOTSTRAP_X86_64"));
pub const BOOTSTRAP_AARCH64: &[u8] = include_bytes!(env!("ONELF_BOOTSTRAP_AARCH64"));
pub const BOOTSTRAP_I686: &[u8] = include_bytes!(env!("ONELF_BOOTSTRAP_I686"));
pub const ONELF_ENV_X86_64: &[u8] = include_bytes!(env!("ONELF_ENV_X86_64"));
pub const ONELF_ENV_AARCH64: &[u8] = include_bytes!(env!("ONELF_ENV_AARCH64"));
pub const ONELF_ENV_I686: &[u8] = include_bytes!(env!("ONELF_ENV_I686"));
pub const ONELF_ENV_SONAME: &str = "libonelf-env.so";
pub fn bootstrap_blob(e_machine: u16) -> Option<&'static [u8]> {
const EM_386: u16 = 3;
const EM_X86_64: u16 = 62;
const EM_AARCH64: u16 = 183;
let blob = match e_machine {
EM_386 => BOOTSTRAP_I686,
EM_X86_64 => BOOTSTRAP_X86_64,
EM_AARCH64 => BOOTSTRAP_AARCH64,
_ => return None,
};
(!blob.is_empty()).then_some(blob)
}
pub fn onelf_env_blob(e_machine: u16) -> Option<&'static [u8]> {
const EM_386: u16 = 3;
const EM_X86_64: u16 = 62;
const EM_AARCH64: u16 = 183;
let blob = match e_machine {
EM_386 => ONELF_ENV_I686,
EM_X86_64 => ONELF_ENV_X86_64,
EM_AARCH64 => ONELF_ENV_AARCH64,
_ => return None,
};
if blob.len() < 20 || &blob[0..4] != b"\x7fELF" {
return None;
}
let m = u16::from_le_bytes([blob[18], blob[19]]);
if m != e_machine {
return None;
}
Some(blob)
}
pub const X86_64_METADATA_LEA_DISP_OFFSET: usize = 0x0d;
pub const X86_64_METADATA_LEA_RIP: usize = 0x11;
pub const I686_METADATA_ADD_PC: usize = 0x0c;
pub const I686_METADATA_ADD_DISP_OFFSET: usize = 0x0f;
pub const AARCH64_METADATA_ADR_OFFSET: usize = 0x10;
pub fn patch_aarch64_adr(blob: &mut [u8], target_offset: usize) {
let pc = AARCH64_METADATA_ADR_OFFSET;
let offset = (target_offset as i64) - (pc as i64);
assert!(
(-1048576..=1048575).contains(&offset),
"adr offset out of range"
);
let off = offset as u32;
let immlo = off & 0x3;
let immhi = (off >> 2) & 0x7ffff;
let mut insn = u32::from_le_bytes(blob[pc..pc + 4].try_into().unwrap());
insn = (insn & 0x9f00001f) | (immlo << 29) | (immhi << 5);
blob[pc..pc + 4].copy_from_slice(&insn.to_le_bytes());
}
#[cfg(test)]
mod tests {
use super::*;
const EM_386: u16 = 3;
const EM_X86_64: u16 = 62;
const EM_AARCH64: u16 = 183;
#[test]
fn built_env_blobs_are_valid_elf_for_their_machine() {
for em in [EM_386, EM_X86_64, EM_AARCH64] {
if let Some(blob) = onelf_env_blob(em) {
assert_eq!(&blob[0..4], b"\x7fELF");
assert_eq!(u16::from_le_bytes([blob[18], blob[19]]), em);
}
}
}
#[test]
fn unknown_arch_returns_none() {
assert!(onelf_env_blob(0xffff).is_none());
}
#[test]
fn machine_mismatch_is_rejected() {
if let Some(b) = onelf_env_blob(EM_AARCH64) {
assert_eq!(u16::from_le_bytes([b[18], b[19]]), EM_AARCH64);
}
}
#[test]
fn x86_64_bootstrap_lea_at_expected_offset() {
let Some(b) = bootstrap_blob(EM_X86_64) else {
return;
};
assert_eq!(
&b[0x00..0x0a],
&[0x48, 0x89, 0xe5, 0x48, 0x83, 0xe4, 0xf0, 0x48, 0x89, 0xef]
);
assert_eq!(&b[0x0a..0x0d], &[0x48, 0x8d, 0x35], "lea opcode moved");
assert_eq!(X86_64_METADATA_LEA_DISP_OFFSET, 0x0d);
assert_eq!(X86_64_METADATA_LEA_RIP, 0x11);
}
#[test]
fn aarch64_bootstrap_adr_at_expected_offset() {
let Some(b) = bootstrap_blob(EM_AARCH64) else {
return;
};
assert_eq!(AARCH64_METADATA_ADR_OFFSET, 0x10);
let insn = u32::from_le_bytes(b[0x10..0x14].try_into().unwrap());
assert_eq!(insn & 0x9f00_001f, 0x1000_0001, "adr x1 opcode moved");
}
#[test]
fn i686_bootstrap_add_at_expected_offset() {
let Some(b) = bootstrap_blob(EM_386) else {
return;
};
assert_eq!(
&b[0x00..0x0c],
&[
0x89, 0xe5, 0x89, 0xd6, 0x83, 0xe4, 0xf0, 0xe8, 0x00, 0x00, 0x00, 0x00
]
);
assert_eq!(b[0x0c], 0x59, "pop ecx moved");
assert_eq!(&b[0x0d..0x0f], &[0x81, 0xc1], "add ecx,imm32 moved");
assert_eq!(I686_METADATA_ADD_PC, 0x0c);
assert_eq!(I686_METADATA_ADD_DISP_OFFSET, 0x0f);
}
}