diff --git a/core/libs/prx/libkernel/Module/src/DynamicLoader.cpp b/core/libs/prx/libkernel/Module/src/DynamicLoader.cpp index e1091def8..8a53d9cc0 100644 --- a/core/libs/prx/libkernel/Module/src/DynamicLoader.cpp +++ b/core/libs/prx/libkernel/Module/src/DynamicLoader.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #ifdef _WIN32 #define NOMINMAX @@ -47,7 +48,14 @@ void* Symbol(Module& module, const char* name) { void* FindSymbol(Module& module, const char* name) { if (auto* symbol = Symbol(module, name)) return symbol; const auto nid = Nid::ComputeNid(name, ""); +#ifdef _WIN32 return Symbol(module, nid.c_str()); +#else + if (auto* symbol = Symbol(module, nid.c_str())) return symbol; + constexpr char guestSuffix[] = "#guest"; + if (auto* symbol = Symbol(module, (std::string(name) + guestSuffix).c_str())) return symbol; + return Symbol(module, (nid + guestSuffix).c_str()); +#endif } } diff --git a/core/libs/tests/GuestDynamicLoader.cpp b/core/libs/tests/GuestDynamicLoader.cpp index 2fa27e917..0259f64bd 100644 --- a/core/libs/tests/GuestDynamicLoader.cpp +++ b/core/libs/tests/GuestDynamicLoader.cpp @@ -24,6 +24,12 @@ int main(int argc, char** argv) { auto add = reinterpret_cast(dlsym_nid_postfix(module, "GuestModuleAdd")); Require(add && add(17, 25) == 42); Require(dlsym_nid_postfix(reinterpret_cast(-2), "GuestModuleAdd") == reinterpret_cast(add)); +#ifndef _WIN32 + auto mul = reinterpret_cast(dlsym_nid_postfix(module, "GuestModuleMul")); + Require(mul && mul(6, 7) == 42); + auto sub = reinterpret_cast(dlsym_nid_postfix(module, "GuestModuleSub")); + Require(sub && sub(50, 8) == 42); +#endif Require(dlsym_nid_postfix(module, "missing_symbol") == nullptr); std::thread other([] { Require(dlerror_nid_postfix() == nullptr); }); other.join(); diff --git a/core/libs/tests/GuestModuleFixture.cpp b/core/libs/tests/GuestModuleFixture.cpp index e8f8521d6..05835ccb9 100644 --- a/core/libs/tests/GuestModuleFixture.cpp +++ b/core/libs/tests/GuestModuleFixture.cpp @@ -1,4 +1,5 @@ #include "prx/libc/include/general/VabiMacros.hpp" +#include "prx/libc/include/general/ExportMacros.hpp" #ifdef _WIN32 #define MODULE_EXPORT __declspec(dllexport) #else @@ -7,3 +8,13 @@ extern "C" MODULE_EXPORT int APS5_VABI GuestModuleAdd_nid_postfix(int a, int b) { return a + b; } +#ifndef _WIN32 +extern "C" MODULE_EXPORT int APS5_VABI GuestModuleMul_nid_no_patch(int a, int b) { + return a * b; +} +APS5_EXPORT("GuestModuleMul#guest", GuestModuleMul_nid_no_patch); +extern "C" MODULE_EXPORT int APS5_VABI GuestModuleSub_nid_no_patch(int a, int b) { + return a - b; +} +APS5_EXPORT("BOyBJaKwOa8#guest", GuestModuleSub_nid_no_patch); +#endif diff --git a/core/relinker/CMakeLists.txt b/core/relinker/CMakeLists.txt index 0dca9d1f2..26276046b 100644 --- a/core/relinker/CMakeLists.txt +++ b/core/relinker/CMakeLists.txt @@ -218,6 +218,8 @@ if(BUILD_TESTING) ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_guest_tls.py $) add_test(NAME guest_module_directories COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_guest_module_directories.py $) + add_test(NAME guest_symbol_names COMMAND ${Python3_EXECUTABLE} + ${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_test(NAME elf_header COMMAND ${Python3_EXECUTABLE} diff --git a/core/relinker/relinker/include/relinker/guest/GuestImage.hpp b/core/relinker/relinker/include/relinker/guest/GuestImage.hpp index 8325e8208..7f50aba5b 100644 --- a/core/relinker/relinker/include/relinker/guest/GuestImage.hpp +++ b/core/relinker/relinker/include/relinker/guest/GuestImage.hpp @@ -10,6 +10,7 @@ namespace Relinker { +inline constexpr char GuestSymbolSuffix[] = "#guest"; inline constexpr char GuestModuleSuffix[] = ".guest.prx"; struct GuestSymbol { diff --git a/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp b/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp index cef7ff2cf..59e5b3289 100644 --- a/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp +++ b/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include namespace Relinker { @@ -100,6 +101,11 @@ std::vector GuestModuleBuilder::Build(const std::filesystem::path for (const auto provider : shared->second) providers += " " + images[provider].SourcePath.string(); throw Domain::RelinkerException("Ambiguous guest import " + name + " in " + importer + ": exported by" + providers); }; + const auto rename = [](std::vector& symbols, std::vector& strings, std::size_t index, const std::string& name) { + if (strings.size() > std::numeric_limits::max()) throw Domain::RelinkerException("Guest string table too large"); + Io::WriteU32(symbols, index * 24, static_cast(strings.size())); + Io::AppendString(strings, name + GuestSymbolSuffix); + }; if (dynamic.DynSymData.size() % 24 != 0) throw Domain::RelinkerException("Invalid executable symbol table"); for (std::size_t offset = 0; offset < dynamic.DynSymData.size(); offset += 24) { if (Io::ReadU16(dynamic.DynSymData, offset + 6) != 0) continue; @@ -110,6 +116,7 @@ std::vector GuestModuleBuilder::Build(const std::filesystem::path if (end == dynamic.DynStrData.end()) throw Domain::RelinkerException("Unterminated executable symbol name"); const std::string name(start, end); rejectSharedImport(name.substr(0, name.find('#')), inputPath.string()); + if (!windows && exports.contains(name)) rename(dynamic.DynSymData, dynamic.DynStrData, offset / 24, name); } std::vector> dependencies(images.size()); for (auto& image : images) image.UsePlatformTlsResolver = !exports.contains("vNe1w4diLCs"); @@ -130,6 +137,15 @@ std::vector GuestModuleBuilder::Build(const std::filesystem::path } else if (windows && (symbol.Info & 15) == 6) throw Domain::RelinkerException("Windows guest TLS import requires a guest TLS export: " + symbol.Name); } } + if (!windows) { + for (auto& image : images) { + for (std::size_t index = 1; index < image.Symbols.size(); ++index) { + const auto& symbol = image.Symbols[index]; + const bool exported = symbol.Section != 0 && (symbol.Info >> 4) != 0 && symbol.Visibility != 1 && symbol.Visibility != 2; + if ((symbol.Section == 0 || exported) && exports.contains(symbol.Name)) rename(image.Dynamic.DynSymData, image.Dynamic.DynStrData, index, symbol.Name); + } + } + } std::vector order; std::vector states(images.size()); const std::function visit = [&](std::size_t index) { diff --git a/core/relinker/relinker/tests/test_guest_symbol_names.py b/core/relinker/relinker/tests/test_guest_symbol_names.py new file mode 100644 index 000000000..35558254c --- /dev/null +++ b/core/relinker/relinker/tests/test_guest_symbol_names.py @@ -0,0 +1,132 @@ +from pathlib import Path +import struct +import subprocess +import sys +import tempfile + +from test_guest_intel_trampolines import PLAIN_SITE, elf_loads, guest_fixture, main_fixture + +DYNAMIC = 0x4800 +STRINGS = 0x4A00 +SYMBOLS = 0x4B00 +RELOCATIONS = 0x4C00 +GOT = 0x4E00 + + +def string_table(names): + strings = b"\0" + offsets = [] + for name in names: + offsets.append(len(strings)) + strings += name.encode() + b"\0" + return strings, offsets + + +def guest_module(symbols): + image = guest_fixture(PLAIN_SITE) + strings, offsets = string_table(name for name, _ in symbols) + assert len(strings) <= 0x40 + image[0x800:0x800 + len(strings)] = strings + struct.pack_into("> 32: + names.add(symbol_name(image, tags, info >> 32)) + return names + + +def convert(relinker, case): + (case / "sce_module").mkdir(parents=True) + source = case / "input.elf" + source.write_bytes(importing_executable(["AAAAAAAAAAA#A#A", "DDDDDDDDDDD#D#D"])) + (case / "sce_module" / "libc.prx").write_bytes( + guest_module([("AAAAAAAAAAA#A#A", True), ("BBBBBBBBBBB#B#B", False), ("CCCCCCCCCCC#C#C", False), ("EEEEEEEEEEE", True)])) + (case / "sce_module" / "other.prx").write_bytes(guest_module([("BBBBBBBBBBB#B#B", True), ("EEEEEEEEEEE", True)])) + output = case / "output.elf" + result = subprocess.run([str(relinker), str(source), str(output)], capture_output=True, text=True, timeout=30) + assert result.returncode == 0, (result.stdout, result.stderr) + return output + + +def main(): + relinker = Path(sys.argv[1]).resolve() + with tempfile.TemporaryDirectory(prefix="anyps5-guest-symbol-names-") as directory: + work = Path(directory) + + output = convert(relinker, work) + modules = output.parent / "app0" / "sce_module" + libc = module_symbols((modules / "libc.prx.guest.prx").read_bytes()) + assert libc == ["AAAAAAAAAAA#guest", "BBBBBBBBBBB#guest", "CCCCCCCCCCC", "EEEEEEEEEEE#guest"], libc + other = module_symbols((modules / "other.prx.guest.prx").read_bytes()) + assert other == ["BBBBBBBBBBB#guest", "EEEEEEEEEEE#guest"], other + imports = imported_symbols(output.read_bytes()) + assert imports == {"AAAAAAAAAAA#guest", "DDDDDDDDDDD"}, imports + print("Guest symbol name tests passed") + + +if __name__ == "__main__": + main()