Skip to main content

macho2/command/
function_starts.rs

1use std::io::{Read, Seek, SeekFrom};
2
3
4use crate::{helpers::read_uleb_many, macho::MachOResult};
5
6use super::{linkedit_data::LinkeditDataCommand, pad_to_size, LCLoadCommand, LoadCommandParser, LoadCommandResolver};
7
8#[derive(Debug, PartialEq, Eq)]
9pub struct FunctionOffset {
10    pub offset: u64,
11    pub size: u64,
12}
13
14#[derive(Debug, PartialEq, Eq)]
15pub struct FunctionStartsCommand {
16    pub cmd: LCLoadCommand,
17    pub cmdsize: u32,
18    pub dataoff: u32,
19    pub datasize: u32,
20}
21
22impl LoadCommandParser for FunctionStartsCommand {
23    fn parse(ldcmd: &[u8]) -> MachOResult<Self> {
24        let (_, linkeditcmd) = LinkeditDataCommand::parse(ldcmd)?;
25        Ok(
26            FunctionStartsCommand {
27                cmd: linkeditcmd.cmd,
28                cmdsize: linkeditcmd.cmdsize,
29                dataoff: linkeditcmd.dataoff,
30                datasize: linkeditcmd.datasize,
31            },
32        )
33    }
34
35    fn serialize(&self) -> Vec<u8> {
36        let mut buf = Vec::new();
37        buf.extend(self.cmd.serialize());
38        buf.extend(self.cmdsize.to_le_bytes());
39        buf.extend(self.dataoff.to_le_bytes());
40        buf.extend(self.datasize.to_le_bytes());
41        pad_to_size(&mut buf, self.cmdsize as usize);
42        buf
43    }
44}
45
46#[derive(Debug, PartialEq, Eq)]
47pub struct FunctionStartsCommandResolved {
48    pub funcs: Vec<FunctionOffset>,
49}
50
51impl<T: Read + Seek> LoadCommandResolver<T, FunctionStartsCommandResolved> for FunctionStartsCommand {
52    fn resolve(&self, buf: &mut T) -> MachOResult<FunctionStartsCommandResolved> {
53        let mut funcs_blob = vec![0u8; self.datasize as usize];
54        buf.seek(SeekFrom::Start(self.dataoff as u64))
55            .unwrap();
56        buf.read_exact(&mut funcs_blob).unwrap();
57
58        let (_, funcs) = read_uleb_many(&funcs_blob).unwrap();
59
60        // Drop leading zeros from the function offsets
61        let funcs: Vec<u64> = funcs.into_iter().skip_while(|&x| x == 0).collect();
62
63        let mut state: u64 = 0;
64        let mut results = vec![];
65        for func in funcs.windows(2) {
66            state = state.wrapping_add(func[0]);
67            results.push(FunctionOffset {
68                offset: state,
69                size: func[1],
70            });
71        }
72
73        Ok(
74            FunctionStartsCommandResolved {
75                funcs: results
76            },
77        )
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use crate::command::LCLoadCommand;
85
86    #[test]
87    fn test_function_starts() {
88        let func_starts = FunctionStartsCommand {
89            cmd: LCLoadCommand::LcFunctionStarts,
90            cmdsize: 0x10,
91            dataoff: 0x20,
92            datasize: 0x30,
93        };
94
95        let serialized = func_starts.serialize();
96        let deserialized = FunctionStartsCommand::parse(&serialized).unwrap();
97        assert_eq!(func_starts, deserialized);
98    }
99}