1use std::borrow::Cow;
4use std::error::Error;
5use std::ffi::CStr;
6use std::fmt;
7
8use core::cmp;
9use flate2::{Decompress, FlushDecompress};
10use goblin::elf::compression_header::{CompressionHeader, ELFCOMPRESS_ZLIB};
11use goblin::elf::SectionHeader;
12use goblin::elf64::sym::SymIterator;
13use goblin::strtab::Strtab;
14use goblin::{
15 container::{Container, Ctx},
16 elf, strtab,
17};
18use scroll::Pread;
19use thiserror::Error;
20
21use symbolic_common::{Arch, AsSelf, CodeId, DebugId, Uuid};
22
23use crate::base::*;
24use crate::dwarf::{Dwarf, DwarfDebugSession, DwarfError, DwarfSection, Endian};
25use crate::ParseObjectOptions;
26
27const UUID_SIZE: usize = 16;
28const PAGE_SIZE: usize = 4096;
29
30const SHN_UNDEF: usize = elf::section_header::SHN_UNDEF as usize;
31const SHF_COMPRESSED: u64 = elf::section_header::SHF_COMPRESSED as u64;
32
33pub const ELFCOMPRESS_ZSTD: u32 = 2;
35
36#[allow(unused)]
38const EF_MIPS_ABI_O32: u32 = 0x0000_1000;
39const EF_MIPS_ABI_O64: u32 = 0x0000_2000;
41#[allow(unused)]
43const EF_MIPS_ABI_EABI32: u32 = 0x0000_3000;
44const EF_MIPS_ABI_EABI64: u32 = 0x0000_4000;
46
47const MIPS_64_FLAGS: u32 = EF_MIPS_ABI_O64 | EF_MIPS_ABI_EABI64;
49
50#[derive(Debug, Error)]
52#[error("invalid ELF file")]
53pub struct ElfError {
54 #[source]
55 source: Option<Box<dyn Error + Send + Sync + 'static>>,
56}
57
58impl ElfError {
59 fn new<E>(source: E) -> Self
61 where
62 E: Into<Box<dyn Error + Send + Sync>>,
63 {
64 let source = Some(source.into());
65 Self { source }
66 }
67}
68
69pub struct ElfObject<'data> {
71 elf: elf::Elf<'data>,
72 data: &'data [u8],
73 is_malformed: bool,
74 max_decompressed_section_size: Option<usize>,
75}
76
77impl<'data> ElfObject<'data> {
78 pub fn test(data: &[u8]) -> bool {
80 data.get(0..elf::header::SELFMAG)
81 .is_some_and(|data| data == elf::header::ELFMAG)
82 }
83
84 fn gnu_hash_len(bytes: &[u8], offset: usize, ctx: Ctx) -> goblin::error::Result<usize> {
87 let buckets_num = bytes.pread_with::<u32>(offset, ctx.le)? as usize;
88 let min_chain = bytes.pread_with::<u32>(offset + 4, ctx.le)? as usize;
89 let bloom_size = bytes.pread_with::<u32>(offset + 8, ctx.le)? as usize;
90 if buckets_num == 0 || min_chain == 0 || bloom_size == 0 {
92 return Err(goblin::error::Error::Malformed(format!(
93 "Invalid DT_GNU_HASH: buckets_num={buckets_num} min_chain={min_chain} bloom_size={bloom_size}"
94 )));
95 }
96 let buckets_offset = offset + 16 + bloom_size * if ctx.container.is_big() { 8 } else { 4 };
98 let mut max_chain = 0;
99 for bucket in 0..buckets_num {
100 let chain = bytes.pread_with::<u32>(buckets_offset + bucket * 4, ctx.le)? as usize;
101 if max_chain < chain {
102 max_chain = chain;
103 }
104 }
105 if max_chain < min_chain {
106 return Ok(0);
107 }
108 let mut chain_offset = buckets_offset + buckets_num * 4 + (max_chain - min_chain) * 4;
110 loop {
111 let hash = bytes.pread_with::<u32>(chain_offset, ctx.le)?;
112 max_chain += 1;
113 chain_offset += 4;
114 if hash & 1 != 0 {
115 return Ok(max_chain);
116 }
117 }
118 }
119
120 fn hash_len(
123 bytes: &[u8],
124 offset: usize,
125 machine: u16,
126 ctx: Ctx,
127 ) -> goblin::error::Result<usize> {
128 let nchain = if (machine == elf::header::EM_FAKE_ALPHA || machine == elf::header::EM_S390)
130 && ctx.container.is_big()
131 {
132 bytes.pread_with::<u64>(offset.saturating_add(4), ctx.le)? as usize
133 } else {
134 bytes.pread_with::<u32>(offset.saturating_add(4), ctx.le)? as usize
135 };
136 Ok(nchain)
137 }
138
139 pub fn parse_with_opts(data: &'data [u8], opts: ParseObjectOptions) -> Result<Self, ElfError> {
144 let header =
145 elf::Elf::parse_header(data).map_err(|_| ElfError::new("ELF header unreadable"))?;
146 let mut obj =
148 elf::Elf::lazy_parse(header).map_err(|_| ElfError::new("cannot parse ELF header"))?;
149
150 let ctx = Ctx {
151 container: if obj.is_64 {
152 Container::Big
153 } else {
154 Container::Little
155 },
156 le: if obj.little_endian {
157 scroll::Endian::Little
158 } else {
159 scroll::Endian::Big
160 },
161 };
162
163 macro_rules! return_partial_on_err {
164 ($parse_func:expr) => {
165 if let Ok(expected) = $parse_func {
166 expected
167 } else {
168 return Ok(ElfObject {
170 elf: obj,
171 data,
172 is_malformed: true,
173 max_decompressed_section_size: opts.max_decompressed_section_size,
174 });
175 }
176 };
177 }
178
179 obj.program_headers =
180 elf::ProgramHeader::parse(data, header.e_phoff as usize, header.e_phnum as usize, ctx)
181 .map_err(|_| ElfError::new("unable to parse program headers"))?;
182
183 for ph in &obj.program_headers {
184 if ph.p_type == elf::program_header::PT_INTERP && ph.p_filesz != 0 {
185 let count = (ph.p_filesz - 1) as usize;
186 let offset = ph.p_offset as usize;
187 obj.interpreter = data
188 .pread_with::<&str>(offset, ::scroll::ctx::StrCtx::Length(count))
189 .ok();
190 }
191 }
192
193 obj.section_headers =
194 SectionHeader::parse(data, header.e_shoff as usize, header.e_shnum as usize, ctx)
195 .map_err(|_| ElfError::new("unable to parse section headers"))?;
196
197 let get_strtab = |section_headers: &[SectionHeader], section_idx: usize| {
198 if section_idx >= section_headers.len() {
199 Ok(Strtab::default())
201 } else {
202 let shdr = §ion_headers[section_idx];
203 shdr.check_size(data.len())?;
204 Strtab::parse(data, shdr.sh_offset as usize, shdr.sh_size as usize, 0x0)
205 }
206 };
207
208 let strtab_idx = header.e_shstrndx as usize;
209 obj.shdr_strtab = return_partial_on_err!(get_strtab(&obj.section_headers, strtab_idx));
210
211 obj.syms = elf::Symtab::default();
212 obj.strtab = Strtab::default();
213 for shdr in &obj.section_headers {
214 if shdr.sh_type == elf::section_header::SHT_SYMTAB {
215 let count = shdr.sh_size.checked_div(shdr.sh_entsize).unwrap_or(0);
216 obj.syms = return_partial_on_err!(elf::Symtab::parse(
217 data,
218 shdr.sh_offset as usize,
219 count as usize,
220 ctx
221 ));
222
223 obj.strtab =
224 return_partial_on_err!(get_strtab(&obj.section_headers, shdr.sh_link as usize));
225 }
226 }
227
228 obj.soname = None;
229 obj.libraries = vec![];
230 obj.dynsyms = elf::Symtab::default();
231 obj.dynrelas = elf::RelocSection::default();
232 obj.dynrels = elf::RelocSection::default();
233 obj.pltrelocs = elf::RelocSection::default();
234 obj.dynstrtab = Strtab::default();
235 let dynamic = return_partial_on_err!(elf::Dynamic::parse(data, &obj.program_headers, ctx));
236 if let Some(ref dynamic) = dynamic {
237 let dyn_info = &dynamic.info;
238 obj.dynstrtab =
239 return_partial_on_err!(Strtab::parse(data, dyn_info.strtab, dyn_info.strsz, 0x0));
240
241 if dyn_info.soname != 0 {
242 obj.soname = obj.dynstrtab.get_at(dyn_info.soname);
244 }
245 if dyn_info.needed_count > 0 {
246 obj.libraries = dynamic.get_libraries(&obj.dynstrtab);
247 }
248 obj.dynrelas = return_partial_on_err!(elf::RelocSection::parse(
250 data,
251 dyn_info.rela,
252 dyn_info.relasz,
253 true,
254 ctx
255 ));
256 obj.dynrels = return_partial_on_err!(elf::RelocSection::parse(
257 data,
258 dyn_info.rel,
259 dyn_info.relsz,
260 false,
261 ctx
262 ));
263 let is_rela = dyn_info.pltrel == elf::dynamic::DT_RELA;
264 obj.pltrelocs = return_partial_on_err!(elf::RelocSection::parse(
265 data,
266 dyn_info.jmprel,
267 dyn_info.pltrelsz,
268 is_rela,
269 ctx
270 ));
271
272 let mut num_syms = if let Some(gnu_hash) = dyn_info.gnu_hash {
273 return_partial_on_err!(ElfObject::gnu_hash_len(data, gnu_hash as usize, ctx))
274 } else if let Some(hash) = dyn_info.hash {
275 return_partial_on_err!(ElfObject::hash_len(
276 data,
277 hash as usize,
278 header.e_machine,
279 ctx
280 ))
281 } else {
282 0
283 };
284 let max_reloc_sym = obj
285 .dynrelas
286 .iter()
287 .chain(obj.dynrels.iter())
288 .chain(obj.pltrelocs.iter())
289 .fold(0, |num, reloc| cmp::max(num, reloc.r_sym));
290 if max_reloc_sym != 0 {
291 num_syms = cmp::max(num_syms, max_reloc_sym + 1);
292 }
293
294 obj.dynsyms =
295 return_partial_on_err!(elf::Symtab::parse(data, dyn_info.symtab, num_syms, ctx));
296 }
297
298 if obj.dynsyms.is_empty() {
303 if let Some(shdr) = obj
304 .section_headers
305 .iter()
306 .find(|h| h.sh_type == elf::section_header::SHT_DYNSYM)
307 {
308 let count = shdr.sh_size.checked_div(shdr.sh_entsize).unwrap_or(0);
309 obj.dynsyms = return_partial_on_err!(elf::Symtab::parse(
310 data,
311 shdr.sh_offset as usize,
312 count as usize,
313 ctx
314 ));
315
316 obj.dynstrtab =
317 return_partial_on_err!(get_strtab(&obj.section_headers, shdr.sh_link as usize));
318 }
319 }
320
321 obj.shdr_relocs = vec![];
322 for (idx, section) in obj.section_headers.iter().enumerate() {
323 let is_rela = section.sh_type == elf::section_header::SHT_RELA;
324 if is_rela || section.sh_type == elf::section_header::SHT_REL {
325 return_partial_on_err!(section.check_size(data.len()));
326 let sh_relocs = return_partial_on_err!(elf::RelocSection::parse(
327 data,
328 section.sh_offset as usize,
329 section.sh_size as usize,
330 is_rela,
331 ctx,
332 ));
333 obj.shdr_relocs.push((idx, sh_relocs));
334 }
335 }
336
337 obj.versym = return_partial_on_err!(elf::symver::VersymSection::parse(
338 data,
339 &obj.section_headers,
340 ctx
341 ));
342 obj.verdef = return_partial_on_err!(elf::symver::VerdefSection::parse(
343 data,
344 &obj.section_headers,
345 ctx
346 ));
347 obj.verneed = return_partial_on_err!(elf::symver::VerneedSection::parse(
348 data,
349 &obj.section_headers,
350 ctx
351 ));
352
353 Ok(ElfObject {
354 elf: obj,
355 data,
356 is_malformed: false,
357 max_decompressed_section_size: opts.max_decompressed_section_size,
358 })
359 }
360
361 pub fn parse(data: &'data [u8]) -> Result<Self, ElfError> {
366 Self::parse_with_opts(data, ParseObjectOptions::default())
367 }
368 pub fn file_format(&self) -> FileFormat {
370 FileFormat::Elf
371 }
372
373 pub fn code_id(&self) -> Option<CodeId> {
379 self.find_build_id()
380 .filter(|slice| !slice.is_empty())
381 .map(CodeId::from_binary)
382 }
383
384 pub fn debug_link(&self) -> Result<Option<DebugLink<'_>>, DebugLinkError<'_>> {
395 self.section("gnu_debuglink")
396 .map(|section| DebugLink::from_data(section.data, self.endianity()))
397 .transpose()
398 }
399
400 pub fn name(&self) -> Option<&'data str> {
402 self.elf.soname
403 }
404
405 pub fn debug_id(&self) -> DebugId {
414 if let Some(identifier) = self.find_build_id() {
418 return self.compute_debug_id(identifier);
419 }
420
421 if let Some(section) = self.raw_section("text") {
425 let mut hash = [0; UUID_SIZE];
426 for i in 0..std::cmp::min(section.data.len(), PAGE_SIZE) {
427 hash[i % UUID_SIZE] ^= section.data[i];
428 }
429
430 return self.compute_debug_id(&hash);
431 }
432
433 DebugId::default()
434 }
435
436 pub fn arch(&self) -> Arch {
438 match self.elf.header.e_machine {
439 goblin::elf::header::EM_386 => Arch::X86,
440 goblin::elf::header::EM_X86_64 => Arch::Amd64,
441 goblin::elf::header::EM_AARCH64 => Arch::Arm64,
442 goblin::elf::header::EM_ARM => Arch::Arm,
451 goblin::elf::header::EM_PPC => Arch::Ppc,
452 goblin::elf::header::EM_PPC64 => Arch::Ppc64,
453 goblin::elf::header::EM_MIPS | goblin::elf::header::EM_MIPS_RS3_LE => {
454 if self.elf.header.e_flags & MIPS_64_FLAGS != 0 {
455 Arch::Mips64
456 } else {
457 Arch::Mips
458 }
459 }
460 _ => Arch::Unknown,
461 }
462 }
463
464 pub fn kind(&self) -> ObjectKind {
466 const ET_SCE_DYNEXEC: u16 = 0xfe10;
467 const ET_SCE_DYNAMIC: u16 = 0xfe18;
468
469 let kind = match self.elf.header.e_type {
470 goblin::elf::header::ET_NONE => ObjectKind::None,
471 goblin::elf::header::ET_REL => ObjectKind::Relocatable,
472 goblin::elf::header::ET_EXEC => ObjectKind::Executable,
473 goblin::elf::header::ET_DYN => ObjectKind::Library,
474 goblin::elf::header::ET_CORE => ObjectKind::Dump,
475 ET_SCE_DYNEXEC => ObjectKind::Executable,
476 ET_SCE_DYNAMIC => ObjectKind::Library,
477 _ => ObjectKind::Other,
478 };
479
480 if kind == ObjectKind::Executable && self.elf.interpreter.is_none() {
485 return ObjectKind::Debug;
486 }
487
488 if kind == ObjectKind::Library && self.raw_section("text").is_none() {
493 return ObjectKind::Debug;
494 }
495
496 kind
497 }
498
499 pub fn load_address(&self) -> u64 {
510 for phdr in &self.elf.program_headers {
516 if phdr.p_type == elf::program_header::PT_LOAD {
517 return phdr.p_vaddr;
518 }
519 }
520
521 0
522 }
523
524 pub fn has_symbols(&self) -> bool {
526 !self.elf.syms.is_empty() || !self.elf.dynsyms.is_empty()
527 }
528
529 pub fn symbols(&self) -> ElfSymbolIterator<'data, '_> {
531 ElfSymbolIterator {
532 symbols: self.elf.syms.iter(),
533 strtab: &self.elf.strtab,
534 dynamic_symbols: self.elf.dynsyms.iter(),
535 dynamic_strtab: &self.elf.dynstrtab,
536 sections: &self.elf.section_headers,
537 load_addr: self.load_address(),
538 }
539 }
540
541 pub fn symbol_map(&self) -> SymbolMap<'data> {
543 self.symbols().collect()
544 }
545
546 pub fn has_debug_info(&self) -> bool {
548 self.has_section("debug_info")
549 }
550
551 pub fn debug_session(&self) -> Result<DwarfDebugSession<'data>, DwarfError> {
564 let symbols = self.symbol_map();
565 DwarfDebugSession::parse(self, symbols, self.load_address() as i64, self.kind())
566 }
567
568 pub fn has_unwind_info(&self) -> bool {
570 self.has_section("eh_frame") || self.has_section("debug_frame")
571 }
572
573 pub fn has_sources(&self) -> bool {
575 false
576 }
577
578 pub fn is_malformed(&self) -> bool {
580 self.is_malformed
581 }
582
583 pub fn data(&self) -> &'data [u8] {
585 self.data
586 }
587
588 fn decompress_section(&self, section_data: &[u8]) -> Option<Vec<u8>> {
590 enum CompressionType {
591 Zlib,
592 Zstd,
593 }
594
595 let (ty, size, compressed) = if section_data.starts_with(b"ZLIB") {
596 if section_data.len() < 12 {
599 return None;
600 }
601
602 let mut size_bytes = [0; 8];
603 size_bytes.copy_from_slice(§ion_data[4..12]);
604
605 (
606 CompressionType::Zlib,
607 u64::from_be_bytes(size_bytes) as usize,
608 §ion_data[12..],
609 )
610 } else {
611 let container = self.elf.header.container().ok()?;
612 let endianness = self.elf.header.endianness().ok()?;
613 let context = Ctx::new(container, endianness);
614
615 let compression = CompressionHeader::parse(section_data, 0, context).ok()?;
616 let ty = match compression.ch_type {
617 ELFCOMPRESS_ZLIB => CompressionType::Zlib,
618 ELFCOMPRESS_ZSTD => CompressionType::Zstd,
619 _ => {
620 return None;
621 }
622 };
623
624 let compressed = §ion_data[CompressionHeader::size(context)..];
625 (ty, compression.ch_size as usize, compressed)
626 };
627
628 if size > self.max_decompressed_section_size.unwrap_or(usize::MAX) {
629 return None;
630 }
631
632 let decompressed = match ty {
633 CompressionType::Zlib => {
634 let mut decompressed = Vec::with_capacity(size);
635 Decompress::new(true)
636 .decompress_vec(compressed, &mut decompressed, FlushDecompress::Finish)
637 .ok()?;
638 decompressed
639 }
640 CompressionType::Zstd => zstd::bulk::decompress(compressed, size).ok()?,
641 };
642
643 Some(decompressed)
644 }
645
646 fn find_section(&self, name: &str) -> Option<(bool, DwarfSection<'data>)> {
648 for header in &self.elf.section_headers {
649 if header.sh_type == elf::section_header::SHT_NOBITS {
653 continue;
654 }
655
656 if let Some(section_name) = self.elf.shdr_strtab.get_at(header.sh_name) {
657 let offset = header.sh_offset as usize;
658 if offset == 0 {
659 continue;
664 }
665
666 if section_name.is_empty() {
667 continue;
668 }
669
670 let (compressed, section_name) = match section_name.strip_prefix(".z") {
673 Some(name) => (true, name),
674 None => match section_name.strip_prefix('.') {
675 Some(name) => (header.sh_flags & SHF_COMPRESSED != 0, name),
676 None => continue,
678 },
679 };
680
681 if section_name != name {
682 continue;
683 }
684
685 let size = header.sh_size as usize;
686 let data = &self.data.get(offset..)?.get(..size)?;
687 let section = DwarfSection {
688 data: Cow::Borrowed(data),
689 address: header.sh_addr,
690 offset: header.sh_offset,
691 align: header.sh_addralign,
692 };
693
694 return Some((compressed, section));
695 }
696 }
697
698 None
699 }
700
701 fn find_build_id(&self) -> Option<&'data [u8]> {
707 if let Some(mut notes) = self.elf.iter_note_headers(self.data) {
711 while let Some(Ok(note)) = notes.next() {
712 if note.n_type == elf::note::NT_GNU_BUILD_ID {
713 return Some(note.desc);
714 }
715 }
716 }
717
718 if let Some(mut notes) = self
723 .elf
724 .iter_note_sections(self.data, Some(".note.gnu.build-id"))
725 {
726 while let Some(Ok(note)) = notes.next() {
727 if note.n_type == elf::note::NT_GNU_BUILD_ID {
728 return Some(note.desc);
729 }
730 }
731 }
732
733 const PT_SCE_DYNLIBDATA: u32 = 0x61000000;
734
735 for ph in &self.elf.program_headers {
736 if ph.p_type == PT_SCE_DYNLIBDATA && ph.p_filesz >= 20 {
737 let offset = ph.p_offset as usize;
738 return self.data.get(offset..offset.saturating_add(20));
739 }
740 }
741
742 None
743 }
744
745 fn compute_debug_id(&self, identifier: &[u8]) -> DebugId {
753 let mut data = [0; UUID_SIZE];
755 let len = std::cmp::min(identifier.len(), UUID_SIZE);
756 data[0..len].copy_from_slice(&identifier[0..len]);
757
758 if self.elf.little_endian {
759 data[0..4].reverse(); data[4..6].reverse(); data[6..8].reverse(); }
766
767 Uuid::from_slice(&data)
768 .map(DebugId::from_uuid)
769 .unwrap_or_default()
770 }
771}
772
773impl fmt::Debug for ElfObject<'_> {
774 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
775 f.debug_struct("ElfObject")
776 .field("code_id", &self.code_id())
777 .field("debug_id", &self.debug_id())
778 .field("arch", &self.arch())
779 .field("kind", &self.kind())
780 .field("load_address", &format_args!("{:#x}", self.load_address()))
781 .field("has_symbols", &self.has_symbols())
782 .field("has_debug_info", &self.has_debug_info())
783 .field("has_unwind_info", &self.has_unwind_info())
784 .field("is_malformed", &self.is_malformed())
785 .finish()
786 }
787}
788
789impl<'slf, 'data: 'slf> AsSelf<'slf> for ElfObject<'data> {
790 type Ref = ElfObject<'slf>;
791
792 fn as_self(&'slf self) -> &'slf Self::Ref {
793 self
794 }
795}
796
797impl<'data> Parse<'data> for ElfObject<'data> {
798 type Error = ElfError;
799
800 fn test(data: &[u8]) -> bool {
801 Self::test(data)
802 }
803
804 fn parse_with_opts(data: &'data [u8], opts: ParseObjectOptions) -> Result<Self, ElfError> {
805 Self::parse_with_opts(data, opts)
806 }
807}
808
809impl<'data: 'object, 'object> ObjectLike<'data, 'object> for ElfObject<'data> {
810 type Error = DwarfError;
811 type Session = DwarfDebugSession<'data>;
812 type SymbolIterator = ElfSymbolIterator<'data, 'object>;
813
814 fn file_format(&self) -> FileFormat {
815 self.file_format()
816 }
817
818 fn code_id(&self) -> Option<CodeId> {
819 self.code_id()
820 }
821
822 fn debug_id(&self) -> DebugId {
823 self.debug_id()
824 }
825
826 fn arch(&self) -> Arch {
827 self.arch()
828 }
829
830 fn kind(&self) -> ObjectKind {
831 self.kind()
832 }
833
834 fn load_address(&self) -> u64 {
835 self.load_address()
836 }
837
838 fn has_symbols(&self) -> bool {
839 self.has_symbols()
840 }
841
842 fn symbols(&'object self) -> Self::SymbolIterator {
843 self.symbols()
844 }
845
846 fn symbol_map(&self) -> SymbolMap<'data> {
847 self.symbol_map()
848 }
849
850 fn has_debug_info(&self) -> bool {
851 self.has_debug_info()
852 }
853
854 fn debug_session(&self) -> Result<Self::Session, Self::Error> {
855 self.debug_session()
856 }
857
858 fn has_unwind_info(&self) -> bool {
859 self.has_unwind_info()
860 }
861
862 fn has_sources(&self) -> bool {
863 self.has_sources()
864 }
865
866 fn is_malformed(&self) -> bool {
867 self.is_malformed()
868 }
869}
870
871impl<'data> Dwarf<'data> for ElfObject<'data> {
872 fn endianity(&self) -> Endian {
873 if self.elf.little_endian {
874 Endian::Little
875 } else {
876 Endian::Big
877 }
878 }
879
880 fn raw_section(&self, name: &str) -> Option<DwarfSection<'data>> {
881 let (_, section) = self.find_section(name)?;
882 Some(section)
883 }
884
885 fn section(&self, name: &str) -> Option<DwarfSection<'data>> {
886 let (compressed, mut section) = self.find_section(name)?;
887
888 if compressed {
889 let decompressed = self.decompress_section(§ion.data)?;
890 section.data = Cow::Owned(decompressed);
891 }
892
893 Some(section)
894 }
895}
896
897pub struct ElfSymbolIterator<'data, 'object> {
901 symbols: elf::sym::SymIterator<'data>,
902 strtab: &'object strtab::Strtab<'data>,
903 dynamic_symbols: elf::sym::SymIterator<'data>,
904 dynamic_strtab: &'object strtab::Strtab<'data>,
905 sections: &'object [elf::SectionHeader],
906 load_addr: u64,
907}
908
909impl<'data> Iterator for ElfSymbolIterator<'data, '_> {
910 type Item = Symbol<'data>;
911
912 fn next(&mut self) -> Option<Self::Item> {
913 fn get_symbols<'data>(
914 symbols: &mut SymIterator,
915 strtab: &Strtab<'data>,
916 load_addr: u64,
917 sections: &[SectionHeader],
918 ) -> Option<Symbol<'data>> {
919 for symbol in symbols {
920 if symbol.st_type() != elf::sym::STT_FUNC {
922 continue;
923 }
924
925 if symbol.st_value < load_addr {
928 continue;
929 }
930
931 let section = match symbol.st_shndx {
932 self::SHN_UNDEF => None,
933 index => sections.get(index),
934 };
935
936 if !section.is_some_and(|header| header.is_executable()) {
938 continue;
939 }
940
941 let name = strtab.get_at(symbol.st_name).map(Cow::Borrowed);
942
943 return Some(Symbol {
944 name,
945 address: symbol.st_value - load_addr,
946 size: symbol.st_size,
947 });
948 }
949
950 None
951 }
952
953 get_symbols(
954 &mut self.symbols,
955 self.strtab,
956 self.load_addr,
957 self.sections,
958 )
959 .or_else(|| {
960 get_symbols(
961 &mut self.dynamic_symbols,
962 self.dynamic_strtab,
963 self.load_addr,
964 self.sections,
965 )
966 })
967 }
968}
969
970#[derive(Debug)]
972pub struct DebugLink<'data> {
973 filename: Cow<'data, CStr>,
974 crc: u32,
975}
976
977impl<'data> DebugLink<'data> {
978 pub fn from_data(
994 data: Cow<'data, [u8]>,
995 endianity: Endian,
996 ) -> Result<Self, DebugLinkError<'data>> {
997 match data {
998 Cow::Owned(data) => {
999 let (filename, crc) = Self::from_borrowed_data(&data, endianity)
1000 .map(|(filename, crc)| (filename.to_owned(), crc))
1001 .map_err(|kind| DebugLinkError {
1002 kind,
1003 data: Cow::Owned(data),
1004 })?;
1005 Ok(Self {
1006 filename: Cow::Owned(filename),
1007 crc,
1008 })
1009 }
1010 Cow::Borrowed(data) => {
1011 let (filename, crc) =
1012 Self::from_borrowed_data(data, endianity).map_err(|kind| DebugLinkError {
1013 kind,
1014 data: Cow::Borrowed(data),
1015 })?;
1016 Ok(Self {
1017 filename: Cow::Borrowed(filename),
1018 crc,
1019 })
1020 }
1021 }
1022 }
1023
1024 fn from_borrowed_data(
1025 data: &[u8],
1026 endianity: Endian,
1027 ) -> Result<(&CStr, u32), DebugLinkErrorKind> {
1028 let nul_pos = data
1029 .iter()
1030 .position(|byte| *byte == 0)
1031 .ok_or(DebugLinkErrorKind::MissingNul)?;
1032
1033 if nul_pos + 1 == data.len() {
1034 return Err(DebugLinkErrorKind::MissingCrc {
1035 filename_len_with_nul: nul_pos + 1,
1036 });
1037 }
1038
1039 let filename = &data[..nul_pos + 1];
1040
1041 let crc = data
1044 .get(nul_pos + 1..)
1045 .and_then(|crc| crc.get(crc.len() - 4..))
1046 .ok_or(DebugLinkErrorKind::MissingCrc {
1047 filename_len_with_nul: filename.len(),
1048 })?;
1049
1050 let crc: [u8; 4] = crc.try_into().map_err(|_| DebugLinkErrorKind::MissingCrc {
1051 filename_len_with_nul: filename.len(),
1052 })?;
1053
1054 let crc = match endianity {
1055 Endian::Little => u32::from_le_bytes(crc),
1056 Endian::Big => u32::from_be_bytes(crc),
1057 };
1058
1059 let filename =
1060 CStr::from_bytes_with_nul(filename).map_err(|_| DebugLinkErrorKind::MissingNul)?;
1061
1062 Ok((filename, crc))
1063 }
1064
1065 pub fn filename(&self) -> &CStr {
1067 &self.filename
1068 }
1069
1070 pub fn crc(&self) -> u32 {
1072 self.crc
1073 }
1074}
1075
1076#[derive(Debug, Error)]
1078pub enum DebugLinkErrorKind {
1079 #[error("missing NUL character")]
1081 MissingNul,
1082 #[error("missing CRC")]
1084 MissingCrc {
1085 filename_len_with_nul: usize,
1087 },
1088}
1089
1090#[derive(Debug, Error)]
1092#[error("could not parse debug link section")]
1093pub struct DebugLinkError<'data> {
1094 #[source]
1095 pub kind: DebugLinkErrorKind,
1097 pub data: Cow<'data, [u8]>,
1099}