fix(relinker): support windows guest tls relocations and accesses
This commit is contained in:
parent
0c6765e6a6
commit
6b2db1d8a8
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef ELFPATCHER_WINDOWSTLSTEMPLATEBUILDER_HPP
|
||||
#define ELFPATCHER_WINDOWSTLSTEMPLATEBUILDER_HPP
|
||||
|
||||
#include <elfpatcher/windows/WindowsLoadImage.hpp>
|
||||
|
||||
namespace Elfpatcher::Windows {
|
||||
|
||||
class WindowsTlsTemplateBuilder {
|
||||
public:
|
||||
std::vector<std::uint8_t> Build(const Domain::ProgramHeader& tls, const WindowsLoadImage& image, const std::vector<PeSection>& sections, std::uint32_t templateRva, std::vector<std::uint32_t>& relocations) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -26,7 +26,12 @@ std::vector<std::uint8_t> 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<std::uint64_t>(templateRva) + header.FileSize;
|
||||
if (rva >= templateEnd || static_cast<std::uint64_t>(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);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include <elfpatcher/windows/WindowsTlsBuilder.hpp>
|
||||
#include <elfpatcher/windows/WindowsStubEmitter.hpp>
|
||||
#include <elfpatcher/windows/WindowsTlsTemplateBuilder.hpp>
|
||||
#include <codegen/x86/X64InstructionDecoder.hpp>
|
||||
#include <io/BufferUtils.hpp>
|
||||
#include <algorithm>
|
||||
|
|
@ -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<PeSection>& sections, const TlsAccess& access, const std::uint32_t target) {
|
||||
|
|
@ -63,9 +66,11 @@ PeDirectory WindowsTlsBuilder::Build(const std::vector<std::uint8_t>& 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<std::uint8_t>& source, co
|
|||
const auto alignment = std::max<std::uint64_t>(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<std::uint8_t>(templateOffset + blockSize + 16)};
|
||||
constexpr std::uint32_t threadControlBlockSize = 0x30;
|
||||
PeSection data{".gtls", nextRva, SectionRead | SectionWrite | 0x40u, std::vector<std::uint8_t>(templateOffset + blockSize + threadControlBlockSize)};
|
||||
|
||||
const auto indexRva = nextRva + 40;
|
||||
if (tlsIndexRva != nullptr) *tlsIndexRva = indexRva;
|
||||
|
|
@ -121,19 +127,26 @@ PeDirectory WindowsTlsBuilder::Build(const std::vector<std::uint8_t>& 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);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
#include <elfpatcher/windows/WindowsTlsTemplateBuilder.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Elfpatcher::Windows {
|
||||
|
||||
std::vector<std::uint8_t> WindowsTlsTemplateBuilder::Build(const Domain::ProgramHeader& tls, const WindowsLoadImage& image, const std::vector<PeSection>& sections, const std::uint32_t templateRva, std::vector<std::uint32_t>& relocations) const {
|
||||
if (tls.FileSize == 0) return {};
|
||||
const auto sourceRva = image.GetRva(tls.MappedAddress, tls.FileSize);
|
||||
const auto sourceEnd = static_cast<std::uint64_t>(sourceRva) + tls.FileSize;
|
||||
std::vector<std::uint8_t> result;
|
||||
result.reserve(static_cast<std::size_t>(tls.FileSize));
|
||||
while (result.size() < tls.FileSize) {
|
||||
const auto currentRva = static_cast<std::uint64_t>(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<std::size_t>(currentRva - section->Rva);
|
||||
const auto size = std::min<std::uint64_t>(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<std::uint64_t>(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<std::uint64_t>(templateRva) + target - sourceRva));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue