use crate::macho::{EXPORT_SYMBOL_FLAGS_REEXPORT, EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER};
use crate::read::{Bytes, ReadError, Result};
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::convert::TryInto;
#[derive(Debug)]
pub struct ExportsTrieIterator<'data> {
node_iter: NodeIterator<'data>,
}
impl<'data> ExportsTrieIterator<'data> {
pub(super) fn new(data: &'data [u8]) -> Self {
ExportsTrieIterator {
node_iter: NodeIterator::new(data),
}
}
pub fn next(&mut self) -> Result<Option<ExportSymbol<'data>>> {
for node in &mut self.node_iter {
if let Some(export_symbol) = node? {
return Ok(Some(export_symbol));
}
}
Ok(None)
}
}
impl<'data> Iterator for ExportsTrieIterator<'data> {
type Item = Result<ExportSymbol<'data>>;
fn next(&mut self) -> Option<Self::Item> {
self.next().transpose()
}
}
#[derive(Debug)]
pub struct ExportSymbol<'data> {
name: Box<[u8]>,
flags: u8,
data: ExportData<'data>,
}
impl<'data> ExportSymbol<'data> {
pub fn name(&self) -> &[u8] {
&self.name
}
pub fn flags(&self) -> u8 {
self.flags
}
pub fn data(&self) -> &ExportData<'data> {
&self.data
}
}
#[derive(Debug)]
struct Frame<'data> {
data: Bytes<'data>,
children_remaining: u8,
name_buf_len: usize,
}
#[derive(Debug)]
struct NodeIterator<'data> {
data: &'data [u8],
offset: usize,
stack: Vec<Frame<'data>>,
name_buf: Vec<u8>,
}
impl<'data> NodeIterator<'data> {
pub(super) fn new(data: &'data [u8]) -> Self {
NodeIterator {
data,
offset: 0,
stack: Vec::new(),
name_buf: Vec::new(),
}
}
fn push_node(&mut self) -> Result<Option<ExportSymbol<'data>>> {
let mut data = Bytes(
self.data
.get(self.offset..)
.read_error("Invalid exports trie offset")?,
);
let terminal_size = data
.read_uleb128()
.read_error("Invalid exports trie terminal size")?;
let export_data = if terminal_size == 0 {
None
} else {
let (flags, export_data) = ExportData::parse(
data.read_bytes(terminal_size as usize)
.read_error("Exports trie terminal size exceeds bounds")?,
)?;
Some(ExportSymbol {
name: self.name_buf.clone().into_boxed_slice(),
flags,
data: export_data,
})
};
let children_count = *data
.read::<u8>()
.read_error("Invalid exports trie children count")?;
self.stack.push(Frame {
data,
children_remaining: children_count,
name_buf_len: self.name_buf.len(),
});
Ok(export_data)
}
fn next(&mut self) -> Result<Option<Option<ExportSymbol<'data>>>> {
let Some(frame) = self.stack.last_mut() else {
if self.offset == 0 {
return Ok(Some(self.push_node()?));
}
return Ok(None);
};
self.name_buf.truncate(frame.name_buf_len);
if frame.children_remaining == 0 {
self.stack.pop();
return self.next();
}
let edge_str = frame
.data
.read_string()
.read_error("Invalid exports trie edge string")?;
let child_offset = frame
.data
.read_uleb128()
.read_error("Invalid exports trie child offset")?;
frame.children_remaining -= 1;
self.name_buf.extend(edge_str);
self.offset = child_offset as usize;
Ok(Some(self.push_node()?))
}
}
impl<'data> Iterator for NodeIterator<'data> {
type Item = Result<Option<ExportSymbol<'data>>>;
fn next(&mut self) -> Option<Self::Item> {
self.next().transpose()
}
}
#[derive(Debug)]
pub enum ExportData<'data> {
Regular {
address: u64,
},
Reexport {
dylib_ordinal: u64,
import_name: &'data [u8],
},
StubAndResolver {
stub_address: u64,
resolver_address: u64,
},
}
impl<'data> ExportData<'data> {
pub(super) fn parse(mut data: Bytes<'data>) -> Result<(u8, Self)> {
let flags = data
.read_uleb128()
.read_error("Invalid exports trie flags")?;
let flags: u8 = flags
.try_into()
.map_err(|_| ())
.read_error("Exports trie flags too large")?;
if flags & EXPORT_SYMBOL_FLAGS_REEXPORT != 0 {
let dylib_ordinal = data
.read_uleb128()
.read_error("Invalid exports trie dylib ordinal")?;
let import_name = data
.read_string()
.read_error("Invalid exports trie import name")?;
return Ok((
flags,
ExportData::Reexport {
dylib_ordinal,
import_name,
},
));
}
if flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER != 0 {
let stub_address = data
.read_uleb128()
.read_error("Invalid exports trie stub address")?;
let resolver_address = data
.read_uleb128()
.read_error("Invalid exports trie resolver address")?;
return Ok((
flags,
ExportData::StubAndResolver {
stub_address,
resolver_address,
},
));
}
let address = data
.read_uleb128()
.read_error("Invalid exports trie address")?;
Ok((flags, ExportData::Regular { address }))
}
}