From 7991f0130e04493d3e0ac392782636f9602e9918 Mon Sep 17 00:00:00 2001 From: eliasbruno124-dev Date: Mon, 19 Oct 2026 23:05:59 +0200 Subject: [PATCH] fix(tools): resolve unscoped registry imports from guest modules --- tools/import_audit.py | 4 +-- tools/tests/test_import_audit.py | 44 ++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/tools/import_audit.py b/tools/import_audit.py index 4887d6d2887..eb35ea35953 100644 --- a/tools/import_audit.py +++ b/tools/import_audit.py @@ -557,13 +557,13 @@ def audit(imports, exports, stubs, libraries, modules, module_imports=None, modu for record in records.values(): nid, library = record["nid"], record["library"] providers = exports.get(nid, []) - if library is None: + if not library: providers = sorted(set(providers) | set((module_exports or {}).get(nid, []))) resolved_nid = nid if not providers: resolved_nid = nid_names.compute_nid(nid) providers = exports.get(resolved_nid, []) - if library is None: + if not library: providers = sorted(set(providers) | set((module_exports or {}).get(resolved_nid, []))) kind = classify(resolved_nid, library, providers, stubs, modules) record["class"] = kind diff --git a/tools/tests/test_import_audit.py b/tools/tests/test_import_audit.py index 98db7f5c7cd..5997280cfe6 100644 --- a/tools/tests/test_import_audit.py +++ b/tools/tests/test_import_audit.py @@ -890,6 +890,50 @@ def test_module_export_suffix_is_stripped_with_and_without_sections(self): record, = json.loads(result.read_text())["imports"] self.assertEqual((record["nid"], record["class"], record["providers"]), (nid, "absent", [])) + def test_unscoped_registry_imports_use_guest_module_exports(self): + work = self.work + (work.libs / "libc.prx").write_bytes(exporting(nid_names.compute_nid("strlen"))) + provider = work.modules / "provider.prx.guest.prx" + registry, result = work.root / "game.registry.json", work.root / "audit.json" + names = [MESSAGE_NID + "#export#module", "strlen"] + symbols = [(name, 1, GLOBAL_FUNCTION) for name in names] + entries = [(MESSAGE_NID + "#import#module", ""), (MESSAGE_NID, ""), + (MESSAGE_NAME, ""), ("strlen", "")] + for layout, image in (("sections", build_elf(symbols)), + ("sysv", build_sectionless_elf(symbols)), + ("gnu", build_sectionless_elf(symbols, gnu_hash=True)), + ("pe", build_pe(sorted(names)))): + for unresolved in (False, True): + with self.subTest(layout=layout, unresolved=unresolved): + provider.write_bytes(image) + write_registry(registry, entries + ([("missing_function", "")] if unresolved else [])) + code, _, err = work.run(registry, "--json", str(result)) + self.assertEqual((code, err), (int(unresolved), "")) + data = json.loads(result.read_text()) + self.assertEqual(data["references"], 4 + int(unresolved)) + self.assertEqual(data["unique_by_class"], + {"implemented": 3, "stub": 0, "absent": int(unresolved), "module": 0}) + records = {record["nid"]: record for record in data["imports"]} + for symbol in (MESSAGE_NID, MESSAGE_NAME, "strlen"): + self.assertEqual(records[symbol]["providers"], [provider.name]) + self.assertFalse(records[symbol]["library_mismatch"]) + self.assertEqual(records[MESSAGE_NID]["references"], 2) + self.assertEqual(data["missing_libraries"], {}) + self.assertEqual(data["stops"][0]["nid"], "missing_function" if unresolved else None) + + def test_named_registry_import_does_not_use_unrelated_guest_export(self): + work = self.work + (work.libs / "libSceExpected.prx").write_bytes(exporting()) + (work.modules / "other.prx").write_bytes(exporting(MESSAGE_NID)) + registry, result = work.root / "game.registry.json", work.root / "audit.json" + write_registry(registry, [(MESSAGE_NID, "libSceExpected.prx")]) + code, _, err = work.run(registry, "--json", str(result)) + self.assertEqual((code, err), (1, "")) + data = json.loads(result.read_text()) + record, = data["imports"] + self.assertEqual((record["class"], record["providers"]), ("absent", [])) + self.assertEqual(data["stops"][0]["nid"], MESSAGE_NID) + def test_clean_audit_exits_zero_and_empty_registry_is_empty(self): work = self.work (work.libs / "libSceA.prx").write_bytes(exporting("AAAAAAAAAAA"))