moto_runtime/external/elfloader/
binary.rs1use super::{
2 DynamicFlags1, DynamicInfo, ElfLoader, ElfLoaderErr, LoadableHeaders, RelocationEntry,
3 RelocationType,
4};
5use core::fmt;
6
7use crate::external::xmas_elf;
8
9use xmas_elf::dynamic::Tag;
10use xmas_elf::program::ProgramHeader::{self, Ph32, Ph64};
11use xmas_elf::program::{ProgramIter, SegmentData, Type};
12use xmas_elf::sections::SectionData;
13pub use xmas_elf::symbol_table::Entry;
14use xmas_elf::ElfFile;
15use xmas_elf::*;
16
17pub struct ElfBinary<'s> {
19 pub file: ElfFile<'s>,
21 pub dynamic: Option<DynamicInfo>,
23}
24
25impl<'s> fmt::Debug for ElfBinary<'s> {
26 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
27 write!(f, "ElfBinary{{ [")?;
28 for p in self.program_headers() {
29 write!(f, " pheader = {}", p)?;
30 }
31 write!(f, "] }}")
32 }
33}
34
35impl<'s> ElfBinary<'s> {
36 pub fn new(region: &'s [u8]) -> Result<ElfBinary<'s>, ElfLoaderErr> {
38 let file = ElfFile::new(region)?;
39
40 let mut dynamic = None;
42 for p in file.program_iter() {
43 let typ = match p {
44 Ph64(header) => header.get_type()?,
45 Ph32(header) => header.get_type()?,
46 };
47
48 if typ == Type::Dynamic {
49 dynamic = ElfBinary::parse_dynamic(&file, &p)?;
50 break;
51 }
52 }
53
54 Ok(ElfBinary { file, dynamic })
55 }
56
57 pub fn is_pie(&self) -> bool {
62 self.dynamic.as_ref().map_or(false, |d: &DynamicInfo| {
63 d.flags1.contains(DynamicFlags1::PIE)
64 })
65 }
66
67 pub fn interpreter(&'s self) -> Option<&'s str> {
73 let section = self.file.find_section_by_name(".interp");
74 section.and_then(|interp_section| {
75 let data = interp_section.get_data(&self.file).ok()?;
76 let cstr = match data {
77 SectionData::Undefined(val) => val,
78 _ => return None,
79 };
80
81 if cstr.len() < 2 {
83 return None;
84 }
85
86 core::str::from_utf8(&cstr[..cstr.len() - 1]).ok()
88 })
89 }
90
91 pub fn get_arch(&self) -> header::Machine {
93 self.file.header.pt2.machine().as_machine()
94 }
95
96 pub fn entry_point(&self) -> u64 {
100 self.file.header.pt2.entry_point()
101 }
102
103 pub fn program_headers(&self) -> ProgramIter {
105 self.file.program_iter()
106 }
107
108 pub fn symbol_name(&self, symbol: &'s dyn Entry) -> &'s str {
110 symbol.get_name(&self.file).unwrap_or("unknown")
111 }
112
113 pub fn for_each_symbol<F: FnMut(&'s dyn Entry)>(
115 &self,
116 mut func: F,
117 ) -> Result<(), ElfLoaderErr> {
118 let symbol_section = self
119 .file
120 .find_section_by_name(".symtab")
121 .ok_or(ElfLoaderErr::SymbolTableNotFound)?;
122 let symbol_table = symbol_section.get_data(&self.file)?;
123 match symbol_table {
124 SectionData::SymbolTable32(entries) => {
125 for entry in entries {
126 func(entry);
127 }
128 Ok(())
129 }
130 SectionData::SymbolTable64(entries) => {
131 for entry in entries {
132 func(entry);
133 }
134 Ok(())
135 }
136 _ => Err(ElfLoaderErr::SymbolTableNotFound),
137 }
138 }
139
140 fn is_loadable(&self) -> Result<(), ElfLoaderErr> {
142 let header = self.file.header;
143 let typ = header.pt2.type_().as_type();
144
145 if header.pt1.version() != header::Version::Current {
146 Err(ElfLoaderErr::UnsupportedElfVersion)
147 } else if header.pt1.data() != header::Data::LittleEndian {
148 Err(ElfLoaderErr::UnsupportedEndianness)
149 } else if !(header.pt1.os_abi() == header::OsAbi::SystemV
150 || header.pt1.os_abi() == header::OsAbi::Linux)
151 {
152 Err(ElfLoaderErr::UnsupportedAbi)
153 } else if !(typ == header::Type::Executable || typ == header::Type::SharedObject) {
154 Err(ElfLoaderErr::UnsupportedElfType)
155 } else {
156 Ok(())
157 }
158 }
159
160 fn maybe_relocate(&self, loader: &mut dyn ElfLoader) -> Result<(), ElfLoaderErr> {
164 let arch = self.get_arch();
166
167 let relocation_section = self
171 .file
172 .find_section_by_name(".rela.dyn")
173 .or_else(|| self.file.find_section_by_name(".rel.dyn"));
174
175 macro_rules! iter_entries_and_relocate {
177 ($rela_entries:expr, $create_addend:ident) => {
178 for entry in $rela_entries {
179 loader.relocate(RelocationEntry {
180 rtype: RelocationType::from(arch, entry.get_type() as u32)?,
181 offset: entry.get_offset() as u64,
182 index: entry.get_symbol_table_index(),
183 addend: $create_addend!(entry),
184 })?;
185 }
186 };
187 }
188
189 macro_rules! rel_entry {
191 ($entry:ident) => {
192 None
193 };
194 }
195
196 macro_rules! rela_entry {
198 ($entry:ident) => {
199 Some($entry.get_addend() as u64)
200 };
201 }
202
203 relocation_section.map_or(Ok(()), |rela_section_dyn| {
205 let data = rela_section_dyn.get_data(&self.file)?;
206 match data {
207 SectionData::Rel32(rel_entries) => {
208 iter_entries_and_relocate!(rel_entries, rel_entry);
209 }
210 SectionData::Rela32(rela_entries) => {
211 iter_entries_and_relocate!(rela_entries, rela_entry);
212 }
213 SectionData::Rel64(rel_entries) => {
214 iter_entries_and_relocate!(rel_entries, rel_entry);
215 }
216 SectionData::Rela64(rela_entries) => {
217 iter_entries_and_relocate!(rela_entries, rela_entry);
218 }
219 _ => return Err(ElfLoaderErr::UnsupportedSectionData),
220 }
221 Ok(())
222 })
223 }
224
225 fn parse_dynamic<'a>(
232 file: &ElfFile,
233 dynamic_header: &'a ProgramHeader<'a>,
234 ) -> Result<Option<DynamicInfo>, ElfLoaderErr> {
235 let segment = dynamic_header.get_data(file)?;
237
238 let mut info = DynamicInfo {
240 flags1: Default::default(),
241 rela: 0,
242 rela_size: 0,
243 };
244
245 macro_rules! parse_entry_tags {
247 ($info:ident, $entry:ident, $tag:ident) => {
248 match $tag {
249 Tag::Needed => {}
251
252 Tag::Rel => $info.rela = $entry.get_ptr()?.into(),
254 Tag::RelSize => $info.rela_size = $entry.get_val()?.into(),
255
256 Tag::Rela => $info.rela = $entry.get_ptr()?.into(),
258 Tag::RelaSize => $info.rela_size = $entry.get_val()?.into(),
259 Tag::Flags1 => {
260 $info.flags1 =
261 unsafe { DynamicFlags1::from_bits_unchecked($entry.get_val()? as _) };
262 }
263 _ => {}
264 }
265 };
266 }
267
268 macro_rules! iter_entries_and_parse {
270 ($info:ident, $dyn_entries:expr) => {
271 for dyn_entry in $dyn_entries {
272 let tag = dyn_entry.get_tag()?;
273 parse_entry_tags!($info, dyn_entry, tag);
274 }
275 };
276 }
277
278 match segment {
279 SegmentData::Dynamic32(dyn_entries) => {
280 iter_entries_and_parse!(info, dyn_entries);
281 }
282 SegmentData::Dynamic64(dyn_entries) => {
283 iter_entries_and_parse!(info, dyn_entries);
284 }
285 _ => {
286 return Err(ElfLoaderErr::UnsupportedSectionData);
287 }
288 };
289
290 Ok(Some(info))
291 }
292
293 pub fn load(&self, loader: &mut dyn ElfLoader) -> Result<(), ElfLoaderErr> {
298 self.is_loadable()?;
299
300 loader.allocate(self.iter_loadable_headers())?;
301
302 for header in self.file.program_iter() {
304 let raw = match header {
305 Ph32(inner) => inner.raw_data(&self.file),
306 Ph64(inner) => inner.raw_data(&self.file),
307 };
308 let typ = header.get_type()?;
309 match typ {
310 Type::Load => {
311 loader.load(header.flags(), header.virtual_addr(), raw)?;
312 }
313 Type::Tls => {
314 loader.tls(
315 header.virtual_addr(),
316 header.file_size(),
317 header.mem_size(),
318 header.align(),
319 )?;
320 }
321 _ => {} }
323 }
324
325 self.maybe_relocate(loader)?;
327
328 for header in self.file.program_iter() {
330 if header.get_type()? == Type::GnuRelro {
331 loader.make_readonly(header.virtual_addr(), header.mem_size() as usize)?
332 }
333 }
334
335 Ok(())
336 }
337
338 fn iter_loadable_headers(&self) -> LoadableHeaders {
339 fn select_load(pheader: &ProgramHeader) -> bool {
341 match pheader {
342 Ph32(header) => header
343 .get_type()
344 .map(|typ| typ == Type::Load)
345 .unwrap_or(false),
346 Ph64(header) => header
347 .get_type()
348 .map(|typ| typ == Type::Load)
349 .unwrap_or(false),
350 }
351 }
352
353 self.file.program_iter().filter(select_load)
358 }
359}