Skip to main content

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 thiserror::Error;
9
10pub mod v1;
11pub mod v2;
12pub mod v3;
13
14#[macro_export]
15macro_rules! read_binary {
16    ($data: expr, $le: expr, $ty: ident, $offset: expr) => {{
17        let data_offset = $offset;
18        let mut data_bytes: [u8; core::mem::size_of::<$ty>()] = [0; core::mem::size_of::<$ty>()];
19        data_bytes.copy_from_slice(&$data[data_offset..data_offset + core::mem::size_of::<$ty>()]);
20        if $le {
21            $ty::from_le_bytes(data_bytes)
22        } else {
23            $ty::from_be_bytes(data_bytes)
24        }
25    }};
26}
27
28#[macro_export]
29macro_rules! read_struct {
30    ($struct: ident, $data: expr, $le: expr, $x: ident, $ty: ident) => {{ read_binary!($data, $le, $ty, core::mem::offset_of!($struct, $x)) }};
31}
32
33/// Result type for the crate
34pub type SFrameResult<T> = core::result::Result<T, SFrameError>;
35
36/// SFrame section
37///
38/// Ref: <https://sourceware.org/binutils/docs/sframe-spec.html#SFrame-Section>
39#[derive(Debug, Clone, Copy)]
40#[allow(dead_code)]
41pub enum SFrameSection<'a> {
42    V1(v1::SFrameSection<'a>),
43    V2(v2::SFrameSection<'a>),
44    V3(v3::SFrameSection<'a>),
45}
46
47/// The magic number for SFrame section: 0xdee2
48const SFRAME_MAGIC: u16 = 0xdee2;
49
50/// Raw SFrame Header
51///
52/// Ref: <https://sourceware.org/binutils/docs/sframe-spec.html#SFrame-Preamble>
53#[repr(C, packed)]
54struct RawSFramePreamble {
55    magic: u16,
56    version: u8,
57    flags: u8,
58}
59
60impl<'a> SFrameSection<'a> {
61    /// Print the section in string in the same way as objdump
62    pub fn to_string(&self) -> SFrameResult<String> {
63        match self {
64            SFrameSection::V1(sframe_section) => sframe_section.to_string(),
65            SFrameSection::V2(sframe_section) => sframe_section.to_string(),
66            SFrameSection::V3(sframe_section) => sframe_section.to_string(),
67        }
68    }
69    /// Parse SFrame section from data
70    pub fn from(data: &'a [u8], section_base: u64) -> SFrameResult<SFrameSection<'a>> {
71        // parse sframe_header
72        if data.len() < core::mem::size_of::<RawSFramePreamble>() {
73            return Err(SFrameError::UnexpectedEndOfData);
74        }
75
76        // probe magic
77        let magic_offset = core::mem::offset_of!(RawSFramePreamble, magic);
78        let mut magic_bytes: [u8; 2] = [0; 2];
79        magic_bytes.copy_from_slice(&data[magic_offset..magic_offset + 2]);
80        let magic_le = u16::from_le_bytes(magic_bytes);
81        if magic_le != SFRAME_MAGIC {
82            let magic_be = u16::from_be_bytes(magic_bytes);
83            if magic_be != SFRAME_MAGIC {
84                return Err(SFrameError::InvalidMagic);
85            }
86        }
87
88        // probe version
89        let version_offset = core::mem::offset_of!(RawSFramePreamble, version);
90        let version = data[version_offset];
91        match version {
92            1 => Ok(SFrameSection::V1(v1::SFrameSection::from(
93                data,
94                section_base,
95            )?)),
96            2 => Ok(SFrameSection::V2(v2::SFrameSection::from(
97                data,
98                section_base,
99            )?)),
100            3 => Ok(SFrameSection::V3(v3::SFrameSection::from(
101                data,
102                section_base,
103            )?)),
104            _ => Err(SFrameError::UnsupportedVersion),
105        }
106    }
107
108    pub fn find_fde(&self, pc: u64) -> SFrameResult<Option<SFrameFDE>> {
109        match self {
110            SFrameSection::V1(sframe_section) => {
111                Ok(sframe_section.find_fde(pc)?.map(SFrameFDE::V1))
112            }
113            SFrameSection::V2(sframe_section) => {
114                Ok(sframe_section.find_fde(pc)?.map(SFrameFDE::V2))
115            }
116            SFrameSection::V3(sframe_section) => {
117                Ok(sframe_section.find_fde(pc)?.map(SFrameFDE::V3))
118            }
119        }
120    }
121}
122
123/// SFrame FDE
124///
125/// Ref: <https://sourceware.org/binutils/docs/sframe-spec.html#SFrame-Function-Descriptor-Entries>
126#[derive(Debug, Clone, Copy)]
127#[allow(dead_code)]
128pub enum SFrameFDE {
129    V1(v1::SFrameFDE),
130    V2(v2::SFrameFDE),
131    V3(v3::SFrameFDE),
132}
133
134impl SFrameFDE {
135    /// Find FRE entry by pc
136    pub fn find_fre(
137        &self,
138        section: &SFrameSection<'_>,
139        pc: u64,
140    ) -> SFrameResult<Option<SFrameFRE>> {
141        match (self, section) {
142            (SFrameFDE::V1(sframe_fde), SFrameSection::V1(sframe_section)) => {
143                Ok(sframe_fde.find_fre(sframe_section, pc)?.map(SFrameFRE::V1))
144            }
145            (SFrameFDE::V2(sframe_fde), SFrameSection::V2(sframe_section)) => {
146                Ok(sframe_fde.find_fre(sframe_section, pc)?.map(SFrameFRE::V2))
147            }
148            (SFrameFDE::V3(sframe_fde), SFrameSection::V3(sframe_section)) => {
149                Ok(sframe_fde.find_fre(sframe_section, pc)?.map(SFrameFRE::V3))
150            }
151            _ => Err(SFrameError::UnsupportedVersion),
152        }
153    }
154}
155
156/// SFrame FRE
157///
158/// Ref: <https://sourceware.org/binutils/docs/sframe-spec.html#SFrame-Frame-Row-Entries>
159#[derive(Debug, Clone)]
160#[allow(dead_code)]
161pub enum SFrameFRE {
162    V1(v1::SFrameFRE),
163    V2(v2::SFrameFRE),
164    V3(v3::SFrameFRE),
165}
166
167impl SFrameFRE {
168    /// Distinguish between SP or FP based CFA recovery.
169    pub fn get_cfa_base_reg_id(&self) -> u8 {
170        match self {
171            SFrameFRE::V1(sframe_fre) => sframe_fre.info.get_cfa_base_reg_id(),
172            SFrameFRE::V2(sframe_fre) => sframe_fre.info.get_cfa_base_reg_id(),
173            SFrameFRE::V3(sframe_fre) => sframe_fre.info.get_cfa_base_reg_id(),
174        }
175    }
176
177    /// Get CFA offset against base reg
178    pub fn get_cfa_offset(&self, section: &SFrameSection<'_>) -> SFrameResult<Option<i32>> {
179        match (self, section) {
180            (SFrameFRE::V1(sframe_fre), SFrameSection::V1(sframe_section)) => {
181                Ok(sframe_fre.get_cfa_offset(sframe_section))
182            }
183            (SFrameFRE::V2(sframe_fre), SFrameSection::V2(sframe_section)) => {
184                Ok(sframe_fre.get_cfa_offset(sframe_section))
185            }
186            (SFrameFRE::V3(sframe_fre), SFrameSection::V3(sframe_section)) => {
187                Ok(sframe_fre.get_cfa_offset(sframe_section))
188            }
189            _ => Err(SFrameError::UnsupportedVersion),
190        }
191    }
192
193    /// Get RA offset against CFA
194    pub fn get_ra_offset(&self, section: &SFrameSection<'_>) -> SFrameResult<Option<i32>> {
195        match (self, section) {
196            (SFrameFRE::V1(sframe_fre), SFrameSection::V1(sframe_section)) => {
197                Ok(sframe_fre.get_ra_offset(sframe_section))
198            }
199            (SFrameFRE::V2(sframe_fre), SFrameSection::V2(sframe_section)) => {
200                Ok(sframe_fre.get_ra_offset(sframe_section))
201            }
202            (SFrameFRE::V3(sframe_fre), SFrameSection::V3(sframe_section)) => {
203                Ok(sframe_fre.get_ra_offset(sframe_section))
204            }
205            _ => Err(SFrameError::UnsupportedVersion),
206        }
207    }
208
209    /// Get FP offset against CFA
210    pub fn get_fp_offset(&self, section: &SFrameSection<'_>) -> SFrameResult<Option<i32>> {
211        match (self, section) {
212            (SFrameFRE::V1(sframe_fre), SFrameSection::V1(sframe_section)) => {
213                Ok(sframe_fre.get_fp_offset(sframe_section))
214            }
215            (SFrameFRE::V2(sframe_fre), SFrameSection::V2(sframe_section)) => {
216                Ok(sframe_fre.get_fp_offset(sframe_section))
217            }
218            (SFrameFRE::V3(sframe_fre), SFrameSection::V3(sframe_section)) => {
219                Ok(sframe_fre.get_fp_offset(sframe_section))
220            }
221            _ => Err(SFrameError::UnsupportedVersion),
222        }
223    }
224}
225
226/// Error types for the crate
227#[derive(Error, Debug)]
228pub enum SFrameError {
229    /// Propagate core::fmt::Error
230    #[error("format error")]
231    Fmt(#[from] core::fmt::Error),
232    /// Unexpected end of data
233    #[error("unexpected end of data")]
234    UnexpectedEndOfData,
235    /// Invalid magic number
236    #[error("invalid magic number")]
237    InvalidMagic,
238    /// Unsupported version
239    #[error("unsupported version")]
240    UnsupportedVersion,
241    /// Unsupported flags
242    #[error("unsupported flags")]
243    UnsupportedFlags,
244    /// Unsupported ABI
245    #[error("unsupported abi")]
246    UnsupportedABI,
247    /// Unsupported FDE type
248    #[error("unsupported fde type")]
249    UnsupportedFDEType,
250    /// Unsupported FRE type
251    #[error("unsupported fre type")]
252    UnsupportedFREType,
253    /// Unsupported FRE stack offset size
254    #[error("unsupported fre stack offset size")]
255    UnsupportedFREStackOffsetSize,
256    /// Unsupported FRE data word size
257    #[error("unsupported fre data word size")]
258    UnsupportedFREDataWordSize,
259}