fix(relinker): report intermediate output image progress

This commit is contained in:
boykopovar 2026-10-13 12:47:30 +03:00
parent d35b1afa51
commit a534bd67ca
10 changed files with 114 additions and 51 deletions

View File

@ -3,18 +3,20 @@
#include <elfpatcher/general/IElfPatcher.hpp>
#include <optional>
#include <functional>
#include <filesystem>
namespace Elfpatcher::Windows {
class WindowsPePatcher : public IElfPatcher {
public:
explicit WindowsPePatcher(bool windowsGui = false, std::optional<std::vector<std::uint8_t>> icon = std::nullopt, std::filesystem::path iconName = "icon0.png");
explicit WindowsPePatcher(bool windowsGui = false, std::optional<std::vector<std::uint8_t>> icon = std::nullopt, std::filesystem::path iconName = "icon0.png", std::function<void(std::uint64_t, std::uint64_t)> progress = {});
const std::string& GetDiagnostics() const;
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, const std::vector<Codegen::TrampolineSite>& trampolines) override;
private:
std::function<void(std::uint64_t, std::uint64_t)> progress;
bool windowsGui;
std::optional<std::vector<std::uint8_t>> icon;
std::string diagnostics;

View File

@ -2,12 +2,13 @@
#define ELFPATCHER_WINDOWS_WINDOWSTLSBUILDER_HPP
#include <elfpatcher/windows/WindowsLoadImage.hpp>
#include <functional>
namespace Elfpatcher::Windows {
class WindowsTlsBuilder {
public:
PeDirectory Build(const std::vector<std::uint8_t>& source, const std::vector<Domain::ProgramHeader>& headers, const WindowsLoadImage& image, std::vector<PeSection>& sections, std::vector<std::uint32_t>& relocations, std::uint32_t& nextRva, std::uint32_t* tlsIndexRva = nullptr) const;
PeDirectory Build(const std::vector<std::uint8_t>& source, const std::vector<Domain::ProgramHeader>& headers, const WindowsLoadImage& image, std::vector<PeSection>& sections, std::vector<std::uint32_t>& relocations, std::uint32_t& nextRva, std::uint32_t* tlsIndexRva = nullptr, const std::function<void(std::uint64_t, std::uint64_t)>& progress = {}) const;
};
}

View File

@ -28,19 +28,24 @@ void writeGotStub(std::vector<PeSection>& sections, const std::uint32_t targetRv
}
WindowsPePatcher::WindowsPePatcher(const bool windowsGui, std::optional<std::vector<std::uint8_t>> icon, std::filesystem::path iconName) : windowsGui(windowsGui), icon(std::move(icon)), iconName(std::move(iconName)) {
WindowsPePatcher::WindowsPePatcher(const bool windowsGui, std::optional<std::vector<std::uint8_t>> icon, std::filesystem::path iconName, std::function<void(std::uint64_t, std::uint64_t)> progress) : progress(std::move(progress)), windowsGui(windowsGui), icon(std::move(icon)), iconName(std::move(iconName)) {
}
const std::string& WindowsPePatcher::GetDiagnostics() const { return diagnostics; }
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, const std::vector<Codegen::TrampolineSite>& trampolines) {
const auto report = [&](std::uint64_t completed) { if (progress) progress(completed, 10000); };
report(0);
diagnostics.clear();
WindowsLoadImage image(sourceElf, originalHeaders);
if (originalPltGotVaddr != 0)
image.GetRva(originalPltGotVaddr, 8);
report(500);
const WindowsRelocationBuilder relocationBuilder;
auto relocations = relocationBuilder.Apply(image, dynamicSection);
report(1000);
auto sections = image.BuildSections();
report(1500);
std::array<PeDirectory, 16> directories{};
auto nextRva = image.GetEndRva();
bool hasProcessParameters = false;
@ -61,7 +66,8 @@ std::vector<std::uint8_t> WindowsPePatcher::Patch(const std::vector<std::uint8_t
sections.push_back({".ehmeta", nextRva, SectionRead | 0x40u, std::move(metadata)});
nextRva = AlignRva(nextRva + sections.back().Data.size());
}
directories[9] = WindowsTlsBuilder().Build(sourceElf, originalHeaders, image, sections, relocations.BaseRelocations, nextRva);
directories[9] = WindowsTlsBuilder().Build(sourceElf, originalHeaders, image, sections, relocations.BaseRelocations, nextRva, nullptr, [&](auto completed, auto total) { report(1500 + 6500 * completed / total); });
report(8000);
WindowsTrampolineBuilder().Build(trampolines, image, sections, nextRva);
const WindowsImportBuilder importBuilder;
auto nativeImports = importBuilder.Build(nextRva);
@ -78,7 +84,9 @@ std::vector<std::uint8_t> WindowsPePatcher::Patch(const std::vector<std::uint8_t
libraries.insert(libraries.begin(), guestPaths.begin(), guestPaths.end());
if (dependencyDiagnostics)
for (const auto& import : relocations.Imports) diagnostics += import.Name + '\n';
report(8500);
auto entry = WindowsEntryStubBuilder().Build(nextRva, image.GetEntryRva(), nativeImports, libraries, relocations.Imports, runPath, lazyBinding, dependencyDiagnostics, dynamicSection.GuestModules, dynamicSection.UnmatchedImportModules);
report(9000);
directories[3] = entry.ExceptionDirectory;
const auto entryRva = entry.Code.Rva;
nextRva = AlignRva(entry.Code.Rva + entry.Code.Data.size());
@ -96,7 +104,10 @@ std::vector<std::uint8_t> WindowsPePatcher::Patch(const std::vector<std::uint8_t
nextRva = AlignRva(nextRva + sections.back().Data.size());
}
directories[2] = icon ? WindowsIconResourceBuilder().Build(*icon, iconName, sections, nextRva) : PeDirectory{};
return WindowsPeWriter().Write(sections, entryRva, directories, windowsGui);
report(9500);
auto result = WindowsPeWriter().Write(sections, entryRva, directories, windowsGui);
report(10000);
return result;
}
}

View File

@ -92,13 +92,15 @@ void patchAccess(std::vector<PeSection>& sections, const TlsAccess& access, cons
}
PeDirectory WindowsTlsBuilder::Build(const std::vector<std::uint8_t>& source, const std::vector<Domain::ProgramHeader>& headers, const WindowsLoadImage& image, std::vector<PeSection>& sections, std::vector<std::uint32_t>& relocations, std::uint32_t& nextRva, std::uint32_t* tlsIndexRva) const {
PeDirectory WindowsTlsBuilder::Build(const std::vector<std::uint8_t>& source, const std::vector<Domain::ProgramHeader>& headers, const WindowsLoadImage& image, std::vector<PeSection>& sections, std::vector<std::uint32_t>& relocations, std::uint32_t& nextRva, std::uint32_t* tlsIndexRva, const std::function<void(std::uint64_t, std::uint64_t)>& progress) const {
const auto report = [&](std::uint64_t completed) { if (progress) progress(completed, 10000); };
const Domain::ProgramHeader* tls = nullptr;
const Codegen::X64InstructionDecoder decoder;
std::vector<TlsAccess> accesses;
std::set<std::uint32_t> branchTargets;
const auto instructions = Relinker::CodeInstructionCollector().Collect(source, headers);
const auto instructions = Relinker::CodeInstructionCollector().Collect(source, headers, [&](auto completed, auto total) { report(7000 * completed / total); });
std::uint64_t scanned = 0;
for (const auto& header : headers) {
if (header.Type == 7) {
@ -110,6 +112,8 @@ PeDirectory WindowsTlsBuilder::Build(const std::vector<std::uint8_t>& source, co
continue;
std::uint64_t decodedEnd = 0;
for (auto instruction = instructions.lower_bound(header.MappedAddress); instruction != instructions.end() && *instruction - header.MappedAddress < header.FileSize; ++instruction) {
if (scanned % 4096 == 0) report(7000 + 2000 * scanned / std::max<std::size_t>(instructions.size(), 1));
++scanned;
const auto offset = *instruction - header.MappedAddress;
const auto* bytes = source.data() + header.Offset + offset;
const auto info = decoder.DecodeInstruction(bytes, header.FileSize - offset);
@ -187,6 +191,7 @@ PeDirectory WindowsTlsBuilder::Build(const std::vector<std::uint8_t>& source, co
}
}
report(9000);
if (tls == nullptr) {
if (!accesses.empty())
throw Domain::RelinkerException("Guest TLS access without PT_TLS");
@ -238,7 +243,10 @@ PeDirectory WindowsTlsBuilder::Build(const std::vector<std::uint8_t>& source, co
code.PatchBranch(skipCallback, code.GetRva());
code.Emit({0xc3});
std::uint64_t patched = 0;
for (const auto& access : accesses) {
if (patched % 256 == 0) report(9000 + 900 * patched / accesses.size());
++patched;
const auto target = branchTargets.upper_bound(access.Rva);
if (target != branchTargets.end() && *target < access.Rva + access.Length)
throw Domain::RelinkerException("Branch enters a guest TLS instruction", *target);
@ -390,6 +398,7 @@ PeDirectory WindowsTlsBuilder::Build(const std::vector<std::uint8_t>& source, co
code.Rip({0xe9}, CheckedRva(access.Rva + access.Length));
}
report(9900);
const auto templateBytes = WindowsTlsTemplateBuilder().Build(*tls, image, sections, templateRva, relocations);
std::copy(templateBytes.begin(), templateBytes.end(), data.Data.begin() + templateOffset);
const auto writeAddress = [&](const std::size_t offset, const std::uint32_t rva) {

View File

@ -3,12 +3,13 @@
#include <domain/Types.hpp>
#include <set>
#include <functional>
namespace Relinker {
class CodeInstructionCollector {
public:
std::set<Domain::VirtualAddress> Collect(const std::vector<std::uint8_t>& bytes, const std::vector<Domain::ProgramHeader>& headers) const;
std::set<Domain::VirtualAddress> Collect(const std::vector<std::uint8_t>& bytes, const std::vector<Domain::ProgramHeader>& headers, const std::function<void(std::uint64_t, std::uint64_t)>& progress = {}) const;
};
}

View File

@ -8,6 +8,7 @@
#include <memory>
#include <span>
#include <vector>
#include <functional>
namespace Relinker::UnusedNidFilter {
@ -27,15 +28,7 @@ std::unique_ptr<IControlFlowGraph> BuildControlFlowGraph(
bool followCodeAddresses = false
);
std::unique_ptr<IControlFlowGraph> BuildControlFlowGraph(
std::span<const std::uint8_t> text,
VirtualAddress textVaddr,
VirtualAddress entryVaddr,
const std::vector<VirtualAddress>& extraEntries,
const IRelativeRelocationIndex& relativeRelocations,
bool followCodeAddresses,
std::unordered_set<VirtualAddress>& reachable
);
std::unique_ptr<IControlFlowGraph> BuildControlFlowGraph(std::span<const std::uint8_t> text, VirtualAddress textVaddr, VirtualAddress entryVaddr, const std::vector<VirtualAddress>& extraEntries, const IRelativeRelocationIndex& relativeRelocations, bool followCodeAddresses, std::unordered_set<VirtualAddress>& reachable, const std::function<void(std::uint64_t)>& progress = {});
std::unique_ptr<IControlFlowGraph> BuildControlFlowGraph(
const std::vector<std::uint8_t>& text,

View File

@ -34,7 +34,13 @@ public:
}
std::set<Domain::VirtualAddress> CodeInstructionCollector::Collect(const std::vector<std::uint8_t>& bytes, const std::vector<Domain::ProgramHeader>& headers) const {
std::set<Domain::VirtualAddress> CodeInstructionCollector::Collect(const std::vector<std::uint8_t>& bytes, const std::vector<Domain::ProgramHeader>& headers, const std::function<void(std::uint64_t, std::uint64_t)>& progress) const {
std::uint64_t lastProgress = 0;
const auto report = [&](std::uint64_t completed) {
if (completed <= lastProgress) return;
lastProgress = completed;
if (progress) progress(completed, 10000);
};
const auto range = [&](std::uint64_t offset, std::uint64_t size) {
if (offset > bytes.size() || size > bytes.size() - offset) throw Domain::RelinkerException("Code analysis: file range exceeds image", offset);
};
@ -223,6 +229,10 @@ std::set<Domain::VirtualAddress> CodeInstructionCollector::Collect(const std::ve
const Codegen::X64InstructionDecoder decoder;
std::vector<std::pair<std::uint64_t, std::uint64_t>> skipped;
std::set<std::uint64_t> staticTargets;
std::uint64_t functionBytes = 0;
for (const auto& [begin, end] : functions) functionBytes += end - begin;
std::uint64_t decodedBytes = 0;
report(1000);
for (const auto& [begin, end] : functions) {
const auto offset = fileOffset(begin, end - begin);
for (auto address = begin; address < end;) {
@ -253,8 +263,14 @@ std::set<Domain::VirtualAddress> CodeInstructionCollector::Collect(const std::ve
addRoot(target);
}
address += info.Length;
decodedBytes += info.Length;
if (progress && (decodedBytes & 65535) < info.Length) report(1000 + 2500 * decodedBytes / functionBytes);
}
}
report(3500);
std::uint64_t scanBytes = 0;
for (const auto& header : headers) if (header.Type == 1 && (header.Flags & 1) != 0) scanBytes += header.FileSize;
std::uint64_t scannedBytes = 0;
for (const auto& header : headers) {
if (header.Type != 1 || (header.Flags & 1) == 0 || header.FileSize < 16) continue;
range(header.Offset, header.FileSize);
@ -263,11 +279,15 @@ std::set<Domain::VirtualAddress> CodeInstructionCollector::Collect(const std::ve
const auto paddedStart = [&](std::size_t address) { return address % 16 == 0 && address >= 2 && text[address - 1] == 0xcc && text[address - 2] == 0xcc; };
const auto nextPaddedStart = [&](std::size_t address) {
address = (address + 16) & ~std::size_t{15};
while (address < size && !paddedStart(address)) address += 16;
while (address < size && !paddedStart(address)) {
if (progress && address % 65536 == 0) report(3500 + 2500 * (scannedBytes + address) / scanBytes);
address += 16;
}
return address;
};
bool afterTransfer = false;
for (std::size_t position = nextPaddedStart(0); position < size;) {
if (progress && position % 65536 < 16) report(3500 + 2500 * (scannedBytes + position) / scanBytes);
const auto aligned = (position + 15) & ~std::size_t{15};
const bool zeroPadding = afterTransfer && aligned != position && aligned <= size && std::all_of(text + position, text + aligned, [](const std::uint8_t value) { return value == 0; });
afterTransfer = false;
@ -301,7 +321,9 @@ std::set<Domain::VirtualAddress> CodeInstructionCollector::Collect(const std::ve
afterTransfer = info.FlowKind != Codegen::ControlFlowKind::Sequential && info.FlowKind != Codegen::ControlFlowKind::ConditionalBranch;
position = next;
}
scannedBytes += header.FileSize;
}
report(6000);
const auto decodesInSegment = [&](std::uint64_t address) {
for (const auto& header : headers) {
if (header.Type != 1 || (header.Flags & 1) == 0 || address < header.MappedAddress || address - header.MappedAddress >= header.FileSize) continue;
@ -333,7 +355,7 @@ std::set<Domain::VirtualAddress> CodeInstructionCollector::Collect(const std::ve
for (const auto root : roots) if (root >= header.MappedAddress && root - header.MappedAddress < header.FileSize) entries.push_back(root);
if (entries.empty()) continue;
const std::span<const std::uint8_t> text(bytes.data() + header.Offset, header.FileSize);
const auto graph = UnusedNidFilter::BuildControlFlowGraph(text, header.MappedAddress, entries.front(), entries, pointers, true, sectionReachable[i]);
const auto graph = UnusedNidFilter::BuildControlFlowGraph(text, header.MappedAddress, entries.front(), entries, pointers, true, sectionReachable[i], [&](std::uint64_t count) { report(6000 + 2500 * count / std::max<std::uint64_t>(scanBytes, 1)); });
for (const auto address : graph->ReachableVaddrs()) {
if (!decodedInstructions.insert(address).second) continue;
instructions.insert(address);
@ -344,8 +366,12 @@ std::set<Domain::VirtualAddress> CodeInstructionCollector::Collect(const std::ve
}
}
} while (roots.size() != previousRoots);
report(8500);
std::uint64_t checked = 0;
std::uint64_t previousEnd = 0;
for (const auto address : instructions) {
if (progress && checked % 4096 == 0) report(8500 + 1499 * checked / instructions.size());
++checked;
if (address < previousEnd) {
std::uint64_t spanEnd = previousEnd;
try {
@ -394,6 +420,7 @@ std::set<Domain::VirtualAddress> CodeInstructionCollector::Collect(const std::ve
break;
}
}
report(10000);
return instructions;
}

View File

@ -29,16 +29,7 @@ private:
namespace {
void buildGraph(
std::span<const std::uint8_t> text,
VirtualAddress textVaddr,
VirtualAddress entryVaddr,
const std::vector<VirtualAddress>& extraEntries,
const IRelativeRelocationIndex& relativeRelocations,
const std::vector<StrictDataRegion>* data,
bool followCodeAddresses,
std::unordered_set<VirtualAddress>& reachable
) {
void buildGraph(std::span<const std::uint8_t> text, VirtualAddress textVaddr, VirtualAddress entryVaddr, const std::vector<VirtualAddress>& extraEntries, const IRelativeRelocationIndex& relativeRelocations, const std::vector<StrictDataRegion>* data, bool followCodeAddresses, std::unordered_set<VirtualAddress>& reachable, const std::function<void(std::uint64_t)>& progress) {
const Codegen::X64InstructionDecoder decoder;
std::unordered_set<VirtualAddress> visited;
std::queue<VirtualAddress> worklist;
@ -90,6 +81,7 @@ void buildGraph(
info = decoder.DecodeInstruction(text.data() + bufOff, available);
}
reachable.insert(va);
if (progress && reachable.size() % 4096 == 0) progress(reachable.size());
visited.erase(va);
VirtualAddress nextVaddr = va + static_cast<VirtualAddress>(info.Length);
@ -162,16 +154,8 @@ void buildGraph(
}
std::unique_ptr<IControlFlowGraph> BuildControlFlowGraph(
std::span<const std::uint8_t> text,
VirtualAddress textVaddr,
VirtualAddress entryVaddr,
const std::vector<VirtualAddress>& extraEntries,
const IRelativeRelocationIndex& relativeRelocations,
bool followCodeAddresses,
std::unordered_set<VirtualAddress>& reachable
) {
buildGraph(text, textVaddr, entryVaddr, extraEntries, relativeRelocations, nullptr, followCodeAddresses, reachable);
std::unique_ptr<IControlFlowGraph> BuildControlFlowGraph(std::span<const std::uint8_t> text, VirtualAddress textVaddr, VirtualAddress entryVaddr, const std::vector<VirtualAddress>& extraEntries, const IRelativeRelocationIndex& relativeRelocations, bool followCodeAddresses, std::unordered_set<VirtualAddress>& reachable, const std::function<void(std::uint64_t)>& progress) {
buildGraph(text, textVaddr, entryVaddr, extraEntries, relativeRelocations, nullptr, followCodeAddresses, reachable, progress);
return std::make_unique<ControlFlowGraph>(&reachable);
}
@ -208,7 +192,7 @@ std::unique_ptr<IControlFlowGraph> BuildAddressTakenControlFlowGraph(
const std::vector<StrictDataRegion>& data
) {
std::unordered_set<VirtualAddress> reachable;
buildGraph(std::span<const std::uint8_t>(text.data(), text.size()), textVaddr, entryVaddr, extraEntries, relativeRelocations, &data, false, reachable);
buildGraph(std::span<const std::uint8_t>(text.data(), text.size()), textVaddr, entryVaddr, extraEntries, relativeRelocations, &data, false, reachable, {});
return std::make_unique<ControlFlowGraph>(std::move(reachable));
}

View File

@ -102,7 +102,7 @@ ConversionOutput Conversion::Convert(ConversionInput input, const ConversionOpti
if (macos) {
patcher = std::make_shared<Elfpatcher::MacOs::MacOsMachOPatcher>();
} else if (windows) {
patcher = std::make_shared<Elfpatcher::Windows::WindowsPePatcher>(options.WindowsGui, std::move(input.Icon), input.InputPath.parent_path() / "sce_sys" / "icon0.png");
patcher = std::make_shared<Elfpatcher::Windows::WindowsPePatcher>(options.WindowsGui, std::move(input.Icon), input.InputPath.parent_path() / "sce_sys" / "icon0.png", [&](auto completed, auto total) { inputProgress.Report({Domain::ProgressStage::Image, {}, completed, total}); });
} else {
patcher = std::make_shared<Elfpatcher::Linux::LinuxElfPatcher>(
std::make_shared<Elfpatcher::EntryStubBuilder>(),
@ -115,7 +115,7 @@ ConversionOutput Conversion::Convert(ConversionInput input, const ConversionOpti
);
}
inputProgress.Report({Domain::ProgressStage::Image, Io::Utf8Path(input.InputPath), 0, 1});
if (!windows) inputProgress.Report({Domain::ProgressStage::Image, Io::Utf8Path(input.InputPath), 0, 1});
std::vector<std::uint8_t> executableBytes;
try {
executableBytes = patcher->Patch(sourceBytes, result.OriginalHeaders, result.DynamicSection, result.OriginalPltGotVaddr, options.RunPath, options.LazyBinding, options.WindowsDiagnostics, trampolines);
@ -124,7 +124,7 @@ ConversionOutput Conversion::Convert(ConversionInput input, const ConversionOpti
throw;
}
inputProgress.Report({Domain::ProgressStage::Image, Io::Utf8Path(input.InputPath), 1, 1});
if (!windows) inputProgress.Report({Domain::ProgressStage::Image, Io::Utf8Path(input.InputPath), 1, 1});
std::string diagnostics;
if (windows) diagnostics = std::static_pointer_cast<Elfpatcher::Windows::WindowsPePatcher>(patcher)->GetDiagnostics();
inputProgress.Report({Domain::ProgressStage::Executable, Io::Utf8Path(input.InputPath), 1, 1});

View File

@ -1,6 +1,7 @@
#include "TestProgress.hpp"
#include <relinker/pipeline/Conversion.hpp>
#include <relinker/analysis/SyscallScanner.hpp>
#include <relinker/analysis/CodeInstructionCollector.hpp>
#include <io/BufferUtils.hpp>
#include <codegen/IAmd64OnlyConverter.hpp>
#include <algorithm>
@ -85,6 +86,19 @@ void convert() {
require(output.Modules.front().Bytes.front() == output.Executable.front(), "Wrong module format");
require(progress.Events.back().Stage == Domain::ProgressStage::Executable && progress.Events.back().Completed == 1, "Conversion did not finish");
for (const auto& event : progress.Events) require(event.Completed <= event.Total, "Invalid counters");
if (target == Relinker::TargetSystem::Windows) {
std::uint64_t previous = 0;
bool intermediate = false;
for (const auto& event : progress.Events) {
if (event.Stage != Domain::ProgressStage::Image) continue;
require(event.Input == "virtual/title/custom-name.bin", "Lost image input identity");
require(event.Total == 10000 && event.Completed >= previous, "Image progress moved backwards");
intermediate |= event.Completed > 0 && event.Completed < event.Total;
previous = event.Completed;
}
require(intermediate && previous == 10000, "Missing image progress");
}
progress.Events.clear();
}
auto source = input();
source.Modules.push_back({"virtual/escaped.prx", fixture()});
@ -126,6 +140,21 @@ void scanning() {
}
require(previous == progress.Events.back().Total, "Instruction conversion progress did not finish");
reject([&] { scanner->ScanCodeSectionForSyscalls(code, 0, (std::uint64_t{1} << 32) + code.size()); });
auto image = code;
image.resize(code.size() + 64, 0x90);
image.back() = 0xc3;
Io::WriteU64(image, 24, 64);
header.Offset = 64;
header.MappedAddress = 64;
previous = 0;
std::size_t updates = 0;
const auto instructions = Relinker::CodeInstructionCollector().Collect(image, {header}, [&](auto completed, auto total) {
require(completed >= previous && completed <= total, "Image analysis progress moved backwards");
previous = completed;
++updates;
});
require(instructions.size() == code.size(), "Progress changed image code analysis");
require(updates > 10 && previous == 10000, "Missing intermediate image analysis progress");
code[70000] = 0x0f;
code[70001] = 0x05;
progress.Events.clear();
@ -135,20 +164,26 @@ void scanning() {
class CancellingProgress final : public Domain::IProgress {
public:
Domain::ProgressStage Stage = Domain::ProgressStage::Decode;
void Report(const Domain::ProgressEvent& event) override {
if (event.Stage == Domain::ProgressStage::Decode && event.Completed > 0) throw std::runtime_error("cancelled");
if (event.Stage == Stage && event.Completed > 0) throw std::runtime_error("cancelled");
}
void Message(std::string_view) override {}
};
void cancellation() {
CancellingProgress progress;
try { Relinker::Conversion(progress).Convert(input(), {}); }
catch (const std::runtime_error& error) {
require(std::string(error.what()) == "cancelled", "Callback exception was replaced");
return;
for (const auto stage : {Domain::ProgressStage::Decode, Domain::ProgressStage::Image}) {
CancellingProgress progress;
progress.Stage = stage;
Relinker::ConversionOptions options;
options.Target = Relinker::TargetSystem::Windows;
try { Relinker::Conversion(progress).Convert(input(), options); }
catch (const std::runtime_error& error) {
require(std::string(error.what()) == "cancelled", "Callback exception was replaced");
continue;
}
throw std::runtime_error("Callback exception was swallowed");
}
throw std::runtime_error("Callback exception was swallowed");
}
}