Skip to content

Commit eae587a

Browse files
committed
rust: validate Apple SDK version and target version in JSON metadata
I could easily see these not being advertised correctly. It is something I worry about when making changes to Apple SDKs. Let's add testing that ensures the Mach-O metadata is in sync with the JSON. Fortunately, there are no failures on the most recently published distributions.
1 parent 32c8e7a commit eae587a

File tree

1 file changed

+68
-0
lines changed

1 file changed

+68
-0
lines changed

src/validation.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,14 +992,29 @@ struct MachOSymbol {
992992
weak: bool,
993993
}
994994

995+
/// Parses an integer with nibbles xxxx.yy.zz into a [semver::Version].
996+
fn parse_version_nibbles(v: u32) -> semver::Version {
997+
let major = v >> 16;
998+
let minor = v << 16 >> 24;
999+
let patch = v & 0xff;
1000+
1001+
semver::Version::new(major as _, minor as _, patch as _)
1002+
}
1003+
9951004
fn validate_macho<Mach: MachHeader<Endian = Endianness>>(
9961005
context: &mut ValidationContext,
9971006
target_triple: &str,
9981007
python_major_minor: &str,
1008+
advertised_target_version: &str,
1009+
advertised_sdk_version: &str,
9991010
path: &Path,
10001011
header: &Mach,
10011012
bytes: &[u8],
10021013
) -> Result<()> {
1014+
let advertised_target_version =
1015+
semver::Version::parse(&format!("{}.0", advertised_target_version))?;
1016+
let advertised_sdk_version = semver::Version::parse(&format!("{}.0", advertised_sdk_version))?;
1017+
10031018
let endian = header.endian()?;
10041019

10051020
let wanted_cpu_type = match target_triple {
@@ -1030,9 +1045,28 @@ fn validate_macho<Mach: MachHeader<Endian = Endianness>>(
10301045

10311046
let mut dylib_names = vec![];
10321047
let mut undefined_symbols = vec![];
1048+
let mut target_version = None;
1049+
let mut sdk_version = None;
10331050

10341051
while let Some(load_command) = load_commands.next()? {
10351052
match load_command.variant()? {
1053+
LoadCommandVariant::BuildVersion(v) => {
1054+
// Sometimes the SDK version is advertised as 0.0.0. In that case just ignore it.
1055+
let version = parse_version_nibbles(v.sdk.get(endian));
1056+
if version > semver::Version::new(0, 0, 0) {
1057+
sdk_version = Some(version);
1058+
}
1059+
1060+
target_version = Some(parse_version_nibbles(v.minos.get(endian)));
1061+
}
1062+
LoadCommandVariant::VersionMin(v) => {
1063+
let version = parse_version_nibbles(v.sdk.get(endian));
1064+
if version > semver::Version::new(0, 0, 0) {
1065+
sdk_version = Some(version);
1066+
}
1067+
1068+
target_version = Some(parse_version_nibbles(v.version.get(endian)));
1069+
}
10361070
LoadCommandVariant::Dylib(command) => {
10371071
let raw_string = load_command.string(endian, command.dylib.name.clone())?;
10381072
let lib = String::from_utf8(raw_string.to_vec())?;
@@ -1134,6 +1168,28 @@ fn validate_macho<Mach: MachHeader<Endian = Endianness>>(
11341168
}
11351169
}
11361170

1171+
if let Some(actual_target_version) = target_version {
1172+
if actual_target_version != advertised_target_version {
1173+
context.errors.push(format!(
1174+
"{} targets SDK {} but JSON advertises SDK {}",
1175+
path.display(),
1176+
actual_target_version,
1177+
advertised_target_version
1178+
));
1179+
}
1180+
}
1181+
1182+
if let Some(actual_sdk_version) = sdk_version {
1183+
if actual_sdk_version != advertised_sdk_version {
1184+
context.errors.push(format!(
1185+
"{} was built with SDK {} but JSON advertises SDK {}",
1186+
path.display(),
1187+
actual_sdk_version,
1188+
advertised_sdk_version,
1189+
))
1190+
}
1191+
}
1192+
11371193
// Don't perform undefined symbol analysis for object files because the object file
11381194
// in isolation lacks context.
11391195
if header.filetype(endian) != MH_OBJECT {
@@ -1248,6 +1304,12 @@ fn validate_possible_object_file(
12481304
&mut context,
12491305
triple,
12501306
python_major_minor,
1307+
json.apple_sdk_deployment_target
1308+
.as_ref()
1309+
.expect("apple_sdk_deployment_target should be set"),
1310+
json.apple_sdk_version
1311+
.as_ref()
1312+
.expect("apple_sdk_version should be set"),
12511313
path.as_ref(),
12521314
header,
12531315
&data,
@@ -1260,6 +1322,12 @@ fn validate_possible_object_file(
12601322
&mut context,
12611323
triple,
12621324
python_major_minor,
1325+
json.apple_sdk_deployment_target
1326+
.as_ref()
1327+
.expect("apple_sdk_deployment_target should be set"),
1328+
json.apple_sdk_version
1329+
.as_ref()
1330+
.expect("apple_sdk_version should be set"),
12631331
path.as_ref(),
12641332
header,
12651333
&data,

0 commit comments

Comments
 (0)