object/read/macho/
exports_trie.rs1use crate::macho::{EXPORT_SYMBOL_FLAGS_REEXPORT, EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER};
2use crate::read::{Bytes, ReadError, Result};
3use alloc::boxed::Box;
4use alloc::vec::Vec;
5use core::convert::TryInto;
6
7#[derive(Debug)]
44pub struct ExportsTrieIterator<'data> {
45 node_iter: NodeIterator<'data>,
46}
47
48impl<'data> ExportsTrieIterator<'data> {
49 pub(super) fn new(data: &'data [u8]) -> Self {
50 ExportsTrieIterator {
51 node_iter: NodeIterator::new(data),
52 }
53 }
54
55 pub fn next(&mut self) -> Result<Option<ExportSymbol<'data>>> {
59 for node in &mut self.node_iter {
60 if let Some(export_symbol) = node? {
61 return Ok(Some(export_symbol));
62 }
63 }
64 Ok(None)
65 }
66}
67
68impl<'data> Iterator for ExportsTrieIterator<'data> {
69 type Item = Result<ExportSymbol<'data>>;
70
71 fn next(&mut self) -> Option<Self::Item> {
72 self.next().transpose()
73 }
74}
75
76#[derive(Debug)]
78pub struct ExportSymbol<'data> {
79 name: Box<[u8]>,
80 flags: u8,
81 data: ExportData<'data>,
82}
83
84impl<'data> ExportSymbol<'data> {
85 pub fn name(&self) -> &[u8] {
87 &self.name
88 }
89
90 pub fn flags(&self) -> u8 {
92 self.flags
93 }
94
95 pub fn data(&self) -> &ExportData<'data> {
97 &self.data
98 }
99}
100
101#[derive(Debug)]
102struct Frame<'data> {
103 data: Bytes<'data>,
104 children_remaining: u8,
105 name_buf_len: usize,
106}
107
108#[derive(Debug)]
109struct NodeIterator<'data> {
110 data: &'data [u8],
111 offset: usize,
112 stack: Vec<Frame<'data>>,
113 name_buf: Vec<u8>,
115}
116
117impl<'data> NodeIterator<'data> {
119 pub(super) fn new(data: &'data [u8]) -> Self {
120 NodeIterator {
121 data,
122 offset: 0,
123 stack: Vec::new(),
124 name_buf: Vec::new(),
125 }
126 }
127
128 fn push_node(&mut self) -> Result<Option<ExportSymbol<'data>>> {
129 let mut data = Bytes(
130 self.data
131 .get(self.offset..)
132 .read_error("Invalid exports trie offset")?,
133 );
134 let terminal_size = data
135 .read_uleb128()
136 .read_error("Invalid exports trie terminal size")?;
137 let export_data = if terminal_size == 0 {
138 None
139 } else {
140 let (flags, export_data) = ExportData::parse(
141 data.read_bytes(terminal_size as usize)
142 .read_error("Exports trie terminal size exceeds bounds")?,
143 )?;
144 Some(ExportSymbol {
145 name: self.name_buf.clone().into_boxed_slice(),
146 flags,
147 data: export_data,
148 })
149 };
150 let children_count = *data
151 .read::<u8>()
152 .read_error("Invalid exports trie children count")?;
153 self.stack.push(Frame {
154 data,
155 children_remaining: children_count,
156 name_buf_len: self.name_buf.len(),
157 });
158 Ok(export_data)
159 }
160
161 fn next(&mut self) -> Result<Option<Option<ExportSymbol<'data>>>> {
166 let Some(frame) = self.stack.last_mut() else {
167 if self.offset == 0 {
170 return Ok(Some(self.push_node()?));
171 }
172 return Ok(None);
173 };
174 self.name_buf.truncate(frame.name_buf_len);
175 if frame.children_remaining == 0 {
176 self.stack.pop();
177 return self.next();
178 }
179 let edge_str = frame
180 .data
181 .read_string()
182 .read_error("Invalid exports trie edge string")?;
183 let child_offset = frame
184 .data
185 .read_uleb128()
186 .read_error("Invalid exports trie child offset")?;
187 frame.children_remaining -= 1;
188 self.name_buf.extend(edge_str);
189 self.offset = child_offset as usize;
190 Ok(Some(self.push_node()?))
191 }
192}
193
194impl<'data> Iterator for NodeIterator<'data> {
195 type Item = Result<Option<ExportSymbol<'data>>>;
196
197 fn next(&mut self) -> Option<Self::Item> {
198 self.next().transpose()
199 }
200}
201
202#[derive(Debug)]
204pub enum ExportData<'data> {
205 Regular {
207 address: u64,
209 },
210 Reexport {
212 dylib_ordinal: u64,
214 import_name: &'data [u8],
216 },
217 StubAndResolver {
219 stub_address: u64,
221 resolver_address: u64,
223 },
224}
225
226impl<'data> ExportData<'data> {
227 pub(super) fn parse(mut data: Bytes<'data>) -> Result<(u8, Self)> {
228 let flags = data
229 .read_uleb128()
230 .read_error("Invalid exports trie flags")?;
231 let flags: u8 = flags
232 .try_into()
233 .map_err(|_| ())
234 .read_error("Exports trie flags too large")?;
235 if flags & EXPORT_SYMBOL_FLAGS_REEXPORT != 0 {
236 let dylib_ordinal = data
237 .read_uleb128()
238 .read_error("Invalid exports trie dylib ordinal")?;
239 let import_name = data
240 .read_string()
241 .read_error("Invalid exports trie import name")?;
242 return Ok((
243 flags,
244 ExportData::Reexport {
245 dylib_ordinal,
246 import_name,
247 },
248 ));
249 }
250 if flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER != 0 {
251 let stub_address = data
252 .read_uleb128()
253 .read_error("Invalid exports trie stub address")?;
254 let resolver_address = data
255 .read_uleb128()
256 .read_error("Invalid exports trie resolver address")?;
257 return Ok((
258 flags,
259 ExportData::StubAndResolver {
260 stub_address,
261 resolver_address,
262 },
263 ));
264 }
265 let address = data
266 .read_uleb128()
267 .read_error("Invalid exports trie address")?;
268 Ok((flags, ExportData::Regular { address }))
269 }
270}