feat(cli): add --windows-diagnostics arg - off by default

This commit is contained in:
boykopovar 2026-09-13 17:21:13 +03:00
parent 04e1bc0a2d
commit 7e5f41aba3
10 changed files with 44 additions and 23 deletions

View File

@ -11,6 +11,7 @@ struct Args {
bool toWindows = false;
bool lazyBinding = false;
bool autorun = false;
bool windowsDiagnostics = false;
std::uint32_t unusedFilterLevel = 2;
std::string inputPath;
std::string outputPath;

View File

@ -33,6 +33,8 @@ Args ParseArgs(int argc, char* argv[]) {
args.lazyBinding = true;
} else if (arg == "--autorun") {
args.autorun = true;
} else if (arg == "--windows-diagnostics") {
args.windowsDiagnostics = true;
} else if (arg.rfind("--", 0) == 0 || arg == "unused-filter") {
throw std::runtime_error("unknown option: " + arg);
} else if (args.inputPath.empty()) {
@ -44,9 +46,12 @@ Args ParseArgs(int argc, char* argv[]) {
}
}
if (args.windowsDiagnostics && !args.toWindows)
throw std::runtime_error("--windows-diagnostics requires --windows");
if (args.inputPath.empty() || args.outputPath.empty())
throw std::runtime_error(
"Usage: relinker [--windows] [--skip-syscall-check] [--to-intel] [unused-filter=0|1|2] [--registry] [--rpath <path>] [--lazy-binding] [--autorun] <input.elf> <output.elf>\n"
"Usage: relinker [--windows] [--windows-diagnostics] [--skip-syscall-check] [--to-intel] [unused-filter=0|1|2] [--registry] [--rpath <path>] [--lazy-binding] [--autorun] <input.elf> <output.elf>\n"
"Example: relinker input.elf output.elf"
);

View File

@ -18,7 +18,8 @@ public:
const Domain::SysVDynamicSection& dynamicSection,
std::uint64_t originalPltGotVaddr,
const std::string& runPath,
bool lazyBinding
bool lazyBinding,
bool dependencyDiagnostics
) = 0;
};

View File

@ -26,7 +26,8 @@ public:
const Domain::SysVDynamicSection& dynamicSection,
std::uint64_t originalPltGotVaddr,
const std::string& runPath,
bool lazyBinding
bool lazyBinding,
bool dependencyDiagnostics
) override;
private:

View File

@ -7,7 +7,7 @@ namespace Elfpatcher::Windows {
class WindowsPePatcher : public IElfPatcher {
public:
std::vector<std::uint8_t> Patch(const std::vector<std::uint8_t>& sourceElf, const std::vector<Domain::ProgramHeader>& originalHeaders, const Domain::SysVDynamicSection& dynamicSection, std::uint64_t originalPltGotVaddr, const std::string& runPath, bool lazyBinding) override;
std::vector<std::uint8_t> Patch(const std::vector<std::uint8_t>& sourceElf, const std::vector<Domain::ProgramHeader>& originalHeaders, const Domain::SysVDynamicSection& dynamicSection, std::uint64_t originalPltGotVaddr, const std::string& runPath, bool lazyBinding, bool dependencyDiagnostics) override;
};
}

View File

@ -19,7 +19,7 @@ struct WindowsEntryStub {
class WindowsEntryStubBuilder {
public:
WindowsEntryStub Build(std::uint32_t dataRva, std::uint32_t entryRva, const WindowsImports& nativeImports, const std::vector<std::string>& libraries, const std::vector<PeImport>& imports, const std::string& runPath, bool lazyBinding) const;
WindowsEntryStub Build(std::uint32_t dataRva, std::uint32_t entryRva, const WindowsImports& nativeImports, const std::vector<std::string>& libraries, const std::vector<PeImport>& imports, const std::string& runPath, bool lazyBinding, bool dependencyDiagnostics) const;
};
}

View File

@ -30,8 +30,12 @@ std::vector<std::uint8_t> LinuxElfPatcher::Patch(
const Domain::SysVDynamicSection& dynSection,
const std::uint64_t originalPltGotVaddr,
const std::string& runPath,
const bool lazyBinding)
const bool lazyBinding,
const bool dependencyDiagnostics)
{
if (dependencyDiagnostics)
throw Domain::RelinkerException("Linux target does not support --windows-diagnostics");
std::vector<std::uint8_t> buf = sourceElf;
buf[kEhdrOsAbiOffset] = 0;

View File

@ -3,6 +3,7 @@
#include <elfpatcher/windows/WindowsDependencyStubBuilder.hpp>
#include <io/BufferUtils.hpp>
#include <algorithm>
#include <optional>
namespace Elfpatcher::Windows {
@ -28,7 +29,7 @@ std::string normalizeRunPath(std::string path) {
}
WindowsEntryStub WindowsEntryStubBuilder::Build(const std::uint32_t dataRva, const std::uint32_t entryRva, const WindowsImports& nativeImports, const std::vector<std::string>& libraries, const std::vector<PeImport>& imports, const std::string& runPath, const bool lazyBinding) const {
WindowsEntryStub WindowsEntryStubBuilder::Build(const std::uint32_t dataRva, const std::uint32_t entryRva, const WindowsImports& nativeImports, const std::vector<std::string>& libraries, const std::vector<PeImport>& imports, const std::string& runPath, const bool lazyBinding, const bool dependencyDiagnostics) const {
if (!imports.empty() && libraries.empty())
throw Domain::RelinkerException("ELF imports have no DT_NEEDED libraries");
const auto path = normalizeRunPath(runPath);
@ -97,7 +98,9 @@ WindowsEntryStub WindowsEntryStubBuilder::Build(const std::uint32_t dataRva, con
if (data.size() <= diagnosticsOffset)
throw Domain::RelinkerException("Empty startup diagnostics");
const WindowsDependencyStubBuilder dependencyBuilder(result.Data);
std::optional<WindowsDependencyStubBuilder> dependencyBuilder;
if (dependencyDiagnostics)
dependencyBuilder.emplace(result.Data);
std::vector<std::size_t> dependencyCalls;
result.Code.Rva = AlignRva(dataRva + data.size());
WindowsStubEmitter code(result.Code.Rva);
@ -247,9 +250,11 @@ WindowsEntryStub WindowsEntryStubBuilder::Build(const std::uint32_t dataRva, con
captureLastError();
writeString(loadFailed, true);
writeLastError();
code.Rip({0x48, 0x8d, 0x0d}, resolvedPaths[index]);
code.Rip({0x48, 0x8d, 0x15}, programPath);
dependencyCalls.push_back(code.Branch({0xe8}));
if (dependencyDiagnostics) {
code.Rip({0x48, 0x8d, 0x0d}, resolvedPaths[index]);
code.Rip({0x48, 0x8d, 0x15}, programPath);
dependencyCalls.push_back(code.Branch({0xe8}));
}
raise(0xc0000135u);
code.PatchBranch(loadSucceeded, code.GetRva());
code.Rip({0x48, 0x89, 0x05}, CheckedRva(handles + index * 8));
@ -332,16 +337,20 @@ WindowsEntryStub WindowsEntryStubBuilder::Build(const std::uint32_t dataRva, con
Io::WriteU32(data, functionTable - dataRva, result.Code.Rva);
Io::WriteU32(data, functionTable - dataRva + 4, functionEnd);
Io::WriteU32(data, functionTable - dataRva + 8, unwindRva);
const auto dependency = dependencyBuilder.Build(code, nativeImports);
for (const auto offset : dependencyCalls)
code.PatchBranch(offset, dependency.EntryRva);
if (dependency.Functions.size() >= 32)
throw Domain::RelinkerException("Too many dependency diagnostic routines");
for (std::size_t index = 0; index < dependency.Functions.size(); ++index) {
for (std::size_t field = 0; field < 3; ++field)
Io::WriteU32(data, functionTable - dataRva + (index + 1) * 12 + field * 4, dependency.Functions[index][field]);
auto exceptionEntries = CheckedRva(1);
if (dependencyBuilder.has_value()) {
const auto dependency = dependencyBuilder->Build(code, nativeImports);
for (const auto offset : dependencyCalls)
code.PatchBranch(offset, dependency.EntryRva);
if (dependency.Functions.size() >= 32)
throw Domain::RelinkerException("Too many dependency diagnostic routines");
for (std::size_t index = 0; index < dependency.Functions.size(); ++index) {
for (std::size_t field = 0; field < 3; ++field)
Io::WriteU32(data, functionTable - dataRva + (index + 1) * 12 + field * 4, dependency.Functions[index][field]);
}
exceptionEntries = CheckedRva(dependency.Functions.size() + 1);
}
result.ExceptionDirectory = {functionTable, CheckedRva((dependency.Functions.size() + 1) * 12)};
result.ExceptionDirectory = {functionTable, CheckedRva(exceptionEntries * 12)};
result.Code.Data = code.TakeBytes();
return result;
}

View File

@ -23,7 +23,7 @@ void writeGotStub(std::vector<PeSection>& sections, const std::uint32_t targetRv
}
std::vector<std::uint8_t> WindowsPePatcher::Patch(const std::vector<std::uint8_t>& sourceElf, const std::vector<Domain::ProgramHeader>& originalHeaders, const Domain::SysVDynamicSection& dynamicSection, const std::uint64_t originalPltGotVaddr, const std::string& runPath, const bool lazyBinding) {
std::vector<std::uint8_t> WindowsPePatcher::Patch(const std::vector<std::uint8_t>& sourceElf, const std::vector<Domain::ProgramHeader>& originalHeaders, const Domain::SysVDynamicSection& dynamicSection, const std::uint64_t originalPltGotVaddr, const std::string& runPath, const bool lazyBinding, const bool dependencyDiagnostics) {
WindowsLoadImage image(sourceElf, originalHeaders);
if (originalPltGotVaddr != 0)
image.GetRva(originalPltGotVaddr, 8);
@ -52,7 +52,7 @@ std::vector<std::uint8_t> WindowsPePatcher::Patch(const std::vector<std::uint8_t
directories[12] = nativeImports.AddressTable;
nextRva = AlignRva(nextRva + nativeImports.Section.Data.size());
const auto libraries = importBuilder.ReadLibraries(dynamicSection);
auto entry = WindowsEntryStubBuilder().Build(nextRva, image.GetEntryRva(), nativeImports, libraries, relocations.Imports, runPath, lazyBinding);
auto entry = WindowsEntryStubBuilder().Build(nextRva, image.GetEntryRva(), nativeImports, libraries, relocations.Imports, runPath, lazyBinding, dependencyDiagnostics);
directories[3] = entry.ExceptionDirectory;
const auto entryRva = entry.Code.Rva;
sections.push_back(std::move(nativeImports.Section));

View File

@ -96,7 +96,7 @@ int main(const int argc, char* argv[]) {
);
}
fileWriter.Write(absPath, patcher->Patch(sourceBytes, result.OriginalHeaders, result.DynamicSection, result.OriginalPltGotVaddr, args.runPath, args.lazyBinding));
fileWriter.Write(absPath, patcher->Patch(sourceBytes, result.OriginalHeaders, result.DynamicSection, result.OriginalPltGotVaddr, args.runPath, args.lazyBinding, args.windowsDiagnostics));
std::cout << "External prx references: " << result.RegistryEntries.size() << "\nOutput file: " << absPath << '\n';
if (args.autorun) Cli::Autorun(absPath, args.toWindows);