Skip to main content

simple_frame_rs/
v1.rs

1//! SFrame Version 1 types and implementation.
2//!
3//! Ref: <https://sourceware.org/binutils/docs-2.40/sframe-spec.html>
4
5use std::{cmp::Ordering, fmt::Write};
6
7use bitflags::bitflags;
8use fallible_iterator::FallibleIterator;
9
10use crate::{SFrameError, SFrameResult, read_binary, read_struct};
11
12/// SFrame ABI/arch Identifier
13///
14/// Ref: <https://sourceware.org/binutils/docs-2.40/sframe-spec.html#SFrame-ABI_002farch-Identifier>
15#[derive(Debug, Clone, Copy)]
16pub enum SFrameABI {
17    /// SFRAME_ABI_AARCH64_ENDIAN_BIG
18    AArch64BigEndian,
19    /// SFRAME_ABI_AARCH64_ENDIAN_LITTLE
20    AArch64LittleEndian,
21    /// SFRAME_ABI_AMD64_ENDIAN_LITTLE
22    AMD64LittleEndian,
23}
24
25bitflags! {
26    /// SFrame Flags
27    ///
28    /// Ref: <https://sourceware.org/binutils/docs-2.40/sframe-spec.html#SFrame-Flags>
29    #[derive(Debug, Clone, Copy)]
30    pub struct SFrameFlags: u8 {
31        /// Function Descriptor Entries are sorted on PC.
32        const SFRAME_F_FDE_SORTED = 0x1;
33        /// Functions preserve frame-pointer.
34        const SFRAME_F_FRAME_POINTER = 0x2;
35    }
36}
37
38/// SFrame section
39///
40/// Ref: <https://sourceware.org/binutils/docs-2.40/sframe-spec.html#SFrame-Section>
41#[derive(Debug, Clone, Copy)]
42#[allow(dead_code)]
43pub struct SFrameSection<'a> {
44    data: &'a [u8],
45    section_base: u64,
46    little_endian: bool,
47    flags: SFrameFlags,
48    abi: SFrameABI,
49    cfa_fixed_fp_offset: i8,
50    cfa_fixed_ra_offset: i8,
51    auxhdr_len: u8,
52    num_fdes: u32,
53    num_fres: u32,
54    fre_len: u32,
55    fdeoff: u32,
56    freoff: u32,
57}
58
59/// The magic number for SFrame section: 0xdee2
60const SFRAME_MAGIC: u16 = 0xdee2;
61
62impl<'a> SFrameSection<'a> {
63    /// Parse SFrame section from data
64    pub fn from(data: &'a [u8], section_base: u64) -> SFrameResult<SFrameSection<'a>> {
65        // parse sframe_header
66        if data.len() < core::mem::size_of::<RawSFrameHeader>() {
67            return Err(SFrameError::UnexpectedEndOfData);
68        }
69
70        // probe magic
71        let magic_offset = core::mem::offset_of!(RawSFrameHeader, magic);
72        let mut magic_bytes: [u8; 2] = [0; 2];
73        magic_bytes.copy_from_slice(&data[magic_offset..magic_offset + 2]);
74        let magic_le = u16::from_le_bytes(magic_bytes);
75        let little_endian;
76        if magic_le == SFRAME_MAGIC {
77            little_endian = true;
78        } else {
79            let magic_be = u16::from_be_bytes(magic_bytes);
80            if magic_be == SFRAME_MAGIC {
81                little_endian = false;
82            } else {
83                return Err(SFrameError::InvalidMagic);
84            }
85        }
86
87        // probe version
88        let version_offset = core::mem::offset_of!(RawSFrameHeader, version);
89        let version = data[version_offset];
90        if version != 1 {
91            return Err(SFrameError::UnsupportedVersion);
92        }
93
94        // probe flag
95        let flags_offset = core::mem::offset_of!(RawSFrameHeader, flags);
96        let flags = data[flags_offset];
97        let flags = match SFrameFlags::from_bits(flags) {
98            Some(flags) => flags,
99            None => return Err(SFrameError::UnsupportedFlags),
100        };
101
102        // probe abi
103        let abi_offset = core::mem::offset_of!(RawSFrameHeader, abi_arch);
104        let abi = data[abi_offset];
105        let abi = match abi {
106            1 => SFrameABI::AArch64BigEndian,
107            2 => SFrameABI::AArch64LittleEndian,
108            3 => SFrameABI::AMD64LittleEndian,
109            _ => return Err(SFrameError::UnsupportedABI),
110        };
111
112        let cfa_fixed_fp_offset =
113            data[core::mem::offset_of!(RawSFrameHeader, cfa_fixed_fp_offset)] as i8;
114        let cfa_fixed_ra_offset =
115            data[core::mem::offset_of!(RawSFrameHeader, cfa_fixed_ra_offset)] as i8;
116        let auxhdr_len = data[core::mem::offset_of!(RawSFrameHeader, auxhdr_len)];
117
118        // initial validation
119        let num_fdes = read_struct!(RawSFrameHeader, data, little_endian, num_fdes, u32);
120        let fdeoff = read_struct!(RawSFrameHeader, data, little_endian, fdeoff, u32);
121        if data.len() - core::mem::size_of::<RawSFrameHeader>() < fdeoff as usize {
122            return Err(SFrameError::UnexpectedEndOfData);
123        } else if (data.len() - core::mem::size_of::<RawSFrameHeader>() - fdeoff as usize)
124            / core::mem::size_of::<RawSFrameFDE>()
125            < num_fdes as usize
126        {
127            return Err(SFrameError::UnexpectedEndOfData);
128        }
129
130        Ok(SFrameSection {
131            data,
132            section_base,
133            little_endian,
134            flags,
135            abi,
136            cfa_fixed_fp_offset,
137            cfa_fixed_ra_offset,
138            auxhdr_len,
139            num_fdes,
140            num_fres: read_struct!(RawSFrameHeader, data, little_endian, num_fres, u32),
141            fre_len: read_struct!(RawSFrameHeader, data, little_endian, fre_len, u32),
142            fdeoff,
143            freoff: read_struct!(RawSFrameHeader, data, little_endian, freoff, u32),
144        })
145    }
146
147    /// Get the count of FDE entries
148    pub fn get_fde_count(&self) -> u32 {
149        self.num_fdes
150    }
151
152    /// Access FDE by index
153    pub fn get_fde(&self, index: u32) -> SFrameResult<Option<SFrameFDE>> {
154        if index >= self.num_fdes {
155            // out of bounds
156            return Ok(None);
157        }
158
159        // The sub-section offsets, namely sfh_fdeoff and sfh_freoff, in the
160        // SFrame header are relative to the end of the SFrame header; they are
161        // each an offset in bytes into the SFrame section where the SFrame FDE
162        // sub-section and the SFrame FRE sub-section respectively start.
163        let offset = self.fdeoff as usize
164            + index as usize * core::mem::size_of::<RawSFrameFDE>()
165            + core::mem::size_of::<RawSFrameHeader>();
166        if offset + core::mem::size_of::<RawSFrameFDE>() > self.data.len() {
167            return Err(SFrameError::UnexpectedEndOfData);
168        }
169
170        Ok(Some(SFrameFDE {
171            func_start_address: read_struct!(
172                RawSFrameFDE,
173                &self.data[offset..],
174                self.little_endian,
175                func_start_address,
176                i32
177            ),
178            func_size: read_struct!(
179                RawSFrameFDE,
180                &self.data[offset..],
181                self.little_endian,
182                func_size,
183                u32
184            ),
185            func_start_fre_off: read_struct!(
186                RawSFrameFDE,
187                &self.data[offset..],
188                self.little_endian,
189                func_start_fre_off,
190                u32
191            ),
192            func_num_fres: read_struct!(
193                RawSFrameFDE,
194                &self.data[offset..],
195                self.little_endian,
196                func_num_fres,
197                u32
198            ),
199            func_info: SFrameFDEInfo(
200                self.data[offset + core::mem::offset_of!(RawSFrameFDE, func_info)],
201            ),
202        }))
203    }
204
205    /// Print the section in string in the same way as objdump
206    pub fn to_string(&self) -> SFrameResult<String> {
207        let mut s = String::new();
208        writeln!(&mut s, "Header :")?;
209        writeln!(&mut s)?;
210        writeln!(&mut s, "  Version: SFRAME_VERSION_1")?;
211        writeln!(
212            &mut s,
213            "  Flags: {}",
214            self.flags
215                .iter_names()
216                .map(|(name, _flag)| name)
217                .collect::<Vec<_>>()
218                .join(" | ")
219        )?;
220        writeln!(&mut s, "  Num FDEs: {:?}", self.num_fdes)?;
221        writeln!(&mut s, "  Num FREs: {:?}", self.num_fres)?;
222        writeln!(&mut s)?;
223        writeln!(&mut s, "Function Index :")?;
224        writeln!(&mut s)?;
225        for i in 0..self.num_fdes {
226            let fde = self.get_fde(i)?.unwrap();
227            let pc = fde.get_pc(self);
228            let mut suffix = String::new();
229
230            // aarch64 pauth
231            if let SFrameAArch64PAuthKey::KeyB = fde.func_info.get_aarch64_pauth_key()? {
232                suffix += ", pauth = B key";
233            }
234            writeln!(
235                &mut s,
236                "  func idx [{i}]: pc = 0x{:x}, size = {} bytes{}",
237                pc, fde.func_size, suffix
238            )?;
239
240            match fde.func_info.get_fde_type()? {
241                SFrameFDEType::PCInc => {
242                    writeln!(&mut s, "  STARTPC           CFA      FP     RA")?;
243                }
244                SFrameFDEType::PCMask => {
245                    writeln!(&mut s, "  STARTPC[m]        CFA      FP     RA")?;
246                }
247            }
248            let mut iter = fde.iter_fre(self);
249            while let Some(fre) = iter.next()? {
250                let start_pc = match fde.func_info.get_fde_type()? {
251                    SFrameFDEType::PCInc => pc + fre.start_address.get() as u64,
252                    SFrameFDEType::PCMask => fre.start_address.get() as u64,
253                };
254                let base_reg = if fre.info.get_cfa_base_reg_id() == 0 {
255                    "fp"
256                } else {
257                    "sp"
258                };
259                let cfa = format!("{}+{}", base_reg, fre.stack_offsets[0].get());
260                let fp = match fre.get_fp_offset(self) {
261                    Some(offset) if self.cfa_fixed_fp_offset == 0 => format!("c{:+}", offset),
262                    _ => "u".to_string(), // without offset
263                };
264                let mut ra = match fre.get_ra_offset(self) {
265                    Some(offset) if self.cfa_fixed_ra_offset == 0 => format!("c{:+}", offset),
266                    _ => "u".to_string(), // without offset
267                };
268                if fre.info.get_mangled_ra_p() {
269                    // ra is mangled with signature
270                    ra.push_str("[s]");
271                }
272                let rest = format!("{cfa:8} {fp:6} {ra}");
273                writeln!(&mut s, "  {:016x}  {}", start_pc, rest)?;
274            }
275            writeln!(&mut s,)?;
276        }
277        Ok(s)
278    }
279
280    /// Iterate FDE entries
281    pub fn iter_fde(&self) -> SFrameFDEIterator<'_> {
282        SFrameFDEIterator {
283            section: self,
284            index: 0,
285        }
286    }
287
288    /// Find FDE entry by pc
289    pub fn find_fde(&self, pc: u64) -> SFrameResult<Option<SFrameFDE>> {
290        if self.flags.contains(SFrameFlags::SFRAME_F_FDE_SORTED) {
291            // binary search
292            // mimic binary_search_by impl from rust std
293            let mut size = self.num_fdes;
294            if size == 0 {
295                return Ok(None);
296            }
297            let mut base = 0;
298
299            while size > 1 {
300                let half = size / 2;
301                let mid = base + half;
302
303                let cmp = self.get_fde(mid)?.unwrap().get_pc(self).cmp(&pc);
304                if cmp != Ordering::Greater {
305                    base = mid;
306                }
307                size -= half;
308            }
309
310            let base_fde = self.get_fde(base)?.unwrap();
311            let base_pc = base_fde.get_pc(self);
312            let cmp = base_pc.cmp(&pc);
313            match cmp {
314                Ordering::Equal | Ordering::Less if pc < base_pc + base_fde.func_size as u64 => {
315                    Ok(Some(base_fde))
316                }
317                _ => Ok(None),
318            }
319        } else {
320            // linear scan
321            let mut iter = self.iter_fde();
322            while let Some(fde) = iter.next()? {
323                let start = fde.get_pc(self);
324                if start <= pc && pc - start < fde.func_size as u64 {
325                    return Ok(Some(fde));
326                }
327            }
328            Ok(None)
329        }
330    }
331
332    /// Get SFrame flags
333    pub fn get_flags(&self) -> SFrameFlags {
334        self.flags
335    }
336
337    /// Get SFrame ABI
338    pub fn get_abi(&self) -> SFrameABI {
339        self.abi
340    }
341
342    /// Get SFrame CFA fixed FP offset
343    pub fn get_cfa_fixed_fp_offset(&self) -> i8 {
344        self.cfa_fixed_fp_offset
345    }
346
347    /// Get SFrame CFA fixed RA offset
348    pub fn get_cfa_fixed_ra_offset(&self) -> i8 {
349        self.cfa_fixed_ra_offset
350    }
351}
352
353/// Raw SFrame Header
354///
355/// Ref: <https://sourceware.org/binutils/docs-2.40/sframe-spec.html#SFrame-Header>
356#[repr(C, packed)]
357struct RawSFrameHeader {
358    magic: u16,
359    version: u8,
360    flags: u8,
361    abi_arch: u8,
362    cfa_fixed_fp_offset: i8,
363    cfa_fixed_ra_offset: i8,
364    auxhdr_len: u8,
365    num_fdes: u32,
366    num_fres: u32,
367    fre_len: u32,
368    fdeoff: u32,
369    freoff: u32,
370}
371
372/// Raw SFrame FDE
373///
374/// Ref: <https://sourceware.org/binutils/docs-2.40/sframe-spec.html#SFrame-Function-Descriptor-Entries>
375#[repr(C, packed)]
376#[allow(dead_code)]
377struct RawSFrameFDE {
378    func_start_address: i32,
379    func_size: u32,
380    func_start_fre_off: u32,
381    func_num_fres: u32,
382    func_info: u8,
383}
384
385/// SFrame FDE Info Word
386///
387/// Note: Bits 6-7 are unused per spec, but not validated to allow for future extensions.
388///
389/// Ref: <https://sourceware.org/binutils/docs-2.40/sframe-spec.html#The-SFrame-FDE-Info-Word>
390#[derive(Debug, Clone, Copy)]
391#[repr(transparent)]
392pub struct SFrameFDEInfo(u8);
393
394impl SFrameFDEInfo {
395    /// Get SFrame FRE type
396    pub fn get_fre_type(&self) -> SFrameResult<SFrameFREType> {
397        let fretype = self.0 & 0b1111;
398        match fretype {
399            0 => Ok(SFrameFREType::Addr1),
400            1 => Ok(SFrameFREType::Addr2),
401            2 => Ok(SFrameFREType::Addr4),
402            _ => Err(SFrameError::UnsupportedFREType),
403        }
404    }
405
406    /// Get SFrame FDE type
407    pub fn get_fde_type(&self) -> SFrameResult<SFrameFDEType> {
408        let fretype = (self.0 >> 4) & 0b1;
409        match fretype {
410            0 => Ok(SFrameFDEType::PCInc),
411            1 => Ok(SFrameFDEType::PCMask),
412            _ => unreachable!(),
413        }
414    }
415
416    /// Get SFrame AArch64 pauth key
417    pub fn get_aarch64_pauth_key(&self) -> SFrameResult<SFrameAArch64PAuthKey> {
418        let fretype = (self.0 >> 5) & 0b1;
419        match fretype {
420            0 => Ok(SFrameAArch64PAuthKey::KeyA),
421            1 => Ok(SFrameAArch64PAuthKey::KeyB),
422            _ => unreachable!(),
423        }
424    }
425}
426
427/// SFrame FRE Types
428///
429/// Ref: <https://sourceware.org/binutils/docs-2.40/sframe-spec.html#The-SFrame-FRE-Types>
430#[derive(Debug, Clone, Copy)]
431pub enum SFrameFREType {
432    /// SFRAME_FRE_TYPE_ADDR1
433    /// The start address offset (in bytes) of the SFrame FRE is an unsigned
434    /// 8-bit value.
435    Addr1,
436    /// SFRAME_FRE_TYPE_ADDR2
437    /// The start address offset (in bytes) of the SFrame FRE is an unsigned
438    /// 16-bit value.
439    Addr2,
440    /// SFRAME_FRE_TYPE_ADDR4
441    /// The start address offset (in bytes) of the SFrame FRE is an unsigned
442    /// 32-bit value.
443    Addr4,
444}
445
446/// SFrame FDE Types
447///
448/// Ref: <https://sourceware.org/binutils/docs-2.40/sframe-spec.html#The-SFrame-FDE-Types>
449#[derive(Debug, Clone, Copy)]
450pub enum SFrameFDEType {
451    /// SFRAME_FDE_TYPE_PCINC
452    PCInc,
453    /// SFRAME_FDE_TYPE_PCMASK
454    PCMask,
455}
456
457/// SFrame PAuth key
458///
459/// Ref: <https://sourceware.org/binutils/docs-2.40/sframe-spec.html#The-SFrame-FDE-Info-Word>
460#[derive(Debug, Clone, Copy)]
461pub enum SFrameAArch64PAuthKey {
462    /// SFRAME_AARCH64_PAUTH_KEY_A
463    KeyA,
464    /// SFRAME_AARCH64_PAUTH_KEY_B
465    KeyB,
466}
467
468/// SFrame FDE
469///
470/// Ref: <https://sourceware.org/binutils/docs-2.40/sframe-spec.html#SFrame-Function-Descriptor-Entries>
471#[derive(Debug, Clone, Copy)]
472pub struct SFrameFDE {
473    /// Signed 32-bit integral field denoting the virtual memory address of the described function.
474    pub func_start_address: i32,
475    /// Unsigned 32-bit integral field specifying the size of the function in bytes.
476    pub func_size: u32,
477    /// Unsigned 32-bit integral field specifying the offset in bytes of the function’s first SFrame FRE in the SFrame section.
478    pub func_start_fre_off: u32,
479    /// Unsigned 32-bit integral field specifying the total number of SFrame FREs used for the function.
480    pub func_num_fres: u32,
481    /// The SFrame FDE info word. See The SFrame FDE info word.
482    pub func_info: SFrameFDEInfo,
483}
484
485impl SFrameFDE {
486    /// Compute pc of the function
487    pub fn get_pc(&self, section: &SFrameSection<'_>) -> u64 {
488        (self.func_start_address as i64).wrapping_add_unsigned(section.section_base) as u64
489    }
490
491    /// Iterate FRE entries
492    pub fn iter_fre<'a>(&'a self, section: &'a SFrameSection<'a>) -> SFrameFREIterator<'a> {
493        // "The sub-section offsets, namely sfh_fdeoff and sfh_freoff, in the
494        // SFrame header are relative to the end of the SFrame header; they are
495        // each an offset in bytes into the SFrame section where the SFrame FDE
496        // sub-section and the SFrame FRE sub-section respectively start."
497        // "sfde_func_start_fre_off is the offset to the first SFrame FRE for
498        // the function. This offset is relative to the end of the SFrame FDE
499        // sub-section (unlike the sub-section offsets in the SFrame header,
500        // which are relative to the end of the SFrame header)."
501        let offset = section.freoff as usize
502            + core::mem::size_of::<RawSFrameHeader>()
503            + self.func_start_fre_off as usize;
504        SFrameFREIterator {
505            fde: self,
506            section,
507            offset,
508            index: 0,
509        }
510    }
511
512    /// Find FRE entry by pc
513    pub fn find_fre(
514        &self,
515        section: &SFrameSection<'_>,
516        pc: u64,
517    ) -> SFrameResult<Option<SFrameFRE>> {
518        let fde_pc = self.get_pc(section);
519        if pc < fde_pc || pc - fde_pc >= self.func_size as u64 {
520            // out of bounds
521            return Ok(None);
522        }
523
524        match self.func_info.get_fde_type()? {
525            SFrameFDEType::PCInc => {
526                // find matching fre entry with max pc
527                let mut last: Option<SFrameFRE> = None;
528                let mut iter = self.iter_fre(section);
529                while let Some(fre) = iter.next()? {
530                    if fre.start_address.get() as u64 + fde_pc > pc {
531                        // last is the matching one
532                        break;
533                    }
534                    last = Some(fre);
535                }
536                if let Some(fre) = last {
537                    // PC >= FRE_START_ADDR
538                    if fre.start_address.get() as u64 + fde_pc <= pc {
539                        return Ok(Some(fre));
540                    }
541                }
542                Ok(None)
543            }
544            SFrameFDEType::PCMask => {
545                // match by pc masking
546                let mut iter = self.iter_fre(section);
547                while let Some(fre) = iter.next()? {
548                    // Unwinders perform a (PC & FRE_START_ADDR_AS_MASK >= FRE_START_ADDR_AS_MASK) to look up a matching FRE.
549                    if fre.start_address.get() != 0
550                        && pc % fre.start_address.get() as u64 >= fre.start_address.get() as u64
551                    {
552                        // found
553                        return Ok(Some(fre));
554                    }
555                }
556                Ok(None)
557            }
558        }
559    }
560}
561
562/// SFrame FRE Start Address
563///
564/// Ref: <https://sourceware.org/binutils/docs-2.40/sframe-spec.html#SFrame-Frame-Row-Entries>
565#[derive(Debug, Clone, Copy)]
566pub enum SFrameFREStartAddress {
567    U8(u8),
568    U16(u16),
569    U32(u32),
570}
571
572impl SFrameFREStartAddress {
573    /// Convert the variable sized address to u32
574    pub fn get(&self) -> u32 {
575        match self {
576            SFrameFREStartAddress::U8(i) => *i as u32,
577            SFrameFREStartAddress::U16(i) => *i as u32,
578            SFrameFREStartAddress::U32(i) => *i,
579        }
580    }
581}
582
583/// SFrame FRE Stack Offset
584///
585/// Ref: <https://sourceware.org/binutils/docs-2.40/sframe-spec.html#SFrame-Frame-Row-Entries>
586#[derive(Debug, Clone, Copy)]
587pub enum SFrameFREStackOffset {
588    I8(i8),
589    I16(i16),
590    I32(i32),
591}
592
593impl SFrameFREStackOffset {
594    /// Convert the variable sized offset to i32
595    pub fn get(&self) -> i32 {
596        match self {
597            SFrameFREStackOffset::I8(i) => *i as i32,
598            SFrameFREStackOffset::I16(i) => *i as i32,
599            SFrameFREStackOffset::I32(i) => *i,
600        }
601    }
602}
603
604/// SFrame FRE
605///
606/// Ref: <https://sourceware.org/binutils/docs-2.40/sframe-spec.html#SFrame-Frame-Row-Entries>
607#[derive(Debug, Clone)]
608pub struct SFrameFRE {
609    /// Start address (in offset form) of the function
610    pub start_address: SFrameFREStartAddress,
611    /// FRE info
612    pub info: SFrameFREInfo,
613    /// Stack offsets to access CFA, FP and RA
614    pub stack_offsets: Vec<SFrameFREStackOffset>,
615}
616
617impl SFrameFRE {
618    /// Get CFA offset against base reg
619    pub fn get_cfa_offset(&self, _section: &SFrameSection<'_>) -> Option<i32> {
620        // currently always the first offset
621        self.stack_offsets.first().map(|offset| offset.get())
622    }
623
624    /// Get RA offset against CFA
625    pub fn get_ra_offset(&self, section: &SFrameSection<'_>) -> Option<i32> {
626        // case 1: RA offset is fixed
627        if section.cfa_fixed_ra_offset != 0 {
628            return Some(section.cfa_fixed_ra_offset as i32);
629        }
630        // case 2: RA offset is saved
631        self.stack_offsets.get(1).map(|offset| offset.get())
632    }
633
634    /// Get FP offset against CFA
635    pub fn get_fp_offset(&self, section: &SFrameSection<'_>) -> Option<i32> {
636        // case 1: FP offset is fixed
637        if section.cfa_fixed_fp_offset != 0 {
638            return Some(section.cfa_fixed_fp_offset as i32);
639        }
640        // case 2: RA offset is fixed, only FP offset is saved
641        if section.cfa_fixed_ra_offset != 0 {
642            return self.stack_offsets.get(1).map(|offset| offset.get());
643        }
644        // case 3: both FP and RA offsets are saved
645        self.stack_offsets.get(2).map(|offset| offset.get())
646    }
647}
648
649/// SFrame FRE Info Word
650///
651/// Ref: <https://sourceware.org/binutils/docs-2.40/sframe-spec.html#The-SFrame-FRE-Info-Word>
652#[derive(Debug, Clone, Copy)]
653#[repr(transparent)]
654pub struct SFrameFREInfo(u8);
655
656impl SFrameFREInfo {
657    /// Indicate whether the return address is mangled with any authorization
658    /// bits (signed RA).
659    pub fn get_mangled_ra_p(&self) -> bool {
660        (self.0 >> 7) & 0b1 == 1
661    }
662
663    /// Size of stack offsets in bytes.
664    pub fn get_offset_size(&self) -> SFrameResult<usize> {
665        match (self.0 >> 5) & 0b11 {
666            // SFRAME_FRE_OFFSET_1B
667            0x0 => Ok(1),
668            // SFRAME_FRE_OFFSET_2B
669            0x1 => Ok(2),
670            // SFRAME_FRE_OFFSET_4B
671            0x2 => Ok(4),
672            _ => Err(SFrameError::UnsupportedFREStackOffsetSize),
673        }
674    }
675
676    /// The number of stack offsets in the FRE
677    pub fn get_offset_count(&self) -> u8 {
678        (self.0 >> 1) & 0b1111
679    }
680
681    /// Distinguish between SP or FP based CFA recovery.
682    pub fn get_cfa_base_reg_id(&self) -> u8 {
683        self.0 & 0b1
684    }
685}
686
687/// Iterator for SFrame FRE
688pub struct SFrameFREIterator<'a> {
689    fde: &'a SFrameFDE,
690    section: &'a SFrameSection<'a>,
691    index: u32,
692    offset: usize,
693}
694
695impl<'a> FallibleIterator for SFrameFREIterator<'a> {
696    type Item = SFrameFRE;
697    type Error = SFrameError;
698
699    fn next(&mut self) -> SFrameResult<Option<SFrameFRE>> {
700        if self.index >= self.fde.func_num_fres {
701            return Ok(None);
702        }
703
704        let fre_type = self.fde.func_info.get_fre_type()?;
705        let entry_size = match fre_type {
706            SFrameFREType::Addr1 => 1 + 1,
707            SFrameFREType::Addr2 => 2 + 1,
708            SFrameFREType::Addr4 => 4 + 1,
709        } as usize;
710        let offset = self.offset;
711        if offset + entry_size > self.section.data.len() {
712            return Err(SFrameError::UnexpectedEndOfData);
713        }
714
715        let (start_address, info) = match self.fde.func_info.get_fre_type()? {
716            SFrameFREType::Addr1 => (
717                SFrameFREStartAddress::U8(self.section.data[offset]),
718                SFrameFREInfo(self.section.data[offset + 1]),
719            ),
720            SFrameFREType::Addr2 => (
721                SFrameFREStartAddress::U16(read_binary!(
722                    self.section.data,
723                    self.section.little_endian,
724                    u16,
725                    offset
726                )),
727                SFrameFREInfo(self.section.data[offset + 2]),
728            ),
729            SFrameFREType::Addr4 => (
730                SFrameFREStartAddress::U32(read_binary!(
731                    self.section.data,
732                    self.section.little_endian,
733                    u32,
734                    offset
735                )),
736                SFrameFREInfo(self.section.data[offset + 4]),
737            ),
738        };
739
740        let offset_size = info.get_offset_size()?;
741        let offset_count = info.get_offset_count() as usize;
742        let offset_total_size = offset_size * offset_count;
743        if offset + entry_size + offset_total_size > self.section.data.len() {
744            return Err(SFrameError::UnexpectedEndOfData);
745        }
746
747        let mut stack_offsets = vec![];
748        for i in 0..offset_count {
749            match offset_size {
750                1 => stack_offsets.push(SFrameFREStackOffset::I8(
751                    self.section.data[offset + entry_size + i * offset_size] as i8,
752                )),
753                2 => stack_offsets.push(SFrameFREStackOffset::I16(read_binary!(
754                    self.section.data,
755                    self.section.little_endian,
756                    i16,
757                    offset + entry_size + i * offset_size
758                ))),
759                4 => stack_offsets.push(SFrameFREStackOffset::I32(read_binary!(
760                    self.section.data,
761                    self.section.little_endian,
762                    i32,
763                    offset + entry_size + i * offset_size
764                ))),
765                _ => unreachable!(),
766            }
767        }
768
769        self.offset += entry_size + offset_total_size;
770        self.index += 1;
771
772        Ok(Some(SFrameFRE {
773            start_address,
774            info,
775            stack_offsets,
776        }))
777    }
778}
779
780/// Iterator for SFrame FDE
781pub struct SFrameFDEIterator<'a> {
782    section: &'a SFrameSection<'a>,
783    index: u32,
784}
785
786impl<'a> FallibleIterator for SFrameFDEIterator<'a> {
787    type Item = SFrameFDE;
788    type Error = SFrameError;
789
790    fn next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
791        let res = self.section.get_fde(self.index);
792        if let Ok(Some(_)) = res {
793            self.index += 1;
794        }
795        res
796    }
797}