1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//! Helpers for running methods from an ELF file.
use crate::core::{Config, Emulator, Processor};
use object::{File, Object, ObjectSection, ObjectSymbol};
use std::collections::BTreeMap;
pub const ADDR_RAM: u32 = 0x10000000;
pub const STACK_SIZE: u32 = 1024;
/// Helper for running easily tests from an ELF file.
pub struct ElfHarness {
/// Processor executing the test methods.
pub proc: Processor,
/// All symbols and their address, extracted from the ELF file.
pub symbols: BTreeMap<String, u32>,
}
impl ElfHarness {
pub fn new(elf: &[u8]) -> Self {
// Parse ELF file to extract all symbols.
// Keep the symbols in a BTreeMap to simplify and discard object::File.
let object = File::parse(elf).unwrap();
let symbols = object
.symbols()
.map(|s| (s.name().unwrap().into(), s.address() as u32))
.collect();
let mut proc = Processor::new(Config::v7m());
proc.map_ram(ADDR_RAM, 1024).unwrap();
// Map program section as read-only RAM memories
for name in [".text", ".rodata", ".data"] {
if let Some(section) = object.section_by_name(name) {
proc.map(
section.address() as u32,
§ion.uncompressed_data().unwrap(),
)
.unwrap();
}
}
Self { proc, symbols }
}
/// Sets PC at given method entry and execute instructions until the function returns (or a
/// timeout is reached).
pub fn call(&mut self, method: &str) -> u32 {
let address = self.symbols[method];
self.proc.set_pc(address & 0xfffffffe); // We drop lsb (thumb mode bit)
self.proc.registers.lr = 0xfffffffe;
self.proc.set_sp(ADDR_RAM + STACK_SIZE);
loop {
self.proc.next_event().unwrap();
// Run util code branches back to initial LR
if self.proc.pc() == 0xfffffffe {
// Verify stack has been poped correctly
assert_eq!(self.proc.sp(), ADDR_RAM + STACK_SIZE);
return self.proc.registers.r0;
}
}
}
/// Similar to [Self::call], with passing a function argument and returning function result
/// using R0 register.
pub fn call1(&mut self, method: &str, arg0: u32) -> u32 {
self.proc.registers.r0 = arg0;
self.call(method);
self.proc.registers.r0
}
/// Similar to [Self::call], with passing two function arguments and returning function result.
pub fn call2(&mut self, method: &str, arg0: u32, arg1: u32) -> u32 {
self.proc.registers.r0 = arg0;
self.proc.registers.r1 = arg1;
self.call(method);
self.proc.registers.r0
}
}