1use {
2 super::{mem_reader::MemReader, serializers::*},
3 crate::{minidump_format::GUID, serializers::*},
4 goblin::{
5 container::{Container, Ctx, Endian},
6 elf,
7 },
8 std::{borrow::Cow, ffi::CStr},
9};
10
11type Buf<'buf> = Cow<'buf, [u8]>;
12type Error = ModuleReaderError;
13
14const NOTE_SECTION_NAME: &[u8] = b".note.gnu.build-id\0";
15
16pub struct ProcessReader {
17 inner: MemReader,
18 start_address: u64,
19}
20
21#[derive(Debug, thiserror::Error, serde::Serialize)]
22pub enum ModuleReaderError {
23 #[error("failed to read module file ({path}): {error}")]
24 MapFile {
25 path: std::path::PathBuf,
26 #[source]
27 #[serde(serialize_with = "serialize_io_error")]
28 error: std::io::Error,
29 },
30 #[error("failed to read module memory: {length} bytes at {offset}{}: {error}", .start_address.map(|addr| format!(" (start address: {addr})")).unwrap_or_default())]
31 ReadModuleMemory {
32 offset: u64,
33 length: u64,
34 start_address: Option<u64>,
35 #[source]
36 #[serde(serialize_with = "serialize_nix_error")]
37 error: nix::Error,
38 },
39 #[error("failed to parse ELF memory: {0}")]
40 Parsing(
41 #[from]
42 #[serde(serialize_with = "serialize_goblin_error")]
43 goblin::error::Error,
44 ),
45 #[error("no build id notes in program headers")]
46 NoProgramHeaderNote,
47 #[error("no string table available to locate note sections")]
48 NoStrTab,
49 #[error("no build id note sections")]
50 NoSectionNote,
51 #[error("the ELF data contains no program headers")]
52 NoProgramHeaders,
53 #[error("the ELF data contains no sections")]
54 NoSections,
55 #[error("the ELF data does not have a .text section from which to generate a build id")]
56 NoTextSection,
57 #[error(
58 "failed to calculate build id\n\
59 ... from program headers: {program_headers}\n\
60 ... from sections: {section}\n\
61 ... from the text section: {section}"
62 )]
63 NoBuildId {
64 program_headers: Box<Self>,
65 section: Box<Self>,
66 generated: Box<Self>,
67 },
68 #[error("no dynamic string table section")]
69 NoDynStrSection,
70 #[error("a string in the strtab did not have a terminating nul byte")]
71 StrTabNoNulByte,
72 #[error("no SONAME found in dynamic linking information")]
73 NoSoNameEntry,
74 #[error("no dynamic linking information section")]
75 NoDynamicSection,
76 #[error(
77 "failed to retrieve soname\n\
78 ... from program headers: {program_headers}\n\
79 ... from sections: {section}"
80 )]
81 NoSoName {
82 program_headers: Box<Self>,
83 section: Box<Self>,
84 },
85}
86
87impl ProcessReader {
88 pub fn new(pid: i32, start_address: usize) -> Self {
89 Self {
90 inner: MemReader::new(pid),
91 start_address: start_address as u64,
92 }
93 }
94}
95
96pub enum ProcessMemory<'buf> {
97 Slice(&'buf [u8]),
98 Process(ProcessReader),
99}
100
101impl<'buf> From<&'buf [u8]> for ProcessMemory<'buf> {
102 fn from(value: &'buf [u8]) -> Self {
103 Self::Slice(value)
104 }
105}
106
107impl From<ProcessReader> for ProcessMemory<'_> {
108 fn from(value: ProcessReader) -> Self {
109 Self::Process(value)
110 }
111}
112
113impl<'buf> ProcessMemory<'buf> {
114 #[inline]
115 fn read(&mut self, offset: u64, length: u64) -> Result<Buf<'buf>, Error> {
116 let error = move |start_address, error| Error::ReadModuleMemory {
117 start_address,
118 offset,
119 length,
120 error,
121 };
122
123 match self {
124 Self::Process(pr) => {
125 let error = |e| error(Some(pr.start_address), e);
126 let len = std::num::NonZeroUsize::new(length as usize)
127 .ok_or_else(|| error(nix::Error::EINVAL))?;
128 let proc_offset = pr
129 .start_address
130 .checked_add(offset)
131 .ok_or_else(|| error(nix::Error::EOVERFLOW))?;
132 pr.inner
133 .read_to_vec(proc_offset as _, len)
134 .map(Cow::Owned)
135 .map_err(|err| error(err.source))
136 }
137 Self::Slice(s) => {
138 let error = |e| error(None, e);
139 let end = offset
140 .checked_add(length)
141 .ok_or_else(|| error(nix::Error::EOVERFLOW))?;
142 s.get(offset as usize..end as usize)
143 .map(Cow::Borrowed)
144 .ok_or_else(|| error(nix::Error::EACCES))
145 }
146 }
147 }
148
149 #[inline]
151 fn absolute(&self, addr: u64) -> u64 {
152 let Self::Process(pr) = self else {
153 return addr;
154 };
155 addr.checked_sub(pr.start_address).unwrap_or(addr)
156 }
157
158 #[inline]
159 fn is_process_memory(&self) -> bool {
160 matches!(self, Self::Process(_))
161 }
162}
163
164#[inline]
165fn is_executable_section(header: &elf::SectionHeader) -> bool {
166 header.sh_type == elf::section_header::SHT_PROGBITS
167 && header.sh_flags & u64::from(elf::section_header::SHF_ALLOC) != 0
168 && header.sh_flags & u64::from(elf::section_header::SHF_EXECINSTR) != 0
169}
170
171fn build_id_from_bytes(data: &[u8]) -> Vec<u8> {
176 data.chunks(std::mem::size_of::<GUID>()).fold(
179 vec![0u8; std::mem::size_of::<GUID>()],
180 |mut bytes, chunk| {
181 bytes
182 .iter_mut()
183 .zip(chunk.iter())
184 .for_each(|(b, c)| *b ^= *c);
185 bytes
186 },
187 )
188}
189
190fn section_header_with_name<'sc>(
192 section_headers: &'sc elf::SectionHeaders,
193 strtab_index: usize,
194 name: &[u8],
195 module_memory: &mut ProcessMemory<'_>,
196) -> Result<Option<&'sc elf::SectionHeader>, Error> {
197 let strtab_section_header = section_headers
198 .get(strtab_index)
199 .and_then(|hdr| (hdr.sh_type == elf::section_header::SHT_STRTAB).then_some(hdr))
200 .ok_or(Error::NoStrTab)?;
201
202 for header in section_headers {
203 let sh_name = header.sh_name as u64;
204 if sh_name >= strtab_section_header.sh_size {
205 log::warn!("invalid sh_name offset for {name:?}");
206 continue;
207 }
208 if sh_name + name.len() as u64 >= strtab_section_header.sh_size {
209 continue;
211 }
212 let n = module_memory.read(strtab_section_header.sh_offset + sh_name, name.len() as u64)?;
213 if name == &*n {
214 return Ok(Some(header));
215 }
216 }
217 Ok(None)
218}
219
220pub trait ReadFromModule: Sized {
222 fn read_from_module(module_memory: ProcessMemory<'_>) -> Result<Self, Error>;
223
224 fn read_from_file(path: &std::path::Path) -> Result<Self, Error> {
225 let map = std::fs::File::open(path)
226 .and_then(|file| unsafe { memmap2::Mmap::map(&file) })
228 .map_err(|error| Error::MapFile {
229 path: path.to_owned(),
230 error,
231 })?;
232 Self::read_from_module(ProcessMemory::Slice(&map))
233 }
234}
235
236pub struct BuildId(pub Vec<u8>);
238
239impl ReadFromModule for BuildId {
240 fn read_from_module(module_memory: ProcessMemory<'_>) -> Result<Self, Error> {
241 let mut reader = ModuleReader::new(module_memory)?;
242 let program_headers = match reader.build_id_from_program_headers() {
243 Ok(v) => return Ok(BuildId(v)),
244 Err(e) => Box::new(e),
245 };
246 let section = match reader.build_id_from_section() {
247 Ok(v) => return Ok(BuildId(v)),
248 Err(e) => Box::new(e),
249 };
250 let generated = match reader.build_id_generate_from_text() {
251 Ok(v) => return Ok(BuildId(v)),
252 Err(e) => Box::new(e),
253 };
254 Err(Error::NoBuildId {
255 program_headers,
256 section,
257 generated,
258 })
259 }
260}
261
262struct DynIter<'a> {
263 data: &'a [u8],
264 offset: usize,
265 ctx: Ctx,
266}
267
268impl<'a> DynIter<'a> {
269 pub fn new(data: &'a [u8], ctx: Ctx) -> Self {
270 DynIter {
271 data,
272 offset: 0,
273 ctx,
274 }
275 }
276}
277
278impl Iterator for DynIter<'_> {
279 type Item = Result<elf::dynamic::Dyn, Error>;
280
281 fn next(&mut self) -> Option<Self::Item> {
282 use scroll::Pread;
283 let dyn_: elf::dynamic::Dyn = match self.data.gread_with(&mut self.offset, self.ctx) {
284 Ok(v) => v,
285 Err(e) => return Some(Err(e.into())),
286 };
287 if dyn_.d_tag == elf::dynamic::DT_NULL {
288 None
289 } else {
290 Some(Ok(dyn_))
291 }
292 }
293}
294
295#[derive(Default, Clone, Debug)]
297pub struct SoName(pub String);
298
299impl ReadFromModule for SoName {
300 fn read_from_module(module_memory: ProcessMemory<'_>) -> Result<Self, Error> {
301 let mut reader = ModuleReader::new(module_memory)?;
302 let program_headers = match reader.soname_from_program_headers() {
303 Ok(v) => return Ok(SoName(v)),
304 Err(e) => Box::new(e),
305 };
306 let section = match reader.soname_from_sections() {
307 Ok(v) => return Ok(SoName(v)),
308 Err(e) => Box::new(e),
309 };
310 Err(Error::NoSoName {
311 program_headers,
312 section,
313 })
314 }
315}
316
317pub struct ModuleReader<'buf> {
318 module_memory: ProcessMemory<'buf>,
319 header: elf::Header,
320 context: Ctx,
321}
322
323impl<'buf> ModuleReader<'buf> {
324 pub fn new(mut module_memory: ProcessMemory<'buf>) -> Result<Self, Error> {
325 let header_size = elf::Header::size(Ctx::new(Container::Big, Endian::default()));
329 let header_data = module_memory.read(0, header_size as u64)?;
330 let header = elf::Elf::parse_header(&header_data)?;
331 let context = Ctx::new(header.container()?, header.endianness()?);
332
333 Ok(Self {
334 module_memory,
335 header,
336 context,
337 })
338 }
339
340 pub fn soname_from_program_headers(&mut self) -> Result<String, Error> {
342 let program_headers = self.read_program_headers()?;
343
344 let dynamic_segment_header = program_headers
345 .iter()
346 .find(|h| h.p_type == elf::program_header::PT_DYNAMIC)
347 .ok_or(Error::NoDynamicSection)?;
348
349 let dynamic_section = self.read_segment(dynamic_segment_header)?;
350
351 let mut soname_strtab_offset = None;
352 let mut strtab_addr = None;
353 let mut strtab_size = None;
354 for dyn_ in DynIter::new(&dynamic_section, self.context) {
355 let dyn_ = dyn_?;
356 match dyn_.d_tag {
357 elf::dynamic::DT_SONAME => soname_strtab_offset = Some(dyn_.d_val),
358 elf::dynamic::DT_STRTAB => strtab_addr = Some(dyn_.d_val),
359 elf::dynamic::DT_STRSZ => strtab_size = Some(dyn_.d_val),
360 _ => (),
361 }
362 }
363
364 match (strtab_addr, strtab_size, soname_strtab_offset) {
365 (None, _, _) | (_, None, _) => Err(Error::NoDynStrSection),
366 (_, _, None) => Err(Error::NoSoNameEntry),
367 (Some(addr), Some(size), Some(offset)) => {
368 if offset < size {
370 self.read_name_from_strtab(self.module_memory.absolute(addr), size, offset)
371 } else {
372 log::warn!("soname strtab offset ({offset}) exceeds strtab size ({size})");
373 Err(Error::NoSoNameEntry)
374 }
375 }
376 }
377 }
378
379 pub fn soname_from_sections(&mut self) -> Result<String, Error> {
381 let section_headers = self.read_section_headers()?;
382
383 let dynamic_section_header = section_headers
384 .iter()
385 .find(|h| h.sh_type == elf::section_header::SHT_DYNAMIC)
386 .ok_or(Error::NoDynamicSection)?;
387
388 let dynstr_section_header =
389 match section_headers.get(dynamic_section_header.sh_link as usize) {
390 Some(header) if header.sh_type == elf::section_header::SHT_STRTAB => header,
391 _ => section_header_with_name(
392 §ion_headers,
393 self.header.e_shstrndx as usize,
394 b".dynstr\0",
395 &mut self.module_memory,
396 )?
397 .ok_or(Error::NoDynStrSection)?,
398 };
399
400 let dynamic_section = self.module_memory.read(
401 self.section_offset(dynamic_section_header),
402 dynamic_section_header.sh_size,
403 )?;
404
405 for dyn_ in DynIter::new(&dynamic_section, self.context) {
406 let dyn_ = dyn_?;
407 if dyn_.d_tag == elf::dynamic::DT_SONAME {
408 let name_offset = dyn_.d_val;
409 if name_offset < dynstr_section_header.sh_size {
410 return self.read_name_from_strtab(
411 self.section_offset(dynstr_section_header),
412 dynstr_section_header.sh_size,
413 name_offset,
414 );
415 } else {
416 log::warn!(
417 "soname offset ({name_offset}) exceeds dynstr section size ({})",
418 dynstr_section_header.sh_size
419 );
420 }
421 }
422 }
423
424 Err(Error::NoSoNameEntry)
425 }
426
427 pub fn build_id_from_program_headers(&mut self) -> Result<Vec<u8>, Error> {
429 let program_headers = self.read_program_headers()?;
430 for header in program_headers {
431 if header.p_type != elf::program_header::PT_NOTE {
432 continue;
433 }
434 if let Ok(Some(result)) =
435 self.find_build_id_note(header.p_offset, header.p_filesz, header.p_align)
436 {
437 return Ok(result);
438 }
439 }
440 Err(Error::NoProgramHeaderNote)
441 }
442
443 pub fn build_id_from_section(&mut self) -> Result<Vec<u8>, Error> {
445 let section_headers = self.read_section_headers()?;
446
447 let header = section_header_with_name(
448 §ion_headers,
449 self.header.e_shstrndx as usize,
450 NOTE_SECTION_NAME,
451 &mut self.module_memory,
452 )?
453 .ok_or(Error::NoSectionNote)?;
454
455 match self.find_build_id_note(header.sh_offset, header.sh_size, header.sh_addralign) {
456 Ok(Some(v)) => Ok(v),
457 Ok(None) => Err(Error::NoSectionNote),
458 Err(e) => Err(e),
459 }
460 }
461
462 pub fn build_id_generate_from_text(&mut self) -> Result<Vec<u8>, Error> {
464 let Some(text_header) = self
465 .read_section_headers()?
466 .into_iter()
467 .find(is_executable_section)
468 else {
469 return Err(Error::NoTextSection);
470 };
471
472 let len = std::cmp::min(4096, text_header.sh_size);
474 let text_data = self.module_memory.read(text_header.sh_offset, len)?;
475 Ok(build_id_from_bytes(&text_data))
476 }
477
478 fn read_segment(&mut self, header: &elf::ProgramHeader) -> Result<Buf<'buf>, Error> {
479 let (offset, size) = if self.module_memory.is_process_memory() {
480 (header.p_vaddr, header.p_memsz)
481 } else {
482 (header.p_offset, header.p_filesz)
483 };
484
485 self.module_memory.read(offset, size)
486 }
487
488 fn read_name_from_strtab(
489 &mut self,
490 strtab_offset: u64,
491 strtab_size: u64,
492 name_offset: u64,
493 ) -> Result<String, Error> {
494 assert!(name_offset < strtab_size);
495 let name = self
496 .module_memory
497 .read(strtab_offset + name_offset, strtab_size - name_offset)?;
498 CStr::from_bytes_until_nul(&name)
499 .map(|s| s.to_string_lossy().into_owned())
500 .map_err(|_| Error::StrTabNoNulByte)
501 }
502
503 fn section_offset(&self, header: &elf::SectionHeader) -> u64 {
504 if self.module_memory.is_process_memory() {
505 header.sh_addr
506 } else {
507 header.sh_offset
508 }
509 }
510
511 fn read_program_headers(&mut self) -> Result<elf::ProgramHeaders, Error> {
512 if self.header.e_phoff == 0 {
513 return Err(Error::NoProgramHeaders);
514 }
515 let program_headers_data = self.module_memory.read(
516 self.header.e_phoff,
517 self.header.e_phentsize as u64 * self.header.e_phnum as u64,
518 )?;
519 let program_headers = elf::ProgramHeader::parse(
520 &program_headers_data,
521 0,
522 self.header.e_phnum as usize,
523 self.context,
524 )?;
525 Ok(program_headers)
526 }
527
528 fn read_section_headers(&mut self) -> Result<elf::SectionHeaders, Error> {
529 if self.header.e_shoff == 0 {
530 return Err(Error::NoSections);
531 }
532
533 let section_headers_data = self.module_memory.read(
534 self.header.e_shoff,
535 self.header.e_shentsize as u64 * self.header.e_shnum as u64,
536 )?;
537 let section_headers = elf::SectionHeader::parse_from(
539 §ion_headers_data,
540 0,
541 self.header.e_shnum as usize,
542 self.context,
543 )?;
544 Ok(section_headers)
545 }
546
547 fn find_build_id_note(
548 &mut self,
549 offset: u64,
550 size: u64,
551 alignment: u64,
552 ) -> Result<Option<Vec<u8>>, Error> {
553 let notes = self.module_memory.read(offset, size)?;
554 for note in (elf::note::NoteDataIterator {
555 data: ¬es,
556 size: size as usize,
559 offset: 0,
560 ctx: (alignment as usize, self.context),
561 }) {
562 let Ok(note) = note else { break };
563 if note.name == "GNU" && note.n_type == elf::note::NT_GNU_BUILD_ID {
564 return Ok(Some(note.desc.to_owned()));
565 }
566 }
567 Ok(None)
568 }
569}
570
571#[cfg(test)]
572mod test {
573 use super::*;
574
575 const TINY_ELF: &[u8] = &[
592 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
593 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x03, 0x40, 0x00, 0x00, 0x00,
594 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00,
595 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x03, 0x00, 0x40, 0x00,
596 0x06, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0a, 0x03, 0x00,
597 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
598 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
599 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
600 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
601 0x00, 0x68, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
602 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
603 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
604 0x00, 0x00, 0x00, 0x00, 0xbd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x02, 0x40,
605 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
606 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
607 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
608 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
609 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
610 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
611 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
612 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x03, 0x40,
613 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,
614 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
615 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
616 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
617 0x00, 0x68, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0x00, 0x00,
618 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
619 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
620 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
621 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x02,
622 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
623 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
624 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
625 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x02, 0x40, 0x00, 0x00, 0x00,
626 0x00, 0x00, 0xbd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
627 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
628 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00,
629 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x02,
630 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d,
631 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
632 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
633 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x4e,
634 0x55, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
635 0x0e, 0x0f, 0x10, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e, 0x6e, 0x6f, 0x74, 0x65,
636 0x2e, 0x67, 0x6e, 0x75, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2d, 0x69, 0x64, 0x00, 0x2e,
637 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x61, 0x6d,
638 0x69, 0x63, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x74, 0x72, 0x00, 0x0e, 0x00, 0x00, 0x00,
639 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
640 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
641 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
642 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
643 0x00, 0x6c, 0x69, 0x62, 0x66, 0x6f, 0x6f, 0x2e, 0x73, 0x6f, 0x2e, 0x31, 0x00, 0x6a, 0x3c,
644 0x58, 0x31, 0xff, 0x0f, 0x05,
645 ];
646
647 #[test]
648 fn build_id_program_headers() {
649 let mut reader = ModuleReader::new(TINY_ELF.into()).unwrap();
650 let id = reader.build_id_from_program_headers().unwrap();
651 assert_eq!(
652 id,
653 vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
654 );
655 }
656
657 #[test]
658 fn build_id_section() {
659 let mut reader = ModuleReader::new(TINY_ELF.into()).unwrap();
660 let id = reader.build_id_from_section().unwrap();
661 assert_eq!(
662 id,
663 vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
664 );
665 }
666
667 #[test]
668 fn build_id_text_hash() {
669 let mut reader = ModuleReader::new(TINY_ELF.into()).unwrap();
670 let id = reader.build_id_generate_from_text().unwrap();
671 assert_eq!(
672 id,
673 vec![0x6a, 0x3c, 0x58, 0x31, 0xff, 0x0f, 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0]
674 );
675 }
676
677 #[test]
678 fn soname_program_headers() {
679 let mut reader = ModuleReader::new(TINY_ELF.into()).unwrap();
680 let soname = reader.soname_from_program_headers().unwrap();
681 assert_eq!(soname, "libfoo.so.1");
682 }
683
684 #[test]
685 fn soname_section() {
686 let mut reader = ModuleReader::new(TINY_ELF.into()).unwrap();
687 let soname = reader.soname_from_sections().unwrap();
688 assert_eq!(soname, "libfoo.so.1");
689 }
690}