1use 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
10pub use goblin_v023::elf::program_header::{PF_R, PF_W, PF_X, PT_LOAD};
12pub use goblin_v023::elf::section_header::SHF_EXECINSTR;
13
14pub 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
34pub 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
74pub 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 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}