Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions core/relinker/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,8 @@ if(BUILD_TESTING)
add_test(NAME windows_runpath COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_windows_runpath.py $<TARGET_FILE:relinker>)
set_tests_properties(windows_runpath PROPERTIES TIMEOUT 30)
add_test(NAME windows_trace_imports COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_windows_trace_imports.py $<TARGET_FILE:relinker>)
add_test(NAME windows_address_space COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_windows_address_space.py $<TARGET_FILE:relinker>)
add_test(NAME windows_tls_thread_pointer COMMAND ${Python3_EXECUTABLE}
Expand Down
1 change: 1 addition & 0 deletions core/relinker/cli/include/Cli.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ struct Args {
bool windowsDiagnostics = false;
bool windowsGui = false;
std::optional<std::uint64_t> windowsFixedBase;
bool traceImports = false;
std::uint32_t unusedFilterLevel = 0;
std::string inputPath;
std::string outputPath;
Expand Down
8 changes: 7 additions & 1 deletion core/relinker/cli/src/CliArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
namespace Cli {

const char* Usage() {
return "Usage: relinker [--help] [--windows | --macos] [--windows-diagnostics] [--windows-gui] [--windows-fixed-base <address>] [--skip-syscall-check | --trap-sdk-syscall-stubs] [--skip-sce-module | --sce-module-path <path>] [--exclude-sce-module <file>]... [--module-dir <dir>]... [--executable <path>]... [--to-intel] [--to-rosetta] [unused-filter=0|1|2] [--registry] [--rpath <path>] [--lazy-binding] [--autorun] <input.elf> <output.elf>\n"
return "Usage: relinker [--help] [--windows | --macos] [--windows-diagnostics] [--windows-gui] [--trace-imports] [--windows-fixed-base <address>] [--skip-syscall-check | --trap-sdk-syscall-stubs] [--skip-sce-module | --sce-module-path <path>] [--exclude-sce-module <file>]... [--module-dir <dir>]... [--executable <path>]... [--to-intel] [--to-rosetta] [unused-filter=0|1|2] [--registry] [--rpath <path>] [--lazy-binding] [--autorun] <input.elf> <output.elf>\n"
"Example: relinker input.elf output.elf";
}

Expand Down Expand Up @@ -86,6 +86,8 @@ Args ParseArgs(int argc, char* argv[]) {
args.windowsDiagnostics = true;
} else if (arg == "--windows-gui") {
args.windowsGui = true;
} else if (arg == "--trace-imports") {
args.traceImports = true;
} else if (arg.rfind("--", 0) == 0 || arg == "unused-filter") {
throw std::runtime_error("unknown option: " + arg);
} else if (arg.empty()) {
Expand Down Expand Up @@ -124,6 +126,10 @@ Args ParseArgs(int argc, char* argv[]) {

if (args.windowsFixedBase && !args.toWindows)
throw std::runtime_error("--windows-fixed-base requires --windows");
if (args.traceImports && !args.toWindows)
throw std::runtime_error("--trace-imports requires --windows");
if (args.traceImports && args.lazyBinding)
throw std::runtime_error("--trace-imports conflicts with --lazy-binding");

if (!args.showHelp && (args.inputPath.empty() || args.outputPath.empty()))
throw std::runtime_error(Usage());
Expand Down
1 change: 1 addition & 0 deletions core/relinker/domain/include/domain/GuestRuntime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ struct GuestImport {
std::uint64_t Addend;
std::uint32_t RelocationType = 1;
std::string Library;
bool Function = false;
};

struct GuestRuntime {
Expand Down
2 changes: 2 additions & 0 deletions core/relinker/domain/include/domain/Types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ struct NidReference {
VirtualAddress RelocationAddress;
std::int64_t Addend;
bool Weak = false;
bool Function = false;
};

struct CallSiteInfo {
Expand Down Expand Up @@ -98,6 +99,7 @@ struct SysVDynamicSection {
std::vector<std::uint8_t> RelaPltData;
std::vector<GuestRuntime> GuestModules;
std::map<VirtualAddress, std::string> ImportModules;
std::set<VirtualAddress> FunctionImports;
std::set<std::string> UnmatchedImportModules;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ 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", std::function<void(std::uint64_t, std::uint64_t)> progress = {}, std::optional<std::uint64_t> fixedBase = std::nullopt);
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 = {}, std::optional<std::uint64_t> fixedBase = std::nullopt, bool traceImports = false);
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;
Expand All @@ -23,6 +23,7 @@ class WindowsPePatcher : public IElfPatcher {
std::string diagnostics;
std::filesystem::path iconName;
std::optional<std::uint64_t> fixedBase;
bool traceImports;
};

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,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, bool dependencyDiagnostics, const std::vector<Domain::GuestRuntime>& guestModules = {}, const std::set<std::string>& unmatchedImportModules = {}) 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 std::vector<Domain::GuestRuntime>& guestModules = {}, const std::set<std::string>& unmatchedImportModules = {}, bool traceImports = false) const;
};

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ struct PeImport {
std::uint32_t RelocationType = 1;
std::string Library;
bool Weak = false;
bool Function = false;
};

struct PeRelocations {
Expand Down
39 changes: 38 additions & 1 deletion core/relinker/elfpatcher/src/windows/WindowsEntryStubBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <io/BufferUtils.hpp>
#include <algorithm>
#include <cctype>
#include <map>
#include <set>
#include <optional>
#include <filesystem>
Expand Down Expand Up @@ -66,7 +67,7 @@ std::u16string toUtf16(const std::string& text) {

}

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 std::vector<Domain::GuestRuntime>& guestModules, const std::set<std::string>& unmatchedImportModules) 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 std::vector<Domain::GuestRuntime>& guestModules, const std::set<std::string>& unmatchedImportModules, const bool traceImports) const {
if (!imports.empty() && libraries.empty())
throw Domain::RelinkerException("ELF imports have no DT_NEEDED libraries");
const auto path = normalizeRunPath(runPath);
Expand Down Expand Up @@ -187,6 +188,24 @@ WindowsEntryStub WindowsEntryStubBuilder::Build(const std::uint32_t dataRva, con
if (unmatched.contains(stem(libraries[index]))) imported[index] = true;
}

std::optional<std::size_t> traceProvider;
std::map<std::string, std::uint32_t> traceLibraries;
std::uint32_t traceSymbol = 0;
std::uint32_t traceFunction = 0;
if (traceImports) {
for (std::size_t index = guestModules.size(); index < libraries.size(); ++index) {
if (libraries[index] == "libc.prx") traceProvider = index;
}
if (!traceProvider.has_value())
throw Domain::RelinkerException("--trace-imports requires libc.prx among the libraries");
imported[*traceProvider] = true;
traceSymbol = addString("TraceImport_nid_no_patch");
traceFunction = reserve(8);
for (const auto& import : imports) {
if (!traceLibraries.contains(import.Library)) traceLibraries.emplace(import.Library, addString(import.Library));
}
}

const auto lastError = reserve(4);
const auto errorDigits = reserve(11);
const auto errorMessage = reserve(ErrorMessageCapacity);
Expand Down Expand Up @@ -215,6 +234,8 @@ WindowsEntryStub WindowsEntryStubBuilder::Build(const std::uint32_t dataRva, con
errors.push_back("FAIL: cannot prepare command-line arguments\n");
const auto directoryError = errors.size();
errors.push_back("FAIL: cannot make the executable directory the current directory\n");
const auto traceError = errors.size();
errors.push_back("FAIL: libc.prx does not export TraceImport_nid_no_patch\n");
std::vector<std::uint32_t> errorRvas;
for (const auto& error : errors)
errorRvas.push_back(addString(error));
Expand Down Expand Up @@ -485,6 +506,21 @@ WindowsEntryStub WindowsEntryStubBuilder::Build(const std::uint32_t dataRva, con
}
}

if (traceImports) {
code.Rip({0x48, 0x8b, 0x0d}, CheckedRva(handles + *traceProvider * 8));
code.Rip({0x48, 0x8d, 0x15}, traceSymbol);
call("GetProcAddress");
requireNonzero(traceError, 0xc0000139u);
code.Rip({0x48, 0x89, 0x05}, traceFunction);
}
const auto trace = [&](const PeImport& import, const std::uint32_t symbolName) {
if (!traceImports || !import.Function || import.Addend != 0) return;
code.Emit({0x48, 0x89, 0xc1});
code.Rip({0x48, 0x8d, 0x15}, symbolName);
code.Rip({0x4c, 0x8d, 0x05}, traceLibraries.at(import.Library));
code.Rip({0xff, 0x15}, traceFunction);
};

std::vector<std::size_t> unresolvedBranches;
std::vector<std::size_t> lazyUnresolvedImports;
std::vector<std::size_t> tlsResolverAddresses;
Expand Down Expand Up @@ -546,6 +582,7 @@ WindowsEntryStub WindowsEntryStubBuilder::Build(const std::uint32_t dataRva, con
code.U64(imports[index].Addend);
code.Emit({0x48, 0x01, 0xd0});
}
trace(imports[index], symbolNames[index]);
guestStartup.WriteImport(code, imports[index], handles);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ std::vector<std::uint8_t> GuestModuleWriter::WriteWindows(const Relinker::GuestI
} else {
if (symbol.Name.empty()) throw Domain::RelinkerException("Empty guest import", target);
image.WritePointer(target, 0);
runtime.Imports.push_back({symbol.Name, rva, addend, 1, symbol.Library});
runtime.Imports.push_back({symbol.Name, rva, addend, 1, symbol.Library, type == 7 || (type == 6 && (symbol.Info & 15) == 2)});
}
} else throw Domain::RelinkerException("Unsupported Windows guest relocation " + std::to_string(type), target);
}
Expand Down
9 changes: 6 additions & 3 deletions core/relinker/elfpatcher/src/windows/WindowsPePatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ 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, std::function<void(std::uint64_t, std::uint64_t)> progress, const std::optional<std::uint64_t> fixedBase) : progress(std::move(progress)), windowsGui(windowsGui), icon(std::move(icon)), iconName(std::move(iconName)), fixedBase(fixedBase) {
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, const std::optional<std::uint64_t> fixedBase, const bool traceImports) : progress(std::move(progress)), windowsGui(windowsGui), icon(std::move(icon)), iconName(std::move(iconName)), fixedBase(fixedBase), traceImports(traceImports) {
if (fixedBase && (*fixedBase < 0x20000 || *fixedBase > 0x7fffffff0000 || (*fixedBase & 0xffff) != 0))
throw Domain::RelinkerException("Invalid fixed Windows guest image base");
}
Expand Down Expand Up @@ -81,13 +81,16 @@ std::vector<std::uint8_t> WindowsPePatcher::Patch(const std::vector<std::uint8_t
for (std::size_t index = 0; index < dynamicSection.GuestModules.size(); ++index) {
const auto& module = dynamicSection.GuestModules[index];
guestPaths.push_back(module.Path);
for (const auto& import : module.Imports) relocations.Imports.push_back({import.Name, import.TargetRva, import.Addend, static_cast<std::int32_t>(index), import.RelocationType, import.Library});
for (const auto& import : module.Imports) {
relocations.Imports.push_back({import.Name, import.TargetRva, import.Addend, static_cast<std::int32_t>(index), import.RelocationType, import.Library});
relocations.Imports.back().Function = import.Function;
}
}
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);
auto entry = WindowsEntryStubBuilder().Build(nextRva, image.GetEntryRva(), nativeImports, libraries, relocations.Imports, runPath, lazyBinding, dependencyDiagnostics, dynamicSection.GuestModules, dynamicSection.UnmatchedImportModules, traceImports);
report(9000);
directories[3] = entry.ExceptionDirectory;
const auto entryRva = entry.Code.Rva;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ PeRelocations WindowsRelocationBuilder::Apply(WindowsLoadImage& image, const Dom
image.WritePointer(target, 0);
result.Imports.push_back({name, rva, addend});
result.Imports.back().Weak = (dynamicSection.DynSymData[symbolOffset + 4] >> 4) == STB_WEAK;
result.Imports.back().Function = type == 7 || (type == 6 && dynamicSection.FunctionImports.contains(target));
if (const auto module = dynamicSection.ImportModules.find(target); module != dynamicSection.ImportModules.end())
result.Imports.back().Library = module->second;
}
Expand Down
1 change: 1 addition & 0 deletions core/relinker/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ ConvertedExecutable Convert(const Cli::Args& args, const std::string& inputPath,
options.LazyBinding = args.lazyBinding;
options.RunPath = args.runPath;
options.WindowsGui = args.windowsGui;
options.TraceImports = args.traceImports;
options.WindowsFixedBase = mainExecutable ? args.windowsFixedBase : std::nullopt;
options.WindowsDiagnostics = args.windowsDiagnostics && mainExecutable;
options.ExcludedModules = args.excludedSceModules;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ struct ConversionOptions {
bool TrapSdkSyscallStubs = false;
bool LazyBinding = false;
bool WindowsGui = false;
bool TraceImports = false;
std::optional<std::uint64_t> WindowsFixedBase;
bool WindowsDiagnostics = false;
std::string RunPath = "$ORIGIN/libs";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ SysVDynamicSection SysVDynamicSectionBuilder::BuildDynamicSection(

for (const auto& ref : nidReferences) {
if (!ref.Library.empty()) result.ImportModules.emplace(ref.RelocationAddress, ref.Library);
if (ref.Function) result.FunctionImports.insert(ref.RelocationAddress);
std::uint32_t relType = ref.RelocationTypeValue;
if (relType == 0) relType = R_X86_64_JUMP_SLOT;

Expand Down
2 changes: 1 addition & 1 deletion core/relinker/relinker/src/pipeline/Conversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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", [&](auto completed, auto total) { inputProgress.Report({Domain::ProgressStage::Image, {}, completed, total}); }, options.WindowsFixedBase);
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}); }, options.WindowsFixedBase, options.TraceImports);
} else {
patcher = std::make_shared<Elfpatcher::Linux::LinuxElfPatcher>(
std::make_shared<Elfpatcher::EntryStubBuilder>(),
Expand Down
4 changes: 3 additions & 1 deletion core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,9 @@ RelinkResult RelinkerPipeline::Relink(const std::vector<std::uint8_t>& sourceElf
|| std::any_of(raw.begin() + static_cast<std::ptrdiff_t>(symOff), raw.begin() + static_cast<std::ptrdiff_t>(symOff + symEntSize),
[](const std::uint8_t byte) { return byte != 0; })))
throw RelinkerException("Unnamed import symbol is not a null symbol entry", symOff);
nidRefs.push_back({name, Domain::ImportModule(name, importModules, neededLibraries), relType, pos, rOffset, rAddend, (raw[symOff + 4] >> 4) == STB_WEAK});
const bool weak = (raw[symOff + 4] >> 4) == STB_WEAK;
const bool function = (raw[symOff + 4] & 15) == STT_FUNC;
nidRefs.push_back({name, Domain::ImportModule(name, importModules, neededLibraries), relType, pos, rOffset, rAddend, weak, function});
}
};

Expand Down
Loading
Loading