AnyPS5/core/relinker/src/pipeline/RelinkerPipeline.cpp

189 lines
7.1 KiB
C++

#include <relinker/pipeline/RelinkerPipeline.hpp>
#include <relinker/analysis/ValidationPolicy.hpp>
#include <sstream>
#include <iostream>
#include <cstring>
namespace Relinker {
RelinkerPipeline::RelinkerPipeline(
std::shared_ptr<IElfReader> elfReader,
std::shared_ptr<ISyscallScanner> syscallScanner,
std::shared_ptr<ICallSiteResolver> callSiteResolver,
std::shared_ptr<IValidationPolicy> validationPolicy,
std::shared_ptr<ISysVDynamicSectionBuilder> dynamicSectionBuilder)
: _elfReader(std::move(elfReader))
, _syscallScanner(std::move(syscallScanner))
, _callSiteResolver(std::move(callSiteResolver))
, _validationPolicy(std::move(validationPolicy))
, _dynamicSectionBuilder(std::move(dynamicSectionBuilder))
{}
std::string RelinkerPipeline::_relocationTypeName(std::uint32_t type) {
switch (type) {
case 1: return "R_X86_64_64";
case 6: return "R_X86_64_GLOB_DAT";
case 7: return "R_X86_64_JUMP_SLOT";
case 10: return "R_X86_64_32";
default: {
std::ostringstream oss;
oss << "UNKNOWN(" << type << ")";
return oss.str();
}
}
}
RelinkResult RelinkerPipeline::Relink(const std::vector<std::uint8_t>& sourceElf) {
auto programHeaders = _elfReader->ReadProgramHeaders();
std::vector<std::uint8_t> textSection;
VirtualAddress textVAddr = 0;
VirtualAddress gotVAddr = 0;
ByteCount gotSize = 0;
for (const auto& ph : programHeaders) {
if (ph.Type == PT_LOAD && (ph.Flags & PF_X) != 0) {
textSection = _elfReader->ReadSegment(ph);
textVAddr = ph.MappedAddress;
break;
}
}
FileByteOffset dynStrTabOffset = 0;
FileByteOffset dynSymTabOffset = 0;
FileByteOffset dynRelaOffset = 0;
ByteCount dynRelaSize = 0;
FileByteOffset dynJmpRelOffset = 0;
ByteCount dynJmpRelSize = 0;
bool hasDynamicSegment = false;
std::vector<std::pair<std::uint64_t, std::string>> neededLibraryNamesByStrOffset;
for (const auto& ph : programHeaders) {
if (ph.Type != PT_DYNAMIC)
continue;
hasDynamicSegment = true;
for (const auto& tag : _elfReader->ReadDynamicTags(ph)) {
if (tag.Tag == DT_PLTGOT || tag.Tag == DT_OS_PLTGOT)
gotVAddr = tag.Value;
else if (tag.Tag == DT_PLTRELSZ || tag.Tag == DT_OS_PLTRELSZ)
gotSize = tag.Value;
else if (tag.Tag == DT_STRTAB)
dynStrTabOffset = _elfReader->TranslateVirtualAddress(tag.Value);
else if (tag.Tag == DT_SYMTAB)
dynSymTabOffset = _elfReader->TranslateVirtualAddress(tag.Value);
else if (tag.Tag == DT_RELA)
dynRelaOffset = _elfReader->TranslateVirtualAddress(tag.Value);
else if (tag.Tag == DT_RELASZ)
dynRelaSize = tag.Value;
else if (tag.Tag == DT_SYSV_JMPREL)
dynJmpRelOffset = _elfReader->TranslateVirtualAddress(tag.Value);
else if (tag.Tag == DT_PLTRELSZ_SYSV)
dynJmpRelSize = tag.Value;
else if (tag.Tag == DT_NEEDED)
neededLibraryNamesByStrOffset.emplace_back(tag.Value, std::string());
}
break;
}
if (!hasDynamicSegment)
throw RelinkerException("No PT_DYNAMIC segment found");
if (dynStrTabOffset == 0)
throw RelinkerException("DT_STRTAB not found in PT_DYNAMIC segment");
if (dynSymTabOffset == 0)
throw RelinkerException("DT_SYMTAB not found in PT_DYNAMIC segment");
std::vector<NidReference> nidRefs;
std::vector<std::string> neededLibraries;
auto policy = std::dynamic_pointer_cast<ValidationPolicy>(_validationPolicy);
const std::vector<std::uint8_t>& raw = _elfReader->GetRawBytes();
auto readCStr = [&](FileByteOffset strOff) -> std::string {
std::string result;
FileByteOffset pos = dynStrTabOffset + strOff;
while (pos < raw.size() && raw[pos] != 0)
result.push_back(static_cast<char>(raw[pos++]));
return result;
};
for (auto& [fst, snd] : neededLibraryNamesByStrOffset) {
snd = readCStr(fst);
neededLibraries.push_back(snd);
if (policy) policy->RegisterLibraryImport(snd);
}
auto extractRela = [&](const FileByteOffset relaOff, const ByteCount relaSize) {
constexpr std::size_t entrySize = 24;
for (ByteCount off = 0; off + entrySize <= relaSize; off += entrySize) {
const FileByteOffset pos = relaOff + off;
if (pos + entrySize > raw.size())
throw RelinkerException("Relocation entry out of bounds", pos);
std::uint64_t rOffset = 0, rInfo = 0;
std::memcpy(&rOffset, raw.data() + pos, 8);
std::memcpy(&rInfo, raw.data() + pos + 8, 8);
const std::uint32_t symIdx = static_cast<std::uint32_t>(rInfo >> 32);
const std::uint32_t relType = static_cast<std::uint32_t>(rInfo & 0xffffffff);
const FileByteOffset symOff = dynSymTabOffset + static_cast<FileByteOffset>(symIdx) * 24;
if (symOff + 4 > raw.size())
throw RelinkerException("Symbol table entry out of bounds", symOff);
std::uint32_t nameOff = 0;
std::memcpy(&nameOff, raw.data() + symOff, 4);
NidReference ref;
ref.Nid = readCStr(nameOff);
ref.Library = {};
ref.RelocationTypeValue = relType;
ref.RelocationTableOffset = pos;
ref.RelocationAddress = rOffset;
nidRefs.push_back(ref);
}
};
if (dynRelaOffset != 0) extractRela(dynRelaOffset, dynRelaSize);
if (dynJmpRelOffset != 0) extractRela(dynJmpRelOffset, dynJmpRelSize);
for (const auto& ref : nidRefs)
_validationPolicy->ValidateRelocationTypeSupported(ref.RelocationTypeValue, ref.RelocationTableOffset);
if (!textSection.empty())
_syscallScanner->ScanCodeSectionForSyscalls(textSection, textVAddr, textSection.size());
_validationPolicy->ValidateSyscallAbsence();
auto dynSection = _dynamicSectionBuilder->BuildDynamicSection(nidRefs, neededLibraries);
std::vector<FileByteOffset> callSites;
bool callSitesResolved = false;
if (!textSection.empty() && gotSize > 0) {
callSites = _callSiteResolver->ResolveCallSites(textSection, textVAddr, gotVAddr, gotSize);
callSitesResolved = !callSites.empty();
}
std::vector<CallRegistryEntry> entries;
entries.reserve(nidRefs.size());
for (const auto& ref : nidRefs) {
CallRegistryEntry entry;
entry.Nid = ref.Nid;
entry.Library = ref.Library;
entry.RelocationTypeString = _relocationTypeName(ref.RelocationTypeValue);
entry.RelocationOffset = ref.RelocationTableOffset;
entry.TargetSection = ".got";
entry.TargetOffset = ref.RelocationAddress;
entry.CallSites = callSites;
entry.CallSitesResolved = callSitesResolved;
entries.push_back(std::move(entry));
}
std::cout << "OK: " << entries.size() << " NID references processed\n";
return RelinkResult{std::move(entries), std::move(programHeaders), std::move(dynSection)};
}
}