simple_frame_rs/
lib.rs

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