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 pub cmd: u32,
13 pub cmdsize: u32,
15 pub segname: String,
17 pub vmaddr: u64,
19 pub vmsize: u64,
21 pub fileoff: u64,
23 pub filesize: u64,
25 pub maxprot: i32,
27 pub initprot: i32,
29 pub nsects: u32,
31 pub flags: u32,
33}
34
35impl SegmentCommand64 {
36 pub const TYPE: u32 = 0x19;
37
38 pub const SIZE: u32 = 0x48; 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 pub sectname: String,
94 pub segname: String,
96 pub addr: u64,
98 pub size: u64,
100 pub offset: u32,
102 pub align: u32,
104 pub reloff: u32,
106 pub nreloc: u32,
108 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; 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 PureInstructions = 0x80000000,
201 NoToc = 0x40000000,
204 StripStaticSyms = 0x20000000,
206 LiveSupport = 0x08000000,
208 Debug = 0x02000000,
216 SomeInstructions = 0x00000400,
218 ExtReloc = 0x00000200,
220 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}