diff --git a/core/relinker/CMakeLists.txt b/core/relinker/CMakeLists.txt index 3de337d78..4bc292b5f 100644 --- a/core/relinker/CMakeLists.txt +++ b/core/relinker/CMakeLists.txt @@ -330,6 +330,8 @@ if(BUILD_TESTING) ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_entry_point_bounds.py $) add_test(NAME init_array_roots COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_init_array_roots.py $) + add_test(NAME to_rosetta COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_to_rosetta.py $) else() message(STATUS "Python3 interpreter not found; skipping Python relinker tests") endif() diff --git a/core/relinker/cli/include/Cli.hpp b/core/relinker/cli/include/Cli.hpp index 9e44a1d9a..38776589d 100644 --- a/core/relinker/cli/include/Cli.hpp +++ b/core/relinker/cli/include/Cli.hpp @@ -12,6 +12,7 @@ struct Args { bool skipSyscallCheck = false; bool skipSceModule = false; bool toIntel = false; + bool toRosetta = false; bool writeRegistry = false; bool toWindows = false; bool toMacos = false; diff --git a/core/relinker/cli/src/CliArgs.cpp b/core/relinker/cli/src/CliArgs.cpp index 1a0f1e00d..f2c6a95a6 100644 --- a/core/relinker/cli/src/CliArgs.cpp +++ b/core/relinker/cli/src/CliArgs.cpp @@ -8,7 +8,7 @@ namespace Cli { const char* Usage() { - return "Usage: relinker [--help] [--windows | --macos] [--windows-diagnostics] [--windows-gui] [--skip-syscall-check] [--skip-sce-module | --sce-module-path ] [--exclude-sce-module ]... [--to-intel] [unused-filter=0|1|2] [--registry] [--rpath ] [--lazy-binding] [--autorun] \n" + return "Usage: relinker [--help] [--windows | --macos] [--windows-diagnostics] [--windows-gui] [--skip-syscall-check] [--skip-sce-module | --sce-module-path ] [--exclude-sce-module ]... [--to-intel] [--to-rosetta] [unused-filter=0|1|2] [--registry] [--rpath ] [--lazy-binding] [--autorun] \n" "Example: relinker input.elf output.elf"; } @@ -36,6 +36,9 @@ Args ParseArgs(int argc, char* argv[]) { args.excludedSceModules.insert(argv[++i]); } else if (arg == "--to-intel") { args.toIntel = true; + } else if (arg == "--to-rosetta") { + args.toRosetta = true; + args.toIntel = true; } else if (arg.rfind("unused-filter=", 0) == 0) { const std::string value = arg.substr(14); if (unusedFilterSpecified || value.size() != 1 || value[0] < '0' || value[0] > '2') @@ -78,6 +81,8 @@ Args ParseArgs(int argc, char* argv[]) { if (args.toWindows && args.toMacos) throw std::runtime_error("--windows conflicts with --macos"); + if (args.toRosetta && args.toWindows) + throw std::runtime_error("--to-rosetta conflicts with --windows"); if (args.windowsDiagnostics && !args.toWindows) throw std::runtime_error("--windows-diagnostics requires --windows"); diff --git a/core/relinker/codegen/include/codegen/IAmd64OnlyConverter.hpp b/core/relinker/codegen/include/codegen/IAmd64OnlyConverter.hpp index 1bc42caf8..ca82119d7 100644 --- a/core/relinker/codegen/include/codegen/IAmd64OnlyConverter.hpp +++ b/core/relinker/codegen/include/codegen/IAmd64OnlyConverter.hpp @@ -18,7 +18,7 @@ public: ) const = 0; }; -std::unique_ptr MakeAmd64OnlyConverter(); +std::unique_ptr MakeAmd64OnlyConverter(Amd64OnlyTarget target = Amd64OnlyTarget::Intel); } diff --git a/core/relinker/codegen/include/codegen/x86/Amd64OnlySubstitutionTable.hpp b/core/relinker/codegen/include/codegen/x86/Amd64OnlySubstitutionTable.hpp index 8a8918671..b7ee6f081 100644 --- a/core/relinker/codegen/include/codegen/x86/Amd64OnlySubstitutionTable.hpp +++ b/core/relinker/codegen/include/codegen/x86/Amd64OnlySubstitutionTable.hpp @@ -38,6 +38,9 @@ inline constexpr Entry kMwaitx = {"MWAITX", nullptr, 0}; inline constexpr Entry kClzero = {"CLZERO", nullptr, 0}; inline constexpr Entry kRdpru = {"RDPRU", nullptr, 0}; inline constexpr Entry kMcommit = {"MCOMMIT", nullptr, 0}; +inline constexpr Entry kRdseed = {"RDSEED", nullptr, 0}; +inline constexpr Entry kRdpid = {"RDPID", nullptr, 0}; +inline constexpr Entry kClwb = {"CLWB", nullptr, 0}; inline constexpr Entry kExtrq = {"EXTRQ", nullptr, 0}; inline constexpr Entry kInsertq = {"INSERTQ", nullptr, 0}; inline constexpr Entry kExtrqRegisterForm = {"EXTRQ register form", nullptr, 0}; diff --git a/core/relinker/codegen/include/codegen/x86/Amd64OnlySubstitutionTypes.hpp b/core/relinker/codegen/include/codegen/x86/Amd64OnlySubstitutionTypes.hpp index 2b835fd94..bcb5df816 100644 --- a/core/relinker/codegen/include/codegen/x86/Amd64OnlySubstitutionTypes.hpp +++ b/core/relinker/codegen/include/codegen/x86/Amd64OnlySubstitutionTypes.hpp @@ -8,6 +8,11 @@ namespace Codegen { +enum class Amd64OnlyTarget : std::uint8_t { + Intel, + Rosetta +}; + enum class Amd64OnlyLowering : std::uint8_t { InPlace, Trampoline, diff --git a/core/relinker/codegen/include/codegen/x86/DecodedInstruction.hpp b/core/relinker/codegen/include/codegen/x86/DecodedInstruction.hpp index dc85cbcd4..798128e36 100644 --- a/core/relinker/codegen/include/codegen/x86/DecodedInstruction.hpp +++ b/core/relinker/codegen/include/codegen/x86/DecodedInstruction.hpp @@ -23,6 +23,9 @@ struct DecodedInstruction { [[nodiscard]] bool IsMcommit() const; [[nodiscard]] bool IsMovntss() const; [[nodiscard]] bool IsMovntsd() const; + [[nodiscard]] bool IsRdseed() const; + [[nodiscard]] bool IsRdpid() const; + [[nodiscard]] bool IsClwb() const; private: [[nodiscard]] std::size_t _skipPrefixesAndRex(bool* outHasOperandSizePrefix, bool* outHasRepnePrefix, bool* outHasRepPrefix) const; diff --git a/core/relinker/codegen/include/codegen/x86/IAmd64OnlyInstructionMatcher.hpp b/core/relinker/codegen/include/codegen/x86/IAmd64OnlyInstructionMatcher.hpp index 65d6f04c1..df17eb175 100644 --- a/core/relinker/codegen/include/codegen/x86/IAmd64OnlyInstructionMatcher.hpp +++ b/core/relinker/codegen/include/codegen/x86/IAmd64OnlyInstructionMatcher.hpp @@ -25,7 +25,7 @@ public: ) const = 0; }; -std::unique_ptr MakeAmd64OnlyInstructionMatcher(); +std::unique_ptr MakeAmd64OnlyInstructionMatcher(Amd64OnlyTarget target = Amd64OnlyTarget::Intel); } diff --git a/core/relinker/codegen/src/Amd64OnlyConverter.cpp b/core/relinker/codegen/src/Amd64OnlyConverter.cpp index 409b62e87..201630464 100644 --- a/core/relinker/codegen/src/Amd64OnlyConverter.cpp +++ b/core/relinker/codegen/src/Amd64OnlyConverter.cpp @@ -26,6 +26,8 @@ auto _atFileOffset(const Domain::FileByteOffset base, const TOperation& operatio class Amd64OnlyConverter : public IAmd64OnlyConverter { public: + explicit Amd64OnlyConverter(const Amd64OnlyTarget target) : _matcher(MakeAmd64OnlyInstructionMatcher(target)) {} + [[nodiscard]] ConvertResult Convert( std::vector fileBytes, const std::vector& codeSegments @@ -38,7 +40,7 @@ private: Amd64OnlyMatch Substitution; }; - std::unique_ptr _matcher = MakeAmd64OnlyInstructionMatcher(); + std::unique_ptr _matcher; std::unique_ptr _scanner = MakeInstructionScanner(); void _convertSegment( @@ -205,8 +207,8 @@ ConvertResult Amd64OnlyConverter::Convert( } -std::unique_ptr MakeAmd64OnlyConverter() { - return std::make_unique(); +std::unique_ptr MakeAmd64OnlyConverter(const Amd64OnlyTarget target) { + return std::make_unique(target); } } diff --git a/core/relinker/codegen/src/x86/Amd64OnlyInstructionMatcher.cpp b/core/relinker/codegen/src/x86/Amd64OnlyInstructionMatcher.cpp index caff05f46..35b1e0cc8 100644 --- a/core/relinker/codegen/src/x86/Amd64OnlyInstructionMatcher.cpp +++ b/core/relinker/codegen/src/x86/Amd64OnlyInstructionMatcher.cpp @@ -107,6 +107,19 @@ bool _isClzeroOpcode(const DecodedInstruction& instr) { return pos + 2 < instr.Length && instr.Data[pos] == X64OpcodeConstants::TwoByteOpcodeEscape && instr.Data[pos + 1] == X64OpcodeConstants::TwoByteGrp7 && instr.Data[pos + 2] == 0xFC; } +std::vector _rdpidReplacement(const DecodedInstruction& instr) { + using namespace X64OpcodeConstants; + const auto opcode = instr.OpcodeOffset(); + const auto rex = opcode > 0 && instr.Data[opcode - 1] >= RexMin && instr.Data[opcode - 1] <= RexMax ? instr.Data[opcode - 1] : 0; + const auto reg = static_cast((instr.Data[opcode + 2] & ModRmRmMask) | ((rex & 1) << 3)); + std::vector bytes; + if (reg >= 8) + bytes.push_back(0x41); + bytes.push_back(static_cast(0xB8 + (reg & 7))); + bytes.insert(bytes.end(), 4, 0); + return bytes; +} + bool _validWait(const DecodedInstruction& instr) { const auto opcode = instr.Data + instr.OpcodeOffset(); return instr.Length <= kMaxInstructionLength && std::find(instr.Data, opcode, X64OpcodeConstants::PrefixLock) == opcode; @@ -114,6 +127,8 @@ bool _validWait(const DecodedInstruction& instr) { class Amd64OnlyInstructionMatcher : public IAmd64OnlyInstructionMatcher { public: + explicit Amd64OnlyInstructionMatcher(const Amd64OnlyTarget target) : _target(target) {} + [[nodiscard]] std::optional Match( const std::uint8_t* data, std::size_t length, @@ -126,6 +141,7 @@ public: ) const override; private: + Amd64OnlyTarget _target; Sse4aLowering _lowering; Sha256Lowering _sha256Lowering; Sha1Lowering _sha1Lowering; @@ -137,6 +153,7 @@ private: [[nodiscard]] Amd64OnlyMatch _matchSha256(const DecodedInstruction& instr, std::span trailing) const; [[nodiscard]] Amd64OnlyMatch _matchSha1(const DecodedInstruction& instr, std::span trailing) const; [[nodiscard]] Amd64OnlyMatch _matchClzero(const DecodedInstruction& instr, std::span trailing) const; + [[nodiscard]] std::optional _matchRosetta(const DecodedInstruction& instr, std::span trailing) const; }; Amd64OnlyMatch Amd64OnlyInstructionMatcher::_matchMovnts(const DecodedInstruction& instr, const Entry& entry) const { @@ -177,6 +194,21 @@ Amd64OnlyMatch Amd64OnlyInstructionMatcher::_matchClzero(const DecodedInstructio return _trampoline(kClzero.Name, instr.Length, _outOfLine(instr.Length, trailing, [&](StubBodyBuilder& body) { _clzeroLowering.EmitOutOfLine(body, operands); })); } +std::optional Amd64OnlyInstructionMatcher::_matchRosetta(const DecodedInstruction& instr, std::span trailing) const { + if (_target != Amd64OnlyTarget::Rosetta) + return std::nullopt; + if (instr.IsRdseed()) { + std::vector replacement(instr.Data, instr.Data + instr.Length); + replacement[instr.OpcodeOffset() + 2] &= static_cast(~(1u << X64OpcodeConstants::ModRmRegShift)); + return Amd64OnlyMatch{kRdseed.Name, instr.Length, Amd64OnlyLowering::InPlace, std::move(replacement), {}, 0}; + } + if (instr.IsClwb()) + return _inPlace(kClwb, instr.Length, {}); + if (instr.IsRdpid()) + return _trampoline(kRdpid.Name, instr.Length, _outOfLine(instr.Length, trailing, [&](StubBodyBuilder& body) { body.Raw(_rdpidReplacement(instr)); }), true); + return std::nullopt; +} + std::optional Amd64OnlyInstructionMatcher::MatchSequence( std::span> instructions, std::span trailing @@ -211,13 +243,18 @@ std::optional Amd64OnlyInstructionMatcher::MatchSequence( if (name == nullptr) name = _reciprocalEntry(*reciprocal).Name; _reciprocalLowering.EmitOutOfLine(body, *reciprocal); + } else if (_target == Amd64OnlyTarget::Rosetta && instr.IsRdpid()) { + if (name == nullptr) + name = kRdpid.Name; + body.Raw(_rdpidReplacement(instr)); } else { return std::nullopt; } body.Advance(instr.Length); } _moveTrailing(body, trailing); - const bool optional = DecodeVexReciprocal(instructions.front().data(), instructions.front().size()).has_value(); + const DecodedInstruction first{instructions.front().data(), instructions.front().size()}; + const bool optional = DecodeVexReciprocal(first.Data, first.Length).has_value() || (_target == Amd64OnlyTarget::Rosetta && first.IsRdpid()); return _trampoline(name, instructions.front().size(), body.Finish(), optional); } @@ -268,13 +305,13 @@ std::optional Amd64OnlyInstructionMatcher::Match( if (instr.IsMcommit()) return _unsupported(kMcommit, length); - return std::nullopt; + return _matchRosetta(instr, trailing); } } -std::unique_ptr MakeAmd64OnlyInstructionMatcher() { - return std::make_unique(); +std::unique_ptr MakeAmd64OnlyInstructionMatcher(const Amd64OnlyTarget target) { + return std::make_unique(target); } } diff --git a/core/relinker/codegen/src/x86/DecodedInstruction.cpp b/core/relinker/codegen/src/x86/DecodedInstruction.cpp index 0c91e49ac..3bdb906ef 100644 --- a/core/relinker/codegen/src/x86/DecodedInstruction.cpp +++ b/core/relinker/codegen/src/x86/DecodedInstruction.cpp @@ -262,6 +262,51 @@ bool DecodedInstruction::IsMcommit() const { return Data[pos] == TwoByteOpcodeEscape && Data[pos + 1] == TwoByteGrp7 && Data[pos + 2] == 0xFA; } +bool DecodedInstruction::IsRdseed() const { + bool operandSizeOverride = false; + bool repnePrefix = false; + bool repPrefix = false; + const std::size_t pos = _skipPrefixesAndRex(&operandSizeOverride, &repnePrefix, &repPrefix); + + if (repnePrefix || repPrefix || pos + 2 >= Length) { + return false; + } + + const auto modrm = Data[pos + 2]; + return Data[pos] == TwoByteOpcodeEscape && Data[pos + 1] == TwoByteGrp9 && + ((modrm >> ModRmModShift) & ModRmModMask) == ModRmModRegister && ((modrm >> ModRmRegShift) & ModRmRegMask) == 7; +} + +bool DecodedInstruction::IsRdpid() const { + bool operandSizeOverride = false; + bool repnePrefix = false; + bool repPrefix = false; + const std::size_t pos = _skipPrefixesAndRex(&operandSizeOverride, &repnePrefix, &repPrefix); + + if (operandSizeOverride || repnePrefix || !repPrefix || pos + 2 >= Length) { + return false; + } + + const auto modrm = Data[pos + 2]; + return Data[pos] == TwoByteOpcodeEscape && Data[pos + 1] == TwoByteGrp9 && + ((modrm >> ModRmModShift) & ModRmModMask) == ModRmModRegister && ((modrm >> ModRmRegShift) & ModRmRegMask) == 7; +} + +bool DecodedInstruction::IsClwb() const { + bool operandSizeOverride = false; + bool repnePrefix = false; + bool repPrefix = false; + const std::size_t pos = _skipPrefixesAndRex(&operandSizeOverride, &repnePrefix, &repPrefix); + + if (!operandSizeOverride || repnePrefix || repPrefix || pos + 2 >= Length) { + return false; + } + + const auto modrm = Data[pos + 2]; + return Data[pos] == TwoByteOpcodeEscape && Data[pos + 1] == TwoByteGrp15 && + ((modrm >> ModRmModShift) & ModRmModMask) != ModRmModRegister && ((modrm >> ModRmRegShift) & ModRmRegMask) == 6; +} + bool DecodedInstruction::IsMovntss() const { bool operandSizeOverride = false; bool repnePrefix = false; diff --git a/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp b/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp index 236b8e9cd..dba57fd9f 100644 --- a/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp +++ b/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp @@ -649,6 +649,56 @@ void converterClzero() { require(failureOffset([&] { (void)converter->Convert(segmentRelative, {segmentHeader(text.size())}); }, "FS-relative CLZERO was accepted") == 0x20A, "CLZERO operand failure does not carry the file offset"); } +void rosettaSubstitutions() { + using Codegen::Amd64OnlyLowering; + const auto intel = Codegen::MakeAmd64OnlyInstructionMatcher(); + const auto rosetta = Codegen::MakeAmd64OnlyInstructionMatcher(Codegen::Amd64OnlyTarget::Rosetta); + const auto match = [](const auto& matcher, const Bytes& bytes) { return matcher->Match(bytes.data(), bytes.size()); }; + for (const auto& bytes : {Bytes{0x0F, 0xC7, 0xF8}, Bytes{0xF3, 0x0F, 0xC7, 0xF8}, Bytes{0x66, 0x0F, 0xAE, 0x30}}) + require(!match(intel, bytes).has_value(), "RDSEED, RDPID or CLWB was converted for an Intel host"); + const auto rdseed = match(rosetta, {0x48, 0x0F, 0xC7, 0xF9}); + require(rdseed && rdseed->Lowering == Amd64OnlyLowering::InPlace && rdseed->ReplacementBytes == Bytes{0x48, 0x0F, 0xC7, 0xF1} && rdseed->InstructionName == "RDSEED", "RDSEED was not rewritten to RDRAND"); + const auto rdseed16 = match(rosetta, {0x66, 0x41, 0x0F, 0xC7, 0xFA}); + require(rdseed16 && rdseed16->ReplacementBytes == Bytes{0x66, 0x41, 0x0F, 0xC7, 0xF2}, "16-bit RDSEED was not rewritten to RDRAND"); + require(!match(rosetta, {0x0F, 0xC7, 0x38}).has_value(), "VMPTRST (the memory form of 0F C7 /7) was taken for RDSEED"); + const auto clwb = match(rosetta, {0x66, 0x0F, 0xAE, 0x74, 0x24, 0x08}); + require(clwb && clwb->Lowering == Amd64OnlyLowering::InPlace && clwb->ReplacementBytes == Bytes{0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00} && clwb->InstructionName == "CLWB", "CLWB was not replaced by a NOP"); + require(!match(rosetta, {0x66, 0x0F, 0xAE, 0xF1}).has_value(), "TPAUSE (the register form of 66 0F AE /6) was taken for CLWB"); + require(!match(rosetta, {0x66, 0x0F, 0xAE, 0x38}).has_value(), "CLFLUSHOPT was taken for CLWB"); + const auto rdpid = match(rosetta, {0xF3, 0x41, 0x0F, 0xC7, 0xFB}); + require(rdpid && rdpid->Lowering == Amd64OnlyLowering::Trampoline && rdpid->Optional && rdpid->InstructionName == "RDPID", "RDPID was not lowered through an optional stub"); + require(rdpid->ReturnBranchOffset == 6 && Bytes(rdpid->StubBody.begin(), rdpid->StubBody.begin() + 7) == Bytes{0x41, 0xBB, 0, 0, 0, 0, 0xE9}, "RDPID r11 stub does not load 0 before returning"); +} + +void converterRosetta() { + const auto converter = Codegen::MakeAmd64OnlyConverter(Codegen::Amd64OnlyTarget::Rosetta); + Bytes file(0x300, 0xCC); + const Bytes text = { + 0x0F, 0xC7, 0xF8, + 0xF3, 0x0F, 0xC7, 0xF9, + 0x48, 0x83, 0xC1, 0x01, + 0x66, 0x0F, 0xAE, 0x37, + 0xC3}; + std::copy(text.begin(), text.end(), file.begin() + 0x200); + const auto result = converter->Convert(file, {segmentHeader(text.size())}); + require(result.ReplacedCount == 2 && result.Trampolines.size() == 1 && result.KeptCount == 0 && result.Reports.size() == 3, "RDSEED, RDPID and CLWB were not converted for Rosetta"); + const auto& site = result.Trampolines[0]; + require(site.Offset == 0x203 && site.Length == 8 && result.Reports[1].InstructionName == "RDPID", "RDPID did not absorb the following instruction"); + const Bytes add = {0x48, 0x83, 0xC1, 0x01}; + require(Bytes(site.Body.begin(), site.Body.begin() + 5) == Bytes{0xB9, 0, 0, 0, 0} && Bytes(site.Body.begin() + static_cast(site.ReturnBranchOffset - add.size()), site.Body.begin() + static_cast(site.ReturnBranchOffset)) == add, "RDPID stub does not load 0 and then run the absorbed instruction"); + auto expected = file; + expected[0x202] = 0xF0; + const Bytes nop = {0x0F, 0x1F, 0x40, 0x00}; + std::copy(nop.begin(), nop.end(), expected.begin() + 0x20B); + require(result.Bytes == expected, "RDSEED or CLWB was replaced with the wrong bytes"); + auto beforeReturn = file; + beforeReturn[0x207] = 0xC3; + const auto kept = converter->Convert(beforeReturn, {segmentHeader(text.size())}); + require(kept.KeptCount == 1 && kept.Trampolines.empty() && kept.ReplacedCount == 2, "RDPID without room for a jump was not kept"); + const auto intel = Codegen::MakeAmd64OnlyConverter()->Convert(file, {segmentHeader(text.size())}); + require(intel.Reports.empty() && intel.Bytes == file, "The Intel target converted RDSEED, RDPID or CLWB"); +} + void reciprocalOperands() { const auto vex2 = Codegen::DecodeVexReciprocal(Bytes{0xC5, 0xF8, 0x52, 0xD5}.data(), 4); require(vex2 && vex2->Operation == Codegen::ReciprocalOperation::ReciprocalSquareRoot && vex2->Destination == 2 && vex2->Source == 5, "VEX2 VRSQRTPS was not decoded"); @@ -1340,6 +1390,8 @@ int main() { converterSha1(); converterMonitorWait(); converterClzero(); + rosettaSubstitutions(); + converterRosetta(); converterReciprocal(); converterStrayRex(); rewriterStrayRex(); diff --git a/core/relinker/main.cpp b/core/relinker/main.cpp index 87bb581ac..922290659 100644 --- a/core/relinker/main.cpp +++ b/core/relinker/main.cpp @@ -66,7 +66,7 @@ int Run(const int argc, char* argv[]) { std::vector trampolines; if (args.toIntel) { const auto codeSegments = Relinker::ElfReader(sourceBytes).ReadCodeSegments(); - auto converted = Codegen::MakeAmd64OnlyConverter()->Convert(std::move(sourceBytes), codeSegments); + auto converted = Codegen::MakeAmd64OnlyConverter(args.toRosetta ? Codegen::Amd64OnlyTarget::Rosetta : Codegen::Amd64OnlyTarget::Intel)->Convert(std::move(sourceBytes), codeSegments); sourceBytes = std::move(converted.Bytes); trampolines = std::move(converted.Trampolines); std::map stubsByName; @@ -108,7 +108,7 @@ int Run(const int argc, char* argv[]) { std::vector guestArtifacts; if (!args.skipSceModule) { - guestArtifacts = Relinker::GuestModuleBuilder().Build(Io::NativePath(args.inputPath), Io::NativePath(absPath), result.DynamicSection, args.toWindows, args.toMacos, args.toIntel, *syscallScanner, args.lazyBinding, args.runPath, args.excludedSceModules, Io::NativePath(args.sceModulePath)); + guestArtifacts = Relinker::GuestModuleBuilder().Build(Io::NativePath(args.inputPath), Io::NativePath(absPath), result.DynamicSection, args.toWindows, args.toMacos, args.toIntel, args.toRosetta, *syscallScanner, args.lazyBinding, args.runPath, args.excludedSceModules, Io::NativePath(args.sceModulePath)); } if (args.writeRegistry) { diff --git a/core/relinker/relinker/include/relinker/guest/GuestImage.hpp b/core/relinker/relinker/include/relinker/guest/GuestImage.hpp index e80610e72..8ba1296d6 100644 --- a/core/relinker/relinker/include/relinker/guest/GuestImage.hpp +++ b/core/relinker/relinker/include/relinker/guest/GuestImage.hpp @@ -58,7 +58,7 @@ struct GuestArtifact { class GuestModuleBuilder { public: - std::vector Build(const std::filesystem::path& inputPath, const std::filesystem::path& outputPath, Domain::SysVDynamicSection& dynamic, bool windows, bool macos, bool toIntel, ISyscallScanner& syscallScanner, bool lazyBinding, const std::string& runPath, const std::set& excludedModules, const std::filesystem::path& sceModulePath) const; + std::vector Build(const std::filesystem::path& inputPath, const std::filesystem::path& outputPath, Domain::SysVDynamicSection& dynamic, bool windows, bool macos, bool toIntel, bool toRosetta, ISyscallScanner& syscallScanner, bool lazyBinding, const std::string& runPath, const std::set& excludedModules, const std::filesystem::path& sceModulePath) const; }; } diff --git a/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp b/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp index 4f8bb12bd..e4ad42a72 100644 --- a/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp +++ b/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp @@ -50,7 +50,7 @@ std::string ModuleStem(std::string name, const bool windows) { } -std::vector GuestModuleBuilder::Build(const std::filesystem::path& inputPath, const std::filesystem::path& outputPath, Domain::SysVDynamicSection& dynamic, const bool windows, const bool macos, const bool toIntel, ISyscallScanner& syscallScanner, const bool lazyBinding, const std::string& runPath, const std::set& excludedModules, const std::filesystem::path& sceModulePath) const { +std::vector GuestModuleBuilder::Build(const std::filesystem::path& inputPath, const std::filesystem::path& outputPath, Domain::SysVDynamicSection& dynamic, const bool windows, const bool macos, const bool toIntel, const bool toRosetta, ISyscallScanner& syscallScanner, const bool lazyBinding, const std::string& runPath, const std::set& excludedModules, const std::filesystem::path& sceModulePath) const { const auto root = std::filesystem::absolute(sceModulePath).lexically_normal(); if (!std::filesystem::exists(root)) throw Domain::RelinkerException("Guest module parent directory does not exist: " + Io::Utf8Path(root)); if (!std::filesystem::is_directory(root)) throw Domain::RelinkerException("Guest module parent path is not a directory: " + Io::Utf8Path(root)); @@ -147,7 +147,7 @@ std::vector GuestModuleBuilder::Build(const std::filesystem::path std::vector codeHeaders; for (const auto& header : image.Headers) if (header.Type == 1 && (header.Flags & 1) != 0) codeHeaders.push_back(header); if (toIntel) { - auto converted = Codegen::MakeAmd64OnlyConverter()->Convert(std::move(image.Bytes), codeHeaders); + auto converted = Codegen::MakeAmd64OnlyConverter(toRosetta ? Codegen::Amd64OnlyTarget::Rosetta : Codegen::Amd64OnlyTarget::Intel)->Convert(std::move(image.Bytes), codeHeaders); image.Trampolines = std::move(converted.Trampolines); image.Bytes = std::move(converted.Bytes); } diff --git a/core/relinker/relinker/tests/test_to_rosetta.py b/core/relinker/relinker/tests/test_to_rosetta.py new file mode 100644 index 000000000..5d35e0186 --- /dev/null +++ b/core/relinker/relinker/tests/test_to_rosetta.py @@ -0,0 +1,59 @@ +"""Check that --to-rosetta lowers what Rosetta lacks, and that --to-intel and a plain relink leave it alone.""" + +from pathlib import Path +import struct +import subprocess +import sys +import tempfile + +from test_linux_load_alignment import fixture + +RDSEED = bytes.fromhex("0fc7f8") +RDRAND = bytes.fromhex("0fc7f0") +CLWB = bytes.fromhex("660fae37") +NOP4 = bytes.fromhex("0f1f4000") +SITES = RDSEED + CLWB + b"\xc3" +LOWERED = RDRAND + NOP4 + b"\xc3" + + +def executable(): + image = fixture() + struct.pack_into("