atom_macho/load_command/
build_version.rs

1use crate::io::{Endian, ReadExt as _, WriteExt as _};
2use num_derive::FromPrimitive;
3use num_traits::FromPrimitive;
4use std::io::{Read, Write};
5
6/// The build_version_command contains the min OS version on which this
7/// binary was built to run for its platform.  The list of known platforms and
8/// tool values following it.
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct BuildVersionCommand {
11    pub cmd: u32,
12    /// BuildVersion::SIZE + ntools * BuildToolVersion::SIZE
13    pub cmdsize: u32,
14    pub platform: Platform,
15    /// X.Y.Z is encoded in nibbles xxxx.yy.zz
16    pub minos: Version,
17    /// X.Y.Z is encoded in nibbles xxxx.yy.zz
18    pub sdk: Version,
19    pub ntools: u32,
20}
21
22impl BuildVersionCommand {
23    pub const TYPE: u32 = 0x32;
24
25    pub const SIZE: u32 = 0x18; // 24
26
27    pub fn read_from_in<R: Read>(read: &mut R, endian: Endian) -> Self {
28        let cmd = read.read_u32_in(endian);
29        assert_eq!(cmd, Self::TYPE);
30
31        let cmdsize = read.read_u32_in(endian);
32
33        let platform_n = read.read_u32_in(endian);
34        let platform = Platform::from_u32(platform_n);
35
36        let minos_n = read.read_u32_in(endian);
37        let minos = Version::from_u32(minos_n);
38
39        let sdk_n = read.read_u32_in(endian);
40        let sdk = Version::from_u32(sdk_n);
41
42        let ntools = read.read_u32_in(endian);
43
44        assert_eq!(cmdsize, Self::SIZE + BuildToolVersion::SIZE * ntools);
45
46        BuildVersionCommand {
47            cmd,
48            cmdsize,
49            platform,
50            minos,
51            sdk,
52            ntools,
53        }
54    }
55
56    pub fn write_into<W: Write>(&self, write: &mut W) {
57        write.write_u32_native(self.cmd);
58        write.write_u32_native(self.cmdsize);
59        write.write_u32_native(self.platform.to_u32());
60        write.write_u32_native(self.minos.to_u32());
61        write.write_u32_native(self.sdk.to_u32());
62        write.write_u32_native(self.ntools);
63    }
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive)]
67pub enum Platform {
68    MacOS = 1,
69    IOS = 2,
70    TvOS = 3,
71    WatchOS = 4,
72    BridgeOS = 5,
73    MacCatalyst = 6,
74    IOSSimulator = 7,
75    TvOSSimulator = 8,
76    WatchOSSimulator = 9,
77    Driverkit = 10,
78}
79
80impl Platform {
81    pub fn from_u32(n: u32) -> Self {
82        FromPrimitive::from_u32(n).unwrap_or_else(|| panic!("Invalid platform number {}", n))
83    }
84
85    pub fn to_u32(self) -> u32 {
86        self as u32
87    }
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub struct Version {
92    pub major: u16,
93    pub minor: u8,
94    pub release: u8,
95}
96
97impl Version {
98    /// version is represented as "xxxx.yy.zz"
99    pub fn from_u32(n: u32) -> Self {
100        let major = ((n & 0xFFFF_0000) >> 16) as u16;
101        let minor = ((n & 0x0000_FF00) >> 8) as u8;
102        let release = (n & 0x0000_00FF) as u8;
103        Version {
104            major,
105            minor,
106            release,
107        }
108    }
109
110    pub fn to_u32(&self) -> u32 {
111        let mut n = 0;
112        n |= (self.major as u32) << 16;
113        n |= (self.minor as u32) << 8;
114        n |= self.release as u32;
115        n
116    }
117}
118
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct BuildToolVersion {
121    pub tool: Tool,
122    pub version: u32,
123}
124
125impl BuildToolVersion {
126    pub const SIZE: u32 = 0x8;
127
128    pub fn read_from_in<R: Read>(read: &mut R, endian: Endian) -> Self {
129        let tool_n = read.read_u32_in(endian);
130        let tool = Tool::from_u32(tool_n);
131        let version = read.read_u32_in(endian);
132
133        BuildToolVersion { tool, version }
134    }
135
136    pub fn write_into<W: Write>(&self, write: &mut W) {
137        write.write_u32_native(self.tool.to_u32());
138        write.write_u32_native(self.version);
139    }
140}
141
142#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive)]
143pub enum Tool {
144    Clang = 1,
145    Swift = 2,
146    LD = 3,
147}
148
149impl Tool {
150    pub fn from_u32(n: u32) -> Self {
151        FromPrimitive::from_u32(n).unwrap_or_else(|| panic!("Unsupported tool number {}", n))
152    }
153
154    pub fn to_u32(self) -> u32 {
155        self as u32
156    }
157}
158
159#[cfg(test)]
160mod tests {
161    use super::*;
162
163    #[test]
164    fn write_and_read_build_version_command() {
165        let cmd = BuildVersionCommand {
166            cmd: BuildVersionCommand::TYPE,
167            cmdsize: BuildVersionCommand::SIZE,
168            platform: Platform::MacOS,
169            minos: Version {
170                major: 3,
171                minor: 10,
172                release: 42,
173            },
174            sdk: Version {
175                major: 1,
176                minor: 11,
177                release: 13,
178            },
179            ntools: 0,
180        };
181
182        let mut buf = Vec::new();
183
184        cmd.write_into(&mut buf);
185
186        assert_eq!(buf.len(), BuildVersionCommand::SIZE as usize);
187
188        let read_cmd = BuildVersionCommand::read_from_in(&mut buf.as_slice(), Endian::NATIVE);
189
190        assert_eq!(read_cmd, cmd);
191    }
192
193    #[test]
194    fn write_and_read_build_tool_version() {
195        let version = BuildToolVersion {
196            tool: Tool::Clang,
197            version: 42,
198        };
199
200        let mut buf = Vec::new();
201
202        version.write_into(&mut buf);
203
204        assert_eq!(buf.len(), BuildToolVersion::SIZE as usize);
205
206        let read_version = BuildToolVersion::read_from_in(&mut buf.as_slice(), Endian::NATIVE);
207
208        assert_eq!(read_version, version);
209    }
210}