Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions tools/import_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -722,9 +722,10 @@ def audit(imports, exports, stubs, libraries, modules, module_imports=None, modu

def system_library(name):
if name:
for suffix in (".sprx", ".suprx", ".debug_prx"):
if name.endswith(suffix):
normalized = name.removesuffix(suffix) + ".prx"
folded = name.casefold()
for suffix in (".sprx", ".suprx", ".debug_prx", ".prx"):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should fix: folding .SPRX, .SUPRX and .DEBUG_PRX makes the audit report a library as available that the Windows output can't load. The relinker maps those suffixes with a case-sensitive ends_with in Domain::NeededLibraryName (core/relinker/domain/include/domain/ImportModule.hpp:14-23): libscea.sprx becomes libscea.prx, but libscea.SPRX stays libscea.SPRX. That is the name the Windows output loads (WindowsImportBuilder.cpp:47, LoadLibraryExW in WindowsDependencyStubBuilder.cpp:251).

With only LibSceA.prx in a directory, LoadLibrary finds it as libscea.prx, libscea.PRX and LIBSCEA.PRX, and fails with error 126 for libscea.SPRX, libscea.SUPRX and libscea.DEBUG_PRX. So main's exit 1 for libscea.SPRX was right, the example in What describes correct behaviour as a bug, and the subtests for .SPRX, .SUPRX and .DEBUG_PRX assert the wrong outcome.

Folding only .prx keeps the half that is right:

                if (folded if suffix == ".prx" else name).endswith(suffix):

That fails exactly those three subtests and passes everything else, so they would need to expect a missing library (or go). If uppercase console suffixes should load, NeededLibraryName has to fold them first, in its own PR.

if folded.endswith(suffix):
normalized = name[:-len(suffix)] + ".prx"
candidates = windows_names.get(normalized.casefold(), ())
if normalized in candidates:
return normalized
Expand Down
17 changes: 17 additions & 0 deletions tools/tests/test_import_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,23 @@ def test_windows_suffix_lookup_matches_provider_case_insensitively(self):
self.assertEqual(data["library_mismatch"], 0)
self.assertEqual(data["imports"][0]["providers"], ["LibSceA.prx"])

def test_windows_prx_suffix_lookup_folds_the_suffix_case(self):
work = self.work
(work.libs / "LibSceA.prx").write_bytes(build_pe(["AAAAAAAAAAA"]))
registry, result = work.root / "game.registry.json", work.root / "audit.json"
for suffix in (".sprx", ".SPRX", ".suprx", ".SUPRX", ".debug_prx", ".DEBUG_PRX", ".prx", ".PRX"):
with self.subTest(suffix=suffix):
name = "libscea" + suffix
(work.modules / "guest.so").write_bytes(build_elf([], [name]))
write_registry(registry, [("AAAAAAAAAAA", name)])
registry.with_name("game.needed.json").write_text(json.dumps([name]))
code, _, err = work.run(registry, "--strict-providers", "--json", str(result))
self.assertEqual((code, err), (0, ""))
data = json.loads(result.read_text())
self.assertEqual(data["missing_libraries"], {})
self.assertEqual(data["library_mismatch"], 0)
self.assertEqual(data["imports"][0]["providers"], ["LibSceA.prx"])

def test_elf_guest_dependencies_keep_literal_host_names(self):
work = self.work
(work.libs / "libSceA.prx").write_bytes(exporting("AAAAAAAAAAA"))
Expand Down
Loading