Skip to main content

simple_frame_rs/
v2.rs

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