1use {
2 super::{
3 process_inspection::{self, ProcessInspector},
4 serializers::*,
5 },
6 crate::module_reader::{ModuleMemoryReadError, ProcessModuleMemoryReader},
7 crate::{minidump_format::GUID, serializers::*},
8 goblin::{
9 container::{Container, Ctx, Endian},
10 elf,
11 },
12 std::{
13 borrow::Cow,
14 ffi::{CStr, OsString},
15 path::Path,
16 },
17};
18
19type Error = ModuleReaderError;
20
21const NOTE_SECTION_NAME: &[u8] = b".note.gnu.build-id\0";
22
23#[derive(Debug, thiserror::Error, serde::Serialize)]
24pub enum ModuleReaderError {
25 #[error("failed to map modile into memory")]
26 MapModuleFailed(#[source] process_inspection::Error),
27 #[error("failed to read module file ({path}): {error}")]
28 MapFile {
29 path: std::path::PathBuf,
30 #[source]
31 #[serde(serialize_with = "serialize_io_error")]
32 error: std::io::Error,
33 },
34 #[error(transparent)]
35 ReadModuleMemory(#[from] ModuleMemoryReadError),
36 #[error("failed to parse ELF memory: {0}")]
37 Parsing(
38 #[from]
39 #[serde(serialize_with = "serialize_goblin_error")]
40 goblin::error::Error,
41 ),
42 #[error("no build id notes in program headers")]
43 NoProgramHeaderNote,
44 #[error("no string table available to locate note sections")]
45 NoStrTab,
46 #[error("no build id note sections")]
47 NoSectionNote,
48 #[error("the ELF data contains no program headers")]
49 NoProgramHeaders,
50 #[error("the ELF data contains no sections")]
51 NoSections,
52 #[error("the ELF data does not have a .text section from which to generate a build id")]
53 NoTextSection,
54 #[error(
55 "failed to calculate build id\n\
56 ... from program headers: {program_headers}\n\
57 ... from sections: {section}\n\
58 ... from the text section: {section}"
59 )]
60 NoBuildId {
61 program_headers: Box<Self>,
62 section: Box<Self>,
63 generated: Box<Self>,
64 },
65 #[error("no dynamic string table section")]
66 NoDynStrSection,
67 #[error("a string in the strtab did not have a terminating nul byte")]
68 StrTabNoNulByte,
69 #[error("no SONAME found in dynamic linking information")]
70 NoSoNameEntry,
71 #[error("no dynamic linking information section")]
72 NoDynamicSection,
73 #[error(
74 "failed to retrieve soname\n\
75 ... from program headers: {program_headers}\n\
76 ... from sections: {section}"
77 )]
78 NoSoName {
79 program_headers: Box<Self>,
80 section: Box<Self>,
81 },
82 #[error("Not safe to open mapping {}", .0.to_string_lossy())]
83 NotSafeToOpenMapping(OsString),
84 #[error("Mmapped file empty or not an ELF file")]
85 MmapSanityCheckFailed,
86 #[error("Linux gate location doesn't fit in the required integer type")]
87 LinuxGateNotConvertable(
88 #[source]
89 #[serde(skip)]
90 std::num::TryFromIntError,
91 ),
92 #[error("IO Error")]
93 FileError(
94 #[source]
95 #[serde(serialize_with = "serialize_io_error")]
96 std::io::Error,
97 ),
98 #[error("failed to map file")]
99 MapError(
100 #[source]
101 #[serde(serialize_with = "serialize_io_error")]
102 std::io::Error,
103 ),
104}
105
106#[inline]
107fn is_executable_section(header: &elf::SectionHeader) -> bool {
108 header.sh_type == elf::section_header::SHT_PROGBITS
109 && header.sh_flags & u64::from(elf::section_header::SHF_ALLOC) != 0
110 && header.sh_flags & u64::from(elf::section_header::SHF_EXECINSTR) != 0
111}
112
113fn build_id_from_bytes(data: &[u8]) -> Vec<u8> {
118 data.chunks(std::mem::size_of::<GUID>()).fold(
121 vec![0u8; std::mem::size_of::<GUID>()],
122 |mut bytes, chunk| {
123 bytes
124 .iter_mut()
125 .zip(chunk.iter())
126 .for_each(|(b, c)| *b ^= *c);
127 bytes
128 },
129 )
130}
131
132fn section_header_with_name<'sc, MM: ReadModuleMemory>(
134 section_headers: &'sc elf::SectionHeaders,
135 strtab_index: usize,
136 name: &[u8],
137 module_memory: &MM,
138) -> Result<Option<&'sc elf::SectionHeader>, Error> {
139 let strtab_section_header = section_headers
140 .get(strtab_index)
141 .and_then(|hdr| (hdr.sh_type == elf::section_header::SHT_STRTAB).then_some(hdr))
142 .ok_or(Error::NoStrTab)?;
143
144 for header in section_headers {
145 let sh_name = header.sh_name as u64;
146 if sh_name >= strtab_section_header.sh_size {
147 log::warn!("invalid sh_name offset for {name:?}");
148 continue;
149 }
150 if sh_name + name.len() as u64 >= strtab_section_header.sh_size {
151 continue;
153 }
154 let n = module_memory.read(strtab_section_header.sh_offset + sh_name, name.len() as u64)?;
155 if name == &*n {
156 return Ok(Some(header));
157 }
158 }
159 Ok(None)
160}
161
162pub fn read_build_id_from_file(
163 process_inspector: &ProcessInspector,
164 path: &Path,
165) -> Result<Vec<u8>, Error> {
166 let module_memory_reader = process_inspector
167 .map_module_into_memory(path, 0)
168 .map_err(Error::MapModuleFailed)?;
169 read_build_id_from_module(module_memory_reader)
170}
171
172pub fn read_build_id_from_module(module_memory: impl ReadModuleMemory) -> Result<Vec<u8>, Error> {
173 let reader = ModuleReader::new(module_memory)?;
174 let program_headers = match reader.build_id_from_program_headers() {
175 Ok(v) => return Ok(v),
176 Err(e) => Box::new(e),
177 };
178 let section = match reader.build_id_from_section() {
179 Ok(v) => return Ok(v),
180 Err(e) => Box::new(e),
181 };
182 let generated = match reader.build_id_generate_from_text() {
183 Ok(v) => return Ok(v),
184 Err(e) => Box::new(e),
185 };
186 Err(Error::NoBuildId {
187 program_headers,
188 section,
189 generated,
190 })
191}
192
193pub fn read_soname_from_file(
194 process_inspector: &ProcessInspector,
195 path: &Path,
196 offset: usize,
197) -> Result<String, Error> {
198 let offset = u64::try_from(offset).map_err(Error::LinuxGateNotConvertable)?;
199
200 if path.starts_with("/dev/") {
205 return Err(Error::NotSafeToOpenMapping(path.as_os_str().to_os_string()));
206 }
207
208 let module_memory_reader = process_inspector
209 .map_module_into_memory(path, offset)
210 .map_err(Error::MapModuleFailed)?;
211
212 let memory_len = module_memory_reader.len().map_err(Error::MapModuleFailed)?;
213
214 if memory_len < elf::header::SELFMAG {
215 return Err(Error::MmapSanityCheckFailed);
216 }
217
218 read_soname_from_module(module_memory_reader)
219}
220
221pub fn read_soname_from_module(module_memory: impl ReadModuleMemory) -> Result<String, Error> {
222 let reader = ModuleReader::new(module_memory)?;
223 let program_headers = match reader.soname_from_program_headers() {
224 Ok(v) => return Ok(v),
225 Err(e) => Box::new(e),
226 };
227 let section = match reader.soname_from_sections() {
228 Ok(v) => return Ok(v),
229 Err(e) => Box::new(e),
230 };
231 Err(Error::NoSoName {
232 program_headers,
233 section,
234 })
235}
236
237struct DynIter<'a> {
238 data: &'a [u8],
239 offset: usize,
240 ctx: Ctx,
241}
242
243impl<'a> DynIter<'a> {
244 pub fn new(data: &'a [u8], ctx: Ctx) -> Self {
245 DynIter {
246 data,
247 offset: 0,
248 ctx,
249 }
250 }
251}
252
253impl Iterator for DynIter<'_> {
254 type Item = Result<elf::dynamic::Dyn, Error>;
255
256 fn next(&mut self) -> Option<Self::Item> {
257 use scroll::Pread;
258 let dyn_: elf::dynamic::Dyn = match self.data.gread_with(&mut self.offset, self.ctx) {
259 Ok(v) => v,
260 Err(e) => return Some(Err(e.into())),
261 };
262 if dyn_.d_tag == elf::dynamic::DT_NULL {
263 None
264 } else {
265 Some(Ok(dyn_))
266 }
267 }
268}
269
270pub struct ModuleReader<MM> {
271 module_memory: MM,
272 header: elf::Header,
273 context: Ctx,
274}
275
276impl<MM: ReadModuleMemory> ModuleReader<MM> {
277 pub fn new(module_memory: MM) -> Result<ModuleReader<MM>, Error> {
278 let header_size = elf::Header::size(Ctx::new(Container::Big, Endian::default()));
282 let header_data = module_memory.read(0, header_size as u64)?;
283 let header = elf::Elf::parse_header(&header_data)?;
284 let context = Ctx::new(header.container()?, header.endianness()?);
285
286 Ok(Self {
287 module_memory,
288 header,
289 context,
290 })
291 }
292
293 pub fn find_program_note(
295 &self,
296 note_type: u32,
297 note_size: usize,
298 note_name: &str,
299 ) -> Result<Option<Vec<u8>>, Error> {
300 let program_headers = self.read_program_headers()?;
301 for header in program_headers {
302 if header.p_type != elf::program_header::PT_NOTE
303 || (header.p_flags & elf::program_header::PF_R) == 0
304 || (header.p_memsz as usize) < note_size
305 {
306 continue;
307 }
308
309 if let Some(data) = self.find_note(
310 header.p_offset,
311 header.p_filesz,
312 header.p_align,
313 note_type,
314 note_size,
315 note_name,
316 )? {
317 return Ok(Some(data));
318 }
319 }
320 Ok(None)
321 }
322
323 pub fn soname_from_program_headers(&self) -> Result<String, Error> {
325 let program_headers = self.read_program_headers()?;
326
327 let dynamic_segment_header = program_headers
328 .iter()
329 .find(|h| h.p_type == elf::program_header::PT_DYNAMIC)
330 .ok_or(Error::NoDynamicSection)?;
331
332 let dynamic_section = self.read_segment(dynamic_segment_header)?;
333
334 let mut soname_strtab_offset = None;
335 let mut strtab_addr = None;
336 let mut strtab_size = None;
337 for dyn_ in DynIter::new(&dynamic_section, self.context) {
338 let dyn_ = dyn_?;
339 match dyn_.d_tag {
340 elf::dynamic::DT_SONAME => soname_strtab_offset = Some(dyn_.d_val),
341 elf::dynamic::DT_STRTAB => strtab_addr = Some(dyn_.d_val),
342 elf::dynamic::DT_STRSZ => strtab_size = Some(dyn_.d_val),
343 _ => (),
344 }
345 }
346
347 match (strtab_addr, strtab_size, soname_strtab_offset) {
348 (None, _, _) | (_, None, _) => Err(Error::NoDynStrSection),
349 (_, _, None) => Err(Error::NoSoNameEntry),
350 (Some(addr), Some(size), Some(offset)) => {
351 if offset < size {
353 self.read_name_from_strtab(
354 self.module_memory
355 .absolute_to_relative(addr)
356 .unwrap_or(addr),
357 size,
358 offset,
359 )
360 } else {
361 log::warn!("soname strtab offset ({offset}) exceeds strtab size ({size})");
362 Err(Error::NoSoNameEntry)
363 }
364 }
365 }
366 }
367
368 pub fn soname_from_sections(&self) -> Result<String, Error> {
370 let section_headers = self.read_section_headers()?;
371
372 let dynamic_section_header = section_headers
373 .iter()
374 .find(|h| h.sh_type == elf::section_header::SHT_DYNAMIC)
375 .ok_or(Error::NoDynamicSection)?;
376
377 let dynstr_section_header =
378 match section_headers.get(dynamic_section_header.sh_link as usize) {
379 Some(header) if header.sh_type == elf::section_header::SHT_STRTAB => header,
380 _ => section_header_with_name(
381 §ion_headers,
382 self.header.e_shstrndx as usize,
383 b".dynstr\0",
384 &self.module_memory,
385 )?
386 .ok_or(Error::NoDynStrSection)?,
387 };
388
389 let dynamic_section = self.module_memory.read(
390 self.section_offset(dynamic_section_header),
391 dynamic_section_header.sh_size,
392 )?;
393
394 for dyn_ in DynIter::new(&dynamic_section, self.context) {
395 let dyn_ = dyn_?;
396 if dyn_.d_tag == elf::dynamic::DT_SONAME {
397 let name_offset = dyn_.d_val;
398 if name_offset < dynstr_section_header.sh_size {
399 return self.read_name_from_strtab(
400 self.section_offset(dynstr_section_header),
401 dynstr_section_header.sh_size,
402 name_offset,
403 );
404 } else {
405 log::warn!(
406 "soname offset ({name_offset}) exceeds dynstr section size ({})",
407 dynstr_section_header.sh_size
408 );
409 }
410 }
411 }
412
413 Err(Error::NoSoNameEntry)
414 }
415
416 pub fn build_id_from_program_headers(&self) -> Result<Vec<u8>, Error> {
418 let program_headers = self.read_program_headers()?;
419 for header in program_headers {
420 if header.p_type != elf::program_header::PT_NOTE {
421 continue;
422 }
423 if let Ok(Some(result)) =
424 self.find_build_id_note(header.p_offset, header.p_filesz, header.p_align)
425 {
426 return Ok(result);
427 }
428 }
429 Err(Error::NoProgramHeaderNote)
430 }
431
432 pub fn build_id_from_section(&self) -> Result<Vec<u8>, Error> {
434 let section_headers = self.read_section_headers()?;
435
436 let header = section_header_with_name(
437 §ion_headers,
438 self.header.e_shstrndx as usize,
439 NOTE_SECTION_NAME,
440 &self.module_memory,
441 )?
442 .ok_or(Error::NoSectionNote)?;
443
444 match self.find_build_id_note(header.sh_offset, header.sh_size, header.sh_addralign) {
445 Ok(Some(v)) => Ok(v),
446 Ok(None) => Err(Error::NoSectionNote),
447 Err(e) => Err(e),
448 }
449 }
450
451 pub fn build_id_generate_from_text(&self) -> Result<Vec<u8>, Error> {
453 let Some(text_header) = self
454 .read_section_headers()?
455 .into_iter()
456 .find(is_executable_section)
457 else {
458 return Err(Error::NoTextSection);
459 };
460
461 let len = std::cmp::min(4096, text_header.sh_size);
463 let text_data = self.module_memory.read(text_header.sh_offset, len)?;
464 Ok(build_id_from_bytes(&text_data))
465 }
466
467 fn read_segment<'a>(&'a self, header: &elf::ProgramHeader) -> Result<Cow<'a, [u8]>, Error> {
468 let (offset, size) = if self.module_memory.is_process_memory() {
469 (header.p_vaddr, header.p_memsz)
470 } else {
471 (header.p_offset, header.p_filesz)
472 };
473
474 self.module_memory.read(offset, size).map_err(|e| e.into())
475 }
476
477 fn read_name_from_strtab(
478 &self,
479 strtab_offset: u64,
480 strtab_size: u64,
481 name_offset: u64,
482 ) -> Result<String, Error> {
483 assert!(name_offset < strtab_size);
484 let name = self
485 .module_memory
486 .read(strtab_offset + name_offset, strtab_size - name_offset)?;
487 CStr::from_bytes_until_nul(&name)
488 .map(|s| s.to_string_lossy().into_owned())
489 .map_err(|_| Error::StrTabNoNulByte)
490 }
491
492 fn section_offset(&self, header: &elf::SectionHeader) -> u64 {
493 if self.module_memory.is_process_memory() {
494 header.sh_addr
495 } else {
496 header.sh_offset
497 }
498 }
499
500 fn read_program_headers(&self) -> Result<elf::ProgramHeaders, Error> {
501 if self.header.e_phoff == 0 {
502 return Err(Error::NoProgramHeaders);
503 }
504 let program_headers_data = self.module_memory.read(
505 self.header.e_phoff,
506 self.header.e_phentsize as u64 * self.header.e_phnum as u64,
507 )?;
508 let program_headers = elf::ProgramHeader::parse(
509 &program_headers_data,
510 0,
511 self.header.e_phnum as usize,
512 self.context,
513 )?;
514 Ok(program_headers)
515 }
516
517 fn read_section_headers(&self) -> Result<elf::SectionHeaders, Error> {
518 if self.header.e_shoff == 0 {
519 return Err(Error::NoSections);
520 }
521
522 let section_headers_data = self.module_memory.read(
523 self.header.e_shoff,
524 self.header.e_shentsize as u64 * self.header.e_shnum as u64,
525 )?;
526 let section_headers = elf::SectionHeader::parse_from(
528 §ion_headers_data,
529 0,
530 self.header.e_shnum as usize,
531 self.context,
532 )?;
533 Ok(section_headers)
534 }
535
536 fn find_build_id_note(
537 &self,
538 offset: u64,
539 size: u64,
540 alignment: u64,
541 ) -> Result<Option<Vec<u8>>, Error> {
542 self.find_note(
543 offset,
544 size,
545 alignment,
546 elf::note::NT_GNU_BUILD_ID,
547 0,
548 "GNU",
549 )
550 }
551
552 fn find_note(
553 &self,
554 offset: u64,
555 size: u64,
556 alignment: u64,
557 note_type: u32,
558 note_min_size: usize,
559 note_name: &str,
560 ) -> Result<Option<Vec<u8>>, Error> {
561 let notes = self.module_memory.read(offset, size)?;
562 for note in (elf::note::NoteDataIterator {
563 data: ¬es,
564 size: size as usize,
567 offset: 0,
568 ctx: (alignment as usize, self.context),
569 }) {
570 let Ok(note) = note else { break };
571 if note.name == note_name
572 && note.n_type == note_type
573 && note.desc.len() >= note_min_size
574 {
575 return Ok(Some(note.desc.to_owned()));
576 }
577 }
578 Ok(None)
579 }
580}
581
582pub trait ReadModuleMemory {
583 fn read<'a>(&'a self, offset: u64, length: u64)
584 -> Result<Cow<'a, [u8]>, ModuleMemoryReadError>;
585 fn absolute_to_relative(&self, addr: u64) -> Option<u64>;
586 fn relative_to_absolute(&self, addr: u64) -> Option<u64>;
588 fn is_process_memory(&self) -> bool;
589}
590
591impl<'a> ReadModuleMemory for ProcessModuleMemoryReader<'a> {
592 fn read(&self, offset: u64, length: u64) -> Result<Cow<'_, [u8]>, ModuleMemoryReadError> {
593 self.read(offset, length)
594 }
595 fn absolute_to_relative(&self, addr: u64) -> Option<u64> {
596 addr.checked_sub(self.start_address)
597 }
598 fn relative_to_absolute(&self, addr: u64) -> Option<u64> {
600 self.start_address.checked_add(addr)
601 }
602 fn is_process_memory(&self) -> bool {
603 true
604 }
605}
606
607impl<T: ReadModuleMemory + ?Sized> ReadModuleMemory for &T {
608 fn read(&self, offset: u64, length: u64) -> Result<Cow<'_, [u8]>, ModuleMemoryReadError> {
609 T::read(self, offset, length)
610 }
611 fn absolute_to_relative(&self, addr: u64) -> Option<u64> {
612 T::absolute_to_relative(self, addr)
613 }
614 fn relative_to_absolute(&self, addr: u64) -> Option<u64> {
615 T::relative_to_absolute(self, addr)
616 }
617 fn is_process_memory(&self) -> bool {
618 T::is_process_memory(self)
619 }
620}
621
622#[cfg(test)]
623mod test {
624 use super::*;
625
626 const TINY_ELF: &[u8] = &[
643 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
644 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x03, 0x40, 0x00, 0x00, 0x00,
645 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00,
646 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x03, 0x00, 0x40, 0x00,
647 0x06, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0a, 0x03, 0x00,
648 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
649 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
650 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
651 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
652 0x00, 0x68, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
653 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
654 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
655 0x00, 0x00, 0x00, 0x00, 0xbd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x02, 0x40,
656 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
657 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
658 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
659 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
660 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
661 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
662 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
663 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x03, 0x40,
664 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,
665 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
666 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
667 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
668 0x00, 0x68, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0x00, 0x00,
669 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
670 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
671 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
672 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x02,
673 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
674 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
675 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
676 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x02, 0x40, 0x00, 0x00, 0x00,
677 0x00, 0x00, 0xbd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
678 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
679 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00,
680 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x02,
681 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d,
682 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
683 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
684 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x4e,
685 0x55, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
686 0x0e, 0x0f, 0x10, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e, 0x6e, 0x6f, 0x74, 0x65,
687 0x2e, 0x67, 0x6e, 0x75, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2d, 0x69, 0x64, 0x00, 0x2e,
688 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x61, 0x6d,
689 0x69, 0x63, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x74, 0x72, 0x00, 0x0e, 0x00, 0x00, 0x00,
690 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
691 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
692 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
693 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
694 0x00, 0x6c, 0x69, 0x62, 0x66, 0x6f, 0x6f, 0x2e, 0x73, 0x6f, 0x2e, 0x31, 0x00, 0x6a, 0x3c,
695 0x58, 0x31, 0xff, 0x0f, 0x05,
696 ];
697
698 #[test]
699 fn build_id_program_headers() {
700 let reader = ModuleReader::new(SliceModuleMemoryReader(TINY_ELF)).unwrap();
701 let id = reader.build_id_from_program_headers().unwrap();
702 assert_eq!(
703 id,
704 vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
705 );
706 }
707
708 #[test]
709 fn build_id_section() {
710 let reader = ModuleReader::new(SliceModuleMemoryReader(TINY_ELF)).unwrap();
711 let id = reader.build_id_from_section().unwrap();
712 assert_eq!(
713 id,
714 vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
715 );
716 }
717
718 #[test]
719 fn build_id_text_hash() {
720 let reader = ModuleReader::new(SliceModuleMemoryReader(TINY_ELF)).unwrap();
721 let id = reader.build_id_generate_from_text().unwrap();
722 assert_eq!(
723 id,
724 vec![
725 0x6a, 0x3c, 0x58, 0x31, 0xff, 0x0f, 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0
726 ]
727 );
728 }
729
730 #[test]
731 fn soname_program_headers() {
732 let reader = ModuleReader::new(SliceModuleMemoryReader(TINY_ELF)).unwrap();
733 let soname = reader.soname_from_program_headers().unwrap();
734 assert_eq!(soname, "libfoo.so.1");
735 }
736
737 #[test]
738 fn soname_section() {
739 let reader = ModuleReader::new(SliceModuleMemoryReader(TINY_ELF)).unwrap();
740 let soname = reader.soname_from_sections().unwrap();
741 assert_eq!(soname, "libfoo.so.1");
742 }
743
744 pub struct SliceModuleMemoryReader<'a>(pub &'a [u8]);
745
746 impl<'a> ReadModuleMemory for SliceModuleMemoryReader<'a> {
747 fn read<'b>(
748 &'b self,
749 offset: u64,
750 length: u64,
751 ) -> Result<Cow<'b, [u8]>, ModuleMemoryReadError> {
752 let inner = || {
753 use crate::module_reader::ReadError as E;
754 let offset = usize::try_from(offset).map_err(|_| E::Overflow)?;
755 let length = usize::try_from(length).map_err(|_| E::Overflow)?;
756 let end = offset.checked_add(length).ok_or(E::Overflow)?;
757 self.0
758 .get(offset..end)
759 .map(Cow::Borrowed)
760 .ok_or(E::OutOfBounds)
761 };
762
763 inner().map_err(|error| ModuleMemoryReadError {
764 start_address: None,
765 offset,
766 length,
767 error,
768 })
769 }
770 fn absolute_to_relative(&self, addr: u64) -> Option<u64> {
771 Some(addr)
772 }
773 fn relative_to_absolute(&self, addr: u64) -> Option<u64> {
775 Some(addr)
776 }
777 fn is_process_memory(&self) -> bool {
778 false
779 }
780 }
781}