macho_unwind_info/opcodes/
x86.rs

1use std::fmt::Display;
2
3use super::bitfield::OpcodeBitfield;
4use super::permutation::decode_permutation_6;
5use crate::consts::*;
6
7#[derive(Copy, Clone, Debug, PartialEq, Eq)]
8pub enum RegisterNameX86 {
9    Ebx,
10    Ecx,
11    Edx,
12    Edi,
13    Esi,
14    Ebp,
15}
16
17impl RegisterNameX86 {
18    pub fn parse(n: u8) -> Option<Self> {
19        match n {
20            1 => Some(RegisterNameX86::Ebx),
21            2 => Some(RegisterNameX86::Ecx),
22            3 => Some(RegisterNameX86::Edx),
23            4 => Some(RegisterNameX86::Edi),
24            5 => Some(RegisterNameX86::Esi),
25            6 => Some(RegisterNameX86::Ebp),
26            _ => None,
27        }
28    }
29
30    pub fn dwarf_name(&self) -> &'static str {
31        match self {
32            RegisterNameX86::Ebx => "reg3",
33            RegisterNameX86::Ecx => "reg1",
34            RegisterNameX86::Edx => "reg2",
35            RegisterNameX86::Edi => "reg7",
36            RegisterNameX86::Esi => "reg6",
37            RegisterNameX86::Ebp => "reg5",
38        }
39    }
40}
41
42#[derive(Copy, Clone, Debug, PartialEq, Eq)]
43pub enum OpcodeX86 {
44    Null,
45    FrameBased {
46        stack_offset_in_bytes: u16,
47        saved_regs: [Option<RegisterNameX86>; 5],
48    },
49    FramelessImmediate {
50        stack_size_in_bytes: u16,
51        saved_regs: [Option<RegisterNameX86>; 6],
52    },
53    FramelessIndirect {
54        /// Offset from the start of the function into the middle of a `sub`
55        /// instruction, pointing right at the instruction's "immediate" which
56        /// is a u32 value with the offset we need. (NOTE: not divided by anything!)
57        immediate_offset_from_function_start: u8,
58
59        /// An offset to add to the loaded stack size.
60        /// This allows the stack size to differ slightly from the `sub`, to
61        /// compensate for any function prologue that pushes a bunch of
62        /// pointer-sized registers. This adjust value includes the return
63        /// address on the stack. For example, if the function begins with six push
64        /// instructions, followed by a sub instruction, then stack_adjust_in_bytes
65        /// is 28: 4 bytes for the return address + 6 * 4 for each pushed register.
66        stack_adjust_in_bytes: u8,
67
68        /// The registers, in the order that they need to be popped in when
69        /// returning / unwinding from this function. (Reverse order from
70        /// function prologue!)
71        /// Can have leading `None`s.
72        saved_regs: [Option<RegisterNameX86>; 6],
73    },
74    Dwarf {
75        eh_frame_fde: u32,
76    },
77    InvalidFrameless,
78    UnrecognizedKind(u8),
79}
80
81impl OpcodeX86 {
82    pub fn parse(opcode: u32) -> Self {
83        match OpcodeBitfield::new(opcode).kind() {
84            OPCODE_KIND_NULL => OpcodeX86::Null,
85            OPCODE_KIND_X86_FRAMEBASED => OpcodeX86::FrameBased {
86                stack_offset_in_bytes: (((opcode >> 16) & 0xff) as u16) * 4,
87                saved_regs: [
88                    RegisterNameX86::parse(((opcode >> 12) & 0b111) as u8),
89                    RegisterNameX86::parse(((opcode >> 9) & 0b111) as u8),
90                    RegisterNameX86::parse(((opcode >> 6) & 0b111) as u8),
91                    RegisterNameX86::parse(((opcode >> 3) & 0b111) as u8),
92                    RegisterNameX86::parse((opcode & 0b111) as u8),
93                ],
94            },
95            OPCODE_KIND_X86_FRAMELESS_IMMEDIATE => {
96                let stack_size_in_bytes = (((opcode >> 16) & 0xff) as u16) * 4;
97                let register_count = (opcode >> 10) & 0b111;
98                let register_permutation = opcode & 0b11_1111_1111;
99                let saved_registers =
100                    match decode_permutation_6(register_count, register_permutation) {
101                        Ok(regs) => regs,
102                        Err(_) => return OpcodeX86::InvalidFrameless,
103                    };
104                OpcodeX86::FramelessImmediate {
105                    stack_size_in_bytes,
106                    saved_regs: [
107                        RegisterNameX86::parse(saved_registers[0]),
108                        RegisterNameX86::parse(saved_registers[1]),
109                        RegisterNameX86::parse(saved_registers[2]),
110                        RegisterNameX86::parse(saved_registers[3]),
111                        RegisterNameX86::parse(saved_registers[4]),
112                        RegisterNameX86::parse(saved_registers[5]),
113                    ],
114                }
115            }
116            OPCODE_KIND_X86_FRAMELESS_INDIRECT => {
117                let immediate_offset_from_function_start = (opcode >> 16) as u8;
118                let stack_adjust_in_bytes = ((opcode >> 13) & 0b111) as u8 * 4;
119                let register_count = (opcode >> 10) & 0b111;
120                let register_permutation = opcode & 0b11_1111_1111;
121                let saved_registers =
122                    match decode_permutation_6(register_count, register_permutation) {
123                        Ok(regs) => regs,
124                        Err(_) => return OpcodeX86::InvalidFrameless,
125                    };
126                OpcodeX86::FramelessIndirect {
127                    immediate_offset_from_function_start,
128                    stack_adjust_in_bytes,
129                    saved_regs: [
130                        RegisterNameX86::parse(saved_registers[0]),
131                        RegisterNameX86::parse(saved_registers[1]),
132                        RegisterNameX86::parse(saved_registers[2]),
133                        RegisterNameX86::parse(saved_registers[3]),
134                        RegisterNameX86::parse(saved_registers[4]),
135                        RegisterNameX86::parse(saved_registers[5]),
136                    ],
137                }
138            }
139            OPCODE_KIND_X86_DWARF => OpcodeX86::Dwarf {
140                eh_frame_fde: (opcode & 0xffffff),
141            },
142            kind => OpcodeX86::UnrecognizedKind(kind),
143        }
144    }
145}
146
147impl Display for OpcodeX86 {
148    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149        match self {
150            OpcodeX86::Null => {
151                write!(f, "(uncovered)")?;
152            }
153            OpcodeX86::FrameBased {
154                stack_offset_in_bytes,
155                saved_regs,
156            } => {
157                // ebp was set to esp before the saved registers were pushed.
158                // The first pushed register is at ebp - 4 (== CFA - 12), the last at ebp - stack_offset_in_bytes.
159                write!(f, "CFA=reg6+8: reg6=[CFA-8], reg16=[CFA-4]")?;
160                let max_count = (*stack_offset_in_bytes / 4) as usize;
161                let mut offset = *stack_offset_in_bytes + 8; // + 2 for rbp, return address
162                for reg in saved_regs.iter().rev().take(max_count) {
163                    if let Some(reg) = reg {
164                        write!(f, ", {}=[CFA-{}]", reg.dwarf_name(), offset)?;
165                    }
166                    offset -= 4;
167                }
168            }
169            OpcodeX86::FramelessImmediate {
170                stack_size_in_bytes,
171                saved_regs,
172            } => {
173                if *stack_size_in_bytes == 0 {
174                    write!(f, "CFA=reg7:",)?;
175                } else {
176                    write!(f, "CFA=reg7+{}:", *stack_size_in_bytes)?;
177                }
178                write!(f, " reg16=[CFA-4]")?;
179                let mut offset = 2 * 4;
180                for reg in saved_regs.iter().rev().flatten() {
181                    write!(f, ", {}=[CFA-{}]", reg.dwarf_name(), offset)?;
182                    offset += 4;
183                }
184            }
185            OpcodeX86::FramelessIndirect {
186                immediate_offset_from_function_start,
187                stack_adjust_in_bytes,
188                saved_regs,
189            } => {
190                write!(
191                    f,
192                    "CFA=[function_start+{}]+{}",
193                    immediate_offset_from_function_start, stack_adjust_in_bytes
194                )?;
195                write!(f, " reg16=[CFA-4]")?;
196                let mut offset = 2 * 4;
197                for reg in saved_regs.iter().rev().flatten() {
198                    write!(f, ", {}=[CFA-{}]", reg.dwarf_name(), offset)?;
199                    offset += 4;
200                }
201            }
202            OpcodeX86::Dwarf { eh_frame_fde } => {
203                write!(f, "(check eh_frame FDE 0x{:x})", eh_frame_fde)?;
204            }
205            OpcodeX86::InvalidFrameless => {
206                write!(
207                    f,
208                    "!! frameless immediate or indirect with invalid permutation encoding"
209                )?;
210            }
211            OpcodeX86::UnrecognizedKind(kind) => {
212                write!(f, "!! Unrecognized kind {}", kind)?;
213            }
214        }
215        Ok(())
216    }
217}
218
219#[cfg(test)]
220mod test {
221    use super::*;
222
223    #[test]
224    fn test_frameless_indirect() {
225        use RegisterNameX86::*;
226        assert_eq!(
227            OpcodeX86::parse(0x30df800),
228            OpcodeX86::FramelessIndirect {
229                immediate_offset_from_function_start: 13,
230                stack_adjust_in_bytes: 28,
231                saved_regs: [
232                    Some(Ebx),
233                    Some(Ecx),
234                    Some(Edx),
235                    Some(Edi),
236                    Some(Esi),
237                    Some(Ebp)
238                ]
239            }
240        )
241    }
242}