1use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
2
3use crate::error::{Error, Result};
4
5mod objc;
6
7pub use objc::*;
8
9pub fn read_pod<T>(data: &[u8], offset: usize) -> Result<T>
12where
13 T: FromBytes + KnownLayout + Immutable + Copy,
14{
15 let size = size_of::<T>();
16 let end = offset
17 .checked_add(size)
18 .ok_or_else(|| Error::bounds(offset as u64, size as u64, data.len() as u64))?;
19 if end > data.len() {
20 return Err(Error::bounds(offset as u64, size as u64, data.len() as u64));
21 }
22 T::read_from_bytes(&data[offset..end]).map_err(|e| {
23 Error::format(format!(
24 "failed to read {} at offset {offset:#x}: {e}",
25 std::any::type_name::<T>()
26 ))
27 })
28}
29
30#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
35#[repr(C)]
36pub struct RawMachHeader32 {
38 pub magic: u32,
40 pub cputype: i32,
42 pub cpusubtype: i32,
44 pub filetype: u32,
46 pub ncmds: u32,
48 pub sizeofcmds: u32,
50 pub flags: u32,
52}
53
54#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
55#[repr(C)]
56pub struct RawMachHeader64 {
58 pub magic: u32,
60 pub cputype: i32,
62 pub cpusubtype: i32,
64 pub filetype: u32,
66 pub ncmds: u32,
68 pub sizeofcmds: u32,
70 pub flags: u32,
72 pub reserved: u32,
74}
75
76#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
77#[repr(C)]
78pub struct RawFatHeader {
80 pub magic: u32,
82 pub nfat_arch: u32,
84}
85
86#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
87#[repr(C)]
88pub struct RawFatArch32 {
90 pub cputype: i32,
92 pub cpusubtype: i32,
94 pub offset: u32,
96 pub size: u32,
98 pub align: u32,
100}
101
102#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
103#[repr(C)]
104pub struct RawFatArch64 {
106 pub cputype: i32,
108 pub cpusubtype: i32,
110 pub offset: u64,
112 pub size: u64,
114 pub align: u32,
116 pub reserved: u32,
118}
119
120#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
121#[repr(C)]
122pub struct RawLoadCommand {
124 pub cmd: u32,
126 pub cmdsize: u32,
128}
129
130#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
131#[repr(C)]
132pub struct RawSegmentCommand32 {
134 pub cmd: u32,
136 pub cmdsize: u32,
138 pub segname: [u8; 16],
140 pub vmaddr: u32,
142 pub vmsize: u32,
144 pub fileoff: u32,
146 pub filesize: u32,
148 pub maxprot: i32,
150 pub initprot: i32,
152 pub nsects: u32,
154 pub flags: u32,
156}
157
158#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
159#[repr(C)]
160pub struct RawSegmentCommand64 {
162 pub cmd: u32,
164 pub cmdsize: u32,
166 pub segname: [u8; 16],
168 pub vmaddr: u64,
170 pub vmsize: u64,
172 pub fileoff: u64,
174 pub filesize: u64,
176 pub maxprot: i32,
178 pub initprot: i32,
180 pub nsects: u32,
182 pub flags: u32,
184}
185
186#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
187#[repr(C)]
188pub struct RawSection32 {
190 pub sectname: [u8; 16],
192 pub segname: [u8; 16],
194 pub addr: u32,
196 pub size: u32,
198 pub offset: u32,
200 pub align: u32,
202 pub reloff: u32,
204 pub nreloc: u32,
206 pub flags: u32,
208 pub reserved1: u32,
210 pub reserved2: u32,
212}
213
214#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
215#[repr(C)]
216pub struct RawSection64 {
218 pub sectname: [u8; 16],
220 pub segname: [u8; 16],
222 pub addr: u64,
224 pub size: u64,
226 pub offset: u32,
228 pub align: u32,
230 pub reloff: u32,
232 pub nreloc: u32,
234 pub flags: u32,
236 pub reserved1: u32,
238 pub reserved2: u32,
240 pub reserved3: u32,
242}
243
244#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
245#[repr(C)]
246pub struct RawSymtabCommand {
248 pub cmd: u32,
250 pub cmdsize: u32,
252 pub symoff: u32,
254 pub nsyms: u32,
256 pub stroff: u32,
258 pub strsize: u32,
260}
261
262#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
263#[repr(C)]
264pub struct RawDysymtabCommand {
266 pub cmd: u32,
268 pub cmdsize: u32,
270 pub ilocalsym: u32,
272 pub nlocalsym: u32,
274 pub iextdefsym: u32,
276 pub nextdefsym: u32,
278 pub iundefsym: u32,
280 pub nundefsym: u32,
282 pub tocoff: u32,
284 pub ntoc: u32,
286 pub modtaboff: u32,
288 pub nmodtab: u32,
290 pub extrefsymoff: u32,
292 pub nextrefsyms: u32,
294 pub indirectsymoff: u32,
296 pub nindirectsyms: u32,
298 pub extreloff: u32,
300 pub nextrel: u32,
302 pub locreloff: u32,
304 pub nlocrel: u32,
306}
307
308#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
309#[repr(C)]
310pub struct RawDylibCommand {
312 pub cmd: u32,
314 pub cmdsize: u32,
316 pub name_offset: u32,
318 pub timestamp: u32,
320 pub current_version: u32,
322 pub compatibility_version: u32,
324}
325
326#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
327#[repr(C)]
328pub struct RawUuidCommand {
330 pub cmd: u32,
332 pub cmdsize: u32,
334 pub uuid: [u8; 16],
336}
337
338#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
339#[repr(C)]
340pub struct RawBuildVersionCommand {
342 pub cmd: u32,
344 pub cmdsize: u32,
346 pub platform: u32,
348 pub minos: u32,
350 pub sdk: u32,
352 pub ntools: u32,
354}
355
356#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
357#[repr(C)]
358pub struct RawBuildToolVersion {
360 pub tool: u32,
362 pub version: u32,
364}
365
366#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
367#[repr(C)]
368pub struct RawEntryPointCommand {
370 pub cmd: u32,
372 pub cmdsize: u32,
374 pub entryoff: u64,
376 pub stacksize: u64,
378}
379
380#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
381#[repr(C)]
382pub struct RawSourceVersionCommand {
384 pub cmd: u32,
386 pub cmdsize: u32,
388 pub version: u64,
390}
391
392#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
393#[repr(C)]
394pub struct RawLinkeditDataCommand {
396 pub cmd: u32,
398 pub cmdsize: u32,
400 pub dataoff: u32,
402 pub datasize: u32,
404}
405
406#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
407#[repr(C)]
408pub struct RawDyldInfoCommand {
410 pub cmd: u32,
412 pub cmdsize: u32,
414 pub rebase_off: u32,
416 pub rebase_size: u32,
418 pub bind_off: u32,
420 pub bind_size: u32,
422 pub weak_bind_off: u32,
424 pub weak_bind_size: u32,
426 pub lazy_bind_off: u32,
428 pub lazy_bind_size: u32,
430 pub export_off: u32,
432 pub export_size: u32,
434}
435
436#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
437#[repr(C)]
438pub struct RawEncryptionInfoCommand {
440 pub cmd: u32,
442 pub cmdsize: u32,
444 pub cryptoff: u32,
446 pub cryptsize: u32,
448 pub cryptid: u32,
450}
451
452#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
453#[repr(C)]
454pub struct RawEncryptionInfoCommand64 {
456 pub cmd: u32,
458 pub cmdsize: u32,
460 pub cryptoff: u32,
462 pub cryptsize: u32,
464 pub cryptid: u32,
466 pub pad: u32,
468}
469
470#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
471#[repr(C)]
472pub struct RawVersionMinCommand {
474 pub cmd: u32,
476 pub cmdsize: u32,
478 pub version: u32,
480 pub sdk: u32,
482}
483
484#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
485#[repr(C)]
486pub struct RawNoteCommand {
488 pub cmd: u32,
490 pub cmdsize: u32,
492 pub data_owner: [u8; 16],
494 pub offset: u64,
496 pub size: u64,
498}
499
500#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
501#[repr(C)]
502pub struct RawFilesetEntryCommand {
504 pub cmd: u32,
506 pub cmdsize: u32,
508 pub vmaddr: u64,
510 pub fileoff: u64,
512 pub entry_id_offset: u32,
514 pub reserved: u32,
516}
517
518#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
519#[repr(C)]
520pub struct RawStringCommand {
522 pub cmd: u32,
524 pub cmdsize: u32,
526 pub string_offset: u32,
528}
529
530#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
531#[repr(C)]
532pub struct RawLinkerOptionCommand {
534 pub cmd: u32,
536 pub cmdsize: u32,
538 pub count: u32,
540}
541
542#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
543#[repr(C)]
544pub struct RawPrebindCksumCommand {
546 pub cmd: u32,
548 pub cmdsize: u32,
550 pub cksum: u32,
552}
553
554#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
555#[repr(C)]
556pub struct RawTwolevelHintsCommand {
558 pub cmd: u32,
560 pub cmdsize: u32,
562 pub offset: u32,
564 pub nhints: u32,
566}
567
568#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
569#[repr(C)]
570pub struct RawRoutinesCommand {
572 pub cmd: u32,
574 pub cmdsize: u32,
576 pub init_address: u32,
578 pub init_module: u32,
580 pub reserved1: u32,
582 pub reserved2: u32,
584 pub reserved3: u32,
586 pub reserved4: u32,
588 pub reserved5: u32,
590 pub reserved6: u32,
592}
593
594#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
595#[repr(C)]
596pub struct RawRoutinesCommand64 {
598 pub cmd: u32,
600 pub cmdsize: u32,
602 pub init_address: u64,
604 pub init_module: u64,
606 pub reserved1: u64,
608 pub reserved2: u64,
610 pub reserved3: u64,
612 pub reserved4: u64,
614 pub reserved5: u64,
616 pub reserved6: u64,
618}
619
620#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, Immutable, KnownLayout)]
623#[repr(C)]
624pub struct RawNlist32 {
626 pub n_strx: u32,
628 pub n_type: u8,
630 pub n_sect: u8,
632 pub n_desc: i16,
634 pub n_value: u32,
636}
637
638#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, Immutable, KnownLayout)]
639#[repr(C)]
640pub struct RawNlist64 {
642 pub n_strx: u32,
644 pub n_type: u8,
646 pub n_sect: u8,
648 pub n_desc: u16,
650 pub n_value: u64,
652}
653
654#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, Immutable, KnownLayout)]
657#[repr(C)]
658pub struct RawRelocationInfo {
660 pub r_address: i32,
662 pub r_symbolnum_and_flags: u32,
664}
665
666#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
669#[repr(C)]
670pub struct RawChainedFixupsHeader {
672 pub fixups_version: u32,
674 pub starts_offset: u32,
676 pub imports_offset: u32,
678 pub symbols_offset: u32,
680 pub imports_count: u32,
682 pub imports_format: u32,
684 pub symbols_format: u32,
686}
687
688#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
689#[repr(C)]
690pub struct RawChainedImport {
692 pub packed: u32,
694}
695
696#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
697#[repr(C)]
698pub struct RawChainedImportAddend {
700 pub packed: u32,
702 pub addend: i32,
704}
705
706#[derive(Debug, Clone, Copy, FromBytes, Immutable, KnownLayout)]
707#[repr(C)]
708pub struct RawChainedImportAddend64 {
710 pub packed: u64,
712 pub addend: u64,
714}
715
716pub fn write_pod<T>(buf: &mut [u8], offset: usize, value: &T) -> Result<()>
718where
719 T: IntoBytes + Immutable + KnownLayout,
720{
721 let size = size_of::<T>();
722 let end = offset
723 .checked_add(size)
724 .ok_or_else(|| Error::bounds(offset as u64, size as u64, buf.len() as u64))?;
725 if end > buf.len() {
726 return Err(Error::bounds(offset as u64, size as u64, buf.len() as u64));
727 }
728 buf[offset..end].copy_from_slice(value.as_bytes());
729 Ok(())
730}
731
732#[cfg(test)]
733mod tests {
734 use super::*;
735
736 #[test]
737 fn read_pod_valid() {
738 let data = 0xDEADBEEFu32.to_le_bytes();
739 let val: u32 = read_pod(&data, 0).unwrap();
740 assert_eq!(val, 0xDEADBEEF);
741 }
742
743 #[test]
744 fn read_pod_truncated() {
745 let data = [0u8; 2];
746 let result: Result<u32> = read_pod(&data, 0);
747 assert!(result.is_err());
748 }
749
750 #[test]
751 fn read_pod_offset_overflow() {
752 let data = [0u8; 8];
753 let result: Result<u32> = read_pod(&data, usize::MAX);
754 assert!(result.is_err());
755 }
756
757 #[test]
758 fn raw_header_size() {
759 assert_eq!(size_of::<RawMachHeader32>(), 28);
760 assert_eq!(size_of::<RawMachHeader64>(), 32);
761 }
762
763 #[test]
764 fn raw_segment_size() {
765 assert_eq!(size_of::<RawSegmentCommand32>(), 56);
766 assert_eq!(size_of::<RawSegmentCommand64>(), 72);
767 }
768
769 #[test]
770 fn raw_section_size() {
771 assert_eq!(size_of::<RawSection32>(), 68);
772 assert_eq!(size_of::<RawSection64>(), 80);
773 }
774
775 #[test]
776 fn raw_nlist_size() {
777 assert_eq!(size_of::<RawNlist32>(), 12);
778 assert_eq!(size_of::<RawNlist64>(), 16);
779 }
780
781 #[test]
782 fn raw_relocation_size() {
783 assert_eq!(size_of::<RawRelocationInfo>(), 8);
784 }
785
786 #[test]
787 fn raw_chained_fixup_sizes() {
788 assert_eq!(size_of::<RawChainedFixupsHeader>(), 28);
789 assert_eq!(size_of::<RawChainedImport>(), 4);
790 assert_eq!(size_of::<RawChainedImportAddend>(), 8);
791 assert_eq!(size_of::<RawChainedImportAddend64>(), 16);
792 }
793
794 #[test]
795 fn write_pod_round_trip() {
796 let mut buf = [0u8; 16];
797 let val: u32 = 0xCAFEBABE;
798 write_pod(&mut buf, 4, &val).unwrap();
799 let read_back: u32 = read_pod(&buf, 4).unwrap();
800 assert_eq!(read_back, val);
801 }
802
803 #[test]
804 fn write_pod_bounds() {
805 let mut buf = [0u8; 4];
806 let val: u64 = 0;
807 assert!(write_pod(&mut buf, 0, &val).is_err());
808 }
809}