Guard Linux speech runtime dependency closure and relocatable lookup
This commit is contained in:
1 parent
210f2b0136
commit
e71cace91f
5 files changed
+80
-1
No files matched your search
@@ -26,3 +26,28 @@ Snapshot: `/home/karl/.cache/searchi-validation/linux-source-9bc7221`.
|
||||
This closes a client portability check only. It does not prove the Linux native
|
||||
speech worker, model setup, runtime packaging, recognition quality, desktop UI
|
||||
or GPU operation. Published Linux downloads remain version 0.1.4.
|
||||
|
||||
## Native runtime preparation
|
||||
|
||||
A normal auto runtime build from that immutable snapshot is running in
|
||||
`/home/karl/.cache/searchi-validation/speech-auto-016`; it includes Vulkan
|
||||
compilation and does not run GPU inference. Do not restart it merely because
|
||||
an observation times out. Its full compilation and relocation remain unchecked.
|
||||
|
||||
Inspection exposed an unverified release path: speech packaging flattened shared
|
||||
libraries without checking ELF dependency closure or their build-tree lookup
|
||||
paths. The builder now checks every staged ELF for origin-only RUNPATH and
|
||||
declared dependencies, allowing a system Vulkan loader only for the dynamic
|
||||
Vulkan module. Three builder tests passed, including rejection of build-tree
|
||||
paths, missing libraries and a Vulkan dependency on the CPU worker.
|
||||
|
||||
The CMake configuration now gives Linux shared targets their own `$ORIGIN`
|
||||
RUNPATH; this is required because executable RUNPATH is not transitive.
|
||||
Actual configuration and a separate native `ggml-base` build passed in
|
||||
`speech-rpath-check-016`. `readelf` verified that the resulting library has
|
||||
RUNPATH `$ORIGIN`; generated executable link flags use the same path.
|
||||
This narrow library check does not establish complete runtime relocation.
|
||||
The running original snapshot does not contain this correction. Its output
|
||||
must not be released as corrected evidence. Next: wait for its terminal result,
|
||||
inspect the complete ELF closure, then validate a corrected normal runtime
|
||||
outside the build tree with `LD_LIBRARY_PATH` unset and forced CPU recognition.
|
||||
@@ -33,6 +33,12 @@ else()
|
||||
set(GGML_METAL OFF CACHE BOOL "" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
if(UNIX AND NOT APPLE AND NOT SEARCHI_INTERNAL_CPU)
|
||||
# The staged worker and every dynamic backend live together. RUNPATH is
|
||||
# non-transitive, so each shared target needs its own relocatable lookup.
|
||||
set(CMAKE_INSTALL_RPATH "$ORIGIN")
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH ON)
|
||||
endif()
|
||||
add_subdirectory("${WHISPER_SOURCE}" whisper)
|
||||
target_compile_features(whisper PRIVATE cxx_std_17)
|
||||
add_executable(searchi-speech-worker worker.cpp)
|
||||
@@ -44,6 +50,8 @@ endif()
|
||||
if(MSVC)
|
||||
target_compile_options(searchi-speech-worker PRIVATE /utf-8)
|
||||
endif()
|
||||
if(UNIX)
|
||||
if(APPLE)
|
||||
set_target_properties(searchi-speech-worker PROPERTIES BUILD_RPATH "$ORIGIN;@loader_path" INSTALL_RPATH "$ORIGIN;@loader_path")
|
||||
elseif(UNIX)
|
||||
set_target_properties(searchi-speech-worker PROPERTIES BUILD_RPATH "$ORIGIN" INSTALL_RPATH "$ORIGIN")
|
||||
endif()
|
||||
@@ -20,6 +20,11 @@ inventory files before launch and incorporates the inventory hash in its source
|
||||
completion fingerprint. A changed backend DLL therefore invalidates reuse.
|
||||
The builder compiles only; native Linux/macOS loading, full system-dependency
|
||||
closure, actual GPU speed/fallback and release packaging still require evidence.
|
||||
Normal Linux builds reject staged ELF files with build-tree RUNPATH or undeclared
|
||||
dependencies. Each shared target uses `$ORIGIN` for relocation; a system Vulkan
|
||||
loader is permitted only for the dynamically loaded Vulkan backend. The declared
|
||||
system C/C++ libraries remain a distribution compatibility requirement. A full
|
||||
relocated recognition test is still required after compilation.
|
||||
Windows auto builds reuse the verified Vulkan SDK/loader provisioning and selected
|
||||
Visual Studio CRT bundling helpers. The isolated runtime inventories its own
|
||||
`vulkan-1.dll`, full release CRT set and their redistribution notices. Shared helper
|
||||
|
||||
@@ -40,6 +40,24 @@ def sha(path):
|
||||
return result.hexdigest()
|
||||
|
||||
|
||||
def verify_linux_closure(directory):
|
||||
"""Reject build-tree lookup and undeclared ELF dependencies before release."""
|
||||
helper = shared_helper("build_runtime.py")
|
||||
directory = Path(directory)
|
||||
artifacts = [directory / "searchi-speech-worker"] + sorted(directory.glob("*.so*"))
|
||||
names = {path.name for path in artifacts}
|
||||
for path in artifacts:
|
||||
metadata = helper.elf_dynamic(path)
|
||||
if metadata.get("RPATH") or metadata.get("RUNPATH") != ["$ORIGIN"]:
|
||||
raise ValueError(f"Linux speech runtime requires origin-only RUNPATH: {path.name}")
|
||||
allowed = names | helper.LINUX_SYSTEM_LIBRARIES
|
||||
if path.name == "libggml-vulkan.so":
|
||||
allowed = allowed | {"libvulkan.so.1"}
|
||||
missing = set(metadata.get("NEEDED", [])) - allowed
|
||||
if missing:
|
||||
raise ValueError(f"Unpackaged Linux speech dependency for {path.name}: {sorted(missing)}")
|
||||
|
||||
|
||||
def validate_source(source):
|
||||
revision = subprocess.check_output(["git", "-C", str(source), "rev-parse", "HEAD"], text=True).strip()
|
||||
dirty = subprocess.check_output(["git", "-C", str(source), "status", "--porcelain"], text=True).strip()
|
||||
@@ -113,6 +131,8 @@ def main():
|
||||
raise ValueError("Ambiguous speech dependency basename")
|
||||
shutil.copy2(path, stage / path.name)
|
||||
records[path.name] = {"path": path.name, "bytes": path.stat().st_size, "sha256": digest}
|
||||
if args.device == "auto" and sys.platform.startswith("linux"):
|
||||
verify_linux_closure(stage)
|
||||
manifest = {"schema": 1, "upstream_commit": UPSTREAM, "device": args.device,
|
||||
"distribution": args.device == "auto", "source_hashes": source_hashes,
|
||||
"files": list(records.values()), "native_os_gpu_checks": "unexecuted"}
|
||||
|
||||
@@ -28,6 +28,27 @@ class BuildContract(unittest.TestCase):
|
||||
self.assertTrue(callable(build.shared_helper("build_runtime.py").bundle_windows_crt))
|
||||
self.assertTrue(callable(build.shared_helper("provision_vulkan.py").provision))
|
||||
|
||||
def test_linux_closure_rejects_build_paths_and_missing_dependencies(self):
|
||||
from types import SimpleNamespace
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
for name in ['searchi-speech-worker', 'libwhisper.so.1', 'libggml-vulkan.so']:
|
||||
(root / name).write_bytes(b'fixture')
|
||||
metadata = {'RUNPATH': ['$ORIGIN'], 'NEEDED': ['libwhisper.so.1', 'libc.so.6']}
|
||||
helper = SimpleNamespace(elf_dynamic=lambda _: metadata, LINUX_SYSTEM_LIBRARIES={'libc.so.6'})
|
||||
with patch.object(build, 'shared_helper', return_value=helper):
|
||||
build.verify_linux_closure(root)
|
||||
metadata['RUNPATH'] = ['/tmp/build']
|
||||
with self.assertRaisesRegex(ValueError, 'origin-only'):
|
||||
build.verify_linux_closure(root)
|
||||
metadata['RUNPATH'] = ['$ORIGIN']
|
||||
metadata['NEEDED'] = ['libmissing.so']
|
||||
with self.assertRaisesRegex(ValueError, 'Unpackaged'):
|
||||
build.verify_linux_closure(root)
|
||||
metadata['NEEDED'] = ['libvulkan.so.1']
|
||||
with self.assertRaisesRegex(ValueError, 'Unpackaged'):
|
||||
build.verify_linux_closure(root)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user