atom_macho/load_command/
segment64.rs

1use crate::io::{Endian, ReadExt as _, WriteExt as _};
2use num_derive::FromPrimitive;
3use num_traits::FromPrimitive;
4use std::{
5    fmt,
6    io::{Read, Write},
7};
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct SegmentCommand64 {
11    /// SegmentCommand64::TYPE
12    pub cmd: u32,
13    /// includes sizeof Section64 structs
14    pub cmdsize: u32,
15    /// segment name. 16byte
16    pub segname: String,
17    /// memory address of this segment
18    pub vmaddr: u64,
19    /// memory size of this segment
20    pub vmsize: u64,
21    /// file offset of this segment
22    pub fileoff: u64,
23    /// amount to map from the file
24    pub filesize: u64,
25    /// maximum VM protection
26    pub maxprot: i32,
27    /// initial VM protection
28    pub initprot: i32,
29    /// number of sections in segment
30    pub nsects: u32,
31    /// flags
32    pub flags: u32,
33}
34
35impl SegmentCommand64 {
36    pub const TYPE: u32 = 0x19;
37
38    /// Byte size of `SegmentCommand64` command.
39    /// This does not include `Section64` command size.
40    /// So this is constant.
41    pub const SIZE: u32 = 0x48; // 72
42
43    pub fn read_from_in<R: Read>(read: &mut R, endian: Endian) -> Self {
44        let cmd = read.read_u32_in(endian);
45        assert_eq!(cmd, Self::TYPE);
46
47        let cmdsize = read.read_u32_in(endian);
48        let segname = read.read_fixed_size_string(16);
49        let vmaddr = read.read_u64_in(endian);
50        let vmsize = read.read_u64_in(endian);
51        let fileoff = read.read_u64_in(endian);
52        let filesize = read.read_u64_in(endian);
53        let maxprot = read.read_i32_in(endian);
54        let initprot = read.read_i32_in(endian);
55        let nsects = read.read_u32_in(endian);
56        let flags = read.read_u32_in(endian);
57
58        assert_eq!(cmdsize, Self::SIZE + nsects * Section64::SIZE);
59
60        SegmentCommand64 {
61            cmd,
62            cmdsize,
63            segname,
64            vmaddr,
65            vmsize,
66            fileoff,
67            filesize,
68            maxprot,
69            initprot,
70            nsects,
71            flags,
72        }
73    }
74
75    pub fn write_into<W: Write>(&self, write: &mut W) {
76        write.write_u32_native(self.cmd);
77        write.write_u32_native(self.cmdsize);
78        write.write_fixed_size_string(self.segname.as_str(), 16);
79        write.write_u64_native(self.vmaddr);
80        write.write_u64_native(self.vmsize);
81        write.write_u64_native(self.fileoff);
82        write.write_u64_native(self.filesize);
83        write.write_i32_native(self.maxprot);
84        write.write_i32_native(self.initprot);
85        write.write_u32_native(self.nsects);
86        write.write_u32_native(self.flags);
87    }
88}
89
90#[derive(Debug, Clone, PartialEq, Eq)]
91pub struct Section64 {
92    /// 16-byte string
93    pub sectname: String,
94    /// 16-byte string
95    pub segname: String,
96    /// memory address of this section
97    pub addr: u64,
98    /// size in bytes of this section
99    pub size: u64,
100    /// file offset of this section
101    pub offset: u32,
102    /// section alignment (power of 2)
103    pub align: u32,
104    /// file offset of the first relocation entry for this section
105    pub reloff: u32,
106    /// number of relocation entries for this section
107    pub nreloc: u32,
108    /// represented as u32.
109    /// higher 3 bytes represent SectionAttrs,
110    /// lower 1 byte represent SectionType.
111    pub flags: (SectionAttrs, SectionType),
112    pub reserved1: u32,
113    pub reserved2: u32,
114    pub reserved3: u32,
115}
116
117impl Section64 {
118    pub const SIZE: u32 = 0x50; // 80
119
120    pub fn read_from_in<R: Read>(read: &mut R, endian: Endian) -> Self {
121        let sectname = read.read_fixed_size_string(16);
122        let segname = read.read_fixed_size_string(16);
123        let addr = read.read_u64_in(endian);
124        let size = read.read_u64_in(endian);
125        let offset = read.read_u32_in(endian);
126        let align = read.read_u32_in(endian);
127        let reloff = read.read_u32_in(endian);
128        let nreloc = read.read_u32_in(endian);
129
130        let flags_n = read.read_u32_in(endian);
131        let sect_type = SectionType::from_u32(flags_n & SectionType::BIT_MASK);
132        let sect_attrs = SectionAttrs::from_u32(flags_n & SectionAttrs::BIT_MASK);
133
134        let reserved1 = read.read_u32_in(endian);
135        let reserved2 = read.read_u32_in(endian);
136        let reserved3 = read.read_u32_in(endian);
137
138        Section64 {
139            sectname,
140            segname,
141            addr,
142            size,
143            offset,
144            align,
145            reloff,
146            nreloc,
147            flags: (sect_attrs, sect_type),
148            reserved1,
149            reserved2,
150            reserved3,
151        }
152    }
153
154    pub fn write_into<W: Write>(&self, write: &mut W) {
155        write.write_fixed_size_string(self.sectname.as_str(), 16);
156        write.write_fixed_size_string(self.segname.as_str(), 16);
157        write.write_u64_native(self.addr);
158        write.write_u64_native(self.size);
159        write.write_u32_native(self.offset);
160        write.write_u32_native(self.align);
161        write.write_u32_native(self.reloff);
162        write.write_u32_native(self.nreloc);
163
164        let flags_n = self.flags.0.to_u32() | self.flags.1.to_u32();
165        write.write_u32_native(flags_n);
166
167        write.write_u32_native(self.reserved1);
168        write.write_u32_native(self.reserved2);
169        write.write_u32_native(self.reserved3);
170    }
171}
172
173#[derive(FromPrimitive, Debug, Clone, Copy, PartialEq, Eq)]
174pub enum SectionType {
175    Regular = 0x0,
176    Zerofill = 0x1,
177    CstringLiterals = 0x2,
178    FourByteLiterals = 0x3,
179    EightByteLiterals = 0x4,
180    LiteralPointers = 0x5,
181    Coalesced = 0xB,
182}
183
184impl SectionType {
185    pub const BIT_MASK: u32 = 0x000000ff;
186
187    pub fn from_u32(n: u32) -> Self {
188        FromPrimitive::from_u32(n).unwrap_or_else(|| panic!("Unsupported section type 0x{:X}", n))
189    }
190
191    pub fn to_u32(self) -> u32 {
192        self as u32
193    }
194}
195
196#[derive(FromPrimitive, Debug, Clone, Copy, PartialEq, Eq)]
197pub enum SectionAttr {
198    /// This section contains only executable machine instructions. The standard tools set this
199    /// flag for the sections __TEXT,__text, __TEXT,__symbol_stub, and __TEXT,__picsymbol_stub.
200    PureInstructions = 0x80000000,
201    /// section contains coalesced symbols that are not to be
202    /// in a ranlib table of contents
203    NoToc = 0x40000000,
204    /// ok to strip static symbols in this section in files with the MH_DYLDLINK flag
205    StripStaticSyms = 0x20000000,
206    /// blocks are live if they reference live blocks
207    LiveSupport = 0x08000000,
208    /// If a segment contains any sections marked with S_ATTR_DEBUG then all
209    /// sections in that segment must have this attribute.  No section other than
210    /// a section marked with this attribute may reference the contents of this
211    /// section.  A section with this attribute may contain no symbols and must have
212    /// a section type S_REGULAR.  The static linker will not copy section contents
213    /// from sections with this attribute into its output file.  These sections
214    /// generally contain DWARF debugging info.
215    Debug = 0x02000000,
216    /// section contains some executable machine instructions.
217    SomeInstructions = 0x00000400,
218    /// section has external relocation entries.
219    ExtReloc = 0x00000200,
220    /// section has local relocation entries.
221    LocReloc = 0x00000100,
222}
223
224impl SectionAttr {
225    pub fn from_u32(n: u32) -> Self {
226        FromPrimitive::from_u32(n)
227            .unwrap_or_else(|| panic!("Unsupported section attribute 0x{:X}", n))
228    }
229
230    pub fn to_u32(self) -> u32 {
231        self as u32
232    }
233}
234
235#[derive(Clone, PartialEq, Eq)]
236pub struct SectionAttrs {
237    attrs: Vec<SectionAttr>,
238}
239
240impl SectionAttrs {
241    pub const BIT_MASK: u32 = 0xffffff00;
242
243    pub fn new() -> SectionAttrs {
244        SectionAttrs { attrs: Vec::new() }
245    }
246
247    pub fn iter<'a>(&'a self) -> impl Iterator<Item = SectionAttr> + 'a {
248        self.attrs.iter().copied()
249    }
250
251    pub fn push(&mut self, attr: SectionAttr) {
252        self.attrs.push(attr);
253    }
254
255    pub fn from_u32(flags: u32) -> Self {
256        let mut attrs = SectionAttrs::new();
257        for i in 8..=31 {
258            let attr_n = flags & (1 << i);
259            if attr_n != 0 {
260                attrs.push(SectionAttr::from_u32(attr_n));
261            }
262        }
263        attrs
264    }
265
266    pub fn to_u32(&self) -> u32 {
267        let mut n = 0;
268        for attr in self.attrs.iter() {
269            n |= attr.to_u32();
270        }
271        n
272    }
273}
274
275impl fmt::Debug for SectionAttrs {
276    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
277        fmt.debug_set().entries(self.attrs.iter()).finish()
278    }
279}
280
281#[cfg(test)]
282mod tests {
283    use super::*;
284
285    #[test]
286    fn write_and_read_segment64_command() {
287        let cmd = SegmentCommand64 {
288            cmd: SegmentCommand64::TYPE,
289            cmdsize: SegmentCommand64::SIZE + Section64::SIZE,
290            segname: String::new(),
291            vmaddr: 0,
292            vmsize: 42,
293            fileoff: 100,
294            filesize: 42,
295            maxprot: 7,
296            initprot: 7,
297            nsects: 1,
298            flags: 0,
299        };
300
301        let mut buf = Vec::new();
302
303        cmd.write_into(&mut buf);
304
305        assert_eq!(buf.len(), SegmentCommand64::SIZE as usize);
306
307        let read_cmd = SegmentCommand64::read_from_in(&mut buf.as_slice(), Endian::NATIVE);
308
309        assert_eq!(read_cmd, cmd);
310    }
311
312    #[test]
313    fn write_and_read_section64() {
314        let cmd = Section64 {
315            sectname: "__TEXT,__text".to_string(),
316            segname: String::new(),
317            addr: 0,
318            size: 42,
319            offset: 100,
320            align: 0,
321            reloff: 53,
322            nreloc: 1,
323            flags: (SectionAttrs::new(), SectionType::Regular),
324            reserved1: 0,
325            reserved2: 0,
326            reserved3: 0,
327        };
328
329        let mut buf = Vec::new();
330
331        cmd.write_into(&mut buf);
332
333        assert_eq!(buf.len(), Section64::SIZE as usize);
334
335        let read_cmd = Section64::read_from_in(&mut buf.as_slice(), Endian::NATIVE);
336
337        assert_eq!(read_cmd, cmd);
338    }
339}