From 6b2db1d8a8342541b0d55901994abc71dd0bdb34 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Sat, 26 Sep 2026 15:33:26 +0300 Subject: [PATCH] fix(relinker): support windows guest tls relocations and accesses --- core/relinker/CMakeLists.txt | 1 + .../windows/WindowsTlsTemplateBuilder.hpp | 15 +++++++++ .../src/windows/WindowsGuestModuleWriter.cpp | 7 +++- .../src/windows/WindowsTlsBuilder.cpp | 23 ++++++++++--- .../src/windows/WindowsTlsTemplateBuilder.cpp | 32 +++++++++++++++++++ 5 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 core/relinker/elfpatcher/include/elfpatcher/windows/WindowsTlsTemplateBuilder.hpp create mode 100644 core/relinker/elfpatcher/src/windows/WindowsTlsTemplateBuilder.cpp diff --git a/core/relinker/CMakeLists.txt b/core/relinker/CMakeLists.txt index 66f3d02bd..95f639935 100644 --- a/core/relinker/CMakeLists.txt +++ b/core/relinker/CMakeLists.txt @@ -12,6 +12,7 @@ add_executable(relinker ${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/src/windows/WindowsPeFormat.cpp ${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/src/windows/WindowsLoadImage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/src/windows/WindowsTlsBuilder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/src/windows/WindowsTlsTemplateBuilder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/src/windows/WindowsRelocationBuilder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/src/windows/WindowsImportBuilder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/src/windows/WindowsEntryStubBuilder.cpp diff --git a/core/relinker/elfpatcher/include/elfpatcher/windows/WindowsTlsTemplateBuilder.hpp b/core/relinker/elfpatcher/include/elfpatcher/windows/WindowsTlsTemplateBuilder.hpp new file mode 100644 index 000000000..4441a73c8 --- /dev/null +++ b/core/relinker/elfpatcher/include/elfpatcher/windows/WindowsTlsTemplateBuilder.hpp @@ -0,0 +1,15 @@ +#ifndef ELFPATCHER_WINDOWSTLSTEMPLATEBUILDER_HPP +#define ELFPATCHER_WINDOWSTLSTEMPLATEBUILDER_HPP + +#include + +namespace Elfpatcher::Windows { + +class WindowsTlsTemplateBuilder { +public: + std::vector Build(const Domain::ProgramHeader& tls, const WindowsLoadImage& image, const std::vector& sections, std::uint32_t templateRva, std::vector& relocations) const; +}; + +} + +#endif diff --git a/core/relinker/elfpatcher/src/windows/WindowsGuestModuleWriter.cpp b/core/relinker/elfpatcher/src/windows/WindowsGuestModuleWriter.cpp index 3e7f5be80..78b7c1d83 100644 --- a/core/relinker/elfpatcher/src/windows/WindowsGuestModuleWriter.cpp +++ b/core/relinker/elfpatcher/src/windows/WindowsGuestModuleWriter.cpp @@ -26,7 +26,12 @@ std::vector GuestModuleWriter::WriteWindows(const Relinker::GuestI const auto& symbol = guest.Symbols.at(symbolIndex); const auto rva = image.GetRva(target, 8); for (const auto& header : guest.Headers) { - if (header.Type == 7 && target >= header.MappedAddress && target - header.MappedAddress < header.FileSize) throw Domain::RelinkerException("Windows guest TLS template relocations require explicit support", target); + if (header.Type != 7 || header.FileSize == 0) continue; + const auto templateRva = image.GetRva(header.MappedAddress, header.FileSize); + const auto templateEnd = static_cast(templateRva) + header.FileSize; + if (rva >= templateEnd || static_cast(rva) + 8 <= templateRva) continue; + if (rva < templateRva || templateEnd - rva < 8) throw Domain::RelinkerException("Guest relocation crosses the TLS template boundary", target); + if (type == 16 || (symbolIndex != 0 && symbol.Section == 0)) throw Domain::RelinkerException("Runtime-bound guest TLS template relocation is not supported", target); } const auto next = targets.lower_bound(target); if ((next != targets.end() && next->first < target + 8) || (next != targets.begin() && std::prev(next)->second > target)) throw Domain::RelinkerException("Overlapping guest relocations", target); diff --git a/core/relinker/elfpatcher/src/windows/WindowsTlsBuilder.cpp b/core/relinker/elfpatcher/src/windows/WindowsTlsBuilder.cpp index 5721381f8..2e0d8de4c 100644 --- a/core/relinker/elfpatcher/src/windows/WindowsTlsBuilder.cpp +++ b/core/relinker/elfpatcher/src/windows/WindowsTlsBuilder.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -14,6 +15,8 @@ namespace { struct TlsAccess { std::uint32_t Rva; std::size_t Length; + bool StoreImmediate; + std::uint32_t Immediate; }; void patchAccess(std::vector& sections, const TlsAccess& access, const std::uint32_t target) { @@ -63,9 +66,11 @@ PeDirectory WindowsTlsBuilder::Build(const std::vector& source, co if (info.SegmentPrefix != 0) { const auto position = info.OpcodeOffset; - if (info.SegmentPrefix != 0x64 || info.RexPrefix != 0x48 || info.Length - position != 7 || bytes[position] != 0x8b || bytes[position + 1] != 0x04 || bytes[position + 2] != 0x25 || Io::ReadU32(source, header.Offset + offset + position + 3) != 0) + const bool loadPointer = info.Length == 9 && position == 2 && bytes[0] == 0x64 && bytes[1] == 0x48 && bytes[2] == 0x8b && bytes[3] == 0x04 && bytes[4] == 0x25 && Io::ReadU32(source, header.Offset + offset + 5) == 0; + const bool storeImmediate = info.Length == 12 && position == 1 && bytes[0] == 0x64 && bytes[1] == 0xc7 && bytes[2] == 0x04 && bytes[3] == 0x25 && Io::ReadU32(source, header.Offset + offset + 4) == 0x28; + if (!loadPointer && !storeImmediate) throw Domain::RelinkerException("Unsupported Windows guest TLS instruction", header.Offset + offset); - accesses.push_back({rva, info.Length}); + accesses.push_back({rva, info.Length, storeImmediate, storeImmediate ? Io::ReadU32(source, header.Offset + offset + 8) : 0}); } offset += info.Length; } @@ -89,7 +94,8 @@ PeDirectory WindowsTlsBuilder::Build(const std::vector& source, co const auto alignment = std::max(tls->Alignment, 16); const auto blockSize = CheckedRva((tls->MemorySize + alignment - 1) & ~(alignment - 1)); const auto templateOffset = CheckedRva((64 + alignment - 1) & ~(alignment - 1)); - PeSection data{".gtls", nextRva, SectionRead | SectionWrite | 0x40u, std::vector(templateOffset + blockSize + 16)}; + constexpr std::uint32_t threadControlBlockSize = 0x30; + PeSection data{".gtls", nextRva, SectionRead | SectionWrite | 0x40u, std::vector(templateOffset + blockSize + threadControlBlockSize)}; const auto indexRva = nextRva + 40; if (tlsIndexRva != nullptr) *tlsIndexRva = indexRva; @@ -121,19 +127,26 @@ PeDirectory WindowsTlsBuilder::Build(const std::vector& source, co throw Domain::RelinkerException("Branch enters a guest TLS instruction", *target); patchAccess(sections, access, code.GetRva()); code.Emit({0x48, 0x8d, 0x64, 0x24, 0x80, 0x51}); + if (access.StoreImmediate) code.Emit({0x50}); loadPointer(); + if (access.StoreImmediate) { + code.Emit({0xc7, 0x40, 0x28}); + code.U32(access.Immediate); + code.Emit({0x58}); + } code.Emit({0x59, 0x48, 0x8d, 0xa4, 0x24, 0x80, 0, 0, 0}); code.Rip({0xe9}, CheckedRva(access.Rva + access.Length)); } - std::copy_n(source.begin() + tls->Offset, tls->FileSize, data.Data.begin() + templateOffset); + 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) { Io::WriteU64(data.Data, offset, ImageBase + rva); relocations.push_back(CheckedRva(data.Rva + offset)); }; writeAddress(0, templateRva); - writeAddress(8, CheckedRva(templateRva + blockSize + 16)); + writeAddress(8, CheckedRva(templateRva + blockSize + threadControlBlockSize)); writeAddress(16, indexRva); writeAddress(24, callbackTableRva); writeAddress(48, codeRva); diff --git a/core/relinker/elfpatcher/src/windows/WindowsTlsTemplateBuilder.cpp b/core/relinker/elfpatcher/src/windows/WindowsTlsTemplateBuilder.cpp new file mode 100644 index 000000000..4ec5556f0 --- /dev/null +++ b/core/relinker/elfpatcher/src/windows/WindowsTlsTemplateBuilder.cpp @@ -0,0 +1,32 @@ +#include +#include + +namespace Elfpatcher::Windows { + +std::vector WindowsTlsTemplateBuilder::Build(const Domain::ProgramHeader& tls, const WindowsLoadImage& image, const std::vector& sections, const std::uint32_t templateRva, std::vector& relocations) const { + if (tls.FileSize == 0) return {}; + const auto sourceRva = image.GetRva(tls.MappedAddress, tls.FileSize); + const auto sourceEnd = static_cast(sourceRva) + tls.FileSize; + std::vector result; + result.reserve(static_cast(tls.FileSize)); + while (result.size() < tls.FileSize) { + const auto currentRva = static_cast(sourceRva) + result.size(); + const auto section = std::find_if(sections.begin(), sections.end(), [&](const auto& candidate) { + return currentRva >= candidate.Rva && currentRva - candidate.Rva < candidate.Data.size(); + }); + if (section == sections.end()) throw Domain::RelinkerException("TLS template is outside PE section data", currentRva); + const auto offset = static_cast(currentRva - section->Rva); + const auto size = std::min(tls.FileSize - result.size(), section->Data.size() - offset); + result.insert(result.end(), section->Data.begin() + offset, section->Data.begin() + offset + size); + } + const auto originalCount = relocations.size(); + for (std::size_t index = 0; index < originalCount; ++index) { + const auto target = static_cast(relocations[index]); + if (target >= sourceEnd || target + 8 <= sourceRva) continue; + if (target < sourceRva || sourceEnd - target < 8) throw Domain::RelinkerException("PE relocation crosses the TLS template boundary", target); + relocations.push_back(CheckedRva(static_cast(templateRva) + target - sourceRva)); + } + return result; +} + +}