#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main(const int argc, char* argv[]) { bool skipSyscallCheck = false; bool toIntel = false; std::uint32_t unusedFilterLevel = 2; bool unusedFilterSpecified = false; bool writeRegistry = false; bool toWindows = false; bool lazyBinding = false; std::string inputPath; std::string outputPath; std::string runPath = "$ORIGIN/libs"; for (int i = 1; i < argc; ++i) { const std::string arg = argv[i]; if (arg == "--skip-syscall-check") { skipSyscallCheck = true; } else if (arg == "--to-intel") { 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') { std::cerr << "FAIL: unused-filter must be specified once with a value of 0, 1 or 2\n"; return 1; } unusedFilterLevel = static_cast(value[0] - '0'); unusedFilterSpecified = true; } else if (arg == "--registry") { writeRegistry = true; } else if (arg == "--rpath") { if (i + 1 >= argc) { std::cerr << "FAIL: --rpath requires a value\n"; return 1; } runPath = argv[++i]; } else if (arg == "--windows") { toWindows = true; } else if (arg == "--lazy-binding") { lazyBinding = true; } else if (arg.rfind("--", 0) == 0 || arg == "unused-filter") { std::cerr << "FAIL: unknown option: " << arg << "\n"; return 1; } else if (inputPath.empty()) { inputPath = arg; } else if (outputPath.empty()) { outputPath = arg; } else { std::cerr << "FAIL: unexpected argument: " << arg << "\n"; return 1; } } if (inputPath.empty() || outputPath.empty()) { std::cerr << "Usage: relinker [--windows] [--skip-syscall-check] [--to-intel] [unused-filter=0|1|2] [--registry] [--rpath ] [--lazy-binding] \n" "Example: relinker input.elf output.elf\n"; return 1; } try { Io::FileReader fileReader; Io::FileWriter fileWriter; auto sourceBytes = fileReader.Read(inputPath); if (toIntel) { std::cout << "Mode: Intel instruction conversion; system unchanged; unused-filter=" << unusedFilterLevel << " (not applied)\n"; const Relinker::ElfReader elfReader(sourceBytes); const auto converter = Codegen::MakeAmd64OnlyConverter(); auto result = converter->Convert(std::move(sourceBytes), elfReader.ReadCodeSegments()); fileWriter.Write(outputPath, std::move(result.Bytes)); std::cout << "OK: " << result.ReplacedCount << " instructions replaced\n"; return 0; } auto elfReader = std::make_shared(sourceBytes); auto syscallScanner = skipSyscallCheck ? Relinker::MakeNullSyscallScanner() : Relinker::MakeSyscallScanner(); const auto pipeline = std::make_shared( elfReader, std::move(syscallScanner), Relinker::MakeCallSiteResolver(), std::make_shared(), std::make_shared(), unusedFilterLevel == 2 ? Relinker::MakeStrictUnusedNidFilter() : Relinker::MakeUnusedNidFilter(), unusedFilterLevel ); std::cout << "System: " << (toWindows ? "Windows" : "Linux") << "; unused-filter=" << unusedFilterLevel << "\n"; auto result = pipeline->Relink(sourceBytes); for (const auto& patch : result.Patches) { if (patch.Offset > sourceBytes.size() || patch.Bytes.size() > sourceBytes.size() - patch.Offset) throw Domain::RelinkerException("Relinker patch exceeds source image", patch.Offset); for (std::size_t index = 0; index < patch.Bytes.size(); ++index) sourceBytes[patch.Offset + index] = patch.Bytes[index]; } if (writeRegistry) { const std::filesystem::path outFsPath(outputPath); const std::string registryPath = (outFsPath.parent_path() / (outFsPath.stem().string() + ".registry.json")).string(); auto callRegistryWriter = std::make_shared(); fileWriter.Write(registryPath, callRegistryWriter->WriteCallRegistry(result.RegistryEntries)); } auto byteWriter = std::make_shared(); std::shared_ptr patcher; if (toWindows) { patcher = std::make_shared(); } else { patcher = std::make_shared( std::make_shared(), std::make_shared( std::make_shared(), byteWriter ), std::make_shared(byteWriter), byteWriter ); } auto patched = patcher->Patch(sourceBytes, result.OriginalHeaders, result.DynamicSection, result.OriginalPltGotVaddr, runPath, lazyBinding); fileWriter.Write(outputPath, patched); std::cout << "External prx references: " << result.RegistryEntries.size() << "\nOutput file: " << outputPath << '\n'; } catch (const Domain::RelinkerException& e) { std::cerr << "FAIL: " << e.what(); if (e.FailureOffset != 0) std::cerr << " (offset 0x" << std::hex << e.FailureOffset << ")"; std::cerr << "\n"; return 2; } catch (const std::exception& e) { std::cerr << "FAIL: " << e.what() << "\n"; return 2; } return 0; }