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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use crate::cannon::{Page, State, PAGE_SIZE};
use elf::{endian::LittleEndian, section::SectionHeader, ElfBytes};
use log::debug;
use std::{collections::HashMap, path::Path};

/// Parse an ELF file and return the parsed data as a structure that is expected
/// by the o1vm RISC-V 32i edition.
// FIXME: parametrize by an architecture. We should return a state depending on the
// architecture. In the meantime, we can have parse_riscv32i and parse_mips.
// FIXME: for now, we return a State structure, either for RISC-V 32i or MIPS.
// We should return a structure specifically built for the o1vm, and not tight
// to Cannon. It will be done in a future PR to avoid breaking the current code
// and have a huge diff.
pub fn parse_riscv32i(path: &Path) -> Result<State, String> {
    debug!("Start parsing the ELF file to load a RISC-V 32i compatible state");
    let file_data = std::fs::read(path).expect("Could not read file.");
    let slice = file_data.as_slice();
    let file = ElfBytes::<LittleEndian>::minimal_parse(slice).expect("Open ELF file failed.");

    // Checking it is RISC-V
    assert_eq!(file.ehdr.e_machine, 243);

    let (shdrs_opt, strtab_opt) = file
        .section_headers_with_strtab()
        .expect("shdrs offsets should be valid");
    let (shdrs, strtab) = (
        shdrs_opt.expect("Should have shdrs"),
        strtab_opt.expect("Should have strtab"),
    );

    // Parse the shdrs and collect them into a map keyed on their zero-copied name
    let sections_by_name: HashMap<&str, SectionHeader> = shdrs
        .iter()
        .map(|shdr| {
            (
                strtab
                    .get(shdr.sh_name as usize)
                    .expect("Failed to get section name"),
                shdr,
            )
        })
        .collect();

    debug!("Loading the text section, which contains the executable code.");
    // Getting the executable code.
    let text_section = sections_by_name
        .get(".text")
        .expect("Should have .text section");

    let (text_section_data, _) = file
        .section_data(text_section)
        .expect("Failed to read data from .text section");

    let code_section_starting_address = text_section.sh_addr as usize;
    let code_section_size = text_section.sh_size as usize;
    let code_section_end_address = code_section_starting_address + code_section_size;
    debug!(
        "The executable code starts at address {}, has size {} bytes, and ends at address {}.",
        code_section_starting_address, code_section_size, code_section_end_address
    );

    // Building the memory pages
    let mut memory: Vec<Page> = vec![];
    let page_size_usize: usize = PAGE_SIZE.try_into().unwrap();
    // Padding to get the right number of pages. We suppose that the memory
    // index starts at 0.
    let start_page_address: usize =
        (code_section_starting_address / page_size_usize) * page_size_usize;
    let end_page_address =
        (code_section_end_address / (page_size_usize - 1)) * page_size_usize + page_size_usize;

    let first_page_index = start_page_address / page_size_usize;
    let last_page_index = (end_page_address - 1) / page_size_usize;
    let mut data_offset = 0;
    (first_page_index..=last_page_index).for_each(|page_index| {
        let mut data = vec![0; page_size_usize];
        // Special case of only one page
        if first_page_index == last_page_index {
            let data_length = code_section_end_address - code_section_starting_address;
            let page_offset = code_section_starting_address - start_page_address;
            data[page_offset..page_offset + data_length]
                .copy_from_slice(&text_section_data[0..data_length]);
            data_offset += data_length;
        } else {
            let data_length = if page_index == last_page_index {
                code_section_end_address - (page_index * page_size_usize)
            } else {
                page_size_usize
            };
            let page_offset = if page_index == first_page_index {
                code_section_starting_address - start_page_address
            } else {
                0
            };
            data[page_offset..]
                .copy_from_slice(&text_section_data[data_offset..data_offset + data_length]);
            data_offset += data_length;
        }
        let page = Page {
            index: page_index as u32,
            data,
        };
        memory.push(page);
    });

    // FIXME: add data section into memory for static data saved in the binary

    // FIXME: we're lucky that RISCV32i and MIPS have the same number of
    let registers: [u32; 32] = [0; 32];

    // FIXME: it is only because we share the same structure for the state.
    let preimage_key: [u8; 32] = [0; 32];
    // FIXME: it is only because we share the same structure for the state.
    let preimage_offset = 0;

    // Entry point of the program
    let pc: u32 = file.ehdr.e_entry as u32;
    assert!(pc != 0, "Entry point is 0. The documentation of the ELF library says that it means the ELF doesn't have an entry point. This is not supported.");
    let next_pc: u32 = pc + 4u32;

    let state = State {
        memory,
        // FIXME: only because Cannon related
        preimage_key,
        // FIXME: only because Cannon related
        preimage_offset,
        pc,
        next_pc,
        // FIXME: only because Cannon related
        lo: 0,
        // FIXME: only because Cannon related
        hi: 0,
        heap: 0,
        exit: 0,
        exited: false,
        step: 0,
        registers,
        // FIXME: only because Cannon related
        last_hint: None,
        // FIXME: only because Cannon related
        preimage: None,
    };

    Ok(state)
}