mirror of
https://github.com/mihawk-99/PS5_RetroArch.git
synced 2026-10-18 23:09:29 -05:00
Build pinned source and core-info with this project's SDK, validate the ELF,
and stage both metadata paths for existing saved configs. Use a deterministic
build ID and require exact core readback; refresh stale metadata caches.
Verified: tools/verify.sh (five host gates, 45 tests, 18 evidence records).
Artifact: evidence/fceumm-build/{abi,capture,expectation}.json.
Target: tools/run-title.sh --no-build --watch 120, manual core/game selection.
Metadata discovery and exact core bytes pass; dynamic core open fails, then
content startup crashes in frontend vulkan_alive(NULL). This commit verifies
build tooling only, not playable FCEUmm. Park native loader/recovery acceptance
in parked/native-core-loading/README.md. PS5_Vulkan is unchanged.
84 lines
3.4 KiB
Python
84 lines
3.4 KiB
Python
"""Exercise the core gate with real ELFs, including incompatible artifacts."""
|
|
import importlib.util
|
|
from pathlib import Path
|
|
import shutil
|
|
import struct
|
|
import subprocess
|
|
import tempfile
|
|
import unittest
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
spec = importlib.util.spec_from_file_location('core_abi', ROOT / 'tools/check-core.py')
|
|
core_abi = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(core_abi)
|
|
|
|
|
|
class CoreABI(unittest.TestCase):
|
|
def setUp(self):
|
|
self.tmp = tempfile.TemporaryDirectory()
|
|
self.addCleanup(self.tmp.cleanup)
|
|
self.folder = Path(self.tmp.name)
|
|
self.cc = shutil.which('clang') or shutil.which('cc')
|
|
if not self.cc:
|
|
self.skipTest('host C compiler unavailable')
|
|
|
|
def build(self, soname='libkernel_web.sprx', omit=None):
|
|
stub = self.folder / 'stub.so'
|
|
subprocess.run([self.cc, '-x', 'c', '-', '-shared', '-nostdlib',
|
|
'-Wl,-soname,' + soname, '-o', str(stub)],
|
|
input='void stub(void) {}', text=True, check=True,
|
|
capture_output=True)
|
|
path = self.folder / 'core.so'
|
|
source = '\n'.join('void ' + name + '(void) {}'
|
|
for name in sorted(core_abi.REQUIRED) if name != omit)
|
|
subprocess.run([self.cc, '-x', 'c', '-', '-x', 'none', str(stub),
|
|
'-shared', '-nostdlib', '-Wl,-z,max-page-size=16384',
|
|
'-o', str(path)], input=source, text=True, check=True,
|
|
capture_output=True)
|
|
data = bytearray(path.read_bytes())
|
|
data[7] = 9 # Test fixture only: mark its ELF OSABI as FreeBSD.
|
|
path.write_bytes(data)
|
|
return path
|
|
|
|
def test_valid_shape_does_not_claim_console_verification(self):
|
|
report = core_abi.inspect(self.build())
|
|
self.assertTrue(report['abi_passed'])
|
|
self.assertFalse(report['console_loading_verified'])
|
|
self.assertEqual(len(report['libretro_exports']), 25)
|
|
|
|
def test_reject_host_abi(self):
|
|
path = self.build()
|
|
data = bytearray(path.read_bytes())
|
|
data[7] = 0
|
|
path.write_bytes(data)
|
|
with self.assertRaisesRegex(ValueError, 'not a host library'):
|
|
core_abi.inspect(path)
|
|
|
|
def test_reject_payload_kernel_import(self):
|
|
with self.assertRaisesRegex(ValueError, 'unexpected PS5 core imports'):
|
|
core_abi.inspect(self.build(soname='libkernel_sys.sprx'))
|
|
|
|
def test_reject_missing_callback(self):
|
|
with self.assertRaisesRegex(ValueError, 'retro_run'):
|
|
core_abi.inspect(self.build(omit='retro_run'))
|
|
|
|
def test_reject_four_kib_segment(self):
|
|
path = self.build()
|
|
data = bytearray(path.read_bytes())
|
|
phoff = struct.unpack_from('<Q', data, 32)[0]
|
|
size, count = struct.unpack_from('<HH', data, 54)
|
|
for i in range(count):
|
|
offset = phoff + i * size
|
|
if struct.unpack_from('<I', data, offset)[0] == 1:
|
|
struct.pack_into('<Q', data, offset + 48, 4096)
|
|
break
|
|
path.write_bytes(data)
|
|
with self.assertRaisesRegex(ValueError, '16 KiB'):
|
|
core_abi.inspect(path)
|
|
|
|
def test_reject_truncated_header(self):
|
|
path = self.folder / 'truncated.so'
|
|
path.write_bytes(b'\x7fELF')
|
|
with self.assertRaisesRegex(ValueError, 'expected ELF64'):
|
|
core_abi.inspect(path)
|