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