diff --git a/core/relinker/CMakeLists.txt b/core/relinker/CMakeLists.txt index e4441e4f19..df54dba194 100644 --- a/core/relinker/CMakeLists.txt +++ b/core/relinker/CMakeLists.txt @@ -523,6 +523,9 @@ if(BUILD_TESTING) set_tests_properties(needed_libraries PROPERTIES TIMEOUT 30) add_test(NAME macos_output COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_macos_output.py $) + add_test(NAME macos_tls_overlap COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_macos_tls_overlap.py $) + set_tests_properties(macos_tls_overlap PROPERTIES TIMEOUT 60) add_test(NAME guest_host_libc COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_guest_host_libc.py $) add_library(guest_module_args_host SHARED elfpatcher/tests/GuestModuleArgsHost.cpp) diff --git a/core/relinker/elfpatcher/src/macos/MacOsImage.cpp b/core/relinker/elfpatcher/src/macos/MacOsImage.cpp index 838d0d6c8b..196084a66e 100644 --- a/core/relinker/elfpatcher/src/macos/MacOsImage.cpp +++ b/core/relinker/elfpatcher/src/macos/MacOsImage.cpp @@ -218,16 +218,19 @@ TlsScan ScanTls(const std::vector& source, const std::vector(rva) + static_cast(info.Length) + info.BranchDisp; if (target >= 0 && target <= std::numeric_limits::max()) scan.BranchTargets.insert(static_cast(target)); } - if (info.SegmentPrefix == 0) continue; + if (info.SegmentPrefix == 0 || (insidePrevious && info.SegmentPrefix != 0x64)) continue; const auto position = info.OpcodeOffset; bool hasOperandSizePrefix = false; bool supportedPrefixes = info.SegmentPrefix == 0x64; diff --git a/core/relinker/relinker/tests/test_macos_tls_overlap.py b/core/relinker/relinker/tests/test_macos_tls_overlap.py new file mode 100644 index 0000000000..0eaeb6396d --- /dev/null +++ b/core/relinker/relinker/tests/test_macos_tls_overlap.py @@ -0,0 +1,50 @@ +"""Check the macOS TLS scan against decodings that start inside another instruction.""" +from pathlib import Path +import subprocess +import sys +import tempfile + +from test_macos_output import MachO +from test_tls_function_coverage import TLS_LOAD, add_alias, make_image + + +def convert(relinker, directory, name, image): + source = Path(directory) / f"{name}.elf" + output = Path(directory) / name + source.write_bytes(bytes(image)) + result = subprocess.run([str(relinker), "--macos", "--skip-sce-module", str(source), str(output)], + capture_output=True, text=True, timeout=60) + return result, output + + +def check_split_function(relinker, directory): + immediate = bytes.fromhex("48 b9 00 00 00 65 2e 62 69 6e") + body = TLS_LOAD + bytes.fromhex("eb 00 8b 40 f0") + immediate + b"\xc3" * 8 + image = add_alias(make_image("register", "symbol", 0x55, body), 0x0c, 0x1256) + result, output = convert(relinker, directory, "function-begins-inside-instruction", image) + if result.returncode != 0: + raise SystemExit(f"function-begins-inside-instruction: relinker failed: {result.stdout}{result.stderr}") + data = output.read_bytes() + code = b"".join(data[lo:hi] for lo, hi in MachO(data).executable_ranges()) + if immediate not in code: + raise SystemExit("function-begins-inside-instruction: the instruction around the GS-prefixed decoding changed") + + +def check_gs_access_refused(relinker, directory): + image = make_image("register", "unwind", body=bytes.fromhex("65 48 8b 04 25 28 00 00 00") + b"\xc3") + result, output = convert(relinker, directory, "gs-access", image) + message = result.stdout + result.stderr + if result.returncode != 2 or "Unsupported macOS guest TLS instruction" not in message or output.exists(): + raise SystemExit(f"gs-access: a GS access at an instruction start was not refused: {message}") + + +def main(): + relinker = Path(sys.argv[1]).resolve() + with tempfile.TemporaryDirectory(prefix="anyps5-macos-tls-overlap-") as directory: + check_split_function(relinker, directory) + check_gs_access_refused(relinker, directory) + print("macOS TLS overlap checks passed") + + +if __name__ == "__main__": + main()