1#[macro_use]
41pub(crate) mod gnu_hash;
42
43pub mod compression_header;
47pub mod header;
48pub mod program_header;
49pub mod section_header;
50#[macro_use]
51pub mod sym;
52pub mod dynamic;
53#[macro_use]
54pub mod reloc;
55pub mod note;
56#[cfg(all(any(feature = "elf32", feature = "elf64"), feature = "alloc"))]
57pub mod symver;
58
59macro_rules! if_sylvan {
60 ($($i:item)*) => ($(
61 #[cfg(all(feature = "elf32", feature = "elf64", feature = "endian_fd"))]
62 $i
63 )*)
64}
65
66if_sylvan! {
67 use scroll::{ctx, Pread, Endian};
68 use crate::strtab::Strtab;
69 use crate::error;
70 use crate::options::{Permissive, ParseOptions};
71 use crate::container::{Container, Ctx};
72 use alloc::vec::Vec;
73 use core::cmp;
74
75 pub use header::Header;
76 pub use program_header::ProgramHeader;
77 pub use section_header::SectionHeader;
78 pub use sym::Symtab;
79 pub use sym::Sym;
80 pub use dynamic::Dyn;
81 pub use dynamic::Dynamic;
82 pub use reloc::Reloc;
83 pub use reloc::RelocSection;
84 pub use symver::{VersymSection, VerdefSection, VerneedSection};
85
86 pub type ProgramHeaders = Vec<ProgramHeader>;
87 pub type SectionHeaders = Vec<SectionHeader>;
88 pub type ShdrIdx = usize;
89
90 #[derive(Debug)]
91 pub struct Elf<'a> {
93 pub header: Header,
95 pub program_headers: ProgramHeaders,
98 pub section_headers: SectionHeaders,
101 pub shdr_strtab: Strtab<'a>,
103 pub dynstrtab: Strtab<'a>,
105 pub dynsyms: Symtab<'a>,
109 pub syms: Symtab<'a>,
111 pub strtab: Strtab<'a>,
113 pub dynamic: Option<Dynamic>,
115 pub dynrelas: RelocSection<'a>,
117 pub dynrels: RelocSection<'a>,
119 pub pltrelocs: RelocSection<'a>,
121 pub shdr_relocs: Vec<(ShdrIdx, RelocSection<'a>)>,
123 pub soname: Option<&'a str>,
125 pub interpreter: Option<&'a str>,
127 pub libraries: Vec<&'a str>,
129 pub rpaths: Vec<&'a str>,
132 pub runpaths: Vec<&'a str>,
135 pub is_64: bool,
137 pub is_lib: bool,
139 pub entry: u64,
141 pub little_endian: bool,
143 pub versym : Option<VersymSection<'a>>,
146 pub verdef : Option<VerdefSection<'a>>,
149 pub verneed : Option<VerneedSection<'a>>,
152 ctx: Ctx,
153 }
154
155 impl<'a> Elf<'a> {
156 pub fn iter_note_headers(&self, data: &'a [u8]) -> Option<note::NoteIterator<'a>> {
158 let mut iters = vec![];
159 for phdr in &self.program_headers {
160 if phdr.p_type == program_header::PT_NOTE {
161 let offset = phdr.p_offset as usize;
162 let alignment = phdr.p_align as usize;
163
164 iters.push(note::NoteDataIterator {
165 data,
166 offset,
167 size: offset.saturating_add(phdr.p_filesz as usize),
168 ctx: (alignment, self.ctx)
169 });
170 }
171 }
172
173 if iters.is_empty() {
174 None
175 } else {
176 Some(note::NoteIterator {
177 iters: iters,
178 index: 0,
179 })
180 }
181 }
182 pub fn iter_note_sections(
186 &self,
187 data: &'a [u8],
188 section_name: Option<&str>,
189 ) -> Option<note::NoteIterator<'a>> {
190 let mut iters = vec![];
191 for sect in &self.section_headers {
192 if sect.sh_type != section_header::SHT_NOTE {
193 continue;
194 }
195
196 if section_name.is_some() && self.shdr_strtab.get_at(sect.sh_name) != section_name {
197 continue;
198 }
199
200 let offset = sect.sh_offset as usize;
201 let alignment = sect.sh_addralign as usize;
202 iters.push(note::NoteDataIterator {
203 data,
204 offset,
205 size: offset.saturating_add(sect.sh_size as usize),
206 ctx: (alignment, self.ctx)
207 });
208 }
209
210 if iters.is_empty() {
211 None
212 } else {
213 Some(note::NoteIterator {
214 iters: iters,
215 index: 0,
216 })
217 }
218 }
219 pub fn is_object_file(&self) -> bool {
220 self.header.e_type == header::ET_REL
221 }
222
223 pub fn parse_header(bytes: &'a [u8]) -> error::Result<Header> {
225 bytes.pread::<Header>(0)
226 }
227
228 pub fn lazy_parse(header: Header) -> error::Result<Self> {
230 let misc = parse_misc(&header)?;
231
232 Ok(Elf {
233 header,
234 program_headers: vec![],
235 section_headers: Default::default(),
236 shdr_strtab: Default::default(),
237 dynamic: None,
238 dynsyms: Default::default(),
239 dynstrtab: Strtab::default(),
240 syms: Default::default(),
241 strtab: Default::default(),
242 dynrelas: Default::default(),
243 dynrels: Default::default(),
244 pltrelocs: Default::default(),
245 shdr_relocs: Default::default(),
246 soname: None,
247 interpreter: None,
248 libraries: vec![],
249 rpaths: vec![],
250 runpaths: vec![],
251 is_64: misc.is_64,
252 is_lib: misc.is_lib,
253 entry: misc.entry,
254 little_endian: misc.little_endian,
255 ctx: misc.ctx,
256 versym: None,
257 verdef: None,
258 verneed: None,
259 })
260 }
261
262 pub fn parse(bytes: &'a [u8]) -> error::Result<Self> {
264 Self::parse_with_opts(bytes, &ParseOptions::default())
265 }
266
267 pub fn parse_with_opts(bytes: &'a [u8], opts: &ParseOptions) -> error::Result<Self> {
269 let header = Self::parse_header(bytes)?;
270 let misc = parse_misc(&header)?;
271 let ctx = misc.ctx;
272 let permissive = opts.parse_mode.is_permissive();
273
274 let program_headers = ProgramHeader::parse(bytes, header.e_phoff as usize, header.e_phnum as usize, ctx)?;
275
276 let mut interpreter = None;
277 for ph in &program_headers {
278 if ph.p_type == program_header::PT_INTERP && ph.p_filesz != 0 {
279 let count = (ph.p_filesz - 1) as usize;
280 let offset = ph.p_offset as usize;
281 interpreter = bytes.pread_with::<&str>(offset, ::scroll::ctx::StrCtx::Length(count)).ok();
282 }
283 }
284
285 let section_headers = SectionHeader::parse(bytes, header.e_shoff as usize, header.e_shnum as usize, ctx)
286 .or_permissive_and_default(permissive, "Failed to parse section headers")?;
287
288 let get_strtab = |section_headers: &[SectionHeader], mut section_idx: usize| {
289 if section_idx == section_header::SHN_XINDEX as usize {
290 if section_headers.is_empty() {
291 return Ok(Strtab::default())
292 }
293 section_idx = section_headers[0].sh_link as usize;
294 }
295
296 if section_idx >= section_headers.len() {
297 Ok(Strtab::default())
299 } else {
300 let shdr = §ion_headers[section_idx];
301 shdr.check_size_with_opts(bytes.len(), permissive)?;
302 Strtab::parse_with_opts(bytes, shdr.sh_offset as usize, shdr.sh_size as usize, 0x0, opts)
303 }
304 };
305
306 let strtab_idx = header.e_shstrndx as usize;
307 let shdr_strtab = get_strtab(§ion_headers, strtab_idx)?;
308
309 let mut syms = Symtab::default();
310 let mut strtab = Strtab::default();
311 if let Some(shdr) = section_headers.iter().rfind(|shdr| shdr.sh_type as u32 == section_header::SHT_SYMTAB) {
312 let size = shdr.sh_entsize;
313 let initial_count = if size == 0 { 0 } else { shdr.sh_size / size };
314
315 let count = if initial_count > usize::MAX as u64 {
317
318 Err(crate::error::Error::Malformed(
319 format!(
320 "Symbol table count ({}) from section header exceeds maximum possible value",
321 initial_count
322 )
323 ))
324 .or_permissive_and_then(
325 permissive,
326 "Symbol table count exceeds maximum; truncating",
327 || usize::MAX as u64,
328 )?
329 } else {
330 initial_count
331 };
332
333 syms = Symtab::parse_with_opts(bytes, shdr.sh_offset as usize, count as usize, ctx, opts)?;
334 strtab = get_strtab(§ion_headers, shdr.sh_link as usize)?;
335 }
336
337 let mut is_pie = false;
338 let mut soname = None;
339 let mut libraries = vec![];
340 let mut rpaths = vec![];
341 let mut runpaths = vec![];
342 let mut dynsyms = Symtab::default();
343 let mut dynrelas = RelocSection::default();
344 let mut dynrels = RelocSection::default();
345 let mut pltrelocs = RelocSection::default();
346 let mut dynstrtab = Strtab::default();
347 let dynamic = Dynamic::parse(bytes, &program_headers, ctx)
348 .or_permissive_and_default(permissive, "Failed to parse dynamic section")?;
349 if let Some(ref dynamic) = dynamic {
350 let dyn_info = &dynamic.info;
351
352 is_pie = dyn_info.flags_1 & dynamic::DF_1_PIE != 0;
353 dynstrtab = Strtab::parse_with_opts(bytes,
354 dyn_info.strtab,
355 dyn_info.strsz,
356 0x0, opts)
357 .or_permissive_and_default(permissive, "Failed to parse dynamic string table")?;
358
359 if dyn_info.soname != 0 {
360 soname = dynstrtab.get_at(dyn_info.soname);
362 }
363 if dyn_info.needed_count > 0 {
364 libraries = dynamic.get_libraries(&dynstrtab);
365 }
366 for dyn_ in &dynamic.dyns {
367 if dyn_.d_tag == dynamic::DT_RPATH {
368 if let Some(path) = dynstrtab.get_at(dyn_.d_val as usize) {
369 rpaths.push(path);
370 }
371 } else if dyn_.d_tag == dynamic::DT_RUNPATH {
372 if let Some(path) = dynstrtab.get_at(dyn_.d_val as usize) {
373 runpaths.push(path);
374 }
375 }
376 }
377 dynrelas = RelocSection::parse(bytes, dyn_info.rela, dyn_info.relasz, true, ctx)
379 .or_permissive_and_default(permissive, "Failed to parse dynamic RELA relocations")?;
380
381 dynrels = RelocSection::parse(bytes, dyn_info.rel, dyn_info.relsz, false, ctx)
382 .or_permissive_and_default(permissive, "Failed to parse dynamic REL relocations")?;
383
384 let is_rela = dyn_info.pltrel as u64 == dynamic::DT_RELA;
385 pltrelocs = RelocSection::parse(bytes, dyn_info.jmprel, dyn_info.pltrelsz, is_rela, ctx)
386 .or_permissive_and_default(permissive, "Failed to parse PLT relocations")?;
387
388 let mut num_syms = if let Some(gnu_hash) = dyn_info.gnu_hash {
389 gnu_hash_len(bytes, gnu_hash as usize, ctx)
390 .or_permissive_and_default(permissive, "Failed to parse GNU hash table")?
391 } else if let Some(hash) = dyn_info.hash {
392 hash_len(bytes, hash as usize, header.e_machine, ctx)
393 .or_permissive_and_default(permissive, "Failed to parse hash table")?
394 } else {
395 0
396 };
397 let max_reloc_sym = dynrelas.iter()
398 .chain(dynrels.iter())
399 .chain(pltrelocs.iter())
400 .fold(0, |num, reloc| cmp::max(num, reloc.r_sym));
401 if max_reloc_sym != 0 {
402 num_syms = cmp::max(num_syms, max_reloc_sym + 1);
403 }
404 dynsyms = Symtab::parse_with_opts(bytes, dyn_info.symtab, num_syms, ctx, opts)?;
405 }
406
407 let mut shdr_relocs = vec![];
408 for (idx, section) in section_headers.iter().enumerate() {
409 let is_rela = section.sh_type == section_header::SHT_RELA;
410 if is_rela || section.sh_type == section_header::SHT_REL {
411
412 section.check_size_with_opts(bytes.len(), permissive)?;
413 let sh_relocs_opt = RelocSection::parse(bytes, section.sh_offset as usize, section.sh_size as usize, is_rela, ctx)
414 .map(Some)
415 .or_permissive_and_default(
416 permissive,
417 "Failed to parse section relocation; skipping",
418 )?;
419
420 if let Some(sh_relocs) = sh_relocs_opt {
421 shdr_relocs.push((idx, sh_relocs));
422 }
423 }
424 }
425
426 let versym = symver::VersymSection::parse(bytes, §ion_headers, ctx)
427 .or_permissive_and_default(permissive, "Failed to parse version symbol section")?;
428
429 let verdef = symver::VerdefSection::parse(bytes, §ion_headers, ctx)
430 .or_permissive_and_default(permissive, "Failed to parse version definition section")?;
431
432 let verneed = symver::VerneedSection::parse(bytes, §ion_headers, ctx)
433 .or_permissive_and_default(permissive, "Failed to parse version need section")?;
434
435 let is_lib = misc.is_lib && !is_pie;
436
437 Ok(Elf {
438 header,
439 program_headers,
440 section_headers,
441 shdr_strtab,
442 dynamic,
443 dynsyms,
444 dynstrtab,
445 syms,
446 strtab,
447 dynrelas,
448 dynrels,
449 pltrelocs,
450 shdr_relocs,
451 soname,
452 interpreter,
453 libraries,
454 rpaths,
455 runpaths,
456 is_64: misc.is_64,
457 is_lib,
458 entry: misc.entry,
459 little_endian: misc.little_endian,
460 ctx: ctx,
461 versym,
462 verdef,
463 verneed,
464 })
465 }
466 }
467
468 impl<'a> ctx::TryFromCtx<'a, (usize, Endian)> for Elf<'a> {
469 type Error = crate::error::Error;
470 fn try_from_ctx(src: &'a [u8], (_, _): (usize, Endian)) -> Result<(Elf<'a>, usize), Self::Error> {
471 let elf = Elf::parse(src)?;
472 Ok((elf, src.len()))
473 }
474 }
475
476 fn gnu_hash_len(bytes: &[u8], offset: usize, ctx: Ctx) -> error::Result<usize> {
477 let buckets_num = bytes.pread_with::<u32>(offset, ctx.le)? as usize;
478 let min_chain = bytes.pread_with::<u32>(offset + 4, ctx.le)? as usize;
479 let bloom_size = bytes.pread_with::<u32>(offset + 8, ctx.le)? as usize;
480 if buckets_num == 0 || min_chain == 0 || bloom_size == 0 {
482 return Err(error::Error::Malformed(format!("Invalid DT_GNU_HASH: buckets_num={} min_chain={} bloom_size={}",
483 buckets_num, min_chain, bloom_size)));
484 }
485 let buckets_offset = offset + 16 + bloom_size * if ctx.container.is_big() { 8 } else { 4 };
487 let mut max_chain = 0;
488 for bucket in 0..buckets_num {
489 let chain = bytes.pread_with::<u32>(buckets_offset + bucket * 4, ctx.le)? as usize;
490 if max_chain < chain {
491 max_chain = chain;
492 }
493 }
494 if max_chain < min_chain {
495 return Ok(0);
496 }
497 let mut chain_offset = buckets_offset + buckets_num * 4 + (max_chain - min_chain) * 4;
499 loop {
500 let hash = bytes.pread_with::<u32>(chain_offset, ctx.le)?;
501 max_chain += 1;
502 chain_offset += 4;
503 if hash & 1 != 0 {
504 return Ok(max_chain);
505 }
506 }
507 }
508
509 fn hash_len(bytes: &[u8], offset: usize, machine: u16, ctx: Ctx) -> error::Result<usize> {
510 let nchain = if (machine == header::EM_FAKE_ALPHA || machine == header::EM_S390) && ctx.container.is_big() {
512 bytes.pread_with::<u64>(offset.saturating_add(4), ctx.le)? as usize
513 } else {
514 bytes.pread_with::<u32>(offset.saturating_add(4), ctx.le)? as usize
515 };
516 Ok(nchain)
517 }
518
519 struct Misc {
520 is_64: bool,
521 is_lib: bool,
522 entry: u64,
523 little_endian: bool,
524 ctx: Ctx,
525 }
526
527 fn parse_misc(header: &Header) -> error::Result<Misc> {
528 let entry = header.e_entry as usize;
529 let is_lib = header.e_type == header::ET_DYN;
530 let is_lsb = header.e_ident[header::EI_DATA] == header::ELFDATA2LSB;
531 let endianness = scroll::Endian::from(is_lsb);
532 let class = header.e_ident[header::EI_CLASS];
533 if class != header::ELFCLASS64 && class != header::ELFCLASS32 {
534 return Err(error::Error::Malformed(format!("Unknown values in ELF ident header: class: {} endianness: {}",
535 class,
536 header.e_ident[header::EI_DATA])));
537 }
538 let is_64 = class == header::ELFCLASS64;
539 let container = if is_64 { Container::Big } else { Container::Little };
540 let ctx = Ctx::new(container, endianness);
541
542 Ok(Misc{
543 is_64,
544 is_lib,
545 entry: entry as u64,
546 little_endian:is_lsb,
547 ctx,
548 })
549 }
550}
551
552#[cfg(test)]
553mod tests {
554 use super::*;
555
556 #[test]
557 fn parse_crt1_64bit() {
558 let crt1: Vec<u8> = include!("../../etc/crt1.rs");
559 match Elf::parse(&crt1) {
560 Ok(binary) => {
561 assert!(binary.is_64);
562 assert!(!binary.is_lib);
563 assert_eq!(binary.entry, 0);
564 assert!(binary.syms.get(1000).is_none());
565 assert!(binary.syms.get(5).is_some());
566 let syms = binary.syms.to_vec();
567 assert!(!binary.section_headers.is_empty());
568 for (i, sym) in syms.iter().enumerate() {
569 if i == 11 {
570 let symtab = binary.strtab;
571 println!("sym: {:?}", &sym);
572 assert_eq!(&symtab[sym.st_name], "_start");
573 break;
574 }
575 }
576 assert!(!syms.is_empty());
577 }
578 Err(err) => {
579 panic!("failed: {}", err);
580 }
581 }
582 }
583
584 #[test]
585 fn parse_crt1_32bit() {
586 let crt1: Vec<u8> = include!("../../etc/crt132.rs");
587 match Elf::parse(&crt1) {
588 Ok(binary) => {
589 assert!(!binary.is_64);
590 assert!(!binary.is_lib);
591 assert_eq!(binary.entry, 0);
592 assert!(binary.syms.get(1000).is_none());
593 assert!(binary.syms.get(5).is_some());
594 let syms = binary.syms.to_vec();
595 assert!(!binary.section_headers.is_empty());
596 for (i, sym) in syms.iter().enumerate() {
597 if i == 11 {
598 let symtab = binary.strtab;
599 println!("sym: {:?}", &sym);
600 assert_eq!(&symtab[sym.st_name], "__libc_csu_fini");
601 break;
602 }
603 }
604 assert!(!syms.is_empty());
605 }
606 Err(err) => {
607 panic!("failed: {}", err);
608 }
609 }
610 }
611
612 #[test]
614 #[allow(unused)]
615 fn no_use_statement_conflict() {
616 use crate::elf::section_header::*;
617 use crate::elf::*;
618
619 fn f(_: SectionHeader) {}
620 }
621}