Source code

Revision control

Copy as Markdown

Other Tools

use super::*;
use crate::minidump_format::*;
/// Retrieve the OS version information.
///
/// Note that this only works on 10.13.4+, but that release is over 4 years old
/// and 1 version behind the latest unsupported release at the time of this writing
///
/// Note that Breakpad/Crashpad use a private API in CoreFoundation to do this
/// via _CFCopySystemVersionDictionary->_kCFSystemVersionProductVersionKey
fn os_version() -> (u32, u32, u32) {
let vers = mach::sysctl_string(b"kern.osproductversion\0");
let inner = || {
let mut it = vers.split('.');
let major: u32 = it.next()?.parse().ok()?;
let minor: u32 = it.next()?.parse().ok()?;
let patch: u32 = it.next().and_then(|p| p.parse().ok()).unwrap_or_default();
Some((major, minor, patch))
};
inner().unwrap_or_default()
}
/// Retrieves the OS build version.
///
/// Note that Breakpad/Crashpad use a private API in CoreFoundation to do this
/// via _CFCopySystemVersionDictionary->_kCFSystemVersionBuildVersionKey. I have
/// no idea how long this has been the case, but the same information can be
/// retrieved via `sysctlbyname` via the `kern.osversion` key as seen by comparing
/// its value versus the output of the `sw_vers -buildVersion` command
#[inline]
fn build_version() -> String {
mach::sysctl_string(b"kern.osversion\0")
}
/// Retrieves more detailed information on the cpu.
///
/// Note that this function is only implemented on `x86_64` as Apple doesn't
/// expose similar info on `aarch64` (or at least, not via the same mechanisms)
fn read_cpu_info(cpu: &mut format::CPU_INFORMATION) {
if !cfg!(target_arch = "x86_64") {
return;
}
let mut md_feats: u64 = 1 << 2 /*PF_COMPARE_EXCHANGE_DOUBLE*/;
let features: u64 = mach::sysctl_by_name(b"machdep.cpu.feature_bits\0");
// Map the cpuid feature to its equivalent minidump cpu feature.
// See https://en.wikipedia.org/wiki/CPUID for where the values for the
// various cpuid bits come from, and
// for where the bits for the the minidump come from
macro_rules! map_feature {
($set:expr, $cpuid_bit:expr, $md_bit:expr) => {
if $set & (1 << $cpuid_bit) != 0 {
md_feats |= 1 << $md_bit;
}
};
}
map_feature!(
features, 4, /*TSC*/
8 /* PF_RDTSC_INSTRUCTION_AVAILABLE */
);
map_feature!(features, 6 /*PAE*/, 9 /* PF_PAE_ENABLED */);
map_feature!(
features, 23, /*MMX*/
3 /* PF_MMX_INSTRUCTIONS_AVAILABLE */
);
map_feature!(
features, 25, /*SSE*/
6 /* PF_XMMI_INSTRUCTIONS_AVAILABLE */
);
map_feature!(
features, 26, /*SSE2*/
10 /* PF_XMMI64_INSTRUCTIONS_AVAILABLE */
);
map_feature!(
features, 32, /*SSE3*/
13 /* PF_SSE3_INSTRUCTIONS_AVAILABLE */
);
map_feature!(
features, 45, /*CX16*/
14 /* PF_COMPARE_EXCHANGE128 */
);
map_feature!(features, 58 /*XSAVE*/, 17 /* PF_XSAVE_ENABLED */);
map_feature!(
features, 62, /*RDRAND*/
28 /* PF_RDRAND_INSTRUCTION_AVAILABLE */
);
let ext_features: u64 = mach::sysctl_by_name(b"machdep.cpu.extfeature_bits\0");
map_feature!(
ext_features,
27, /* RDTSCP */
32 /* PF_RDTSCP_INSTRUCTION_AVAILABLE */
);
map_feature!(
ext_features,
31, /* 3DNOW */
7 /* PF_3DNOW_INSTRUCTIONS_AVAILABLE */
);
let leaf_features: u32 = mach::sysctl_by_name(b"machdep.cpu.leaf7_feature_bits\0");
map_feature!(
leaf_features,
0, /* F7_FSGSBASE */
22 /* PF_RDWRFSGSBASE_AVAILABLE */
);
// In newer production kernels, NX is always enabled.
// See 10.15.0 xnu-6153.11.26/osfmk/x86_64/pmap.c nx_enabled.
md_feats |= 1 << 12 /* PF_NX_ENABLED */;
// All CPUs that Apple is known to have shipped should support DAZ.
md_feats |= 1 << 11 /* PF_SSE_DAZ_MODE_AVAILABLE */;
// minidump_common::format::OtherCpuInfo is just 2 adjacent u64's, we only
// set the first, so just do a direct write to the bytes
cpu.data[..std::mem::size_of::<u64>()].copy_from_slice(&md_feats.to_ne_bytes());
}
impl MinidumpWriter {
/// Writes the [`MDStreamType::SystemInfoStream`] stream.
///
/// On MacOS we includes basic CPU information, though some of it is not
/// available on `aarch64` at the time of this writing, as well as kernel
/// version information.
pub(crate) fn write_system_info(
&mut self,
buffer: &mut DumpBuf,
_dumper: &TaskDumper,
) -> Result<MDRawDirectory, WriterError> {
let mut info_section = MemoryWriter::<MDRawSystemInfo>::alloc(buffer)?;
let dirent = MDRawDirectory {
stream_type: MDStreamType::SystemInfoStream as u32,
location: info_section.location(),
};
let number_of_processors: u8 = mach::int_sysctl_by_name(b"hw.ncpu\0");
// SAFETY: POD buffer
let mut cpu: format::CPU_INFORMATION = unsafe { std::mem::zeroed() };
read_cpu_info(&mut cpu);
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] {
let processor_architecture = MDCPUArchitecture::PROCESSOR_ARCHITECTURE_AMD64;
// machdep.cpu.family and machdep.cpu.model already take the extended family
// and model IDs into account. See 10.9.2 xnu-2422.90.20/osfmk/i386/cpuid.c
// cpuid_set_generic_info().
let processor_level: u16 = mach::int_sysctl_by_name(b"machdep.cpu.family\0");
let model: u8 = mach::int_sysctl_by_name(b"machdep.cpu.model\0");
let stepping: u8 = mach::int_sysctl_by_name(b"machdep.cpu.stepping\0");
let processor_revision = ((model as u16) << 8) | stepping as u16;
} else if #[cfg(target_arch = "aarch64")] {
let processor_architecture = MDCPUArchitecture::PROCESSOR_ARCHITECTURE_ARM64_OLD;
let family: u32 = mach::sysctl_by_name(b"hw.cpufamily\0");
let processor_level = (family & 0xffff0000 >> 16) as u16;
let processor_revision = (family & 0x0000ffff) as u16;
} else {
compile_error!("unsupported target architecture");
}
}
let (major_version, minor_version, build_number) = os_version();
let os_version_loc = write_string_to_location(buffer, &build_version())?;
let info = MDRawSystemInfo {
// CPU
processor_architecture: processor_architecture as u16,
processor_level,
processor_revision,
number_of_processors,
cpu,
// OS
platform_id: PlatformId::MacOs as u32,
product_type: 1, // VER_NT_WORKSTATION, could also be VER_NT_SERVER but...seriously?
major_version,
minor_version,
build_number,
csd_version_rva: os_version_loc.rva,
suite_mask: 0,
reserved2: 0,
};
info_section.set_value(buffer, info)?;
Ok(dirent)
}
}