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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// This module maps the data structure of different versions of goblin to the
// same internal structure.
use crate::machine::VERSION1;
use crate::memory::{round_page_down, round_page_up, FLAG_EXECUTABLE, FLAG_FREEZED};
use crate::{Error, Register};
use bytes::Bytes;
use scroll::Pread;
use std::ops::Range;

// Even for different versions of goblin, their values must be consistent.
pub use goblin_v023::elf::program_header::{PF_R, PF_W, PF_X, PT_LOAD};
pub use goblin_v023::elf::section_header::SHF_EXECINSTR;

/// Converts goblin's ELF flags into RISC-V flags
pub fn convert_flags(p_flags: u32, allow_freeze_writable: bool) -> Result<u8, Error> {
    let readable = p_flags & PF_R != 0;
    let writable = p_flags & PF_W != 0;
    let executable = p_flags & PF_X != 0;
    if !readable {
        return Err(Error::ElfSegmentUnreadable);
    }
    if writable && executable {
        return Err(Error::ElfSegmentWritableAndExecutable);
    }
    if executable {
        Ok(FLAG_EXECUTABLE | FLAG_FREEZED)
    } else if writable && !allow_freeze_writable {
        Ok(0)
    } else {
        Ok(FLAG_FREEZED)
    }
}

/// Same as goblin::elf::ProgramHeader.
pub struct ProgramHeader {
    pub p_type: u32,
    pub p_flags: u32,
    pub p_offset: u64,
    pub p_vaddr: u64,
    pub p_paddr: u64,
    pub p_filesz: u64,
    pub p_memsz: u64,
    pub p_align: u64,
}

impl ProgramHeader {
    pub fn from_v0(header: &goblin_v023::elf::ProgramHeader) -> Self {
        Self {
            p_type: header.p_type,
            p_flags: header.p_flags,
            p_offset: header.p_offset,
            p_vaddr: header.p_vaddr,
            p_paddr: header.p_paddr,
            p_filesz: header.p_filesz,
            p_memsz: header.p_memsz,
            p_align: header.p_align,
        }
    }

    pub fn from_v1(header: &goblin_v040::elf::ProgramHeader) -> Self {
        Self {
            p_type: header.p_type,
            p_flags: header.p_flags,
            p_offset: header.p_offset,
            p_vaddr: header.p_vaddr,
            p_paddr: header.p_paddr,
            p_filesz: header.p_filesz,
            p_memsz: header.p_memsz,
            p_align: header.p_align,
        }
    }
}

/// Same as goblin::elf::SectionHeader.
pub struct SectionHeader {
    pub sh_name: usize,
    pub sh_type: u32,
    pub sh_flags: u64,
    pub sh_addr: u64,
    pub sh_offset: u64,
    pub sh_size: u64,
    pub sh_link: u32,
    pub sh_info: u32,
    pub sh_addralign: u64,
    pub sh_entsize: u64,
}

impl SectionHeader {
    pub fn from_v0(header: &goblin_v023::elf::SectionHeader) -> Self {
        Self {
            sh_name: header.sh_name,
            sh_type: header.sh_type,
            sh_flags: header.sh_flags,
            sh_addr: header.sh_addr,
            sh_offset: header.sh_offset,
            sh_size: header.sh_size,
            sh_link: header.sh_link,
            sh_info: header.sh_info,
            sh_addralign: header.sh_addralign,
            sh_entsize: header.sh_entsize,
        }
    }

    pub fn from_v1(header: &goblin_v040::elf::SectionHeader) -> Self {
        Self {
            sh_name: header.sh_name,
            sh_type: header.sh_type,
            sh_flags: header.sh_flags,
            sh_addr: header.sh_addr,
            sh_offset: header.sh_offset,
            sh_size: header.sh_size,
            sh_link: header.sh_link,
            sh_info: header.sh_info,
            sh_addralign: header.sh_addralign,
            sh_entsize: header.sh_entsize,
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LoadingAction {
    pub addr: u64,
    pub size: u64,
    pub flags: u8,
    pub source: Range<u64>,
    pub offset_from_addr: u64,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ProgramMetadata {
    pub actions: Vec<LoadingAction>,
    pub entry: u64,
}

pub fn parse_elf<R: Register>(program: &Bytes, version: u32) -> Result<ProgramMetadata, Error> {
    // We did not use Elf::parse here to avoid triggering potential bugs in goblin.
    // * https://github.com/nervosnetwork/ckb-vm/issues/143
    let (entry, program_headers): (u64, Vec<ProgramHeader>) = if version < VERSION1 {
        use goblin_v023::container::Ctx;
        use goblin_v023::elf::{program_header::ProgramHeader as GoblinProgramHeader, Header};
        let header = program.pread::<Header>(0)?;
        let container = header.container().map_err(|_e| Error::ElfBits)?;
        let endianness = header.endianness().map_err(|_e| Error::ElfBits)?;
        if R::BITS != if container.is_big() { 64 } else { 32 } {
            return Err(Error::ElfBits);
        }
        let ctx = Ctx::new(container, endianness);
        let program_headers = GoblinProgramHeader::parse(
            program,
            header.e_phoff as usize,
            header.e_phnum as usize,
            ctx,
        )?
        .iter()
        .map(ProgramHeader::from_v0)
        .collect();
        (header.e_entry, program_headers)
    } else {
        use goblin_v040::container::Ctx;
        use goblin_v040::elf::{program_header::ProgramHeader as GoblinProgramHeader, Header};
        let header = program.pread::<Header>(0)?;
        let container = header.container().map_err(|_e| Error::ElfBits)?;
        let endianness = header.endianness().map_err(|_e| Error::ElfBits)?;
        if R::BITS != if container.is_big() { 64 } else { 32 } {
            return Err(Error::ElfBits);
        }
        let ctx = Ctx::new(container, endianness);
        let program_headers = GoblinProgramHeader::parse(
            program,
            header.e_phoff as usize,
            header.e_phnum as usize,
            ctx,
        )?
        .iter()
        .map(ProgramHeader::from_v1)
        .collect();
        (header.e_entry, program_headers)
    };
    let mut bytes: u64 = 0;
    let mut actions = vec![];
    for program_header in program_headers {
        if program_header.p_type == PT_LOAD {
            let aligned_start = round_page_down(program_header.p_vaddr);
            let padding_start = program_header.p_vaddr.wrapping_sub(aligned_start);
            let size = round_page_up(program_header.p_memsz.wrapping_add(padding_start));
            let slice_start = program_header.p_offset;
            let slice_end = program_header
                .p_offset
                .wrapping_add(program_header.p_filesz);
            if slice_start > slice_end || slice_end > program.len() as u64 {
                return Err(Error::ElfSegmentAddrOrSizeError);
            }
            actions.push(LoadingAction {
                addr: aligned_start,
                size,
                flags: convert_flags(program_header.p_flags, version < VERSION1)?,
                source: slice_start..slice_end,
                offset_from_addr: padding_start,
            });
            bytes = bytes.checked_add(slice_end - slice_start).ok_or_else(|| {
                Error::Unexpected(String::from("The bytes count overflowed on loading elf"))
            })?;
        }
    }
    Ok(ProgramMetadata { actions, entry })
}