feat(core): relocation tables from pt dynamic instead of hardcoded offset
This commit is contained in:
parent
7689e85d8f
commit
39ae562b51
|
|
@ -4,4 +4,6 @@ project(Relinker CXX)
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static -static-libgcc -static-libstdc++")
|
||||||
|
|
||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@ add_executable(relinker
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/io/src/FileReader.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/io/src/FileReader.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/io/src/FileWriter.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/io/src/FileWriter.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/parsing/ElfReader.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/parsing/ElfReader.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/parsing/SceDynlibParser.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/parsing/SdkRevisionProfile.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/analysis/SyscallScanner.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/analysis/SyscallScanner.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/analysis/CallSiteResolver.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/analysis/CallSiteResolver.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/analysis/ValidationPolicy.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/analysis/ValidationPolicy.cpp
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@
|
||||||
#include <elfpatcher/sections/SectionHeaderTableBuilder.hpp>
|
#include <elfpatcher/sections/SectionHeaderTableBuilder.hpp>
|
||||||
#include <io/ByteWriter.hpp>
|
#include <io/ByteWriter.hpp>
|
||||||
#include <relinker/parsing/ElfReader.hpp>
|
#include <relinker/parsing/ElfReader.hpp>
|
||||||
#include <relinker/parsing/SceDynlibParser.hpp>
|
|
||||||
#include <relinker/parsing/SdkRevisionProfile.hpp>
|
|
||||||
#include <relinker/analysis/ValidationPolicy.hpp>
|
#include <relinker/analysis/ValidationPolicy.hpp>
|
||||||
#include <relinker/analysis/SyscallScanner.hpp>
|
#include <relinker/analysis/SyscallScanner.hpp>
|
||||||
#include <relinker/analysis/CallSiteResolver.hpp>
|
#include <relinker/analysis/CallSiteResolver.hpp>
|
||||||
|
|
@ -52,14 +50,6 @@ int main(const int argc, char* argv[]) {
|
||||||
auto sourceBytes = fileReader.Read(inputPath);
|
auto sourceBytes = fileReader.Read(inputPath);
|
||||||
|
|
||||||
auto elfReader = std::make_shared<Relinker::ElfReader>(sourceBytes);
|
auto elfReader = std::make_shared<Relinker::ElfReader>(sourceBytes);
|
||||||
auto programHeaders = elfReader->ReadProgramHeaders();
|
|
||||||
|
|
||||||
std::vector<std::uint8_t> sceDynlibData;
|
|
||||||
for (const auto& ph : programHeaders)
|
|
||||||
if (ph.Type == 0x61000000)
|
|
||||||
sceDynlibData = elfReader->ReadSegment(ph);
|
|
||||||
|
|
||||||
auto sdkProfile = std::make_shared<Relinker::SdkRevisionProfile>(sceDynlibData);
|
|
||||||
|
|
||||||
auto syscallScanner = skipSyscallCheck
|
auto syscallScanner = skipSyscallCheck
|
||||||
? Relinker::MakeNullSyscallScanner()
|
? Relinker::MakeNullSyscallScanner()
|
||||||
|
|
@ -67,7 +57,6 @@ int main(const int argc, char* argv[]) {
|
||||||
|
|
||||||
const auto pipeline = std::make_shared<Relinker::RelinkerPipeline>(
|
const auto pipeline = std::make_shared<Relinker::RelinkerPipeline>(
|
||||||
elfReader,
|
elfReader,
|
||||||
std::make_shared<Relinker::SceDynlibParser>(sdkProfile),
|
|
||||||
std::move(syscallScanner),
|
std::move(syscallScanner),
|
||||||
Relinker::MakeCallSiteResolver(),
|
Relinker::MakeCallSiteResolver(),
|
||||||
std::make_shared<Relinker::ValidationPolicy>(),
|
std::make_shared<Relinker::ValidationPolicy>(),
|
||||||
|
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
#ifndef RELINKER_DOMAIN_ISCEDYNLIBPARSER_HPP
|
|
||||||
#define RELINKER_DOMAIN_ISCEDYNLIBPARSER_HPP
|
|
||||||
|
|
||||||
#include <relinker/domain/Types.hpp>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace Relinker {
|
|
||||||
|
|
||||||
struct LibraryImport {
|
|
||||||
std::string LibraryName;
|
|
||||||
std::uint32_t LibraryId;
|
|
||||||
std::uint32_t Version;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RelocationEntry {
|
|
||||||
FileByteOffset EntryOffset;
|
|
||||||
std::uint64_t Info;
|
|
||||||
std::int64_t Addend;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ISceDynlibParser {
|
|
||||||
public:
|
|
||||||
virtual ~ISceDynlibParser() = default;
|
|
||||||
|
|
||||||
virtual std::vector<LibraryImport> ParseLibraryImports(const std::vector<std::uint8_t>& sceDynlibData) = 0;
|
|
||||||
virtual std::vector<RelocationEntry> ParseRelocationTable(const std::vector<std::uint8_t>& sceDynlibData) = 0;
|
|
||||||
virtual std::vector<NidReference> ExtractNidReferences(
|
|
||||||
const std::vector<RelocationEntry>& relocations,
|
|
||||||
const std::vector<LibraryImport>& imports
|
|
||||||
) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
#ifndef RELINKER_DOMAIN_ISDKREVISIONPROFILE_HPP
|
|
||||||
#define RELINKER_DOMAIN_ISDKREVISIONPROFILE_HPP
|
|
||||||
|
|
||||||
#include <relinker/domain/Types.hpp>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace Relinker {
|
|
||||||
|
|
||||||
class ISdkRevisionProfile {
|
|
||||||
public:
|
|
||||||
virtual ~ISdkRevisionProfile() = default;
|
|
||||||
|
|
||||||
[[nodiscard]] virtual std::uint32_t GetSceModuleParamHeaderSize() const = 0;
|
|
||||||
[[nodiscard]] virtual std::uint32_t GetSceModuleParamEntrySize() const = 0;
|
|
||||||
[[nodiscard]] virtual std::uint32_t GetSceDynlibDataHeaderSize() const = 0;
|
|
||||||
[[nodiscard]] virtual std::uint32_t GetSceDynlibDataEntrySize() const = 0;
|
|
||||||
[[nodiscard]] virtual std::uint32_t GetRelocationEntrySize() const = 0;
|
|
||||||
[[nodiscard]] virtual std::uint32_t GetImportsOffset() const = 0;
|
|
||||||
[[nodiscard]] virtual std::uint32_t GetImportsCount(const std::vector<std::uint8_t>& data) const = 0;
|
|
||||||
[[nodiscard]] virtual std::uint32_t GetRelocationTableOffset() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
#ifndef RELINKER_PARSING_SCEDYNLIBPARSER_HPP
|
|
||||||
#define RELINKER_PARSING_SCEDYNLIBPARSER_HPP
|
|
||||||
|
|
||||||
#include <relinker/domain/ISceDynlibParser.hpp>
|
|
||||||
#include <relinker/domain/ISdkRevisionProfile.hpp>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace Relinker {
|
|
||||||
|
|
||||||
class SceDynlibParser : public ISceDynlibParser {
|
|
||||||
public:
|
|
||||||
explicit SceDynlibParser(std::shared_ptr<ISdkRevisionProfile> sdkProfile);
|
|
||||||
|
|
||||||
std::vector<LibraryImport> ParseLibraryImports(const std::vector<std::uint8_t>& sceDynlibData) override;
|
|
||||||
std::vector<RelocationEntry> ParseRelocationTable(const std::vector<std::uint8_t>& sceDynlibData) override;
|
|
||||||
std::vector<NidReference> ExtractNidReferences(
|
|
||||||
const std::vector<RelocationEntry>& relocations,
|
|
||||||
const std::vector<LibraryImport>& imports
|
|
||||||
) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<ISdkRevisionProfile> _sdkProfile;
|
|
||||||
|
|
||||||
[[nodiscard]] std::uint32_t _readU32At(const std::vector<std::uint8_t>& data, FileByteOffset offset) const;
|
|
||||||
[[nodiscard]] std::uint16_t _readU16At(const std::vector<std::uint8_t>& data, FileByteOffset offset) const;
|
|
||||||
[[nodiscard]] std::uint8_t _readU8At(const std::vector<std::uint8_t>& data, FileByteOffset offset) const;
|
|
||||||
[[nodiscard]] std::uint64_t _readU64At(const std::vector<std::uint8_t>& data, FileByteOffset offset) const;
|
|
||||||
[[nodiscard]] std::string _extractNidFromRelocationInfo(std::uint64_t info) const;
|
|
||||||
[[nodiscard]] std::string _getLibraryNameForRelocation(const std::vector<LibraryImport>& imports, std::uint64_t info) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
#ifndef RELINKER_PARSING_SDKREVISIONPROFILE_HPP
|
|
||||||
#define RELINKER_PARSING_SDKREVISIONPROFILE_HPP
|
|
||||||
|
|
||||||
#include <relinker/domain/ISdkRevisionProfile.hpp>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
namespace Relinker {
|
|
||||||
|
|
||||||
class SdkRevisionProfile : public ISdkRevisionProfile {
|
|
||||||
public:
|
|
||||||
explicit SdkRevisionProfile(const std::vector<std::uint8_t>& sceDynlibData);
|
|
||||||
|
|
||||||
[[nodiscard]] std::uint32_t GetSceModuleParamHeaderSize() const override;
|
|
||||||
[[nodiscard]] std::uint32_t GetSceModuleParamEntrySize() const override;
|
|
||||||
[[nodiscard]] std::uint32_t GetSceDynlibDataHeaderSize() const override;
|
|
||||||
[[nodiscard]] std::uint32_t GetSceDynlibDataEntrySize() const override;
|
|
||||||
[[nodiscard]] std::uint32_t GetRelocationEntrySize() const override;
|
|
||||||
[[nodiscard]] std::uint32_t GetImportsOffset() const override;
|
|
||||||
[[nodiscard]] std::uint32_t GetImportsCount(const std::vector<std::uint8_t>& data) const override;
|
|
||||||
[[nodiscard]] std::uint32_t GetRelocationTableOffset() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::uint32_t _sdkRevision;
|
|
||||||
|
|
||||||
void _detectSdkRevision(const std::vector<std::uint8_t>& data);
|
|
||||||
[[nodiscard]] std::uint32_t _readU32LittleEndian(const std::vector<std::uint8_t>& data, FileByteOffset fileByteOffset) const;
|
|
||||||
[[nodiscard]] std::uint16_t _readU16LittleEndian(const std::vector<std::uint8_t>& data, FileByteOffset fileByteOffset) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // RELINKER_SDKREVISIONPROFILE_HPP
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
|
|
||||||
#include <relinker/domain/IRelinkerPipeline.hpp>
|
#include <relinker/domain/IRelinkerPipeline.hpp>
|
||||||
#include <relinker/domain/IElfReader.hpp>
|
#include <relinker/domain/IElfReader.hpp>
|
||||||
#include <relinker/domain/ISceDynlibParser.hpp>
|
|
||||||
#include <relinker/domain/ISdkRevisionProfile.hpp>
|
|
||||||
#include <relinker/domain/ISyscallScanner.hpp>
|
#include <relinker/domain/ISyscallScanner.hpp>
|
||||||
#include <relinker/domain/ICallSiteResolver.hpp>
|
#include <relinker/domain/ICallSiteResolver.hpp>
|
||||||
#include <relinker/domain/IValidationPolicy.hpp>
|
#include <relinker/domain/IValidationPolicy.hpp>
|
||||||
|
|
@ -17,7 +15,6 @@ class RelinkerPipeline : public IRelinkerPipeline {
|
||||||
public:
|
public:
|
||||||
RelinkerPipeline(
|
RelinkerPipeline(
|
||||||
std::shared_ptr<IElfReader> elfReader,
|
std::shared_ptr<IElfReader> elfReader,
|
||||||
std::shared_ptr<ISceDynlibParser> dynlibParser,
|
|
||||||
std::shared_ptr<ISyscallScanner> syscallScanner,
|
std::shared_ptr<ISyscallScanner> syscallScanner,
|
||||||
std::shared_ptr<ICallSiteResolver> callSiteResolver,
|
std::shared_ptr<ICallSiteResolver> callSiteResolver,
|
||||||
std::shared_ptr<IValidationPolicy> validationPolicy,
|
std::shared_ptr<IValidationPolicy> validationPolicy,
|
||||||
|
|
@ -28,7 +25,6 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<IElfReader> _elfReader;
|
std::shared_ptr<IElfReader> _elfReader;
|
||||||
std::shared_ptr<ISceDynlibParser> _dynlibParser;
|
|
||||||
std::shared_ptr<ISyscallScanner> _syscallScanner;
|
std::shared_ptr<ISyscallScanner> _syscallScanner;
|
||||||
std::shared_ptr<ICallSiteResolver> _callSiteResolver;
|
std::shared_ptr<ICallSiteResolver> _callSiteResolver;
|
||||||
std::shared_ptr<IValidationPolicy> _validationPolicy;
|
std::shared_ptr<IValidationPolicy> _validationPolicy;
|
||||||
|
|
@ -36,7 +32,6 @@ private:
|
||||||
|
|
||||||
static constexpr std::uint32_t PT_LOAD = 1;
|
static constexpr std::uint32_t PT_LOAD = 1;
|
||||||
static constexpr std::uint32_t PT_DYNAMIC = 2;
|
static constexpr std::uint32_t PT_DYNAMIC = 2;
|
||||||
static constexpr std::uint32_t PT_SCE_DYNLIBDATA = 0x61000000;
|
|
||||||
static constexpr std::uint32_t PF_X = 0x1;
|
static constexpr std::uint32_t PF_X = 0x1;
|
||||||
|
|
||||||
static constexpr std::int64_t DT_PLTGOT = 0x00000003;
|
static constexpr std::int64_t DT_PLTGOT = 0x00000003;
|
||||||
|
|
@ -45,10 +40,8 @@ private:
|
||||||
static constexpr std::int64_t DT_OS_PLTRELSZ = 0x6100002d;
|
static constexpr std::int64_t DT_OS_PLTRELSZ = 0x6100002d;
|
||||||
static constexpr std::int64_t DT_NEEDED = 0x00000001;
|
static constexpr std::int64_t DT_NEEDED = 0x00000001;
|
||||||
static constexpr std::int64_t DT_STRTAB = 0x00000005;
|
static constexpr std::int64_t DT_STRTAB = 0x00000005;
|
||||||
static constexpr std::int64_t DT_STRSZ = 0x0000000a;
|
|
||||||
static constexpr std::int64_t DT_RELA = 0x00000007;
|
static constexpr std::int64_t DT_RELA = 0x00000007;
|
||||||
static constexpr std::int64_t DT_RELASZ = 0x00000008;
|
static constexpr std::int64_t DT_RELASZ = 0x00000008;
|
||||||
static constexpr std::int64_t DT_JMPREL = 0x61000029;
|
|
||||||
static constexpr std::int64_t DT_SYSV_JMPREL = 0x00000017;
|
static constexpr std::int64_t DT_SYSV_JMPREL = 0x00000017;
|
||||||
static constexpr std::int64_t DT_PLTRELSZ_SYSV = 0x00000002;
|
static constexpr std::int64_t DT_PLTRELSZ_SYSV = 0x00000002;
|
||||||
static constexpr std::int64_t DT_SYMTAB = 0x00000006;
|
static constexpr std::int64_t DT_SYMTAB = 0x00000006;
|
||||||
|
|
|
||||||
|
|
@ -1,158 +0,0 @@
|
||||||
#include <relinker/parsing/SceDynlibParser.hpp>
|
|
||||||
#include <relinker/domain/ISdkRevisionProfile.hpp>
|
|
||||||
#include <cstring>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace Relinker {
|
|
||||||
|
|
||||||
static constexpr std::uint64_t NID_INDEX_MASK = 0x000000000000FFFFULL;
|
|
||||||
static constexpr std::uint64_t LIB_INDEX_MASK = 0x00000000FFFF0000ULL;
|
|
||||||
static constexpr int LIB_INDEX_SHIFT = 16;
|
|
||||||
static constexpr std::uint32_t RELOC_TYPE_MASK = 0xFFFFFFFFU;
|
|
||||||
|
|
||||||
SceDynlibParser::SceDynlibParser(std::shared_ptr<ISdkRevisionProfile> SdkProfile)
|
|
||||||
: _sdkProfile(std::move(SdkProfile)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint8_t SceDynlibParser::_readU8At(
|
|
||||||
const std::vector<std::uint8_t>& data, const FileByteOffset fileByteOffset) const {
|
|
||||||
if (fileByteOffset >= data.size()) {
|
|
||||||
throw RelinkerException("SCE dynlib data offset out of bounds", fileByteOffset);
|
|
||||||
}
|
|
||||||
return data[fileByteOffset];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint16_t SceDynlibParser::_readU16At(
|
|
||||||
const std::vector<std::uint8_t>& data, const FileByteOffset fileByteOffset) const {
|
|
||||||
if (fileByteOffset + 2 > data.size()) {
|
|
||||||
throw RelinkerException("SCE dynlib data offset out of bounds", fileByteOffset);
|
|
||||||
}
|
|
||||||
std::uint16_t value;
|
|
||||||
std::memcpy(&value, data.data() + fileByteOffset, 2);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t SceDynlibParser::_readU32At(
|
|
||||||
const std::vector<std::uint8_t>& data, const FileByteOffset fileByteOffset) const {
|
|
||||||
if (fileByteOffset + 4 > data.size()) {
|
|
||||||
throw RelinkerException("SCE dynlib data offset out of bounds", fileByteOffset);
|
|
||||||
}
|
|
||||||
std::uint32_t value;
|
|
||||||
std::memcpy(&value, data.data() + fileByteOffset, 4);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint64_t SceDynlibParser::_readU64At(
|
|
||||||
const std::vector<std::uint8_t>& data, const FileByteOffset fileByteOffset) const {
|
|
||||||
if (fileByteOffset + 8 > data.size()) {
|
|
||||||
throw RelinkerException("SCE dynlib data offset out of bounds", fileByteOffset);
|
|
||||||
}
|
|
||||||
std::uint64_t value;
|
|
||||||
std::memcpy(&value, data.data() + fileByteOffset, 8);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<LibraryImport> SceDynlibParser::ParseLibraryImports(
|
|
||||||
const std::vector<std::uint8_t>& sceDynlibData) {
|
|
||||||
std::uint32_t count = _sdkProfile->GetImportsCount(sceDynlibData);
|
|
||||||
std::uint32_t baseOff = _sdkProfile->GetImportsOffset();
|
|
||||||
std::uint32_t entrySize = _sdkProfile->GetSceDynlibDataEntrySize();
|
|
||||||
|
|
||||||
std::vector<LibraryImport> imports;
|
|
||||||
imports.reserve(count);
|
|
||||||
|
|
||||||
FileByteOffset cursor = baseOff;
|
|
||||||
for (std::uint32_t i = 0; i < count; ++i) {
|
|
||||||
if (cursor + entrySize > sceDynlibData.size()) {
|
|
||||||
throw RelinkerException("Library import entry out of bounds", cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t nameOffset = _readU32At(sceDynlibData, cursor);
|
|
||||||
std::uint32_t libraryId = _readU16At(sceDynlibData, cursor + 0x04);
|
|
||||||
std::uint32_t version = _readU16At(sceDynlibData, cursor + 0x06);
|
|
||||||
|
|
||||||
std::string name;
|
|
||||||
FileByteOffset namePos = nameOffset;
|
|
||||||
while (namePos < sceDynlibData.size() && sceDynlibData[namePos] != '\0') {
|
|
||||||
name += static_cast<char>(sceDynlibData[namePos]);
|
|
||||||
++namePos;
|
|
||||||
}
|
|
||||||
|
|
||||||
LibraryImport imp;
|
|
||||||
imp.LibraryName = std::move(name);
|
|
||||||
imp.LibraryId = libraryId;
|
|
||||||
imp.Version = version;
|
|
||||||
imports.push_back(std::move(imp));
|
|
||||||
|
|
||||||
cursor += entrySize;
|
|
||||||
}
|
|
||||||
|
|
||||||
return imports;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<RelocationEntry> SceDynlibParser::ParseRelocationTable(
|
|
||||||
const std::vector<std::uint8_t>& sceDynlibData) {
|
|
||||||
const std::uint32_t tableOffset = _sdkProfile->GetRelocationTableOffset();
|
|
||||||
const std::uint32_t entrySize = _sdkProfile->GetRelocationEntrySize();
|
|
||||||
|
|
||||||
if (tableOffset >= sceDynlibData.size()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<RelocationEntry> entries;
|
|
||||||
FileByteOffset cursor = tableOffset;
|
|
||||||
|
|
||||||
while (cursor + entrySize <= sceDynlibData.size()) {
|
|
||||||
RelocationEntry entry;
|
|
||||||
entry.EntryOffset = _readU64At(sceDynlibData, cursor);
|
|
||||||
entry.Info = _readU64At(sceDynlibData, cursor + 0x08);
|
|
||||||
entry.Addend = static_cast<std::int64_t>(_readU64At(sceDynlibData, cursor + 0x10));
|
|
||||||
entries.push_back(entry);
|
|
||||||
cursor += entrySize;
|
|
||||||
}
|
|
||||||
|
|
||||||
return entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string SceDynlibParser::_extractNidFromRelocationInfo(std::uint64_t info) const {
|
|
||||||
std::uint64_t symIndex = info >> 32;
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << std::hex << symIndex;
|
|
||||||
return oss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string SceDynlibParser::_getLibraryNameForRelocation(
|
|
||||||
const std::vector<LibraryImport>& imports, const std::uint64_t info) const {
|
|
||||||
std::uint32_t libIndex = static_cast<std::uint32_t>((info & LIB_INDEX_MASK) >> LIB_INDEX_SHIFT);
|
|
||||||
if (libIndex == 0 || libIndex > imports.size()) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return imports[libIndex - 1].LibraryName;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<NidReference> SceDynlibParser::ExtractNidReferences(
|
|
||||||
const std::vector<RelocationEntry>& relocations,
|
|
||||||
const std::vector<LibraryImport>& imports) {
|
|
||||||
std::vector<NidReference> refs;
|
|
||||||
refs.reserve(relocations.size());
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < relocations.size(); ++i) {
|
|
||||||
const auto& reloc = relocations[i];
|
|
||||||
std::uint32_t relocType = static_cast<std::uint32_t>(reloc.Info & RELOC_TYPE_MASK);
|
|
||||||
|
|
||||||
std::string nid = _extractNidFromRelocationInfo(reloc.Info);
|
|
||||||
std::string library = _getLibraryNameForRelocation(imports, reloc.Info);
|
|
||||||
|
|
||||||
NidReference ref;
|
|
||||||
ref.Nid = std::move(nid);
|
|
||||||
ref.Library = std::move(library);
|
|
||||||
ref.RelocationTypeValue = relocType;
|
|
||||||
ref.RelocationTableOffset = static_cast<FileByteOffset>(i * _sdkProfile->GetRelocationEntrySize());
|
|
||||||
ref.RelocationAddress = reloc.EntryOffset;
|
|
||||||
refs.push_back(std::move(ref));
|
|
||||||
}
|
|
||||||
|
|
||||||
return refs;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
#include <relinker/parsing/SdkRevisionProfile.hpp>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
namespace Relinker {
|
|
||||||
|
|
||||||
constexpr std::uint32_t DEFAULT_SDK_REVISION = 0x05000000;
|
|
||||||
|
|
||||||
constexpr std::uint32_t SCE_MODULE_PARAM_HEADER_SIZE = 0x38;
|
|
||||||
constexpr std::uint32_t SCE_MODULE_PARAM_ENTRY_SIZE = 0x08;
|
|
||||||
constexpr std::uint32_t SCE_DYNLIBDATA_HEADER_SIZE = 0x10;
|
|
||||||
constexpr std::uint32_t SCE_DYNLIBDATA_ENTRY_SIZE = 0x10;
|
|
||||||
constexpr std::uint32_t RELOCATION_ENTRY_SIZE = 0x18;
|
|
||||||
|
|
||||||
SdkRevisionProfile::SdkRevisionProfile(const std::vector<std::uint8_t>& sceDynlibData)
|
|
||||||
: _sdkRevision(DEFAULT_SDK_REVISION) {
|
|
||||||
_detectSdkRevision(sceDynlibData);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SdkRevisionProfile::_detectSdkRevision(const std::vector<std::uint8_t>& data) {
|
|
||||||
if (data.size() < 8) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_sdkRevision = _readU32LittleEndian(data, 0x04);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t SdkRevisionProfile::_readU32LittleEndian(
|
|
||||||
const std::vector<std::uint8_t>& data, const FileByteOffset fileByteOffset) const {
|
|
||||||
if (fileByteOffset + 4 > data.size()) {
|
|
||||||
throw RelinkerException("FileByteOffset out of bounds", fileByteOffset);
|
|
||||||
}
|
|
||||||
std::uint32_t value;
|
|
||||||
std::memcpy(&value, data.data() + fileByteOffset, 4);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint16_t SdkRevisionProfile::_readU16LittleEndian(
|
|
||||||
const std::vector<std::uint8_t>& data, const FileByteOffset fileByteOffset) const {
|
|
||||||
if (fileByteOffset + 2 > data.size()) {
|
|
||||||
throw RelinkerException("FileByteOffset out of bounds", fileByteOffset);
|
|
||||||
}
|
|
||||||
std::uint16_t value;
|
|
||||||
std::memcpy(&value, data.data() + fileByteOffset, 2);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t SdkRevisionProfile::GetSceModuleParamHeaderSize() const {
|
|
||||||
return SCE_MODULE_PARAM_HEADER_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t SdkRevisionProfile::GetSceModuleParamEntrySize() const {
|
|
||||||
return SCE_MODULE_PARAM_ENTRY_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t SdkRevisionProfile::GetSceDynlibDataHeaderSize() const {
|
|
||||||
return SCE_DYNLIBDATA_HEADER_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t SdkRevisionProfile::GetSceDynlibDataEntrySize() const {
|
|
||||||
return SCE_DYNLIBDATA_ENTRY_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t SdkRevisionProfile::GetRelocationEntrySize() const {
|
|
||||||
return RELOCATION_ENTRY_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t SdkRevisionProfile::GetImportsOffset() const {
|
|
||||||
return SCE_DYNLIBDATA_HEADER_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t SdkRevisionProfile::GetImportsCount(const std::vector<std::uint8_t>& data) const {
|
|
||||||
if (data.size() < 8) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return _readU32LittleEndian(data, 0x00);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t SdkRevisionProfile::GetRelocationTableOffset() const {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -8,13 +8,11 @@ namespace Relinker {
|
||||||
|
|
||||||
RelinkerPipeline::RelinkerPipeline(
|
RelinkerPipeline::RelinkerPipeline(
|
||||||
std::shared_ptr<IElfReader> elfReader,
|
std::shared_ptr<IElfReader> elfReader,
|
||||||
std::shared_ptr<ISceDynlibParser> dynlibParser,
|
|
||||||
std::shared_ptr<ISyscallScanner> syscallScanner,
|
std::shared_ptr<ISyscallScanner> syscallScanner,
|
||||||
std::shared_ptr<ICallSiteResolver> callSiteResolver,
|
std::shared_ptr<ICallSiteResolver> callSiteResolver,
|
||||||
std::shared_ptr<IValidationPolicy> validationPolicy,
|
std::shared_ptr<IValidationPolicy> validationPolicy,
|
||||||
std::shared_ptr<ISysVDynamicSectionBuilder> dynamicSectionBuilder)
|
std::shared_ptr<ISysVDynamicSectionBuilder> dynamicSectionBuilder)
|
||||||
: _elfReader(std::move(elfReader))
|
: _elfReader(std::move(elfReader))
|
||||||
, _dynlibParser(std::move(dynlibParser))
|
|
||||||
, _syscallScanner(std::move(syscallScanner))
|
, _syscallScanner(std::move(syscallScanner))
|
||||||
, _callSiteResolver(std::move(callSiteResolver))
|
, _callSiteResolver(std::move(callSiteResolver))
|
||||||
, _validationPolicy(std::move(validationPolicy))
|
, _validationPolicy(std::move(validationPolicy))
|
||||||
|
|
@ -38,16 +36,11 @@ std::string RelinkerPipeline::_relocationTypeName(std::uint32_t type) {
|
||||||
RelinkResult RelinkerPipeline::Relink(const std::vector<std::uint8_t>& sourceElf) {
|
RelinkResult RelinkerPipeline::Relink(const std::vector<std::uint8_t>& sourceElf) {
|
||||||
auto programHeaders = _elfReader->ReadProgramHeaders();
|
auto programHeaders = _elfReader->ReadProgramHeaders();
|
||||||
|
|
||||||
std::vector<std::uint8_t> sceDynlibData;
|
|
||||||
std::vector<std::uint8_t> textSection;
|
std::vector<std::uint8_t> textSection;
|
||||||
VirtualAddress textVAddr = 0;
|
VirtualAddress textVAddr = 0;
|
||||||
VirtualAddress gotVAddr = 0;
|
VirtualAddress gotVAddr = 0;
|
||||||
ByteCount gotSize = 0;
|
ByteCount gotSize = 0;
|
||||||
|
|
||||||
for (const auto& ph : programHeaders)
|
|
||||||
if (ph.Type == PT_SCE_DYNLIBDATA)
|
|
||||||
sceDynlibData = _elfReader->ReadSegment(ph);
|
|
||||||
|
|
||||||
for (const auto& ph : programHeaders) {
|
for (const auto& ph : programHeaders) {
|
||||||
if (ph.Type == PT_LOAD && (ph.Flags & PF_X) != 0) {
|
if (ph.Type == PT_LOAD && (ph.Flags & PF_X) != 0) {
|
||||||
textSection = _elfReader->ReadSegment(ph);
|
textSection = _elfReader->ReadSegment(ph);
|
||||||
|
|
@ -57,18 +50,20 @@ RelinkResult RelinkerPipeline::Relink(const std::vector<std::uint8_t>& sourceElf
|
||||||
}
|
}
|
||||||
|
|
||||||
FileByteOffset dynStrTabOffset = 0;
|
FileByteOffset dynStrTabOffset = 0;
|
||||||
ByteCount dynStrTabSize = 0;
|
|
||||||
FileByteOffset dynSymTabOffset = 0;
|
FileByteOffset dynSymTabOffset = 0;
|
||||||
FileByteOffset dynRelaOffset = 0;
|
FileByteOffset dynRelaOffset = 0;
|
||||||
ByteCount dynRelaSize = 0;
|
ByteCount dynRelaSize = 0;
|
||||||
FileByteOffset dynJmpRelOffset = 0;
|
FileByteOffset dynJmpRelOffset = 0;
|
||||||
ByteCount dynJmpRelSize = 0;
|
ByteCount dynJmpRelSize = 0;
|
||||||
|
bool hasDynamicSegment = false;
|
||||||
std::vector<std::pair<std::uint64_t, std::string>> neededLibraryNamesByStrOffset;
|
std::vector<std::pair<std::uint64_t, std::string>> neededLibraryNamesByStrOffset;
|
||||||
|
|
||||||
for (const auto& ph : programHeaders) {
|
for (const auto& ph : programHeaders) {
|
||||||
if (ph.Type != PT_DYNAMIC)
|
if (ph.Type != PT_DYNAMIC)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
hasDynamicSegment = true;
|
||||||
|
|
||||||
for (const auto& tag : _elfReader->ReadDynamicTags(ph)) {
|
for (const auto& tag : _elfReader->ReadDynamicTags(ph)) {
|
||||||
if (tag.Tag == DT_PLTGOT || tag.Tag == DT_OS_PLTGOT)
|
if (tag.Tag == DT_PLTGOT || tag.Tag == DT_OS_PLTGOT)
|
||||||
gotVAddr = tag.Value;
|
gotVAddr = tag.Value;
|
||||||
|
|
@ -76,8 +71,6 @@ RelinkResult RelinkerPipeline::Relink(const std::vector<std::uint8_t>& sourceElf
|
||||||
gotSize = tag.Value;
|
gotSize = tag.Value;
|
||||||
else if (tag.Tag == DT_STRTAB)
|
else if (tag.Tag == DT_STRTAB)
|
||||||
dynStrTabOffset = _elfReader->TranslateVirtualAddress(tag.Value);
|
dynStrTabOffset = _elfReader->TranslateVirtualAddress(tag.Value);
|
||||||
else if (tag.Tag == DT_STRSZ)
|
|
||||||
dynStrTabSize = tag.Value;
|
|
||||||
else if (tag.Tag == DT_SYMTAB)
|
else if (tag.Tag == DT_SYMTAB)
|
||||||
dynSymTabOffset = _elfReader->TranslateVirtualAddress(tag.Value);
|
dynSymTabOffset = _elfReader->TranslateVirtualAddress(tag.Value);
|
||||||
else if (tag.Tag == DT_RELA)
|
else if (tag.Tag == DT_RELA)
|
||||||
|
|
@ -94,83 +87,70 @@ RelinkResult RelinkerPipeline::Relink(const std::vector<std::uint8_t>& sourceElf
|
||||||
break;
|
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<NidReference> nidRefs;
|
||||||
std::vector<std::string> neededLibraries;
|
std::vector<std::string> neededLibraries;
|
||||||
auto policy = std::dynamic_pointer_cast<ValidationPolicy>(_validationPolicy);
|
auto policy = std::dynamic_pointer_cast<ValidationPolicy>(_validationPolicy);
|
||||||
|
|
||||||
if (!sceDynlibData.empty()) {
|
const std::vector<std::uint8_t>& raw = _elfReader->GetRawBytes();
|
||||||
auto imports = _dynlibParser->ParseLibraryImports(sceDynlibData);
|
|
||||||
auto relocations = _dynlibParser->ParseRelocationTable(sceDynlibData);
|
|
||||||
nidRefs = _dynlibParser->ExtractNidReferences(relocations, imports);
|
|
||||||
|
|
||||||
for (const auto& imp : imports) {
|
auto readCStr = [&](FileByteOffset strOff) -> std::string {
|
||||||
if (policy) policy->RegisterLibraryImport(imp.LibraryName);
|
std::string result;
|
||||||
neededLibraries.push_back(imp.LibraryName);
|
FileByteOffset pos = dynStrTabOffset + strOff;
|
||||||
}
|
while (pos < raw.size() && raw[pos] != 0)
|
||||||
for (const auto& ref : nidRefs) {
|
result.push_back(static_cast<char>(raw[pos++]));
|
||||||
_validationPolicy->ValidateRelocationTypeSupported(ref.RelocationTypeValue, ref.RelocationTableOffset);
|
return result;
|
||||||
_validationPolicy->ValidateNidBelongsToLibrary(ref.Nid, ref.Library);
|
};
|
||||||
}
|
|
||||||
} else if (dynStrTabOffset != 0) {
|
|
||||||
const std::vector<std::uint8_t>& raw = _elfReader->GetRawBytes();
|
|
||||||
|
|
||||||
auto readCStr = [&](FileByteOffset strOff) -> std::string {
|
for (auto& [fst, snd] : neededLibraryNamesByStrOffset) {
|
||||||
std::string result;
|
snd = readCStr(fst);
|
||||||
FileByteOffset pos = dynStrTabOffset + strOff;
|
neededLibraries.push_back(snd);
|
||||||
while (pos < raw.size() && raw[pos] != 0)
|
if (policy) policy->RegisterLibraryImport(snd);
|
||||||
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())
|
|
||||||
break;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (dynSymTabOffset == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const FileByteOffset symOff = dynSymTabOffset + static_cast<FileByteOffset>(symIdx) * 24;
|
|
||||||
if (symOff + 4 > raw.size())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
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);
|
|
||||||
} else {
|
|
||||||
throw RelinkerException("Neither SCE_DYNLIBDATA segment nor hybrid PT_DYNAMIC NID data found");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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())
|
if (!textSection.empty())
|
||||||
_syscallScanner->ScanCodeSectionForSyscalls(textSection, textVAddr, textSection.size());
|
_syscallScanner->ScanCodeSectionForSyscalls(textSection, textVAddr, textSection.size());
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue