1use crate::error::{Error, Result};
4use crate::patch::{Location, Rooted};
5use goblin::container::{Container, Ctx, Endian};
6use goblin::elf::section_header::{SHT_DYNSYM, SHT_SYMTAB};
7use goblin::elf::sym::Sym;
8use goblin::elf::{Elf, SectionHeader};
9use scroll::ctx::TryFromCtx;
10use scroll::Pread;
11
12fn context_from_elf(elf: &Elf) -> Ctx {
13 let container = if elf.is_64 {
14 Container::Big
15 } else {
16 Container::Little
17 };
18 let endian = if elf.little_endian {
19 Endian::Little
20 } else {
21 Endian::Big
22 };
23 Ctx::new(container, endian)
24}
25
26pub struct SymtabIter<'a> {
28 bytes: &'a [u8],
29 ctx: Ctx,
30 symoff: usize,
31 stroff: usize,
32 step: usize,
33 count: usize,
34 index: usize,
35}
36
37impl<'a> SymtabIter<'a> {
38 pub fn from_section_header(
42 bytes: &'a [u8],
43 header: &SectionHeader,
44 headers: &[SectionHeader],
45 ctx: Ctx,
46 ) -> Result<Self> {
47 if header.sh_type != SHT_SYMTAB && header.sh_type != SHT_DYNSYM {
48 return Err(Error::WrongSectionHeader(
49 "symtab requires sh_type equal to SHT_SYMTAB or SHT_DYNSYM".to_string(),
50 ));
51 }
52 if (header.sh_entsize as usize) < Sym::size(ctx.container) {
53 return Err(Error::Malformed("sh_entsize too small".to_string()));
54 };
55 if (header.sh_link as usize) > headers.len() {
56 return Err(Error::Malformed("sh_link too large".to_string()));
57 }
58 Ok(Self {
59 bytes,
60 ctx,
61 symoff: header.sh_offset as usize,
62 stroff: headers[header.sh_link as usize].sh_offset as usize,
63 step: header.sh_entsize as usize,
64 count: if header.sh_entsize == 0 {
65 0
66 } else {
67 header.sh_size / header.sh_entsize
68 } as usize,
69 index: 0,
70 })
71 }
72
73 pub fn symtab_from_elf(bytes: &'a [u8], elf: &Elf) -> Result<Option<Self>> {
77 let ctx = context_from_elf(elf);
78 for header in &elf.section_headers {
79 if header.sh_type == SHT_SYMTAB {
80 return Some(Self::from_section_header(
81 bytes,
82 header,
83 &elf.section_headers,
84 ctx,
85 ))
86 .transpose();
87 }
88 }
89 Ok(None)
90 }
91
92 pub fn dynsym_from_elf(bytes: &'a [u8], elf: &Elf) -> Result<Option<Self>> {
96 let ctx = context_from_elf(elf);
97 for header in &elf.section_headers {
98 if header.sh_type == SHT_DYNSYM {
99 return Some(Self::from_section_header(
100 bytes,
101 header,
102 &elf.section_headers,
103 ctx,
104 ))
105 .transpose();
106 }
107 }
108 Ok(None)
109 }
110}
111
112impl<'a> std::iter::Iterator for SymtabIter<'a> {
113 type Item = Result<(Option<Rooted<&'a str>>, Rooted<Sym>)>;
114
115 fn next(&mut self) -> Option<Self::Item> {
116 if self.index >= self.count {
117 None
118 } else {
119 Some((|| {
120 let sym_offset = self.symoff + self.index * self.step;
121 self.index += 1;
122 let sym = {
123 let (sym, sym_size) = Sym::try_from_ctx(&self.bytes[sym_offset..], self.ctx)?;
124 let location = Location {
125 offset: sym_offset,
126 size: sym_size,
127 ctx: self.ctx,
128 };
129 Rooted::new(location, sym)
130 };
131 let name = if sym.st_name != 0 {
132 let offset = self.stroff + sym.st_name as usize;
133 let name: &str = self.bytes.pread(offset)?;
134 let location = Location {
135 offset,
136 size: name.len(),
137 ctx: self.ctx,
138 };
139 Some(Rooted::new(location, name))
140 } else {
141 None
142 };
143 Ok((name, sym))
144 })())
145 }
146 }
147}