From 633c129a4f71aaa30c7806b286c3c9096f62b722 Mon Sep 17 00:00:00 2001 From: lucannez64 <58551020+lucannez64@users.noreply.github.com> Date: Sat, 17 Oct 2026 23:04:23 +0200 Subject: [PATCH] Extract runtime compatibility fixes from PR 41 --- docs/reviews/pr41-adaptation.md | 82 +++++++++++++++ patches/darlingserver.patch | 96 ++++++++++++++++-- patches/foundation.patch | 59 ++++++++++- patches/xnu.patch | 11 ++- tests/test_runtime_compatibility.py | 132 +++++++++++++++++++++++++ tools/build-riot-process-info-check.py | 24 +++++ tools/riot-process-info-check.m | 44 +++++++++ 7 files changed, 436 insertions(+), 12 deletions(-) create mode 100644 docs/reviews/pr41-adaptation.md create mode 100644 tests/test_runtime_compatibility.py create mode 100644 tools/build-riot-process-info-check.py create mode 100644 tools/riot-process-info-check.m diff --git a/docs/reviews/pr41-adaptation.md b/docs/reviews/pr41-adaptation.md new file mode 100644 index 0000000..1483097 --- /dev/null +++ b/docs/reviews/pr41-adaptation.md @@ -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. diff --git a/patches/darlingserver.patch b/patches/darlingserver.patch index 93505c0..5a5e0c3 100644 --- a/patches/darlingserver.patch +++ b/patches/darlingserver.patch @@ -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 }; diff --git a/patches/foundation.patch b/patches/foundation.patch index 701827e..4fdb8b6 100644 --- a/patches/foundation.patch +++ b/patches/foundation.patch @@ -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 diff --git a/patches/xnu.patch b/patches/xnu.patch index 2132025..265cef6 100644 --- a/patches/xnu.patch +++ b/patches/xnu.patch @@ -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; diff --git a/tests/test_runtime_compatibility.py b/tests/test_runtime_compatibility.py new file mode 100644 index 0000000..721c4ae --- /dev/null +++ b/tests/test_runtime_compatibility.py @@ -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 +#include +#include +#include +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 +#include +#include +#include +#include +#include +#include +#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); +} +''') diff --git a/tools/build-riot-process-info-check.py b/tools/build-riot-process-info-check.py new file mode 100644 index 0000000..62ce739 --- /dev/null +++ b/tools/build-riot-process-info-check.py @@ -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) diff --git a/tools/riot-process-info-check.m b/tools/riot-process-info-check.m new file mode 100644 index 0000000..7fb3160 --- /dev/null +++ b/tools/riot-process-info-check.m @@ -0,0 +1,44 @@ +#import +#import +#include +#include +#include + +// 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; +}