atom_macho/
nlist.rs

1use crate::io::{Endian, ReadExt as _, WriteExt as _};
2use num_derive::FromPrimitive;
3use num_traits::FromPrimitive;
4use std::io::{Read, Write};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct NList64 {
8    /// An index into the string table. To specify an empty string (""), set this value to 0.
9    pub n_strx: u32,
10    /// this field really contains four fields.
11    pub n_type: NTypeField,
12    /// If the type is NType::Sect then the n_sect field contains an ordinal of the section the
13    /// symbol is defined in. The sections are numbered from 1 and refer to sections in order they
14    /// appear in the load commands for the file they are in. This means the same ordinal may very
15    /// well refer to different sections in different files.
16    ///
17    /// The n_value field for all symbol table entries (including n_stab's) gets updated by the
18    /// link editor based on the value of its n_sect field and where the section n_sect references
19    /// gets relocated. If the value of the n_sect field is NO_SECT then it's n_value field is not
20    /// changed by the link editor.
21    pub n_sect: u8,
22    /// A 16-bit value providing additional information about the nature of this symbol.
23    pub n_desc: u16,
24    /// An integer that contains the value of the symbol. The format of this value is different for
25    /// each type of symbol table entry (as specified by the n_type field). For the N_SECT symbol
26    /// type, n_value is the address of the symbol. See the description of the n_type field for
27    /// information on other possible values.
28    pub n_value: u64,
29}
30
31impl NList64 {
32    pub const SIZE: u32 = 0x10; // 16
33
34    pub const NO_SECT: u8 = 0;
35    pub const MAX_SECT: u8 = 255;
36
37    pub fn read_from_in<R: Read>(read: &mut R, endian: Endian) -> Self {
38        let n_strx = read.read_u32_in(endian);
39        let n_type_n = read.read_u8();
40        let n_type = NTypeField::from_u8(n_type_n);
41        let n_sect = read.read_u8();
42        let n_desc = read.read_u16_in(endian);
43        let n_value = read.read_u64_in(endian);
44
45        NList64 {
46            n_strx,
47            n_type,
48            n_sect,
49            n_desc,
50            n_value,
51        }
52    }
53
54    pub fn write_into<W: Write>(&self, write: &mut W) {
55        write.write_u32_native(self.n_strx);
56        write.write_u8(self.n_type.to_u8());
57        write.write_u8(self.n_sect);
58        write.write_u16_native(self.n_desc);
59        write.write_u64_native(self.n_value);
60    }
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub enum NTypeField {
65    Norm {
66        /// private external symbol bit
67        n_pext: bool,
68        n_type: NType,
69        /// set for external symbols
70        n_ext: bool,
71    },
72    /// if any of n_stab bits on, this entry is a symbolic debugging entriy.
73    Stab(DebugSymbol),
74}
75
76impl NTypeField {
77    pub const N_STAB_MASK: u8 = 0xe0;
78    pub const N_PEXT_MASK: u8 = 0x10;
79    pub const N_TYPE_MASK: u8 = 0x0e;
80    pub const N_EXT_MASK: u8 = 0x01;
81
82    pub fn from_u8(n: u8) -> Self {
83        let n_stab = n & Self::N_STAB_MASK;
84        if n_stab == 0 {
85            let n_pext = n & Self::N_PEXT_MASK == Self::N_PEXT_MASK;
86            let n_type = NType::from_u8(n & Self::N_TYPE_MASK);
87            let n_ext = n & Self::N_EXT_MASK == Self::N_EXT_MASK;
88            NTypeField::Norm {
89                n_pext,
90                n_type,
91                n_ext,
92            }
93        } else {
94            NTypeField::Stab(DebugSymbol::from_u8(n_stab))
95        }
96    }
97
98    pub fn to_u8(self) -> u8 {
99        match self {
100            NTypeField::Norm {
101                n_pext,
102                n_type,
103                n_ext,
104            } => n_pext as u8 * Self::N_PEXT_MASK | n_type.to_u8() | n_ext as u8 * Self::N_EXT_MASK,
105            NTypeField::Stab(stab) => stab.to_u8(),
106        }
107    }
108}
109
110#[derive(FromPrimitive, Debug, Clone, Copy, PartialEq, Eq)]
111pub enum NType {
112    /// Undefined, n_sect == NO_SECT
113    /// Undefined symbols are symbols referenced in this module but defined in a different module.
114    /// n_value is 0.
115    Undf = 0x0,
116    /// Absolute, n_sect == NO_SECT
117    Abs = 0x2,
118    /// Defined in section number n_sect
119    Sect = 0xe,
120    /// Prebound undefined (defined in a dylib)
121    Pbud = 0xc,
122    /// Indirect.
123    /// If the type is NType::Indr then the symbol is defined to be the same as another symbol. In
124    /// this case the n_value field is an index into the string table of the other symbol's name.
125    /// When the other symbol is defined then they both take on the defined type and value.
126    Indr = 0xa,
127}
128
129impl NType {
130    pub fn from_u8(n: u8) -> Self {
131        FromPrimitive::from_u8(n).unwrap_or_else(|| panic!("Invalid n_type number {}", n))
132    }
133
134    pub fn to_u8(self) -> u8 {
135        self as u8
136    }
137}
138
139/// TODO : implement all
140#[derive(FromPrimitive, Debug, Clone, Copy, PartialEq, Eq)]
141pub enum DebugSymbol {
142    /// global symbol
143    Gsym = 0x20,
144}
145
146impl DebugSymbol {
147    pub fn from_u8(n: u8) -> Self {
148        FromPrimitive::from_u8(n).unwrap_or_else(|| panic!("Unsupported debug symbol {}", n))
149    }
150
151    pub fn to_u8(self) -> u8 {
152        self as u8
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159
160    #[test]
161    fn write_and_read_nlist() {
162        let nlist = NList64 {
163            n_strx: 42,
164            n_type: NTypeField::Norm {
165                n_pext: false,
166                n_type: NType::Sect,
167                n_ext: true,
168            },
169            n_sect: 2,
170            n_desc: 0,
171            n_value: 42,
172        };
173
174        let mut buf = Vec::new();
175
176        nlist.write_into(&mut buf);
177
178        assert_eq!(buf.len(), NList64::SIZE as usize);
179
180        let read = NList64::read_from_in(&mut buf.as_slice(), Endian::NATIVE);
181
182        assert_eq!(read, nlist);
183    }
184}