mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-07 13:39:55 +00:00
Merge pull request #5269 from Nixitov/feat/relinker-weak-imports
fix(tools): do not report weak imports as a title's current stop
This commit is contained in:
6 files changed
+55
-13
No files matched your search
@@ -112,6 +112,7 @@ struct CallRegistryEntry {
|
||||
FileByteOffset TargetOffset;
|
||||
std::vector<FileByteOffset> CallSites;
|
||||
bool CallSitesResolved;
|
||||
bool Weak = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -52,7 +52,8 @@ std::string CallRegistryWriter::WriteCallRegistry(const std::vector<CallRegistry
|
||||
}
|
||||
|
||||
out << "],\n";
|
||||
out << " \"callSitesResolved\": " << (e.CallSitesResolved ? "true" : "false") << "\n";
|
||||
out << " \"callSitesResolved\": " << (e.CallSitesResolved ? "true" : "false") << ",\n";
|
||||
out << " \"weak\": " << (e.Weak ? "true" : "false") << "\n";
|
||||
out << " }";
|
||||
if (i + 1 < entries.size()) out << ",";
|
||||
out << "\n";
|
||||
|
||||
@@ -525,6 +525,7 @@ RelinkResult RelinkerPipeline::Relink() {
|
||||
entry.TargetSection = ".got";
|
||||
entry.TargetOffset = ref.RelocationAddress;
|
||||
entry.CallSitesResolved = false;
|
||||
entry.Weak = ref.Weak;
|
||||
entries.push_back(std::move(entry));
|
||||
}
|
||||
|
||||
|
||||
@@ -35,9 +35,28 @@ def main():
|
||||
entries = json.loads(registry.read_text(encoding="utf-8"))
|
||||
expected = [{"nid": name, "library": "", "relocationType": "R_X86_64_GLOB_DAT",
|
||||
"relocationOffset": "0x700", "targetSection": ".got", "targetOffset": "0x300",
|
||||
"callSites": ["0x200"], "callSitesResolved": True}]
|
||||
"callSites": ["0x200"], "callSitesResolved": True, "weak": False}]
|
||||
if entries != expected:
|
||||
raise AssertionError((name, options, entries))
|
||||
strings = b"\0lib.so\0" + names[0].encode("utf-8") + b"\0"
|
||||
image = fixture(str_size=len(strings), table_bytes=strings)
|
||||
struct.pack_into("<H", image, 56, 6)
|
||||
struct.pack_into("<IIQQQQQQ", image, 64,
|
||||
1, 7, 0x200, 0x200, 0x200, len(image) - 0x200, len(image) - 0x200, 0x1000)
|
||||
for slot in (3, 4, 5):
|
||||
struct.pack_into("<IIQQQQQQ", image, 64 + slot * 56,
|
||||
0x6fffff01, 0, 0, 0, 0, 0, 0, 1)
|
||||
image[0x63C] = 0x20
|
||||
source = work / "weak.elf"
|
||||
source.write_bytes(image)
|
||||
output = work / "weak-windows.exe"
|
||||
result = subprocess.run([str(relinker), "--skip-sce-module", "--registry", "--windows",
|
||||
str(source), str(output)], capture_output=True, text=True, timeout=20)
|
||||
if result.returncode != 0 or not output.exists():
|
||||
raise AssertionError((result.returncode, result.stdout, result.stderr))
|
||||
entries = json.loads(output.with_suffix(".registry.json").read_text(encoding="utf-8"))
|
||||
if not entries or entries[0]["weak"] is not True:
|
||||
raise AssertionError(("weak", entries))
|
||||
print("Call registry JSON tests passed")
|
||||
|
||||
|
||||
|
||||
@@ -385,7 +385,7 @@ def read_registry(path):
|
||||
nid = entry["nid"].split("#", 1)[0]
|
||||
if not NID.fullmatch(nid):
|
||||
raise AuditError(f"{path}: entry {index} has an invalid NID {entry['nid']!r}")
|
||||
imports.append((nid, entry["library"]))
|
||||
imports.append((nid, entry["library"], bool(entry.get("weak", False))))
|
||||
return imports
|
||||
|
||||
|
||||
@@ -408,12 +408,13 @@ def classify(nid, library, providers, stubs, modules):
|
||||
|
||||
def audit(imports, exports, stubs, libraries, modules, module_imports=None, module_exports=None):
|
||||
records = {}
|
||||
for nid, library in imports:
|
||||
record = records.setdefault((nid, library), {"nid": nid, "library": library, "references": 0})
|
||||
for nid, library, weak in imports:
|
||||
record = records.setdefault((nid, library), {"nid": nid, "library": library, "references": 0, "weak": weak})
|
||||
record["references"] += 1
|
||||
record["weak"] = weak
|
||||
for module, (symbols, needed) in (module_imports or {}).items():
|
||||
for nid in symbols:
|
||||
record = records.setdefault((nid, None, module), {"nid": nid, "library": None, "module": module, "references": 0})
|
||||
record = records.setdefault((nid, None, module), {"nid": nid, "library": None, "module": module, "references": 0, "weak": False})
|
||||
record["references"] += 1
|
||||
for record in records.values():
|
||||
nid, library = record["nid"], record["library"]
|
||||
@@ -440,9 +441,11 @@ def audit(imports, exports, stubs, libraries, modules, module_imports=None, modu
|
||||
|
||||
|
||||
def current_stop(imports, records):
|
||||
"""The first import the loader cannot resolve, in registry order."""
|
||||
"""The first import the loader cannot resolve, in registry order; a weak import resolves to null instead."""
|
||||
classes = {(record["nid"], record["library"]): record for record in records}
|
||||
for nid, library in imports:
|
||||
for nid, library, weak in imports:
|
||||
if weak:
|
||||
continue
|
||||
record = classes.get((nid, library))
|
||||
if record is not None and record["class"] == "absent":
|
||||
return record
|
||||
|
||||
@@ -143,7 +143,13 @@ def write_source(root, library, text):
|
||||
|
||||
|
||||
def write_registry(path, entries, encoding="utf-8"):
|
||||
path.write_text(json.dumps([{"nid": nid, "library": library, "relocationType": "R_X86_64_GLOB_DAT"} for nid, library in entries]), encoding=encoding)
|
||||
records = []
|
||||
for entry in entries:
|
||||
record = {"nid": entry[0], "library": entry[1], "relocationType": "R_X86_64_GLOB_DAT"}
|
||||
if len(entry) > 2:
|
||||
record["weak"] = entry[2]
|
||||
records.append(record)
|
||||
path.write_text(json.dumps(records), encoding=encoding)
|
||||
|
||||
|
||||
class Workspace:
|
||||
@@ -485,6 +491,17 @@ class ImportAuditTests(unittest.TestCase):
|
||||
self.assertEqual(data["stops"], [{"registry": str(registry), "nid": None, "library": None, "name": None}])
|
||||
self.assertIn(f" {registry}: none", out)
|
||||
|
||||
def test_a_weak_unresolved_import_is_not_a_stop(self):
|
||||
work = self.work
|
||||
(work.libs / "libSceA.prx").write_bytes(exporting("AAAAAAAAAAA"))
|
||||
registry = work.root / "game.json"
|
||||
write_registry(registry, [("BBBBBBBBBBB", "libSceA.prx", True), ("CCCCCCCCCCC", "libSceA.prx")])
|
||||
result = work.root / "audit.json"
|
||||
code, out, err = work.run(registry, "--json", str(result))
|
||||
self.assertEqual((code, err), (1, ""))
|
||||
data = json.loads(result.read_text())
|
||||
self.assertEqual(data["stops"], [{"registry": str(registry), "nid": "CCCCCCCCCCC", "library": "libSceA.prx", "name": None}])
|
||||
|
||||
def test_removing_an_export_flips_the_class(self):
|
||||
work = self.work
|
||||
registry = work.root / "game.json"
|
||||
@@ -517,11 +534,11 @@ class ImportAuditTests(unittest.TestCase):
|
||||
nid = nid_names.compute_nid("sceTwinCall")
|
||||
stubs = import_audit.source_stubs(source)
|
||||
exports = {nid: ["libA.prx", "libB.prx"]}
|
||||
records, _ = import_audit.audit([(nid, "libB.prx")], {nid: ["libB.prx"]}, stubs, {"libB.prx"}, set())
|
||||
records, _ = import_audit.audit([(nid, "libB.prx", False)], {nid: ["libB.prx"]}, stubs, {"libB.prx"}, set())
|
||||
self.assertEqual((records[0]["class"], records[0]["name"]), ("stub", "sceTwinCall"))
|
||||
records, _ = import_audit.audit([(nid, "libA.prx")], {nid: ["libA.prx"]}, stubs, {"libA.prx"}, set())
|
||||
records, _ = import_audit.audit([(nid, "libA.prx", False)], {nid: ["libA.prx"]}, stubs, {"libA.prx"}, set())
|
||||
self.assertEqual((records[0]["class"], records[0]["name"]), ("implemented", None))
|
||||
records, _ = import_audit.audit([(nid, "libB.prx")], exports, stubs, {"libA.prx", "libB.prx"}, set())
|
||||
records, _ = import_audit.audit([(nid, "libB.prx", False)], exports, stubs, {"libA.prx", "libB.prx"}, set())
|
||||
self.assertEqual(records[0]["class"], "implemented")
|
||||
|
||||
def test_cmake_shared_library_preserves_stub_names_and_implemented_exports(self):
|
||||
@@ -889,7 +906,7 @@ class ImportAuditTests(unittest.TestCase):
|
||||
with self.assertRaisesRegex(import_audit.AuditError, r"entry 1 has an invalid NID 'bad\\x01nid'"):
|
||||
import_audit.read_registry(path)
|
||||
write_registry(path, [("AAAAAAAAAAA#A#B", "libSceA.prx")], encoding="utf-8-sig")
|
||||
self.assertEqual(import_audit.read_registry(path), [("AAAAAAAAAAA", "libSceA.prx")])
|
||||
self.assertEqual(import_audit.read_registry(path), [("AAAAAAAAAAA", "libSceA.prx", False)])
|
||||
|
||||
def test_names_never_download(self):
|
||||
work = self.work
|
||||
|
||||
Reference in new issue
Block a user