diff options
| author | Ian McIntyre <ianpmcintyre@gmail.com> | 2022-08-02 06:21:12 -0400 |
|---|---|---|
| committer | Ian McIntyre <ianpmcintyre@gmail.com> | 2022-12-01 20:21:05 -0500 |
| commit | c7a9b9f3d4b9e71303c7b988d2bd916c2e4df9bc (patch) | |
| tree | 6d41ea7e433cac328fa165d45d1bc0cd71a1bf8f /tests/inspect_elf.rs | |
First commit
Diffstat (limited to 'tests/inspect_elf.rs')
| -rw-r--r-- | tests/inspect_elf.rs | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/tests/inspect_elf.rs b/tests/inspect_elf.rs new file mode 100644 index 0000000..c45260d --- /dev/null +++ b/tests/inspect_elf.rs @@ -0,0 +1,445 @@ +//! Automatically inspect the programs generated by the examples. +//! +//! Do not refer to this as a specification for the runtime. These values +//! are subject to change. + +#![allow(clippy::unusual_byte_groupings)] // Spacing delimits ITCM / DTCM / OCRAM banks. + +use goblin::elf::Elf; +use std::{fs, path::PathBuf, process::Command}; + +type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>; + +/// Build an example, returning a path to the ELF. +fn cargo_build(board: &str) -> Result<PathBuf> { + Command::new("cargo") + .arg("build") + .arg("--example=blink-rtic") + .arg(format!("--features=board/{},board/rtic", board)) + .arg("--target=thumbv7em-none-eabihf") + .arg(format!("--target-dir=target/{}", board)) + .arg("--quiet") + .spawn()? + .wait()?; + + let path = PathBuf::from(format!( + "target/{}/thumbv7em-none-eabihf/debug/examples/blink-rtic", + board + )); + Ok(path) +} + +struct ImxrtBinary<'a> { + elf: &'a Elf<'a>, +} + +impl<'a> ImxrtBinary<'a> { + fn new(elf: &'a Elf<'a>) -> Self { + Self { elf } + } + + fn symbol(&self, symbol_name: &str) -> Option<goblin::elf::Sym> { + self.elf + .syms + .iter() + .flat_map(|sym| self.elf.strtab.get_at(sym.st_name).map(|name| (sym, name))) + .find(|(_, name)| symbol_name == *name) + .map(|(sym, _)| sym) + } + + fn fcb(&self) -> Result<Fcb> { + self.symbol("FLEXSPI_CONFIGURATION_BLOCK") + .map(|sym| Fcb { + address: sym.st_value, + size: sym.st_size, + }) + .ok_or_else(|| { + String::from("Could not find FLEXSPI_CONFIGURATION_BLOCK in program").into() + }) + } + + fn flexram_config(&self) -> Result<u64> { + self.symbol("__flexram_config") + .map(|sym| sym.st_value) + .ok_or_else(|| String::from("Could not find FlexRAM configuration in program").into()) + } + + fn section(&self, section_name: &str) -> Result<Section> { + self.elf + .section_headers + .iter() + .flat_map(|sec| { + self.elf + .shdr_strtab + .get_at(sec.sh_name) + .map(|name| (sec, name)) + }) + .find(|(_, name)| section_name == *name) + .map(|(sec, _)| Section { + address: sec.sh_addr, + size: sec.sh_size, + }) + .ok_or_else(|| format!("Could not find {section_name} in program").into()) + } + + fn section_lma(&self, section: &Section) -> u64 { + self.elf + .program_headers + .iter() + .filter(|phdr| goblin::elf::program_header::PT_LOAD == phdr.p_type) + .find(|phdr| { + phdr.p_vaddr <= section.address && (phdr.p_vaddr + phdr.p_memsz) > section.address + }) + .map(|phdr| section.address - phdr.p_vaddr + phdr.p_paddr) + .unwrap_or(section.address) // VMA == LMA + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct Fcb { + address: u64, + size: u64, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct Section { + address: u64, + size: u64, +} + +const DTCM: u64 = 0x2000_0000; +const ITCM: u64 = 0x0000_0000; + +const fn aligned(value: u64, alignment: u64) -> u64 { + (value + (alignment - 1)) & !(alignment - 1) +} + +#[test] +#[ignore = "building an example can take time"] +fn imxrt1010evk() { + let path = cargo_build("imxrt1010evk").expect("Unable to build example"); + let contents = fs::read(path).expect("Could not read ELF file"); + let elf = Elf::parse(&contents).expect("Could not parse ELF"); + + let binary = ImxrtBinary::new(&elf); + assert_eq!( + Fcb { + address: 0x6000_0400, + size: 512 + }, + binary.fcb().unwrap() + ); + assert_eq!(binary.flexram_config().unwrap(), 0b11_10_0101); + + let stack = binary.section(".stack").unwrap(); + assert_eq!( + Section { + address: DTCM, + size: 8 * 1024 + }, + stack, + "stack not at ORIGIN(DTCM), or not 8 KiB large" + ); + assert_eq!(binary.section_lma(&stack), stack.address); + + let vector_table = binary.section(".vector_table").unwrap(); + assert_eq!( + Section { + address: stack.address + stack.size, + size: 16 * 4 + 240 * 4 + }, + vector_table, + "vector table not at expected VMA behind the stack" + ); + assert!( + vector_table.address % 1024 == 0, + "vector table is not 1024-byte aligned" + ); + assert_eq!(binary.section_lma(&vector_table), 0x6000_2000); + + let text = binary.section(".text").unwrap(); + assert_eq!(text.address, ITCM, "text"); + assert_eq!( + binary.section_lma(&text), + 0x6000_2000 + vector_table.size, + "text VMA expected behind vector table" + ); + + let rodata = binary.section(".rodata").unwrap(); + assert_eq!( + rodata.address, + 0x6000_2000 + vector_table.size + aligned(text.size, 16), + "rodata LMA & VMA expected behind text" + ); + assert_eq!(rodata.address, binary.section_lma(&rodata)); + + let data = binary.section(".data").unwrap(); + assert_eq!(data.address, 0x2020_0000, "data VMA in OCRAM"); + assert_eq!( + data.size, 4, + "blink-rtic expected to have a single static mut u32" + ); + assert_eq!( + binary.section_lma(&data), + rodata.address + aligned(rodata.size, 4), + "data LMA starts behind rodata" + ); + + let bss = binary.section(".bss").unwrap(); + assert_eq!( + bss.address, + data.address + aligned(data.size, 4), + "bss in OCRAM behind data" + ); + assert_eq!(binary.section_lma(&bss), bss.address, "bss is NOLOAD"); + + let uninit = binary.section(".uninit").unwrap(); + assert_eq!( + uninit.address, + bss.address + aligned(bss.size, 4), + "uninit in OCRAM behind bss" + ); + assert_eq!( + binary.section_lma(&uninit), + uninit.address, + "uninit is NOLOAD" + ); + + let heap = binary.section(".heap").unwrap(); + assert_eq!( + Section { + address: vector_table.address + vector_table.size, + size: 1024 + }, + heap, + "1 KiB heap in DTCM behind vector table" + ); + assert_eq!(heap.size, 1024); + assert_eq!(binary.section_lma(&heap), heap.address, "Heap is NOLOAD"); +} + +#[test] +#[ignore = "building an example can take time"] +fn teensy4() { + let path = cargo_build("teensy4").expect("Unable to build example"); + let contents = fs::read(path).expect("Could not read ELF file"); + let elf = Elf::parse(&contents).expect("Could not parse ELF"); + + let binary = ImxrtBinary::new(&elf); + assert_eq!( + Fcb { + address: 0x6000_0000, + size: 512 + }, + binary.fcb().unwrap() + ); + assert_eq!( + binary.flexram_config().unwrap(), + 0b11111111_101010101010101010101010 + ); + + let stack = binary.section(".stack").unwrap(); + assert_eq!( + Section { + address: DTCM, + size: 8 * 1024 + }, + stack, + "stack not at ORIGIN(DTCM), or not 8 KiB large" + ); + assert_eq!(binary.section_lma(&stack), stack.address); + + let vector_table = binary.section(".vector_table").unwrap(); + assert_eq!( + Section { + address: stack.address + stack.size, + size: 16 * 4 + 240 * 4 + }, + vector_table, + "vector table not at expected VMA behind the stack" + ); + assert!( + vector_table.address % 1024 == 0, + "vector table is not 1024-byte aligned" + ); + assert_eq!(binary.section_lma(&vector_table), 0x6000_2000); + + let text = binary.section(".text").unwrap(); + assert_eq!( + text.address, + binary.section_lma(&vector_table) + vector_table.size, + "text" + ); + assert_eq!( + binary.section_lma(&text), + 0x6000_2000 + vector_table.size, + "text VMA expected behind vector table" + ); + + let rodata = binary.section(".rodata").unwrap(); + assert_eq!( + rodata.address, + vector_table.address + vector_table.size, + "rodata LMA & VMA expected behind text" + ); + assert_eq!( + binary.section_lma(&rodata), + binary.section_lma(&text) + aligned(text.size, 16) + ); + + let data = binary.section(".data").unwrap(); + assert_eq!( + data.address, + rodata.address + rodata.size, + "data VMA in DTCM behind rodata" + ); + assert_eq!( + data.size, 4, + "blink-rtic expected to have a single static mut u32" + ); + assert_eq!( + binary.section_lma(&data), + binary.section_lma(&rodata) + aligned(rodata.size, 4), + "data LMA starts behind rodata" + ); + + let bss = binary.section(".bss").unwrap(); + assert_eq!( + bss.address, + data.address + aligned(data.size, 4), + "bss in DTCM behind data" + ); + assert_eq!(binary.section_lma(&bss), bss.address, "bss is NOLOAD"); + + let uninit = binary.section(".uninit").unwrap(); + assert_eq!( + uninit.address, + bss.address + aligned(bss.size, 4), + "uninit in DTCM behind bss" + ); + assert_eq!( + binary.section_lma(&uninit), + uninit.address, + "uninit is NOLOAD" + ); + + let heap = binary.section(".heap").unwrap(); + assert_eq!( + Section { + address: uninit.address + aligned(uninit.size, 4), + size: 1024 + }, + heap, + "1 KiB heap in DTCM behind uninit" + ); + assert_eq!(binary.section_lma(&heap), heap.address, "Heap is NOLOAD"); +} + +#[test] +#[ignore = "building an example can take time"] +fn imxrt1170evk_cm7() { + let path = cargo_build("imxrt1170evk-cm7").expect("Unable to build example"); + let contents = fs::read(path).expect("Could not read ELF file"); + let elf = Elf::parse(&contents).expect("Could not parse ELF"); + + let binary = ImxrtBinary::new(&elf); + assert_eq!( + Fcb { + address: 0x3000_0400, + size: 512 + }, + binary.fcb().unwrap() + ); + assert_eq!( + binary.flexram_config().unwrap(), + 0b1111111111111111_1010101010101010 + ); + + let stack = binary.section(".stack").unwrap(); + assert_eq!( + Section { + address: DTCM, + size: 8 * 1024 + }, + stack, + "stack not at ORIGIN(DTCM), or not 8 KiB large" + ); + assert_eq!(binary.section_lma(&stack), stack.address); + + let vector_table = binary.section(".vector_table").unwrap(); + assert_eq!( + Section { + address: stack.address + stack.size, + size: 16 * 4 + 240 * 4 + }, + vector_table, + "vector table not at expected VMA behind the stack" + ); + assert!( + vector_table.address % 1024 == 0, + "vector table is not 1024-byte aligned" + ); + assert_eq!(binary.section_lma(&vector_table), 0x3000_2000); + + let text = binary.section(".text").unwrap(); + assert_eq!(text.address, ITCM, "text"); + assert_eq!( + binary.section_lma(&text), + 0x3000_2000 + vector_table.size, + "text VMA expected behind vector table" + ); + + let rodata = binary.section(".rodata").unwrap(); + assert_eq!( + rodata.address, + vector_table.address + vector_table.size, + "rodata moved to DTCM behind vector table" + ); + assert_eq!( + binary.section_lma(&rodata), + 0x3000_2000 + vector_table.size + aligned(text.size, 16), + ); + + let data = binary.section(".data").unwrap(); + assert_eq!(data.address, 0x2024_0000, "data VMA in OCRAM"); + assert_eq!( + data.size, 4, + "blink-rtic expected to have a single static mut u32" + ); + assert_eq!( + binary.section_lma(&data), + binary.section_lma(&rodata) + aligned(rodata.size, 4), + "data LMA starts behind rodata" + ); + + let bss = binary.section(".bss").unwrap(); + assert_eq!( + bss.address, + data.address + aligned(data.size, 4), + "bss in OCRAM behind data" + ); + assert_eq!(binary.section_lma(&bss), bss.address, "bss is NOLOAD"); + + let uninit = binary.section(".uninit").unwrap(); + assert_eq!( + uninit.address, + bss.address + aligned(bss.size, 4), + "uninit in OCRAM behind bss" + ); + assert_eq!( + binary.section_lma(&uninit), + uninit.address, + "uninit is NOLOAD" + ); + + let heap = binary.section(".heap").unwrap(); + assert_eq!( + Section { + address: rodata.address + aligned(rodata.size, 4), + size: 0, + }, + heap, + "0 byte heap in DTCM behind rodata table" + ); + assert_eq!(binary.section_lma(&heap), heap.address, "Heap is NOLOAD"); +} |
