dof/
dof.rs

1//! Types representing DTrace Object Format data structures.
2//!
3//! The [`Section`] struct is used to represent a complete DTrace Object Format section as
4//! contained in an object file. It contains one or more [`Provider`]s, each with one or more
5//! [`Probe`]s. The `Probe` type contains all the information required to locate a probe callsite
6//! within an object file.
7
8// Copyright 2021 Oxide Computer Company
9//
10// Licensed under the Apache License, Version 2.0 (the "License");
11// you may not use this file except in compliance with the License.
12// You may obtain a copy of the License at
13//
14//     http://www.apache.org/licenses/LICENSE-2.0
15//
16// Unless required by applicable law or agreed to in writing, software
17// distributed under the License is distributed on an "AS IS" BASIS,
18// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19// See the License for the specific language governing permissions and
20// limitations under the License.
21
22use std::mem::size_of;
23use std::{
24    collections::BTreeMap,
25    convert::{TryFrom, TryInto},
26};
27
28use serde::Serialize;
29use thiserror::Error;
30
31// Magic bytes for a DOF section
32pub(crate) const DOF_MAGIC: [u8; 4] = [0x7F, b'D', b'O', b'F'];
33
34/// Errors related to building or manipulating the DOF format
35#[derive(Error, Debug)]
36pub enum Error {
37    /// The DOF identifier is invalid, such as invalid magic bytes
38    #[error("invalid DOF identifier (magic bytes, endianness, or version)")]
39    InvalidIdentifier,
40
41    /// An error occurred parsing a type from an underlying byte slice
42    #[error("data does not match expected struct layout or is misaligned")]
43    ParseError,
44
45    /// Attempt to read from an unsupported object file format
46    #[error("unsupported object file format")]
47    UnsupportedObjectFile,
48
49    /// An error related to parsing the object file
50    #[cfg(feature = "des")]
51    #[error(transparent)]
52    ObjectError(#[from] goblin::error::Error),
53
54    /// An error during IO
55    #[error(transparent)]
56    IO(#[from] std::io::Error),
57}
58
59/// Represents the DTrace data model, e.g. the pointer width of the platform
60#[derive(Debug, Clone, Copy, Serialize)]
61#[repr(u8)]
62pub enum DataModel {
63    None = 0,
64    ILP32 = 1,
65    LP64 = 2,
66}
67
68impl Default for DataModel {
69    fn default() -> Self {
70        if cfg!(target_pointer_width = "64") {
71            DataModel::LP64
72        } else {
73            DataModel::ILP32
74        }
75    }
76}
77
78impl TryFrom<u8> for DataModel {
79    type Error = Error;
80    fn try_from(x: u8) -> Result<Self, Self::Error> {
81        match x {
82            0 => Ok(DataModel::None),
83            1 => Ok(DataModel::ILP32),
84            2 => Ok(DataModel::LP64),
85            _ => Err(Error::InvalidIdentifier),
86        }
87    }
88}
89
90/// Represents the endianness of the platform
91#[derive(Debug, Clone, Copy, Serialize)]
92#[repr(u8)]
93pub enum DataEncoding {
94    None = 0,
95    LittleEndian = 1,
96    BigEndian = 2,
97}
98
99impl Default for DataEncoding {
100    fn default() -> Self {
101        if cfg!(target_endian = "big") {
102            DataEncoding::BigEndian
103        } else {
104            DataEncoding::LittleEndian
105        }
106    }
107}
108
109impl TryFrom<u8> for DataEncoding {
110    type Error = Error;
111    fn try_from(x: u8) -> Result<Self, Self::Error> {
112        match x {
113            0 => Ok(DataEncoding::None),
114            1 => Ok(DataEncoding::LittleEndian),
115            2 => Ok(DataEncoding::BigEndian),
116            _ => Err(Error::InvalidIdentifier),
117        }
118    }
119}
120
121/// Static identifying information about a DOF section (such as version numbers)
122#[derive(Debug, Clone, Copy, Serialize)]
123pub struct Ident {
124    pub magic: [u8; 4],
125    pub model: DataModel,
126    pub encoding: DataEncoding,
127    pub version: u8,
128    pub dif_vers: u8,
129    pub dif_ireg: u8,
130    pub dif_treg: u8,
131}
132
133impl<'a> TryFrom<&'a [u8]> for Ident {
134    type Error = Error;
135    fn try_from(buf: &'a [u8]) -> Result<Self, Self::Error> {
136        if buf.len() < size_of::<Ident>() {
137            return Err(Error::ParseError);
138        }
139        let magic = &buf[..DOF_MAGIC.len()];
140        if magic != DOF_MAGIC {
141            return Err(Error::InvalidIdentifier);
142        }
143        let model = DataModel::try_from(buf[crate::dof_bindings::DOF_ID_MODEL as usize])?;
144        let encoding = DataEncoding::try_from(buf[crate::dof_bindings::DOF_ID_ENCODING as usize])?;
145        let version = buf[crate::dof_bindings::DOF_ID_VERSION as usize];
146        let dif_vers = buf[crate::dof_bindings::DOF_ID_DIFVERS as usize];
147        let dif_ireg = buf[crate::dof_bindings::DOF_ID_DIFIREG as usize];
148        let dif_treg = buf[crate::dof_bindings::DOF_ID_DIFTREG as usize];
149        Ok(Ident {
150            // Unwrap is safe if the above check against DOF_MAGIC passes
151            magic: magic.try_into().unwrap(),
152            model,
153            encoding,
154            version,
155            dif_vers,
156            dif_ireg,
157            dif_treg,
158        })
159    }
160}
161
162impl Ident {
163    pub fn as_bytes(&self) -> [u8; 16] {
164        let mut out = [0; 16];
165        let start = self.magic.len();
166        out[..start].copy_from_slice(&self.magic[..]);
167        out[start] = self.model as _;
168        out[start + 1] = self.encoding as _;
169        out[start + 2] = self.version;
170        out[start + 3] = self.dif_vers;
171        out[start + 4] = self.dif_ireg;
172        out[start + 5] = self.dif_treg;
173        out
174    }
175}
176
177/// Representation of a DOF section of an object file
178#[derive(Debug, Clone, Serialize)]
179pub struct Section {
180    /// The identifying bytes of this section
181    pub ident: Ident,
182    /// The list of providers defined in this section
183    pub providers: BTreeMap<String, Provider>,
184}
185
186impl Section {
187    /// Construct a section from a DOF byte array.
188    #[cfg(feature = "des")]
189    pub fn from_bytes(buf: &[u8]) -> Result<Section, Error> {
190        crate::des::deserialize_section(buf)
191    }
192
193    /// Serialize a section into DOF object file section.
194    pub fn as_bytes(&self) -> Vec<u8> {
195        crate::ser::serialize_section(self)
196    }
197
198    /// Serialize a section into a JSON representation of the DOF object file section.
199    pub fn to_json(&self) -> String {
200        serde_json::to_string_pretty(self).unwrap()
201    }
202}
203
204impl Default for Section {
205    fn default() -> Self {
206        Self {
207            ident: Ident {
208                magic: DOF_MAGIC,
209                model: DataModel::LP64,
210                encoding: DataEncoding::LittleEndian,
211                version: crate::dof_bindings::DOF_VERSION as u8,
212                dif_vers: crate::dof_bindings::DIF_VERSION as u8,
213                dif_ireg: crate::dof_bindings::DIF_DIR_NREGS as u8,
214                dif_treg: crate::dof_bindings::DIF_DTR_NREGS as u8,
215            },
216            providers: BTreeMap::new(),
217        }
218    }
219}
220
221/// Information about a single DTrace probe
222#[derive(Debug, Clone, Serialize)]
223pub struct Probe {
224    /// Name of this probe
225    pub name: String,
226    /// Name of the function containing this probe
227    pub function: String,
228    /// Address or offset in the resulting object code
229    pub address: u64,
230    /// Offsets in containing function at which this probe occurs.
231    pub offsets: Vec<u32>,
232    /// Offsets in the containing function at which this probe's is-enabled functions occur.
233    pub enabled_offsets: Vec<u32>,
234    /// Type information for each argument
235    pub arguments: Vec<String>,
236}
237
238/// Information about a single provider
239#[derive(Debug, Clone, Serialize)]
240pub struct Provider {
241    /// Name of the provider
242    pub name: String,
243    /// List of probes this provider exports
244    pub probes: BTreeMap<String, Probe>,
245}