Merge pull request #44 from lucannez64/fix/pr41-compatibility

Extract tested runtime compatibility fixes from #41
This commit is contained in:
Kalah authored and GitHub committed 2026-10-17 23:40:44 +02:00
commit a6c119ed0e
7 files changed
+436 -12

No files matched your search

+82
View File
@@ -0,0 +1,82 @@
# PR #41 compatibility adaptation
[Original PR](https://github.com/lucannez64/darling-lol/pull/41)
This adaptation keeps the compatibility corrections that can be implemented and
checked independently of a fabricated machine or security identity. It starts
from main after PRs #43 and #42. It does not claim to resolve account bans.
## Included
- Foundation returns the installed system's full version/build description,
falling back to ProductVersion and then Unknown. Numeric version comparisons
read ProductVersion directly, independently of the localized or overridden
description. No macOS version or build number is invented.
- physicalMemory queries 64-bit hw.memsize, then retries hw.physmem with its
correct 32-bit buffer width. The existing failure fallback is preserved.
- Unknown ptrace requests return EINVAL instead of an uninitialized value.
Existing handled requests retain their behavior.
- Unsupported page-accounting and task-policy requests return
KERN_NOT_SUPPORTED, initializing output counts where appropriate. Unsupported
VM-region and thread-info flavors return KERN_INVALID_ARGUMENT. These calls
no longer terminate darlingserver through unsafe stubs.
- The kernel-debugger capability query returns false and initializes its output.
- The 32-bit debug-state conversion passes the converted structure instead of
the address of its input pointer. Hardware debug-state support remains
unsupported.
## Work left outside this adaptation
The original PR's rendered source changes were inspected against its merge base,
so patch regeneration noise was excluded from this review.
- Its darling.patch deletes the existing vDSP arith.c and fft.c implementations.
Those working routines must be preserved.
- GPU reporting is already implemented using actual Vulkan device properties.
A fixed Radeon Pro name and 4 GB budget would replace that behavior.
- The per-host platform ID fix is already merged. A separate configurable
identity feature would need an explicit schema, stable defaults, import
validation, and migration tests before replacing it. Automatic Apple serial,
NVRAM, OS-version, and model fabrication is excluded.
- CPUID trapping changes the SIGSEGV path and advertises a specific Intel CPU
on every host. It needs a separate design around actual host capabilities,
instruction safety, signal chaining, and performance; it is not necessary
for the compatibility corrections above.
- csops, AMFI, SIP, and debugger APIs must reflect implemented checks and state.
Fixed signing identities, fabricated trust-cache membership, and successful
setters that do not apply their settings cannot serve as those implementations.
- IOKit registry lookup/property routines should be extracted with bounds,
lifetime, traversal, and serialization tests, independent of synthetic NVRAM.
- Launcher environment and descriptor changes need a defined ownership boundary.
Blanket removal of driver variables or closing descriptors 3 through 256 can
remove intentional configuration and live descriptors. Keep #42's path setup.
- Log verbosity, descriptor leaks in shellspawn, CPU-frequency units, and other
independent correctness fixes can be separate changes with focused checks.
## Validation
The patch-level C regression tests compile the actual patched function bodies
with inert RPC substitutes and exercise error returns, output initialization,
null output pointers, unknown ptrace requests, and existing ptrace branches:
```sh
python3 -m unittest discover -s tests
python3 scripts/prepare.py --check-only
python3 scripts/validate.py
python3 tools/riot-launcher-offline-check.py
python3 tools/riot-input-offline-check.py
python3 tools/riot-runtime-cleanup-offline-check.py
```
With the adapted patches prepared and Foundation rebuilt, the guest probe checks
full version/build reporting, numeric version independence from an overridden
description, and physicalMemory against 64-bit hw.memsize:
```sh
python3 build/runtime/tools/build-riot-process-info-check.py
darling shell /Volumes/SystemRoot/ABSOLUTE_PROJECT_PATH/build/riot-process-info-check
```
The probe must load the rebuilt Foundation using the configured runtime or an
isolated DYLD_FRAMEWORK_PATH. The adaptation was checked with an isolated
Foundation build; the installed runtime and running server were not replaced.
+88 -8
View File
@@ -445,7 +445,7 @@ index 458e659..2e33f20 100644
// wait until it's read
if (!dtape_semaphore_down_simple(kqchan->waiter_read_semaphore)) {
diff --git a/duct-tape/src/memory.c b/duct-tape/src/memory.c
index 3114947..fcff461 100644
index 3114947..e2a8d5e 100644
--- a/duct-tape/src/memory.c
+++ b/duct-tape/src/memory.c
@@ -11,6 +11,7 @@
@@ -807,7 +807,43 @@ index 3114947..fcff461 100644
};
kern_return_t mach_vm_msync(vm_map_t map, mach_vm_offset_t address, mach_vm_size_t size, vm_sync_t sync_flags) {
@@ -856,7 +1163,7 @@ static kern_return_t mach_vm_remap_external_shared(vm_map_t target_map, mach_vm_
@@ -620,15 +927,22 @@ kern_return_t mach_vm_msync(vm_map_t map, mach_vm_offset_t address, mach_vm_size
};
kern_return_t mach_vm_page_info(vm_map_t map, mach_vm_address_t address, vm_page_info_flavor_t flavor, vm_page_info_t info, mach_msg_type_number_t* count) {
- dtape_stub_unsafe();
+ // Page accounting is not implemented; let the caller handle unsupported queries.
+ if (count) *count = 0;
+ return KERN_NOT_SUPPORTED;
};
kern_return_t mach_vm_page_query(vm_map_t map, mach_vm_offset_t offset, int* disposition, int* ref_count) {
- dtape_stub_unsafe();
+ // Page accounting is not implemented; let the caller handle unsupported queries.
+ if (disposition) *disposition = 0;
+ if (ref_count) *ref_count = 0;
+ return KERN_NOT_SUPPORTED;
};
kern_return_t mach_vm_page_range_query(vm_map_t map, mach_vm_offset_t address, mach_vm_size_t size, mach_vm_address_t dispositions_addr, mach_vm_size_t* dispositions_count) {
- dtape_stub_unsafe();
+ // Page accounting is not implemented; let the caller handle unsupported queries.
+ if (dispositions_count) *dispositions_count = 0;
+ return KERN_NOT_SUPPORTED;
};
kern_return_t mach_vm_protect(vm_map_t map, mach_vm_offset_t start, mach_vm_size_t size, boolean_t set_maximum, vm_prot_t new_protection) {
@@ -756,7 +1070,8 @@ region_info_out:
return kr;
};
default:
- dtape_stub_unsafe("Unimplemented flavor");
+ if (object_name) *object_name = IP_NULL;
+ return KERN_INVALID_ARGUMENT;
}
};
@@ -856,7 +1171,7 @@ static kern_return_t mach_vm_remap_external_shared(vm_map_t target_map, mach_vm_
src_existing_entry_count = dtape_map_find_shared_entries_locked(src_map, start_memaddr, map_size, src_existing_entries, sizeof(src_existing_entries) / sizeof(*src_existing_entries));
@@ -816,7 +852,7 @@ index 3114947..fcff461 100644
// special case for what LLDB does with dyld info
dtape_map_shared_descriptor_retain(src_existing_entries[0]->descriptor);
descriptor = src_existing_entries[0]->descriptor;
@@ -1441,6 +1748,10 @@ mach_destroy_memory_entry(
@@ -1441,6 +1756,10 @@ mach_destroy_memory_entry(
named_entry->ref_count -= 1;
if (named_entry->ref_count == 0) {
@@ -827,7 +863,7 @@ index 3114947..fcff461 100644
if (named_entry->is_sub_map) {
vm_map_deallocate(named_entry->backing.map);
} else if (named_entry->is_copy) {
@@ -1456,6 +1767,9 @@ mach_destroy_memory_entry(
@@ -1456,6 +1775,9 @@ mach_destroy_memory_entry(
named_entry_unlock(named_entry);
named_entry_lock_destroy(named_entry);
@@ -882,7 +918,7 @@ index afe60fc..02968b2 100644
void Assert(const char* file, int line, const char* expression) {
diff --git a/duct-tape/src/stubs.c b/duct-tape/src/stubs.c
index dc1cfa5..242c6da 100644
index dc1cfa5..39a8c8d 100644
--- a/duct-tape/src/stubs.c
+++ b/duct-tape/src/stubs.c
@@ -37,9 +37,18 @@ extern char* getenv(const char* name);
@@ -913,7 +949,7 @@ index dc1cfa5..242c6da 100644
kind_info = " (unsafe)";
} else {
if (!log_safe_stubs) {
@@ -210,7 +219,9 @@ kern_return_t kmod_get_info(host_t host, kmod_info_array_t* kmod_list, mach_msg_
@@ -210,11 +219,15 @@ kern_return_t kmod_get_info(host_t host, kmod_info_array_t* kmod_list, mach_msg_
};
kern_return_t kext_request(host_priv_t hostPriv, uint32_t clientLogSpec, vm_offset_t requestIn, mach_msg_type_number_t requestLengthIn, vm_offset_t* responseOut, mach_msg_type_number_t* responseLengthOut, vm_offset_t* logDataOut, mach_msg_type_number_t* logDataLengthOut, kern_return_t* op_result) {
@@ -924,8 +960,15 @@ index dc1cfa5..242c6da 100644
};
uint32_t PE_i_can_has_debugger(uint32_t* something) {
- dtape_stub_unsafe();
+ // No kernel debugger capability is provided by this runtime.
+ if (something) *something = 0;
+ return 0;
};
bool work_interval_port_type_render_server(mach_port_name_t port_name) {
diff --git a/duct-tape/src/task.c b/duct-tape/src/task.c
index a9eb4bc..b77187d 100644
index a9eb4bc..7ef2800 100644
--- a/duct-tape/src/task.c
+++ b/duct-tape/src/task.c
@@ -521,7 +521,9 @@ kern_return_t task_info(task_t xtask, task_flavor_t flavor, task_info_t task_inf
@@ -939,8 +982,27 @@ index a9eb4bc..b77187d 100644
}
};
@@ -585,7 +587,7 @@ kern_return_t task_restartable_ranges_synchronize(task_t task) {
};
kern_return_t task_set_exc_guard_behavior(task_t task, task_exc_guard_behavior_t behavior) {
- dtape_stub_unsafe();
+ return KERN_NOT_SUPPORTED;
};
kern_return_t task_set_info(task_t task, task_flavor_t flavor, task_info_t task_info_in, mach_msg_type_number_t task_info_count) {
@@ -593,7 +595,8 @@ kern_return_t task_set_info(task_t task, task_flavor_t flavor, task_info_t task_
};
kern_return_t task_set_phys_footprint_limit(task_t task, int new_limit_mb, int* old_limit_mb) {
- dtape_stub_unsafe();
+ if (old_limit_mb) *old_limit_mb = 0;
+ return KERN_NOT_SUPPORTED;
};
kern_return_t task_set_state(task_t task, int flavor, thread_state_t state, mach_msg_type_number_t state_count) {
diff --git a/duct-tape/src/thread.c b/duct-tape/src/thread.c
index f4ce909..e14fe79 100644
index f4ce909..a6cdb2c 100644
--- a/duct-tape/src/thread.c
+++ b/duct-tape/src/thread.c
@@ -53,6 +53,7 @@ dtape_thread_t* dtape_thread_create(dtape_task_t* task, uint64_t nsid, void* con
@@ -990,6 +1052,24 @@ index f4ce909..e14fe79 100644
return TRUE;
};
@@ -782,7 +800,7 @@ thread_set_state(
s64.dr6 = s->dr6;
s64.dr7 = s->dr7;
- return thread_set_state(thread, x86_DEBUG_STATE64, (thread_state_t) &s,
+ return thread_set_state(thread, x86_DEBUG_STATE64, (thread_state_t) &s64,
x86_DEBUG_STATE64_COUNT);
}
case x86_DEBUG_STATE64:
@@ -1293,7 +1311,7 @@ kern_return_t thread_info(thread_t xthread, thread_flavor_t flavor, thread_info_
};
default:
- dtape_stub_unsafe("Unimplemented flavor");
+ return KERN_INVALID_ARGUMENT;
}
};
@@ -1324,11 +1342,11 @@ kern_return_t thread_wire(host_priv_t host_priv, thread_t thread, boolean_t wire
};
+56 -3
View File
@@ -34,10 +34,51 @@ index 80c92bc07..2a80df649 100644
+
+FOUNDATION_EXPORT NSString *const NSUserActivityTypeBrowsingWeb;
diff --git a/src/NSProcessInfo.m b/src/NSProcessInfo.m
index 1c21f176d..f9f354c80 100644
index 1c21f17..420955a 100644
--- a/src/NSProcessInfo.m
+++ b/src/NSProcessInfo.m
@@ -305,6 +305,13 @@ SINGLETON_RR()
@@ -251,12 +251,13 @@ SINGLETON_RR()
}
#else
NSDictionary* dict = (NSDictionary*) _CFCopySystemVersionDictionary();
- NSString* string = [dict objectForKey: @"ProductVersion"];
+ NSString* string = [dict objectForKey: @"FullVersionString"];
+ if (![string length]) string = [dict objectForKey: @"ProductVersion"];
[string retain];
[dict release];
- return [string autorelease];
+ return [string autorelease] ?: @"Unknown";
#endif
}
@@ -285,13 +286,17 @@ SINGLETON_RR()
- (unsigned long long)physicalMemory
{
- unsigned long long amt = 1;
- size_t len = sizeof(unsigned long long);
- if (sysctlbyname("hw.physmem", &amt, &len, 0, 0) != 0)
- {
- amt = 1;
- }
- return amt;
+ uint64_t memory = 0;
+ size_t length = sizeof(memory);
+ if (sysctlbyname("hw.memsize", &memory, &length, NULL, 0) == 0)
+ return memory;
+
+ // hw.physmem is a legacy 32-bit value; reset the buffer width on fallback.
+ uint32_t legacyMemory = 0;
+ length = sizeof(legacyMemory);
+ if (sysctlbyname("hw.physmem", &legacyMemory, &length, NULL, 0) == 0)
+ return legacyMemory;
+ return 1;
}
- (NSTimeInterval)systemUptime
@@ -305,12 +310,23 @@ SINGLETON_RR()
return t.tv_sec + t.tv_usec / (NSTimeInterval)USEC_PER_SEC;
}
@@ -51,7 +92,19 @@ index 1c21f176d..f9f354c80 100644
- (NSOperatingSystemVersion)operatingSystemVersion
{
NSOperatingSystemVersion version = { 0, 0, 0 };
@@ -396,3 +403,5 @@ void __NSInitializeProcess(int argc,const char *argv[]) {
- NSString* string = [self operatingSystemVersionString];
- NSArray* arr = [string componentsSeparatedByString:@"."];
+ // Parse the numeric product version independently of its localized display string.
+ NSDictionary* dict = (NSDictionary*) _CFCopySystemVersionDictionary();
+ NSArray* arr = [[dict objectForKey: @"ProductVersion"] componentsSeparatedByString:@"."];
+ [arr retain];
+ [dict release];
+ [arr autorelease];
if ([arr count] >= 1)
{
@@ -396,3 +412,5 @@ void __NSInitializeProcess(int argc,const char *argv[]) {
}
}
#endif
+10 -1
View File
@@ -3276,9 +3276,18 @@ index c2f70fb..af904ed 100644
default:
__simple_printf("sys_proc_info(): Unsupported callnum: %d\n",
diff --git a/darling/src/libsystem_kernel/emulation/src/xnu_syscall/bsd/impl/misc/ptrace.c b/darling/src/libsystem_kernel/emulation/src/xnu_syscall/bsd/impl/misc/ptrace.c
index 0a8f1e4..55aadbe 100644
index 0a8f1e4..a19f12e 100644
--- a/darling/src/libsystem_kernel/emulation/src/xnu_syscall/bsd/impl/misc/ptrace.c
+++ b/darling/src/libsystem_kernel/emulation/src/xnu_syscall/bsd/impl/misc/ptrace.c
@@ -22,7 +22,7 @@ extern int getpid(void);
long sys_ptrace(int request, int pid, void* addr, int data)
{
const char* cmd = NULL;
- int ret;
+ int ret = -EINVAL;
switch (request)
{
@@ -171,6 +171,17 @@ long sys_ptrace(int request, int pid, void* addr, int data)
// return ret;
cmd = "PT_THUPDATE"; break;
+132
View File
@@ -0,0 +1,132 @@
"""Compile actual patched syscall bodies with inert RPC stubs; never start a server."""
import json
from pathlib import Path
import re
import shutil
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
def function(source, name):
start = re.search(r'^[\w* ]+\b' + re.escape(name) + r'\([^;]*?\)\s*\{', source, re.M).start()
opening = source.index('{', start)
depth = 1
end = opening + 1
while depth:
depth += (source[end] == '{') - (source[end] == '}')
end += 1
return source[start:end] + '\n'
@unittest.skipUnless(shutil.which('cc') and shutil.which('patch'), 'C compiler and patch required')
class CompatibilityChecks(unittest.TestCase):
def patched(self, name, path):
component = next(c for c in json.loads((ROOT / 'upstream/manifest.json').read_text())['components']
if c['patch'] == 'patches/' + name + '.patch')
repo = ROOT / 'build/darling-src' / component['path']
if not (repo / '.git').exists():
self.skipTest('prepared Darling sources required')
original = subprocess.check_output(['git', '-C', str(repo), 'show', component['revision'] + ':' + path])
marker = 'diff --git a/' + path + ' b/' + path + '\n'
patch = (ROOT / component['patch']).read_text().split(marker, 1)[1].split('\ndiff --git ', 1)[0]
with tempfile.TemporaryDirectory() as folder:
target = Path(folder) / path
target.parent.mkdir(parents=True)
target.write_bytes(original)
subprocess.run(['patch', '--silent', '-p1', '-d', folder], input=marker + patch,
text=True, check=True, capture_output=True)
return target.read_text()
def run_c(self, source):
with tempfile.TemporaryDirectory() as folder:
file = Path(folder) / 'check.c'
file.write_text(source)
binary = Path(folder) / 'check'
result = subprocess.run(['cc', '-std=c11', '-O0', str(file), '-o', str(binary)],
capture_output=True, text=True)
self.assertEqual(result.returncode, 0, result.stderr)
result = subprocess.run([str(binary)], capture_output=True, text=True, timeout=10)
self.assertEqual(result.returncode, 0, result.stderr)
def test_unsupported_mach_calls_return_errors_and_initialize_outputs(self):
bodies = []
for path, names in {
'duct-tape/src/memory.c': ['mach_vm_page_info', 'mach_vm_page_query', 'mach_vm_page_range_query'],
'duct-tape/src/task.c': ['task_set_exc_guard_behavior', 'task_set_phys_footprint_limit'],
'duct-tape/src/stubs.c': ['PE_i_can_has_debugger'],
}.items():
source = self.patched('darlingserver', path)
bodies.extend(function(source, name) for name in names)
self.run_c('''
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>
typedef int kern_return_t;
typedef void *vm_map_t;
typedef void *vm_page_info_t;
typedef void *task_t;
typedef uint64_t mach_vm_address_t;
typedef uint64_t mach_vm_offset_t;
typedef uint64_t mach_vm_size_t;
typedef unsigned mach_msg_type_number_t;
typedef int vm_page_info_flavor_t;
typedef unsigned task_exc_guard_behavior_t;
#define KERN_NOT_SUPPORTED 46
#define dtape_stub_unsafe(...) abort()
''' + ''.join(bodies) + '''
int main(void) {
mach_msg_type_number_t count = 123;
assert(mach_vm_page_info(NULL, 0, 0, NULL, &count) == KERN_NOT_SUPPORTED && count == 0);
assert(mach_vm_page_info(NULL, 0, 0, NULL, NULL) == KERN_NOT_SUPPORTED);
int disposition = 123, refs = 123, old = 123;
assert(mach_vm_page_query(NULL, 0, &disposition, &refs) == KERN_NOT_SUPPORTED);
assert(disposition == 0 && refs == 0);
assert(mach_vm_page_query(NULL, 0, NULL, NULL) == KERN_NOT_SUPPORTED);
mach_vm_size_t rangeCount = 123;
assert(mach_vm_page_range_query(NULL, 0, 4096, 0, &rangeCount) == KERN_NOT_SUPPORTED && rangeCount == 0);
assert(mach_vm_page_range_query(NULL, 0, 4096, 0, NULL) == KERN_NOT_SUPPORTED);
assert(task_set_exc_guard_behavior(NULL, 0) == KERN_NOT_SUPPORTED);
assert(task_set_phys_footprint_limit(NULL, 100, &old) == KERN_NOT_SUPPORTED && old == 0);
assert(task_set_phys_footprint_limit(NULL, 100, NULL) == KERN_NOT_SUPPORTED);
uint32_t debugger = 123;
assert(PE_i_can_has_debugger(&debugger) == 0 && debugger == 0);
assert(PE_i_can_has_debugger(NULL) == 0);
}
''')
def test_ptrace_unknown_request_and_existing_requests(self):
base = 'darling/src/libsystem_kernel/emulation/'
source = self.patched('xnu', base + 'src/xnu_syscall/bsd/impl/misc/ptrace.c')
header = self.patched('xnu', base + 'include/xnu_syscall/bsd/impl/misc/ptrace.h')
self.run_c('''
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <signal.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#define __simple_printf(...) ((void)0)
#define __simple_kprintf(...) ((void)0)
#define __simple_abort() abort()
static int calls;
static int sys_getppid(void) { return 1; }
static int errno_linux_to_bsd(int value) { return value; }
static int dserver_rpc_set_tracer(int a, int b) { ++calls; return 0; }
static int sys_kill(int a, int b, int c) { ++calls; return 0; }
static int dserver_rpc_ptrace_sigexc(int a, bool b) { ++calls; return 0; }
static int dserver_rpc_tid_for_thread(void *a, int *b) { ++calls; *b = 1; return 0; }
static int dserver_rpc_ptrace_thupdate(int a, int b) { ++calls; return 0; }
''' + header + function(source, 'sys_ptrace') + '''
int main(void) {
for (int i = 0; i < 1000; ++i) assert(sys_ptrace(0x7fffffff, 0, NULL, 0) == -EINVAL);
assert(calls == 0);
assert(sys_ptrace(XNU_PT_ATTACH, 0, NULL, 0) == -ENOSYS && calls == 0);
assert(sys_ptrace(XNU_PT_DENY_ATTACH, 0, NULL, 0) == 0 && calls == 0);
assert(sys_ptrace(XNU_PT_PTRACE_ME, 0, NULL, 0) == 0 && calls == 1);
}
''')
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env python3
"""Build the Foundation version/memory probe against the prepared Darling SDK."""
from pathlib import Path
import json
import re
import shlex
import subprocess
root = Path(__file__).resolve().parent.parent
folder = root / 'build/darling-src/build/src/frameworks/CoreVideo'
sdk = json.loads((root / 'build/config.json').read_text())['sdk']
flags = {}
resource = subprocess.check_output(['clang', '-print-resource-dir'], text=True).strip()
for line in (folder / 'CMakeFiles/CoreVideo.dir/flags.make').read_text().splitlines():
if line.startswith('C_') and ' = ' in line:
key, value = line.split(' = ', 1)
flags[key] = [re.sub(r'/usr/lib/clang/\d+/include', resource + '/include', v) for v in shlex.split(value)]
obj = root / 'build/riot-process-info-check.o'
subprocess.run(['clang', *flags['C_DEFINES'], *flags['C_INCLUDES'], *flags['C_FLAGS'],
'-O2', '-c', str(root / 'tools/riot-process-info-check.m'), '-o', str(obj)], check=True)
subprocess.run(['clang', '-target', 'x86_64-apple-macosx11.0', '-fuse-ld=lld', '-nostdlib',
'-isysroot', sdk, str(obj), '-lSystem', '-lobjc',
'-framework', 'Foundation', '-framework', 'CoreFoundation',
'-o', str(root / 'build/riot-process-info-check')], check=True)
+44
View File
@@ -0,0 +1,44 @@
#import <Foundation/Foundation.h>
#import <CoreFoundation/CFPriv.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/sysctl.h>
// Numeric version queries must not depend on an overridable, localized string.
@interface LocalizedProcessInfo : NSProcessInfo
@end
@implementation LocalizedProcessInfo
- (NSString *)operatingSystemVersionString { return @"Localized operating system description"; }
@end
int main(void) {
@autoreleasepool {
NSDictionary *dictionary = (NSDictionary *)_CFCopySystemVersionDictionary();
NSString *expected = [dictionary objectForKey:@"FullVersionString"];
if (![expected length]) expected = [dictionary objectForKey:@"ProductVersion"];
if (!expected) expected = @"Unknown";
NSProcessInfo *process = [NSProcessInfo processInfo];
if (![[process operatingSystemVersionString] isEqualToString:expected]) {
fprintf(stderr, "FAIL: process version string omits the system version/build description\n");
return 1;
}
NSOperatingSystemVersion version = [process operatingSystemVersion];
NSOperatingSystemVersion localized = [[[LocalizedProcessInfo alloc] init] operatingSystemVersion];
if (version.majorVersion != localized.majorVersion ||
version.minorVersion != localized.minorVersion || version.patchVersion != localized.patchVersion) {
fprintf(stderr, "FAIL: numeric version depends on the localized description\n");
return 2;
}
uint64_t memory = 0;
size_t length = sizeof(memory);
if (sysctlbyname("hw.memsize", &memory, &length, NULL, 0) || memory == 0 ||
[process physicalMemory] != memory) {
fprintf(stderr, "FAIL: physicalMemory differs from 64-bit hw.memsize\n");
return 3;
}
[dictionary release];
printf("PROCESS_INFO version_build=1 localized_numeric=1 memory64=%llu pass=1\n",
(unsigned long long)memory);
}
return 0;
}