1use std::ops::Range;
6
7pub const MAX_HEADER_BYTES: usize = 1 << 16;
9const MAGIC_64: u32 = 0xfeed_facf;
10const HEADER_BYTES: usize = 32;
11const COMMAND_COUNT_AT: usize = 16;
12const COMMANDS_SIZE_AT: usize = 20;
13const SEGMENT_64: u32 = 0x19;
14const CODE_SIGNATURE: u32 = 0x1d;
15const SEGMENT_VMSIZE_AT: usize = 32;
17const SEGMENT_FILEOFF_AT: usize = 40;
18const SEGMENT_FILESIZE_AT: usize = 48;
19const DATA_OFFSET_AT: usize = 8;
21const PAGE_BYTES: u64 = 0x4000;
23
24fn u32_at(bytes: &[u8], at: usize) -> Option<u32> {
25 Some(u32::from_le_bytes(bytes.get(at..at + 4)?.try_into().ok()?))
26}
27
28fn u64_at(bytes: &[u8], at: usize) -> Option<u64> {
29 Some(u64::from_le_bytes(bytes.get(at..at + 8)?.try_into().ok()?))
30}
31
32fn set_u32(bytes: &mut [u8], at: usize, value: u32) {
33 bytes[at..at + 4].copy_from_slice(&value.to_le_bytes());
34}
35
36fn set_u64(bytes: &mut [u8], at: usize, value: u64) {
37 bytes[at..at + 8].copy_from_slice(&value.to_le_bytes());
38}
39
40fn load_commands(bytes: &[u8]) -> Vec<(u32, Range<usize>)> {
42 let mut commands = Vec::new();
43 if u32_at(bytes, 0) != Some(MAGIC_64) {
44 return commands;
45 }
46 let count = u32_at(bytes, COMMAND_COUNT_AT).unwrap_or(0);
47 let mut at = HEADER_BYTES;
48 for _ in 0..count {
49 let (Some(command), Some(size)) = (u32_at(bytes, at), u32_at(bytes, at + 4)) else {
50 break;
51 };
52 commands.push((command, at..at + size as usize));
53 at += size as usize;
54 }
55 commands
56}
57
58pub fn signature_offset(bytes: &[u8]) -> Option<usize> {
60 let (_, signature) = load_commands(bytes).into_iter().find(|(command, _)| *command == CODE_SIGNATURE)?;
61 u32_at(bytes, signature.start + DATA_OFFSET_AT).map(|offset| offset as usize)
62}
63
64pub fn unsigned(bytes: &[u8]) -> Vec<u8> {
66 let mut image = bytes.to_vec();
67 let Some((_, signature)) = load_commands(bytes).into_iter().find(|(command, _)| *command == CODE_SIGNATURE) else {
68 return image;
69 };
70 let offset = signature_offset(bytes).expect("a signature command names its offset");
71 image.truncate(offset);
72 let commands_end = HEADER_BYTES + u32_at(bytes, COMMANDS_SIZE_AT).unwrap_or(0) as usize;
73 let removed = signature.len();
74 image.copy_within(signature.end..commands_end, signature.start);
75 image[commands_end - removed..commands_end].fill(0);
76 set_u32(&mut image, COMMAND_COUNT_AT, u32_at(bytes, COMMAND_COUNT_AT).unwrap_or(1) - 1);
77 set_u32(&mut image, COMMANDS_SIZE_AT, (commands_end - HEADER_BYTES - removed) as u32);
78 extend_last_segment_to_end(&mut image);
79 image
80}
81
82pub fn extend_last_segment_to_end(image: &mut [u8]) {
84 let last_segment = load_commands(image)
85 .into_iter()
86 .filter(|(command, _)| *command == SEGMENT_64)
87 .max_by_key(|(_, segment)| u64_at(image, segment.start + SEGMENT_FILEOFF_AT));
88 let Some((_, segment)) = last_segment else { return };
89 let file_offset = u64_at(image, segment.start + SEGMENT_FILEOFF_AT).unwrap_or(0);
90 let file_size = image.len() as u64 - file_offset;
91 set_u64(image, segment.start + SEGMENT_FILESIZE_AT, file_size);
92 set_u64(image, segment.start + SEGMENT_VMSIZE_AT, file_size.next_multiple_of(PAGE_BYTES));
93}