From 39bed4b9d7e7afd6a58361c8295cccd181029ec7 Mon Sep 17 00:00:00 2001 From: Borooooo <150533906+Borooooo@users.noreply.github.com> Date: Mon, 12 Oct 2026 18:50:50 +0200 Subject: [PATCH] fix(relinker): accept paths longer than 260 characters on Windows relinker.exe carried only MinGW's default manifest, so Windows kept MAX_PATH for it even with long paths enabled, and an input under a deep directory failed with 'Cannot open file'. Embed a manifest with longPathAware that keeps asInvoker and the Windows 10 supportedOS entry. --- core/relinker/CMakeLists.txt | 5 +++ core/relinker/relinker.manifest | 20 +++++++++ core/relinker/relinker.rc | 3 ++ .../relinker/tests/test_long_paths.py | 43 +++++++++++++++++++ 4 files changed, 71 insertions(+) create mode 100644 core/relinker/relinker.manifest create mode 100644 core/relinker/relinker.rc create mode 100644 core/relinker/relinker/tests/test_long_paths.py diff --git a/core/relinker/CMakeLists.txt b/core/relinker/CMakeLists.txt index 89709dc942..13a9b00b03 100644 --- a/core/relinker/CMakeLists.txt +++ b/core/relinker/CMakeLists.txt @@ -98,6 +98,8 @@ if(BUILD_TESTING AND WIN32) endif() if(WIN32) + target_sources(relinker PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/relinker.rc) + set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/relinker.rc PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/relinker.manifest) target_compile_options(relinker PRIVATE -municode) target_link_options(relinker PRIVATE -municode) endif() @@ -461,6 +463,9 @@ if(BUILD_TESTING) ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_input_file.py $) add_test(NAME input_nonascii COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_input_nonascii.py $) + add_test(NAME long_paths COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_long_paths.py $) + set_tests_properties(long_paths PROPERTIES SKIP_RETURN_CODE 77) add_test(NAME string_table_bounds COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_string_table_bounds.py $) add_test(NAME sce_code_extent COMMAND ${Python3_EXECUTABLE} diff --git a/core/relinker/relinker.manifest b/core/relinker/relinker.manifest new file mode 100644 index 0000000000..8f006058f1 --- /dev/null +++ b/core/relinker/relinker.manifest @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + true + + + diff --git a/core/relinker/relinker.rc b/core/relinker/relinker.rc new file mode 100644 index 0000000000..a543e8bc25 --- /dev/null +++ b/core/relinker/relinker.rc @@ -0,0 +1,3 @@ +#include + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "relinker.manifest" diff --git a/core/relinker/relinker/tests/test_long_paths.py b/core/relinker/relinker/tests/test_long_paths.py new file mode 100644 index 0000000000..e5c5bb857f --- /dev/null +++ b/core/relinker/relinker/tests/test_long_paths.py @@ -0,0 +1,43 @@ +"""Convert an input and write outputs whose paths exceed the 260 characters of the Windows MAX_PATH limit.""" + +import os +from pathlib import Path +import subprocess +import sys +import tempfile + +from test_guest_intel_trampolines import main_fixture + + +def long_paths_enabled(): + if os.name != "nt": + return True + import winreg + try: + with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\FileSystem") as key: + return winreg.QueryValueEx(key, "LongPathsEnabled")[0] == 1 + except OSError: + return False + + +def main(): + relinker = Path(sys.argv[1]).resolve() + if not long_paths_enabled(): + print("skipped, Windows long paths are disabled: LongPathsEnabled is not 1") + sys.exit(77) + with tempfile.TemporaryDirectory(prefix="anyps5-long-path-") as directory: + work = Path(directory).joinpath(*["long-path-component-" + "x" * 40] * 5) + work.mkdir(parents=True) + source = work / "input.elf" + source.write_bytes(main_fixture()) + for options, name in (([], "output.elf"), (["--windows", "--registry"], "output.exe")): + output = work / name + assert len(str(output)) > 260, output + result = subprocess.run([str(relinker), "--skip-sce-module", *options, str(source), str(output)], + capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=20) + assert result.returncode == 0 and output.is_file(), (options, result) + print("Long path tests passed") + + +if __name__ == "__main__": + main()