Prepare inspected static Metal runtime inventory for macOS staging
This commit is contained in:
1 parent
07bbcb76e8
commit
4a7c4acdcb
11 files changed
+236
-13
No files matched your search
@@ -0,0 +1,69 @@
|
||||
"""Inspect a static Metal worker using Apple's tools, without executing it."""
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
MAGIC = {b"\xcf\xfa\xed\xfe", b"\xfe\xed\xfa\xcf", b"\xca\xfe\xba\xbe",
|
||||
b"\xbe\xba\xfe\xca", b"\xca\xfe\xba\xbf", b"\xbf\xba\xfe\xca"}
|
||||
SYSTEM_LIBRARIES = {"/usr/lib/libSystem.B.dylib", "/usr/lib/libc++.1.dylib",
|
||||
"/usr/lib/libc++abi.dylib", "/usr/lib/libobjc.A.dylib"}
|
||||
FRAMEWORKS = {"Foundation", "CoreFoundation", "Metal", "MetalKit", "Accelerate"}
|
||||
|
||||
|
||||
def system_dependency(name):
|
||||
if name in SYSTEM_LIBRARIES:
|
||||
return True
|
||||
return any(re.fullmatch(rf"/System/Library/Frameworks/{framework}\.framework/Versions/[A-Z]/{framework}", name)
|
||||
for framework in FRAMEWORKS)
|
||||
|
||||
|
||||
def validate_inventory(manifest, worker):
|
||||
with Path(worker).open("rb") as stream:
|
||||
if stream.read(4) not in MAGIC:
|
||||
raise RuntimeError("macOS worker is not a Mach-O artifact")
|
||||
arches = manifest.get("architectures")
|
||||
dependencies = manifest.get("system_libraries")
|
||||
if (manifest.get("platform") != "macos" or manifest.get("layout") != "static-metal-embedded" or manifest.get("libraries") != []
|
||||
or manifest.get("metal_embedded") is not True
|
||||
or not isinstance(arches, list) or not 1 <= len(arches) <= 2
|
||||
or not all(isinstance(arch, str) for arch in arches)
|
||||
or len(set(arches)) != len(arches) or not set(arches) <= {"arm64", "x86_64"}
|
||||
or not isinstance(dependencies, list) or not 1 <= len(dependencies) <= 32
|
||||
or not all(isinstance(name, str) and system_dependency(name) for name in dependencies)
|
||||
or not any("/Metal.framework/" in name for name in dependencies)):
|
||||
raise RuntimeError("Invalid static Metal runtime inventory")
|
||||
|
||||
|
||||
def inspect(worker):
|
||||
worker = Path(worker)
|
||||
|
||||
def tool(arguments):
|
||||
try:
|
||||
output = subprocess.check_output(arguments, text=True, timeout=30)
|
||||
except FileNotFoundError as error:
|
||||
raise RuntimeError("macOS runtime inspection requires Apple lipo/otool") from error
|
||||
if len(output.encode("utf-8")) > 65536:
|
||||
raise RuntimeError("macOS metadata exceeds inspection limit")
|
||||
return output
|
||||
|
||||
arches = tool(["lipo", "-archs", str(worker)]).split()
|
||||
dependencies = []
|
||||
for line in tool(["otool", "-L", str(worker)]).splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
if line == f"{worker}:" or line in {f"{worker} (architecture {arch}):" for arch in arches}:
|
||||
continue
|
||||
match = re.fullmatch(r"\s+(.+?) \(compatibility version [0-9.]+, current version [0-9.]+\)", line)
|
||||
if not match:
|
||||
raise RuntimeError("Unrecognized Mach-O dependency metadata")
|
||||
if match[1] not in dependencies:
|
||||
dependencies.append(match[1])
|
||||
sections = tool(["otool", "-l", str(worker)])
|
||||
sizes = re.findall(r"sectname\s+__ggml_metallib\s+segname\s+__DATA\s+addr\s+0x[0-9a-fA-F]+\s+size\s+(0x[0-9a-fA-F]+)", sections)
|
||||
if len(sizes) != len(arches) or not all(int(size, 16) > 0 for size in sizes):
|
||||
raise RuntimeError("Each Mach-O architecture must contain nonempty embedded Metal kernels")
|
||||
result = {"platform": "macos", "layout": "static-metal-embedded",
|
||||
"architectures": arches, "system_libraries": sorted(dependencies),
|
||||
"metal_embedded": True, "libraries": []}
|
||||
validate_inventory(result, worker)
|
||||
return result
|
||||
Reference in new issue
Block a user