diff --git a/core/libs/CMakeLists.txt b/core/libs/CMakeLists.txt index 880bf340b..f6c6df5e0 100644 --- a/core/libs/CMakeLists.txt +++ b/core/libs/CMakeLists.txt @@ -76,7 +76,6 @@ file(GLOB LIBS_DIRS LIST_DIRECTORIES true ${CMAKE_CURRENT_SOURCE_DIR}/prx/*) list(REMOVE_ITEM LIBS_DIRS ${LIBC_DIR}) set(PATCHED_OUTPUTS ${LIBC_PATCHED}) -set(ANYPS5_REPLACEMENT_MODULES libc.prx) foreach(LIB_DIR ${LIBS_DIRS}) if(IS_DIRECTORY ${LIB_DIR}) @@ -126,10 +125,8 @@ foreach(LIB_DIR ${LIBS_DIRS}) ) list(APPEND PATCHED_OUTPUTS ${PATCHED_FILE}) - list(APPEND ANYPS5_REPLACEMENT_MODULES ${LIB_NAME}.prx) endif() endforeach() -set(ANYPS5_REPLACEMENT_MODULES ${ANYPS5_REPLACEMENT_MODULES} PARENT_SCOPE) add_custom_target(libs DEPENDS ${PATCHED_OUTPUTS}) diff --git a/core/relinker/CMakeLists.txt b/core/relinker/CMakeLists.txt index 63f04cbbe..13912f3f4 100644 --- a/core/relinker/CMakeLists.txt +++ b/core/relinker/CMakeLists.txt @@ -1,13 +1,3 @@ -if(NOT ANYPS5_REPLACEMENT_MODULES) - message(FATAL_ERROR "Replacement module catalog is empty") -endif() -set(replacementModuleEntries "") -foreach(module IN LISTS ANYPS5_REPLACEMENT_MODULES) - string(APPEND replacementModuleEntries " \"${module}\",\n") -endforeach() -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/generated/relinker/guest/ReplacementModules.hpp" - "#ifndef RELINKER_REPLACEMENTMODULES_HPP\n#define RELINKER_REPLACEMENTMODULES_HPP\n#include \nnamespace Relinker {\ninline constexpr std::string_view ReplacementModules[] = {\n${replacementModuleEntries}};\n}\n#endif\n") - add_executable(relinker ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/cli/src/CliArgs.cpp @@ -76,7 +66,6 @@ add_executable(relinker ${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/pipeline/RelinkerPipeline.cpp ) target_include_directories(relinker PRIVATE - ${CMAKE_CURRENT_BINARY_DIR}/generated ${CMAKE_CURRENT_SOURCE_DIR}/cli/include ${CMAKE_CURRENT_SOURCE_DIR}/domain/include ${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/include @@ -241,18 +230,6 @@ if(BUILD_TESTING) ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_guest_symbol_names.py $) add_test(NAME guest_host_libc COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_guest_host_libc.py $) - add_library(guest_lifecycle_replacement_fixture SHARED elfpatcher/tests/GuestLifecycleReplacement.cpp) - target_include_directories(guest_lifecycle_replacement_fixture PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../libs) - if(MINGW) - target_compile_options(guest_lifecycle_replacement_fixture PRIVATE - -fno-asynchronous-unwind-tables -fno-exceptions) - endif() - configure_windows_unwind(guest_lifecycle_replacement_fixture) - set_target_properties(guest_lifecycle_replacement_fixture PROPERTIES - PREFIX "" SUFFIX ".prx" WINDOWS_EXPORT_ALL_SYMBOLS ON) - add_test(NAME guest_lifecycle_ownership COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_guest_lifecycle_ownership.py - $ $) add_test(NAME guest_absolute_symbols COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_guest_absolute_symbols.py $) add_test(NAME windows_import_modules COMMAND ${Python3_EXECUTABLE} diff --git a/core/relinker/elfpatcher/src/linux/LinuxGuestModuleWriter.cpp b/core/relinker/elfpatcher/src/linux/LinuxGuestModuleWriter.cpp index 81fc8549e..f7744ddfa 100644 --- a/core/relinker/elfpatcher/src/linux/LinuxGuestModuleWriter.cpp +++ b/core/relinker/elfpatcher/src/linux/LinuxGuestModuleWriter.cpp @@ -73,9 +73,8 @@ std::vector GuestModuleWriter::WriteLinux(const Relinker::GuestIma Io::AppendU32(bytes, static_cast(displacement)); return start; }; - const bool ownsLifecycle = image.ReplacementModule.empty(); - const auto init = ownsLifecycle ? lifecycle(image.Init) : 0; - const auto fini = ownsLifecycle ? lifecycle(image.Fini) : 0; + const auto init = lifecycle(image.Init); + const auto fini = lifecycle(image.Fini); Io::AlignBuffer(bytes, 8); const auto dynamicOffset = bytes.size(); const auto dynamicAddress = address(); @@ -99,8 +98,8 @@ std::vector GuestModuleWriter::WriteLinux(const Relinker::GuestIma tag(20, 7); tag(3, image.Got); } - if (ownsLifecycle && !image.InitArray.empty()) { tag(25, image.InitArray.front()); tag(27, image.InitArray.size() * 8); } - if (ownsLifecycle && !image.FiniArray.empty()) { tag(26, image.FiniArray.front()); tag(28, image.FiniArray.size() * 8); } + if (!image.InitArray.empty()) { tag(25, image.InitArray.front()); tag(27, image.InitArray.size() * 8); } + if (!image.FiniArray.empty()) { tag(26, image.FiniArray.front()); tag(28, image.FiniArray.size() * 8); } if (init != 0) tag(12, init); if (fini != 0) tag(13, fini); tag(30, 8); diff --git a/core/relinker/elfpatcher/src/windows/WindowsGuestModuleWriter.cpp b/core/relinker/elfpatcher/src/windows/WindowsGuestModuleWriter.cpp index 3dbc36e26..2dc27774d 100644 --- a/core/relinker/elfpatcher/src/windows/WindowsGuestModuleWriter.cpp +++ b/core/relinker/elfpatcher/src/windows/WindowsGuestModuleWriter.cpp @@ -170,12 +170,10 @@ std::vector GuestModuleWriter::WriteWindows(const Relinker::GuestI } const auto entry = nextRva; sections.push_back({".dllmain", entry, SectionRead | SectionExecute | 0x20u, {0xb8, 1, 0, 0, 0, 0xc3}}); - if (guest.ReplacementModule.empty()) { - for (const auto slot : guest.InitArray) runtime.InitArrayRvas.push_back(image.GetRva(slot, 8)); - for (const auto slot : guest.FiniArray) runtime.FiniArrayRvas.push_back(image.GetRva(slot, 8)); - runtime.InitRva = guest.Init == 0 ? 0 : image.GetRva(guest.Init); - runtime.FiniRva = guest.Fini == 0 ? 0 : image.GetRva(guest.Fini); - } + for (const auto slot : guest.InitArray) runtime.InitArrayRvas.push_back(image.GetRva(slot, 8)); + for (const auto slot : guest.FiniArray) runtime.FiniArrayRvas.push_back(image.GetRva(slot, 8)); + runtime.InitRva = guest.Init == 0 ? 0 : image.GetRva(guest.Init); + runtime.FiniRva = guest.Fini == 0 ? 0 : image.GetRva(guest.Fini); auto result = WindowsPeWriter().Write(sections, entry, directories); const auto characteristics = Io::ReadU16(result, 0x80 + 22); Io::WriteU16(result, 0x80 + 22, static_cast((characteristics | 0x2000) & ~1u)); diff --git a/core/relinker/elfpatcher/tests/GuestLifecycleReplacement.cpp b/core/relinker/elfpatcher/tests/GuestLifecycleReplacement.cpp deleted file mode 100644 index bb0975d98..000000000 --- a/core/relinker/elfpatcher/tests/GuestLifecycleReplacement.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "prx/libc/include/general/VabiMacros.hpp" -#include -#include - -namespace { - -[[noreturn]] void Fail(const char* message) noexcept { - std::fprintf(stderr, "Guest lifecycle replacement fixture: %s\n", message); - std::fflush(stderr); - std::_Exit(EXIT_FAILURE); -} - -} - -extern "C" void APS5_VABI Record(int event) { - const auto path = std::getenv("ANYPS5_LIFECYCLE_EVENTS"); - if (!path) Fail("Missing ANYPS5_LIFECYCLE_EVENTS"); - auto* file = std::fopen(path, "ab"); - if (!file) Fail("Cannot open lifecycle event file"); - const auto result = std::fputc(event, file); - const auto closed = std::fclose(file); - if (result == EOF || closed != 0) Fail("Cannot write lifecycle event"); -} - -namespace { - -struct Runtime { - Runtime() { Record('H'); } - ~Runtime() { Record('h'); } -} runtime; - -} diff --git a/core/relinker/relinker/include/relinker/guest/GuestImage.hpp b/core/relinker/relinker/include/relinker/guest/GuestImage.hpp index 397c650a5..fe34092b5 100644 --- a/core/relinker/relinker/include/relinker/guest/GuestImage.hpp +++ b/core/relinker/relinker/include/relinker/guest/GuestImage.hpp @@ -28,8 +28,6 @@ struct GuestImage { std::filesystem::path SourcePath; std::string OutputName; std::string Soname; - std::vector ModuleNames; - std::string ReplacementModule; std::vector Bytes; std::vector Trampolines; std::vector Headers; diff --git a/core/relinker/relinker/src/guest/GuestImageReader.cpp b/core/relinker/relinker/src/guest/GuestImageReader.cpp index aea9b6362..56551a48d 100644 --- a/core/relinker/relinker/src/guest/GuestImageReader.cpp +++ b/core/relinker/relinker/src/guest/GuestImageReader.cpp @@ -63,7 +63,6 @@ GuestImage GuestImageReader::Read(const std::filesystem::path& path, std::vector std::map tags; std::vector needed; std::vector moduleImports; - std::vector moduleExports; bool terminated = false; for (std::uint64_t offset = dynamic->Offset; offset < dynamic->Offset + dynamic->FileSize; offset += 16) { const auto tag = Io::ReadU64(bytes, offset); @@ -71,7 +70,6 @@ GuestImage GuestImageReader::Read(const std::filesystem::path& path, std::vector if (tag == 0) { terminated = true; break; } if (tag == 1) needed.push_back(value); else if (tag == 0x61000045) moduleImports.push_back(value); - else if (tag == 0x6100000d || tag == 0x61000043) moduleExports.push_back(value); else if (tag < 0x60000000 || tag == 0x6100003f || (tag >= 0x61000027 && tag <= 0x6100003b)) { if (!tags.emplace(tag, value).second) fail("Duplicate dynamic tag " + std::to_string(tag)); } @@ -161,13 +159,6 @@ GuestImage GuestImageReader::Read(const std::filesystem::path& path, std::vector image.Soname = string(tags.at(14)); if (image.Soname.empty() || image.Soname.find_first_of("\\:$\r\n") != std::string::npos) fail("Invalid guest SONAME: " + image.Soname); } - std::set moduleIds; - for (const auto value : moduleExports) { - const auto name = string(value & 0xffffffffu); - if (name.empty() || name.find_first_of("/\\:$\r\n") != std::string::npos) fail("Invalid guest module name: " + name); - if (!moduleIds.insert(value >> 48).second) fail("Duplicate export module ID"); - image.ModuleNames.push_back(name); - } std::set dependencies; for (const auto offset : needed) { const auto name = string(offset); diff --git a/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp b/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp index 7dd28aba3..f82baec84 100644 --- a/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp +++ b/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp @@ -1,6 +1,4 @@ #include -#include -#include #include #include #include @@ -86,25 +84,6 @@ std::vector GuestModuleBuilder::Build(const std::filesystem::path Io::FileReader reader; for (const auto& path : paths) { auto image = GuestImageReader().Read(path, reader.Read(path.string())); - auto identities = image.ModuleNames; - if (identities.empty()) identities.push_back(image.Soname.empty() ? path.filename().string() : image.Soname); - for (const auto& identity : identities) { - const auto moduleName = identity.ends_with(".prx") ? identity : identity + ".prx"; - std::string owner; - for (const auto replacement : ReplacementModules) { - if (replacement == moduleName) { owner = replacement; break; } - } - if (owner.empty()) { - for (const auto replacement : ReplacementModules) { - if (Domain::ImportModuleName(std::string(replacement)) != Domain::ImportModuleName(moduleName)) continue; - if (!owner.empty()) throw Domain::RelinkerException("Ambiguous replacement module identity: " + identity); - owner = replacement; - } - } - if (owner.empty()) continue; - if (!image.ReplacementModule.empty() && image.ReplacementModule != owner) throw Domain::RelinkerException("Conflicting replacement lifecycle owners: " + path.string()); - image.ReplacementModule = std::move(owner); - } if (image.OutputName.find_first_of("$\r\n") != std::string::npos) throw Domain::RelinkerException("Unsupported guest filename: " + image.OutputName); std::string folded = image.OutputName; if (windows) { @@ -249,9 +228,6 @@ std::vector GuestModuleBuilder::Build(const std::filesystem::path } for (const auto& image : images) for (const auto& dependency : image.Dependencies) addHost(dependency); if (uniqueHosts.contains("libSceLibcInternal.prx") && uniqueHosts.insert("libc.prx").second) hostLibraries.push_back("libc.prx"); - for (const auto& image : images) { - if (!image.ReplacementModule.empty() && uniqueHosts.insert(image.ReplacementModule).second) hostLibraries.push_back(image.ReplacementModule); - } dynamic.DynamicSegmentData.clear(); const auto addNeeded = [&](const std::string& name) { Io::AppendU64(dynamic.DynamicSegmentData, 1); diff --git a/core/relinker/relinker/tests/test_guest_host_libc.py b/core/relinker/relinker/tests/test_guest_host_libc.py index e27a02269..bb1c776b0 100644 --- a/core/relinker/relinker/tests/test_guest_host_libc.py +++ b/core/relinker/relinker/tests/test_guest_host_libc.py @@ -52,7 +52,7 @@ def main(): "libkernel.prx", "libSceLibcInternal.prx", "libc.prx"], needed needed = convert(relinker, work / "without-internal", ("libkernel.prx", "libc.prx"), ["libc.prx"]) - assert needed == ["$ORIGIN/app0/sce_module/libc.prx.guest.prx", "libkernel.prx", "libc.prx"], needed + assert needed == ["$ORIGIN/app0/sce_module/libc.prx.guest.prx", "libkernel.prx"], needed needed = convert(relinker, work / "not-bundled", imports, ["other.prx"]) assert needed == ["$ORIGIN/app0/sce_module/other.prx.guest.prx", *imports], needed diff --git a/core/relinker/relinker/tests/test_guest_lifecycle_ownership.py b/core/relinker/relinker/tests/test_guest_lifecycle_ownership.py deleted file mode 100644 index 35117e5b2..000000000 --- a/core/relinker/relinker/tests/test_guest_lifecycle_ownership.py +++ /dev/null @@ -1,198 +0,0 @@ -from pathlib import Path -import os -import shutil -import struct -import subprocess -import sys -import tempfile - -from test_guest_intel_trampolines import elf_loads -from test_guest_module_directories import needed_libraries -from test_guest_symbol_names import module_symbols -from test_windows_import_modules import executable - - -def guest(identity=None, module_name=None, missing_import=False, module_info_tag=0x6100000d): - image = bytearray(0x3000) - image[:16] = b'\x7fELF\x02\x01\x01' + bytes(9) - struct.pack_into(' 2 else None - with tempfile.TemporaryDirectory(prefix='anyps5-guest-lifecycle-') as directory: - work = Path(directory) - for windows in (False, True): - for name, identity, module_name, replacement, module_info_tag in ( - ('libc.prx', None, None, 'libc.prx', 0x6100000d), - ('renamed.prx', 'libc.prx', None, 'libc.prx', 0x6100000d), - ('renamed.prx', 'libc', None, 'libc.prx', 0x6100000d), - ('renamed.prx', None, 'libc', 'libc.prx', 0x6100000d), - ('renamed.prx', 'ordinary.prx', 'libc', 'libc.prx', 0x61000043), - ('renamed.prx', None, ('custom', 'libc'), 'libc.prx', 0x6100000d), - ('ordinary.prx', None, ('custom', 'other'), None, 0x6100000d), - ('renamed.prx', None, 'libSceFont_native', 'libSceFont.native.prx', 0x6100000d), - ('libkernel.prx', None, None, 'libkernel.prx', 0x6100000d), - ('ordinary.prx', None, None, None, 0x6100000d), - ('unimplemented.prx', 'libNotReplaced.prx', None, None, 0x6100000d)): - suppressed = replacement is not None - case = work / f'{windows}-{name}-{identity}-{module_name}' - modules = case / 'sce_module' - modules.mkdir(parents=True) - original = guest(identity, module_name, module_info_tag=module_info_tag) - (modules / name).write_bytes(original) - (modules / 'consumer.prx').write_bytes(consumer(name)) - source = case / 'input.elf' - source.write_bytes(executable(name)) - output = case / ('output.exe' if windows else 'output.elf') - result = subprocess.run([str(relinker), *(['--windows'] if windows else []), - '--skip-syscall-check', str(source), str(output)], - capture_output=True, text=True, timeout=30) - assert result.returncode == 0, (result.stdout, result.stderr) - converted = case / 'app0' / 'sce_module' / (name + '.guest.prx') - image = converted.read_bytes() - if not windows: - assert 'shared#guest' in module_symbols(image) - assert 'shared#guest' in module_symbols((converted.parent / 'consumer.prx.guest.prx').read_bytes()) - assert lifecycle_tags(image) == (set() if suppressed else {12, 13, 25, 26, 27, 28}), name - for header in elf_loads(image): - if header[3] <= 0x1000 < header[3] + header[5]: - start = header[2] + 0x1000 - header[3] - assert image[start:start + 0x100] == original[0x1000:0x1100] - needed = needed_libraries(image) - assert 'libc.prx' in needed - if replacement: - assert replacement in needed - native = (os.name == 'nt') if windows else (sys.platform == 'linux' and os.uname().machine == 'x86_64') - if native and not fixture: - raise AssertionError('Native lifecycle test requires the replacement fixture') - if native and fixture: - libraries = case / 'libs' - libraries.mkdir() - shutil.copyfile(fixture, libraries / 'libc.prx') - if replacement and replacement != 'libc.prx': - shutil.copyfile(fixture, libraries / replacement) - events = case / 'events.txt' - output.chmod(0o755) - command = [str(output)] if windows else [sys.executable, __file__, '--load', - str(converted.parent / 'consumer.prx.guest.prx')] - run = subprocess.run(command, env={**os.environ, 'ANYPS5_LIFECYCLE_EVENTS': str(events)}, - capture_output=True, text=True, timeout=30) - assert run.returncode == (42 if windows else 0), (name, run.returncode, run.stdout, run.stderr) - actual = events.read_text() - assert actual == ('HHhh' if replacement and replacement != 'libc.prx' else 'Hh' if suppressed else 'HIAaih'), (name, actual) - case = work / f'{windows}-missing-import' - modules = case / 'sce_module' - modules.mkdir(parents=True) - (modules / 'libc.prx').write_bytes(guest(missing_import=True)) - source = case / 'input.elf' - source.write_bytes(executable('libc.prx')) - output = case / ('output.exe' if windows else 'output.elf') - result = subprocess.run([str(relinker), *(['--windows'] if windows else []), - '--skip-syscall-check', str(source), str(output)], - capture_output=True, text=True, timeout=30) - assert result.returncode == 0, (result.stdout, result.stderr) - converted = case / 'app0' / 'sce_module' / 'libc.prx.guest.prx' - if not windows: - assert 'absent' in module_symbols(converted.read_bytes()) - assert lifecycle_tags(converted.read_bytes()) == set() - if native: - libraries = case / 'libs' - libraries.mkdir() - shutil.copyfile(fixture, libraries / 'libc.prx') - output.chmod(0o755) - command = [str(output)] if windows else [sys.executable, __file__, '--load', str(converted)] - run = subprocess.run(command, env={**os.environ, 'ANYPS5_LIFECYCLE_EVENTS': str(case / 'events.txt')}, - capture_output=True, text=True, timeout=30) - assert run.returncode != 0 and 'absent' in run.stderr, (run.returncode, run.stderr) - print('Guest lifecycle ownership conversion tests passed') - if os.name != 'nt' and (sys.platform != 'linux' or os.uname().machine != 'x86_64'): - print('Native lifecycle execution skipped: Linux x86-64 or Windows required') - - -if __name__ == '__main__': - main()