mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-27 00:28:14 +00:00
feat(relinker): log deferred Windows modules the executable imports from
Since #8162 a --module-dir module that the executable needs is deferred on Windows: the entry stub maps it and the title starts it with sceKernelLoadStartModule. A module the executable imports from but the title never loads gets no module_start and no initializers, silently. The entry stub now prints one line at startup for each deferred guest module that an import of the executable names. Guest module bytes are unchanged. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
cecb540a63
commit
960863f0e2
3 files changed
+25
-1
No files matched your search
@@ -157,6 +157,7 @@ WindowsEntryStub WindowsEntryStubBuilder::Build(const std::uint32_t dataRva, con
|
||||
std::vector<std::uint32_t> searchCounts;
|
||||
std::vector<bool> platformTlsResolvers;
|
||||
std::vector<bool> imported(libraries.size());
|
||||
std::vector<bool> executableImports(guestModules.size());
|
||||
const bool unscopedImports = std::any_of(imports.begin(), imports.end(), [](const auto& import) { return import.Library.empty(); });
|
||||
for (const auto& import : imports) {
|
||||
const auto library = Domain::NeededLibraryName(import.Library);
|
||||
@@ -167,6 +168,7 @@ WindowsEntryStub WindowsEntryStubBuilder::Build(const std::uint32_t dataRva, con
|
||||
const bool guestName = index < guestModules.size() &&
|
||||
std::find(guestModules[index].Names.begin(), guestModules[index].Names.end(), import.Library) != guestModules[index].Names.end();
|
||||
if (import.Library.empty() || name == library || guestName) order.push_back(CheckedRva(index));
|
||||
if (import.TargetModule < 0 && !import.Library.empty() && index < guestModules.size() && (name == library || guestName)) executableImports[index] = true;
|
||||
}
|
||||
if (order.empty()) throw Domain::RelinkerException("Import module is not a dependency: " + import.Library);
|
||||
const bool hostModule = !import.Library.empty() &&
|
||||
@@ -244,6 +246,11 @@ WindowsEntryStub WindowsEntryStubBuilder::Build(const std::uint32_t dataRva, con
|
||||
const auto searched = addString("Searched libraries:\n");
|
||||
const auto indent = addString(" ");
|
||||
const auto enteringElf = addString("Transferring control to ELF entry point\n");
|
||||
std::vector<std::uint32_t> deferredImports;
|
||||
for (std::size_t index = 0; index < guestModules.size(); ++index) {
|
||||
if (guestModules[index].DeferInitialization && executableImports[index])
|
||||
deferredImports.push_back(addString("Deferred module " + guestModules[index].Path + ": the executable imports from it, but it starts only when the title loads it\n"));
|
||||
}
|
||||
std::vector<std::uint32_t> resolvedPaths;
|
||||
for (std::size_t index = 0; index < libraries.size(); ++index)
|
||||
resolvedPaths.push_back(reserve(PathCapacity));
|
||||
@@ -712,6 +719,7 @@ WindowsEntryStub WindowsEntryStubBuilder::Build(const std::uint32_t dataRva, con
|
||||
code.Emit({0x85, 0xc0});
|
||||
unresolvedBranches.push_back(code.Branch({0x0f, 0x85}));
|
||||
}
|
||||
for (const auto message : deferredImports) writeString(message);
|
||||
guestStartup.Initialize(code, guestModules, handles);
|
||||
writeString(enteringElf);
|
||||
code.Rip({0x48, 0x8d, 0x0d}, shellLibrary);
|
||||
|
||||
@@ -10,6 +10,7 @@ from test_guest_intel_trampolines import main_fixture, pe_sections
|
||||
from test_guest_module_directories import module_with_symbol, needed_libraries
|
||||
from test_guest_needed_modules import executable_with_needed, module_with_needed
|
||||
from test_macos_output import MachO
|
||||
from test_windows_import_modules import executable, provider
|
||||
|
||||
|
||||
def pe_export_names(data):
|
||||
@@ -199,6 +200,21 @@ def main():
|
||||
else:
|
||||
assert "$ORIGIN/app0/Media/Modules/needed.prx.guest.prx" in needed_libraries(output.read_bytes()), needed_libraries(output.read_bytes())
|
||||
|
||||
for directory, deferred in (("Media/Modules", True), ("sce_module", False)):
|
||||
case = work / f"{windows}-imported-{deferred}"
|
||||
(case / directory).mkdir(parents=True)
|
||||
(case / directory / "needed.prx").write_bytes(provider(42))
|
||||
result, output = convert(case, windows, ["--module-dir", directory] if deferred else [], executable("needed.prx"))
|
||||
assert result.returncode == 0, (result.stdout, result.stderr)
|
||||
line = f"Deferred module app0/{directory}/needed.prx.guest.prx: the executable imports from it, but it starts only when the title loads it\n"
|
||||
if windows:
|
||||
assert (line.encode() in output.read_bytes()) == deferred, directory
|
||||
if os.name == "nt":
|
||||
run = subprocess.run([str(output)], capture_output=True, text=True, timeout=30)
|
||||
assert run.returncode == 42 and (line in run.stdout) == deferred, (directory, run.returncode, run.stdout, run.stderr)
|
||||
else:
|
||||
assert f"$ORIGIN/app0/{directory}/needed.prx.guest.prx" in needed_libraries(output.read_bytes()), needed_libraries(output.read_bytes())
|
||||
|
||||
case = work / f"{windows}-root"
|
||||
case.mkdir()
|
||||
(case / "root.prx").write_bytes(module_with_symbol(True))
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
- [Executable guest imports](../../../core/relinker/relinker/src/guest/GuestModuleBuilder.cpp) - on Linux and macOS, a relocated executable import resolved to one guest provider requires that module and its dependencies at startup, even when it was discovered through `--module-dir` without a DT_NEEDED entry. Unreferenced symbols and no-op relocations do not require an otherwise deferred module. Console module-start ordering is unmeasured.
|
||||
|
||||
- [Runtime-loaded modules](../../../core/relinker/relinker/src/guest/GuestModuleBuilder.cpp) (`--module-dir`) that another module needs are initialized at startup with no start arguments, before the module that needs them is loaded; the console is assumed to start them when that module is loaded.
|
||||
- [Runtime-loaded modules the executable needs](../../../core/relinker/relinker/src/guest/GuestModuleBuilder.cpp) (`--module-dir`) are mapped at startup on Windows and started when the title loads them with `sceKernelLoadStartModule`, with their start arguments, as a Unity player loads `mono-ps4.sprx`. A title that calls such a module before loading it finds its initializers not run; whether the console starts it earlier is not measured. A module the executable only imports from and the title never loads itself gets no `module_start` and no initializers at all under this deferral, and nothing reports it. Linux and macOS still start it at startup.
|
||||
- [Runtime-loaded modules the executable needs](../../../core/relinker/relinker/src/guest/GuestModuleBuilder.cpp) (`--module-dir`) are mapped at startup on Windows and started when the title loads them with `sceKernelLoadStartModule`, with their start arguments, as a Unity player loads `mono-ps4.sprx`. A title that calls such a module before loading it finds its initializers not run; whether the console starts it earlier is not measured. A module the executable only imports from and the title never loads itself gets no `module_start` and no initializers at all under this deferral. The [entry stub](../../../core/relinker/elfpatcher/src/windows/WindowsEntryStubBuilder.cpp) prints `Deferred module <path>` at startup for each such module that an import of the executable names, whether or not the title loads it later; an import without a module name is not reported. Linux and macOS still start it at startup.
|
||||
- The executable file that [relinker](../../../core/relinker/elfpatcher/src/windows/WindowsPeWriter.cpp) generates opens a console when launched, unless it is relinked with `--windows-gui`, which selects the GUI subsystem. A GUI build started from Explorer has no standard output or error handle; the [entry stub](../../../core/relinker/elfpatcher/src/windows/WindowsEntryStubBuilder.cpp) gives each missing one `anyps5.log` beside the executable, replacing the previous run's file (#3282). Errors about the executable path or about waiting for the previous LoadExec process are written before the log is opened and are lost when the standard error handle is missing. If the log cannot be created, output to the missing handles is lost.
|
||||
- [Guest import registries](../../../core/relinker/relinker/src/guest/GuestModuleBuilder.cpp) - declared host imports of bundled modules are recorded with their module name and relocation target, even when another guest module exports the same stripped NID. No-op relocations are omitted. Registries describe import bindings, not implementation correctness or runtime reachability.
|
||||
- Converted Windows main executables [omit `DYNAMIC_BASE`](../../../core/relinker/elfpatcher/src/windows/WindowsPeWriter.cpp) by default, not only with `--windows-fixed-base`: on the console the executable lies inside the 40-bit window that the title's own `sceLibcMspaceCreate` checks (#3043). Relocations are kept, so if `0x140000000` is taken Windows relocates the image, possibly above that window. Verified only on Windows 10.
|
||||
|
||||
Reference in new issue
Block a user