use wazabin_binary::elf::{ElfBinary, ElfKind};
const EHDR: usize = 64;
const PHENT: usize = 56;
const PT_LOAD: u32 = 1;
const PT_PHDR: u32 = 6;
const PT_TLS: u32 = 7;
struct Phdr {
kind: u32,
flags: u32,
offset: u64,
vaddr: u64,
filesz: u64,
memsz: u64,
align: u64,
}
fn image(e_type: u16, phdrs: &[Phdr]) -> Vec<u8> {
let mut out = vec![0u8; EHDR + PHENT * phdrs.len()];
out[..4].copy_from_slice(b"\x7fELF");
out[4] = 2; out[5] = 1; out[6] = 1; out[16..18].copy_from_slice(&e_type.to_le_bytes());
out[18..20].copy_from_slice(&62u16.to_le_bytes()); out[20..24].copy_from_slice(&1u32.to_le_bytes());
out[24..32].copy_from_slice(&0x1000u64.to_le_bytes()); out[32..40].copy_from_slice(&(EHDR as u64).to_le_bytes()); out[52..54].copy_from_slice(&(EHDR as u16).to_le_bytes());
out[54..56].copy_from_slice(&(PHENT as u16).to_le_bytes());
out[56..58].copy_from_slice(&(phdrs.len() as u16).to_le_bytes());
out[58..60].copy_from_slice(&64u16.to_le_bytes()); for (i, p) in phdrs.iter().enumerate() {
let at = EHDR + i * PHENT;
let ph = &mut out[at..at + PHENT];
ph[0..4].copy_from_slice(&p.kind.to_le_bytes());
ph[4..8].copy_from_slice(&p.flags.to_le_bytes());
ph[8..16].copy_from_slice(&p.offset.to_le_bytes());
ph[16..24].copy_from_slice(&p.vaddr.to_le_bytes());
ph[24..32].copy_from_slice(&p.vaddr.to_le_bytes());
ph[32..40].copy_from_slice(&p.filesz.to_le_bytes());
ph[40..48].copy_from_slice(&p.memsz.to_le_bytes());
ph[48..56].copy_from_slice(&p.align.to_le_bytes());
}
out.resize(0x200, 0);
out
}
fn without_phdr(e_type: u16, base: u64) -> Vec<u8> {
image(
e_type,
&[
Phdr {
kind: PT_LOAD,
flags: 5,
offset: 0,
vaddr: base,
filesz: 0x200,
memsz: 0x300,
align: 0x1000,
},
Phdr {
kind: PT_TLS,
flags: 4,
offset: 0x100,
vaddr: base + 0x100,
filesz: 0x20,
memsz: 0x60,
align: 8,
},
],
)
}
#[test]
fn executable_without_pt_phdr_locates_the_table_inside_the_load_segment() {
let elf = ElfBinary::parse(&without_phdr(2, 0x40_0000)).unwrap();
assert_eq!(elf.kind, ElfKind::Executable);
assert_eq!(elf.program_headers.offset, 64);
assert_eq!(elf.program_headers.entry_size, 56);
assert_eq!(elf.program_headers.count, 2);
assert_eq!(elf.program_headers.vaddr, Some(0x40_0040));
let tls = elf.tls.expect("PT_TLS is reported");
assert_eq!(tls.vaddr, 0x40_0100);
assert_eq!(tls.file_size, 0x20);
assert_eq!(tls.mem_size, 0x60);
assert_eq!(tls.align, 8);
assert_eq!(elf.segments.len(), 1, "PT_TLS is not a load segment");
assert_eq!(elf.segments[0].align, 0x1000);
}
#[test]
fn pie_is_a_shared_object_with_unrelocated_addresses() {
let elf = ElfBinary::parse(&without_phdr(3, 0)).unwrap();
assert_eq!(elf.kind, ElfKind::SharedObject);
assert_eq!(elf.program_headers.vaddr, Some(0x40));
assert_eq!(elf.tls.unwrap().vaddr, 0x100);
}
#[test]
fn pt_phdr_names_the_table_outright() {
let bytes = image(
2,
&[
Phdr {
kind: PT_PHDR,
flags: 4,
offset: 64,
vaddr: 0x40_0040,
filesz: 56 * 2,
memsz: 56 * 2,
align: 8,
},
Phdr {
kind: PT_LOAD,
flags: 5,
offset: 0x100,
vaddr: 0x40_1000,
filesz: 0x100,
memsz: 0x100,
align: 0x1000,
},
],
);
let elf = ElfBinary::parse(&bytes).unwrap();
assert_eq!(elf.program_headers.vaddr, Some(0x40_0040));
assert_eq!(elf.tls, None);
}
#[test]
fn a_table_no_segment_covers_has_no_address() {
let bytes = image(
2,
&[Phdr {
kind: PT_LOAD,
flags: 5,
offset: 0x100,
vaddr: 0x40_1000,
filesz: 0x100,
memsz: 0x100,
align: 0x1000,
}],
);
let elf = ElfBinary::parse(&bytes).unwrap();
assert_eq!(elf.program_headers.vaddr, None);
}