ckb_vm/
elf.rs

1// This module maps the data structure of different versions of goblin to the
2// same internal structure.
3use crate::machine::VERSION1;
4use crate::memory::{round_page_down, round_page_up, FLAG_EXECUTABLE, FLAG_FREEZED};
5use crate::{Error, Register};
6use bytes::Bytes;
7use scroll::Pread;
8use std::ops::Range;
9
10// Even for different versions of goblin, their values must be consistent.
11pub use goblin_v023::elf::program_header::{PF_R, PF_W, PF_X, PT_LOAD};
12pub use goblin_v023::elf::section_header::SHF_EXECINSTR;
13
14/// Converts goblin's ELF flags into RISC-V flags
15pub fn convert_flags(p_flags: u32, allow_freeze_writable: bool) -> Result<u8, Error> {
16    let readable = p_flags & PF_R != 0;
17    let writable = p_flags & PF_W != 0;
18    let executable = p_flags & PF_X != 0;
19    if !readable {
20        return Err(Error::ElfSegmentUnreadable);
21    }
22    if writable && executable {
23        return Err(Error::ElfSegmentWritableAndExecutable);
24    }
25    if executable {
26        Ok(FLAG_EXECUTABLE | FLAG_FREEZED)
27    } else if writable && !allow_freeze_writable {
28        Ok(0)
29    } else {
30        Ok(FLAG_FREEZED)
31    }
32}
33
34/// Same as goblin::elf::ProgramHeader.
35pub struct ProgramHeader {
36    pub p_type: u32,
37    pub p_flags: u32,
38    pub p_offset: u64,
39    pub p_vaddr: u64,
40    pub p_paddr: u64,
41    pub p_filesz: u64,
42    pub p_memsz: u64,
43    pub p_align: u64,
44}
45
46impl ProgramHeader {
47    pub fn from_v0(header: &goblin_v023::elf::ProgramHeader) -> Self {
48        Self {
49            p_type: header.p_type,
50            p_flags: header.p_flags,
51            p_offset: header.p_offset,
52            p_vaddr: header.p_vaddr,
53            p_paddr: header.p_paddr,
54            p_filesz: header.p_filesz,
55            p_memsz: header.p_memsz,
56            p_align: header.p_align,
57        }
58    }
59
60    pub fn from_v1(header: &goblin_v040::elf::ProgramHeader) -> Self {
61        Self {
62            p_type: header.p_type,
63            p_flags: header.p_flags,
64            p_offset: header.p_offset,
65            p_vaddr: header.p_vaddr,
66            p_paddr: header.p_paddr,
67            p_filesz: header.p_filesz,
68            p_memsz: header.p_memsz,
69            p_align: header.p_align,
70        }
71    }
72}
73
74/// Same as goblin::elf::SectionHeader.
75pub struct SectionHeader {
76    pub sh_name: usize,
77    pub sh_type: u32,
78    pub sh_flags: u64,
79    pub sh_addr: u64,
80    pub sh_offset: u64,
81    pub sh_size: u64,
82    pub sh_link: u32,
83    pub sh_info: u32,
84    pub sh_addralign: u64,
85    pub sh_entsize: u64,
86}
87
88impl SectionHeader {
89    pub fn from_v0(header: &goblin_v023::elf::SectionHeader) -> Self {
90        Self {
91            sh_name: header.sh_name,
92            sh_type: header.sh_type,
93            sh_flags: header.sh_flags,
94            sh_addr: header.sh_addr,
95            sh_offset: header.sh_offset,
96            sh_size: header.sh_size,
97            sh_link: header.sh_link,
98            sh_info: header.sh_info,
99            sh_addralign: header.sh_addralign,
100            sh_entsize: header.sh_entsize,
101        }
102    }
103
104    pub fn from_v1(header: &goblin_v040::elf::SectionHeader) -> Self {
105        Self {
106            sh_name: header.sh_name,
107            sh_type: header.sh_type,
108            sh_flags: header.sh_flags,
109            sh_addr: header.sh_addr,
110            sh_offset: header.sh_offset,
111            sh_size: header.sh_size,
112            sh_link: header.sh_link,
113            sh_info: header.sh_info,
114            sh_addralign: header.sh_addralign,
115            sh_entsize: header.sh_entsize,
116        }
117    }
118}
119
120#[derive(Clone, Debug, PartialEq, Eq)]
121pub struct LoadingAction {
122    pub addr: u64,
123    pub size: u64,
124    pub flags: u8,
125    pub source: Range<u64>,
126    pub offset_from_addr: u64,
127}
128
129#[derive(Clone, Debug, PartialEq, Eq)]
130pub struct ProgramMetadata {
131    pub actions: Vec<LoadingAction>,
132    pub entry: u64,
133}
134
135pub fn parse_elf<R: Register>(program: &Bytes, version: u32) -> Result<ProgramMetadata, Error> {
136    // We did not use Elf::parse here to avoid triggering potential bugs in goblin.
137    // * https://github.com/nervosnetwork/ckb-vm/issues/143
138    let (entry, program_headers): (u64, Vec<ProgramHeader>) = if version < VERSION1 {
139        use goblin_v023::container::Ctx;
140        use goblin_v023::elf::{program_header::ProgramHeader as GoblinProgramHeader, Header};
141        let header = program.pread::<Header>(0)?;
142        let container = header.container().map_err(|_e| Error::ElfBits)?;
143        let endianness = header.endianness().map_err(|_e| Error::ElfBits)?;
144        if R::BITS != if container.is_big() { 64 } else { 32 } {
145            return Err(Error::ElfBits);
146        }
147        let ctx = Ctx::new(container, endianness);
148        let program_headers = GoblinProgramHeader::parse(
149            program,
150            header.e_phoff as usize,
151            header.e_phnum as usize,
152            ctx,
153        )?
154        .iter()
155        .map(ProgramHeader::from_v0)
156        .collect();
157        (header.e_entry, program_headers)
158    } else {
159        use goblin_v040::container::Ctx;
160        use goblin_v040::elf::{program_header::ProgramHeader as GoblinProgramHeader, Header};
161        let header = program.pread::<Header>(0)?;
162        let container = header.container().map_err(|_e| Error::ElfBits)?;
163        let endianness = header.endianness().map_err(|_e| Error::ElfBits)?;
164        if R::BITS != if container.is_big() { 64 } else { 32 } {
165            return Err(Error::ElfBits);
166        }
167        let ctx = Ctx::new(container, endianness);
168        let program_headers = GoblinProgramHeader::parse(
169            program,
170            header.e_phoff as usize,
171            header.e_phnum as usize,
172            ctx,
173        )?
174        .iter()
175        .map(ProgramHeader::from_v1)
176        .collect();
177        (header.e_entry, program_headers)
178    };
179    let mut bytes: u64 = 0;
180    let mut actions = vec![];
181    for program_header in program_headers {
182        if program_header.p_type == PT_LOAD {
183            let aligned_start = round_page_down(program_header.p_vaddr);
184            let padding_start = program_header.p_vaddr.wrapping_sub(aligned_start);
185            let size = round_page_up(program_header.p_memsz.wrapping_add(padding_start));
186            let slice_start = program_header.p_offset;
187            let slice_end = program_header
188                .p_offset
189                .wrapping_add(program_header.p_filesz);
190            if slice_start > slice_end || slice_end > program.len() as u64 {
191                return Err(Error::ElfSegmentAddrOrSizeError);
192            }
193            actions.push(LoadingAction {
194                addr: aligned_start,
195                size,
196                flags: convert_flags(program_header.p_flags, version < VERSION1)?,
197                source: slice_start..slice_end,
198                offset_from_addr: padding_start,
199            });
200            bytes = bytes.checked_add(slice_end - slice_start).ok_or_else(|| {
201                Error::Unexpected(String::from("The bytes count overflowed on loading elf"))
202            })?;
203        }
204    }
205    Ok(ProgramMetadata { actions, entry })
206}