Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion core/relinker/cli/src/NativeModules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ NativeModules ReadModules(const std::filesystem::path& inputPath, const std::fil
const bool hasPlural = std::filesystem::exists(plural);
const bool hasPrx = std::filesystem::exists(prx);
if (hasSingular && hasPlural) throw Domain::RelinkerException("Both sce_module and sce_modules exist in the guest module parent directory");
if (!hasSingular && !hasPlural && !hasPrx) throw Domain::RelinkerException(missingModuleMessage);
if (!hasSingular && !hasPlural && !hasPrx && moduleDirectories.empty()) throw Domain::RelinkerException(missingModuleMessage);
std::vector<std::filesystem::path> directories;
if (hasSingular || hasPlural) directories.push_back(hasSingular ? singular : plural);
if (hasPrx) directories.push_back(prx);
Expand Down
26 changes: 24 additions & 2 deletions core/relinker/relinker/tests/test_guest_module_dir.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ def main():
with tempfile.TemporaryDirectory(prefix="anyps5-module-dir-") as directory:
work = Path(directory)

def convert(case, windows, options, executable=None, cwd=None):
(case / "sce_module").mkdir(parents=True, exist_ok=True)
def convert(case, windows, options, executable=None, cwd=None, standard=True):
case.mkdir(parents=True, exist_ok=True)
if standard:
(case / "sce_module").mkdir(exist_ok=True)
source = case / "input.elf"
source.write_bytes(main_fixture() if executable is None else executable)
output = case / ("output.exe" if windows else "output.elf")
Expand All @@ -49,6 +51,26 @@ def convert(case, windows, options, executable=None, cwd=None):
return result, output

for windows in (False, True):
for relative in (".", "Media/Plugins"):
case = work / f"{windows}-explicit-only-{relative == '.'}"
modules = case / relative
modules.mkdir(parents=True)
module = modules / "plugin.prx"
original = bytes(module_with_symbol(True))
module.write_bytes(original)
result, output = convert(case, windows, [], standard=False)
assert result.returncode == 2 and "was not found" in result.stderr, result.stderr
assert not output.exists()
result, output = convert(case, windows, ["--module-dir", relative], standard=False)
assert result.returncode == 0, (result.stdout, result.stderr)
artifact = case / "app0" / relative / "plugin.prx.guest.prx"
assert artifact.read_bytes().startswith(b"MZ" if windows else b"\x7fELF")
assert module.read_bytes() == original
assert not (case / "sce_module").exists()
for invalid in ("missing", "../outside"):
failed, _ = convert(case, windows, ["--module-dir", invalid], standard=False)
assert failed.returncode == 2, (invalid, failed.stdout, failed.stderr)

case = work / f"{windows}-plugins"
plugins = case / "Media" / "Plugins"
plugins.mkdir(parents=True)
Expand Down
4 changes: 3 additions & 1 deletion docs/user/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The online converter can save Linux output as an uncompressed folder in supporte

## Input and conversion

Use a clean ELF executable. Place its bundled ELF modules in `sce_module/`, `sce_modules/`, or `prx/` beside the input executable, or under the parent directory selected with `--sce-module-path <path>`. The option accepts absolute paths and paths relative to the current working directory; do not include the `sce_module` directory itself. Only files directly inside the module directories are processed. SELF/fSELF containers are skipped with a warning; unwrap them before relinking if they need to be included. `tools/unwrap_self.py <input.self> <output.elf>` does that for a decrypted container: it places the segments at the offsets of the SELF segment records and restores `p_offset % p_align == p_vaddr % p_align`, and it refuses a container whose load segments all look encrypted. The command-line relinker skips an ELF module as a backup copy, with a message, when its file name extends the name of another ELF module in the same directory and both have the same SONAME, such as a dumper's `libX.prx.esbak` or `libX.prxbak` beside an ELF `libX.prx`; a single backup beside a SELF container is still relinked. `prx/` can coexist with either `sce_module/` or `sce_modules/`. Both `sce_module/` and `sce_modules/` present, or all three absent, is an error.
Use a clean ELF executable. Place its bundled ELF modules in `sce_module/`, `sce_modules/`, or `prx/` beside the input executable, or under the parent directory selected with `--sce-module-path <path>`. The option accepts absolute paths and paths relative to the current working directory; do not include the `sce_module` directory itself. Only files directly inside the module directories are processed. SELF/fSELF containers are skipped with a warning; unwrap them before relinking if they need to be included. `tools/unwrap_self.py <input.self> <output.elf>` does that for a decrypted container: it places the segments at the offsets of the SELF segment records and restores `p_offset % p_align == p_vaddr % p_align`, and it refuses a container whose load segments all look encrypted. The command-line relinker skips an ELF module as a backup copy, with a message, when its file name extends the name of another ELF module in the same directory and both have the same SONAME, such as a dumper's `libX.prx.esbak` or `libX.prxbak` beside an ELF `libX.prx`; a single backup beside a SELF container is still relinked. `prx/` can coexist with either `sce_module/` or `sce_modules/`. Both `sce_module/` and `sce_modules/` present is an error. All three absent is an error unless an explicit `--module-dir` is supplied.

```text
source/
Expand All @@ -21,6 +21,8 @@ source/
relinker [options] <input.elf> <output>
```

An explicit `--module-dir` also works when the module parent has no `sce_module`, `sce_modules` or `prx` directory. For a homebrew bundle with shared libraries beside its executable, use `--module-dir .`; no empty standard directory is required. Without an explicit module directory, the standard-directory requirement remains.

Linux output:

```sh
Expand Down
Loading